authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-02 12:09:38-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-02 12:09:38-07:00
loga0e89c9b46fc91d9b1dfebd02eaae233802e3cbc
tree6a1ee37f130b4cb69204158ff915ecc2c6bb002b
parent94383d14df77fa638dac14f4b2bda5a2e3f21c5c
parent228a1ce3e8d112a7710fa47c6b9486cf320b5d6f

Merge remote-tracking branch 'origin/master' into llvm12


52 files changed, 17120 insertions(+), 11701 deletions(-)

CMakeLists.txt+2-2
......@@ -539,7 +539,7 @@ set(ZIG_STAGE2_SOURCES
539539 "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig"
540540 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"
541541 "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig"
542 "${CMAKE_SOURCE_DIR}/src/astgen.zig"
542 "${CMAKE_SOURCE_DIR}/src/AstGen.zig"
543543 "${CMAKE_SOURCE_DIR}/src/clang.zig"
544544 "${CMAKE_SOURCE_DIR}/src/clang_options.zig"
545545 "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"
......@@ -591,7 +591,7 @@ set(ZIG_STAGE2_SOURCES
591591 "${CMAKE_SOURCE_DIR}/src/value.zig"
592592 "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"
593593 "${CMAKE_SOURCE_DIR}/src/zir.zig"
594 "${CMAKE_SOURCE_DIR}/src/zir_sema.zig"
594 "${CMAKE_SOURCE_DIR}/src/Sema.zig"
595595)
596596
597597if(MSVC)
doc/docgen.zig+1-1
......@@ -1349,7 +1349,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
13491349 }
13501350 const escaped_stderr = try escapeHtml(allocator, result.stderr);
13511351 const colored_stderr = try termColor(allocator, escaped_stderr);
1352 try out.print("<pre><code class=\"shell\">$ zig test {s}.zig{s}\n{s}</code></pre>\n", .{
1352 try out.print("<pre><code class=\"shell\">$ zig test {s}.zig {s}\n{s}</code></pre>\n", .{
13531353 code.name,
13541354 mode_arg,
13551355 colored_stderr,
doc/langref.html.in+14-9
......@@ -6594,13 +6594,11 @@ const std = @import("std");
65946594const expect = std.testing.expect;
65956595
65966596test "async and await" {
6597 // Here we have an exception where we do not match an async
6598 // with an await. The test block is not async and so cannot
6599 // have a suspend point in it.
6600 // This is well-defined behavior, and everything is OK here.
6601 // Note however that there would be no way to collect the
6602 // return value of amain, if it were something other than void.
6603 _ = async amain();
6597 // The test block is not async and so cannot have a suspend
6598 // point in it. By using the nosuspend keyword, we promise that
6599 // the code in amain will finish executing without suspending
6600 // back to the test block.
6601 nosuspend amain();
66046602}
66056603
66066604fn amain() void {
......@@ -10799,9 +10797,16 @@ fn readU32Be() u32 {}
1079910797 <pre>{#syntax#}nosuspend{#endsyntax#}</pre>
1080010798 </td>
1080110799 <td>
10802 The {#syntax#}nosuspend{#endsyntax#} keyword.
10800 The {#syntax#}nosuspend{#endsyntax#} keyword can be used in front of a block, statement or expression, to mark a scope where no suspension points are reached.
10801 In particular, inside a {#syntax#}nosuspend{#endsyntax#} scope:
10802 <ul>
10803 <li>Using the {#syntax#}suspend{#endsyntax#} keyword results in a compile error.</li>
10804 <li>Using {#syntax#}await{#endsyntax#} on a function frame which hasn't completed yet results in safety-checked {#link|Undefined Behavior#}.</li>
10805 <li>Calling an async function may result in safety-checked {#link|Undefined Behavior#}, because it's equivalent to <code>await async some_async_fn()</code>, which contains an {#syntax#}await{#endsyntax#}.</li>
10806 </ul>
10807 Code inside a {#syntax#}nosuspend{#endsyntax#} scope does not cause the enclosing function to become an {#link|async function|Async Functions#}.
1080310808 <ul>
10804 <li>TODO add documentation for nosuspend</li>
10809 <li>See also {#link|Async Functions#}</li>
1080510810 </ul>
1080610811 </td>
1080710812 </tr>
lib/std/enums.zig+48-41
......@@ -32,7 +32,7 @@ pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_def
3232 .fields = fields,
3333 .decls = &[_]std.builtin.TypeInfo.Declaration{},
3434 .is_tuple = false,
35 }});
35 } });
3636}
3737
3838/// Looks up the supplied fields in the given enum type.
......@@ -70,7 +70,7 @@ pub fn values(comptime E: type) []const E {
7070
7171test "std.enum.values" {
7272 const E = extern enum { a, b, c, d = 0 };
73 testing.expectEqualSlices(E, &.{.a, .b, .c, .d}, values(E));
73 testing.expectEqualSlices(E, &.{ .a, .b, .c, .d }, values(E));
7474}
7575
7676/// Returns the set of all unique named values in the given enum, in
......@@ -82,10 +82,10 @@ pub fn uniqueValues(comptime E: type) []const E {
8282
8383test "std.enum.uniqueValues" {
8484 const E = extern enum { a, b, c, d = 0, e, f = 3 };
85 testing.expectEqualSlices(E, &.{.a, .b, .c, .f}, uniqueValues(E));
85 testing.expectEqualSlices(E, &.{ .a, .b, .c, .f }, uniqueValues(E));
8686
8787 const F = enum { a, b, c };
88 testing.expectEqualSlices(F, &.{.a, .b, .c}, uniqueValues(F));
88 testing.expectEqualSlices(F, &.{ .a, .b, .c }, uniqueValues(F));
8989}
9090
9191/// Returns the set of all unique field values in the given enum, in
......@@ -102,8 +102,7 @@ pub fn uniqueFields(comptime E: type) []const EnumField {
102102 }
103103
104104 var unique_fields: []const EnumField = &[_]EnumField{};
105 outer:
106 for (raw_fields) |candidate| {
105 outer: for (raw_fields) |candidate| {
107106 for (unique_fields) |u| {
108107 if (u.value == candidate.value)
109108 continue :outer;
......@@ -116,28 +115,25 @@ pub fn uniqueFields(comptime E: type) []const EnumField {
116115}
117116
118117/// Determines the length of a direct-mapped enum array, indexed by
119/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
118/// @intCast(usize, @enumToInt(enum_value)).
119/// If the enum is non-exhaustive, the resulting length will only be enough
120/// to hold all explicit fields.
120121/// If the enum contains any fields with values that cannot be represented
121122/// by usize, a compile error is issued. The max_unused_slots parameter limits
122123/// the total number of items which have no matching enum key (holes in the enum
123124/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
124125/// must be at least 3, to allow unused slots 0, 3, and 4.
125126fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {
126 const info = @typeInfo(E).Enum;
127 if (!info.is_exhaustive) {
128 @compileError("Cannot create direct array of non-exhaustive enum "++@typeName(E));
129 }
130
131127 var max_value: comptime_int = -1;
132128 const max_usize: comptime_int = ~@as(usize, 0);
133129 const fields = uniqueFields(E);
134130 for (fields) |f| {
135131 if (f.value < 0) {
136 @compileError("Cannot create a direct enum array for "++@typeName(E)++", field ."++f.name++" has a negative value.");
132 @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " has a negative value.");
137133 }
138134 if (f.value > max_value) {
139135 if (f.value > max_usize) {
140 @compileError("Cannot create a direct enum array for "++@typeName(E)++", field ."++f.name++" is larger than the max value of usize.");
136 @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " is larger than the max value of usize.");
141137 }
142138 max_value = f.value;
143139 }
......@@ -147,14 +143,16 @@ fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int)
147143 if (unused_slots > max_unused_slots) {
148144 const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots});
149145 const allowed_str = std.fmt.comptimePrint("{d}", .{max_unused_slots});
150 @compileError("Cannot create a direct enum array for "++@typeName(E)++". It would have "++unused_str++" unused slots, but only "++allowed_str++" are allowed.");
146 @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ". It would have " ++ unused_str ++ " unused slots, but only " ++ allowed_str ++ " are allowed.");
151147 }
152148
153149 return max_value + 1;
154150}
155151
156152/// Initializes an array of Data which can be indexed by
157/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
153/// @intCast(usize, @enumToInt(enum_value)).
154/// If the enum is non-exhaustive, the resulting array will only be large enough
155/// to hold all explicit fields.
158156/// If the enum contains any fields with values that cannot be represented
159157/// by usize, a compile error is issued. The max_unused_slots parameter limits
160158/// the total number of items which have no matching enum key (holes in the enum
......@@ -243,9 +241,9 @@ pub fn nameCast(comptime E: type, comptime value: anytype) E {
243241 if (@hasField(E, n)) {
244242 return @field(E, n);
245243 }
246 @compileError("Enum "++@typeName(E)++" has no field named "++n);
244 @compileError("Enum " ++ @typeName(E) ++ " has no field named " ++ n);
247245 }
248 @compileError("Cannot cast from "++@typeName(@TypeOf(value))++" to "++@typeName(E));
246 @compileError("Cannot cast from " ++ @typeName(@TypeOf(value)) ++ " to " ++ @typeName(E));
249247 }
250248}
251249
......@@ -256,7 +254,7 @@ test "std.enums.nameCast" {
256254 testing.expectEqual(A.a, nameCast(A, A.a));
257255 testing.expectEqual(A.a, nameCast(A, B.a));
258256 testing.expectEqual(A.a, nameCast(A, "a"));
259 testing.expectEqual(A.a, nameCast(A, @as(*const[1]u8, "a")));
257 testing.expectEqual(A.a, nameCast(A, @as(*const [1]u8, "a")));
260258 testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a")));
261259 testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a")));
262260
......@@ -398,12 +396,12 @@ pub fn EnumArray(comptime E: type, comptime V: type) type {
398396pub fn NoExtension(comptime Self: type) type {
399397 return NoExt;
400398}
401const NoExt = struct{};
399const NoExt = struct {};
402400
403401/// A set type with an Indexer mapping from keys to indices.
404402/// Presence or absence is stored as a dense bitfield. This
405403/// type does no allocation and can be copied by value.
406pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
404pub fn IndexedSet(comptime I: type, comptime Ext: fn (type) type) type {
407405 comptime ensureIndexer(I);
408406 return struct {
409407 const Self = @This();
......@@ -422,7 +420,7 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
422420
423421 bits: BitSet = BitSet.initEmpty(),
424422
425 /// Returns a set containing all possible keys.
423 /// Returns a set containing all possible keys.
426424 pub fn initFull() Self {
427425 return .{ .bits = BitSet.initFull() };
428426 }
......@@ -492,7 +490,8 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
492490 pub fn next(self: *Iterator) ?Key {
493491 return if (self.inner.next()) |index|
494492 Indexer.keyForIndex(index)
495 else null;
493 else
494 null;
496495 }
497496 };
498497 };
......@@ -501,7 +500,7 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
501500/// A map from keys to values, using an index lookup. Uses a
502501/// bitfield to track presence and a dense array of values.
503502/// This type does no allocation and can be copied by value.
504pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type {
503pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn (type) type) type {
505504 comptime ensureIndexer(I);
506505 return struct {
507506 const Self = @This();
......@@ -652,7 +651,8 @@ pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type
652651 .key = Indexer.keyForIndex(index),
653652 .value = &self.values[index],
654653 }
655 else null;
654 else
655 null;
656656 }
657657 };
658658 };
......@@ -660,7 +660,7 @@ pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type
660660
661661/// A dense array of values, using an indexed lookup.
662662/// This type does no allocation and can be copied by value.
663pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type {
663pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn (type) type) type {
664664 comptime ensureIndexer(I);
665665 return struct {
666666 const Self = @This();
......@@ -769,9 +769,9 @@ pub fn ensureIndexer(comptime T: type) void {
769769 if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize.");
770770 if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize.");
771771 if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn(Key)usize.");
772 if (@TypeOf(T.indexOf) != fn(T.Key)usize) @compileError("Indexer must have decl indexOf: fn(Key)usize.");
772 if (@TypeOf(T.indexOf) != fn (T.Key) usize) @compileError("Indexer must have decl indexOf: fn(Key)usize.");
773773 if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn(usize)Key.");
774 if (@TypeOf(T.keyForIndex) != fn(usize)T.Key) @compileError("Indexer.keyForIndex must be a fn(usize)Key.");
774 if (@TypeOf(T.keyForIndex) != fn (usize) T.Key) @compileError("Indexer.keyForIndex must be a fn(usize)Key.");
775775 }
776776}
777777
......@@ -802,14 +802,18 @@ pub fn EnumIndexer(comptime E: type) type {
802802 return struct {
803803 pub const Key = E;
804804 pub const count: usize = 0;
805 pub fn indexOf(e: E) usize { unreachable; }
806 pub fn keyForIndex(i: usize) E { unreachable; }
805 pub fn indexOf(e: E) usize {
806 unreachable;
807 }
808 pub fn keyForIndex(i: usize) E {
809 unreachable;
810 }
807811 };
808812 }
809813 std.sort.sort(EnumField, &fields, {}, ascByValue);
810814 const min = fields[0].value;
811 const max = fields[fields.len-1].value;
812 if (max - min == fields.len-1) {
815 const max = fields[fields.len - 1].value;
816 if (max - min == fields.len - 1) {
813817 return struct {
814818 pub const Key = E;
815819 pub const count = fields.len;
......@@ -844,7 +848,7 @@ pub fn EnumIndexer(comptime E: type) type {
844848}
845849
846850test "std.enums.EnumIndexer dense zeroed" {
847 const E = enum{ b = 1, a = 0, c = 2 };
851 const E = enum { b = 1, a = 0, c = 2 };
848852 const Indexer = EnumIndexer(E);
849853 ensureIndexer(Indexer);
850854 testing.expectEqual(E, Indexer.Key);
......@@ -908,7 +912,7 @@ test "std.enums.EnumIndexer sparse" {
908912}
909913
910914test "std.enums.EnumIndexer repeats" {
911 const E = extern enum{ a = -2, c = 6, b = 4, b2 = 4 };
915 const E = extern enum { a = -2, c = 6, b = 4, b2 = 4 };
912916 const Indexer = EnumIndexer(E);
913917 ensureIndexer(Indexer);
914918 testing.expectEqual(E, Indexer.Key);
......@@ -957,7 +961,8 @@ test "std.enums.EnumSet" {
957961 }
958962
959963 var mut = Set.init(.{
960 .a=true, .c=true,
964 .a = true,
965 .c = true,
961966 });
962967 testing.expectEqual(@as(usize, 2), mut.count());
963968 testing.expectEqual(true, mut.contains(.a));
......@@ -986,7 +991,7 @@ test "std.enums.EnumSet" {
986991 testing.expectEqual(@as(?E, null), it.next());
987992 }
988993
989 mut.toggleSet(Set.init(.{ .a=true, .b=true }));
994 mut.toggleSet(Set.init(.{ .a = true, .b = true }));
990995 testing.expectEqual(@as(usize, 2), mut.count());
991996 testing.expectEqual(true, mut.contains(.a));
992997 testing.expectEqual(false, mut.contains(.b));
......@@ -994,7 +999,7 @@ test "std.enums.EnumSet" {
994999 testing.expectEqual(true, mut.contains(.d));
9951000 testing.expectEqual(true, mut.contains(.e)); // aliases a
9961001
997 mut.setUnion(Set.init(.{ .a=true, .b=true }));
1002 mut.setUnion(Set.init(.{ .a = true, .b = true }));
9981003 testing.expectEqual(@as(usize, 3), mut.count());
9991004 testing.expectEqual(true, mut.contains(.a));
10001005 testing.expectEqual(true, mut.contains(.b));
......@@ -1009,7 +1014,7 @@ test "std.enums.EnumSet" {
10091014 testing.expectEqual(false, mut.contains(.c));
10101015 testing.expectEqual(true, mut.contains(.d));
10111016
1012 mut.setIntersection(Set.init(.{ .a=true, .b=true }));
1017 mut.setIntersection(Set.init(.{ .a = true, .b = true }));
10131018 testing.expectEqual(@as(usize, 1), mut.count());
10141019 testing.expectEqual(true, mut.contains(.a));
10151020 testing.expectEqual(false, mut.contains(.b));
......@@ -1072,7 +1077,7 @@ test "std.enums.EnumArray sized" {
10721077 const undef = Array.initUndefined();
10731078 var inst = Array.initFill(5);
10741079 const inst2 = Array.init(.{ .a = 1, .b = 2, .c = 3, .d = 4 });
1075 const inst3 = Array.initDefault(6, .{.b = 4, .c = 2});
1080 const inst3 = Array.initDefault(6, .{ .b = 4, .c = 2 });
10761081
10771082 testing.expectEqual(@as(usize, 5), inst.get(.a));
10781083 testing.expectEqual(@as(usize, 5), inst.get(.b));
......@@ -1272,10 +1277,12 @@ test "std.enums.EnumMap sized" {
12721277 var iter = a.iterator();
12731278 const Entry = Map.Entry;
12741279 testing.expectEqual(@as(?Entry, Entry{
1275 .key = .b, .value = &a.values[1],
1280 .key = .b,
1281 .value = &a.values[1],
12761282 }), iter.next());
12771283 testing.expectEqual(@as(?Entry, Entry{
1278 .key = .d, .value = &a.values[3],
1284 .key = .d,
1285 .value = &a.values[3],
12791286 }), iter.next());
12801287 testing.expectEqual(@as(?Entry, null), iter.next());
12811288}
lib/std/os.zig+2
......@@ -3267,6 +3267,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne
32673267 .WSAEADDRINUSE => return error.AddressInUse,
32683268 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
32693269 .WSAECONNREFUSED => return error.ConnectionRefused,
3270 .WSAECONNRESET => return error.ConnectionResetByPeer,
32703271 .WSAETIMEDOUT => return error.ConnectionTimedOut,
32713272 .WSAEHOSTUNREACH, // TODO: should we return NetworkUnreachable in this case as well?
32723273 .WSAENETUNREACH,
......@@ -3296,6 +3297,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne
32963297 EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
32973298 EBADF => unreachable, // sockfd is not a valid open file descriptor.
32983299 ECONNREFUSED => return error.ConnectionRefused,
3300 ECONNRESET => return error.ConnectionResetByPeer,
32993301 EFAULT => unreachable, // The socket structure address is outside the user's address space.
33003302 EINTR => continue,
33013303 EISCONN => unreachable, // The socket is already connected.
lib/std/os/linux/io_uring.zig+3-1
......@@ -1353,7 +1353,9 @@ test "timeout (after a relative time)" {
13531353 .res = -linux.ETIME,
13541354 .flags = 0,
13551355 }, cqe);
1356 testing.expectApproxEqAbs(@intToFloat(f64, ms), @intToFloat(f64, stopped - started), margin);
1356
1357 // Tests should not depend on timings: skip test (result) if outside margin.
1358 if (!std.math.approxEqAbs(f64, ms, @intToFloat(f64, stopped - started), margin)) return error.SkipZigTest;
13571359}
13581360
13591361test "timeout (after a number of completions)" {
lib/std/priority_dequeue.zig created+972
......@@ -0,0 +1,972 @@
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 Allocator = std.mem.Allocator;
8const assert = std.debug.assert;
9const warn = std.debug.warn;
10const Order = std.math.Order;
11const testing = std.testing;
12const expect = testing.expect;
13const expectEqual = testing.expectEqual;
14const expectError = testing.expectError;
15
16/// Priority Dequeue for storing generic data. Initialize with `init`.
17pub fn PriorityDequeue(comptime T: type) type {
18 return struct {
19 const Self = @This();
20
21 items: []T,
22 len: usize,
23 allocator: *Allocator,
24 compareFn: fn (a: T, b: T) Order,
25
26 /// Initialize and return a new priority dequeue. Provide `compareFn`
27 /// that returns `Order.lt` when its first argument should
28 /// get min-popped before its second argument, `Order.eq` if the
29 /// arguments are of equal priority, or `Order.gt` if the second
30 /// argument should be min-popped first. Popping the max element works
31 /// in reverse. For example, to make `popMin` return the smallest
32 /// number, provide
33 ///
34 /// `fn lessThan(a: T, b: T) Order { return std.math.order(a, b); }`
35 pub fn init(allocator: *Allocator, compareFn: fn (T, T) Order) Self {
36 return Self{
37 .items = &[_]T{},
38 .len = 0,
39 .allocator = allocator,
40 .compareFn = compareFn,
41 };
42 }
43
44 /// Free memory used by the dequeue.
45 pub fn deinit(self: Self) void {
46 self.allocator.free(self.items);
47 }
48
49 /// Insert a new element, maintaining priority.
50 pub fn add(self: *Self, elem: T) !void {
51 try ensureCapacity(self, self.len + 1);
52 addUnchecked(self, elem);
53 }
54
55 /// Add each element in `items` to the dequeue.
56 pub fn addSlice(self: *Self, items: []const T) !void {
57 try self.ensureCapacity(self.len + items.len);
58 for (items) |e| {
59 self.addUnchecked(e);
60 }
61 }
62
63 fn addUnchecked(self: *Self, elem: T) void {
64 self.items[self.len] = elem;
65
66 if (self.len > 0) {
67 const start = self.getStartForSiftUp(elem, self.len);
68 self.siftUp(start);
69 }
70
71 self.len += 1;
72 }
73
74 fn isMinLayer(index: usize) bool {
75 // In the min-max heap structure:
76 // The first element is on a min layer;
77 // next two are on a max layer;
78 // next four are on a min layer, and so on.
79 const leading_zeros = @clz(usize, index + 1);
80 const highest_set_bit = @bitSizeOf(usize) - 1 - leading_zeros;
81 return (highest_set_bit & 1) == 0;
82 }
83
84 fn nextIsMinLayer(self: Self) bool {
85 return isMinLayer(self.len);
86 }
87
88 const StartIndexAndLayer = struct {
89 index: usize,
90 min_layer: bool,
91 };
92
93 fn getStartForSiftUp(self: Self, child: T, index: usize) StartIndexAndLayer {
94 var child_index = index;
95 var parent_index = parentIndex(child_index);
96 const parent = self.items[parent_index];
97
98 const min_layer = self.nextIsMinLayer();
99 const order = self.compareFn(child, parent);
100 if ((min_layer and order == .gt) or (!min_layer and order == .lt)) {
101 // We must swap the item with it's parent if it is on the "wrong" layer
102 self.items[parent_index] = child;
103 self.items[child_index] = parent;
104 return .{
105 .index = parent_index,
106 .min_layer = !min_layer,
107 };
108 } else {
109 return .{
110 .index = child_index,
111 .min_layer = min_layer,
112 };
113 }
114 }
115
116 fn siftUp(self: *Self, start: StartIndexAndLayer) void {
117 if (start.min_layer) {
118 doSiftUp(self, start.index, .lt);
119 } else {
120 doSiftUp(self, start.index, .gt);
121 }
122 }
123
124 fn doSiftUp(self: *Self, start_index: usize, target_order: Order) void {
125 var child_index = start_index;
126 while (child_index > 2) {
127 var grandparent_index = grandparentIndex(child_index);
128 const child = self.items[child_index];
129 const grandparent = self.items[grandparent_index];
130
131 // If the grandparent is already better or equal, we have gone as far as we need to
132 if (self.compareFn(child, grandparent) != target_order) break;
133
134 // Otherwise swap the item with it's grandparent
135 self.items[grandparent_index] = child;
136 self.items[child_index] = grandparent;
137 child_index = grandparent_index;
138 }
139 }
140
141 /// Look at the smallest element in the dequeue. Returns
142 /// `null` if empty.
143 pub fn peekMin(self: *Self) ?T {
144 return if (self.len > 0) self.items[0] else null;
145 }
146
147 /// Look at the largest element in the dequeue. Returns
148 /// `null` if empty.
149 pub fn peekMax(self: *Self) ?T {
150 if (self.len == 0) return null;
151 if (self.len == 1) return self.items[0];
152 if (self.len == 2) return self.items[1];
153 return self.bestItemAtIndices(1, 2, .gt).item;
154 }
155
156 fn maxIndex(self: Self) ?usize {
157 if (self.len == 0) return null;
158 if (self.len == 1) return 0;
159 if (self.len == 2) return 1;
160 return self.bestItemAtIndices(1, 2, .gt).index;
161 }
162
163 /// Pop the smallest element from the dequeue. Returns
164 /// `null` if empty.
165 pub fn removeMinOrNull(self: *Self) ?T {
166 return if (self.len > 0) self.removeMin() else null;
167 }
168
169 /// Remove and return the smallest element from the
170 /// dequeue.
171 pub fn removeMin(self: *Self) T {
172 return self.removeIndex(0);
173 }
174
175 /// Pop the largest element from the dequeue. Returns
176 /// `null` if empty.
177 pub fn removeMaxOrNull(self: *Self) ?T {
178 return if (self.len > 0) self.removeMax() else null;
179 }
180
181 /// Remove and return the largest element from the
182 /// dequeue.
183 pub fn removeMax(self: *Self) T {
184 return self.removeIndex(self.maxIndex().?);
185 }
186
187 /// Remove and return element at index. Indices are in the
188 /// same order as iterator, which is not necessarily priority
189 /// order.
190 pub fn removeIndex(self: *Self, index: usize) T {
191 assert(self.len > index);
192 const item = self.items[index];
193 const last = self.items[self.len - 1];
194
195 self.items[index] = last;
196 self.len -= 1;
197 siftDown(self, index);
198
199 return item;
200 }
201
202 fn siftDown(self: *Self, index: usize) void {
203 if (isMinLayer(index)) {
204 self.doSiftDown(index, .lt);
205 } else {
206 self.doSiftDown(index, .gt);
207 }
208 }
209
210 fn doSiftDown(self: *Self, start_index: usize, target_order: Order) void {
211 var index = start_index;
212 const half = self.len >> 1;
213 while (true) {
214 const first_grandchild_index = firstGrandchildIndex(index);
215 const last_grandchild_index = first_grandchild_index + 3;
216
217 const elem = self.items[index];
218
219 if (last_grandchild_index < self.len) {
220 // All four grandchildren exist
221 const index2 = first_grandchild_index + 1;
222 const index3 = index2 + 1;
223
224 // Find the best grandchild
225 const best_left = self.bestItemAtIndices(first_grandchild_index, index2, target_order);
226 const best_right = self.bestItemAtIndices(index3, last_grandchild_index, target_order);
227 const best_grandchild = self.bestItem(best_left, best_right, target_order);
228
229 // If the item is better than or equal to its best grandchild, we are done
230 if (self.compareFn(best_grandchild.item, elem) != target_order) return;
231
232 // Otherwise, swap them
233 self.items[best_grandchild.index] = elem;
234 self.items[index] = best_grandchild.item;
235 index = best_grandchild.index;
236
237 // We might need to swap the element with it's parent
238 self.swapIfParentIsBetter(elem, index, target_order);
239 } else {
240 // The children or grandchildren are the last layer
241 const first_child_index = firstChildIndex(index);
242 if (first_child_index > self.len) return;
243
244 const best_descendent = self.bestDescendent(first_child_index, first_grandchild_index, target_order);
245
246 // If the item is better than or equal to its best descendant, we are done
247 if (self.compareFn(best_descendent.item, elem) != target_order) return;
248
249 // Otherwise swap them
250 self.items[best_descendent.index] = elem;
251 self.items[index] = best_descendent.item;
252 index = best_descendent.index;
253
254 // If we didn't swap a grandchild, we are done
255 if (index < first_grandchild_index) return;
256
257 // We might need to swap the element with it's parent
258 self.swapIfParentIsBetter(elem, index, target_order);
259 return;
260 }
261
262 // If we are now in the last layer, we are done
263 if (index >= half) return;
264 }
265 }
266
267 fn swapIfParentIsBetter(self: *Self, child: T, child_index: usize, target_order: Order) void {
268 const parent_index = parentIndex(child_index);
269 const parent = self.items[parent_index];
270
271 if (self.compareFn(parent, child) == target_order) {
272 self.items[parent_index] = child;
273 self.items[child_index] = parent;
274 }
275 }
276
277 const ItemAndIndex = struct {
278 item: T,
279 index: usize,
280 };
281
282 fn getItem(self: Self, index: usize) ItemAndIndex {
283 return .{
284 .item = self.items[index],
285 .index = index,
286 };
287 }
288
289 fn bestItem(self: Self, item1: ItemAndIndex, item2: ItemAndIndex, target_order: Order) ItemAndIndex {
290 if (self.compareFn(item1.item, item2.item) == target_order) {
291 return item1;
292 } else {
293 return item2;
294 }
295 }
296
297 fn bestItemAtIndices(self: Self, index1: usize, index2: usize, target_order: Order) ItemAndIndex {
298 var item1 = self.getItem(index1);
299 var item2 = self.getItem(index2);
300 return self.bestItem(item1, item2, target_order);
301 }
302
303 fn bestDescendent(self: Self, first_child_index: usize, first_grandchild_index: usize, target_order: Order) ItemAndIndex {
304 const second_child_index = first_child_index + 1;
305 if (first_grandchild_index >= self.len) {
306 // No grandchildren, find the best child (second may not exist)
307 if (second_child_index >= self.len) {
308 return .{
309 .item = self.items[first_child_index],
310 .index = first_child_index,
311 };
312 } else {
313 return self.bestItemAtIndices(first_child_index, second_child_index, target_order);
314 }
315 }
316
317 const second_grandchild_index = first_grandchild_index + 1;
318 if (second_grandchild_index >= self.len) {
319 // One grandchild, so we know there is a second child. Compare first grandchild and second child
320 return self.bestItemAtIndices(first_grandchild_index, second_child_index, target_order);
321 }
322
323 const best_left_grandchild_index = self.bestItemAtIndices(first_grandchild_index, second_grandchild_index, target_order).index;
324 const third_grandchild_index = second_grandchild_index + 1;
325 if (third_grandchild_index >= self.len) {
326 // Two grandchildren, and we know the best. Compare this to second child.
327 return self.bestItemAtIndices(best_left_grandchild_index, second_child_index, target_order);
328 } else {
329 // Three grandchildren, compare the min of the first two with the third
330 return self.bestItemAtIndices(best_left_grandchild_index, third_grandchild_index, target_order);
331 }
332 }
333
334 /// Return the number of elements remaining in the dequeue
335 pub fn count(self: Self) usize {
336 return self.len;
337 }
338
339 /// Return the number of elements that can be added to the
340 /// dequeue before more memory is allocated.
341 pub fn capacity(self: Self) usize {
342 return self.items.len;
343 }
344
345 /// Dequeue takes ownership of the passed in slice. The slice must have been
346 /// allocated with `allocator`.
347 /// De-initialize with `deinit`.
348 pub fn fromOwnedSlice(allocator: *Allocator, compareFn: fn (T, T) Order, items: []T) Self {
349 var queue = Self{
350 .items = items,
351 .len = items.len,
352 .allocator = allocator,
353 .compareFn = compareFn,
354 };
355
356 if (queue.len <= 1) return queue;
357
358 const half = (queue.len >> 1) - 1;
359 var i: usize = 0;
360 while (i <= half) : (i += 1) {
361 const index = half - i;
362 queue.siftDown(index);
363 }
364 return queue;
365 }
366
367 pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
368 var better_capacity = self.capacity();
369 if (better_capacity >= new_capacity) return;
370 while (true) {
371 better_capacity += better_capacity / 2 + 8;
372 if (better_capacity >= new_capacity) break;
373 }
374 self.items = try self.allocator.realloc(self.items, better_capacity);
375 }
376
377 /// Reduce allocated capacity to `new_len`.
378 pub fn shrinkAndFree(self: *Self, new_len: usize) void {
379 assert(new_len <= self.items.len);
380
381 // Cannot shrink to smaller than the current queue size without invalidating the heap property
382 assert(new_len >= self.len);
383
384 self.items = self.allocator.realloc(self.items[0..], new_len) catch |e| switch (e) {
385 error.OutOfMemory => { // no problem, capacity is still correct then.
386 self.items.len = new_len;
387 return;
388 },
389 };
390 self.len = new_len;
391 }
392
393 /// Reduce length to `new_len`.
394 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
395 assert(new_len <= self.items.len);
396
397 // Cannot shrink to smaller than the current queue size without invalidating the heap property
398 assert(new_len >= self.len);
399
400 self.len = new_len;
401 }
402
403 pub fn update(self: *Self, elem: T, new_elem: T) !void {
404 var old_index: usize = std.mem.indexOfScalar(T, self.items[0..self.len], elem) orelse return error.ElementNotFound;
405 _ = self.removeIndex(old_index);
406 self.addUnchecked(new_elem);
407 }
408
409 pub const Iterator = struct {
410 queue: *PriorityDequeue(T),
411 count: usize,
412
413 pub fn next(it: *Iterator) ?T {
414 if (it.count >= it.queue.len) return null;
415 const out = it.count;
416 it.count += 1;
417 return it.queue.items[out];
418 }
419
420 pub fn reset(it: *Iterator) void {
421 it.count = 0;
422 }
423 };
424
425 /// Return an iterator that walks the queue without consuming
426 /// it. Invalidated if the queue is modified.
427 pub fn iterator(self: *Self) Iterator {
428 return Iterator{
429 .queue = self,
430 .count = 0,
431 };
432 }
433
434 fn dump(self: *Self) void {
435 warn("{{ ", .{});
436 warn("items: ", .{});
437 for (self.items) |e, i| {
438 if (i >= self.len) break;
439 warn("{}, ", .{e});
440 }
441 warn("array: ", .{});
442 for (self.items) |e, i| {
443 warn("{}, ", .{e});
444 }
445 warn("len: {} ", .{self.len});
446 warn("capacity: {}", .{self.capacity()});
447 warn(" }}\n", .{});
448 }
449
450 fn parentIndex(index: usize) usize {
451 return (index - 1) >> 1;
452 }
453
454 fn grandparentIndex(index: usize) usize {
455 return parentIndex(parentIndex(index));
456 }
457
458 fn firstChildIndex(index: usize) usize {
459 return (index << 1) + 1;
460 }
461
462 fn firstGrandchildIndex(index: usize) usize {
463 return firstChildIndex(firstChildIndex(index));
464 }
465 };
466}
467
468fn lessThanComparison(a: u32, b: u32) Order {
469 return std.math.order(a, b);
470}
471
472const PDQ = PriorityDequeue(u32);
473
474test "std.PriorityDequeue: add and remove min" {
475 var queue = PDQ.init(testing.allocator, lessThanComparison);
476 defer queue.deinit();
477
478 try queue.add(54);
479 try queue.add(12);
480 try queue.add(7);
481 try queue.add(23);
482 try queue.add(25);
483 try queue.add(13);
484
485 expectEqual(@as(u32, 7), queue.removeMin());
486 expectEqual(@as(u32, 12), queue.removeMin());
487 expectEqual(@as(u32, 13), queue.removeMin());
488 expectEqual(@as(u32, 23), queue.removeMin());
489 expectEqual(@as(u32, 25), queue.removeMin());
490 expectEqual(@as(u32, 54), queue.removeMin());
491}
492
493test "std.PriorityDequeue: add and remove min structs" {
494 const S = struct {
495 size: u32,
496 };
497 var queue = PriorityDequeue(S).init(testing.allocator, struct {
498 fn order(a: S, b: S) Order {
499 return std.math.order(a.size, b.size);
500 }
501 }.order);
502 defer queue.deinit();
503
504 try queue.add(.{ .size = 54 });
505 try queue.add(.{ .size = 12 });
506 try queue.add(.{ .size = 7 });
507 try queue.add(.{ .size = 23 });
508 try queue.add(.{ .size = 25 });
509 try queue.add(.{ .size = 13 });
510
511 expectEqual(@as(u32, 7), queue.removeMin().size);
512 expectEqual(@as(u32, 12), queue.removeMin().size);
513 expectEqual(@as(u32, 13), queue.removeMin().size);
514 expectEqual(@as(u32, 23), queue.removeMin().size);
515 expectEqual(@as(u32, 25), queue.removeMin().size);
516 expectEqual(@as(u32, 54), queue.removeMin().size);
517}
518
519test "std.PriorityDequeue: add and remove max" {
520 var queue = PDQ.init(testing.allocator, lessThanComparison);
521 defer queue.deinit();
522
523 try queue.add(54);
524 try queue.add(12);
525 try queue.add(7);
526 try queue.add(23);
527 try queue.add(25);
528 try queue.add(13);
529
530 expectEqual(@as(u32, 54), queue.removeMax());
531 expectEqual(@as(u32, 25), queue.removeMax());
532 expectEqual(@as(u32, 23), queue.removeMax());
533 expectEqual(@as(u32, 13), queue.removeMax());
534 expectEqual(@as(u32, 12), queue.removeMax());
535 expectEqual(@as(u32, 7), queue.removeMax());
536}
537
538test "std.PriorityDequeue: add and remove same min" {
539 var queue = PDQ.init(testing.allocator, lessThanComparison);
540 defer queue.deinit();
541
542 try queue.add(1);
543 try queue.add(1);
544 try queue.add(2);
545 try queue.add(2);
546 try queue.add(1);
547 try queue.add(1);
548
549 expectEqual(@as(u32, 1), queue.removeMin());
550 expectEqual(@as(u32, 1), queue.removeMin());
551 expectEqual(@as(u32, 1), queue.removeMin());
552 expectEqual(@as(u32, 1), queue.removeMin());
553 expectEqual(@as(u32, 2), queue.removeMin());
554 expectEqual(@as(u32, 2), queue.removeMin());
555}
556
557test "std.PriorityDequeue: add and remove same max" {
558 var queue = PDQ.init(testing.allocator, lessThanComparison);
559 defer queue.deinit();
560
561 try queue.add(1);
562 try queue.add(1);
563 try queue.add(2);
564 try queue.add(2);
565 try queue.add(1);
566 try queue.add(1);
567
568 expectEqual(@as(u32, 2), queue.removeMax());
569 expectEqual(@as(u32, 2), queue.removeMax());
570 expectEqual(@as(u32, 1), queue.removeMax());
571 expectEqual(@as(u32, 1), queue.removeMax());
572 expectEqual(@as(u32, 1), queue.removeMax());
573 expectEqual(@as(u32, 1), queue.removeMax());
574}
575
576test "std.PriorityDequeue: removeOrNull empty" {
577 var queue = PDQ.init(testing.allocator, lessThanComparison);
578 defer queue.deinit();
579
580 expect(queue.removeMinOrNull() == null);
581 expect(queue.removeMaxOrNull() == null);
582}
583
584test "std.PriorityDequeue: edge case 3 elements" {
585 var queue = PDQ.init(testing.allocator, lessThanComparison);
586 defer queue.deinit();
587
588 try queue.add(9);
589 try queue.add(3);
590 try queue.add(2);
591
592 expectEqual(@as(u32, 2), queue.removeMin());
593 expectEqual(@as(u32, 3), queue.removeMin());
594 expectEqual(@as(u32, 9), queue.removeMin());
595}
596
597test "std.PriorityDequeue: edge case 3 elements max" {
598 var queue = PDQ.init(testing.allocator, lessThanComparison);
599 defer queue.deinit();
600
601 try queue.add(9);
602 try queue.add(3);
603 try queue.add(2);
604
605 expectEqual(@as(u32, 9), queue.removeMax());
606 expectEqual(@as(u32, 3), queue.removeMax());
607 expectEqual(@as(u32, 2), queue.removeMax());
608}
609
610test "std.PriorityDequeue: peekMin" {
611 var queue = PDQ.init(testing.allocator, lessThanComparison);
612 defer queue.deinit();
613
614 expect(queue.peekMin() == null);
615
616 try queue.add(9);
617 try queue.add(3);
618 try queue.add(2);
619
620 expect(queue.peekMin().? == 2);
621 expect(queue.peekMin().? == 2);
622}
623
624test "std.PriorityDequeue: peekMax" {
625 var queue = PDQ.init(testing.allocator, lessThanComparison);
626 defer queue.deinit();
627
628 expect(queue.peekMin() == null);
629
630 try queue.add(9);
631 try queue.add(3);
632 try queue.add(2);
633
634 expect(queue.peekMax().? == 9);
635 expect(queue.peekMax().? == 9);
636}
637
638test "std.PriorityDequeue: sift up with odd indices" {
639 var queue = PDQ.init(testing.allocator, lessThanComparison);
640 defer queue.deinit();
641 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
642 for (items) |e| {
643 try queue.add(e);
644 }
645
646 const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 };
647 for (sorted_items) |e| {
648 expectEqual(e, queue.removeMin());
649 }
650}
651
652test "std.PriorityDequeue: sift up with odd indices" {
653 var queue = PDQ.init(testing.allocator, lessThanComparison);
654 defer queue.deinit();
655 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
656 for (items) |e| {
657 try queue.add(e);
658 }
659
660 const sorted_items = [_]u32{ 25, 24, 24, 22, 21, 16, 15, 15, 14, 13, 12, 11, 7, 7, 6, 5, 2, 1 };
661 for (sorted_items) |e| {
662 expectEqual(e, queue.removeMax());
663 }
664}
665
666test "std.PriorityDequeue: addSlice min" {
667 var queue = PDQ.init(testing.allocator, lessThanComparison);
668 defer queue.deinit();
669 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
670 try queue.addSlice(items[0..]);
671
672 const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 };
673 for (sorted_items) |e| {
674 expectEqual(e, queue.removeMin());
675 }
676}
677
678test "std.PriorityDequeue: addSlice max" {
679 var queue = PDQ.init(testing.allocator, lessThanComparison);
680 defer queue.deinit();
681 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
682 try queue.addSlice(items[0..]);
683
684 const sorted_items = [_]u32{ 25, 24, 24, 22, 21, 16, 15, 15, 14, 13, 12, 11, 7, 7, 6, 5, 2, 1 };
685 for (sorted_items) |e| {
686 expectEqual(e, queue.removeMax());
687 }
688}
689
690test "std.PriorityDequeue: fromOwnedSlice trivial case 0" {
691 const items = [0]u32{};
692 const queue_items = try testing.allocator.dupe(u32, &items);
693 var queue = PDQ.fromOwnedSlice(testing.allocator, lessThanComparison, queue_items[0..]);
694 defer queue.deinit();
695 expectEqual(@as(usize, 0), queue.len);
696 expect(queue.removeMinOrNull() == null);
697}
698
699test "std.PriorityDequeue: fromOwnedSlice trivial case 1" {
700 const items = [1]u32{1};
701 const queue_items = try testing.allocator.dupe(u32, &items);
702 var queue = PDQ.fromOwnedSlice(testing.allocator, lessThanComparison, queue_items[0..]);
703 defer queue.deinit();
704
705 expectEqual(@as(usize, 1), queue.len);
706 expectEqual(items[0], queue.removeMin());
707 expect(queue.removeMinOrNull() == null);
708}
709
710test "std.PriorityDequeue: fromOwnedSlice" {
711 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
712 const queue_items = try testing.allocator.dupe(u32, items[0..]);
713 var queue = PDQ.fromOwnedSlice(testing.allocator, lessThanComparison, queue_items[0..]);
714 defer queue.deinit();
715
716 const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 };
717 for (sorted_items) |e| {
718 expectEqual(e, queue.removeMin());
719 }
720}
721
722test "std.PriorityDequeue: update min queue" {
723 var queue = PDQ.init(testing.allocator, lessThanComparison);
724 defer queue.deinit();
725
726 try queue.add(55);
727 try queue.add(44);
728 try queue.add(11);
729 try queue.update(55, 5);
730 try queue.update(44, 4);
731 try queue.update(11, 1);
732 expectEqual(@as(u32, 1), queue.removeMin());
733 expectEqual(@as(u32, 4), queue.removeMin());
734 expectEqual(@as(u32, 5), queue.removeMin());
735}
736
737test "std.PriorityDequeue: update same min queue" {
738 var queue = PDQ.init(testing.allocator, lessThanComparison);
739 defer queue.deinit();
740
741 try queue.add(1);
742 try queue.add(1);
743 try queue.add(2);
744 try queue.add(2);
745 try queue.update(1, 5);
746 try queue.update(2, 4);
747 expectEqual(@as(u32, 1), queue.removeMin());
748 expectEqual(@as(u32, 2), queue.removeMin());
749 expectEqual(@as(u32, 4), queue.removeMin());
750 expectEqual(@as(u32, 5), queue.removeMin());
751}
752
753test "std.PriorityDequeue: update max queue" {
754 var queue = PDQ.init(testing.allocator, lessThanComparison);
755 defer queue.deinit();
756
757 try queue.add(55);
758 try queue.add(44);
759 try queue.add(11);
760 try queue.update(55, 5);
761 try queue.update(44, 1);
762 try queue.update(11, 4);
763
764 expectEqual(@as(u32, 5), queue.removeMax());
765 expectEqual(@as(u32, 4), queue.removeMax());
766 expectEqual(@as(u32, 1), queue.removeMax());
767}
768
769test "std.PriorityDequeue: update same max queue" {
770 var queue = PDQ.init(testing.allocator, lessThanComparison);
771 defer queue.deinit();
772
773 try queue.add(1);
774 try queue.add(1);
775 try queue.add(2);
776 try queue.add(2);
777 try queue.update(1, 5);
778 try queue.update(2, 4);
779 expectEqual(@as(u32, 5), queue.removeMax());
780 expectEqual(@as(u32, 4), queue.removeMax());
781 expectEqual(@as(u32, 2), queue.removeMax());
782 expectEqual(@as(u32, 1), queue.removeMax());
783}
784
785test "std.PriorityDequeue: iterator" {
786 var queue = PDQ.init(testing.allocator, lessThanComparison);
787 var map = std.AutoHashMap(u32, void).init(testing.allocator);
788 defer {
789 queue.deinit();
790 map.deinit();
791 }
792
793 const items = [_]u32{ 54, 12, 7, 23, 25, 13 };
794 for (items) |e| {
795 _ = try queue.add(e);
796 _ = try map.put(e, {});
797 }
798
799 var it = queue.iterator();
800 while (it.next()) |e| {
801 _ = map.remove(e);
802 }
803
804 expectEqual(@as(usize, 0), map.count());
805}
806
807test "std.PriorityDequeue: remove at index" {
808 var queue = PDQ.init(testing.allocator, lessThanComparison);
809 defer queue.deinit();
810
811 try queue.add(3);
812 try queue.add(2);
813 try queue.add(1);
814
815 var it = queue.iterator();
816 var elem = it.next();
817 var idx: usize = 0;
818 const two_idx = while (elem != null) : (elem = it.next()) {
819 if (elem.? == 2)
820 break idx;
821 idx += 1;
822 } else unreachable;
823
824 expectEqual(queue.removeIndex(two_idx), 2);
825 expectEqual(queue.removeMin(), 1);
826 expectEqual(queue.removeMin(), 3);
827 expectEqual(queue.removeMinOrNull(), null);
828}
829
830test "std.PriorityDequeue: iterator while empty" {
831 var queue = PDQ.init(testing.allocator, lessThanComparison);
832 defer queue.deinit();
833
834 var it = queue.iterator();
835
836 expectEqual(it.next(), null);
837}
838
839test "std.PriorityDequeue: shrinkRetainingCapacity and shrinkAndFree" {
840 var queue = PDQ.init(testing.allocator, lessThanComparison);
841 defer queue.deinit();
842
843 try queue.ensureCapacity(4);
844 expect(queue.capacity() >= 4);
845
846 try queue.add(1);
847 try queue.add(2);
848 try queue.add(3);
849 expect(queue.capacity() >= 4);
850 expectEqual(@as(usize, 3), queue.len);
851
852 queue.shrinkRetainingCapacity(3);
853 expect(queue.capacity() >= 4);
854 expectEqual(@as(usize, 3), queue.len);
855
856 queue.shrinkAndFree(3);
857 expectEqual(@as(usize, 3), queue.capacity());
858 expectEqual(@as(usize, 3), queue.len);
859
860 expectEqual(@as(u32, 3), queue.removeMax());
861 expectEqual(@as(u32, 2), queue.removeMax());
862 expectEqual(@as(u32, 1), queue.removeMax());
863 expect(queue.removeMaxOrNull() == null);
864}
865
866test "std.PriorityDequeue: fuzz testing min" {
867 var prng = std.rand.DefaultPrng.init(0x12345678);
868
869 const test_case_count = 100;
870 const queue_size = 1_000;
871
872 var i: usize = 0;
873 while (i < test_case_count) : (i += 1) {
874 try fuzzTestMin(&prng.random, queue_size);
875 }
876}
877
878fn fuzzTestMin(rng: *std.rand.Random, comptime queue_size: usize) !void {
879 const allocator = testing.allocator;
880 const items = try generateRandomSlice(allocator, rng, queue_size);
881
882 var queue = PDQ.fromOwnedSlice(allocator, lessThanComparison, items);
883 defer queue.deinit();
884
885 var last_removed: ?u32 = null;
886 while (queue.removeMinOrNull()) |next| {
887 if (last_removed) |last| {
888 expect(last <= next);
889 }
890 last_removed = next;
891 }
892}
893
894test "std.PriorityDequeue: fuzz testing max" {
895 var prng = std.rand.DefaultPrng.init(0x87654321);
896
897 const test_case_count = 100;
898 const queue_size = 1_000;
899
900 var i: usize = 0;
901 while (i < test_case_count) : (i += 1) {
902 try fuzzTestMax(&prng.random, queue_size);
903 }
904}
905
906fn fuzzTestMax(rng: *std.rand.Random, queue_size: usize) !void {
907 const allocator = testing.allocator;
908 const items = try generateRandomSlice(allocator, rng, queue_size);
909
910 var queue = PDQ.fromOwnedSlice(testing.allocator, lessThanComparison, items);
911 defer queue.deinit();
912
913 var last_removed: ?u32 = null;
914 while (queue.removeMaxOrNull()) |next| {
915 if (last_removed) |last| {
916 expect(last >= next);
917 }
918 last_removed = next;
919 }
920}
921
922test "std.PriorityDequeue: fuzz testing min and max" {
923 var prng = std.rand.DefaultPrng.init(0x87654321);
924
925 const test_case_count = 100;
926 const queue_size = 1_000;
927
928 var i: usize = 0;
929 while (i < test_case_count) : (i += 1) {
930 try fuzzTestMinMax(&prng.random, queue_size);
931 }
932}
933
934fn fuzzTestMinMax(rng: *std.rand.Random, queue_size: usize) !void {
935 const allocator = testing.allocator;
936 const items = try generateRandomSlice(allocator, rng, queue_size);
937
938 var queue = PDQ.fromOwnedSlice(allocator, lessThanComparison, items);
939 defer queue.deinit();
940
941 var last_min: ?u32 = null;
942 var last_max: ?u32 = null;
943 var i: usize = 0;
944 while (i < queue_size) : (i += 1) {
945 if (i % 2 == 0) {
946 const next = queue.removeMin();
947 if (last_min) |last| {
948 expect(last <= next);
949 }
950 last_min = next;
951 } else {
952 const next = queue.removeMax();
953 if (last_max) |last| {
954 expect(last >= next);
955 }
956 last_max = next;
957 }
958 }
959}
960
961fn generateRandomSlice(allocator: *std.mem.Allocator, rng: *std.rand.Random, size: usize) ![]u32 {
962 var array = std.ArrayList(u32).init(allocator);
963 try array.ensureCapacity(size);
964
965 var i: usize = 0;
966 while (i < size) : (i += 1) {
967 const elem = rng.int(u32);
968 try array.append(elem);
969 }
970
971 return array.toOwnedSlice();
972}
lib/std/priority_queue.zig+95-26
......@@ -6,6 +6,8 @@
66const std = @import("std.zig");
77const Allocator = std.mem.Allocator;
88const assert = std.debug.assert;
9const warn = std.debug.warn;
10const Order = std.math.Order;
911const testing = std.testing;
1012const expect = testing.expect;
1113const expectEqual = testing.expectEqual;
......@@ -19,15 +21,17 @@ pub fn PriorityQueue(comptime T: type) type {
1921 items: []T,
2022 len: usize,
2123 allocator: *Allocator,
22 compareFn: fn (a: T, b: T) bool,
23
24 /// Initialize and return a priority queue. Provide
25 /// `compareFn` that returns `true` when its first argument
26 /// should get popped before its second argument. For example,
27 /// to make `pop` return the minimum value, provide
24 compareFn: fn (a: T, b: T) Order,
25
26 /// Initialize and return a priority queue. Provide `compareFn`
27 /// that returns `Order.lt` when its first argument should
28 /// get popped before its second argument, `Order.eq` if the
29 /// arguments are of equal priority, or `Order.gt` if the second
30 /// argument should be popped first. For example, to make `pop`
31 /// return the smallest number, provide
2832 ///
29 /// `fn lessThan(a: T, b: T) bool { return a < b; }`
30 pub fn init(allocator: *Allocator, compareFn: fn (a: T, b: T) bool) Self {
33 /// `fn lessThan(a: T, b: T) Order { return std.math.order(a, b); }`
34 pub fn init(allocator: *Allocator, compareFn: fn (a: T, b: T) Order) Self {
3135 return Self{
3236 .items = &[_]T{},
3337 .len = 0,
......@@ -60,7 +64,7 @@ pub fn PriorityQueue(comptime T: type) type {
6064 const child = self.items[child_index];
6165 const parent = self.items[parent_index];
6266
63 if (!self.compareFn(child, parent)) break;
67 if (self.compareFn(child, parent) != .lt) break;
6468
6569 self.items[parent_index] = child;
6670 self.items[child_index] = parent;
......@@ -132,14 +136,14 @@ pub fn PriorityQueue(comptime T: type) type {
132136 var smallest = self.items[index];
133137
134138 if (left) |e| {
135 if (self.compareFn(e, smallest)) {
139 if (self.compareFn(e, smallest) == .lt) {
136140 smallest_index = left_index;
137141 smallest = e;
138142 }
139143 }
140144
141145 if (right) |e| {
142 if (self.compareFn(e, smallest)) {
146 if (self.compareFn(e, smallest) == .lt) {
143147 smallest_index = right_index;
144148 smallest = e;
145149 }
......@@ -158,13 +162,16 @@ pub fn PriorityQueue(comptime T: type) type {
158162 /// PriorityQueue takes ownership of the passed in slice. The slice must have been
159163 /// allocated with `allocator`.
160164 /// Deinitialize with `deinit`.
161 pub fn fromOwnedSlice(allocator: *Allocator, compareFn: fn (a: T, b: T) bool, items: []T) Self {
165 pub fn fromOwnedSlice(allocator: *Allocator, compareFn: fn (a: T, b: T) Order, items: []T) Self {
162166 var queue = Self{
163167 .items = items,
164168 .len = items.len,
165169 .allocator = allocator,
166170 .compareFn = compareFn,
167171 };
172
173 if (queue.len <= 1) return queue;
174
168175 const half = (queue.len >> 1) - 1;
169176 var i: usize = 0;
170177 while (i <= half) : (i += 1) {
......@@ -183,25 +190,40 @@ pub fn PriorityQueue(comptime T: type) type {
183190 self.items = try self.allocator.realloc(self.items, better_capacity);
184191 }
185192
186 pub fn resize(self: *Self, new_len: usize) !void {
187 try self.ensureCapacity(new_len);
193 /// Reduce allocated capacity to `new_len`.
194 pub fn shrinkAndFree(self: *Self, new_len: usize) void {
195 assert(new_len <= self.items.len);
196
197 // Cannot shrink to smaller than the current queue size without invalidating the heap property
198 assert(new_len >= self.len);
199
200 self.items = self.allocator.realloc(self.items[0..], new_len) catch |e| switch (e) {
201 error.OutOfMemory => { // no problem, capacity is still correct then.
202 self.items.len = new_len;
203 return;
204 },
205 };
188206 self.len = new_len;
189207 }
190208
191 pub fn shrink(self: *Self, new_len: usize) void {
192 // TODO take advantage of the new realloc semantics
193 assert(new_len <= self.len);
209 /// Reduce length to `new_len`.
210 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
211 assert(new_len <= self.items.len);
212
213 // Cannot shrink to smaller than the current queue size without invalidating the heap property
214 assert(new_len >= self.len);
215
194216 self.len = new_len;
195217 }
196218
197219 pub fn update(self: *Self, elem: T, new_elem: T) !void {
198 var update_index: usize = std.mem.indexOfScalar(T, self.items, elem) orelse return error.ElementNotFound;
220 var update_index: usize = std.mem.indexOfScalar(T, self.items[0..self.len], elem) orelse return error.ElementNotFound;
199221 const old_elem: T = self.items[update_index];
200222 self.items[update_index] = new_elem;
201 if (self.compareFn(new_elem, old_elem)) {
202 siftUp(self, update_index);
203 } else {
204 siftDown(self, update_index);
223 switch (self.compareFn(new_elem, old_elem)) {
224 .lt => siftUp(self, update_index),
225 .gt => siftDown(self, update_index),
226 .eq => {}, // Nothing to do as the items have equal priority
205227 }
206228 }
207229
......@@ -248,12 +270,12 @@ pub fn PriorityQueue(comptime T: type) type {
248270 };
249271}
250272
251fn lessThan(a: u32, b: u32) bool {
252 return a < b;
273fn lessThan(a: u32, b: u32) Order {
274 return std.math.order(a, b);
253275}
254276
255fn greaterThan(a: u32, b: u32) bool {
256 return a > b;
277fn greaterThan(a: u32, b: u32) Order {
278 return lessThan(a, b).invert();
257279}
258280
259281const PQ = PriorityQueue(u32);
......@@ -351,6 +373,26 @@ test "std.PriorityQueue: addSlice" {
351373 }
352374}
353375
376test "std.PriorityQueue: fromOwnedSlice trivial case 0" {
377 const items = [0]u32{};
378 const queue_items = try testing.allocator.dupe(u32, &items);
379 var queue = PQ.fromOwnedSlice(testing.allocator, lessThan, queue_items[0..]);
380 defer queue.deinit();
381 expectEqual(@as(usize, 0), queue.len);
382 expect(queue.removeOrNull() == null);
383}
384
385test "std.PriorityQueue: fromOwnedSlice trivial case 1" {
386 const items = [1]u32{1};
387 const queue_items = try testing.allocator.dupe(u32, &items);
388 var queue = PQ.fromOwnedSlice(testing.allocator, lessThan, queue_items[0..]);
389 defer queue.deinit();
390
391 expectEqual(@as(usize, 1), queue.len);
392 expectEqual(items[0], queue.remove());
393 expect(queue.removeOrNull() == null);
394}
395
354396test "std.PriorityQueue: fromOwnedSlice" {
355397 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
356398 const heap_items = try testing.allocator.dupe(u32, items[0..]);
......@@ -453,6 +495,33 @@ test "std.PriorityQueue: iterator while empty" {
453495 expectEqual(it.next(), null);
454496}
455497
498test "std.PriorityQueue: shrinkRetainingCapacity and shrinkAndFree" {
499 var queue = PQ.init(testing.allocator, lessThan);
500 defer queue.deinit();
501
502 try queue.ensureCapacity(4);
503 expect(queue.capacity() >= 4);
504
505 try queue.add(1);
506 try queue.add(2);
507 try queue.add(3);
508 expect(queue.capacity() >= 4);
509 expectEqual(@as(usize, 3), queue.len);
510
511 queue.shrinkRetainingCapacity(3);
512 expect(queue.capacity() >= 4);
513 expectEqual(@as(usize, 3), queue.len);
514
515 queue.shrinkAndFree(3);
516 expectEqual(@as(usize, 3), queue.capacity());
517 expectEqual(@as(usize, 3), queue.len);
518
519 expectEqual(@as(u32, 1), queue.remove());
520 expectEqual(@as(u32, 2), queue.remove());
521 expectEqual(@as(u32, 3), queue.remove());
522 expect(queue.removeOrNull() == null);
523}
524
456525test "std.PriorityQueue: update min heap" {
457526 var queue = PQ.init(testing.allocator, lessThan);
458527 defer queue.deinit();
lib/std/rand/Isaac64.zig+32
......@@ -208,3 +208,35 @@ test "isaac64 sequence" {
208208 std.testing.expect(s == r.next());
209209 }
210210}
211
212test "isaac64 fill" {
213 var r = Isaac64.init(0);
214
215 // from reference implementation
216 const seq = [_]u64{
217 0xf67dfba498e4937c,
218 0x84a5066a9204f380,
219 0xfee34bd5f5514dbb,
220 0x4d1664739b8f80d6,
221 0x8607459ab52a14aa,
222 0x0e78bc5a98529e49,
223 0xfe5332822ad13777,
224 0x556c27525e33d01a,
225 0x08643ca615f3149f,
226 0xd0771faf3cb04714,
227 0x30e86f68a37b008d,
228 0x3074ebc0488a3adf,
229 0x270645ea7a2790bc,
230 0x5601a0a8d3763c6a,
231 0x2f83071f53f325dd,
232 0xb9090f3d42d2d2ea,
233 };
234
235 for (seq) |s| {
236 var buf0: [8]u8 = undefined;
237 var buf1: [7]u8 = undefined;
238 std.mem.writeIntLittle(u64, &buf0, s);
239 Isaac64.fill(&r.random, &buf1);
240 std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
241 }
242}
lib/std/rand/Pcg.zig+25-1
......@@ -75,7 +75,7 @@ fn fill(r: *Random, buf: []u8) void {
7575 var n = self.next();
7676 while (i < buf.len) : (i += 1) {
7777 buf[i] = @truncate(u8, n);
78 n >>= 4;
78 n >>= 8;
7979 }
8080 }
8181}
......@@ -99,3 +99,27 @@ test "pcg sequence" {
9999 std.testing.expect(s == r.next());
100100 }
101101}
102
103test "pcg fill" {
104 var r = Pcg.init(0);
105 const s0: u64 = 0x9394bf54ce5d79de;
106 const s1: u64 = 0x84e9c579ef59bbf7;
107 r.seedTwo(s0, s1);
108
109 const seq = [_]u32{
110 2881561918,
111 3063928540,
112 1199791034,
113 2487695858,
114 1479648952,
115 3247963454,
116 };
117
118 for (seq) |s| {
119 var buf0: [4]u8 = undefined;
120 var buf1: [3]u8 = undefined;
121 std.mem.writeIntLittle(u32, &buf0, s);
122 Pcg.fill(&r.random, &buf1);
123 std.testing.expect(std.mem.eql(u8, buf0[0..3], buf1[0..]));
124 }
125}
lib/std/rand/Sfc64.zig+32
......@@ -106,3 +106,35 @@ test "Sfc64 sequence" {
106106 std.testing.expectEqual(s, r.next());
107107 }
108108}
109
110test "Sfc64 fill" {
111 // Unfortunately there does not seem to be an official test sequence.
112 var r = Sfc64.init(0);
113
114 const seq = [_]u64{
115 0x3acfa029e3cc6041,
116 0xf5b6515bf2ee419c,
117 0x1259635894a29b61,
118 0xb6ae75395f8ebd6,
119 0x225622285ce302e2,
120 0x520d28611395cb21,
121 0xdb909c818901599d,
122 0x8ffd195365216f57,
123 0xe8c4ad5e258ac04a,
124 0x8f8ef2c89fdb63ca,
125 0xf9865b01d98d8e2f,
126 0x46555871a65d08ba,
127 0x66868677c6298fcd,
128 0x2ce15a7e6329f57d,
129 0xb2f1833ca91ca79,
130 0x4b0890ac9bf453ca,
131 };
132
133 for (seq) |s| {
134 var buf0: [8]u8 = undefined;
135 var buf1: [7]u8 = undefined;
136 std.mem.writeIntLittle(u64, &buf0, s);
137 Sfc64.fill(&r.random, &buf1);
138 std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
139 }
140}
lib/std/rand/Xoroshiro128.zig+23
......@@ -131,3 +131,26 @@ test "xoroshiro sequence" {
131131 std.testing.expect(s == r.next());
132132 }
133133}
134
135test "xoroshiro fill" {
136 var r = Xoroshiro128.init(0);
137 r.s[0] = 0xaeecf86f7878dd75;
138 r.s[1] = 0x01cd153642e72622;
139
140 const seq = [_]u64{
141 0xb0ba0da5bb600397,
142 0x18a08afde614dccc,
143 0xa2635b956a31b929,
144 0xabe633c971efa045,
145 0x9ac19f9706ca3cac,
146 0xf62b426578c1e3fb,
147 };
148
149 for (seq) |s| {
150 var buf0: [8]u8 = undefined;
151 var buf1: [7]u8 = undefined;
152 std.mem.writeIntLittle(u64, &buf0, s);
153 Xoroshiro128.fill(&r.random, &buf1);
154 std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
155 }
156}
lib/std/special/docs/index.html+1
......@@ -515,6 +515,7 @@
515515 </style>
516516 </head>
517517 <body class="canvas">
518 <div style="background-color: darkred; width: 100vw; text-align: center; color: white; padding: 15px 5px;">These docs are experimental. <a style="color: bisque;text-decoration: underline;" href="https://kristoff.it/blog/zig-new-relationship-llvm/">Progress depends on the self-hosted compiler</a>, <a style="color: bisque;text-decoration: underline;" href="https://github.com/ziglang/zig/wiki/How-to-read-the-standard-library-source-code">consider reading the stlib source in the meantime</a>.</div>
518519 <div class="flex-main">
519520 <div class="flex-filler"></div>
520521 <div class="flex-left sidebar">
lib/std/std.zig+1
......@@ -31,6 +31,7 @@ pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayE
3131pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
3232pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
3333pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
34pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue;
3435pub const Progress = @import("Progress.zig");
3536pub const SemanticVersion = @import("SemanticVersion.zig");
3637pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
lib/std/zig.zig+1-1
......@@ -11,7 +11,7 @@ pub const Tokenizer = tokenizer.Tokenizer;
1111pub const fmtId = @import("zig/fmt.zig").fmtId;
1212pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes;
1313pub const parse = @import("zig/parse.zig").parse;
14pub const parseStringLiteral = @import("zig/string_literal.zig").parse;
14pub const string_literal = @import("zig/string_literal.zig");
1515pub const ast = @import("zig/ast.zig");
1616pub const system = @import("zig/system.zig");
1717pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
lib/std/zig/ast.zig+10-4
......@@ -1252,6 +1252,7 @@ pub const Tree = struct {
12521252 buffer[0] = data.lhs;
12531253 const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1];
12541254 return tree.fullFnProto(.{
1255 .proto_node = node,
12551256 .fn_token = tree.nodes.items(.main_token)[node],
12561257 .return_type = data.rhs,
12571258 .params = params,
......@@ -1267,6 +1268,7 @@ pub const Tree = struct {
12671268 const params_range = tree.extraData(data.lhs, Node.SubRange);
12681269 const params = tree.extra_data[params_range.start..params_range.end];
12691270 return tree.fullFnProto(.{
1271 .proto_node = node,
12701272 .fn_token = tree.nodes.items(.main_token)[node],
12711273 .return_type = data.rhs,
12721274 .params = params,
......@@ -1283,6 +1285,7 @@ pub const Tree = struct {
12831285 buffer[0] = extra.param;
12841286 const params = if (extra.param == 0) buffer[0..0] else buffer[0..1];
12851287 return tree.fullFnProto(.{
1288 .proto_node = node,
12861289 .fn_token = tree.nodes.items(.main_token)[node],
12871290 .return_type = data.rhs,
12881291 .params = params,
......@@ -1298,6 +1301,7 @@ pub const Tree = struct {
12981301 const extra = tree.extraData(data.lhs, Node.FnProto);
12991302 const params = tree.extra_data[extra.params_start..extra.params_end];
13001303 return tree.fullFnProto(.{
1304 .proto_node = node,
13011305 .fn_token = tree.nodes.items(.main_token)[node],
13021306 .return_type = data.rhs,
13031307 .params = params,
......@@ -1430,7 +1434,7 @@ pub const Tree = struct {
14301434 .ast = .{
14311435 .lbracket = tree.nodes.items(.main_token)[node],
14321436 .elem_count = data.lhs,
1433 .sentinel = null,
1437 .sentinel = 0,
14341438 .elem_type = data.rhs,
14351439 },
14361440 };
......@@ -1440,6 +1444,7 @@ pub const Tree = struct {
14401444 assert(tree.nodes.items(.tag)[node] == .array_type_sentinel);
14411445 const data = tree.nodes.items(.data)[node];
14421446 const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel);
1447 assert(extra.sentinel != 0);
14431448 return .{
14441449 .ast = .{
14451450 .lbracket = tree.nodes.items(.main_token)[node],
......@@ -2119,6 +2124,7 @@ pub const full = struct {
21192124 ast: Ast,
21202125
21212126 pub const Ast = struct {
2127 proto_node: Node.Index,
21222128 fn_token: TokenIndex,
21232129 return_type: Node.Index,
21242130 params: []const Node.Index,
......@@ -2262,7 +2268,7 @@ pub const full = struct {
22622268 pub const Ast = struct {
22632269 lbracket: TokenIndex,
22642270 elem_count: Node.Index,
2265 sentinel: ?Node.Index,
2271 sentinel: Node.Index,
22662272 elem_type: Node.Index,
22672273 };
22682274 };
......@@ -2549,9 +2555,9 @@ pub const Node = struct {
25492555 @"await",
25502556 /// `?lhs`. rhs unused. main_token is the `?`.
25512557 optional_type,
2552 /// `[lhs]rhs`. lhs can be omitted to make it a slice.
2558 /// `[lhs]rhs`.
25532559 array_type,
2554 /// `[lhs:a]b`. `array_type_sentinel[rhs]`.
2560 /// `[lhs:a]b`. `ArrayTypeSentinel[rhs]`.
25552561 array_type_sentinel,
25562562 /// `[*]align(lhs) rhs`. lhs can be omitted.
25572563 /// `*align(lhs) rhs`. lhs can be omitted.
lib/std/zig/parse.zig+21-9
......@@ -59,10 +59,7 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {
5959 parser.nodes.appendAssumeCapacity(.{
6060 .tag = .root,
6161 .main_token = 0,
62 .data = .{
63 .lhs = undefined,
64 .rhs = undefined,
65 },
62 .data = undefined,
6663 });
6764 const root_members = try parser.parseContainerMembers();
6865 const root_decls = try root_members.toSpan(&parser);
......@@ -139,6 +136,16 @@ const Parser = struct {
139136 return result;
140137 }
141138
139 fn setNode(p: *Parser, i: usize, elem: ast.NodeList.Elem) Node.Index {
140 p.nodes.set(i, elem);
141 return @intCast(Node.Index, i);
142 }
143
144 fn reserveNode(p: *Parser) !usize {
145 try p.nodes.resize(p.gpa, p.nodes.len + 1);
146 return p.nodes.len - 1;
147 }
148
142149 fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {
143150 const fields = std.meta.fields(@TypeOf(extra));
144151 try p.extra_data.ensureCapacity(p.gpa, p.extra_data.items.len + fields.len);
......@@ -554,9 +561,10 @@ const Parser = struct {
554561 return fn_proto;
555562 },
556563 .l_brace => {
564 const fn_decl_index = try p.reserveNode();
557565 const body_block = try p.parseBlock();
558566 assert(body_block != 0);
559 return p.addNode(.{
567 return p.setNode(fn_decl_index, .{
560568 .tag = .fn_decl,
561569 .main_token = p.nodes.items(.main_token)[fn_proto],
562570 .data = .{
......@@ -634,6 +642,10 @@ const Parser = struct {
634642 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
635643 fn parseFnProto(p: *Parser) !Node.Index {
636644 const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
645
646 // We want the fn proto node to be before its children in the array.
647 const fn_proto_index = try p.reserveNode();
648
637649 _ = p.eatToken(.identifier);
638650 const params = try p.parseParamDeclList();
639651 defer params.deinit(p.gpa);
......@@ -651,7 +663,7 @@ const Parser = struct {
651663
652664 if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
653665 switch (params) {
654 .zero_or_one => |param| return p.addNode(.{
666 .zero_or_one => |param| return p.setNode(fn_proto_index, .{
655667 .tag = .fn_proto_simple,
656668 .main_token = fn_token,
657669 .data = .{
......@@ -661,7 +673,7 @@ const Parser = struct {
661673 }),
662674 .multi => |list| {
663675 const span = try p.listToSpan(list);
664 return p.addNode(.{
676 return p.setNode(fn_proto_index, .{
665677 .tag = .fn_proto_multi,
666678 .main_token = fn_token,
667679 .data = .{
......@@ -676,7 +688,7 @@ const Parser = struct {
676688 }
677689 }
678690 switch (params) {
679 .zero_or_one => |param| return p.addNode(.{
691 .zero_or_one => |param| return p.setNode(fn_proto_index, .{
680692 .tag = .fn_proto_one,
681693 .main_token = fn_token,
682694 .data = .{
......@@ -691,7 +703,7 @@ const Parser = struct {
691703 }),
692704 .multi => |list| {
693705 const span = try p.listToSpan(list);
694 return p.addNode(.{
706 return p.setNode(fn_proto_index, .{
695707 .tag = .fn_proto,
696708 .main_token = fn_token,
697709 .data = .{
lib/std/zig/render.zig+3-3
......@@ -717,9 +717,9 @@ fn renderArrayType(
717717 ais.pushIndentNextLine();
718718 try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket
719719 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);
720 if (array_type.ast.sentinel != 0) {
721 try renderToken(ais, tree, tree.firstToken(array_type.ast.sentinel) - 1, inner_space); // colon
722 try renderExpression(gpa, ais, tree, array_type.ast.sentinel, inner_space);
723723 }
724724 ais.popIndent();
725725 try renderToken(ais, tree, rbracket, .none); // rbracket
lib/std/zig/string_literal.zig+82-52
......@@ -6,112 +6,143 @@
66const std = @import("../std.zig");
77const assert = std.debug.assert;
88
9const State = enum {
10 Start,
11 Backslash,
12};
13
149pub const ParseError = error{
1510 OutOfMemory,
11 InvalidStringLiteral,
12};
1613
17 /// When this is returned, index will be the position of the character.
18 InvalidCharacter,
14pub const Result = union(enum) {
15 success,
16 /// Found an invalid character at this index.
17 invalid_character: usize,
18 /// Expected hex digits at this index.
19 expected_hex_digits: usize,
20 /// Invalid hex digits at this index.
21 invalid_hex_escape: usize,
22 /// Invalid unicode escape at this index.
23 invalid_unicode_escape: usize,
24 /// The left brace at this index is missing a matching right brace.
25 missing_matching_rbrace: usize,
26 /// Expected unicode digits at this index.
27 expected_unicode_digits: usize,
1928};
2029
21/// caller owns returned memory
22pub fn parse(
23 allocator: *std.mem.Allocator,
24 bytes: []const u8,
25 bad_index: *usize, // populated if error.InvalidCharacter is returned
26) ParseError![]u8 {
30/// Parses `bytes` as a Zig string literal and appends the result to `buf`.
31/// Asserts `bytes` has '"' at beginning and end.
32pub fn parseAppend(buf: *std.ArrayList(u8), bytes: []const u8) error{OutOfMemory}!Result {
2733 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');
34 const slice = bytes[1..];
2835
29 var list = std.ArrayList(u8).init(allocator);
30 errdefer list.deinit();
36 const prev_len = buf.items.len;
37 try buf.ensureCapacity(prev_len + slice.len - 1);
38 errdefer buf.shrinkRetainingCapacity(prev_len);
3139
32 const slice = bytes[1..];
33 try list.ensureCapacity(slice.len - 1);
40 const State = enum {
41 Start,
42 Backslash,
43 };
3444
3545 var state = State.Start;
3646 var index: usize = 0;
37 while (index < slice.len) : (index += 1) {
47 while (true) : (index += 1) {
3848 const b = slice[index];
3949
4050 switch (state) {
4151 State.Start => switch (b) {
4252 '\\' => state = State.Backslash,
4353 '\n' => {
44 bad_index.* = index;
45 return error.InvalidCharacter;
54 return Result{ .invalid_character = index };
4655 },
47 '"' => return list.toOwnedSlice(),
48 else => try list.append(b),
56 '"' => return Result.success,
57 else => try buf.append(b),
4958 },
5059 State.Backslash => switch (b) {
5160 'n' => {
52 try list.append('\n');
61 try buf.append('\n');
5362 state = State.Start;
5463 },
5564 'r' => {
56 try list.append('\r');
65 try buf.append('\r');
5766 state = State.Start;
5867 },
5968 '\\' => {
60 try list.append('\\');
69 try buf.append('\\');
6170 state = State.Start;
6271 },
6372 't' => {
64 try list.append('\t');
73 try buf.append('\t');
6574 state = State.Start;
6675 },
6776 '\'' => {
68 try list.append('\'');
77 try buf.append('\'');
6978 state = State.Start;
7079 },
7180 '"' => {
72 try list.append('"');
81 try buf.append('"');
7382 state = State.Start;
7483 },
7584 'x' => {
7685 // TODO: add more/better/broader tests for this.
7786 const index_continue = index + 3;
78 if (slice.len >= index_continue)
79 if (std.fmt.parseUnsigned(u8, slice[index + 1 .. index_continue], 16)) |char| {
80 try list.append(char);
81 state = State.Start;
82 index = index_continue - 1; // loop-header increments again
83 continue;
84 } else |_| {};
85
86 bad_index.* = index;
87 return error.InvalidCharacter;
87 if (slice.len < index_continue) {
88 return Result{ .expected_hex_digits = index };
89 }
90 if (std.fmt.parseUnsigned(u8, slice[index + 1 .. index_continue], 16)) |byte| {
91 try buf.append(byte);
92 state = State.Start;
93 index = index_continue - 1; // loop-header increments again
94 } else |err| switch (err) {
95 error.Overflow => unreachable, // 2 digits base 16 fits in a u8.
96 error.InvalidCharacter => {
97 return Result{ .invalid_hex_escape = index + 1 };
98 },
99 }
88100 },
89101 'u' => {
90102 // TODO: add more/better/broader tests for this.
91 if (slice.len > index + 2 and slice[index + 1] == '{')
103 // TODO: we are already inside a nice, clean state machine... use it
104 // instead of this hacky code.
105 if (slice.len > index + 2 and slice[index + 1] == '{') {
92106 if (std.mem.indexOfScalarPos(u8, slice[0..std.math.min(index + 9, slice.len)], index + 3, '}')) |index_end| {
93107 const hex_str = slice[index + 2 .. index_end];
94108 if (std.fmt.parseUnsigned(u32, hex_str, 16)) |uint| {
95109 if (uint <= 0x10ffff) {
96 try list.appendSlice(std.mem.toBytes(uint)[0..]);
110 try buf.appendSlice(std.mem.toBytes(uint)[0..]);
97111 state = State.Start;
98112 index = index_end; // loop-header increments
99113 continue;
100114 }
101 } else |_| {}
102 };
103
104 bad_index.* = index;
105 return error.InvalidCharacter;
115 } else |err| switch (err) {
116 error.Overflow => unreachable,
117 error.InvalidCharacter => {
118 return Result{ .invalid_unicode_escape = index + 1 };
119 },
120 }
121 } else {
122 return Result{ .missing_matching_rbrace = index + 1 };
123 }
124 } else {
125 return Result{ .expected_unicode_digits = index };
126 }
106127 },
107128 else => {
108 bad_index.* = index;
109 return error.InvalidCharacter;
129 return Result{ .invalid_character = index };
110130 },
111131 },
112132 }
133 } else unreachable; // TODO should not need else unreachable on while(true)
134}
135
136/// Higher level API. Does not return extra info about parse errors.
137/// Caller owns returned memory.
138pub fn parseAlloc(allocator: *std.mem.Allocator, bytes: []const u8) ParseError![]u8 {
139 var buf = std.ArrayList(u8).init(allocator);
140 defer buf.deinit();
141
142 switch (try parseAppend(&buf, bytes)) {
143 .success => return buf.toOwnedSlice(),
144 else => return error.InvalidStringLiteral,
113145 }
114 unreachable;
115146}
116147
117148test "parse" {
......@@ -121,9 +152,8 @@ test "parse" {
121152 var fixed_buf_mem: [32]u8 = undefined;
122153 var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buf_mem[0..]);
123154 var alloc = &fixed_buf_alloc.allocator;
124 var bad_index: usize = undefined;
125155
126 expect(eql(u8, "foo", try parse(alloc, "\"foo\"", &bad_index)));
127 expect(eql(u8, "foo", try parse(alloc, "\"f\x6f\x6f\"", &bad_index)));
128 expect(eql(u8, "f💯", try parse(alloc, "\"f\u{1f4af}\"", &bad_index)));
156 expect(eql(u8, "foo", try parseAlloc(alloc, "\"foo\"")));
157 expect(eql(u8, "foo", try parseAlloc(alloc, "\"f\x6f\x6f\"")));
158 expect(eql(u8, "f💯", try parseAlloc(alloc, "\"f\u{1f4af}\"")));
129159}
src/AstGen.zig created+4450
......@@ -0,0 +1,4450 @@
1//! A Work-In-Progress `zir.Code`. This is a shared parent of all
2//! `GenZir` scopes. Once the `zir.Code` is produced, this struct
3//! is deinitialized.
4//! The `GenZir.finish` function converts this to a `zir.Code`.
5
6const AstGen = @This();
7
8const std = @import("std");
9const ast = std.zig.ast;
10const mem = std.mem;
11const Allocator = std.mem.Allocator;
12const assert = std.debug.assert;
13const ArrayListUnmanaged = std.ArrayListUnmanaged;
14
15const Value = @import("value.zig").Value;
16const Type = @import("type.zig").Type;
17const TypedValue = @import("TypedValue.zig");
18const zir = @import("zir.zig");
19const Module = @import("Module.zig");
20const trace = @import("tracy.zig").trace;
21const Scope = Module.Scope;
22const GenZir = Scope.GenZir;
23const InnerError = Module.InnerError;
24const Decl = Module.Decl;
25const LazySrcLoc = Module.LazySrcLoc;
26const BuiltinFn = @import("BuiltinFn.zig");
27
28instructions: std.MultiArrayList(zir.Inst) = .{},
29string_bytes: ArrayListUnmanaged(u8) = .{},
30extra: ArrayListUnmanaged(u32) = .{},
31decl_map: std.StringArrayHashMapUnmanaged(void) = .{},
32decls: ArrayListUnmanaged(*Decl) = .{},
33/// The end of special indexes. `zir.Inst.Ref` subtracts against this number to convert
34/// to `zir.Inst.Index`. The default here is correct if there are 0 parameters.
35ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len,
36mod: *Module,
37decl: *Decl,
38arena: *Allocator,
39
40/// Call `deinit` on the result.
41pub fn init(mod: *Module, decl: *Decl, arena: *Allocator) !AstGen {
42 var astgen: AstGen = .{
43 .mod = mod,
44 .decl = decl,
45 .arena = arena,
46 };
47 // Must be a block instruction at index 0 with the root body.
48 try astgen.instructions.append(mod.gpa, .{
49 .tag = .block,
50 .data = .{ .pl_node = .{
51 .src_node = 0,
52 .payload_index = undefined,
53 } },
54 });
55 return astgen;
56}
57
58pub fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 {
59 const fields = std.meta.fields(@TypeOf(extra));
60 try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len + fields.len);
61 return addExtraAssumeCapacity(astgen, extra);
62}
63
64pub fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
65 const fields = std.meta.fields(@TypeOf(extra));
66 const result = @intCast(u32, astgen.extra.items.len);
67 inline for (fields) |field| {
68 astgen.extra.appendAssumeCapacity(switch (field.field_type) {
69 u32 => @field(extra, field.name),
70 zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
71 else => @compileError("bad field type"),
72 });
73 }
74 return result;
75}
76
77pub fn appendRefs(astgen: *AstGen, refs: []const zir.Inst.Ref) !void {
78 const coerced = @bitCast([]const u32, refs);
79 return astgen.extra.appendSlice(astgen.mod.gpa, coerced);
80}
81
82pub fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const zir.Inst.Ref) void {
83 const coerced = @bitCast([]const u32, refs);
84 astgen.extra.appendSliceAssumeCapacity(coerced);
85}
86
87pub fn refIsNoReturn(astgen: AstGen, inst_ref: zir.Inst.Ref) bool {
88 if (inst_ref == .unreachable_value) return true;
89 if (astgen.refToIndex(inst_ref)) |inst_index| {
90 return astgen.instructions.items(.tag)[inst_index].isNoReturn();
91 }
92 return false;
93}
94
95pub fn indexToRef(astgen: AstGen, inst: zir.Inst.Index) zir.Inst.Ref {
96 return @intToEnum(zir.Inst.Ref, astgen.ref_start_index + inst);
97}
98
99pub fn refToIndex(astgen: AstGen, inst: zir.Inst.Ref) ?zir.Inst.Index {
100 const ref_int = @enumToInt(inst);
101 if (ref_int >= astgen.ref_start_index) {
102 return ref_int - astgen.ref_start_index;
103 } else {
104 return null;
105 }
106}
107
108pub fn deinit(astgen: *AstGen) void {
109 const gpa = astgen.mod.gpa;
110 astgen.instructions.deinit(gpa);
111 astgen.extra.deinit(gpa);
112 astgen.string_bytes.deinit(gpa);
113 astgen.decl_map.deinit(gpa);
114 astgen.decls.deinit(gpa);
115}
116
117pub const ResultLoc = union(enum) {
118 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
119 /// expression should be generated. The result instruction from the expression must
120 /// be ignored.
121 discard,
122 /// The expression has an inferred type, and it will be evaluated as an rvalue.
123 none,
124 /// The expression must generate a pointer rather than a value. For example, the left hand side
125 /// of an assignment uses this kind of result location.
126 ref,
127 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
128 ty: zir.Inst.Ref,
129 /// The expression must store its result into this typed pointer. The result instruction
130 /// from the expression must be ignored.
131 ptr: zir.Inst.Ref,
132 /// The expression must store its result into this allocation, which has an inferred type.
133 /// The result instruction from the expression must be ignored.
134 /// Always an instruction with tag `alloc_inferred`.
135 inferred_ptr: zir.Inst.Ref,
136 /// There is a pointer for the expression to store its result into, however, its type
137 /// is inferred based on peer type resolution for a `zir.Inst.Block`.
138 /// The result instruction from the expression must be ignored.
139 block_ptr: *GenZir,
140
141 pub const Strategy = struct {
142 elide_store_to_block_ptr_instructions: bool,
143 tag: Tag,
144
145 pub const Tag = enum {
146 /// Both branches will use break_void; result location is used to communicate the
147 /// result instruction.
148 break_void,
149 /// Use break statements to pass the block result value, and call rvalue() at
150 /// the end depending on rl. Also elide the store_to_block_ptr instructions
151 /// depending on rl.
152 break_operand,
153 };
154 };
155
156 fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy {
157 var elide_store_to_block_ptr_instructions = false;
158 switch (rl) {
159 // In this branch there will not be any store_to_block_ptr instructions.
160 .discard, .none, .ty, .ref => return .{
161 .tag = .break_operand,
162 .elide_store_to_block_ptr_instructions = false,
163 },
164 // The pointer got passed through to the sub-expressions, so we will use
165 // break_void here.
166 // In this branch there will not be any store_to_block_ptr instructions.
167 .ptr => return .{
168 .tag = .break_void,
169 .elide_store_to_block_ptr_instructions = false,
170 },
171 .inferred_ptr, .block_ptr => {
172 if (block_scope.rvalue_rl_count == block_scope.break_count) {
173 // Neither prong of the if consumed the result location, so we can
174 // use break instructions to create an rvalue.
175 return .{
176 .tag = .break_operand,
177 .elide_store_to_block_ptr_instructions = true,
178 };
179 } else {
180 // Allow the store_to_block_ptr instructions to remain so that
181 // semantic analysis can turn them into bitcasts.
182 return .{
183 .tag = .break_void,
184 .elide_store_to_block_ptr_instructions = false,
185 };
186 }
187 },
188 }
189 }
190};
191
192pub fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref {
193 return expr(gz, scope, .{ .ty = .type_type }, type_node);
194}
195
196fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
197 const tree = gz.tree();
198 const node_tags = tree.nodes.items(.tag);
199 const main_tokens = tree.nodes.items(.main_token);
200 switch (node_tags[node]) {
201 .root => unreachable,
202 .@"usingnamespace" => unreachable,
203 .test_decl => unreachable,
204 .global_var_decl => unreachable,
205 .local_var_decl => unreachable,
206 .simple_var_decl => unreachable,
207 .aligned_var_decl => unreachable,
208 .switch_case => unreachable,
209 .switch_case_one => unreachable,
210 .container_field_init => unreachable,
211 .container_field_align => unreachable,
212 .container_field => unreachable,
213 .asm_output => unreachable,
214 .asm_input => unreachable,
215
216 .assign,
217 .assign_bit_and,
218 .assign_bit_or,
219 .assign_bit_shift_left,
220 .assign_bit_shift_right,
221 .assign_bit_xor,
222 .assign_div,
223 .assign_sub,
224 .assign_sub_wrap,
225 .assign_mod,
226 .assign_add,
227 .assign_add_wrap,
228 .assign_mul,
229 .assign_mul_wrap,
230 .add,
231 .add_wrap,
232 .sub,
233 .sub_wrap,
234 .mul,
235 .mul_wrap,
236 .div,
237 .mod,
238 .bit_and,
239 .bit_or,
240 .bit_shift_left,
241 .bit_shift_right,
242 .bit_xor,
243 .bang_equal,
244 .equal_equal,
245 .greater_than,
246 .greater_or_equal,
247 .less_than,
248 .less_or_equal,
249 .array_cat,
250 .array_mult,
251 .bool_and,
252 .bool_or,
253 .@"asm",
254 .asm_simple,
255 .string_literal,
256 .integer_literal,
257 .call,
258 .call_comma,
259 .async_call,
260 .async_call_comma,
261 .call_one,
262 .call_one_comma,
263 .async_call_one,
264 .async_call_one_comma,
265 .unreachable_literal,
266 .@"return",
267 .@"if",
268 .if_simple,
269 .@"while",
270 .while_simple,
271 .while_cont,
272 .bool_not,
273 .address_of,
274 .float_literal,
275 .undefined_literal,
276 .true_literal,
277 .false_literal,
278 .null_literal,
279 .optional_type,
280 .block,
281 .block_semicolon,
282 .block_two,
283 .block_two_semicolon,
284 .@"break",
285 .ptr_type_aligned,
286 .ptr_type_sentinel,
287 .ptr_type,
288 .ptr_type_bit_range,
289 .array_type,
290 .array_type_sentinel,
291 .enum_literal,
292 .multiline_string_literal,
293 .char_literal,
294 .@"defer",
295 .@"errdefer",
296 .@"catch",
297 .error_union,
298 .merge_error_sets,
299 .switch_range,
300 .@"await",
301 .bit_not,
302 .negation,
303 .negation_wrap,
304 .@"resume",
305 .@"try",
306 .slice,
307 .slice_open,
308 .slice_sentinel,
309 .array_init_one,
310 .array_init_one_comma,
311 .array_init_dot_two,
312 .array_init_dot_two_comma,
313 .array_init_dot,
314 .array_init_dot_comma,
315 .array_init,
316 .array_init_comma,
317 .struct_init_one,
318 .struct_init_one_comma,
319 .struct_init_dot_two,
320 .struct_init_dot_two_comma,
321 .struct_init_dot,
322 .struct_init_dot_comma,
323 .struct_init,
324 .struct_init_comma,
325 .@"switch",
326 .switch_comma,
327 .@"for",
328 .for_simple,
329 .@"suspend",
330 .@"continue",
331 .@"anytype",
332 .fn_proto_simple,
333 .fn_proto_multi,
334 .fn_proto_one,
335 .fn_proto,
336 .fn_decl,
337 .anyframe_type,
338 .anyframe_literal,
339 .error_set_decl,
340 .container_decl,
341 .container_decl_trailing,
342 .container_decl_two,
343 .container_decl_two_trailing,
344 .container_decl_arg,
345 .container_decl_arg_trailing,
346 .tagged_union,
347 .tagged_union_trailing,
348 .tagged_union_two,
349 .tagged_union_two_trailing,
350 .tagged_union_enum_tag,
351 .tagged_union_enum_tag_trailing,
352 .@"comptime",
353 .@"nosuspend",
354 .error_value,
355 => return gz.astgen.mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
356
357 .builtin_call,
358 .builtin_call_comma,
359 .builtin_call_two,
360 .builtin_call_two_comma,
361 => {
362 const builtin_token = main_tokens[node];
363 const builtin_name = tree.tokenSlice(builtin_token);
364 // If the builtin is an invalid name, we don't cause an error here; instead
365 // let it pass, and the error will be "invalid builtin function" later.
366 if (BuiltinFn.list.get(builtin_name)) |info| {
367 if (!info.allows_lvalue) {
368 return gz.astgen.mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
369 }
370 }
371 },
372
373 // These can be assigned to.
374 .unwrap_optional,
375 .deref,
376 .field_access,
377 .array_access,
378 .identifier,
379 .grouped_expression,
380 .@"orelse",
381 => {},
382 }
383 return expr(gz, scope, .ref, node);
384}
385
386/// Turn Zig AST into untyped ZIR istructions.
387/// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
388/// result instruction can be used to inspect whether it is isNoReturn() but that is it,
389/// it must otherwise not be used.
390pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
391 const mod = gz.astgen.mod;
392 const tree = gz.tree();
393 const main_tokens = tree.nodes.items(.main_token);
394 const token_tags = tree.tokens.items(.tag);
395 const node_datas = tree.nodes.items(.data);
396 const node_tags = tree.nodes.items(.tag);
397
398 switch (node_tags[node]) {
399 .root => unreachable, // Top-level declaration.
400 .@"usingnamespace" => unreachable, // Top-level declaration.
401 .test_decl => unreachable, // Top-level declaration.
402 .container_field_init => unreachable, // Top-level declaration.
403 .container_field_align => unreachable, // Top-level declaration.
404 .container_field => unreachable, // Top-level declaration.
405 .fn_decl => unreachable, // Top-level declaration.
406
407 .global_var_decl => unreachable, // Handled in `blockExpr`.
408 .local_var_decl => unreachable, // Handled in `blockExpr`.
409 .simple_var_decl => unreachable, // Handled in `blockExpr`.
410 .aligned_var_decl => unreachable, // Handled in `blockExpr`.
411
412 .switch_case => unreachable, // Handled in `switchExpr`.
413 .switch_case_one => unreachable, // Handled in `switchExpr`.
414 .switch_range => unreachable, // Handled in `switchExpr`.
415
416 .asm_output => unreachable, // Handled in `asmExpr`.
417 .asm_input => unreachable, // Handled in `asmExpr`.
418
419 .assign => {
420 try assign(gz, scope, node);
421 return rvalue(gz, scope, rl, .void_value, node);
422 },
423 .assign_bit_and => {
424 try assignOp(gz, scope, node, .bit_and);
425 return rvalue(gz, scope, rl, .void_value, node);
426 },
427 .assign_bit_or => {
428 try assignOp(gz, scope, node, .bit_or);
429 return rvalue(gz, scope, rl, .void_value, node);
430 },
431 .assign_bit_shift_left => {
432 try assignOp(gz, scope, node, .shl);
433 return rvalue(gz, scope, rl, .void_value, node);
434 },
435 .assign_bit_shift_right => {
436 try assignOp(gz, scope, node, .shr);
437 return rvalue(gz, scope, rl, .void_value, node);
438 },
439 .assign_bit_xor => {
440 try assignOp(gz, scope, node, .xor);
441 return rvalue(gz, scope, rl, .void_value, node);
442 },
443 .assign_div => {
444 try assignOp(gz, scope, node, .div);
445 return rvalue(gz, scope, rl, .void_value, node);
446 },
447 .assign_sub => {
448 try assignOp(gz, scope, node, .sub);
449 return rvalue(gz, scope, rl, .void_value, node);
450 },
451 .assign_sub_wrap => {
452 try assignOp(gz, scope, node, .subwrap);
453 return rvalue(gz, scope, rl, .void_value, node);
454 },
455 .assign_mod => {
456 try assignOp(gz, scope, node, .mod_rem);
457 return rvalue(gz, scope, rl, .void_value, node);
458 },
459 .assign_add => {
460 try assignOp(gz, scope, node, .add);
461 return rvalue(gz, scope, rl, .void_value, node);
462 },
463 .assign_add_wrap => {
464 try assignOp(gz, scope, node, .addwrap);
465 return rvalue(gz, scope, rl, .void_value, node);
466 },
467 .assign_mul => {
468 try assignOp(gz, scope, node, .mul);
469 return rvalue(gz, scope, rl, .void_value, node);
470 },
471 .assign_mul_wrap => {
472 try assignOp(gz, scope, node, .mulwrap);
473 return rvalue(gz, scope, rl, .void_value, node);
474 },
475
476 .add => return simpleBinOp(gz, scope, rl, node, .add),
477 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),
478 .sub => return simpleBinOp(gz, scope, rl, node, .sub),
479 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),
480 .mul => return simpleBinOp(gz, scope, rl, node, .mul),
481 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),
482 .div => return simpleBinOp(gz, scope, rl, node, .div),
483 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),
484 .bit_and => return simpleBinOp(gz, scope, rl, node, .bit_and),
485 .bit_or => return simpleBinOp(gz, scope, rl, node, .bit_or),
486 .bit_shift_left => return simpleBinOp(gz, scope, rl, node, .shl),
487 .bit_shift_right => return simpleBinOp(gz, scope, rl, node, .shr),
488 .bit_xor => return simpleBinOp(gz, scope, rl, node, .xor),
489
490 .bang_equal => return simpleBinOp(gz, scope, rl, node, .cmp_neq),
491 .equal_equal => return simpleBinOp(gz, scope, rl, node, .cmp_eq),
492 .greater_than => return simpleBinOp(gz, scope, rl, node, .cmp_gt),
493 .greater_or_equal => return simpleBinOp(gz, scope, rl, node, .cmp_gte),
494 .less_than => return simpleBinOp(gz, scope, rl, node, .cmp_lt),
495 .less_or_equal => return simpleBinOp(gz, scope, rl, node, .cmp_lte),
496
497 .array_cat => return simpleBinOp(gz, scope, rl, node, .array_cat),
498 .array_mult => return simpleBinOp(gz, scope, rl, node, .array_mul),
499
500 .error_union => return simpleBinOp(gz, scope, rl, node, .error_union_type),
501 .merge_error_sets => return simpleBinOp(gz, scope, rl, node, .merge_error_sets),
502
503 .bool_and => return boolBinOp(gz, scope, rl, node, .bool_br_and),
504 .bool_or => return boolBinOp(gz, scope, rl, node, .bool_br_or),
505
506 .bool_not => return boolNot(gz, scope, rl, node),
507 .bit_not => return bitNot(gz, scope, rl, node),
508
509 .negation => return negation(gz, scope, rl, node, .negate),
510 .negation_wrap => return negation(gz, scope, rl, node, .negate_wrap),
511
512 .identifier => return identifier(gz, scope, rl, node),
513
514 .asm_simple => return asmExpr(gz, scope, rl, node, tree.asmSimple(node)),
515 .@"asm" => return asmExpr(gz, scope, rl, node, tree.asmFull(node)),
516
517 .string_literal => return stringLiteral(gz, scope, rl, node),
518 .multiline_string_literal => return multilineStringLiteral(gz, scope, rl, node),
519
520 .integer_literal => return integerLiteral(gz, scope, rl, node),
521
522 .builtin_call_two, .builtin_call_two_comma => {
523 if (node_datas[node].lhs == 0) {
524 const params = [_]ast.Node.Index{};
525 return builtinCall(gz, scope, rl, node, &params);
526 } else if (node_datas[node].rhs == 0) {
527 const params = [_]ast.Node.Index{node_datas[node].lhs};
528 return builtinCall(gz, scope, rl, node, &params);
529 } else {
530 const params = [_]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
531 return builtinCall(gz, scope, rl, node, &params);
532 }
533 },
534 .builtin_call, .builtin_call_comma => {
535 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
536 return builtinCall(gz, scope, rl, node, params);
537 },
538
539 .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
540 var params: [1]ast.Node.Index = undefined;
541 return callExpr(gz, scope, rl, node, tree.callOne(&params, node));
542 },
543 .call, .call_comma, .async_call, .async_call_comma => {
544 return callExpr(gz, scope, rl, node, tree.callFull(node));
545 },
546
547 .unreachable_literal => {
548 _ = try gz.addAsIndex(.{
549 .tag = .@"unreachable",
550 .data = .{ .@"unreachable" = .{
551 .safety = true,
552 .src_node = gz.astgen.decl.nodeIndexToRelative(node),
553 } },
554 });
555 return zir.Inst.Ref.unreachable_value;
556 },
557 .@"return" => return ret(gz, scope, node),
558 .field_access => return fieldAccess(gz, scope, rl, node),
559 .float_literal => return floatLiteral(gz, scope, rl, node),
560
561 .if_simple => return ifExpr(gz, scope, rl, node, tree.ifSimple(node)),
562 .@"if" => return ifExpr(gz, scope, rl, node, tree.ifFull(node)),
563
564 .while_simple => return whileExpr(gz, scope, rl, node, tree.whileSimple(node)),
565 .while_cont => return whileExpr(gz, scope, rl, node, tree.whileCont(node)),
566 .@"while" => return whileExpr(gz, scope, rl, node, tree.whileFull(node)),
567
568 .for_simple => return forExpr(gz, scope, rl, node, tree.forSimple(node)),
569 .@"for" => return forExpr(gz, scope, rl, node, tree.forFull(node)),
570
571 .slice_open => {
572 const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
573 const start = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs);
574 const result = try gz.addPlNode(.slice_start, node, zir.Inst.SliceStart{
575 .lhs = lhs,
576 .start = start,
577 });
578 return rvalue(gz, scope, rl, result, node);
579 },
580 .slice => {
581 const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
582 const extra = tree.extraData(node_datas[node].rhs, ast.Node.Slice);
583 const start = try expr(gz, scope, .{ .ty = .usize_type }, extra.start);
584 const end = try expr(gz, scope, .{ .ty = .usize_type }, extra.end);
585 const result = try gz.addPlNode(.slice_end, node, zir.Inst.SliceEnd{
586 .lhs = lhs,
587 .start = start,
588 .end = end,
589 });
590 return rvalue(gz, scope, rl, result, node);
591 },
592 .slice_sentinel => {
593 const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
594 const extra = tree.extraData(node_datas[node].rhs, ast.Node.SliceSentinel);
595 const start = try expr(gz, scope, .{ .ty = .usize_type }, extra.start);
596 const end = try expr(gz, scope, .{ .ty = .usize_type }, extra.end);
597 const sentinel = try expr(gz, scope, .{ .ty = .usize_type }, extra.sentinel);
598 const result = try gz.addPlNode(.slice_sentinel, node, zir.Inst.SliceSentinel{
599 .lhs = lhs,
600 .start = start,
601 .end = end,
602 .sentinel = sentinel,
603 });
604 return rvalue(gz, scope, rl, result, node);
605 },
606
607 .deref => {
608 const lhs = try expr(gz, scope, .none, node_datas[node].lhs);
609 const result = try gz.addUnNode(.load, lhs, node);
610 return rvalue(gz, scope, rl, result, node);
611 },
612 .address_of => {
613 const result = try expr(gz, scope, .ref, node_datas[node].lhs);
614 return rvalue(gz, scope, rl, result, node);
615 },
616 .undefined_literal => return rvalue(gz, scope, rl, .undef, node),
617 .true_literal => return rvalue(gz, scope, rl, .bool_true, node),
618 .false_literal => return rvalue(gz, scope, rl, .bool_false, node),
619 .null_literal => return rvalue(gz, scope, rl, .null_value, node),
620 .optional_type => {
621 const operand = try typeExpr(gz, scope, node_datas[node].lhs);
622 const result = try gz.addUnNode(.optional_type, operand, node);
623 return rvalue(gz, scope, rl, result, node);
624 },
625 .unwrap_optional => switch (rl) {
626 .ref => return gz.addUnNode(
627 .optional_payload_safe_ptr,
628 try expr(gz, scope, .ref, node_datas[node].lhs),
629 node,
630 ),
631 else => return rvalue(gz, scope, rl, try gz.addUnNode(
632 .optional_payload_safe,
633 try expr(gz, scope, .none, node_datas[node].lhs),
634 node,
635 ), node),
636 },
637 .block_two, .block_two_semicolon => {
638 const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
639 if (node_datas[node].lhs == 0) {
640 return blockExpr(gz, scope, rl, node, statements[0..0]);
641 } else if (node_datas[node].rhs == 0) {
642 return blockExpr(gz, scope, rl, node, statements[0..1]);
643 } else {
644 return blockExpr(gz, scope, rl, node, statements[0..2]);
645 }
646 },
647 .block, .block_semicolon => {
648 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
649 return blockExpr(gz, scope, rl, node, statements);
650 },
651 .enum_literal => return simpleStrTok(gz, scope, rl, main_tokens[node], node, .enum_literal),
652 .error_value => return simpleStrTok(gz, scope, rl, node_datas[node].rhs, node, .error_value),
653 .anyframe_literal => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
654 .anyframe_type => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
655 .@"catch" => {
656 const catch_token = main_tokens[node];
657 const payload_token: ?ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
658 catch_token + 2
659 else
660 null;
661 switch (rl) {
662 .ref => return orelseCatchExpr(
663 gz,
664 scope,
665 rl,
666 node,
667 node_datas[node].lhs,
668 .is_err_ptr,
669 .err_union_payload_unsafe_ptr,
670 .err_union_code_ptr,
671 node_datas[node].rhs,
672 payload_token,
673 ),
674 else => return orelseCatchExpr(
675 gz,
676 scope,
677 rl,
678 node,
679 node_datas[node].lhs,
680 .is_err,
681 .err_union_payload_unsafe,
682 .err_union_code,
683 node_datas[node].rhs,
684 payload_token,
685 ),
686 }
687 },
688 .@"orelse" => switch (rl) {
689 .ref => return orelseCatchExpr(
690 gz,
691 scope,
692 rl,
693 node,
694 node_datas[node].lhs,
695 .is_null_ptr,
696 .optional_payload_unsafe_ptr,
697 undefined,
698 node_datas[node].rhs,
699 null,
700 ),
701 else => return orelseCatchExpr(
702 gz,
703 scope,
704 rl,
705 node,
706 node_datas[node].lhs,
707 .is_null,
708 .optional_payload_unsafe,
709 undefined,
710 node_datas[node].rhs,
711 null,
712 ),
713 },
714
715 .ptr_type_aligned => return ptrType(gz, scope, rl, node, tree.ptrTypeAligned(node)),
716 .ptr_type_sentinel => return ptrType(gz, scope, rl, node, tree.ptrTypeSentinel(node)),
717 .ptr_type => return ptrType(gz, scope, rl, node, tree.ptrType(node)),
718 .ptr_type_bit_range => return ptrType(gz, scope, rl, node, tree.ptrTypeBitRange(node)),
719
720 .container_decl,
721 .container_decl_trailing,
722 => return containerDecl(gz, scope, rl, node, tree.containerDecl(node)),
723 .container_decl_two, .container_decl_two_trailing => {
724 var buffer: [2]ast.Node.Index = undefined;
725 return containerDecl(gz, scope, rl, node, tree.containerDeclTwo(&buffer, node));
726 },
727 .container_decl_arg,
728 .container_decl_arg_trailing,
729 => return containerDecl(gz, scope, rl, node, tree.containerDeclArg(node)),
730
731 .tagged_union,
732 .tagged_union_trailing,
733 => return containerDecl(gz, scope, rl, node, tree.taggedUnion(node)),
734 .tagged_union_two, .tagged_union_two_trailing => {
735 var buffer: [2]ast.Node.Index = undefined;
736 return containerDecl(gz, scope, rl, node, tree.taggedUnionTwo(&buffer, node));
737 },
738 .tagged_union_enum_tag,
739 .tagged_union_enum_tag_trailing,
740 => return containerDecl(gz, scope, rl, node, tree.taggedUnionEnumTag(node)),
741
742 .@"break" => return breakExpr(gz, scope, node),
743 .@"continue" => return continueExpr(gz, scope, node),
744 .grouped_expression => return expr(gz, scope, rl, node_datas[node].lhs),
745 .array_type => return arrayType(gz, scope, rl, node),
746 .array_type_sentinel => return arrayTypeSentinel(gz, scope, rl, node),
747 .char_literal => return charLiteral(gz, scope, rl, node),
748 .error_set_decl => return errorSetDecl(gz, scope, rl, node),
749 .array_access => return arrayAccess(gz, scope, rl, node),
750 .@"comptime" => return comptimeExpr(gz, scope, rl, node_datas[node].lhs),
751 .@"switch", .switch_comma => return switchExpr(gz, scope, rl, node),
752
753 .@"nosuspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
754 .@"suspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
755 .@"await" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
756 .@"resume" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
757
758 .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}),
759 .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}),
760 .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
761
762 .array_init_one,
763 .array_init_one_comma,
764 .array_init_dot_two,
765 .array_init_dot_two_comma,
766 .array_init_dot,
767 .array_init_dot_comma,
768 .array_init,
769 .array_init_comma,
770 => return mod.failNode(scope, node, "TODO implement astgen.expr for array literals", .{}),
771
772 .struct_init_one, .struct_init_one_comma => {
773 var fields: [1]ast.Node.Index = undefined;
774 return structInitExpr(gz, scope, rl, node, tree.structInitOne(&fields, node));
775 },
776 .struct_init_dot_two, .struct_init_dot_two_comma => {
777 var fields: [2]ast.Node.Index = undefined;
778 return structInitExpr(gz, scope, rl, node, tree.structInitDotTwo(&fields, node));
779 },
780 .struct_init_dot,
781 .struct_init_dot_comma,
782 => return structInitExpr(gz, scope, rl, node, tree.structInitDot(node)),
783 .struct_init,
784 .struct_init_comma,
785 => return structInitExpr(gz, scope, rl, node, tree.structInit(node)),
786
787 .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}),
788 .fn_proto_simple,
789 .fn_proto_multi,
790 .fn_proto_one,
791 .fn_proto,
792 => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}),
793 }
794}
795
796pub fn structInitExpr(
797 gz: *GenZir,
798 scope: *Scope,
799 rl: ResultLoc,
800 node: ast.Node.Index,
801 struct_init: ast.full.StructInit,
802) InnerError!zir.Inst.Ref {
803 const tree = gz.tree();
804 const astgen = gz.astgen;
805 const mod = astgen.mod;
806 const gpa = mod.gpa;
807
808 if (struct_init.ast.fields.len == 0) {
809 if (struct_init.ast.type_expr == 0) {
810 return rvalue(gz, scope, rl, .empty_struct, node);
811 } else {
812 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
813 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
814 return rvalue(gz, scope, rl, result, node);
815 }
816 }
817 switch (rl) {
818 .discard => return mod.failNode(scope, node, "TODO implement structInitExpr discard", .{}),
819 .none => return mod.failNode(scope, node, "TODO implement structInitExpr none", .{}),
820 .ref => unreachable, // struct literal not valid as l-value
821 .ty => |ty_inst| {
822 return mod.failNode(scope, node, "TODO implement structInitExpr ty", .{});
823 },
824 .ptr => |ptr_inst| {
825 const field_ptr_list = try gpa.alloc(zir.Inst.Index, struct_init.ast.fields.len);
826 defer gpa.free(field_ptr_list);
827
828 for (struct_init.ast.fields) |field_init, i| {
829 const name_token = tree.firstToken(field_init) - 2;
830 const str_index = try gz.identAsString(name_token);
831 const field_ptr = try gz.addPlNode(.field_ptr, field_init, zir.Inst.Field{
832 .lhs = ptr_inst,
833 .field_name_start = str_index,
834 });
835 field_ptr_list[i] = astgen.refToIndex(field_ptr).?;
836 _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init);
837 }
838 const validate_inst = try gz.addPlNode(.validate_struct_init_ptr, node, zir.Inst.Block{
839 .body_len = @intCast(u32, field_ptr_list.len),
840 });
841 try astgen.extra.appendSlice(gpa, field_ptr_list);
842 return validate_inst;
843 },
844 .inferred_ptr => |ptr_inst| {
845 return mod.failNode(scope, node, "TODO implement structInitExpr inferred_ptr", .{});
846 },
847 .block_ptr => |block_gz| {
848 return mod.failNode(scope, node, "TODO implement structInitExpr block", .{});
849 },
850 }
851}
852
853pub fn comptimeExpr(
854 gz: *GenZir,
855 scope: *Scope,
856 rl: ResultLoc,
857 node: ast.Node.Index,
858) InnerError!zir.Inst.Ref {
859 const prev_force_comptime = gz.force_comptime;
860 gz.force_comptime = true;
861 const result = try expr(gz, scope, rl, node);
862 gz.force_comptime = prev_force_comptime;
863 return result;
864}
865
866fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
867 const mod = parent_gz.astgen.mod;
868 const tree = parent_gz.tree();
869 const node_datas = tree.nodes.items(.data);
870 const break_label = node_datas[node].lhs;
871 const rhs = node_datas[node].rhs;
872
873 // Look for the label in the scope.
874 var scope = parent_scope;
875 while (true) {
876 switch (scope.tag) {
877 .gen_zir => {
878 const block_gz = scope.cast(GenZir).?;
879
880 const block_inst = blk: {
881 if (break_label != 0) {
882 if (block_gz.label) |*label| {
883 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
884 label.used = true;
885 break :blk label.block_inst;
886 }
887 }
888 } else if (block_gz.break_block != 0) {
889 break :blk block_gz.break_block;
890 }
891 scope = block_gz.parent;
892 continue;
893 };
894
895 if (rhs == 0) {
896 _ = try parent_gz.addBreak(.@"break", block_inst, .void_value);
897 return zir.Inst.Ref.unreachable_value;
898 }
899 block_gz.break_count += 1;
900 const prev_rvalue_rl_count = block_gz.rvalue_rl_count;
901 const operand = try expr(parent_gz, parent_scope, block_gz.break_result_loc, rhs);
902 const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count;
903
904 const br = try parent_gz.addBreak(.@"break", block_inst, operand);
905
906 if (block_gz.break_result_loc == .block_ptr) {
907 try block_gz.labeled_breaks.append(mod.gpa, br);
908
909 if (have_store_to_block) {
910 const zir_tags = parent_gz.astgen.instructions.items(.tag);
911 const zir_datas = parent_gz.astgen.instructions.items(.data);
912 const store_inst = @intCast(u32, zir_tags.len - 2);
913 assert(zir_tags[store_inst] == .store_to_block_ptr);
914 assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr);
915 try block_gz.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst);
916 }
917 }
918 return zir.Inst.Ref.unreachable_value;
919 },
920 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
921 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
922 else => if (break_label != 0) {
923 const label_name = try mod.identifierTokenString(parent_scope, break_label);
924 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
925 } else {
926 return mod.failNode(parent_scope, node, "break expression outside loop", .{});
927 },
928 }
929 }
930}
931
932fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
933 const mod = parent_gz.astgen.mod;
934 const tree = parent_gz.tree();
935 const node_datas = tree.nodes.items(.data);
936 const break_label = node_datas[node].lhs;
937
938 // Look for the label in the scope.
939 var scope = parent_scope;
940 while (true) {
941 switch (scope.tag) {
942 .gen_zir => {
943 const gen_zir = scope.cast(GenZir).?;
944 const continue_block = gen_zir.continue_block;
945 if (continue_block == 0) {
946 scope = gen_zir.parent;
947 continue;
948 }
949 if (break_label != 0) blk: {
950 if (gen_zir.label) |*label| {
951 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
952 label.used = true;
953 break :blk;
954 }
955 }
956 // found continue but either it has a different label, or no label
957 scope = gen_zir.parent;
958 continue;
959 }
960
961 // TODO emit a break_inline if the loop being continued is inline
962 _ = try parent_gz.addBreak(.@"break", continue_block, .void_value);
963 return zir.Inst.Ref.unreachable_value;
964 },
965 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
966 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
967 else => if (break_label != 0) {
968 const label_name = try mod.identifierTokenString(parent_scope, break_label);
969 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
970 } else {
971 return mod.failNode(parent_scope, node, "continue expression outside loop", .{});
972 },
973 }
974 }
975}
976
977pub fn blockExpr(
978 gz: *GenZir,
979 scope: *Scope,
980 rl: ResultLoc,
981 block_node: ast.Node.Index,
982 statements: []const ast.Node.Index,
983) InnerError!zir.Inst.Ref {
984 const tracy = trace(@src());
985 defer tracy.end();
986
987 const tree = gz.tree();
988 const main_tokens = tree.nodes.items(.main_token);
989 const token_tags = tree.tokens.items(.tag);
990
991 const lbrace = main_tokens[block_node];
992 if (token_tags[lbrace - 1] == .colon and
993 token_tags[lbrace - 2] == .identifier)
994 {
995 return labeledBlockExpr(gz, scope, rl, block_node, statements, .block);
996 }
997
998 try blockExprStmts(gz, scope, block_node, statements);
999 return rvalue(gz, scope, rl, .void_value, block_node);
1000}
1001
1002fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {
1003 // Look for the label in the scope.
1004 var scope = parent_scope;
1005 while (true) {
1006 switch (scope.tag) {
1007 .gen_zir => {
1008 const gen_zir = scope.cast(GenZir).?;
1009 if (gen_zir.label) |prev_label| {
1010 if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {
1011 const tree = parent_scope.tree();
1012 const main_tokens = tree.nodes.items(.main_token);
1013
1014 const label_name = try mod.identifierTokenString(parent_scope, label);
1015 const msg = msg: {
1016 const msg = try mod.errMsg(
1017 parent_scope,
1018 gen_zir.tokSrcLoc(label),
1019 "redefinition of label '{s}'",
1020 .{label_name},
1021 );
1022 errdefer msg.destroy(mod.gpa);
1023 try mod.errNote(
1024 parent_scope,
1025 gen_zir.tokSrcLoc(prev_label.token),
1026 msg,
1027 "previous definition is here",
1028 .{},
1029 );
1030 break :msg msg;
1031 };
1032 return mod.failWithOwnedErrorMsg(parent_scope, msg);
1033 }
1034 }
1035 scope = gen_zir.parent;
1036 },
1037 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
1038 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
1039 else => return,
1040 }
1041 }
1042}
1043
1044fn labeledBlockExpr(
1045 gz: *GenZir,
1046 parent_scope: *Scope,
1047 rl: ResultLoc,
1048 block_node: ast.Node.Index,
1049 statements: []const ast.Node.Index,
1050 zir_tag: zir.Inst.Tag,
1051) InnerError!zir.Inst.Ref {
1052 const tracy = trace(@src());
1053 defer tracy.end();
1054
1055 assert(zir_tag == .block);
1056
1057 const mod = gz.astgen.mod;
1058 const tree = gz.tree();
1059 const main_tokens = tree.nodes.items(.main_token);
1060 const token_tags = tree.tokens.items(.tag);
1061
1062 const lbrace = main_tokens[block_node];
1063 const label_token = lbrace - 2;
1064 assert(token_tags[label_token] == .identifier);
1065
1066 try checkLabelRedefinition(mod, parent_scope, label_token);
1067
1068 // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
1069 // so that break statements can reference it.
1070 const block_inst = try gz.addBlock(zir_tag, block_node);
1071 try gz.instructions.append(mod.gpa, block_inst);
1072
1073 var block_scope: GenZir = .{
1074 .parent = parent_scope,
1075 .astgen = gz.astgen,
1076 .force_comptime = gz.force_comptime,
1077 .instructions = .{},
1078 // TODO @as here is working around a stage1 miscompilation bug :(
1079 .label = @as(?GenZir.Label, GenZir.Label{
1080 .token = label_token,
1081 .block_inst = block_inst,
1082 }),
1083 };
1084 block_scope.setBreakResultLoc(rl);
1085 defer block_scope.instructions.deinit(mod.gpa);
1086 defer block_scope.labeled_breaks.deinit(mod.gpa);
1087 defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa);
1088
1089 try blockExprStmts(&block_scope, &block_scope.base, block_node, statements);
1090
1091 if (!block_scope.label.?.used) {
1092 return mod.failTok(parent_scope, label_token, "unused block label", .{});
1093 }
1094
1095 const zir_tags = gz.astgen.instructions.items(.tag);
1096 const zir_datas = gz.astgen.instructions.items(.data);
1097
1098 const strat = rl.strategy(&block_scope);
1099 switch (strat.tag) {
1100 .break_void => {
1101 // The code took advantage of the result location as a pointer.
1102 // Turn the break instruction operands into void.
1103 for (block_scope.labeled_breaks.items) |br| {
1104 zir_datas[br].@"break".operand = .void_value;
1105 }
1106 try block_scope.setBlockBody(block_inst);
1107
1108 return gz.astgen.indexToRef(block_inst);
1109 },
1110 .break_operand => {
1111 // All break operands are values that did not use the result location pointer.
1112 if (strat.elide_store_to_block_ptr_instructions) {
1113 for (block_scope.labeled_store_to_block_ptr_list.items) |inst| {
1114 zir_tags[inst] = .elided;
1115 zir_datas[inst] = undefined;
1116 }
1117 // TODO technically not needed since we changed the tag to elided but
1118 // would be better still to elide the ones that are in this list.
1119 }
1120 try block_scope.setBlockBody(block_inst);
1121 const block_ref = gz.astgen.indexToRef(block_inst);
1122 switch (rl) {
1123 .ref => return block_ref,
1124 else => return rvalue(gz, parent_scope, rl, block_ref, block_node),
1125 }
1126 },
1127 }
1128}
1129
1130fn blockExprStmts(
1131 gz: *GenZir,
1132 parent_scope: *Scope,
1133 node: ast.Node.Index,
1134 statements: []const ast.Node.Index,
1135) !void {
1136 const tree = gz.tree();
1137 const main_tokens = tree.nodes.items(.main_token);
1138 const node_tags = tree.nodes.items(.tag);
1139
1140 var block_arena = std.heap.ArenaAllocator.init(gz.astgen.mod.gpa);
1141 defer block_arena.deinit();
1142
1143 var scope = parent_scope;
1144 for (statements) |statement| {
1145 if (!gz.force_comptime) {
1146 _ = try gz.addNode(.dbg_stmt_node, statement);
1147 }
1148 switch (node_tags[statement]) {
1149 .global_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.globalVarDecl(statement)),
1150 .local_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.localVarDecl(statement)),
1151 .simple_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.simpleVarDecl(statement)),
1152 .aligned_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.alignedVarDecl(statement)),
1153
1154 .assign => try assign(gz, scope, statement),
1155 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),
1156 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or),
1157 .assign_bit_shift_left => try assignOp(gz, scope, statement, .shl),
1158 .assign_bit_shift_right => try assignOp(gz, scope, statement, .shr),
1159 .assign_bit_xor => try assignOp(gz, scope, statement, .xor),
1160 .assign_div => try assignOp(gz, scope, statement, .div),
1161 .assign_sub => try assignOp(gz, scope, statement, .sub),
1162 .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap),
1163 .assign_mod => try assignOp(gz, scope, statement, .mod_rem),
1164 .assign_add => try assignOp(gz, scope, statement, .add),
1165 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
1166 .assign_mul => try assignOp(gz, scope, statement, .mul),
1167 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
1168
1169 else => {
1170 // We need to emit an error if the result is not `noreturn` or `void`, but
1171 // we want to avoid adding the ZIR instruction if possible for performance.
1172 const maybe_unused_result = try expr(gz, scope, .none, statement);
1173 const elide_check = if (gz.astgen.refToIndex(maybe_unused_result)) |inst| b: {
1174 // Note that this array becomes invalid after appending more items to it
1175 // in the above while loop.
1176 const zir_tags = gz.astgen.instructions.items(.tag);
1177 switch (zir_tags[inst]) {
1178 .@"const" => {
1179 const tv = gz.astgen.instructions.items(.data)[inst].@"const";
1180 break :b switch (tv.ty.zigTypeTag()) {
1181 .NoReturn, .Void => true,
1182 else => false,
1183 };
1184 },
1185 // For some instructions, swap in a slightly different ZIR tag
1186 // so we can avoid a separate ensure_result_used instruction.
1187 .call_none_chkused => unreachable,
1188 .call_none => {
1189 zir_tags[inst] = .call_none_chkused;
1190 break :b true;
1191 },
1192 .call_chkused => unreachable,
1193 .call => {
1194 zir_tags[inst] = .call_chkused;
1195 break :b true;
1196 },
1197
1198 // ZIR instructions that might be a type other than `noreturn` or `void`.
1199 .add,
1200 .addwrap,
1201 .alloc,
1202 .alloc_mut,
1203 .alloc_inferred,
1204 .alloc_inferred_mut,
1205 .array_cat,
1206 .array_mul,
1207 .array_type,
1208 .array_type_sentinel,
1209 .indexable_ptr_len,
1210 .as,
1211 .as_node,
1212 .@"asm",
1213 .asm_volatile,
1214 .bit_and,
1215 .bitcast,
1216 .bitcast_result_ptr,
1217 .bit_or,
1218 .block,
1219 .block_inline,
1220 .loop,
1221 .bool_br_and,
1222 .bool_br_or,
1223 .bool_not,
1224 .bool_and,
1225 .bool_or,
1226 .call_compile_time,
1227 .cmp_lt,
1228 .cmp_lte,
1229 .cmp_eq,
1230 .cmp_gte,
1231 .cmp_gt,
1232 .cmp_neq,
1233 .coerce_result_ptr,
1234 .decl_ref,
1235 .decl_val,
1236 .load,
1237 .div,
1238 .elem_ptr,
1239 .elem_val,
1240 .elem_ptr_node,
1241 .elem_val_node,
1242 .floatcast,
1243 .field_ptr,
1244 .field_val,
1245 .field_ptr_named,
1246 .field_val_named,
1247 .fn_type,
1248 .fn_type_var_args,
1249 .fn_type_cc,
1250 .fn_type_cc_var_args,
1251 .int,
1252 .intcast,
1253 .int_type,
1254 .is_non_null,
1255 .is_null,
1256 .is_non_null_ptr,
1257 .is_null_ptr,
1258 .is_err,
1259 .is_err_ptr,
1260 .mod_rem,
1261 .mul,
1262 .mulwrap,
1263 .param_type,
1264 .ptrtoint,
1265 .ref,
1266 .ret_ptr,
1267 .ret_type,
1268 .shl,
1269 .shr,
1270 .str,
1271 .sub,
1272 .subwrap,
1273 .negate,
1274 .negate_wrap,
1275 .typeof,
1276 .typeof_elem,
1277 .xor,
1278 .optional_type,
1279 .optional_type_from_ptr_elem,
1280 .optional_payload_safe,
1281 .optional_payload_unsafe,
1282 .optional_payload_safe_ptr,
1283 .optional_payload_unsafe_ptr,
1284 .err_union_payload_safe,
1285 .err_union_payload_unsafe,
1286 .err_union_payload_safe_ptr,
1287 .err_union_payload_unsafe_ptr,
1288 .err_union_code,
1289 .err_union_code_ptr,
1290 .ptr_type,
1291 .ptr_type_simple,
1292 .enum_literal,
1293 .enum_literal_small,
1294 .merge_error_sets,
1295 .error_union_type,
1296 .bit_not,
1297 .error_value,
1298 .error_to_int,
1299 .int_to_error,
1300 .slice_start,
1301 .slice_end,
1302 .slice_sentinel,
1303 .import,
1304 .typeof_peer,
1305 .switch_block,
1306 .switch_block_multi,
1307 .switch_block_else,
1308 .switch_block_else_multi,
1309 .switch_block_under,
1310 .switch_block_under_multi,
1311 .switch_block_ref,
1312 .switch_block_ref_multi,
1313 .switch_block_ref_else,
1314 .switch_block_ref_else_multi,
1315 .switch_block_ref_under,
1316 .switch_block_ref_under_multi,
1317 .switch_capture,
1318 .switch_capture_ref,
1319 .switch_capture_multi,
1320 .switch_capture_multi_ref,
1321 .switch_capture_else,
1322 .switch_capture_else_ref,
1323 .struct_init_empty,
1324 .struct_decl,
1325 .struct_decl_packed,
1326 .struct_decl_extern,
1327 .union_decl,
1328 .enum_decl,
1329 .opaque_decl,
1330 => break :b false,
1331
1332 // ZIR instructions that are always either `noreturn` or `void`.
1333 .breakpoint,
1334 .dbg_stmt_node,
1335 .ensure_result_used,
1336 .ensure_result_non_error,
1337 .set_eval_branch_quota,
1338 .compile_log,
1339 .ensure_err_payload_void,
1340 .@"break",
1341 .break_inline,
1342 .condbr,
1343 .condbr_inline,
1344 .compile_error,
1345 .ret_node,
1346 .ret_tok,
1347 .ret_coerce,
1348 .@"unreachable",
1349 .elided,
1350 .store,
1351 .store_node,
1352 .store_to_block_ptr,
1353 .store_to_inferred_ptr,
1354 .resolve_inferred_alloc,
1355 .repeat,
1356 .repeat_inline,
1357 .validate_struct_init_ptr,
1358 => break :b true,
1359 }
1360 } else switch (maybe_unused_result) {
1361 .none => unreachable,
1362
1363 .void_value,
1364 .unreachable_value,
1365 => true,
1366
1367 else => false,
1368 };
1369 if (!elide_check) {
1370 _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
1371 }
1372 },
1373 }
1374 }
1375}
1376
1377fn varDecl(
1378 gz: *GenZir,
1379 scope: *Scope,
1380 node: ast.Node.Index,
1381 block_arena: *Allocator,
1382 var_decl: ast.full.VarDecl,
1383) InnerError!*Scope {
1384 const mod = gz.astgen.mod;
1385 if (var_decl.comptime_token) |comptime_token| {
1386 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
1387 }
1388 if (var_decl.ast.align_node != 0) {
1389 return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{});
1390 }
1391 const astgen = gz.astgen;
1392 const tree = gz.tree();
1393 const token_tags = tree.tokens.items(.tag);
1394
1395 const name_token = var_decl.ast.mut_token + 1;
1396 const name_src = gz.tokSrcLoc(name_token);
1397 const ident_name = try mod.identifierTokenString(scope, name_token);
1398
1399 // Local variables shadowing detection, including function parameters.
1400 {
1401 var s = scope;
1402 while (true) switch (s.tag) {
1403 .local_val => {
1404 const local_val = s.cast(Scope.LocalVal).?;
1405 if (mem.eql(u8, local_val.name, ident_name)) {
1406 const msg = msg: {
1407 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
1408 ident_name,
1409 });
1410 errdefer msg.destroy(mod.gpa);
1411 try mod.errNote(scope, local_val.src, msg, "previous definition is here", .{});
1412 break :msg msg;
1413 };
1414 return mod.failWithOwnedErrorMsg(scope, msg);
1415 }
1416 s = local_val.parent;
1417 },
1418 .local_ptr => {
1419 const local_ptr = s.cast(Scope.LocalPtr).?;
1420 if (mem.eql(u8, local_ptr.name, ident_name)) {
1421 const msg = msg: {
1422 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
1423 ident_name,
1424 });
1425 errdefer msg.destroy(mod.gpa);
1426 try mod.errNote(scope, local_ptr.src, msg, "previous definition is here", .{});
1427 break :msg msg;
1428 };
1429 return mod.failWithOwnedErrorMsg(scope, msg);
1430 }
1431 s = local_ptr.parent;
1432 },
1433 .gen_zir => s = s.cast(GenZir).?.parent,
1434 else => break,
1435 };
1436 }
1437
1438 // Namespace vars shadowing detection
1439 if (mod.lookupDeclName(scope, ident_name)) |_| {
1440 // TODO add note for other definition
1441 return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
1442 }
1443 if (var_decl.ast.init_node == 0) {
1444 return mod.fail(scope, name_src, "variables must be initialized", .{});
1445 }
1446
1447 switch (token_tags[var_decl.ast.mut_token]) {
1448 .keyword_const => {
1449 // Depending on the type of AST the initialization expression is, we may need an lvalue
1450 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
1451 // the variable, no memory location needed.
1452 if (!nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node)) {
1453 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{
1454 .ty = try typeExpr(gz, scope, var_decl.ast.type_node),
1455 } else .none;
1456 const init_inst = try expr(gz, scope, result_loc, var_decl.ast.init_node);
1457 const sub_scope = try block_arena.create(Scope.LocalVal);
1458 sub_scope.* = .{
1459 .parent = scope,
1460 .gen_zir = gz,
1461 .name = ident_name,
1462 .inst = init_inst,
1463 .src = name_src,
1464 };
1465 return &sub_scope.base;
1466 }
1467
1468 // Detect whether the initialization expression actually uses the
1469 // result location pointer.
1470 var init_scope: GenZir = .{
1471 .parent = scope,
1472 .force_comptime = gz.force_comptime,
1473 .astgen = astgen,
1474 };
1475 defer init_scope.instructions.deinit(mod.gpa);
1476
1477 var resolve_inferred_alloc: zir.Inst.Ref = .none;
1478 var opt_type_inst: zir.Inst.Ref = .none;
1479 if (var_decl.ast.type_node != 0) {
1480 const type_inst = try typeExpr(gz, &init_scope.base, var_decl.ast.type_node);
1481 opt_type_inst = type_inst;
1482 init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node);
1483 init_scope.rl_ty_inst = type_inst;
1484 } else {
1485 const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node);
1486 resolve_inferred_alloc = alloc;
1487 init_scope.rl_ptr = alloc;
1488 }
1489 const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
1490 const init_inst = try expr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node);
1491 const zir_tags = astgen.instructions.items(.tag);
1492 const zir_datas = astgen.instructions.items(.data);
1493
1494 const parent_zir = &gz.instructions;
1495 if (init_scope.rvalue_rl_count == 1) {
1496 // Result location pointer not used. We don't need an alloc for this
1497 // const local, and type inference becomes trivial.
1498 // Move the init_scope instructions into the parent scope, eliding
1499 // the alloc instruction and the store_to_block_ptr instruction.
1500 const expected_len = parent_zir.items.len + init_scope.instructions.items.len - 2;
1501 try parent_zir.ensureCapacity(mod.gpa, expected_len);
1502 for (init_scope.instructions.items) |src_inst| {
1503 if (astgen.indexToRef(src_inst) == init_scope.rl_ptr) continue;
1504 if (zir_tags[src_inst] == .store_to_block_ptr) {
1505 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue;
1506 }
1507 parent_zir.appendAssumeCapacity(src_inst);
1508 }
1509 assert(parent_zir.items.len == expected_len);
1510
1511 const sub_scope = try block_arena.create(Scope.LocalVal);
1512 sub_scope.* = .{
1513 .parent = scope,
1514 .gen_zir = gz,
1515 .name = ident_name,
1516 .inst = init_inst,
1517 .src = name_src,
1518 };
1519 return &sub_scope.base;
1520 }
1521 // The initialization expression took advantage of the result location
1522 // of the const local. In this case we will create an alloc and a LocalPtr for it.
1523 // Move the init_scope instructions into the parent scope, swapping
1524 // store_to_block_ptr for store_to_inferred_ptr.
1525 const expected_len = parent_zir.items.len + init_scope.instructions.items.len;
1526 try parent_zir.ensureCapacity(mod.gpa, expected_len);
1527 for (init_scope.instructions.items) |src_inst| {
1528 if (zir_tags[src_inst] == .store_to_block_ptr) {
1529 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) {
1530 zir_tags[src_inst] = .store_to_inferred_ptr;
1531 }
1532 }
1533 parent_zir.appendAssumeCapacity(src_inst);
1534 }
1535 assert(parent_zir.items.len == expected_len);
1536 if (resolve_inferred_alloc != .none) {
1537 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
1538 }
1539 const sub_scope = try block_arena.create(Scope.LocalPtr);
1540 sub_scope.* = .{
1541 .parent = scope,
1542 .gen_zir = gz,
1543 .name = ident_name,
1544 .ptr = init_scope.rl_ptr,
1545 .src = name_src,
1546 };
1547 return &sub_scope.base;
1548 },
1549 .keyword_var => {
1550 var resolve_inferred_alloc: zir.Inst.Ref = .none;
1551 const var_data: struct {
1552 result_loc: ResultLoc,
1553 alloc: zir.Inst.Ref,
1554 } = if (var_decl.ast.type_node != 0) a: {
1555 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
1556
1557 const alloc = try gz.addUnNode(.alloc_mut, type_inst, node);
1558 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
1559 } else a: {
1560 const alloc = try gz.addUnNode(.alloc_inferred_mut, undefined, node);
1561 resolve_inferred_alloc = alloc;
1562 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } };
1563 };
1564 const init_inst = try expr(gz, scope, var_data.result_loc, var_decl.ast.init_node);
1565 if (resolve_inferred_alloc != .none) {
1566 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
1567 }
1568 const sub_scope = try block_arena.create(Scope.LocalPtr);
1569 sub_scope.* = .{
1570 .parent = scope,
1571 .gen_zir = gz,
1572 .name = ident_name,
1573 .ptr = var_data.alloc,
1574 .src = name_src,
1575 };
1576 return &sub_scope.base;
1577 },
1578 else => unreachable,
1579 }
1580}
1581
1582fn assign(gz: *GenZir, scope: *Scope, infix_node: ast.Node.Index) InnerError!void {
1583 const tree = gz.tree();
1584 const node_datas = tree.nodes.items(.data);
1585 const main_tokens = tree.nodes.items(.main_token);
1586 const node_tags = tree.nodes.items(.tag);
1587
1588 const lhs = node_datas[infix_node].lhs;
1589 const rhs = node_datas[infix_node].rhs;
1590 if (node_tags[lhs] == .identifier) {
1591 // This intentionally does not support `@"_"` syntax.
1592 const ident_name = tree.tokenSlice(main_tokens[lhs]);
1593 if (mem.eql(u8, ident_name, "_")) {
1594 _ = try expr(gz, scope, .discard, rhs);
1595 return;
1596 }
1597 }
1598 const lvalue = try lvalExpr(gz, scope, lhs);
1599 _ = try expr(gz, scope, .{ .ptr = lvalue }, rhs);
1600}
1601
1602fn assignOp(
1603 gz: *GenZir,
1604 scope: *Scope,
1605 infix_node: ast.Node.Index,
1606 op_inst_tag: zir.Inst.Tag,
1607) InnerError!void {
1608 const tree = gz.tree();
1609 const node_datas = tree.nodes.items(.data);
1610
1611 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
1612 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
1613 const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
1614 const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
1615
1616 const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{
1617 .lhs = lhs,
1618 .rhs = rhs,
1619 });
1620 _ = try gz.addBin(.store, lhs_ptr, result);
1621}
1622
1623fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
1624 const tree = gz.tree();
1625 const node_datas = tree.nodes.items(.data);
1626
1627 const operand = try expr(gz, scope, .{ .ty = .bool_type }, node_datas[node].lhs);
1628 const result = try gz.addUnNode(.bool_not, operand, node);
1629 return rvalue(gz, scope, rl, result, node);
1630}
1631
1632fn bitNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
1633 const tree = gz.tree();
1634 const node_datas = tree.nodes.items(.data);
1635
1636 const operand = try expr(gz, scope, .none, node_datas[node].lhs);
1637 const result = try gz.addUnNode(.bit_not, operand, node);
1638 return rvalue(gz, scope, rl, result, node);
1639}
1640
1641fn negation(
1642 gz: *GenZir,
1643 scope: *Scope,
1644 rl: ResultLoc,
1645 node: ast.Node.Index,
1646 tag: zir.Inst.Tag,
1647) InnerError!zir.Inst.Ref {
1648 const tree = gz.tree();
1649 const node_datas = tree.nodes.items(.data);
1650
1651 const operand = try expr(gz, scope, .none, node_datas[node].lhs);
1652 const result = try gz.addUnNode(tag, operand, node);
1653 return rvalue(gz, scope, rl, result, node);
1654}
1655
1656fn ptrType(
1657 gz: *GenZir,
1658 scope: *Scope,
1659 rl: ResultLoc,
1660 node: ast.Node.Index,
1661 ptr_info: ast.full.PtrType,
1662) InnerError!zir.Inst.Ref {
1663 const tree = gz.tree();
1664
1665 const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
1666
1667 const simple = ptr_info.ast.align_node == 0 and
1668 ptr_info.ast.sentinel == 0 and
1669 ptr_info.ast.bit_range_start == 0;
1670
1671 if (simple) {
1672 const result = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
1673 .ptr_type_simple = .{
1674 .is_allowzero = ptr_info.allowzero_token != null,
1675 .is_mutable = ptr_info.const_token == null,
1676 .is_volatile = ptr_info.volatile_token != null,
1677 .size = ptr_info.size,
1678 .elem_type = elem_type,
1679 },
1680 } });
1681 return rvalue(gz, scope, rl, result, node);
1682 }
1683
1684 var sentinel_ref: zir.Inst.Ref = .none;
1685 var align_ref: zir.Inst.Ref = .none;
1686 var bit_start_ref: zir.Inst.Ref = .none;
1687 var bit_end_ref: zir.Inst.Ref = .none;
1688 var trailing_count: u32 = 0;
1689
1690 if (ptr_info.ast.sentinel != 0) {
1691 sentinel_ref = try expr(gz, scope, .{ .ty = elem_type }, ptr_info.ast.sentinel);
1692 trailing_count += 1;
1693 }
1694 if (ptr_info.ast.align_node != 0) {
1695 align_ref = try expr(gz, scope, .none, ptr_info.ast.align_node);
1696 trailing_count += 1;
1697 }
1698 if (ptr_info.ast.bit_range_start != 0) {
1699 assert(ptr_info.ast.bit_range_end != 0);
1700 bit_start_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_start);
1701 bit_end_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_end);
1702 trailing_count += 2;
1703 }
1704
1705 const gpa = gz.astgen.mod.gpa;
1706 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1707 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1708 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1709 @typeInfo(zir.Inst.PtrType).Struct.fields.len + trailing_count);
1710
1711 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.PtrType{ .elem_type = elem_type });
1712 if (sentinel_ref != .none) {
1713 gz.astgen.extra.appendAssumeCapacity(@enumToInt(sentinel_ref));
1714 }
1715 if (align_ref != .none) {
1716 gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref));
1717 }
1718 if (bit_start_ref != .none) {
1719 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
1720 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
1721 }
1722
1723 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1724 const result = gz.astgen.indexToRef(new_index);
1725 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
1726 .ptr_type = .{
1727 .flags = .{
1728 .is_allowzero = ptr_info.allowzero_token != null,
1729 .is_mutable = ptr_info.const_token == null,
1730 .is_volatile = ptr_info.volatile_token != null,
1731 .has_sentinel = sentinel_ref != .none,
1732 .has_align = align_ref != .none,
1733 .has_bit_range = bit_start_ref != .none,
1734 },
1735 .size = ptr_info.size,
1736 .payload_index = payload_index,
1737 },
1738 } });
1739 gz.instructions.appendAssumeCapacity(new_index);
1740
1741 return rvalue(gz, scope, rl, result, node);
1742}
1743
1744fn arrayType(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
1745 const tree = gz.tree();
1746 const node_datas = tree.nodes.items(.data);
1747
1748 // TODO check for [_]T
1749 const len = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].lhs);
1750 const elem_type = try typeExpr(gz, scope, node_datas[node].rhs);
1751
1752 const result = try gz.addBin(.array_type, len, elem_type);
1753 return rvalue(gz, scope, rl, result, node);
1754}
1755
1756fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
1757 const tree = gz.tree();
1758 const node_datas = tree.nodes.items(.data);
1759 const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel);
1760
1761 // TODO check for [_]T
1762 const len = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].lhs);
1763 const elem_type = try typeExpr(gz, scope, extra.elem_type);
1764 const sentinel = try expr(gz, scope, .{ .ty = elem_type }, extra.sentinel);
1765
1766 const result = try gz.addArrayTypeSentinel(len, elem_type, sentinel);
1767 return rvalue(gz, scope, rl, result, node);
1768}
1769
1770fn containerDecl(
1771 gz: *GenZir,
1772 scope: *Scope,
1773 rl: ResultLoc,
1774 node: ast.Node.Index,
1775 container_decl: ast.full.ContainerDecl,
1776) InnerError!zir.Inst.Ref {
1777 const astgen = gz.astgen;
1778 const mod = astgen.mod;
1779 const gpa = mod.gpa;
1780 const tree = gz.tree();
1781 const token_tags = tree.tokens.items(.tag);
1782 const node_tags = tree.nodes.items(.tag);
1783
1784 // We must not create any types until Sema. Here the goal is only to generate
1785 // ZIR for all the field types, alignments, and default value expressions.
1786
1787 const arg_inst: zir.Inst.Ref = if (container_decl.ast.arg != 0)
1788 try comptimeExpr(gz, scope, .none, container_decl.ast.arg)
1789 else
1790 .none;
1791
1792 switch (token_tags[container_decl.ast.main_token]) {
1793 .keyword_struct => {
1794 const tag = if (container_decl.layout_token) |t| switch (token_tags[t]) {
1795 .keyword_packed => zir.Inst.Tag.struct_decl_packed,
1796 .keyword_extern => zir.Inst.Tag.struct_decl_extern,
1797 else => unreachable,
1798 } else zir.Inst.Tag.struct_decl;
1799 if (container_decl.ast.members.len == 0) {
1800 const result = try gz.addPlNode(tag, node, zir.Inst.StructDecl{
1801 .fields_len = 0,
1802 });
1803 return rvalue(gz, scope, rl, result, node);
1804 }
1805
1806 assert(arg_inst == .none);
1807 var fields_data = ArrayListUnmanaged(u32){};
1808 defer fields_data.deinit(gpa);
1809
1810 // field_name and field_type are both mandatory
1811 try fields_data.ensureCapacity(gpa, container_decl.ast.members.len * 2);
1812
1813 // We only need this if there are greater than 16 fields.
1814 var bit_bag = ArrayListUnmanaged(u32){};
1815 defer bit_bag.deinit(gpa);
1816
1817 var cur_bit_bag: u32 = 0;
1818 var member_index: usize = 0;
1819 while (true) {
1820 const member_node = container_decl.ast.members[member_index];
1821 const member = switch (node_tags[member_node]) {
1822 .container_field_init => tree.containerFieldInit(member_node),
1823 .container_field_align => tree.containerFieldAlign(member_node),
1824 .container_field => tree.containerField(member_node),
1825 else => unreachable,
1826 };
1827 if (member.comptime_token) |comptime_token| {
1828 return mod.failTok(scope, comptime_token, "TODO implement comptime struct fields", .{});
1829 }
1830 try fields_data.ensureCapacity(gpa, fields_data.items.len + 4);
1831
1832 const field_name = try gz.identAsString(member.ast.name_token);
1833 fields_data.appendAssumeCapacity(field_name);
1834
1835 const field_type = try typeExpr(gz, scope, member.ast.type_expr);
1836 fields_data.appendAssumeCapacity(@enumToInt(field_type));
1837
1838 const have_align = member.ast.align_expr != 0;
1839 const have_value = member.ast.value_expr != 0;
1840 cur_bit_bag = (cur_bit_bag >> 2) |
1841 (@as(u32, @boolToInt(have_align)) << 30) |
1842 (@as(u32, @boolToInt(have_value)) << 31);
1843
1844 if (have_align) {
1845 const align_inst = try comptimeExpr(gz, scope, .{ .ty = .u32_type }, member.ast.align_expr);
1846 fields_data.appendAssumeCapacity(@enumToInt(align_inst));
1847 }
1848 if (have_value) {
1849 const default_inst = try comptimeExpr(gz, scope, .{ .ty = field_type }, member.ast.value_expr);
1850 fields_data.appendAssumeCapacity(@enumToInt(default_inst));
1851 }
1852
1853 member_index += 1;
1854 if (member_index < container_decl.ast.members.len) {
1855 if (member_index % 16 == 0) {
1856 try bit_bag.append(gpa, cur_bit_bag);
1857 cur_bit_bag = 0;
1858 }
1859 } else {
1860 break;
1861 }
1862 }
1863 const empty_slot_count = 16 - ((member_index - 1) % 16);
1864 cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
1865
1866 const result = try gz.addPlNode(tag, node, zir.Inst.StructDecl{
1867 .fields_len = @intCast(u32, container_decl.ast.members.len),
1868 });
1869 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
1870 bit_bag.items.len + 1 + fields_data.items.len);
1871 astgen.extra.appendSliceAssumeCapacity(bit_bag.items); // Likely empty.
1872 astgen.extra.appendAssumeCapacity(cur_bit_bag);
1873 astgen.extra.appendSliceAssumeCapacity(fields_data.items);
1874 return rvalue(gz, scope, rl, result, node);
1875 },
1876 .keyword_union => {
1877 return mod.failTok(scope, container_decl.ast.main_token, "TODO AstGen for union decl", .{});
1878 },
1879 .keyword_enum => {
1880 return mod.failTok(scope, container_decl.ast.main_token, "TODO AstGen for enum decl", .{});
1881 },
1882 .keyword_opaque => {
1883 const result = try gz.addNode(.opaque_decl, node);
1884 return rvalue(gz, scope, rl, result, node);
1885 },
1886 else => unreachable,
1887 }
1888}
1889
1890fn errorSetDecl(
1891 gz: *GenZir,
1892 scope: *Scope,
1893 rl: ResultLoc,
1894 node: ast.Node.Index,
1895) InnerError!zir.Inst.Ref {
1896 const mod = gz.astgen.mod;
1897 const tree = gz.tree();
1898 const main_tokens = tree.nodes.items(.main_token);
1899 const token_tags = tree.tokens.items(.tag);
1900 const arena = gz.astgen.arena;
1901
1902 // Count how many fields there are.
1903 const error_token = main_tokens[node];
1904 const count: usize = count: {
1905 var tok_i = error_token + 2;
1906 var count: usize = 0;
1907 while (true) : (tok_i += 1) {
1908 switch (token_tags[tok_i]) {
1909 .doc_comment, .comma => {},
1910 .identifier => count += 1,
1911 .r_brace => break :count count,
1912 else => unreachable,
1913 }
1914 } else unreachable; // TODO should not need else unreachable here
1915 };
1916
1917 const fields = try arena.alloc([]const u8, count);
1918 {
1919 var tok_i = error_token + 2;
1920 var field_i: usize = 0;
1921 while (true) : (tok_i += 1) {
1922 switch (token_tags[tok_i]) {
1923 .doc_comment, .comma => {},
1924 .identifier => {
1925 fields[field_i] = try mod.identifierTokenString(scope, tok_i);
1926 field_i += 1;
1927 },
1928 .r_brace => break,
1929 else => unreachable,
1930 }
1931 }
1932 }
1933 const error_set = try arena.create(Module.ErrorSet);
1934 error_set.* = .{
1935 .owner_decl = gz.astgen.decl,
1936 .node_offset = gz.astgen.decl.nodeIndexToRelative(node),
1937 .names_ptr = fields.ptr,
1938 .names_len = @intCast(u32, fields.len),
1939 };
1940 const error_set_ty = try Type.Tag.error_set.create(arena, error_set);
1941 const typed_value = try arena.create(TypedValue);
1942 typed_value.* = .{
1943 .ty = Type.initTag(.type),
1944 .val = try Value.Tag.ty.create(arena, error_set_ty),
1945 };
1946 const result = try gz.addConst(typed_value);
1947 return rvalue(gz, scope, rl, result, node);
1948}
1949
1950fn orelseCatchExpr(
1951 parent_gz: *GenZir,
1952 scope: *Scope,
1953 rl: ResultLoc,
1954 node: ast.Node.Index,
1955 lhs: ast.Node.Index,
1956 cond_op: zir.Inst.Tag,
1957 unwrap_op: zir.Inst.Tag,
1958 unwrap_code_op: zir.Inst.Tag,
1959 rhs: ast.Node.Index,
1960 payload_token: ?ast.TokenIndex,
1961) InnerError!zir.Inst.Ref {
1962 const mod = parent_gz.astgen.mod;
1963 const tree = parent_gz.tree();
1964
1965 var block_scope: GenZir = .{
1966 .parent = scope,
1967 .astgen = parent_gz.astgen,
1968 .force_comptime = parent_gz.force_comptime,
1969 .instructions = .{},
1970 };
1971 block_scope.setBreakResultLoc(rl);
1972 defer block_scope.instructions.deinit(mod.gpa);
1973
1974 // This could be a pointer or value depending on the `operand_rl` parameter.
1975 // We cannot use `block_scope.break_result_loc` because that has the bare
1976 // type, whereas this expression has the optional type. Later we make
1977 // up for this fact by calling rvalue on the else branch.
1978 block_scope.break_count += 1;
1979
1980 // TODO handle catch
1981 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
1982 .ref => .ref,
1983 .discard, .none, .block_ptr, .inferred_ptr => .none,
1984 .ty => |elem_ty| blk: {
1985 const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node);
1986 break :blk .{ .ty = wrapped_ty };
1987 },
1988 .ptr => |ptr_ty| blk: {
1989 const wrapped_ty = try block_scope.addUnNode(.optional_type_from_ptr_elem, ptr_ty, node);
1990 break :blk .{ .ty = wrapped_ty };
1991 },
1992 };
1993 const operand = try expr(&block_scope, &block_scope.base, operand_rl, lhs);
1994 const cond = try block_scope.addUnNode(cond_op, operand, node);
1995 const condbr = try block_scope.addCondBr(.condbr, node);
1996
1997 const block = try parent_gz.addBlock(.block, node);
1998 try parent_gz.instructions.append(mod.gpa, block);
1999 try block_scope.setBlockBody(block);
2000
2001 var then_scope: GenZir = .{
2002 .parent = scope,
2003 .astgen = parent_gz.astgen,
2004 .force_comptime = block_scope.force_comptime,
2005 .instructions = .{},
2006 };
2007 defer then_scope.instructions.deinit(mod.gpa);
2008
2009 var err_val_scope: Scope.LocalVal = undefined;
2010 const then_sub_scope = blk: {
2011 const payload = payload_token orelse break :blk &then_scope.base;
2012 if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
2013 return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{});
2014 }
2015 const err_name = try mod.identifierTokenString(scope, payload);
2016 err_val_scope = .{
2017 .parent = &then_scope.base,
2018 .gen_zir = &then_scope,
2019 .name = err_name,
2020 .inst = try then_scope.addUnNode(unwrap_code_op, operand, node),
2021 .src = parent_gz.tokSrcLoc(payload),
2022 };
2023 break :blk &err_val_scope.base;
2024 };
2025
2026 block_scope.break_count += 1;
2027 const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, rhs);
2028 // We hold off on the break instructions as well as copying the then/else
2029 // instructions into place until we know whether to keep store_to_block_ptr
2030 // instructions or not.
2031
2032 var else_scope: GenZir = .{
2033 .parent = scope,
2034 .astgen = parent_gz.astgen,
2035 .force_comptime = block_scope.force_comptime,
2036 .instructions = .{},
2037 };
2038 defer else_scope.instructions.deinit(mod.gpa);
2039
2040 // This could be a pointer or value depending on `unwrap_op`.
2041 const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node);
2042 const else_result = switch (rl) {
2043 .ref => unwrapped_payload,
2044 else => try rvalue(&else_scope, &else_scope.base, block_scope.break_result_loc, unwrapped_payload, node),
2045 };
2046
2047 return finishThenElseBlock(
2048 parent_gz,
2049 scope,
2050 rl,
2051 node,
2052 &block_scope,
2053 &then_scope,
2054 &else_scope,
2055 condbr,
2056 cond,
2057 node,
2058 node,
2059 then_result,
2060 else_result,
2061 block,
2062 block,
2063 .@"break",
2064 );
2065}
2066
2067fn finishThenElseBlock(
2068 parent_gz: *GenZir,
2069 parent_scope: *Scope,
2070 rl: ResultLoc,
2071 node: ast.Node.Index,
2072 block_scope: *GenZir,
2073 then_scope: *GenZir,
2074 else_scope: *GenZir,
2075 condbr: zir.Inst.Index,
2076 cond: zir.Inst.Ref,
2077 then_src: ast.Node.Index,
2078 else_src: ast.Node.Index,
2079 then_result: zir.Inst.Ref,
2080 else_result: zir.Inst.Ref,
2081 main_block: zir.Inst.Index,
2082 then_break_block: zir.Inst.Index,
2083 break_tag: zir.Inst.Tag,
2084) InnerError!zir.Inst.Ref {
2085 // We now have enough information to decide whether the result instruction should
2086 // be communicated via result location pointer or break instructions.
2087 const strat = rl.strategy(block_scope);
2088 const astgen = block_scope.astgen;
2089 switch (strat.tag) {
2090 .break_void => {
2091 if (!astgen.refIsNoReturn(then_result)) {
2092 _ = try then_scope.addBreak(break_tag, then_break_block, .void_value);
2093 }
2094 const elide_else = if (else_result != .none) astgen.refIsNoReturn(else_result) else false;
2095 if (!elide_else) {
2096 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
2097 }
2098 assert(!strat.elide_store_to_block_ptr_instructions);
2099 try setCondBrPayload(condbr, cond, then_scope, else_scope);
2100 return astgen.indexToRef(main_block);
2101 },
2102 .break_operand => {
2103 if (!astgen.refIsNoReturn(then_result)) {
2104 _ = try then_scope.addBreak(break_tag, then_break_block, then_result);
2105 }
2106 if (else_result != .none) {
2107 if (!astgen.refIsNoReturn(else_result)) {
2108 _ = try else_scope.addBreak(break_tag, main_block, else_result);
2109 }
2110 } else {
2111 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
2112 }
2113 if (strat.elide_store_to_block_ptr_instructions) {
2114 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope);
2115 } else {
2116 try setCondBrPayload(condbr, cond, then_scope, else_scope);
2117 }
2118 const block_ref = astgen.indexToRef(main_block);
2119 switch (rl) {
2120 .ref => return block_ref,
2121 else => return rvalue(parent_gz, parent_scope, rl, block_ref, node),
2122 }
2123 },
2124 }
2125}
2126
2127/// Return whether the identifier names of two tokens are equal. Resolves @""
2128/// tokens without allocating.
2129/// OK in theory it could do it without allocating. This implementation
2130/// allocates when the @"" form is used.
2131fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
2132 const ident_name_1 = try mod.identifierTokenString(scope, token1);
2133 const ident_name_2 = try mod.identifierTokenString(scope, token2);
2134 return mem.eql(u8, ident_name_1, ident_name_2);
2135}
2136
2137pub fn fieldAccess(
2138 gz: *GenZir,
2139 scope: *Scope,
2140 rl: ResultLoc,
2141 node: ast.Node.Index,
2142) InnerError!zir.Inst.Ref {
2143 const astgen = gz.astgen;
2144 const mod = astgen.mod;
2145 const tree = gz.tree();
2146 const main_tokens = tree.nodes.items(.main_token);
2147 const node_datas = tree.nodes.items(.data);
2148
2149 const object_node = node_datas[node].lhs;
2150 const dot_token = main_tokens[node];
2151 const field_ident = dot_token + 1;
2152 const str_index = try gz.identAsString(field_ident);
2153 switch (rl) {
2154 .ref => return gz.addPlNode(.field_ptr, node, zir.Inst.Field{
2155 .lhs = try expr(gz, scope, .ref, object_node),
2156 .field_name_start = str_index,
2157 }),
2158 else => return rvalue(gz, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{
2159 .lhs = try expr(gz, scope, .none, object_node),
2160 .field_name_start = str_index,
2161 }), node),
2162 }
2163}
2164
2165fn arrayAccess(
2166 gz: *GenZir,
2167 scope: *Scope,
2168 rl: ResultLoc,
2169 node: ast.Node.Index,
2170) InnerError!zir.Inst.Ref {
2171 const tree = gz.tree();
2172 const main_tokens = tree.nodes.items(.main_token);
2173 const node_datas = tree.nodes.items(.data);
2174 switch (rl) {
2175 .ref => return gz.addBin(
2176 .elem_ptr,
2177 try expr(gz, scope, .ref, node_datas[node].lhs),
2178 try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
2179 ),
2180 else => return rvalue(gz, scope, rl, try gz.addBin(
2181 .elem_val,
2182 try expr(gz, scope, .none, node_datas[node].lhs),
2183 try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
2184 ), node),
2185 }
2186}
2187
2188fn simpleBinOp(
2189 gz: *GenZir,
2190 scope: *Scope,
2191 rl: ResultLoc,
2192 node: ast.Node.Index,
2193 op_inst_tag: zir.Inst.Tag,
2194) InnerError!zir.Inst.Ref {
2195 const tree = gz.tree();
2196 const node_datas = tree.nodes.items(.data);
2197
2198 const result = try gz.addPlNode(op_inst_tag, node, zir.Inst.Bin{
2199 .lhs = try expr(gz, scope, .none, node_datas[node].lhs),
2200 .rhs = try expr(gz, scope, .none, node_datas[node].rhs),
2201 });
2202 return rvalue(gz, scope, rl, result, node);
2203}
2204
2205fn simpleStrTok(
2206 gz: *GenZir,
2207 scope: *Scope,
2208 rl: ResultLoc,
2209 ident_token: ast.TokenIndex,
2210 node: ast.Node.Index,
2211 op_inst_tag: zir.Inst.Tag,
2212) InnerError!zir.Inst.Ref {
2213 const str_index = try gz.identAsString(ident_token);
2214 const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
2215 return rvalue(gz, scope, rl, result, node);
2216}
2217
2218fn boolBinOp(
2219 gz: *GenZir,
2220 scope: *Scope,
2221 rl: ResultLoc,
2222 node: ast.Node.Index,
2223 zir_tag: zir.Inst.Tag,
2224) InnerError!zir.Inst.Ref {
2225 const node_datas = gz.tree().nodes.items(.data);
2226
2227 const lhs = try expr(gz, scope, .{ .ty = .bool_type }, node_datas[node].lhs);
2228 const bool_br = try gz.addBoolBr(zir_tag, lhs);
2229
2230 var rhs_scope: GenZir = .{
2231 .parent = scope,
2232 .astgen = gz.astgen,
2233 .force_comptime = gz.force_comptime,
2234 };
2235 defer rhs_scope.instructions.deinit(gz.astgen.mod.gpa);
2236 const rhs = try expr(&rhs_scope, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs);
2237 _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
2238 try rhs_scope.setBoolBrBody(bool_br);
2239
2240 const block_ref = gz.astgen.indexToRef(bool_br);
2241 return rvalue(gz, scope, rl, block_ref, node);
2242}
2243
2244fn ifExpr(
2245 parent_gz: *GenZir,
2246 scope: *Scope,
2247 rl: ResultLoc,
2248 node: ast.Node.Index,
2249 if_full: ast.full.If,
2250) InnerError!zir.Inst.Ref {
2251 const mod = parent_gz.astgen.mod;
2252
2253 var block_scope: GenZir = .{
2254 .parent = scope,
2255 .astgen = parent_gz.astgen,
2256 .force_comptime = parent_gz.force_comptime,
2257 .instructions = .{},
2258 };
2259 block_scope.setBreakResultLoc(rl);
2260 defer block_scope.instructions.deinit(mod.gpa);
2261
2262 const cond = c: {
2263 // TODO https://github.com/ziglang/zig/issues/7929
2264 if (if_full.error_token) |error_token| {
2265 return mod.failTok(scope, error_token, "TODO implement if error union", .{});
2266 } else if (if_full.payload_token) |payload_token| {
2267 return mod.failTok(scope, payload_token, "TODO implement if optional", .{});
2268 } else {
2269 break :c try expr(&block_scope, &block_scope.base, .{ .ty = .bool_type }, if_full.ast.cond_expr);
2270 }
2271 };
2272
2273 const condbr = try block_scope.addCondBr(.condbr, node);
2274
2275 const block = try parent_gz.addBlock(.block, node);
2276 try parent_gz.instructions.append(mod.gpa, block);
2277 try block_scope.setBlockBody(block);
2278
2279 var then_scope: GenZir = .{
2280 .parent = scope,
2281 .astgen = parent_gz.astgen,
2282 .force_comptime = block_scope.force_comptime,
2283 .instructions = .{},
2284 };
2285 defer then_scope.instructions.deinit(mod.gpa);
2286
2287 // declare payload to the then_scope
2288 const then_sub_scope = &then_scope.base;
2289
2290 block_scope.break_count += 1;
2291 const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);
2292 // We hold off on the break instructions as well as copying the then/else
2293 // instructions into place until we know whether to keep store_to_block_ptr
2294 // instructions or not.
2295
2296 var else_scope: GenZir = .{
2297 .parent = scope,
2298 .astgen = parent_gz.astgen,
2299 .force_comptime = block_scope.force_comptime,
2300 .instructions = .{},
2301 };
2302 defer else_scope.instructions.deinit(mod.gpa);
2303
2304 const else_node = if_full.ast.else_expr;
2305 const else_info: struct {
2306 src: ast.Node.Index,
2307 result: zir.Inst.Ref,
2308 } = if (else_node != 0) blk: {
2309 block_scope.break_count += 1;
2310 const sub_scope = &else_scope.base;
2311 break :blk .{
2312 .src = else_node,
2313 .result = try expr(&else_scope, sub_scope, block_scope.break_result_loc, else_node),
2314 };
2315 } else .{
2316 .src = if_full.ast.then_expr,
2317 .result = .none,
2318 };
2319
2320 return finishThenElseBlock(
2321 parent_gz,
2322 scope,
2323 rl,
2324 node,
2325 &block_scope,
2326 &then_scope,
2327 &else_scope,
2328 condbr,
2329 cond,
2330 if_full.ast.then_expr,
2331 else_info.src,
2332 then_result,
2333 else_info.result,
2334 block,
2335 block,
2336 .@"break",
2337 );
2338}
2339
2340fn setCondBrPayload(
2341 condbr: zir.Inst.Index,
2342 cond: zir.Inst.Ref,
2343 then_scope: *GenZir,
2344 else_scope: *GenZir,
2345) !void {
2346 const astgen = then_scope.astgen;
2347
2348 try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len +
2349 @typeInfo(zir.Inst.CondBr).Struct.fields.len +
2350 then_scope.instructions.items.len + else_scope.instructions.items.len);
2351
2352 const zir_datas = astgen.instructions.items(.data);
2353 zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{
2354 .condition = cond,
2355 .then_body_len = @intCast(u32, then_scope.instructions.items.len),
2356 .else_body_len = @intCast(u32, else_scope.instructions.items.len),
2357 });
2358 astgen.extra.appendSliceAssumeCapacity(then_scope.instructions.items);
2359 astgen.extra.appendSliceAssumeCapacity(else_scope.instructions.items);
2360}
2361
2362/// If `elide_block_store_ptr` is set, expects to find exactly 1 .store_to_block_ptr instruction.
2363fn setCondBrPayloadElideBlockStorePtr(
2364 condbr: zir.Inst.Index,
2365 cond: zir.Inst.Ref,
2366 then_scope: *GenZir,
2367 else_scope: *GenZir,
2368) !void {
2369 const astgen = then_scope.astgen;
2370
2371 try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len +
2372 @typeInfo(zir.Inst.CondBr).Struct.fields.len +
2373 then_scope.instructions.items.len + else_scope.instructions.items.len - 2);
2374
2375 const zir_datas = astgen.instructions.items(.data);
2376 zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{
2377 .condition = cond,
2378 .then_body_len = @intCast(u32, then_scope.instructions.items.len - 1),
2379 .else_body_len = @intCast(u32, else_scope.instructions.items.len - 1),
2380 });
2381
2382 const zir_tags = astgen.instructions.items(.tag);
2383 for ([_]*GenZir{ then_scope, else_scope }) |scope| {
2384 for (scope.instructions.items) |src_inst| {
2385 if (zir_tags[src_inst] != .store_to_block_ptr) {
2386 astgen.extra.appendAssumeCapacity(src_inst);
2387 }
2388 }
2389 }
2390}
2391
2392fn whileExpr(
2393 parent_gz: *GenZir,
2394 scope: *Scope,
2395 rl: ResultLoc,
2396 node: ast.Node.Index,
2397 while_full: ast.full.While,
2398) InnerError!zir.Inst.Ref {
2399 const mod = parent_gz.astgen.mod;
2400 if (while_full.label_token) |label_token| {
2401 try checkLabelRedefinition(mod, scope, label_token);
2402 }
2403
2404 const is_inline = parent_gz.force_comptime or while_full.inline_token != null;
2405 const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
2406 const loop_block = try parent_gz.addBlock(loop_tag, node);
2407 try parent_gz.instructions.append(mod.gpa, loop_block);
2408
2409 var loop_scope: GenZir = .{
2410 .parent = scope,
2411 .astgen = parent_gz.astgen,
2412 .force_comptime = parent_gz.force_comptime,
2413 .instructions = .{},
2414 };
2415 loop_scope.setBreakResultLoc(rl);
2416 defer loop_scope.instructions.deinit(mod.gpa);
2417
2418 var continue_scope: GenZir = .{
2419 .parent = &loop_scope.base,
2420 .astgen = parent_gz.astgen,
2421 .force_comptime = loop_scope.force_comptime,
2422 .instructions = .{},
2423 };
2424 defer continue_scope.instructions.deinit(mod.gpa);
2425
2426 const cond = c: {
2427 // TODO https://github.com/ziglang/zig/issues/7929
2428 if (while_full.error_token) |error_token| {
2429 return mod.failTok(scope, error_token, "TODO implement while error union", .{});
2430 } else if (while_full.payload_token) |payload_token| {
2431 return mod.failTok(scope, payload_token, "TODO implement while optional", .{});
2432 } else {
2433 const bool_type_rl: ResultLoc = .{ .ty = .bool_type };
2434 break :c try expr(&continue_scope, &continue_scope.base, bool_type_rl, while_full.ast.cond_expr);
2435 }
2436 };
2437
2438 const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
2439 const condbr = try continue_scope.addCondBr(condbr_tag, node);
2440 const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
2441 const cond_block = try loop_scope.addBlock(block_tag, node);
2442 try loop_scope.instructions.append(mod.gpa, cond_block);
2443 try continue_scope.setBlockBody(cond_block);
2444
2445 // TODO avoid emitting the continue expr when there
2446 // are no jumps to it. This happens when the last statement of a while body is noreturn
2447 // and there are no `continue` statements.
2448 if (while_full.ast.cont_expr != 0) {
2449 _ = try expr(&loop_scope, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr);
2450 }
2451 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
2452 _ = try loop_scope.addNode(repeat_tag, node);
2453
2454 try loop_scope.setBlockBody(loop_block);
2455 loop_scope.break_block = loop_block;
2456 loop_scope.continue_block = cond_block;
2457 if (while_full.label_token) |label_token| {
2458 loop_scope.label = @as(?GenZir.Label, GenZir.Label{
2459 .token = label_token,
2460 .block_inst = loop_block,
2461 });
2462 }
2463
2464 var then_scope: GenZir = .{
2465 .parent = &continue_scope.base,
2466 .astgen = parent_gz.astgen,
2467 .force_comptime = continue_scope.force_comptime,
2468 .instructions = .{},
2469 };
2470 defer then_scope.instructions.deinit(mod.gpa);
2471
2472 const then_sub_scope = &then_scope.base;
2473
2474 loop_scope.break_count += 1;
2475 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
2476
2477 var else_scope: GenZir = .{
2478 .parent = &continue_scope.base,
2479 .astgen = parent_gz.astgen,
2480 .force_comptime = continue_scope.force_comptime,
2481 .instructions = .{},
2482 };
2483 defer else_scope.instructions.deinit(mod.gpa);
2484
2485 const else_node = while_full.ast.else_expr;
2486 const else_info: struct {
2487 src: ast.Node.Index,
2488 result: zir.Inst.Ref,
2489 } = if (else_node != 0) blk: {
2490 loop_scope.break_count += 1;
2491 const sub_scope = &else_scope.base;
2492 break :blk .{
2493 .src = else_node,
2494 .result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node),
2495 };
2496 } else .{
2497 .src = while_full.ast.then_expr,
2498 .result = .none,
2499 };
2500
2501 if (loop_scope.label) |some| {
2502 if (!some.used) {
2503 return mod.failTok(scope, some.token, "unused while loop label", .{});
2504 }
2505 }
2506 const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
2507 return finishThenElseBlock(
2508 parent_gz,
2509 scope,
2510 rl,
2511 node,
2512 &loop_scope,
2513 &then_scope,
2514 &else_scope,
2515 condbr,
2516 cond,
2517 while_full.ast.then_expr,
2518 else_info.src,
2519 then_result,
2520 else_info.result,
2521 loop_block,
2522 cond_block,
2523 break_tag,
2524 );
2525}
2526
2527fn forExpr(
2528 parent_gz: *GenZir,
2529 scope: *Scope,
2530 rl: ResultLoc,
2531 node: ast.Node.Index,
2532 for_full: ast.full.While,
2533) InnerError!zir.Inst.Ref {
2534 const mod = parent_gz.astgen.mod;
2535 if (for_full.label_token) |label_token| {
2536 try checkLabelRedefinition(mod, scope, label_token);
2537 }
2538 // Set up variables and constants.
2539 const is_inline = parent_gz.force_comptime or for_full.inline_token != null;
2540 const tree = parent_gz.tree();
2541 const token_tags = tree.tokens.items(.tag);
2542
2543 const array_ptr = try expr(parent_gz, scope, .ref, for_full.ast.cond_expr);
2544 const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
2545
2546 const index_ptr = blk: {
2547 const index_ptr = try parent_gz.addUnNode(.alloc, .usize_type, node);
2548 // initialize to zero
2549 _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
2550 break :blk index_ptr;
2551 };
2552
2553 const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
2554 const loop_block = try parent_gz.addBlock(loop_tag, node);
2555 try parent_gz.instructions.append(mod.gpa, loop_block);
2556
2557 var loop_scope: GenZir = .{
2558 .parent = scope,
2559 .astgen = parent_gz.astgen,
2560 .force_comptime = parent_gz.force_comptime,
2561 .instructions = .{},
2562 };
2563 loop_scope.setBreakResultLoc(rl);
2564 defer loop_scope.instructions.deinit(mod.gpa);
2565
2566 var cond_scope: GenZir = .{
2567 .parent = &loop_scope.base,
2568 .astgen = parent_gz.astgen,
2569 .force_comptime = loop_scope.force_comptime,
2570 .instructions = .{},
2571 };
2572 defer cond_scope.instructions.deinit(mod.gpa);
2573
2574 // check condition i < array_expr.len
2575 const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
2576 const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, zir.Inst.Bin{
2577 .lhs = index,
2578 .rhs = len,
2579 });
2580
2581 const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
2582 const condbr = try cond_scope.addCondBr(condbr_tag, node);
2583 const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
2584 const cond_block = try loop_scope.addBlock(block_tag, node);
2585 try loop_scope.instructions.append(mod.gpa, cond_block);
2586 try cond_scope.setBlockBody(cond_block);
2587
2588 // Increment the index variable.
2589 const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
2590 const index_plus_one = try loop_scope.addPlNode(.add, node, zir.Inst.Bin{
2591 .lhs = index_2,
2592 .rhs = .one_usize,
2593 });
2594 _ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
2595 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
2596 _ = try loop_scope.addNode(repeat_tag, node);
2597
2598 try loop_scope.setBlockBody(loop_block);
2599 loop_scope.break_block = loop_block;
2600 loop_scope.continue_block = cond_block;
2601 if (for_full.label_token) |label_token| {
2602 loop_scope.label = @as(?GenZir.Label, GenZir.Label{
2603 .token = label_token,
2604 .block_inst = loop_block,
2605 });
2606 }
2607
2608 var then_scope: GenZir = .{
2609 .parent = &cond_scope.base,
2610 .astgen = parent_gz.astgen,
2611 .force_comptime = cond_scope.force_comptime,
2612 .instructions = .{},
2613 };
2614 defer then_scope.instructions.deinit(mod.gpa);
2615
2616 var index_scope: Scope.LocalPtr = undefined;
2617 const then_sub_scope = blk: {
2618 const payload_token = for_full.payload_token.?;
2619 const ident = if (token_tags[payload_token] == .asterisk)
2620 payload_token + 1
2621 else
2622 payload_token;
2623 const is_ptr = ident != payload_token;
2624 const value_name = tree.tokenSlice(ident);
2625 if (!mem.eql(u8, value_name, "_")) {
2626 return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{});
2627 } else if (is_ptr) {
2628 return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{});
2629 }
2630
2631 const index_token = if (token_tags[ident + 1] == .comma)
2632 ident + 2
2633 else
2634 break :blk &then_scope.base;
2635 if (mem.eql(u8, tree.tokenSlice(index_token), "_")) {
2636 return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{});
2637 }
2638 const index_name = try mod.identifierTokenString(&then_scope.base, index_token);
2639 index_scope = .{
2640 .parent = &then_scope.base,
2641 .gen_zir = &then_scope,
2642 .name = index_name,
2643 .ptr = index_ptr,
2644 .src = parent_gz.tokSrcLoc(index_token),
2645 };
2646 break :blk &index_scope.base;
2647 };
2648
2649 loop_scope.break_count += 1;
2650 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
2651
2652 var else_scope: GenZir = .{
2653 .parent = &cond_scope.base,
2654 .astgen = parent_gz.astgen,
2655 .force_comptime = cond_scope.force_comptime,
2656 .instructions = .{},
2657 };
2658 defer else_scope.instructions.deinit(mod.gpa);
2659
2660 const else_node = for_full.ast.else_expr;
2661 const else_info: struct {
2662 src: ast.Node.Index,
2663 result: zir.Inst.Ref,
2664 } = if (else_node != 0) blk: {
2665 loop_scope.break_count += 1;
2666 const sub_scope = &else_scope.base;
2667 break :blk .{
2668 .src = else_node,
2669 .result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node),
2670 };
2671 } else .{
2672 .src = for_full.ast.then_expr,
2673 .result = .none,
2674 };
2675
2676 if (loop_scope.label) |some| {
2677 if (!some.used) {
2678 return mod.failTok(scope, some.token, "unused for loop label", .{});
2679 }
2680 }
2681 const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
2682 return finishThenElseBlock(
2683 parent_gz,
2684 scope,
2685 rl,
2686 node,
2687 &loop_scope,
2688 &then_scope,
2689 &else_scope,
2690 condbr,
2691 cond,
2692 for_full.ast.then_expr,
2693 else_info.src,
2694 then_result,
2695 else_info.result,
2696 loop_block,
2697 cond_block,
2698 break_tag,
2699 );
2700}
2701
2702fn getRangeNode(
2703 node_tags: []const ast.Node.Tag,
2704 node_datas: []const ast.Node.Data,
2705 node: ast.Node.Index,
2706) ?ast.Node.Index {
2707 switch (node_tags[node]) {
2708 .switch_range => return node,
2709 .grouped_expression => unreachable,
2710 else => return null,
2711 }
2712}
2713
2714pub const SwitchProngSrc = union(enum) {
2715 scalar: u32,
2716 multi: Multi,
2717 range: Multi,
2718
2719 pub const Multi = struct {
2720 prong: u32,
2721 item: u32,
2722 };
2723
2724 pub const RangeExpand = enum { none, first, last };
2725
2726 /// This function is intended to be called only when it is certain that we need
2727 /// the LazySrcLoc in order to emit a compile error.
2728 pub fn resolve(
2729 prong_src: SwitchProngSrc,
2730 decl: *Decl,
2731 switch_node_offset: i32,
2732 range_expand: RangeExpand,
2733 ) LazySrcLoc {
2734 @setCold(true);
2735 const switch_node = decl.relativeToNodeIndex(switch_node_offset);
2736 const tree = decl.container.file_scope.base.tree();
2737 const main_tokens = tree.nodes.items(.main_token);
2738 const node_datas = tree.nodes.items(.data);
2739 const node_tags = tree.nodes.items(.tag);
2740 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2741 const case_nodes = tree.extra_data[extra.start..extra.end];
2742
2743 var multi_i: u32 = 0;
2744 var scalar_i: u32 = 0;
2745 for (case_nodes) |case_node| {
2746 const case = switch (node_tags[case_node]) {
2747 .switch_case_one => tree.switchCaseOne(case_node),
2748 .switch_case => tree.switchCase(case_node),
2749 else => unreachable,
2750 };
2751 if (case.ast.values.len == 0)
2752 continue;
2753 if (case.ast.values.len == 1 and
2754 node_tags[case.ast.values[0]] == .identifier and
2755 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2756 {
2757 continue;
2758 }
2759 const is_multi = case.ast.values.len != 1 or
2760 getRangeNode(node_tags, node_datas, case.ast.values[0]) != null;
2761
2762 switch (prong_src) {
2763 .scalar => |i| if (!is_multi and i == scalar_i) return LazySrcLoc{
2764 .node_offset = decl.nodeIndexToRelative(case.ast.values[0]),
2765 },
2766 .multi => |s| if (is_multi and s.prong == multi_i) {
2767 var item_i: u32 = 0;
2768 for (case.ast.values) |item_node| {
2769 if (getRangeNode(node_tags, node_datas, item_node) != null)
2770 continue;
2771
2772 if (item_i == s.item) return LazySrcLoc{
2773 .node_offset = decl.nodeIndexToRelative(item_node),
2774 };
2775 item_i += 1;
2776 } else unreachable;
2777 },
2778 .range => |s| if (is_multi and s.prong == multi_i) {
2779 var range_i: u32 = 0;
2780 for (case.ast.values) |item_node| {
2781 const range = getRangeNode(node_tags, node_datas, item_node) orelse continue;
2782
2783 if (range_i == s.item) switch (range_expand) {
2784 .none => return LazySrcLoc{
2785 .node_offset = decl.nodeIndexToRelative(item_node),
2786 },
2787 .first => return LazySrcLoc{
2788 .node_offset = decl.nodeIndexToRelative(node_datas[range].lhs),
2789 },
2790 .last => return LazySrcLoc{
2791 .node_offset = decl.nodeIndexToRelative(node_datas[range].rhs),
2792 },
2793 };
2794 range_i += 1;
2795 } else unreachable;
2796 },
2797 }
2798 if (is_multi) {
2799 multi_i += 1;
2800 } else {
2801 scalar_i += 1;
2802 }
2803 } else unreachable;
2804 }
2805};
2806
2807fn switchExpr(
2808 parent_gz: *GenZir,
2809 scope: *Scope,
2810 rl: ResultLoc,
2811 switch_node: ast.Node.Index,
2812) InnerError!zir.Inst.Ref {
2813 const astgen = parent_gz.astgen;
2814 const mod = astgen.mod;
2815 const gpa = mod.gpa;
2816 const tree = parent_gz.tree();
2817 const node_datas = tree.nodes.items(.data);
2818 const node_tags = tree.nodes.items(.tag);
2819 const main_tokens = tree.nodes.items(.main_token);
2820 const token_tags = tree.tokens.items(.tag);
2821 const operand_node = node_datas[switch_node].lhs;
2822 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2823 const case_nodes = tree.extra_data[extra.start..extra.end];
2824
2825 // We perform two passes over the AST. This first pass is to collect information
2826 // for the following variables, make note of the special prong AST node index,
2827 // and bail out with a compile error if there are multiple special prongs present.
2828 var any_payload_is_ref = false;
2829 var scalar_cases_len: u32 = 0;
2830 var multi_cases_len: u32 = 0;
2831 var special_prong: zir.SpecialProng = .none;
2832 var special_node: ast.Node.Index = 0;
2833 var else_src: ?LazySrcLoc = null;
2834 var underscore_src: ?LazySrcLoc = null;
2835 for (case_nodes) |case_node| {
2836 const case = switch (node_tags[case_node]) {
2837 .switch_case_one => tree.switchCaseOne(case_node),
2838 .switch_case => tree.switchCase(case_node),
2839 else => unreachable,
2840 };
2841 if (case.payload_token) |payload_token| {
2842 if (token_tags[payload_token] == .asterisk) {
2843 any_payload_is_ref = true;
2844 }
2845 }
2846 // Check for else/`_` prong.
2847 if (case.ast.values.len == 0) {
2848 const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1);
2849 if (else_src) |src| {
2850 const msg = msg: {
2851 const msg = try mod.errMsg(
2852 scope,
2853 case_src,
2854 "multiple else prongs in switch expression",
2855 .{},
2856 );
2857 errdefer msg.destroy(gpa);
2858 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2859 break :msg msg;
2860 };
2861 return mod.failWithOwnedErrorMsg(scope, msg);
2862 } else if (underscore_src) |some_underscore| {
2863 const msg = msg: {
2864 const msg = try mod.errMsg(
2865 scope,
2866 parent_gz.nodeSrcLoc(switch_node),
2867 "else and '_' prong in switch expression",
2868 .{},
2869 );
2870 errdefer msg.destroy(gpa);
2871 try mod.errNote(scope, case_src, msg, "else prong is here", .{});
2872 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
2873 break :msg msg;
2874 };
2875 return mod.failWithOwnedErrorMsg(scope, msg);
2876 }
2877 special_node = case_node;
2878 special_prong = .@"else";
2879 else_src = case_src;
2880 continue;
2881 } else if (case.ast.values.len == 1 and
2882 node_tags[case.ast.values[0]] == .identifier and
2883 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2884 {
2885 const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1);
2886 if (underscore_src) |src| {
2887 const msg = msg: {
2888 const msg = try mod.errMsg(
2889 scope,
2890 case_src,
2891 "multiple '_' prongs in switch expression",
2892 .{},
2893 );
2894 errdefer msg.destroy(gpa);
2895 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2896 break :msg msg;
2897 };
2898 return mod.failWithOwnedErrorMsg(scope, msg);
2899 } else if (else_src) |some_else| {
2900 const msg = msg: {
2901 const msg = try mod.errMsg(
2902 scope,
2903 parent_gz.nodeSrcLoc(switch_node),
2904 "else and '_' prong in switch expression",
2905 .{},
2906 );
2907 errdefer msg.destroy(gpa);
2908 try mod.errNote(scope, some_else, msg, "else prong is here", .{});
2909 try mod.errNote(scope, case_src, msg, "'_' prong is here", .{});
2910 break :msg msg;
2911 };
2912 return mod.failWithOwnedErrorMsg(scope, msg);
2913 }
2914 special_node = case_node;
2915 special_prong = .under;
2916 underscore_src = case_src;
2917 continue;
2918 }
2919
2920 if (case.ast.values.len == 1 and
2921 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2922 {
2923 scalar_cases_len += 1;
2924 } else {
2925 multi_cases_len += 1;
2926 }
2927 }
2928
2929 const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
2930 const operand = try expr(parent_gz, scope, operand_rl, operand_node);
2931 // We need the type of the operand to use as the result location for all the prong items.
2932 const typeof_tag: zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof;
2933 const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node);
2934 const item_rl: ResultLoc = .{ .ty = operand_ty_inst };
2935
2936 // Contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti.
2937 // This is the header as well as the optional else prong body, as well as all the
2938 // scalar cases.
2939 // At the end we will memcpy this into place.
2940 var scalar_cases_payload = ArrayListUnmanaged(u32){};
2941 defer scalar_cases_payload.deinit(gpa);
2942 // Same deal, but this is only the `extra` data for the multi cases.
2943 var multi_cases_payload = ArrayListUnmanaged(u32){};
2944 defer multi_cases_payload.deinit(gpa);
2945
2946 var block_scope: GenZir = .{
2947 .parent = scope,
2948 .astgen = astgen,
2949 .force_comptime = parent_gz.force_comptime,
2950 .instructions = .{},
2951 };
2952 block_scope.setBreakResultLoc(rl);
2953 defer block_scope.instructions.deinit(gpa);
2954
2955 // This gets added to the parent block later, after the item expressions.
2956 const switch_block = try parent_gz.addBlock(undefined, switch_node);
2957
2958 // We re-use this same scope for all cases, including the special prong, if any.
2959 var case_scope: GenZir = .{
2960 .parent = &block_scope.base,
2961 .astgen = astgen,
2962 .force_comptime = parent_gz.force_comptime,
2963 .instructions = .{},
2964 };
2965 defer case_scope.instructions.deinit(gpa);
2966
2967 // Do the else/`_` first because it goes first in the payload.
2968 var capture_val_scope: Scope.LocalVal = undefined;
2969 if (special_node != 0) {
2970 const case = switch (node_tags[special_node]) {
2971 .switch_case_one => tree.switchCaseOne(special_node),
2972 .switch_case => tree.switchCase(special_node),
2973 else => unreachable,
2974 };
2975 const sub_scope = blk: {
2976 const payload_token = case.payload_token orelse break :blk &case_scope.base;
2977 const ident = if (token_tags[payload_token] == .asterisk)
2978 payload_token + 1
2979 else
2980 payload_token;
2981 const is_ptr = ident != payload_token;
2982 if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
2983 if (is_ptr) {
2984 return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{});
2985 }
2986 break :blk &case_scope.base;
2987 }
2988 const capture_tag: zir.Inst.Tag = if (is_ptr)
2989 .switch_capture_else_ref
2990 else
2991 .switch_capture_else;
2992 const capture = try case_scope.add(.{
2993 .tag = capture_tag,
2994 .data = .{ .switch_capture = .{
2995 .switch_inst = switch_block,
2996 .prong_index = undefined,
2997 } },
2998 });
2999 const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token);
3000 capture_val_scope = .{
3001 .parent = &case_scope.base,
3002 .gen_zir = &case_scope,
3003 .name = capture_name,
3004 .inst = capture,
3005 .src = parent_gz.tokSrcLoc(payload_token),
3006 };
3007 break :blk &capture_val_scope.base;
3008 };
3009 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
3010 if (!astgen.refIsNoReturn(case_result)) {
3011 block_scope.break_count += 1;
3012 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
3013 }
3014 // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`.
3015 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
3016 3 + // operand, scalar_cases_len, else body len
3017 @boolToInt(multi_cases_len != 0) +
3018 case_scope.instructions.items.len);
3019 scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
3020 scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
3021 if (multi_cases_len != 0) {
3022 scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
3023 }
3024 scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
3025 scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
3026 } else {
3027 // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`.
3028 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
3029 2 + // operand, scalar_cases_len
3030 @boolToInt(multi_cases_len != 0));
3031 scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
3032 scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
3033 if (multi_cases_len != 0) {
3034 scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
3035 }
3036 }
3037
3038 // In this pass we generate all the item and prong expressions except the special case.
3039 var multi_case_index: u32 = 0;
3040 var scalar_case_index: u32 = 0;
3041 for (case_nodes) |case_node| {
3042 if (case_node == special_node)
3043 continue;
3044 const case = switch (node_tags[case_node]) {
3045 .switch_case_one => tree.switchCaseOne(case_node),
3046 .switch_case => tree.switchCase(case_node),
3047 else => unreachable,
3048 };
3049
3050 // Reset the scope.
3051 case_scope.instructions.shrinkRetainingCapacity(0);
3052
3053 const is_multi_case = case.ast.values.len != 1 or
3054 getRangeNode(node_tags, node_datas, case.ast.values[0]) != null;
3055
3056 const sub_scope = blk: {
3057 const payload_token = case.payload_token orelse break :blk &case_scope.base;
3058 const ident = if (token_tags[payload_token] == .asterisk)
3059 payload_token + 1
3060 else
3061 payload_token;
3062 const is_ptr = ident != payload_token;
3063 if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
3064 if (is_ptr) {
3065 return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{});
3066 }
3067 break :blk &case_scope.base;
3068 }
3069 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
3070 const is_ptr_bits: u2 = @boolToInt(is_ptr);
3071 const capture_tag: zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
3072 0b00 => .switch_capture,
3073 0b01 => .switch_capture_ref,
3074 0b10 => .switch_capture_multi,
3075 0b11 => .switch_capture_multi_ref,
3076 };
3077 const capture_index = if (is_multi_case) ci: {
3078 multi_case_index += 1;
3079 break :ci multi_case_index - 1;
3080 } else ci: {
3081 scalar_case_index += 1;
3082 break :ci scalar_case_index - 1;
3083 };
3084 const capture = try case_scope.add(.{
3085 .tag = capture_tag,
3086 .data = .{ .switch_capture = .{
3087 .switch_inst = switch_block,
3088 .prong_index = capture_index,
3089 } },
3090 });
3091 const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token);
3092 capture_val_scope = .{
3093 .parent = &case_scope.base,
3094 .gen_zir = &case_scope,
3095 .name = capture_name,
3096 .inst = capture,
3097 .src = parent_gz.tokSrcLoc(payload_token),
3098 };
3099 break :blk &capture_val_scope.base;
3100 };
3101
3102 if (is_multi_case) {
3103 // items_len, ranges_len, body_len
3104 const header_index = multi_cases_payload.items.len;
3105 try multi_cases_payload.resize(gpa, multi_cases_payload.items.len + 3);
3106
3107 // items
3108 var items_len: u32 = 0;
3109 for (case.ast.values) |item_node| {
3110 if (getRangeNode(node_tags, node_datas, item_node) != null) continue;
3111 items_len += 1;
3112
3113 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
3114 try multi_cases_payload.append(gpa, @enumToInt(item_inst));
3115 }
3116
3117 // ranges
3118 var ranges_len: u32 = 0;
3119 for (case.ast.values) |item_node| {
3120 const range = getRangeNode(node_tags, node_datas, item_node) orelse continue;
3121 ranges_len += 1;
3122
3123 const first = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].lhs);
3124 const last = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].rhs);
3125 try multi_cases_payload.appendSlice(gpa, &[_]u32{
3126 @enumToInt(first), @enumToInt(last),
3127 });
3128 }
3129
3130 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
3131 if (!astgen.refIsNoReturn(case_result)) {
3132 block_scope.break_count += 1;
3133 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
3134 }
3135
3136 multi_cases_payload.items[header_index + 0] = items_len;
3137 multi_cases_payload.items[header_index + 1] = ranges_len;
3138 multi_cases_payload.items[header_index + 2] = @intCast(u32, case_scope.instructions.items.len);
3139 try multi_cases_payload.appendSlice(gpa, case_scope.instructions.items);
3140 } else {
3141 const item_node = case.ast.values[0];
3142 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
3143 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
3144 if (!astgen.refIsNoReturn(case_result)) {
3145 block_scope.break_count += 1;
3146 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
3147 }
3148 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
3149 2 + case_scope.instructions.items.len);
3150 scalar_cases_payload.appendAssumeCapacity(@enumToInt(item_inst));
3151 scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
3152 scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
3153 }
3154 }
3155 // Now that the item expressions are generated we can add this.
3156 try parent_gz.instructions.append(gpa, switch_block);
3157
3158 const ref_bit: u4 = @boolToInt(any_payload_is_ref);
3159 const multi_bit: u4 = @boolToInt(multi_cases_len != 0);
3160 const special_prong_bits: u4 = @enumToInt(special_prong);
3161 comptime {
3162 assert(@enumToInt(zir.SpecialProng.none) == 0b00);
3163 assert(@enumToInt(zir.SpecialProng.@"else") == 0b01);
3164 assert(@enumToInt(zir.SpecialProng.under) == 0b10);
3165 }
3166 const zir_tags = astgen.instructions.items(.tag);
3167 zir_tags[switch_block] = switch ((ref_bit << 3) | (special_prong_bits << 1) | multi_bit) {
3168 0b0_00_0 => .switch_block,
3169 0b0_00_1 => .switch_block_multi,
3170 0b0_01_0 => .switch_block_else,
3171 0b0_01_1 => .switch_block_else_multi,
3172 0b0_10_0 => .switch_block_under,
3173 0b0_10_1 => .switch_block_under_multi,
3174 0b1_00_0 => .switch_block_ref,
3175 0b1_00_1 => .switch_block_ref_multi,
3176 0b1_01_0 => .switch_block_ref_else,
3177 0b1_01_1 => .switch_block_ref_else_multi,
3178 0b1_10_0 => .switch_block_ref_under,
3179 0b1_10_1 => .switch_block_ref_under_multi,
3180 else => unreachable,
3181 };
3182 const payload_index = astgen.extra.items.len;
3183 const zir_datas = astgen.instructions.items(.data);
3184 zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index);
3185 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
3186 scalar_cases_payload.items.len + multi_cases_payload.items.len);
3187 const strat = rl.strategy(&block_scope);
3188 switch (strat.tag) {
3189 .break_operand => {
3190 // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus
3191 // this is always true.
3192 assert(strat.elide_store_to_block_ptr_instructions);
3193
3194 // There will necessarily be a store_to_block_ptr for
3195 // all prongs, except for prongs that ended with a noreturn instruction.
3196 // Elide all the `store_to_block_ptr` instructions.
3197
3198 // The break instructions need to have their operands coerced if the
3199 // switch's result location is a `ty`. In this case we overwrite the
3200 // `store_to_block_ptr` instruction with an `as` instruction and repurpose
3201 // it as the break operand.
3202
3203 var extra_index: usize = 0;
3204 extra_index += 2;
3205 extra_index += @boolToInt(multi_cases_len != 0);
3206 if (special_prong != .none) special_prong: {
3207 const body_len_index = extra_index;
3208 const body_len = scalar_cases_payload.items[extra_index];
3209 extra_index += 1;
3210 if (body_len < 2) {
3211 extra_index += body_len;
3212 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3213 break :special_prong;
3214 }
3215 extra_index += body_len - 2;
3216 const store_inst = scalar_cases_payload.items[extra_index];
3217 if (zir_tags[store_inst] != .store_to_block_ptr) {
3218 extra_index += 2;
3219 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3220 break :special_prong;
3221 }
3222 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
3223 if (block_scope.rl_ty_inst != .none) {
3224 extra_index += 1;
3225 const break_inst = scalar_cases_payload.items[extra_index];
3226 extra_index += 1;
3227 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3228 zir_tags[store_inst] = .as;
3229 zir_datas[store_inst].bin = .{
3230 .lhs = block_scope.rl_ty_inst,
3231 .rhs = zir_datas[break_inst].@"break".operand,
3232 };
3233 zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
3234 } else {
3235 scalar_cases_payload.items[body_len_index] -= 1;
3236 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3237 extra_index += 1;
3238 astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
3239 extra_index += 1;
3240 }
3241 } else {
3242 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3243 }
3244 var scalar_i: u32 = 0;
3245 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
3246 const start_index = extra_index;
3247 extra_index += 1;
3248 const body_len_index = extra_index;
3249 const body_len = scalar_cases_payload.items[extra_index];
3250 extra_index += 1;
3251 if (body_len < 2) {
3252 extra_index += body_len;
3253 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
3254 continue;
3255 }
3256 extra_index += body_len - 2;
3257 const store_inst = scalar_cases_payload.items[extra_index];
3258 if (zir_tags[store_inst] != .store_to_block_ptr) {
3259 extra_index += 2;
3260 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
3261 continue;
3262 }
3263 if (block_scope.rl_ty_inst != .none) {
3264 extra_index += 1;
3265 const break_inst = scalar_cases_payload.items[extra_index];
3266 extra_index += 1;
3267 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
3268 zir_tags[store_inst] = .as;
3269 zir_datas[store_inst].bin = .{
3270 .lhs = block_scope.rl_ty_inst,
3271 .rhs = zir_datas[break_inst].@"break".operand,
3272 };
3273 zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
3274 } else {
3275 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
3276 scalar_cases_payload.items[body_len_index] -= 1;
3277 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
3278 extra_index += 1;
3279 astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
3280 extra_index += 1;
3281 }
3282 }
3283 extra_index = 0;
3284 var multi_i: u32 = 0;
3285 while (multi_i < multi_cases_len) : (multi_i += 1) {
3286 const start_index = extra_index;
3287 const items_len = multi_cases_payload.items[extra_index];
3288 extra_index += 1;
3289 const ranges_len = multi_cases_payload.items[extra_index];
3290 extra_index += 1;
3291 const body_len_index = extra_index;
3292 const body_len = multi_cases_payload.items[extra_index];
3293 extra_index += 1;
3294 extra_index += items_len;
3295 extra_index += 2 * ranges_len;
3296 if (body_len < 2) {
3297 extra_index += body_len;
3298 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3299 continue;
3300 }
3301 extra_index += body_len - 2;
3302 const store_inst = multi_cases_payload.items[extra_index];
3303 if (zir_tags[store_inst] != .store_to_block_ptr) {
3304 extra_index += 2;
3305 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3306 continue;
3307 }
3308 if (block_scope.rl_ty_inst != .none) {
3309 extra_index += 1;
3310 const break_inst = multi_cases_payload.items[extra_index];
3311 extra_index += 1;
3312 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3313 zir_tags[store_inst] = .as;
3314 zir_datas[store_inst].bin = .{
3315 .lhs = block_scope.rl_ty_inst,
3316 .rhs = zir_datas[break_inst].@"break".operand,
3317 };
3318 zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
3319 } else {
3320 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
3321 multi_cases_payload.items[body_len_index] -= 1;
3322 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3323 extra_index += 1;
3324 astgen.extra.appendAssumeCapacity(multi_cases_payload.items[extra_index]);
3325 extra_index += 1;
3326 }
3327 }
3328
3329 const block_ref = astgen.indexToRef(switch_block);
3330 switch (rl) {
3331 .ref => return block_ref,
3332 else => return rvalue(parent_gz, scope, rl, block_ref, switch_node),
3333 }
3334 },
3335 .break_void => {
3336 assert(!strat.elide_store_to_block_ptr_instructions);
3337 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
3338 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
3339 // Modify all the terminating instruction tags to become `break` variants.
3340 var extra_index: usize = payload_index;
3341 extra_index += 2;
3342 extra_index += @boolToInt(multi_cases_len != 0);
3343 if (special_prong != .none) {
3344 const body_len = astgen.extra.items[extra_index];
3345 extra_index += 1;
3346 const body = astgen.extra.items[extra_index..][0..body_len];
3347 extra_index += body_len;
3348 const last = body[body.len - 1];
3349 if (zir_tags[last] == .@"break" and
3350 zir_datas[last].@"break".block_inst == switch_block)
3351 {
3352 zir_datas[last].@"break".operand = .void_value;
3353 }
3354 }
3355 var scalar_i: u32 = 0;
3356 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
3357 extra_index += 1;
3358 const body_len = astgen.extra.items[extra_index];
3359 extra_index += 1;
3360 const body = astgen.extra.items[extra_index..][0..body_len];
3361 extra_index += body_len;
3362 const last = body[body.len - 1];
3363 if (zir_tags[last] == .@"break" and
3364 zir_datas[last].@"break".block_inst == switch_block)
3365 {
3366 zir_datas[last].@"break".operand = .void_value;
3367 }
3368 }
3369 var multi_i: u32 = 0;
3370 while (multi_i < multi_cases_len) : (multi_i += 1) {
3371 const items_len = astgen.extra.items[extra_index];
3372 extra_index += 1;
3373 const ranges_len = astgen.extra.items[extra_index];
3374 extra_index += 1;
3375 const body_len = astgen.extra.items[extra_index];
3376 extra_index += 1;
3377 extra_index += items_len;
3378 extra_index += 2 * ranges_len;
3379 const body = astgen.extra.items[extra_index..][0..body_len];
3380 extra_index += body_len;
3381 const last = body[body.len - 1];
3382 if (zir_tags[last] == .@"break" and
3383 zir_datas[last].@"break".block_inst == switch_block)
3384 {
3385 zir_datas[last].@"break".operand = .void_value;
3386 }
3387 }
3388
3389 return astgen.indexToRef(switch_block);
3390 },
3391 }
3392}
3393
3394fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
3395 const tree = gz.tree();
3396 const node_datas = tree.nodes.items(.data);
3397 const main_tokens = tree.nodes.items(.main_token);
3398
3399 const operand_node = node_datas[node].lhs;
3400 const operand: zir.Inst.Ref = if (operand_node != 0) operand: {
3401 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{
3402 .ptr = try gz.addNode(.ret_ptr, node),
3403 } else .{
3404 .ty = try gz.addNode(.ret_type, node),
3405 };
3406 break :operand try expr(gz, scope, rl, operand_node);
3407 } else .void_value;
3408 _ = try gz.addUnNode(.ret_node, operand, node);
3409 return zir.Inst.Ref.unreachable_value;
3410}
3411
3412fn identifier(
3413 gz: *GenZir,
3414 scope: *Scope,
3415 rl: ResultLoc,
3416 ident: ast.Node.Index,
3417) InnerError!zir.Inst.Ref {
3418 const tracy = trace(@src());
3419 defer tracy.end();
3420
3421 const mod = gz.astgen.mod;
3422 const tree = gz.tree();
3423 const main_tokens = tree.nodes.items(.main_token);
3424
3425 const ident_token = main_tokens[ident];
3426 const ident_name = try mod.identifierTokenString(scope, ident_token);
3427 if (mem.eql(u8, ident_name, "_")) {
3428 return mod.failNode(scope, ident, "TODO implement '_' identifier", .{});
3429 }
3430
3431 if (simple_types.get(ident_name)) |zir_const_ref| {
3432 return rvalue(gz, scope, rl, zir_const_ref, ident);
3433 }
3434
3435 if (ident_name.len >= 2) integer: {
3436 const first_c = ident_name[0];
3437 if (first_c == 'i' or first_c == 'u') {
3438 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
3439 true => .signed,
3440 false => .unsigned,
3441 };
3442 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
3443 error.Overflow => return mod.failNode(
3444 scope,
3445 ident,
3446 "primitive integer type '{s}' exceeds maximum bit width of 65535",
3447 .{ident_name},
3448 ),
3449 error.InvalidCharacter => break :integer,
3450 };
3451 const result = try gz.add(.{
3452 .tag = .int_type,
3453 .data = .{ .int_type = .{
3454 .src_node = gz.astgen.decl.nodeIndexToRelative(ident),
3455 .signedness = signedness,
3456 .bit_count = bit_count,
3457 } },
3458 });
3459 return rvalue(gz, scope, rl, result, ident);
3460 }
3461 }
3462
3463 // Local variables, including function parameters.
3464 {
3465 var s = scope;
3466 while (true) switch (s.tag) {
3467 .local_val => {
3468 const local_val = s.cast(Scope.LocalVal).?;
3469 if (mem.eql(u8, local_val.name, ident_name)) {
3470 return rvalue(gz, scope, rl, local_val.inst, ident);
3471 }
3472 s = local_val.parent;
3473 },
3474 .local_ptr => {
3475 const local_ptr = s.cast(Scope.LocalPtr).?;
3476 if (mem.eql(u8, local_ptr.name, ident_name)) {
3477 if (rl == .ref) return local_ptr.ptr;
3478 const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident);
3479 return rvalue(gz, scope, rl, loaded, ident);
3480 }
3481 s = local_ptr.parent;
3482 },
3483 .gen_zir => s = s.cast(GenZir).?.parent,
3484 else => break,
3485 };
3486 }
3487
3488 const gop = try gz.astgen.decl_map.getOrPut(mod.gpa, ident_name);
3489 if (!gop.found_existing) {
3490 const decl = mod.lookupDeclName(scope, ident_name) orelse
3491 return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name});
3492 try gz.astgen.decls.append(mod.gpa, decl);
3493 }
3494 const decl_index = @intCast(u32, gop.index);
3495 switch (rl) {
3496 .ref => return gz.addDecl(.decl_ref, decl_index, ident),
3497 else => return rvalue(gz, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident),
3498 }
3499}
3500
3501fn stringLiteral(
3502 gz: *GenZir,
3503 scope: *Scope,
3504 rl: ResultLoc,
3505 node: ast.Node.Index,
3506) InnerError!zir.Inst.Ref {
3507 const tree = gz.tree();
3508 const main_tokens = tree.nodes.items(.main_token);
3509 const string_bytes = &gz.astgen.string_bytes;
3510 const str_index = string_bytes.items.len;
3511 const str_lit_token = main_tokens[node];
3512 const token_bytes = tree.tokenSlice(str_lit_token);
3513 try gz.astgen.mod.parseStrLit(scope, str_lit_token, string_bytes, token_bytes, 0);
3514 const str_len = string_bytes.items.len - str_index;
3515 const result = try gz.add(.{
3516 .tag = .str,
3517 .data = .{ .str = .{
3518 .start = @intCast(u32, str_index),
3519 .len = @intCast(u32, str_len),
3520 } },
3521 });
3522 return rvalue(gz, scope, rl, result, node);
3523}
3524
3525fn multilineStringLiteral(
3526 gz: *GenZir,
3527 scope: *Scope,
3528 rl: ResultLoc,
3529 node: ast.Node.Index,
3530) InnerError!zir.Inst.Ref {
3531 const tree = gz.tree();
3532 const node_datas = tree.nodes.items(.data);
3533 const main_tokens = tree.nodes.items(.main_token);
3534
3535 const start = node_datas[node].lhs;
3536 const end = node_datas[node].rhs;
3537
3538 const gpa = gz.astgen.mod.gpa;
3539 const string_bytes = &gz.astgen.string_bytes;
3540 const str_index = string_bytes.items.len;
3541
3542 // First line: do not append a newline.
3543 var tok_i = start;
3544 {
3545 const slice = tree.tokenSlice(tok_i);
3546 const line_bytes = slice[2 .. slice.len - 1];
3547 try string_bytes.appendSlice(gpa, line_bytes);
3548 tok_i += 1;
3549 }
3550 // Following lines: each line prepends a newline.
3551 while (tok_i <= end) : (tok_i += 1) {
3552 const slice = tree.tokenSlice(tok_i);
3553 const line_bytes = slice[2 .. slice.len - 1];
3554 try string_bytes.ensureCapacity(gpa, string_bytes.items.len + line_bytes.len + 1);
3555 string_bytes.appendAssumeCapacity('\n');
3556 string_bytes.appendSliceAssumeCapacity(line_bytes);
3557 }
3558 const result = try gz.add(.{
3559 .tag = .str,
3560 .data = .{ .str = .{
3561 .start = @intCast(u32, str_index),
3562 .len = @intCast(u32, string_bytes.items.len - str_index),
3563 } },
3564 });
3565 return rvalue(gz, scope, rl, result, node);
3566}
3567
3568fn charLiteral(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
3569 const mod = gz.astgen.mod;
3570 const tree = gz.tree();
3571 const main_tokens = tree.nodes.items(.main_token);
3572 const main_token = main_tokens[node];
3573 const slice = tree.tokenSlice(main_token);
3574
3575 var bad_index: usize = undefined;
3576 const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
3577 error.InvalidCharacter => {
3578 const bad_byte = slice[bad_index];
3579 const token_starts = tree.tokens.items(.start);
3580 const src_off = @intCast(u32, token_starts[main_token] + bad_index);
3581 return mod.failOff(scope, src_off, "invalid character: '{c}'\n", .{bad_byte});
3582 },
3583 };
3584 const result = try gz.addInt(value);
3585 return rvalue(gz, scope, rl, result, node);
3586}
3587
3588fn integerLiteral(
3589 gz: *GenZir,
3590 scope: *Scope,
3591 rl: ResultLoc,
3592 node: ast.Node.Index,
3593) InnerError!zir.Inst.Ref {
3594 const tree = gz.tree();
3595 const main_tokens = tree.nodes.items(.main_token);
3596 const int_token = main_tokens[node];
3597 const prefixed_bytes = tree.tokenSlice(int_token);
3598 if (std.fmt.parseInt(u64, prefixed_bytes, 0)) |small_int| {
3599 const result: zir.Inst.Ref = switch (small_int) {
3600 0 => .zero,
3601 1 => .one,
3602 else => try gz.addInt(small_int),
3603 };
3604 return rvalue(gz, scope, rl, result, node);
3605 } else |err| {
3606 return gz.astgen.mod.failNode(scope, node, "TODO implement int literals that don't fit in a u64", .{});
3607 }
3608}
3609
3610fn floatLiteral(
3611 gz: *GenZir,
3612 scope: *Scope,
3613 rl: ResultLoc,
3614 node: ast.Node.Index,
3615) InnerError!zir.Inst.Ref {
3616 const arena = gz.astgen.arena;
3617 const tree = gz.tree();
3618 const main_tokens = tree.nodes.items(.main_token);
3619
3620 const main_token = main_tokens[node];
3621 const bytes = tree.tokenSlice(main_token);
3622 if (bytes.len > 2 and bytes[1] == 'x') {
3623 assert(bytes[0] == '0'); // validated by tokenizer
3624 return gz.astgen.mod.failTok(scope, main_token, "TODO implement hex floats", .{});
3625 }
3626 const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
3627 error.InvalidCharacter => unreachable, // validated by tokenizer
3628 };
3629 const typed_value = try arena.create(TypedValue);
3630 typed_value.* = .{
3631 .ty = Type.initTag(.comptime_float),
3632 .val = try Value.Tag.float_128.create(arena, float_number),
3633 };
3634 const result = try gz.addConst(typed_value);
3635 return rvalue(gz, scope, rl, result, node);
3636}
3637
3638fn asmExpr(
3639 gz: *GenZir,
3640 scope: *Scope,
3641 rl: ResultLoc,
3642 node: ast.Node.Index,
3643 full: ast.full.Asm,
3644) InnerError!zir.Inst.Ref {
3645 const mod = gz.astgen.mod;
3646 const arena = gz.astgen.arena;
3647 const tree = gz.tree();
3648 const main_tokens = tree.nodes.items(.main_token);
3649 const node_datas = tree.nodes.items(.data);
3650
3651 const asm_source = try expr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);
3652
3653 if (full.outputs.len != 0) {
3654 // when implementing this be sure to add test coverage for the asm return type
3655 // not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc)
3656 return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{});
3657 }
3658
3659 const constraints = try arena.alloc(u32, full.inputs.len);
3660 const args = try arena.alloc(zir.Inst.Ref, full.inputs.len);
3661
3662 for (full.inputs) |input, i| {
3663 const constraint_token = main_tokens[input] + 2;
3664 const string_bytes = &gz.astgen.string_bytes;
3665 constraints[i] = @intCast(u32, string_bytes.items.len);
3666 const token_bytes = tree.tokenSlice(constraint_token);
3667 try mod.parseStrLit(scope, constraint_token, string_bytes, token_bytes, 0);
3668 try string_bytes.append(mod.gpa, 0);
3669
3670 args[i] = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input].lhs);
3671 }
3672
3673 const tag: zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm";
3674 const result = try gz.addPlNode(tag, node, zir.Inst.Asm{
3675 .asm_source = asm_source,
3676 .return_type = .void_type,
3677 .output = .none,
3678 .args_len = @intCast(u32, full.inputs.len),
3679 .clobbers_len = 0, // TODO implement asm clobbers
3680 });
3681
3682 try gz.astgen.extra.ensureCapacity(mod.gpa, gz.astgen.extra.items.len +
3683 args.len + constraints.len);
3684 gz.astgen.appendRefsAssumeCapacity(args);
3685 gz.astgen.extra.appendSliceAssumeCapacity(constraints);
3686
3687 return rvalue(gz, scope, rl, result, node);
3688}
3689
3690fn as(
3691 gz: *GenZir,
3692 scope: *Scope,
3693 rl: ResultLoc,
3694 node: ast.Node.Index,
3695 lhs: ast.Node.Index,
3696 rhs: ast.Node.Index,
3697) InnerError!zir.Inst.Ref {
3698 const dest_type = try typeExpr(gz, scope, lhs);
3699 switch (rl) {
3700 .none, .discard, .ref, .ty => {
3701 const result = try expr(gz, scope, .{ .ty = dest_type }, rhs);
3702 return rvalue(gz, scope, rl, result, node);
3703 },
3704
3705 .ptr => |result_ptr| {
3706 return asRlPtr(gz, scope, rl, result_ptr, rhs, dest_type);
3707 },
3708 .block_ptr => |block_scope| {
3709 return asRlPtr(gz, scope, rl, block_scope.rl_ptr, rhs, dest_type);
3710 },
3711
3712 .inferred_ptr => |result_alloc| {
3713 // TODO here we should be able to resolve the inference; we now have a type for the result.
3714 return gz.astgen.mod.failNode(scope, node, "TODO implement @as with inferred-type result location pointer", .{});
3715 },
3716 }
3717}
3718
3719fn asRlPtr(
3720 parent_gz: *GenZir,
3721 scope: *Scope,
3722 rl: ResultLoc,
3723 result_ptr: zir.Inst.Ref,
3724 operand_node: ast.Node.Index,
3725 dest_type: zir.Inst.Ref,
3726) InnerError!zir.Inst.Ref {
3727 // Detect whether this expr() call goes into rvalue() to store the result into the
3728 // result location. If it does, elide the coerce_result_ptr instruction
3729 // as well as the store instruction, instead passing the result as an rvalue.
3730 const astgen = parent_gz.astgen;
3731
3732 var as_scope: GenZir = .{
3733 .parent = scope,
3734 .astgen = astgen,
3735 .force_comptime = parent_gz.force_comptime,
3736 .instructions = .{},
3737 };
3738 defer as_scope.instructions.deinit(astgen.mod.gpa);
3739
3740 as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
3741 const result = try expr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);
3742 const parent_zir = &parent_gz.instructions;
3743 if (as_scope.rvalue_rl_count == 1) {
3744 // Busted! This expression didn't actually need a pointer.
3745 const zir_tags = astgen.instructions.items(.tag);
3746 const zir_datas = astgen.instructions.items(.data);
3747 const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2;
3748 try parent_zir.ensureCapacity(astgen.mod.gpa, expected_len);
3749 for (as_scope.instructions.items) |src_inst| {
3750 if (astgen.indexToRef(src_inst) == as_scope.rl_ptr) continue;
3751 if (zir_tags[src_inst] == .store_to_block_ptr) {
3752 if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
3753 }
3754 parent_zir.appendAssumeCapacity(src_inst);
3755 }
3756 assert(parent_zir.items.len == expected_len);
3757 const casted_result = try parent_gz.addBin(.as, dest_type, result);
3758 return rvalue(parent_gz, scope, rl, casted_result, operand_node);
3759 } else {
3760 try parent_zir.appendSlice(astgen.mod.gpa, as_scope.instructions.items);
3761 return result;
3762 }
3763}
3764
3765fn bitCast(
3766 gz: *GenZir,
3767 scope: *Scope,
3768 rl: ResultLoc,
3769 node: ast.Node.Index,
3770 lhs: ast.Node.Index,
3771 rhs: ast.Node.Index,
3772) InnerError!zir.Inst.Ref {
3773 const mod = gz.astgen.mod;
3774 const dest_type = try typeExpr(gz, scope, lhs);
3775 switch (rl) {
3776 .none, .discard, .ty => {
3777 const operand = try expr(gz, scope, .none, rhs);
3778 const result = try gz.addPlNode(.bitcast, node, zir.Inst.Bin{
3779 .lhs = dest_type,
3780 .rhs = operand,
3781 });
3782 return rvalue(gz, scope, rl, result, node);
3783 },
3784 .ref => unreachable, // `@bitCast` is not allowed as an r-value.
3785 .ptr => |result_ptr| {
3786 const casted_result_ptr = try gz.addUnNode(.bitcast_result_ptr, result_ptr, node);
3787 return expr(gz, scope, .{ .ptr = casted_result_ptr }, rhs);
3788 },
3789 .block_ptr => |block_ptr| {
3790 return mod.failNode(scope, node, "TODO implement @bitCast with result location inferred peer types", .{});
3791 },
3792 .inferred_ptr => |result_alloc| {
3793 // TODO here we should be able to resolve the inference; we now have a type for the result.
3794 return mod.failNode(scope, node, "TODO implement @bitCast with inferred-type result location pointer", .{});
3795 },
3796 }
3797}
3798
3799fn typeOf(
3800 gz: *GenZir,
3801 scope: *Scope,
3802 rl: ResultLoc,
3803 node: ast.Node.Index,
3804 params: []const ast.Node.Index,
3805) InnerError!zir.Inst.Ref {
3806 if (params.len < 1) {
3807 return gz.astgen.mod.failNode(scope, node, "expected at least 1 argument, found 0", .{});
3808 }
3809 if (params.len == 1) {
3810 const result = try gz.addUnNode(.typeof, try expr(gz, scope, .none, params[0]), node);
3811 return rvalue(gz, scope, rl, result, node);
3812 }
3813 const arena = gz.astgen.arena;
3814 var items = try arena.alloc(zir.Inst.Ref, params.len);
3815 for (params) |param, param_i| {
3816 items[param_i] = try expr(gz, scope, .none, param);
3817 }
3818
3819 const result = try gz.addPlNode(.typeof_peer, node, zir.Inst.MultiOp{
3820 .operands_len = @intCast(u32, params.len),
3821 });
3822 try gz.astgen.appendRefs(items);
3823
3824 return rvalue(gz, scope, rl, result, node);
3825}
3826
3827fn builtinCall(
3828 gz: *GenZir,
3829 scope: *Scope,
3830 rl: ResultLoc,
3831 node: ast.Node.Index,
3832 params: []const ast.Node.Index,
3833) InnerError!zir.Inst.Ref {
3834 const mod = gz.astgen.mod;
3835 const tree = gz.tree();
3836 const main_tokens = tree.nodes.items(.main_token);
3837
3838 const builtin_token = main_tokens[node];
3839 const builtin_name = tree.tokenSlice(builtin_token);
3840
3841 // We handle the different builtins manually because they have different semantics depending
3842 // on the function. For example, `@as` and others participate in result location semantics,
3843 // and `@cImport` creates a special scope that collects a .c source code text buffer.
3844 // Also, some builtins have a variable number of parameters.
3845
3846 const info = BuiltinFn.list.get(builtin_name) orelse {
3847 return mod.failNode(scope, node, "invalid builtin function: '{s}'", .{
3848 builtin_name,
3849 });
3850 };
3851 if (info.param_count) |expected| {
3852 if (expected != params.len) {
3853 const s = if (expected == 1) "" else "s";
3854 return mod.failNode(scope, node, "expected {d} parameter{s}, found {d}", .{
3855 expected, s, params.len,
3856 });
3857 }
3858 }
3859
3860 switch (info.tag) {
3861 .ptr_to_int => {
3862 const operand = try expr(gz, scope, .none, params[0]);
3863 const result = try gz.addUnNode(.ptrtoint, operand, node);
3864 return rvalue(gz, scope, rl, result, node);
3865 },
3866 .float_cast => {
3867 const dest_type = try typeExpr(gz, scope, params[0]);
3868 const rhs = try expr(gz, scope, .none, params[1]);
3869 const result = try gz.addPlNode(.floatcast, node, zir.Inst.Bin{
3870 .lhs = dest_type,
3871 .rhs = rhs,
3872 });
3873 return rvalue(gz, scope, rl, result, node);
3874 },
3875 .int_cast => {
3876 const dest_type = try typeExpr(gz, scope, params[0]);
3877 const rhs = try expr(gz, scope, .none, params[1]);
3878 const result = try gz.addPlNode(.intcast, node, zir.Inst.Bin{
3879 .lhs = dest_type,
3880 .rhs = rhs,
3881 });
3882 return rvalue(gz, scope, rl, result, node);
3883 },
3884 .breakpoint => {
3885 const result = try gz.add(.{
3886 .tag = .breakpoint,
3887 .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(node) },
3888 });
3889 return rvalue(gz, scope, rl, result, node);
3890 },
3891 .import => {
3892 const target = try expr(gz, scope, .none, params[0]);
3893 const result = try gz.addUnNode(.import, target, node);
3894 return rvalue(gz, scope, rl, result, node);
3895 },
3896 .error_to_int => {
3897 const target = try expr(gz, scope, .none, params[0]);
3898 const result = try gz.addUnNode(.error_to_int, target, node);
3899 return rvalue(gz, scope, rl, result, node);
3900 },
3901 .int_to_error => {
3902 const target = try expr(gz, scope, .{ .ty = .u16_type }, params[0]);
3903 const result = try gz.addUnNode(.int_to_error, target, node);
3904 return rvalue(gz, scope, rl, result, node);
3905 },
3906 .compile_error => {
3907 const target = try expr(gz, scope, .none, params[0]);
3908 const result = try gz.addUnNode(.compile_error, target, node);
3909 return rvalue(gz, scope, rl, result, node);
3910 },
3911 .set_eval_branch_quota => {
3912 const quota = try expr(gz, scope, .{ .ty = .u32_type }, params[0]);
3913 const result = try gz.addUnNode(.set_eval_branch_quota, quota, node);
3914 return rvalue(gz, scope, rl, result, node);
3915 },
3916 .compile_log => {
3917 const arg_refs = try mod.gpa.alloc(zir.Inst.Ref, params.len);
3918 defer mod.gpa.free(arg_refs);
3919
3920 for (params) |param, i| arg_refs[i] = try expr(gz, scope, .none, param);
3921
3922 const result = try gz.addPlNode(.compile_log, node, zir.Inst.MultiOp{
3923 .operands_len = @intCast(u32, params.len),
3924 });
3925 try gz.astgen.appendRefs(arg_refs);
3926 return rvalue(gz, scope, rl, result, node);
3927 },
3928 .field => {
3929 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
3930 if (rl == .ref) {
3931 return try gz.addPlNode(.field_ptr_named, node, zir.Inst.FieldNamed{
3932 .lhs = try expr(gz, scope, .ref, params[0]),
3933 .field_name = field_name,
3934 });
3935 }
3936 const result = try gz.addPlNode(.field_val_named, node, zir.Inst.FieldNamed{
3937 .lhs = try expr(gz, scope, .none, params[0]),
3938 .field_name = field_name,
3939 });
3940 return rvalue(gz, scope, rl, result, node);
3941 },
3942 .as => return as(gz, scope, rl, node, params[0], params[1]),
3943 .bit_cast => return bitCast(gz, scope, rl, node, params[0], params[1]),
3944 .TypeOf => return typeOf(gz, scope, rl, node, params),
3945
3946 .add_with_overflow,
3947 .align_cast,
3948 .align_of,
3949 .atomic_load,
3950 .atomic_rmw,
3951 .atomic_store,
3952 .bit_offset_of,
3953 .bool_to_int,
3954 .bit_size_of,
3955 .mul_add,
3956 .byte_swap,
3957 .bit_reverse,
3958 .byte_offset_of,
3959 .call,
3960 .c_define,
3961 .c_import,
3962 .c_include,
3963 .clz,
3964 .cmpxchg_strong,
3965 .cmpxchg_weak,
3966 .ctz,
3967 .c_undef,
3968 .div_exact,
3969 .div_floor,
3970 .div_trunc,
3971 .embed_file,
3972 .enum_to_int,
3973 .error_name,
3974 .error_return_trace,
3975 .err_set_cast,
3976 .@"export",
3977 .fence,
3978 .field_parent_ptr,
3979 .float_to_int,
3980 .has_decl,
3981 .has_field,
3982 .int_to_enum,
3983 .int_to_float,
3984 .int_to_ptr,
3985 .memcpy,
3986 .memset,
3987 .wasm_memory_size,
3988 .wasm_memory_grow,
3989 .mod,
3990 .mul_with_overflow,
3991 .panic,
3992 .pop_count,
3993 .ptr_cast,
3994 .rem,
3995 .return_address,
3996 .set_align_stack,
3997 .set_cold,
3998 .set_float_mode,
3999 .set_runtime_safety,
4000 .shl_exact,
4001 .shl_with_overflow,
4002 .shr_exact,
4003 .shuffle,
4004 .size_of,
4005 .splat,
4006 .reduce,
4007 .src,
4008 .sqrt,
4009 .sin,
4010 .cos,
4011 .exp,
4012 .exp2,
4013 .log,
4014 .log2,
4015 .log10,
4016 .fabs,
4017 .floor,
4018 .ceil,
4019 .trunc,
4020 .round,
4021 .sub_with_overflow,
4022 .tag_name,
4023 .This,
4024 .truncate,
4025 .Type,
4026 .type_info,
4027 .type_name,
4028 .union_init,
4029 => return mod.failNode(scope, node, "TODO: implement builtin function {s}", .{
4030 builtin_name,
4031 }),
4032
4033 .async_call,
4034 .frame,
4035 .Frame,
4036 .frame_address,
4037 .frame_size,
4038 => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
4039 }
4040}
4041
4042fn callExpr(
4043 gz: *GenZir,
4044 scope: *Scope,
4045 rl: ResultLoc,
4046 node: ast.Node.Index,
4047 call: ast.full.Call,
4048) InnerError!zir.Inst.Ref {
4049 const mod = gz.astgen.mod;
4050 if (call.async_token) |async_token| {
4051 return mod.failTok(scope, async_token, "async and related features are not yet supported", .{});
4052 }
4053 const lhs = try expr(gz, scope, .none, call.ast.fn_expr);
4054
4055 const args = try mod.gpa.alloc(zir.Inst.Ref, call.ast.params.len);
4056 defer mod.gpa.free(args);
4057
4058 for (call.ast.params) |param_node, i| {
4059 const param_type = try gz.add(.{
4060 .tag = .param_type,
4061 .data = .{ .param_type = .{
4062 .callee = lhs,
4063 .param_index = @intCast(u32, i),
4064 } },
4065 });
4066 args[i] = try expr(gz, scope, .{ .ty = param_type }, param_node);
4067 }
4068
4069 const modifier: std.builtin.CallOptions.Modifier = switch (call.async_token != null) {
4070 true => .async_kw,
4071 false => .auto,
4072 };
4073 const result: zir.Inst.Ref = res: {
4074 const tag: zir.Inst.Tag = switch (modifier) {
4075 .auto => switch (args.len == 0) {
4076 true => break :res try gz.addUnNode(.call_none, lhs, node),
4077 false => .call,
4078 },
4079 .async_kw => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
4080 .never_tail => unreachable,
4081 .never_inline => unreachable,
4082 .no_async => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
4083 .always_tail => unreachable,
4084 .always_inline => unreachable,
4085 .compile_time => .call_compile_time,
4086 };
4087 break :res try gz.addCall(tag, lhs, args, node);
4088 };
4089 return rvalue(gz, scope, rl, result, node); // TODO function call with result location
4090}
4091
4092pub const simple_types = std.ComptimeStringMap(zir.Inst.Ref, .{
4093 .{ "u8", .u8_type },
4094 .{ "i8", .i8_type },
4095 .{ "u16", .u16_type },
4096 .{ "i16", .i16_type },
4097 .{ "u32", .u32_type },
4098 .{ "i32", .i32_type },
4099 .{ "u64", .u64_type },
4100 .{ "i64", .i64_type },
4101 .{ "usize", .usize_type },
4102 .{ "isize", .isize_type },
4103 .{ "c_short", .c_short_type },
4104 .{ "c_ushort", .c_ushort_type },
4105 .{ "c_int", .c_int_type },
4106 .{ "c_uint", .c_uint_type },
4107 .{ "c_long", .c_long_type },
4108 .{ "c_ulong", .c_ulong_type },
4109 .{ "c_longlong", .c_longlong_type },
4110 .{ "c_ulonglong", .c_ulonglong_type },
4111 .{ "c_longdouble", .c_longdouble_type },
4112 .{ "f16", .f16_type },
4113 .{ "f32", .f32_type },
4114 .{ "f64", .f64_type },
4115 .{ "f128", .f128_type },
4116 .{ "c_void", .c_void_type },
4117 .{ "bool", .bool_type },
4118 .{ "void", .void_type },
4119 .{ "type", .type_type },
4120 .{ "anyerror", .anyerror_type },
4121 .{ "comptime_int", .comptime_int_type },
4122 .{ "comptime_float", .comptime_float_type },
4123 .{ "noreturn", .noreturn_type },
4124 .{ "null", .null_type },
4125 .{ "undefined", .undefined_type },
4126 .{ "undefined", .undef },
4127 .{ "null", .null_value },
4128 .{ "true", .bool_true },
4129 .{ "false", .bool_false },
4130});
4131
4132fn nodeMayNeedMemoryLocation(tree: *const ast.Tree, start_node: ast.Node.Index) bool {
4133 const node_tags = tree.nodes.items(.tag);
4134 const node_datas = tree.nodes.items(.data);
4135 const main_tokens = tree.nodes.items(.main_token);
4136 const token_tags = tree.tokens.items(.tag);
4137
4138 var node = start_node;
4139 while (true) {
4140 switch (node_tags[node]) {
4141 .root,
4142 .@"usingnamespace",
4143 .test_decl,
4144 .switch_case,
4145 .switch_case_one,
4146 .container_field_init,
4147 .container_field_align,
4148 .container_field,
4149 .asm_output,
4150 .asm_input,
4151 => unreachable,
4152
4153 .@"return",
4154 .@"break",
4155 .@"continue",
4156 .bit_not,
4157 .bool_not,
4158 .global_var_decl,
4159 .local_var_decl,
4160 .simple_var_decl,
4161 .aligned_var_decl,
4162 .@"defer",
4163 .@"errdefer",
4164 .address_of,
4165 .optional_type,
4166 .negation,
4167 .negation_wrap,
4168 .@"resume",
4169 .array_type,
4170 .array_type_sentinel,
4171 .ptr_type_aligned,
4172 .ptr_type_sentinel,
4173 .ptr_type,
4174 .ptr_type_bit_range,
4175 .@"suspend",
4176 .@"anytype",
4177 .fn_proto_simple,
4178 .fn_proto_multi,
4179 .fn_proto_one,
4180 .fn_proto,
4181 .fn_decl,
4182 .anyframe_type,
4183 .anyframe_literal,
4184 .integer_literal,
4185 .float_literal,
4186 .enum_literal,
4187 .string_literal,
4188 .multiline_string_literal,
4189 .char_literal,
4190 .true_literal,
4191 .false_literal,
4192 .null_literal,
4193 .undefined_literal,
4194 .unreachable_literal,
4195 .identifier,
4196 .error_set_decl,
4197 .container_decl,
4198 .container_decl_trailing,
4199 .container_decl_two,
4200 .container_decl_two_trailing,
4201 .container_decl_arg,
4202 .container_decl_arg_trailing,
4203 .tagged_union,
4204 .tagged_union_trailing,
4205 .tagged_union_two,
4206 .tagged_union_two_trailing,
4207 .tagged_union_enum_tag,
4208 .tagged_union_enum_tag_trailing,
4209 .@"asm",
4210 .asm_simple,
4211 .add,
4212 .add_wrap,
4213 .array_cat,
4214 .array_mult,
4215 .assign,
4216 .assign_bit_and,
4217 .assign_bit_or,
4218 .assign_bit_shift_left,
4219 .assign_bit_shift_right,
4220 .assign_bit_xor,
4221 .assign_div,
4222 .assign_sub,
4223 .assign_sub_wrap,
4224 .assign_mod,
4225 .assign_add,
4226 .assign_add_wrap,
4227 .assign_mul,
4228 .assign_mul_wrap,
4229 .bang_equal,
4230 .bit_and,
4231 .bit_or,
4232 .bit_shift_left,
4233 .bit_shift_right,
4234 .bit_xor,
4235 .bool_and,
4236 .bool_or,
4237 .div,
4238 .equal_equal,
4239 .error_union,
4240 .greater_or_equal,
4241 .greater_than,
4242 .less_or_equal,
4243 .less_than,
4244 .merge_error_sets,
4245 .mod,
4246 .mul,
4247 .mul_wrap,
4248 .switch_range,
4249 .field_access,
4250 .sub,
4251 .sub_wrap,
4252 .slice,
4253 .slice_open,
4254 .slice_sentinel,
4255 .deref,
4256 .array_access,
4257 .error_value,
4258 .while_simple, // This variant cannot have an else expression.
4259 .while_cont, // This variant cannot have an else expression.
4260 .for_simple, // This variant cannot have an else expression.
4261 .if_simple, // This variant cannot have an else expression.
4262 => return false,
4263
4264 // Forward the question to the LHS sub-expression.
4265 .grouped_expression,
4266 .@"try",
4267 .@"await",
4268 .@"comptime",
4269 .@"nosuspend",
4270 .unwrap_optional,
4271 => node = node_datas[node].lhs,
4272
4273 // Forward the question to the RHS sub-expression.
4274 .@"catch",
4275 .@"orelse",
4276 => node = node_datas[node].rhs,
4277
4278 // True because these are exactly the expressions we need memory locations for.
4279 .array_init_one,
4280 .array_init_one_comma,
4281 .array_init_dot_two,
4282 .array_init_dot_two_comma,
4283 .array_init_dot,
4284 .array_init_dot_comma,
4285 .array_init,
4286 .array_init_comma,
4287 .struct_init_one,
4288 .struct_init_one_comma,
4289 .struct_init_dot_two,
4290 .struct_init_dot_two_comma,
4291 .struct_init_dot,
4292 .struct_init_dot_comma,
4293 .struct_init,
4294 .struct_init_comma,
4295 => return true,
4296
4297 // True because depending on comptime conditions, sub-expressions
4298 // may be the kind that need memory locations.
4299 .@"while", // This variant always has an else expression.
4300 .@"if", // This variant always has an else expression.
4301 .@"for", // This variant always has an else expression.
4302 .@"switch",
4303 .switch_comma,
4304 .call_one,
4305 .call_one_comma,
4306 .async_call_one,
4307 .async_call_one_comma,
4308 .call,
4309 .call_comma,
4310 .async_call,
4311 .async_call_comma,
4312 => return true,
4313
4314 .block_two,
4315 .block_two_semicolon,
4316 .block,
4317 .block_semicolon,
4318 => {
4319 const lbrace = main_tokens[node];
4320 if (token_tags[lbrace - 1] == .colon) {
4321 // Labeled blocks may need a memory location to forward
4322 // to their break statements.
4323 return true;
4324 } else {
4325 return false;
4326 }
4327 },
4328
4329 .builtin_call,
4330 .builtin_call_comma,
4331 .builtin_call_two,
4332 .builtin_call_two_comma,
4333 => {
4334 const builtin_token = main_tokens[node];
4335 const builtin_name = tree.tokenSlice(builtin_token);
4336 // If the builtin is an invalid name, we don't cause an error here; instead
4337 // let it pass, and the error will be "invalid builtin function" later.
4338 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
4339 return builtin_info.needs_mem_loc;
4340 },
4341 }
4342 }
4343}
4344
4345/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
4346/// result locations must call this function on their result.
4347/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
4348/// If the `ResultLoc` is `ty`, it will coerce the result to the type.
4349fn rvalue(
4350 gz: *GenZir,
4351 scope: *Scope,
4352 rl: ResultLoc,
4353 result: zir.Inst.Ref,
4354 src_node: ast.Node.Index,
4355) InnerError!zir.Inst.Ref {
4356 switch (rl) {
4357 .none => return result,
4358 .discard => {
4359 // Emit a compile error for discarding error values.
4360 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
4361 return result;
4362 },
4363 .ref => {
4364 // We need a pointer but we have a value.
4365 const tree = gz.tree();
4366 const src_token = tree.firstToken(src_node);
4367 return gz.addUnTok(.ref, result, src_token);
4368 },
4369 .ty => |ty_inst| {
4370 // Quickly eliminate some common, unnecessary type coercion.
4371 const as_ty = @as(u64, @enumToInt(zir.Inst.Ref.type_type)) << 32;
4372 const as_comptime_int = @as(u64, @enumToInt(zir.Inst.Ref.comptime_int_type)) << 32;
4373 const as_bool = @as(u64, @enumToInt(zir.Inst.Ref.bool_type)) << 32;
4374 const as_usize = @as(u64, @enumToInt(zir.Inst.Ref.usize_type)) << 32;
4375 const as_void = @as(u64, @enumToInt(zir.Inst.Ref.void_type)) << 32;
4376 switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) {
4377 as_ty | @enumToInt(zir.Inst.Ref.u8_type),
4378 as_ty | @enumToInt(zir.Inst.Ref.i8_type),
4379 as_ty | @enumToInt(zir.Inst.Ref.u16_type),
4380 as_ty | @enumToInt(zir.Inst.Ref.i16_type),
4381 as_ty | @enumToInt(zir.Inst.Ref.u32_type),
4382 as_ty | @enumToInt(zir.Inst.Ref.i32_type),
4383 as_ty | @enumToInt(zir.Inst.Ref.u64_type),
4384 as_ty | @enumToInt(zir.Inst.Ref.i64_type),
4385 as_ty | @enumToInt(zir.Inst.Ref.usize_type),
4386 as_ty | @enumToInt(zir.Inst.Ref.isize_type),
4387 as_ty | @enumToInt(zir.Inst.Ref.c_short_type),
4388 as_ty | @enumToInt(zir.Inst.Ref.c_ushort_type),
4389 as_ty | @enumToInt(zir.Inst.Ref.c_int_type),
4390 as_ty | @enumToInt(zir.Inst.Ref.c_uint_type),
4391 as_ty | @enumToInt(zir.Inst.Ref.c_long_type),
4392 as_ty | @enumToInt(zir.Inst.Ref.c_ulong_type),
4393 as_ty | @enumToInt(zir.Inst.Ref.c_longlong_type),
4394 as_ty | @enumToInt(zir.Inst.Ref.c_ulonglong_type),
4395 as_ty | @enumToInt(zir.Inst.Ref.c_longdouble_type),
4396 as_ty | @enumToInt(zir.Inst.Ref.f16_type),
4397 as_ty | @enumToInt(zir.Inst.Ref.f32_type),
4398 as_ty | @enumToInt(zir.Inst.Ref.f64_type),
4399 as_ty | @enumToInt(zir.Inst.Ref.f128_type),
4400 as_ty | @enumToInt(zir.Inst.Ref.c_void_type),
4401 as_ty | @enumToInt(zir.Inst.Ref.bool_type),
4402 as_ty | @enumToInt(zir.Inst.Ref.void_type),
4403 as_ty | @enumToInt(zir.Inst.Ref.type_type),
4404 as_ty | @enumToInt(zir.Inst.Ref.anyerror_type),
4405 as_ty | @enumToInt(zir.Inst.Ref.comptime_int_type),
4406 as_ty | @enumToInt(zir.Inst.Ref.comptime_float_type),
4407 as_ty | @enumToInt(zir.Inst.Ref.noreturn_type),
4408 as_ty | @enumToInt(zir.Inst.Ref.null_type),
4409 as_ty | @enumToInt(zir.Inst.Ref.undefined_type),
4410 as_ty | @enumToInt(zir.Inst.Ref.fn_noreturn_no_args_type),
4411 as_ty | @enumToInt(zir.Inst.Ref.fn_void_no_args_type),
4412 as_ty | @enumToInt(zir.Inst.Ref.fn_naked_noreturn_no_args_type),
4413 as_ty | @enumToInt(zir.Inst.Ref.fn_ccc_void_no_args_type),
4414 as_ty | @enumToInt(zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
4415 as_ty | @enumToInt(zir.Inst.Ref.const_slice_u8_type),
4416 as_ty | @enumToInt(zir.Inst.Ref.enum_literal_type),
4417 as_comptime_int | @enumToInt(zir.Inst.Ref.zero),
4418 as_comptime_int | @enumToInt(zir.Inst.Ref.one),
4419 as_bool | @enumToInt(zir.Inst.Ref.bool_true),
4420 as_bool | @enumToInt(zir.Inst.Ref.bool_false),
4421 as_usize | @enumToInt(zir.Inst.Ref.zero_usize),
4422 as_usize | @enumToInt(zir.Inst.Ref.one_usize),
4423 as_void | @enumToInt(zir.Inst.Ref.void_value),
4424 => return result, // type of result is already correct
4425
4426 // Need an explicit type coercion instruction.
4427 else => return gz.addPlNode(.as_node, src_node, zir.Inst.As{
4428 .dest_type = ty_inst,
4429 .operand = result,
4430 }),
4431 }
4432 },
4433 .ptr => |ptr_inst| {
4434 _ = try gz.addPlNode(.store_node, src_node, zir.Inst.Bin{
4435 .lhs = ptr_inst,
4436 .rhs = result,
4437 });
4438 return result;
4439 },
4440 .inferred_ptr => |alloc| {
4441 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
4442 return result;
4443 },
4444 .block_ptr => |block_scope| {
4445 block_scope.rvalue_rl_count += 1;
4446 _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
4447 return result;
4448 },
4449 }
4450}
src/Compilation.zig+22-17
......@@ -259,7 +259,7 @@ pub const CObject = struct {
259259/// To support incremental compilation, errors are stored in various places
260260/// so that they can be created and destroyed appropriately. This structure
261261/// is used to collect all the errors from the various places into one
262/// convenient place for API users to consume. It is allocated into 1 heap
262/// convenient place for API users to consume. It is allocated into 1 arena
263263/// and freed all at once.
264264pub const AllErrors = struct {
265265 arena: std.heap.ArenaAllocator.State,
......@@ -267,11 +267,11 @@ pub const AllErrors = struct {
267267
268268 pub const Message = union(enum) {
269269 src: struct {
270 src_path: []const u8,
271 line: usize,
272 column: usize,
273 byte_offset: usize,
274270 msg: []const u8,
271 src_path: []const u8,
272 line: u32,
273 column: u32,
274 byte_offset: u32,
275275 notes: []Message = &.{},
276276 },
277277 plain: struct {
......@@ -316,29 +316,31 @@ pub const AllErrors = struct {
316316 const notes = try arena.allocator.alloc(Message, module_err_msg.notes.len);
317317 for (notes) |*note, i| {
318318 const module_note = module_err_msg.notes[i];
319 const source = try module_note.src_loc.file_scope.getSource(module);
320 const loc = std.zig.findLineColumn(source, module_note.src_loc.byte_offset);
321 const sub_file_path = module_note.src_loc.file_scope.sub_file_path;
319 const source = try module_note.src_loc.fileScope().getSource(module);
320 const byte_offset = try module_note.src_loc.byteOffset();
321 const loc = std.zig.findLineColumn(source, byte_offset);
322 const sub_file_path = module_note.src_loc.fileScope().sub_file_path;
322323 note.* = .{
323324 .src = .{
324325 .src_path = try arena.allocator.dupe(u8, sub_file_path),
325326 .msg = try arena.allocator.dupe(u8, module_note.msg),
326 .byte_offset = module_note.src_loc.byte_offset,
327 .line = loc.line,
328 .column = loc.column,
327 .byte_offset = byte_offset,
328 .line = @intCast(u32, loc.line),
329 .column = @intCast(u32, loc.column),
329330 },
330331 };
331332 }
332 const source = try module_err_msg.src_loc.file_scope.getSource(module);
333 const loc = std.zig.findLineColumn(source, module_err_msg.src_loc.byte_offset);
334 const sub_file_path = module_err_msg.src_loc.file_scope.sub_file_path;
333 const source = try module_err_msg.src_loc.fileScope().getSource(module);
334 const byte_offset = try module_err_msg.src_loc.byteOffset();
335 const loc = std.zig.findLineColumn(source, byte_offset);
336 const sub_file_path = module_err_msg.src_loc.fileScope().sub_file_path;
335337 try errors.append(.{
336338 .src = .{
337339 .src_path = try arena.allocator.dupe(u8, sub_file_path),
338340 .msg = try arena.allocator.dupe(u8, module_err_msg.msg),
339 .byte_offset = module_err_msg.src_loc.byte_offset,
340 .line = loc.line,
341 .column = loc.column,
341 .byte_offset = byte_offset,
342 .line = @intCast(u32, loc.line),
343 .column = @intCast(u32, loc.column),
342344 .notes = notes,
343345 },
344346 });
......@@ -939,6 +941,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
939941 };
940942
941943 const module = try arena.create(Module);
944 errdefer module.deinit();
942945 module.* = .{
943946 .gpa = gpa,
944947 .comp = comp,
......@@ -946,7 +949,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
946949 .root_scope = root_scope,
947950 .zig_cache_artifact_directory = zig_cache_artifact_directory,
948951 .emit_h = options.emit_h,
952 .error_name_list = try std.ArrayListUnmanaged([]const u8).initCapacity(gpa, 1),
949953 };
954 module.error_name_list.appendAssumeCapacity("(no error)");
950955 break :blk module;
951956 } else blk: {
952957 if (options.emit_h != null) return error.NoZigModuleForCHeader;
src/Module.zig+2171-1939
......@@ -1,31 +1,32 @@
1const Module = @This();
1//! Compilation of all Zig source code is represented by one `Module`.
2//! Each `Compilation` has exactly one or zero `Module`, depending on whether
3//! there is or is not any zig source code, respectively.
4
25const std = @import("std");
3const Compilation = @import("Compilation.zig");
46const mem = std.mem;
57const Allocator = std.mem.Allocator;
68const ArrayListUnmanaged = std.ArrayListUnmanaged;
7const Value = @import("value.zig").Value;
8const Type = @import("type.zig").Type;
9const TypedValue = @import("TypedValue.zig");
109const assert = std.debug.assert;
1110const log = std.log.scoped(.module);
1211const BigIntConst = std.math.big.int.Const;
1312const BigIntMutable = std.math.big.int.Mutable;
1413const Target = std.Target;
14const ast = std.zig.ast;
15
16const Module = @This();
17const Compilation = @import("Compilation.zig");
18const Value = @import("value.zig").Value;
19const Type = @import("type.zig").Type;
20const TypedValue = @import("TypedValue.zig");
1521const Package = @import("Package.zig");
1622const link = @import("link.zig");
1723const ir = @import("ir.zig");
1824const zir = @import("zir.zig");
19const Inst = ir.Inst;
20const Body = ir.Body;
21const ast = std.zig.ast;
2225const trace = @import("tracy.zig").trace;
23const astgen = @import("astgen.zig");
24const zir_sema = @import("zir_sema.zig");
26const AstGen = @import("AstGen.zig");
27const Sema = @import("Sema.zig");
2528const target_util = @import("target.zig");
2629
27const default_eval_branch_quota = 1000;
28
2930/// General-purpose allocator. Used for both temporary and long-term storage.
3031gpa: *Allocator,
3132comp: *Compilation,
......@@ -77,7 +78,12 @@ next_anon_name_index: usize = 0,
7778deletion_set: ArrayListUnmanaged(*Decl) = .{},
7879
7980/// Error tags and their values, tag names are duped with mod.gpa.
80global_error_set: std.StringHashMapUnmanaged(u16) = .{},
81/// Corresponds with `error_name_list`.
82global_error_set: std.StringHashMapUnmanaged(ErrorInt) = .{},
83
84/// ErrorInt -> []const u8 for fast lookups for @intToError at comptime
85/// Corresponds with `global_error_set`.
86error_name_list: ArrayListUnmanaged([]const u8) = .{},
8187
8288/// Keys are fully qualified paths
8389import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
......@@ -102,12 +108,13 @@ stage1_flags: packed struct {
102108
103109emit_h: ?Compilation.EmitLoc,
104110
105compile_log_text: std.ArrayListUnmanaged(u8) = .{},
111compile_log_text: ArrayListUnmanaged(u8) = .{},
112
113pub const ErrorInt = u32;
106114
107115pub const Export = struct {
108116 options: std.builtin.ExportOptions,
109 /// Byte offset into the file that contains the export directive.
110 src: usize,
117 src: LazySrcLoc,
111118 /// Represents the position of the export, if any, in the output file.
112119 link: link.File.Export,
113120 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
......@@ -132,11 +139,12 @@ pub const DeclPlusEmitH = struct {
132139};
133140
134141pub const Decl = struct {
135 /// This name is relative to the containing namespace of the decl. It uses a null-termination
136 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
137 /// in symbol names, because executable file formats use null-terminated strings for symbol names.
138 /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for
139 /// mapping them to an address in the output file.
142 /// This name is relative to the containing namespace of the decl. It uses
143 /// null-termination to save bytes, since there can be a lot of decls in a
144 /// compilation. The null byte is not allowed in symbol names, because
145 /// executable file formats use null-terminated strings for symbol names.
146 /// All Decls have names, even values that are not bound to a zig namespace.
147 /// This is necessary for mapping them to an address in the output file.
140148 /// Memory owned by this decl, using Module's allocator.
141149 name: [*:0]const u8,
142150 /// The direct parent container of the Decl.
......@@ -219,73 +227,102 @@ pub const Decl = struct {
219227 /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
220228 pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false);
221229
222 pub fn destroy(self: *Decl, module: *Module) void {
230 pub fn destroy(decl: *Decl, module: *Module) void {
223231 const gpa = module.gpa;
224 gpa.free(mem.spanZ(self.name));
225 if (self.typedValueManaged()) |tvm| {
232 gpa.free(mem.spanZ(decl.name));
233 if (decl.typedValueManaged()) |tvm| {
234 if (tvm.typed_value.val.castTag(.function)) |payload| {
235 const func = payload.data;
236 func.deinit(gpa);
237 }
226238 tvm.deinit(gpa);
227239 }
228 self.dependants.deinit(gpa);
229 self.dependencies.deinit(gpa);
240 decl.dependants.deinit(gpa);
241 decl.dependencies.deinit(gpa);
230242 if (module.emit_h != null) {
231 const decl_plus_emit_h = @fieldParentPtr(DeclPlusEmitH, "decl", self);
243 const decl_plus_emit_h = @fieldParentPtr(DeclPlusEmitH, "decl", decl);
232244 decl_plus_emit_h.emit_h.fwd_decl.deinit(gpa);
233245 gpa.destroy(decl_plus_emit_h);
234246 } else {
235 gpa.destroy(self);
247 gpa.destroy(decl);
236248 }
237249 }
238250
239 pub fn srcLoc(self: Decl) SrcLoc {
251 pub fn relativeToNodeIndex(decl: Decl, offset: i32) ast.Node.Index {
252 return @bitCast(ast.Node.Index, offset + @bitCast(i32, decl.srcNode()));
253 }
254
255 pub fn nodeIndexToRelative(decl: Decl, node_index: ast.Node.Index) i32 {
256 return @bitCast(i32, node_index) - @bitCast(i32, decl.srcNode());
257 }
258
259 pub fn tokSrcLoc(decl: Decl, token_index: ast.TokenIndex) LazySrcLoc {
260 return .{ .token_offset = token_index - decl.srcToken() };
261 }
262
263 pub fn nodeSrcLoc(decl: Decl, node_index: ast.Node.Index) LazySrcLoc {
264 return .{ .node_offset = decl.nodeIndexToRelative(node_index) };
265 }
266
267 pub fn srcLoc(decl: *Decl) SrcLoc {
240268 return .{
241 .byte_offset = self.src(),
242 .file_scope = self.getFileScope(),
269 .container = .{ .decl = decl },
270 .lazy = .{ .node_offset = 0 },
243271 };
244272 }
245273
246 pub fn src(self: Decl) usize {
247 const tree = &self.container.file_scope.tree;
248 const decl_node = tree.rootDecls()[self.src_index];
249 return tree.tokens.items(.start)[tree.firstToken(decl_node)];
274 pub fn srcNode(decl: Decl) u32 {
275 const tree = &decl.container.file_scope.tree;
276 return tree.rootDecls()[decl.src_index];
277 }
278
279 pub fn srcToken(decl: Decl) u32 {
280 const tree = &decl.container.file_scope.tree;
281 return tree.firstToken(decl.srcNode());
282 }
283
284 pub fn srcByteOffset(decl: Decl) u32 {
285 const tree = &decl.container.file_scope.tree;
286 return tree.tokens.items(.start)[decl.srcToken()];
250287 }
251288
252 pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
253 return self.container.fullyQualifiedNameHash(mem.spanZ(self.name));
289 pub fn fullyQualifiedNameHash(decl: Decl) Scope.NameHash {
290 return decl.container.fullyQualifiedNameHash(mem.spanZ(decl.name));
254291 }
255292
256 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
257 const tvm = self.typedValueManaged() orelse return error.AnalysisFail;
293 pub fn typedValue(decl: *Decl) error{AnalysisFail}!TypedValue {
294 const tvm = decl.typedValueManaged() orelse return error.AnalysisFail;
258295 return tvm.typed_value;
259296 }
260297
261 pub fn value(self: *Decl) error{AnalysisFail}!Value {
262 return (try self.typedValue()).val;
298 pub fn value(decl: *Decl) error{AnalysisFail}!Value {
299 return (try decl.typedValue()).val;
263300 }
264301
265 pub fn dump(self: *Decl) void {
266 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
302 pub fn dump(decl: *Decl) void {
303 const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src);
267304 std.debug.print("{s}:{d}:{d} name={s} status={s}", .{
268 self.scope.sub_file_path,
305 decl.scope.sub_file_path,
269306 loc.line + 1,
270307 loc.column + 1,
271 mem.spanZ(self.name),
272 @tagName(self.analysis),
308 mem.spanZ(decl.name),
309 @tagName(decl.analysis),
273310 });
274 if (self.typedValueManaged()) |tvm| {
311 if (decl.typedValueManaged()) |tvm| {
275312 std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
276313 }
277314 std.debug.print("\n", .{});
278315 }
279316
280 pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
281 switch (self.typed_value) {
317 pub fn typedValueManaged(decl: *Decl) ?*TypedValue.Managed {
318 switch (decl.typed_value) {
282319 .most_recent => |*x| return x,
283320 .never_succeeded => return null,
284321 }
285322 }
286323
287 pub fn getFileScope(self: Decl) *Scope.File {
288 return self.container.file_scope;
324 pub fn getFileScope(decl: Decl) *Scope.File {
325 return decl.container.file_scope;
289326 }
290327
291328 pub fn getEmitH(decl: *Decl, module: *Module) *EmitH {
......@@ -294,21 +331,51 @@ pub const Decl = struct {
294331 return &decl_plus_emit_h.emit_h;
295332 }
296333
297 fn removeDependant(self: *Decl, other: *Decl) void {
298 self.dependants.removeAssertDiscard(other);
334 fn removeDependant(decl: *Decl, other: *Decl) void {
335 decl.dependants.removeAssertDiscard(other);
299336 }
300337
301 fn removeDependency(self: *Decl, other: *Decl) void {
302 self.dependencies.removeAssertDiscard(other);
338 fn removeDependency(decl: *Decl, other: *Decl) void {
339 decl.dependencies.removeAssertDiscard(other);
303340 }
304341};
305342
306343/// This state is attached to every Decl when Module emit_h is non-null.
307344pub const EmitH = struct {
308 fwd_decl: std.ArrayListUnmanaged(u8) = .{},
345 fwd_decl: ArrayListUnmanaged(u8) = .{},
309346};
310347
311/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
348/// Represents the data that an explicit error set syntax provides.
349pub const ErrorSet = struct {
350 owner_decl: *Decl,
351 /// Offset from Decl node index, points to the error set AST node.
352 node_offset: i32,
353 names_len: u32,
354 /// The string bytes are stored in the owner Decl arena.
355 /// They are in the same order they appear in the AST.
356 names_ptr: [*]const []const u8,
357};
358
359/// Represents the data that a struct declaration provides.
360pub const Struct = struct {
361 owner_decl: *Decl,
362 /// Set of field names in declaration order.
363 fields: std.StringArrayHashMapUnmanaged(Field),
364 /// Represents the declarations inside this struct.
365 container: Scope.Container,
366
367 /// Offset from Decl node index, points to the struct AST node.
368 node_offset: i32,
369
370 pub const Field = struct {
371 ty: Type,
372 abi_align: Value,
373 /// Uses `unreachable_value` to indicate no default.
374 default_val: Value,
375 };
376};
377
378/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
312379/// Extern functions do not have this data structure; they are represented by
313380/// the `Decl` only, with a `Value` tag of `extern_fn`.
314381pub const Fn = struct {
......@@ -316,9 +383,15 @@ pub const Fn = struct {
316383 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
317384 /// Even after we finish analysis, the ZIR is kept in memory, so that
318385 /// comptime and inline function calls can happen.
319 zir: zir.Body,
386 /// Parameter names are stored here so that they may be referenced for debug info,
387 /// without having source code bytes loaded into memory.
388 /// The number of parameters is determined by referring to the type.
389 /// The first N elements of `extra` are indexes into `string_bytes` to
390 /// a null-terminated string.
391 /// This memory is managed with gpa, must be freed when the function is freed.
392 zir: zir.Code,
320393 /// undefined unless analysis state is `success`.
321 body: Body,
394 body: ir.Body,
322395 state: Analysis,
323396
324397 pub const Analysis = enum {
......@@ -336,8 +409,12 @@ pub const Fn = struct {
336409 };
337410
338411 /// For debugging purposes.
339 pub fn dump(self: *Fn, mod: Module) void {
340 zir.dumpFn(mod, self);
412 pub fn dump(func: *Fn, mod: Module) void {
413 ir.dumpFn(mod, func);
414 }
415
416 pub fn deinit(func: *Fn, gpa: *Allocator) void {
417 func.zir.deinit(gpa);
341418 }
342419};
343420
......@@ -364,103 +441,93 @@ pub const Scope = struct {
364441 }
365442
366443 /// Returns the arena Allocator associated with the Decl of the Scope.
367 pub fn arena(self: *Scope) *Allocator {
368 switch (self.tag) {
369 .block => return self.cast(Block).?.arena,
370 .gen_zir => return self.cast(GenZIR).?.arena,
371 .local_val => return self.cast(LocalVal).?.gen_zir.arena,
372 .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
373 .gen_suspend => return self.cast(GenZIR).?.arena,
374 .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.arena,
444 pub fn arena(scope: *Scope) *Allocator {
445 switch (scope.tag) {
446 .block => return scope.cast(Block).?.sema.arena,
447 .gen_zir => return scope.cast(GenZir).?.astgen.arena,
448 .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.arena,
449 .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.arena,
375450 .file => unreachable,
376451 .container => unreachable,
452 .decl_ref => unreachable,
377453 }
378454 }
379455
380 pub fn isComptime(self: *Scope) bool {
381 return self.getGenZIR().force_comptime;
382 }
383
384 pub fn ownerDecl(self: *Scope) ?*Decl {
385 return switch (self.tag) {
386 .block => self.cast(Block).?.owner_decl,
387 .gen_zir => self.cast(GenZIR).?.decl,
388 .local_val => self.cast(LocalVal).?.gen_zir.decl,
389 .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
390 .gen_suspend => return self.cast(GenZIR).?.decl,
391 .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl,
456 pub fn ownerDecl(scope: *Scope) ?*Decl {
457 return switch (scope.tag) {
458 .block => scope.cast(Block).?.sema.owner_decl,
459 .gen_zir => scope.cast(GenZir).?.astgen.decl,
460 .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl,
461 .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl,
392462 .file => null,
393463 .container => null,
464 .decl_ref => scope.cast(DeclRef).?.decl,
394465 };
395466 }
396467
397 pub fn srcDecl(self: *Scope) ?*Decl {
398 return switch (self.tag) {
399 .block => self.cast(Block).?.src_decl,
400 .gen_zir => self.cast(GenZIR).?.decl,
401 .local_val => self.cast(LocalVal).?.gen_zir.decl,
402 .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
403 .gen_suspend => return self.cast(GenZIR).?.decl,
404 .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl,
468 pub fn srcDecl(scope: *Scope) ?*Decl {
469 return switch (scope.tag) {
470 .block => scope.cast(Block).?.src_decl,
471 .gen_zir => scope.cast(GenZir).?.astgen.decl,
472 .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl,
473 .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl,
405474 .file => null,
406475 .container => null,
476 .decl_ref => scope.cast(DeclRef).?.decl,
407477 };
408478 }
409479
410480 /// Asserts the scope has a parent which is a Container and returns it.
411 pub fn namespace(self: *Scope) *Container {
412 switch (self.tag) {
413 .block => return self.cast(Block).?.owner_decl.container,
414 .gen_zir => return self.cast(GenZIR).?.decl.container,
415 .local_val => return self.cast(LocalVal).?.gen_zir.decl.container,
416 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container,
417 .file => return &self.cast(File).?.root_container,
418 .container => return self.cast(Container).?,
419 .gen_suspend => return self.cast(GenZIR).?.decl.container,
420 .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl.container,
481 pub fn namespace(scope: *Scope) *Container {
482 switch (scope.tag) {
483 .block => return scope.cast(Block).?.sema.owner_decl.container,
484 .gen_zir => return scope.cast(GenZir).?.astgen.decl.container,
485 .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.decl.container,
486 .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.decl.container,
487 .file => return &scope.cast(File).?.root_container,
488 .container => return scope.cast(Container).?,
489 .decl_ref => return scope.cast(DeclRef).?.decl.container,
421490 }
422491 }
423492
424493 /// Must generate unique bytes with no collisions with other decls.
425494 /// The point of hashing here is only to limit the number of bytes of
426495 /// the unique identifier to a fixed size (16 bytes).
427 pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
428 switch (self.tag) {
496 pub fn fullyQualifiedNameHash(scope: *Scope, name: []const u8) NameHash {
497 switch (scope.tag) {
429498 .block => unreachable,
430499 .gen_zir => unreachable,
431500 .local_val => unreachable,
432501 .local_ptr => unreachable,
433 .gen_suspend => unreachable,
434 .gen_nosuspend => unreachable,
435502 .file => unreachable,
436 .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
503 .container => return scope.cast(Container).?.fullyQualifiedNameHash(name),
504 .decl_ref => unreachable,
437505 }
438506 }
439507
440508 /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
441 pub fn tree(self: *Scope) *const ast.Tree {
442 switch (self.tag) {
443 .file => return &self.cast(File).?.tree,
444 .block => return &self.cast(Block).?.src_decl.container.file_scope.tree,
445 .gen_zir => return &self.cast(GenZIR).?.decl.container.file_scope.tree,
446 .local_val => return &self.cast(LocalVal).?.gen_zir.decl.container.file_scope.tree,
447 .local_ptr => return &self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree,
448 .container => return &self.cast(Container).?.file_scope.tree,
449 .gen_suspend => return &self.cast(GenZIR).?.decl.container.file_scope.tree,
450 .gen_nosuspend => return &self.cast(Nosuspend).?.gen_zir.decl.container.file_scope.tree,
509 pub fn tree(scope: *Scope) *const ast.Tree {
510 switch (scope.tag) {
511 .file => return &scope.cast(File).?.tree,
512 .block => return &scope.cast(Block).?.src_decl.container.file_scope.tree,
513 .gen_zir => return scope.cast(GenZir).?.tree(),
514 .local_val => return &scope.cast(LocalVal).?.gen_zir.astgen.decl.container.file_scope.tree,
515 .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.astgen.decl.container.file_scope.tree,
516 .container => return &scope.cast(Container).?.file_scope.tree,
517 .decl_ref => return &scope.cast(DeclRef).?.decl.container.file_scope.tree,
451518 }
452519 }
453520
454 /// Asserts the scope is a child of a `GenZIR` and returns it.
455 pub fn getGenZIR(self: *Scope) *GenZIR {
456 return switch (self.tag) {
521 /// Asserts the scope is a child of a `GenZir` and returns it.
522 pub fn getGenZir(scope: *Scope) *GenZir {
523 return switch (scope.tag) {
457524 .block => unreachable,
458 .gen_zir, .gen_suspend => self.cast(GenZIR).?,
459 .local_val => return self.cast(LocalVal).?.gen_zir,
460 .local_ptr => return self.cast(LocalPtr).?.gen_zir,
461 .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir,
525 .gen_zir => scope.cast(GenZir).?,
526 .local_val => return scope.cast(LocalVal).?.gen_zir,
527 .local_ptr => return scope.cast(LocalPtr).?.gen_zir,
462528 .file => unreachable,
463529 .container => unreachable,
530 .decl_ref => unreachable,
464531 };
465532 }
466533
......@@ -474,8 +541,7 @@ pub const Scope = struct {
474541 .gen_zir => unreachable,
475542 .local_val => unreachable,
476543 .local_ptr => unreachable,
477 .gen_suspend => unreachable,
478 .gen_nosuspend => unreachable,
544 .decl_ref => unreachable,
479545 }
480546 }
481547
......@@ -487,8 +553,7 @@ pub const Scope = struct {
487553 .local_val => unreachable,
488554 .local_ptr => unreachable,
489555 .block => unreachable,
490 .gen_suspend => unreachable,
491 .gen_nosuspend => unreachable,
556 .decl_ref => unreachable,
492557 }
493558 }
494559
......@@ -499,40 +564,11 @@ pub const Scope = struct {
499564 cur = switch (cur.tag) {
500565 .container => return @fieldParentPtr(Container, "base", cur).file_scope,
501566 .file => return @fieldParentPtr(File, "base", cur),
502 .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent,
567 .gen_zir => @fieldParentPtr(GenZir, "base", cur).parent,
503568 .local_val => @fieldParentPtr(LocalVal, "base", cur).parent,
504569 .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent,
505570 .block => return @fieldParentPtr(Block, "base", cur).src_decl.container.file_scope,
506 .gen_suspend => @fieldParentPtr(GenZIR, "base", cur).parent,
507 .gen_nosuspend => @fieldParentPtr(Nosuspend, "base", cur).parent,
508 };
509 }
510 }
511
512 pub fn getSuspend(base: *Scope) ?*Scope.GenZIR {
513 var cur = base;
514 while (true) {
515 cur = switch (cur.tag) {
516 .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent,
517 .local_val => @fieldParentPtr(LocalVal, "base", cur).parent,
518 .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent,
519 .gen_nosuspend => @fieldParentPtr(Nosuspend, "base", cur).parent,
520 .gen_suspend => return @fieldParentPtr(GenZIR, "base", cur),
521 else => return null,
522 };
523 }
524 }
525
526 pub fn getNosuspend(base: *Scope) ?*Scope.Nosuspend {
527 var cur = base;
528 while (true) {
529 cur = switch (cur.tag) {
530 .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent,
531 .local_val => @fieldParentPtr(LocalVal, "base", cur).parent,
532 .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent,
533 .gen_suspend => @fieldParentPtr(GenZIR, "base", cur).parent,
534 .gen_nosuspend => return @fieldParentPtr(Nosuspend, "base", cur),
535 else => return null,
571 .decl_ref => return @fieldParentPtr(DeclRef, "base", cur).decl.container.file_scope,
536572 };
537573 }
538574 }
......@@ -554,8 +590,10 @@ pub const Scope = struct {
554590 gen_zir,
555591 local_val,
556592 local_ptr,
557 gen_suspend,
558 gen_nosuspend,
593 /// Used for simple error reporting. Only contains a reference to a
594 /// `Decl` for use with `srcDecl` and `ownerDecl`.
595 /// Has no parents or children.
596 decl_ref,
559597 };
560598
561599 pub const Container = struct {
......@@ -568,19 +606,19 @@ pub const Scope = struct {
568606 decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
569607 ty: Type,
570608
571 pub fn deinit(self: *Container, gpa: *Allocator) void {
572 self.decls.deinit(gpa);
609 pub fn deinit(cont: *Container, gpa: *Allocator) void {
610 cont.decls.deinit(gpa);
573611 // TODO either Container of File should have an arena for sub_file_path and ty
574 gpa.destroy(self.ty.castTag(.empty_struct).?);
575 gpa.free(self.file_scope.sub_file_path);
576 self.* = undefined;
612 gpa.destroy(cont.ty.castTag(.empty_struct).?);
613 gpa.free(cont.file_scope.sub_file_path);
614 cont.* = undefined;
577615 }
578616
579 pub fn removeDecl(self: *Container, child: *Decl) void {
580 _ = self.decls.swapRemove(child);
617 pub fn removeDecl(cont: *Container, child: *Decl) void {
618 _ = cont.decls.swapRemove(child);
581619 }
582620
583 pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
621 pub fn fullyQualifiedNameHash(cont: *Container, name: []const u8) NameHash {
584622 // TODO container scope qualified names.
585623 return std.zig.hashSrc(name);
586624 }
......@@ -610,55 +648,55 @@ pub const Scope = struct {
610648
611649 root_container: Container,
612650
613 pub fn unload(self: *File, gpa: *Allocator) void {
614 switch (self.status) {
651 pub fn unload(file: *File, gpa: *Allocator) void {
652 switch (file.status) {
615653 .never_loaded,
616654 .unloaded_parse_failure,
617655 .unloaded_success,
618656 => {},
619657
620658 .loaded_success => {
621 self.tree.deinit(gpa);
622 self.status = .unloaded_success;
659 file.tree.deinit(gpa);
660 file.status = .unloaded_success;
623661 },
624662 }
625 switch (self.source) {
663 switch (file.source) {
626664 .bytes => |bytes| {
627665 gpa.free(bytes);
628 self.source = .{ .unloaded = {} };
666 file.source = .{ .unloaded = {} };
629667 },
630668 .unloaded => {},
631669 }
632670 }
633671
634 pub fn deinit(self: *File, gpa: *Allocator) void {
635 self.root_container.deinit(gpa);
636 self.unload(gpa);
637 self.* = undefined;
672 pub fn deinit(file: *File, gpa: *Allocator) void {
673 file.root_container.deinit(gpa);
674 file.unload(gpa);
675 file.* = undefined;
638676 }
639677
640 pub fn destroy(self: *File, gpa: *Allocator) void {
641 self.deinit(gpa);
642 gpa.destroy(self);
678 pub fn destroy(file: *File, gpa: *Allocator) void {
679 file.deinit(gpa);
680 gpa.destroy(file);
643681 }
644682
645 pub fn dumpSrc(self: *File, src: usize) void {
646 const loc = std.zig.findLineColumn(self.source.bytes, src);
647 std.debug.print("{s}:{d}:{d}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
683 pub fn dumpSrc(file: *File, src: LazySrcLoc) void {
684 const loc = std.zig.findLineColumn(file.source.bytes, src);
685 std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 });
648686 }
649687
650 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
651 switch (self.source) {
688 pub fn getSource(file: *File, module: *Module) ![:0]const u8 {
689 switch (file.source) {
652690 .unloaded => {
653 const source = try self.pkg.root_src_directory.handle.readFileAllocOptions(
691 const source = try file.pkg.root_src_directory.handle.readFileAllocOptions(
654692 module.gpa,
655 self.sub_file_path,
693 file.sub_file_path,
656694 std.math.maxInt(u32),
657695 null,
658696 1,
659697 0,
660698 );
661 self.source = .{ .bytes = source };
699 file.source = .{ .bytes = source };
662700 return source;
663701 },
664702 .bytes => |bytes| return bytes,
......@@ -666,37 +704,30 @@ pub const Scope = struct {
666704 }
667705 };
668706
669 /// This is a temporary structure, references to it are valid only
707 /// This is the context needed to semantically analyze ZIR instructions and
708 /// produce TZIR instructions.
709 /// This is a temporary structure stored on the stack; references to it are valid only
670710 /// during semantic analysis of the block.
671711 pub const Block = struct {
672712 pub const base_tag: Tag = .block;
673713
674714 base: Scope = Scope{ .tag = base_tag },
675715 parent: ?*Block,
676 /// Maps ZIR to TZIR. Shared to sub-blocks.
677 inst_table: *InstTable,
678 func: ?*Fn,
679 /// When analyzing an inline function call, owner_decl is the Decl of the caller
680 /// and src_decl is the Decl of the callee.
681 /// This Decl owns the arena memory of this Block.
682 owner_decl: *Decl,
716 /// Shared among all child blocks.
717 sema: *Sema,
683718 /// This Decl is the Decl according to the Zig source code corresponding to this Block.
719 /// This can vary during inline or comptime function calls. See `Sema.owner_decl`
720 /// for the one that will be the same for all Block instances.
684721 src_decl: *Decl,
685 instructions: ArrayListUnmanaged(*Inst),
686 /// Points to the arena allocator of the Decl.
687 arena: *Allocator,
722 instructions: ArrayListUnmanaged(*ir.Inst),
688723 label: ?Label = null,
689724 inlining: ?*Inlining,
690725 is_comptime: bool,
691 /// Shared to sub-blocks.
692 branch_quota: *u32,
693
694 pub const InstTable = std.AutoHashMap(*zir.Inst, *Inst);
695726
696727 /// This `Block` maps a block ZIR instruction to the corresponding
697728 /// TZIR instruction for break instruction analysis.
698729 pub const Label = struct {
699 zir_block: *zir.Inst.Block,
730 zir_block: zir.Inst.Index,
700731 merges: Merges,
701732 };
702733
......@@ -706,73 +737,254 @@ pub const Scope = struct {
706737 /// It is shared among all the blocks in an inline or comptime called
707738 /// function.
708739 pub const Inlining = struct {
709 /// Shared state among the entire inline/comptime call stack.
710 shared: *Shared,
711 /// We use this to count from 0 so that arg instructions know
712 /// which parameter index they are, without having to store
713 /// a parameter index with each arg instruction.
714 param_index: usize,
715 casted_args: []*Inst,
716740 merges: Merges,
717
718 pub const Shared = struct {
719 caller: ?*Fn,
720 branch_count: u32,
721 };
722741 };
723742
724743 pub const Merges = struct {
725 block_inst: *Inst.Block,
744 block_inst: *ir.Inst.Block,
726745 /// Separate array list from break_inst_list so that it can be passed directly
727746 /// to resolvePeerTypes.
728 results: ArrayListUnmanaged(*Inst),
747 results: ArrayListUnmanaged(*ir.Inst),
729748 /// Keeps track of the break instructions so that the operand can be replaced
730749 /// if we need to add type coercion at the end of block analysis.
731750 /// Same indexes, capacity, length as `results`.
732 br_list: ArrayListUnmanaged(*Inst.Br),
751 br_list: ArrayListUnmanaged(*ir.Inst.Br),
733752 };
734753
735754 /// For debugging purposes.
736 pub fn dump(self: *Block, mod: Module) void {
737 zir.dumpBlock(mod, self);
755 pub fn dump(block: *Block, mod: Module) void {
756 zir.dumpBlock(mod, block);
738757 }
739758
740759 pub fn makeSubBlock(parent: *Block) Block {
741760 return .{
742761 .parent = parent,
743 .inst_table = parent.inst_table,
744 .func = parent.func,
745 .owner_decl = parent.owner_decl,
762 .sema = parent.sema,
746763 .src_decl = parent.src_decl,
747764 .instructions = .{},
748 .arena = parent.arena,
749765 .label = null,
750766 .inlining = parent.inlining,
751767 .is_comptime = parent.is_comptime,
752 .branch_quota = parent.branch_quota,
753768 };
754769 }
770
771 pub fn wantSafety(block: *const Block) bool {
772 // TODO take into account scope's safety overrides
773 return switch (block.sema.mod.optimizeMode()) {
774 .Debug => true,
775 .ReleaseSafe => true,
776 .ReleaseFast => false,
777 .ReleaseSmall => false,
778 };
779 }
780
781 pub fn getFileScope(block: *Block) *Scope.File {
782 return block.src_decl.container.file_scope;
783 }
784
785 pub fn addNoOp(
786 block: *Scope.Block,
787 src: LazySrcLoc,
788 ty: Type,
789 comptime tag: ir.Inst.Tag,
790 ) !*ir.Inst {
791 const inst = try block.sema.arena.create(tag.Type());
792 inst.* = .{
793 .base = .{
794 .tag = tag,
795 .ty = ty,
796 .src = src,
797 },
798 };
799 try block.instructions.append(block.sema.gpa, &inst.base);
800 return &inst.base;
801 }
802
803 pub fn addUnOp(
804 block: *Scope.Block,
805 src: LazySrcLoc,
806 ty: Type,
807 tag: ir.Inst.Tag,
808 operand: *ir.Inst,
809 ) !*ir.Inst {
810 const inst = try block.sema.arena.create(ir.Inst.UnOp);
811 inst.* = .{
812 .base = .{
813 .tag = tag,
814 .ty = ty,
815 .src = src,
816 },
817 .operand = operand,
818 };
819 try block.instructions.append(block.sema.gpa, &inst.base);
820 return &inst.base;
821 }
822
823 pub fn addBinOp(
824 block: *Scope.Block,
825 src: LazySrcLoc,
826 ty: Type,
827 tag: ir.Inst.Tag,
828 lhs: *ir.Inst,
829 rhs: *ir.Inst,
830 ) !*ir.Inst {
831 const inst = try block.sema.arena.create(ir.Inst.BinOp);
832 inst.* = .{
833 .base = .{
834 .tag = tag,
835 .ty = ty,
836 .src = src,
837 },
838 .lhs = lhs,
839 .rhs = rhs,
840 };
841 try block.instructions.append(block.sema.gpa, &inst.base);
842 return &inst.base;
843 }
844
845 pub fn addBr(
846 scope_block: *Scope.Block,
847 src: LazySrcLoc,
848 target_block: *ir.Inst.Block,
849 operand: *ir.Inst,
850 ) !*ir.Inst.Br {
851 const inst = try scope_block.sema.arena.create(ir.Inst.Br);
852 inst.* = .{
853 .base = .{
854 .tag = .br,
855 .ty = Type.initTag(.noreturn),
856 .src = src,
857 },
858 .operand = operand,
859 .block = target_block,
860 };
861 try scope_block.instructions.append(scope_block.sema.gpa, &inst.base);
862 return inst;
863 }
864
865 pub fn addCondBr(
866 block: *Scope.Block,
867 src: LazySrcLoc,
868 condition: *ir.Inst,
869 then_body: ir.Body,
870 else_body: ir.Body,
871 ) !*ir.Inst {
872 const inst = try block.sema.arena.create(ir.Inst.CondBr);
873 inst.* = .{
874 .base = .{
875 .tag = .condbr,
876 .ty = Type.initTag(.noreturn),
877 .src = src,
878 },
879 .condition = condition,
880 .then_body = then_body,
881 .else_body = else_body,
882 };
883 try block.instructions.append(block.sema.gpa, &inst.base);
884 return &inst.base;
885 }
886
887 pub fn addCall(
888 block: *Scope.Block,
889 src: LazySrcLoc,
890 ty: Type,
891 func: *ir.Inst,
892 args: []const *ir.Inst,
893 ) !*ir.Inst {
894 const inst = try block.sema.arena.create(ir.Inst.Call);
895 inst.* = .{
896 .base = .{
897 .tag = .call,
898 .ty = ty,
899 .src = src,
900 },
901 .func = func,
902 .args = args,
903 };
904 try block.instructions.append(block.sema.gpa, &inst.base);
905 return &inst.base;
906 }
907
908 pub fn addSwitchBr(
909 block: *Scope.Block,
910 src: LazySrcLoc,
911 operand: *ir.Inst,
912 cases: []ir.Inst.SwitchBr.Case,
913 else_body: ir.Body,
914 ) !*ir.Inst {
915 const inst = try block.sema.arena.create(ir.Inst.SwitchBr);
916 inst.* = .{
917 .base = .{
918 .tag = .switchbr,
919 .ty = Type.initTag(.noreturn),
920 .src = src,
921 },
922 .target = operand,
923 .cases = cases,
924 .else_body = else_body,
925 };
926 try block.instructions.append(block.sema.gpa, &inst.base);
927 return &inst.base;
928 }
929
930 pub fn addDbgStmt(block: *Scope.Block, src: LazySrcLoc, abs_byte_off: u32) !*ir.Inst {
931 const inst = try block.sema.arena.create(ir.Inst.DbgStmt);
932 inst.* = .{
933 .base = .{
934 .tag = .dbg_stmt,
935 .ty = Type.initTag(.void),
936 .src = src,
937 },
938 .byte_offset = abs_byte_off,
939 };
940 try block.instructions.append(block.sema.gpa, &inst.base);
941 return &inst.base;
942 }
943
944 pub fn addStructFieldPtr(
945 block: *Scope.Block,
946 src: LazySrcLoc,
947 ty: Type,
948 struct_ptr: *ir.Inst,
949 field_index: u32,
950 ) !*ir.Inst {
951 const inst = try block.sema.arena.create(ir.Inst.StructFieldPtr);
952 inst.* = .{
953 .base = .{
954 .tag = .struct_field_ptr,
955 .ty = ty,
956 .src = src,
957 },
958 .struct_ptr = struct_ptr,
959 .field_index = field_index,
960 };
961 try block.instructions.append(block.sema.gpa, &inst.base);
962 return &inst.base;
963 }
755964 };
756965
757 /// This is a temporary structure, references to it are valid only
758 /// during semantic analysis of the decl.
759 pub const GenZIR = struct {
966 /// This is a temporary structure; references to it are valid only
967 /// while constructing a `zir.Code`.
968 pub const GenZir = struct {
760969 pub const base_tag: Tag = .gen_zir;
761970 base: Scope = Scope{ .tag = base_tag },
762 /// Parents can be: `GenZIR`, `File`
763 parent: *Scope,
764 decl: *Decl,
765 arena: *Allocator,
766971 force_comptime: bool,
767 /// The first N instructions in a function body ZIR are arg instructions.
768 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
972 /// Parents can be: `GenZir`, `File`
973 parent: *Scope,
974 /// All `GenZir` scopes for the same ZIR share this.
975 astgen: *AstGen,
976 /// Keeps track of the list of instructions in this scope only. Indexes
977 /// to instructions in `astgen`.
978 instructions: ArrayListUnmanaged(zir.Inst.Index) = .{},
769979 label: ?Label = null,
770 break_block: ?*zir.Inst.Block = null,
771 continue_block: ?*zir.Inst.Block = null,
772 /// Only valid when setBlockResultLoc is called.
773 break_result_loc: astgen.ResultLoc = undefined,
980 break_block: zir.Inst.Index = 0,
981 continue_block: zir.Inst.Index = 0,
982 /// Only valid when setBreakResultLoc is called.
983 break_result_loc: AstGen.ResultLoc = undefined,
774984 /// When a block has a pointer result location, here it is.
775 rl_ptr: ?*zir.Inst = null,
985 rl_ptr: zir.Inst.Ref = .none,
986 /// When a block has a type result location, here it is.
987 rl_ty_inst: zir.Inst.Ref = .none,
776988 /// Keeps track of how many branches of a block did not actually
777989 /// consume the result location. astgen uses this to figure out
778990 /// whether to rely on break instructions or writing to the result
......@@ -784,19 +996,466 @@ pub const Scope = struct {
784996 break_count: usize = 0,
785997 /// Tracks `break :foo bar` instructions so they can possibly be elided later if
786998 /// the labeled block ends up not needing a result location pointer.
787 labeled_breaks: std.ArrayListUnmanaged(*zir.Inst.Break) = .{},
999 labeled_breaks: ArrayListUnmanaged(zir.Inst.Index) = .{},
7881000 /// Tracks `store_to_block_ptr` instructions that correspond to break instructions
7891001 /// so they can possibly be elided later if the labeled block ends up not needing
7901002 /// a result location pointer.
791 labeled_store_to_block_ptr_list: std.ArrayListUnmanaged(*zir.Inst.BinOp) = .{},
792 /// for suspend error notes
793 src: usize = 0,
1003 labeled_store_to_block_ptr_list: ArrayListUnmanaged(zir.Inst.Index) = .{},
7941004
7951005 pub const Label = struct {
7961006 token: ast.TokenIndex,
797 block_inst: *zir.Inst.Block,
1007 block_inst: zir.Inst.Index,
7981008 used: bool = false,
7991009 };
1010
1011 /// Only valid to call on the top of the `GenZir` stack. Completes the
1012 /// `AstGen` into a `zir.Code`. Leaves the `AstGen` in an
1013 /// initialized, but empty, state.
1014 pub fn finish(gz: *GenZir) !zir.Code {
1015 const gpa = gz.astgen.mod.gpa;
1016 try gz.setBlockBody(0);
1017 return zir.Code{
1018 .instructions = gz.astgen.instructions.toOwnedSlice(),
1019 .string_bytes = gz.astgen.string_bytes.toOwnedSlice(gpa),
1020 .extra = gz.astgen.extra.toOwnedSlice(gpa),
1021 .decls = gz.astgen.decls.toOwnedSlice(gpa),
1022 };
1023 }
1024
1025 pub fn tokSrcLoc(gz: GenZir, token_index: ast.TokenIndex) LazySrcLoc {
1026 return gz.astgen.decl.tokSrcLoc(token_index);
1027 }
1028
1029 pub fn nodeSrcLoc(gz: GenZir, node_index: ast.Node.Index) LazySrcLoc {
1030 return gz.astgen.decl.nodeSrcLoc(node_index);
1031 }
1032
1033 pub fn tree(gz: *const GenZir) *const ast.Tree {
1034 return &gz.astgen.decl.container.file_scope.tree;
1035 }
1036
1037 pub fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void {
1038 // Depending on whether the result location is a pointer or value, different
1039 // ZIR needs to be generated. In the former case we rely on storing to the
1040 // pointer to communicate the result, and use breakvoid; in the latter case
1041 // the block break instructions will have the result values.
1042 // One more complication: when the result location is a pointer, we detect
1043 // the scenario where the result location is not consumed. In this case
1044 // we emit ZIR for the block break instructions to have the result values,
1045 // and then rvalue() on that to pass the value to the result location.
1046 switch (parent_rl) {
1047 .ty => |ty_inst| {
1048 gz.rl_ty_inst = ty_inst;
1049 gz.break_result_loc = parent_rl;
1050 },
1051 .discard, .none, .ptr, .ref => {
1052 gz.break_result_loc = parent_rl;
1053 },
1054
1055 .inferred_ptr => |ptr| {
1056 gz.rl_ptr = ptr;
1057 gz.break_result_loc = .{ .block_ptr = gz };
1058 },
1059
1060 .block_ptr => |parent_block_scope| {
1061 gz.rl_ty_inst = parent_block_scope.rl_ty_inst;
1062 gz.rl_ptr = parent_block_scope.rl_ptr;
1063 gz.break_result_loc = .{ .block_ptr = gz };
1064 },
1065 }
1066 }
1067
1068 pub fn setBoolBrBody(gz: GenZir, inst: zir.Inst.Index) !void {
1069 const gpa = gz.astgen.mod.gpa;
1070 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1071 @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len);
1072 const zir_datas = gz.astgen.instructions.items(.data);
1073 zir_datas[inst].bool_br.payload_index = gz.astgen.addExtraAssumeCapacity(
1074 zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) },
1075 );
1076 gz.astgen.extra.appendSliceAssumeCapacity(gz.instructions.items);
1077 }
1078
1079 pub fn setBlockBody(gz: GenZir, inst: zir.Inst.Index) !void {
1080 const gpa = gz.astgen.mod.gpa;
1081 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1082 @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len);
1083 const zir_datas = gz.astgen.instructions.items(.data);
1084 zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity(
1085 zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) },
1086 );
1087 gz.astgen.extra.appendSliceAssumeCapacity(gz.instructions.items);
1088 }
1089
1090 pub fn identAsString(gz: *GenZir, ident_token: ast.TokenIndex) !u32 {
1091 const astgen = gz.astgen;
1092 const gpa = astgen.mod.gpa;
1093 const string_bytes = &astgen.string_bytes;
1094 const str_index = @intCast(u32, string_bytes.items.len);
1095 try astgen.mod.appendIdentStr(&gz.base, ident_token, string_bytes);
1096 try string_bytes.append(gpa, 0);
1097 return str_index;
1098 }
1099
1100 pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct {
1101 src_node: ast.Node.Index,
1102 param_types: []const zir.Inst.Ref,
1103 ret_ty: zir.Inst.Ref,
1104 cc: zir.Inst.Ref,
1105 }) !zir.Inst.Ref {
1106 assert(args.src_node != 0);
1107 assert(args.ret_ty != .none);
1108 assert(args.cc != .none);
1109 const gpa = gz.astgen.mod.gpa;
1110 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1111 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1112 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1113 @typeInfo(zir.Inst.FnTypeCc).Struct.fields.len + args.param_types.len);
1114
1115 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnTypeCc{
1116 .return_type = args.ret_ty,
1117 .cc = args.cc,
1118 .param_types_len = @intCast(u32, args.param_types.len),
1119 });
1120 gz.astgen.appendRefsAssumeCapacity(args.param_types);
1121
1122 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1123 gz.astgen.instructions.appendAssumeCapacity(.{
1124 .tag = tag,
1125 .data = .{ .pl_node = .{
1126 .src_node = gz.astgen.decl.nodeIndexToRelative(args.src_node),
1127 .payload_index = payload_index,
1128 } },
1129 });
1130 gz.instructions.appendAssumeCapacity(new_index);
1131 return gz.astgen.indexToRef(new_index);
1132 }
1133
1134 pub fn addFnType(gz: *GenZir, tag: zir.Inst.Tag, args: struct {
1135 src_node: ast.Node.Index,
1136 ret_ty: zir.Inst.Ref,
1137 param_types: []const zir.Inst.Ref,
1138 }) !zir.Inst.Ref {
1139 assert(args.src_node != 0);
1140 assert(args.ret_ty != .none);
1141 const gpa = gz.astgen.mod.gpa;
1142 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1143 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1144 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1145 @typeInfo(zir.Inst.FnType).Struct.fields.len + args.param_types.len);
1146
1147 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnType{
1148 .return_type = args.ret_ty,
1149 .param_types_len = @intCast(u32, args.param_types.len),
1150 });
1151 gz.astgen.appendRefsAssumeCapacity(args.param_types);
1152
1153 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1154 gz.astgen.instructions.appendAssumeCapacity(.{
1155 .tag = tag,
1156 .data = .{ .pl_node = .{
1157 .src_node = gz.astgen.decl.nodeIndexToRelative(args.src_node),
1158 .payload_index = payload_index,
1159 } },
1160 });
1161 gz.instructions.appendAssumeCapacity(new_index);
1162 return gz.astgen.indexToRef(new_index);
1163 }
1164
1165 pub fn addCall(
1166 gz: *GenZir,
1167 tag: zir.Inst.Tag,
1168 callee: zir.Inst.Ref,
1169 args: []const zir.Inst.Ref,
1170 /// Absolute node index. This function does the conversion to offset from Decl.
1171 src_node: ast.Node.Index,
1172 ) !zir.Inst.Ref {
1173 assert(callee != .none);
1174 assert(src_node != 0);
1175 const gpa = gz.astgen.mod.gpa;
1176 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1177 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1178 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1179 @typeInfo(zir.Inst.Call).Struct.fields.len + args.len);
1180
1181 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.Call{
1182 .callee = callee,
1183 .args_len = @intCast(u32, args.len),
1184 });
1185 gz.astgen.appendRefsAssumeCapacity(args);
1186
1187 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1188 gz.astgen.instructions.appendAssumeCapacity(.{
1189 .tag = tag,
1190 .data = .{ .pl_node = .{
1191 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
1192 .payload_index = payload_index,
1193 } },
1194 });
1195 gz.instructions.appendAssumeCapacity(new_index);
1196 return gz.astgen.indexToRef(new_index);
1197 }
1198
1199 /// Note that this returns a `zir.Inst.Index` not a ref.
1200 /// Leaves the `payload_index` field undefined.
1201 pub fn addBoolBr(
1202 gz: *GenZir,
1203 tag: zir.Inst.Tag,
1204 lhs: zir.Inst.Ref,
1205 ) !zir.Inst.Index {
1206 assert(lhs != .none);
1207 const gpa = gz.astgen.mod.gpa;
1208 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1209 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1210
1211 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1212 gz.astgen.instructions.appendAssumeCapacity(.{
1213 .tag = tag,
1214 .data = .{ .bool_br = .{
1215 .lhs = lhs,
1216 .payload_index = undefined,
1217 } },
1218 });
1219 gz.instructions.appendAssumeCapacity(new_index);
1220 return new_index;
1221 }
1222
1223 pub fn addInt(gz: *GenZir, integer: u64) !zir.Inst.Ref {
1224 return gz.add(.{
1225 .tag = .int,
1226 .data = .{ .int = integer },
1227 });
1228 }
1229
1230 pub fn addUnNode(
1231 gz: *GenZir,
1232 tag: zir.Inst.Tag,
1233 operand: zir.Inst.Ref,
1234 /// Absolute node index. This function does the conversion to offset from Decl.
1235 src_node: ast.Node.Index,
1236 ) !zir.Inst.Ref {
1237 assert(operand != .none);
1238 return gz.add(.{
1239 .tag = tag,
1240 .data = .{ .un_node = .{
1241 .operand = operand,
1242 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
1243 } },
1244 });
1245 }
1246
1247 pub fn addPlNode(
1248 gz: *GenZir,
1249 tag: zir.Inst.Tag,
1250 /// Absolute node index. This function does the conversion to offset from Decl.
1251 src_node: ast.Node.Index,
1252 extra: anytype,
1253 ) !zir.Inst.Ref {
1254 const gpa = gz.astgen.mod.gpa;
1255 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1256 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1257
1258 const payload_index = try gz.astgen.addExtra(extra);
1259 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1260 gz.astgen.instructions.appendAssumeCapacity(.{
1261 .tag = tag,
1262 .data = .{ .pl_node = .{
1263 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
1264 .payload_index = payload_index,
1265 } },
1266 });
1267 gz.instructions.appendAssumeCapacity(new_index);
1268 return gz.astgen.indexToRef(new_index);
1269 }
1270
1271 pub fn addArrayTypeSentinel(
1272 gz: *GenZir,
1273 len: zir.Inst.Ref,
1274 sentinel: zir.Inst.Ref,
1275 elem_type: zir.Inst.Ref,
1276 ) !zir.Inst.Ref {
1277 const gpa = gz.astgen.mod.gpa;
1278 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1279 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1280
1281 const payload_index = try gz.astgen.addExtra(zir.Inst.ArrayTypeSentinel{
1282 .sentinel = sentinel,
1283 .elem_type = elem_type,
1284 });
1285 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1286 gz.astgen.instructions.appendAssumeCapacity(.{
1287 .tag = .array_type_sentinel,
1288 .data = .{ .array_type_sentinel = .{
1289 .len = len,
1290 .payload_index = payload_index,
1291 } },
1292 });
1293 gz.instructions.appendAssumeCapacity(new_index);
1294 return gz.astgen.indexToRef(new_index);
1295 }
1296
1297 pub fn addUnTok(
1298 gz: *GenZir,
1299 tag: zir.Inst.Tag,
1300 operand: zir.Inst.Ref,
1301 /// Absolute token index. This function does the conversion to Decl offset.
1302 abs_tok_index: ast.TokenIndex,
1303 ) !zir.Inst.Ref {
1304 assert(operand != .none);
1305 return gz.add(.{
1306 .tag = tag,
1307 .data = .{ .un_tok = .{
1308 .operand = operand,
1309 .src_tok = abs_tok_index - gz.astgen.decl.srcToken(),
1310 } },
1311 });
1312 }
1313
1314 pub fn addStrTok(
1315 gz: *GenZir,
1316 tag: zir.Inst.Tag,
1317 str_index: u32,
1318 /// Absolute token index. This function does the conversion to Decl offset.
1319 abs_tok_index: ast.TokenIndex,
1320 ) !zir.Inst.Ref {
1321 return gz.add(.{
1322 .tag = tag,
1323 .data = .{ .str_tok = .{
1324 .start = str_index,
1325 .src_tok = abs_tok_index - gz.astgen.decl.srcToken(),
1326 } },
1327 });
1328 }
1329
1330 pub fn addBreak(
1331 gz: *GenZir,
1332 tag: zir.Inst.Tag,
1333 break_block: zir.Inst.Index,
1334 operand: zir.Inst.Ref,
1335 ) !zir.Inst.Index {
1336 return gz.addAsIndex(.{
1337 .tag = tag,
1338 .data = .{ .@"break" = .{
1339 .block_inst = break_block,
1340 .operand = operand,
1341 } },
1342 });
1343 }
1344
1345 pub fn addBin(
1346 gz: *GenZir,
1347 tag: zir.Inst.Tag,
1348 lhs: zir.Inst.Ref,
1349 rhs: zir.Inst.Ref,
1350 ) !zir.Inst.Ref {
1351 assert(lhs != .none);
1352 assert(rhs != .none);
1353 return gz.add(.{
1354 .tag = tag,
1355 .data = .{ .bin = .{
1356 .lhs = lhs,
1357 .rhs = rhs,
1358 } },
1359 });
1360 }
1361
1362 pub fn addDecl(
1363 gz: *GenZir,
1364 tag: zir.Inst.Tag,
1365 decl_index: u32,
1366 src_node: ast.Node.Index,
1367 ) !zir.Inst.Ref {
1368 return gz.add(.{
1369 .tag = tag,
1370 .data = .{ .pl_node = .{
1371 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
1372 .payload_index = decl_index,
1373 } },
1374 });
1375 }
1376
1377 pub fn addNode(
1378 gz: *GenZir,
1379 tag: zir.Inst.Tag,
1380 /// Absolute node index. This function does the conversion to offset from Decl.
1381 src_node: ast.Node.Index,
1382 ) !zir.Inst.Ref {
1383 return gz.add(.{
1384 .tag = tag,
1385 .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(src_node) },
1386 });
1387 }
1388
1389 /// Asserts that `str` is 8 or fewer bytes.
1390 pub fn addSmallStr(
1391 gz: *GenZir,
1392 tag: zir.Inst.Tag,
1393 str: []const u8,
1394 ) !zir.Inst.Ref {
1395 var buf: [9]u8 = undefined;
1396 mem.copy(u8, &buf, str);
1397 buf[str.len] = 0;
1398
1399 return gz.add(.{
1400 .tag = tag,
1401 .data = .{ .small_str = .{ .bytes = buf[0..8].* } },
1402 });
1403 }
1404
1405 /// Note that this returns a `zir.Inst.Index` not a ref.
1406 /// Does *not* append the block instruction to the scope.
1407 /// Leaves the `payload_index` field undefined.
1408 pub fn addBlock(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index {
1409 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1410 const gpa = gz.astgen.mod.gpa;
1411 try gz.astgen.instructions.append(gpa, .{
1412 .tag = tag,
1413 .data = .{ .pl_node = .{
1414 .src_node = gz.astgen.decl.nodeIndexToRelative(node),
1415 .payload_index = undefined,
1416 } },
1417 });
1418 return new_index;
1419 }
1420
1421 /// Note that this returns a `zir.Inst.Index` not a ref.
1422 /// Leaves the `payload_index` field undefined.
1423 pub fn addCondBr(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index {
1424 const gpa = gz.astgen.mod.gpa;
1425 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1426 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1427 try gz.astgen.instructions.append(gpa, .{
1428 .tag = tag,
1429 .data = .{ .pl_node = .{
1430 .src_node = gz.astgen.decl.nodeIndexToRelative(node),
1431 .payload_index = undefined,
1432 } },
1433 });
1434 gz.instructions.appendAssumeCapacity(new_index);
1435 return new_index;
1436 }
1437
1438 pub fn addConst(gz: *GenZir, typed_value: *TypedValue) !zir.Inst.Ref {
1439 return gz.add(.{
1440 .tag = .@"const",
1441 .data = .{ .@"const" = typed_value },
1442 });
1443 }
1444
1445 pub fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref {
1446 return gz.astgen.indexToRef(try gz.addAsIndex(inst));
1447 }
1448
1449 pub fn addAsIndex(gz: *GenZir, inst: zir.Inst) !zir.Inst.Index {
1450 const gpa = gz.astgen.mod.gpa;
1451 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1452 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1453
1454 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1455 gz.astgen.instructions.appendAssumeCapacity(inst);
1456 gz.instructions.appendAssumeCapacity(new_index);
1457 return new_index;
1458 }
8001459 };
8011460
8021461 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
......@@ -805,11 +1464,13 @@ pub const Scope = struct {
8051464 pub const LocalVal = struct {
8061465 pub const base_tag: Tag = .local_val;
8071466 base: Scope = Scope{ .tag = base_tag },
808 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
1467 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`.
8091468 parent: *Scope,
810 gen_zir: *GenZIR,
1469 gen_zir: *GenZir,
8111470 name: []const u8,
812 inst: *zir.Inst,
1471 inst: zir.Inst.Ref,
1472 /// Source location of the corresponding variable declaration.
1473 src: LazySrcLoc,
8131474 };
8141475
8151476 /// This could be a `const` or `var` local. It has a pointer instead of a value.
......@@ -818,21 +1479,19 @@ pub const Scope = struct {
8181479 pub const LocalPtr = struct {
8191480 pub const base_tag: Tag = .local_ptr;
8201481 base: Scope = Scope{ .tag = base_tag },
821 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
1482 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`.
8221483 parent: *Scope,
823 gen_zir: *GenZIR,
1484 gen_zir: *GenZir,
8241485 name: []const u8,
825 ptr: *zir.Inst,
1486 ptr: zir.Inst.Ref,
1487 /// Source location of the corresponding variable declaration.
1488 src: LazySrcLoc,
8261489 };
8271490
828 pub const Nosuspend = struct {
829 pub const base_tag: Tag = .gen_nosuspend;
830
1491 pub const DeclRef = struct {
1492 pub const base_tag: Tag = .decl_ref;
8311493 base: Scope = Scope{ .tag = base_tag },
832 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
833 parent: *Scope,
834 gen_zir: *GenZIR,
835 src: usize,
1494 decl: *Decl,
8361495 };
8371496};
8381497
......@@ -855,17 +1514,17 @@ pub const ErrorMsg = struct {
8551514 comptime format: []const u8,
8561515 args: anytype,
8571516 ) !*ErrorMsg {
858 const self = try gpa.create(ErrorMsg);
859 errdefer gpa.destroy(self);
860 self.* = try init(gpa, src_loc, format, args);
861 return self;
1517 const err_msg = try gpa.create(ErrorMsg);
1518 errdefer gpa.destroy(err_msg);
1519 err_msg.* = try init(gpa, src_loc, format, args);
1520 return err_msg;
8621521 }
8631522
8641523 /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`,
8651524 /// as well as all notes.
866 pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
867 self.deinit(gpa);
868 gpa.destroy(self);
1525 pub fn destroy(err_msg: *ErrorMsg, gpa: *Allocator) void {
1526 err_msg.deinit(gpa);
1527 gpa.destroy(err_msg);
8691528 }
8701529
8711530 pub fn init(
......@@ -880,84 +1539,715 @@ pub const ErrorMsg = struct {
8801539 };
8811540 }
8821541
883 pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void {
884 for (self.notes) |*note| {
1542 pub fn deinit(err_msg: *ErrorMsg, gpa: *Allocator) void {
1543 for (err_msg.notes) |*note| {
8851544 note.deinit(gpa);
8861545 }
887 gpa.free(self.notes);
888 gpa.free(self.msg);
889 self.* = undefined;
1546 gpa.free(err_msg.notes);
1547 gpa.free(err_msg.msg);
1548 err_msg.* = undefined;
8901549 }
8911550};
8921551
8931552/// Canonical reference to a position within a source file.
8941553pub const SrcLoc = struct {
895 file_scope: *Scope.File,
896 byte_offset: usize,
897};
898
899pub const InnerError = error{ OutOfMemory, AnalysisFail };
900
901pub fn deinit(self: *Module) void {
902 const gpa = self.gpa;
903
904 self.compile_log_text.deinit(gpa);
905
906 self.zig_cache_artifact_directory.handle.close();
907
908 self.deletion_set.deinit(gpa);
909
910 for (self.decl_table.items()) |entry| {
911 entry.value.destroy(self);
912 }
913 self.decl_table.deinit(gpa);
914
915 for (self.failed_decls.items()) |entry| {
916 entry.value.destroy(gpa);
917 }
918 self.failed_decls.deinit(gpa);
919
920 for (self.emit_h_failed_decls.items()) |entry| {
921 entry.value.destroy(gpa);
922 }
923 self.emit_h_failed_decls.deinit(gpa);
924
925 for (self.failed_files.items()) |entry| {
926 entry.value.destroy(gpa);
927 }
928 self.failed_files.deinit(gpa);
929
930 for (self.failed_exports.items()) |entry| {
931 entry.value.destroy(gpa);
932 }
933 self.failed_exports.deinit(gpa);
934
935 self.compile_log_decls.deinit(gpa);
936
937 for (self.decl_exports.items()) |entry| {
938 const export_list = entry.value;
939 gpa.free(export_list);
940 }
941 self.decl_exports.deinit(gpa);
942
943 for (self.export_owners.items()) |entry| {
944 freeExportList(gpa, entry.value);
1554 /// The active field is determined by tag of `lazy`.
1555 container: union {
1556 /// The containing `Decl` according to the source code.
1557 decl: *Decl,
1558 file_scope: *Scope.File,
1559 },
1560 /// Relative to `decl`.
1561 lazy: LazySrcLoc,
1562
1563 pub fn fileScope(src_loc: SrcLoc) *Scope.File {
1564 return switch (src_loc.lazy) {
1565 .unneeded => unreachable,
1566
1567 .byte_abs,
1568 .token_abs,
1569 .node_abs,
1570 => src_loc.container.file_scope,
1571
1572 .byte_offset,
1573 .token_offset,
1574 .node_offset,
1575 .node_offset_var_decl_ty,
1576 .node_offset_for_cond,
1577 .node_offset_builtin_call_arg0,
1578 .node_offset_builtin_call_arg1,
1579 .node_offset_array_access_index,
1580 .node_offset_slice_sentinel,
1581 .node_offset_call_func,
1582 .node_offset_field_name,
1583 .node_offset_deref_ptr,
1584 .node_offset_asm_source,
1585 .node_offset_asm_ret_ty,
1586 .node_offset_if_cond,
1587 .node_offset_bin_op,
1588 .node_offset_bin_lhs,
1589 .node_offset_bin_rhs,
1590 .node_offset_switch_operand,
1591 .node_offset_switch_special_prong,
1592 .node_offset_switch_range,
1593 .node_offset_fn_type_cc,
1594 .node_offset_fn_type_ret_ty,
1595 => src_loc.container.decl.container.file_scope,
1596 };
9451597 }
946 self.export_owners.deinit(gpa);
9471598
948 self.symbol_exports.deinit(gpa);
949 self.root_scope.destroy(gpa);
1599 pub fn byteOffset(src_loc: SrcLoc) !u32 {
1600 switch (src_loc.lazy) {
1601 .unneeded => unreachable,
9501602
951 var it = self.global_error_set.iterator();
952 while (it.next()) |entry| {
953 gpa.free(entry.key);
954 }
955 self.global_error_set.deinit(gpa);
1603 .byte_abs => |byte_index| return byte_index,
9561604
957 for (self.import_table.items()) |entry| {
1605 .token_abs => |tok_index| {
1606 const tree = src_loc.container.file_scope.base.tree();
1607 const token_starts = tree.tokens.items(.start);
1608 return token_starts[tok_index];
1609 },
1610 .node_abs => |node| {
1611 const tree = src_loc.container.file_scope.base.tree();
1612 const token_starts = tree.tokens.items(.start);
1613 const tok_index = tree.firstToken(node);
1614 return token_starts[tok_index];
1615 },
1616 .byte_offset => |byte_off| {
1617 const decl = src_loc.container.decl;
1618 return decl.srcByteOffset() + byte_off;
1619 },
1620 .token_offset => |tok_off| {
1621 const decl = src_loc.container.decl;
1622 const tok_index = decl.srcToken() + tok_off;
1623 const tree = decl.container.file_scope.base.tree();
1624 const token_starts = tree.tokens.items(.start);
1625 return token_starts[tok_index];
1626 },
1627 .node_offset, .node_offset_bin_op => |node_off| {
1628 const decl = src_loc.container.decl;
1629 const node = decl.relativeToNodeIndex(node_off);
1630 const tree = decl.container.file_scope.base.tree();
1631 const main_tokens = tree.nodes.items(.main_token);
1632 const tok_index = main_tokens[node];
1633 const token_starts = tree.tokens.items(.start);
1634 return token_starts[tok_index];
1635 },
1636 .node_offset_var_decl_ty => |node_off| {
1637 const decl = src_loc.container.decl;
1638 const node = decl.relativeToNodeIndex(node_off);
1639 const tree = decl.container.file_scope.base.tree();
1640 const node_tags = tree.nodes.items(.tag);
1641 const full = switch (node_tags[node]) {
1642 .global_var_decl => tree.globalVarDecl(node),
1643 .local_var_decl => tree.localVarDecl(node),
1644 .simple_var_decl => tree.simpleVarDecl(node),
1645 .aligned_var_decl => tree.alignedVarDecl(node),
1646 else => unreachable,
1647 };
1648 const tok_index = if (full.ast.type_node != 0) blk: {
1649 const main_tokens = tree.nodes.items(.main_token);
1650 break :blk main_tokens[full.ast.type_node];
1651 } else blk: {
1652 break :blk full.ast.mut_token + 1; // the name token
1653 };
1654 const token_starts = tree.tokens.items(.start);
1655 return token_starts[tok_index];
1656 },
1657 .node_offset_builtin_call_arg0 => |node_off| {
1658 const decl = src_loc.container.decl;
1659 const tree = decl.container.file_scope.base.tree();
1660 const node_datas = tree.nodes.items(.data);
1661 const node_tags = tree.nodes.items(.tag);
1662 const node = decl.relativeToNodeIndex(node_off);
1663 const param = switch (node_tags[node]) {
1664 .builtin_call_two, .builtin_call_two_comma => node_datas[node].lhs,
1665 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs],
1666 else => unreachable,
1667 };
1668 const main_tokens = tree.nodes.items(.main_token);
1669 const tok_index = main_tokens[param];
1670 const token_starts = tree.tokens.items(.start);
1671 return token_starts[tok_index];
1672 },
1673 .node_offset_builtin_call_arg1 => |node_off| {
1674 const decl = src_loc.container.decl;
1675 const tree = decl.container.file_scope.base.tree();
1676 const node_datas = tree.nodes.items(.data);
1677 const node_tags = tree.nodes.items(.tag);
1678 const node = decl.relativeToNodeIndex(node_off);
1679 const param = switch (node_tags[node]) {
1680 .builtin_call_two, .builtin_call_two_comma => node_datas[node].rhs,
1681 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + 1],
1682 else => unreachable,
1683 };
1684 const main_tokens = tree.nodes.items(.main_token);
1685 const tok_index = main_tokens[param];
1686 const token_starts = tree.tokens.items(.start);
1687 return token_starts[tok_index];
1688 },
1689 .node_offset_array_access_index => |node_off| {
1690 const decl = src_loc.container.decl;
1691 const tree = decl.container.file_scope.base.tree();
1692 const node_datas = tree.nodes.items(.data);
1693 const node_tags = tree.nodes.items(.tag);
1694 const node = decl.relativeToNodeIndex(node_off);
1695 const main_tokens = tree.nodes.items(.main_token);
1696 const tok_index = main_tokens[node_datas[node].rhs];
1697 const token_starts = tree.tokens.items(.start);
1698 return token_starts[tok_index];
1699 },
1700 .node_offset_slice_sentinel => |node_off| {
1701 const decl = src_loc.container.decl;
1702 const tree = decl.container.file_scope.base.tree();
1703 const node_datas = tree.nodes.items(.data);
1704 const node_tags = tree.nodes.items(.tag);
1705 const node = decl.relativeToNodeIndex(node_off);
1706 const full = switch (node_tags[node]) {
1707 .slice_open => tree.sliceOpen(node),
1708 .slice => tree.slice(node),
1709 .slice_sentinel => tree.sliceSentinel(node),
1710 else => unreachable,
1711 };
1712 const main_tokens = tree.nodes.items(.main_token);
1713 const tok_index = main_tokens[full.ast.sentinel];
1714 const token_starts = tree.tokens.items(.start);
1715 return token_starts[tok_index];
1716 },
1717 .node_offset_call_func => |node_off| {
1718 const decl = src_loc.container.decl;
1719 const tree = decl.container.file_scope.base.tree();
1720 const node_datas = tree.nodes.items(.data);
1721 const node_tags = tree.nodes.items(.tag);
1722 const node = decl.relativeToNodeIndex(node_off);
1723 var params: [1]ast.Node.Index = undefined;
1724 const full = switch (node_tags[node]) {
1725 .call_one,
1726 .call_one_comma,
1727 .async_call_one,
1728 .async_call_one_comma,
1729 => tree.callOne(&params, node),
1730
1731 .call,
1732 .call_comma,
1733 .async_call,
1734 .async_call_comma,
1735 => tree.callFull(node),
1736
1737 else => unreachable,
1738 };
1739 const main_tokens = tree.nodes.items(.main_token);
1740 const tok_index = main_tokens[full.ast.fn_expr];
1741 const token_starts = tree.tokens.items(.start);
1742 return token_starts[tok_index];
1743 },
1744 .node_offset_field_name => |node_off| {
1745 const decl = src_loc.container.decl;
1746 const tree = decl.container.file_scope.base.tree();
1747 const node_datas = tree.nodes.items(.data);
1748 const node_tags = tree.nodes.items(.tag);
1749 const node = decl.relativeToNodeIndex(node_off);
1750 const tok_index = node_datas[node].rhs;
1751 const token_starts = tree.tokens.items(.start);
1752 return token_starts[tok_index];
1753 },
1754 .node_offset_deref_ptr => |node_off| {
1755 const decl = src_loc.container.decl;
1756 const tree = decl.container.file_scope.base.tree();
1757 const node_datas = tree.nodes.items(.data);
1758 const node_tags = tree.nodes.items(.tag);
1759 const node = decl.relativeToNodeIndex(node_off);
1760 const tok_index = node_datas[node].lhs;
1761 const token_starts = tree.tokens.items(.start);
1762 return token_starts[tok_index];
1763 },
1764 .node_offset_asm_source => |node_off| {
1765 const decl = src_loc.container.decl;
1766 const tree = decl.container.file_scope.base.tree();
1767 const node_datas = tree.nodes.items(.data);
1768 const node_tags = tree.nodes.items(.tag);
1769 const node = decl.relativeToNodeIndex(node_off);
1770 const full = switch (node_tags[node]) {
1771 .asm_simple => tree.asmSimple(node),
1772 .@"asm" => tree.asmFull(node),
1773 else => unreachable,
1774 };
1775 const main_tokens = tree.nodes.items(.main_token);
1776 const tok_index = main_tokens[full.ast.template];
1777 const token_starts = tree.tokens.items(.start);
1778 return token_starts[tok_index];
1779 },
1780 .node_offset_asm_ret_ty => |node_off| {
1781 const decl = src_loc.container.decl;
1782 const tree = decl.container.file_scope.base.tree();
1783 const node_datas = tree.nodes.items(.data);
1784 const node_tags = tree.nodes.items(.tag);
1785 const node = decl.relativeToNodeIndex(node_off);
1786 const full = switch (node_tags[node]) {
1787 .asm_simple => tree.asmSimple(node),
1788 .@"asm" => tree.asmFull(node),
1789 else => unreachable,
1790 };
1791 const main_tokens = tree.nodes.items(.main_token);
1792 const tok_index = main_tokens[full.outputs[0]];
1793 const token_starts = tree.tokens.items(.start);
1794 return token_starts[tok_index];
1795 },
1796
1797 .node_offset_for_cond, .node_offset_if_cond => |node_off| {
1798 const decl = src_loc.container.decl;
1799 const node = decl.relativeToNodeIndex(node_off);
1800 const tree = decl.container.file_scope.base.tree();
1801 const node_tags = tree.nodes.items(.tag);
1802 const src_node = switch (node_tags[node]) {
1803 .if_simple => tree.ifSimple(node).ast.cond_expr,
1804 .@"if" => tree.ifFull(node).ast.cond_expr,
1805 .while_simple => tree.whileSimple(node).ast.cond_expr,
1806 .while_cont => tree.whileCont(node).ast.cond_expr,
1807 .@"while" => tree.whileFull(node).ast.cond_expr,
1808 .for_simple => tree.forSimple(node).ast.cond_expr,
1809 .@"for" => tree.forFull(node).ast.cond_expr,
1810 else => unreachable,
1811 };
1812 const main_tokens = tree.nodes.items(.main_token);
1813 const tok_index = main_tokens[src_node];
1814 const token_starts = tree.tokens.items(.start);
1815 return token_starts[tok_index];
1816 },
1817 .node_offset_bin_lhs => |node_off| {
1818 const decl = src_loc.container.decl;
1819 const node = decl.relativeToNodeIndex(node_off);
1820 const tree = decl.container.file_scope.base.tree();
1821 const node_datas = tree.nodes.items(.data);
1822 const src_node = node_datas[node].lhs;
1823 const main_tokens = tree.nodes.items(.main_token);
1824 const tok_index = main_tokens[src_node];
1825 const token_starts = tree.tokens.items(.start);
1826 return token_starts[tok_index];
1827 },
1828 .node_offset_bin_rhs => |node_off| {
1829 const decl = src_loc.container.decl;
1830 const node = decl.relativeToNodeIndex(node_off);
1831 const tree = decl.container.file_scope.base.tree();
1832 const node_datas = tree.nodes.items(.data);
1833 const src_node = node_datas[node].rhs;
1834 const main_tokens = tree.nodes.items(.main_token);
1835 const tok_index = main_tokens[src_node];
1836 const token_starts = tree.tokens.items(.start);
1837 return token_starts[tok_index];
1838 },
1839
1840 .node_offset_switch_operand => |node_off| {
1841 const decl = src_loc.container.decl;
1842 const node = decl.relativeToNodeIndex(node_off);
1843 const tree = decl.container.file_scope.base.tree();
1844 const node_datas = tree.nodes.items(.data);
1845 const src_node = node_datas[node].lhs;
1846 const main_tokens = tree.nodes.items(.main_token);
1847 const tok_index = main_tokens[src_node];
1848 const token_starts = tree.tokens.items(.start);
1849 return token_starts[tok_index];
1850 },
1851
1852 .node_offset_switch_special_prong => |node_off| {
1853 const decl = src_loc.container.decl;
1854 const switch_node = decl.relativeToNodeIndex(node_off);
1855 const tree = decl.container.file_scope.base.tree();
1856 const node_datas = tree.nodes.items(.data);
1857 const node_tags = tree.nodes.items(.tag);
1858 const main_tokens = tree.nodes.items(.main_token);
1859 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
1860 const case_nodes = tree.extra_data[extra.start..extra.end];
1861 for (case_nodes) |case_node| {
1862 const case = switch (node_tags[case_node]) {
1863 .switch_case_one => tree.switchCaseOne(case_node),
1864 .switch_case => tree.switchCase(case_node),
1865 else => unreachable,
1866 };
1867 const is_special = (case.ast.values.len == 0) or
1868 (case.ast.values.len == 1 and
1869 node_tags[case.ast.values[0]] == .identifier and
1870 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));
1871 if (!is_special) continue;
1872
1873 const tok_index = main_tokens[case_node];
1874 const token_starts = tree.tokens.items(.start);
1875 return token_starts[tok_index];
1876 } else unreachable;
1877 },
1878
1879 .node_offset_switch_range => |node_off| {
1880 const decl = src_loc.container.decl;
1881 const switch_node = decl.relativeToNodeIndex(node_off);
1882 const tree = decl.container.file_scope.base.tree();
1883 const node_datas = tree.nodes.items(.data);
1884 const node_tags = tree.nodes.items(.tag);
1885 const main_tokens = tree.nodes.items(.main_token);
1886 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
1887 const case_nodes = tree.extra_data[extra.start..extra.end];
1888 for (case_nodes) |case_node| {
1889 const case = switch (node_tags[case_node]) {
1890 .switch_case_one => tree.switchCaseOne(case_node),
1891 .switch_case => tree.switchCase(case_node),
1892 else => unreachable,
1893 };
1894 const is_special = (case.ast.values.len == 0) or
1895 (case.ast.values.len == 1 and
1896 node_tags[case.ast.values[0]] == .identifier and
1897 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));
1898 if (is_special) continue;
1899
1900 for (case.ast.values) |item_node| {
1901 if (node_tags[item_node] == .switch_range) {
1902 const tok_index = main_tokens[item_node];
1903 const token_starts = tree.tokens.items(.start);
1904 return token_starts[tok_index];
1905 }
1906 }
1907 } else unreachable;
1908 },
1909
1910 .node_offset_fn_type_cc => |node_off| {
1911 const decl = src_loc.container.decl;
1912 const tree = decl.container.file_scope.base.tree();
1913 const node_datas = tree.nodes.items(.data);
1914 const node_tags = tree.nodes.items(.tag);
1915 const node = decl.relativeToNodeIndex(node_off);
1916 var params: [1]ast.Node.Index = undefined;
1917 const full = switch (node_tags[node]) {
1918 .fn_proto_simple => tree.fnProtoSimple(&params, node),
1919 .fn_proto_multi => tree.fnProtoMulti(node),
1920 .fn_proto_one => tree.fnProtoOne(&params, node),
1921 .fn_proto => tree.fnProto(node),
1922 else => unreachable,
1923 };
1924 const main_tokens = tree.nodes.items(.main_token);
1925 const tok_index = main_tokens[full.ast.callconv_expr];
1926 const token_starts = tree.tokens.items(.start);
1927 return token_starts[tok_index];
1928 },
1929
1930 .node_offset_fn_type_ret_ty => |node_off| {
1931 const decl = src_loc.container.decl;
1932 const tree = decl.container.file_scope.base.tree();
1933 const node_datas = tree.nodes.items(.data);
1934 const node_tags = tree.nodes.items(.tag);
1935 const node = decl.relativeToNodeIndex(node_off);
1936 var params: [1]ast.Node.Index = undefined;
1937 const full = switch (node_tags[node]) {
1938 .fn_proto_simple => tree.fnProtoSimple(&params, node),
1939 .fn_proto_multi => tree.fnProtoMulti(node),
1940 .fn_proto_one => tree.fnProtoOne(&params, node),
1941 .fn_proto => tree.fnProto(node),
1942 else => unreachable,
1943 };
1944 const main_tokens = tree.nodes.items(.main_token);
1945 const tok_index = main_tokens[full.ast.return_type];
1946 const token_starts = tree.tokens.items(.start);
1947 return token_starts[tok_index];
1948 },
1949 }
1950 }
1951};
1952
1953/// Resolving a source location into a byte offset may require doing work
1954/// that we would rather not do unless the error actually occurs.
1955/// Therefore we need a data structure that contains the information necessary
1956/// to lazily produce a `SrcLoc` as required.
1957/// Most of the offsets in this data structure are relative to the containing Decl.
1958/// This makes the source location resolve properly even when a Decl gets
1959/// shifted up or down in the file, as long as the Decl's contents itself
1960/// do not change.
1961pub const LazySrcLoc = union(enum) {
1962 /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting
1963 /// that all code paths which would need to resolve the source location are
1964 /// unreachable. If you are debugging this tag incorrectly being this value,
1965 /// look into using reverse-continue with a memory watchpoint to see where the
1966 /// value is being set to this tag.
1967 unneeded,
1968 /// The source location points to a byte offset within a source file,
1969 /// offset from 0. The source file is determined contextually.
1970 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
1971 byte_abs: u32,
1972 /// The source location points to a token within a source file,
1973 /// offset from 0. The source file is determined contextually.
1974 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
1975 token_abs: u32,
1976 /// The source location points to an AST node within a source file,
1977 /// offset from 0. The source file is determined contextually.
1978 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
1979 node_abs: u32,
1980 /// The source location points to a byte offset within a source file,
1981 /// offset from the byte offset of the Decl within the file.
1982 /// The Decl is determined contextually.
1983 byte_offset: u32,
1984 /// This data is the offset into the token list from the Decl token.
1985 /// The Decl is determined contextually.
1986 token_offset: u32,
1987 /// The source location points to an AST node, which is this value offset
1988 /// from its containing Decl node AST index.
1989 /// The Decl is determined contextually.
1990 node_offset: i32,
1991 /// The source location points to a variable declaration type expression,
1992 /// found by taking this AST node index offset from the containing
1993 /// Decl AST node, which points to a variable declaration AST node. Next, navigate
1994 /// to the type expression.
1995 /// The Decl is determined contextually.
1996 node_offset_var_decl_ty: i32,
1997 /// The source location points to a for loop condition expression,
1998 /// found by taking this AST node index offset from the containing
1999 /// Decl AST node, which points to a for loop AST node. Next, navigate
2000 /// to the condition expression.
2001 /// The Decl is determined contextually.
2002 node_offset_for_cond: i32,
2003 /// The source location points to the first parameter of a builtin
2004 /// function call, found by taking this AST node index offset from the containing
2005 /// Decl AST node, which points to a builtin call AST node. Next, navigate
2006 /// to the first parameter.
2007 /// The Decl is determined contextually.
2008 node_offset_builtin_call_arg0: i32,
2009 /// Same as `node_offset_builtin_call_arg0` except arg index 1.
2010 node_offset_builtin_call_arg1: i32,
2011 /// The source location points to the index expression of an array access
2012 /// expression, found by taking this AST node index offset from the containing
2013 /// Decl AST node, which points to an array access AST node. Next, navigate
2014 /// to the index expression.
2015 /// The Decl is determined contextually.
2016 node_offset_array_access_index: i32,
2017 /// The source location points to the sentinel expression of a slice
2018 /// expression, found by taking this AST node index offset from the containing
2019 /// Decl AST node, which points to a slice AST node. Next, navigate
2020 /// to the sentinel expression.
2021 /// The Decl is determined contextually.
2022 node_offset_slice_sentinel: i32,
2023 /// The source location points to the callee expression of a function
2024 /// call expression, found by taking this AST node index offset from the containing
2025 /// Decl AST node, which points to a function call AST node. Next, navigate
2026 /// to the callee expression.
2027 /// The Decl is determined contextually.
2028 node_offset_call_func: i32,
2029 /// The source location points to the field name of a field access expression,
2030 /// found by taking this AST node index offset from the containing
2031 /// Decl AST node, which points to a field access AST node. Next, navigate
2032 /// to the field name token.
2033 /// The Decl is determined contextually.
2034 node_offset_field_name: i32,
2035 /// The source location points to the pointer of a pointer deref expression,
2036 /// found by taking this AST node index offset from the containing
2037 /// Decl AST node, which points to a pointer deref AST node. Next, navigate
2038 /// to the pointer expression.
2039 /// The Decl is determined contextually.
2040 node_offset_deref_ptr: i32,
2041 /// The source location points to the assembly source code of an inline assembly
2042 /// expression, found by taking this AST node index offset from the containing
2043 /// Decl AST node, which points to inline assembly AST node. Next, navigate
2044 /// to the asm template source code.
2045 /// The Decl is determined contextually.
2046 node_offset_asm_source: i32,
2047 /// The source location points to the return type of an inline assembly
2048 /// expression, found by taking this AST node index offset from the containing
2049 /// Decl AST node, which points to inline assembly AST node. Next, navigate
2050 /// to the return type expression.
2051 /// The Decl is determined contextually.
2052 node_offset_asm_ret_ty: i32,
2053 /// The source location points to the condition expression of an if
2054 /// expression, found by taking this AST node index offset from the containing
2055 /// Decl AST node, which points to an if expression AST node. Next, navigate
2056 /// to the condition expression.
2057 /// The Decl is determined contextually.
2058 node_offset_if_cond: i32,
2059 /// The source location points to a binary expression, such as `a + b`, found
2060 /// by taking this AST node index offset from the containing Decl AST node.
2061 /// The Decl is determined contextually.
2062 node_offset_bin_op: i32,
2063 /// The source location points to the LHS of a binary expression, found
2064 /// by taking this AST node index offset from the containing Decl AST node,
2065 /// which points to a binary expression AST node. Next, nagivate to the LHS.
2066 /// The Decl is determined contextually.
2067 node_offset_bin_lhs: i32,
2068 /// The source location points to the RHS of a binary expression, found
2069 /// by taking this AST node index offset from the containing Decl AST node,
2070 /// which points to a binary expression AST node. Next, nagivate to the RHS.
2071 /// The Decl is determined contextually.
2072 node_offset_bin_rhs: i32,
2073 /// The source location points to the operand of a switch expression, found
2074 /// by taking this AST node index offset from the containing Decl AST node,
2075 /// which points to a switch expression AST node. Next, nagivate to the operand.
2076 /// The Decl is determined contextually.
2077 node_offset_switch_operand: i32,
2078 /// The source location points to the else/`_` prong of a switch expression, found
2079 /// by taking this AST node index offset from the containing Decl AST node,
2080 /// which points to a switch expression AST node. Next, nagivate to the else/`_` prong.
2081 /// The Decl is determined contextually.
2082 node_offset_switch_special_prong: i32,
2083 /// The source location points to all the ranges of a switch expression, found
2084 /// by taking this AST node index offset from the containing Decl AST node,
2085 /// which points to a switch expression AST node. Next, nagivate to any of the
2086 /// range nodes. The error applies to all of them.
2087 /// The Decl is determined contextually.
2088 node_offset_switch_range: i32,
2089 /// The source location points to the calling convention of a function type
2090 /// expression, found by taking this AST node index offset from the containing
2091 /// Decl AST node, which points to a function type AST node. Next, nagivate to
2092 /// the calling convention node.
2093 /// The Decl is determined contextually.
2094 node_offset_fn_type_cc: i32,
2095 /// The source location points to the return type of a function type
2096 /// expression, found by taking this AST node index offset from the containing
2097 /// Decl AST node, which points to a function type AST node. Next, nagivate to
2098 /// the return type node.
2099 /// The Decl is determined contextually.
2100 node_offset_fn_type_ret_ty: i32,
2101
2102 /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.
2103 pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {
2104 return switch (lazy) {
2105 .unneeded,
2106 .byte_abs,
2107 .token_abs,
2108 .node_abs,
2109 => .{
2110 .container = .{ .file_scope = scope.getFileScope() },
2111 .lazy = lazy,
2112 },
2113
2114 .byte_offset,
2115 .token_offset,
2116 .node_offset,
2117 .node_offset_var_decl_ty,
2118 .node_offset_for_cond,
2119 .node_offset_builtin_call_arg0,
2120 .node_offset_builtin_call_arg1,
2121 .node_offset_array_access_index,
2122 .node_offset_slice_sentinel,
2123 .node_offset_call_func,
2124 .node_offset_field_name,
2125 .node_offset_deref_ptr,
2126 .node_offset_asm_source,
2127 .node_offset_asm_ret_ty,
2128 .node_offset_if_cond,
2129 .node_offset_bin_op,
2130 .node_offset_bin_lhs,
2131 .node_offset_bin_rhs,
2132 .node_offset_switch_operand,
2133 .node_offset_switch_special_prong,
2134 .node_offset_switch_range,
2135 .node_offset_fn_type_cc,
2136 .node_offset_fn_type_ret_ty,
2137 => .{
2138 .container = .{ .decl = scope.srcDecl().? },
2139 .lazy = lazy,
2140 },
2141 };
2142 }
2143
2144 /// Upgrade to a `SrcLoc` based on the `Decl` provided.
2145 pub fn toSrcLocWithDecl(lazy: LazySrcLoc, decl: *Decl) SrcLoc {
2146 return switch (lazy) {
2147 .unneeded,
2148 .byte_abs,
2149 .token_abs,
2150 .node_abs,
2151 => .{
2152 .container = .{ .file_scope = decl.getFileScope() },
2153 .lazy = lazy,
2154 },
2155
2156 .byte_offset,
2157 .token_offset,
2158 .node_offset,
2159 .node_offset_var_decl_ty,
2160 .node_offset_for_cond,
2161 .node_offset_builtin_call_arg0,
2162 .node_offset_builtin_call_arg1,
2163 .node_offset_array_access_index,
2164 .node_offset_slice_sentinel,
2165 .node_offset_call_func,
2166 .node_offset_field_name,
2167 .node_offset_deref_ptr,
2168 .node_offset_asm_source,
2169 .node_offset_asm_ret_ty,
2170 .node_offset_if_cond,
2171 .node_offset_bin_op,
2172 .node_offset_bin_lhs,
2173 .node_offset_bin_rhs,
2174 .node_offset_switch_operand,
2175 .node_offset_switch_special_prong,
2176 .node_offset_switch_range,
2177 .node_offset_fn_type_cc,
2178 .node_offset_fn_type_ret_ty,
2179 => .{
2180 .container = .{ .decl = decl },
2181 .lazy = lazy,
2182 },
2183 };
2184 }
2185};
2186
2187pub const InnerError = error{ OutOfMemory, AnalysisFail };
2188
2189pub fn deinit(mod: *Module) void {
2190 const gpa = mod.gpa;
2191
2192 mod.compile_log_text.deinit(gpa);
2193
2194 mod.zig_cache_artifact_directory.handle.close();
2195
2196 mod.deletion_set.deinit(gpa);
2197
2198 for (mod.decl_table.items()) |entry| {
2199 entry.value.destroy(mod);
2200 }
2201 mod.decl_table.deinit(gpa);
2202
2203 for (mod.failed_decls.items()) |entry| {
2204 entry.value.destroy(gpa);
2205 }
2206 mod.failed_decls.deinit(gpa);
2207
2208 for (mod.emit_h_failed_decls.items()) |entry| {
2209 entry.value.destroy(gpa);
2210 }
2211 mod.emit_h_failed_decls.deinit(gpa);
2212
2213 for (mod.failed_files.items()) |entry| {
2214 entry.value.destroy(gpa);
2215 }
2216 mod.failed_files.deinit(gpa);
2217
2218 for (mod.failed_exports.items()) |entry| {
2219 entry.value.destroy(gpa);
2220 }
2221 mod.failed_exports.deinit(gpa);
2222
2223 mod.compile_log_decls.deinit(gpa);
2224
2225 for (mod.decl_exports.items()) |entry| {
2226 const export_list = entry.value;
2227 gpa.free(export_list);
2228 }
2229 mod.decl_exports.deinit(gpa);
2230
2231 for (mod.export_owners.items()) |entry| {
2232 freeExportList(gpa, entry.value);
2233 }
2234 mod.export_owners.deinit(gpa);
2235
2236 mod.symbol_exports.deinit(gpa);
2237 mod.root_scope.destroy(gpa);
2238
2239 var it = mod.global_error_set.iterator();
2240 while (it.next()) |entry| {
2241 gpa.free(entry.key);
2242 }
2243 mod.global_error_set.deinit(gpa);
2244
2245 mod.error_name_list.deinit(gpa);
2246
2247 for (mod.import_table.items()) |entry| {
9582248 entry.value.destroy(gpa);
9592249 }
960 self.import_table.deinit(gpa);
2250 mod.import_table.deinit(gpa);
9612251}
9622252
9632253fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
......@@ -1102,42 +2392,51 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
11022392 // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
11032393 var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa);
11042394 defer analysis_arena.deinit();
1105 var gen_scope: Scope.GenZIR = .{
1106 .decl = decl,
1107 .arena = &analysis_arena.allocator,
1108 .parent = &decl.container.base,
1109 .force_comptime = true,
1110 };
1111 defer gen_scope.instructions.deinit(mod.gpa);
11122395
1113 const block_expr = node_datas[decl_node].lhs;
1114 _ = try astgen.comptimeExpr(mod, &gen_scope.base, .none, block_expr);
1115 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1116 zir.dumpZir(mod.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};
1117 }
2396 var code: zir.Code = blk: {
2397 var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator);
2398 defer astgen.deinit();
2399
2400 var gen_scope: Scope.GenZir = .{
2401 .force_comptime = true,
2402 .parent = &decl.container.base,
2403 .astgen = &astgen,
2404 };
2405 defer gen_scope.instructions.deinit(mod.gpa);
11182406
1119 var inst_table = Scope.Block.InstTable.init(mod.gpa);
1120 defer inst_table.deinit();
2407 const block_expr = node_datas[decl_node].lhs;
2408 _ = try AstGen.comptimeExpr(&gen_scope, &gen_scope.base, .none, block_expr);
11212409
1122 var branch_quota: u32 = default_eval_branch_quota;
2410 const code = try gen_scope.finish();
2411 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
2412 code.dump(mod.gpa, "comptime_block", &gen_scope.base, 0) catch {};
2413 }
2414 break :blk code;
2415 };
2416 defer code.deinit(mod.gpa);
11232417
2418 var sema: Sema = .{
2419 .mod = mod,
2420 .gpa = mod.gpa,
2421 .arena = &analysis_arena.allocator,
2422 .code = code,
2423 .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, code.instructions.len),
2424 .owner_decl = decl,
2425 .func = null,
2426 .owner_func = null,
2427 .param_inst_list = &.{},
2428 };
11242429 var block_scope: Scope.Block = .{
11252430 .parent = null,
1126 .inst_table = &inst_table,
1127 .func = null,
1128 .owner_decl = decl,
2431 .sema = &sema,
11292432 .src_decl = decl,
11302433 .instructions = .{},
1131 .arena = &analysis_arena.allocator,
11322434 .inlining = null,
11332435 .is_comptime = true,
1134 .branch_quota = &branch_quota,
11352436 };
11362437 defer block_scope.instructions.deinit(mod.gpa);
11372438
1138 _ = try zir_sema.analyzeBody(mod, &block_scope, .{
1139 .instructions = gen_scope.instructions.items,
1140 });
2439 _ = try sema.root(&block_scope);
11412440
11422441 decl.analysis = .complete;
11432442 decl.generation = mod.generation;
......@@ -1160,7 +2459,6 @@ fn astgenAndSemaFn(
11602459
11612460 decl.analysis = .in_progress;
11622461
1163 const token_starts = tree.tokens.items(.start);
11642462 const token_tags = tree.tokens.items(.tag);
11652463
11662464 // This arena allocator's memory is discarded at the end of this function. It is used
......@@ -1168,11 +2466,14 @@ fn astgenAndSemaFn(
11682466 // to complete the Decl analysis.
11692467 var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
11702468 defer fn_type_scope_arena.deinit();
1171 var fn_type_scope: Scope.GenZIR = .{
1172 .decl = decl,
1173 .arena = &fn_type_scope_arena.allocator,
1174 .parent = &decl.container.base,
2469
2470 var fn_type_astgen = try AstGen.init(mod, decl, &fn_type_scope_arena.allocator);
2471 defer fn_type_astgen.deinit();
2472
2473 var fn_type_scope: Scope.GenZir = .{
11752474 .force_comptime = true,
2475 .parent = &decl.container.base,
2476 .astgen = &fn_type_astgen,
11762477 };
11772478 defer fn_type_scope.instructions.deinit(mod.gpa);
11782479
......@@ -1189,13 +2490,7 @@ fn astgenAndSemaFn(
11892490 }
11902491 break :blk count;
11912492 };
1192 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_count);
1193 const fn_src = token_starts[fn_proto.ast.fn_token];
1194 const type_type = try astgen.addZIRInstConst(mod, &fn_type_scope.base, fn_src, .{
1195 .ty = Type.initTag(.type),
1196 .val = Value.initTag(.type_type),
1197 });
1198 const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
2493 const param_types = try fn_type_scope_arena.allocator.alloc(zir.Inst.Ref, param_count);
11992494
12002495 var is_var_args = false;
12012496 {
......@@ -1220,7 +2515,7 @@ fn astgenAndSemaFn(
12202515 const param_type_node = param.type_expr;
12212516 assert(param_type_node != 0);
12222517 param_types[param_type_i] =
1223 try astgen.expr(mod, &fn_type_scope.base, type_type_rl, param_type_node);
2518 try AstGen.expr(&fn_type_scope, &fn_type_scope.base, .{ .ty = .type_type }, param_type_node);
12242519 }
12252520 assert(param_type_i == param_count);
12262521 }
......@@ -1289,10 +2584,10 @@ fn astgenAndSemaFn(
12892584 if (token_tags[maybe_bang] == .bang) {
12902585 return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{});
12912586 }
1292 const return_type_inst = try astgen.expr(
1293 mod,
2587 const return_type_inst = try AstGen.expr(
2588 &fn_type_scope,
12942589 &fn_type_scope.base,
1295 type_type_rl,
2590 .{ .ty = .type_type },
12962591 fn_proto.ast.return_type,
12972592 );
12982593
......@@ -1301,73 +2596,72 @@ fn astgenAndSemaFn(
13012596 else
13022597 false;
13032598
1304 const cc_inst = if (fn_proto.ast.callconv_expr != 0) cc: {
2599 const cc: zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
13052600 // TODO instead of enum literal type, this needs to be the
13062601 // std.builtin.CallingConvention enum. We need to implement importing other files
13072602 // and enums in order to fix this.
1308 const src = token_starts[tree.firstToken(fn_proto.ast.callconv_expr)];
1309 const enum_lit_ty = try astgen.addZIRInstConst(mod, &fn_type_scope.base, src, .{
1310 .ty = Type.initTag(.type),
1311 .val = Value.initTag(.enum_literal_type),
1312 });
1313 break :cc try astgen.comptimeExpr(mod, &fn_type_scope.base, .{
1314 .ty = enum_lit_ty,
1315 }, fn_proto.ast.callconv_expr);
1316 } else if (is_extern) cc: {
1317 // note: https://github.com/ziglang/zig/issues/5269
1318 const src = token_starts[fn_proto.extern_export_token.?];
1319 break :cc try astgen.addZIRInst(mod, &fn_type_scope.base, src, zir.Inst.EnumLiteral, .{ .name = "C" }, .{});
1320 } else null;
1321
1322 const fn_type_inst = if (cc_inst) |cc| fn_type: {
1323 var fn_type = try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{
1324 .return_type = return_type_inst,
2603 try AstGen.comptimeExpr(
2604 &fn_type_scope,
2605 &fn_type_scope.base,
2606 .{ .ty = .enum_literal_type },
2607 fn_proto.ast.callconv_expr,
2608 )
2609 else if (is_extern) // note: https://github.com/ziglang/zig/issues/5269
2610 try fn_type_scope.addSmallStr(.enum_literal_small, "C")
2611 else
2612 .none;
2613
2614 const fn_type_inst: zir.Inst.Ref = if (cc != .none) fn_type: {
2615 const tag: zir.Inst.Tag = if (is_var_args) .fn_type_cc_var_args else .fn_type_cc;
2616 break :fn_type try fn_type_scope.addFnTypeCc(tag, .{
2617 .src_node = fn_proto.ast.proto_node,
2618 .ret_ty = return_type_inst,
13252619 .param_types = param_types,
13262620 .cc = cc,
13272621 });
1328 if (is_var_args) fn_type.tag = .fn_type_cc_var_args;
1329 break :fn_type fn_type;
13302622 } else fn_type: {
1331 var fn_type = try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type, .{
1332 .return_type = return_type_inst,
2623 const tag: zir.Inst.Tag = if (is_var_args) .fn_type_var_args else .fn_type;
2624 break :fn_type try fn_type_scope.addFnType(tag, .{
2625 .src_node = fn_proto.ast.proto_node,
2626 .ret_ty = return_type_inst,
13332627 .param_types = param_types,
13342628 });
1335 if (is_var_args) fn_type.tag = .fn_type_var_args;
1336 break :fn_type fn_type;
13372629 };
1338
1339 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1340 zir.dumpZir(mod.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {};
1341 }
2630 _ = try fn_type_scope.addBreak(.break_inline, 0, fn_type_inst);
13422631
13432632 // We need the memory for the Type to go into the arena for the Decl
13442633 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
13452634 errdefer decl_arena.deinit();
13462635 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
13472636
1348 var inst_table = Scope.Block.InstTable.init(mod.gpa);
1349 defer inst_table.deinit();
1350
1351 var branch_quota: u32 = default_eval_branch_quota;
2637 var fn_type_code = try fn_type_scope.finish();
2638 defer fn_type_code.deinit(mod.gpa);
2639 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
2640 fn_type_code.dump(mod.gpa, "fn_type", &fn_type_scope.base, 0) catch {};
2641 }
13522642
2643 var fn_type_sema: Sema = .{
2644 .mod = mod,
2645 .gpa = mod.gpa,
2646 .arena = &decl_arena.allocator,
2647 .code = fn_type_code,
2648 .inst_map = try fn_type_scope_arena.allocator.alloc(*ir.Inst, fn_type_code.instructions.len),
2649 .owner_decl = decl,
2650 .func = null,
2651 .owner_func = null,
2652 .param_inst_list = &.{},
2653 };
13532654 var block_scope: Scope.Block = .{
13542655 .parent = null,
1355 .inst_table = &inst_table,
1356 .func = null,
1357 .owner_decl = decl,
2656 .sema = &fn_type_sema,
13582657 .src_decl = decl,
13592658 .instructions = .{},
1360 .arena = &decl_arena.allocator,
13612659 .inlining = null,
1362 .is_comptime = false,
1363 .branch_quota = &branch_quota,
2660 .is_comptime = true,
13642661 };
13652662 defer block_scope.instructions.deinit(mod.gpa);
13662663
1367 const fn_type = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, fn_type_inst, .{
1368 .instructions = fn_type_scope.instructions.items,
1369 });
1370
2664 const fn_type = try fn_type_sema.rootAsType(&block_scope);
13712665 if (body_node == 0) {
13722666 if (!is_extern) {
13732667 return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function has no body", .{});
......@@ -1409,63 +2703,69 @@ fn astgenAndSemaFn(
14092703 const new_func = try decl_arena.allocator.create(Fn);
14102704 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
14112705
1412 const fn_zir: zir.Body = blk: {
2706 const fn_zir: zir.Code = blk: {
14132707 // We put the ZIR inside the Decl arena.
1414 var gen_scope: Scope.GenZIR = .{
1415 .decl = decl,
1416 .arena = &decl_arena.allocator,
1417 .parent = &decl.container.base,
2708 var astgen = try AstGen.init(mod, decl, &decl_arena.allocator);
2709 astgen.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count);
2710 defer astgen.deinit();
2711
2712 var gen_scope: Scope.GenZir = .{
14182713 .force_comptime = false,
2714 .parent = &decl.container.base,
2715 .astgen = &astgen,
14192716 };
14202717 defer gen_scope.instructions.deinit(mod.gpa);
14212718
1422 // We need an instruction for each parameter, and they must be first in the body.
1423 try gen_scope.instructions.resize(mod.gpa, param_count);
2719 // Iterate over the parameters. We put the param names as the first N
2720 // items inside `extra` so that debug info later can refer to the parameter names
2721 // even while the respective source code is unloaded.
2722 try astgen.extra.ensureCapacity(mod.gpa, param_count);
2723
14242724 var params_scope = &gen_scope.base;
14252725 var i: usize = 0;
14262726 var it = fn_proto.iterate(tree);
14272727 while (it.next()) |param| : (i += 1) {
14282728 const name_token = param.name_token.?;
1429 const src = token_starts[name_token];
14302729 const param_name = try mod.identifierTokenString(&gen_scope.base, name_token);
1431 const arg = try decl_arena.allocator.create(zir.Inst.Arg);
1432 arg.* = .{
1433 .base = .{
1434 .tag = .arg,
1435 .src = src,
1436 },
1437 .positionals = .{
1438 .name = param_name,
1439 },
1440 .kw_args = .{},
1441 };
1442 gen_scope.instructions.items[i] = &arg.base;
14432730 const sub_scope = try decl_arena.allocator.create(Scope.LocalVal);
14442731 sub_scope.* = .{
14452732 .parent = params_scope,
14462733 .gen_zir = &gen_scope,
14472734 .name = param_name,
1448 .inst = &arg.base,
2735 // Implicit const list first, then implicit arg list.
2736 .inst = @intToEnum(zir.Inst.Ref, @intCast(u32, zir.Inst.Ref.typed_value_map.len + i)),
2737 .src = decl.tokSrcLoc(name_token),
14492738 };
14502739 params_scope = &sub_scope.base;
2740
2741 // Additionally put the param name into `string_bytes` and reference it with
2742 // `extra` so that we have access to the data in codegen, for debug info.
2743 const str_index = @intCast(u32, astgen.string_bytes.items.len);
2744 astgen.extra.appendAssumeCapacity(str_index);
2745 const used_bytes = astgen.string_bytes.items.len;
2746 try astgen.string_bytes.ensureCapacity(mod.gpa, used_bytes + param_name.len + 1);
2747 astgen.string_bytes.appendSliceAssumeCapacity(param_name);
2748 astgen.string_bytes.appendAssumeCapacity(0);
14512749 }
14522750
1453 _ = try astgen.expr(mod, params_scope, .none, body_node);
2751 _ = try AstGen.expr(&gen_scope, params_scope, .none, body_node);
14542752
14552753 if (gen_scope.instructions.items.len == 0 or
1456 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
2754 !astgen.instructions.items(.tag)[gen_scope.instructions.items.len - 1]
2755 .isNoReturn())
14572756 {
1458 const src = token_starts[tree.lastToken(body_node)];
1459 _ = try astgen.addZIRNoOp(mod, &gen_scope.base, src, .return_void);
2757 // astgen uses result location semantics to coerce return operands.
2758 // Since we are adding the return instruction here, we must handle the coercion.
2759 // We do this by using the `ret_coerce` instruction.
2760 _ = try gen_scope.addUnTok(.ret_coerce, .void_value, tree.lastToken(body_node));
14602761 }
14612762
2763 const code = try gen_scope.finish();
14622764 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1463 zir.dumpZir(mod.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};
2765 code.dump(mod.gpa, "fn_body", &gen_scope.base, param_count) catch {};
14642766 }
14652767
1466 break :blk .{
1467 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1468 };
2768 break :blk code;
14692769 };
14702770
14712771 const is_inline = fn_type.fnCallingConvention() == .Inline;
......@@ -1492,6 +2792,7 @@ fn astgenAndSemaFn(
14922792 if (tvm.typed_value.val.castTag(.function)) |payload| {
14932793 const prev_func = payload.data;
14942794 prev_is_inline = prev_func.state == .inline_only;
2795 prev_func.deinit(mod.gpa);
14952796 }
14962797
14972798 tvm.deinit(mod.gpa);
......@@ -1533,7 +2834,7 @@ fn astgenAndSemaFn(
15332834 .{},
15342835 );
15352836 }
1536 const export_src = token_starts[maybe_export_token];
2837 const export_src = decl.tokSrcLoc(maybe_export_token);
15372838 const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString
15382839 // The scope needs to have the decl in it.
15392840 try mod.analyzeExport(&block_scope.base, export_src, name, decl);
......@@ -1552,8 +2853,8 @@ fn astgenAndSemaVarDecl(
15522853 defer tracy.end();
15532854
15542855 decl.analysis = .in_progress;
2856 decl.is_pub = var_decl.visib_token != null;
15552857
1556 const token_starts = tree.tokens.items(.start);
15572858 const token_tags = tree.tokens.items(.tag);
15582859
15592860 // We need the memory for the Type to go into the arena for the Decl
......@@ -1561,54 +2862,29 @@ fn astgenAndSemaVarDecl(
15612862 errdefer decl_arena.deinit();
15622863 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
15632864
1564 var decl_inst_table = Scope.Block.InstTable.init(mod.gpa);
1565 defer decl_inst_table.deinit();
2865 // Used for simple error reporting.
2866 var decl_scope: Scope.DeclRef = .{ .decl = decl };
15662867
1567 var branch_quota: u32 = default_eval_branch_quota;
1568
1569 var block_scope: Scope.Block = .{
1570 .parent = null,
1571 .inst_table = &decl_inst_table,
1572 .func = null,
1573 .owner_decl = decl,
1574 .src_decl = decl,
1575 .instructions = .{},
1576 .arena = &decl_arena.allocator,
1577 .inlining = null,
1578 .is_comptime = true,
1579 .branch_quota = &branch_quota,
1580 };
1581 defer block_scope.instructions.deinit(mod.gpa);
1582
1583 decl.is_pub = var_decl.visib_token != null;
15842868 const is_extern = blk: {
15852869 const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
1586 if (token_tags[maybe_extern_token] != .keyword_extern) break :blk false;
1587 if (var_decl.ast.init_node != 0) {
1588 return mod.failNode(
1589 &block_scope.base,
1590 var_decl.ast.init_node,
1591 "extern variables have no initializers",
1592 .{},
1593 );
1594 }
1595 break :blk true;
2870 break :blk token_tags[maybe_extern_token] == .keyword_extern;
15962871 };
2872
15972873 if (var_decl.lib_name) |lib_name| {
15982874 assert(is_extern);
1599 return mod.failTok(&block_scope.base, lib_name, "TODO implement function library name", .{});
2875 return mod.failTok(&decl_scope.base, lib_name, "TODO implement function library name", .{});
16002876 }
16012877 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
16022878 const is_threadlocal = if (var_decl.threadlocal_token) |some| blk: {
16032879 if (!is_mutable) {
1604 return mod.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
2880 return mod.failTok(&decl_scope.base, some, "threadlocal variable cannot be constant", .{});
16052881 }
16062882 break :blk true;
16072883 } else false;
16082884 assert(var_decl.comptime_token == null);
16092885 if (var_decl.ast.align_node != 0) {
16102886 return mod.failNode(
1611 &block_scope.base,
2887 &decl_scope.base,
16122888 var_decl.ast.align_node,
16132889 "TODO implement function align expression",
16142890 .{},
......@@ -1616,7 +2892,7 @@ fn astgenAndSemaVarDecl(
16162892 }
16172893 if (var_decl.ast.section_node != 0) {
16182894 return mod.failNode(
1619 &block_scope.base,
2895 &decl_scope.base,
16202896 var_decl.ast.section_node,
16212897 "TODO implement function section expression",
16222898 .{},
......@@ -1624,103 +2900,136 @@ fn astgenAndSemaVarDecl(
16242900 }
16252901
16262902 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.ast.init_node != 0) vi: {
2903 if (is_extern) {
2904 return mod.failNode(
2905 &decl_scope.base,
2906 var_decl.ast.init_node,
2907 "extern variables have no initializers",
2908 .{},
2909 );
2910 }
2911
16272912 var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
16282913 defer gen_scope_arena.deinit();
1629 var gen_scope: Scope.GenZIR = .{
1630 .decl = decl,
1631 .arena = &gen_scope_arena.allocator,
1632 .parent = &decl.container.base,
2914
2915 var astgen = try AstGen.init(mod, decl, &gen_scope_arena.allocator);
2916 defer astgen.deinit();
2917
2918 var gen_scope: Scope.GenZir = .{
16332919 .force_comptime = true,
2920 .parent = &decl.container.base,
2921 .astgen = &astgen,
16342922 };
16352923 defer gen_scope.instructions.deinit(mod.gpa);
16362924
1637 const init_result_loc: astgen.ResultLoc = if (var_decl.ast.type_node != 0) rl: {
1638 const type_node = var_decl.ast.type_node;
1639 const src = token_starts[tree.firstToken(type_node)];
1640 const type_type = try astgen.addZIRInstConst(mod, &gen_scope.base, src, .{
1641 .ty = Type.initTag(.type),
1642 .val = Value.initTag(.type_type),
1643 });
1644 const var_type = try astgen.expr(mod, &gen_scope.base, .{ .ty = type_type }, type_node);
1645 break :rl .{ .ty = var_type };
2925 const init_result_loc: AstGen.ResultLoc = if (var_decl.ast.type_node != 0) .{
2926 .ty = try AstGen.expr(&gen_scope, &gen_scope.base, .{ .ty = .type_type }, var_decl.ast.type_node),
16462927 } else .none;
16472928
1648 const init_inst = try astgen.comptimeExpr(
1649 mod,
2929 const init_inst = try AstGen.comptimeExpr(
2930 &gen_scope,
16502931 &gen_scope.base,
16512932 init_result_loc,
16522933 var_decl.ast.init_node,
16532934 );
2935 _ = try gen_scope.addBreak(.break_inline, 0, init_inst);
2936 var code = try gen_scope.finish();
2937 defer code.deinit(mod.gpa);
16542938 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1655 zir.dumpZir(mod.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};
2939 code.dump(mod.gpa, "var_init", &gen_scope.base, 0) catch {};
16562940 }
16572941
1658 var var_inst_table = Scope.Block.InstTable.init(mod.gpa);
1659 defer var_inst_table.deinit();
1660
1661 var branch_quota_vi: u32 = default_eval_branch_quota;
1662 var inner_block: Scope.Block = .{
1663 .parent = null,
1664 .inst_table = &var_inst_table,
1665 .func = null,
2942 var sema: Sema = .{
2943 .mod = mod,
2944 .gpa = mod.gpa,
2945 .arena = &gen_scope_arena.allocator,
2946 .code = code,
2947 .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
16662948 .owner_decl = decl,
2949 .func = null,
2950 .owner_func = null,
2951 .param_inst_list = &.{},
2952 };
2953 var block_scope: Scope.Block = .{
2954 .parent = null,
2955 .sema = &sema,
16672956 .src_decl = decl,
16682957 .instructions = .{},
1669 .arena = &gen_scope_arena.allocator,
16702958 .inlining = null,
16712959 .is_comptime = true,
1672 .branch_quota = &branch_quota_vi,
16732960 };
1674 defer inner_block.instructions.deinit(mod.gpa);
1675 try zir_sema.analyzeBody(mod, &inner_block, .{
1676 .instructions = gen_scope.instructions.items,
1677 });
2961 defer block_scope.instructions.deinit(mod.gpa);
16782962
2963 const init_inst_zir_ref = try sema.rootAsRef(&block_scope);
16792964 // The result location guarantees the type coercion.
1680 const analyzed_init_inst = var_inst_table.get(init_inst).?;
2965 const analyzed_init_inst = try sema.resolveInst(init_inst_zir_ref);
16812966 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
16822967 const val = analyzed_init_inst.value().?;
16832968
1684 const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
16852969 break :vi .{
1686 .ty = ty,
1687 .val = try val.copy(block_scope.arena),
2970 .ty = try analyzed_init_inst.ty.copy(&decl_arena.allocator),
2971 .val = try val.copy(&decl_arena.allocator),
16882972 };
16892973 } else if (!is_extern) {
16902974 return mod.failTok(
1691 &block_scope.base,
2975 &decl_scope.base,
16922976 var_decl.ast.mut_token,
16932977 "variables must be initialized",
16942978 .{},
16952979 );
16962980 } else if (var_decl.ast.type_node != 0) vi: {
1697 const type_node = var_decl.ast.type_node;
1698 // Temporary arena for the zir instructions.
16992981 var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
17002982 defer type_scope_arena.deinit();
1701 var type_scope: Scope.GenZIR = .{
1702 .decl = decl,
1703 .arena = &type_scope_arena.allocator,
1704 .parent = &decl.container.base,
2983
2984 var astgen = try AstGen.init(mod, decl, &type_scope_arena.allocator);
2985 defer astgen.deinit();
2986
2987 var type_scope: Scope.GenZir = .{
17052988 .force_comptime = true,
2989 .parent = &decl.container.base,
2990 .astgen = &astgen,
17062991 };
17072992 defer type_scope.instructions.deinit(mod.gpa);
17082993
1709 const var_type = try astgen.typeExpr(mod, &type_scope.base, type_node);
2994 const var_type = try AstGen.typeExpr(&type_scope, &type_scope.base, var_decl.ast.type_node);
2995 _ = try type_scope.addBreak(.break_inline, 0, var_type);
2996
2997 var code = try type_scope.finish();
2998 defer code.deinit(mod.gpa);
17102999 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1711 zir.dumpZir(mod.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};
3000 code.dump(mod.gpa, "var_type", &type_scope.base, 0) catch {};
17123001 }
17133002
1714 const ty = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, var_type, .{
1715 .instructions = type_scope.instructions.items,
1716 });
3003 var sema: Sema = .{
3004 .mod = mod,
3005 .gpa = mod.gpa,
3006 .arena = &type_scope_arena.allocator,
3007 .code = code,
3008 .inst_map = try type_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
3009 .owner_decl = decl,
3010 .func = null,
3011 .owner_func = null,
3012 .param_inst_list = &.{},
3013 };
3014 var block_scope: Scope.Block = .{
3015 .parent = null,
3016 .sema = &sema,
3017 .src_decl = decl,
3018 .instructions = .{},
3019 .inlining = null,
3020 .is_comptime = true,
3021 };
3022 defer block_scope.instructions.deinit(mod.gpa);
3023
3024 const ty = try sema.rootAsType(&block_scope);
3025
17173026 break :vi .{
1718 .ty = ty,
3027 .ty = try ty.copy(&decl_arena.allocator),
17193028 .val = null,
17203029 };
17213030 } else {
17223031 return mod.failTok(
1723 &block_scope.base,
3032 &decl_scope.base,
17243033 var_decl.ast.mut_token,
17253034 "unable to infer variable type",
17263035 .{},
......@@ -1729,7 +3038,7 @@ fn astgenAndSemaVarDecl(
17293038
17303039 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
17313040 return mod.failTok(
1732 &block_scope.base,
3041 &decl_scope.base,
17333042 var_decl.ast.mut_token,
17343043 "variable of type '{}' must be const",
17353044 .{var_info.ty},
......@@ -1768,57 +3077,57 @@ fn astgenAndSemaVarDecl(
17683077
17693078 if (var_decl.extern_export_token) |maybe_export_token| {
17703079 if (token_tags[maybe_export_token] == .keyword_export) {
1771 const export_src = token_starts[maybe_export_token];
3080 const export_src = decl.tokSrcLoc(maybe_export_token);
17723081 const name_token = var_decl.ast.mut_token + 1;
17733082 const name = tree.tokenSlice(name_token); // TODO identifierTokenString
17743083 // The scope needs to have the decl in it.
1775 try mod.analyzeExport(&block_scope.base, export_src, name, decl);
3084 try mod.analyzeExport(&decl_scope.base, export_src, name, decl);
17763085 }
17773086 }
17783087 return type_changed;
17793088}
17803089
1781fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
1782 try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
1783 try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
3090pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !void {
3091 try depender.dependencies.ensureCapacity(mod.gpa, depender.dependencies.items().len + 1);
3092 try dependee.dependants.ensureCapacity(mod.gpa, dependee.dependants.items().len + 1);
17843093
17853094 depender.dependencies.putAssumeCapacity(dependee, {});
17863095 dependee.dependants.putAssumeCapacity(depender, {});
17873096}
17883097
1789pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree {
3098pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree {
17903099 const tracy = trace(@src());
17913100 defer tracy.end();
17923101
17933102 switch (root_scope.status) {
17943103 .never_loaded, .unloaded_success => {
1795 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
3104 try mod.failed_files.ensureCapacity(mod.gpa, mod.failed_files.items().len + 1);
17963105
1797 const source = try root_scope.getSource(self);
3106 const source = try root_scope.getSource(mod);
17983107
17993108 var keep_tree = false;
1800 root_scope.tree = try std.zig.parse(self.gpa, source);
1801 defer if (!keep_tree) root_scope.tree.deinit(self.gpa);
3109 root_scope.tree = try std.zig.parse(mod.gpa, source);
3110 defer if (!keep_tree) root_scope.tree.deinit(mod.gpa);
18023111
18033112 const tree = &root_scope.tree;
18043113
18053114 if (tree.errors.len != 0) {
18063115 const parse_err = tree.errors[0];
18073116
1808 var msg = std.ArrayList(u8).init(self.gpa);
3117 var msg = std.ArrayList(u8).init(mod.gpa);
18093118 defer msg.deinit();
18103119
18113120 try tree.renderError(parse_err, msg.writer());
1812 const err_msg = try self.gpa.create(ErrorMsg);
3121 const err_msg = try mod.gpa.create(ErrorMsg);
18133122 err_msg.* = .{
18143123 .src_loc = .{
1815 .file_scope = root_scope,
1816 .byte_offset = tree.tokens.items(.start)[parse_err.token],
3124 .container = .{ .file_scope = root_scope },
3125 .lazy = .{ .token_abs = parse_err.token },
18173126 },
18183127 .msg = msg.toOwnedSlice(),
18193128 };
18203129
1821 self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
3130 mod.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
18223131 root_scope.status = .unloaded_parse_failure;
18233132 return error.AnalysisFail;
18243133 }
......@@ -2051,11 +3360,9 @@ fn semaContainerFn(
20513360 const tracy = trace(@src());
20523361 defer tracy.end();
20533362
2054 const token_starts = tree.tokens.items(.start);
2055 const token_tags = tree.tokens.items(.tag);
2056
20573363 // We will create a Decl for it regardless of analysis status.
20583364 const name_tok = fn_proto.name_token orelse {
3365 // This problem will go away with #1717.
20593366 @panic("TODO missing function name");
20603367 };
20613368 const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString
......@@ -2068,8 +3375,8 @@ fn semaContainerFn(
20683375 if (deleted_decls.swapRemove(decl) == null) {
20693376 decl.analysis = .sema_failure;
20703377 const msg = try ErrorMsg.create(mod.gpa, .{
2071 .file_scope = container_scope.file_scope,
2072 .byte_offset = token_starts[name_tok],
3378 .container = .{ .file_scope = container_scope.file_scope },
3379 .lazy = .{ .token_abs = name_tok },
20733380 }, "redefinition of '{s}'", .{decl.name});
20743381 errdefer msg.destroy(mod.gpa);
20753382 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
......@@ -2098,6 +3405,7 @@ fn semaContainerFn(
20983405 const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
20993406 container_scope.decls.putAssumeCapacity(new_decl, {});
21003407 if (fn_proto.extern_export_token) |maybe_export_token| {
3408 const token_tags = tree.tokens.items(.tag);
21013409 if (token_tags[maybe_export_token] == .keyword_export) {
21023410 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
21033411 }
......@@ -2117,11 +3425,7 @@ fn semaContainerVar(
21173425 const tracy = trace(@src());
21183426 defer tracy.end();
21193427
2120 const token_starts = tree.tokens.items(.start);
2121 const token_tags = tree.tokens.items(.tag);
2122
21233428 const name_token = var_decl.ast.mut_token + 1;
2124 const name_src = token_starts[name_token];
21253429 const name = tree.tokenSlice(name_token); // TODO identifierTokenString
21263430 const name_hash = container_scope.fullyQualifiedNameHash(name);
21273431 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
......@@ -2132,8 +3436,8 @@ fn semaContainerVar(
21323436 if (deleted_decls.swapRemove(decl) == null) {
21333437 decl.analysis = .sema_failure;
21343438 const err_msg = try ErrorMsg.create(mod.gpa, .{
2135 .file_scope = container_scope.file_scope,
2136 .byte_offset = name_src,
3439 .container = .{ .file_scope = container_scope.file_scope },
3440 .lazy = .{ .token_abs = name_token },
21373441 }, "redefinition of '{s}'", .{decl.name});
21383442 errdefer err_msg.destroy(mod.gpa);
21393443 try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg);
......@@ -2145,6 +3449,7 @@ fn semaContainerVar(
21453449 const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
21463450 container_scope.decls.putAssumeCapacity(new_decl, {});
21473451 if (var_decl.extern_export_token) |maybe_export_token| {
3452 const token_tags = tree.tokens.items(.tag);
21483453 if (token_tags[maybe_export_token] == .keyword_export) {
21493454 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
21503455 }
......@@ -2167,11 +3472,11 @@ fn semaContainerField(
21673472 log.err("TODO: analyze container field", .{});
21683473}
21693474
2170pub fn deleteDecl(self: *Module, decl: *Decl) !void {
3475pub fn deleteDecl(mod: *Module, decl: *Decl) !void {
21713476 const tracy = trace(@src());
21723477 defer tracy.end();
21733478
2174 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
3479 try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.items.len + decl.dependencies.items().len);
21753480
21763481 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
21773482 // not be present in the set, and this does nothing.
......@@ -2179,7 +3484,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {
21793484
21803485 log.debug("deleting decl '{s}'", .{decl.name});
21813486 const name_hash = decl.fullyQualifiedNameHash();
2182 self.decl_table.removeAssertDiscard(name_hash);
3487 mod.decl_table.removeAssertDiscard(name_hash);
21833488 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
21843489 for (decl.dependencies.items()) |entry| {
21853490 const dep = entry.key;
......@@ -2188,7 +3493,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {
21883493 // We don't recursively perform a deletion here, because during the update,
21893494 // another reference to it may turn up.
21903495 dep.deletion_flag = true;
2191 self.deletion_set.appendAssumeCapacity(dep);
3496 mod.deletion_set.appendAssumeCapacity(dep);
21923497 }
21933498 }
21943499 // Anything that depends on this deleted decl certainly needs to be re-analyzed.
......@@ -2197,29 +3502,29 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {
21973502 dep.removeDependency(decl);
21983503 if (dep.analysis != .outdated) {
21993504 // TODO Move this failure possibility to the top of the function.
2200 try self.markOutdatedDecl(dep);
3505 try mod.markOutdatedDecl(dep);
22013506 }
22023507 }
2203 if (self.failed_decls.swapRemove(decl)) |entry| {
2204 entry.value.destroy(self.gpa);
3508 if (mod.failed_decls.swapRemove(decl)) |entry| {
3509 entry.value.destroy(mod.gpa);
22053510 }
2206 if (self.emit_h_failed_decls.swapRemove(decl)) |entry| {
2207 entry.value.destroy(self.gpa);
3511 if (mod.emit_h_failed_decls.swapRemove(decl)) |entry| {
3512 entry.value.destroy(mod.gpa);
22083513 }
2209 _ = self.compile_log_decls.swapRemove(decl);
2210 self.deleteDeclExports(decl);
2211 self.comp.bin_file.freeDecl(decl);
3514 _ = mod.compile_log_decls.swapRemove(decl);
3515 mod.deleteDeclExports(decl);
3516 mod.comp.bin_file.freeDecl(decl);
22123517
2213 decl.destroy(self);
3518 decl.destroy(mod);
22143519}
22153520
22163521/// Delete all the Export objects that are caused by this Decl. Re-analysis of
22173522/// this Decl will cause them to be re-created (or not).
2218fn deleteDeclExports(self: *Module, decl: *Decl) void {
2219 const kv = self.export_owners.swapRemove(decl) orelse return;
3523fn deleteDeclExports(mod: *Module, decl: *Decl) void {
3524 const kv = mod.export_owners.swapRemove(decl) orelse return;
22203525
22213526 for (kv.value) |exp| {
2222 if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
3527 if (mod.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
22233528 // Remove exports with owner_decl matching the regenerating decl.
22243529 const list = decl_exports_kv.value;
22253530 var i: usize = 0;
......@@ -2232,73 +3537,101 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
22323537 i += 1;
22333538 }
22343539 }
2235 decl_exports_kv.value = self.gpa.shrink(list, new_len);
3540 decl_exports_kv.value = mod.gpa.shrink(list, new_len);
22363541 if (new_len == 0) {
2237 self.decl_exports.removeAssertDiscard(exp.exported_decl);
3542 mod.decl_exports.removeAssertDiscard(exp.exported_decl);
22383543 }
22393544 }
2240 if (self.comp.bin_file.cast(link.File.Elf)) |elf| {
3545 if (mod.comp.bin_file.cast(link.File.Elf)) |elf| {
22413546 elf.deleteExport(exp.link.elf);
22423547 }
2243 if (self.comp.bin_file.cast(link.File.MachO)) |macho| {
3548 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {
22443549 macho.deleteExport(exp.link.macho);
22453550 }
2246 if (self.failed_exports.swapRemove(exp)) |entry| {
2247 entry.value.destroy(self.gpa);
3551 if (mod.failed_exports.swapRemove(exp)) |entry| {
3552 entry.value.destroy(mod.gpa);
22483553 }
2249 _ = self.symbol_exports.swapRemove(exp.options.name);
2250 self.gpa.free(exp.options.name);
2251 self.gpa.destroy(exp);
3554 _ = mod.symbol_exports.swapRemove(exp.options.name);
3555 mod.gpa.free(exp.options.name);
3556 mod.gpa.destroy(exp);
22523557 }
2253 self.gpa.free(kv.value);
3558 mod.gpa.free(kv.value);
22543559}
22553560
2256pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
3561pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
22573562 const tracy = trace(@src());
22583563 defer tracy.end();
22593564
22603565 // Use the Decl's arena for function memory.
2261 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
3566 var arena = decl.typed_value.most_recent.arena.?.promote(mod.gpa);
22623567 defer decl.typed_value.most_recent.arena.?.* = arena.state;
2263 var inst_table = Scope.Block.InstTable.init(self.gpa);
2264 defer inst_table.deinit();
2265 var branch_quota: u32 = default_eval_branch_quota;
3568
3569 const fn_ty = decl.typed_value.most_recent.typed_value.ty;
3570 const param_inst_list = try mod.gpa.alloc(*ir.Inst, fn_ty.fnParamLen());
3571 defer mod.gpa.free(param_inst_list);
3572
3573 for (param_inst_list) |*param_inst, param_index| {
3574 const param_type = fn_ty.fnParamType(param_index);
3575 const name = func.zir.nullTerminatedString(func.zir.extra[param_index]);
3576 const arg_inst = try arena.allocator.create(ir.Inst.Arg);
3577 arg_inst.* = .{
3578 .base = .{
3579 .tag = .arg,
3580 .ty = param_type,
3581 .src = .unneeded,
3582 },
3583 .name = name,
3584 };
3585 param_inst.* = &arg_inst.base;
3586 }
3587
3588 var sema: Sema = .{
3589 .mod = mod,
3590 .gpa = mod.gpa,
3591 .arena = &arena.allocator,
3592 .code = func.zir,
3593 .inst_map = try mod.gpa.alloc(*ir.Inst, func.zir.instructions.len),
3594 .owner_decl = decl,
3595 .func = func,
3596 .owner_func = func,
3597 .param_inst_list = param_inst_list,
3598 };
3599 defer mod.gpa.free(sema.inst_map);
22663600
22673601 var inner_block: Scope.Block = .{
22683602 .parent = null,
2269 .inst_table = &inst_table,
2270 .func = func,
2271 .owner_decl = decl,
3603 .sema = &sema,
22723604 .src_decl = decl,
22733605 .instructions = .{},
2274 .arena = &arena.allocator,
22753606 .inlining = null,
22763607 .is_comptime = false,
2277 .branch_quota = &branch_quota,
22783608 };
2279 defer inner_block.instructions.deinit(self.gpa);
3609 defer inner_block.instructions.deinit(mod.gpa);
3610
3611 // TZIR currently requires the arg parameters to be the first N instructions
3612 try inner_block.instructions.appendSlice(mod.gpa, param_inst_list);
22803613
22813614 func.state = .in_progress;
22823615 log.debug("set {s} to in_progress", .{decl.name});
22833616
2284 try zir_sema.analyzeBody(self, &inner_block, func.zir);
3617 _ = try sema.root(&inner_block);
22853618
2286 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
3619 const instructions = try arena.allocator.dupe(*ir.Inst, inner_block.instructions.items);
22873620 func.state = .success;
22883621 func.body = .{ .instructions = instructions };
22893622 log.debug("set {s} to success", .{decl.name});
22903623}
22913624
2292fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
3625fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
22933626 log.debug("mark {s} outdated", .{decl.name});
2294 try self.comp.work_queue.writeItem(.{ .analyze_decl = decl });
2295 if (self.failed_decls.swapRemove(decl)) |entry| {
2296 entry.value.destroy(self.gpa);
3627 try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl });
3628 if (mod.failed_decls.swapRemove(decl)) |entry| {
3629 entry.value.destroy(mod.gpa);
22973630 }
2298 if (self.emit_h_failed_decls.swapRemove(decl)) |entry| {
2299 entry.value.destroy(self.gpa);
3631 if (mod.emit_h_failed_decls.swapRemove(decl)) |entry| {
3632 entry.value.destroy(mod.gpa);
23003633 }
2301 _ = self.compile_log_decls.swapRemove(decl);
3634 _ = mod.compile_log_decls.swapRemove(decl);
23023635 decl.analysis = .outdated;
23033636}
23043637
......@@ -2349,65 +3682,39 @@ fn allocateNewDecl(
23493682}
23503683
23513684fn createNewDecl(
2352 self: *Module,
3685 mod: *Module,
23533686 scope: *Scope,
23543687 decl_name: []const u8,
23553688 src_index: usize,
23563689 name_hash: Scope.NameHash,
23573690 contents_hash: std.zig.SrcHash,
23583691) !*Decl {
2359 try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1);
2360 const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash);
2361 errdefer self.gpa.destroy(new_decl);
2362 new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name);
2363 self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
3692 try mod.decl_table.ensureCapacity(mod.gpa, mod.decl_table.items().len + 1);
3693 const new_decl = try mod.allocateNewDecl(scope, src_index, contents_hash);
3694 errdefer mod.gpa.destroy(new_decl);
3695 new_decl.name = try mem.dupeZ(mod.gpa, u8, decl_name);
3696 mod.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
23643697 return new_decl;
23653698}
23663699
23673700/// Get error value for error tag `name`.
2368pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry {
2369 const gop = try self.global_error_set.getOrPut(self.gpa, name);
3701pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged(ErrorInt).Entry {
3702 const gop = try mod.global_error_set.getOrPut(mod.gpa, name);
23703703 if (gop.found_existing)
23713704 return gop.entry.*;
2372 errdefer self.global_error_set.removeAssertDiscard(name);
23733705
2374 gop.entry.key = try self.gpa.dupe(u8, name);
2375 gop.entry.value = @intCast(u16, self.global_error_set.count() - 1);
3706 errdefer mod.global_error_set.removeAssertDiscard(name);
3707 try mod.error_name_list.ensureCapacity(mod.gpa, mod.error_name_list.items.len + 1);
3708 gop.entry.key = try mod.gpa.dupe(u8, name);
3709 gop.entry.value = @intCast(ErrorInt, mod.error_name_list.items.len);
3710 mod.error_name_list.appendAssumeCapacity(gop.entry.key);
23763711 return gop.entry.*;
23773712}
23783713
2379pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
2380 return scope.cast(Scope.Block) orelse
2381 return self.fail(scope, src, "instruction illegal outside function body", .{});
2382}
2383
2384pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
2385 const block = try self.requireFunctionBlock(scope, src);
2386 if (block.is_comptime) {
2387 return self.fail(scope, src, "unable to resolve comptime value", .{});
2388 }
2389 return block;
2390}
2391
2392pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
2393 return (try self.resolveDefinedValue(scope, base)) orelse
2394 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
2395}
2396
2397pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
2398 if (base.value()) |val| {
2399 if (val.isUndef()) {
2400 return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
2401 }
2402 return val;
2403 }
2404 return null;
2405}
2406
24073714pub fn analyzeExport(
24083715 mod: *Module,
24093716 scope: *Scope,
2410 src: usize,
3717 src: LazySrcLoc,
24113718 borrowed_symbol_name: []const u8,
24123719 exported_decl: *Decl,
24133720) !void {
......@@ -2496,178 +3803,11 @@ pub fn analyzeExport(
24963803 },
24973804 };
24983805}
2499
2500pub fn addNoOp(
2501 self: *Module,
2502 block: *Scope.Block,
2503 src: usize,
2504 ty: Type,
2505 comptime tag: Inst.Tag,
2506) !*Inst {
2507 const inst = try block.arena.create(tag.Type());
2508 inst.* = .{
2509 .base = .{
2510 .tag = tag,
2511 .ty = ty,
2512 .src = src,
2513 },
2514 };
2515 try block.instructions.append(self.gpa, &inst.base);
2516 return &inst.base;
2517}
2518
2519pub fn addUnOp(
2520 self: *Module,
2521 block: *Scope.Block,
2522 src: usize,
2523 ty: Type,
2524 tag: Inst.Tag,
2525 operand: *Inst,
2526) !*Inst {
2527 const inst = try block.arena.create(Inst.UnOp);
2528 inst.* = .{
2529 .base = .{
2530 .tag = tag,
2531 .ty = ty,
2532 .src = src,
2533 },
2534 .operand = operand,
2535 };
2536 try block.instructions.append(self.gpa, &inst.base);
2537 return &inst.base;
2538}
2539
2540pub fn addBinOp(
2541 self: *Module,
2542 block: *Scope.Block,
2543 src: usize,
2544 ty: Type,
2545 tag: Inst.Tag,
2546 lhs: *Inst,
2547 rhs: *Inst,
2548) !*Inst {
2549 const inst = try block.arena.create(Inst.BinOp);
2550 inst.* = .{
2551 .base = .{
2552 .tag = tag,
2553 .ty = ty,
2554 .src = src,
2555 },
2556 .lhs = lhs,
2557 .rhs = rhs,
2558 };
2559 try block.instructions.append(self.gpa, &inst.base);
2560 return &inst.base;
2561}
2562
2563pub fn addArg(self: *Module, block: *Scope.Block, src: usize, ty: Type, name: [*:0]const u8) !*Inst {
2564 const inst = try block.arena.create(Inst.Arg);
2565 inst.* = .{
2566 .base = .{
2567 .tag = .arg,
2568 .ty = ty,
2569 .src = src,
2570 },
2571 .name = name,
2572 };
2573 try block.instructions.append(self.gpa, &inst.base);
2574 return &inst.base;
2575}
2576
2577pub fn addBr(
2578 self: *Module,
2579 scope_block: *Scope.Block,
2580 src: usize,
2581 target_block: *Inst.Block,
2582 operand: *Inst,
2583) !*Inst.Br {
2584 const inst = try scope_block.arena.create(Inst.Br);
2585 inst.* = .{
2586 .base = .{
2587 .tag = .br,
2588 .ty = Type.initTag(.noreturn),
2589 .src = src,
2590 },
2591 .operand = operand,
2592 .block = target_block,
2593 };
2594 try scope_block.instructions.append(self.gpa, &inst.base);
2595 return inst;
2596}
2597
2598pub fn addCondBr(
2599 self: *Module,
2600 block: *Scope.Block,
2601 src: usize,
2602 condition: *Inst,
2603 then_body: ir.Body,
2604 else_body: ir.Body,
2605) !*Inst {
2606 const inst = try block.arena.create(Inst.CondBr);
2607 inst.* = .{
2608 .base = .{
2609 .tag = .condbr,
2610 .ty = Type.initTag(.noreturn),
2611 .src = src,
2612 },
2613 .condition = condition,
2614 .then_body = then_body,
2615 .else_body = else_body,
2616 };
2617 try block.instructions.append(self.gpa, &inst.base);
2618 return &inst.base;
2619}
2620
2621pub fn addCall(
2622 self: *Module,
2623 block: *Scope.Block,
2624 src: usize,
2625 ty: Type,
2626 func: *Inst,
2627 args: []const *Inst,
2628) !*Inst {
2629 const inst = try block.arena.create(Inst.Call);
2630 inst.* = .{
2631 .base = .{
2632 .tag = .call,
2633 .ty = ty,
2634 .src = src,
2635 },
2636 .func = func,
2637 .args = args,
2638 };
2639 try block.instructions.append(self.gpa, &inst.base);
2640 return &inst.base;
2641}
2642
2643pub fn addSwitchBr(
2644 self: *Module,
2645 block: *Scope.Block,
2646 src: usize,
2647 target: *Inst,
2648 cases: []Inst.SwitchBr.Case,
2649 else_body: ir.Body,
2650) !*Inst {
2651 const inst = try block.arena.create(Inst.SwitchBr);
2652 inst.* = .{
2653 .base = .{
2654 .tag = .switchbr,
2655 .ty = Type.initTag(.noreturn),
2656 .src = src,
2657 },
2658 .target = target,
2659 .cases = cases,
2660 .else_body = else_body,
2661 };
2662 try block.instructions.append(self.gpa, &inst.base);
2663 return &inst.base;
2664}
2665
2666pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
2667 const const_inst = try scope.arena().create(Inst.Constant);
3806pub fn constInst(mod: *Module, arena: *Allocator, src: LazySrcLoc, typed_value: TypedValue) !*ir.Inst {
3807 const const_inst = try arena.create(ir.Inst.Constant);
26683808 const_inst.* = .{
26693809 .base = .{
2670 .tag = Inst.Constant.base_tag,
3810 .tag = ir.Inst.Constant.base_tag,
26713811 .ty = typed_value.ty,
26723812 .src = src,
26733813 },
......@@ -2676,94 +3816,94 @@ pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedVal
26763816 return &const_inst.base;
26773817}
26783818
2679pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2680 return self.constInst(scope, src, .{
3819pub fn constType(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type) !*ir.Inst {
3820 return mod.constInst(arena, src, .{
26813821 .ty = Type.initTag(.type),
2682 .val = try ty.toValue(scope.arena()),
3822 .val = try ty.toValue(arena),
26833823 });
26843824}
26853825
2686pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
2687 return self.constInst(scope, src, .{
3826pub fn constVoid(mod: *Module, arena: *Allocator, src: LazySrcLoc) !*ir.Inst {
3827 return mod.constInst(arena, src, .{
26883828 .ty = Type.initTag(.void),
26893829 .val = Value.initTag(.void_value),
26903830 });
26913831}
26923832
2693pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
2694 return self.constInst(scope, src, .{
3833pub fn constNoReturn(mod: *Module, arena: *Allocator, src: LazySrcLoc) !*ir.Inst {
3834 return mod.constInst(arena, src, .{
26953835 .ty = Type.initTag(.noreturn),
26963836 .val = Value.initTag(.unreachable_value),
26973837 });
26983838}
26993839
2700pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2701 return self.constInst(scope, src, .{
3840pub fn constUndef(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type) !*ir.Inst {
3841 return mod.constInst(arena, src, .{
27023842 .ty = ty,
27033843 .val = Value.initTag(.undef),
27043844 });
27053845}
27063846
2707pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
2708 return self.constInst(scope, src, .{
3847pub fn constBool(mod: *Module, arena: *Allocator, src: LazySrcLoc, v: bool) !*ir.Inst {
3848 return mod.constInst(arena, src, .{
27093849 .ty = Type.initTag(.bool),
27103850 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
27113851 });
27123852}
27133853
2714pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
2715 return self.constInst(scope, src, .{
3854pub fn constIntUnsigned(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, int: u64) !*ir.Inst {
3855 return mod.constInst(arena, src, .{
27163856 .ty = ty,
2717 .val = try Value.Tag.int_u64.create(scope.arena(), int),
3857 .val = try Value.Tag.int_u64.create(arena, int),
27183858 });
27193859}
27203860
2721pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
2722 return self.constInst(scope, src, .{
3861pub fn constIntSigned(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, int: i64) !*ir.Inst {
3862 return mod.constInst(arena, src, .{
27233863 .ty = ty,
2724 .val = try Value.Tag.int_i64.create(scope.arena(), int),
3864 .val = try Value.Tag.int_i64.create(arena, int),
27253865 });
27263866}
27273867
2728pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
3868pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, big_int: BigIntConst) !*ir.Inst {
27293869 if (big_int.positive) {
27303870 if (big_int.to(u64)) |x| {
2731 return self.constIntUnsigned(scope, src, ty, x);
3871 return mod.constIntUnsigned(arena, src, ty, x);
27323872 } else |err| switch (err) {
27333873 error.NegativeIntoUnsigned => unreachable,
27343874 error.TargetTooSmall => {}, // handled below
27353875 }
2736 return self.constInst(scope, src, .{
3876 return mod.constInst(arena, src, .{
27373877 .ty = ty,
2738 .val = try Value.Tag.int_big_positive.create(scope.arena(), big_int.limbs),
3878 .val = try Value.Tag.int_big_positive.create(arena, big_int.limbs),
27393879 });
27403880 } else {
27413881 if (big_int.to(i64)) |x| {
2742 return self.constIntSigned(scope, src, ty, x);
3882 return mod.constIntSigned(arena, src, ty, x);
27433883 } else |err| switch (err) {
27443884 error.NegativeIntoUnsigned => unreachable,
27453885 error.TargetTooSmall => {}, // handled below
27463886 }
2747 return self.constInst(scope, src, .{
3887 return mod.constInst(arena, src, .{
27483888 .ty = ty,
2749 .val = try Value.Tag.int_big_negative.create(scope.arena(), big_int.limbs),
3889 .val = try Value.Tag.int_big_negative.create(arena, big_int.limbs),
27503890 });
27513891 }
27523892}
27533893
27543894pub fn createAnonymousDecl(
2755 self: *Module,
3895 mod: *Module,
27563896 scope: *Scope,
27573897 decl_arena: *std.heap.ArenaAllocator,
27583898 typed_value: TypedValue,
27593899) !*Decl {
2760 const name_index = self.getNextAnonNameIndex();
3900 const name_index = mod.getNextAnonNameIndex();
27613901 const scope_decl = scope.ownerDecl().?;
2762 const name = try std.fmt.allocPrint(self.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index });
2763 defer self.gpa.free(name);
3902 const name = try std.fmt.allocPrint(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index });
3903 defer mod.gpa.free(name);
27643904 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
27653905 const src_hash: std.zig.SrcHash = undefined;
2766 const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
3906 const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
27673907 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
27683908
27693909 decl_arena_state.* = decl_arena.state;
......@@ -2774,32 +3914,32 @@ pub fn createAnonymousDecl(
27743914 },
27753915 };
27763916 new_decl.analysis = .complete;
2777 new_decl.generation = self.generation;
3917 new_decl.generation = mod.generation;
27783918
2779 // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size.
2780 // We should be able to further improve the compiler to not omit Decls which are only referenced at
2781 // compile-time and not runtime.
3919 // TODO: This generates the Decl into the machine code file if it is of a
3920 // type that is non-zero size. We should be able to further improve the
3921 // compiler to omit Decls which are only referenced at compile-time and not runtime.
27823922 if (typed_value.ty.hasCodeGenBits()) {
2783 try self.comp.bin_file.allocateDeclIndexes(new_decl);
2784 try self.comp.work_queue.writeItem(.{ .codegen_decl = new_decl });
3923 try mod.comp.bin_file.allocateDeclIndexes(new_decl);
3924 try mod.comp.work_queue.writeItem(.{ .codegen_decl = new_decl });
27853925 }
27863926
27873927 return new_decl;
27883928}
27893929
27903930pub fn createContainerDecl(
2791 self: *Module,
3931 mod: *Module,
27923932 scope: *Scope,
27933933 base_token: std.zig.ast.TokenIndex,
27943934 decl_arena: *std.heap.ArenaAllocator,
27953935 typed_value: TypedValue,
27963936) !*Decl {
27973937 const scope_decl = scope.ownerDecl().?;
2798 const name = try self.getAnonTypeName(scope, base_token);
2799 defer self.gpa.free(name);
3938 const name = try mod.getAnonTypeName(scope, base_token);
3939 defer mod.gpa.free(name);
28003940 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
28013941 const src_hash: std.zig.SrcHash = undefined;
2802 const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
3942 const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
28033943 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
28043944
28053945 decl_arena_state.* = decl_arena.state;
......@@ -2810,12 +3950,12 @@ pub fn createContainerDecl(
28103950 },
28113951 };
28123952 new_decl.analysis = .complete;
2813 new_decl.generation = self.generation;
3953 new_decl.generation = mod.generation;
28143954
28153955 return new_decl;
28163956}
28173957
2818fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
3958fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
28193959 // TODO add namespaces, generic function signatrues
28203960 const tree = scope.tree();
28213961 const token_tags = tree.tokens.items(.tag);
......@@ -2827,775 +3967,39 @@ fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIn
28273967 else => unreachable,
28283968 };
28293969 const loc = tree.tokenLocation(0, base_token);
2830 return std.fmt.allocPrint(self.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column });
3970 return std.fmt.allocPrint(mod.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column });
28313971}
28323972
2833fn getNextAnonNameIndex(self: *Module) usize {
2834 return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
3973fn getNextAnonNameIndex(mod: *Module) usize {
3974 return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic);
28353975}
28363976
2837pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
3977pub fn lookupDeclName(mod: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
28383978 const namespace = scope.namespace();
28393979 const name_hash = namespace.fullyQualifiedNameHash(ident_name);
2840 return self.decl_table.get(name_hash);
2841}
2842
2843pub fn analyzeDeclVal(mod: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
2844 const decl_ref = try mod.analyzeDeclRef(scope, src, decl);
2845 return mod.analyzeDeref(scope, src, decl_ref, src);
3980 return mod.decl_table.get(name_hash);
28463981}
28473982
2848pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
2849 const scope_decl = scope.ownerDecl().?;
2850 try self.declareDeclDependency(scope_decl, decl);
2851 self.ensureDeclAnalyzed(decl) catch |err| {
2852 if (scope.cast(Scope.Block)) |block| {
2853 if (block.func) |func| {
2854 func.state = .dependency_failure;
2855 } else {
2856 block.owner_decl.analysis = .dependency_failure;
2857 }
2858 } else {
2859 scope_decl.analysis = .dependency_failure;
2860 }
2861 return err;
2862 };
2863
2864 const decl_tv = try decl.typedValue();
2865 if (decl_tv.val.tag() == .variable) {
2866 return self.analyzeVarRef(scope, src, decl_tv);
2867 }
2868 return self.constInst(scope, src, .{
2869 .ty = try self.simplePtrType(scope, src, decl_tv.ty, false, .One),
2870 .val = try Value.Tag.decl_ref.create(scope.arena(), decl),
2871 });
2872}
2873
2874fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst {
2875 const variable = tv.val.castTag(.variable).?.data;
2876
2877 const ty = try self.simplePtrType(scope, src, tv.ty, variable.is_mutable, .One);
2878 if (!variable.is_mutable and !variable.is_extern) {
2879 return self.constInst(scope, src, .{
2880 .ty = ty,
2881 .val = try Value.Tag.ref_val.create(scope.arena(), variable.init),
2882 });
2883 }
2884
2885 const b = try self.requireRuntimeBlock(scope, src);
2886 const inst = try b.arena.create(Inst.VarPtr);
2887 inst.* = .{
2888 .base = .{
2889 .tag = .varptr,
2890 .ty = ty,
2891 .src = src,
2892 },
2893 .variable = variable,
2894 };
2895 try b.instructions.append(self.gpa, &inst.base);
2896 return &inst.base;
2897}
2898
2899pub fn analyzeRef(mod: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
2900 const ptr_type = try mod.simplePtrType(scope, src, operand.ty, false, .One);
2901
2902 if (operand.value()) |val| {
2903 return mod.constInst(scope, src, .{
2904 .ty = ptr_type,
2905 .val = try Value.Tag.ref_val.create(scope.arena(), val),
2906 });
2907 }
2908
2909 const b = try mod.requireRuntimeBlock(scope, src);
2910 return mod.addUnOp(b, src, ptr_type, .ref, operand);
2911}
2912
2913pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
2914 const elem_ty = switch (ptr.ty.zigTypeTag()) {
2915 .Pointer => ptr.ty.elemType(),
2916 else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
2917 };
2918 if (ptr.value()) |val| {
2919 return self.constInst(scope, src, .{
2920 .ty = elem_ty,
2921 .val = try val.pointerDeref(scope.arena()),
2922 });
2923 }
2924
2925 const b = try self.requireRuntimeBlock(scope, src);
2926 return self.addUnOp(b, src, elem_ty, .load, ptr);
2927}
2928
2929pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
2930 const decl = self.lookupDeclName(scope, decl_name) orelse
2931 return self.fail(scope, src, "decl '{s}' not found", .{decl_name});
2932 return self.analyzeDeclRef(scope, src, decl);
2933}
2934
2935pub fn wantSafety(self: *Module, scope: *Scope) bool {
2936 // TODO take into account scope's safety overrides
2937 return switch (self.optimizeMode()) {
2938 .Debug => true,
2939 .ReleaseSafe => true,
2940 .ReleaseFast => false,
2941 .ReleaseSmall => false,
2942 };
2943}
2944
2945pub fn analyzeIsNull(
2946 self: *Module,
2947 scope: *Scope,
2948 src: usize,
2949 operand: *Inst,
2950 invert_logic: bool,
2951) InnerError!*Inst {
2952 if (operand.value()) |opt_val| {
2953 const is_null = opt_val.isNull();
2954 const bool_value = if (invert_logic) !is_null else is_null;
2955 return self.constBool(scope, src, bool_value);
2956 }
2957 const b = try self.requireRuntimeBlock(scope, src);
2958 const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null;
2959 return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand);
2960}
2961
2962pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
2963 const ot = operand.ty.zigTypeTag();
2964 if (ot != .ErrorSet and ot != .ErrorUnion) return self.constBool(scope, src, false);
2965 if (ot == .ErrorSet) return self.constBool(scope, src, true);
2966 assert(ot == .ErrorUnion);
2967 if (operand.value()) |err_union| {
2968 return self.constBool(scope, src, err_union.getError() != null);
2969 }
2970 const b = try self.requireRuntimeBlock(scope, src);
2971 return self.addUnOp(b, src, Type.initTag(.bool), .is_err, operand);
2972}
2973
2974pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
2975 const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
2976 .Pointer => array_ptr.ty.elemType(),
2977 else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}),
2978 };
2979
2980 var array_type = ptr_child;
2981 const elem_type = switch (ptr_child.zigTypeTag()) {
2982 .Array => ptr_child.elemType(),
2983 .Pointer => blk: {
2984 if (ptr_child.isSinglePointer()) {
2985 if (ptr_child.elemType().zigTypeTag() == .Array) {
2986 array_type = ptr_child.elemType();
2987 break :blk ptr_child.elemType().elemType();
2988 }
2989
2990 return self.fail(scope, src, "slice of single-item pointer", .{});
2991 }
2992 break :blk ptr_child.elemType();
2993 },
2994 else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}),
2995 };
2996
2997 const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
2998 const casted = try self.coerce(scope, elem_type, sentinel);
2999 break :blk try self.resolveConstValue(scope, casted);
3000 } else null;
3001
3002 var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
3003 var return_elem_type = elem_type;
3004 if (end_opt) |end| {
3005 if (end.value()) |end_val| {
3006 if (start.value()) |start_val| {
3007 const start_u64 = start_val.toUnsignedInt();
3008 const end_u64 = end_val.toUnsignedInt();
3009 if (start_u64 > end_u64) {
3010 return self.fail(scope, src, "out of bounds slice", .{});
3011 }
3012
3013 const len = end_u64 - start_u64;
3014 const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
3015 array_type.sentinel()
3016 else
3017 slice_sentinel;
3018 return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type);
3019 return_ptr_size = .One;
3020 }
3021 }
3022 }
3023 const return_type = try self.ptrType(
3024 scope,
3025 src,
3026 return_elem_type,
3027 if (end_opt == null) slice_sentinel else null,
3028 0, // TODO alignment
3029 0,
3030 0,
3031 !ptr_child.isConstPtr(),
3032 ptr_child.isAllowzeroPtr(),
3033 ptr_child.isVolatilePtr(),
3034 return_ptr_size,
3035 );
3036
3037 return self.fail(scope, src, "TODO implement analysis of slice", .{});
3038}
3039
3040pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []const u8) !*Scope.File {
3041 const cur_pkg = scope.getFileScope().pkg;
3042 const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse ".";
3043 const found_pkg = cur_pkg.table.get(target_string);
3044
3045 const resolved_path = if (found_pkg) |pkg|
3046 try std.fs.path.resolve(self.gpa, &[_][]const u8{ pkg.root_src_directory.path orelse ".", pkg.root_src_path })
3047 else
3048 try std.fs.path.resolve(self.gpa, &[_][]const u8{ cur_pkg_dir_path, target_string });
3049 errdefer self.gpa.free(resolved_path);
3050
3051 if (self.import_table.get(resolved_path)) |some| {
3052 self.gpa.free(resolved_path);
3053 return some;
3054 }
3055
3056 if (found_pkg == null) {
3057 const resolved_root_path = try std.fs.path.resolve(self.gpa, &[_][]const u8{cur_pkg_dir_path});
3058 defer self.gpa.free(resolved_root_path);
3059
3060 if (!mem.startsWith(u8, resolved_path, resolved_root_path)) {
3061 return error.ImportOutsidePkgPath;
3062 }
3063 }
3064
3065 // TODO Scope.Container arena for ty and sub_file_path
3066 const file_scope = try self.gpa.create(Scope.File);
3067 errdefer self.gpa.destroy(file_scope);
3068 const struct_ty = try Type.Tag.empty_struct.create(self.gpa, &file_scope.root_container);
3069 errdefer self.gpa.destroy(struct_ty.castTag(.empty_struct).?);
3070
3071 file_scope.* = .{
3072 .sub_file_path = resolved_path,
3073 .source = .{ .unloaded = {} },
3074 .tree = undefined,
3075 .status = .never_loaded,
3076 .pkg = found_pkg orelse cur_pkg,
3077 .root_container = .{
3078 .file_scope = file_scope,
3079 .decls = .{},
3080 .ty = struct_ty,
3081 },
3082 };
3083 self.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
3084 error.AnalysisFail => {
3085 assert(self.comp.totalErrorCount() != 0);
3086 },
3087 else => |e| return e,
3088 };
3089 try self.import_table.put(self.gpa, file_scope.sub_file_path, file_scope);
3090 return file_scope;
3091}
3092
3093/// Asserts that lhs and rhs types are both numeric.
3094pub fn cmpNumeric(
3095 self: *Module,
3096 scope: *Scope,
3097 src: usize,
3098 lhs: *Inst,
3099 rhs: *Inst,
3100 op: std.math.CompareOperator,
3101) InnerError!*Inst {
3102 assert(lhs.ty.isNumeric());
3103 assert(rhs.ty.isNumeric());
3104
3105 const lhs_ty_tag = lhs.ty.zigTypeTag();
3106 const rhs_ty_tag = rhs.ty.zigTypeTag();
3107
3108 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
3109 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
3110 return self.fail(scope, src, "vector length mismatch: {d} and {d}", .{
3111 lhs.ty.arrayLen(),
3112 rhs.ty.arrayLen(),
3113 });
3114 }
3115 return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
3116 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
3117 return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
3118 lhs.ty,
3119 rhs.ty,
3120 });
3121 }
3122
3123 if (lhs.value()) |lhs_val| {
3124 if (rhs.value()) |rhs_val| {
3125 return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val));
3126 }
3127 }
3128
3129 // TODO handle comparisons against lazy zero values
3130 // Some values can be compared against zero without being runtime known or without forcing
3131 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
3132 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
3133 // of this function if we don't need to.
3134
3135 // It must be a runtime comparison.
3136 const b = try self.requireRuntimeBlock(scope, src);
3137 // For floats, emit a float comparison instruction.
3138 const lhs_is_float = switch (lhs_ty_tag) {
3139 .Float, .ComptimeFloat => true,
3140 else => false,
3141 };
3142 const rhs_is_float = switch (rhs_ty_tag) {
3143 .Float, .ComptimeFloat => true,
3144 else => false,
3145 };
3146 if (lhs_is_float and rhs_is_float) {
3147 // Implicit cast the smaller one to the larger one.
3148 const dest_type = x: {
3149 if (lhs_ty_tag == .ComptimeFloat) {
3150 break :x rhs.ty;
3151 } else if (rhs_ty_tag == .ComptimeFloat) {
3152 break :x lhs.ty;
3153 }
3154 if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) {
3155 break :x lhs.ty;
3156 } else {
3157 break :x rhs.ty;
3158 }
3159 };
3160 const casted_lhs = try self.coerce(scope, dest_type, lhs);
3161 const casted_rhs = try self.coerce(scope, dest_type, rhs);
3162 return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
3163 }
3164 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
3165 // For mixed signed and unsigned integers, implicit cast both operands to a signed
3166 // integer with + 1 bit.
3167 // For mixed floats and integers, extract the integer part from the float, cast that to
3168 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
3169 // add/subtract 1.
3170 const lhs_is_signed = if (lhs.value()) |lhs_val|
3171 lhs_val.compareWithZero(.lt)
3172 else
3173 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
3174 const rhs_is_signed = if (rhs.value()) |rhs_val|
3175 rhs_val.compareWithZero(.lt)
3176 else
3177 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
3178 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
3179
3180 var dest_float_type: ?Type = null;
3181
3182 var lhs_bits: usize = undefined;
3183 if (lhs.value()) |lhs_val| {
3184 if (lhs_val.isUndef())
3185 return self.constUndef(scope, src, Type.initTag(.bool));
3186 const is_unsigned = if (lhs_is_float) x: {
3187 var bigint_space: Value.BigIntSpace = undefined;
3188 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
3189 defer bigint.deinit();
3190 const zcmp = lhs_val.orderAgainstZero();
3191 if (lhs_val.floatHasFraction()) {
3192 switch (op) {
3193 .eq => return self.constBool(scope, src, false),
3194 .neq => return self.constBool(scope, src, true),
3195 else => {},
3196 }
3197 if (zcmp == .lt) {
3198 try bigint.addScalar(bigint.toConst(), -1);
3199 } else {
3200 try bigint.addScalar(bigint.toConst(), 1);
3201 }
3202 }
3203 lhs_bits = bigint.toConst().bitCountTwosComp();
3204 break :x (zcmp != .lt);
3205 } else x: {
3206 lhs_bits = lhs_val.intBitCountTwosComp();
3207 break :x (lhs_val.orderAgainstZero() != .lt);
3208 };
3209 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
3210 } else if (lhs_is_float) {
3211 dest_float_type = lhs.ty;
3212 } else {
3213 const int_info = lhs.ty.intInfo(self.getTarget());
3214 lhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
3215 }
3216
3217 var rhs_bits: usize = undefined;
3218 if (rhs.value()) |rhs_val| {
3219 if (rhs_val.isUndef())
3220 return self.constUndef(scope, src, Type.initTag(.bool));
3221 const is_unsigned = if (rhs_is_float) x: {
3222 var bigint_space: Value.BigIntSpace = undefined;
3223 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
3224 defer bigint.deinit();
3225 const zcmp = rhs_val.orderAgainstZero();
3226 if (rhs_val.floatHasFraction()) {
3227 switch (op) {
3228 .eq => return self.constBool(scope, src, false),
3229 .neq => return self.constBool(scope, src, true),
3230 else => {},
3231 }
3232 if (zcmp == .lt) {
3233 try bigint.addScalar(bigint.toConst(), -1);
3234 } else {
3235 try bigint.addScalar(bigint.toConst(), 1);
3236 }
3237 }
3238 rhs_bits = bigint.toConst().bitCountTwosComp();
3239 break :x (zcmp != .lt);
3240 } else x: {
3241 rhs_bits = rhs_val.intBitCountTwosComp();
3242 break :x (rhs_val.orderAgainstZero() != .lt);
3243 };
3244 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
3245 } else if (rhs_is_float) {
3246 dest_float_type = rhs.ty;
3247 } else {
3248 const int_info = rhs.ty.intInfo(self.getTarget());
3249 rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
3250 }
3251
3252 const dest_type = if (dest_float_type) |ft| ft else blk: {
3253 const max_bits = std.math.max(lhs_bits, rhs_bits);
3254 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
3255 error.Overflow => return self.fail(scope, src, "{d} exceeds maximum integer bit count", .{max_bits}),
3256 };
3257 break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
3258 };
3259 const casted_lhs = try self.coerce(scope, dest_type, lhs);
3260 const casted_rhs = try self.coerce(scope, dest_type, rhs);
3261
3262 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
3263}
3264
3265fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3266 if (inst.value()) |val| {
3267 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3268 }
3269
3270 const b = try self.requireRuntimeBlock(scope, inst.src);
3271 return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
3272}
3273
3274fn wrapErrorUnion(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3275 // TODO deal with inferred error sets
3276 const err_union = dest_type.castTag(.error_union).?;
3277 if (inst.value()) |val| {
3278 const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: {
3279 _ = try self.coerce(scope, err_union.data.payload, inst);
3280 break :blk val;
3281 } else switch (err_union.data.error_set.tag()) {
3282 .anyerror => val,
3283 .error_set_single => blk: {
3284 const n = err_union.data.error_set.castTag(.error_set_single).?.data;
3285 if (!mem.eql(u8, val.castTag(.@"error").?.data.name, n))
3286 return self.fail(scope, inst.src, "expected type '{}', found type '{}'", .{ err_union.data.error_set, inst.ty });
3287 break :blk val;
3288 },
3289 .error_set => blk: {
3290 const f = err_union.data.error_set.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields;
3291 if (f.get(val.castTag(.@"error").?.data.name) == null)
3292 return self.fail(scope, inst.src, "expected type '{}', found type '{}'", .{ err_union.data.error_set, inst.ty });
3293 break :blk val;
3294 },
3295 else => unreachable,
3296 };
3297
3298 return self.constInst(scope, inst.src, .{
3299 .ty = dest_type,
3300 // creating a SubValue for the error_union payload
3301 .val = try Value.Tag.error_union.create(
3302 scope.arena(),
3303 to_wrap,
3304 ),
3305 });
3306 }
3307
3308 const b = try self.requireRuntimeBlock(scope, inst.src);
3309
3310 // we are coercing from E to E!T
3311 if (inst.ty.zigTypeTag() == .ErrorSet) {
3312 var coerced = try self.coerce(scope, err_union.data.error_set, inst);
3313 return self.addUnOp(b, inst.src, dest_type, .wrap_errunion_err, coerced);
3314 } else {
3315 var coerced = try self.coerce(scope, err_union.data.payload, inst);
3316 return self.addUnOp(b, inst.src, dest_type, .wrap_errunion_payload, coerced);
3317 }
3318}
3319
3320fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
3321 const int_payload = try scope.arena().create(Type.Payload.Bits);
3983pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type {
3984 const int_payload = try arena.create(Type.Payload.Bits);
33223985 int_payload.* = .{
33233986 .base = .{
3324 .tag = if (signed) .int_signed else .int_unsigned,
3987 .tag = switch (signedness) {
3988 .signed => .int_signed,
3989 .unsigned => .int_unsigned,
3990 },
33253991 },
33263992 .data = bits,
33273993 };
33283994 return Type.initPayload(&int_payload.base);
33293995}
33303996
3331pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
3332 if (instructions.len == 0)
3333 return Type.initTag(.noreturn);
3334
3335 if (instructions.len == 1)
3336 return instructions[0].ty;
3337
3338 var chosen = instructions[0];
3339 for (instructions[1..]) |candidate| {
3340 if (candidate.ty.eql(chosen.ty))
3341 continue;
3342 if (candidate.ty.zigTypeTag() == .NoReturn)
3343 continue;
3344 if (chosen.ty.zigTypeTag() == .NoReturn) {
3345 chosen = candidate;
3346 continue;
3347 }
3348 if (candidate.ty.zigTypeTag() == .Undefined)
3349 continue;
3350 if (chosen.ty.zigTypeTag() == .Undefined) {
3351 chosen = candidate;
3352 continue;
3353 }
3354 if (chosen.ty.isInt() and
3355 candidate.ty.isInt() and
3356 chosen.ty.isSignedInt() == candidate.ty.isSignedInt())
3357 {
3358 if (chosen.ty.intInfo(self.getTarget()).bits < candidate.ty.intInfo(self.getTarget()).bits) {
3359 chosen = candidate;
3360 }
3361 continue;
3362 }
3363 if (chosen.ty.isFloat() and candidate.ty.isFloat()) {
3364 if (chosen.ty.floatBits(self.getTarget()) < candidate.ty.floatBits(self.getTarget())) {
3365 chosen = candidate;
3366 }
3367 continue;
3368 }
3369
3370 if (chosen.ty.zigTypeTag() == .ComptimeInt and candidate.ty.isInt()) {
3371 chosen = candidate;
3372 continue;
3373 }
3374
3375 if (chosen.ty.isInt() and candidate.ty.zigTypeTag() == .ComptimeInt) {
3376 continue;
3377 }
3378
3379 // TODO error notes pointing out each type
3380 return self.fail(scope, candidate.src, "incompatible types: '{}' and '{}'", .{ chosen.ty, candidate.ty });
3381 }
3382
3383 return chosen.ty;
3384}
3385
3386pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) InnerError!*Inst {
3387 if (dest_type.tag() == .var_args_param) {
3388 return self.coerceVarArgParam(scope, inst);
3389 }
3390 // If the types are the same, we can return the operand.
3391 if (dest_type.eql(inst.ty))
3392 return inst;
3393
3394 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
3395 if (in_memory_result == .ok) {
3396 return self.bitcast(scope, dest_type, inst);
3397 }
3398
3399 // undefined to anything
3400 if (inst.value()) |val| {
3401 if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
3402 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3403 }
3404 }
3405 assert(inst.ty.zigTypeTag() != .Undefined);
3406
3407 // null to ?T
3408 if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
3409 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
3410 }
3411
3412 // T to ?T
3413 if (dest_type.zigTypeTag() == .Optional) {
3414 var buf: Type.Payload.ElemType = undefined;
3415 const child_type = dest_type.optionalChild(&buf);
3416 if (child_type.eql(inst.ty)) {
3417 return self.wrapOptional(scope, dest_type, inst);
3418 } else if (try self.coerceNum(scope, child_type, inst)) |some| {
3419 return self.wrapOptional(scope, dest_type, some);
3420 }
3421 }
3422
3423 // T to E!T or E to E!T
3424 if (dest_type.tag() == .error_union) {
3425 return try self.wrapErrorUnion(scope, dest_type, inst);
3426 }
3427
3428 // Coercions where the source is a single pointer to an array.
3429 src_array_ptr: {
3430 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
3431 const array_type = inst.ty.elemType();
3432 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
3433 const array_elem_type = array_type.elemType();
3434 if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
3435 if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
3436
3437 const dst_elem_type = dest_type.elemType();
3438 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
3439 .ok => {},
3440 .no_match => break :src_array_ptr,
3441 }
3442
3443 switch (dest_type.ptrSize()) {
3444 .Slice => {
3445 // *[N]T to []T
3446 return self.coerceArrayPtrToSlice(scope, dest_type, inst);
3447 },
3448 .C => {
3449 // *[N]T to [*c]T
3450 return self.coerceArrayPtrToMany(scope, dest_type, inst);
3451 },
3452 .Many => {
3453 // *[N]T to [*]T
3454 // *[N:s]T to [*:s]T
3455 const src_sentinel = array_type.sentinel();
3456 const dst_sentinel = dest_type.sentinel();
3457 if (src_sentinel == null and dst_sentinel == null)
3458 return self.coerceArrayPtrToMany(scope, dest_type, inst);
3459
3460 if (src_sentinel) |src_s| {
3461 if (dst_sentinel) |dst_s| {
3462 if (src_s.eql(dst_s)) {
3463 return self.coerceArrayPtrToMany(scope, dest_type, inst);
3464 }
3465 }
3466 }
3467 },
3468 .One => {},
3469 }
3470 }
3471
3472 // comptime known number to other number
3473 if (try self.coerceNum(scope, dest_type, inst)) |some|
3474 return some;
3475
3476 // integer widening
3477 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
3478 assert(inst.value() == null); // handled above
3479
3480 const src_info = inst.ty.intInfo(self.getTarget());
3481 const dst_info = dest_type.intInfo(self.getTarget());
3482 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
3483 // small enough unsigned ints can get casted to large enough signed ints
3484 (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))
3485 {
3486 const b = try self.requireRuntimeBlock(scope, inst.src);
3487 return self.addUnOp(b, inst.src, dest_type, .intcast, inst);
3488 }
3489 }
3490
3491 // float widening
3492 if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
3493 assert(inst.value() == null); // handled above
3494
3495 const src_bits = inst.ty.floatBits(self.getTarget());
3496 const dst_bits = dest_type.floatBits(self.getTarget());
3497 if (dst_bits >= src_bits) {
3498 const b = try self.requireRuntimeBlock(scope, inst.src);
3499 return self.addUnOp(b, inst.src, dest_type, .floatcast, inst);
3500 }
3501 }
3502
3503 return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
3504}
3505
3506pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) InnerError!?*Inst {
3507 const val = inst.value() orelse return null;
3508 const src_zig_tag = inst.ty.zigTypeTag();
3509 const dst_zig_tag = dest_type.zigTypeTag();
3510
3511 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
3512 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
3513 if (val.floatHasFraction()) {
3514 return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
3515 }
3516 return self.fail(scope, inst.src, "TODO float to int", .{});
3517 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
3518 if (!val.intFitsInType(dest_type, self.getTarget())) {
3519 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
3520 }
3521 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3522 }
3523 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
3524 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
3525 const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) {
3526 error.Overflow => return self.fail(
3527 scope,
3528 inst.src,
3529 "cast of value {} to type '{}' loses information",
3530 .{ val, dest_type },
3531 ),
3532 error.OutOfMemory => return error.OutOfMemory,
3533 };
3534 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
3535 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
3536 return self.fail(scope, inst.src, "TODO int to float", .{});
3537 }
3538 }
3539 return null;
3540}
3541
3542pub fn coerceVarArgParam(mod: *Module, scope: *Scope, inst: *Inst) !*Inst {
3543 switch (inst.ty.zigTypeTag()) {
3544 .ComptimeInt, .ComptimeFloat => return mod.fail(scope, inst.src, "integer and float literals in var args function must be casted", .{}),
3545 else => {},
3546 }
3547 // TODO implement more of this function.
3548 return inst;
3549}
3550
3551pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
3552 if (ptr.ty.isConstPtr())
3553 return self.fail(scope, src, "cannot assign to constant", .{});
3554
3555 const elem_ty = ptr.ty.elemType();
3556 const value = try self.coerce(scope, elem_ty, uncasted_value);
3557 if (elem_ty.onePossibleValue() != null)
3558 return self.constVoid(scope, src);
3559
3560 // TODO handle comptime pointer writes
3561 // TODO handle if the element type requires comptime
3562
3563 const b = try self.requireRuntimeBlock(scope, src);
3564 return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value);
3565}
3566
3567pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3568 if (inst.value()) |val| {
3569 // Keep the comptime Value representation; take the new type.
3570 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3571 }
3572 // TODO validate the type size and other compile errors
3573 const b = try self.requireRuntimeBlock(scope, inst.src);
3574 return self.addUnOp(b, inst.src, dest_type, .bitcast, inst);
3575}
3576
3577fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3578 if (inst.value()) |val| {
3579 // The comptime Value representation is compatible with both types.
3580 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3581 }
3582 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
3583}
3584
3585fn coerceArrayPtrToMany(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3586 if (inst.value()) |val| {
3587 // The comptime Value representation is compatible with both types.
3588 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
3589 }
3590 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{});
3591}
3592
35933997/// We don't return a pointer to the new error note because the pointer
35943998/// becomes invalid when you add another one.
35953999pub fn errNote(
35964000 mod: *Module,
35974001 scope: *Scope,
3598 src: usize,
4002 src: LazySrcLoc,
35994003 parent: *ErrorMsg,
36004004 comptime format: []const u8,
36014005 args: anytype,
......@@ -3605,10 +4009,7 @@ pub fn errNote(
36054009
36064010 parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1);
36074011 parent.notes[parent.notes.len - 1] = .{
3608 .src_loc = .{
3609 .file_scope = scope.getFileScope(),
3610 .byte_offset = src,
3611 },
4012 .src_loc = src.toSrcLoc(scope),
36124013 .msg = msg,
36134014 };
36144015}
......@@ -3616,121 +4017,112 @@ pub fn errNote(
36164017pub fn errMsg(
36174018 mod: *Module,
36184019 scope: *Scope,
3619 src_byte_offset: usize,
4020 src: LazySrcLoc,
36204021 comptime format: []const u8,
36214022 args: anytype,
36224023) error{OutOfMemory}!*ErrorMsg {
3623 return ErrorMsg.create(mod.gpa, .{
3624 .file_scope = scope.getFileScope(),
3625 .byte_offset = src_byte_offset,
3626 }, format, args);
4024 return ErrorMsg.create(mod.gpa, src.toSrcLoc(scope), format, args);
36274025}
36284026
36294027pub fn fail(
36304028 mod: *Module,
36314029 scope: *Scope,
3632 src_byte_offset: usize,
4030 src: LazySrcLoc,
36334031 comptime format: []const u8,
36344032 args: anytype,
36354033) InnerError {
3636 const err_msg = try mod.errMsg(scope, src_byte_offset, format, args);
4034 const err_msg = try mod.errMsg(scope, src, format, args);
36374035 return mod.failWithOwnedErrorMsg(scope, err_msg);
36384036}
36394037
4038/// Same as `fail`, except given an absolute byte offset, and the function sets up the `LazySrcLoc`
4039/// for pointing at it relatively by subtracting from the containing `Decl`.
4040pub fn failOff(
4041 mod: *Module,
4042 scope: *Scope,
4043 byte_offset: u32,
4044 comptime format: []const u8,
4045 args: anytype,
4046) InnerError {
4047 const decl_byte_offset = scope.srcDecl().?.srcByteOffset();
4048 const src: LazySrcLoc = .{ .byte_offset = byte_offset - decl_byte_offset };
4049 return mod.fail(scope, src, format, args);
4050}
4051
4052/// Same as `fail`, except given a token index, and the function sets up the `LazySrcLoc`
4053/// for pointing at it relatively by subtracting from the containing `Decl`.
36404054pub fn failTok(
3641 self: *Module,
4055 mod: *Module,
36424056 scope: *Scope,
36434057 token_index: ast.TokenIndex,
36444058 comptime format: []const u8,
36454059 args: anytype,
36464060) InnerError {
3647 const src = scope.tree().tokens.items(.start)[token_index];
3648 return self.fail(scope, src, format, args);
4061 const src = scope.srcDecl().?.tokSrcLoc(token_index);
4062 return mod.fail(scope, src, format, args);
36494063}
36504064
4065/// Same as `fail`, except given an AST node index, and the function sets up the `LazySrcLoc`
4066/// for pointing at it relatively by subtracting from the containing `Decl`.
36514067pub fn failNode(
3652 self: *Module,
4068 mod: *Module,
36534069 scope: *Scope,
3654 ast_node: ast.Node.Index,
4070 node_index: ast.Node.Index,
36554071 comptime format: []const u8,
36564072 args: anytype,
36574073) InnerError {
3658 const tree = scope.tree();
3659 const src = tree.tokens.items(.start)[tree.firstToken(ast_node)];
3660 return self.fail(scope, src, format, args);
4074 const src = scope.srcDecl().?.nodeSrcLoc(node_index);
4075 return mod.fail(scope, src, format, args);
36614076}
36624077
3663pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError {
4078pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError {
36644079 @setCold(true);
36654080 {
3666 errdefer err_msg.destroy(self.gpa);
3667 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
3668 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
4081 errdefer err_msg.destroy(mod.gpa);
4082 try mod.failed_decls.ensureCapacity(mod.gpa, mod.failed_decls.items().len + 1);
4083 try mod.failed_files.ensureCapacity(mod.gpa, mod.failed_files.items().len + 1);
36694084 }
36704085 switch (scope.tag) {
36714086 .block => {
36724087 const block = scope.cast(Scope.Block).?;
3673 if (block.inlining) |inlining| {
3674 if (inlining.shared.caller) |func| {
3675 func.state = .sema_failure;
3676 } else {
3677 block.owner_decl.analysis = .sema_failure;
3678 block.owner_decl.generation = self.generation;
3679 }
4088 if (block.sema.owner_func) |func| {
4089 func.state = .sema_failure;
36804090 } else {
3681 if (block.func) |func| {
3682 func.state = .sema_failure;
3683 } else {
3684 block.owner_decl.analysis = .sema_failure;
3685 block.owner_decl.generation = self.generation;
3686 }
4091 block.sema.owner_decl.analysis = .sema_failure;
4092 block.sema.owner_decl.generation = mod.generation;
36874093 }
3688 self.failed_decls.putAssumeCapacityNoClobber(block.owner_decl, err_msg);
4094 mod.failed_decls.putAssumeCapacityNoClobber(block.sema.owner_decl, err_msg);
36894095 },
3690 .gen_zir, .gen_suspend => {
3691 const gen_zir = scope.cast(Scope.GenZIR).?;
3692 gen_zir.decl.analysis = .sema_failure;
3693 gen_zir.decl.generation = self.generation;
3694 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
4096 .gen_zir => {
4097 const gen_zir = scope.cast(Scope.GenZir).?;
4098 gen_zir.astgen.decl.analysis = .sema_failure;
4099 gen_zir.astgen.decl.generation = mod.generation;
4100 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg);
36954101 },
36964102 .local_val => {
36974103 const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
3698 gen_zir.decl.analysis = .sema_failure;
3699 gen_zir.decl.generation = self.generation;
3700 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
4104 gen_zir.astgen.decl.analysis = .sema_failure;
4105 gen_zir.astgen.decl.generation = mod.generation;
4106 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg);
37014107 },
37024108 .local_ptr => {
37034109 const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
3704 gen_zir.decl.analysis = .sema_failure;
3705 gen_zir.decl.generation = self.generation;
3706 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
3707 },
3708 .gen_nosuspend => {
3709 const gen_zir = scope.cast(Scope.Nosuspend).?.gen_zir;
3710 gen_zir.decl.analysis = .sema_failure;
3711 gen_zir.decl.generation = self.generation;
3712 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
4110 gen_zir.astgen.decl.analysis = .sema_failure;
4111 gen_zir.astgen.decl.generation = mod.generation;
4112 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg);
37134113 },
37144114 .file => unreachable,
37154115 .container => unreachable,
4116 .decl_ref => {
4117 const decl_ref = scope.cast(Scope.DeclRef).?;
4118 decl_ref.decl.analysis = .sema_failure;
4119 decl_ref.decl.generation = mod.generation;
4120 mod.failed_decls.putAssumeCapacityNoClobber(decl_ref.decl, err_msg);
4121 },
37164122 }
37174123 return error.AnalysisFail;
37184124}
37194125
3720const InMemoryCoercionResult = enum {
3721 ok,
3722 no_match,
3723};
3724
3725fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
3726 if (dest_type.eql(src_type))
3727 return .ok;
3728
3729 // TODO: implement more of this function
3730
3731 return .no_match;
3732}
3733
37344126fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
37354127 return @bitCast(u128, a) == @bitCast(u128, b);
37364128}
......@@ -3780,14 +4172,12 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
37804172}
37814173
37824174pub fn floatAdd(
3783 self: *Module,
3784 scope: *Scope,
4175 arena: *Allocator,
37854176 float_type: Type,
3786 src: usize,
4177 src: LazySrcLoc,
37874178 lhs: Value,
37884179 rhs: Value,
37894180) !Value {
3790 const arena = scope.arena();
37914181 switch (float_type.tag()) {
37924182 .f16 => {
37934183 @panic("TODO add __trunctfhf2 to compiler-rt");
......@@ -3815,14 +4205,12 @@ pub fn floatAdd(
38154205}
38164206
38174207pub fn floatSub(
3818 self: *Module,
3819 scope: *Scope,
4208 arena: *Allocator,
38204209 float_type: Type,
3821 src: usize,
4210 src: LazySrcLoc,
38224211 lhs: Value,
38234212 rhs: Value,
38244213) !Value {
3825 const arena = scope.arena();
38264214 switch (float_type.tag()) {
38274215 .f16 => {
38284216 @panic("TODO add __trunctfhf2 to compiler-rt");
......@@ -3850,9 +4238,8 @@ pub fn floatSub(
38504238}
38514239
38524240pub fn simplePtrType(
3853 self: *Module,
3854 scope: *Scope,
3855 src: usize,
4241 mod: *Module,
4242 arena: *Allocator,
38564243 elem_ty: Type,
38574244 mutable: bool,
38584245 size: std.builtin.TypeInfo.Pointer.Size,
......@@ -3863,7 +4250,7 @@ pub fn simplePtrType(
38634250 // TODO stage1 type inference bug
38644251 const T = Type.Tag;
38654252
3866 const type_payload = try scope.arena().create(Type.Payload.ElemType);
4253 const type_payload = try arena.create(Type.Payload.ElemType);
38674254 type_payload.* = .{
38684255 .base = .{
38694256 .tag = switch (size) {
......@@ -3879,9 +4266,8 @@ pub fn simplePtrType(
38794266}
38804267
38814268pub fn ptrType(
3882 self: *Module,
3883 scope: *Scope,
3884 src: usize,
4269 mod: *Module,
4270 arena: *Allocator,
38854271 elem_ty: Type,
38864272 sentinel: ?Value,
38874273 @"align": u32,
......@@ -3895,7 +4281,7 @@ pub fn ptrType(
38954281 assert(host_size == 0 or bit_offset < host_size * 8);
38964282
38974283 // TODO check if type can be represented by simplePtrType
3898 return Type.Tag.pointer.create(scope.arena(), .{
4284 return Type.Tag.pointer.create(arena, .{
38994285 .pointee_type = elem_ty,
39004286 .sentinel = sentinel,
39014287 .@"align" = @"align",
......@@ -3908,23 +4294,23 @@ pub fn ptrType(
39084294 });
39094295}
39104296
3911pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type {
4297pub fn optionalType(mod: *Module, arena: *Allocator, child_type: Type) Allocator.Error!Type {
39124298 switch (child_type.tag()) {
39134299 .single_const_pointer => return Type.Tag.optional_single_const_pointer.create(
3914 scope.arena(),
4300 arena,
39154301 child_type.elemType(),
39164302 ),
39174303 .single_mut_pointer => return Type.Tag.optional_single_mut_pointer.create(
3918 scope.arena(),
4304 arena,
39194305 child_type.elemType(),
39204306 ),
3921 else => return Type.Tag.optional.create(scope.arena(), child_type),
4307 else => return Type.Tag.optional.create(arena, child_type),
39224308 }
39234309}
39244310
39254311pub fn arrayType(
3926 self: *Module,
3927 scope: *Scope,
4312 mod: *Module,
4313 arena: *Allocator,
39284314 len: u64,
39294315 sentinel: ?Value,
39304316 elem_type: Type,
......@@ -3932,30 +4318,30 @@ pub fn arrayType(
39324318 if (elem_type.eql(Type.initTag(.u8))) {
39334319 if (sentinel) |some| {
39344320 if (some.eql(Value.initTag(.zero))) {
3935 return Type.Tag.array_u8_sentinel_0.create(scope.arena(), len);
4321 return Type.Tag.array_u8_sentinel_0.create(arena, len);
39364322 }
39374323 } else {
3938 return Type.Tag.array_u8.create(scope.arena(), len);
4324 return Type.Tag.array_u8.create(arena, len);
39394325 }
39404326 }
39414327
39424328 if (sentinel) |some| {
3943 return Type.Tag.array_sentinel.create(scope.arena(), .{
4329 return Type.Tag.array_sentinel.create(arena, .{
39444330 .len = len,
39454331 .sentinel = some,
39464332 .elem_type = elem_type,
39474333 });
39484334 }
39494335
3950 return Type.Tag.array.create(scope.arena(), .{
4336 return Type.Tag.array.create(arena, .{
39514337 .len = len,
39524338 .elem_type = elem_type,
39534339 });
39544340}
39554341
39564342pub fn errorUnionType(
3957 self: *Module,
3958 scope: *Scope,
4343 mod: *Module,
4344 arena: *Allocator,
39594345 error_set: Type,
39604346 payload: Type,
39614347) Allocator.Error!Type {
......@@ -3964,19 +4350,15 @@ pub fn errorUnionType(
39644350 return Type.initTag(.anyerror_void_error_union);
39654351 }
39664352
3967 return Type.Tag.error_union.create(scope.arena(), .{
4353 return Type.Tag.error_union.create(arena, .{
39684354 .error_set = error_set,
39694355 .payload = payload,
39704356 });
39714357}
39724358
3973pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type {
3974 return Type.Tag.anyframe_T.create(scope.arena(), return_type);
3975}
3976
3977pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
4359pub fn dumpInst(mod: *Module, scope: *Scope, inst: *ir.Inst) void {
39784360 const zir_module = scope.namespace();
3979 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
4361 const source = zir_module.getSource(mod) catch @panic("dumpInst failed to get source");
39804362 const loc = std.zig.findLineColumn(source, inst.src);
39814363 if (inst.tag == .constant) {
39824364 std.debug.print("constant ty={} val={} src={s}:{d}:{d}\n", .{
......@@ -4006,267 +4388,117 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
40064388 }
40074389}
40084390
4009pub const PanicId = enum {
4010 unreach,
4011 unwrap_null,
4012 unwrap_errunion,
4013};
4014
4015pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
4016 const block_inst = try parent_block.arena.create(Inst.Block);
4017 block_inst.* = .{
4018 .base = .{
4019 .tag = Inst.Block.base_tag,
4020 .ty = Type.initTag(.void),
4021 .src = ok.src,
4022 },
4023 .body = .{
4024 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr.
4025 },
4026 };
4027
4028 const ok_body: ir.Body = .{
4029 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the br_void.
4030 };
4031 const br_void = try parent_block.arena.create(Inst.BrVoid);
4032 br_void.* = .{
4033 .base = .{
4034 .tag = .br_void,
4035 .ty = Type.initTag(.noreturn),
4036 .src = ok.src,
4037 },
4038 .block = block_inst,
4039 };
4040 ok_body.instructions[0] = &br_void.base;
4041
4042 var fail_block: Scope.Block = .{
4043 .parent = parent_block,
4044 .inst_table = parent_block.inst_table,
4045 .func = parent_block.func,
4046 .owner_decl = parent_block.owner_decl,
4047 .src_decl = parent_block.src_decl,
4048 .instructions = .{},
4049 .arena = parent_block.arena,
4050 .inlining = parent_block.inlining,
4051 .is_comptime = parent_block.is_comptime,
4052 .branch_quota = parent_block.branch_quota,
4053 };
4054
4055 defer fail_block.instructions.deinit(mod.gpa);
4056
4057 _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
4058
4059 const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) };
4060
4061 const condbr = try parent_block.arena.create(Inst.CondBr);
4062 condbr.* = .{
4063 .base = .{
4064 .tag = .condbr,
4065 .ty = Type.initTag(.noreturn),
4066 .src = ok.src,
4067 },
4068 .condition = ok,
4069 .then_body = ok_body,
4070 .else_body = fail_body,
4071 };
4072 block_inst.body.instructions[0] = &condbr.base;
4073
4074 try parent_block.instructions.append(mod.gpa, &block_inst.base);
4075}
4076
4077pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst {
4078 // TODO Once we have a panic function to call, call it here instead of breakpoint.
4079 _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
4080 return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
4081}
4082
4083pub fn getTarget(self: Module) Target {
4084 return self.comp.bin_file.options.target;
4391pub fn getTarget(mod: Module) Target {
4392 return mod.comp.bin_file.options.target;
40854393}
40864394
4087pub fn optimizeMode(self: Module) std.builtin.Mode {
4088 return self.comp.bin_file.options.optimize_mode;
4395pub fn optimizeMode(mod: Module) std.builtin.Mode {
4396 return mod.comp.bin_file.options.optimize_mode;
40894397}
40904398
4091pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void {
4092 if (!ty.isValidVarType(false)) {
4093 return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty});
4094 }
4095}
4096
4097/// Identifier token -> String (allocated in scope.arena())
4399/// Given an identifier token, obtain the string for it.
4400/// If the token uses @"" syntax, parses as a string, reports errors if applicable,
4401/// and allocates the result within `scope.arena()`.
4402/// Otherwise, returns a reference to the source code bytes directly.
4403/// See also `appendIdentStr` and `parseStrLit`.
40984404pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
40994405 const tree = scope.tree();
41004406 const token_tags = tree.tokens.items(.tag);
4101 const token_starts = tree.tokens.items(.start);
41024407 assert(token_tags[token] == .identifier);
4103
41044408 const ident_name = tree.tokenSlice(token);
4105 if (mem.startsWith(u8, ident_name, "@")) {
4106 const raw_string = ident_name[1..];
4107 var bad_index: usize = undefined;
4108 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
4109 error.InvalidCharacter => {
4110 const bad_byte = raw_string[bad_index];
4111 const src = token_starts[token];
4112 return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
4113 },
4114 else => |e| return e,
4115 };
4409 if (!mem.startsWith(u8, ident_name, "@")) {
4410 return ident_name;
41164411 }
4117 return ident_name;
4412 var buf: ArrayListUnmanaged(u8) = .{};
4413 defer buf.deinit(mod.gpa);
4414 try parseStrLit(mod, scope, token, &buf, ident_name, 1);
4415 return buf.toOwnedSlice(mod.gpa);
41184416}
41194417
4120pub fn emitBackwardBranch(mod: *Module, block: *Scope.Block, src: usize) !void {
4121 const shared = block.inlining.?.shared;
4122 shared.branch_count += 1;
4123 if (shared.branch_count > block.branch_quota.*) {
4124 // TODO show the "called from here" stack
4125 return mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{
4126 block.branch_quota.*,
4127 });
4418/// Given an identifier token, obtain the string for it (possibly parsing as a string
4419/// literal if it is @"" syntax), and append the string to `buf`.
4420/// See also `identifierTokenString` and `parseStrLit`.
4421pub fn appendIdentStr(
4422 mod: *Module,
4423 scope: *Scope,
4424 token: ast.TokenIndex,
4425 buf: *ArrayListUnmanaged(u8),
4426) InnerError!void {
4427 const tree = scope.tree();
4428 const token_tags = tree.tokens.items(.tag);
4429 assert(token_tags[token] == .identifier);
4430 const ident_name = tree.tokenSlice(token);
4431 if (!mem.startsWith(u8, ident_name, "@")) {
4432 return buf.appendSlice(mod.gpa, ident_name);
4433 } else {
4434 return mod.parseStrLit(scope, token, buf, ident_name, 1);
41284435 }
41294436}
41304437
4131pub fn namedFieldPtr(
4438/// Appends the result to `buf`.
4439pub fn parseStrLit(
41324440 mod: *Module,
41334441 scope: *Scope,
4134 src: usize,
4135 object_ptr: *Inst,
4136 field_name: []const u8,
4137 field_name_src: usize,
4138) InnerError!*Inst {
4139 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
4140 .Pointer => object_ptr.ty.elemType(),
4141 else => return mod.fail(scope, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
4142 };
4143 switch (elem_ty.zigTypeTag()) {
4144 .Array => {
4145 if (mem.eql(u8, field_name, "len")) {
4146 return mod.constInst(scope, src, .{
4147 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
4148 .val = try Value.Tag.ref_val.create(
4149 scope.arena(),
4150 try Value.Tag.int_u64.create(scope.arena(), elem_ty.arrayLen()),
4151 ),
4152 });
4153 } else {
4154 return mod.fail(
4155 scope,
4156 field_name_src,
4157 "no member named '{s}' in '{}'",
4158 .{ field_name, elem_ty },
4159 );
4160 }
4442 token: ast.TokenIndex,
4443 buf: *ArrayListUnmanaged(u8),
4444 bytes: []const u8,
4445 offset: u32,
4446) InnerError!void {
4447 const tree = scope.tree();
4448 const token_starts = tree.tokens.items(.start);
4449 const raw_string = bytes[offset..];
4450 var buf_managed = buf.toManaged(mod.gpa);
4451 const result = std.zig.string_literal.parseAppend(&buf_managed, raw_string);
4452 buf.* = buf_managed.toUnmanaged();
4453 switch (try result) {
4454 .success => return,
4455 .invalid_character => |bad_index| {
4456 return mod.failOff(
4457 scope,
4458 token_starts[token] + offset + @intCast(u32, bad_index),
4459 "invalid string literal character: '{c}'",
4460 .{raw_string[bad_index]},
4461 );
41614462 },
4162 .Pointer => {
4163 const ptr_child = elem_ty.elemType();
4164 switch (ptr_child.zigTypeTag()) {
4165 .Array => {
4166 if (mem.eql(u8, field_name, "len")) {
4167 return mod.constInst(scope, src, .{
4168 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
4169 .val = try Value.Tag.ref_val.create(
4170 scope.arena(),
4171 try Value.Tag.int_u64.create(scope.arena(), ptr_child.arrayLen()),
4172 ),
4173 });
4174 } else {
4175 return mod.fail(
4176 scope,
4177 field_name_src,
4178 "no member named '{s}' in '{}'",
4179 .{ field_name, elem_ty },
4180 );
4181 }
4182 },
4183 else => {},
4184 }
4463 .expected_hex_digits => |bad_index| {
4464 return mod.failOff(
4465 scope,
4466 token_starts[token] + offset + @intCast(u32, bad_index),
4467 "expected hex digits after '\\x'",
4468 .{},
4469 );
41854470 },
4186 .Type => {
4187 _ = try mod.resolveConstValue(scope, object_ptr);
4188 const result = try mod.analyzeDeref(scope, src, object_ptr, object_ptr.src);
4189 const val = result.value().?;
4190 const child_type = try val.toType(scope.arena());
4191 switch (child_type.zigTypeTag()) {
4192 .ErrorSet => {
4193 var name: []const u8 = undefined;
4194 // TODO resolve inferred error sets
4195 if (val.castTag(.error_set)) |payload|
4196 name = (payload.data.fields.getEntry(field_name) orelse return mod.fail(scope, src, "no error named '{s}' in '{}'", .{ field_name, child_type })).key
4197 else
4198 name = (try mod.getErrorValue(field_name)).key;
4199
4200 const result_type = if (child_type.tag() == .anyerror)
4201 try Type.Tag.error_set_single.create(scope.arena(), name)
4202 else
4203 child_type;
4204
4205 return mod.constInst(scope, src, .{
4206 .ty = try mod.simplePtrType(scope, src, result_type, false, .One),
4207 .val = try Value.Tag.ref_val.create(
4208 scope.arena(),
4209 try Value.Tag.@"error".create(scope.arena(), .{
4210 .name = name,
4211 }),
4212 ),
4213 });
4214 },
4215 .Struct => {
4216 const container_scope = child_type.getContainerScope();
4217 if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
4218 // TODO if !decl.is_pub and inDifferentFiles() "{} is private"
4219 return mod.analyzeDeclRef(scope, src, decl);
4220 }
4221
4222 if (container_scope.file_scope == mod.root_scope) {
4223 return mod.fail(scope, src, "root source file has no member called '{s}'", .{field_name});
4224 } else {
4225 return mod.fail(scope, src, "container '{}' has no member called '{s}'", .{ child_type, field_name });
4226 }
4227 },
4228 else => return mod.fail(scope, src, "type '{}' does not support field access", .{child_type}),
4229 }
4471 .invalid_hex_escape => |bad_index| {
4472 return mod.failOff(
4473 scope,
4474 token_starts[token] + offset + @intCast(u32, bad_index),
4475 "invalid hex digit: '{c}'",
4476 .{raw_string[bad_index]},
4477 );
4478 },
4479 .invalid_unicode_escape => |bad_index| {
4480 return mod.failOff(
4481 scope,
4482 token_starts[token] + offset + @intCast(u32, bad_index),
4483 "invalid unicode digit: '{c}'",
4484 .{raw_string[bad_index]},
4485 );
4486 },
4487 .missing_matching_rbrace => |bad_index| {
4488 return mod.failOff(
4489 scope,
4490 token_starts[token] + offset + @intCast(u32, bad_index),
4491 "missing matching '}}' character",
4492 .{},
4493 );
4494 },
4495 .expected_unicode_digits => |bad_index| {
4496 return mod.failOff(
4497 scope,
4498 token_starts[token] + offset + @intCast(u32, bad_index),
4499 "expected unicode digits after '\\u'",
4500 .{},
4501 );
42304502 },
4231 else => {},
4232 }
4233 return mod.fail(scope, src, "type '{}' does not support field access", .{elem_ty});
4234}
4235
4236pub fn elemPtr(
4237 mod: *Module,
4238 scope: *Scope,
4239 src: usize,
4240 array_ptr: *Inst,
4241 elem_index: *Inst,
4242) InnerError!*Inst {
4243 const elem_ty = switch (array_ptr.ty.zigTypeTag()) {
4244 .Pointer => array_ptr.ty.elemType(),
4245 else => return mod.fail(scope, array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
4246 };
4247 if (!elem_ty.isIndexable()) {
4248 return mod.fail(scope, src, "array access of non-array type '{}'", .{elem_ty});
4249 }
4250
4251 if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) {
4252 // we have to deref the ptr operand to get the actual array pointer
4253 const array_ptr_deref = try mod.analyzeDeref(scope, src, array_ptr, array_ptr.src);
4254 if (array_ptr_deref.value()) |array_ptr_val| {
4255 if (elem_index.value()) |index_val| {
4256 // Both array pointer and index are compile-time known.
4257 const index_u64 = index_val.toUnsignedInt();
4258 // @intCast here because it would have been impossible to construct a value that
4259 // required a larger index.
4260 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
4261 const pointee_type = elem_ty.elemType().elemType();
4262
4263 return mod.constInst(scope, src, .{
4264 .ty = try Type.Tag.single_const_pointer.create(scope.arena(), pointee_type),
4265 .val = elem_ptr,
4266 });
4267 }
4268 }
42694503 }
4270
4271 return mod.fail(scope, src, "TODO implement more analyze elemptr", .{});
42724504}
src/RangeSet.zig+19-18
......@@ -2,13 +2,14 @@ const std = @import("std");
22const Order = std.math.Order;
33const Value = @import("value.zig").Value;
44const RangeSet = @This();
5const SwitchProngSrc = @import("AstGen.zig").SwitchProngSrc;
56
67ranges: std.ArrayList(Range),
78
89pub const Range = struct {
9 start: Value,
10 end: Value,
11 src: usize,
10 first: Value,
11 last: Value,
12 src: SwitchProngSrc,
1213};
1314
1415pub fn init(allocator: *std.mem.Allocator) RangeSet {
......@@ -21,18 +22,15 @@ pub fn deinit(self: *RangeSet) void {
2122 self.ranges.deinit();
2223}
2324
24pub fn add(self: *RangeSet, start: Value, end: Value, src: usize) !?usize {
25pub fn add(self: *RangeSet, first: Value, last: Value, src: SwitchProngSrc) !?SwitchProngSrc {
2526 for (self.ranges.items) |range| {
26 if ((start.compare(.gte, range.start) and start.compare(.lte, range.end)) or
27 (end.compare(.gte, range.start) and end.compare(.lte, range.end)))
28 {
29 // ranges overlap
30 return range.src;
27 if (last.compare(.gte, range.first) and first.compare(.lte, range.last)) {
28 return range.src; // They overlap.
3129 }
3230 }
3331 try self.ranges.append(.{
34 .start = start,
35 .end = end,
32 .first = first,
33 .last = last,
3634 .src = src,
3735 });
3836 return null;
......@@ -40,14 +38,17 @@ pub fn add(self: *RangeSet, start: Value, end: Value, src: usize) !?usize {
4038
4139/// Assumes a and b do not overlap
4240fn lessThan(_: void, a: Range, b: Range) bool {
43 return a.start.compare(.lt, b.start);
41 return a.first.compare(.lt, b.first);
4442}
4543
46pub fn spans(self: *RangeSet, start: Value, end: Value) !bool {
44pub fn spans(self: *RangeSet, first: Value, last: Value) !bool {
45 if (self.ranges.items.len == 0)
46 return false;
47
4748 std.sort.sort(Range, self.ranges.items, {}, lessThan);
4849
49 if (!self.ranges.items[0].start.eql(start) or
50 !self.ranges.items[self.ranges.items.len - 1].end.eql(end))
50 if (!self.ranges.items[0].first.eql(first) or
51 !self.ranges.items[self.ranges.items.len - 1].last.eql(last))
5152 {
5253 return false;
5354 }
......@@ -62,11 +63,11 @@ pub fn spans(self: *RangeSet, start: Value, end: Value) !bool {
6263 // i starts counting from the second item.
6364 const prev = self.ranges.items[i];
6465
65 // prev.end + 1 == cur.start
66 try counter.copy(prev.end.toBigInt(&space));
66 // prev.last + 1 == cur.first
67 try counter.copy(prev.last.toBigInt(&space));
6768 try counter.addScalar(counter.toConst(), 1);
6869
69 const cur_start_int = cur.start.toBigInt(&space);
70 const cur_start_int = cur.first.toBigInt(&space);
7071 if (!cur_start_int.eq(counter.toConst())) {
7172 return false;
7273 }
src/Sema.zig created+5091
......@@ -0,0 +1,5091 @@
1//! Semantic analysis of ZIR instructions.
2//! Shared to every Block. Stored on the stack.
3//! State used for compiling a `zir.Code` into TZIR.
4//! Transforms untyped ZIR instructions into semantically-analyzed TZIR instructions.
5//! Does type checking, comptime control flow, and safety-check generation.
6//! This is the the heart of the Zig compiler.
7
8mod: *Module,
9/// Alias to `mod.gpa`.
10gpa: *Allocator,
11/// Points to the arena allocator of the Decl.
12arena: *Allocator,
13code: zir.Code,
14/// Maps ZIR to TZIR.
15inst_map: []*Inst,
16/// When analyzing an inline function call, owner_decl is the Decl of the caller
17/// and `src_decl` of `Scope.Block` is the `Decl` of the callee.
18/// This `Decl` owns the arena memory of this `Sema`.
19owner_decl: *Decl,
20/// For an inline or comptime function call, this will be the root parent function
21/// which contains the callsite. Corresponds to `owner_decl`.
22owner_func: ?*Module.Fn,
23/// The function this ZIR code is the body of, according to the source code.
24/// This starts out the same as `owner_func` and then diverges in the case of
25/// an inline or comptime function call.
26func: ?*Module.Fn,
27/// For now, TZIR requires arg instructions to be the first N instructions in the
28/// TZIR code. We store references here for the purpose of `resolveInst`.
29/// This can get reworked with TZIR memory layout changes, into simply:
30/// > Denormalized data to make `resolveInst` faster. This is 0 if not inside a function,
31/// > otherwise it is the number of parameters of the function.
32/// > param_count: u32
33param_inst_list: []const *ir.Inst,
34branch_quota: u32 = 1000,
35branch_count: u32 = 0,
36/// This field is updated when a new source location becomes active, so that
37/// instructions which do not have explicitly mapped source locations still have
38/// access to the source location set by the previous instruction which did
39/// contain a mapped source location.
40src: LazySrcLoc = .{ .token_offset = 0 },
41
42const std = @import("std");
43const mem = std.mem;
44const Allocator = std.mem.Allocator;
45const assert = std.debug.assert;
46const log = std.log.scoped(.sema);
47
48const Sema = @This();
49const Value = @import("value.zig").Value;
50const Type = @import("type.zig").Type;
51const TypedValue = @import("TypedValue.zig");
52const ir = @import("ir.zig");
53const zir = @import("zir.zig");
54const Module = @import("Module.zig");
55const Inst = ir.Inst;
56const Body = ir.Body;
57const trace = @import("tracy.zig").trace;
58const Scope = Module.Scope;
59const InnerError = Module.InnerError;
60const Decl = Module.Decl;
61const LazySrcLoc = Module.LazySrcLoc;
62const RangeSet = @import("RangeSet.zig");
63const AstGen = @import("AstGen.zig");
64
65pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Index {
66 const inst_data = sema.code.instructions.items(.data)[0].pl_node;
67 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
68 const root_body = sema.code.extra[extra.end..][0..extra.data.body_len];
69 return sema.analyzeBody(root_block, root_body);
70}
71
72pub fn rootAsRef(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Ref {
73 const break_inst = try sema.root(root_block);
74 return sema.code.instructions.items(.data)[break_inst].@"break".operand;
75}
76
77/// Assumes that `root_block` ends with `break_inline`.
78pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
79 assert(root_block.is_comptime);
80 const zir_inst_ref = try sema.rootAsRef(root_block);
81 // Source location is unneeded because resolveConstValue must have already
82 // been successfully called when coercing the value to a type, from the
83 // result location.
84 return sema.resolveType(root_block, .unneeded, zir_inst_ref);
85}
86
87/// Returns only the result from the body that is specified.
88/// Only appropriate to call when it is determined at comptime that this body
89/// has no peers.
90fn resolveBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) InnerError!*Inst {
91 const break_inst = try sema.analyzeBody(block, body);
92 const operand_ref = sema.code.instructions.items(.data)[break_inst].@"break".operand;
93 return sema.resolveInst(operand_ref);
94}
95
96/// ZIR instructions which are always `noreturn` return this. This matches the
97/// return type of `analyzeBody` so that we can tail call them.
98/// Only appropriate to return when the instruction is known to be NoReturn
99/// solely based on the ZIR tag.
100const always_noreturn: InnerError!zir.Inst.Index = @as(zir.Inst.Index, undefined);
101
102/// This function is the main loop of `Sema` and it can be used in two different ways:
103/// * The traditional way where there are N breaks out of the block and peer type
104/// resolution is done on the break operands. In this case, the `zir.Inst.Index`
105/// part of the return value will be `undefined`, and callsites should ignore it,
106/// finding the block result value via the block scope.
107/// * The "flat" way. There is only 1 break out of the block, and it is with a `break_inline`
108/// instruction. In this case, the `zir.Inst.Index` part of the return value will be
109/// the break instruction. This communicates both which block the break applies to, as
110/// well as the operand. No block scope needs to be created for this strategy.
111pub fn analyzeBody(
112 sema: *Sema,
113 block: *Scope.Block,
114 body: []const zir.Inst.Index,
115) InnerError!zir.Inst.Index {
116 // No tracy calls here, to avoid interfering with the tail call mechanism.
117
118 const map = block.sema.inst_map;
119 const tags = block.sema.code.instructions.items(.tag);
120 const datas = block.sema.code.instructions.items(.data);
121
122 // We use a while(true) loop here to avoid a redundant way of breaking out of
123 // the loop. The only way to break out of the loop is with a `noreturn`
124 // instruction.
125 // TODO: As an optimization, make sure the codegen for these switch prongs
126 // directly jump to the next one, rather than detouring through the loop
127 // continue expression. Related: https://github.com/ziglang/zig/issues/8220
128 var i: usize = 0;
129 while (true) : (i += 1) {
130 const inst = body[i];
131 map[inst] = switch (tags[inst]) {
132 .elided => continue,
133
134 .add => try sema.zirArithmetic(block, inst),
135 .addwrap => try sema.zirArithmetic(block, inst),
136 .alloc => try sema.zirAlloc(block, inst),
137 .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),
138 .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),
139 .alloc_mut => try sema.zirAllocMut(block, inst),
140 .array_cat => try sema.zirArrayCat(block, inst),
141 .array_mul => try sema.zirArrayMul(block, inst),
142 .array_type => try sema.zirArrayType(block, inst),
143 .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst),
144 .as => try sema.zirAs(block, inst),
145 .as_node => try sema.zirAsNode(block, inst),
146 .@"asm" => try sema.zirAsm(block, inst, false),
147 .asm_volatile => try sema.zirAsm(block, inst, true),
148 .bit_and => try sema.zirBitwise(block, inst, .bit_and),
149 .bit_not => try sema.zirBitNot(block, inst),
150 .bit_or => try sema.zirBitwise(block, inst, .bit_or),
151 .bitcast => try sema.zirBitcast(block, inst),
152 .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),
153 .block => try sema.zirBlock(block, inst),
154 .bool_not => try sema.zirBoolNot(block, inst),
155 .bool_and => try sema.zirBoolOp(block, inst, false),
156 .bool_or => try sema.zirBoolOp(block, inst, true),
157 .bool_br_and => try sema.zirBoolBr(block, inst, false),
158 .bool_br_or => try sema.zirBoolBr(block, inst, true),
159 .call => try sema.zirCall(block, inst, .auto, false),
160 .call_chkused => try sema.zirCall(block, inst, .auto, true),
161 .call_compile_time => try sema.zirCall(block, inst, .compile_time, false),
162 .call_none => try sema.zirCallNone(block, inst, false),
163 .call_none_chkused => try sema.zirCallNone(block, inst, true),
164 .cmp_eq => try sema.zirCmp(block, inst, .eq),
165 .cmp_gt => try sema.zirCmp(block, inst, .gt),
166 .cmp_gte => try sema.zirCmp(block, inst, .gte),
167 .cmp_lt => try sema.zirCmp(block, inst, .lt),
168 .cmp_lte => try sema.zirCmp(block, inst, .lte),
169 .cmp_neq => try sema.zirCmp(block, inst, .neq),
170 .coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst),
171 .@"const" => try sema.zirConst(block, inst),
172 .decl_ref => try sema.zirDeclRef(block, inst),
173 .decl_val => try sema.zirDeclVal(block, inst),
174 .load => try sema.zirLoad(block, inst),
175 .div => try sema.zirArithmetic(block, inst),
176 .elem_ptr => try sema.zirElemPtr(block, inst),
177 .elem_ptr_node => try sema.zirElemPtrNode(block, inst),
178 .elem_val => try sema.zirElemVal(block, inst),
179 .elem_val_node => try sema.zirElemValNode(block, inst),
180 .enum_literal => try sema.zirEnumLiteral(block, inst),
181 .enum_literal_small => try sema.zirEnumLiteralSmall(block, inst),
182 .err_union_code => try sema.zirErrUnionCode(block, inst),
183 .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst),
184 .err_union_payload_safe => try sema.zirErrUnionPayload(block, inst, true),
185 .err_union_payload_safe_ptr => try sema.zirErrUnionPayloadPtr(block, inst, true),
186 .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst, false),
187 .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst, false),
188 .error_union_type => try sema.zirErrorUnionType(block, inst),
189 .error_value => try sema.zirErrorValue(block, inst),
190 .error_to_int => try sema.zirErrorToInt(block, inst),
191 .int_to_error => try sema.zirIntToError(block, inst),
192 .field_ptr => try sema.zirFieldPtr(block, inst),
193 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
194 .field_val => try sema.zirFieldVal(block, inst),
195 .field_val_named => try sema.zirFieldValNamed(block, inst),
196 .floatcast => try sema.zirFloatcast(block, inst),
197 .fn_type => try sema.zirFnType(block, inst, false),
198 .fn_type_cc => try sema.zirFnTypeCc(block, inst, false),
199 .fn_type_cc_var_args => try sema.zirFnTypeCc(block, inst, true),
200 .fn_type_var_args => try sema.zirFnType(block, inst, true),
201 .import => try sema.zirImport(block, inst),
202 .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst),
203 .int => try sema.zirInt(block, inst),
204 .int_type => try sema.zirIntType(block, inst),
205 .intcast => try sema.zirIntcast(block, inst),
206 .is_err => try sema.zirIsErr(block, inst),
207 .is_err_ptr => try sema.zirIsErrPtr(block, inst),
208 .is_non_null => try sema.zirIsNull(block, inst, true),
209 .is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true),
210 .is_null => try sema.zirIsNull(block, inst, false),
211 .is_null_ptr => try sema.zirIsNullPtr(block, inst, false),
212 .loop => try sema.zirLoop(block, inst),
213 .merge_error_sets => try sema.zirMergeErrorSets(block, inst),
214 .mod_rem => try sema.zirArithmetic(block, inst),
215 .mul => try sema.zirArithmetic(block, inst),
216 .mulwrap => try sema.zirArithmetic(block, inst),
217 .negate => try sema.zirNegate(block, inst, .sub),
218 .negate_wrap => try sema.zirNegate(block, inst, .subwrap),
219 .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true),
220 .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true),
221 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),
222 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),
223 .optional_type => try sema.zirOptionalType(block, inst),
224 .optional_type_from_ptr_elem => try sema.zirOptionalTypeFromPtrElem(block, inst),
225 .param_type => try sema.zirParamType(block, inst),
226 .ptr_type => try sema.zirPtrType(block, inst),
227 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
228 .ptrtoint => try sema.zirPtrtoint(block, inst),
229 .ref => try sema.zirRef(block, inst),
230 .ret_ptr => try sema.zirRetPtr(block, inst),
231 .ret_type => try sema.zirRetType(block, inst),
232 .shl => try sema.zirShl(block, inst),
233 .shr => try sema.zirShr(block, inst),
234 .slice_end => try sema.zirSliceEnd(block, inst),
235 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
236 .slice_start => try sema.zirSliceStart(block, inst),
237 .str => try sema.zirStr(block, inst),
238 .sub => try sema.zirArithmetic(block, inst),
239 .subwrap => try sema.zirArithmetic(block, inst),
240 .switch_block => try sema.zirSwitchBlock(block, inst, false, .none),
241 .switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),
242 .switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),
243 .switch_block_else_multi => try sema.zirSwitchBlockMulti(block, inst, false, .@"else"),
244 .switch_block_under => try sema.zirSwitchBlock(block, inst, false, .under),
245 .switch_block_under_multi => try sema.zirSwitchBlockMulti(block, inst, false, .under),
246 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true, .none),
247 .switch_block_ref_multi => try sema.zirSwitchBlockMulti(block, inst, true, .none),
248 .switch_block_ref_else => try sema.zirSwitchBlock(block, inst, true, .@"else"),
249 .switch_block_ref_else_multi => try sema.zirSwitchBlockMulti(block, inst, true, .@"else"),
250 .switch_block_ref_under => try sema.zirSwitchBlock(block, inst, true, .under),
251 .switch_block_ref_under_multi => try sema.zirSwitchBlockMulti(block, inst, true, .under),
252 .switch_capture => try sema.zirSwitchCapture(block, inst, false, false),
253 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
254 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
255 .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
256 .switch_capture_else => try sema.zirSwitchCaptureElse(block, inst, false),
257 .switch_capture_else_ref => try sema.zirSwitchCaptureElse(block, inst, true),
258 .typeof => try sema.zirTypeof(block, inst),
259 .typeof_elem => try sema.zirTypeofElem(block, inst),
260 .typeof_peer => try sema.zirTypeofPeer(block, inst),
261 .xor => try sema.zirBitwise(block, inst, .xor),
262 .struct_init_empty => try sema.zirStructInitEmpty(block, inst),
263
264 .struct_decl => try sema.zirStructDecl(block, inst, .Auto),
265 .struct_decl_packed => try sema.zirStructDecl(block, inst, .Packed),
266 .struct_decl_extern => try sema.zirStructDecl(block, inst, .Extern),
267 .enum_decl => try sema.zirEnumDecl(block, inst),
268 .union_decl => try sema.zirUnionDecl(block, inst),
269 .opaque_decl => try sema.zirOpaqueDecl(block, inst),
270
271 // Instructions that we know to *always* be noreturn based solely on their tag.
272 // These functions match the return type of analyzeBody so that we can
273 // tail call them here.
274 .condbr => return sema.zirCondbr(block, inst),
275 .@"break" => return sema.zirBreak(block, inst),
276 .break_inline => return inst,
277 .compile_error => return sema.zirCompileError(block, inst),
278 .ret_coerce => return sema.zirRetTok(block, inst, true),
279 .ret_node => return sema.zirRetNode(block, inst),
280 .ret_tok => return sema.zirRetTok(block, inst, false),
281 .@"unreachable" => return sema.zirUnreachable(block, inst),
282 .repeat => return sema.zirRepeat(block, inst),
283
284 // Instructions that we know can *never* be noreturn based solely on
285 // their tag. We avoid needlessly checking if they are noreturn and
286 // continue the loop.
287 // We also know that they cannot be referenced later, so we avoid
288 // putting them into the map.
289 .breakpoint => {
290 try sema.zirBreakpoint(block, inst);
291 continue;
292 },
293 .dbg_stmt_node => {
294 try sema.zirDbgStmtNode(block, inst);
295 continue;
296 },
297 .ensure_err_payload_void => {
298 try sema.zirEnsureErrPayloadVoid(block, inst);
299 continue;
300 },
301 .ensure_result_non_error => {
302 try sema.zirEnsureResultNonError(block, inst);
303 continue;
304 },
305 .ensure_result_used => {
306 try sema.zirEnsureResultUsed(block, inst);
307 continue;
308 },
309 .compile_log => {
310 try sema.zirCompileLog(block, inst);
311 continue;
312 },
313 .set_eval_branch_quota => {
314 try sema.zirSetEvalBranchQuota(block, inst);
315 continue;
316 },
317 .store => {
318 try sema.zirStore(block, inst);
319 continue;
320 },
321 .store_node => {
322 try sema.zirStoreNode(block, inst);
323 continue;
324 },
325 .store_to_block_ptr => {
326 try sema.zirStoreToBlockPtr(block, inst);
327 continue;
328 },
329 .store_to_inferred_ptr => {
330 try sema.zirStoreToInferredPtr(block, inst);
331 continue;
332 },
333 .resolve_inferred_alloc => {
334 try sema.zirResolveInferredAlloc(block, inst);
335 continue;
336 },
337 .validate_struct_init_ptr => {
338 try sema.zirValidateStructInitPtr(block, inst);
339 continue;
340 },
341
342 // Special case instructions to handle comptime control flow.
343 .repeat_inline => {
344 // Send comptime control flow back to the beginning of this block.
345 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
346 try sema.emitBackwardBranch(block, src);
347 i = 0;
348 continue;
349 },
350 .block_inline => blk: {
351 // Directly analyze the block body without introducing a new block.
352 const inst_data = datas[inst].pl_node;
353 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
354 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
355 const break_inst = try sema.analyzeBody(block, inline_body);
356 const break_data = datas[break_inst].@"break";
357 if (inst == break_data.block_inst) {
358 break :blk try sema.resolveInst(break_data.operand);
359 } else {
360 return break_inst;
361 }
362 },
363 .condbr_inline => blk: {
364 const inst_data = datas[inst].pl_node;
365 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
366 const extra = sema.code.extraData(zir.Inst.CondBr, inst_data.payload_index);
367 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
368 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
369 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition);
370 const inline_body = if (cond.val.toBool()) then_body else else_body;
371 const break_inst = try sema.analyzeBody(block, inline_body);
372 const break_data = datas[break_inst].@"break";
373 if (inst == break_data.block_inst) {
374 break :blk try sema.resolveInst(break_data.operand);
375 } else {
376 return break_inst;
377 }
378 },
379 };
380 if (map[inst].ty.isNoReturn())
381 return always_noreturn;
382 }
383}
384
385/// TODO when we rework TZIR memory layout, this function will no longer have a possible error.
386pub fn resolveInst(sema: *Sema, zir_ref: zir.Inst.Ref) error{OutOfMemory}!*ir.Inst {
387 var i: usize = @enumToInt(zir_ref);
388
389 // First section of indexes correspond to a set number of constant values.
390 if (i < zir.Inst.Ref.typed_value_map.len) {
391 // TODO when we rework TZIR memory layout, this function can be as simple as:
392 // if (zir_ref < zir.const_inst_list.len + sema.param_count)
393 // return zir_ref;
394 // Until then we allocate memory for a new, mutable `ir.Inst` to match what
395 // TZIR expects.
396 return sema.mod.constInst(sema.arena, .unneeded, zir.Inst.Ref.typed_value_map[i]);
397 }
398 i -= zir.Inst.Ref.typed_value_map.len;
399
400 // Next section of indexes correspond to function parameters, if any.
401 if (i < sema.param_inst_list.len) {
402 return sema.param_inst_list[i];
403 }
404 i -= sema.param_inst_list.len;
405
406 // Finally, the last section of indexes refers to the map of ZIR=>TZIR.
407 return sema.inst_map[i];
408}
409
410fn resolveConstString(
411 sema: *Sema,
412 block: *Scope.Block,
413 src: LazySrcLoc,
414 zir_ref: zir.Inst.Ref,
415) ![]u8 {
416 const tzir_inst = try sema.resolveInst(zir_ref);
417 const wanted_type = Type.initTag(.const_slice_u8);
418 const coerced_inst = try sema.coerce(block, wanted_type, tzir_inst, src);
419 const val = try sema.resolveConstValue(block, src, coerced_inst);
420 return val.toAllocatedBytes(sema.arena);
421}
422
423fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: zir.Inst.Ref) !Type {
424 const tzir_inst = try sema.resolveInst(zir_ref);
425 const wanted_type = Type.initTag(.@"type");
426 const coerced_inst = try sema.coerce(block, wanted_type, tzir_inst, src);
427 const val = try sema.resolveConstValue(block, src, coerced_inst);
428 return val.toType(sema.arena);
429}
430
431fn resolveConstValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !Value {
432 return (try sema.resolveDefinedValue(block, src, base)) orelse
433 return sema.failWithNeededComptime(block, src);
434}
435
436fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !?Value {
437 if (base.value()) |val| {
438 if (val.isUndef()) {
439 return sema.failWithUseOfUndef(block, src);
440 }
441 return val;
442 }
443 return null;
444}
445
446fn failWithNeededComptime(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError {
447 return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{});
448}
449
450fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError {
451 return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{});
452}
453
454/// Appropriate to call when the coercion has already been done by result
455/// location semantics. Asserts the value fits in the provided `Int` type.
456/// Only supports `Int` types 64 bits or less.
457fn resolveAlreadyCoercedInt(
458 sema: *Sema,
459 block: *Scope.Block,
460 src: LazySrcLoc,
461 zir_ref: zir.Inst.Ref,
462 comptime Int: type,
463) !Int {
464 comptime assert(@typeInfo(Int).Int.bits <= 64);
465 const tzir_inst = try sema.resolveInst(zir_ref);
466 const val = try sema.resolveConstValue(block, src, tzir_inst);
467 switch (@typeInfo(Int).Int.signedness) {
468 .signed => return @intCast(Int, val.toSignedInt()),
469 .unsigned => return @intCast(Int, val.toUnsignedInt()),
470 }
471}
472
473fn resolveInt(
474 sema: *Sema,
475 block: *Scope.Block,
476 src: LazySrcLoc,
477 zir_ref: zir.Inst.Ref,
478 dest_type: Type,
479) !u64 {
480 const tzir_inst = try sema.resolveInst(zir_ref);
481 const coerced = try sema.coerce(block, dest_type, tzir_inst, src);
482 const val = try sema.resolveConstValue(block, src, coerced);
483
484 return val.toUnsignedInt();
485}
486
487fn resolveInstConst(
488 sema: *Sema,
489 block: *Scope.Block,
490 src: LazySrcLoc,
491 zir_ref: zir.Inst.Ref,
492) InnerError!TypedValue {
493 const tzir_inst = try sema.resolveInst(zir_ref);
494 const val = try sema.resolveConstValue(block, src, tzir_inst);
495 return TypedValue{
496 .ty = tzir_inst.ty,
497 .val = val,
498 };
499}
500
501fn zirConst(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
502 const tracy = trace(@src());
503 defer tracy.end();
504
505 const tv_ptr = sema.code.instructions.items(.data)[inst].@"const";
506 // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
507 // after analysis. This happens, for example, with variable declaration initialization
508 // expressions.
509 const typed_value_copy = try tv_ptr.copy(sema.arena);
510 return sema.mod.constInst(sema.arena, .unneeded, typed_value_copy);
511}
512
513fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
514 const tracy = trace(@src());
515 defer tracy.end();
516 return sema.mod.fail(&block.base, sema.src, "TODO implement zir_sema.zirBitcastResultPtr", .{});
517}
518
519fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
520 const tracy = trace(@src());
521 defer tracy.end();
522 return sema.mod.fail(&block.base, sema.src, "TODO implement zirCoerceResultPtr", .{});
523}
524
525fn zirStructDecl(
526 sema: *Sema,
527 block: *Scope.Block,
528 inst: zir.Inst.Index,
529 layout: std.builtin.TypeInfo.ContainerLayout,
530) InnerError!*Inst {
531 const tracy = trace(@src());
532 defer tracy.end();
533
534 const gpa = sema.gpa;
535 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
536 const src = inst_data.src();
537 const extra = sema.code.extraData(zir.Inst.StructDecl, inst_data.payload_index);
538 const fields_len = extra.data.fields_len;
539 const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable;
540
541 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
542 errdefer new_decl_arena.deinit();
543
544 var fields_map: std.StringArrayHashMapUnmanaged(Module.Struct.Field) = .{};
545 try fields_map.ensureCapacity(&new_decl_arena.allocator, fields_len);
546
547 {
548 var field_index: usize = extra.end + bit_bags_count;
549 var bit_bag_index: usize = extra.end;
550 var cur_bit_bag: u32 = undefined;
551 var field_i: u32 = 0;
552 while (field_i < fields_len) : (field_i += 1) {
553 if (field_i % 16 == 0) {
554 cur_bit_bag = sema.code.extra[bit_bag_index];
555 bit_bag_index += 1;
556 }
557 const has_align = @truncate(u1, cur_bit_bag) != 0;
558 cur_bit_bag >>= 1;
559 const has_default = @truncate(u1, cur_bit_bag) != 0;
560 cur_bit_bag >>= 1;
561
562 const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[field_index]);
563 field_index += 1;
564 const field_type_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]);
565 field_index += 1;
566
567 // This string needs to outlive the ZIR code.
568 const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir);
569 // TODO: if we need to report an error here, use a source location
570 // that points to this type expression rather than the struct.
571 // But only resolve the source location if we need to emit a compile error.
572 const field_ty = try sema.resolveType(block, src, field_type_ref);
573
574 const gop = fields_map.getOrPutAssumeCapacity(field_name);
575 assert(!gop.found_existing);
576 gop.entry.value = .{
577 .ty = field_ty,
578 .abi_align = Value.initTag(.abi_align_default),
579 .default_val = Value.initTag(.unreachable_value),
580 };
581
582 if (has_align) {
583 const align_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]);
584 field_index += 1;
585 // TODO: if we need to report an error here, use a source location
586 // that points to this alignment expression rather than the struct.
587 // But only resolve the source location if we need to emit a compile error.
588 gop.entry.value.abi_align = (try sema.resolveInstConst(block, src, align_ref)).val;
589 }
590 if (has_default) {
591 const default_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]);
592 field_index += 1;
593 // TODO: if we need to report an error here, use a source location
594 // that points to this default value expression rather than the struct.
595 // But only resolve the source location if we need to emit a compile error.
596 gop.entry.value.default_val = (try sema.resolveInstConst(block, src, default_ref)).val;
597 }
598 }
599 }
600
601 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);
602 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);
603 struct_obj.* = .{
604 .owner_decl = sema.owner_decl,
605 .fields = fields_map,
606 .node_offset = inst_data.src_node,
607 .container = .{
608 .ty = struct_ty,
609 .file_scope = block.getFileScope(),
610 },
611 };
612 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{
613 .ty = Type.initTag(.type),
614 .val = try Value.Tag.ty.create(gpa, struct_ty),
615 });
616 return sema.analyzeDeclVal(block, src, new_decl);
617}
618
619fn zirEnumDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
620 const tracy = trace(@src());
621 defer tracy.end();
622
623 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
624 const src = inst_data.src();
625 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
626
627 return sema.mod.fail(&block.base, sema.src, "TODO implement zirEnumDecl", .{});
628}
629
630fn zirUnionDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
631 const tracy = trace(@src());
632 defer tracy.end();
633
634 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
635 const src = inst_data.src();
636 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
637
638 return sema.mod.fail(&block.base, sema.src, "TODO implement zirUnionDecl", .{});
639}
640
641fn zirOpaqueDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
642 const tracy = trace(@src());
643 defer tracy.end();
644
645 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
646 const src = inst_data.src();
647 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
648
649 return sema.mod.fail(&block.base, sema.src, "TODO implement zirOpaqueDecl", .{});
650}
651
652fn zirRetPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
653 const tracy = trace(@src());
654 defer tracy.end();
655
656 const src: LazySrcLoc = .unneeded;
657 try sema.requireFunctionBlock(block, src);
658 const fn_ty = sema.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
659 const ret_type = fn_ty.fnReturnType();
660 const ptr_type = try sema.mod.simplePtrType(sema.arena, ret_type, true, .One);
661 return block.addNoOp(src, ptr_type, .alloc);
662}
663
664fn zirRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
665 const tracy = trace(@src());
666 defer tracy.end();
667
668 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
669 const operand = try sema.resolveInst(inst_data.operand);
670 return sema.analyzeRef(block, inst_data.src(), operand);
671}
672
673fn zirRetType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
674 const tracy = trace(@src());
675 defer tracy.end();
676
677 const src: LazySrcLoc = .unneeded;
678 try sema.requireFunctionBlock(block, src);
679 const fn_ty = sema.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
680 const ret_type = fn_ty.fnReturnType();
681 return sema.mod.constType(sema.arena, src, ret_type);
682}
683
684fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
685 const tracy = trace(@src());
686 defer tracy.end();
687
688 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
689 const operand = try sema.resolveInst(inst_data.operand);
690 const src = inst_data.src();
691
692 return sema.ensureResultUsed(block, operand, src);
693}
694
695fn ensureResultUsed(
696 sema: *Sema,
697 block: *Scope.Block,
698 operand: *Inst,
699 src: LazySrcLoc,
700) InnerError!void {
701 switch (operand.ty.zigTypeTag()) {
702 .Void, .NoReturn => return,
703 else => return sema.mod.fail(&block.base, src, "expression value is ignored", .{}),
704 }
705}
706
707fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
708 const tracy = trace(@src());
709 defer tracy.end();
710
711 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
712 const operand = try sema.resolveInst(inst_data.operand);
713 const src = inst_data.src();
714 switch (operand.ty.zigTypeTag()) {
715 .ErrorSet, .ErrorUnion => return sema.mod.fail(&block.base, src, "error is discarded", .{}),
716 else => return,
717 }
718}
719
720fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
721 const tracy = trace(@src());
722 defer tracy.end();
723
724 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
725 const src = inst_data.src();
726 const array_ptr = try sema.resolveInst(inst_data.operand);
727
728 const elem_ty = array_ptr.ty.elemType();
729 if (!elem_ty.isIndexable()) {
730 const cond_src: LazySrcLoc = .{ .node_offset_for_cond = inst_data.src_node };
731 const msg = msg: {
732 const msg = try sema.mod.errMsg(
733 &block.base,
734 cond_src,
735 "type '{}' does not support indexing",
736 .{elem_ty},
737 );
738 errdefer msg.destroy(sema.gpa);
739 try sema.mod.errNote(
740 &block.base,
741 cond_src,
742 msg,
743 "for loop operand must be an array, slice, tuple, or vector",
744 .{},
745 );
746 break :msg msg;
747 };
748 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
749 }
750 const result_ptr = try sema.namedFieldPtr(block, src, array_ptr, "len", src);
751 return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
752}
753
754fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
755 const tracy = trace(@src());
756 defer tracy.end();
757
758 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
759 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
760 const var_decl_src = inst_data.src();
761 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
762 const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One);
763 try sema.requireRuntimeBlock(block, var_decl_src);
764 return block.addNoOp(var_decl_src, ptr_type, .alloc);
765}
766
767fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
768 const tracy = trace(@src());
769 defer tracy.end();
770
771 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
772 const var_decl_src = inst_data.src();
773 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
774 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
775 try sema.validateVarType(block, ty_src, var_type);
776 const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One);
777 try sema.requireRuntimeBlock(block, var_decl_src);
778 return block.addNoOp(var_decl_src, ptr_type, .alloc);
779}
780
781fn zirAllocInferred(
782 sema: *Sema,
783 block: *Scope.Block,
784 inst: zir.Inst.Index,
785 inferred_alloc_ty: Type,
786) InnerError!*Inst {
787 const tracy = trace(@src());
788 defer tracy.end();
789
790 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
791 const src = inst_data.src();
792
793 const val_payload = try sema.arena.create(Value.Payload.InferredAlloc);
794 val_payload.* = .{
795 .data = .{},
796 };
797 // `Module.constInst` does not add the instruction to the block because it is
798 // not needed in the case of constant values. However here, we plan to "downgrade"
799 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
800 // to the block even though it is currently a `.constant`.
801 const result = try sema.mod.constInst(sema.arena, src, .{
802 .ty = inferred_alloc_ty,
803 .val = Value.initPayload(&val_payload.base),
804 });
805 try sema.requireFunctionBlock(block, src);
806 try block.instructions.append(sema.gpa, result);
807 return result;
808}
809
810fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
811 const tracy = trace(@src());
812 defer tracy.end();
813
814 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
815 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
816 const ptr = try sema.resolveInst(inst_data.operand);
817 const ptr_val = ptr.castTag(.constant).?.val;
818 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
819 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
820 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list);
821 const var_is_mut = switch (ptr.ty.tag()) {
822 .inferred_alloc_const => false,
823 .inferred_alloc_mut => true,
824 else => unreachable,
825 };
826 if (var_is_mut) {
827 try sema.validateVarType(block, ty_src, final_elem_ty);
828 }
829 const final_ptr_ty = try sema.mod.simplePtrType(sema.arena, final_elem_ty, true, .One);
830
831 // Change it to a normal alloc.
832 ptr.ty = final_ptr_ty;
833 ptr.tag = .alloc;
834}
835
836fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
837 const tracy = trace(@src());
838 defer tracy.end();
839
840 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
841 const src = inst_data.src();
842 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
843 const instrs = sema.code.extra[extra.end..][0..extra.data.body_len];
844
845 log.warn("TODO implement zirValidateStructInitPtr (compile errors for missing/dupe fields)", .{});
846}
847
848fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
849 const tracy = trace(@src());
850 defer tracy.end();
851
852 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
853 const ptr = try sema.resolveInst(bin_inst.lhs);
854 const value = try sema.resolveInst(bin_inst.rhs);
855 const ptr_ty = try sema.mod.simplePtrType(sema.arena, value.ty, true, .One);
856 // TODO detect when this store should be done at compile-time. For example,
857 // if expressions should force it when the condition is compile-time known.
858 const src: LazySrcLoc = .unneeded;
859 try sema.requireRuntimeBlock(block, src);
860 const bitcasted_ptr = try block.addUnOp(src, ptr_ty, .bitcast, ptr);
861 return sema.storePtr(block, src, bitcasted_ptr, value);
862}
863
864fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
865 const tracy = trace(@src());
866 defer tracy.end();
867
868 const src: LazySrcLoc = .unneeded;
869 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
870 const ptr = try sema.resolveInst(bin_inst.lhs);
871 const value = try sema.resolveInst(bin_inst.rhs);
872 const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?;
873 // Add the stored instruction to the set we will use to resolve peer types
874 // for the inferred allocation.
875 try inferred_alloc.data.stored_inst_list.append(sema.arena, value);
876 // Create a runtime bitcast instruction with exactly the type the pointer wants.
877 const ptr_ty = try sema.mod.simplePtrType(sema.arena, value.ty, true, .One);
878 try sema.requireRuntimeBlock(block, src);
879 const bitcasted_ptr = try block.addUnOp(src, ptr_ty, .bitcast, ptr);
880 return sema.storePtr(block, src, bitcasted_ptr, value);
881}
882
883fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
884 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
885 const src = inst_data.src();
886 try sema.requireFunctionBlock(block, src);
887 const quota = try sema.resolveAlreadyCoercedInt(block, src, inst_data.operand, u32);
888 if (sema.branch_quota < quota)
889 sema.branch_quota = quota;
890}
891
892fn zirStore(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
893 const tracy = trace(@src());
894 defer tracy.end();
895
896 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
897 const ptr = try sema.resolveInst(bin_inst.lhs);
898 const value = try sema.resolveInst(bin_inst.rhs);
899 return sema.storePtr(block, sema.src, ptr, value);
900}
901
902fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
903 const tracy = trace(@src());
904 defer tracy.end();
905
906 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
907 const src = inst_data.src();
908 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
909 const ptr = try sema.resolveInst(extra.lhs);
910 const value = try sema.resolveInst(extra.rhs);
911 return sema.storePtr(block, src, ptr, value);
912}
913
914fn zirParamType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
915 const tracy = trace(@src());
916 defer tracy.end();
917
918 const src: LazySrcLoc = .unneeded;
919 const inst_data = sema.code.instructions.items(.data)[inst].param_type;
920 const fn_inst = try sema.resolveInst(inst_data.callee);
921 const param_index = inst_data.param_index;
922
923 const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) {
924 .Fn => fn_inst.ty,
925 .BoundFn => {
926 return sema.mod.fail(&block.base, fn_inst.src, "TODO implement zirParamType for method call syntax", .{});
927 },
928 else => {
929 return sema.mod.fail(&block.base, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty});
930 },
931 };
932
933 const param_count = fn_ty.fnParamLen();
934 if (param_index >= param_count) {
935 if (fn_ty.fnIsVarArgs()) {
936 return sema.mod.constType(sema.arena, src, Type.initTag(.var_args_param));
937 }
938 return sema.mod.fail(&block.base, src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{
939 param_index,
940 fn_ty,
941 param_count,
942 });
943 }
944
945 // TODO support generic functions
946 const param_type = fn_ty.fnParamType(param_index);
947 return sema.mod.constType(sema.arena, src, param_type);
948}
949
950fn zirStr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
951 const tracy = trace(@src());
952 defer tracy.end();
953
954 const zir_bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code);
955
956 // `zir_bytes` references memory inside the ZIR module, which can get deallocated
957 // after semantic analysis is complete, for example in the case of the initialization
958 // expression of a variable declaration. We need the memory to be in the new
959 // anonymous Decl's arena.
960
961 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
962 errdefer new_decl_arena.deinit();
963
964 const bytes = try new_decl_arena.allocator.dupe(u8, zir_bytes);
965
966 const decl_ty = try Type.Tag.array_u8_sentinel_0.create(&new_decl_arena.allocator, bytes.len);
967 const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, bytes);
968
969 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{
970 .ty = decl_ty,
971 .val = decl_val,
972 });
973 return sema.analyzeDeclRef(block, .unneeded, new_decl);
974}
975
976fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
977 const tracy = trace(@src());
978 defer tracy.end();
979
980 const int = sema.code.instructions.items(.data)[inst].int;
981 return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int);
982}
983
984fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
985 const tracy = trace(@src());
986 defer tracy.end();
987
988 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
989 const src = inst_data.src();
990 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
991 const msg = try sema.resolveConstString(block, operand_src, inst_data.operand);
992 return sema.mod.fail(&block.base, src, "{s}", .{msg});
993}
994
995fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
996 var managed = sema.mod.compile_log_text.toManaged(sema.gpa);
997 defer sema.mod.compile_log_text = managed.moveToUnmanaged();
998 const writer = managed.writer();
999
1000 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1001 const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index);
1002 const args = sema.code.refSlice(extra.end, extra.data.operands_len);
1003
1004 for (args) |arg_ref, i| {
1005 if (i != 0) try writer.print(", ", .{});
1006
1007 const arg = try sema.resolveInst(arg_ref);
1008 if (arg.value()) |val| {
1009 try writer.print("@as({}, {})", .{ arg.ty, val });
1010 } else {
1011 try writer.print("@as({}, [runtime value])", .{arg.ty});
1012 }
1013 }
1014 try writer.print("\n", .{});
1015
1016 const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, sema.owner_decl);
1017 if (!gop.found_existing) {
1018 gop.entry.value = inst_data.src().toSrcLoc(&block.base);
1019 }
1020}
1021
1022fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
1023 const tracy = trace(@src());
1024 defer tracy.end();
1025
1026 const src_node = sema.code.instructions.items(.data)[inst].node;
1027 const src: LazySrcLoc = .{ .node_offset = src_node };
1028 try sema.requireRuntimeBlock(block, src);
1029 return always_noreturn;
1030}
1031
1032fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1033 const tracy = trace(@src());
1034 defer tracy.end();
1035
1036 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1037 const src = inst_data.src();
1038 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
1039 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
1040
1041 // TZIR expects a block outside the loop block too.
1042 const block_inst = try sema.arena.create(Inst.Block);
1043 block_inst.* = .{
1044 .base = .{
1045 .tag = Inst.Block.base_tag,
1046 .ty = undefined,
1047 .src = src,
1048 },
1049 .body = undefined,
1050 };
1051
1052 var child_block = parent_block.makeSubBlock();
1053 child_block.label = Scope.Block.Label{
1054 .zir_block = inst,
1055 .merges = .{
1056 .results = .{},
1057 .br_list = .{},
1058 .block_inst = block_inst,
1059 },
1060 };
1061 const merges = &child_block.label.?.merges;
1062
1063 defer child_block.instructions.deinit(sema.gpa);
1064 defer merges.results.deinit(sema.gpa);
1065 defer merges.br_list.deinit(sema.gpa);
1066
1067 // Reserve space for a Loop instruction so that generated Break instructions can
1068 // point to it, even if it doesn't end up getting used because the code ends up being
1069 // comptime evaluated.
1070 const loop_inst = try sema.arena.create(Inst.Loop);
1071 loop_inst.* = .{
1072 .base = .{
1073 .tag = Inst.Loop.base_tag,
1074 .ty = Type.initTag(.noreturn),
1075 .src = src,
1076 },
1077 .body = undefined,
1078 };
1079
1080 var loop_block = child_block.makeSubBlock();
1081 defer loop_block.instructions.deinit(sema.gpa);
1082
1083 _ = try sema.analyzeBody(&loop_block, body);
1084
1085 // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
1086
1087 try child_block.instructions.append(sema.gpa, &loop_inst.base);
1088 loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, loop_block.instructions.items) };
1089
1090 return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
1091}
1092
1093fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1094 const tracy = trace(@src());
1095 defer tracy.end();
1096
1097 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1098 const src = inst_data.src();
1099 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
1100 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
1101
1102 // Reserve space for a Block instruction so that generated Break instructions can
1103 // point to it, even if it doesn't end up getting used because the code ends up being
1104 // comptime evaluated.
1105 const block_inst = try sema.arena.create(Inst.Block);
1106 block_inst.* = .{
1107 .base = .{
1108 .tag = Inst.Block.base_tag,
1109 .ty = undefined, // Set after analysis.
1110 .src = src,
1111 },
1112 .body = undefined,
1113 };
1114
1115 var child_block: Scope.Block = .{
1116 .parent = parent_block,
1117 .sema = sema,
1118 .src_decl = parent_block.src_decl,
1119 .instructions = .{},
1120 // TODO @as here is working around a stage1 miscompilation bug :(
1121 .label = @as(?Scope.Block.Label, Scope.Block.Label{
1122 .zir_block = inst,
1123 .merges = .{
1124 .results = .{},
1125 .br_list = .{},
1126 .block_inst = block_inst,
1127 },
1128 }),
1129 .inlining = parent_block.inlining,
1130 .is_comptime = parent_block.is_comptime,
1131 };
1132 const merges = &child_block.label.?.merges;
1133
1134 defer child_block.instructions.deinit(sema.gpa);
1135 defer merges.results.deinit(sema.gpa);
1136 defer merges.br_list.deinit(sema.gpa);
1137
1138 _ = try sema.analyzeBody(&child_block, body);
1139
1140 return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
1141}
1142
1143fn analyzeBlockBody(
1144 sema: *Sema,
1145 parent_block: *Scope.Block,
1146 src: LazySrcLoc,
1147 child_block: *Scope.Block,
1148 merges: *Scope.Block.Merges,
1149) InnerError!*Inst {
1150 const tracy = trace(@src());
1151 defer tracy.end();
1152
1153 // Blocks must terminate with noreturn instruction.
1154 assert(child_block.instructions.items.len != 0);
1155 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
1156
1157 if (merges.results.items.len == 0) {
1158 // No need for a block instruction. We can put the new instructions
1159 // directly into the parent block.
1160 const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items);
1161 try parent_block.instructions.appendSlice(sema.gpa, copied_instructions);
1162 return copied_instructions[copied_instructions.len - 1];
1163 }
1164 if (merges.results.items.len == 1) {
1165 const last_inst_index = child_block.instructions.items.len - 1;
1166 const last_inst = child_block.instructions.items[last_inst_index];
1167 if (last_inst.breakBlock()) |br_block| {
1168 if (br_block == merges.block_inst) {
1169 // No need for a block instruction. We can put the new instructions directly
1170 // into the parent block. Here we omit the break instruction.
1171 const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
1172 try parent_block.instructions.appendSlice(sema.gpa, copied_instructions);
1173 return merges.results.items[0];
1174 }
1175 }
1176 }
1177 // It is impossible to have the number of results be > 1 in a comptime scope.
1178 assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition.
1179
1180 // Need to set the type and emit the Block instruction. This allows machine code generation
1181 // to emit a jump instruction to after the block when it encounters the break.
1182 try parent_block.instructions.append(sema.gpa, &merges.block_inst.base);
1183 const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items);
1184 merges.block_inst.base.ty = resolved_ty;
1185 merges.block_inst.body = .{
1186 .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items),
1187 };
1188 // Now that the block has its type resolved, we need to go back into all the break
1189 // instructions, and insert type coercion on the operands.
1190 for (merges.br_list.items) |br| {
1191 if (br.operand.ty.eql(resolved_ty)) {
1192 // No type coercion needed.
1193 continue;
1194 }
1195 var coerce_block = parent_block.makeSubBlock();
1196 defer coerce_block.instructions.deinit(sema.gpa);
1197 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br.operand, br.operand.src);
1198 // If no instructions were produced, such as in the case of a coercion of a
1199 // constant value to a new type, we can simply point the br operand to it.
1200 if (coerce_block.instructions.items.len == 0) {
1201 br.operand = coerced_operand;
1202 continue;
1203 }
1204 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);
1205 // Here we depend on the br instruction having been over-allocated (if necessary)
1206 // inside zirBreak so that it can be converted into a br_block_flat instruction.
1207 const br_src = br.base.src;
1208 const br_ty = br.base.ty;
1209 const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);
1210 br_block_flat.* = .{
1211 .base = .{
1212 .src = br_src,
1213 .ty = br_ty,
1214 .tag = .br_block_flat,
1215 },
1216 .block = merges.block_inst,
1217 .body = .{
1218 .instructions = try sema.arena.dupe(*Inst, coerce_block.instructions.items),
1219 },
1220 };
1221 }
1222 return &merges.block_inst.base;
1223}
1224
1225fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
1226 const tracy = trace(@src());
1227 defer tracy.end();
1228
1229 const src_node = sema.code.instructions.items(.data)[inst].node;
1230 const src: LazySrcLoc = .{ .node_offset = src_node };
1231 try sema.requireRuntimeBlock(block, src);
1232 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
1233}
1234
1235fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
1236 const tracy = trace(@src());
1237 defer tracy.end();
1238
1239 const inst_data = sema.code.instructions.items(.data)[inst].@"break";
1240 const src = sema.src;
1241 const operand = try sema.resolveInst(inst_data.operand);
1242 const zir_block = inst_data.block_inst;
1243
1244 var block = start_block;
1245 while (true) {
1246 if (block.label) |*label| {
1247 if (label.zir_block == zir_block) {
1248 // Here we add a br instruction, but we over-allocate a little bit
1249 // (if necessary) to make it possible to convert the instruction into
1250 // a br_block_flat instruction later.
1251 const br = @ptrCast(*Inst.Br, try sema.arena.alignedAlloc(
1252 u8,
1253 Inst.convertable_br_align,
1254 Inst.convertable_br_size,
1255 ));
1256 br.* = .{
1257 .base = .{
1258 .tag = .br,
1259 .ty = Type.initTag(.noreturn),
1260 .src = src,
1261 },
1262 .operand = operand,
1263 .block = label.merges.block_inst,
1264 };
1265 try start_block.instructions.append(sema.gpa, &br.base);
1266 try label.merges.results.append(sema.gpa, operand);
1267 try label.merges.br_list.append(sema.gpa, br);
1268 return inst;
1269 }
1270 }
1271 block = block.parent.?;
1272 }
1273}
1274
1275fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
1276 const tracy = trace(@src());
1277 defer tracy.end();
1278
1279 // We do not set sema.src here because dbg_stmt instructions are only emitted for
1280 // ZIR code that possibly will need to generate runtime code. So error messages
1281 // and other source locations must not rely on sema.src being set from dbg_stmt
1282 // instructions.
1283 if (block.is_comptime) return;
1284
1285 const src_node = sema.code.instructions.items(.data)[inst].node;
1286 const src: LazySrcLoc = .{ .node_offset = src_node };
1287
1288 const src_loc = src.toSrcLoc(&block.base);
1289 const abs_byte_off = try src_loc.byteOffset();
1290 _ = try block.addDbgStmt(src, abs_byte_off);
1291}
1292
1293fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1294 const tracy = trace(@src());
1295 defer tracy.end();
1296
1297 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1298 const src = inst_data.src();
1299 const decl = sema.code.decls[inst_data.payload_index];
1300 return sema.analyzeDeclRef(block, src, decl);
1301}
1302
1303fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1304 const tracy = trace(@src());
1305 defer tracy.end();
1306
1307 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1308 const src = inst_data.src();
1309 const decl = sema.code.decls[inst_data.payload_index];
1310 return sema.analyzeDeclVal(block, src, decl);
1311}
1312
1313fn zirCallNone(
1314 sema: *Sema,
1315 block: *Scope.Block,
1316 inst: zir.Inst.Index,
1317 ensure_result_used: bool,
1318) InnerError!*Inst {
1319 const tracy = trace(@src());
1320 defer tracy.end();
1321
1322 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1323 const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
1324
1325 return sema.analyzeCall(block, inst_data.operand, func_src, inst_data.src(), .auto, ensure_result_used, &.{});
1326}
1327
1328fn zirCall(
1329 sema: *Sema,
1330 block: *Scope.Block,
1331 inst: zir.Inst.Index,
1332 modifier: std.builtin.CallOptions.Modifier,
1333 ensure_result_used: bool,
1334) InnerError!*Inst {
1335 const tracy = trace(@src());
1336 defer tracy.end();
1337
1338 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1339 const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
1340 const call_src = inst_data.src();
1341 const extra = sema.code.extraData(zir.Inst.Call, inst_data.payload_index);
1342 const args = sema.code.refSlice(extra.end, extra.data.args_len);
1343
1344 return sema.analyzeCall(block, extra.data.callee, func_src, call_src, modifier, ensure_result_used, args);
1345}
1346
1347fn analyzeCall(
1348 sema: *Sema,
1349 block: *Scope.Block,
1350 zir_func: zir.Inst.Ref,
1351 func_src: LazySrcLoc,
1352 call_src: LazySrcLoc,
1353 modifier: std.builtin.CallOptions.Modifier,
1354 ensure_result_used: bool,
1355 zir_args: []const zir.Inst.Ref,
1356) InnerError!*ir.Inst {
1357 const func = try sema.resolveInst(zir_func);
1358
1359 if (func.ty.zigTypeTag() != .Fn)
1360 return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty});
1361
1362 const cc = func.ty.fnCallingConvention();
1363 if (cc == .Naked) {
1364 // TODO add error note: declared here
1365 return sema.mod.fail(
1366 &block.base,
1367 func_src,
1368 "unable to call function with naked calling convention",
1369 .{},
1370 );
1371 }
1372 const fn_params_len = func.ty.fnParamLen();
1373 if (func.ty.fnIsVarArgs()) {
1374 assert(cc == .C);
1375 if (zir_args.len < fn_params_len) {
1376 // TODO add error note: declared here
1377 return sema.mod.fail(
1378 &block.base,
1379 func_src,
1380 "expected at least {d} argument(s), found {d}",
1381 .{ fn_params_len, zir_args.len },
1382 );
1383 }
1384 } else if (fn_params_len != zir_args.len) {
1385 // TODO add error note: declared here
1386 return sema.mod.fail(
1387 &block.base,
1388 func_src,
1389 "expected {d} argument(s), found {d}",
1390 .{ fn_params_len, zir_args.len },
1391 );
1392 }
1393
1394 if (modifier == .compile_time) {
1395 return sema.mod.fail(&block.base, call_src, "TODO implement comptime function calls", .{});
1396 }
1397 if (modifier != .auto) {
1398 return sema.mod.fail(&block.base, call_src, "TODO implement call with modifier {}", .{modifier});
1399 }
1400
1401 // TODO handle function calls of generic functions
1402 const casted_args = try sema.arena.alloc(*Inst, zir_args.len);
1403 for (zir_args) |zir_arg, i| {
1404 // the args are already casted to the result of a param type instruction.
1405 casted_args[i] = try sema.resolveInst(zir_arg);
1406 }
1407
1408 const ret_type = func.ty.fnReturnType();
1409
1410 const is_comptime_call = block.is_comptime or modifier == .compile_time;
1411 const is_inline_call = is_comptime_call or modifier == .always_inline or
1412 func.ty.fnCallingConvention() == .Inline;
1413 const result: *Inst = if (is_inline_call) res: {
1414 const func_val = try sema.resolveConstValue(block, func_src, func);
1415 const module_fn = switch (func_val.tag()) {
1416 .function => func_val.castTag(.function).?.data,
1417 .extern_fn => return sema.mod.fail(&block.base, call_src, "{s} call of extern function", .{
1418 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
1419 }),
1420 else => unreachable,
1421 };
1422
1423 // Analyze the ZIR. The same ZIR gets analyzed into a runtime function
1424 // or an inlined call depending on what union tag the `label` field is
1425 // set to in the `Scope.Block`.
1426 // This block instruction will be used to capture the return value from the
1427 // inlined function.
1428 const block_inst = try sema.arena.create(Inst.Block);
1429 block_inst.* = .{
1430 .base = .{
1431 .tag = Inst.Block.base_tag,
1432 .ty = ret_type,
1433 .src = call_src,
1434 },
1435 .body = undefined,
1436 };
1437 // This one is shared among sub-blocks within the same callee, but not
1438 // shared among the entire inline/comptime call stack.
1439 var inlining: Scope.Block.Inlining = .{
1440 .merges = .{
1441 .results = .{},
1442 .br_list = .{},
1443 .block_inst = block_inst,
1444 },
1445 };
1446 var inline_sema: Sema = .{
1447 .mod = sema.mod,
1448 .gpa = sema.mod.gpa,
1449 .arena = sema.arena,
1450 .code = module_fn.zir,
1451 .inst_map = try sema.gpa.alloc(*ir.Inst, module_fn.zir.instructions.len),
1452 .owner_decl = sema.owner_decl,
1453 .owner_func = sema.owner_func,
1454 .func = module_fn,
1455 .param_inst_list = casted_args,
1456 .branch_quota = sema.branch_quota,
1457 .branch_count = sema.branch_count,
1458 };
1459 defer sema.gpa.free(inline_sema.inst_map);
1460
1461 var child_block: Scope.Block = .{
1462 .parent = null,
1463 .sema = &inline_sema,
1464 .src_decl = module_fn.owner_decl,
1465 .instructions = .{},
1466 .label = null,
1467 .inlining = &inlining,
1468 .is_comptime = is_comptime_call,
1469 };
1470
1471 const merges = &child_block.inlining.?.merges;
1472
1473 defer child_block.instructions.deinit(sema.gpa);
1474 defer merges.results.deinit(sema.gpa);
1475 defer merges.br_list.deinit(sema.gpa);
1476
1477 try inline_sema.emitBackwardBranch(&child_block, call_src);
1478
1479 // This will have return instructions analyzed as break instructions to
1480 // the block_inst above.
1481 _ = try inline_sema.root(&child_block);
1482
1483 const result = try inline_sema.analyzeBlockBody(block, call_src, &child_block, merges);
1484
1485 sema.branch_quota = inline_sema.branch_quota;
1486 sema.branch_count = inline_sema.branch_count;
1487
1488 break :res result;
1489 } else res: {
1490 try sema.requireRuntimeBlock(block, call_src);
1491 break :res try block.addCall(call_src, ret_type, func, casted_args);
1492 };
1493
1494 if (ensure_result_used) {
1495 try sema.ensureResultUsed(block, result, call_src);
1496 }
1497 return result;
1498}
1499
1500fn zirIntType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1501 const tracy = trace(@src());
1502 defer tracy.end();
1503
1504 const int_type = sema.code.instructions.items(.data)[inst].int_type;
1505 const src = int_type.src();
1506 const ty = try Module.makeIntType(sema.arena, int_type.signedness, int_type.bit_count);
1507
1508 return sema.mod.constType(sema.arena, src, ty);
1509}
1510
1511fn zirOptionalType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1512 const tracy = trace(@src());
1513 defer tracy.end();
1514
1515 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1516 const src = inst_data.src();
1517 const child_type = try sema.resolveType(block, src, inst_data.operand);
1518 const opt_type = try sema.mod.optionalType(sema.arena, child_type);
1519
1520 return sema.mod.constType(sema.arena, src, opt_type);
1521}
1522
1523fn zirOptionalTypeFromPtrElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1524 const tracy = trace(@src());
1525 defer tracy.end();
1526
1527 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1528 const ptr = try sema.resolveInst(inst_data.operand);
1529 const elem_ty = ptr.ty.elemType();
1530 const opt_ty = try sema.mod.optionalType(sema.arena, elem_ty);
1531
1532 return sema.mod.constType(sema.arena, inst_data.src(), opt_ty);
1533}
1534
1535fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1536 const tracy = trace(@src());
1537 defer tracy.end();
1538
1539 // TODO these should be lazily evaluated
1540 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
1541 const len = try sema.resolveInstConst(block, .unneeded, bin_inst.lhs);
1542 const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs);
1543 const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), null, elem_type);
1544
1545 return sema.mod.constType(sema.arena, .unneeded, array_ty);
1546}
1547
1548fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1549 const tracy = trace(@src());
1550 defer tracy.end();
1551
1552 // TODO these should be lazily evaluated
1553 const inst_data = sema.code.instructions.items(.data)[inst].array_type_sentinel;
1554 const len = try sema.resolveInstConst(block, .unneeded, inst_data.len);
1555 const extra = sema.code.extraData(zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data;
1556 const sentinel = try sema.resolveInstConst(block, .unneeded, extra.sentinel);
1557 const elem_type = try sema.resolveType(block, .unneeded, extra.elem_type);
1558 const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), sentinel.val, elem_type);
1559
1560 return sema.mod.constType(sema.arena, .unneeded, array_ty);
1561}
1562
1563fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1564 const tracy = trace(@src());
1565 defer tracy.end();
1566
1567 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1568 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
1569 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
1570 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
1571 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
1572 const error_union = try sema.resolveType(block, lhs_src, extra.lhs);
1573 const payload = try sema.resolveType(block, rhs_src, extra.rhs);
1574
1575 if (error_union.zigTypeTag() != .ErrorSet) {
1576 return sema.mod.fail(&block.base, lhs_src, "expected error set type, found {}", .{
1577 error_union.elemType(),
1578 });
1579 }
1580 const err_union_ty = try sema.mod.errorUnionType(sema.arena, error_union, payload);
1581 return sema.mod.constType(sema.arena, src, err_union_ty);
1582}
1583
1584fn zirErrorValue(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1585 const tracy = trace(@src());
1586 defer tracy.end();
1587
1588 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
1589 const src = inst_data.src();
1590
1591 // Create an anonymous error set type with only this error value, and return the value.
1592 const entry = try sema.mod.getErrorValue(inst_data.get(sema.code));
1593 const result_type = try Type.Tag.error_set_single.create(sema.arena, entry.key);
1594 return sema.mod.constInst(sema.arena, src, .{
1595 .ty = result_type,
1596 .val = try Value.Tag.@"error".create(sema.arena, .{
1597 .name = entry.key,
1598 }),
1599 });
1600}
1601
1602fn zirErrorToInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1603 const tracy = trace(@src());
1604 defer tracy.end();
1605
1606 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1607 const src = inst_data.src();
1608 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1609 const op = try sema.resolveInst(inst_data.operand);
1610 const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);
1611
1612 if (op_coerced.value()) |val| {
1613 const payload = try sema.arena.create(Value.Payload.U64);
1614 payload.* = .{
1615 .base = .{ .tag = .int_u64 },
1616 .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
1617 };
1618 return sema.mod.constInst(sema.arena, src, .{
1619 .ty = Type.initTag(.u16),
1620 .val = Value.initPayload(&payload.base),
1621 });
1622 }
1623
1624 try sema.requireRuntimeBlock(block, src);
1625 return block.addUnOp(src, Type.initTag(.u16), .error_to_int, op_coerced);
1626}
1627
1628fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1629 const tracy = trace(@src());
1630 defer tracy.end();
1631
1632 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1633 const src = inst_data.src();
1634 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1635
1636 const op = try sema.resolveInst(inst_data.operand);
1637
1638 if (try sema.resolveDefinedValue(block, operand_src, op)) |value| {
1639 const int = value.toUnsignedInt();
1640 if (int > sema.mod.global_error_set.count() or int == 0)
1641 return sema.mod.fail(&block.base, operand_src, "integer value {d} represents no error", .{int});
1642 const payload = try sema.arena.create(Value.Payload.Error);
1643 payload.* = .{
1644 .base = .{ .tag = .@"error" },
1645 .data = .{ .name = sema.mod.error_name_list.items[int] },
1646 };
1647 return sema.mod.constInst(sema.arena, src, .{
1648 .ty = Type.initTag(.anyerror),
1649 .val = Value.initPayload(&payload.base),
1650 });
1651 }
1652 try sema.requireRuntimeBlock(block, src);
1653 if (block.wantSafety()) {
1654 return sema.mod.fail(&block.base, src, "TODO: get max errors in compilation", .{});
1655 // const is_gt_max = @panic("TODO get max errors in compilation");
1656 // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
1657 }
1658 return block.addUnOp(src, Type.initTag(.anyerror), .int_to_error, op);
1659}
1660
1661fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1662 const tracy = trace(@src());
1663 defer tracy.end();
1664
1665 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1666 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
1667 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
1668 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
1669 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
1670 const lhs_ty = try sema.resolveType(block, lhs_src, extra.lhs);
1671 const rhs_ty = try sema.resolveType(block, rhs_src, extra.rhs);
1672 if (rhs_ty.zigTypeTag() != .ErrorSet)
1673 return sema.mod.fail(&block.base, rhs_src, "expected error set type, found {}", .{rhs_ty});
1674 if (lhs_ty.zigTypeTag() != .ErrorSet)
1675 return sema.mod.fail(&block.base, lhs_src, "expected error set type, found {}", .{lhs_ty});
1676
1677 // Anything merged with anyerror is anyerror.
1678 if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {
1679 return sema.mod.constInst(sema.arena, src, .{
1680 .ty = Type.initTag(.type),
1681 .val = Value.initTag(.anyerror_type),
1682 });
1683 }
1684 // When we support inferred error sets, we'll want to use a data structure that can
1685 // represent a merged set of errors without forcing them to be resolved here. Until then
1686 // we re-use the same data structure that is used for explicit error set declarations.
1687 var set: std.StringHashMapUnmanaged(void) = .{};
1688 defer set.deinit(sema.gpa);
1689
1690 switch (lhs_ty.tag()) {
1691 .error_set_single => {
1692 const name = lhs_ty.castTag(.error_set_single).?.data;
1693 try set.put(sema.gpa, name, {});
1694 },
1695 .error_set => {
1696 const lhs_set = lhs_ty.castTag(.error_set).?.data;
1697 try set.ensureCapacity(sema.gpa, set.count() + lhs_set.names_len);
1698 for (lhs_set.names_ptr[0..lhs_set.names_len]) |name| {
1699 set.putAssumeCapacityNoClobber(name, {});
1700 }
1701 },
1702 else => unreachable,
1703 }
1704 switch (rhs_ty.tag()) {
1705 .error_set_single => {
1706 const name = rhs_ty.castTag(.error_set_single).?.data;
1707 try set.put(sema.gpa, name, {});
1708 },
1709 .error_set => {
1710 const rhs_set = rhs_ty.castTag(.error_set).?.data;
1711 try set.ensureCapacity(sema.gpa, set.count() + rhs_set.names_len);
1712 for (rhs_set.names_ptr[0..rhs_set.names_len]) |name| {
1713 set.putAssumeCapacity(name, {});
1714 }
1715 },
1716 else => unreachable,
1717 }
1718
1719 const new_names = try sema.arena.alloc([]const u8, set.count());
1720 var it = set.iterator();
1721 var i: usize = 0;
1722 while (it.next()) |entry| : (i += 1) {
1723 new_names[i] = entry.key;
1724 }
1725
1726 const new_error_set = try sema.arena.create(Module.ErrorSet);
1727 new_error_set.* = .{
1728 .owner_decl = sema.owner_decl,
1729 .node_offset = inst_data.src_node,
1730 .names_ptr = new_names.ptr,
1731 .names_len = @intCast(u32, new_names.len),
1732 };
1733 const error_set_ty = try Type.Tag.error_set.create(sema.arena, new_error_set);
1734 return sema.mod.constInst(sema.arena, src, .{
1735 .ty = Type.initTag(.type),
1736 .val = try Value.Tag.ty.create(sema.arena, error_set_ty),
1737 });
1738}
1739
1740fn zirEnumLiteral(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1741 const tracy = trace(@src());
1742 defer tracy.end();
1743
1744 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
1745 const src = inst_data.src();
1746 const duped_name = try sema.arena.dupe(u8, inst_data.get(sema.code));
1747 return sema.mod.constInst(sema.arena, src, .{
1748 .ty = Type.initTag(.enum_literal),
1749 .val = try Value.Tag.enum_literal.create(sema.arena, duped_name),
1750 });
1751}
1752
1753fn zirEnumLiteralSmall(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1754 const tracy = trace(@src());
1755 defer tracy.end();
1756
1757 const name = sema.code.instructions.items(.data)[inst].small_str.get();
1758 const src: LazySrcLoc = .unneeded;
1759 const duped_name = try sema.arena.dupe(u8, name);
1760 return sema.mod.constInst(sema.arena, src, .{
1761 .ty = Type.initTag(.enum_literal),
1762 .val = try Value.Tag.enum_literal.create(sema.arena, duped_name),
1763 });
1764}
1765
1766/// Pointer in, pointer out.
1767fn zirOptionalPayloadPtr(
1768 sema: *Sema,
1769 block: *Scope.Block,
1770 inst: zir.Inst.Index,
1771 safety_check: bool,
1772) InnerError!*Inst {
1773 const tracy = trace(@src());
1774 defer tracy.end();
1775
1776 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1777 const optional_ptr = try sema.resolveInst(inst_data.operand);
1778 assert(optional_ptr.ty.zigTypeTag() == .Pointer);
1779 const src = inst_data.src();
1780
1781 const opt_type = optional_ptr.ty.elemType();
1782 if (opt_type.zigTypeTag() != .Optional) {
1783 return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type});
1784 }
1785
1786 const child_type = try opt_type.optionalChildAlloc(sema.arena);
1787 const child_pointer = try sema.mod.simplePtrType(sema.arena, child_type, !optional_ptr.ty.isConstPtr(), .One);
1788
1789 if (optional_ptr.value()) |pointer_val| {
1790 const val = try pointer_val.pointerDeref(sema.arena);
1791 if (val.isNull()) {
1792 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
1793 }
1794 // The same Value represents the pointer to the optional and the payload.
1795 return sema.mod.constInst(sema.arena, src, .{
1796 .ty = child_pointer,
1797 .val = pointer_val,
1798 });
1799 }
1800
1801 try sema.requireRuntimeBlock(block, src);
1802 if (safety_check and block.wantSafety()) {
1803 const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null_ptr, optional_ptr);
1804 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
1805 }
1806 return block.addUnOp(src, child_pointer, .optional_payload_ptr, optional_ptr);
1807}
1808
1809/// Value in, value out.
1810fn zirOptionalPayload(
1811 sema: *Sema,
1812 block: *Scope.Block,
1813 inst: zir.Inst.Index,
1814 safety_check: bool,
1815) InnerError!*Inst {
1816 const tracy = trace(@src());
1817 defer tracy.end();
1818
1819 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1820 const src = inst_data.src();
1821 const operand = try sema.resolveInst(inst_data.operand);
1822 const opt_type = operand.ty;
1823 if (opt_type.zigTypeTag() != .Optional) {
1824 return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type});
1825 }
1826
1827 const child_type = try opt_type.optionalChildAlloc(sema.arena);
1828
1829 if (operand.value()) |val| {
1830 if (val.isNull()) {
1831 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
1832 }
1833 return sema.mod.constInst(sema.arena, src, .{
1834 .ty = child_type,
1835 .val = val,
1836 });
1837 }
1838
1839 try sema.requireRuntimeBlock(block, src);
1840 if (safety_check and block.wantSafety()) {
1841 const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null, operand);
1842 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
1843 }
1844 return block.addUnOp(src, child_type, .optional_payload, operand);
1845}
1846
1847/// Value in, value out
1848fn zirErrUnionPayload(
1849 sema: *Sema,
1850 block: *Scope.Block,
1851 inst: zir.Inst.Index,
1852 safety_check: bool,
1853) InnerError!*Inst {
1854 const tracy = trace(@src());
1855 defer tracy.end();
1856
1857 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1858 const src = inst_data.src();
1859 const operand = try sema.resolveInst(inst_data.operand);
1860 if (operand.ty.zigTypeTag() != .ErrorUnion)
1861 return sema.mod.fail(&block.base, operand.src, "expected error union type, found '{}'", .{operand.ty});
1862
1863 if (operand.value()) |val| {
1864 if (val.getError()) |name| {
1865 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
1866 }
1867 const data = val.castTag(.error_union).?.data;
1868 return sema.mod.constInst(sema.arena, src, .{
1869 .ty = operand.ty.castTag(.error_union).?.data.payload,
1870 .val = data,
1871 });
1872 }
1873 try sema.requireRuntimeBlock(block, src);
1874 if (safety_check and block.wantSafety()) {
1875 const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand);
1876 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
1877 }
1878 return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_payload, operand);
1879}
1880
1881/// Pointer in, pointer out.
1882fn zirErrUnionPayloadPtr(
1883 sema: *Sema,
1884 block: *Scope.Block,
1885 inst: zir.Inst.Index,
1886 safety_check: bool,
1887) InnerError!*Inst {
1888 const tracy = trace(@src());
1889 defer tracy.end();
1890
1891 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1892 const src = inst_data.src();
1893 const operand = try sema.resolveInst(inst_data.operand);
1894 assert(operand.ty.zigTypeTag() == .Pointer);
1895
1896 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
1897 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});
1898
1899 const operand_pointer_ty = try sema.mod.simplePtrType(sema.arena, operand.ty.elemType().castTag(.error_union).?.data.payload, !operand.ty.isConstPtr(), .One);
1900
1901 if (operand.value()) |pointer_val| {
1902 const val = try pointer_val.pointerDeref(sema.arena);
1903 if (val.getError()) |name| {
1904 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
1905 }
1906 const data = val.castTag(.error_union).?.data;
1907 // The same Value represents the pointer to the error union and the payload.
1908 return sema.mod.constInst(sema.arena, src, .{
1909 .ty = operand_pointer_ty,
1910 .val = try Value.Tag.ref_val.create(
1911 sema.arena,
1912 data,
1913 ),
1914 });
1915 }
1916
1917 try sema.requireRuntimeBlock(block, src);
1918 if (safety_check and block.wantSafety()) {
1919 const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand);
1920 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
1921 }
1922 return block.addUnOp(src, operand_pointer_ty, .unwrap_errunion_payload_ptr, operand);
1923}
1924
1925/// Value in, value out
1926fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1927 const tracy = trace(@src());
1928 defer tracy.end();
1929
1930 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1931 const src = inst_data.src();
1932 const operand = try sema.resolveInst(inst_data.operand);
1933 if (operand.ty.zigTypeTag() != .ErrorUnion)
1934 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});
1935
1936 if (operand.value()) |val| {
1937 assert(val.getError() != null);
1938 const data = val.castTag(.error_union).?.data;
1939 return sema.mod.constInst(sema.arena, src, .{
1940 .ty = operand.ty.castTag(.error_union).?.data.error_set,
1941 .val = data,
1942 });
1943 }
1944
1945 try sema.requireRuntimeBlock(block, src);
1946 return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand);
1947}
1948
1949/// Pointer in, value out
1950fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1951 const tracy = trace(@src());
1952 defer tracy.end();
1953
1954 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1955 const src = inst_data.src();
1956 const operand = try sema.resolveInst(inst_data.operand);
1957 assert(operand.ty.zigTypeTag() == .Pointer);
1958
1959 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
1960 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});
1961
1962 if (operand.value()) |pointer_val| {
1963 const val = try pointer_val.pointerDeref(sema.arena);
1964 assert(val.getError() != null);
1965 const data = val.castTag(.error_union).?.data;
1966 return sema.mod.constInst(sema.arena, src, .{
1967 .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set,
1968 .val = data,
1969 });
1970 }
1971
1972 try sema.requireRuntimeBlock(block, src);
1973 return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand);
1974}
1975
1976fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
1977 const tracy = trace(@src());
1978 defer tracy.end();
1979
1980 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
1981 const src = inst_data.src();
1982 const operand = try sema.resolveInst(inst_data.operand);
1983 if (operand.ty.zigTypeTag() != .ErrorUnion)
1984 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});
1985 if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) {
1986 return sema.mod.fail(&block.base, src, "expression value is ignored", .{});
1987 }
1988}
1989
1990fn zirFnType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: bool) InnerError!*Inst {
1991 const tracy = trace(@src());
1992 defer tracy.end();
1993
1994 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1995 const src = inst_data.src();
1996 const extra = sema.code.extraData(zir.Inst.FnType, inst_data.payload_index);
1997 const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len);
1998
1999 return sema.fnTypeCommon(
2000 block,
2001 inst_data.src_node,
2002 param_types,
2003 extra.data.return_type,
2004 .Unspecified,
2005 var_args,
2006 );
2007}
2008
2009fn zirFnTypeCc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: bool) InnerError!*Inst {
2010 const tracy = trace(@src());
2011 defer tracy.end();
2012
2013 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2014 const src = inst_data.src();
2015 const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node };
2016 const extra = sema.code.extraData(zir.Inst.FnTypeCc, inst_data.payload_index);
2017 const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len);
2018
2019 const cc_tv = try sema.resolveInstConst(block, cc_src, extra.data.cc);
2020 // TODO once we're capable of importing and analyzing decls from
2021 // std.builtin, this needs to change
2022 const cc_str = cc_tv.val.castTag(.enum_literal).?.data;
2023 const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse
2024 return sema.mod.fail(&block.base, cc_src, "Unknown calling convention {s}", .{cc_str});
2025 return sema.fnTypeCommon(
2026 block,
2027 inst_data.src_node,
2028 param_types,
2029 extra.data.return_type,
2030 cc,
2031 var_args,
2032 );
2033}
2034
2035fn fnTypeCommon(
2036 sema: *Sema,
2037 block: *Scope.Block,
2038 src_node_offset: i32,
2039 zir_param_types: []const zir.Inst.Ref,
2040 zir_return_type: zir.Inst.Ref,
2041 cc: std.builtin.CallingConvention,
2042 var_args: bool,
2043) InnerError!*Inst {
2044 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
2045 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
2046 const return_type = try sema.resolveType(block, ret_ty_src, zir_return_type);
2047
2048 // Hot path for some common function types.
2049 if (zir_param_types.len == 0 and !var_args) {
2050 if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
2051 return sema.mod.constType(sema.arena, src, Type.initTag(.fn_noreturn_no_args));
2052 }
2053
2054 if (return_type.zigTypeTag() == .Void and cc == .Unspecified) {
2055 return sema.mod.constType(sema.arena, src, Type.initTag(.fn_void_no_args));
2056 }
2057
2058 if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
2059 return sema.mod.constType(sema.arena, src, Type.initTag(.fn_naked_noreturn_no_args));
2060 }
2061
2062 if (return_type.zigTypeTag() == .Void and cc == .C) {
2063 return sema.mod.constType(sema.arena, src, Type.initTag(.fn_ccc_void_no_args));
2064 }
2065 }
2066
2067 const param_types = try sema.arena.alloc(Type, zir_param_types.len);
2068 for (zir_param_types) |param_type, i| {
2069 // TODO make a compile error from `resolveType` report the source location
2070 // of the specific parameter. Will need to take a similar strategy as
2071 // `resolveSwitchItemVal` to avoid resolving the source location unless
2072 // we actually need to report an error.
2073 param_types[i] = try sema.resolveType(block, src, param_type);
2074 }
2075
2076 const fn_ty = try Type.Tag.function.create(sema.arena, .{
2077 .param_types = param_types,
2078 .return_type = return_type,
2079 .cc = cc,
2080 .is_var_args = var_args,
2081 });
2082 return sema.mod.constType(sema.arena, src, fn_ty);
2083}
2084
2085fn zirAs(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2086 const tracy = trace(@src());
2087 defer tracy.end();
2088
2089 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
2090 return sema.analyzeAs(block, .unneeded, bin_inst.lhs, bin_inst.rhs);
2091}
2092
2093fn zirAsNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2094 const tracy = trace(@src());
2095 defer tracy.end();
2096
2097 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2098 const src = inst_data.src();
2099 const extra = sema.code.extraData(zir.Inst.As, inst_data.payload_index).data;
2100 return sema.analyzeAs(block, src, extra.dest_type, extra.operand);
2101}
2102
2103fn analyzeAs(
2104 sema: *Sema,
2105 block: *Scope.Block,
2106 src: LazySrcLoc,
2107 zir_dest_type: zir.Inst.Ref,
2108 zir_operand: zir.Inst.Ref,
2109) InnerError!*Inst {
2110 const dest_type = try sema.resolveType(block, src, zir_dest_type);
2111 const operand = try sema.resolveInst(zir_operand);
2112 return sema.coerce(block, dest_type, operand, src);
2113}
2114
2115fn zirPtrtoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2116 const tracy = trace(@src());
2117 defer tracy.end();
2118
2119 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2120 const ptr = try sema.resolveInst(inst_data.operand);
2121 if (ptr.ty.zigTypeTag() != .Pointer) {
2122 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2123 return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty});
2124 }
2125 // TODO handle known-pointer-address
2126 const src = inst_data.src();
2127 try sema.requireRuntimeBlock(block, src);
2128 const ty = Type.initTag(.usize);
2129 return block.addUnOp(src, ty, .ptrtoint, ptr);
2130}
2131
2132fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2133 const tracy = trace(@src());
2134 defer tracy.end();
2135
2136 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2137 const src = inst_data.src();
2138 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
2139 const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data;
2140 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
2141 const object = try sema.resolveInst(extra.lhs);
2142 const object_ptr = try sema.analyzeRef(block, src, object);
2143 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
2144 return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
2145}
2146
2147fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2148 const tracy = trace(@src());
2149 defer tracy.end();
2150
2151 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2152 const src = inst_data.src();
2153 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
2154 const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data;
2155 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
2156 const object_ptr = try sema.resolveInst(extra.lhs);
2157 return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
2158}
2159
2160fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2161 const tracy = trace(@src());
2162 defer tracy.end();
2163
2164 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2165 const src = inst_data.src();
2166 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2167 const extra = sema.code.extraData(zir.Inst.FieldNamed, inst_data.payload_index).data;
2168 const object = try sema.resolveInst(extra.lhs);
2169 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
2170 const object_ptr = try sema.analyzeRef(block, src, object);
2171 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
2172 return sema.analyzeLoad(block, src, result_ptr, src);
2173}
2174
2175fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2176 const tracy = trace(@src());
2177 defer tracy.end();
2178
2179 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2180 const src = inst_data.src();
2181 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2182 const extra = sema.code.extraData(zir.Inst.FieldNamed, inst_data.payload_index).data;
2183 const object_ptr = try sema.resolveInst(extra.lhs);
2184 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
2185 return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
2186}
2187
2188fn zirIntcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2189 const tracy = trace(@src());
2190 defer tracy.end();
2191
2192 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2193 const src = inst_data.src();
2194 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2195 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2196 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2197
2198 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
2199 const operand = try sema.resolveInst(extra.rhs);
2200
2201 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
2202 .ComptimeInt => true,
2203 .Int => false,
2204 else => return sema.mod.fail(
2205 &block.base,
2206 dest_ty_src,
2207 "expected integer type, found '{}'",
2208 .{dest_type},
2209 ),
2210 };
2211
2212 switch (operand.ty.zigTypeTag()) {
2213 .ComptimeInt, .Int => {},
2214 else => return sema.mod.fail(
2215 &block.base,
2216 operand_src,
2217 "expected integer type, found '{}'",
2218 .{operand.ty},
2219 ),
2220 }
2221
2222 if (operand.value() != null) {
2223 return sema.coerce(block, dest_type, operand, operand_src);
2224 } else if (dest_is_comptime_int) {
2225 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_int'", .{});
2226 }
2227
2228 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten int", .{});
2229}
2230
2231fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2232 const tracy = trace(@src());
2233 defer tracy.end();
2234
2235 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2236 const src = inst_data.src();
2237 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2238 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2239 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2240
2241 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
2242 const operand = try sema.resolveInst(extra.rhs);
2243 return sema.bitcast(block, dest_type, operand);
2244}
2245
2246fn zirFloatcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2247 const tracy = trace(@src());
2248 defer tracy.end();
2249
2250 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2251 const src = inst_data.src();
2252 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2253 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2254 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2255
2256 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
2257 const operand = try sema.resolveInst(extra.rhs);
2258
2259 const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
2260 .ComptimeFloat => true,
2261 .Float => false,
2262 else => return sema.mod.fail(
2263 &block.base,
2264 dest_ty_src,
2265 "expected float type, found '{}'",
2266 .{dest_type},
2267 ),
2268 };
2269
2270 switch (operand.ty.zigTypeTag()) {
2271 .ComptimeFloat, .Float, .ComptimeInt => {},
2272 else => return sema.mod.fail(
2273 &block.base,
2274 operand_src,
2275 "expected float type, found '{}'",
2276 .{operand.ty},
2277 ),
2278 }
2279
2280 if (operand.value() != null) {
2281 return sema.coerce(block, dest_type, operand, operand_src);
2282 } else if (dest_is_comptime_float) {
2283 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_float'", .{});
2284 }
2285
2286 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten float", .{});
2287}
2288
2289fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2290 const tracy = trace(@src());
2291 defer tracy.end();
2292
2293 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
2294 const array = try sema.resolveInst(bin_inst.lhs);
2295 const array_ptr = try sema.analyzeRef(block, sema.src, array);
2296 const elem_index = try sema.resolveInst(bin_inst.rhs);
2297 const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
2298 return sema.analyzeLoad(block, sema.src, result_ptr, sema.src);
2299}
2300
2301fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2302 const tracy = trace(@src());
2303 defer tracy.end();
2304
2305 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2306 const src = inst_data.src();
2307 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
2308 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2309 const array = try sema.resolveInst(extra.lhs);
2310 const array_ptr = try sema.analyzeRef(block, src, array);
2311 const elem_index = try sema.resolveInst(extra.rhs);
2312 const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
2313 return sema.analyzeLoad(block, src, result_ptr, src);
2314}
2315
2316fn zirElemPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2317 const tracy = trace(@src());
2318 defer tracy.end();
2319
2320 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
2321 const array_ptr = try sema.resolveInst(bin_inst.lhs);
2322 const elem_index = try sema.resolveInst(bin_inst.rhs);
2323 return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
2324}
2325
2326fn zirElemPtrNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2327 const tracy = trace(@src());
2328 defer tracy.end();
2329
2330 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2331 const src = inst_data.src();
2332 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
2333 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2334 const array_ptr = try sema.resolveInst(extra.lhs);
2335 const elem_index = try sema.resolveInst(extra.rhs);
2336 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
2337}
2338
2339fn zirSliceStart(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2340 const tracy = trace(@src());
2341 defer tracy.end();
2342
2343 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2344 const src = inst_data.src();
2345 const extra = sema.code.extraData(zir.Inst.SliceStart, inst_data.payload_index).data;
2346 const array_ptr = try sema.resolveInst(extra.lhs);
2347 const start = try sema.resolveInst(extra.start);
2348
2349 return sema.analyzeSlice(block, src, array_ptr, start, null, null, .unneeded);
2350}
2351
2352fn zirSliceEnd(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2353 const tracy = trace(@src());
2354 defer tracy.end();
2355
2356 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2357 const src = inst_data.src();
2358 const extra = sema.code.extraData(zir.Inst.SliceEnd, inst_data.payload_index).data;
2359 const array_ptr = try sema.resolveInst(extra.lhs);
2360 const start = try sema.resolveInst(extra.start);
2361 const end = try sema.resolveInst(extra.end);
2362
2363 return sema.analyzeSlice(block, src, array_ptr, start, end, null, .unneeded);
2364}
2365
2366fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2367 const tracy = trace(@src());
2368 defer tracy.end();
2369
2370 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2371 const src = inst_data.src();
2372 const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node };
2373 const extra = sema.code.extraData(zir.Inst.SliceSentinel, inst_data.payload_index).data;
2374 const array_ptr = try sema.resolveInst(extra.lhs);
2375 const start = try sema.resolveInst(extra.start);
2376 const end = try sema.resolveInst(extra.end);
2377 const sentinel = try sema.resolveInst(extra.sentinel);
2378
2379 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
2380}
2381
2382fn zirSwitchCapture(
2383 sema: *Sema,
2384 block: *Scope.Block,
2385 inst: zir.Inst.Index,
2386 is_multi: bool,
2387 is_ref: bool,
2388) InnerError!*Inst {
2389 const tracy = trace(@src());
2390 defer tracy.end();
2391
2392 const zir_datas = sema.code.instructions.items(.data);
2393 const capture_info = zir_datas[inst].switch_capture;
2394 const switch_info = zir_datas[capture_info.switch_inst].pl_node;
2395 const src = switch_info.src();
2396
2397 return sema.mod.fail(&block.base, src, "TODO implement Sema for zirSwitchCapture", .{});
2398}
2399
2400fn zirSwitchCaptureElse(
2401 sema: *Sema,
2402 block: *Scope.Block,
2403 inst: zir.Inst.Index,
2404 is_ref: bool,
2405) InnerError!*Inst {
2406 const tracy = trace(@src());
2407 defer tracy.end();
2408
2409 const zir_datas = sema.code.instructions.items(.data);
2410 const capture_info = zir_datas[inst].switch_capture;
2411 const switch_info = zir_datas[capture_info.switch_inst].pl_node;
2412 const src = switch_info.src();
2413
2414 return sema.mod.fail(&block.base, src, "TODO implement Sema for zirSwitchCaptureElse", .{});
2415}
2416
2417fn zirSwitchBlock(
2418 sema: *Sema,
2419 block: *Scope.Block,
2420 inst: zir.Inst.Index,
2421 is_ref: bool,
2422 special_prong: zir.SpecialProng,
2423) InnerError!*Inst {
2424 const tracy = trace(@src());
2425 defer tracy.end();
2426
2427 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2428 const src = inst_data.src();
2429 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2430 const extra = sema.code.extraData(zir.Inst.SwitchBlock, inst_data.payload_index);
2431
2432 const operand_ptr = try sema.resolveInst(extra.data.operand);
2433 const operand = if (is_ref)
2434 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
2435 else
2436 operand_ptr;
2437
2438 return sema.analyzeSwitch(
2439 block,
2440 operand,
2441 extra.end,
2442 special_prong,
2443 extra.data.cases_len,
2444 0,
2445 inst,
2446 inst_data.src_node,
2447 );
2448}
2449
2450fn zirSwitchBlockMulti(
2451 sema: *Sema,
2452 block: *Scope.Block,
2453 inst: zir.Inst.Index,
2454 is_ref: bool,
2455 special_prong: zir.SpecialProng,
2456) InnerError!*Inst {
2457 const tracy = trace(@src());
2458 defer tracy.end();
2459
2460 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2461 const src = inst_data.src();
2462 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2463 const extra = sema.code.extraData(zir.Inst.SwitchBlockMulti, inst_data.payload_index);
2464
2465 const operand_ptr = try sema.resolveInst(extra.data.operand);
2466 const operand = if (is_ref)
2467 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
2468 else
2469 operand_ptr;
2470
2471 return sema.analyzeSwitch(
2472 block,
2473 operand,
2474 extra.end,
2475 special_prong,
2476 extra.data.scalar_cases_len,
2477 extra.data.multi_cases_len,
2478 inst,
2479 inst_data.src_node,
2480 );
2481}
2482
2483fn analyzeSwitch(
2484 sema: *Sema,
2485 block: *Scope.Block,
2486 operand: *Inst,
2487 extra_end: usize,
2488 special_prong: zir.SpecialProng,
2489 scalar_cases_len: usize,
2490 multi_cases_len: usize,
2491 switch_inst: zir.Inst.Index,
2492 src_node_offset: i32,
2493) InnerError!*Inst {
2494 const gpa = sema.gpa;
2495 const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {
2496 .none => .{ .body = &.{}, .end = extra_end },
2497 .under, .@"else" => blk: {
2498 const body_len = sema.code.extra[extra_end];
2499 const extra_body_start = extra_end + 1;
2500 break :blk .{
2501 .body = sema.code.extra[extra_body_start..][0..body_len],
2502 .end = extra_body_start + body_len,
2503 };
2504 },
2505 };
2506
2507 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
2508 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
2509 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
2510
2511 // Validate usage of '_' prongs.
2512 if (special_prong == .under and !operand.ty.isExhaustiveEnum()) {
2513 const msg = msg: {
2514 const msg = try sema.mod.errMsg(
2515 &block.base,
2516 src,
2517 "'_' prong only allowed when switching on non-exhaustive enums",
2518 .{},
2519 );
2520 errdefer msg.destroy(gpa);
2521 try sema.mod.errNote(
2522 &block.base,
2523 special_prong_src,
2524 msg,
2525 "'_' prong here",
2526 .{},
2527 );
2528 break :msg msg;
2529 };
2530 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
2531 }
2532
2533 // Validate for duplicate items, missing else prong, and invalid range.
2534 switch (operand.ty.zigTypeTag()) {
2535 .Enum => return sema.mod.fail(&block.base, src, "TODO validate switch .Enum", .{}),
2536 .ErrorSet => return sema.mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}),
2537 .Union => return sema.mod.fail(&block.base, src, "TODO validate switch .Union", .{}),
2538 .Int, .ComptimeInt => {
2539 var range_set = RangeSet.init(gpa);
2540 defer range_set.deinit();
2541
2542 var extra_index: usize = special.end;
2543 {
2544 var scalar_i: u32 = 0;
2545 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2546 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2547 extra_index += 1;
2548 const body_len = sema.code.extra[extra_index];
2549 extra_index += 1;
2550 const body = sema.code.extra[extra_index..][0..body_len];
2551 extra_index += body_len;
2552
2553 try sema.validateSwitchItem(
2554 block,
2555 &range_set,
2556 item_ref,
2557 src_node_offset,
2558 .{ .scalar = scalar_i },
2559 );
2560 }
2561 }
2562 {
2563 var multi_i: u32 = 0;
2564 while (multi_i < multi_cases_len) : (multi_i += 1) {
2565 const items_len = sema.code.extra[extra_index];
2566 extra_index += 1;
2567 const ranges_len = sema.code.extra[extra_index];
2568 extra_index += 1;
2569 const body_len = sema.code.extra[extra_index];
2570 extra_index += 1;
2571 const items = sema.code.refSlice(extra_index, items_len);
2572 extra_index += items_len;
2573
2574 for (items) |item_ref, item_i| {
2575 try sema.validateSwitchItem(
2576 block,
2577 &range_set,
2578 item_ref,
2579 src_node_offset,
2580 .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
2581 );
2582 }
2583
2584 var range_i: u32 = 0;
2585 while (range_i < ranges_len) : (range_i += 1) {
2586 const item_first = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2587 extra_index += 1;
2588 const item_last = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2589 extra_index += 1;
2590
2591 try sema.validateSwitchRange(
2592 block,
2593 &range_set,
2594 item_first,
2595 item_last,
2596 src_node_offset,
2597 .{ .range = .{ .prong = multi_i, .item = range_i } },
2598 );
2599 }
2600
2601 extra_index += body_len;
2602 }
2603 }
2604
2605 check_range: {
2606 if (operand.ty.zigTypeTag() == .Int) {
2607 var arena = std.heap.ArenaAllocator.init(gpa);
2608 defer arena.deinit();
2609
2610 const min_int = try operand.ty.minInt(&arena, sema.mod.getTarget());
2611 const max_int = try operand.ty.maxInt(&arena, sema.mod.getTarget());
2612 if (try range_set.spans(min_int, max_int)) {
2613 if (special_prong == .@"else") {
2614 return sema.mod.fail(
2615 &block.base,
2616 special_prong_src,
2617 "unreachable else prong; all cases already handled",
2618 .{},
2619 );
2620 }
2621 break :check_range;
2622 }
2623 }
2624 if (special_prong != .@"else") {
2625 return sema.mod.fail(
2626 &block.base,
2627 src,
2628 "switch must handle all possibilities",
2629 .{},
2630 );
2631 }
2632 }
2633 },
2634 .Bool => {
2635 var true_count: u8 = 0;
2636 var false_count: u8 = 0;
2637
2638 var extra_index: usize = special.end;
2639 {
2640 var scalar_i: u32 = 0;
2641 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2642 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2643 extra_index += 1;
2644 const body_len = sema.code.extra[extra_index];
2645 extra_index += 1;
2646 const body = sema.code.extra[extra_index..][0..body_len];
2647 extra_index += body_len;
2648
2649 try sema.validateSwitchItemBool(
2650 block,
2651 &true_count,
2652 &false_count,
2653 item_ref,
2654 src_node_offset,
2655 .{ .scalar = scalar_i },
2656 );
2657 }
2658 }
2659 {
2660 var multi_i: u32 = 0;
2661 while (multi_i < multi_cases_len) : (multi_i += 1) {
2662 const items_len = sema.code.extra[extra_index];
2663 extra_index += 1;
2664 const ranges_len = sema.code.extra[extra_index];
2665 extra_index += 1;
2666 const body_len = sema.code.extra[extra_index];
2667 extra_index += 1;
2668 const items = sema.code.refSlice(extra_index, items_len);
2669 extra_index += items_len + body_len;
2670
2671 for (items) |item_ref, item_i| {
2672 try sema.validateSwitchItemBool(
2673 block,
2674 &true_count,
2675 &false_count,
2676 item_ref,
2677 src_node_offset,
2678 .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
2679 );
2680 }
2681
2682 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
2683 }
2684 }
2685 switch (special_prong) {
2686 .@"else" => {
2687 if (true_count + false_count == 2) {
2688 return sema.mod.fail(
2689 &block.base,
2690 src,
2691 "unreachable else prong; all cases already handled",
2692 .{},
2693 );
2694 }
2695 },
2696 .under, .none => {
2697 if (true_count + false_count < 2) {
2698 return sema.mod.fail(
2699 &block.base,
2700 src,
2701 "switch must handle all possibilities",
2702 .{},
2703 );
2704 }
2705 },
2706 }
2707 },
2708 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
2709 if (special_prong != .@"else") {
2710 return sema.mod.fail(
2711 &block.base,
2712 src,
2713 "else prong required when switching on type '{}'",
2714 .{operand.ty},
2715 );
2716 }
2717
2718 var seen_values = ValueSrcMap.init(gpa);
2719 defer seen_values.deinit();
2720
2721 var extra_index: usize = special.end;
2722 {
2723 var scalar_i: u32 = 0;
2724 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2725 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2726 extra_index += 1;
2727 const body_len = sema.code.extra[extra_index];
2728 extra_index += 1;
2729 const body = sema.code.extra[extra_index..][0..body_len];
2730 extra_index += body_len;
2731
2732 try sema.validateSwitchItemSparse(
2733 block,
2734 &seen_values,
2735 item_ref,
2736 src_node_offset,
2737 .{ .scalar = scalar_i },
2738 );
2739 }
2740 }
2741 {
2742 var multi_i: u32 = 0;
2743 while (multi_i < multi_cases_len) : (multi_i += 1) {
2744 const items_len = sema.code.extra[extra_index];
2745 extra_index += 1;
2746 const ranges_len = sema.code.extra[extra_index];
2747 extra_index += 1;
2748 const body_len = sema.code.extra[extra_index];
2749 extra_index += 1;
2750 const items = sema.code.refSlice(extra_index, items_len);
2751 extra_index += items_len + body_len;
2752
2753 for (items) |item_ref, item_i| {
2754 try sema.validateSwitchItemSparse(
2755 block,
2756 &seen_values,
2757 item_ref,
2758 src_node_offset,
2759 .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
2760 );
2761 }
2762
2763 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
2764 }
2765 }
2766 },
2767
2768 .ErrorUnion,
2769 .NoReturn,
2770 .Array,
2771 .Struct,
2772 .Undefined,
2773 .Null,
2774 .Optional,
2775 .BoundFn,
2776 .Opaque,
2777 .Vector,
2778 .Frame,
2779 .AnyFrame,
2780 .ComptimeFloat,
2781 .Float,
2782 => return sema.mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{
2783 operand.ty,
2784 }),
2785 }
2786
2787 if (try sema.resolveDefinedValue(block, src, operand)) |operand_val| {
2788 var extra_index: usize = special.end;
2789 {
2790 var scalar_i: usize = 0;
2791 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2792 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2793 extra_index += 1;
2794 const body_len = sema.code.extra[extra_index];
2795 extra_index += 1;
2796 const body = sema.code.extra[extra_index..][0..body_len];
2797 extra_index += body_len;
2798
2799 // Validation above ensured these will succeed.
2800 const item = sema.resolveInst(item_ref) catch unreachable;
2801 const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable;
2802 if (operand_val.eql(item_val)) {
2803 return sema.resolveBody(block, body);
2804 }
2805 }
2806 }
2807 {
2808 var multi_i: usize = 0;
2809 while (multi_i < multi_cases_len) : (multi_i += 1) {
2810 const items_len = sema.code.extra[extra_index];
2811 extra_index += 1;
2812 const ranges_len = sema.code.extra[extra_index];
2813 extra_index += 1;
2814 const body_len = sema.code.extra[extra_index];
2815 extra_index += 1;
2816 const items = sema.code.refSlice(extra_index, items_len);
2817 extra_index += items_len;
2818 const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len];
2819
2820 for (items) |item_ref| {
2821 // Validation above ensured these will succeed.
2822 const item = sema.resolveInst(item_ref) catch unreachable;
2823 const item_val = sema.resolveConstValue(block, item.src, item) catch unreachable;
2824 if (operand_val.eql(item_val)) {
2825 return sema.resolveBody(block, body);
2826 }
2827 }
2828
2829 var range_i: usize = 0;
2830 while (range_i < ranges_len) : (range_i += 1) {
2831 const item_first = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2832 extra_index += 1;
2833 const item_last = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2834 extra_index += 1;
2835
2836 // Validation above ensured these will succeed.
2837 const first_tv = sema.resolveInstConst(block, .unneeded, item_first) catch unreachable;
2838 const last_tv = sema.resolveInstConst(block, .unneeded, item_last) catch unreachable;
2839 if (Value.compare(operand_val, .gte, first_tv.val) and
2840 Value.compare(operand_val, .lte, last_tv.val))
2841 {
2842 return sema.resolveBody(block, body);
2843 }
2844 }
2845
2846 extra_index += body_len;
2847 }
2848 }
2849 return sema.resolveBody(block, special.body);
2850 }
2851
2852 if (scalar_cases_len + multi_cases_len == 0) {
2853 return sema.resolveBody(block, special.body);
2854 }
2855
2856 try sema.requireRuntimeBlock(block, src);
2857
2858 const block_inst = try sema.arena.create(Inst.Block);
2859 block_inst.* = .{
2860 .base = .{
2861 .tag = Inst.Block.base_tag,
2862 .ty = undefined, // Set after analysis.
2863 .src = src,
2864 },
2865 .body = undefined,
2866 };
2867
2868 var child_block: Scope.Block = .{
2869 .parent = block,
2870 .sema = sema,
2871 .src_decl = block.src_decl,
2872 .instructions = .{},
2873 // TODO @as here is working around a stage1 miscompilation bug :(
2874 .label = @as(?Scope.Block.Label, Scope.Block.Label{
2875 .zir_block = switch_inst,
2876 .merges = .{
2877 .results = .{},
2878 .br_list = .{},
2879 .block_inst = block_inst,
2880 },
2881 }),
2882 .inlining = block.inlining,
2883 .is_comptime = block.is_comptime,
2884 };
2885 const merges = &child_block.label.?.merges;
2886 defer child_block.instructions.deinit(gpa);
2887 defer merges.results.deinit(gpa);
2888 defer merges.br_list.deinit(gpa);
2889
2890 // TODO when reworking TZIR memory layout make multi cases get generated as cases,
2891 // not as part of the "else" block.
2892 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
2893
2894 var case_block = child_block.makeSubBlock();
2895 defer case_block.instructions.deinit(gpa);
2896
2897 var extra_index: usize = special.end;
2898
2899 var scalar_i: usize = 0;
2900 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2901 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2902 extra_index += 1;
2903 const body_len = sema.code.extra[extra_index];
2904 extra_index += 1;
2905 const body = sema.code.extra[extra_index..][0..body_len];
2906 extra_index += body_len;
2907
2908 case_block.instructions.shrinkRetainingCapacity(0);
2909 // We validate these above; these two calls are guaranteed to succeed.
2910 const item = sema.resolveInst(item_ref) catch unreachable;
2911 const item_val = sema.resolveConstValue(&case_block, .unneeded, item) catch unreachable;
2912
2913 _ = try sema.analyzeBody(&case_block, body);
2914
2915 cases[scalar_i] = .{
2916 .item = item_val,
2917 .body = .{ .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items) },
2918 };
2919 }
2920
2921 var first_else_body: Body = undefined;
2922 var prev_condbr: ?*Inst.CondBr = null;
2923
2924 var multi_i: usize = 0;
2925 while (multi_i < multi_cases_len) : (multi_i += 1) {
2926 const items_len = sema.code.extra[extra_index];
2927 extra_index += 1;
2928 const ranges_len = sema.code.extra[extra_index];
2929 extra_index += 1;
2930 const body_len = sema.code.extra[extra_index];
2931 extra_index += 1;
2932 const items = sema.code.refSlice(extra_index, items_len);
2933 extra_index += items_len;
2934
2935 case_block.instructions.shrinkRetainingCapacity(0);
2936
2937 var any_ok: ?*Inst = null;
2938 const bool_ty = comptime Type.initTag(.bool);
2939
2940 for (items) |item_ref| {
2941 const item = try sema.resolveInst(item_ref);
2942 _ = try sema.resolveConstValue(&child_block, item.src, item);
2943
2944 const cmp_ok = try case_block.addBinOp(item.src, bool_ty, .cmp_eq, operand, item);
2945 if (any_ok) |some| {
2946 any_ok = try case_block.addBinOp(item.src, bool_ty, .bool_or, some, cmp_ok);
2947 } else {
2948 any_ok = cmp_ok;
2949 }
2950 }
2951
2952 var range_i: usize = 0;
2953 while (range_i < ranges_len) : (range_i += 1) {
2954 const first_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2955 extra_index += 1;
2956 const last_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2957 extra_index += 1;
2958
2959 const item_first = try sema.resolveInst(first_ref);
2960 const item_last = try sema.resolveInst(last_ref);
2961
2962 _ = try sema.resolveConstValue(&child_block, item_first.src, item_first);
2963 _ = try sema.resolveConstValue(&child_block, item_last.src, item_last);
2964
2965 const range_src = item_first.src;
2966
2967 // operand >= first and operand <= last
2968 const range_first_ok = try case_block.addBinOp(
2969 item_first.src,
2970 bool_ty,
2971 .cmp_gte,
2972 operand,
2973 item_first,
2974 );
2975 const range_last_ok = try case_block.addBinOp(
2976 item_last.src,
2977 bool_ty,
2978 .cmp_lte,
2979 operand,
2980 item_last,
2981 );
2982 const range_ok = try case_block.addBinOp(
2983 range_src,
2984 bool_ty,
2985 .bool_and,
2986 range_first_ok,
2987 range_last_ok,
2988 );
2989 if (any_ok) |some| {
2990 any_ok = try case_block.addBinOp(range_src, bool_ty, .bool_or, some, range_ok);
2991 } else {
2992 any_ok = range_ok;
2993 }
2994 }
2995
2996 const new_condbr = try sema.arena.create(Inst.CondBr);
2997 new_condbr.* = .{
2998 .base = .{
2999 .tag = .condbr,
3000 .ty = Type.initTag(.noreturn),
3001 .src = src,
3002 },
3003 .condition = any_ok.?,
3004 .then_body = undefined,
3005 .else_body = undefined,
3006 };
3007 try case_block.instructions.append(gpa, &new_condbr.base);
3008
3009 const cond_body: Body = .{
3010 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
3011 };
3012
3013 case_block.instructions.shrinkRetainingCapacity(0);
3014 const body = sema.code.extra[extra_index..][0..body_len];
3015 extra_index += body_len;
3016 _ = try sema.analyzeBody(&case_block, body);
3017 new_condbr.then_body = .{
3018 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
3019 };
3020 if (prev_condbr) |condbr| {
3021 condbr.else_body = cond_body;
3022 } else {
3023 first_else_body = cond_body;
3024 }
3025 prev_condbr = new_condbr;
3026 }
3027
3028 const final_else_body: Body = blk: {
3029 if (special.body.len != 0) {
3030 case_block.instructions.shrinkRetainingCapacity(0);
3031 _ = try sema.analyzeBody(&case_block, special.body);
3032 const else_body: Body = .{
3033 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
3034 };
3035 if (prev_condbr) |condbr| {
3036 condbr.else_body = else_body;
3037 break :blk first_else_body;
3038 } else {
3039 break :blk else_body;
3040 }
3041 } else {
3042 break :blk .{ .instructions = &.{} };
3043 }
3044 };
3045
3046 _ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
3047 return sema.analyzeBlockBody(block, src, &child_block, merges);
3048}
3049
3050fn resolveSwitchItemVal(
3051 sema: *Sema,
3052 block: *Scope.Block,
3053 item_ref: zir.Inst.Ref,
3054 switch_node_offset: i32,
3055 switch_prong_src: AstGen.SwitchProngSrc,
3056 range_expand: AstGen.SwitchProngSrc.RangeExpand,
3057) InnerError!Value {
3058 const item = try sema.resolveInst(item_ref);
3059 // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc
3060 // because we only have the switch AST node. Only if we know for sure we need to report
3061 // a compile error do we resolve the full source locations.
3062 if (item.value()) |val| {
3063 if (val.isUndef()) {
3064 const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand);
3065 return sema.failWithUseOfUndef(block, src);
3066 }
3067 return val;
3068 }
3069 const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand);
3070 return sema.failWithNeededComptime(block, src);
3071}
3072
3073fn validateSwitchRange(
3074 sema: *Sema,
3075 block: *Scope.Block,
3076 range_set: *RangeSet,
3077 first_ref: zir.Inst.Ref,
3078 last_ref: zir.Inst.Ref,
3079 src_node_offset: i32,
3080 switch_prong_src: AstGen.SwitchProngSrc,
3081) InnerError!void {
3082 const first_val = try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first);
3083 const last_val = try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last);
3084 const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src);
3085 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
3086}
3087
3088fn validateSwitchItem(
3089 sema: *Sema,
3090 block: *Scope.Block,
3091 range_set: *RangeSet,
3092 item_ref: zir.Inst.Ref,
3093 src_node_offset: i32,
3094 switch_prong_src: AstGen.SwitchProngSrc,
3095) InnerError!void {
3096 const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
3097 const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src);
3098 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
3099}
3100
3101fn validateSwitchDupe(
3102 sema: *Sema,
3103 block: *Scope.Block,
3104 maybe_prev_src: ?AstGen.SwitchProngSrc,
3105 switch_prong_src: AstGen.SwitchProngSrc,
3106 src_node_offset: i32,
3107) InnerError!void {
3108 const prev_prong_src = maybe_prev_src orelse return;
3109 const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none);
3110 const prev_src = prev_prong_src.resolve(block.src_decl, src_node_offset, .none);
3111 const msg = msg: {
3112 const msg = try sema.mod.errMsg(
3113 &block.base,
3114 src,
3115 "duplicate switch value",
3116 .{},
3117 );
3118 errdefer msg.destroy(sema.gpa);
3119 try sema.mod.errNote(
3120 &block.base,
3121 prev_src,
3122 msg,
3123 "previous value here",
3124 .{},
3125 );
3126 break :msg msg;
3127 };
3128 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
3129}
3130
3131fn validateSwitchItemBool(
3132 sema: *Sema,
3133 block: *Scope.Block,
3134 true_count: *u8,
3135 false_count: *u8,
3136 item_ref: zir.Inst.Ref,
3137 src_node_offset: i32,
3138 switch_prong_src: AstGen.SwitchProngSrc,
3139) InnerError!void {
3140 const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
3141 if (item_val.toBool()) {
3142 true_count.* += 1;
3143 } else {
3144 false_count.* += 1;
3145 }
3146 if (true_count.* + false_count.* > 2) {
3147 const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none);
3148 return sema.mod.fail(&block.base, src, "duplicate switch value", .{});
3149 }
3150}
3151
3152const ValueSrcMap = std.HashMap(Value, AstGen.SwitchProngSrc, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage);
3153
3154fn validateSwitchItemSparse(
3155 sema: *Sema,
3156 block: *Scope.Block,
3157 seen_values: *ValueSrcMap,
3158 item_ref: zir.Inst.Ref,
3159 src_node_offset: i32,
3160 switch_prong_src: AstGen.SwitchProngSrc,
3161) InnerError!void {
3162 const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
3163 const entry = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return;
3164 return sema.validateSwitchDupe(block, entry.value, switch_prong_src, src_node_offset);
3165}
3166
3167fn validateSwitchNoRange(
3168 sema: *Sema,
3169 block: *Scope.Block,
3170 ranges_len: u32,
3171 operand_ty: Type,
3172 src_node_offset: i32,
3173) InnerError!void {
3174 if (ranges_len == 0)
3175 return;
3176
3177 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
3178 const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset };
3179
3180 const msg = msg: {
3181 const msg = try sema.mod.errMsg(
3182 &block.base,
3183 operand_src,
3184 "ranges not allowed when switching on type '{}'",
3185 .{operand_ty},
3186 );
3187 errdefer msg.destroy(sema.gpa);
3188 try sema.mod.errNote(
3189 &block.base,
3190 range_src,
3191 msg,
3192 "range here",
3193 .{},
3194 );
3195 break :msg msg;
3196 };
3197 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
3198}
3199
3200fn zirImport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3201 const tracy = trace(@src());
3202 defer tracy.end();
3203
3204 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3205 const src = inst_data.src();
3206 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
3207 const operand = try sema.resolveConstString(block, operand_src, inst_data.operand);
3208
3209 const file_scope = sema.analyzeImport(block, src, operand) catch |err| switch (err) {
3210 error.ImportOutsidePkgPath => {
3211 return sema.mod.fail(&block.base, src, "import of file outside package path: '{s}'", .{operand});
3212 },
3213 error.FileNotFound => {
3214 return sema.mod.fail(&block.base, src, "unable to find '{s}'", .{operand});
3215 },
3216 else => {
3217 // TODO: make sure this gets retried and not cached
3218 return sema.mod.fail(&block.base, src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });
3219 },
3220 };
3221 return sema.mod.constType(sema.arena, src, file_scope.root_container.ty);
3222}
3223
3224fn zirShl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3225 const tracy = trace(@src());
3226 defer tracy.end();
3227 return sema.mod.fail(&block.base, sema.src, "TODO implement zirShl", .{});
3228}
3229
3230fn zirShr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3231 const tracy = trace(@src());
3232 defer tracy.end();
3233 return sema.mod.fail(&block.base, sema.src, "TODO implement zirShr", .{});
3234}
3235
3236fn zirBitwise(
3237 sema: *Sema,
3238 block: *Scope.Block,
3239 inst: zir.Inst.Index,
3240 ir_tag: ir.Inst.Tag,
3241) InnerError!*Inst {
3242 const tracy = trace(@src());
3243 defer tracy.end();
3244
3245 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3246 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
3247 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
3248 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
3249 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
3250 const lhs = try sema.resolveInst(extra.lhs);
3251 const rhs = try sema.resolveInst(extra.rhs);
3252
3253 const instructions = &[_]*Inst{ lhs, rhs };
3254 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
3255 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
3256 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
3257
3258 const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
3259 resolved_type.elemType()
3260 else
3261 resolved_type;
3262
3263 const scalar_tag = scalar_type.zigTypeTag();
3264
3265 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
3266 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
3267 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
3268 lhs.ty.arrayLen(),
3269 rhs.ty.arrayLen(),
3270 });
3271 }
3272 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBitwise", .{});
3273 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
3274 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
3275 lhs.ty,
3276 rhs.ty,
3277 });
3278 }
3279
3280 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
3281
3282 if (!is_int) {
3283 return sema.mod.fail(&block.base, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
3284 }
3285
3286 if (casted_lhs.value()) |lhs_val| {
3287 if (casted_rhs.value()) |rhs_val| {
3288 if (lhs_val.isUndef() or rhs_val.isUndef()) {
3289 return sema.mod.constInst(sema.arena, src, .{
3290 .ty = resolved_type,
3291 .val = Value.initTag(.undef),
3292 });
3293 }
3294 return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});
3295 }
3296 }
3297
3298 try sema.requireRuntimeBlock(block, src);
3299 return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);
3300}
3301
3302fn zirBitNot(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3303 const tracy = trace(@src());
3304 defer tracy.end();
3305 return sema.mod.fail(&block.base, sema.src, "TODO implement zirBitNot", .{});
3306}
3307
3308fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3309 const tracy = trace(@src());
3310 defer tracy.end();
3311 return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayCat", .{});
3312}
3313
3314fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3315 const tracy = trace(@src());
3316 defer tracy.end();
3317 return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayMul", .{});
3318}
3319
3320fn zirNegate(
3321 sema: *Sema,
3322 block: *Scope.Block,
3323 inst: zir.Inst.Index,
3324 tag_override: zir.Inst.Tag,
3325) InnerError!*Inst {
3326 const tracy = trace(@src());
3327 defer tracy.end();
3328
3329 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3330 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
3331 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
3332 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
3333 const lhs = try sema.resolveInst(.zero);
3334 const rhs = try sema.resolveInst(inst_data.operand);
3335
3336 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
3337}
3338
3339fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3340 const tracy = trace(@src());
3341 defer tracy.end();
3342
3343 const tag_override = block.sema.code.instructions.items(.tag)[inst];
3344 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3345 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
3346 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
3347 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
3348 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
3349 const lhs = try sema.resolveInst(extra.lhs);
3350 const rhs = try sema.resolveInst(extra.rhs);
3351
3352 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
3353}
3354
3355fn analyzeArithmetic(
3356 sema: *Sema,
3357 block: *Scope.Block,
3358 zir_tag: zir.Inst.Tag,
3359 lhs: *Inst,
3360 rhs: *Inst,
3361 src: LazySrcLoc,
3362 lhs_src: LazySrcLoc,
3363 rhs_src: LazySrcLoc,
3364) InnerError!*Inst {
3365 const instructions = &[_]*Inst{ lhs, rhs };
3366 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
3367 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
3368 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
3369
3370 const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
3371 resolved_type.elemType()
3372 else
3373 resolved_type;
3374
3375 const scalar_tag = scalar_type.zigTypeTag();
3376
3377 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
3378 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
3379 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
3380 lhs.ty.arrayLen(),
3381 rhs.ty.arrayLen(),
3382 });
3383 }
3384 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBinOp", .{});
3385 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
3386 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
3387 lhs.ty,
3388 rhs.ty,
3389 });
3390 }
3391
3392 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
3393 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
3394
3395 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
3396 return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
3397 }
3398
3399 if (casted_lhs.value()) |lhs_val| {
3400 if (casted_rhs.value()) |rhs_val| {
3401 if (lhs_val.isUndef() or rhs_val.isUndef()) {
3402 return sema.mod.constInst(sema.arena, src, .{
3403 .ty = resolved_type,
3404 .val = Value.initTag(.undef),
3405 });
3406 }
3407 // incase rhs is 0, simply return lhs without doing any calculations
3408 // TODO Once division is implemented we should throw an error when dividing by 0.
3409 if (rhs_val.compareWithZero(.eq)) {
3410 return sema.mod.constInst(sema.arena, src, .{
3411 .ty = scalar_type,
3412 .val = lhs_val,
3413 });
3414 }
3415
3416 const value = switch (zir_tag) {
3417 .add => blk: {
3418 const val = if (is_int)
3419 try Module.intAdd(sema.arena, lhs_val, rhs_val)
3420 else
3421 try Module.floatAdd(sema.arena, scalar_type, src, lhs_val, rhs_val);
3422 break :blk val;
3423 },
3424 .sub => blk: {
3425 const val = if (is_int)
3426 try Module.intSub(sema.arena, lhs_val, rhs_val)
3427 else
3428 try Module.floatSub(sema.arena, scalar_type, src, lhs_val, rhs_val);
3429 break :blk val;
3430 },
3431 else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tag)}),
3432 };
3433
3434 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });
3435
3436 return sema.mod.constInst(sema.arena, src, .{
3437 .ty = scalar_type,
3438 .val = value,
3439 });
3440 }
3441 }
3442
3443 try sema.requireRuntimeBlock(block, src);
3444 const ir_tag: Inst.Tag = switch (zir_tag) {
3445 .add => .add,
3446 .addwrap => .addwrap,
3447 .sub => .sub,
3448 .subwrap => .subwrap,
3449 .mul => .mul,
3450 .mulwrap => .mulwrap,
3451 else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tag)}),
3452 };
3453
3454 return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);
3455}
3456
3457fn zirLoad(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3458 const tracy = trace(@src());
3459 defer tracy.end();
3460
3461 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3462 const src = inst_data.src();
3463 const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node };
3464 const ptr = try sema.resolveInst(inst_data.operand);
3465 return sema.analyzeLoad(block, src, ptr, ptr_src);
3466}
3467
3468fn zirAsm(
3469 sema: *Sema,
3470 block: *Scope.Block,
3471 inst: zir.Inst.Index,
3472 is_volatile: bool,
3473) InnerError!*Inst {
3474 const tracy = trace(@src());
3475 defer tracy.end();
3476
3477 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3478 const src = inst_data.src();
3479 const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = inst_data.src_node };
3480 const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = inst_data.src_node };
3481 const extra = sema.code.extraData(zir.Inst.Asm, inst_data.payload_index);
3482 const return_type = try sema.resolveType(block, ret_ty_src, extra.data.return_type);
3483 const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source);
3484
3485 var extra_i = extra.end;
3486 const Output = struct { name: []const u8, inst: *Inst };
3487 const output: ?Output = if (extra.data.output != .none) blk: {
3488 const name = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
3489 extra_i += 1;
3490 break :blk Output{
3491 .name = name,
3492 .inst = try sema.resolveInst(extra.data.output),
3493 };
3494 } else null;
3495
3496 const args = try sema.arena.alloc(*Inst, extra.data.args_len);
3497 const inputs = try sema.arena.alloc([]const u8, extra.data.args_len);
3498 const clobbers = try sema.arena.alloc([]const u8, extra.data.clobbers_len);
3499
3500 for (args) |*arg| {
3501 arg.* = try sema.resolveInst(@intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]));
3502 extra_i += 1;
3503 }
3504 for (inputs) |*name| {
3505 name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
3506 extra_i += 1;
3507 }
3508 for (clobbers) |*name| {
3509 name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
3510 extra_i += 1;
3511 }
3512
3513 try sema.requireRuntimeBlock(block, src);
3514 const asm_tzir = try sema.arena.create(Inst.Assembly);
3515 asm_tzir.* = .{
3516 .base = .{
3517 .tag = .assembly,
3518 .ty = return_type,
3519 .src = src,
3520 },
3521 .asm_source = asm_source,
3522 .is_volatile = is_volatile,
3523 .output = if (output) |o| o.inst else null,
3524 .output_name = if (output) |o| o.name else null,
3525 .inputs = inputs,
3526 .clobbers = clobbers,
3527 .args = args,
3528 };
3529 try block.instructions.append(sema.gpa, &asm_tzir.base);
3530 return &asm_tzir.base;
3531}
3532
3533fn zirCmp(
3534 sema: *Sema,
3535 block: *Scope.Block,
3536 inst: zir.Inst.Index,
3537 op: std.math.CompareOperator,
3538) InnerError!*Inst {
3539 const tracy = trace(@src());
3540 defer tracy.end();
3541
3542 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3543 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
3544 const src: LazySrcLoc = inst_data.src();
3545 const lhs = try sema.resolveInst(extra.lhs);
3546 const rhs = try sema.resolveInst(extra.rhs);
3547
3548 const is_equality_cmp = switch (op) {
3549 .eq, .neq => true,
3550 else => false,
3551 };
3552 const lhs_ty_tag = lhs.ty.zigTypeTag();
3553 const rhs_ty_tag = rhs.ty.zigTypeTag();
3554 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
3555 // null == null, null != null
3556 return sema.mod.constBool(sema.arena, src, op == .eq);
3557 } else if (is_equality_cmp and
3558 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
3559 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
3560 {
3561 // comparing null with optionals
3562 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
3563 return sema.analyzeIsNull(block, src, opt_operand, op == .neq);
3564 } else if (is_equality_cmp and
3565 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
3566 {
3567 return sema.mod.fail(&block.base, src, "TODO implement C pointer cmp", .{});
3568 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
3569 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
3570 return sema.mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type});
3571 } else if (is_equality_cmp and
3572 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
3573 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
3574 {
3575 return sema.mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
3576 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
3577 if (!is_equality_cmp) {
3578 return sema.mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)});
3579 }
3580 if (rhs.value()) |rval| {
3581 if (lhs.value()) |lval| {
3582 // TODO optimisation oppurtunity: evaluate if std.mem.eql is faster with the names, or calling to Module.getErrorValue to get the values and then compare them is faster
3583 return sema.mod.constBool(sema.arena, src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq));
3584 }
3585 }
3586 try sema.requireRuntimeBlock(block, src);
3587 return block.addBinOp(src, Type.initTag(.bool), if (op == .eq) .cmp_eq else .cmp_neq, lhs, rhs);
3588 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
3589 // This operation allows any combination of integer and float types, regardless of the
3590 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
3591 // numeric types.
3592 return sema.cmpNumeric(block, src, lhs, rhs, op);
3593 } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {
3594 if (!is_equality_cmp) {
3595 return sema.mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)});
3596 }
3597 return sema.mod.constBool(sema.arena, src, lhs.value().?.eql(rhs.value().?) == (op == .eq));
3598 }
3599 return sema.mod.fail(&block.base, src, "TODO implement more cmp analysis", .{});
3600}
3601
3602fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3603 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3604 const src = inst_data.src();
3605 const operand = try sema.resolveInst(inst_data.operand);
3606 return sema.mod.constType(sema.arena, src, operand.ty);
3607}
3608
3609fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3610 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3611 const src = inst_data.src();
3612 const operand_ptr = try sema.resolveInst(inst_data.operand);
3613 const elem_ty = operand_ptr.ty.elemType();
3614 return sema.mod.constType(sema.arena, src, elem_ty);
3615}
3616
3617fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3618 const tracy = trace(@src());
3619 defer tracy.end();
3620
3621 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3622 const src = inst_data.src();
3623 const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index);
3624 const args = sema.code.refSlice(extra.end, extra.data.operands_len);
3625
3626 const inst_list = try sema.gpa.alloc(*ir.Inst, extra.data.operands_len);
3627 defer sema.gpa.free(inst_list);
3628
3629 for (args) |arg_ref, i| {
3630 inst_list[i] = try sema.resolveInst(arg_ref);
3631 }
3632
3633 const result_type = try sema.resolvePeerTypes(block, src, inst_list);
3634 return sema.mod.constType(sema.arena, src, result_type);
3635}
3636
3637fn zirBoolNot(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3638 const tracy = trace(@src());
3639 defer tracy.end();
3640
3641 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3642 const src = inst_data.src();
3643 const uncasted_operand = try sema.resolveInst(inst_data.operand);
3644
3645 const bool_type = Type.initTag(.bool);
3646 const operand = try sema.coerce(block, bool_type, uncasted_operand, uncasted_operand.src);
3647 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
3648 return sema.mod.constBool(sema.arena, src, !val.toBool());
3649 }
3650 try sema.requireRuntimeBlock(block, src);
3651 return block.addUnOp(src, bool_type, .not, operand);
3652}
3653
3654fn zirBoolOp(
3655 sema: *Sema,
3656 block: *Scope.Block,
3657 inst: zir.Inst.Index,
3658 comptime is_bool_or: bool,
3659) InnerError!*Inst {
3660 const tracy = trace(@src());
3661 defer tracy.end();
3662
3663 const src: LazySrcLoc = .unneeded;
3664 const bool_type = Type.initTag(.bool);
3665 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
3666 const uncasted_lhs = try sema.resolveInst(bin_inst.lhs);
3667 const lhs = try sema.coerce(block, bool_type, uncasted_lhs, uncasted_lhs.src);
3668 const uncasted_rhs = try sema.resolveInst(bin_inst.rhs);
3669 const rhs = try sema.coerce(block, bool_type, uncasted_rhs, uncasted_rhs.src);
3670
3671 if (lhs.value()) |lhs_val| {
3672 if (rhs.value()) |rhs_val| {
3673 if (is_bool_or) {
3674 return sema.mod.constBool(sema.arena, src, lhs_val.toBool() or rhs_val.toBool());
3675 } else {
3676 return sema.mod.constBool(sema.arena, src, lhs_val.toBool() and rhs_val.toBool());
3677 }
3678 }
3679 }
3680 try sema.requireRuntimeBlock(block, src);
3681 const tag: ir.Inst.Tag = if (is_bool_or) .bool_or else .bool_and;
3682 return block.addBinOp(src, bool_type, tag, lhs, rhs);
3683}
3684
3685fn zirBoolBr(
3686 sema: *Sema,
3687 parent_block: *Scope.Block,
3688 inst: zir.Inst.Index,
3689 is_bool_or: bool,
3690) InnerError!*Inst {
3691 const tracy = trace(@src());
3692 defer tracy.end();
3693
3694 const datas = sema.code.instructions.items(.data);
3695 const inst_data = datas[inst].bool_br;
3696 const src: LazySrcLoc = .unneeded;
3697 const lhs = try sema.resolveInst(inst_data.lhs);
3698 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
3699 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
3700
3701 if (try sema.resolveDefinedValue(parent_block, src, lhs)) |lhs_val| {
3702 if (lhs_val.toBool() == is_bool_or) {
3703 return sema.mod.constBool(sema.arena, src, is_bool_or);
3704 }
3705 // comptime-known left-hand side. No need for a block here; the result
3706 // is simply the rhs expression. Here we rely on there only being 1
3707 // break instruction (`break_inline`).
3708 return sema.resolveBody(parent_block, body);
3709 }
3710
3711 const block_inst = try sema.arena.create(Inst.Block);
3712 block_inst.* = .{
3713 .base = .{
3714 .tag = Inst.Block.base_tag,
3715 .ty = Type.initTag(.bool),
3716 .src = src,
3717 },
3718 .body = undefined,
3719 };
3720
3721 var child_block = parent_block.makeSubBlock();
3722 defer child_block.instructions.deinit(sema.gpa);
3723
3724 var then_block = child_block.makeSubBlock();
3725 defer then_block.instructions.deinit(sema.gpa);
3726
3727 var else_block = child_block.makeSubBlock();
3728 defer else_block.instructions.deinit(sema.gpa);
3729
3730 const lhs_block = if (is_bool_or) &then_block else &else_block;
3731 const rhs_block = if (is_bool_or) &else_block else &then_block;
3732
3733 const lhs_result = try sema.mod.constInst(sema.arena, src, .{
3734 .ty = Type.initTag(.bool),
3735 .val = if (is_bool_or) Value.initTag(.bool_true) else Value.initTag(.bool_false),
3736 });
3737 _ = try lhs_block.addBr(src, block_inst, lhs_result);
3738
3739 const rhs_result = try sema.resolveBody(rhs_block, body);
3740 _ = try rhs_block.addBr(src, block_inst, rhs_result);
3741
3742 const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };
3743 const tzir_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, rhs_block.instructions.items) };
3744 _ = try child_block.addCondBr(src, lhs, tzir_then_body, tzir_else_body);
3745
3746 block_inst.body = .{
3747 .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items),
3748 };
3749 try parent_block.instructions.append(sema.gpa, &block_inst.base);
3750 return &block_inst.base;
3751}
3752
3753fn zirIsNull(
3754 sema: *Sema,
3755 block: *Scope.Block,
3756 inst: zir.Inst.Index,
3757 invert_logic: bool,
3758) InnerError!*Inst {
3759 const tracy = trace(@src());
3760 defer tracy.end();
3761
3762 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3763 const src = inst_data.src();
3764 const operand = try sema.resolveInst(inst_data.operand);
3765 return sema.analyzeIsNull(block, src, operand, invert_logic);
3766}
3767
3768fn zirIsNullPtr(
3769 sema: *Sema,
3770 block: *Scope.Block,
3771 inst: zir.Inst.Index,
3772 invert_logic: bool,
3773) InnerError!*Inst {
3774 const tracy = trace(@src());
3775 defer tracy.end();
3776
3777 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3778 const src = inst_data.src();
3779 const ptr = try sema.resolveInst(inst_data.operand);
3780 const loaded = try sema.analyzeLoad(block, src, ptr, src);
3781 return sema.analyzeIsNull(block, src, loaded, invert_logic);
3782}
3783
3784fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3785 const tracy = trace(@src());
3786 defer tracy.end();
3787
3788 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3789 const operand = try sema.resolveInst(inst_data.operand);
3790 return sema.analyzeIsErr(block, inst_data.src(), operand);
3791}
3792
3793fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3794 const tracy = trace(@src());
3795 defer tracy.end();
3796
3797 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3798 const src = inst_data.src();
3799 const ptr = try sema.resolveInst(inst_data.operand);
3800 const loaded = try sema.analyzeLoad(block, src, ptr, src);
3801 return sema.analyzeIsErr(block, src, loaded);
3802}
3803
3804fn zirCondbr(
3805 sema: *Sema,
3806 parent_block: *Scope.Block,
3807 inst: zir.Inst.Index,
3808) InnerError!zir.Inst.Index {
3809 const tracy = trace(@src());
3810 defer tracy.end();
3811
3812 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3813 const src = inst_data.src();
3814 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
3815 const extra = sema.code.extraData(zir.Inst.CondBr, inst_data.payload_index);
3816
3817 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
3818 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
3819
3820 const uncasted_cond = try sema.resolveInst(extra.data.condition);
3821 const cond = try sema.coerce(parent_block, Type.initTag(.bool), uncasted_cond, cond_src);
3822
3823 if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| {
3824 const body = if (cond_val.toBool()) then_body else else_body;
3825 _ = try sema.analyzeBody(parent_block, body);
3826 return always_noreturn;
3827 }
3828
3829 var sub_block = parent_block.makeSubBlock();
3830 defer sub_block.instructions.deinit(sema.gpa);
3831
3832 _ = try sema.analyzeBody(&sub_block, then_body);
3833 const tzir_then_body: ir.Body = .{
3834 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),
3835 };
3836
3837 sub_block.instructions.shrinkRetainingCapacity(0);
3838
3839 _ = try sema.analyzeBody(&sub_block, else_body);
3840 const tzir_else_body: ir.Body = .{
3841 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),
3842 };
3843
3844 _ = try parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body);
3845 return always_noreturn;
3846}
3847
3848fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
3849 const tracy = trace(@src());
3850 defer tracy.end();
3851
3852 const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
3853 const src = inst_data.src();
3854 const safety_check = inst_data.safety;
3855 try sema.requireRuntimeBlock(block, src);
3856 // TODO Add compile error for @optimizeFor occurring too late in a scope.
3857 if (safety_check and block.wantSafety()) {
3858 return sema.safetyPanic(block, src, .unreach);
3859 } else {
3860 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
3861 return always_noreturn;
3862 }
3863}
3864
3865fn zirRetTok(
3866 sema: *Sema,
3867 block: *Scope.Block,
3868 inst: zir.Inst.Index,
3869 need_coercion: bool,
3870) InnerError!zir.Inst.Index {
3871 const tracy = trace(@src());
3872 defer tracy.end();
3873
3874 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
3875 const operand = try sema.resolveInst(inst_data.operand);
3876 const src = inst_data.src();
3877
3878 return sema.analyzeRet(block, operand, src, need_coercion);
3879}
3880
3881fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
3882 const tracy = trace(@src());
3883 defer tracy.end();
3884
3885 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3886 const operand = try sema.resolveInst(inst_data.operand);
3887 const src = inst_data.src();
3888
3889 return sema.analyzeRet(block, operand, src, false);
3890}
3891
3892fn analyzeRet(
3893 sema: *Sema,
3894 block: *Scope.Block,
3895 operand: *Inst,
3896 src: LazySrcLoc,
3897 need_coercion: bool,
3898) InnerError!zir.Inst.Index {
3899 if (block.inlining) |inlining| {
3900 // We are inlining a function call; rewrite the `ret` as a `break`.
3901 try inlining.merges.results.append(sema.gpa, operand);
3902 _ = try block.addBr(src, inlining.merges.block_inst, operand);
3903 return always_noreturn;
3904 }
3905
3906 if (need_coercion) {
3907 if (sema.func) |func| {
3908 const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
3909 const fn_ret_ty = fn_ty.fnReturnType();
3910 const casted_operand = try sema.coerce(block, fn_ret_ty, operand, src);
3911 if (fn_ret_ty.zigTypeTag() == .Void)
3912 _ = try block.addNoOp(src, Type.initTag(.noreturn), .retvoid)
3913 else
3914 _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, casted_operand);
3915 return always_noreturn;
3916 }
3917 }
3918 _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, operand);
3919 return always_noreturn;
3920}
3921
3922fn floatOpAllowed(tag: zir.Inst.Tag) bool {
3923 // extend this swich as additional operators are implemented
3924 return switch (tag) {
3925 .add, .sub => true,
3926 else => false,
3927 };
3928}
3929
3930fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3931 const tracy = trace(@src());
3932 defer tracy.end();
3933
3934 const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple;
3935 const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type);
3936 const ty = try sema.mod.ptrType(
3937 sema.arena,
3938 elem_type,
3939 null,
3940 0,
3941 0,
3942 0,
3943 inst_data.is_mutable,
3944 inst_data.is_allowzero,
3945 inst_data.is_volatile,
3946 inst_data.size,
3947 );
3948 return sema.mod.constType(sema.arena, .unneeded, ty);
3949}
3950
3951fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3952 const tracy = trace(@src());
3953 defer tracy.end();
3954
3955 const src: LazySrcLoc = .unneeded;
3956 const inst_data = sema.code.instructions.items(.data)[inst].ptr_type;
3957 const extra = sema.code.extraData(zir.Inst.PtrType, inst_data.payload_index);
3958
3959 var extra_i = extra.end;
3960
3961 const sentinel = if (inst_data.flags.has_sentinel) blk: {
3962 const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]);
3963 extra_i += 1;
3964 break :blk (try sema.resolveInstConst(block, .unneeded, ref)).val;
3965 } else null;
3966
3967 const abi_align = if (inst_data.flags.has_align) blk: {
3968 const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]);
3969 extra_i += 1;
3970 break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32);
3971 } else 0;
3972
3973 const bit_start = if (inst_data.flags.has_bit_range) blk: {
3974 const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]);
3975 extra_i += 1;
3976 break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16);
3977 } else 0;
3978
3979 const bit_end = if (inst_data.flags.has_bit_range) blk: {
3980 const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]);
3981 extra_i += 1;
3982 break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16);
3983 } else 0;
3984
3985 if (bit_end != 0 and bit_start >= bit_end * 8)
3986 return sema.mod.fail(&block.base, src, "bit offset starts after end of host integer", .{});
3987
3988 const elem_type = try sema.resolveType(block, .unneeded, extra.data.elem_type);
3989
3990 const ty = try sema.mod.ptrType(
3991 sema.arena,
3992 elem_type,
3993 sentinel,
3994 abi_align,
3995 bit_start,
3996 bit_end,
3997 inst_data.flags.is_mutable,
3998 inst_data.flags.is_allowzero,
3999 inst_data.flags.is_volatile,
4000 inst_data.size,
4001 );
4002 return sema.mod.constType(sema.arena, src, ty);
4003}
4004
4005fn zirStructInitEmpty(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
4006 const tracy = trace(@src());
4007 defer tracy.end();
4008
4009 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
4010 const src = inst_data.src();
4011 const struct_type = try sema.resolveType(block, src, inst_data.operand);
4012
4013 return sema.mod.constInst(sema.arena, src, .{
4014 .ty = struct_type,
4015 .val = Value.initTag(.empty_struct_value),
4016 });
4017}
4018
4019fn requireFunctionBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
4020 if (sema.func == null) {
4021 return sema.mod.fail(&block.base, src, "instruction illegal outside function body", .{});
4022 }
4023}
4024
4025fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
4026 if (block.is_comptime) {
4027 return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{});
4028 }
4029 try sema.requireFunctionBlock(block, src);
4030}
4031
4032fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {
4033 if (!ty.isValidVarType(false)) {
4034 return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty});
4035 }
4036}
4037
4038pub const PanicId = enum {
4039 unreach,
4040 unwrap_null,
4041 unwrap_errunion,
4042 invalid_error_code,
4043};
4044
4045fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
4046 const block_inst = try sema.arena.create(Inst.Block);
4047 block_inst.* = .{
4048 .base = .{
4049 .tag = Inst.Block.base_tag,
4050 .ty = Type.initTag(.void),
4051 .src = ok.src,
4052 },
4053 .body = .{
4054 .instructions = try sema.arena.alloc(*Inst, 1), // Only need space for the condbr.
4055 },
4056 };
4057
4058 const ok_body: ir.Body = .{
4059 .instructions = try sema.arena.alloc(*Inst, 1), // Only need space for the br_void.
4060 };
4061 const br_void = try sema.arena.create(Inst.BrVoid);
4062 br_void.* = .{
4063 .base = .{
4064 .tag = .br_void,
4065 .ty = Type.initTag(.noreturn),
4066 .src = ok.src,
4067 },
4068 .block = block_inst,
4069 };
4070 ok_body.instructions[0] = &br_void.base;
4071
4072 var fail_block: Scope.Block = .{
4073 .parent = parent_block,
4074 .sema = sema,
4075 .src_decl = parent_block.src_decl,
4076 .instructions = .{},
4077 .inlining = parent_block.inlining,
4078 .is_comptime = parent_block.is_comptime,
4079 };
4080
4081 defer fail_block.instructions.deinit(sema.gpa);
4082
4083 _ = try sema.safetyPanic(&fail_block, ok.src, panic_id);
4084
4085 const fail_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, fail_block.instructions.items) };
4086
4087 const condbr = try sema.arena.create(Inst.CondBr);
4088 condbr.* = .{
4089 .base = .{
4090 .tag = .condbr,
4091 .ty = Type.initTag(.noreturn),
4092 .src = ok.src,
4093 },
4094 .condition = ok,
4095 .then_body = ok_body,
4096 .else_body = fail_body,
4097 };
4098 block_inst.body.instructions[0] = &condbr.base;
4099
4100 try parent_block.instructions.append(sema.gpa, &block_inst.base);
4101}
4102
4103fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Index {
4104 // TODO Once we have a panic function to call, call it here instead of breakpoint.
4105 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
4106 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
4107 return always_noreturn;
4108}
4109
4110fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
4111 sema.branch_count += 1;
4112 if (sema.branch_count > sema.branch_quota) {
4113 // TODO show the "called from here" stack
4114 return sema.mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{sema.branch_quota});
4115 }
4116}
4117
4118fn namedFieldPtr(
4119 sema: *Sema,
4120 block: *Scope.Block,
4121 src: LazySrcLoc,
4122 object_ptr: *Inst,
4123 field_name: []const u8,
4124 field_name_src: LazySrcLoc,
4125) InnerError!*Inst {
4126 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
4127 .Pointer => object_ptr.ty.elemType(),
4128 else => return sema.mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
4129 };
4130 switch (elem_ty.zigTypeTag()) {
4131 .Array => {
4132 if (mem.eql(u8, field_name, "len")) {
4133 return sema.mod.constInst(sema.arena, src, .{
4134 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
4135 .val = try Value.Tag.ref_val.create(
4136 sema.arena,
4137 try Value.Tag.int_u64.create(sema.arena, elem_ty.arrayLen()),
4138 ),
4139 });
4140 } else {
4141 return sema.mod.fail(
4142 &block.base,
4143 field_name_src,
4144 "no member named '{s}' in '{}'",
4145 .{ field_name, elem_ty },
4146 );
4147 }
4148 },
4149 .Pointer => {
4150 const ptr_child = elem_ty.elemType();
4151 switch (ptr_child.zigTypeTag()) {
4152 .Array => {
4153 if (mem.eql(u8, field_name, "len")) {
4154 return sema.mod.constInst(sema.arena, src, .{
4155 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
4156 .val = try Value.Tag.ref_val.create(
4157 sema.arena,
4158 try Value.Tag.int_u64.create(sema.arena, ptr_child.arrayLen()),
4159 ),
4160 });
4161 } else {
4162 return sema.mod.fail(
4163 &block.base,
4164 field_name_src,
4165 "no member named '{s}' in '{}'",
4166 .{ field_name, elem_ty },
4167 );
4168 }
4169 },
4170 else => {},
4171 }
4172 },
4173 .Type => {
4174 _ = try sema.resolveConstValue(block, object_ptr.src, object_ptr);
4175 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src);
4176 const val = result.value().?;
4177 const child_type = try val.toType(sema.arena);
4178 switch (child_type.zigTypeTag()) {
4179 .ErrorSet => {
4180 // TODO resolve inferred error sets
4181 const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {
4182 const error_set = payload.data;
4183 // TODO this is O(N). I'm putting off solving this until we solve inferred
4184 // error sets at the same time.
4185 const names = error_set.names_ptr[0..error_set.names_len];
4186 for (names) |name| {
4187 if (mem.eql(u8, field_name, name)) {
4188 break :blk name;
4189 }
4190 }
4191 return sema.mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{
4192 field_name,
4193 child_type,
4194 });
4195 } else (try sema.mod.getErrorValue(field_name)).key;
4196
4197 return sema.mod.constInst(sema.arena, src, .{
4198 .ty = try sema.mod.simplePtrType(sema.arena, child_type, false, .One),
4199 .val = try Value.Tag.ref_val.create(
4200 sema.arena,
4201 try Value.Tag.@"error".create(sema.arena, .{
4202 .name = name,
4203 }),
4204 ),
4205 });
4206 },
4207 .Struct => {
4208 const container_scope = child_type.getContainerScope();
4209 if (sema.mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
4210 // TODO if !decl.is_pub and inDifferentFiles() "{} is private"
4211 return sema.analyzeDeclRef(block, src, decl);
4212 }
4213
4214 if (container_scope.file_scope == sema.mod.root_scope) {
4215 return sema.mod.fail(&block.base, src, "root source file has no member called '{s}'", .{field_name});
4216 } else {
4217 return sema.mod.fail(&block.base, src, "container '{}' has no member called '{s}'", .{ child_type, field_name });
4218 }
4219 },
4220 else => return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{child_type}),
4221 }
4222 },
4223 .Struct => return sema.analyzeStructFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty),
4224 else => {},
4225 }
4226 return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty});
4227}
4228
4229fn analyzeStructFieldPtr(
4230 sema: *Sema,
4231 block: *Scope.Block,
4232 src: LazySrcLoc,
4233 struct_ptr: *Inst,
4234 field_name: []const u8,
4235 field_name_src: LazySrcLoc,
4236 elem_ty: Type,
4237) InnerError!*Inst {
4238 const mod = sema.mod;
4239 const arena = sema.arena;
4240 assert(elem_ty.zigTypeTag() == .Struct);
4241
4242 const struct_obj = elem_ty.castTag(.@"struct").?.data;
4243
4244 const field_index = struct_obj.fields.getIndex(field_name) orelse {
4245 // TODO note: struct S declared here
4246 return mod.fail(&block.base, field_name_src, "no field named '{s}' in struct '{}'", .{
4247 field_name, elem_ty,
4248 });
4249 };
4250 const field = struct_obj.fields.entries.items[field_index].value;
4251 const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);
4252 // TODO comptime field access
4253 try sema.requireRuntimeBlock(block, src);
4254 return block.addStructFieldPtr(src, ptr_field_ty, struct_ptr, @intCast(u32, field_index));
4255}
4256
4257fn elemPtr(
4258 sema: *Sema,
4259 block: *Scope.Block,
4260 src: LazySrcLoc,
4261 array_ptr: *Inst,
4262 elem_index: *Inst,
4263 elem_index_src: LazySrcLoc,
4264) InnerError!*Inst {
4265 const elem_ty = switch (array_ptr.ty.zigTypeTag()) {
4266 .Pointer => array_ptr.ty.elemType(),
4267 else => return sema.mod.fail(&block.base, array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
4268 };
4269 if (!elem_ty.isIndexable()) {
4270 return sema.mod.fail(&block.base, src, "array access of non-array type '{}'", .{elem_ty});
4271 }
4272
4273 if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) {
4274 // we have to deref the ptr operand to get the actual array pointer
4275 const array_ptr_deref = try sema.analyzeLoad(block, src, array_ptr, array_ptr.src);
4276 if (array_ptr_deref.value()) |array_ptr_val| {
4277 if (elem_index.value()) |index_val| {
4278 // Both array pointer and index are compile-time known.
4279 const index_u64 = index_val.toUnsignedInt();
4280 // @intCast here because it would have been impossible to construct a value that
4281 // required a larger index.
4282 const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64));
4283 const pointee_type = elem_ty.elemType().elemType();
4284
4285 return sema.mod.constInst(sema.arena, src, .{
4286 .ty = try Type.Tag.single_const_pointer.create(sema.arena, pointee_type),
4287 .val = elem_ptr,
4288 });
4289 }
4290 }
4291 }
4292
4293 return sema.mod.fail(&block.base, src, "TODO implement more analyze elemptr", .{});
4294}
4295
4296fn coerce(
4297 sema: *Sema,
4298 block: *Scope.Block,
4299 dest_type: Type,
4300 inst: *Inst,
4301 inst_src: LazySrcLoc,
4302) InnerError!*Inst {
4303 if (dest_type.tag() == .var_args_param) {
4304 return sema.coerceVarArgParam(block, inst);
4305 }
4306 // If the types are the same, we can return the operand.
4307 if (dest_type.eql(inst.ty))
4308 return inst;
4309
4310 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
4311 if (in_memory_result == .ok) {
4312 return sema.bitcast(block, dest_type, inst);
4313 }
4314
4315 // undefined to anything
4316 if (inst.value()) |val| {
4317 if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
4318 return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = val });
4319 }
4320 }
4321 assert(inst.ty.zigTypeTag() != .Undefined);
4322
4323 // T to E!T or E to E!T
4324 if (dest_type.tag() == .error_union) {
4325 return try sema.wrapErrorUnion(block, dest_type, inst);
4326 }
4327
4328 // comptime known number to other number
4329 if (try sema.coerceNum(block, dest_type, inst)) |some|
4330 return some;
4331
4332 const target = sema.mod.getTarget();
4333
4334 switch (dest_type.zigTypeTag()) {
4335 .Optional => {
4336 // null to ?T
4337 if (inst.ty.zigTypeTag() == .Null) {
4338 return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
4339 }
4340
4341 // T to ?T
4342 var buf: Type.Payload.ElemType = undefined;
4343 const child_type = dest_type.optionalChild(&buf);
4344 if (child_type.eql(inst.ty)) {
4345 return sema.wrapOptional(block, dest_type, inst);
4346 } else if (try sema.coerceNum(block, child_type, inst)) |some| {
4347 return sema.wrapOptional(block, dest_type, some);
4348 }
4349 },
4350 .Pointer => {
4351 // Coercions where the source is a single pointer to an array.
4352 src_array_ptr: {
4353 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
4354 const array_type = inst.ty.elemType();
4355 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
4356 const array_elem_type = array_type.elemType();
4357 if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
4358 if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
4359
4360 const dst_elem_type = dest_type.elemType();
4361 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
4362 .ok => {},
4363 .no_match => break :src_array_ptr,
4364 }
4365
4366 switch (dest_type.ptrSize()) {
4367 .Slice => {
4368 // *[N]T to []T
4369 return sema.coerceArrayPtrToSlice(block, dest_type, inst);
4370 },
4371 .C => {
4372 // *[N]T to [*c]T
4373 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4374 },
4375 .Many => {
4376 // *[N]T to [*]T
4377 // *[N:s]T to [*:s]T
4378 const src_sentinel = array_type.sentinel();
4379 const dst_sentinel = dest_type.sentinel();
4380 if (src_sentinel == null and dst_sentinel == null)
4381 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4382
4383 if (src_sentinel) |src_s| {
4384 if (dst_sentinel) |dst_s| {
4385 if (src_s.eql(dst_s)) {
4386 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4387 }
4388 }
4389 }
4390 },
4391 .One => {},
4392 }
4393 }
4394 },
4395 .Int => {
4396 // integer widening
4397 if (inst.ty.zigTypeTag() == .Int) {
4398 assert(inst.value() == null); // handled above
4399
4400 const dst_info = dest_type.intInfo(target);
4401 const src_info = inst.ty.intInfo(target);
4402 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
4403 // small enough unsigned ints can get casted to large enough signed ints
4404 (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))
4405 {
4406 try sema.requireRuntimeBlock(block, inst_src);
4407 return block.addUnOp(inst_src, dest_type, .intcast, inst);
4408 }
4409 }
4410 },
4411 .Float => {
4412 // float widening
4413 if (inst.ty.zigTypeTag() == .Float) {
4414 assert(inst.value() == null); // handled above
4415
4416 const src_bits = inst.ty.floatBits(target);
4417 const dst_bits = dest_type.floatBits(target);
4418 if (dst_bits >= src_bits) {
4419 try sema.requireRuntimeBlock(block, inst_src);
4420 return block.addUnOp(inst_src, dest_type, .floatcast, inst);
4421 }
4422 }
4423 },
4424 else => {},
4425 }
4426
4427 return sema.mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });
4428}
4429
4430const InMemoryCoercionResult = enum {
4431 ok,
4432 no_match,
4433};
4434
4435fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
4436 if (dest_type.eql(src_type))
4437 return .ok;
4438
4439 // TODO: implement more of this function
4440
4441 return .no_match;
4442}
4443
4444fn coerceNum(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) InnerError!?*Inst {
4445 const val = inst.value() orelse return null;
4446 const src_zig_tag = inst.ty.zigTypeTag();
4447 const dst_zig_tag = dest_type.zigTypeTag();
4448
4449 const target = sema.mod.getTarget();
4450
4451 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
4452 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
4453 if (val.floatHasFraction()) {
4454 return sema.mod.fail(&block.base, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
4455 }
4456 return sema.mod.fail(&block.base, inst.src, "TODO float to int", .{});
4457 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
4458 if (!val.intFitsInType(dest_type, target)) {
4459 return sema.mod.fail(&block.base, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
4460 }
4461 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
4462 }
4463 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
4464 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
4465 const res = val.floatCast(sema.arena, dest_type, target) catch |err| switch (err) {
4466 error.Overflow => return sema.mod.fail(
4467 &block.base,
4468 inst.src,
4469 "cast of value {} to type '{}' loses information",
4470 .{ val, dest_type },
4471 ),
4472 error.OutOfMemory => return error.OutOfMemory,
4473 };
4474 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = res });
4475 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
4476 return sema.mod.fail(&block.base, inst.src, "TODO int to float", .{});
4477 }
4478 }
4479 return null;
4480}
4481
4482fn coerceVarArgParam(sema: *Sema, block: *Scope.Block, inst: *Inst) !*Inst {
4483 switch (inst.ty.zigTypeTag()) {
4484 .ComptimeInt, .ComptimeFloat => return sema.mod.fail(&block.base, inst.src, "integer and float literals in var args function must be casted", .{}),
4485 else => {},
4486 }
4487 // TODO implement more of this function.
4488 return inst;
4489}
4490
4491fn storePtr(
4492 sema: *Sema,
4493 block: *Scope.Block,
4494 src: LazySrcLoc,
4495 ptr: *Inst,
4496 uncasted_value: *Inst,
4497) !void {
4498 if (ptr.ty.isConstPtr())
4499 return sema.mod.fail(&block.base, src, "cannot assign to constant", .{});
4500
4501 const elem_ty = ptr.ty.elemType();
4502 const value = try sema.coerce(block, elem_ty, uncasted_value, src);
4503 if (elem_ty.onePossibleValue() != null)
4504 return;
4505
4506 // TODO handle comptime pointer writes
4507 // TODO handle if the element type requires comptime
4508
4509 try sema.requireRuntimeBlock(block, src);
4510 _ = try block.addBinOp(src, Type.initTag(.void), .store, ptr, value);
4511}
4512
4513fn bitcast(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
4514 if (inst.value()) |val| {
4515 // Keep the comptime Value representation; take the new type.
4516 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
4517 }
4518 // TODO validate the type size and other compile errors
4519 try sema.requireRuntimeBlock(block, inst.src);
4520 return block.addUnOp(inst.src, dest_type, .bitcast, inst);
4521}
4522
4523fn coerceArrayPtrToSlice(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
4524 if (inst.value()) |val| {
4525 // The comptime Value representation is compatible with both types.
4526 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
4527 }
4528 return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
4529}
4530
4531fn coerceArrayPtrToMany(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
4532 if (inst.value()) |val| {
4533 // The comptime Value representation is compatible with both types.
4534 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
4535 }
4536 return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{});
4537}
4538
4539fn analyzeDeclVal(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
4540 const decl_ref = try sema.analyzeDeclRef(block, src, decl);
4541 return sema.analyzeLoad(block, src, decl_ref, src);
4542}
4543
4544fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
4545 try sema.mod.declareDeclDependency(sema.owner_decl, decl);
4546 sema.mod.ensureDeclAnalyzed(decl) catch |err| {
4547 if (sema.func) |func| {
4548 func.state = .dependency_failure;
4549 } else {
4550 sema.owner_decl.analysis = .dependency_failure;
4551 }
4552 return err;
4553 };
4554
4555 const decl_tv = try decl.typedValue();
4556 if (decl_tv.val.tag() == .variable) {
4557 return sema.analyzeVarRef(block, src, decl_tv);
4558 }
4559 return sema.mod.constInst(sema.arena, src, .{
4560 .ty = try sema.mod.simplePtrType(sema.arena, decl_tv.ty, false, .One),
4561 .val = try Value.Tag.decl_ref.create(sema.arena, decl),
4562 });
4563}
4564
4565fn analyzeVarRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, tv: TypedValue) InnerError!*Inst {
4566 const variable = tv.val.castTag(.variable).?.data;
4567
4568 const ty = try sema.mod.simplePtrType(sema.arena, tv.ty, variable.is_mutable, .One);
4569 if (!variable.is_mutable and !variable.is_extern) {
4570 return sema.mod.constInst(sema.arena, src, .{
4571 .ty = ty,
4572 .val = try Value.Tag.ref_val.create(sema.arena, variable.init),
4573 });
4574 }
4575
4576 try sema.requireRuntimeBlock(block, src);
4577 const inst = try sema.arena.create(Inst.VarPtr);
4578 inst.* = .{
4579 .base = .{
4580 .tag = .varptr,
4581 .ty = ty,
4582 .src = src,
4583 },
4584 .variable = variable,
4585 };
4586 try block.instructions.append(sema.gpa, &inst.base);
4587 return &inst.base;
4588}
4589
4590fn analyzeRef(
4591 sema: *Sema,
4592 block: *Scope.Block,
4593 src: LazySrcLoc,
4594 operand: *Inst,
4595) InnerError!*Inst {
4596 const ptr_type = try sema.mod.simplePtrType(sema.arena, operand.ty, false, .One);
4597
4598 if (operand.value()) |val| {
4599 return sema.mod.constInst(sema.arena, src, .{
4600 .ty = ptr_type,
4601 .val = try Value.Tag.ref_val.create(sema.arena, val),
4602 });
4603 }
4604
4605 try sema.requireRuntimeBlock(block, src);
4606 return block.addUnOp(src, ptr_type, .ref, operand);
4607}
4608
4609fn analyzeLoad(
4610 sema: *Sema,
4611 block: *Scope.Block,
4612 src: LazySrcLoc,
4613 ptr: *Inst,
4614 ptr_src: LazySrcLoc,
4615) InnerError!*Inst {
4616 const elem_ty = switch (ptr.ty.zigTypeTag()) {
4617 .Pointer => ptr.ty.elemType(),
4618 else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
4619 };
4620 if (ptr.value()) |val| {
4621 return sema.mod.constInst(sema.arena, src, .{
4622 .ty = elem_ty,
4623 .val = try val.pointerDeref(sema.arena),
4624 });
4625 }
4626
4627 try sema.requireRuntimeBlock(block, src);
4628 return block.addUnOp(src, elem_ty, .load, ptr);
4629}
4630
4631fn analyzeIsNull(
4632 sema: *Sema,
4633 block: *Scope.Block,
4634 src: LazySrcLoc,
4635 operand: *Inst,
4636 invert_logic: bool,
4637) InnerError!*Inst {
4638 if (operand.value()) |opt_val| {
4639 const is_null = opt_val.isNull();
4640 const bool_value = if (invert_logic) !is_null else is_null;
4641 return sema.mod.constBool(sema.arena, src, bool_value);
4642 }
4643 try sema.requireRuntimeBlock(block, src);
4644 const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null;
4645 return block.addUnOp(src, Type.initTag(.bool), inst_tag, operand);
4646}
4647
4648fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst {
4649 const ot = operand.ty.zigTypeTag();
4650 if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false);
4651 if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true);
4652 assert(ot == .ErrorUnion);
4653 if (operand.value()) |err_union| {
4654 return sema.mod.constBool(sema.arena, src, err_union.getError() != null);
4655 }
4656 try sema.requireRuntimeBlock(block, src);
4657 return block.addUnOp(src, Type.initTag(.bool), .is_err, operand);
4658}
4659
4660fn analyzeSlice(
4661 sema: *Sema,
4662 block: *Scope.Block,
4663 src: LazySrcLoc,
4664 array_ptr: *Inst,
4665 start: *Inst,
4666 end_opt: ?*Inst,
4667 sentinel_opt: ?*Inst,
4668 sentinel_src: LazySrcLoc,
4669) InnerError!*Inst {
4670 const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
4671 .Pointer => array_ptr.ty.elemType(),
4672 else => return sema.mod.fail(&block.base, src, "expected pointer, found '{}'", .{array_ptr.ty}),
4673 };
4674
4675 var array_type = ptr_child;
4676 const elem_type = switch (ptr_child.zigTypeTag()) {
4677 .Array => ptr_child.elemType(),
4678 .Pointer => blk: {
4679 if (ptr_child.isSinglePointer()) {
4680 if (ptr_child.elemType().zigTypeTag() == .Array) {
4681 array_type = ptr_child.elemType();
4682 break :blk ptr_child.elemType().elemType();
4683 }
4684
4685 return sema.mod.fail(&block.base, src, "slice of single-item pointer", .{});
4686 }
4687 break :blk ptr_child.elemType();
4688 },
4689 else => return sema.mod.fail(&block.base, src, "slice of non-array type '{}'", .{ptr_child}),
4690 };
4691
4692 const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
4693 const casted = try sema.coerce(block, elem_type, sentinel, sentinel.src);
4694 break :blk try sema.resolveConstValue(block, sentinel_src, casted);
4695 } else null;
4696
4697 var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
4698 var return_elem_type = elem_type;
4699 if (end_opt) |end| {
4700 if (end.value()) |end_val| {
4701 if (start.value()) |start_val| {
4702 const start_u64 = start_val.toUnsignedInt();
4703 const end_u64 = end_val.toUnsignedInt();
4704 if (start_u64 > end_u64) {
4705 return sema.mod.fail(&block.base, src, "out of bounds slice", .{});
4706 }
4707
4708 const len = end_u64 - start_u64;
4709 const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
4710 array_type.sentinel()
4711 else
4712 slice_sentinel;
4713 return_elem_type = try sema.mod.arrayType(sema.arena, len, array_sentinel, elem_type);
4714 return_ptr_size = .One;
4715 }
4716 }
4717 }
4718 const return_type = try sema.mod.ptrType(
4719 sema.arena,
4720 return_elem_type,
4721 if (end_opt == null) slice_sentinel else null,
4722 0, // TODO alignment
4723 0,
4724 0,
4725 !ptr_child.isConstPtr(),
4726 ptr_child.isAllowzeroPtr(),
4727 ptr_child.isVolatilePtr(),
4728 return_ptr_size,
4729 );
4730
4731 return sema.mod.fail(&block.base, src, "TODO implement analysis of slice", .{});
4732}
4733
4734fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_string: []const u8) !*Scope.File {
4735 const cur_pkg = block.getFileScope().pkg;
4736 const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse ".";
4737 const found_pkg = cur_pkg.table.get(target_string);
4738
4739 const resolved_path = if (found_pkg) |pkg|
4740 try std.fs.path.resolve(sema.gpa, &[_][]const u8{ pkg.root_src_directory.path orelse ".", pkg.root_src_path })
4741 else
4742 try std.fs.path.resolve(sema.gpa, &[_][]const u8{ cur_pkg_dir_path, target_string });
4743 errdefer sema.gpa.free(resolved_path);
4744
4745 if (sema.mod.import_table.get(resolved_path)) |some| {
4746 sema.gpa.free(resolved_path);
4747 return some;
4748 }
4749
4750 if (found_pkg == null) {
4751 const resolved_root_path = try std.fs.path.resolve(sema.gpa, &[_][]const u8{cur_pkg_dir_path});
4752 defer sema.gpa.free(resolved_root_path);
4753
4754 if (!mem.startsWith(u8, resolved_path, resolved_root_path)) {
4755 return error.ImportOutsidePkgPath;
4756 }
4757 }
4758
4759 // TODO Scope.Container arena for ty and sub_file_path
4760 const file_scope = try sema.gpa.create(Scope.File);
4761 errdefer sema.gpa.destroy(file_scope);
4762 const struct_ty = try Type.Tag.empty_struct.create(sema.gpa, &file_scope.root_container);
4763 errdefer sema.gpa.destroy(struct_ty.castTag(.empty_struct).?);
4764
4765 file_scope.* = .{
4766 .sub_file_path = resolved_path,
4767 .source = .{ .unloaded = {} },
4768 .tree = undefined,
4769 .status = .never_loaded,
4770 .pkg = found_pkg orelse cur_pkg,
4771 .root_container = .{
4772 .file_scope = file_scope,
4773 .decls = .{},
4774 .ty = struct_ty,
4775 },
4776 };
4777 sema.mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
4778 error.AnalysisFail => {
4779 assert(sema.mod.comp.totalErrorCount() != 0);
4780 },
4781 else => |e| return e,
4782 };
4783 try sema.mod.import_table.put(sema.gpa, file_scope.sub_file_path, file_scope);
4784 return file_scope;
4785}
4786
4787/// Asserts that lhs and rhs types are both numeric.
4788fn cmpNumeric(
4789 sema: *Sema,
4790 block: *Scope.Block,
4791 src: LazySrcLoc,
4792 lhs: *Inst,
4793 rhs: *Inst,
4794 op: std.math.CompareOperator,
4795) InnerError!*Inst {
4796 assert(lhs.ty.isNumeric());
4797 assert(rhs.ty.isNumeric());
4798
4799 const lhs_ty_tag = lhs.ty.zigTypeTag();
4800 const rhs_ty_tag = rhs.ty.zigTypeTag();
4801
4802 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
4803 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
4804 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
4805 lhs.ty.arrayLen(),
4806 rhs.ty.arrayLen(),
4807 });
4808 }
4809 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{});
4810 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
4811 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
4812 lhs.ty,
4813 rhs.ty,
4814 });
4815 }
4816
4817 if (lhs.value()) |lhs_val| {
4818 if (rhs.value()) |rhs_val| {
4819 return sema.mod.constBool(sema.arena, src, Value.compare(lhs_val, op, rhs_val));
4820 }
4821 }
4822
4823 // TODO handle comparisons against lazy zero values
4824 // Some values can be compared against zero without being runtime known or without forcing
4825 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
4826 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
4827 // of this function if we don't need to.
4828
4829 // It must be a runtime comparison.
4830 try sema.requireRuntimeBlock(block, src);
4831 // For floats, emit a float comparison instruction.
4832 const lhs_is_float = switch (lhs_ty_tag) {
4833 .Float, .ComptimeFloat => true,
4834 else => false,
4835 };
4836 const rhs_is_float = switch (rhs_ty_tag) {
4837 .Float, .ComptimeFloat => true,
4838 else => false,
4839 };
4840 const target = sema.mod.getTarget();
4841 if (lhs_is_float and rhs_is_float) {
4842 // Implicit cast the smaller one to the larger one.
4843 const dest_type = x: {
4844 if (lhs_ty_tag == .ComptimeFloat) {
4845 break :x rhs.ty;
4846 } else if (rhs_ty_tag == .ComptimeFloat) {
4847 break :x lhs.ty;
4848 }
4849 if (lhs.ty.floatBits(target) >= rhs.ty.floatBits(target)) {
4850 break :x lhs.ty;
4851 } else {
4852 break :x rhs.ty;
4853 }
4854 };
4855 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs.src);
4856 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs.src);
4857 return block.addBinOp(src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
4858 }
4859 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
4860 // For mixed signed and unsigned integers, implicit cast both operands to a signed
4861 // integer with + 1 bit.
4862 // For mixed floats and integers, extract the integer part from the float, cast that to
4863 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
4864 // add/subtract 1.
4865 const lhs_is_signed = if (lhs.value()) |lhs_val|
4866 lhs_val.compareWithZero(.lt)
4867 else
4868 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
4869 const rhs_is_signed = if (rhs.value()) |rhs_val|
4870 rhs_val.compareWithZero(.lt)
4871 else
4872 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
4873 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
4874
4875 var dest_float_type: ?Type = null;
4876
4877 var lhs_bits: usize = undefined;
4878 if (lhs.value()) |lhs_val| {
4879 if (lhs_val.isUndef())
4880 return sema.mod.constUndef(sema.arena, src, Type.initTag(.bool));
4881 const is_unsigned = if (lhs_is_float) x: {
4882 var bigint_space: Value.BigIntSpace = undefined;
4883 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
4884 defer bigint.deinit();
4885 const zcmp = lhs_val.orderAgainstZero();
4886 if (lhs_val.floatHasFraction()) {
4887 switch (op) {
4888 .eq => return sema.mod.constBool(sema.arena, src, false),
4889 .neq => return sema.mod.constBool(sema.arena, src, true),
4890 else => {},
4891 }
4892 if (zcmp == .lt) {
4893 try bigint.addScalar(bigint.toConst(), -1);
4894 } else {
4895 try bigint.addScalar(bigint.toConst(), 1);
4896 }
4897 }
4898 lhs_bits = bigint.toConst().bitCountTwosComp();
4899 break :x (zcmp != .lt);
4900 } else x: {
4901 lhs_bits = lhs_val.intBitCountTwosComp();
4902 break :x (lhs_val.orderAgainstZero() != .lt);
4903 };
4904 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
4905 } else if (lhs_is_float) {
4906 dest_float_type = lhs.ty;
4907 } else {
4908 const int_info = lhs.ty.intInfo(target);
4909 lhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
4910 }
4911
4912 var rhs_bits: usize = undefined;
4913 if (rhs.value()) |rhs_val| {
4914 if (rhs_val.isUndef())
4915 return sema.mod.constUndef(sema.arena, src, Type.initTag(.bool));
4916 const is_unsigned = if (rhs_is_float) x: {
4917 var bigint_space: Value.BigIntSpace = undefined;
4918 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
4919 defer bigint.deinit();
4920 const zcmp = rhs_val.orderAgainstZero();
4921 if (rhs_val.floatHasFraction()) {
4922 switch (op) {
4923 .eq => return sema.mod.constBool(sema.arena, src, false),
4924 .neq => return sema.mod.constBool(sema.arena, src, true),
4925 else => {},
4926 }
4927 if (zcmp == .lt) {
4928 try bigint.addScalar(bigint.toConst(), -1);
4929 } else {
4930 try bigint.addScalar(bigint.toConst(), 1);
4931 }
4932 }
4933 rhs_bits = bigint.toConst().bitCountTwosComp();
4934 break :x (zcmp != .lt);
4935 } else x: {
4936 rhs_bits = rhs_val.intBitCountTwosComp();
4937 break :x (rhs_val.orderAgainstZero() != .lt);
4938 };
4939 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
4940 } else if (rhs_is_float) {
4941 dest_float_type = rhs.ty;
4942 } else {
4943 const int_info = rhs.ty.intInfo(target);
4944 rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
4945 }
4946
4947 const dest_type = if (dest_float_type) |ft| ft else blk: {
4948 const max_bits = std.math.max(lhs_bits, rhs_bits);
4949 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
4950 error.Overflow => return sema.mod.fail(&block.base, src, "{d} exceeds maximum integer bit count", .{max_bits}),
4951 };
4952 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
4953 break :blk try Module.makeIntType(sema.arena, signedness, casted_bits);
4954 };
4955 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs.src);
4956 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs.src);
4957
4958 return block.addBinOp(src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
4959}
4960
4961fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
4962 if (inst.value()) |val| {
4963 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
4964 }
4965
4966 try sema.requireRuntimeBlock(block, inst.src);
4967 return block.addUnOp(inst.src, dest_type, .wrap_optional, inst);
4968}
4969
4970fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
4971 // TODO deal with inferred error sets
4972 const err_union = dest_type.castTag(.error_union).?;
4973 if (inst.value()) |val| {
4974 const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: {
4975 _ = try sema.coerce(block, err_union.data.payload, inst, inst.src);
4976 break :blk val;
4977 } else switch (err_union.data.error_set.tag()) {
4978 .anyerror => val,
4979 .error_set_single => blk: {
4980 const expected_name = val.castTag(.@"error").?.data.name;
4981 const n = err_union.data.error_set.castTag(.error_set_single).?.data;
4982 if (!mem.eql(u8, expected_name, n)) {
4983 return sema.mod.fail(
4984 &block.base,
4985 inst.src,
4986 "expected type '{}', found type '{}'",
4987 .{ err_union.data.error_set, inst.ty },
4988 );
4989 }
4990 break :blk val;
4991 },
4992 .error_set => blk: {
4993 const expected_name = val.castTag(.@"error").?.data.name;
4994 const error_set = err_union.data.error_set.castTag(.error_set).?.data;
4995 const names = error_set.names_ptr[0..error_set.names_len];
4996 // TODO this is O(N). I'm putting off solving this until we solve inferred
4997 // error sets at the same time.
4998 const found = for (names) |name| {
4999 if (mem.eql(u8, expected_name, name)) break true;
5000 } else false;
5001 if (!found) {
5002 return sema.mod.fail(
5003 &block.base,
5004 inst.src,
5005 "expected type '{}', found type '{}'",
5006 .{ err_union.data.error_set, inst.ty },
5007 );
5008 }
5009 break :blk val;
5010 },
5011 else => unreachable,
5012 };
5013
5014 return sema.mod.constInst(sema.arena, inst.src, .{
5015 .ty = dest_type,
5016 // creating a SubValue for the error_union payload
5017 .val = try Value.Tag.error_union.create(
5018 sema.arena,
5019 to_wrap,
5020 ),
5021 });
5022 }
5023
5024 try sema.requireRuntimeBlock(block, inst.src);
5025
5026 // we are coercing from E to E!T
5027 if (inst.ty.zigTypeTag() == .ErrorSet) {
5028 var coerced = try sema.coerce(block, err_union.data.error_set, inst, inst.src);
5029 return block.addUnOp(inst.src, dest_type, .wrap_errunion_err, coerced);
5030 } else {
5031 var coerced = try sema.coerce(block, err_union.data.payload, inst, inst.src);
5032 return block.addUnOp(inst.src, dest_type, .wrap_errunion_payload, coerced);
5033 }
5034}
5035
5036fn resolvePeerTypes(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, instructions: []*Inst) !Type {
5037 if (instructions.len == 0)
5038 return Type.initTag(.noreturn);
5039
5040 if (instructions.len == 1)
5041 return instructions[0].ty;
5042
5043 const target = sema.mod.getTarget();
5044
5045 var chosen = instructions[0];
5046 for (instructions[1..]) |candidate| {
5047 if (candidate.ty.eql(chosen.ty))
5048 continue;
5049 if (candidate.ty.zigTypeTag() == .NoReturn)
5050 continue;
5051 if (chosen.ty.zigTypeTag() == .NoReturn) {
5052 chosen = candidate;
5053 continue;
5054 }
5055 if (candidate.ty.zigTypeTag() == .Undefined)
5056 continue;
5057 if (chosen.ty.zigTypeTag() == .Undefined) {
5058 chosen = candidate;
5059 continue;
5060 }
5061 if (chosen.ty.isInt() and
5062 candidate.ty.isInt() and
5063 chosen.ty.isSignedInt() == candidate.ty.isSignedInt())
5064 {
5065 if (chosen.ty.intInfo(target).bits < candidate.ty.intInfo(target).bits) {
5066 chosen = candidate;
5067 }
5068 continue;
5069 }
5070 if (chosen.ty.isFloat() and candidate.ty.isFloat()) {
5071 if (chosen.ty.floatBits(target) < candidate.ty.floatBits(target)) {
5072 chosen = candidate;
5073 }
5074 continue;
5075 }
5076
5077 if (chosen.ty.zigTypeTag() == .ComptimeInt and candidate.ty.isInt()) {
5078 chosen = candidate;
5079 continue;
5080 }
5081
5082 if (chosen.ty.isInt() and candidate.ty.zigTypeTag() == .ComptimeInt) {
5083 continue;
5084 }
5085
5086 // TODO error notes pointing out each type
5087 return sema.mod.fail(&block.base, src, "incompatible types: '{}' and '{}'", .{ chosen.ty, candidate.ty });
5088 }
5089
5090 return chosen.ty;
5091}
src/astgen.zig deleted-4318
......@@ -1,4318 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;
5
6const Value = @import("value.zig").Value;
7const Type = @import("type.zig").Type;
8const TypedValue = @import("TypedValue.zig");
9const zir = @import("zir.zig");
10const Module = @import("Module.zig");
11const ast = std.zig.ast;
12const trace = @import("tracy.zig").trace;
13const Scope = Module.Scope;
14const InnerError = Module.InnerError;
15const BuiltinFn = @import("BuiltinFn.zig");
16
17pub const ResultLoc = union(enum) {
18 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
19 /// expression should be generated. The result instruction from the expression must
20 /// be ignored.
21 discard,
22 /// The expression has an inferred type, and it will be evaluated as an rvalue.
23 none,
24 /// The expression must generate a pointer rather than a value. For example, the left hand side
25 /// of an assignment uses this kind of result location.
26 ref,
27 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
28 ty: *zir.Inst,
29 /// The expression must store its result into this typed pointer. The result instruction
30 /// from the expression must be ignored.
31 ptr: *zir.Inst,
32 /// The expression must store its result into this allocation, which has an inferred type.
33 /// The result instruction from the expression must be ignored.
34 inferred_ptr: *zir.Inst.Tag.alloc_inferred.Type(),
35 /// The expression must store its result into this pointer, which is a typed pointer that
36 /// has been bitcasted to whatever the expression's type is.
37 /// The result instruction from the expression must be ignored.
38 bitcasted_ptr: *zir.Inst.UnOp,
39 /// There is a pointer for the expression to store its result into, however, its type
40 /// is inferred based on peer type resolution for a `zir.Inst.Block`.
41 /// The result instruction from the expression must be ignored.
42 block_ptr: *Module.Scope.GenZIR,
43
44 pub const Strategy = struct {
45 elide_store_to_block_ptr_instructions: bool,
46 tag: Tag,
47
48 pub const Tag = enum {
49 /// Both branches will use break_void; result location is used to communicate the
50 /// result instruction.
51 break_void,
52 /// Use break statements to pass the block result value, and call rvalue() at
53 /// the end depending on rl. Also elide the store_to_block_ptr instructions
54 /// depending on rl.
55 break_operand,
56 };
57 };
58};
59
60pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!*zir.Inst {
61 const tree = scope.tree();
62 const token_starts = tree.tokens.items(.start);
63
64 const type_src = token_starts[tree.firstToken(type_node)];
65 const type_type = try addZIRInstConst(mod, scope, type_src, .{
66 .ty = Type.initTag(.type),
67 .val = Value.initTag(.type_type),
68 });
69 const type_rl: ResultLoc = .{ .ty = type_type };
70 return expr(mod, scope, type_rl, type_node);
71}
72
73fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
74 const tree = scope.tree();
75 const node_tags = tree.nodes.items(.tag);
76 const main_tokens = tree.nodes.items(.main_token);
77 switch (node_tags[node]) {
78 .root => unreachable,
79 .@"usingnamespace" => unreachable,
80 .test_decl => unreachable,
81 .global_var_decl => unreachable,
82 .local_var_decl => unreachable,
83 .simple_var_decl => unreachable,
84 .aligned_var_decl => unreachable,
85 .switch_case => unreachable,
86 .switch_case_one => unreachable,
87 .container_field_init => unreachable,
88 .container_field_align => unreachable,
89 .container_field => unreachable,
90 .asm_output => unreachable,
91 .asm_input => unreachable,
92
93 .assign,
94 .assign_bit_and,
95 .assign_bit_or,
96 .assign_bit_shift_left,
97 .assign_bit_shift_right,
98 .assign_bit_xor,
99 .assign_div,
100 .assign_sub,
101 .assign_sub_wrap,
102 .assign_mod,
103 .assign_add,
104 .assign_add_wrap,
105 .assign_mul,
106 .assign_mul_wrap,
107 .add,
108 .add_wrap,
109 .sub,
110 .sub_wrap,
111 .mul,
112 .mul_wrap,
113 .div,
114 .mod,
115 .bit_and,
116 .bit_or,
117 .bit_shift_left,
118 .bit_shift_right,
119 .bit_xor,
120 .bang_equal,
121 .equal_equal,
122 .greater_than,
123 .greater_or_equal,
124 .less_than,
125 .less_or_equal,
126 .array_cat,
127 .array_mult,
128 .bool_and,
129 .bool_or,
130 .@"asm",
131 .asm_simple,
132 .string_literal,
133 .integer_literal,
134 .call,
135 .call_comma,
136 .async_call,
137 .async_call_comma,
138 .call_one,
139 .call_one_comma,
140 .async_call_one,
141 .async_call_one_comma,
142 .unreachable_literal,
143 .@"return",
144 .@"if",
145 .if_simple,
146 .@"while",
147 .while_simple,
148 .while_cont,
149 .bool_not,
150 .address_of,
151 .float_literal,
152 .undefined_literal,
153 .true_literal,
154 .false_literal,
155 .null_literal,
156 .optional_type,
157 .block,
158 .block_semicolon,
159 .block_two,
160 .block_two_semicolon,
161 .@"break",
162 .ptr_type_aligned,
163 .ptr_type_sentinel,
164 .ptr_type,
165 .ptr_type_bit_range,
166 .array_type,
167 .array_type_sentinel,
168 .enum_literal,
169 .multiline_string_literal,
170 .char_literal,
171 .@"defer",
172 .@"errdefer",
173 .@"catch",
174 .error_union,
175 .merge_error_sets,
176 .switch_range,
177 .@"await",
178 .bit_not,
179 .negation,
180 .negation_wrap,
181 .@"resume",
182 .@"try",
183 .slice,
184 .slice_open,
185 .slice_sentinel,
186 .array_init_one,
187 .array_init_one_comma,
188 .array_init_dot_two,
189 .array_init_dot_two_comma,
190 .array_init_dot,
191 .array_init_dot_comma,
192 .array_init,
193 .array_init_comma,
194 .struct_init_one,
195 .struct_init_one_comma,
196 .struct_init_dot_two,
197 .struct_init_dot_two_comma,
198 .struct_init_dot,
199 .struct_init_dot_comma,
200 .struct_init,
201 .struct_init_comma,
202 .@"switch",
203 .switch_comma,
204 .@"for",
205 .for_simple,
206 .@"suspend",
207 .@"continue",
208 .@"anytype",
209 .fn_proto_simple,
210 .fn_proto_multi,
211 .fn_proto_one,
212 .fn_proto,
213 .fn_decl,
214 .anyframe_type,
215 .anyframe_literal,
216 .error_set_decl,
217 .container_decl,
218 .container_decl_trailing,
219 .container_decl_two,
220 .container_decl_two_trailing,
221 .container_decl_arg,
222 .container_decl_arg_trailing,
223 .tagged_union,
224 .tagged_union_trailing,
225 .tagged_union_two,
226 .tagged_union_two_trailing,
227 .tagged_union_enum_tag,
228 .tagged_union_enum_tag_trailing,
229 .@"comptime",
230 .@"nosuspend",
231 .error_value,
232 => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
233
234 .builtin_call,
235 .builtin_call_comma,
236 .builtin_call_two,
237 .builtin_call_two_comma,
238 => {
239 const builtin_token = main_tokens[node];
240 const builtin_name = tree.tokenSlice(builtin_token);
241 // If the builtin is an invalid name, we don't cause an error here; instead
242 // let it pass, and the error will be "invalid builtin function" later.
243 if (BuiltinFn.list.get(builtin_name)) |info| {
244 if (!info.allows_lvalue) {
245 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
246 }
247 }
248 },
249
250 // These can be assigned to.
251 .unwrap_optional,
252 .deref,
253 .field_access,
254 .array_access,
255 .identifier,
256 .grouped_expression,
257 .@"orelse",
258 => {},
259 }
260 return expr(mod, scope, .ref, node);
261}
262
263/// Turn Zig AST into untyped ZIR istructions.
264/// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the
265/// result instruction can be used to inspect whether it is isNoReturn() but that is it,
266/// it must otherwise not be used.
267pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst {
268 const tree = scope.tree();
269 const main_tokens = tree.nodes.items(.main_token);
270 const token_tags = tree.tokens.items(.tag);
271 const node_datas = tree.nodes.items(.data);
272 const node_tags = tree.nodes.items(.tag);
273 const token_starts = tree.tokens.items(.start);
274
275 switch (node_tags[node]) {
276 .root => unreachable, // Top-level declaration.
277 .@"usingnamespace" => unreachable, // Top-level declaration.
278 .test_decl => unreachable, // Top-level declaration.
279 .container_field_init => unreachable, // Top-level declaration.
280 .container_field_align => unreachable, // Top-level declaration.
281 .container_field => unreachable, // Top-level declaration.
282 .fn_decl => unreachable, // Top-level declaration.
283
284 .global_var_decl => unreachable, // Handled in `blockExpr`.
285 .local_var_decl => unreachable, // Handled in `blockExpr`.
286 .simple_var_decl => unreachable, // Handled in `blockExpr`.
287 .aligned_var_decl => unreachable, // Handled in `blockExpr`.
288
289 .switch_case => unreachable, // Handled in `switchExpr`.
290 .switch_case_one => unreachable, // Handled in `switchExpr`.
291 .switch_range => unreachable, // Handled in `switchExpr`.
292
293 .asm_output => unreachable, // Handled in `asmExpr`.
294 .asm_input => unreachable, // Handled in `asmExpr`.
295
296 .assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node)),
297 .assign_bit_and => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_and)),
298 .assign_bit_or => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_or)),
299 .assign_bit_shift_left => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .shl)),
300 .assign_bit_shift_right => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .shr)),
301 .assign_bit_xor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .xor)),
302 .assign_div => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .div)),
303 .assign_sub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .sub)),
304 .assign_sub_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .subwrap)),
305 .assign_mod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mod_rem)),
306 .assign_add => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .add)),
307 .assign_add_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .addwrap)),
308 .assign_mul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mul)),
309 .assign_mul_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mulwrap)),
310
311 .add => return simpleBinOp(mod, scope, rl, node, .add),
312 .add_wrap => return simpleBinOp(mod, scope, rl, node, .addwrap),
313 .sub => return simpleBinOp(mod, scope, rl, node, .sub),
314 .sub_wrap => return simpleBinOp(mod, scope, rl, node, .subwrap),
315 .mul => return simpleBinOp(mod, scope, rl, node, .mul),
316 .mul_wrap => return simpleBinOp(mod, scope, rl, node, .mulwrap),
317 .div => return simpleBinOp(mod, scope, rl, node, .div),
318 .mod => return simpleBinOp(mod, scope, rl, node, .mod_rem),
319 .bit_and => return simpleBinOp(mod, scope, rl, node, .bit_and),
320 .bit_or => return simpleBinOp(mod, scope, rl, node, .bit_or),
321 .bit_shift_left => return simpleBinOp(mod, scope, rl, node, .shl),
322 .bit_shift_right => return simpleBinOp(mod, scope, rl, node, .shr),
323 .bit_xor => return simpleBinOp(mod, scope, rl, node, .xor),
324
325 .bang_equal => return simpleBinOp(mod, scope, rl, node, .cmp_neq),
326 .equal_equal => return simpleBinOp(mod, scope, rl, node, .cmp_eq),
327 .greater_than => return simpleBinOp(mod, scope, rl, node, .cmp_gt),
328 .greater_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_gte),
329 .less_than => return simpleBinOp(mod, scope, rl, node, .cmp_lt),
330 .less_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_lte),
331
332 .array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat),
333 .array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul),
334
335 .bool_and => return boolBinOp(mod, scope, rl, node, true),
336 .bool_or => return boolBinOp(mod, scope, rl, node, false),
337
338 .bool_not => return rvalue(mod, scope, rl, try boolNot(mod, scope, node)),
339 .bit_not => return rvalue(mod, scope, rl, try bitNot(mod, scope, node)),
340 .negation => return rvalue(mod, scope, rl, try negation(mod, scope, node, .sub)),
341 .negation_wrap => return rvalue(mod, scope, rl, try negation(mod, scope, node, .subwrap)),
342
343 .identifier => return identifier(mod, scope, rl, node),
344
345 .asm_simple => return asmExpr(mod, scope, rl, tree.asmSimple(node)),
346 .@"asm" => return asmExpr(mod, scope, rl, tree.asmFull(node)),
347
348 .string_literal => return stringLiteral(mod, scope, rl, node),
349 .multiline_string_literal => return multilineStringLiteral(mod, scope, rl, node),
350
351 .integer_literal => return integerLiteral(mod, scope, rl, node),
352
353 .builtin_call_two, .builtin_call_two_comma => {
354 if (node_datas[node].lhs == 0) {
355 const params = [_]ast.Node.Index{};
356 return builtinCall(mod, scope, rl, node, &params);
357 } else if (node_datas[node].rhs == 0) {
358 const params = [_]ast.Node.Index{node_datas[node].lhs};
359 return builtinCall(mod, scope, rl, node, &params);
360 } else {
361 const params = [_]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
362 return builtinCall(mod, scope, rl, node, &params);
363 }
364 },
365 .builtin_call, .builtin_call_comma => {
366 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
367 return builtinCall(mod, scope, rl, node, params);
368 },
369
370 .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
371 var params: [1]ast.Node.Index = undefined;
372 return callExpr(mod, scope, rl, tree.callOne(&params, node));
373 },
374 .call, .call_comma, .async_call, .async_call_comma => {
375 return callExpr(mod, scope, rl, tree.callFull(node));
376 },
377
378 .unreachable_literal => {
379 const main_token = main_tokens[node];
380 const src = token_starts[main_token];
381 return addZIRNoOp(mod, scope, src, .unreachable_safe);
382 },
383 .@"return" => return ret(mod, scope, node),
384 .field_access => return fieldAccess(mod, scope, rl, node),
385 .float_literal => return floatLiteral(mod, scope, rl, node),
386
387 .if_simple => return ifExpr(mod, scope, rl, tree.ifSimple(node)),
388 .@"if" => return ifExpr(mod, scope, rl, tree.ifFull(node)),
389
390 .while_simple => return whileExpr(mod, scope, rl, tree.whileSimple(node)),
391 .while_cont => return whileExpr(mod, scope, rl, tree.whileCont(node)),
392 .@"while" => return whileExpr(mod, scope, rl, tree.whileFull(node)),
393
394 .for_simple => return forExpr(mod, scope, rl, tree.forSimple(node)),
395 .@"for" => return forExpr(mod, scope, rl, tree.forFull(node)),
396
397 // TODO handling these separately would actually be simpler & have fewer branches
398 // once we have a ZIR instruction for each of these 3 cases.
399 .slice_open => return sliceExpr(mod, scope, rl, tree.sliceOpen(node)),
400 .slice => return sliceExpr(mod, scope, rl, tree.slice(node)),
401 .slice_sentinel => return sliceExpr(mod, scope, rl, tree.sliceSentinel(node)),
402
403 .deref => {
404 const lhs = try expr(mod, scope, .none, node_datas[node].lhs);
405 const src = token_starts[main_tokens[node]];
406 const result = try addZIRUnOp(mod, scope, src, .deref, lhs);
407 return rvalue(mod, scope, rl, result);
408 },
409 .address_of => {
410 const result = try expr(mod, scope, .ref, node_datas[node].lhs);
411 return rvalue(mod, scope, rl, result);
412 },
413 .undefined_literal => {
414 const main_token = main_tokens[node];
415 const src = token_starts[main_token];
416 const result = try addZIRInstConst(mod, scope, src, .{
417 .ty = Type.initTag(.@"undefined"),
418 .val = Value.initTag(.undef),
419 });
420 return rvalue(mod, scope, rl, result);
421 },
422 .true_literal => {
423 const main_token = main_tokens[node];
424 const src = token_starts[main_token];
425 const result = try addZIRInstConst(mod, scope, src, .{
426 .ty = Type.initTag(.bool),
427 .val = Value.initTag(.bool_true),
428 });
429 return rvalue(mod, scope, rl, result);
430 },
431 .false_literal => {
432 const main_token = main_tokens[node];
433 const src = token_starts[main_token];
434 const result = try addZIRInstConst(mod, scope, src, .{
435 .ty = Type.initTag(.bool),
436 .val = Value.initTag(.bool_false),
437 });
438 return rvalue(mod, scope, rl, result);
439 },
440 .null_literal => {
441 const main_token = main_tokens[node];
442 const src = token_starts[main_token];
443 const result = try addZIRInstConst(mod, scope, src, .{
444 .ty = Type.initTag(.@"null"),
445 .val = Value.initTag(.null_value),
446 });
447 return rvalue(mod, scope, rl, result);
448 },
449 .optional_type => {
450 const src = token_starts[main_tokens[node]];
451 const operand = try typeExpr(mod, scope, node_datas[node].lhs);
452 const result = try addZIRUnOp(mod, scope, src, .optional_type, operand);
453 return rvalue(mod, scope, rl, result);
454 },
455 .unwrap_optional => {
456 const src = token_starts[main_tokens[node]];
457 switch (rl) {
458 .ref => return addZIRUnOp(
459 mod,
460 scope,
461 src,
462 .optional_payload_safe_ptr,
463 try expr(mod, scope, .ref, node_datas[node].lhs),
464 ),
465 else => return rvalue(mod, scope, rl, try addZIRUnOp(
466 mod,
467 scope,
468 src,
469 .optional_payload_safe,
470 try expr(mod, scope, .none, node_datas[node].lhs),
471 )),
472 }
473 },
474 .block_two, .block_two_semicolon => {
475 const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
476 if (node_datas[node].lhs == 0) {
477 return blockExpr(mod, scope, rl, node, statements[0..0]);
478 } else if (node_datas[node].rhs == 0) {
479 return blockExpr(mod, scope, rl, node, statements[0..1]);
480 } else {
481 return blockExpr(mod, scope, rl, node, statements[0..2]);
482 }
483 },
484 .block, .block_semicolon => {
485 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
486 return blockExpr(mod, scope, rl, node, statements);
487 },
488 .enum_literal => {
489 const ident_token = main_tokens[node];
490 const name = try mod.identifierTokenString(scope, ident_token);
491 const src = token_starts[ident_token];
492 const result = try addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
493 return rvalue(mod, scope, rl, result);
494 },
495 .error_value => {
496 const ident_token = node_datas[node].rhs;
497 const name = try mod.identifierTokenString(scope, ident_token);
498 const src = token_starts[ident_token];
499 const result = try addZirInstTag(mod, scope, src, .error_value, .{ .name = name });
500 return rvalue(mod, scope, rl, result);
501 },
502 .error_union => {
503 const error_set = try typeExpr(mod, scope, node_datas[node].lhs);
504 const payload = try typeExpr(mod, scope, node_datas[node].rhs);
505 const src = token_starts[main_tokens[node]];
506 const result = try addZIRBinOp(mod, scope, src, .error_union_type, error_set, payload);
507 return rvalue(mod, scope, rl, result);
508 },
509 .merge_error_sets => {
510 const lhs = try typeExpr(mod, scope, node_datas[node].lhs);
511 const rhs = try typeExpr(mod, scope, node_datas[node].rhs);
512 const src = token_starts[main_tokens[node]];
513 const result = try addZIRBinOp(mod, scope, src, .merge_error_sets, lhs, rhs);
514 return rvalue(mod, scope, rl, result);
515 },
516 .anyframe_literal => {
517 const main_token = main_tokens[node];
518 const src = token_starts[main_token];
519 const result = try addZIRInstConst(mod, scope, src, .{
520 .ty = Type.initTag(.type),
521 .val = Value.initTag(.anyframe_type),
522 });
523 return rvalue(mod, scope, rl, result);
524 },
525 .anyframe_type => {
526 const src = token_starts[node_datas[node].lhs];
527 const return_type = try typeExpr(mod, scope, node_datas[node].rhs);
528 const result = try addZIRUnOp(mod, scope, src, .anyframe_type, return_type);
529 return rvalue(mod, scope, rl, result);
530 },
531 .@"catch" => {
532 const catch_token = main_tokens[node];
533 const payload_token: ?ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
534 catch_token + 2
535 else
536 null;
537 switch (rl) {
538 .ref => return orelseCatchExpr(
539 mod,
540 scope,
541 rl,
542 node_datas[node].lhs,
543 main_tokens[node],
544 .is_err_ptr,
545 .err_union_payload_unsafe_ptr,
546 .err_union_code_ptr,
547 node_datas[node].rhs,
548 payload_token,
549 ),
550 else => return orelseCatchExpr(
551 mod,
552 scope,
553 rl,
554 node_datas[node].lhs,
555 main_tokens[node],
556 .is_err,
557 .err_union_payload_unsafe,
558 .err_union_code,
559 node_datas[node].rhs,
560 payload_token,
561 ),
562 }
563 },
564 .@"orelse" => switch (rl) {
565 .ref => return orelseCatchExpr(
566 mod,
567 scope,
568 rl,
569 node_datas[node].lhs,
570 main_tokens[node],
571 .is_null_ptr,
572 .optional_payload_unsafe_ptr,
573 undefined,
574 node_datas[node].rhs,
575 null,
576 ),
577 else => return orelseCatchExpr(
578 mod,
579 scope,
580 rl,
581 node_datas[node].lhs,
582 main_tokens[node],
583 .is_null,
584 .optional_payload_unsafe,
585 undefined,
586 node_datas[node].rhs,
587 null,
588 ),
589 },
590
591 .ptr_type_aligned => return ptrType(mod, scope, rl, tree.ptrTypeAligned(node)),
592 .ptr_type_sentinel => return ptrType(mod, scope, rl, tree.ptrTypeSentinel(node)),
593 .ptr_type => return ptrType(mod, scope, rl, tree.ptrType(node)),
594 .ptr_type_bit_range => return ptrType(mod, scope, rl, tree.ptrTypeBitRange(node)),
595
596 .container_decl,
597 .container_decl_trailing,
598 => return containerDecl(mod, scope, rl, tree.containerDecl(node)),
599 .container_decl_two, .container_decl_two_trailing => {
600 var buffer: [2]ast.Node.Index = undefined;
601 return containerDecl(mod, scope, rl, tree.containerDeclTwo(&buffer, node));
602 },
603 .container_decl_arg,
604 .container_decl_arg_trailing,
605 => return containerDecl(mod, scope, rl, tree.containerDeclArg(node)),
606
607 .tagged_union,
608 .tagged_union_trailing,
609 => return containerDecl(mod, scope, rl, tree.taggedUnion(node)),
610 .tagged_union_two, .tagged_union_two_trailing => {
611 var buffer: [2]ast.Node.Index = undefined;
612 return containerDecl(mod, scope, rl, tree.taggedUnionTwo(&buffer, node));
613 },
614 .tagged_union_enum_tag,
615 .tagged_union_enum_tag_trailing,
616 => return containerDecl(mod, scope, rl, tree.taggedUnionEnumTag(node)),
617
618 .@"break" => return breakExpr(mod, scope, rl, node),
619 .@"continue" => return continueExpr(mod, scope, rl, node),
620 .grouped_expression => return expr(mod, scope, rl, node_datas[node].lhs),
621 .array_type => return arrayType(mod, scope, rl, node),
622 .array_type_sentinel => return arrayTypeSentinel(mod, scope, rl, node),
623 .char_literal => return charLiteral(mod, scope, rl, node),
624 .error_set_decl => return errorSetDecl(mod, scope, rl, node),
625 .array_access => return arrayAccess(mod, scope, rl, node),
626 .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs),
627 .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node),
628
629 .@"nosuspend" => return nosuspendExpr(mod, scope, rl, node),
630 .@"suspend" => return rvalue(mod, scope, rl, try suspendExpr(mod, scope, node)),
631 .@"await" => return awaitExpr(mod, scope, rl, node),
632 .@"resume" => return rvalue(mod, scope, rl, try resumeExpr(mod, scope, node)),
633
634 .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}),
635 .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}),
636 .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
637
638 .array_init_one,
639 .array_init_one_comma,
640 .array_init_dot_two,
641 .array_init_dot_two_comma,
642 .array_init_dot,
643 .array_init_dot_comma,
644 .array_init,
645 .array_init_comma,
646 => return mod.failNode(scope, node, "TODO implement astgen.expr for array literals", .{}),
647
648 .struct_init_one,
649 .struct_init_one_comma,
650 .struct_init_dot_two,
651 .struct_init_dot_two_comma,
652 .struct_init_dot,
653 .struct_init_dot_comma,
654 .struct_init,
655 .struct_init_comma,
656 => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}),
657
658 .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}),
659 .fn_proto_simple,
660 .fn_proto_multi,
661 .fn_proto_one,
662 .fn_proto,
663 => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}),
664 }
665}
666
667pub fn comptimeExpr(
668 mod: *Module,
669 parent_scope: *Scope,
670 rl: ResultLoc,
671 node: ast.Node.Index,
672) InnerError!*zir.Inst {
673 // If we are already in a comptime scope, no need to make another one.
674 if (parent_scope.isComptime()) {
675 return expr(mod, parent_scope, rl, node);
676 }
677
678 const tree = parent_scope.tree();
679 const token_starts = tree.tokens.items(.start);
680
681 // Make a scope to collect generated instructions in the sub-expression.
682 var block_scope: Scope.GenZIR = .{
683 .parent = parent_scope,
684 .decl = parent_scope.ownerDecl().?,
685 .arena = parent_scope.arena(),
686 .force_comptime = true,
687 .instructions = .{},
688 };
689 defer block_scope.instructions.deinit(mod.gpa);
690
691 // No need to capture the result here because block_comptime_flat implies that the final
692 // instruction is the block's result value.
693 _ = try expr(mod, &block_scope.base, rl, node);
694
695 const src = token_starts[tree.firstToken(node)];
696 const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
697 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
698 });
699
700 return &block.base;
701}
702
703fn breakExpr(
704 mod: *Module,
705 parent_scope: *Scope,
706 rl: ResultLoc,
707 node: ast.Node.Index,
708) InnerError!*zir.Inst {
709 const tree = parent_scope.tree();
710 const node_datas = tree.nodes.items(.data);
711 const main_tokens = tree.nodes.items(.main_token);
712 const token_starts = tree.tokens.items(.start);
713
714 const src = token_starts[main_tokens[node]];
715 const break_label = node_datas[node].lhs;
716 const rhs = node_datas[node].rhs;
717
718 // Look for the label in the scope.
719 var scope = parent_scope;
720 while (true) {
721 switch (scope.tag) {
722 .gen_zir => {
723 const gen_zir = scope.cast(Scope.GenZIR).?;
724
725 const block_inst = blk: {
726 if (break_label != 0) {
727 if (gen_zir.label) |*label| {
728 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
729 label.used = true;
730 break :blk label.block_inst;
731 }
732 }
733 } else if (gen_zir.break_block) |inst| {
734 break :blk inst;
735 }
736 scope = gen_zir.parent;
737 continue;
738 };
739
740 if (rhs == 0) {
741 const result = try addZirInstTag(mod, parent_scope, src, .break_void, .{
742 .block = block_inst,
743 });
744 return rvalue(mod, parent_scope, rl, result);
745 }
746 gen_zir.break_count += 1;
747 const prev_rvalue_rl_count = gen_zir.rvalue_rl_count;
748 const operand = try expr(mod, parent_scope, gen_zir.break_result_loc, rhs);
749 const have_store_to_block = gen_zir.rvalue_rl_count != prev_rvalue_rl_count;
750 const br = try addZirInstTag(mod, parent_scope, src, .@"break", .{
751 .block = block_inst,
752 .operand = operand,
753 });
754 if (gen_zir.break_result_loc == .block_ptr) {
755 try gen_zir.labeled_breaks.append(mod.gpa, br.castTag(.@"break").?);
756
757 if (have_store_to_block) {
758 const inst_list = parent_scope.getGenZIR().instructions.items;
759 const last_inst = inst_list[inst_list.len - 2];
760 const store_inst = last_inst.castTag(.store_to_block_ptr).?;
761 assert(store_inst.positionals.lhs == gen_zir.rl_ptr.?);
762 try gen_zir.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst);
763 }
764 }
765 return rvalue(mod, parent_scope, rl, br);
766 },
767 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
768 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
769 .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent,
770 .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent,
771 else => if (break_label != 0) {
772 const label_name = try mod.identifierTokenString(parent_scope, break_label);
773 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
774 } else {
775 return mod.failTok(parent_scope, src, "break expression outside loop", .{});
776 },
777 }
778 }
779}
780
781fn continueExpr(
782 mod: *Module,
783 parent_scope: *Scope,
784 rl: ResultLoc,
785 node: ast.Node.Index,
786) InnerError!*zir.Inst {
787 const tree = parent_scope.tree();
788 const node_datas = tree.nodes.items(.data);
789 const main_tokens = tree.nodes.items(.main_token);
790 const token_starts = tree.tokens.items(.start);
791
792 const src = token_starts[main_tokens[node]];
793 const break_label = node_datas[node].lhs;
794
795 // Look for the label in the scope.
796 var scope = parent_scope;
797 while (true) {
798 switch (scope.tag) {
799 .gen_zir => {
800 const gen_zir = scope.cast(Scope.GenZIR).?;
801 const continue_block = gen_zir.continue_block orelse {
802 scope = gen_zir.parent;
803 continue;
804 };
805 if (break_label != 0) blk: {
806 if (gen_zir.label) |*label| {
807 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
808 label.used = true;
809 break :blk;
810 }
811 }
812 // found continue but either it has a different label, or no label
813 scope = gen_zir.parent;
814 continue;
815 }
816
817 const result = try addZirInstTag(mod, parent_scope, src, .break_void, .{
818 .block = continue_block,
819 });
820 return rvalue(mod, parent_scope, rl, result);
821 },
822 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
823 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
824 .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent,
825 .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent,
826 else => if (break_label != 0) {
827 const label_name = try mod.identifierTokenString(parent_scope, break_label);
828 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
829 } else {
830 return mod.failTok(parent_scope, src, "continue expression outside loop", .{});
831 },
832 }
833 }
834}
835
836pub fn blockExpr(
837 mod: *Module,
838 scope: *Scope,
839 rl: ResultLoc,
840 block_node: ast.Node.Index,
841 statements: []const ast.Node.Index,
842) InnerError!*zir.Inst {
843 const tracy = trace(@src());
844 defer tracy.end();
845
846 const tree = scope.tree();
847 const main_tokens = tree.nodes.items(.main_token);
848 const token_tags = tree.tokens.items(.tag);
849
850 const lbrace = main_tokens[block_node];
851 if (token_tags[lbrace - 1] == .colon and
852 token_tags[lbrace - 2] == .identifier)
853 {
854 return labeledBlockExpr(mod, scope, rl, block_node, statements, .block);
855 }
856
857 try blockExprStmts(mod, scope, block_node, statements);
858 return rvalueVoid(mod, scope, rl, block_node, {});
859}
860
861fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {
862 // Look for the label in the scope.
863 var scope = parent_scope;
864 while (true) {
865 switch (scope.tag) {
866 .gen_zir => {
867 const gen_zir = scope.cast(Scope.GenZIR).?;
868 if (gen_zir.label) |prev_label| {
869 if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {
870 const tree = parent_scope.tree();
871 const main_tokens = tree.nodes.items(.main_token);
872 const token_starts = tree.tokens.items(.start);
873
874 const label_src = token_starts[label];
875 const prev_label_src = token_starts[prev_label.token];
876
877 const label_name = try mod.identifierTokenString(parent_scope, label);
878 const msg = msg: {
879 const msg = try mod.errMsg(
880 parent_scope,
881 label_src,
882 "redefinition of label '{s}'",
883 .{label_name},
884 );
885 errdefer msg.destroy(mod.gpa);
886 try mod.errNote(
887 parent_scope,
888 prev_label_src,
889 msg,
890 "previous definition is here",
891 .{},
892 );
893 break :msg msg;
894 };
895 return mod.failWithOwnedErrorMsg(parent_scope, msg);
896 }
897 }
898 scope = gen_zir.parent;
899 },
900 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
901 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
902 .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent,
903 .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent,
904 else => return,
905 }
906 }
907}
908
909fn labeledBlockExpr(
910 mod: *Module,
911 parent_scope: *Scope,
912 rl: ResultLoc,
913 block_node: ast.Node.Index,
914 statements: []const ast.Node.Index,
915 zir_tag: zir.Inst.Tag,
916) InnerError!*zir.Inst {
917 const tracy = trace(@src());
918 defer tracy.end();
919
920 assert(zir_tag == .block or zir_tag == .block_comptime);
921
922 const tree = parent_scope.tree();
923 const main_tokens = tree.nodes.items(.main_token);
924 const token_starts = tree.tokens.items(.start);
925 const token_tags = tree.tokens.items(.tag);
926
927 const lbrace = main_tokens[block_node];
928 const label_token = lbrace - 2;
929 assert(token_tags[label_token] == .identifier);
930 const src = token_starts[lbrace];
931
932 try checkLabelRedefinition(mod, parent_scope, label_token);
933
934 // Create the Block ZIR instruction so that we can put it into the GenZIR struct
935 // so that break statements can reference it.
936 const gen_zir = parent_scope.getGenZIR();
937 const block_inst = try gen_zir.arena.create(zir.Inst.Block);
938 block_inst.* = .{
939 .base = .{
940 .tag = zir_tag,
941 .src = src,
942 },
943 .positionals = .{
944 .body = .{ .instructions = undefined },
945 },
946 .kw_args = .{},
947 };
948
949 var block_scope: Scope.GenZIR = .{
950 .parent = parent_scope,
951 .decl = parent_scope.ownerDecl().?,
952 .arena = gen_zir.arena,
953 .force_comptime = parent_scope.isComptime(),
954 .instructions = .{},
955 // TODO @as here is working around a stage1 miscompilation bug :(
956 .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
957 .token = label_token,
958 .block_inst = block_inst,
959 }),
960 };
961 setBlockResultLoc(&block_scope, rl);
962 defer block_scope.instructions.deinit(mod.gpa);
963 defer block_scope.labeled_breaks.deinit(mod.gpa);
964 defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa);
965
966 try blockExprStmts(mod, &block_scope.base, block_node, statements);
967
968 if (!block_scope.label.?.used) {
969 return mod.failTok(parent_scope, label_token, "unused block label", .{});
970 }
971
972 try gen_zir.instructions.append(mod.gpa, &block_inst.base);
973
974 const strat = rlStrategy(rl, &block_scope);
975 switch (strat.tag) {
976 .break_void => {
977 // The code took advantage of the result location as a pointer.
978 // Turn the break instructions into break_void instructions.
979 for (block_scope.labeled_breaks.items) |br| {
980 br.base.tag = .break_void;
981 }
982 // TODO technically not needed since we changed the tag to break_void but
983 // would be better still to elide the ones that are in this list.
984 try copyBodyNoEliding(&block_inst.positionals.body, block_scope);
985
986 return &block_inst.base;
987 },
988 .break_operand => {
989 // All break operands are values that did not use the result location pointer.
990 if (strat.elide_store_to_block_ptr_instructions) {
991 for (block_scope.labeled_store_to_block_ptr_list.items) |inst| {
992 inst.base.tag = .void_value;
993 }
994 // TODO technically not needed since we changed the tag to void_value but
995 // would be better still to elide the ones that are in this list.
996 }
997 try copyBodyNoEliding(&block_inst.positionals.body, block_scope);
998 switch (rl) {
999 .ref => return &block_inst.base,
1000 else => return rvalue(mod, parent_scope, rl, &block_inst.base),
1001 }
1002 },
1003 }
1004}
1005
1006fn blockExprStmts(
1007 mod: *Module,
1008 parent_scope: *Scope,
1009 node: ast.Node.Index,
1010 statements: []const ast.Node.Index,
1011) !void {
1012 const tree = parent_scope.tree();
1013 const main_tokens = tree.nodes.items(.main_token);
1014 const token_starts = tree.tokens.items(.start);
1015 const node_tags = tree.nodes.items(.tag);
1016
1017 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
1018 defer block_arena.deinit();
1019
1020 var scope = parent_scope;
1021 for (statements) |statement| {
1022 const src = token_starts[tree.firstToken(statement)];
1023 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
1024 switch (node_tags[statement]) {
1025 .global_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.globalVarDecl(statement)),
1026 .local_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.localVarDecl(statement)),
1027 .simple_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.simpleVarDecl(statement)),
1028 .aligned_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.alignedVarDecl(statement)),
1029
1030 .assign => try assign(mod, scope, statement),
1031 .assign_bit_and => try assignOp(mod, scope, statement, .bit_and),
1032 .assign_bit_or => try assignOp(mod, scope, statement, .bit_or),
1033 .assign_bit_shift_left => try assignOp(mod, scope, statement, .shl),
1034 .assign_bit_shift_right => try assignOp(mod, scope, statement, .shr),
1035 .assign_bit_xor => try assignOp(mod, scope, statement, .xor),
1036 .assign_div => try assignOp(mod, scope, statement, .div),
1037 .assign_sub => try assignOp(mod, scope, statement, .sub),
1038 .assign_sub_wrap => try assignOp(mod, scope, statement, .subwrap),
1039 .assign_mod => try assignOp(mod, scope, statement, .mod_rem),
1040 .assign_add => try assignOp(mod, scope, statement, .add),
1041 .assign_add_wrap => try assignOp(mod, scope, statement, .addwrap),
1042 .assign_mul => try assignOp(mod, scope, statement, .mul),
1043 .assign_mul_wrap => try assignOp(mod, scope, statement, .mulwrap),
1044
1045 else => {
1046 const possibly_unused_result = try expr(mod, scope, .none, statement);
1047 if (!possibly_unused_result.tag.isNoReturn()) {
1048 _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
1049 }
1050 },
1051 }
1052 }
1053}
1054
1055fn varDecl(
1056 mod: *Module,
1057 scope: *Scope,
1058 block_arena: *Allocator,
1059 var_decl: ast.full.VarDecl,
1060) InnerError!*Scope {
1061 if (var_decl.comptime_token) |comptime_token| {
1062 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
1063 }
1064 if (var_decl.ast.align_node != 0) {
1065 return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{});
1066 }
1067 const tree = scope.tree();
1068 const main_tokens = tree.nodes.items(.main_token);
1069 const token_starts = tree.tokens.items(.start);
1070 const token_tags = tree.tokens.items(.tag);
1071
1072 const name_token = var_decl.ast.mut_token + 1;
1073 const name_src = token_starts[name_token];
1074 const ident_name = try mod.identifierTokenString(scope, name_token);
1075
1076 // Local variables shadowing detection, including function parameters.
1077 {
1078 var s = scope;
1079 while (true) switch (s.tag) {
1080 .local_val => {
1081 const local_val = s.cast(Scope.LocalVal).?;
1082 if (mem.eql(u8, local_val.name, ident_name)) {
1083 const msg = msg: {
1084 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
1085 ident_name,
1086 });
1087 errdefer msg.destroy(mod.gpa);
1088 try mod.errNote(scope, local_val.inst.src, msg, "previous definition is here", .{});
1089 break :msg msg;
1090 };
1091 return mod.failWithOwnedErrorMsg(scope, msg);
1092 }
1093 s = local_val.parent;
1094 },
1095 .local_ptr => {
1096 const local_ptr = s.cast(Scope.LocalPtr).?;
1097 if (mem.eql(u8, local_ptr.name, ident_name)) {
1098 const msg = msg: {
1099 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
1100 ident_name,
1101 });
1102 errdefer msg.destroy(mod.gpa);
1103 try mod.errNote(scope, local_ptr.ptr.src, msg, "previous definition is here", .{});
1104 break :msg msg;
1105 };
1106 return mod.failWithOwnedErrorMsg(scope, msg);
1107 }
1108 s = local_ptr.parent;
1109 },
1110 .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
1111 .gen_suspend => s = s.cast(Scope.GenZIR).?.parent,
1112 .gen_nosuspend => s = s.cast(Scope.Nosuspend).?.parent,
1113 else => break,
1114 };
1115 }
1116
1117 // Namespace vars shadowing detection
1118 if (mod.lookupDeclName(scope, ident_name)) |_| {
1119 // TODO add note for other definition
1120 return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
1121 }
1122 if (var_decl.ast.init_node == 0) {
1123 return mod.fail(scope, name_src, "variables must be initialized", .{});
1124 }
1125
1126 switch (token_tags[var_decl.ast.mut_token]) {
1127 .keyword_const => {
1128 // Depending on the type of AST the initialization expression is, we may need an lvalue
1129 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
1130 // the variable, no memory location needed.
1131 if (!nodeMayNeedMemoryLocation(scope, var_decl.ast.init_node)) {
1132 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0)
1133 .{ .ty = try typeExpr(mod, scope, var_decl.ast.type_node) }
1134 else
1135 .none;
1136 const init_inst = try expr(mod, scope, result_loc, var_decl.ast.init_node);
1137 const sub_scope = try block_arena.create(Scope.LocalVal);
1138 sub_scope.* = .{
1139 .parent = scope,
1140 .gen_zir = scope.getGenZIR(),
1141 .name = ident_name,
1142 .inst = init_inst,
1143 };
1144 return &sub_scope.base;
1145 }
1146
1147 // Detect whether the initialization expression actually uses the
1148 // result location pointer.
1149 var init_scope: Scope.GenZIR = .{
1150 .parent = scope,
1151 .decl = scope.ownerDecl().?,
1152 .arena = scope.arena(),
1153 .force_comptime = scope.isComptime(),
1154 .instructions = .{},
1155 };
1156 defer init_scope.instructions.deinit(mod.gpa);
1157
1158 var resolve_inferred_alloc: ?*zir.Inst = null;
1159 var opt_type_inst: ?*zir.Inst = null;
1160 if (var_decl.ast.type_node != 0) {
1161 const type_inst = try typeExpr(mod, &init_scope.base, var_decl.ast.type_node);
1162 opt_type_inst = type_inst;
1163 init_scope.rl_ptr = try addZIRUnOp(mod, &init_scope.base, name_src, .alloc, type_inst);
1164 } else {
1165 const alloc = try addZIRNoOpT(mod, &init_scope.base, name_src, .alloc_inferred);
1166 resolve_inferred_alloc = &alloc.base;
1167 init_scope.rl_ptr = &alloc.base;
1168 }
1169 const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
1170 const init_inst = try expr(mod, &init_scope.base, init_result_loc, var_decl.ast.init_node);
1171 const parent_zir = &scope.getGenZIR().instructions;
1172 if (init_scope.rvalue_rl_count == 1) {
1173 // Result location pointer not used. We don't need an alloc for this
1174 // const local, and type inference becomes trivial.
1175 // Move the init_scope instructions into the parent scope, eliding
1176 // the alloc instruction and the store_to_block_ptr instruction.
1177 const expected_len = parent_zir.items.len + init_scope.instructions.items.len - 2;
1178 try parent_zir.ensureCapacity(mod.gpa, expected_len);
1179 for (init_scope.instructions.items) |src_inst| {
1180 if (src_inst == init_scope.rl_ptr.?) continue;
1181 if (src_inst.castTag(.store_to_block_ptr)) |store| {
1182 if (store.positionals.lhs == init_scope.rl_ptr.?) continue;
1183 }
1184 parent_zir.appendAssumeCapacity(src_inst);
1185 }
1186 assert(parent_zir.items.len == expected_len);
1187 const casted_init = if (opt_type_inst) |type_inst|
1188 try addZIRBinOp(mod, scope, type_inst.src, .as, type_inst, init_inst)
1189 else
1190 init_inst;
1191
1192 const sub_scope = try block_arena.create(Scope.LocalVal);
1193 sub_scope.* = .{
1194 .parent = scope,
1195 .gen_zir = scope.getGenZIR(),
1196 .name = ident_name,
1197 .inst = casted_init,
1198 };
1199 return &sub_scope.base;
1200 }
1201 // The initialization expression took advantage of the result location
1202 // of the const local. In this case we will create an alloc and a LocalPtr for it.
1203 // Move the init_scope instructions into the parent scope, swapping
1204 // store_to_block_ptr for store_to_inferred_ptr.
1205 const expected_len = parent_zir.items.len + init_scope.instructions.items.len;
1206 try parent_zir.ensureCapacity(mod.gpa, expected_len);
1207 for (init_scope.instructions.items) |src_inst| {
1208 if (src_inst.castTag(.store_to_block_ptr)) |store| {
1209 if (store.positionals.lhs == init_scope.rl_ptr.?) {
1210 src_inst.tag = .store_to_inferred_ptr;
1211 }
1212 }
1213 parent_zir.appendAssumeCapacity(src_inst);
1214 }
1215 assert(parent_zir.items.len == expected_len);
1216 if (resolve_inferred_alloc) |inst| {
1217 _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);
1218 }
1219 const sub_scope = try block_arena.create(Scope.LocalPtr);
1220 sub_scope.* = .{
1221 .parent = scope,
1222 .gen_zir = scope.getGenZIR(),
1223 .name = ident_name,
1224 .ptr = init_scope.rl_ptr.?,
1225 };
1226 return &sub_scope.base;
1227 },
1228 .keyword_var => {
1229 var resolve_inferred_alloc: ?*zir.Inst = null;
1230 const var_data: struct {
1231 result_loc: ResultLoc,
1232 alloc: *zir.Inst,
1233 } = if (var_decl.ast.type_node != 0) a: {
1234 const type_inst = try typeExpr(mod, scope, var_decl.ast.type_node);
1235 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst);
1236 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
1237 } else a: {
1238 const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred_mut);
1239 resolve_inferred_alloc = &alloc.base;
1240 break :a .{ .alloc = &alloc.base, .result_loc = .{ .inferred_ptr = alloc } };
1241 };
1242 const init_inst = try expr(mod, scope, var_data.result_loc, var_decl.ast.init_node);
1243 if (resolve_inferred_alloc) |inst| {
1244 _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);
1245 }
1246 const sub_scope = try block_arena.create(Scope.LocalPtr);
1247 sub_scope.* = .{
1248 .parent = scope,
1249 .gen_zir = scope.getGenZIR(),
1250 .name = ident_name,
1251 .ptr = var_data.alloc,
1252 };
1253 return &sub_scope.base;
1254 },
1255 else => unreachable,
1256 }
1257}
1258
1259fn assign(mod: *Module, scope: *Scope, infix_node: ast.Node.Index) InnerError!void {
1260 const tree = scope.tree();
1261 const node_datas = tree.nodes.items(.data);
1262 const main_tokens = tree.nodes.items(.main_token);
1263 const node_tags = tree.nodes.items(.tag);
1264
1265 const lhs = node_datas[infix_node].lhs;
1266 const rhs = node_datas[infix_node].rhs;
1267 if (node_tags[lhs] == .identifier) {
1268 // This intentionally does not support `@"_"` syntax.
1269 const ident_name = tree.tokenSlice(main_tokens[lhs]);
1270 if (mem.eql(u8, ident_name, "_")) {
1271 _ = try expr(mod, scope, .discard, rhs);
1272 return;
1273 }
1274 }
1275 const lvalue = try lvalExpr(mod, scope, lhs);
1276 _ = try expr(mod, scope, .{ .ptr = lvalue }, rhs);
1277}
1278
1279fn assignOp(
1280 mod: *Module,
1281 scope: *Scope,
1282 infix_node: ast.Node.Index,
1283 op_inst_tag: zir.Inst.Tag,
1284) InnerError!void {
1285 const tree = scope.tree();
1286 const node_datas = tree.nodes.items(.data);
1287 const main_tokens = tree.nodes.items(.main_token);
1288 const token_starts = tree.tokens.items(.start);
1289
1290 const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs);
1291 const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
1292 const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
1293 const rhs = try expr(mod, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
1294 const src = token_starts[main_tokens[infix_node]];
1295 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
1296 _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);
1297}
1298
1299fn boolNot(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
1300 const tree = scope.tree();
1301 const node_datas = tree.nodes.items(.data);
1302 const main_tokens = tree.nodes.items(.main_token);
1303 const token_starts = tree.tokens.items(.start);
1304
1305 const src = token_starts[main_tokens[node]];
1306 const bool_type = try addZIRInstConst(mod, scope, src, .{
1307 .ty = Type.initTag(.type),
1308 .val = Value.initTag(.bool_type),
1309 });
1310 const operand = try expr(mod, scope, .{ .ty = bool_type }, node_datas[node].lhs);
1311 return addZIRUnOp(mod, scope, src, .bool_not, operand);
1312}
1313
1314fn bitNot(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
1315 const tree = scope.tree();
1316 const node_datas = tree.nodes.items(.data);
1317 const main_tokens = tree.nodes.items(.main_token);
1318 const token_starts = tree.tokens.items(.start);
1319
1320 const src = token_starts[main_tokens[node]];
1321 const operand = try expr(mod, scope, .none, node_datas[node].lhs);
1322 return addZIRUnOp(mod, scope, src, .bit_not, operand);
1323}
1324
1325fn negation(
1326 mod: *Module,
1327 scope: *Scope,
1328 node: ast.Node.Index,
1329 op_inst_tag: zir.Inst.Tag,
1330) InnerError!*zir.Inst {
1331 const tree = scope.tree();
1332 const node_datas = tree.nodes.items(.data);
1333 const main_tokens = tree.nodes.items(.main_token);
1334 const token_starts = tree.tokens.items(.start);
1335
1336 const src = token_starts[main_tokens[node]];
1337 const lhs = try addZIRInstConst(mod, scope, src, .{
1338 .ty = Type.initTag(.comptime_int),
1339 .val = Value.initTag(.zero),
1340 });
1341 const rhs = try expr(mod, scope, .none, node_datas[node].lhs);
1342 return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
1343}
1344
1345fn ptrType(
1346 mod: *Module,
1347 scope: *Scope,
1348 rl: ResultLoc,
1349 ptr_info: ast.full.PtrType,
1350) InnerError!*zir.Inst {
1351 const tree = scope.tree();
1352 const token_starts = tree.tokens.items(.start);
1353
1354 const src = token_starts[ptr_info.ast.main_token];
1355
1356 const simple = ptr_info.allowzero_token == null and
1357 ptr_info.ast.align_node == 0 and
1358 ptr_info.volatile_token == null and
1359 ptr_info.ast.sentinel == 0;
1360
1361 if (simple) {
1362 const child_type = try typeExpr(mod, scope, ptr_info.ast.child_type);
1363 const mutable = ptr_info.const_token == null;
1364 const T = zir.Inst.Tag;
1365 const result = try addZIRUnOp(mod, scope, src, switch (ptr_info.size) {
1366 .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
1367 .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
1368 .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
1369 .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,
1370 }, child_type);
1371 return rvalue(mod, scope, rl, result);
1372 }
1373
1374 var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, .kw_args).field_type = .{};
1375 kw_args.size = ptr_info.size;
1376 kw_args.@"allowzero" = ptr_info.allowzero_token != null;
1377 if (ptr_info.ast.align_node != 0) {
1378 kw_args.@"align" = try expr(mod, scope, .none, ptr_info.ast.align_node);
1379 if (ptr_info.ast.bit_range_start != 0) {
1380 kw_args.align_bit_start = try expr(mod, scope, .none, ptr_info.ast.bit_range_start);
1381 kw_args.align_bit_end = try expr(mod, scope, .none, ptr_info.ast.bit_range_end);
1382 }
1383 }
1384 kw_args.mutable = ptr_info.const_token == null;
1385 kw_args.@"volatile" = ptr_info.volatile_token != null;
1386 const child_type = try typeExpr(mod, scope, ptr_info.ast.child_type);
1387 if (ptr_info.ast.sentinel != 0) {
1388 kw_args.sentinel = try expr(mod, scope, .{ .ty = child_type }, ptr_info.ast.sentinel);
1389 }
1390 const result = try addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);
1391 return rvalue(mod, scope, rl, result);
1392}
1393
1394fn arrayType(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst {
1395 const tree = scope.tree();
1396 const main_tokens = tree.nodes.items(.main_token);
1397 const node_datas = tree.nodes.items(.data);
1398 const token_starts = tree.tokens.items(.start);
1399
1400 const src = token_starts[main_tokens[node]];
1401 const usize_type = try addZIRInstConst(mod, scope, src, .{
1402 .ty = Type.initTag(.type),
1403 .val = Value.initTag(.usize_type),
1404 });
1405 const len_node = node_datas[node].lhs;
1406 const elem_node = node_datas[node].rhs;
1407 if (len_node == 0) {
1408 const elem_type = try typeExpr(mod, scope, elem_node);
1409 const result = try addZIRUnOp(mod, scope, src, .mut_slice_type, elem_type);
1410 return rvalue(mod, scope, rl, result);
1411 } else {
1412 // TODO check for [_]T
1413 const len = try expr(mod, scope, .{ .ty = usize_type }, len_node);
1414 const elem_type = try typeExpr(mod, scope, elem_node);
1415
1416 const result = try addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
1417 return rvalue(mod, scope, rl, result);
1418 }
1419}
1420
1421fn arrayTypeSentinel(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst {
1422 const tree = scope.tree();
1423 const main_tokens = tree.nodes.items(.main_token);
1424 const token_starts = tree.tokens.items(.start);
1425 const node_datas = tree.nodes.items(.data);
1426
1427 const len_node = node_datas[node].lhs;
1428 const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel);
1429 const src = token_starts[main_tokens[node]];
1430 const usize_type = try addZIRInstConst(mod, scope, src, .{
1431 .ty = Type.initTag(.type),
1432 .val = Value.initTag(.usize_type),
1433 });
1434
1435 // TODO check for [_]T
1436 const len = try expr(mod, scope, .{ .ty = usize_type }, len_node);
1437 const sentinel_uncasted = try expr(mod, scope, .none, extra.sentinel);
1438 const elem_type = try typeExpr(mod, scope, extra.elem_type);
1439 const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);
1440
1441 const result = try addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{
1442 .len = len,
1443 .sentinel = sentinel,
1444 .elem_type = elem_type,
1445 }, .{});
1446 return rvalue(mod, scope, rl, result);
1447}
1448
1449fn containerField(
1450 mod: *Module,
1451 scope: *Scope,
1452 field: ast.full.ContainerField,
1453) InnerError!*zir.Inst {
1454 const tree = scope.tree();
1455 const token_starts = tree.tokens.items(.start);
1456
1457 const src = token_starts[field.ast.name_token];
1458 const name = try mod.identifierTokenString(scope, field.ast.name_token);
1459
1460 if (field.comptime_token == null and field.ast.value_expr == 0 and field.ast.align_expr == 0) {
1461 if (field.ast.type_expr != 0) {
1462 const ty = try typeExpr(mod, scope, field.ast.type_expr);
1463 return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldTyped, .{
1464 .bytes = name,
1465 .ty = ty,
1466 }, .{});
1467 } else {
1468 return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldNamed, .{
1469 .bytes = name,
1470 }, .{});
1471 }
1472 }
1473
1474 const ty = if (field.ast.type_expr != 0) try typeExpr(mod, scope, field.ast.type_expr) else null;
1475 // TODO result location should be alignment type
1476 const alignment = if (field.ast.align_expr != 0) try expr(mod, scope, .none, field.ast.align_expr) else null;
1477 // TODO result location should be the field type
1478 const init = if (field.ast.value_expr != 0) try expr(mod, scope, .none, field.ast.value_expr) else null;
1479
1480 return addZIRInst(mod, scope, src, zir.Inst.ContainerField, .{
1481 .bytes = name,
1482 }, .{
1483 .ty = ty,
1484 .init = init,
1485 .alignment = alignment,
1486 .is_comptime = field.comptime_token != null,
1487 });
1488}
1489
1490fn containerDecl(
1491 mod: *Module,
1492 scope: *Scope,
1493 rl: ResultLoc,
1494 container_decl: ast.full.ContainerDecl,
1495) InnerError!*zir.Inst {
1496 const tree = scope.tree();
1497 const token_starts = tree.tokens.items(.start);
1498 const node_tags = tree.nodes.items(.tag);
1499 const token_tags = tree.tokens.items(.tag);
1500
1501 const src = token_starts[container_decl.ast.main_token];
1502
1503 var gen_scope: Scope.GenZIR = .{
1504 .parent = scope,
1505 .decl = scope.ownerDecl().?,
1506 .arena = scope.arena(),
1507 .force_comptime = scope.isComptime(),
1508 .instructions = .{},
1509 };
1510 defer gen_scope.instructions.deinit(mod.gpa);
1511
1512 var fields = std.ArrayList(*zir.Inst).init(mod.gpa);
1513 defer fields.deinit();
1514
1515 for (container_decl.ast.members) |member| {
1516 // TODO just handle these cases differently since they end up with different ZIR
1517 // instructions anyway. It will be simpler & have fewer branches.
1518 const field = switch (node_tags[member]) {
1519 .container_field_init => try containerField(mod, &gen_scope.base, tree.containerFieldInit(member)),
1520 .container_field_align => try containerField(mod, &gen_scope.base, tree.containerFieldAlign(member)),
1521 .container_field => try containerField(mod, &gen_scope.base, tree.containerField(member)),
1522 else => continue,
1523 };
1524 try fields.append(field);
1525 }
1526
1527 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
1528 errdefer decl_arena.deinit();
1529 const arena = &decl_arena.allocator;
1530
1531 var layout: std.builtin.TypeInfo.ContainerLayout = .Auto;
1532 if (container_decl.layout_token) |some| switch (token_tags[some]) {
1533 .keyword_extern => layout = .Extern,
1534 .keyword_packed => layout = .Packed,
1535 else => unreachable,
1536 };
1537
1538 // TODO this implementation is incorrect. The types must be created in semantic
1539 // analysis, not astgen, because the same ZIR is re-used for multiple inline function calls,
1540 // comptime function calls, and generic function instantiations, and these
1541 // must result in different instances of container types.
1542 const container_type = switch (token_tags[container_decl.ast.main_token]) {
1543 .keyword_enum => blk: {
1544 const tag_type: ?*zir.Inst = if (container_decl.ast.arg != 0)
1545 try typeExpr(mod, &gen_scope.base, container_decl.ast.arg)
1546 else
1547 null;
1548 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.EnumType, .{
1549 .fields = try arena.dupe(*zir.Inst, fields.items),
1550 }, .{
1551 .layout = layout,
1552 .tag_type = tag_type,
1553 });
1554 const enum_type = try arena.create(Type.Payload.Enum);
1555 enum_type.* = .{
1556 .analysis = .{
1557 .queued = .{
1558 .body = .{ .instructions = try arena.dupe(*zir.Inst, gen_scope.instructions.items) },
1559 .inst = inst,
1560 },
1561 },
1562 .scope = .{
1563 .file_scope = scope.getFileScope(),
1564 .ty = Type.initPayload(&enum_type.base),
1565 },
1566 };
1567 break :blk Type.initPayload(&enum_type.base);
1568 },
1569 .keyword_struct => blk: {
1570 assert(container_decl.ast.arg == 0);
1571 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.StructType, .{
1572 .fields = try arena.dupe(*zir.Inst, fields.items),
1573 }, .{
1574 .layout = layout,
1575 });
1576 const struct_type = try arena.create(Type.Payload.Struct);
1577 struct_type.* = .{
1578 .analysis = .{
1579 .queued = .{
1580 .body = .{ .instructions = try arena.dupe(*zir.Inst, gen_scope.instructions.items) },
1581 .inst = inst,
1582 },
1583 },
1584 .scope = .{
1585 .file_scope = scope.getFileScope(),
1586 .ty = Type.initPayload(&struct_type.base),
1587 },
1588 };
1589 break :blk Type.initPayload(&struct_type.base);
1590 },
1591 .keyword_union => blk: {
1592 const init_inst: ?*zir.Inst = if (container_decl.ast.arg != 0)
1593 try typeExpr(mod, &gen_scope.base, container_decl.ast.arg)
1594 else
1595 null;
1596 const has_enum_token = container_decl.ast.enum_token != null;
1597 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.UnionType, .{
1598 .fields = try arena.dupe(*zir.Inst, fields.items),
1599 }, .{
1600 .layout = layout,
1601 .has_enum_token = has_enum_token,
1602 .init_inst = init_inst,
1603 });
1604 const union_type = try arena.create(Type.Payload.Union);
1605 union_type.* = .{
1606 .analysis = .{
1607 .queued = .{
1608 .body = .{ .instructions = try arena.dupe(*zir.Inst, gen_scope.instructions.items) },
1609 .inst = inst,
1610 },
1611 },
1612 .scope = .{
1613 .file_scope = scope.getFileScope(),
1614 .ty = Type.initPayload(&union_type.base),
1615 },
1616 };
1617 break :blk Type.initPayload(&union_type.base);
1618 },
1619 .keyword_opaque => blk: {
1620 if (fields.items.len > 0) {
1621 return mod.fail(scope, fields.items[0].src, "opaque types cannot have fields", .{});
1622 }
1623 const opaque_type = try arena.create(Type.Payload.Opaque);
1624 opaque_type.* = .{
1625 .scope = .{
1626 .file_scope = scope.getFileScope(),
1627 .ty = Type.initPayload(&opaque_type.base),
1628 },
1629 };
1630 break :blk Type.initPayload(&opaque_type.base);
1631 },
1632 else => unreachable,
1633 };
1634 const val = try Value.Tag.ty.create(arena, container_type);
1635 const decl = try mod.createContainerDecl(scope, container_decl.ast.main_token, &decl_arena, .{
1636 .ty = Type.initTag(.type),
1637 .val = val,
1638 });
1639 if (rl == .ref) {
1640 return addZIRInst(mod, scope, src, zir.Inst.DeclRef, .{ .decl = decl }, .{});
1641 } else {
1642 return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclVal, .{
1643 .decl = decl,
1644 }, .{}));
1645 }
1646}
1647
1648fn errorSetDecl(
1649 mod: *Module,
1650 scope: *Scope,
1651 rl: ResultLoc,
1652 node: ast.Node.Index,
1653) InnerError!*zir.Inst {
1654 const tree = scope.tree();
1655 const main_tokens = tree.nodes.items(.main_token);
1656 const token_tags = tree.tokens.items(.tag);
1657 const token_starts = tree.tokens.items(.start);
1658
1659 // Count how many fields there are.
1660 const error_token = main_tokens[node];
1661 const count: usize = count: {
1662 var tok_i = error_token + 2;
1663 var count: usize = 0;
1664 while (true) : (tok_i += 1) {
1665 switch (token_tags[tok_i]) {
1666 .doc_comment, .comma => {},
1667 .identifier => count += 1,
1668 .r_brace => break :count count,
1669 else => unreachable,
1670 }
1671 } else unreachable; // TODO should not need else unreachable here
1672 };
1673
1674 const fields = try scope.arena().alloc([]const u8, count);
1675 {
1676 var tok_i = error_token + 2;
1677 var field_i: usize = 0;
1678 while (true) : (tok_i += 1) {
1679 switch (token_tags[tok_i]) {
1680 .doc_comment, .comma => {},
1681 .identifier => {
1682 fields[field_i] = try mod.identifierTokenString(scope, tok_i);
1683 field_i += 1;
1684 },
1685 .r_brace => break,
1686 else => unreachable,
1687 }
1688 }
1689 }
1690 const src = token_starts[error_token];
1691 const result = try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{});
1692 return rvalue(mod, scope, rl, result);
1693}
1694
1695fn orelseCatchExpr(
1696 mod: *Module,
1697 scope: *Scope,
1698 rl: ResultLoc,
1699 lhs: ast.Node.Index,
1700 op_token: ast.TokenIndex,
1701 cond_op: zir.Inst.Tag,
1702 unwrap_op: zir.Inst.Tag,
1703 unwrap_code_op: zir.Inst.Tag,
1704 rhs: ast.Node.Index,
1705 payload_token: ?ast.TokenIndex,
1706) InnerError!*zir.Inst {
1707 const tree = scope.tree();
1708 const token_starts = tree.tokens.items(.start);
1709
1710 const src = token_starts[op_token];
1711
1712 var block_scope: Scope.GenZIR = .{
1713 .parent = scope,
1714 .decl = scope.ownerDecl().?,
1715 .arena = scope.arena(),
1716 .force_comptime = scope.isComptime(),
1717 .instructions = .{},
1718 };
1719 setBlockResultLoc(&block_scope, rl);
1720 defer block_scope.instructions.deinit(mod.gpa);
1721
1722 // This could be a pointer or value depending on the `operand_rl` parameter.
1723 // We cannot use `block_scope.break_result_loc` because that has the bare
1724 // type, whereas this expression has the optional type. Later we make
1725 // up for this fact by calling rvalue on the else branch.
1726 block_scope.break_count += 1;
1727 const operand_rl = try makeOptionalTypeResultLoc(mod, &block_scope.base, src, block_scope.break_result_loc);
1728 const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
1729 const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
1730
1731 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
1732 .condition = cond,
1733 .then_body = undefined, // populated below
1734 .else_body = undefined, // populated below
1735 }, .{});
1736
1737 const block = try addZIRInstBlock(mod, scope, src, .block, .{
1738 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
1739 });
1740
1741 var then_scope: Scope.GenZIR = .{
1742 .parent = &block_scope.base,
1743 .decl = block_scope.decl,
1744 .arena = block_scope.arena,
1745 .force_comptime = block_scope.force_comptime,
1746 .instructions = .{},
1747 };
1748 defer then_scope.instructions.deinit(mod.gpa);
1749
1750 var err_val_scope: Scope.LocalVal = undefined;
1751 const then_sub_scope = blk: {
1752 const payload = payload_token orelse break :blk &then_scope.base;
1753 if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
1754 return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{});
1755 }
1756 const err_name = try mod.identifierTokenString(scope, payload);
1757 err_val_scope = .{
1758 .parent = &then_scope.base,
1759 .gen_zir = &then_scope,
1760 .name = err_name,
1761 .inst = try addZIRUnOp(mod, &then_scope.base, src, unwrap_code_op, operand),
1762 };
1763 break :blk &err_val_scope.base;
1764 };
1765
1766 block_scope.break_count += 1;
1767 const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, rhs);
1768
1769 var else_scope: Scope.GenZIR = .{
1770 .parent = &block_scope.base,
1771 .decl = block_scope.decl,
1772 .arena = block_scope.arena,
1773 .force_comptime = block_scope.force_comptime,
1774 .instructions = .{},
1775 };
1776 defer else_scope.instructions.deinit(mod.gpa);
1777
1778 // This could be a pointer or value depending on `unwrap_op`.
1779 const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand);
1780 const else_result = switch (rl) {
1781 .ref => unwrapped_payload,
1782 else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload),
1783 };
1784
1785 return finishThenElseBlock(
1786 mod,
1787 scope,
1788 rl,
1789 &block_scope,
1790 &then_scope,
1791 &else_scope,
1792 &condbr.positionals.then_body,
1793 &condbr.positionals.else_body,
1794 src,
1795 src,
1796 then_result,
1797 else_result,
1798 block,
1799 block,
1800 );
1801}
1802
1803fn finishThenElseBlock(
1804 mod: *Module,
1805 parent_scope: *Scope,
1806 rl: ResultLoc,
1807 block_scope: *Scope.GenZIR,
1808 then_scope: *Scope.GenZIR,
1809 else_scope: *Scope.GenZIR,
1810 then_body: *zir.Body,
1811 else_body: *zir.Body,
1812 then_src: usize,
1813 else_src: usize,
1814 then_result: *zir.Inst,
1815 else_result: ?*zir.Inst,
1816 main_block: *zir.Inst.Block,
1817 then_break_block: *zir.Inst.Block,
1818) InnerError!*zir.Inst {
1819 // We now have enough information to decide whether the result instruction should
1820 // be communicated via result location pointer or break instructions.
1821 const strat = rlStrategy(rl, block_scope);
1822 switch (strat.tag) {
1823 .break_void => {
1824 if (!then_result.tag.isNoReturn()) {
1825 _ = try addZirInstTag(mod, &then_scope.base, then_src, .break_void, .{
1826 .block = then_break_block,
1827 });
1828 }
1829 if (else_result) |inst| {
1830 if (!inst.tag.isNoReturn()) {
1831 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
1832 .block = main_block,
1833 });
1834 }
1835 } else {
1836 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
1837 .block = main_block,
1838 });
1839 }
1840 assert(!strat.elide_store_to_block_ptr_instructions);
1841 try copyBodyNoEliding(then_body, then_scope.*);
1842 try copyBodyNoEliding(else_body, else_scope.*);
1843 return &main_block.base;
1844 },
1845 .break_operand => {
1846 if (!then_result.tag.isNoReturn()) {
1847 _ = try addZirInstTag(mod, &then_scope.base, then_src, .@"break", .{
1848 .block = then_break_block,
1849 .operand = then_result,
1850 });
1851 }
1852 if (else_result) |inst| {
1853 if (!inst.tag.isNoReturn()) {
1854 _ = try addZirInstTag(mod, &else_scope.base, else_src, .@"break", .{
1855 .block = main_block,
1856 .operand = inst,
1857 });
1858 }
1859 } else {
1860 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
1861 .block = main_block,
1862 });
1863 }
1864 if (strat.elide_store_to_block_ptr_instructions) {
1865 try copyBodyWithElidedStoreBlockPtr(then_body, then_scope.*);
1866 try copyBodyWithElidedStoreBlockPtr(else_body, else_scope.*);
1867 } else {
1868 try copyBodyNoEliding(then_body, then_scope.*);
1869 try copyBodyNoEliding(else_body, else_scope.*);
1870 }
1871 switch (rl) {
1872 .ref => return &main_block.base,
1873 else => return rvalue(mod, parent_scope, rl, &main_block.base),
1874 }
1875 },
1876 }
1877}
1878
1879/// Return whether the identifier names of two tokens are equal. Resolves @""
1880/// tokens without allocating.
1881/// OK in theory it could do it without allocating. This implementation
1882/// allocates when the @"" form is used.
1883fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
1884 const ident_name_1 = try mod.identifierTokenString(scope, token1);
1885 const ident_name_2 = try mod.identifierTokenString(scope, token2);
1886 return mem.eql(u8, ident_name_1, ident_name_2);
1887}
1888
1889pub fn fieldAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst {
1890 const tree = scope.tree();
1891 const token_starts = tree.tokens.items(.start);
1892 const main_tokens = tree.nodes.items(.main_token);
1893 const node_datas = tree.nodes.items(.data);
1894
1895 const dot_token = main_tokens[node];
1896 const src = token_starts[dot_token];
1897 const field_ident = dot_token + 1;
1898 const field_name = try mod.identifierTokenString(scope, field_ident);
1899 if (rl == .ref) {
1900 return addZirInstTag(mod, scope, src, .field_ptr, .{
1901 .object = try expr(mod, scope, .ref, node_datas[node].lhs),
1902 .field_name = field_name,
1903 });
1904 } else {
1905 return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{
1906 .object = try expr(mod, scope, .none, node_datas[node].lhs),
1907 .field_name = field_name,
1908 }));
1909 }
1910}
1911
1912fn arrayAccess(
1913 mod: *Module,
1914 scope: *Scope,
1915 rl: ResultLoc,
1916 node: ast.Node.Index,
1917) InnerError!*zir.Inst {
1918 const tree = scope.tree();
1919 const main_tokens = tree.nodes.items(.main_token);
1920 const token_starts = tree.tokens.items(.start);
1921 const node_datas = tree.nodes.items(.data);
1922
1923 const src = token_starts[main_tokens[node]];
1924 const usize_type = try addZIRInstConst(mod, scope, src, .{
1925 .ty = Type.initTag(.type),
1926 .val = Value.initTag(.usize_type),
1927 });
1928 const index_rl: ResultLoc = .{ .ty = usize_type };
1929 switch (rl) {
1930 .ref => return addZirInstTag(mod, scope, src, .elem_ptr, .{
1931 .array = try expr(mod, scope, .ref, node_datas[node].lhs),
1932 .index = try expr(mod, scope, index_rl, node_datas[node].rhs),
1933 }),
1934 else => return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{
1935 .array = try expr(mod, scope, .none, node_datas[node].lhs),
1936 .index = try expr(mod, scope, index_rl, node_datas[node].rhs),
1937 })),
1938 }
1939}
1940
1941fn sliceExpr(
1942 mod: *Module,
1943 scope: *Scope,
1944 rl: ResultLoc,
1945 slice: ast.full.Slice,
1946) InnerError!*zir.Inst {
1947 const tree = scope.tree();
1948 const token_starts = tree.tokens.items(.start);
1949
1950 const src = token_starts[slice.ast.lbracket];
1951
1952 const usize_type = try addZIRInstConst(mod, scope, src, .{
1953 .ty = Type.initTag(.type),
1954 .val = Value.initTag(.usize_type),
1955 });
1956
1957 const array_ptr = try expr(mod, scope, .ref, slice.ast.sliced);
1958 const start = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.start);
1959
1960 if (slice.ast.sentinel == 0) {
1961 if (slice.ast.end == 0) {
1962 const result = try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);
1963 return rvalue(mod, scope, rl, result);
1964 } else {
1965 const end = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.end);
1966 // TODO a ZIR slice_open instruction
1967 const result = try addZIRInst(mod, scope, src, zir.Inst.Slice, .{
1968 .array_ptr = array_ptr,
1969 .start = start,
1970 }, .{ .end = end });
1971 return rvalue(mod, scope, rl, result);
1972 }
1973 }
1974
1975 const end = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.end);
1976 // TODO pass the proper result loc to this expression using a ZIR instruction
1977 // "get the child element type for a slice target".
1978 const sentinel = try expr(mod, scope, .none, slice.ast.sentinel);
1979 const result = try addZIRInst(mod, scope, src, zir.Inst.Slice, .{
1980 .array_ptr = array_ptr,
1981 .start = start,
1982 }, .{
1983 .end = end,
1984 .sentinel = sentinel,
1985 });
1986 return rvalue(mod, scope, rl, result);
1987}
1988
1989fn simpleBinOp(
1990 mod: *Module,
1991 scope: *Scope,
1992 rl: ResultLoc,
1993 infix_node: ast.Node.Index,
1994 op_inst_tag: zir.Inst.Tag,
1995) InnerError!*zir.Inst {
1996 const tree = scope.tree();
1997 const node_datas = tree.nodes.items(.data);
1998 const main_tokens = tree.nodes.items(.main_token);
1999 const token_starts = tree.tokens.items(.start);
2000
2001 const lhs = try expr(mod, scope, .none, node_datas[infix_node].lhs);
2002 const rhs = try expr(mod, scope, .none, node_datas[infix_node].rhs);
2003 const src = token_starts[main_tokens[infix_node]];
2004 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
2005 return rvalue(mod, scope, rl, result);
2006}
2007
2008fn boolBinOp(
2009 mod: *Module,
2010 scope: *Scope,
2011 rl: ResultLoc,
2012 infix_node: ast.Node.Index,
2013 is_bool_and: bool,
2014) InnerError!*zir.Inst {
2015 const tree = scope.tree();
2016 const node_datas = tree.nodes.items(.data);
2017 const main_tokens = tree.nodes.items(.main_token);
2018 const token_starts = tree.tokens.items(.start);
2019
2020 const src = token_starts[main_tokens[infix_node]];
2021 const bool_type = try addZIRInstConst(mod, scope, src, .{
2022 .ty = Type.initTag(.type),
2023 .val = Value.initTag(.bool_type),
2024 });
2025
2026 var block_scope: Scope.GenZIR = .{
2027 .parent = scope,
2028 .decl = scope.ownerDecl().?,
2029 .arena = scope.arena(),
2030 .force_comptime = scope.isComptime(),
2031 .instructions = .{},
2032 };
2033 defer block_scope.instructions.deinit(mod.gpa);
2034
2035 const lhs = try expr(mod, scope, .{ .ty = bool_type }, node_datas[infix_node].lhs);
2036 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
2037 .condition = lhs,
2038 .then_body = undefined, // populated below
2039 .else_body = undefined, // populated below
2040 }, .{});
2041
2042 const block = try addZIRInstBlock(mod, scope, src, .block, .{
2043 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
2044 });
2045
2046 var rhs_scope: Scope.GenZIR = .{
2047 .parent = scope,
2048 .decl = block_scope.decl,
2049 .arena = block_scope.arena,
2050 .force_comptime = block_scope.force_comptime,
2051 .instructions = .{},
2052 };
2053 defer rhs_scope.instructions.deinit(mod.gpa);
2054
2055 const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, node_datas[infix_node].rhs);
2056 _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{
2057 .block = block,
2058 .operand = rhs,
2059 }, .{});
2060
2061 var const_scope: Scope.GenZIR = .{
2062 .parent = scope,
2063 .decl = block_scope.decl,
2064 .arena = block_scope.arena,
2065 .force_comptime = block_scope.force_comptime,
2066 .instructions = .{},
2067 };
2068 defer const_scope.instructions.deinit(mod.gpa);
2069
2070 _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{
2071 .block = block,
2072 .operand = try addZIRInstConst(mod, &const_scope.base, src, .{
2073 .ty = Type.initTag(.bool),
2074 .val = if (is_bool_and) Value.initTag(.bool_false) else Value.initTag(.bool_true),
2075 }),
2076 }, .{});
2077
2078 if (is_bool_and) {
2079 // if lhs // AND
2080 // break rhs
2081 // else
2082 // break false
2083 condbr.positionals.then_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
2084 condbr.positionals.else_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
2085 } else {
2086 // if lhs // OR
2087 // break true
2088 // else
2089 // break rhs
2090 condbr.positionals.then_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
2091 condbr.positionals.else_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
2092 }
2093
2094 return rvalue(mod, scope, rl, &block.base);
2095}
2096
2097fn ifExpr(
2098 mod: *Module,
2099 scope: *Scope,
2100 rl: ResultLoc,
2101 if_full: ast.full.If,
2102) InnerError!*zir.Inst {
2103 var block_scope: Scope.GenZIR = .{
2104 .parent = scope,
2105 .decl = scope.ownerDecl().?,
2106 .arena = scope.arena(),
2107 .force_comptime = scope.isComptime(),
2108 .instructions = .{},
2109 };
2110 setBlockResultLoc(&block_scope, rl);
2111 defer block_scope.instructions.deinit(mod.gpa);
2112
2113 const tree = scope.tree();
2114 const main_tokens = tree.nodes.items(.main_token);
2115 const token_starts = tree.tokens.items(.start);
2116
2117 const if_src = token_starts[if_full.ast.if_token];
2118
2119 const cond = c: {
2120 // TODO https://github.com/ziglang/zig/issues/7929
2121 if (if_full.error_token) |error_token| {
2122 return mod.failTok(scope, error_token, "TODO implement if error union", .{});
2123 } else if (if_full.payload_token) |payload_token| {
2124 return mod.failTok(scope, payload_token, "TODO implement if optional", .{});
2125 } else {
2126 const bool_type = try addZIRInstConst(mod, &block_scope.base, if_src, .{
2127 .ty = Type.initTag(.type),
2128 .val = Value.initTag(.bool_type),
2129 });
2130 break :c try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_full.ast.cond_expr);
2131 }
2132 };
2133
2134 const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
2135 .condition = cond,
2136 .then_body = undefined, // populated below
2137 .else_body = undefined, // populated below
2138 }, .{});
2139
2140 const block = try addZIRInstBlock(mod, scope, if_src, .block, .{
2141 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
2142 });
2143
2144 const then_src = token_starts[tree.lastToken(if_full.ast.then_expr)];
2145 var then_scope: Scope.GenZIR = .{
2146 .parent = scope,
2147 .decl = block_scope.decl,
2148 .arena = block_scope.arena,
2149 .force_comptime = block_scope.force_comptime,
2150 .instructions = .{},
2151 };
2152 defer then_scope.instructions.deinit(mod.gpa);
2153
2154 // declare payload to the then_scope
2155 const then_sub_scope = &then_scope.base;
2156
2157 block_scope.break_count += 1;
2158 const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);
2159 // We hold off on the break instructions as well as copying the then/else
2160 // instructions into place until we know whether to keep store_to_block_ptr
2161 // instructions or not.
2162
2163 var else_scope: Scope.GenZIR = .{
2164 .parent = scope,
2165 .decl = block_scope.decl,
2166 .arena = block_scope.arena,
2167 .force_comptime = block_scope.force_comptime,
2168 .instructions = .{},
2169 };
2170 defer else_scope.instructions.deinit(mod.gpa);
2171
2172 const else_node = if_full.ast.else_expr;
2173 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
2174 block_scope.break_count += 1;
2175 const sub_scope = &else_scope.base;
2176 break :blk .{
2177 .src = token_starts[tree.lastToken(else_node)],
2178 .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node),
2179 };
2180 } else .{
2181 .src = token_starts[tree.lastToken(if_full.ast.then_expr)],
2182 .result = null,
2183 };
2184
2185 return finishThenElseBlock(
2186 mod,
2187 scope,
2188 rl,
2189 &block_scope,
2190 &then_scope,
2191 &else_scope,
2192 &condbr.positionals.then_body,
2193 &condbr.positionals.else_body,
2194 then_src,
2195 else_info.src,
2196 then_result,
2197 else_info.result,
2198 block,
2199 block,
2200 );
2201}
2202
2203/// Expects to find exactly 1 .store_to_block_ptr instruction.
2204fn copyBodyWithElidedStoreBlockPtr(body: *zir.Body, scope: Module.Scope.GenZIR) !void {
2205 body.* = .{
2206 .instructions = try scope.arena.alloc(*zir.Inst, scope.instructions.items.len - 1),
2207 };
2208 var dst_index: usize = 0;
2209 for (scope.instructions.items) |src_inst| {
2210 if (src_inst.tag != .store_to_block_ptr) {
2211 body.instructions[dst_index] = src_inst;
2212 dst_index += 1;
2213 }
2214 }
2215 assert(dst_index == body.instructions.len);
2216}
2217
2218fn copyBodyNoEliding(body: *zir.Body, scope: Module.Scope.GenZIR) !void {
2219 body.* = .{
2220 .instructions = try scope.arena.dupe(*zir.Inst, scope.instructions.items),
2221 };
2222}
2223
2224fn whileExpr(
2225 mod: *Module,
2226 scope: *Scope,
2227 rl: ResultLoc,
2228 while_full: ast.full.While,
2229) InnerError!*zir.Inst {
2230 if (while_full.label_token) |label_token| {
2231 try checkLabelRedefinition(mod, scope, label_token);
2232 }
2233 if (while_full.inline_token) |inline_token| {
2234 return mod.failTok(scope, inline_token, "TODO inline while", .{});
2235 }
2236
2237 var loop_scope: Scope.GenZIR = .{
2238 .parent = scope,
2239 .decl = scope.ownerDecl().?,
2240 .arena = scope.arena(),
2241 .force_comptime = scope.isComptime(),
2242 .instructions = .{},
2243 };
2244 setBlockResultLoc(&loop_scope, rl);
2245 defer loop_scope.instructions.deinit(mod.gpa);
2246
2247 var continue_scope: Scope.GenZIR = .{
2248 .parent = &loop_scope.base,
2249 .decl = loop_scope.decl,
2250 .arena = loop_scope.arena,
2251 .force_comptime = loop_scope.force_comptime,
2252 .instructions = .{},
2253 };
2254 defer continue_scope.instructions.deinit(mod.gpa);
2255
2256 const tree = scope.tree();
2257 const main_tokens = tree.nodes.items(.main_token);
2258 const token_starts = tree.tokens.items(.start);
2259
2260 const while_src = token_starts[while_full.ast.while_token];
2261 const void_type = try addZIRInstConst(mod, scope, while_src, .{
2262 .ty = Type.initTag(.type),
2263 .val = Value.initTag(.void_type),
2264 });
2265 const cond = c: {
2266 // TODO https://github.com/ziglang/zig/issues/7929
2267 if (while_full.error_token) |error_token| {
2268 return mod.failTok(scope, error_token, "TODO implement while error union", .{});
2269 } else if (while_full.payload_token) |payload_token| {
2270 return mod.failTok(scope, payload_token, "TODO implement while optional", .{});
2271 } else {
2272 const bool_type = try addZIRInstConst(mod, &continue_scope.base, while_src, .{
2273 .ty = Type.initTag(.type),
2274 .val = Value.initTag(.bool_type),
2275 });
2276 break :c try expr(mod, &continue_scope.base, .{ .ty = bool_type }, while_full.ast.cond_expr);
2277 }
2278 };
2279
2280 const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
2281 .condition = cond,
2282 .then_body = undefined, // populated below
2283 .else_body = undefined, // populated below
2284 }, .{});
2285 const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .block, .{
2286 .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),
2287 });
2288 // TODO avoid emitting the continue expr when there
2289 // are no jumps to it. This happens when the last statement of a while body is noreturn
2290 // and there are no `continue` statements.
2291 // The "repeat" at the end of a loop body is implied.
2292 if (while_full.ast.cont_expr != 0) {
2293 _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, while_full.ast.cont_expr);
2294 }
2295 const loop = try scope.arena().create(zir.Inst.Loop);
2296 loop.* = .{
2297 .base = .{
2298 .tag = .loop,
2299 .src = while_src,
2300 },
2301 .positionals = .{
2302 .body = .{
2303 .instructions = try scope.arena().dupe(*zir.Inst, loop_scope.instructions.items),
2304 },
2305 },
2306 .kw_args = .{},
2307 };
2308 const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
2309 .instructions = try scope.arena().dupe(*zir.Inst, &[1]*zir.Inst{&loop.base}),
2310 });
2311 loop_scope.break_block = while_block;
2312 loop_scope.continue_block = cond_block;
2313 if (while_full.label_token) |label_token| {
2314 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
2315 .token = label_token,
2316 .block_inst = while_block,
2317 });
2318 }
2319
2320 const then_src = token_starts[tree.lastToken(while_full.ast.then_expr)];
2321 var then_scope: Scope.GenZIR = .{
2322 .parent = &continue_scope.base,
2323 .decl = continue_scope.decl,
2324 .arena = continue_scope.arena,
2325 .force_comptime = continue_scope.force_comptime,
2326 .instructions = .{},
2327 };
2328 defer then_scope.instructions.deinit(mod.gpa);
2329
2330 const then_sub_scope = &then_scope.base;
2331
2332 loop_scope.break_count += 1;
2333 const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
2334
2335 var else_scope: Scope.GenZIR = .{
2336 .parent = &continue_scope.base,
2337 .decl = continue_scope.decl,
2338 .arena = continue_scope.arena,
2339 .force_comptime = continue_scope.force_comptime,
2340 .instructions = .{},
2341 };
2342 defer else_scope.instructions.deinit(mod.gpa);
2343
2344 const else_node = while_full.ast.else_expr;
2345 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
2346 loop_scope.break_count += 1;
2347 const sub_scope = &else_scope.base;
2348 break :blk .{
2349 .src = token_starts[tree.lastToken(else_node)],
2350 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
2351 };
2352 } else .{
2353 .src = token_starts[tree.lastToken(while_full.ast.then_expr)],
2354 .result = null,
2355 };
2356
2357 if (loop_scope.label) |some| {
2358 if (!some.used) {
2359 return mod.fail(scope, token_starts[some.token], "unused while loop label", .{});
2360 }
2361 }
2362 return finishThenElseBlock(
2363 mod,
2364 scope,
2365 rl,
2366 &loop_scope,
2367 &then_scope,
2368 &else_scope,
2369 &condbr.positionals.then_body,
2370 &condbr.positionals.else_body,
2371 then_src,
2372 else_info.src,
2373 then_result,
2374 else_info.result,
2375 while_block,
2376 cond_block,
2377 );
2378}
2379
2380fn forExpr(
2381 mod: *Module,
2382 scope: *Scope,
2383 rl: ResultLoc,
2384 for_full: ast.full.While,
2385) InnerError!*zir.Inst {
2386 if (for_full.label_token) |label_token| {
2387 try checkLabelRedefinition(mod, scope, label_token);
2388 }
2389
2390 if (for_full.inline_token) |inline_token| {
2391 return mod.failTok(scope, inline_token, "TODO inline for", .{});
2392 }
2393
2394 // Set up variables and constants.
2395 const tree = scope.tree();
2396 const main_tokens = tree.nodes.items(.main_token);
2397 const token_starts = tree.tokens.items(.start);
2398 const token_tags = tree.tokens.items(.tag);
2399
2400 const for_src = token_starts[for_full.ast.while_token];
2401 const index_ptr = blk: {
2402 const usize_type = try addZIRInstConst(mod, scope, for_src, .{
2403 .ty = Type.initTag(.type),
2404 .val = Value.initTag(.usize_type),
2405 });
2406 const index_ptr = try addZIRUnOp(mod, scope, for_src, .alloc, usize_type);
2407 // initialize to zero
2408 const zero = try addZIRInstConst(mod, scope, for_src, .{
2409 .ty = Type.initTag(.usize),
2410 .val = Value.initTag(.zero),
2411 });
2412 _ = try addZIRBinOp(mod, scope, for_src, .store, index_ptr, zero);
2413 break :blk index_ptr;
2414 };
2415 const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr);
2416 const cond_src = token_starts[tree.firstToken(for_full.ast.cond_expr)];
2417 const len = try addZIRUnOp(mod, scope, cond_src, .indexable_ptr_len, array_ptr);
2418
2419 var loop_scope: Scope.GenZIR = .{
2420 .parent = scope,
2421 .decl = scope.ownerDecl().?,
2422 .arena = scope.arena(),
2423 .force_comptime = scope.isComptime(),
2424 .instructions = .{},
2425 };
2426 setBlockResultLoc(&loop_scope, rl);
2427 defer loop_scope.instructions.deinit(mod.gpa);
2428
2429 var cond_scope: Scope.GenZIR = .{
2430 .parent = &loop_scope.base,
2431 .decl = loop_scope.decl,
2432 .arena = loop_scope.arena,
2433 .force_comptime = loop_scope.force_comptime,
2434 .instructions = .{},
2435 };
2436 defer cond_scope.instructions.deinit(mod.gpa);
2437
2438 // check condition i < array_expr.len
2439 const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
2440 const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
2441
2442 const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
2443 .condition = cond,
2444 .then_body = undefined, // populated below
2445 .else_body = undefined, // populated below
2446 }, .{});
2447 const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .block, .{
2448 .instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),
2449 });
2450
2451 // increment index variable
2452 const one = try addZIRInstConst(mod, &loop_scope.base, for_src, .{
2453 .ty = Type.initTag(.usize),
2454 .val = Value.initTag(.one),
2455 });
2456 const index_2 = try addZIRUnOp(mod, &loop_scope.base, cond_src, .deref, index_ptr);
2457 const index_plus_one = try addZIRBinOp(mod, &loop_scope.base, for_src, .add, index_2, one);
2458 _ = try addZIRBinOp(mod, &loop_scope.base, for_src, .store, index_ptr, index_plus_one);
2459
2460 const loop = try scope.arena().create(zir.Inst.Loop);
2461 loop.* = .{
2462 .base = .{
2463 .tag = .loop,
2464 .src = for_src,
2465 },
2466 .positionals = .{
2467 .body = .{
2468 .instructions = try scope.arena().dupe(*zir.Inst, loop_scope.instructions.items),
2469 },
2470 },
2471 .kw_args = .{},
2472 };
2473 const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
2474 .instructions = try scope.arena().dupe(*zir.Inst, &[1]*zir.Inst{&loop.base}),
2475 });
2476 loop_scope.break_block = for_block;
2477 loop_scope.continue_block = cond_block;
2478 if (for_full.label_token) |label_token| {
2479 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
2480 .token = label_token,
2481 .block_inst = for_block,
2482 });
2483 }
2484
2485 // while body
2486 const then_src = token_starts[tree.lastToken(for_full.ast.then_expr)];
2487 var then_scope: Scope.GenZIR = .{
2488 .parent = &cond_scope.base,
2489 .decl = cond_scope.decl,
2490 .arena = cond_scope.arena,
2491 .force_comptime = cond_scope.force_comptime,
2492 .instructions = .{},
2493 };
2494 defer then_scope.instructions.deinit(mod.gpa);
2495
2496 var index_scope: Scope.LocalPtr = undefined;
2497 const then_sub_scope = blk: {
2498 const payload_token = for_full.payload_token.?;
2499 const ident = if (token_tags[payload_token] == .asterisk)
2500 payload_token + 1
2501 else
2502 payload_token;
2503 const is_ptr = ident != payload_token;
2504 const value_name = tree.tokenSlice(ident);
2505 if (!mem.eql(u8, value_name, "_")) {
2506 return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{});
2507 } else if (is_ptr) {
2508 return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{});
2509 }
2510
2511 const index_token = if (token_tags[ident + 1] == .comma)
2512 ident + 2
2513 else
2514 break :blk &then_scope.base;
2515 if (mem.eql(u8, tree.tokenSlice(index_token), "_")) {
2516 return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{});
2517 }
2518 const index_name = try mod.identifierTokenString(&then_scope.base, index_token);
2519 index_scope = .{
2520 .parent = &then_scope.base,
2521 .gen_zir = &then_scope,
2522 .name = index_name,
2523 .ptr = index_ptr,
2524 };
2525 break :blk &index_scope.base;
2526 };
2527
2528 loop_scope.break_count += 1;
2529 const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
2530
2531 // else branch
2532 var else_scope: Scope.GenZIR = .{
2533 .parent = &cond_scope.base,
2534 .decl = cond_scope.decl,
2535 .arena = cond_scope.arena,
2536 .force_comptime = cond_scope.force_comptime,
2537 .instructions = .{},
2538 };
2539 defer else_scope.instructions.deinit(mod.gpa);
2540
2541 const else_node = for_full.ast.else_expr;
2542 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
2543 loop_scope.break_count += 1;
2544 const sub_scope = &else_scope.base;
2545 break :blk .{
2546 .src = token_starts[tree.lastToken(else_node)],
2547 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
2548 };
2549 } else .{
2550 .src = token_starts[tree.lastToken(for_full.ast.then_expr)],
2551 .result = null,
2552 };
2553
2554 if (loop_scope.label) |some| {
2555 if (!some.used) {
2556 return mod.fail(scope, token_starts[some.token], "unused for loop label", .{});
2557 }
2558 }
2559 return finishThenElseBlock(
2560 mod,
2561 scope,
2562 rl,
2563 &loop_scope,
2564 &then_scope,
2565 &else_scope,
2566 &condbr.positionals.then_body,
2567 &condbr.positionals.else_body,
2568 then_src,
2569 else_info.src,
2570 then_result,
2571 else_info.result,
2572 for_block,
2573 cond_block,
2574 );
2575}
2576
2577fn getRangeNode(
2578 node_tags: []const ast.Node.Tag,
2579 node_datas: []const ast.Node.Data,
2580 start_node: ast.Node.Index,
2581) ?ast.Node.Index {
2582 var node = start_node;
2583 while (true) {
2584 switch (node_tags[node]) {
2585 .switch_range => return node,
2586 .grouped_expression => node = node_datas[node].lhs,
2587 else => return null,
2588 }
2589 }
2590}
2591
2592fn switchExpr(
2593 mod: *Module,
2594 scope: *Scope,
2595 rl: ResultLoc,
2596 switch_node: ast.Node.Index,
2597) InnerError!*zir.Inst {
2598 const tree = scope.tree();
2599 const node_datas = tree.nodes.items(.data);
2600 const main_tokens = tree.nodes.items(.main_token);
2601 const token_tags = tree.tokens.items(.tag);
2602 const token_starts = tree.tokens.items(.start);
2603 const node_tags = tree.nodes.items(.tag);
2604
2605 const switch_token = main_tokens[switch_node];
2606 const target_node = node_datas[switch_node].lhs;
2607 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2608 const case_nodes = tree.extra_data[extra.start..extra.end];
2609
2610 const switch_src = token_starts[switch_token];
2611
2612 var block_scope: Scope.GenZIR = .{
2613 .parent = scope,
2614 .decl = scope.ownerDecl().?,
2615 .arena = scope.arena(),
2616 .force_comptime = scope.isComptime(),
2617 .instructions = .{},
2618 };
2619 setBlockResultLoc(&block_scope, rl);
2620 defer block_scope.instructions.deinit(mod.gpa);
2621
2622 var items = std.ArrayList(*zir.Inst).init(mod.gpa);
2623 defer items.deinit();
2624
2625 // First we gather all the switch items and check else/'_' prongs.
2626 var else_src: ?usize = null;
2627 var underscore_src: ?usize = null;
2628 var first_range: ?*zir.Inst = null;
2629 var simple_case_count: usize = 0;
2630 var any_payload_is_ref = false;
2631 for (case_nodes) |case_node| {
2632 const case = switch (node_tags[case_node]) {
2633 .switch_case_one => tree.switchCaseOne(case_node),
2634 .switch_case => tree.switchCase(case_node),
2635 else => unreachable,
2636 };
2637 if (case.payload_token) |payload_token| {
2638 if (token_tags[payload_token] == .asterisk) {
2639 any_payload_is_ref = true;
2640 }
2641 }
2642 // Check for else/_ prong, those are handled last.
2643 if (case.ast.values.len == 0) {
2644 const case_src = token_starts[case.ast.arrow_token - 1];
2645 if (else_src) |src| {
2646 const msg = msg: {
2647 const msg = try mod.errMsg(
2648 scope,
2649 case_src,
2650 "multiple else prongs in switch expression",
2651 .{},
2652 );
2653 errdefer msg.destroy(mod.gpa);
2654 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2655 break :msg msg;
2656 };
2657 return mod.failWithOwnedErrorMsg(scope, msg);
2658 }
2659 else_src = case_src;
2660 continue;
2661 } else if (case.ast.values.len == 1 and
2662 node_tags[case.ast.values[0]] == .identifier and
2663 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2664 {
2665 const case_src = token_starts[case.ast.arrow_token - 1];
2666 if (underscore_src) |src| {
2667 const msg = msg: {
2668 const msg = try mod.errMsg(
2669 scope,
2670 case_src,
2671 "multiple '_' prongs in switch expression",
2672 .{},
2673 );
2674 errdefer msg.destroy(mod.gpa);
2675 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2676 break :msg msg;
2677 };
2678 return mod.failWithOwnedErrorMsg(scope, msg);
2679 }
2680 underscore_src = case_src;
2681 continue;
2682 }
2683
2684 if (else_src) |some_else| {
2685 if (underscore_src) |some_underscore| {
2686 const msg = msg: {
2687 const msg = try mod.errMsg(
2688 scope,
2689 switch_src,
2690 "else and '_' prong in switch expression",
2691 .{},
2692 );
2693 errdefer msg.destroy(mod.gpa);
2694 try mod.errNote(scope, some_else, msg, "else prong is here", .{});
2695 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
2696 break :msg msg;
2697 };
2698 return mod.failWithOwnedErrorMsg(scope, msg);
2699 }
2700 }
2701
2702 if (case.ast.values.len == 1 and
2703 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2704 {
2705 simple_case_count += 1;
2706 }
2707
2708 // Generate all the switch items as comptime expressions.
2709 for (case.ast.values) |item| {
2710 if (getRangeNode(node_tags, node_datas, item)) |range| {
2711 const start = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].lhs);
2712 const end = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].rhs);
2713 const range_src = token_starts[main_tokens[range]];
2714 const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end);
2715 try items.append(range_inst);
2716 } else {
2717 const item_inst = try comptimeExpr(mod, &block_scope.base, .none, item);
2718 try items.append(item_inst);
2719 }
2720 }
2721 }
2722
2723 var special_prong: zir.Inst.SwitchBr.SpecialProng = .none;
2724 if (else_src != null) special_prong = .@"else";
2725 if (underscore_src != null) special_prong = .underscore;
2726 var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count);
2727
2728 const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref)
2729 .{
2730 .rl = .ref,
2731 .tag = .switchbr_ref,
2732 }
2733 else
2734 .{
2735 .rl = .none,
2736 .tag = .switchbr,
2737 };
2738 const target = try expr(mod, &block_scope.base, rl_and_tag.rl, target_node);
2739 const switch_inst = try addZirInstT(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, rl_and_tag.tag, .{
2740 .target = target,
2741 .cases = cases,
2742 .items = try block_scope.arena.dupe(*zir.Inst, items.items),
2743 .else_body = undefined, // populated below
2744 .range = first_range,
2745 .special_prong = special_prong,
2746 });
2747 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
2748 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
2749 });
2750
2751 var case_scope: Scope.GenZIR = .{
2752 .parent = scope,
2753 .decl = block_scope.decl,
2754 .arena = block_scope.arena,
2755 .force_comptime = block_scope.force_comptime,
2756 .instructions = .{},
2757 };
2758 defer case_scope.instructions.deinit(mod.gpa);
2759
2760 var else_scope: Scope.GenZIR = .{
2761 .parent = scope,
2762 .decl = case_scope.decl,
2763 .arena = case_scope.arena,
2764 .force_comptime = case_scope.force_comptime,
2765 .instructions = .{},
2766 };
2767 defer else_scope.instructions.deinit(mod.gpa);
2768
2769 // Now generate all but the special cases.
2770 var special_case: ?ast.full.SwitchCase = null;
2771 var items_index: usize = 0;
2772 var case_index: usize = 0;
2773 for (case_nodes) |case_node| {
2774 const case = switch (node_tags[case_node]) {
2775 .switch_case_one => tree.switchCaseOne(case_node),
2776 .switch_case => tree.switchCase(case_node),
2777 else => unreachable,
2778 };
2779 const case_src = token_starts[main_tokens[case_node]];
2780 case_scope.instructions.shrinkRetainingCapacity(0);
2781
2782 // Check for else/_ prong, those are handled last.
2783 if (case.ast.values.len == 0) {
2784 special_case = case;
2785 continue;
2786 } else if (case.ast.values.len == 1 and
2787 node_tags[case.ast.values[0]] == .identifier and
2788 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2789 {
2790 special_case = case;
2791 continue;
2792 }
2793
2794 // If this is a simple one item prong then it is handled by the switchbr.
2795 if (case.ast.values.len == 1 and
2796 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2797 {
2798 const item = items.items[items_index];
2799 items_index += 1;
2800 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target);
2801
2802 cases[case_index] = .{
2803 .item = item,
2804 .body = .{ .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items) },
2805 };
2806 case_index += 1;
2807 continue;
2808 }
2809
2810 // Check if the target matches any of the items.
2811 // 1, 2, 3..6 will result in
2812 // target == 1 or target == 2 or (target >= 3 and target <= 6)
2813 // TODO handle multiple items as switch prongs rather than along with ranges.
2814 var any_ok: ?*zir.Inst = null;
2815 for (case.ast.values) |item| {
2816 if (getRangeNode(node_tags, node_datas, item)) |range| {
2817 const range_src = token_starts[main_tokens[range]];
2818 const range_inst = items.items[items_index].castTag(.switch_range).?;
2819 items_index += 1;
2820
2821 // target >= start and target <= end
2822 const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, range_inst.positionals.lhs);
2823 const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, range_inst.positionals.rhs);
2824 const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok);
2825
2826 if (any_ok) |some| {
2827 any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok);
2828 } else {
2829 any_ok = range_ok;
2830 }
2831 continue;
2832 }
2833
2834 const item_inst = items.items[items_index];
2835 items_index += 1;
2836 const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst);
2837
2838 if (any_ok) |some| {
2839 any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok);
2840 } else {
2841 any_ok = cpm_ok;
2842 }
2843 }
2844
2845 const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{
2846 .condition = any_ok.?,
2847 .then_body = undefined, // populated below
2848 .else_body = undefined, // populated below
2849 }, .{});
2850 const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{
2851 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
2852 });
2853
2854 // reset cond_scope for then_body
2855 case_scope.instructions.items.len = 0;
2856 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target);
2857 condbr.positionals.then_body = .{
2858 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
2859 };
2860
2861 // reset cond_scope for else_body
2862 case_scope.instructions.items.len = 0;
2863 _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{
2864 .block = cond_block,
2865 }, .{});
2866 condbr.positionals.else_body = .{
2867 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
2868 };
2869 }
2870
2871 // Finally generate else block or a break.
2872 if (special_case) |case| {
2873 try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target);
2874 } else {
2875 // Not handling all possible cases is a compile error.
2876 _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe);
2877 }
2878 switch_inst.positionals.else_body = .{
2879 .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
2880 };
2881
2882 return &block.base;
2883}
2884
2885fn switchCaseExpr(
2886 mod: *Module,
2887 scope: *Scope,
2888 rl: ResultLoc,
2889 block: *zir.Inst.Block,
2890 case: ast.full.SwitchCase,
2891 target: *zir.Inst,
2892) !void {
2893 const tree = scope.tree();
2894 const node_datas = tree.nodes.items(.data);
2895 const main_tokens = tree.nodes.items(.main_token);
2896 const token_starts = tree.tokens.items(.start);
2897 const token_tags = tree.tokens.items(.tag);
2898
2899 const case_src = token_starts[case.ast.arrow_token];
2900 const sub_scope = blk: {
2901 const payload_token = case.payload_token orelse break :blk scope;
2902 const ident = if (token_tags[payload_token] == .asterisk)
2903 payload_token + 1
2904 else
2905 payload_token;
2906 const is_ptr = ident != payload_token;
2907 const value_name = tree.tokenSlice(ident);
2908 if (mem.eql(u8, value_name, "_")) {
2909 if (is_ptr) {
2910 return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{});
2911 }
2912 break :blk scope;
2913 }
2914 return mod.failTok(scope, ident, "TODO implement switch value payload", .{});
2915 };
2916
2917 const case_body = try expr(mod, sub_scope, rl, case.ast.target_expr);
2918 if (!case_body.tag.isNoReturn()) {
2919 _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{
2920 .block = block,
2921 .operand = case_body,
2922 }, .{});
2923 }
2924}
2925
2926fn ret(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
2927 const tree = scope.tree();
2928 const node_datas = tree.nodes.items(.data);
2929 const main_tokens = tree.nodes.items(.main_token);
2930 const token_starts = tree.tokens.items(.start);
2931
2932 const src = token_starts[main_tokens[node]];
2933 const rhs_node = node_datas[node].lhs;
2934 if (rhs_node != 0) {
2935 if (nodeMayNeedMemoryLocation(scope, rhs_node)) {
2936 const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
2937 const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
2938 return addZIRUnOp(mod, scope, src, .@"return", operand);
2939 } else {
2940 const fn_ret_ty = try addZIRNoOp(mod, scope, src, .ret_type);
2941 const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node);
2942 return addZIRUnOp(mod, scope, src, .@"return", operand);
2943 }
2944 } else {
2945 return addZIRNoOp(mod, scope, src, .return_void);
2946 }
2947}
2948
2949fn identifier(
2950 mod: *Module,
2951 scope: *Scope,
2952 rl: ResultLoc,
2953 ident: ast.Node.Index,
2954) InnerError!*zir.Inst {
2955 const tracy = trace(@src());
2956 defer tracy.end();
2957
2958 const tree = scope.tree();
2959 const main_tokens = tree.nodes.items(.main_token);
2960 const token_starts = tree.tokens.items(.start);
2961
2962 const ident_token = main_tokens[ident];
2963 const ident_name = try mod.identifierTokenString(scope, ident_token);
2964 const src = token_starts[ident_token];
2965 if (mem.eql(u8, ident_name, "_")) {
2966 return mod.failNode(scope, ident, "TODO implement '_' identifier", .{});
2967 }
2968
2969 if (simple_types.get(ident_name)) |val_tag| {
2970 const result = try addZIRInstConst(mod, scope, src, TypedValue{
2971 .ty = Type.initTag(.type),
2972 .val = Value.initTag(val_tag),
2973 });
2974 return rvalue(mod, scope, rl, result);
2975 }
2976
2977 if (ident_name.len >= 2) integer: {
2978 const first_c = ident_name[0];
2979 if (first_c == 'i' or first_c == 'u') {
2980 const is_signed = first_c == 'i';
2981 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
2982 error.Overflow => return mod.failNode(
2983 scope,
2984 ident,
2985 "primitive integer type '{s}' exceeds maximum bit width of 65535",
2986 .{ident_name},
2987 ),
2988 error.InvalidCharacter => break :integer,
2989 };
2990 const val = switch (bit_count) {
2991 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
2992 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
2993 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
2994 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
2995 else => {
2996 return rvalue(mod, scope, rl, try addZIRInstConst(mod, scope, src, .{
2997 .ty = Type.initTag(.type),
2998 .val = try Value.Tag.int_type.create(scope.arena(), .{
2999 .signed = is_signed,
3000 .bits = bit_count,
3001 }),
3002 }));
3003 },
3004 };
3005 const result = try addZIRInstConst(mod, scope, src, .{
3006 .ty = Type.initTag(.type),
3007 .val = val,
3008 });
3009 return rvalue(mod, scope, rl, result);
3010 }
3011 }
3012
3013 // Local variables, including function parameters.
3014 {
3015 var s = scope;
3016 while (true) switch (s.tag) {
3017 .local_val => {
3018 const local_val = s.cast(Scope.LocalVal).?;
3019 if (mem.eql(u8, local_val.name, ident_name)) {
3020 return rvalue(mod, scope, rl, local_val.inst);
3021 }
3022 s = local_val.parent;
3023 },
3024 .local_ptr => {
3025 const local_ptr = s.cast(Scope.LocalPtr).?;
3026 if (mem.eql(u8, local_ptr.name, ident_name)) {
3027 if (rl == .ref) return local_ptr.ptr;
3028 const loaded = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);
3029 return rvalue(mod, scope, rl, loaded);
3030 }
3031 s = local_ptr.parent;
3032 },
3033 .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
3034 .gen_suspend => s = s.cast(Scope.GenZIR).?.parent,
3035 .gen_nosuspend => s = s.cast(Scope.Nosuspend).?.parent,
3036 else => break,
3037 };
3038 }
3039
3040 if (mod.lookupDeclName(scope, ident_name)) |decl| {
3041 if (rl == .ref) {
3042 return addZIRInst(mod, scope, src, zir.Inst.DeclRef, .{ .decl = decl }, .{});
3043 } else {
3044 return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclVal, .{
3045 .decl = decl,
3046 }, .{}));
3047 }
3048 }
3049
3050 return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name});
3051}
3052
3053fn parseStringLiteral(mod: *Module, scope: *Scope, token: ast.TokenIndex) ![]u8 {
3054 const tree = scope.tree();
3055 const token_tags = tree.tokens.items(.tag);
3056 const token_starts = tree.tokens.items(.start);
3057 assert(token_tags[token] == .string_literal);
3058 const unparsed = tree.tokenSlice(token);
3059 const arena = scope.arena();
3060 var bad_index: usize = undefined;
3061 const bytes = std.zig.parseStringLiteral(arena, unparsed, &bad_index) catch |err| switch (err) {
3062 error.InvalidCharacter => {
3063 const bad_byte = unparsed[bad_index];
3064 const src = token_starts[token];
3065 return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'", .{
3066 bad_byte,
3067 });
3068 },
3069 else => |e| return e,
3070 };
3071 return bytes;
3072}
3073
3074fn stringLiteral(
3075 mod: *Module,
3076 scope: *Scope,
3077 rl: ResultLoc,
3078 str_lit: ast.Node.Index,
3079) InnerError!*zir.Inst {
3080 const tree = scope.tree();
3081 const main_tokens = tree.nodes.items(.main_token);
3082 const token_starts = tree.tokens.items(.start);
3083
3084 const str_lit_token = main_tokens[str_lit];
3085 const bytes = try parseStringLiteral(mod, scope, str_lit_token);
3086 const src = token_starts[str_lit_token];
3087 const str_inst = try addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
3088 return rvalue(mod, scope, rl, str_inst);
3089}
3090
3091fn multilineStringLiteral(
3092 mod: *Module,
3093 scope: *Scope,
3094 rl: ResultLoc,
3095 str_lit: ast.Node.Index,
3096) InnerError!*zir.Inst {
3097 const tree = scope.tree();
3098 const node_datas = tree.nodes.items(.data);
3099 const main_tokens = tree.nodes.items(.main_token);
3100 const token_starts = tree.tokens.items(.start);
3101
3102 const start = node_datas[str_lit].lhs;
3103 const end = node_datas[str_lit].rhs;
3104
3105 // Count the number of bytes to allocate.
3106 const len: usize = len: {
3107 var tok_i = start;
3108 var len: usize = end - start + 1;
3109 while (tok_i <= end) : (tok_i += 1) {
3110 // 2 for the '//' + 1 for '\n'
3111 len += tree.tokenSlice(tok_i).len - 3;
3112 }
3113 break :len len;
3114 };
3115 const bytes = try scope.arena().alloc(u8, len);
3116 // First line: do not append a newline.
3117 var byte_i: usize = 0;
3118 var tok_i = start;
3119 {
3120 const slice = tree.tokenSlice(tok_i);
3121 const line_bytes = slice[2 .. slice.len - 1];
3122 mem.copy(u8, bytes[byte_i..], line_bytes);
3123 byte_i += line_bytes.len;
3124 tok_i += 1;
3125 }
3126 // Following lines: each line prepends a newline.
3127 while (tok_i <= end) : (tok_i += 1) {
3128 bytes[byte_i] = '\n';
3129 byte_i += 1;
3130 const slice = tree.tokenSlice(tok_i);
3131 const line_bytes = slice[2 .. slice.len - 1];
3132 mem.copy(u8, bytes[byte_i..], line_bytes);
3133 byte_i += line_bytes.len;
3134 }
3135 const src = token_starts[start];
3136 const str_inst = try addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
3137 return rvalue(mod, scope, rl, str_inst);
3138}
3139
3140fn charLiteral(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst {
3141 const tree = scope.tree();
3142 const main_tokens = tree.nodes.items(.main_token);
3143 const main_token = main_tokens[node];
3144 const token_starts = tree.tokens.items(.start);
3145
3146 const src = token_starts[main_token];
3147 const slice = tree.tokenSlice(main_token);
3148
3149 var bad_index: usize = undefined;
3150 const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
3151 error.InvalidCharacter => {
3152 const bad_byte = slice[bad_index];
3153 return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte});
3154 },
3155 };
3156 const result = try addZIRInstConst(mod, scope, src, .{
3157 .ty = Type.initTag(.comptime_int),
3158 .val = try Value.Tag.int_u64.create(scope.arena(), value),
3159 });
3160 return rvalue(mod, scope, rl, result);
3161}
3162
3163fn integerLiteral(
3164 mod: *Module,
3165 scope: *Scope,
3166 rl: ResultLoc,
3167 int_lit: ast.Node.Index,
3168) InnerError!*zir.Inst {
3169 const arena = scope.arena();
3170 const tree = scope.tree();
3171 const main_tokens = tree.nodes.items(.main_token);
3172 const token_starts = tree.tokens.items(.start);
3173
3174 const int_token = main_tokens[int_lit];
3175 const prefixed_bytes = tree.tokenSlice(int_token);
3176 const base: u8 = if (mem.startsWith(u8, prefixed_bytes, "0x"))
3177 16
3178 else if (mem.startsWith(u8, prefixed_bytes, "0o"))
3179 8
3180 else if (mem.startsWith(u8, prefixed_bytes, "0b"))
3181 2
3182 else
3183 @as(u8, 10);
3184
3185 const bytes = if (base == 10)
3186 prefixed_bytes
3187 else
3188 prefixed_bytes[2..];
3189
3190 if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
3191 const src = token_starts[int_token];
3192 const result = try addZIRInstConst(mod, scope, src, .{
3193 .ty = Type.initTag(.comptime_int),
3194 .val = try Value.Tag.int_u64.create(arena, small_int),
3195 });
3196 return rvalue(mod, scope, rl, result);
3197 } else |err| {
3198 return mod.failTok(scope, int_token, "TODO implement int literals that don't fit in a u64", .{});
3199 }
3200}
3201
3202fn floatLiteral(
3203 mod: *Module,
3204 scope: *Scope,
3205 rl: ResultLoc,
3206 float_lit: ast.Node.Index,
3207) InnerError!*zir.Inst {
3208 const arena = scope.arena();
3209 const tree = scope.tree();
3210 const main_tokens = tree.nodes.items(.main_token);
3211 const token_starts = tree.tokens.items(.start);
3212
3213 const main_token = main_tokens[float_lit];
3214 const bytes = tree.tokenSlice(main_token);
3215 if (bytes.len > 2 and bytes[1] == 'x') {
3216 return mod.failTok(scope, main_token, "TODO implement hex floats", .{});
3217 }
3218 const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
3219 error.InvalidCharacter => unreachable, // validated by tokenizer
3220 };
3221 const src = token_starts[main_token];
3222 const result = try addZIRInstConst(mod, scope, src, .{
3223 .ty = Type.initTag(.comptime_float),
3224 .val = try Value.Tag.float_128.create(arena, float_number),
3225 });
3226 return rvalue(mod, scope, rl, result);
3227}
3228
3229fn asmExpr(mod: *Module, scope: *Scope, rl: ResultLoc, full: ast.full.Asm) InnerError!*zir.Inst {
3230 const arena = scope.arena();
3231 const tree = scope.tree();
3232 const main_tokens = tree.nodes.items(.main_token);
3233 const token_starts = tree.tokens.items(.start);
3234 const node_datas = tree.nodes.items(.data);
3235
3236 if (full.outputs.len != 0) {
3237 return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{});
3238 }
3239
3240 const inputs = try arena.alloc([]const u8, full.inputs.len);
3241 const args = try arena.alloc(*zir.Inst, full.inputs.len);
3242
3243 const src = token_starts[full.ast.asm_token];
3244 const str_type = try addZIRInstConst(mod, scope, src, .{
3245 .ty = Type.initTag(.type),
3246 .val = Value.initTag(.const_slice_u8_type),
3247 });
3248 const str_type_rl: ResultLoc = .{ .ty = str_type };
3249
3250 for (full.inputs) |input, i| {
3251 // TODO semantically analyze constraints
3252 const constraint_token = main_tokens[input] + 2;
3253 inputs[i] = try parseStringLiteral(mod, scope, constraint_token);
3254 args[i] = try expr(mod, scope, .none, node_datas[input].lhs);
3255 }
3256
3257 const return_type = try addZIRInstConst(mod, scope, src, .{
3258 .ty = Type.initTag(.type),
3259 .val = Value.initTag(.void_type),
3260 });
3261 const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
3262 .asm_source = try expr(mod, scope, str_type_rl, full.ast.template),
3263 .return_type = return_type,
3264 }, .{
3265 .@"volatile" = full.volatile_token != null,
3266 //.clobbers = TODO handle clobbers
3267 .inputs = inputs,
3268 .args = args,
3269 });
3270 return rvalue(mod, scope, rl, asm_inst);
3271}
3272
3273fn as(
3274 mod: *Module,
3275 scope: *Scope,
3276 rl: ResultLoc,
3277 builtin_token: ast.TokenIndex,
3278 src: usize,
3279 lhs: ast.Node.Index,
3280 rhs: ast.Node.Index,
3281) InnerError!*zir.Inst {
3282 const dest_type = try typeExpr(mod, scope, lhs);
3283 switch (rl) {
3284 .none, .discard, .ref, .ty => {
3285 const result = try expr(mod, scope, .{ .ty = dest_type }, rhs);
3286 return rvalue(mod, scope, rl, result);
3287 },
3288
3289 .ptr => |result_ptr| {
3290 return asRlPtr(mod, scope, rl, src, result_ptr, rhs, dest_type);
3291 },
3292 .block_ptr => |block_scope| {
3293 return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, rhs, dest_type);
3294 },
3295
3296 .bitcasted_ptr => |bitcasted_ptr| {
3297 // TODO here we should be able to resolve the inference; we now have a type for the result.
3298 return mod.failTok(scope, builtin_token, "TODO implement @as with result location @bitCast", .{});
3299 },
3300 .inferred_ptr => |result_alloc| {
3301 // TODO here we should be able to resolve the inference; we now have a type for the result.
3302 return mod.failTok(scope, builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
3303 },
3304 }
3305}
3306
3307fn asRlPtr(
3308 mod: *Module,
3309 scope: *Scope,
3310 rl: ResultLoc,
3311 src: usize,
3312 result_ptr: *zir.Inst,
3313 operand_node: ast.Node.Index,
3314 dest_type: *zir.Inst,
3315) InnerError!*zir.Inst {
3316 // Detect whether this expr() call goes into rvalue() to store the result into the
3317 // result location. If it does, elide the coerce_result_ptr instruction
3318 // as well as the store instruction, instead passing the result as an rvalue.
3319 var as_scope: Scope.GenZIR = .{
3320 .parent = scope,
3321 .decl = scope.ownerDecl().?,
3322 .arena = scope.arena(),
3323 .force_comptime = scope.isComptime(),
3324 .instructions = .{},
3325 };
3326 defer as_scope.instructions.deinit(mod.gpa);
3327
3328 as_scope.rl_ptr = try addZIRBinOp(mod, &as_scope.base, src, .coerce_result_ptr, dest_type, result_ptr);
3329 const result = try expr(mod, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);
3330 const parent_zir = &scope.getGenZIR().instructions;
3331 if (as_scope.rvalue_rl_count == 1) {
3332 // Busted! This expression didn't actually need a pointer.
3333 const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2;
3334 try parent_zir.ensureCapacity(mod.gpa, expected_len);
3335 for (as_scope.instructions.items) |src_inst| {
3336 if (src_inst == as_scope.rl_ptr.?) continue;
3337 if (src_inst.castTag(.store_to_block_ptr)) |store| {
3338 if (store.positionals.lhs == as_scope.rl_ptr.?) continue;
3339 }
3340 parent_zir.appendAssumeCapacity(src_inst);
3341 }
3342 assert(parent_zir.items.len == expected_len);
3343 const casted_result = try addZIRBinOp(mod, scope, dest_type.src, .as, dest_type, result);
3344 return rvalue(mod, scope, rl, casted_result);
3345 } else {
3346 try parent_zir.appendSlice(mod.gpa, as_scope.instructions.items);
3347 return result;
3348 }
3349}
3350
3351fn bitCast(
3352 mod: *Module,
3353 scope: *Scope,
3354 rl: ResultLoc,
3355 builtin_token: ast.TokenIndex,
3356 src: usize,
3357 lhs: ast.Node.Index,
3358 rhs: ast.Node.Index,
3359) InnerError!*zir.Inst {
3360 const dest_type = try typeExpr(mod, scope, lhs);
3361 switch (rl) {
3362 .none => {
3363 const operand = try expr(mod, scope, .none, rhs);
3364 return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
3365 },
3366 .discard => {
3367 const operand = try expr(mod, scope, .none, rhs);
3368 const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
3369 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
3370 return result;
3371 },
3372 .ref => {
3373 const operand = try expr(mod, scope, .ref, rhs);
3374 const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
3375 return result;
3376 },
3377 .ty => |result_ty| {
3378 const result = try expr(mod, scope, .none, rhs);
3379 const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
3380 return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
3381 },
3382 .ptr => |result_ptr| {
3383 const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
3384 return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, rhs);
3385 },
3386 .bitcasted_ptr => |bitcasted_ptr| {
3387 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
3388 },
3389 .block_ptr => |block_ptr| {
3390 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
3391 },
3392 .inferred_ptr => |result_alloc| {
3393 // TODO here we should be able to resolve the inference; we now have a type for the result.
3394 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
3395 },
3396 }
3397}
3398
3399fn typeOf(
3400 mod: *Module,
3401 scope: *Scope,
3402 rl: ResultLoc,
3403 builtin_token: ast.TokenIndex,
3404 src: usize,
3405 params: []const ast.Node.Index,
3406) InnerError!*zir.Inst {
3407 if (params.len < 1) {
3408 return mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
3409 }
3410 if (params.len == 1) {
3411 return rvalue(mod, scope, rl, try addZIRUnOp(mod, scope, src, .typeof, try expr(mod, scope, .none, params[0])));
3412 }
3413 const arena = scope.arena();
3414 var items = try arena.alloc(*zir.Inst, params.len);
3415 for (params) |param, param_i|
3416 items[param_i] = try expr(mod, scope, .none, param);
3417 return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{}));
3418}
3419
3420fn builtinCall(
3421 mod: *Module,
3422 scope: *Scope,
3423 rl: ResultLoc,
3424 call: ast.Node.Index,
3425 params: []const ast.Node.Index,
3426) InnerError!*zir.Inst {
3427 const tree = scope.tree();
3428 const main_tokens = tree.nodes.items(.main_token);
3429 const token_starts = tree.tokens.items(.start);
3430
3431 const builtin_token = main_tokens[call];
3432 const builtin_name = tree.tokenSlice(builtin_token);
3433
3434 // We handle the different builtins manually because they have different semantics depending
3435 // on the function. For example, `@as` and others participate in result location semantics,
3436 // and `@cImport` creates a special scope that collects a .c source code text buffer.
3437 // Also, some builtins have a variable number of parameters.
3438
3439 const info = BuiltinFn.list.get(builtin_name) orelse {
3440 return mod.failTok(scope, builtin_token, "invalid builtin function: '{s}'", .{
3441 builtin_name,
3442 });
3443 };
3444 if (info.param_count) |expected| {
3445 if (expected != params.len) {
3446 const s = if (expected == 1) "" else "s";
3447 return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{
3448 expected, s, params.len,
3449 });
3450 }
3451 }
3452 const src = token_starts[builtin_token];
3453
3454 switch (info.tag) {
3455 .ptr_to_int => {
3456 const operand = try expr(mod, scope, .none, params[0]);
3457 const result = try addZIRUnOp(mod, scope, src, .ptrtoint, operand);
3458 return rvalue(mod, scope, rl, result);
3459 },
3460 .float_cast => {
3461 const dest_type = try typeExpr(mod, scope, params[0]);
3462 const rhs = try expr(mod, scope, .none, params[1]);
3463 const result = try addZIRBinOp(mod, scope, src, .floatcast, dest_type, rhs);
3464 return rvalue(mod, scope, rl, result);
3465 },
3466 .int_cast => {
3467 const dest_type = try typeExpr(mod, scope, params[0]);
3468 const rhs = try expr(mod, scope, .none, params[1]);
3469 const result = try addZIRBinOp(mod, scope, src, .intcast, dest_type, rhs);
3470 return rvalue(mod, scope, rl, result);
3471 },
3472 .breakpoint => {
3473 const result = try addZIRNoOp(mod, scope, src, .breakpoint);
3474 return rvalue(mod, scope, rl, result);
3475 },
3476 .import => {
3477 const target = try expr(mod, scope, .none, params[0]);
3478 const result = try addZIRUnOp(mod, scope, src, .import, target);
3479 return rvalue(mod, scope, rl, result);
3480 },
3481 .compile_error => {
3482 const target = try expr(mod, scope, .none, params[0]);
3483 const result = try addZIRUnOp(mod, scope, src, .compile_error, target);
3484 return rvalue(mod, scope, rl, result);
3485 },
3486 .set_eval_branch_quota => {
3487 const u32_type = try addZIRInstConst(mod, scope, src, .{
3488 .ty = Type.initTag(.type),
3489 .val = Value.initTag(.u32_type),
3490 });
3491 const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]);
3492 const result = try addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota);
3493 return rvalue(mod, scope, rl, result);
3494 },
3495 .compile_log => {
3496 const arena = scope.arena();
3497 var targets = try arena.alloc(*zir.Inst, params.len);
3498 for (params) |param, param_i|
3499 targets[param_i] = try expr(mod, scope, .none, param);
3500 const result = try addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{});
3501 return rvalue(mod, scope, rl, result);
3502 },
3503 .field => {
3504 const string_type = try addZIRInstConst(mod, scope, src, .{
3505 .ty = Type.initTag(.type),
3506 .val = Value.initTag(.const_slice_u8_type),
3507 });
3508 const string_rl: ResultLoc = .{ .ty = string_type };
3509
3510 if (rl == .ref) {
3511 return addZirInstTag(mod, scope, src, .field_ptr_named, .{
3512 .object = try expr(mod, scope, .ref, params[0]),
3513 .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
3514 });
3515 }
3516 return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{
3517 .object = try expr(mod, scope, .none, params[0]),
3518 .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
3519 }));
3520 },
3521 .as => return as(mod, scope, rl, builtin_token, src, params[0], params[1]),
3522 .bit_cast => return bitCast(mod, scope, rl, builtin_token, src, params[0], params[1]),
3523 .TypeOf => return typeOf(mod, scope, rl, builtin_token, src, params),
3524
3525 .add_with_overflow,
3526 .align_cast,
3527 .align_of,
3528 .async_call,
3529 .atomic_load,
3530 .atomic_rmw,
3531 .atomic_store,
3532 .bit_offset_of,
3533 .bool_to_int,
3534 .bit_size_of,
3535 .mul_add,
3536 .byte_swap,
3537 .bit_reverse,
3538 .byte_offset_of,
3539 .call,
3540 .c_define,
3541 .c_import,
3542 .c_include,
3543 .clz,
3544 .cmpxchg_strong,
3545 .cmpxchg_weak,
3546 .ctz,
3547 .c_undef,
3548 .div_exact,
3549 .div_floor,
3550 .div_trunc,
3551 .embed_file,
3552 .enum_to_int,
3553 .error_name,
3554 .error_return_trace,
3555 .error_to_int,
3556 .err_set_cast,
3557 .@"export",
3558 .fence,
3559 .field_parent_ptr,
3560 .float_to_int,
3561 .frame,
3562 .Frame,
3563 .frame_address,
3564 .frame_size,
3565 .has_decl,
3566 .has_field,
3567 .int_to_enum,
3568 .int_to_error,
3569 .int_to_float,
3570 .int_to_ptr,
3571 .memcpy,
3572 .memset,
3573 .wasm_memory_size,
3574 .wasm_memory_grow,
3575 .mod,
3576 .mul_with_overflow,
3577 .panic,
3578 .pop_count,
3579 .ptr_cast,
3580 .rem,
3581 .return_address,
3582 .set_align_stack,
3583 .set_cold,
3584 .set_float_mode,
3585 .set_runtime_safety,
3586 .shl_exact,
3587 .shl_with_overflow,
3588 .shr_exact,
3589 .shuffle,
3590 .size_of,
3591 .splat,
3592 .reduce,
3593 .src,
3594 .sqrt,
3595 .sin,
3596 .cos,
3597 .exp,
3598 .exp2,
3599 .log,
3600 .log2,
3601 .log10,
3602 .fabs,
3603 .floor,
3604 .ceil,
3605 .trunc,
3606 .round,
3607 .sub_with_overflow,
3608 .tag_name,
3609 .This,
3610 .truncate,
3611 .Type,
3612 .type_info,
3613 .type_name,
3614 .union_init,
3615 => return mod.failTok(scope, builtin_token, "TODO: implement builtin function {s}", .{
3616 builtin_name,
3617 }),
3618 }
3619}
3620
3621fn callExpr(
3622 mod: *Module,
3623 scope: *Scope,
3624 rl: ResultLoc,
3625 call: ast.full.Call,
3626) InnerError!*zir.Inst {
3627 if (call.async_token) |async_token| {
3628 return mod.failTok(scope, async_token, "TODO implement async fn call", .{});
3629 }
3630
3631 const tree = scope.tree();
3632 const main_tokens = tree.nodes.items(.main_token);
3633 const token_starts = tree.tokens.items(.start);
3634
3635 const lhs = try expr(mod, scope, .none, call.ast.fn_expr);
3636
3637 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, call.ast.params.len);
3638 for (call.ast.params) |param_node, i| {
3639 const param_src = token_starts[tree.firstToken(param_node)];
3640 const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
3641 .func = lhs,
3642 .arg_index = i,
3643 }, .{});
3644 args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
3645 }
3646
3647 const src = token_starts[call.ast.lparen];
3648 var modifier: std.builtin.CallOptions.Modifier = .auto;
3649 if (call.async_token) |_| modifier = .async_kw;
3650
3651 const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
3652 .func = lhs,
3653 .args = args,
3654 .modifier = modifier,
3655 }, .{});
3656 // TODO function call with result location
3657 return rvalue(mod, scope, rl, result);
3658}
3659
3660fn suspendExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
3661 const tree = scope.tree();
3662 const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]];
3663
3664 if (scope.getNosuspend()) |some| {
3665 const msg = msg: {
3666 const msg = try mod.errMsg(scope, src, "suspend in nosuspend block", .{});
3667 errdefer msg.destroy(mod.gpa);
3668 try mod.errNote(scope, some.src, msg, "nosuspend block here", .{});
3669 break :msg msg;
3670 };
3671 return mod.failWithOwnedErrorMsg(scope, msg);
3672 }
3673
3674 if (scope.getSuspend()) |some| {
3675 const msg = msg: {
3676 const msg = try mod.errMsg(scope, src, "cannot suspend inside suspend block", .{});
3677 errdefer msg.destroy(mod.gpa);
3678 try mod.errNote(scope, some.src, msg, "other suspend block here", .{});
3679 break :msg msg;
3680 };
3681 return mod.failWithOwnedErrorMsg(scope, msg);
3682 }
3683
3684 var suspend_scope: Scope.GenZIR = .{
3685 .base = .{ .tag = .gen_suspend },
3686 .parent = scope,
3687 .decl = scope.ownerDecl().?,
3688 .arena = scope.arena(),
3689 .force_comptime = scope.isComptime(),
3690 .instructions = .{},
3691 };
3692 defer suspend_scope.instructions.deinit(mod.gpa);
3693
3694 const operand = tree.nodes.items(.data)[node].lhs;
3695 if (operand != 0) {
3696 const possibly_unused_result = try expr(mod, &suspend_scope.base, .none, operand);
3697 if (!possibly_unused_result.tag.isNoReturn()) {
3698 _ = try addZIRUnOp(mod, &suspend_scope.base, src, .ensure_result_used, possibly_unused_result);
3699 }
3700 } else {
3701 return addZIRNoOp(mod, scope, src, .@"suspend");
3702 }
3703
3704 const block = try addZIRInstBlock(mod, scope, src, .suspend_block, .{
3705 .instructions = try scope.arena().dupe(*zir.Inst, suspend_scope.instructions.items),
3706 });
3707 return &block.base;
3708}
3709
3710fn nosuspendExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst {
3711 const tree = scope.tree();
3712 var child_scope = Scope.Nosuspend{
3713 .parent = scope,
3714 .gen_zir = scope.getGenZIR(),
3715 .src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]],
3716 };
3717
3718 return expr(mod, &child_scope.base, rl, tree.nodes.items(.data)[node].lhs);
3719}
3720
3721fn awaitExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst {
3722 const tree = scope.tree();
3723 const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]];
3724 const is_nosuspend = scope.getNosuspend() != null;
3725
3726 // TODO some @asyncCall stuff
3727
3728 if (scope.getSuspend()) |some| {
3729 const msg = msg: {
3730 const msg = try mod.errMsg(scope, src, "cannot await inside suspend block", .{});
3731 errdefer msg.destroy(mod.gpa);
3732 try mod.errNote(scope, some.src, msg, "suspend block here", .{});
3733 break :msg msg;
3734 };
3735 return mod.failWithOwnedErrorMsg(scope, msg);
3736 }
3737
3738 const operand = try expr(mod, scope, .ref, tree.nodes.items(.data)[node].lhs);
3739 // TODO pass result location
3740 return addZIRUnOp(mod, scope, src, if (is_nosuspend) .nosuspend_await else .@"await", operand);
3741}
3742
3743fn resumeExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
3744 const tree = scope.tree();
3745 const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]];
3746
3747 const operand = try expr(mod, scope, .ref, tree.nodes.items(.data)[node].lhs);
3748 return addZIRUnOp(mod, scope, src, .@"resume", operand);
3749}
3750
3751pub const simple_types = std.ComptimeStringMap(Value.Tag, .{
3752 .{ "u8", .u8_type },
3753 .{ "i8", .i8_type },
3754 .{ "isize", .isize_type },
3755 .{ "usize", .usize_type },
3756 .{ "c_short", .c_short_type },
3757 .{ "c_ushort", .c_ushort_type },
3758 .{ "c_int", .c_int_type },
3759 .{ "c_uint", .c_uint_type },
3760 .{ "c_long", .c_long_type },
3761 .{ "c_ulong", .c_ulong_type },
3762 .{ "c_longlong", .c_longlong_type },
3763 .{ "c_ulonglong", .c_ulonglong_type },
3764 .{ "c_longdouble", .c_longdouble_type },
3765 .{ "f16", .f16_type },
3766 .{ "f32", .f32_type },
3767 .{ "f64", .f64_type },
3768 .{ "f128", .f128_type },
3769 .{ "c_void", .c_void_type },
3770 .{ "bool", .bool_type },
3771 .{ "void", .void_type },
3772 .{ "type", .type_type },
3773 .{ "anyerror", .anyerror_type },
3774 .{ "comptime_int", .comptime_int_type },
3775 .{ "comptime_float", .comptime_float_type },
3776 .{ "noreturn", .noreturn_type },
3777});
3778
3779fn nodeMayNeedMemoryLocation(scope: *Scope, start_node: ast.Node.Index) bool {
3780 const tree = scope.tree();
3781 const node_tags = tree.nodes.items(.tag);
3782 const node_datas = tree.nodes.items(.data);
3783 const main_tokens = tree.nodes.items(.main_token);
3784 const token_tags = tree.tokens.items(.tag);
3785
3786 var node = start_node;
3787 while (true) {
3788 switch (node_tags[node]) {
3789 .root,
3790 .@"usingnamespace",
3791 .test_decl,
3792 .switch_case,
3793 .switch_case_one,
3794 .container_field_init,
3795 .container_field_align,
3796 .container_field,
3797 .asm_output,
3798 .asm_input,
3799 => unreachable,
3800
3801 .@"return",
3802 .@"break",
3803 .@"continue",
3804 .bit_not,
3805 .bool_not,
3806 .global_var_decl,
3807 .local_var_decl,
3808 .simple_var_decl,
3809 .aligned_var_decl,
3810 .@"defer",
3811 .@"errdefer",
3812 .address_of,
3813 .optional_type,
3814 .negation,
3815 .negation_wrap,
3816 .@"resume",
3817 .array_type,
3818 .array_type_sentinel,
3819 .ptr_type_aligned,
3820 .ptr_type_sentinel,
3821 .ptr_type,
3822 .ptr_type_bit_range,
3823 .@"suspend",
3824 .@"anytype",
3825 .fn_proto_simple,
3826 .fn_proto_multi,
3827 .fn_proto_one,
3828 .fn_proto,
3829 .fn_decl,
3830 .anyframe_type,
3831 .anyframe_literal,
3832 .integer_literal,
3833 .float_literal,
3834 .enum_literal,
3835 .string_literal,
3836 .multiline_string_literal,
3837 .char_literal,
3838 .true_literal,
3839 .false_literal,
3840 .null_literal,
3841 .undefined_literal,
3842 .unreachable_literal,
3843 .identifier,
3844 .error_set_decl,
3845 .container_decl,
3846 .container_decl_trailing,
3847 .container_decl_two,
3848 .container_decl_two_trailing,
3849 .container_decl_arg,
3850 .container_decl_arg_trailing,
3851 .tagged_union,
3852 .tagged_union_trailing,
3853 .tagged_union_two,
3854 .tagged_union_two_trailing,
3855 .tagged_union_enum_tag,
3856 .tagged_union_enum_tag_trailing,
3857 .@"asm",
3858 .asm_simple,
3859 .add,
3860 .add_wrap,
3861 .array_cat,
3862 .array_mult,
3863 .assign,
3864 .assign_bit_and,
3865 .assign_bit_or,
3866 .assign_bit_shift_left,
3867 .assign_bit_shift_right,
3868 .assign_bit_xor,
3869 .assign_div,
3870 .assign_sub,
3871 .assign_sub_wrap,
3872 .assign_mod,
3873 .assign_add,
3874 .assign_add_wrap,
3875 .assign_mul,
3876 .assign_mul_wrap,
3877 .bang_equal,
3878 .bit_and,
3879 .bit_or,
3880 .bit_shift_left,
3881 .bit_shift_right,
3882 .bit_xor,
3883 .bool_and,
3884 .bool_or,
3885 .div,
3886 .equal_equal,
3887 .error_union,
3888 .greater_or_equal,
3889 .greater_than,
3890 .less_or_equal,
3891 .less_than,
3892 .merge_error_sets,
3893 .mod,
3894 .mul,
3895 .mul_wrap,
3896 .switch_range,
3897 .field_access,
3898 .sub,
3899 .sub_wrap,
3900 .slice,
3901 .slice_open,
3902 .slice_sentinel,
3903 .deref,
3904 .array_access,
3905 .error_value,
3906 .while_simple, // This variant cannot have an else expression.
3907 .while_cont, // This variant cannot have an else expression.
3908 .for_simple, // This variant cannot have an else expression.
3909 .if_simple, // This variant cannot have an else expression.
3910 => return false,
3911
3912 // Forward the question to the LHS sub-expression.
3913 .grouped_expression,
3914 .@"try",
3915 .@"await",
3916 .@"comptime",
3917 .@"nosuspend",
3918 .unwrap_optional,
3919 => node = node_datas[node].lhs,
3920
3921 // Forward the question to the RHS sub-expression.
3922 .@"catch",
3923 .@"orelse",
3924 => node = node_datas[node].rhs,
3925
3926 // True because these are exactly the expressions we need memory locations for.
3927 .array_init_one,
3928 .array_init_one_comma,
3929 .array_init_dot_two,
3930 .array_init_dot_two_comma,
3931 .array_init_dot,
3932 .array_init_dot_comma,
3933 .array_init,
3934 .array_init_comma,
3935 .struct_init_one,
3936 .struct_init_one_comma,
3937 .struct_init_dot_two,
3938 .struct_init_dot_two_comma,
3939 .struct_init_dot,
3940 .struct_init_dot_comma,
3941 .struct_init,
3942 .struct_init_comma,
3943 => return true,
3944
3945 // True because depending on comptime conditions, sub-expressions
3946 // may be the kind that need memory locations.
3947 .@"while", // This variant always has an else expression.
3948 .@"if", // This variant always has an else expression.
3949 .@"for", // This variant always has an else expression.
3950 .@"switch",
3951 .switch_comma,
3952 .call_one,
3953 .call_one_comma,
3954 .async_call_one,
3955 .async_call_one_comma,
3956 .call,
3957 .call_comma,
3958 .async_call,
3959 .async_call_comma,
3960 => return true,
3961
3962 .block_two,
3963 .block_two_semicolon,
3964 .block,
3965 .block_semicolon,
3966 => {
3967 const lbrace = main_tokens[node];
3968 if (token_tags[lbrace - 1] == .colon) {
3969 // Labeled blocks may need a memory location to forward
3970 // to their break statements.
3971 return true;
3972 } else {
3973 return false;
3974 }
3975 },
3976
3977 .builtin_call,
3978 .builtin_call_comma,
3979 .builtin_call_two,
3980 .builtin_call_two_comma,
3981 => {
3982 const builtin_token = main_tokens[node];
3983 const builtin_name = tree.tokenSlice(builtin_token);
3984 // If the builtin is an invalid name, we don't cause an error here; instead
3985 // let it pass, and the error will be "invalid builtin function" later.
3986 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
3987 return builtin_info.needs_mem_loc;
3988 },
3989 }
3990 }
3991}
3992
3993/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
3994/// result locations must call this function on their result.
3995/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
3996/// If the `ResultLoc` is `ty`, it will coerce the result to the type.
3997fn rvalue(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerError!*zir.Inst {
3998 switch (rl) {
3999 .none => return result,
4000 .discard => {
4001 // Emit a compile error for discarding error values.
4002 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
4003 return result;
4004 },
4005 .ref => {
4006 // We need a pointer but we have a value.
4007 return addZIRUnOp(mod, scope, result.src, .ref, result);
4008 },
4009 .ty => |ty_inst| return addZIRBinOp(mod, scope, result.src, .as, ty_inst, result),
4010 .ptr => |ptr_inst| {
4011 _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, result);
4012 return result;
4013 },
4014 .bitcasted_ptr => |bitcasted_ptr| {
4015 return mod.fail(scope, result.src, "TODO implement rvalue .bitcasted_ptr", .{});
4016 },
4017 .inferred_ptr => |alloc| {
4018 _ = try addZIRBinOp(mod, scope, result.src, .store_to_inferred_ptr, &alloc.base, result);
4019 return result;
4020 },
4021 .block_ptr => |block_scope| {
4022 block_scope.rvalue_rl_count += 1;
4023 _ = try addZIRBinOp(mod, scope, result.src, .store_to_block_ptr, block_scope.rl_ptr.?, result);
4024 return result;
4025 },
4026 }
4027}
4028
4029/// TODO when reworking ZIR memory layout, make the void value correspond to a hard coded
4030/// index; that way this does not actually need to allocate anything.
4031fn rvalueVoid(
4032 mod: *Module,
4033 scope: *Scope,
4034 rl: ResultLoc,
4035 node: ast.Node.Index,
4036 result: void,
4037) InnerError!*zir.Inst {
4038 const tree = scope.tree();
4039 const main_tokens = tree.nodes.items(.main_token);
4040 const src = tree.tokens.items(.start)[tree.firstToken(node)];
4041 const void_inst = try addZIRInstConst(mod, scope, src, .{
4042 .ty = Type.initTag(.void),
4043 .val = Value.initTag(.void_value),
4044 });
4045 return rvalue(mod, scope, rl, void_inst);
4046}
4047
4048fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy {
4049 var elide_store_to_block_ptr_instructions = false;
4050 switch (rl) {
4051 // In this branch there will not be any store_to_block_ptr instructions.
4052 .discard, .none, .ty, .ref => return .{
4053 .tag = .break_operand,
4054 .elide_store_to_block_ptr_instructions = false,
4055 },
4056 // The pointer got passed through to the sub-expressions, so we will use
4057 // break_void here.
4058 // In this branch there will not be any store_to_block_ptr instructions.
4059 .ptr => return .{
4060 .tag = .break_void,
4061 .elide_store_to_block_ptr_instructions = false,
4062 },
4063 .inferred_ptr, .bitcasted_ptr, .block_ptr => {
4064 if (block_scope.rvalue_rl_count == block_scope.break_count) {
4065 // Neither prong of the if consumed the result location, so we can
4066 // use break instructions to create an rvalue.
4067 return .{
4068 .tag = .break_operand,
4069 .elide_store_to_block_ptr_instructions = true,
4070 };
4071 } else {
4072 // Allow the store_to_block_ptr instructions to remain so that
4073 // semantic analysis can turn them into bitcasts.
4074 return .{
4075 .tag = .break_void,
4076 .elide_store_to_block_ptr_instructions = false,
4077 };
4078 }
4079 },
4080 }
4081}
4082
4083/// If the input ResultLoc is ref, returns ResultLoc.ref. Otherwise:
4084/// Returns ResultLoc.ty, where the type is determined by the input
4085/// ResultLoc type, wrapped in an optional type. If the input ResultLoc
4086/// has no type, .none is returned.
4087fn makeOptionalTypeResultLoc(mod: *Module, scope: *Scope, src: usize, rl: ResultLoc) !ResultLoc {
4088 switch (rl) {
4089 .ref => return ResultLoc.ref,
4090 .discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => return ResultLoc.none,
4091 .ty => |elem_ty| {
4092 const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type, elem_ty);
4093 return ResultLoc{ .ty = wrapped_ty };
4094 },
4095 .ptr => |ptr_ty| {
4096 const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type_from_ptr_elem, ptr_ty);
4097 return ResultLoc{ .ty = wrapped_ty };
4098 },
4099 }
4100}
4101
4102fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void {
4103 // Depending on whether the result location is a pointer or value, different
4104 // ZIR needs to be generated. In the former case we rely on storing to the
4105 // pointer to communicate the result, and use breakvoid; in the latter case
4106 // the block break instructions will have the result values.
4107 // One more complication: when the result location is a pointer, we detect
4108 // the scenario where the result location is not consumed. In this case
4109 // we emit ZIR for the block break instructions to have the result values,
4110 // and then rvalue() on that to pass the value to the result location.
4111 switch (parent_rl) {
4112 .discard, .none, .ty, .ptr, .ref => {
4113 block_scope.break_result_loc = parent_rl;
4114 },
4115
4116 .inferred_ptr => |ptr| {
4117 block_scope.rl_ptr = &ptr.base;
4118 block_scope.break_result_loc = .{ .block_ptr = block_scope };
4119 },
4120
4121 .bitcasted_ptr => |ptr| {
4122 block_scope.rl_ptr = &ptr.base;
4123 block_scope.break_result_loc = .{ .block_ptr = block_scope };
4124 },
4125
4126 .block_ptr => |parent_block_scope| {
4127 block_scope.rl_ptr = parent_block_scope.rl_ptr.?;
4128 block_scope.break_result_loc = .{ .block_ptr = block_scope };
4129 },
4130 }
4131}
4132
4133pub fn addZirInstTag(
4134 mod: *Module,
4135 scope: *Scope,
4136 src: usize,
4137 comptime tag: zir.Inst.Tag,
4138 positionals: std.meta.fieldInfo(tag.Type(), .positionals).field_type,
4139) !*zir.Inst {
4140 const gen_zir = scope.getGenZIR();
4141 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
4142 const inst = try gen_zir.arena.create(tag.Type());
4143 inst.* = .{
4144 .base = .{
4145 .tag = tag,
4146 .src = src,
4147 },
4148 .positionals = positionals,
4149 .kw_args = .{},
4150 };
4151 gen_zir.instructions.appendAssumeCapacity(&inst.base);
4152 return &inst.base;
4153}
4154
4155pub fn addZirInstT(
4156 mod: *Module,
4157 scope: *Scope,
4158 src: usize,
4159 comptime T: type,
4160 tag: zir.Inst.Tag,
4161 positionals: std.meta.fieldInfo(T, .positionals).field_type,
4162) !*T {
4163 const gen_zir = scope.getGenZIR();
4164 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
4165 const inst = try gen_zir.arena.create(T);
4166 inst.* = .{
4167 .base = .{
4168 .tag = tag,
4169 .src = src,
4170 },
4171 .positionals = positionals,
4172 .kw_args = .{},
4173 };
4174 gen_zir.instructions.appendAssumeCapacity(&inst.base);
4175 return inst;
4176}
4177
4178pub fn addZIRInstSpecial(
4179 mod: *Module,
4180 scope: *Scope,
4181 src: usize,
4182 comptime T: type,
4183 positionals: std.meta.fieldInfo(T, .positionals).field_type,
4184 kw_args: std.meta.fieldInfo(T, .kw_args).field_type,
4185) !*T {
4186 const gen_zir = scope.getGenZIR();
4187 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
4188 const inst = try gen_zir.arena.create(T);
4189 inst.* = .{
4190 .base = .{
4191 .tag = T.base_tag,
4192 .src = src,
4193 },
4194 .positionals = positionals,
4195 .kw_args = kw_args,
4196 };
4197 gen_zir.instructions.appendAssumeCapacity(&inst.base);
4198 return inst;
4199}
4200
4201pub fn addZIRNoOpT(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp {
4202 const gen_zir = scope.getGenZIR();
4203 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
4204 const inst = try gen_zir.arena.create(zir.Inst.NoOp);
4205 inst.* = .{
4206 .base = .{
4207 .tag = tag,
4208 .src = src,
4209 },
4210 .positionals = .{},
4211 .kw_args = .{},
4212 };
4213 gen_zir.instructions.appendAssumeCapacity(&inst.base);
4214 return inst;
4215}
4216
4217pub fn addZIRNoOp(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst {
4218 const inst = try addZIRNoOpT(mod, scope, src, tag);
4219 return &inst.base;
4220}
4221
4222pub fn addZIRUnOp(
4223 mod: *Module,
4224 scope: *Scope,
4225 src: usize,
4226 tag: zir.Inst.Tag,
4227 operand: *zir.Inst,
4228) !*zir.Inst {
4229 const gen_zir = scope.getGenZIR();
4230 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
4231 const inst = try gen_zir.arena.create(zir.Inst.UnOp);
4232 inst.* = .{
4233 .base = .{
4234 .tag = tag,
4235 .src = src,
4236 },
4237 .positionals = .{
4238 .operand = operand,
4239 },
4240 .kw_args = .{},
4241 };
4242 gen_zir.instructions.appendAssumeCapacity(&inst.base);
4243 return &inst.base;
4244}
4245
4246pub fn addZIRBinOp(
4247 mod: *Module,
4248 scope: *Scope,
4249 src: usize,
4250 tag: zir.Inst.Tag,
4251 lhs: *zir.Inst,
4252 rhs: *zir.Inst,
4253) !*zir.Inst {
4254 const gen_zir = scope.getGenZIR();
4255 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
4256 const inst = try gen_zir.arena.create(zir.Inst.BinOp);
4257 inst.* = .{
4258 .base = .{
4259 .tag = tag,
4260 .src = src,
4261 },
4262 .positionals = .{
4263 .lhs = lhs,
4264 .rhs = rhs,
4265 },
4266 .kw_args = .{},
4267 };
4268 gen_zir.instructions.appendAssumeCapacity(&inst.base);
4269 return &inst.base;
4270}
4271
4272pub fn addZIRInstBlock(
4273 mod: *Module,
4274 scope: *Scope,
4275 src: usize,
4276 tag: zir.Inst.Tag,
4277 body: zir.Body,
4278) !*zir.Inst.Block {
4279 const gen_zir = scope.getGenZIR();
4280 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
4281 const inst = try gen_zir.arena.create(zir.Inst.Block);
4282 inst.* = .{
4283 .base = .{
4284 .tag = tag,
4285 .src = src,
4286 },
4287 .positionals = .{
4288 .body = body,
4289 },
4290 .kw_args = .{},
4291 };
4292 gen_zir.instructions.appendAssumeCapacity(&inst.base);
4293 return inst;
4294}
4295
4296pub fn addZIRInst(
4297 mod: *Module,
4298 scope: *Scope,
4299 src: usize,
4300 comptime T: type,
4301 positionals: std.meta.fieldInfo(T, .positionals).field_type,
4302 kw_args: std.meta.fieldInfo(T, .kw_args).field_type,
4303) !*zir.Inst {
4304 const inst_special = try addZIRInstSpecial(mod, scope, src, T, positionals, kw_args);
4305 return &inst_special.base;
4306}
4307
4308/// TODO The existence of this function is a workaround for a bug in stage1.
4309pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
4310 const P = std.meta.fieldInfo(zir.Inst.Const, .positionals).field_type;
4311 return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
4312}
4313
4314/// TODO The existence of this function is a workaround for a bug in stage1.
4315pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Body) !*zir.Inst.Loop {
4316 const P = std.meta.fieldInfo(zir.Inst.Loop, .positionals).field_type;
4317 return addZIRInstSpecial(mod, scope, src, zir.Inst.Loop, P{ .body = body }, .{});
4318}
src/codegen.zig+97-136
......@@ -17,6 +17,8 @@ const DW = std.dwarf;
1717const leb128 = std.leb;
1818const log = std.log.scoped(.codegen);
1919const build_options = @import("build_options");
20const LazySrcLoc = Module.LazySrcLoc;
21const RegisterManager = @import("register_manager.zig").RegisterManager;
2022
2123/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
2224pub const BlockData = struct {
......@@ -285,11 +287,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
285287 /// across each runtime branch upon joining.
286288 branch_stack: *std.ArrayList(Branch),
287289
288 /// The key must be canonical register.
289 registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
290 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
291 /// Tracks all registers allocated in the course of this function
292 allocated_registers: FreeRegInt = 0,
290 register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
293291 /// Maps offset to what is stored there.
294292 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
295293
......@@ -381,49 +379,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
381379 }
382380 };
383381
384 fn markRegUsed(self: *Self, reg: Register) void {
385 if (FreeRegInt == u0) return;
386 const index = reg.allocIndex() orelse return;
387 const ShiftInt = math.Log2Int(FreeRegInt);
388 const shift = @intCast(ShiftInt, index);
389 const mask = @as(FreeRegInt, 1) << shift;
390 self.free_registers &= ~mask;
391 self.allocated_registers |= mask;
392 }
393
394 fn markRegFree(self: *Self, reg: Register) void {
395 if (FreeRegInt == u0) return;
396 const index = reg.allocIndex() orelse return;
397 const ShiftInt = math.Log2Int(FreeRegInt);
398 const shift = @intCast(ShiftInt, index);
399 self.free_registers |= @as(FreeRegInt, 1) << shift;
400 }
401
402 /// Before calling, must ensureCapacity + 1 on self.registers.
403 /// Returns `null` if all registers are allocated.
404 fn allocReg(self: *Self, inst: *ir.Inst) ?Register {
405 const free_index = @ctz(FreeRegInt, self.free_registers);
406 if (free_index >= callee_preserved_regs.len) {
407 return null;
408 }
409 const mask = @as(FreeRegInt, 1) << free_index;
410 self.free_registers &= ~mask;
411 self.allocated_registers |= mask;
412 const reg = callee_preserved_regs[free_index];
413 self.registers.putAssumeCapacityNoClobber(reg, inst);
414 log.debug("alloc {} => {*}", .{ reg, inst });
415 return reg;
416 }
417
418 /// Does not track the register.
419 fn findUnusedReg(self: *Self) ?Register {
420 const free_index = @ctz(FreeRegInt, self.free_registers);
421 if (free_index >= callee_preserved_regs.len) {
422 return null;
423 }
424 return callee_preserved_regs[free_index];
425 }
426
427382 const StackAllocation = struct {
428383 inst: *ir.Inst,
429384 /// TODO do we need size? should be determined by inst.ty.abiSize()
......@@ -494,11 +449,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
494449 .rbrace_src = src_data.rbrace_src,
495450 .source = src_data.source,
496451 };
497 defer function.registers.deinit(bin_file.allocator);
452 defer function.register_manager.deinit(bin_file.allocator);
498453 defer function.stack.deinit(bin_file.allocator);
499454 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
500455
501 var call_info = function.resolveCallingConventionValues(src_loc.byte_offset, fn_type) catch |err| switch (err) {
456 var call_info = function.resolveCallingConventionValues(src_loc.lazy, fn_type) catch |err| switch (err) {
502457 error.CodegenFail => return Result{ .fail = function.err_msg.? },
503458 else => |e| return e,
504459 };
......@@ -606,10 +561,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
606561 .r14 = true, // lr
607562 };
608563 inline for (callee_preserved_regs) |reg, i| {
609 const ShiftInt = math.Log2Int(FreeRegInt);
610 const shift = @intCast(ShiftInt, i);
611 const mask = @as(FreeRegInt, 1) << shift;
612 if (self.allocated_registers & mask != 0) {
564 if (self.register_manager.isRegAllocated(reg)) {
613565 @field(saved_regs, @tagName(reg)) = true;
614566 }
615567 }
......@@ -791,8 +743,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
791743 }
792744 }
793745
794 fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {
795 self.prev_di_src = src;
746 fn dbgAdvancePCAndLine(self: *Self, abs_byte_off: usize) InnerError!void {
747 self.prev_di_src = abs_byte_off;
796748 self.prev_di_pc = self.code.items.len;
797749 switch (self.debug_output) {
798750 .dwarf => |dbg_out| {
......@@ -800,7 +752,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
800752 // lookup table, and changing ir.Inst from storing byte offset to token. Currently
801753 // this involves scanning over the source code for newlines
802754 // (but only from the previous byte offset to the new one).
803 const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
755 const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, abs_byte_off);
804756 const delta_pc = self.code.items.len - self.prev_di_pc;
805757 // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
806758 // single-byte opcodes that add different numbers to both the PC and the line number
......@@ -828,8 +780,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
828780 switch (prev_value) {
829781 .register => |reg| {
830782 const canon_reg = toCanonicalReg(reg);
831 _ = self.registers.remove(canon_reg);
832 self.markRegFree(canon_reg);
783 self.register_manager.freeReg(canon_reg);
833784 },
834785 else => {}, // TODO process stack allocation death
835786 }
......@@ -897,6 +848,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
897848 .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
898849 .is_err => return self.genIsErr(inst.castTag(.is_err).?),
899850 .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
851 .error_to_int => return self.genErrorToInt(inst.castTag(.error_to_int).?),
852 .int_to_error => return self.genIntToError(inst.castTag(.int_to_error).?),
900853 .load => return self.genLoad(inst.castTag(.load).?),
901854 .loop => return self.genLoop(inst.castTag(.loop).?),
902855 .not => return self.genNot(inst.castTag(.not).?),
......@@ -907,6 +860,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
907860 .ret => return self.genRet(inst.castTag(.ret).?),
908861 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
909862 .store => return self.genStore(inst.castTag(.store).?),
863 .struct_field_ptr => return self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),
910864 .sub => return self.genSub(inst.castTag(.sub).?),
911865 .subwrap => return self.genSubWrap(inst.castTag(.subwrap).?),
912866 .switchbr => return self.genSwitch(inst.castTag(.switchbr).?),
......@@ -965,8 +919,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
965919 const ptr_bits = arch.ptrBitWidth();
966920 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
967921 if (abi_size <= ptr_bytes) {
968 try self.registers.ensureCapacity(self.gpa, self.registers.count() + 1);
969 if (self.allocReg(inst)) |reg| {
922 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
923 if (self.register_manager.tryAllocReg(inst)) |reg| {
970924 return MCValue{ .register = registerAlias(reg, abi_size) };
971925 }
972926 }
......@@ -975,26 +929,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
975929 return MCValue{ .stack_offset = stack_offset };
976930 }
977931
932 pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: Register, inst: *ir.Inst) !void {
933 const stack_mcv = try self.allocRegOrMem(inst, false);
934 const reg_mcv = self.getResolvedInstValue(inst);
935 assert(reg == toCanonicalReg(reg_mcv.register));
936 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
937 try branch.inst_table.put(self.gpa, inst, stack_mcv);
938 try self.genSetStack(src, inst.ty, stack_mcv.stack_offset, reg_mcv);
939 }
940
978941 /// Copies a value to a register without tracking the register. The register is not considered
979942 /// allocated. A second call to `copyToTmpRegister` may return the same register.
980943 /// This can have a side effect of spilling instructions to the stack to free up a register.
981 fn copyToTmpRegister(self: *Self, src: usize, ty: Type, mcv: MCValue) !Register {
982 const reg = self.findUnusedReg() orelse b: {
983 // We'll take over the first register. Move the instruction that was previously
984 // there to a stack allocation.
985 const reg = callee_preserved_regs[0];
986 const regs_entry = self.registers.remove(reg).?;
987 const spilled_inst = regs_entry.value;
988
989 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
990 const reg_mcv = self.getResolvedInstValue(spilled_inst);
991 assert(reg == toCanonicalReg(reg_mcv.register));
992 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
993 try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv);
994 try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
995
996 break :b reg;
997 };
944 fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register {
945 const reg = try self.register_manager.allocRegWithoutTracking();
998946 try self.genSetReg(src, ty, reg, mcv);
999947 return reg;
1000948 }
......@@ -1003,25 +951,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1003951 /// `reg_owner` is the instruction that gets associated with the register in the register table.
1004952 /// This can have a side effect of spilling instructions to the stack to free up a register.
1005953 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
1006 try self.registers.ensureCapacity(self.gpa, @intCast(u32, self.registers.count() + 1));
1007
1008 const reg = self.allocReg(reg_owner) orelse b: {
1009 // We'll take over the first register. Move the instruction that was previously
1010 // there to a stack allocation.
1011 const reg = callee_preserved_regs[0];
1012 const regs_entry = self.registers.getEntry(reg).?;
1013 const spilled_inst = regs_entry.value;
1014 regs_entry.value = reg_owner;
1015
1016 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
1017 const reg_mcv = self.getResolvedInstValue(spilled_inst);
1018 assert(reg == toCanonicalReg(reg_mcv.register));
1019 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1020 try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv);
1021 try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
1022
1023 break :b reg;
1024 };
954 try self.register_manager.registers.ensureCapacity(self.gpa, @intCast(u32, self.register_manager.registers.count() + 1));
955
956 const reg = try self.register_manager.allocReg(reg_owner);
1025957 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
1026958 return MCValue{ .register = reg };
1027959 }
......@@ -1298,7 +1230,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12981230 .register => |reg| {
12991231 // If it's in the registers table, need to associate the register with the
13001232 // new instruction.
1301 if (self.registers.getEntry(toCanonicalReg(reg))) |entry| {
1233 if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| {
13021234 entry.value = inst;
13031235 }
13041236 log.debug("reusing {} => {*}", .{ reg, inst });
......@@ -1400,6 +1332,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14001332 return .none;
14011333 }
14021334
1335 fn genStructFieldPtr(self: *Self, inst: *ir.Inst.StructFieldPtr) !MCValue {
1336 return self.fail(inst.base.src, "TODO implement codegen struct_field_ptr", .{});
1337 }
1338
14031339 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
14041340 // No side effects, so if it's unreferenced, do nothing.
14051341 if (inst.base.isUnused())
......@@ -1457,7 +1393,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14571393
14581394 fn genArmBinOpCode(
14591395 self: *Self,
1460 src: usize,
1396 src: LazySrcLoc,
14611397 dst_reg: Register,
14621398 lhs_mcv: MCValue,
14631399 rhs_mcv: MCValue,
......@@ -1620,7 +1556,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16201556
16211557 fn genX8664BinMathCode(
16221558 self: *Self,
1623 src: usize,
1559 src: LazySrcLoc,
16241560 dst_ty: Type,
16251561 dst_mcv: MCValue,
16261562 src_mcv: MCValue,
......@@ -1706,7 +1642,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17061642 }
17071643 }
17081644
1709 fn genX8664ModRMRegToStack(self: *Self, src: usize, ty: Type, off: u32, reg: Register, opcode: u8) !void {
1645 fn genX8664ModRMRegToStack(self: *Self, src: LazySrcLoc, ty: Type, off: u32, reg: Register, opcode: u8) !void {
17101646 const abi_size = ty.abiSize(self.target.*);
17111647 const adj_off = off + abi_size;
17121648 try self.code.ensureCapacity(self.code.items.len + 7);
......@@ -1787,7 +1723,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17871723 const arg_index = self.arg_index;
17881724 self.arg_index += 1;
17891725
1790 if (FreeRegInt == u0) {
1726 if (callee_preserved_regs.len == 0) {
17911727 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
17921728 }
17931729
......@@ -1799,15 +1735,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17991735
18001736 switch (result) {
18011737 .register => |reg| {
1802 try self.registers.putNoClobber(self.gpa, toCanonicalReg(reg), &inst.base);
1803 self.markRegUsed(reg);
1738 try self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
18041739 },
18051740 else => {},
18061741 }
18071742 return result;
18081743 }
18091744
1810 fn genBreakpoint(self: *Self, src: usize) !MCValue {
1745 fn genBreakpoint(self: *Self, src: LazySrcLoc) !MCValue {
18111746 switch (arch) {
18121747 .i386, .x86_64 => {
18131748 try self.code.append(0xcc); // int3
......@@ -2194,6 +2129,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21942129 unreachable;
21952130 }
21962131
2132 switch (info.return_value) {
2133 .register => |reg| {
2134 if (Register.allocIndex(reg) == null) {
2135 // Save function return value in a callee saved register
2136 return try self.copyToNewRegister(&inst.base, info.return_value);
2137 }
2138 },
2139 else => {},
2140 }
2141
21972142 return info.return_value;
21982143 }
21992144
......@@ -2224,7 +2169,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22242169 }
22252170 }
22262171
2227 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
2172 fn ret(self: *Self, src: LazySrcLoc, mcv: MCValue) !MCValue {
22282173 const ret_ty = self.fn_type.fnReturnType();
22292174 try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);
22302175 switch (arch) {
......@@ -2314,8 +2259,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23142259 }
23152260 }
23162261
2317 fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
2318 try self.dbgAdvancePCAndLine(inst.base.src);
2262 fn genDbgStmt(self: *Self, inst: *ir.Inst.DbgStmt) !MCValue {
2263 // TODO when reworking tzir memory layout, rework source locations here as
2264 // well to be more efficient, as well as support inlined function calls correctly.
2265 // For now we convert LazySrcLoc to absolute byte offset, to match what the
2266 // existing codegen code expects.
2267 try self.dbgAdvancePCAndLine(inst.byte_offset);
23192268 assert(inst.base.isUnused());
23202269 return MCValue.dead;
23212270 }
......@@ -2409,10 +2358,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24092358
24102359 // Capture the state of register and stack allocation state so that we can revert to it.
24112360 const parent_next_stack_offset = self.next_stack_offset;
2412 const parent_free_registers = self.free_registers;
2361 const parent_free_registers = self.register_manager.free_registers;
24132362 var parent_stack = try self.stack.clone(self.gpa);
24142363 defer parent_stack.deinit(self.gpa);
2415 var parent_registers = try self.registers.clone(self.gpa);
2364 var parent_registers = try self.register_manager.registers.clone(self.gpa);
24162365 defer parent_registers.deinit(self.gpa);
24172366
24182367 try self.branch_stack.append(.{});
......@@ -2429,8 +2378,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24292378 var saved_then_branch = self.branch_stack.pop();
24302379 defer saved_then_branch.deinit(self.gpa);
24312380
2432 self.registers.deinit(self.gpa);
2433 self.registers = parent_registers;
2381 self.register_manager.registers.deinit(self.gpa);
2382 self.register_manager.registers = parent_registers;
24342383 parent_registers = .{};
24352384
24362385 self.stack.deinit(self.gpa);
......@@ -2438,7 +2387,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24382387 parent_stack = .{};
24392388
24402389 self.next_stack_offset = parent_next_stack_offset;
2441 self.free_registers = parent_free_registers;
2390 self.register_manager.free_registers = parent_free_registers;
24422391
24432392 try self.performReloc(inst.base.src, reloc);
24442393 const else_branch = self.branch_stack.addOneAssumeCapacity();
......@@ -2552,6 +2501,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25522501 return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});
25532502 }
25542503
2504 fn genErrorToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
2505 return self.resolveInst(inst.operand);
2506 }
2507
2508 fn genIntToError(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
2509 return self.resolveInst(inst.operand);
2510 }
2511
25552512 fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
25562513 // A loop is a setup to be able to jump back to the beginning.
25572514 const start_index = self.code.items.len;
......@@ -2561,7 +2518,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25612518 }
25622519
25632520 /// Send control flow to the `index` of `self.code`.
2564 fn jump(self: *Self, src: usize, index: usize) !void {
2521 fn jump(self: *Self, src: LazySrcLoc, index: usize) !void {
25652522 switch (arch) {
25662523 .i386, .x86_64 => {
25672524 try self.code.ensureCapacity(self.code.items.len + 5);
......@@ -2618,7 +2575,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26182575 }
26192576 }
26202577
2621 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
2578 fn performReloc(self: *Self, src: LazySrcLoc, reloc: Reloc) !void {
26222579 switch (reloc) {
26232580 .rel32 => |pos| {
26242581 const amt = self.code.items.len - (pos + 4);
......@@ -2682,7 +2639,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26822639 }
26832640 }
26842641
2685 fn br(self: *Self, src: usize, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue {
2642 fn br(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue {
26862643 if (operand.ty.hasCodeGenBits()) {
26872644 const operand_mcv = try self.resolveInst(operand);
26882645 const block_mcv = @bitCast(MCValue, block.codegen.mcv);
......@@ -2695,7 +2652,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26952652 return self.brVoid(src, block);
26962653 }
26972654
2698 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
2655 fn brVoid(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block) !MCValue {
26992656 // Emit a jump with a relocation. It will be patched up after the block ends.
27002657 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
27012658
......@@ -2757,7 +2714,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27572714 return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
27582715 }
27592716
2760 if (inst.output) |output| {
2717 if (inst.output_name) |output| {
27612718 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
27622719 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
27632720 }
......@@ -2789,7 +2746,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27892746 return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});
27902747 }
27912748
2792 if (inst.output) |output| {
2749 if (inst.output_name) |output| {
27932750 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
27942751 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
27952752 }
......@@ -2819,7 +2776,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28192776 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
28202777 }
28212778
2822 if (inst.output) |output| {
2779 if (inst.output_name) |output| {
28232780 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
28242781 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
28252782 }
......@@ -2849,7 +2806,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28492806 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
28502807 }
28512808
2852 if (inst.output) |output| {
2809 if (inst.output_name) |output| {
28532810 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
28542811 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
28552812 }
......@@ -2899,7 +2856,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28992856 }
29002857
29012858 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
2902 fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void {
2859 fn setRegOrMem(self: *Self, src: LazySrcLoc, ty: Type, loc: MCValue, val: MCValue) !void {
29032860 switch (loc) {
29042861 .none => return,
29052862 .register => |reg| return self.genSetReg(src, ty, reg, val),
......@@ -2911,7 +2868,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29112868 }
29122869 }
29132870
2914 fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
2871 fn genSetStack(self: *Self, src: LazySrcLoc, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
29152872 switch (arch) {
29162873 .arm, .armeb => switch (mcv) {
29172874 .dead => unreachable,
......@@ -3111,7 +3068,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31113068 const adj_off = stack_offset + abi_size;
31123069
31133070 switch (abi_size) {
3114 4, 8 => {
3071 1, 2, 4, 8 => {
31153072 const offset = if (math.cast(i9, adj_off)) |imm|
31163073 Instruction.LoadStoreOffset.imm_post_index(-imm)
31173074 else |_|
......@@ -3121,8 +3078,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31213078 .aarch64_32 => .w29,
31223079 else => unreachable,
31233080 };
3081 const str = switch (abi_size) {
3082 1 => Instruction.strb,
3083 2 => Instruction.strh,
3084 4, 8 => Instruction.str,
3085 else => unreachable, // unexpected abi size
3086 };
31243087
3125 writeInt(u32, try self.code.addManyAsArray(4), Instruction.str(reg, rn, .{
3088 writeInt(u32, try self.code.addManyAsArray(4), str(reg, rn, .{
31263089 .offset = offset,
31273090 }).toU32());
31283091 },
......@@ -3144,7 +3107,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31443107 }
31453108 }
31463109
3147 fn genSetReg(self: *Self, src: usize, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
3110 fn genSetReg(self: *Self, src: LazySrcLoc, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
31483111 switch (arch) {
31493112 .arm, .armeb => switch (mcv) {
31503113 .dead => unreachable,
......@@ -3687,7 +3650,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
36873650 return mcv;
36883651 }
36893652
3690 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) InnerError!MCValue {
3653 fn genTypedValue(self: *Self, src: LazySrcLoc, typed_value: TypedValue) InnerError!MCValue {
36913654 if (typed_value.val.isUndef())
36923655 return MCValue{ .undef = {} };
36933656 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
......@@ -3762,7 +3725,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37623725 };
37633726
37643727 /// Caller must call `CallMCValues.deinit`.
3765 fn resolveCallingConventionValues(self: *Self, src: usize, fn_ty: Type) !CallMCValues {
3728 fn resolveCallingConventionValues(self: *Self, src: LazySrcLoc, fn_ty: Type) !CallMCValues {
37663729 const cc = fn_ty.fnCallingConvention();
37673730 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
37683731 defer self.gpa.free(param_types);
......@@ -3976,13 +3939,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
39763939 };
39773940 }
39783941
3979 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) InnerError {
3942 fn fail(self: *Self, src: LazySrcLoc, comptime format: []const u8, args: anytype) InnerError {
39803943 @setCold(true);
39813944 assert(self.err_msg == null);
3982 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, .{
3983 .file_scope = self.src_loc.file_scope,
3984 .byte_offset = src,
3985 }, format, args);
3945 const src_loc = if (src != .unneeded)
3946 src.toSrcLocWithDecl(self.mod_fn.owner_decl)
3947 else
3948 self.src_loc;
3949 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src_loc, format, args);
39863950 return error.CodegenFail;
39873951 }
39883952
......@@ -4012,9 +3976,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
40123976 },
40133977 };
40143978
4015 /// An integer whose bits represent all the registers and whether they are free.
4016 const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len);
4017
40183979 fn parseRegName(name: []const u8) ?Register {
40193980 if (@hasDecl(Register, "parseRegName")) {
40203981 return Register.parseRegName(name);
src/codegen/aarch64.zig+55-31
......@@ -484,7 +484,23 @@ pub const Instruction = union(enum) {
484484 }
485485 };
486486
487 fn loadStoreRegister(rt: Register, rn: Register, offset: LoadStoreOffset, load: bool) Instruction {
487 /// Which kind of load/store to perform
488 const LoadStoreVariant = enum {
489 /// 32-bit or 64-bit
490 normal,
491 /// 16-bit
492 half,
493 /// 8-bit
494 byte,
495 };
496
497 fn loadStoreRegister(
498 rt: Register,
499 rn: Register,
500 offset: LoadStoreOffset,
501 variant: LoadStoreVariant,
502 load: bool,
503 ) Instruction {
488504 const off = offset.toU12();
489505 const op1: u2 = blk: {
490506 switch (offset) {
......@@ -497,35 +513,27 @@ pub const Instruction = union(enum) {
497513 break :blk 0b00;
498514 };
499515 const opc: u2 = if (load) 0b01 else 0b00;
500 switch (rt.size()) {
501 32 => {
502 return Instruction{
503 .LoadStoreRegister = .{
504 .rt = rt.id(),
505 .rn = rn.id(),
506 .offset = offset.toU12(),
507 .opc = opc,
508 .op1 = op1,
509 .v = 0,
510 .size = 0b10,
511 },
512 };
513 },
514 64 => {
515 return Instruction{
516 .LoadStoreRegister = .{
517 .rt = rt.id(),
518 .rn = rn.id(),
519 .offset = offset.toU12(),
520 .opc = opc,
521 .op1 = op1,
522 .v = 0,
523 .size = 0b11,
524 },
525 };
516 return Instruction{
517 .LoadStoreRegister = .{
518 .rt = rt.id(),
519 .rn = rn.id(),
520 .offset = off,
521 .opc = opc,
522 .op1 = op1,
523 .v = 0,
524 .size = blk: {
525 switch (variant) {
526 .normal => switch (rt.size()) {
527 32 => break :blk 0b10,
528 64 => break :blk 0b11,
529 else => unreachable, // unexpected register size
530 },
531 .half => break :blk 0b01,
532 .byte => break :blk 0b00,
533 }
534 },
526535 },
527 else => unreachable, // unexpected register size
528 }
536 };
529537 }
530538
531539 fn loadStorePairOfRegisters(
......@@ -748,7 +756,7 @@ pub const Instruction = union(enum) {
748756
749757 pub fn ldr(rt: Register, args: LdrArgs) Instruction {
750758 switch (args) {
751 .register => |info| return loadStoreRegister(rt, info.rn, info.offset, true),
759 .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .normal, true),
752760 .literal => |literal| return loadLiteral(rt, literal),
753761 }
754762 }
......@@ -758,7 +766,15 @@ pub const Instruction = union(enum) {
758766 };
759767
760768 pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction {
761 return loadStoreRegister(rt, rn, args.offset, false);
769 return loadStoreRegister(rt, rn, args.offset, .normal, false);
770 }
771
772 pub fn strh(rt: Register, rn: Register, args: StrArgs) Instruction {
773 return loadStoreRegister(rt, rn, args.offset, .half, false);
774 }
775
776 pub fn strb(rt: Register, rn: Register, args: StrArgs) Instruction {
777 return loadStoreRegister(rt, rn, args.offset, .byte, false);
762778 }
763779
764780 // Load or store pair of registers
......@@ -996,6 +1012,14 @@ test "serialize instructions" {
9961012 .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }),
9971013 .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010,
9981014 },
1015 .{ // strh w0, [x1]
1016 .inst = Instruction.strh(.w0, .x1, .{}),
1017 .expected = 0b01_111_0_01_00_000000000000_00001_00000,
1018 },
1019 .{ // strb w8, [x9]
1020 .inst = Instruction.strb(.w8, .x9, .{}),
1021 .expected = 0b00_111_0_01_00_000000000000_01001_01000,
1022 },
9991023 .{ // adr x2, #0x8
10001024 .inst = Instruction.adr(.x2, 0x8),
10011025 .expected = 0b0_00_10000_0000000000000000010_00010,
src/codegen/c.zig+27-17
......@@ -14,6 +14,7 @@ const TypedValue = @import("../TypedValue.zig");
1414const C = link.File.C;
1515const Decl = Module.Decl;
1616const trace = @import("../tracy.zig").trace;
17const LazySrcLoc = Module.LazySrcLoc;
1718
1819const Mutability = enum { Const, Mut };
1920
......@@ -145,11 +146,10 @@ pub const DeclGen = struct {
145146 error_msg: ?*Module.ErrorMsg,
146147 typedefs: TypedefMap,
147148
148 fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
149 dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, .{
150 .file_scope = dg.decl.getFileScope(),
151 .byte_offset = src,
152 }, format, args);
149 fn fail(dg: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
150 @setCold(true);
151 const src_loc = src.toSrcLocWithDecl(dg.decl);
152 dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, src_loc, format, args);
153153 return error.AnalysisFail;
154154 }
155155
......@@ -160,7 +160,7 @@ pub const DeclGen = struct {
160160 val: Value,
161161 ) error{ OutOfMemory, AnalysisFail }!void {
162162 if (val.isUndef()) {
163 return dg.fail(dg.decl.src(), "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{});
163 return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{});
164164 }
165165 switch (t.zigTypeTag()) {
166166 .Int => {
......@@ -193,7 +193,7 @@ pub const DeclGen = struct {
193193 try writer.print("{s}", .{decl.name});
194194 },
195195 else => |e| return dg.fail(
196 dg.decl.src(),
196 .{ .node_offset = 0 },
197197 "TODO: C backend: implement Pointer value {s}",
198198 .{@tagName(e)},
199199 ),
......@@ -276,7 +276,7 @@ pub const DeclGen = struct {
276276 try writer.writeAll(", .error = 0 }");
277277 }
278278 },
279 else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{
279 else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{
280280 @tagName(e),
281281 }),
282282 }
......@@ -350,7 +350,7 @@ pub const DeclGen = struct {
350350 break;
351351 }
352352 } else {
353 return dg.fail(dg.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{});
353 return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement integer types larger than 128 bits", .{});
354354 }
355355 },
356356 else => unreachable,
......@@ -358,7 +358,7 @@ pub const DeclGen = struct {
358358 },
359359 .Pointer => {
360360 if (t.isSlice()) {
361 return dg.fail(dg.decl.src(), "TODO: C backend: implement slices", .{});
361 return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement slices", .{});
362362 } else {
363363 try dg.renderType(w, t.elemType());
364364 try w.writeAll(" *");
......@@ -431,7 +431,7 @@ pub const DeclGen = struct {
431431 dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
432432 },
433433 .Null, .Undefined => unreachable, // must be const or comptime
434 else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{
434 else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type {s}", .{
435435 @tagName(e),
436436 }),
437437 }
......@@ -569,13 +569,15 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
569569 .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?),
570570 .is_err => try genIsErr(o, inst.castTag(.is_err).?),
571571 .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),
572 .error_to_int => try genErrorToInt(o, inst.castTag(.error_to_int).?),
573 .int_to_error => try genIntToError(o, inst.castTag(.int_to_error).?),
572574 .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
573575 .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
574576 .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),
575577 .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?),
576578 .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?),
577579 .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?),
578 else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
580 else => |e| return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for {}", .{e}),
579581 };
580582 switch (result_value) {
581583 .none => {},
......@@ -756,11 +758,11 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue {
756758 try writer.writeAll(");\n");
757759 return result_local;
758760 } else {
759 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement function pointers", .{});
761 return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement function pointers", .{});
760762 }
761763}
762764
763fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue {
765fn genDbgStmt(o: *Object, inst: *Inst.DbgStmt) !CValue {
764766 // TODO emit #line directive here with line number and filename
765767 return CValue.none;
766768}
......@@ -913,13 +915,13 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
913915 try o.writeCValue(writer, arg_c_value);
914916 try writer.writeAll(";\n");
915917 } else {
916 return o.dg.fail(o.dg.decl.src(), "TODO non-explicit inline asm regs", .{});
918 return o.dg.fail(.{ .node_offset = 0 }, "TODO non-explicit inline asm regs", .{});
917919 }
918920 }
919921 const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";
920922 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source });
921923 if (as.output) |_| {
922 return o.dg.fail(o.dg.decl.src(), "TODO inline asm output", .{});
924 return o.dg.fail(.{ .node_offset = 0 }, "TODO inline asm output", .{});
923925 }
924926 if (as.inputs.len > 0) {
925927 if (as.output == null) {
......@@ -945,7 +947,7 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
945947 if (as.base.isUnused())
946948 return CValue.none;
947949
948 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});
950 return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: inline asm expression result used", .{});
949951}
950952
951953fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue {
......@@ -1072,6 +1074,14 @@ fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue {
10721074 return local;
10731075}
10741076
1077fn genIntToError(o: *Object, inst: *Inst.UnOp) !CValue {
1078 return o.resolveInst(inst.operand);
1079}
1080
1081fn genErrorToInt(o: *Object, inst: *Inst.UnOp) !CValue {
1082 return o.resolveInst(inst.operand);
1083}
1084
10751085fn IndentWriter(comptime UnderlyingWriter: type) type {
10761086 return struct {
10771087 const Self = @This();
src/codegen/llvm.zig+400-361
......@@ -15,6 +15,8 @@ const Inst = ir.Inst;
1515const Value = @import("../value.zig").Value;
1616const Type = @import("../type.zig").Type;
1717
18const LazySrcLoc = Module.LazySrcLoc;
19
1820pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
1921 const llvm_arch = switch (target.cpu.arch) {
2022 .arm => "arm",
......@@ -146,79 +148,42 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
146148 return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi });
147149}
148150
149pub const LLVMIRModule = struct {
150 module: *Module,
151pub const Object = struct {
151152 llvm_module: *const llvm.Module,
152153 context: *const llvm.Context,
153154 target_machine: *const llvm.TargetMachine,
154 builder: *const llvm.Builder,
155
156 object_path: []const u8,
157
158 gpa: *Allocator,
159 err_msg: ?*Module.ErrorMsg = null,
160
161 // TODO: The fields below should really move into a different struct,
162 // because they are only valid when generating a function
163
164 /// This stores the LLVM values used in a function, such that they can be
165 /// referred to in other instructions. This table is cleared before every function is generated.
166 /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks
167 /// in here, however if a block ends, the instructions can be thrown away.
168 func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{},
169
170 /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction.
171 args: []*const llvm.Value = &[_]*const llvm.Value{},
172 arg_index: usize = 0,
173
174 entry_block: *const llvm.BasicBlock = undefined,
175 /// This fields stores the last alloca instruction, such that we can append more alloca instructions
176 /// to the top of the function.
177 latest_alloca_inst: ?*const llvm.Value = null,
155 object_pathZ: [:0]const u8,
178156
179 llvm_func: *const llvm.Value = undefined,
180
181 /// This data structure is used to implement breaking to blocks.
182 blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct {
183 parent_bb: *const llvm.BasicBlock,
184 break_bbs: *BreakBasicBlocks,
185 break_vals: *BreakValues,
186 }) = .{},
187
188 src_loc: Module.SrcLoc,
189
190 const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock);
191 const BreakValues = std.ArrayListUnmanaged(*const llvm.Value);
192
193 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {
194 const self = try allocator.create(LLVMIRModule);
157 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Object {
158 const self = try allocator.create(Object);
195159 errdefer allocator.destroy(self);
196160
197 const gpa = options.module.?.gpa;
198
199 const obj_basename = try std.zig.binNameAlloc(gpa, .{
161 const obj_basename = try std.zig.binNameAlloc(allocator, .{
200162 .root_name = options.root_name,
201163 .target = options.target,
202164 .output_mode = .Obj,
203165 });
204 defer gpa.free(obj_basename);
166 defer allocator.free(obj_basename);
205167
206168 const o_directory = options.module.?.zig_cache_artifact_directory;
207 const object_path = try o_directory.join(gpa, &[_][]const u8{obj_basename});
208 errdefer gpa.free(object_path);
169 const object_path = try o_directory.join(allocator, &[_][]const u8{obj_basename});
170 defer allocator.free(object_path);
171
172 const object_pathZ = try allocator.dupeZ(u8, object_path);
173 errdefer allocator.free(object_pathZ);
209174
210175 const context = llvm.Context.create();
211176 errdefer context.dispose();
212177
213178 initializeLLVMTargets();
214179
215 const root_nameZ = try gpa.dupeZ(u8, options.root_name);
216 defer gpa.free(root_nameZ);
180 const root_nameZ = try allocator.dupeZ(u8, options.root_name);
181 defer allocator.free(root_nameZ);
217182 const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context);
218183 errdefer llvm_module.dispose();
219184
220 const llvm_target_triple = try targetTriple(gpa, options.target);
221 defer gpa.free(llvm_target_triple);
185 const llvm_target_triple = try targetTriple(allocator, options.target);
186 defer allocator.free(llvm_target_triple);
222187
223188 var error_message: [*:0]const u8 = undefined;
224189 var target: *const llvm.Target = undefined;
......@@ -253,34 +218,21 @@ pub const LLVMIRModule = struct {
253218 );
254219 errdefer target_machine.dispose();
255220
256 const builder = context.createBuilder();
257 errdefer builder.dispose();
258
259221 self.* = .{
260 .module = options.module.?,
261222 .llvm_module = llvm_module,
262223 .context = context,
263224 .target_machine = target_machine,
264 .builder = builder,
265 .object_path = object_path,
266 .gpa = gpa,
267 // TODO move this field into a struct that is only instantiated per gen() call
268 .src_loc = undefined,
225 .object_pathZ = object_pathZ,
269226 };
270227 return self;
271228 }
272229
273 pub fn deinit(self: *LLVMIRModule, allocator: *Allocator) void {
274 self.builder.dispose();
230 pub fn deinit(self: *Object, allocator: *Allocator) void {
275231 self.target_machine.dispose();
276232 self.llvm_module.dispose();
277233 self.context.dispose();
278234
279 self.func_inst_table.deinit(self.gpa);
280 self.gpa.free(self.object_path);
281
282 self.blocks.deinit(self.gpa);
283
235 allocator.free(self.object_pathZ);
284236 allocator.destroy(self);
285237 }
286238
......@@ -292,7 +244,7 @@ pub const LLVMIRModule = struct {
292244 llvm.initializeAllAsmParsers();
293245 }
294246
295 pub fn flushModule(self: *LLVMIRModule, comp: *Compilation) !void {
247 pub fn flushModule(self: *Object, comp: *Compilation) !void {
296248 if (comp.verbose_llvm_ir) {
297249 const dump = self.llvm_module.printToString();
298250 defer llvm.disposeMessage(dump);
......@@ -313,13 +265,10 @@ pub const LLVMIRModule = struct {
313265 }
314266 }
315267
316 const object_pathZ = try self.gpa.dupeZ(u8, self.object_path);
317 defer self.gpa.free(object_pathZ);
318
319268 var error_message: [*:0]const u8 = undefined;
320269 if (self.target_machine.emitToFile(
321270 self.llvm_module,
322 object_pathZ.ptr,
271 self.object_pathZ.ptr,
323272 .ObjectFile,
324273 &error_message,
325274 ).toBool()) {
......@@ -331,44 +280,68 @@ pub const LLVMIRModule = struct {
331280 }
332281 }
333282
334 pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
335 self.gen(module, decl) catch |err| switch (err) {
283 pub fn updateDecl(self: *Object, module: *Module, decl: *Module.Decl) !void {
284 var dg: DeclGen = .{
285 .object = self,
286 .module = module,
287 .decl = decl,
288 .err_msg = null,
289 .gpa = module.gpa,
290 };
291 dg.genDecl() catch |err| switch (err) {
336292 error.CodegenFail => {
337293 decl.analysis = .codegen_failure;
338 try module.failed_decls.put(module.gpa, decl, self.err_msg.?);
339 self.err_msg = null;
294 try module.failed_decls.put(module.gpa, decl, dg.err_msg.?);
295 dg.err_msg = null;
340296 return;
341297 },
342298 else => |e| return e,
343299 };
344300 }
301};
345302
346 fn gen(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
347 const typed_value = decl.typed_value.most_recent.typed_value;
348 const src = decl.src();
303pub const DeclGen = struct {
304 object: *Object,
305 module: *Module,
306 decl: *Module.Decl,
307 err_msg: ?*Module.ErrorMsg,
349308
350 self.src_loc = decl.srcLoc();
309 gpa: *Allocator,
310
311 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
312 @setCold(true);
313 assert(self.err_msg == null);
314 const src_loc = @as(LazySrcLoc, .{ .node_offset = 0 }).toSrcLocWithDecl(self.decl);
315 self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, "TODO (LLVM): " ++ format, args);
316 return error.CodegenFail;
317 }
318
319 fn llvmModule(self: *DeclGen) *const llvm.Module {
320 return self.object.llvm_module;
321 }
322
323 fn context(self: *DeclGen) *const llvm.Context {
324 return self.object.context;
325 }
326
327 fn genDecl(self: *DeclGen) !void {
328 const decl = self.decl;
329 const typed_value = decl.typed_value.most_recent.typed_value;
351330
352331 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val });
353332
354333 if (typed_value.val.castTag(.function)) |func_payload| {
355334 const func = func_payload.data;
356335
357 const llvm_func = try self.resolveLLVMFunction(func.owner_decl, src);
336 const llvm_func = try self.resolveLLVMFunction(func.owner_decl);
358337
359338 // This gets the LLVM values from the function and stores them in `self.args`.
360339 const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen();
361340 var args = try self.gpa.alloc(*const llvm.Value, fn_param_len);
362 defer self.gpa.free(args);
363341
364342 for (args) |*arg, i| {
365343 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));
366344 }
367 self.args = args;
368 self.arg_index = 0;
369
370 // Make sure no other LLVM values from other functions can be referenced
371 self.func_inst_table.clearRetainingCapacity();
372345
373346 // We remove all the basic blocks of a function to support incremental
374347 // compilation!
......@@ -377,20 +350,293 @@ pub const LLVMIRModule = struct {
377350 bb.deleteBasicBlock();
378351 }
379352
380 self.entry_block = self.context.appendBasicBlock(llvm_func, "Entry");
381 self.builder.positionBuilderAtEnd(self.entry_block);
382 self.latest_alloca_inst = null;
383 self.llvm_func = llvm_func;
353 const builder = self.context().createBuilder();
354
355 const entry_block = self.context().appendBasicBlock(llvm_func, "Entry");
356 builder.positionBuilderAtEnd(entry_block);
357
358 var fg: FuncGen = .{
359 .dg = self,
360 .builder = builder,
361 .args = args,
362 .arg_index = 0,
363 .func_inst_table = .{},
364 .entry_block = entry_block,
365 .latest_alloca_inst = null,
366 .llvm_func = llvm_func,
367 .blocks = .{},
368 };
369 defer fg.deinit();
384370
385 try self.genBody(func.body);
371 try fg.genBody(func.body);
386372 } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| {
387 _ = try self.resolveLLVMFunction(extern_fn.data, src);
373 _ = try self.resolveLLVMFunction(extern_fn.data);
388374 } else {
389 _ = try self.resolveGlobalDecl(decl, src);
375 _ = try self.resolveGlobalDecl(decl);
376 }
377 }
378
379 /// If the llvm function does not exist, create it
380 fn resolveLLVMFunction(self: *DeclGen, func: *Module.Decl) !*const llvm.Value {
381 // TODO: do we want to store this in our own datastructure?
382 if (self.llvmModule().getNamedFunction(func.name)) |llvm_fn| return llvm_fn;
383
384 const zig_fn_type = func.typed_value.most_recent.typed_value.ty;
385 const return_type = zig_fn_type.fnReturnType();
386
387 const fn_param_len = zig_fn_type.fnParamLen();
388
389 const fn_param_types = try self.gpa.alloc(Type, fn_param_len);
390 defer self.gpa.free(fn_param_types);
391 zig_fn_type.fnParamTypes(fn_param_types);
392
393 const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len);
394 defer self.gpa.free(llvm_param);
395
396 for (fn_param_types) |fn_param, i| {
397 llvm_param[i] = try self.getLLVMType(fn_param);
398 }
399
400 const fn_type = llvm.Type.functionType(
401 try self.getLLVMType(return_type),
402 if (fn_param_len == 0) null else llvm_param.ptr,
403 @intCast(c_uint, fn_param_len),
404 .False,
405 );
406 const llvm_fn = self.llvmModule().addFunction(func.name, fn_type);
407
408 if (return_type.tag() == .noreturn) {
409 self.addFnAttr(llvm_fn, "noreturn");
410 }
411
412 return llvm_fn;
413 }
414
415 fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
416 // TODO: do we want to store this in our own datastructure?
417 if (self.llvmModule().getNamedGlobal(decl.name)) |val| return val;
418
419 const typed_value = decl.typed_value.most_recent.typed_value;
420
421 // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`.
422 const llvm_type = try self.getLLVMType(typed_value.ty);
423 const val = try self.genTypedValue(typed_value, null);
424 const global = self.llvmModule().addGlobal(llvm_type, decl.name);
425 llvm.setInitializer(global, val);
426
427 // TODO ask the Decl if it is const
428 // https://github.com/ziglang/zig/issues/7582
429
430 return global;
431 }
432
433 fn getLLVMType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
434 switch (t.zigTypeTag()) {
435 .Void => return self.context().voidType(),
436 .NoReturn => return self.context().voidType(),
437 .Int => {
438 const info = t.intInfo(self.module.getTarget());
439 return self.context().intType(info.bits);
440 },
441 .Bool => return self.context().intType(1),
442 .Pointer => {
443 if (t.isSlice()) {
444 return self.todo("implement slices", .{});
445 } else {
446 const elem_type = try self.getLLVMType(t.elemType());
447 return elem_type.pointerType(0);
448 }
449 },
450 .Array => {
451 const elem_type = try self.getLLVMType(t.elemType());
452 return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
453 },
454 .Optional => {
455 if (!t.isPtrLikeOptional()) {
456 var buf: Type.Payload.ElemType = undefined;
457 const child_type = t.optionalChild(&buf);
458
459 var optional_types: [2]*const llvm.Type = .{
460 try self.getLLVMType(child_type),
461 self.context().intType(1),
462 };
463 return self.context().structType(&optional_types, 2, .False);
464 } else {
465 return self.todo("implement optional pointers as actual pointers", .{});
466 }
467 },
468 else => return self.todo("implement getLLVMType for type '{}'", .{t}),
469 }
470 }
471
472 // TODO: figure out a way to remove the FuncGen argument
473 fn genTypedValue(self: *DeclGen, tv: TypedValue, fg: ?*FuncGen) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
474 const llvm_type = try self.getLLVMType(tv.ty);
475
476 if (tv.val.isUndef())
477 return llvm_type.getUndef();
478
479 switch (tv.ty.zigTypeTag()) {
480 .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
481 .Int => {
482 var bigint_space: Value.BigIntSpace = undefined;
483 const bigint = tv.val.toBigInt(&bigint_space);
484
485 if (bigint.eqZero()) return llvm_type.constNull();
486
487 if (bigint.limbs.len != 1) {
488 return self.todo("implement bigger bigint", .{});
489 }
490 const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
491 if (!bigint.positive) {
492 return llvm.constNeg(llvm_int);
493 }
494 return llvm_int;
495 },
496 .Pointer => switch (tv.val.tag()) {
497 .decl_ref => {
498 const decl = tv.val.castTag(.decl_ref).?.data;
499 const val = try self.resolveGlobalDecl(decl);
500
501 const usize_type = try self.getLLVMType(Type.initTag(.usize));
502
503 // TODO: second index should be the index into the memory!
504 var indices: [2]*const llvm.Value = .{
505 usize_type.constNull(),
506 usize_type.constNull(),
507 };
508
509 // TODO: consider using buildInBoundsGEP2 for opaque pointers
510 return fg.?.builder.buildInBoundsGEP(val, &indices, 2, "");
511 },
512 .ref_val => {
513 const elem_value = tv.val.castTag(.ref_val).?.data;
514 const elem_type = tv.ty.castPointer().?.data;
515 const alloca = fg.?.buildAlloca(try self.getLLVMType(elem_type));
516 _ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca);
517 return alloca;
518 },
519 else => return self.todo("implement const of pointer type '{}'", .{tv.ty}),
520 },
521 .Array => {
522 if (tv.val.castTag(.bytes)) |payload| {
523 const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: {
524 if (sentinel.tag() == .zero) break :blk true;
525 return self.todo("handle other sentinel values", .{});
526 } else false;
527
528 return self.context().constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel));
529 } else {
530 return self.todo("handle more array values", .{});
531 }
532 },
533 .Optional => {
534 if (!tv.ty.isPtrLikeOptional()) {
535 var buf: Type.Payload.ElemType = undefined;
536 const child_type = tv.ty.optionalChild(&buf);
537 const llvm_child_type = try self.getLLVMType(child_type);
538
539 if (tv.val.tag() == .null_value) {
540 var optional_values: [2]*const llvm.Value = .{
541 llvm_child_type.constNull(),
542 self.context().intType(1).constNull(),
543 };
544 return self.context().constStruct(&optional_values, 2, .False);
545 } else {
546 var optional_values: [2]*const llvm.Value = .{
547 try self.genTypedValue(.{ .ty = child_type, .val = tv.val }, fg),
548 self.context().intType(1).constAllOnes(),
549 };
550 return self.context().constStruct(&optional_values, 2, .False);
551 }
552 } else {
553 return self.todo("implement const of optional pointer", .{});
554 }
555 },
556 else => return self.todo("implement const of type '{}'", .{tv.ty}),
557 }
558 }
559
560 // Helper functions
561 fn addAttr(self: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
562 const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);
563 assert(kind_id != 0);
564 const llvm_attr = self.context().createEnumAttribute(kind_id, 0);
565 val.addAttributeAtIndex(index, llvm_attr);
566 }
567
568 fn addFnAttr(self: *DeclGen, val: *const llvm.Value, attr_name: []const u8) void {
569 // TODO: improve this API, `addAttr(-1, attr_name)`
570 self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name);
571 }
572};
573
574pub const FuncGen = struct {
575 dg: *DeclGen,
576
577 builder: *const llvm.Builder,
578
579 /// This stores the LLVM values used in a function, such that they can be
580 /// referred to in other instructions. This table is cleared before every function is generated.
581 /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks
582 /// in here, however if a block ends, the instructions can be thrown away.
583 func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value),
584
585 /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction.
586 args: []*const llvm.Value,
587 arg_index: usize,
588
589 entry_block: *const llvm.BasicBlock,
590 /// This fields stores the last alloca instruction, such that we can append more alloca instructions
591 /// to the top of the function.
592 latest_alloca_inst: ?*const llvm.Value,
593
594 llvm_func: *const llvm.Value,
595
596 /// This data structure is used to implement breaking to blocks.
597 blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct {
598 parent_bb: *const llvm.BasicBlock,
599 break_bbs: *BreakBasicBlocks,
600 break_vals: *BreakValues,
601 }),
602
603 const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock);
604 const BreakValues = std.ArrayListUnmanaged(*const llvm.Value);
605
606 fn deinit(self: *FuncGen) void {
607 self.builder.dispose();
608 self.func_inst_table.deinit(self.gpa());
609 self.gpa().free(self.args);
610 self.blocks.deinit(self.gpa());
611 }
612
613 fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
614 @setCold(true);
615 return self.dg.todo(format, args);
616 }
617
618 fn llvmModule(self: *FuncGen) *const llvm.Module {
619 return self.dg.object.llvm_module;
620 }
621
622 fn context(self: *FuncGen) *const llvm.Context {
623 return self.dg.object.context;
624 }
625
626 fn gpa(self: *FuncGen) *Allocator {
627 return self.dg.gpa;
628 }
629
630 fn resolveInst(self: *FuncGen, inst: *ir.Inst) !*const llvm.Value {
631 if (inst.value()) |val| {
632 return self.dg.genTypedValue(.{ .ty = inst.ty, .val = val }, self);
390633 }
634 if (self.func_inst_table.get(inst)) |value| return value;
635
636 return self.todo("implement global llvm values (or the value is not in the func_inst_table table)", .{});
391637 }
392638
393 fn genBody(self: *LLVMIRModule, body: ir.Body) error{ OutOfMemory, CodegenFail }!void {
639 fn genBody(self: *FuncGen, body: ir.Body) error{ OutOfMemory, CodegenFail }!void {
394640 for (body.instructions) |inst| {
395641 const opt_value = switch (inst.tag) {
396642 .add => try self.genAdd(inst.castTag(.add).?),
......@@ -428,13 +674,13 @@ pub const LLVMIRModule = struct {
428674 // TODO: implement debug info
429675 break :blk null;
430676 },
431 else => |tag| return self.fail(inst.src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),
677 else => |tag| return self.todo("implement TZIR instruction: {}", .{tag}),
432678 };
433 if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val);
679 if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa(), inst, val);
434680 }
435681 }
436682
437 fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value {
683 fn genCall(self: *FuncGen, inst: *Inst.Call) !?*const llvm.Value {
438684 if (inst.func.value()) |func_value| {
439685 const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn|
440686 extern_fn.data
......@@ -444,12 +690,12 @@ pub const LLVMIRModule = struct {
444690 unreachable;
445691
446692 const zig_fn_type = fn_decl.typed_value.most_recent.typed_value.ty;
447 const llvm_fn = try self.resolveLLVMFunction(fn_decl, inst.base.src);
693 const llvm_fn = try self.dg.resolveLLVMFunction(fn_decl);
448694
449695 const num_args = inst.args.len;
450696
451 const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, num_args);
452 defer self.gpa.free(llvm_param_vals);
697 const llvm_param_vals = try self.gpa().alloc(*const llvm.Value, num_args);
698 defer self.gpa().free(llvm_param_vals);
453699
454700 for (inst.args) |arg, i| {
455701 llvm_param_vals[i] = try self.resolveInst(arg);
......@@ -474,27 +720,32 @@ pub const LLVMIRModule = struct {
474720
475721 return call;
476722 } else {
477 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
723 return self.todo("implement calling runtime known function pointer", .{});
478724 }
479725 }
480726
481 fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value {
727 fn genRetVoid(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value {
482728 _ = self.builder.buildRetVoid();
483729 return null;
484730 }
485731
486 fn genRet(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
732 fn genRet(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
733 if (!inst.operand.ty.hasCodeGenBits()) {
734 // TODO: in astgen these instructions should turn into `retvoid` instructions.
735 _ = self.builder.buildRetVoid();
736 return null;
737 }
487738 _ = self.builder.buildRet(try self.resolveInst(inst.operand));
488739 return null;
489740 }
490741
491 fn genCmp(self: *LLVMIRModule, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value {
742 fn genCmp(self: *FuncGen, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value {
492743 const lhs = try self.resolveInst(inst.lhs);
493744 const rhs = try self.resolveInst(inst.rhs);
494745
495746 if (!inst.base.ty.isInt())
496747 if (inst.base.ty.tag() != .bool)
497 return self.fail(inst.base.src, "TODO implement 'genCmp' for type {}", .{inst.base.ty});
748 return self.todo("implement 'genCmp' for type {}", .{inst.base.ty});
498749
499750 const is_signed = inst.base.ty.isSignedInt();
500751 const operation = switch (op) {
......@@ -509,21 +760,21 @@ pub const LLVMIRModule = struct {
509760 return self.builder.buildICmp(operation, lhs, rhs, "");
510761 }
511762
512 fn genBlock(self: *LLVMIRModule, inst: *Inst.Block) !?*const llvm.Value {
513 const parent_bb = self.context.createBasicBlock("Block");
763 fn genBlock(self: *FuncGen, inst: *Inst.Block) !?*const llvm.Value {
764 const parent_bb = self.context().createBasicBlock("Block");
514765
515766 // 5 breaks to a block seems like a reasonable default.
516 var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5);
517 var break_vals = try BreakValues.initCapacity(self.gpa, 5);
518 try self.blocks.putNoClobber(self.gpa, inst, .{
767 var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa(), 5);
768 var break_vals = try BreakValues.initCapacity(self.gpa(), 5);
769 try self.blocks.putNoClobber(self.gpa(), inst, .{
519770 .parent_bb = parent_bb,
520771 .break_bbs = &break_bbs,
521772 .break_vals = &break_vals,
522773 });
523774 defer {
524775 self.blocks.removeAssertDiscard(inst);
525 break_bbs.deinit(self.gpa);
526 break_vals.deinit(self.gpa);
776 break_bbs.deinit(self.gpa());
777 break_vals.deinit(self.gpa());
527778 }
528779
529780 try self.genBody(inst.body);
......@@ -534,7 +785,7 @@ pub const LLVMIRModule = struct {
534785 // If the block does not return a value, we dont have to create a phi node.
535786 if (!inst.base.ty.hasCodeGenBits()) return null;
536787
537 const phi_node = self.builder.buildPhi(try self.getLLVMType(inst.base.ty, inst.base.src), "");
788 const phi_node = self.builder.buildPhi(try self.dg.getLLVMType(inst.base.ty), "");
538789 phi_node.addIncoming(
539790 break_vals.items.ptr,
540791 break_bbs.items.ptr,
......@@ -543,7 +794,7 @@ pub const LLVMIRModule = struct {
543794 return phi_node;
544795 }
545796
546 fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value {
797 fn genBr(self: *FuncGen, inst: *Inst.Br) !?*const llvm.Value {
547798 var block = self.blocks.get(inst.block).?;
548799
549800 // If the break doesn't break a value, then we don't have to add
......@@ -556,25 +807,25 @@ pub const LLVMIRModule = struct {
556807
557808 // For the phi node, we need the basic blocks and the values of the
558809 // break instructions.
559 try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());
560 try block.break_vals.append(self.gpa, val);
810 try block.break_bbs.append(self.gpa(), self.builder.getInsertBlock());
811 try block.break_vals.append(self.gpa(), val);
561812
562813 _ = self.builder.buildBr(block.parent_bb);
563814 }
564815 return null;
565816 }
566817
567 fn genBrVoid(self: *LLVMIRModule, inst: *Inst.BrVoid) !?*const llvm.Value {
818 fn genBrVoid(self: *FuncGen, inst: *Inst.BrVoid) !?*const llvm.Value {
568819 var block = self.blocks.get(inst.block).?;
569820 _ = self.builder.buildBr(block.parent_bb);
570821 return null;
571822 }
572823
573 fn genCondBr(self: *LLVMIRModule, inst: *Inst.CondBr) !?*const llvm.Value {
824 fn genCondBr(self: *FuncGen, inst: *Inst.CondBr) !?*const llvm.Value {
574825 const condition_value = try self.resolveInst(inst.condition);
575826
576 const then_block = self.context.appendBasicBlock(self.llvm_func, "Then");
577 const else_block = self.context.appendBasicBlock(self.llvm_func, "Else");
827 const then_block = self.context().appendBasicBlock(self.llvm_func, "Then");
828 const else_block = self.context().appendBasicBlock(self.llvm_func, "Else");
578829 {
579830 const prev_block = self.builder.getInsertBlock();
580831 defer self.builder.positionBuilderAtEnd(prev_block);
......@@ -589,8 +840,8 @@ pub const LLVMIRModule = struct {
589840 return null;
590841 }
591842
592 fn genLoop(self: *LLVMIRModule, inst: *Inst.Loop) !?*const llvm.Value {
593 const loop_block = self.context.appendBasicBlock(self.llvm_func, "Loop");
843 fn genLoop(self: *FuncGen, inst: *Inst.Loop) !?*const llvm.Value {
844 const loop_block = self.context().appendBasicBlock(self.llvm_func, "Loop");
594845 _ = self.builder.buildBr(loop_block);
595846
596847 self.builder.positionBuilderAtEnd(loop_block);
......@@ -600,20 +851,20 @@ pub const LLVMIRModule = struct {
600851 return null;
601852 }
602853
603 fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
854 fn genNot(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
604855 return self.builder.buildNot(try self.resolveInst(inst.operand), "");
605856 }
606857
607 fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value {
858 fn genUnreach(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value {
608859 _ = self.builder.buildUnreachable();
609860 return null;
610861 }
611862
612 fn genIsNonNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
863 fn genIsNonNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
613864 const operand = try self.resolveInst(inst.operand);
614865
615866 if (operand_is_ptr) {
616 const index_type = self.context.intType(32);
867 const index_type = self.context().intType(32);
617868
618869 var indices: [2]*const llvm.Value = .{
619870 index_type.constNull(),
......@@ -626,15 +877,15 @@ pub const LLVMIRModule = struct {
626877 }
627878 }
628879
629 fn genIsNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
880 fn genIsNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
630881 return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, "");
631882 }
632883
633 fn genOptionalPayload(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
884 fn genOptionalPayload(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
634885 const operand = try self.resolveInst(inst.operand);
635886
636887 if (operand_is_ptr) {
637 const index_type = self.context.intType(32);
888 const index_type = self.context().intType(32);
638889
639890 var indices: [2]*const llvm.Value = .{
640891 index_type.constNull(),
......@@ -647,12 +898,12 @@ pub const LLVMIRModule = struct {
647898 }
648899 }
649900
650 fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
901 fn genAdd(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {
651902 const lhs = try self.resolveInst(inst.lhs);
652903 const rhs = try self.resolveInst(inst.rhs);
653904
654905 if (!inst.base.ty.isInt())
655 return self.fail(inst.base.src, "TODO implement 'genAdd' for type {}", .{inst.base.ty});
906 return self.todo("implement 'genAdd' for type {}", .{inst.base.ty});
656907
657908 return if (inst.base.ty.isSignedInt())
658909 self.builder.buildNSWAdd(lhs, rhs, "")
......@@ -660,12 +911,12 @@ pub const LLVMIRModule = struct {
660911 self.builder.buildNUWAdd(lhs, rhs, "");
661912 }
662913
663 fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
914 fn genSub(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {
664915 const lhs = try self.resolveInst(inst.lhs);
665916 const rhs = try self.resolveInst(inst.rhs);
666917
667918 if (!inst.base.ty.isInt())
668 return self.fail(inst.base.src, "TODO implement 'genSub' for type {}", .{inst.base.ty});
919 return self.todo("implement 'genSub' for type {}", .{inst.base.ty});
669920
670921 return if (inst.base.ty.isSignedInt())
671922 self.builder.buildNSWSub(lhs, rhs, "")
......@@ -673,44 +924,44 @@ pub const LLVMIRModule = struct {
673924 self.builder.buildNUWSub(lhs, rhs, "");
674925 }
675926
676 fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
927 fn genIntCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
677928 const val = try self.resolveInst(inst.operand);
678929
679930 const signed = inst.base.ty.isSignedInt();
680931 // TODO: Should we use intcast here or just a simple bitcast?
681932 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes
682 return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), llvm.Bool.fromBool(signed), "");
933 return self.builder.buildIntCast2(val, try self.dg.getLLVMType(inst.base.ty), llvm.Bool.fromBool(signed), "");
683934 }
684935
685 fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
936 fn genBitCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
686937 const val = try self.resolveInst(inst.operand);
687 const dest_type = try self.getLLVMType(inst.base.ty, inst.base.src);
938 const dest_type = try self.dg.getLLVMType(inst.base.ty);
688939
689940 return self.builder.buildBitCast(val, dest_type, "");
690941 }
691942
692 fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.Value {
943 fn genArg(self: *FuncGen, inst: *Inst.Arg) !?*const llvm.Value {
693944 const arg_val = self.args[self.arg_index];
694945 self.arg_index += 1;
695946
696 const ptr_val = self.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src));
947 const ptr_val = self.buildAlloca(try self.dg.getLLVMType(inst.base.ty));
697948 _ = self.builder.buildStore(arg_val, ptr_val);
698949 return self.builder.buildLoad(ptr_val, "");
699950 }
700951
701 fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value {
952 fn genAlloc(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value {
702953 // buildAlloca expects the pointee type, not the pointer type, so assert that
703954 // a Payload.PointerSimple is passed to the alloc instruction.
704955 const pointee_type = inst.base.ty.castPointer().?.data;
705956
706957 // TODO: figure out a way to get the name of the var decl.
707958 // TODO: set alignment and volatile
708 return self.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src));
959 return self.buildAlloca(try self.dg.getLLVMType(pointee_type));
709960 }
710961
711962 /// Use this instead of builder.buildAlloca, because this function makes sure to
712963 /// put the alloca instruction at the top of the function!
713 fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value {
964 fn buildAlloca(self: *FuncGen, t: *const llvm.Type) *const llvm.Value {
714965 const prev_block = self.builder.getInsertBlock();
715966 defer self.builder.positionBuilderAtEnd(prev_block);
716967
......@@ -732,242 +983,30 @@ pub const LLVMIRModule = struct {
732983 return val;
733984 }
734985
735 fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
986 fn genStore(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {
736987 const val = try self.resolveInst(inst.rhs);
737988 const ptr = try self.resolveInst(inst.lhs);
738989 _ = self.builder.buildStore(val, ptr);
739990 return null;
740991 }
741992
742 fn genLoad(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
993 fn genLoad(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
743994 const ptr_val = try self.resolveInst(inst.operand);
744995 return self.builder.buildLoad(ptr_val, "");
745996 }
746997
747 fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value {
998 fn genBreakpoint(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value {
748999 const llvn_fn = self.getIntrinsic("llvm.debugtrap");
7491000 _ = self.builder.buildCall(llvn_fn, null, 0, "");
7501001 return null;
7511002 }
7521003
753 fn getIntrinsic(self: *LLVMIRModule, name: []const u8) *const llvm.Value {
1004 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
7541005 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
7551006 assert(id != 0);
7561007 // TODO: add support for overload intrinsics by passing the prefix of the intrinsic
7571008 // to `lookupIntrinsicID` and then passing the correct types to
7581009 // `getIntrinsicDeclaration`
759 return self.llvm_module.getIntrinsicDeclaration(id, null, 0);
760 }
761
762 fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.Value {
763 if (inst.value()) |val| {
764 return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val });
765 }
766 if (self.func_inst_table.get(inst)) |value| return value;
767
768 return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{});
769 }
770
771 fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
772 const llvm_type = try self.getLLVMType(tv.ty, src);
773
774 if (tv.val.isUndef())
775 return llvm_type.getUndef();
776
777 switch (tv.ty.zigTypeTag()) {
778 .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
779 .Int => {
780 var bigint_space: Value.BigIntSpace = undefined;
781 const bigint = tv.val.toBigInt(&bigint_space);
782
783 if (bigint.eqZero()) return llvm_type.constNull();
784
785 if (bigint.limbs.len != 1) {
786 return self.fail(src, "TODO implement bigger bigint", .{});
787 }
788 const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
789 if (!bigint.positive) {
790 return llvm.constNeg(llvm_int);
791 }
792 return llvm_int;
793 },
794 .Pointer => switch (tv.val.tag()) {
795 .decl_ref => {
796 const decl = tv.val.castTag(.decl_ref).?.data;
797 const val = try self.resolveGlobalDecl(decl, src);
798
799 const usize_type = try self.getLLVMType(Type.initTag(.usize), src);
800
801 // TODO: second index should be the index into the memory!
802 var indices: [2]*const llvm.Value = .{
803 usize_type.constNull(),
804 usize_type.constNull(),
805 };
806
807 // TODO: consider using buildInBoundsGEP2 for opaque pointers
808 return self.builder.buildInBoundsGEP(val, &indices, 2, "");
809 },
810 .ref_val => {
811 const elem_value = tv.val.castTag(.ref_val).?.data;
812 const elem_type = tv.ty.castPointer().?.data;
813 const alloca = self.buildAlloca(try self.getLLVMType(elem_type, src));
814 _ = self.builder.buildStore(try self.genTypedValue(src, .{ .ty = elem_type, .val = elem_value }), alloca);
815 return alloca;
816 },
817 else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}),
818 },
819 .Array => {
820 if (tv.val.castTag(.bytes)) |payload| {
821 const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: {
822 if (sentinel.tag() == .zero) break :blk true;
823 return self.fail(src, "TODO handle other sentinel values", .{});
824 } else false;
825
826 return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel));
827 } else {
828 return self.fail(src, "TODO handle more array values", .{});
829 }
830 },
831 .Optional => {
832 if (!tv.ty.isPtrLikeOptional()) {
833 var buf: Type.Payload.ElemType = undefined;
834 const child_type = tv.ty.optionalChild(&buf);
835 const llvm_child_type = try self.getLLVMType(child_type, src);
836
837 if (tv.val.tag() == .null_value) {
838 var optional_values: [2]*const llvm.Value = .{
839 llvm_child_type.constNull(),
840 self.context.intType(1).constNull(),
841 };
842 return self.context.constStruct(&optional_values, 2, .False);
843 } else {
844 var optional_values: [2]*const llvm.Value = .{
845 try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),
846 self.context.intType(1).constAllOnes(),
847 };
848 return self.context.constStruct(&optional_values, 2, .False);
849 }
850 } else {
851 return self.fail(src, "TODO implement const of optional pointer", .{});
852 }
853 },
854 else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),
855 }
856 }
857
858 fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
859 switch (t.zigTypeTag()) {
860 .Void => return self.context.voidType(),
861 .NoReturn => return self.context.voidType(),
862 .Int => {
863 const info = t.intInfo(self.module.getTarget());
864 return self.context.intType(info.bits);
865 },
866 .Bool => return self.context.intType(1),
867 .Pointer => {
868 if (t.isSlice()) {
869 return self.fail(src, "TODO: LLVM backend: implement slices", .{});
870 } else {
871 const elem_type = try self.getLLVMType(t.elemType(), src);
872 return elem_type.pointerType(0);
873 }
874 },
875 .Array => {
876 const elem_type = try self.getLLVMType(t.elemType(), src);
877 return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
878 },
879 .Optional => {
880 if (!t.isPtrLikeOptional()) {
881 var buf: Type.Payload.ElemType = undefined;
882 const child_type = t.optionalChild(&buf);
883
884 var optional_types: [2]*const llvm.Type = .{
885 try self.getLLVMType(child_type, src),
886 self.context.intType(1),
887 };
888 return self.context.structType(&optional_types, 2, .False);
889 } else {
890 return self.fail(src, "TODO implement optional pointers as actual pointers", .{});
891 }
892 },
893 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
894 }
895 }
896
897 fn resolveGlobalDecl(self: *LLVMIRModule, decl: *Module.Decl, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
898 // TODO: do we want to store this in our own datastructure?
899 if (self.llvm_module.getNamedGlobal(decl.name)) |val| return val;
900
901 const typed_value = decl.typed_value.most_recent.typed_value;
902
903 // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`.
904 const llvm_type = try self.getLLVMType(typed_value.ty, src);
905 const val = try self.genTypedValue(src, typed_value);
906 const global = self.llvm_module.addGlobal(llvm_type, decl.name);
907 llvm.setInitializer(global, val);
908
909 // TODO ask the Decl if it is const
910 // https://github.com/ziglang/zig/issues/7582
911
912 return global;
913 }
914
915 /// If the llvm function does not exist, create it
916 fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Decl, src: usize) !*const llvm.Value {
917 // TODO: do we want to store this in our own datastructure?
918 if (self.llvm_module.getNamedFunction(func.name)) |llvm_fn| return llvm_fn;
919
920 const zig_fn_type = func.typed_value.most_recent.typed_value.ty;
921 const return_type = zig_fn_type.fnReturnType();
922
923 const fn_param_len = zig_fn_type.fnParamLen();
924
925 const fn_param_types = try self.gpa.alloc(Type, fn_param_len);
926 defer self.gpa.free(fn_param_types);
927 zig_fn_type.fnParamTypes(fn_param_types);
928
929 const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len);
930 defer self.gpa.free(llvm_param);
931
932 for (fn_param_types) |fn_param, i| {
933 llvm_param[i] = try self.getLLVMType(fn_param, src);
934 }
935
936 const fn_type = llvm.Type.functionType(
937 try self.getLLVMType(return_type, src),
938 if (fn_param_len == 0) null else llvm_param.ptr,
939 @intCast(c_uint, fn_param_len),
940 .False,
941 );
942 const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);
943
944 if (return_type.tag() == .noreturn) {
945 self.addFnAttr(llvm_fn, "noreturn");
946 }
947
948 return llvm_fn;
949 }
950
951 // Helper functions
952 fn addAttr(self: LLVMIRModule, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
953 const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);
954 assert(kind_id != 0);
955 const llvm_attr = self.context.createEnumAttribute(kind_id, 0);
956 val.addAttributeAtIndex(index, llvm_attr);
957 }
958
959 fn addFnAttr(self: *LLVMIRModule, val: *const llvm.Value, attr_name: []const u8) void {
960 // TODO: improve this API, `addAttr(-1, attr_name)`
961 self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name);
962 }
963
964 pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
965 @setCold(true);
966 assert(self.err_msg == null);
967 self.err_msg = try Module.ErrorMsg.create(self.gpa, .{
968 .file_scope = self.src_loc.file_scope,
969 .byte_offset = src,
970 }, format, args);
971 return error.CodegenFail;
1010 return self.llvmModule().getIntrinsicDeclaration(id, null, 0);
9721011 }
9731012};
src/codegen/wasm.zig+9-10
......@@ -14,6 +14,7 @@ const Type = @import("../type.zig").Type;
1414const Value = @import("../value.zig").Value;
1515const Compilation = @import("../Compilation.zig");
1616const AnyMCValue = @import("../codegen.zig").AnyMCValue;
17const LazySrcLoc = Module.LazySrcLoc;
1718
1819/// Wasm Value, created when generating an instruction
1920const WValue = union(enum) {
......@@ -70,11 +71,9 @@ pub const Context = struct {
7071 }
7172
7273 /// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig
73 fn fail(self: *Context, src: usize, comptime fmt: []const u8, args: anytype) InnerError {
74 self.err_msg = try Module.ErrorMsg.create(self.gpa, .{
75 .file_scope = self.decl.getFileScope(),
76 .byte_offset = src,
77 }, fmt, args);
74 fn fail(self: *Context, src: LazySrcLoc, comptime fmt: []const u8, args: anytype) InnerError {
75 const src_loc = src.toSrcLocWithDecl(self.decl);
76 self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);
7877 return error.CodegenFail;
7978 }
8079
......@@ -91,7 +90,7 @@ pub const Context = struct {
9190 }
9291
9392 /// Using a given `Type`, returns the corresponding wasm value type
94 fn genValtype(self: *Context, src: usize, ty: Type) InnerError!u8 {
93 fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {
9594 return switch (ty.tag()) {
9695 .f32 => wasm.valtype(.f32),
9796 .f64 => wasm.valtype(.f64),
......@@ -104,7 +103,7 @@ pub const Context = struct {
104103 /// Using a given `Type`, returns the corresponding wasm value type
105104 /// Differently from `genValtype` this also allows `void` to create a block
106105 /// with no return type
107 fn genBlockType(self: *Context, src: usize, ty: Type) InnerError!u8 {
106 fn genBlockType(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {
108107 return switch (ty.tag()) {
109108 .void, .noreturn => wasm.block_empty,
110109 else => self.genValtype(src, ty),
......@@ -139,7 +138,7 @@ pub const Context = struct {
139138 ty.fnParamTypes(params);
140139 for (params) |param_type| {
141140 // Can we maybe get the source index of each param?
142 const val_type = try self.genValtype(self.decl.src(), param_type);
141 const val_type = try self.genValtype(.{ .node_offset = 0 }, param_type);
143142 try writer.writeByte(val_type);
144143 }
145144 }
......@@ -151,7 +150,7 @@ pub const Context = struct {
151150 else => |ret_type| {
152151 try leb.writeULEB128(writer, @as(u32, 1));
153152 // Can we maybe get the source index of the return type?
154 const val_type = try self.genValtype(self.decl.src(), return_type);
153 const val_type = try self.genValtype(.{ .node_offset = 0 }, return_type);
155154 try writer.writeByte(val_type);
156155 },
157156 }
......@@ -168,7 +167,7 @@ pub const Context = struct {
168167 const mod_fn = blk: {
169168 if (tv.val.castTag(.function)) |func| break :blk func.data;
170169 if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions
171 return self.fail(self.decl.src(), "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()});
170 return self.fail(.{ .node_offset = 0 }, "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()});
172171 };
173172
174173 // Reserve space to write the size after generating the code as well as space for locals count
src/ir.zig+564-12
......@@ -25,8 +25,7 @@ pub const Inst = struct {
2525 /// lifetimes of operands are encoded elsewhere.
2626 deaths: DeathsInt = undefined,
2727 ty: Type,
28 /// Byte offset into the source.
29 src: usize,
28 src: Module.LazySrcLoc,
3029
3130 pub const DeathsInt = u16;
3231 pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
......@@ -81,22 +80,28 @@ pub const Inst = struct {
8180 condbr,
8281 constant,
8382 dbg_stmt,
84 // ?T => bool
83 /// ?T => bool
8584 is_null,
86 // ?T => bool (inverted logic)
85 /// ?T => bool (inverted logic)
8786 is_non_null,
88 // *?T => bool
87 /// *?T => bool
8988 is_null_ptr,
90 // *?T => bool (inverted logic)
89 /// *?T => bool (inverted logic)
9190 is_non_null_ptr,
92 // E!T => bool
91 /// E!T => bool
9392 is_err,
94 // *E!T => bool
93 /// *E!T => bool
9594 is_err_ptr,
95 /// E => u16
96 error_to_int,
97 /// u16 => E
98 int_to_error,
9699 bool_and,
97100 bool_or,
98101 /// Read a value from a pointer.
99102 load,
103 /// A labeled block of code that loops forever. At the end of the body it is implied
104 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
100105 loop,
101106 ptrtoint,
102107 ref,
......@@ -113,9 +118,9 @@ pub const Inst = struct {
113118 not,
114119 floatcast,
115120 intcast,
116 // ?T => T
121 /// ?T => T
117122 optional_payload,
118 // *?T => *T
123 /// *?T => *T
119124 optional_payload_ptr,
120125 wrap_optional,
121126 /// E!T -> T
......@@ -132,6 +137,8 @@ pub const Inst = struct {
132137 wrap_errunion_err,
133138 xor,
134139 switchbr,
140 /// Given a pointer to a struct and a field index, returns a pointer to the field.
141 struct_field_ptr,
135142
136143 pub fn Type(tag: Tag) type {
137144 return switch (tag) {
......@@ -139,7 +146,6 @@ pub const Inst = struct {
139146 .retvoid,
140147 .unreach,
141148 .breakpoint,
142 .dbg_stmt,
143149 => NoOp,
144150
145151 .ref,
......@@ -152,6 +158,8 @@ pub const Inst = struct {
152158 .is_null_ptr,
153159 .is_err,
154160 .is_err_ptr,
161 .int_to_error,
162 .error_to_int,
155163 .ptrtoint,
156164 .floatcast,
157165 .intcast,
......@@ -198,7 +206,9 @@ pub const Inst = struct {
198206 .constant => Constant,
199207 .loop => Loop,
200208 .varptr => VarPtr,
209 .struct_field_ptr => StructFieldPtr,
201210 .switchbr => SwitchBr,
211 .dbg_stmt => DbgStmt,
202212 };
203213 }
204214
......@@ -360,7 +370,8 @@ pub const Inst = struct {
360370 base: Inst,
361371 asm_source: []const u8,
362372 is_volatile: bool,
363 output: ?[]const u8,
373 output: ?*Inst,
374 output_name: ?[]const u8,
364375 inputs: []const []const u8,
365376 clobbers: []const []const u8,
366377 args: []const *Inst,
......@@ -544,6 +555,27 @@ pub const Inst = struct {
544555 }
545556 };
546557
558 pub const StructFieldPtr = struct {
559 pub const base_tag = Tag.struct_field_ptr;
560
561 base: Inst,
562 struct_ptr: *Inst,
563 field_index: usize,
564
565 pub fn operandCount(self: *const StructFieldPtr) usize {
566 return 1;
567 }
568 pub fn getOperand(self: *const StructFieldPtr, index: usize) ?*Inst {
569 var i = index;
570
571 if (i < 1)
572 return self.struct_ptr;
573 i -= 1;
574
575 return null;
576 }
577 };
578
547579 pub const SwitchBr = struct {
548580 pub const base_tag = Tag.switchbr;
549581
......@@ -584,8 +616,528 @@ pub const Inst = struct {
584616 return (self.deaths + self.else_index)[0..self.else_deaths];
585617 }
586618 };
619
620 pub const DbgStmt = struct {
621 pub const base_tag = Tag.dbg_stmt;
622
623 base: Inst,
624 byte_offset: u32,
625
626 pub fn operandCount(self: *const DbgStmt) usize {
627 return 0;
628 }
629 pub fn getOperand(self: *const DbgStmt, index: usize) ?*Inst {
630 return null;
631 }
632 };
587633};
588634
589635pub const Body = struct {
590636 instructions: []*Inst,
591637};
638
639/// For debugging purposes, prints a function representation to stderr.
640pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
641 const allocator = old_module.gpa;
642 var ctx: DumpTzir = .{
643 .allocator = allocator,
644 .arena = std.heap.ArenaAllocator.init(allocator),
645 .old_module = &old_module,
646 .module_fn = module_fn,
647 .indent = 2,
648 .inst_table = DumpTzir.InstTable.init(allocator),
649 .partial_inst_table = DumpTzir.InstTable.init(allocator),
650 .const_table = DumpTzir.InstTable.init(allocator),
651 };
652 defer ctx.inst_table.deinit();
653 defer ctx.partial_inst_table.deinit();
654 defer ctx.const_table.deinit();
655 defer ctx.arena.deinit();
656
657 switch (module_fn.state) {
658 .queued => std.debug.print("(queued)", .{}),
659 .inline_only => std.debug.print("(inline_only)", .{}),
660 .in_progress => std.debug.print("(in_progress)", .{}),
661 .sema_failure => std.debug.print("(sema_failure)", .{}),
662 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
663 .success => {
664 const writer = std.io.getStdErr().writer();
665 ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR");
666 },
667 }
668}
669
670const DumpTzir = struct {
671 allocator: *std.mem.Allocator,
672 arena: std.heap.ArenaAllocator,
673 old_module: *const Module,
674 module_fn: *Module.Fn,
675 indent: usize,
676 inst_table: InstTable,
677 partial_inst_table: InstTable,
678 const_table: InstTable,
679 next_index: usize = 0,
680 next_partial_index: usize = 0,
681 next_const_index: usize = 0,
682
683 const InstTable = std.AutoArrayHashMap(*Inst, usize);
684
685 /// TODO: Improve this code to include a stack of Body and store the instructions
686 /// in there. Now we are putting all the instructions in a function local table,
687 /// however instructions that are in a Body can be thown away when the Body ends.
688 fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void {
689 // First pass to pre-populate the table so that we can show even invalid references.
690 // Must iterate the same order we iterate the second time.
691 // We also look for constants and put them in the const_table.
692 try dtz.fetchInstsAndResolveConsts(body);
693
694 std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
695
696 for (dtz.const_table.items()) |entry| {
697 const constant = entry.key.castTag(.constant).?;
698 try writer.print(" @{d}: {} = {};\n", .{
699 entry.value, constant.base.ty, constant.val,
700 });
701 }
702
703 return dtz.dumpBody(body, writer);
704 }
705
706 fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void {
707 for (body.instructions) |inst| {
708 try dtz.inst_table.put(inst, dtz.next_index);
709 dtz.next_index += 1;
710 switch (inst.tag) {
711 .alloc,
712 .retvoid,
713 .unreach,
714 .breakpoint,
715 .dbg_stmt,
716 .arg,
717 => {},
718
719 .ref,
720 .ret,
721 .bitcast,
722 .not,
723 .is_non_null,
724 .is_non_null_ptr,
725 .is_null,
726 .is_null_ptr,
727 .is_err,
728 .is_err_ptr,
729 .error_to_int,
730 .int_to_error,
731 .ptrtoint,
732 .floatcast,
733 .intcast,
734 .load,
735 .optional_payload,
736 .optional_payload_ptr,
737 .wrap_optional,
738 .wrap_errunion_payload,
739 .wrap_errunion_err,
740 .unwrap_errunion_payload,
741 .unwrap_errunion_err,
742 .unwrap_errunion_payload_ptr,
743 .unwrap_errunion_err_ptr,
744 => {
745 const un_op = inst.cast(Inst.UnOp).?;
746 try dtz.findConst(un_op.operand);
747 },
748
749 .add,
750 .addwrap,
751 .sub,
752 .subwrap,
753 .mul,
754 .mulwrap,
755 .cmp_lt,
756 .cmp_lte,
757 .cmp_eq,
758 .cmp_gte,
759 .cmp_gt,
760 .cmp_neq,
761 .store,
762 .bool_and,
763 .bool_or,
764 .bit_and,
765 .bit_or,
766 .xor,
767 => {
768 const bin_op = inst.cast(Inst.BinOp).?;
769 try dtz.findConst(bin_op.lhs);
770 try dtz.findConst(bin_op.rhs);
771 },
772
773 .br => {
774 const br = inst.castTag(.br).?;
775 try dtz.findConst(&br.block.base);
776 try dtz.findConst(br.operand);
777 },
778
779 .br_block_flat => {
780 const br_block_flat = inst.castTag(.br_block_flat).?;
781 try dtz.findConst(&br_block_flat.block.base);
782 try dtz.fetchInstsAndResolveConsts(br_block_flat.body);
783 },
784
785 .br_void => {
786 const br_void = inst.castTag(.br_void).?;
787 try dtz.findConst(&br_void.block.base);
788 },
789
790 .block => {
791 const block = inst.castTag(.block).?;
792 try dtz.fetchInstsAndResolveConsts(block.body);
793 },
794
795 .condbr => {
796 const condbr = inst.castTag(.condbr).?;
797 try dtz.findConst(condbr.condition);
798 try dtz.fetchInstsAndResolveConsts(condbr.then_body);
799 try dtz.fetchInstsAndResolveConsts(condbr.else_body);
800 },
801 .switchbr => {
802 const switchbr = inst.castTag(.switchbr).?;
803 try dtz.findConst(switchbr.target);
804 try dtz.fetchInstsAndResolveConsts(switchbr.else_body);
805 for (switchbr.cases) |case| {
806 try dtz.fetchInstsAndResolveConsts(case.body);
807 }
808 },
809
810 .loop => {
811 const loop = inst.castTag(.loop).?;
812 try dtz.fetchInstsAndResolveConsts(loop.body);
813 },
814 .call => {
815 const call = inst.castTag(.call).?;
816 try dtz.findConst(call.func);
817 for (call.args) |arg| {
818 try dtz.findConst(arg);
819 }
820 },
821 .struct_field_ptr => {
822 const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
823 try dtz.findConst(struct_field_ptr.struct_ptr);
824 },
825
826 // TODO fill out this debug printing
827 .assembly,
828 .constant,
829 .varptr,
830 => {},
831 }
832 }
833 }
834
835 fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
836 for (body.instructions) |inst| {
837 const my_index = dtz.next_partial_index;
838 try dtz.partial_inst_table.put(inst, my_index);
839 dtz.next_partial_index += 1;
840
841 try writer.writeByteNTimes(' ', dtz.indent);
842 try writer.print("%{d}: {} = {s}(", .{
843 my_index, inst.ty, @tagName(inst.tag),
844 });
845 switch (inst.tag) {
846 .alloc,
847 .retvoid,
848 .unreach,
849 .breakpoint,
850 .dbg_stmt,
851 => try writer.writeAll(")\n"),
852
853 .ref,
854 .ret,
855 .bitcast,
856 .not,
857 .is_non_null,
858 .is_null,
859 .is_non_null_ptr,
860 .is_null_ptr,
861 .is_err,
862 .is_err_ptr,
863 .error_to_int,
864 .int_to_error,
865 .ptrtoint,
866 .floatcast,
867 .intcast,
868 .load,
869 .optional_payload,
870 .optional_payload_ptr,
871 .wrap_optional,
872 .wrap_errunion_err,
873 .wrap_errunion_payload,
874 .unwrap_errunion_err,
875 .unwrap_errunion_payload,
876 .unwrap_errunion_payload_ptr,
877 .unwrap_errunion_err_ptr,
878 => {
879 const un_op = inst.cast(Inst.UnOp).?;
880 const kinky = try dtz.writeInst(writer, un_op.operand);
881 if (kinky != null) {
882 try writer.writeAll(") // Instruction does not dominate all uses!\n");
883 } else {
884 try writer.writeAll(")\n");
885 }
886 },
887
888 .add,
889 .addwrap,
890 .sub,
891 .subwrap,
892 .mul,
893 .mulwrap,
894 .cmp_lt,
895 .cmp_lte,
896 .cmp_eq,
897 .cmp_gte,
898 .cmp_gt,
899 .cmp_neq,
900 .store,
901 .bool_and,
902 .bool_or,
903 .bit_and,
904 .bit_or,
905 .xor,
906 => {
907 const bin_op = inst.cast(Inst.BinOp).?;
908
909 const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs);
910 try writer.writeAll(", ");
911 const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs);
912
913 if (lhs_kinky != null or rhs_kinky != null) {
914 try writer.writeAll(") // Instruction does not dominate all uses!");
915 if (lhs_kinky) |lhs| {
916 try writer.print(" %{d}", .{lhs});
917 }
918 if (rhs_kinky) |rhs| {
919 try writer.print(" %{d}", .{rhs});
920 }
921 try writer.writeAll("\n");
922 } else {
923 try writer.writeAll(")\n");
924 }
925 },
926
927 .arg => {
928 const arg = inst.castTag(.arg).?;
929 try writer.print("{s})\n", .{arg.name});
930 },
931
932 .br => {
933 const br = inst.castTag(.br).?;
934
935 const lhs_kinky = try dtz.writeInst(writer, &br.block.base);
936 try writer.writeAll(", ");
937 const rhs_kinky = try dtz.writeInst(writer, br.operand);
938
939 if (lhs_kinky != null or rhs_kinky != null) {
940 try writer.writeAll(") // Instruction does not dominate all uses!");
941 if (lhs_kinky) |lhs| {
942 try writer.print(" %{d}", .{lhs});
943 }
944 if (rhs_kinky) |rhs| {
945 try writer.print(" %{d}", .{rhs});
946 }
947 try writer.writeAll("\n");
948 } else {
949 try writer.writeAll(")\n");
950 }
951 },
952
953 .br_block_flat => {
954 const br_block_flat = inst.castTag(.br_block_flat).?;
955 const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base);
956 if (block_kinky != null) {
957 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
958 } else {
959 try writer.writeAll(", {\n");
960 }
961
962 const old_indent = dtz.indent;
963 dtz.indent += 2;
964 try dtz.dumpBody(br_block_flat.body, writer);
965 dtz.indent = old_indent;
966
967 try writer.writeByteNTimes(' ', dtz.indent);
968 try writer.writeAll("})\n");
969 },
970
971 .br_void => {
972 const br_void = inst.castTag(.br_void).?;
973 const kinky = try dtz.writeInst(writer, &br_void.block.base);
974 if (kinky) |_| {
975 try writer.writeAll(") // Instruction does not dominate all uses!\n");
976 } else {
977 try writer.writeAll(")\n");
978 }
979 },
980
981 .block => {
982 const block = inst.castTag(.block).?;
983
984 try writer.writeAll("{\n");
985
986 const old_indent = dtz.indent;
987 dtz.indent += 2;
988 try dtz.dumpBody(block.body, writer);
989 dtz.indent = old_indent;
990
991 try writer.writeByteNTimes(' ', dtz.indent);
992 try writer.writeAll("})\n");
993 },
994
995 .condbr => {
996 const condbr = inst.castTag(.condbr).?;
997
998 const condition_kinky = try dtz.writeInst(writer, condbr.condition);
999 if (condition_kinky != null) {
1000 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1001 } else {
1002 try writer.writeAll(", {\n");
1003 }
1004
1005 const old_indent = dtz.indent;
1006 dtz.indent += 2;
1007 try dtz.dumpBody(condbr.then_body, writer);
1008
1009 try writer.writeByteNTimes(' ', old_indent);
1010 try writer.writeAll("}, {\n");
1011
1012 try dtz.dumpBody(condbr.else_body, writer);
1013 dtz.indent = old_indent;
1014
1015 try writer.writeByteNTimes(' ', old_indent);
1016 try writer.writeAll("})\n");
1017 },
1018
1019 .switchbr => {
1020 const switchbr = inst.castTag(.switchbr).?;
1021
1022 const condition_kinky = try dtz.writeInst(writer, switchbr.target);
1023 if (condition_kinky != null) {
1024 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1025 } else {
1026 try writer.writeAll(", {\n");
1027 }
1028 const old_indent = dtz.indent;
1029
1030 if (switchbr.else_body.instructions.len != 0) {
1031 dtz.indent += 2;
1032 try dtz.dumpBody(switchbr.else_body, writer);
1033
1034 try writer.writeByteNTimes(' ', old_indent);
1035 try writer.writeAll("}, {\n");
1036 dtz.indent = old_indent;
1037 }
1038 for (switchbr.cases) |case| {
1039 dtz.indent += 2;
1040 try dtz.dumpBody(case.body, writer);
1041
1042 try writer.writeByteNTimes(' ', old_indent);
1043 try writer.writeAll("}, {\n");
1044 dtz.indent = old_indent;
1045 }
1046
1047 try writer.writeByteNTimes(' ', old_indent);
1048 try writer.writeAll("})\n");
1049 },
1050
1051 .loop => {
1052 const loop = inst.castTag(.loop).?;
1053
1054 try writer.writeAll("{\n");
1055
1056 const old_indent = dtz.indent;
1057 dtz.indent += 2;
1058 try dtz.dumpBody(loop.body, writer);
1059 dtz.indent = old_indent;
1060
1061 try writer.writeByteNTimes(' ', dtz.indent);
1062 try writer.writeAll("})\n");
1063 },
1064
1065 .call => {
1066 const call = inst.castTag(.call).?;
1067
1068 const args_kinky = try dtz.allocator.alloc(?usize, call.args.len);
1069 defer dtz.allocator.free(args_kinky);
1070 std.mem.set(?usize, args_kinky, null);
1071 var any_kinky_args = false;
1072
1073 const func_kinky = try dtz.writeInst(writer, call.func);
1074
1075 for (call.args) |arg, i| {
1076 try writer.writeAll(", ");
1077
1078 args_kinky[i] = try dtz.writeInst(writer, arg);
1079 any_kinky_args = any_kinky_args or args_kinky[i] != null;
1080 }
1081
1082 if (func_kinky != null or any_kinky_args) {
1083 try writer.writeAll(") // Instruction does not dominate all uses!");
1084 if (func_kinky) |func_index| {
1085 try writer.print(" %{d}", .{func_index});
1086 }
1087 for (args_kinky) |arg_kinky| {
1088 if (arg_kinky) |arg_index| {
1089 try writer.print(" %{d}", .{arg_index});
1090 }
1091 }
1092 try writer.writeAll("\n");
1093 } else {
1094 try writer.writeAll(")\n");
1095 }
1096 },
1097
1098 .struct_field_ptr => {
1099 const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
1100 const kinky = try dtz.writeInst(writer, struct_field_ptr.struct_ptr);
1101 if (kinky != null) {
1102 try writer.print("{d}) // Instruction does not dominate all uses!\n", .{
1103 struct_field_ptr.field_index,
1104 });
1105 } else {
1106 try writer.print("{d})\n", .{struct_field_ptr.field_index});
1107 }
1108 },
1109
1110 // TODO fill out this debug printing
1111 .assembly,
1112 .constant,
1113 .varptr,
1114 => {
1115 try writer.writeAll("!TODO!)\n");
1116 },
1117 }
1118 }
1119 }
1120
1121 fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize {
1122 if (dtz.partial_inst_table.get(inst)) |operand_index| {
1123 try writer.print("%{d}", .{operand_index});
1124 return null;
1125 } else if (dtz.const_table.get(inst)) |operand_index| {
1126 try writer.print("@{d}", .{operand_index});
1127 return null;
1128 } else if (dtz.inst_table.get(inst)) |operand_index| {
1129 try writer.print("%{d}", .{operand_index});
1130 return operand_index;
1131 } else {
1132 try writer.writeAll("!BADREF!");
1133 return null;
1134 }
1135 }
1136
1137 fn findConst(dtz: *DumpTzir, operand: *Inst) !void {
1138 if (operand.tag == .constant) {
1139 try dtz.const_table.put(operand, dtz.next_const_index);
1140 dtz.next_const_index += 1;
1141 }
1142 }
1143};
src/link/C.zig+1-2
......@@ -185,8 +185,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
185185 if (module.global_error_set.size == 0) break :render_errors;
186186 var it = module.global_error_set.iterator();
187187 while (it.next()) |entry| {
188 // + 1 because 0 represents no error
189 try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value + 1 });
188 try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value });
190189 }
191190 try err_typedef_writer.writeByte('\n');
192191 }
src/link/Coff.zig+10-10
......@@ -34,7 +34,7 @@ pub const base_tag: link.File.Tag = .coff;
3434const msdos_stub = @embedFile("msdos-stub.bin");
3535
3636/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
37llvm_ir_module: ?*llvm_backend.LLVMIRModule = null,
37llvm_object: ?*llvm_backend.Object = null,
3838
3939base: link.File,
4040ptr_width: PtrWidth,
......@@ -129,7 +129,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
129129 const self = try createEmpty(allocator, options);
130130 errdefer self.base.destroy();
131131
132 self.llvm_ir_module = try llvm_backend.LLVMIRModule.create(allocator, sub_path, options);
132 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
133133 return self;
134134 }
135135
......@@ -413,7 +413,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff {
413413}
414414
415415pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
416 if (self.llvm_ir_module) |_| return;
416 if (self.llvm_object) |_| return;
417417
418418 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
419419
......@@ -660,7 +660,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
660660 defer tracy.end();
661661
662662 if (build_options.have_llvm)
663 if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.updateDecl(module, decl);
663 if (self.llvm_object) |llvm_object| return try llvm_object.updateDecl(module, decl);
664664
665665 const typed_value = decl.typed_value.most_recent.typed_value;
666666 if (typed_value.val.tag() == .extern_fn) {
......@@ -720,15 +720,15 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
720720}
721721
722722pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
723 if (self.llvm_ir_module) |_| return;
723 if (self.llvm_object) |_| return;
724724
725725 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
726726 self.freeTextBlock(&decl.link.coff);
727727 self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
728728}
729729
730pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void {
731 if (self.llvm_ir_module) |_| return;
730pub fn updateDeclExports(self: *Coff, module: *Module, decl: *Module.Decl, exports: []const *Module.Export) !void {
731 if (self.llvm_object) |_| return;
732732
733733 for (exports) |exp| {
734734 if (exp.options.section) |section_name| {
......@@ -771,7 +771,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
771771 defer tracy.end();
772772
773773 if (build_options.have_llvm)
774 if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.flushModule(comp);
774 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
775775
776776 if (self.text_section_size_dirty) {
777777 // Write the new raw size in the .text header
......@@ -1308,7 +1308,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
13081308}
13091309
13101310pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
1311 assert(self.llvm_ir_module == null);
1311 assert(self.llvm_object == null);
13121312 return self.text_section_virtual_address + decl.link.coff.text_offset;
13131313}
13141314
......@@ -1318,7 +1318,7 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v
13181318
13191319pub fn deinit(self: *Coff) void {
13201320 if (build_options.have_llvm)
1321 if (self.llvm_ir_module) |ir_module| ir_module.deinit(self.base.allocator);
1321 if (self.llvm_object) |ir_module| ir_module.deinit(self.base.allocator);
13221322
13231323 self.text_block_free_list.deinit(self.base.allocator);
13241324 self.offset_table.deinit(self.base.allocator);
src/link/Elf.zig+13-13
......@@ -35,7 +35,7 @@ base: File,
3535ptr_width: PtrWidth,
3636
3737/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
38llvm_ir_module: ?*llvm_backend.LLVMIRModule = null,
38llvm_object: ?*llvm_backend.Object = null,
3939
4040/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
4141/// Same order as in the file.
......@@ -232,7 +232,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
232232 const self = try createEmpty(allocator, options);
233233 errdefer self.base.destroy();
234234
235 self.llvm_ir_module = try llvm_backend.LLVMIRModule.create(allocator, sub_path, options);
235 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
236236 return self;
237237 }
238238
......@@ -299,7 +299,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
299299
300300pub fn deinit(self: *Elf) void {
301301 if (build_options.have_llvm)
302 if (self.llvm_ir_module) |ir_module|
302 if (self.llvm_object) |ir_module|
303303 ir_module.deinit(self.base.allocator);
304304
305305 self.sections.deinit(self.base.allocator);
......@@ -318,7 +318,7 @@ pub fn deinit(self: *Elf) void {
318318}
319319
320320pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 {
321 assert(self.llvm_ir_module == null);
321 assert(self.llvm_object == null);
322322 assert(decl.link.elf.local_sym_index != 0);
323323 return self.local_symbols.items[decl.link.elf.local_sym_index].st_value;
324324}
......@@ -438,7 +438,7 @@ fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
438438}
439439
440440pub fn populateMissingMetadata(self: *Elf) !void {
441 assert(self.llvm_ir_module == null);
441 assert(self.llvm_object == null);
442442
443443 const small_ptr = switch (self.ptr_width) {
444444 .p32 => true,
......@@ -745,7 +745,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
745745 defer tracy.end();
746746
747747 if (build_options.have_llvm)
748 if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.flushModule(comp);
748 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
749749
750750 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
751751 // Zig source code.
......@@ -2111,7 +2111,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
21112111}
21122112
21132113pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
2114 if (self.llvm_ir_module) |_| return;
2114 if (self.llvm_object) |_| return;
21152115
21162116 if (decl.link.elf.local_sym_index != 0) return;
21172117
......@@ -2149,7 +2149,7 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
21492149}
21502150
21512151pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
2152 if (self.llvm_ir_module) |_| return;
2152 if (self.llvm_object) |_| return;
21532153
21542154 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
21552155 self.freeTextBlock(&decl.link.elf);
......@@ -2189,7 +2189,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
21892189 defer tracy.end();
21902190
21912191 if (build_options.have_llvm)
2192 if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.updateDecl(module, decl);
2192 if (self.llvm_object) |llvm_object| return try llvm_object.updateDecl(module, decl);
21932193
21942194 const typed_value = decl.typed_value.most_recent.typed_value;
21952195 if (typed_value.val.tag() == .extern_fn) {
......@@ -2670,10 +2670,10 @@ fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const
26702670pub fn updateDeclExports(
26712671 self: *Elf,
26722672 module: *Module,
2673 decl: *const Module.Decl,
2673 decl: *Module.Decl,
26742674 exports: []const *Module.Export,
26752675) !void {
2676 if (self.llvm_ir_module) |_| return;
2676 if (self.llvm_object) |_| return;
26772677
26782678 const tracy = trace(@src());
26792679 defer tracy.end();
......@@ -2748,7 +2748,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
27482748 const tracy = trace(@src());
27492749 defer tracy.end();
27502750
2751 if (self.llvm_ir_module) |_| return;
2751 if (self.llvm_object) |_| return;
27522752
27532753 const tree = decl.container.file_scope.tree;
27542754 const node_tags = tree.nodes.items(.tag);
......@@ -2773,7 +2773,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
27732773}
27742774
27752775pub fn deleteExport(self: *Elf, exp: Export) void {
2776 if (self.llvm_ir_module) |_| return;
2776 if (self.llvm_object) |_| return;
27772777
27782778 const sym_index = exp.sym_index orelse return;
27792779 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
src/link/MachO.zig+1-1
......@@ -1340,7 +1340,7 @@ pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.D
13401340pub fn updateDeclExports(
13411341 self: *MachO,
13421342 module: *Module,
1343 decl: *const Module.Decl,
1343 decl: *Module.Decl,
13441344 exports: []const *Module.Export,
13451345) !void {
13461346 const tracy = trace(@src());
src/main.zig+46-28
......@@ -1487,7 +1487,7 @@ fn buildOutputType(
14871487 for (diags.arch.?.allCpuModels()) |cpu| {
14881488 help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
14891489 }
1490 std.log.info("Available CPUs for architecture '{s}': {s}", .{
1490 std.log.info("Available CPUs for architecture '{s}':\n{s}", .{
14911491 @tagName(diags.arch.?), help_text.items,
14921492 });
14931493 }
......@@ -1499,7 +1499,7 @@ fn buildOutputType(
14991499 for (diags.arch.?.allFeaturesList()) |feature| {
15001500 help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
15011501 }
1502 std.log.info("Available CPU features for architecture '{s}': {s}", .{
1502 std.log.info("Available CPU features for architecture '{s}':\n{s}", .{
15031503 @tagName(diags.arch.?), help_text.items,
15041504 });
15051505 }
......@@ -1750,15 +1750,12 @@ fn buildOutputType(
17501750 }
17511751
17521752 const self_exe_path = try fs.selfExePathAlloc(arena);
1753 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
1754 .{
1755 .path = lib_dir,
1756 .handle = try fs.cwd().openDir(lib_dir, .{}),
1757 }
1758 else
1759 introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
1760 fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
1761 };
1753 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
1754 .path = lib_dir,
1755 .handle = try fs.cwd().openDir(lib_dir, .{}),
1756 } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
1757 fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
1758 };
17621759 defer zig_lib_directory.handle.close();
17631760
17641761 var thread_pool: ThreadPool = undefined;
......@@ -2115,12 +2112,37 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi
21152112 } else switch (hook) {
21162113 .none => {},
21172114 .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}),
2118 .update => |full_path| _ = try comp.bin_file.options.emit.?.directory.handle.updateFile(
2119 comp.bin_file.options.emit.?.sub_path,
2120 fs.cwd(),
2121 full_path,
2122 .{},
2123 ),
2115 .update => |full_path| {
2116 const bin_sub_path = comp.bin_file.options.emit.?.sub_path;
2117 const cwd = fs.cwd();
2118 const cache_dir = comp.bin_file.options.emit.?.directory.handle;
2119 _ = try cache_dir.updateFile(bin_sub_path, cwd, full_path, .{});
2120
2121 // If a .pdb file is part of the expected output, we must also copy
2122 // it into place here.
2123 const coff_or_pe = switch (comp.bin_file.options.object_format) {
2124 .coff, .pe => true,
2125 else => false,
2126 };
2127 const have_pdb = coff_or_pe and !comp.bin_file.options.strip;
2128 if (have_pdb) {
2129 // Replace `.out` or `.exe` with `.pdb` on both the source and destination
2130 const src_bin_ext = fs.path.extension(bin_sub_path);
2131 const dst_bin_ext = fs.path.extension(full_path);
2132
2133 const src_pdb_path = try std.fmt.allocPrint(gpa, "{s}.pdb", .{
2134 bin_sub_path[0 .. bin_sub_path.len - src_bin_ext.len],
2135 });
2136 defer gpa.free(src_pdb_path);
2137
2138 const dst_pdb_path = try std.fmt.allocPrint(gpa, "{s}.pdb", .{
2139 full_path[0 .. full_path.len - dst_bin_ext.len],
2140 });
2141 defer gpa.free(dst_pdb_path);
2142
2143 _ = try cache_dir.updateFile(src_pdb_path, cwd, dst_pdb_path, .{});
2144 }
2145 },
21242146 }
21252147}
21262148
......@@ -2461,15 +2483,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
24612483 }
24622484 }
24632485
2464 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
2465 .{
2466 .path = lib_dir,
2467 .handle = try fs.cwd().openDir(lib_dir, .{}),
2468 }
2469 else
2470 introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
2471 fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
2472 };
2486 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
2487 .path = lib_dir,
2488 .handle = try fs.cwd().openDir(lib_dir, .{}),
2489 } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
2490 fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
2491 };
24732492 defer zig_lib_directory.handle.close();
24742493
24752494 const std_special = "std" ++ fs.path.sep_str ++ "special";
......@@ -3281,8 +3300,7 @@ pub const ClangArgIterator = struct {
32813300 self.zig_equivalent = clang_arg.zig_equivalent;
32823301 break :find_clang_arg;
32833302 },
3284 }
3285 else {
3303 } else {
32863304 fatal("Unknown Clang option: '{s}'", .{arg});
32873305 }
32883306 }
src/register_manager.zig created+228
......@@ -0,0 +1,228 @@
1const std = @import("std");
2const math = std.math;
3const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;
5const ir = @import("ir.zig");
6const Type = @import("type.zig").Type;
7const Module = @import("Module.zig");
8const LazySrcLoc = Module.LazySrcLoc;
9
10const log = std.log.scoped(.register_manager);
11
12pub fn RegisterManager(
13 comptime Function: type,
14 comptime Register: type,
15 comptime callee_preserved_regs: []const Register,
16) type {
17 return struct {
18 /// The key must be canonical register.
19 registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
20 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
21 /// Tracks all registers allocated in the course of this function
22 allocated_registers: FreeRegInt = 0,
23
24 const Self = @This();
25
26 /// An integer whose bits represent all the registers and whether they are free.
27 const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len);
28 const ShiftInt = math.Log2Int(FreeRegInt);
29
30 fn getFunction(self: *Self) *Function {
31 return @fieldParentPtr(Function, "register_manager", self);
32 }
33
34 pub fn deinit(self: *Self, allocator: *Allocator) void {
35 self.registers.deinit(allocator);
36 }
37
38 fn markRegUsed(self: *Self, reg: Register) void {
39 if (FreeRegInt == u0) return;
40 const index = reg.allocIndex() orelse return;
41 const shift = @intCast(ShiftInt, index);
42 const mask = @as(FreeRegInt, 1) << shift;
43 self.free_registers &= ~mask;
44 self.allocated_registers |= mask;
45 }
46
47 fn markRegFree(self: *Self, reg: Register) void {
48 if (FreeRegInt == u0) return;
49 const index = reg.allocIndex() orelse return;
50 const shift = @intCast(ShiftInt, index);
51 self.free_registers |= @as(FreeRegInt, 1) << shift;
52 }
53
54 /// Returns whether this register was allocated in the course
55 /// of this function
56 pub fn isRegAllocated(self: Self, reg: Register) bool {
57 if (FreeRegInt == u0) return false;
58 const index = reg.allocIndex() orelse return false;
59 const shift = @intCast(ShiftInt, index);
60 return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;
61 }
62
63 /// Before calling, must ensureCapacity + 1 on self.registers.
64 /// Returns `null` if all registers are allocated.
65 pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
66 const free_index = @ctz(FreeRegInt, self.free_registers);
67 if (free_index >= callee_preserved_regs.len) {
68 return null;
69 }
70
71 // This is necessary because the return type of @ctz is 1
72 // bit longer than ShiftInt if callee_preserved_regs.len
73 // is a power of two. This int cast is always safe because
74 // free_index < callee_preserved_regs.len
75 const shift = @intCast(ShiftInt, free_index);
76 const mask = @as(FreeRegInt, 1) << shift;
77 self.free_registers &= ~mask;
78 self.allocated_registers |= mask;
79
80 const reg = callee_preserved_regs[free_index];
81 self.registers.putAssumeCapacityNoClobber(reg, inst);
82 log.debug("alloc {} => {*}", .{ reg, inst });
83 return reg;
84 }
85
86 /// Before calling, must ensureCapacity + 1 on self.registers.
87 pub fn allocReg(self: *Self, inst: *ir.Inst) !Register {
88 return self.tryAllocReg(inst) orelse b: {
89 // We'll take over the first register. Move the instruction that was previously
90 // there to a stack allocation.
91 const reg = callee_preserved_regs[0];
92 const regs_entry = self.registers.getEntry(reg).?;
93 const spilled_inst = regs_entry.value;
94 regs_entry.value = inst;
95 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
96
97 break :b reg;
98 };
99 }
100
101 /// Does not track the register.
102 /// Returns `null` if all registers are allocated.
103 pub fn findUnusedReg(self: *Self) ?Register {
104 const free_index = @ctz(FreeRegInt, self.free_registers);
105 if (free_index >= callee_preserved_regs.len) {
106 return null;
107 }
108 return callee_preserved_regs[free_index];
109 }
110
111 /// Does not track the register.
112 pub fn allocRegWithoutTracking(self: *Self) !Register {
113 return self.findUnusedReg() orelse b: {
114 // We'll take over the first register. Move the instruction that was previously
115 // there to a stack allocation.
116 const reg = callee_preserved_regs[0];
117 const regs_entry = self.registers.remove(reg).?;
118 const spilled_inst = regs_entry.value;
119 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
120
121 break :b reg;
122 };
123 }
124
125 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) !void {
126 try self.registers.putNoClobber(self.getFunction().gpa, reg, inst);
127 self.markRegUsed(reg);
128 }
129
130 pub fn freeReg(self: *Self, reg: Register) void {
131 _ = self.registers.remove(reg);
132 self.markRegFree(reg);
133 }
134 };
135}
136
137const MockRegister = enum(u2) {
138 r0, r1, r2, r3,
139
140 pub fn allocIndex(self: MockRegister) ?u2 {
141 inline for (mock_callee_preserved_regs) |cpreg, i| {
142 if (self == cpreg) return i;
143 }
144 return null;
145 }
146};
147
148const mock_callee_preserved_regs = [_]MockRegister{ .r2, .r3 };
149
150const MockFunction = struct {
151 allocator: *Allocator,
152 register_manager: RegisterManager(Self, MockRegister, &mock_callee_preserved_regs) = .{},
153 spilled: std.ArrayListUnmanaged(MockRegister) = .{},
154
155 const Self = @This();
156
157 pub fn deinit(self: *Self) void {
158 self.register_manager.deinit(self.allocator);
159 self.spilled.deinit(self.allocator);
160 }
161
162 pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: MockRegister, inst: *ir.Inst) !void {
163 try self.spilled.append(self.allocator, reg);
164 }
165};
166
167test "tryAllocReg: no spilling" {
168 const allocator = std.testing.allocator;
169
170 var function = MockFunction{
171 .allocator = allocator,
172 };
173 defer function.deinit();
174
175 var mock_instruction = ir.Inst{
176 .tag = .breakpoint,
177 .ty = Type.initTag(.void),
178 .src = .unneeded,
179 };
180
181 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
182 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
183
184 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
185 std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction));
186 std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction));
187 std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction));
188
189 std.testing.expect(function.register_manager.isRegAllocated(.r2));
190 std.testing.expect(function.register_manager.isRegAllocated(.r3));
191
192 function.register_manager.freeReg(.r2);
193 function.register_manager.freeReg(.r3);
194
195 std.testing.expect(function.register_manager.isRegAllocated(.r2));
196 std.testing.expect(function.register_manager.isRegAllocated(.r3));
197}
198
199test "allocReg: spilling" {
200 const allocator = std.testing.allocator;
201
202 var function = MockFunction{
203 .allocator = allocator,
204 };
205 defer function.deinit();
206
207 var mock_instruction = ir.Inst{
208 .tag = .breakpoint,
209 .ty = Type.initTag(.void),
210 .src = .unneeded,
211 };
212
213 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
214 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
215
216 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
217 std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction));
218 std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction));
219
220 // Spill a register
221 std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction));
222 std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items);
223
224 // No spilling necessary
225 function.register_manager.freeReg(.r3);
226 std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction));
227 std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items);
228}
src/stage1/analyze.cpp+3-1
......@@ -8723,7 +8723,9 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
87238723 assert(async_frame_type->id == ZigTypeIdFnFrame);
87248724 assert(field_type->id == ZigTypeIdFn);
87258725 resolve_llvm_types_fn(g, async_frame_type->data.frame.fn);
8726 llvm_type = LLVMPointerType(async_frame_type->data.frame.fn->raw_type_ref, 0);
8726
8727 const unsigned addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref);
8728 llvm_type = LLVMPointerType(async_frame_type->data.frame.fn->raw_type_ref, addrspace);
87278729 } else {
87288730 llvm_type = get_llvm_type(g, field_type);
87298731 }
src/stage1/ir.cpp+1-10
......@@ -7641,12 +7641,7 @@ static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node,
76417641
76427642 bool is_nosuspend = get_scope_nosuspend(scope) != nullptr;
76437643 CallModifier modifier = node->data.fn_call_expr.modifier;
7644 if (is_nosuspend) {
7645 if (modifier == CallModifierAsync) {
7646 add_node_error(irb->codegen, node,
7647 buf_sprintf("async call in nosuspend scope"));
7648 return irb->codegen->invalid_inst_src;
7649 }
7644 if (is_nosuspend && modifier != CallModifierAsync) {
76507645 modifier = CallModifierNoSuspend;
76517646 }
76527647
......@@ -10129,10 +10124,6 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod
1012910124
1013010125static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
1013110126 assert(node->type == NodeTypeResume);
10132 if (get_scope_nosuspend(scope) != nullptr) {
10133 add_node_error(irb->codegen, node, buf_sprintf("resume in nosuspend scope"));
10134 return irb->codegen->invalid_inst_src;
10135 }
1013610127
1013710128 IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr);
1013810129 if (target_inst == irb->codegen->invalid_inst_src)
src/translate_c.zig+1-1
......@@ -4343,7 +4343,7 @@ fn isZigPrimitiveType(name: []const u8) bool {
43434343 }
43444344 return true;
43454345 }
4346 return @import("astgen.zig").simple_types.has(name);
4346 return @import("AstGen.zig").simple_types.has(name);
43474347}
43484348
43494349const MacroCtx = struct {
src/type.zig+290-196
......@@ -4,6 +4,7 @@ const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
55const Target = std.Target;
66const Module = @import("Module.zig");
7const log = std.log.scoped(.Type);
78
89/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
910/// It's important for this type to be small.
......@@ -92,11 +93,9 @@ pub const Type = extern union {
9293
9394 .anyerror_void_error_union, .error_union => return .ErrorUnion,
9495
95 .anyframe_T, .@"anyframe" => return .AnyFrame,
96
97 .@"struct", .empty_struct => return .Struct,
98 .@"enum" => return .Enum,
99 .@"union" => return .Union,
96 .empty_struct => return .Struct,
97 .empty_struct_literal => return .Struct,
98 .@"struct" => return .Struct,
10099
101100 .var_args_param => unreachable, // can be any type
102101 }
......@@ -173,6 +172,125 @@ pub const Type = extern union {
173172 };
174173 }
175174
175 pub fn ptrInfo(self: Type) Payload.Pointer {
176 switch (self.tag()) {
177 .single_const_pointer_to_comptime_int => return .{ .data = .{
178 .pointee_type = Type.initTag(.comptime_int),
179 .sentinel = null,
180 .@"align" = 0,
181 .bit_offset = 0,
182 .host_size = 0,
183 .@"allowzero" = false,
184 .mutable = false,
185 .@"volatile" = false,
186 .size = .One,
187 } },
188 .const_slice_u8 => return .{ .data = .{
189 .pointee_type = Type.initTag(.u8),
190 .sentinel = null,
191 .@"align" = 0,
192 .bit_offset = 0,
193 .host_size = 0,
194 .@"allowzero" = false,
195 .mutable = false,
196 .@"volatile" = false,
197 .size = .Slice,
198 } },
199 .single_const_pointer => return .{ .data = .{
200 .pointee_type = self.castPointer().?.data,
201 .sentinel = null,
202 .@"align" = 0,
203 .bit_offset = 0,
204 .host_size = 0,
205 .@"allowzero" = false,
206 .mutable = false,
207 .@"volatile" = false,
208 .size = .One,
209 } },
210 .single_mut_pointer => return .{ .data = .{
211 .pointee_type = self.castPointer().?.data,
212 .sentinel = null,
213 .@"align" = 0,
214 .bit_offset = 0,
215 .host_size = 0,
216 .@"allowzero" = false,
217 .mutable = true,
218 .@"volatile" = false,
219 .size = .One,
220 } },
221 .many_const_pointer => return .{ .data = .{
222 .pointee_type = self.castPointer().?.data,
223 .sentinel = null,
224 .@"align" = 0,
225 .bit_offset = 0,
226 .host_size = 0,
227 .@"allowzero" = false,
228 .mutable = false,
229 .@"volatile" = false,
230 .size = .Many,
231 } },
232 .many_mut_pointer => return .{ .data = .{
233 .pointee_type = self.castPointer().?.data,
234 .sentinel = null,
235 .@"align" = 0,
236 .bit_offset = 0,
237 .host_size = 0,
238 .@"allowzero" = false,
239 .mutable = true,
240 .@"volatile" = false,
241 .size = .Many,
242 } },
243 .c_const_pointer => return .{ .data = .{
244 .pointee_type = self.castPointer().?.data,
245 .sentinel = null,
246 .@"align" = 0,
247 .bit_offset = 0,
248 .host_size = 0,
249 .@"allowzero" = false,
250 .mutable = false,
251 .@"volatile" = false,
252 .size = .C,
253 } },
254 .c_mut_pointer => return .{ .data = .{
255 .pointee_type = self.castPointer().?.data,
256 .sentinel = null,
257 .@"align" = 0,
258 .bit_offset = 0,
259 .host_size = 0,
260 .@"allowzero" = false,
261 .mutable = true,
262 .@"volatile" = false,
263 .size = .C,
264 } },
265 .const_slice => return .{ .data = .{
266 .pointee_type = self.castPointer().?.data,
267 .sentinel = null,
268 .@"align" = 0,
269 .bit_offset = 0,
270 .host_size = 0,
271 .@"allowzero" = false,
272 .mutable = false,
273 .@"volatile" = false,
274 .size = .Slice,
275 } },
276 .mut_slice => return .{ .data = .{
277 .pointee_type = self.castPointer().?.data,
278 .sentinel = null,
279 .@"align" = 0,
280 .bit_offset = 0,
281 .host_size = 0,
282 .@"allowzero" = false,
283 .mutable = true,
284 .@"volatile" = false,
285 .size = .Slice,
286 } },
287
288 .pointer => return self.castTag(.pointer).?.*,
289
290 else => unreachable,
291 }
292 }
293
176294 pub fn eql(a: Type, b: Type) bool {
177295 // As a shortcut, if the small tags / addresses match, we're done.
178296 if (a.tag_if_small_enough == b.tag_if_small_enough)
......@@ -195,25 +313,38 @@ pub const Type = extern union {
195313 return a.elemType().eql(b.elemType());
196314 },
197315 .Pointer => {
198 // Hot path for common case:
199 if (a.castPointer()) |a_payload| {
200 if (b.castPointer()) |b_payload| {
201 return a.tag() == b.tag() and eql(a_payload.data, b_payload.data);
202 }
203 }
204 const is_slice_a = isSlice(a);
205 const is_slice_b = isSlice(b);
206 if (is_slice_a != is_slice_b)
316 const info_a = a.ptrInfo().data;
317 const info_b = b.ptrInfo().data;
318 if (!info_a.pointee_type.eql(info_b.pointee_type))
207319 return false;
208
209 const ptr_size_a = ptrSize(a);
210 const ptr_size_b = ptrSize(b);
211 if (ptr_size_a != ptr_size_b)
320 if (info_a.size != info_b.size)
321 return false;
322 if (info_a.mutable != info_b.mutable)
323 return false;
324 if (info_a.@"volatile" != info_b.@"volatile")
325 return false;
326 if (info_a.@"allowzero" != info_b.@"allowzero")
327 return false;
328 if (info_a.bit_offset != info_b.bit_offset)
329 return false;
330 if (info_a.host_size != info_b.host_size)
212331 return false;
213332
214 std.debug.panic("TODO implement more pointer Type equality comparison: {} and {}", .{
215 a, b,
216 });
333 const sentinel_a = info_a.sentinel;
334 const sentinel_b = info_b.sentinel;
335 if (sentinel_a) |sa| {
336 if (sentinel_b) |sb| {
337 if (!sa.eql(sb))
338 return false;
339 } else {
340 return false;
341 }
342 } else {
343 if (sentinel_b != null)
344 return false;
345 }
346
347 return true;
217348 },
218349 .Int => {
219350 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
......@@ -399,10 +530,10 @@ pub const Type = extern union {
399530 .const_slice_u8,
400531 .enum_literal,
401532 .anyerror_void_error_union,
402 .@"anyframe",
403533 .inferred_alloc_const,
404534 .inferred_alloc_mut,
405535 .var_args_param,
536 .empty_struct_literal,
406537 => unreachable,
407538
408539 .array_u8,
......@@ -420,7 +551,6 @@ pub const Type = extern union {
420551 .optional,
421552 .optional_single_mut_pointer,
422553 .optional_single_const_pointer,
423 .anyframe_T,
424554 => return self.copyPayloadShallow(allocator, Payload.ElemType),
425555
426556 .int_signed,
......@@ -480,13 +610,10 @@ pub const Type = extern union {
480610 .payload = try payload.payload.copy(allocator),
481611 });
482612 },
483 .error_set => return self.copyPayloadShallow(allocator, Payload.Decl),
613 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
484614 .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
485615 .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
486
487 .@"enum" => return self.copyPayloadShallow(allocator, Payload.Enum),
488616 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
489 .@"union" => return self.copyPayloadShallow(allocator, Payload.Union),
490617 .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
491618 }
492619 }
......@@ -549,9 +676,8 @@ pub const Type = extern union {
549676 .@"null" => return out_stream.writeAll("@Type(.Null)"),
550677 .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"),
551678
552 // TODO this should print the structs name
553 .empty_struct => return out_stream.writeAll("struct {}"),
554 .@"anyframe" => return out_stream.writeAll("anyframe"),
679 .empty_struct, .empty_struct_literal => return out_stream.writeAll("struct {}"),
680 .@"struct" => return out_stream.writeAll("(struct)"),
555681 .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
556682 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
557683 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
......@@ -579,12 +705,6 @@ pub const Type = extern union {
579705 continue;
580706 },
581707
582 .anyframe_T => {
583 const return_type = ty.castTag(.anyframe_T).?.data;
584 try out_stream.print("anyframe->", .{});
585 ty = return_type;
586 continue;
587 },
588708 .array_u8 => {
589709 const len = ty.castTag(.array_u8).?.data;
590710 return out_stream.print("[{d}]u8", .{len});
......@@ -715,8 +835,8 @@ pub const Type = extern union {
715835 continue;
716836 },
717837 .error_set => {
718 const decl = ty.castTag(.error_set).?.data;
719 return out_stream.writeAll(std.mem.spanZ(decl.name));
838 const error_set = ty.castTag(.error_set).?.data;
839 return out_stream.writeAll(std.mem.spanZ(error_set.owner_decl.name));
720840 },
721841 .error_set_single => {
722842 const name = ty.castTag(.error_set_single).?.data;
......@@ -725,9 +845,6 @@ pub const Type = extern union {
725845 .inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"),
726846 .inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"),
727847 // TODO use declaration name
728 .@"enum" => return out_stream.writeAll("enum {}"),
729 .@"struct" => return out_stream.writeAll("struct {}"),
730 .@"union" => return out_stream.writeAll("union {}"),
731848 .@"opaque" => return out_stream.writeAll("opaque {}"),
732849 }
733850 unreachable;
......@@ -822,12 +939,22 @@ pub const Type = extern union {
822939 .optional,
823940 .optional_single_mut_pointer,
824941 .optional_single_const_pointer,
825 .@"anyframe",
826 .anyframe_T,
827942 .anyerror_void_error_union,
828943 .error_set,
829944 .error_set_single,
830945 => true,
946
947 .@"struct" => {
948 // TODO introduce lazy value mechanism
949 const struct_obj = self.castTag(.@"struct").?.data;
950 for (struct_obj.fields.entries.items) |entry| {
951 if (entry.value.ty.hasCodeGenBits())
952 return true;
953 } else {
954 return false;
955 }
956 },
957
831958 // TODO lazy types
832959 .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
833960 .array_u8 => self.arrayLen() != 0,
......@@ -839,10 +966,6 @@ pub const Type = extern union {
839966 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
840967 },
841968
842 .@"enum" => @panic("TODO"),
843 .@"struct" => @panic("TODO"),
844 .@"union" => @panic("TODO"),
845
846969 .c_void,
847970 .void,
848971 .type,
......@@ -853,6 +976,7 @@ pub const Type = extern union {
853976 .@"undefined",
854977 .enum_literal,
855978 .empty_struct,
979 .empty_struct_literal,
856980 .@"opaque",
857981 => false,
858982
......@@ -863,7 +987,39 @@ pub const Type = extern union {
863987 }
864988
865989 pub fn isNoReturn(self: Type) bool {
866 return self.zigTypeTag() == .NoReturn;
990 const definitely_correct_result = self.zigTypeTag() == .NoReturn;
991 const fast_result = self.tag_if_small_enough == @enumToInt(Tag.noreturn);
992 assert(fast_result == definitely_correct_result);
993 return fast_result;
994 }
995
996 pub fn ptrAlignment(self: Type, target: Target) u32 {
997 switch (self.tag()) {
998 .single_const_pointer,
999 .single_mut_pointer,
1000 .many_const_pointer,
1001 .many_mut_pointer,
1002 .c_const_pointer,
1003 .c_mut_pointer,
1004 .const_slice,
1005 .mut_slice,
1006 .optional_single_const_pointer,
1007 .optional_single_mut_pointer,
1008 => return self.cast(Payload.ElemType).?.data.abiAlignment(target),
1009
1010 .const_slice_u8 => return 1,
1011
1012 .pointer => {
1013 const ptr_info = self.castTag(.pointer).?.data;
1014 if (ptr_info.@"align" != 0) {
1015 return ptr_info.@"align";
1016 } else {
1017 return ptr_info.pointee_type.abiAlignment();
1018 }
1019 },
1020
1021 else => unreachable,
1022 }
8671023 }
8681024
8691025 /// Asserts that hasCodeGenBits() is true.
......@@ -907,17 +1063,9 @@ pub const Type = extern union {
9071063 .mut_slice,
9081064 .optional_single_const_pointer,
9091065 .optional_single_mut_pointer,
910 .@"anyframe",
911 .anyframe_T,
1066 .pointer,
9121067 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
9131068
914 .pointer => {
915 const payload = self.castTag(.pointer).?.data;
916
917 if (payload.@"align" != 0) return payload.@"align";
918 return @divExact(target.cpu.arch.ptrBitWidth(), 8);
919 },
920
9211069 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
9221070 .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
9231071 .c_int => return @divExact(CType.int.sizeInBits(target), 8),
......@@ -967,9 +1115,9 @@ pub const Type = extern union {
9671115 @panic("TODO abiAlignment error union");
9681116 },
9691117
970 .@"enum" => self.cast(Payload.Enum).?.abiAlignment(target),
971 .@"struct" => @panic("TODO"),
972 .@"union" => @panic("TODO"),
1118 .@"struct" => {
1119 @panic("TODO abiAlignment struct");
1120 },
9731121
9741122 .c_void,
9751123 .void,
......@@ -981,6 +1129,7 @@ pub const Type = extern union {
9811129 .@"undefined",
9821130 .enum_literal,
9831131 .empty_struct,
1132 .empty_struct_literal,
9841133 .inferred_alloc_const,
9851134 .inferred_alloc_mut,
9861135 .@"opaque",
......@@ -1008,11 +1157,16 @@ pub const Type = extern union {
10081157 .enum_literal => unreachable,
10091158 .single_const_pointer_to_comptime_int => unreachable,
10101159 .empty_struct => unreachable,
1160 .empty_struct_literal => unreachable,
10111161 .inferred_alloc_const => unreachable,
10121162 .inferred_alloc_mut => unreachable,
10131163 .@"opaque" => unreachable,
10141164 .var_args_param => unreachable,
10151165
1166 .@"struct" => {
1167 @panic("TODO abiSize struct");
1168 },
1169
10161170 .u8,
10171171 .i8,
10181172 .bool,
......@@ -1038,7 +1192,7 @@ pub const Type = extern union {
10381192 .i64, .u64 => return 8,
10391193 .u128, .i128 => return 16,
10401194
1041 .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
1195 .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
10421196
10431197 .const_slice,
10441198 .mut_slice,
......@@ -1119,10 +1273,6 @@ pub const Type = extern union {
11191273 }
11201274 @panic("TODO abiSize error union");
11211275 },
1122
1123 .@"enum" => @panic("TODO"),
1124 .@"struct" => @panic("TODO"),
1125 .@"union" => @panic("TODO"),
11261276 };
11271277 }
11281278
......@@ -1186,15 +1336,12 @@ pub const Type = extern union {
11861336 .const_slice,
11871337 .mut_slice,
11881338 .error_union,
1189 .@"anyframe",
1190 .anyframe_T,
11911339 .anyerror_void_error_union,
11921340 .error_set,
11931341 .error_set_single,
1194 .empty_struct,
1195 .@"enum",
11961342 .@"struct",
1197 .@"union",
1343 .empty_struct,
1344 .empty_struct_literal,
11981345 .@"opaque",
11991346 .var_args_param,
12001347 => false,
......@@ -1264,16 +1411,13 @@ pub const Type = extern union {
12641411 .optional_single_const_pointer,
12651412 .enum_literal,
12661413 .error_union,
1267 .@"anyframe",
1268 .anyframe_T,
12691414 .anyerror_void_error_union,
12701415 .error_set,
12711416 .error_set_single,
12721417 .empty_struct,
1273 .@"enum",
1274 .@"struct",
1275 .@"union",
1418 .empty_struct_literal,
12761419 .@"opaque",
1420 .@"struct",
12771421 .var_args_param,
12781422 => unreachable,
12791423
......@@ -1361,17 +1505,14 @@ pub const Type = extern union {
13611505 .optional_single_const_pointer,
13621506 .enum_literal,
13631507 .error_union,
1364 .@"anyframe",
1365 .anyframe_T,
13661508 .anyerror_void_error_union,
13671509 .error_set,
13681510 .error_set_single,
13691511 .empty_struct,
1512 .empty_struct_literal,
13701513 .inferred_alloc_const,
13711514 .inferred_alloc_mut,
1372 .@"enum",
13731515 .@"struct",
1374 .@"union",
13751516 .@"opaque",
13761517 .var_args_param,
13771518 => false,
......@@ -1442,17 +1583,14 @@ pub const Type = extern union {
14421583 .enum_literal,
14431584 .mut_slice,
14441585 .error_union,
1445 .@"anyframe",
1446 .anyframe_T,
14471586 .anyerror_void_error_union,
14481587 .error_set,
14491588 .error_set_single,
14501589 .empty_struct,
1590 .empty_struct_literal,
14511591 .inferred_alloc_const,
14521592 .inferred_alloc_mut,
1453 .@"enum",
14541593 .@"struct",
1455 .@"union",
14561594 .@"opaque",
14571595 .var_args_param,
14581596 => false,
......@@ -1532,17 +1670,14 @@ pub const Type = extern union {
15321670 .optional_single_const_pointer,
15331671 .enum_literal,
15341672 .error_union,
1535 .@"anyframe",
1536 .anyframe_T,
15371673 .anyerror_void_error_union,
15381674 .error_set,
15391675 .error_set_single,
15401676 .empty_struct,
1677 .empty_struct_literal,
15411678 .inferred_alloc_const,
15421679 .inferred_alloc_mut,
1543 .@"enum",
15441680 .@"struct",
1545 .@"union",
15461681 .@"opaque",
15471682 .var_args_param,
15481683 => false,
......@@ -1617,17 +1752,14 @@ pub const Type = extern union {
16171752 .optional_single_const_pointer,
16181753 .enum_literal,
16191754 .error_union,
1620 .@"anyframe",
1621 .anyframe_T,
16221755 .anyerror_void_error_union,
16231756 .error_set,
16241757 .error_set_single,
16251758 .empty_struct,
1759 .empty_struct_literal,
16261760 .inferred_alloc_const,
16271761 .inferred_alloc_mut,
1628 .@"enum",
16291762 .@"struct",
1630 .@"union",
16311763 .@"opaque",
16321764 .var_args_param,
16331765 => false,
......@@ -1689,7 +1821,11 @@ pub const Type = extern union {
16891821 .ErrorUnion => ty = ty.errorUnionChild(),
16901822
16911823 .Fn => @panic("TODO fn isValidVarType"),
1692 .Struct => @panic("TODO struct isValidVarType"),
1824 .Struct => {
1825 // TODO this is not always correct; introduce lazy value mechanism
1826 // and here we need to force a resolve of "type requires comptime".
1827 return true;
1828 },
16931829 .Union => @panic("TODO union isValidVarType"),
16941830 };
16951831 }
......@@ -1744,17 +1880,14 @@ pub const Type = extern union {
17441880 .optional_single_mut_pointer => unreachable,
17451881 .enum_literal => unreachable,
17461882 .error_union => unreachable,
1747 .@"anyframe" => unreachable,
1748 .anyframe_T => unreachable,
17491883 .anyerror_void_error_union => unreachable,
17501884 .error_set => unreachable,
17511885 .error_set_single => unreachable,
1886 .@"struct" => unreachable,
17521887 .empty_struct => unreachable,
1888 .empty_struct_literal => unreachable,
17531889 .inferred_alloc_const => unreachable,
17541890 .inferred_alloc_mut => unreachable,
1755 .@"enum" => unreachable,
1756 .@"struct" => unreachable,
1757 .@"union" => unreachable,
17581891 .@"opaque" => unreachable,
17591892 .var_args_param => unreachable,
17601893
......@@ -1897,17 +2030,14 @@ pub const Type = extern union {
18972030 .optional_single_const_pointer,
18982031 .enum_literal,
18992032 .error_union,
1900 .@"anyframe",
1901 .anyframe_T,
19022033 .anyerror_void_error_union,
19032034 .error_set,
19042035 .error_set_single,
2036 .@"struct",
19052037 .empty_struct,
2038 .empty_struct_literal,
19062039 .inferred_alloc_const,
19072040 .inferred_alloc_mut,
1908 .@"enum",
1909 .@"struct",
1910 .@"union",
19112041 .@"opaque",
19122042 .var_args_param,
19132043 => unreachable,
......@@ -1972,17 +2102,14 @@ pub const Type = extern union {
19722102 .optional_single_const_pointer,
19732103 .enum_literal,
19742104 .error_union,
1975 .@"anyframe",
1976 .anyframe_T,
19772105 .anyerror_void_error_union,
19782106 .error_set,
19792107 .error_set_single,
2108 .@"struct",
19802109 .empty_struct,
2110 .empty_struct_literal,
19812111 .inferred_alloc_const,
19822112 .inferred_alloc_mut,
1983 .@"enum",
1984 .@"struct",
1985 .@"union",
19862113 .@"opaque",
19872114 .var_args_param,
19882115 => unreachable,
......@@ -2062,17 +2189,14 @@ pub const Type = extern union {
20622189 .optional_single_const_pointer,
20632190 .enum_literal,
20642191 .error_union,
2065 .@"anyframe",
2066 .anyframe_T,
20672192 .anyerror_void_error_union,
20682193 .error_set,
20692194 .error_set_single,
2195 .@"struct",
20702196 .empty_struct,
2197 .empty_struct_literal,
20712198 .inferred_alloc_const,
20722199 .inferred_alloc_mut,
2073 .@"enum",
2074 .@"struct",
2075 .@"union",
20762200 .@"opaque",
20772201 .var_args_param,
20782202 => false,
......@@ -2148,17 +2272,14 @@ pub const Type = extern union {
21482272 .optional_single_const_pointer,
21492273 .enum_literal,
21502274 .error_union,
2151 .@"anyframe",
2152 .anyframe_T,
21532275 .anyerror_void_error_union,
21542276 .error_set,
21552277 .error_set_single,
2278 .@"struct",
21562279 .empty_struct,
2280 .empty_struct_literal,
21572281 .inferred_alloc_const,
21582282 .inferred_alloc_mut,
2159 .@"enum",
2160 .@"struct",
2161 .@"union",
21622283 .@"opaque",
21632284 .var_args_param,
21642285 => false,
......@@ -2220,17 +2341,14 @@ pub const Type = extern union {
22202341 .optional_single_const_pointer,
22212342 .enum_literal,
22222343 .error_union,
2223 .@"anyframe",
2224 .anyframe_T,
22252344 .anyerror_void_error_union,
22262345 .error_set,
22272346 .error_set_single,
2347 .@"struct",
22282348 .empty_struct,
2349 .empty_struct_literal,
22292350 .inferred_alloc_const,
22302351 .inferred_alloc_mut,
2231 .@"enum",
2232 .@"struct",
2233 .@"union",
22342352 .@"opaque",
22352353 .var_args_param,
22362354 => unreachable,
......@@ -2320,17 +2438,14 @@ pub const Type = extern union {
23202438 .optional_single_const_pointer,
23212439 .enum_literal,
23222440 .error_union,
2323 .@"anyframe",
2324 .anyframe_T,
23252441 .anyerror_void_error_union,
23262442 .error_set,
23272443 .error_set_single,
2444 .@"struct",
23282445 .empty_struct,
2446 .empty_struct_literal,
23292447 .inferred_alloc_const,
23302448 .inferred_alloc_mut,
2331 .@"enum",
2332 .@"struct",
2333 .@"union",
23342449 .@"opaque",
23352450 .var_args_param,
23362451 => false,
......@@ -2441,17 +2556,14 @@ pub const Type = extern union {
24412556 .optional_single_const_pointer,
24422557 .enum_literal,
24432558 .error_union,
2444 .@"anyframe",
2445 .anyframe_T,
24462559 .anyerror_void_error_union,
24472560 .error_set,
24482561 .error_set_single,
2562 .@"struct",
24492563 .empty_struct,
2564 .empty_struct_literal,
24502565 .inferred_alloc_const,
24512566 .inferred_alloc_mut,
2452 .@"enum",
2453 .@"struct",
2454 .@"union",
24552567 .@"opaque",
24562568 .var_args_param,
24572569 => unreachable,
......@@ -2528,17 +2640,14 @@ pub const Type = extern union {
25282640 .optional_single_const_pointer,
25292641 .enum_literal,
25302642 .error_union,
2531 .@"anyframe",
2532 .anyframe_T,
25332643 .anyerror_void_error_union,
25342644 .error_set,
25352645 .error_set_single,
2646 .@"struct",
25362647 .empty_struct,
2648 .empty_struct_literal,
25372649 .inferred_alloc_const,
25382650 .inferred_alloc_mut,
2539 .@"enum",
2540 .@"struct",
2541 .@"union",
25422651 .@"opaque",
25432652 .var_args_param,
25442653 => unreachable,
......@@ -2614,17 +2723,14 @@ pub const Type = extern union {
26142723 .optional_single_const_pointer,
26152724 .enum_literal,
26162725 .error_union,
2617 .@"anyframe",
2618 .anyframe_T,
26192726 .anyerror_void_error_union,
26202727 .error_set,
26212728 .error_set_single,
2729 .@"struct",
26222730 .empty_struct,
2731 .empty_struct_literal,
26232732 .inferred_alloc_const,
26242733 .inferred_alloc_mut,
2625 .@"enum",
2626 .@"struct",
2627 .@"union",
26282734 .@"opaque",
26292735 .var_args_param,
26302736 => unreachable,
......@@ -2700,17 +2806,14 @@ pub const Type = extern union {
27002806 .optional_single_const_pointer,
27012807 .enum_literal,
27022808 .error_union,
2703 .@"anyframe",
2704 .anyframe_T,
27052809 .anyerror_void_error_union,
27062810 .error_set,
27072811 .error_set_single,
2812 .@"struct",
27082813 .empty_struct,
2814 .empty_struct_literal,
27092815 .inferred_alloc_const,
27102816 .inferred_alloc_mut,
2711 .@"enum",
2712 .@"struct",
2713 .@"union",
27142817 .@"opaque",
27152818 .var_args_param,
27162819 => unreachable,
......@@ -2783,17 +2886,14 @@ pub const Type = extern union {
27832886 .optional_single_const_pointer,
27842887 .enum_literal,
27852888 .error_union,
2786 .@"anyframe",
2787 .anyframe_T,
27882889 .anyerror_void_error_union,
27892890 .error_set,
27902891 .error_set_single,
2892 .@"struct",
27912893 .empty_struct,
2894 .empty_struct_literal,
27922895 .inferred_alloc_const,
27932896 .inferred_alloc_mut,
2794 .@"enum",
2795 .@"struct",
2796 .@"union",
27972897 .@"opaque",
27982898 .var_args_param,
27992899 => unreachable,
......@@ -2866,17 +2966,14 @@ pub const Type = extern union {
28662966 .optional_single_const_pointer,
28672967 .enum_literal,
28682968 .error_union,
2869 .@"anyframe",
2870 .anyframe_T,
28712969 .anyerror_void_error_union,
28722970 .error_set,
28732971 .error_set_single,
2972 .@"struct",
28742973 .empty_struct,
2974 .empty_struct_literal,
28752975 .inferred_alloc_const,
28762976 .inferred_alloc_mut,
2877 .@"enum",
2878 .@"struct",
2879 .@"union",
28802977 .@"opaque",
28812978 .var_args_param,
28822979 => unreachable,
......@@ -2949,17 +3046,14 @@ pub const Type = extern union {
29493046 .optional_single_const_pointer,
29503047 .enum_literal,
29513048 .error_union,
2952 .@"anyframe",
2953 .anyframe_T,
29543049 .anyerror_void_error_union,
29553050 .error_set,
29563051 .error_set_single,
3052 .@"struct",
29573053 .empty_struct,
3054 .empty_struct_literal,
29583055 .inferred_alloc_const,
29593056 .inferred_alloc_mut,
2960 .@"enum",
2961 .@"struct",
2962 .@"union",
29633057 .@"opaque",
29643058 .var_args_param,
29653059 => false,
......@@ -3016,8 +3110,6 @@ pub const Type = extern union {
30163110 .optional_single_const_pointer,
30173111 .enum_literal,
30183112 .anyerror_void_error_union,
3019 .anyframe_T,
3020 .@"anyframe",
30213113 .error_union,
30223114 .error_set,
30233115 .error_set_single,
......@@ -3025,11 +3117,12 @@ pub const Type = extern union {
30253117 .var_args_param,
30263118 => return null,
30273119
3028 .@"enum" => @panic("TODO onePossibleValue enum"),
3029 .@"struct" => @panic("TODO onePossibleValue struct"),
3030 .@"union" => @panic("TODO onePossibleValue union"),
3120 .@"struct" => {
3121 log.warn("TODO implement Type.onePossibleValue for structs", .{});
3122 return null;
3123 },
30313124
3032 .empty_struct => return Value.initTag(.empty_struct_value),
3125 .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
30333126 .void => return Value.initTag(.void_value),
30343127 .noreturn => return Value.initTag(.unreachable_value),
30353128 .@"null" => return Value.initTag(.null_value),
......@@ -3128,17 +3221,14 @@ pub const Type = extern union {
31283221 .optional_single_const_pointer,
31293222 .enum_literal,
31303223 .error_union,
3131 .@"anyframe",
3132 .anyframe_T,
31333224 .anyerror_void_error_union,
31343225 .error_set,
31353226 .error_set_single,
3227 .@"struct",
31363228 .empty_struct,
3229 .empty_struct_literal,
31373230 .inferred_alloc_const,
31383231 .inferred_alloc_mut,
3139 .@"enum",
3140 .@"struct",
3141 .@"union",
31423232 .@"opaque",
31433233 .var_args_param,
31443234 => return false,
......@@ -3220,8 +3310,6 @@ pub const Type = extern union {
32203310 .optional_single_const_pointer,
32213311 .enum_literal,
32223312 .error_union,
3223 .@"anyframe",
3224 .anyframe_T,
32253313 .anyerror_void_error_union,
32263314 .error_set,
32273315 .error_set_single,
......@@ -3231,13 +3319,12 @@ pub const Type = extern union {
32313319 .inferred_alloc_const,
32323320 .inferred_alloc_mut,
32333321 .var_args_param,
3322 .empty_struct_literal,
32343323 => unreachable,
32353324
3325 .@"struct" => &self.castTag(.@"struct").?.data.container,
32363326 .empty_struct => self.castTag(.empty_struct).?.data,
3237 .@"enum" => &self.castTag(.@"enum").?.scope,
3238 .@"struct" => &self.castTag(.@"struct").?.scope,
3239 .@"union" => &self.castTag(.@"union").?.scope,
3240 .@"opaque" => &self.castTag(.@"opaque").?.scope,
3327 .@"opaque" => &self.castTag(.@"opaque").?.data,
32413328 };
32423329 }
32433330
......@@ -3296,6 +3383,10 @@ pub const Type = extern union {
32963383 }
32973384 }
32983385
3386 pub fn isExhaustiveEnum(ty: Type) bool {
3387 return false; // TODO
3388 }
3389
32993390 /// This enum does not directly correspond to `std.builtin.TypeId` because
33003391 /// it has extra enum tags in it, as a way of using less memory. For example,
33013392 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
......@@ -3346,11 +3437,12 @@ pub const Type = extern union {
33463437 fn_ccc_void_no_args,
33473438 single_const_pointer_to_comptime_int,
33483439 anyerror_void_error_union,
3349 @"anyframe",
33503440 const_slice_u8,
33513441 /// This is a special type for variadic parameters of a function call.
33523442 /// Casts to it will validate that the type can be passed to a c calling convetion function.
33533443 var_args_param,
3444 /// Same as `empty_struct` except it has an empty namespace.
3445 empty_struct_literal,
33543446 /// This is a special value that tracks a set of types that have been stored
33553447 /// to an inferred allocation. It does not support most of the normal type queries.
33563448 /// However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc.
......@@ -3379,14 +3471,11 @@ pub const Type = extern union {
33793471 optional_single_mut_pointer,
33803472 optional_single_const_pointer,
33813473 error_union,
3382 anyframe_T,
33833474 error_set,
33843475 error_set_single,
33853476 empty_struct,
3386 @"enum",
3387 @"struct",
3388 @"union",
33893477 @"opaque",
3478 @"struct",
33903479
33913480 pub const last_no_payload_tag = Tag.inferred_alloc_const;
33923481 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -3435,11 +3524,11 @@ pub const Type = extern union {
34353524 .fn_ccc_void_no_args,
34363525 .single_const_pointer_to_comptime_int,
34373526 .anyerror_void_error_union,
3438 .@"anyframe",
34393527 .const_slice_u8,
34403528 .inferred_alloc_const,
34413529 .inferred_alloc_mut,
34423530 .var_args_param,
3531 .empty_struct_literal,
34433532 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
34443533
34453534 .array_u8,
......@@ -3457,25 +3546,23 @@ pub const Type = extern union {
34573546 .optional,
34583547 .optional_single_mut_pointer,
34593548 .optional_single_const_pointer,
3460 .anyframe_T,
34613549 => Payload.ElemType,
34623550
34633551 .int_signed,
34643552 .int_unsigned,
34653553 => Payload.Bits,
34663554
3555 .error_set => Payload.ErrorSet,
3556
34673557 .array => Payload.Array,
34683558 .array_sentinel => Payload.ArraySentinel,
34693559 .pointer => Payload.Pointer,
34703560 .function => Payload.Function,
34713561 .error_union => Payload.ErrorUnion,
3472 .error_set => Payload.Decl,
34733562 .error_set_single => Payload.Name,
3474 .empty_struct => Payload.ContainerScope,
3475 .@"enum" => Payload.Enum,
3476 .@"struct" => Payload.Struct,
3477 .@"union" => Payload.Union,
34783563 .@"opaque" => Payload.Opaque,
3564 .@"struct" => Payload.Struct,
3565 .empty_struct => Payload.ContainerScope,
34793566 };
34803567 }
34813568
......@@ -3550,6 +3637,13 @@ pub const Type = extern union {
35503637 },
35513638 };
35523639
3640 pub const ErrorSet = struct {
3641 pub const base_tag = Tag.error_set;
3642
3643 base: Payload = Payload{ .tag = base_tag },
3644 data: *Module.ErrorSet,
3645 };
3646
35533647 pub const Pointer = struct {
35543648 pub const base_tag = Tag.pointer;
35553649
......@@ -3598,13 +3692,13 @@ pub const Type = extern union {
35983692
35993693 pub const Opaque = struct {
36003694 base: Payload = .{ .tag = .@"opaque" },
3601
3602 scope: Module.Scope.Container,
3695 data: Module.Scope.Container,
36033696 };
36043697
3605 pub const Enum = @import("type/Enum.zig");
3606 pub const Struct = @import("type/Struct.zig");
3607 pub const Union = @import("type/Union.zig");
3698 pub const Struct = struct {
3699 base: Payload = .{ .tag = .@"struct" },
3700 data: *Module.Struct,
3701 };
36083702 };
36093703};
36103704
src/type/Enum.zig deleted-55
......@@ -1,55 +0,0 @@
1const std = @import("std");
2const zir = @import("../zir.zig");
3const Value = @import("../value.zig").Value;
4const Type = @import("../type.zig").Type;
5const Module = @import("../Module.zig");
6const Scope = Module.Scope;
7const Enum = @This();
8
9base: Type.Payload = .{ .tag = .@"enum" },
10
11analysis: union(enum) {
12 queued: Zir,
13 in_progress,
14 resolved: Size,
15 failed,
16},
17scope: Scope.Container,
18
19pub const Field = struct {
20 value: Value,
21};
22
23pub const Zir = struct {
24 body: zir.Body,
25 inst: *zir.Inst,
26};
27
28pub const Size = struct {
29 tag_type: Type,
30 fields: std.StringArrayHashMapUnmanaged(Field),
31};
32
33pub fn resolve(self: *Enum, mod: *Module, scope: *Scope) !void {
34 const zir = switch (self.analysis) {
35 .failed => return error.AnalysisFail,
36 .resolved => return,
37 .in_progress => {
38 return mod.fail(scope, src, "enum '{}' depends on itself", .{enum_name});
39 },
40 .queued => |zir| zir,
41 };
42 self.analysis = .in_progress;
43
44 // TODO
45}
46
47// TODO should this resolve the type or assert that it has already been resolved?
48pub fn abiAlignment(self: *Enum, target: std.Target) u32 {
49 switch (self.analysis) {
50 .queued => unreachable, // alignment has not been resolved
51 .in_progress => unreachable, // alignment has not been resolved
52 .failed => unreachable, // type resolution failed
53 .resolved => |r| return r.tag_type.abiAlignment(target),
54 }
55}
src/type/Struct.zig deleted-56
......@@ -1,56 +0,0 @@
1const std = @import("std");
2const zir = @import("../zir.zig");
3const Value = @import("../value.zig").Value;
4const Type = @import("../type.zig").Type;
5const Module = @import("../Module.zig");
6const Scope = Module.Scope;
7const Struct = @This();
8
9base: Type.Payload = .{ .tag = .@"struct" },
10
11analysis: union(enum) {
12 queued: Zir,
13 zero_bits_in_progress,
14 zero_bits: Zero,
15 in_progress,
16 // alignment: Align,
17 resolved: Size,
18 failed,
19},
20scope: Scope.Container,
21
22pub const Field = struct {
23 value: Value,
24};
25
26pub const Zir = struct {
27 body: zir.Body,
28 inst: *zir.Inst,
29};
30
31pub const Zero = struct {
32 is_zero_bits: bool,
33 fields: std.StringArrayHashMapUnmanaged(Field),
34};
35
36pub const Size = struct {
37 is_zero_bits: bool,
38 alignment: u32,
39 size: u32,
40 fields: std.StringArrayHashMapUnmanaged(Field),
41};
42
43pub fn resolveZeroBits(self: *Struct, mod: *Module, scope: *Scope) !void {
44 const zir = switch (self.analysis) {
45 .failed => return error.AnalysisFail,
46 .zero_bits_in_progress => {
47 return mod.fail(scope, src, "struct '{}' depends on itself", .{});
48 },
49 .queued => |zir| zir,
50 else => return,
51 };
52
53 self.analysis = .zero_bits_in_progress;
54
55 // TODO
56}
src/type/Union.zig deleted-56
......@@ -1,56 +0,0 @@
1const std = @import("std");
2const zir = @import("../zir.zig");
3const Value = @import("../value.zig").Value;
4const Type = @import("../type.zig").Type;
5const Module = @import("../Module.zig");
6const Scope = Module.Scope;
7const Union = @This();
8
9base: Type.Payload = .{ .tag = .@"struct" },
10
11analysis: union(enum) {
12 queued: Zir,
13 zero_bits_in_progress,
14 zero_bits: Zero,
15 in_progress,
16 // alignment: Align,
17 resolved: Size,
18 failed,
19},
20scope: Scope.Container,
21
22pub const Field = struct {
23 value: Value,
24};
25
26pub const Zir = struct {
27 body: zir.Body,
28 inst: *zir.Inst,
29};
30
31pub const Zero = struct {
32 is_zero_bits: bool,
33 fields: std.StringArrayHashMapUnmanaged(Field),
34};
35
36pub const Size = struct {
37 is_zero_bits: bool,
38 alignment: u32,
39 size: u32,
40 fields: std.StringArrayHashMapUnmanaged(Field),
41};
42
43pub fn resolveZeroBits(self: *Union, mod: *Module, scope: *Scope) !void {
44 const zir = switch (self.analysis) {
45 .failed => return error.AnalysisFail,
46 .zero_bits_in_progress => {
47 return mod.fail(scope, src, "union '{}' depends on itself", .{});
48 },
49 .queued => |zir| zir,
50 else => return,
51 };
52
53 self.analysis = .zero_bits_in_progress;
54
55 // TODO
56}
src/value.zig+60-69
......@@ -30,6 +30,8 @@ pub const Value = extern union {
3030 i32_type,
3131 u64_type,
3232 i64_type,
33 u128_type,
34 i128_type,
3335 usize_type,
3436 isize_type,
3537 c_short_type,
......@@ -62,18 +64,19 @@ pub const Value = extern union {
6264 single_const_pointer_to_comptime_int_type,
6365 const_slice_u8_type,
6466 enum_literal_type,
65 anyframe_type,
6667
6768 undef,
6869 zero,
6970 one,
7071 void_value,
7172 unreachable_value,
72 empty_struct_value,
73 empty_array,
7473 null_value,
7574 bool_true,
76 bool_false, // See last_no_payload_tag below.
75 bool_false,
76
77 abi_align_default,
78 empty_struct_value,
79 empty_array, // See last_no_payload_tag below.
7780 // After this, the tag requires a payload.
7881
7982 ty,
......@@ -100,14 +103,13 @@ pub const Value = extern union {
100103 float_64,
101104 float_128,
102105 enum_literal,
103 error_set,
104106 @"error",
105107 error_union,
106108 /// This is a special value that tracks a set of types that have been stored
107109 /// to an inferred allocation. It does not support any of the normal value queries.
108110 inferred_alloc,
109111
110 pub const last_no_payload_tag = Tag.bool_false;
112 pub const last_no_payload_tag = Tag.empty_array;
111113 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
112114
113115 pub fn Type(comptime t: Tag) type {
......@@ -120,6 +122,8 @@ pub const Value = extern union {
120122 .i32_type,
121123 .u64_type,
122124 .i64_type,
125 .u128_type,
126 .i128_type,
123127 .usize_type,
124128 .isize_type,
125129 .c_short_type,
......@@ -152,7 +156,6 @@ pub const Value = extern union {
152156 .single_const_pointer_to_comptime_int_type,
153157 .const_slice_u8_type,
154158 .enum_literal_type,
155 .anyframe_type,
156159 .undef,
157160 .zero,
158161 .one,
......@@ -163,6 +166,7 @@ pub const Value = extern union {
163166 .null_value,
164167 .bool_true,
165168 .bool_false,
169 .abi_align_default,
166170 => @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"),
167171
168172 .int_big_positive,
......@@ -193,7 +197,6 @@ pub const Value = extern union {
193197 .float_32 => Payload.Float_32,
194198 .float_64 => Payload.Float_64,
195199 .float_128 => Payload.Float_128,
196 .error_set => Payload.ErrorSet,
197200 .@"error" => Payload.Error,
198201 .inferred_alloc => Payload.InferredAlloc,
199202 };
......@@ -275,6 +278,8 @@ pub const Value = extern union {
275278 .i32_type,
276279 .u64_type,
277280 .i64_type,
281 .u128_type,
282 .i128_type,
278283 .usize_type,
279284 .isize_type,
280285 .c_short_type,
......@@ -307,7 +312,6 @@ pub const Value = extern union {
307312 .single_const_pointer_to_comptime_int_type,
308313 .const_slice_u8_type,
309314 .enum_literal_type,
310 .anyframe_type,
311315 .undef,
312316 .zero,
313317 .one,
......@@ -318,6 +322,7 @@ pub const Value = extern union {
318322 .bool_true,
319323 .bool_false,
320324 .empty_struct_value,
325 .abi_align_default,
321326 => unreachable,
322327
323328 .ty => {
......@@ -400,7 +405,6 @@ pub const Value = extern union {
400405 return Value{ .ptr_otherwise = &new_payload.base };
401406 },
402407
403 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
404408 .inferred_alloc => unreachable,
405409 }
406410 }
......@@ -429,6 +433,8 @@ pub const Value = extern union {
429433 .i32_type => return out_stream.writeAll("i32"),
430434 .u64_type => return out_stream.writeAll("u64"),
431435 .i64_type => return out_stream.writeAll("i64"),
436 .u128_type => return out_stream.writeAll("u128"),
437 .i128_type => return out_stream.writeAll("i128"),
432438 .isize_type => return out_stream.writeAll("isize"),
433439 .usize_type => return out_stream.writeAll("usize"),
434440 .c_short_type => return out_stream.writeAll("c_short"),
......@@ -461,9 +467,8 @@ pub const Value = extern union {
461467 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
462468 .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
463469 .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
464 .anyframe_type => return out_stream.writeAll("anyframe"),
470 .abi_align_default => return out_stream.writeAll("(default ABI alignment)"),
465471
466 // TODO this should print `NAME{}`
467472 .empty_struct_value => return out_stream.writeAll("struct {}{}"),
468473 .null_value => return out_stream.writeAll("null"),
469474 .undef => return out_stream.writeAll("undefined"),
......@@ -510,15 +515,6 @@ pub const Value = extern union {
510515 .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}),
511516 .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}),
512517 .float_128 => return out_stream.print("{}", .{val.castTag(.float_128).?.data}),
513 .error_set => {
514 const error_set = val.castTag(.error_set).?.data;
515 try out_stream.writeAll("error{");
516 var it = error_set.fields.iterator();
517 while (it.next()) |entry| {
518 try out_stream.print("{},", .{entry.value});
519 }
520 return out_stream.writeAll("}");
521 },
522518 .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}),
523519 // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that
524520 .error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}),
......@@ -557,6 +553,8 @@ pub const Value = extern union {
557553 .i32_type => Type.initTag(.i32),
558554 .u64_type => Type.initTag(.u64),
559555 .i64_type => Type.initTag(.i64),
556 .u128_type => Type.initTag(.u128),
557 .i128_type => Type.initTag(.i128),
560558 .usize_type => Type.initTag(.usize),
561559 .isize_type => Type.initTag(.isize),
562560 .c_short_type => Type.initTag(.c_short),
......@@ -589,7 +587,6 @@ pub const Value = extern union {
589587 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
590588 .const_slice_u8_type => Type.initTag(.const_slice_u8),
591589 .enum_literal_type => Type.initTag(.enum_literal),
592 .anyframe_type => Type.initTag(.@"anyframe"),
593590
594591 .int_type => {
595592 const payload = self.castTag(.int_type).?.data;
......@@ -602,10 +599,6 @@ pub const Value = extern union {
602599 };
603600 return Type.initPayload(&new.base);
604601 },
605 .error_set => {
606 const payload = self.castTag(.error_set).?.data;
607 return Type.Tag.error_set.create(allocator, payload.decl);
608 },
609602
610603 .undef,
611604 .zero,
......@@ -637,6 +630,7 @@ pub const Value = extern union {
637630 .error_union,
638631 .empty_struct_value,
639632 .inferred_alloc,
633 .abi_align_default,
640634 => unreachable,
641635 };
642636 }
......@@ -654,6 +648,8 @@ pub const Value = extern union {
654648 .i32_type,
655649 .u64_type,
656650 .i64_type,
651 .u128_type,
652 .i128_type,
657653 .usize_type,
658654 .isize_type,
659655 .c_short_type,
......@@ -686,7 +682,6 @@ pub const Value = extern union {
686682 .single_const_pointer_to_comptime_int_type,
687683 .const_slice_u8_type,
688684 .enum_literal_type,
689 .anyframe_type,
690685 .null_value,
691686 .function,
692687 .extern_fn,
......@@ -704,11 +699,11 @@ pub const Value = extern union {
704699 .unreachable_value,
705700 .empty_array,
706701 .enum_literal,
707 .error_set,
708702 .error_union,
709703 .@"error",
710704 .empty_struct_value,
711705 .inferred_alloc,
706 .abi_align_default,
712707 => unreachable,
713708
714709 .undef => unreachable,
......@@ -741,6 +736,8 @@ pub const Value = extern union {
741736 .i32_type,
742737 .u64_type,
743738 .i64_type,
739 .u128_type,
740 .i128_type,
744741 .usize_type,
745742 .isize_type,
746743 .c_short_type,
......@@ -773,7 +770,6 @@ pub const Value = extern union {
773770 .single_const_pointer_to_comptime_int_type,
774771 .const_slice_u8_type,
775772 .enum_literal_type,
776 .anyframe_type,
777773 .null_value,
778774 .function,
779775 .extern_fn,
......@@ -791,11 +787,11 @@ pub const Value = extern union {
791787 .unreachable_value,
792788 .empty_array,
793789 .enum_literal,
794 .error_set,
795790 .@"error",
796791 .error_union,
797792 .empty_struct_value,
798793 .inferred_alloc,
794 .abi_align_default,
799795 => unreachable,
800796
801797 .undef => unreachable,
......@@ -828,6 +824,8 @@ pub const Value = extern union {
828824 .i32_type,
829825 .u64_type,
830826 .i64_type,
827 .u128_type,
828 .i128_type,
831829 .usize_type,
832830 .isize_type,
833831 .c_short_type,
......@@ -860,7 +858,6 @@ pub const Value = extern union {
860858 .single_const_pointer_to_comptime_int_type,
861859 .const_slice_u8_type,
862860 .enum_literal_type,
863 .anyframe_type,
864861 .null_value,
865862 .function,
866863 .extern_fn,
......@@ -878,11 +875,11 @@ pub const Value = extern union {
878875 .unreachable_value,
879876 .empty_array,
880877 .enum_literal,
881 .error_set,
882878 .@"error",
883879 .error_union,
884880 .empty_struct_value,
885881 .inferred_alloc,
882 .abi_align_default,
886883 => unreachable,
887884
888885 .undef => unreachable,
......@@ -942,6 +939,8 @@ pub const Value = extern union {
942939 .i32_type,
943940 .u64_type,
944941 .i64_type,
942 .u128_type,
943 .i128_type,
945944 .usize_type,
946945 .isize_type,
947946 .c_short_type,
......@@ -974,7 +973,6 @@ pub const Value = extern union {
974973 .single_const_pointer_to_comptime_int_type,
975974 .const_slice_u8_type,
976975 .enum_literal_type,
977 .anyframe_type,
978976 .null_value,
979977 .function,
980978 .extern_fn,
......@@ -993,11 +991,11 @@ pub const Value = extern union {
993991 .unreachable_value,
994992 .empty_array,
995993 .enum_literal,
996 .error_set,
997994 .@"error",
998995 .error_union,
999996 .empty_struct_value,
1000997 .inferred_alloc,
998 .abi_align_default,
1001999 => unreachable,
10021000
10031001 .zero,
......@@ -1034,6 +1032,8 @@ pub const Value = extern union {
10341032 .i32_type,
10351033 .u64_type,
10361034 .i64_type,
1035 .u128_type,
1036 .i128_type,
10371037 .usize_type,
10381038 .isize_type,
10391039 .c_short_type,
......@@ -1066,7 +1066,6 @@ pub const Value = extern union {
10661066 .single_const_pointer_to_comptime_int_type,
10671067 .const_slice_u8_type,
10681068 .enum_literal_type,
1069 .anyframe_type,
10701069 .null_value,
10711070 .function,
10721071 .extern_fn,
......@@ -1084,11 +1083,11 @@ pub const Value = extern union {
10841083 .unreachable_value,
10851084 .empty_array,
10861085 .enum_literal,
1087 .error_set,
10881086 .@"error",
10891087 .error_union,
10901088 .empty_struct_value,
10911089 .inferred_alloc,
1090 .abi_align_default,
10921091 => unreachable,
10931092
10941093 .zero,
......@@ -1191,6 +1190,8 @@ pub const Value = extern union {
11911190 .i32_type,
11921191 .u64_type,
11931192 .i64_type,
1193 .u128_type,
1194 .i128_type,
11941195 .usize_type,
11951196 .isize_type,
11961197 .c_short_type,
......@@ -1223,7 +1224,6 @@ pub const Value = extern union {
12231224 .single_const_pointer_to_comptime_int_type,
12241225 .const_slice_u8_type,
12251226 .enum_literal_type,
1226 .anyframe_type,
12271227 .bool_true,
12281228 .bool_false,
12291229 .null_value,
......@@ -1244,11 +1244,11 @@ pub const Value = extern union {
12441244 .void_value,
12451245 .unreachable_value,
12461246 .enum_literal,
1247 .error_set,
12481247 .@"error",
12491248 .error_union,
12501249 .empty_struct_value,
12511250 .inferred_alloc,
1251 .abi_align_default,
12521252 => unreachable,
12531253
12541254 .zero,
......@@ -1275,6 +1275,8 @@ pub const Value = extern union {
12751275 .i32_type,
12761276 .u64_type,
12771277 .i64_type,
1278 .u128_type,
1279 .i128_type,
12781280 .usize_type,
12791281 .isize_type,
12801282 .c_short_type,
......@@ -1307,7 +1309,6 @@ pub const Value = extern union {
13071309 .single_const_pointer_to_comptime_int_type,
13081310 .const_slice_u8_type,
13091311 .enum_literal_type,
1310 .anyframe_type,
13111312 .null_value,
13121313 .function,
13131314 .extern_fn,
......@@ -1322,11 +1323,11 @@ pub const Value = extern union {
13221323 .unreachable_value,
13231324 .empty_array,
13241325 .enum_literal,
1325 .error_set,
13261326 .@"error",
13271327 .error_union,
13281328 .empty_struct_value,
13291329 .inferred_alloc,
1330 .abi_align_default,
13301331 => unreachable,
13311332
13321333 .zero,
......@@ -1427,6 +1428,8 @@ pub const Value = extern union {
14271428 .i32_type,
14281429 .u64_type,
14291430 .i64_type,
1431 .u128_type,
1432 .i128_type,
14301433 .usize_type,
14311434 .isize_type,
14321435 .c_short_type,
......@@ -1459,18 +1462,13 @@ pub const Value = extern union {
14591462 .single_const_pointer_to_comptime_int_type,
14601463 .const_slice_u8_type,
14611464 .enum_literal_type,
1462 .anyframe_type,
14631465 .ty,
1466 .abi_align_default,
14641467 => {
1465 // Directly return Type.hash, toType can only fail for .int_type and .error_set.
1468 // Directly return Type.hash, toType can only fail for .int_type.
14661469 var allocator = std.heap.FixedBufferAllocator.init(&[_]u8{});
14671470 return (self.toType(&allocator.allocator) catch unreachable).hash();
14681471 },
1469 .error_set => {
1470 // Payload.decl should be same for all instances of the type.
1471 const payload = self.castTag(.error_set).?.data;
1472 std.hash.autoHash(&hasher, payload.decl);
1473 },
14741472 .int_type => {
14751473 const payload = self.castTag(.int_type).?.data;
14761474 var int_payload = Type.Payload.Bits{
......@@ -1585,6 +1583,8 @@ pub const Value = extern union {
15851583 .i32_type,
15861584 .u64_type,
15871585 .i64_type,
1586 .u128_type,
1587 .i128_type,
15881588 .usize_type,
15891589 .isize_type,
15901590 .c_short_type,
......@@ -1617,7 +1617,6 @@ pub const Value = extern union {
16171617 .single_const_pointer_to_comptime_int_type,
16181618 .const_slice_u8_type,
16191619 .enum_literal_type,
1620 .anyframe_type,
16211620 .zero,
16221621 .one,
16231622 .bool_true,
......@@ -1641,11 +1640,11 @@ pub const Value = extern union {
16411640 .unreachable_value,
16421641 .empty_array,
16431642 .enum_literal,
1644 .error_set,
16451643 .@"error",
16461644 .error_union,
16471645 .empty_struct_value,
16481646 .inferred_alloc,
1647 .abi_align_default,
16491648 => unreachable,
16501649
16511650 .ref_val => self.castTag(.ref_val).?.data,
......@@ -1672,6 +1671,8 @@ pub const Value = extern union {
16721671 .i32_type,
16731672 .u64_type,
16741673 .i64_type,
1674 .u128_type,
1675 .i128_type,
16751676 .usize_type,
16761677 .isize_type,
16771678 .c_short_type,
......@@ -1704,7 +1705,6 @@ pub const Value = extern union {
17041705 .single_const_pointer_to_comptime_int_type,
17051706 .const_slice_u8_type,
17061707 .enum_literal_type,
1707 .anyframe_type,
17081708 .zero,
17091709 .one,
17101710 .bool_true,
......@@ -1728,11 +1728,11 @@ pub const Value = extern union {
17281728 .void_value,
17291729 .unreachable_value,
17301730 .enum_literal,
1731 .error_set,
17321731 .@"error",
17331732 .error_union,
17341733 .empty_struct_value,
17351734 .inferred_alloc,
1735 .abi_align_default,
17361736 => unreachable,
17371737
17381738 .empty_array => unreachable, // out of bounds array index
......@@ -1776,6 +1776,8 @@ pub const Value = extern union {
17761776 .i32_type,
17771777 .u64_type,
17781778 .i64_type,
1779 .u128_type,
1780 .i128_type,
17791781 .usize_type,
17801782 .isize_type,
17811783 .c_short_type,
......@@ -1808,7 +1810,6 @@ pub const Value = extern union {
18081810 .single_const_pointer_to_comptime_int_type,
18091811 .const_slice_u8_type,
18101812 .enum_literal_type,
1811 .anyframe_type,
18121813 .zero,
18131814 .one,
18141815 .empty_array,
......@@ -1832,10 +1833,10 @@ pub const Value = extern union {
18321833 .float_128,
18331834 .void_value,
18341835 .enum_literal,
1835 .error_set,
18361836 .@"error",
18371837 .error_union,
18381838 .empty_struct_value,
1839 .abi_align_default,
18391840 => false,
18401841
18411842 .undef => unreachable,
......@@ -1858,6 +1859,8 @@ pub const Value = extern union {
18581859 .i32_type,
18591860 .u64_type,
18601861 .i64_type,
1862 .u128_type,
1863 .i128_type,
18611864 .usize_type,
18621865 .isize_type,
18631866 .c_short_type,
......@@ -1890,7 +1893,6 @@ pub const Value = extern union {
18901893 .single_const_pointer_to_comptime_int_type,
18911894 .const_slice_u8_type,
18921895 .enum_literal_type,
1893 .anyframe_type,
18941896 .zero,
18951897 .one,
18961898 .null_value,
......@@ -1915,8 +1917,8 @@ pub const Value = extern union {
19151917 .float_128,
19161918 .void_value,
19171919 .enum_literal,
1918 .error_set,
19191920 .empty_struct_value,
1921 .abi_align_default,
19201922 => null,
19211923
19221924 .error_union => {
......@@ -1960,6 +1962,8 @@ pub const Value = extern union {
19601962 .i32_type,
19611963 .u64_type,
19621964 .i64_type,
1965 .u128_type,
1966 .i128_type,
19631967 .usize_type,
19641968 .isize_type,
19651969 .c_short_type,
......@@ -1992,8 +1996,6 @@ pub const Value = extern union {
19921996 .single_const_pointer_to_comptime_int_type,
19931997 .const_slice_u8_type,
19941998 .enum_literal_type,
1995 .anyframe_type,
1996 .error_set,
19971999 => true,
19982000
19992001 .zero,
......@@ -2023,6 +2025,7 @@ pub const Value = extern union {
20232025 .error_union,
20242026 .empty_struct_value,
20252027 .null_value,
2028 .abi_align_default,
20262029 => false,
20272030
20282031 .undef => unreachable,
......@@ -2137,18 +2140,6 @@ pub const Value = extern union {
21372140 data: f128,
21382141 };
21392142
2140 /// TODO move to type.zig
2141 pub const ErrorSet = struct {
2142 pub const base_tag = Tag.error_set;
2143
2144 base: Payload = .{ .tag = base_tag },
2145 data: struct {
2146 /// TODO revisit this when we have the concept of the error tag type
2147 fields: std.StringHashMapUnmanaged(void),
2148 decl: *Module.Decl,
2149 },
2150 };
2151
21522143 pub const Error = struct {
21532144 base: Payload = .{ .tag = .@"error" },
21542145 data: struct {
src/zir.zig+1859-1584
......@@ -1,4 +1,5 @@
1//! This file has to do with parsing and rendering the ZIR text format.
1//! Zig Intermediate Representation. Astgen.zig converts AST nodes to these
2//! untyped IR instructions. Next, Sema.zig processes these into TZIR.
23
34const std = @import("std");
45const mem = std.mem;
......@@ -6,524 +7,663 @@ const Allocator = std.mem.Allocator;
67const assert = std.debug.assert;
78const BigIntConst = std.math.big.int.Const;
89const BigIntMutable = std.math.big.int.Mutable;
10const ast = std.zig.ast;
11
912const Type = @import("type.zig").Type;
1013const Value = @import("value.zig").Value;
1114const TypedValue = @import("TypedValue.zig");
1215const ir = @import("ir.zig");
13const IrModule = @import("Module.zig");
16const Module = @import("Module.zig");
17const LazySrcLoc = Module.LazySrcLoc;
18
19/// The minimum amount of information needed to represent a list of ZIR instructions.
20/// Once this structure is completed, it can be used to generate TZIR, followed by
21/// machine code, without any memory access into the AST tree token list, node list,
22/// or source bytes. Exceptions include:
23/// * Compile errors, which may need to reach into these data structures to
24/// create a useful report.
25/// * In the future, possibly inline assembly, which needs to get parsed and
26/// handled by the codegen backend, and errors reported there. However for now,
27/// inline assembly is not an exception.
28pub const Code = struct {
29 /// There is always implicitly a `block` instruction at index 0.
30 /// This is so that `break_inline` can break from the root block.
31 instructions: std.MultiArrayList(Inst).Slice,
32 /// In order to store references to strings in fewer bytes, we copy all
33 /// string bytes into here. String bytes can be null. It is up to whomever
34 /// is referencing the data here whether they want to store both index and length,
35 /// thus allowing null bytes, or store only index, and use null-termination. The
36 /// `string_bytes` array is agnostic to either usage.
37 string_bytes: []u8,
38 /// The meaning of this data is determined by `Inst.Tag` value.
39 extra: []u32,
40 /// Used for decl_val and decl_ref instructions.
41 decls: []*Module.Decl,
42
43 /// Returns the requested data, as well as the new index which is at the start of the
44 /// trailers for the object.
45 pub fn extraData(code: Code, comptime T: type, index: usize) struct { data: T, end: usize } {
46 const fields = std.meta.fields(T);
47 var i: usize = index;
48 var result: T = undefined;
49 inline for (fields) |field| {
50 @field(result, field.name) = switch (field.field_type) {
51 u32 => code.extra[i],
52 Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
53 else => unreachable,
54 };
55 i += 1;
56 }
57 return .{
58 .data = result,
59 .end = i,
60 };
61 }
62
63 /// Given an index into `string_bytes` returns the null-terminated string found there.
64 pub fn nullTerminatedString(code: Code, index: usize) [:0]const u8 {
65 var end: usize = index;
66 while (code.string_bytes[end] != 0) {
67 end += 1;
68 }
69 return code.string_bytes[index..end :0];
70 }
71
72 pub fn refSlice(code: Code, start: usize, len: usize) []Inst.Ref {
73 const raw_slice = code.extra[start..][0..len];
74 return @bitCast([]Inst.Ref, raw_slice);
75 }
76
77 pub fn deinit(code: *Code, gpa: *Allocator) void {
78 code.instructions.deinit(gpa);
79 gpa.free(code.string_bytes);
80 gpa.free(code.extra);
81 gpa.free(code.decls);
82 code.* = undefined;
83 }
84
85 /// For debugging purposes, like dumpFn but for unanalyzed zir blocks
86 pub fn dump(
87 code: Code,
88 gpa: *Allocator,
89 kind: []const u8,
90 scope: *Module.Scope,
91 param_count: usize,
92 ) !void {
93 var arena = std.heap.ArenaAllocator.init(gpa);
94 defer arena.deinit();
95
96 var writer: Writer = .{
97 .gpa = gpa,
98 .arena = &arena.allocator,
99 .scope = scope,
100 .code = code,
101 .indent = 0,
102 .param_count = param_count,
103 };
104
105 const decl_name = scope.srcDecl().?.name;
106 const stderr = std.io.getStdErr().writer();
107 try stderr.print("ZIR {s} {s} %0 ", .{ kind, decl_name });
108 try writer.writeInstToStream(stderr, 0);
109 try stderr.print(" // end ZIR {s} {s}\n\n", .{ kind, decl_name });
110 }
111};
14112
15/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
16/// in-memory, analyzed instructions with types and values.
17/// We use a table to map these instruction to their respective semantically analyzed
18/// instructions because it is possible to have multiple analyses on the same ZIR
19/// happening at the same time.
113/// These are untyped instructions generated from an Abstract Syntax Tree.
114/// The data here is immutable because it is possible to have multiple
115/// analyses on the same ZIR happening at the same time.
20116pub const Inst = struct {
21117 tag: Tag,
22 /// Byte offset into the source.
23 src: usize,
118 data: Data,
24119
25120 /// These names are used directly as the instruction names in the text format.
26 pub const Tag = enum {
121 pub const Tag = enum(u8) {
27122 /// Arithmetic addition, asserts no integer overflow.
123 /// Uses the `pl_node` union field. Payload is `Bin`.
28124 add,
29125 /// Twos complement wrapping integer addition.
126 /// Uses the `pl_node` union field. Payload is `Bin`.
30127 addwrap,
31 /// Allocates stack local memory. Its lifetime ends when the block ends that contains
32 /// this instruction. The operand is the type of the allocated object.
128 /// Allocates stack local memory.
129 /// Uses the `un_node` union field. The operand is the type of the allocated object.
130 /// The node source location points to a var decl node.
131 /// Indicates the beginning of a new statement in debug info.
33132 alloc,
34133 /// Same as `alloc` except mutable.
35134 alloc_mut,
36135 /// Same as `alloc` except the type is inferred.
136 /// The operand is unused.
37137 alloc_inferred,
38138 /// Same as `alloc_inferred` except mutable.
39139 alloc_inferred_mut,
40 /// Create an `anyframe->T`.
41 anyframe_type,
42140 /// Array concatenation. `a ++ b`
141 /// Uses the `pl_node` union field. Payload is `Bin`.
43142 array_cat,
44143 /// Array multiplication `a ** b`
144 /// Uses the `pl_node` union field. Payload is `Bin`.
45145 array_mul,
46 /// Create an array type
146 /// `[N]T` syntax. No source location provided.
147 /// Uses the `bin` union field. lhs is length, rhs is element type.
47148 array_type,
48 /// Create an array type with sentinel
149 /// `[N:S]T` syntax. No source location provided.
150 /// Uses the `array_type_sentinel` field.
49151 array_type_sentinel,
50152 /// Given a pointer to an indexable object, returns the len property. This is
51 /// used by for loops. This instruction also emits a for-loop specific instruction
52 /// if the indexable object is not indexable.
153 /// used by for loops. This instruction also emits a for-loop specific compile
154 /// error if the indexable object is not indexable.
155 /// Uses the `un_node` field. The AST node is the for loop node.
53156 indexable_ptr_len,
54 /// Function parameter value. These must be first in a function's main block,
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.
59 arg,
60 /// Type coercion.
157 /// Type coercion. No source location attached.
158 /// Uses the `bin` field.
61159 as,
62 /// Inline assembly.
160 /// Type coercion to the function's return type.
161 /// Uses the `pl_node` field. Payload is `As`. AST node could be many things.
162 as_node,
163 /// Inline assembly. Non-volatile.
164 /// Uses the `pl_node` union field. Payload is `Asm`. AST node is the assembly node.
63165 @"asm",
64 /// Await an async function.
65 @"await",
166 /// Inline assembly with the volatile attribute.
167 /// Uses the `pl_node` union field. Payload is `Asm`. AST node is the assembly node.
168 asm_volatile,
66169 /// Bitwise AND. `&`
67170 bit_and,
68 /// TODO delete this instruction, it has no purpose.
171 /// Bitcast a value to a different type.
172 /// Uses the pl_node field with payload `Bin`.
69173 bitcast,
70 /// An arbitrary typed pointer is pointer-casted to a new Pointer.
71 /// The destination type is given by LHS. The cast is to be evaluated
72 /// as if it were a bit-cast operation from the operand pointer element type to the
73 /// provided destination type.
74 bitcast_ref,
75174 /// A typed result location pointer is bitcasted to a new result location pointer.
76175 /// The new result location pointer has an inferred type.
176 /// Uses the un_node field.
77177 bitcast_result_ptr,
78178 /// Bitwise NOT. `~`
179 /// Uses `un_node`.
79180 bit_not,
80181 /// Bitwise OR. `|`
81182 bit_or,
82183 /// A labeled block of code, which can return a value.
184 /// Uses the `pl_node` union field. Payload is `Block`.
83185 block,
84 /// A block of code, which can return a value. There are no instructions that break out of
85 /// this block; it is implied that the final instruction is the result.
86 block_flat,
87 /// Same as `block` but additionally makes the inner instructions execute at comptime.
88 block_comptime,
89 /// Same as `block_flat` but additionally makes the inner instructions execute at comptime.
90 block_comptime_flat,
186 /// A list of instructions which are analyzed in the parent context, without
187 /// generating a runtime block. Must terminate with an "inline" variant of
188 /// a noreturn instruction.
189 /// Uses the `pl_node` union field. Payload is `Block`.
190 block_inline,
91191 /// Boolean AND. See also `bit_and`.
192 /// Uses the `pl_node` union field. Payload is `Bin`.
92193 bool_and,
93194 /// Boolean NOT. See also `bit_not`.
195 /// Uses the `un_node` field.
94196 bool_not,
95197 /// Boolean OR. See also `bit_or`.
198 /// Uses the `pl_node` union field. Payload is `Bin`.
96199 bool_or,
97 /// Return a value from a `Block`.
200 /// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand
201 /// is a block, which is evaluated if `lhs` is `true`.
202 /// Uses the `bool_br` union field.
203 bool_br_and,
204 /// Short-circuiting boolean `or`. `lhs` is a boolean `Ref` and the other operand
205 /// is a block, which is evaluated if `lhs` is `false`.
206 /// Uses the `bool_br` union field.
207 bool_br_or,
208 /// Return a value from a block.
209 /// Uses the `break` union field.
210 /// Uses the source information from previous instruction.
98211 @"break",
212 /// Return a value from a block. This instruction is used as the terminator
213 /// of a `block_inline`. It allows using the return value from `Sema.analyzeBody`.
214 /// This instruction may also be used when it is known that there is only one
215 /// break instruction in a block, and the target block is the parent.
216 /// Uses the `break` union field.
217 break_inline,
218 /// Uses the `node` union field.
99219 breakpoint,
100 /// Same as `break` but without an operand; the operand is assumed to be the void value.
101 break_void,
102 /// Function call.
220 /// Function call with modifier `.auto`.
221 /// Uses `pl_node`. AST node is the function call. Payload is `Call`.
103222 call,
223 /// Same as `call` but it also does `ensure_result_used` on the return value.
224 call_chkused,
225 /// Same as `call` but with modifier `.compile_time`.
226 call_compile_time,
227 /// Function call with modifier `.auto`, empty parameter list.
228 /// Uses the `un_node` field. Operand is callee. AST node is the function call.
229 call_none,
230 /// Same as `call_none` but it also does `ensure_result_used` on the return value.
231 call_none_chkused,
104232 /// `<`
233 /// Uses the `pl_node` union field. Payload is `Bin`.
105234 cmp_lt,
106235 /// `<=`
236 /// Uses the `pl_node` union field. Payload is `Bin`.
107237 cmp_lte,
108238 /// `==`
239 /// Uses the `pl_node` union field. Payload is `Bin`.
109240 cmp_eq,
110241 /// `>=`
242 /// Uses the `pl_node` union field. Payload is `Bin`.
111243 cmp_gte,
112244 /// `>`
245 /// Uses the `pl_node` union field. Payload is `Bin`.
113246 cmp_gt,
114247 /// `!=`
248 /// Uses the `pl_node` union field. Payload is `Bin`.
115249 cmp_neq,
116250 /// Coerces a result location pointer to a new element type. It is evaluated "backwards"-
117251 /// as type coercion from the new element type to the old element type.
252 /// Uses the `bin` union field.
118253 /// LHS is destination element type, RHS is result pointer.
119254 coerce_result_ptr,
120255 /// Emit an error message and fail compilation.
256 /// Uses the `un_node` field.
121257 compile_error,
122258 /// Log compile time variables and emit an error message.
259 /// Uses the `pl_node` union field. The AST node is the compile log builtin call.
260 /// The payload is `MultiOp`.
123261 compile_log,
124262 /// Conditional branch. Splits control flow based on a boolean condition value.
263 /// Uses the `pl_node` union field. AST node is an if, while, for, etc.
264 /// Payload is `CondBr`.
125265 condbr,
126 /// Special case, has no textual representation.
266 /// Same as `condbr`, except the condition is coerced to a comptime value, and
267 /// only the taken branch is analyzed. The then block and else block must
268 /// terminate with an "inline" variant of a noreturn instruction.
269 condbr_inline,
270 /// A comptime known value.
271 /// Uses the `const` union field.
127272 @"const",
128 /// Container field with just the name.
129 container_field_named,
130 /// Container field with a type and a name,
131 container_field_typed,
132 /// Container field with all the bells and whistles.
133 container_field,
273 /// A struct type definition. Contains references to ZIR instructions for
274 /// the field types, defaults, and alignments.
275 /// Uses the `pl_node` union field. Payload is `StructDecl`.
276 struct_decl,
277 /// Same as `struct_decl`, except has the `packed` layout.
278 struct_decl_packed,
279 /// Same as `struct_decl`, except has the `extern` layout.
280 struct_decl_extern,
281 /// A union type definition. Contains references to ZIR instructions for
282 /// the field types and optional type tag expression.
283 /// Uses the `pl_node` union field. Payload is `UnionDecl`.
284 union_decl,
285 /// An enum type definition. Contains references to ZIR instructions for
286 /// the field value expressions and optional type tag expression.
287 /// Uses the `pl_node` union field. Payload is `EnumDecl`.
288 enum_decl,
289 /// An opaque type definition. Provides an AST node only.
290 /// Uses the `node` union field.
291 opaque_decl,
134292 /// Declares the beginning of a statement. Used for debug info.
135 dbg_stmt,
293 /// Uses the `node` union field.
294 dbg_stmt_node,
136295 /// Represents a pointer to a global decl.
296 /// Uses the `pl_node` union field. `payload_index` is into `decls`.
137297 decl_ref,
138 /// Represents a pointer to a global decl by string name.
139 decl_ref_str,
140 /// Equivalent to a decl_ref followed by deref.
298 /// Equivalent to a decl_ref followed by load.
299 /// Uses the `pl_node` union field. `payload_index` is into `decls`.
141300 decl_val,
142 /// Load the value from a pointer.
143 deref,
301 /// Load the value from a pointer. Assumes `x.*` syntax.
302 /// Uses `un_node` field. AST node is the `x.*` syntax.
303 load,
144304 /// Arithmetic division. Asserts no integer overflow.
305 /// Uses the `pl_node` union field. Payload is `Bin`.
145306 div,
146307 /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at
147 /// the provided index.
308 /// the provided index. Uses the `bin` union field. Source location is implied
309 /// to be the same as the previous instruction.
148310 elem_ptr,
311 /// Same as `elem_ptr` except also stores a source location node.
312 /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
313 elem_ptr_node,
149314 /// Given an array, slice, or pointer, returns the element at the provided index.
315 /// Uses the `bin` union field. Source location is implied to be the same
316 /// as the previous instruction.
150317 elem_val,
318 /// Same as `elem_val` except also stores a source location node.
319 /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
320 elem_val_node,
321 /// This instruction has been deleted late in the astgen phase. It must
322 /// be ignored, and the corresponding `Data` is undefined.
323 elided,
151324 /// Emits a compile error if the operand is not `void`.
325 /// Uses the `un_node` field.
152326 ensure_result_used,
153327 /// Emits a compile error if an error is ignored.
328 /// Uses the `un_node` field.
154329 ensure_result_non_error,
155330 /// Create a `E!T` type.
331 /// Uses the `pl_node` field with `Bin` payload.
156332 error_union_type,
157 /// Create an error set.
158 error_set,
159 /// `error.Foo` syntax.
333 /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
160334 error_value,
161 /// Export the provided Decl as the provided name in the compilation's output object file.
162 @"export",
163335 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
164 /// to the named field. The field name is a []const u8. Used by a.b syntax.
336 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
337 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
165338 field_ptr,
166339 /// Given a struct or object that contains virtual fields, returns the named field.
167 /// The field name is a []const u8. Used by a.b syntax.
340 /// The field name is stored in string_bytes. Used by a.b syntax.
341 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
168342 field_val,
169343 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
170344 /// to the named field. The field name is a comptime instruction. Used by @field.
345 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
171346 field_ptr_named,
172347 /// Given a struct or object that contains virtual fields, returns the named field.
173348 /// The field name is a comptime instruction. Used by @field.
349 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
174350 field_val_named,
175 /// Convert a larger float type to any other float type, possibly causing a loss of precision.
351 /// Convert a larger float type to any other float type, possibly causing
352 /// a loss of precision.
353 /// Uses the `pl_node` field. AST is the `@floatCast` syntax.
354 /// Payload is `Bin` with lhs as the dest type, rhs the operand.
176355 floatcast,
177 /// Declare a function body.
178 @"fn",
179356 /// Returns a function type, assuming unspecified calling convention.
357 /// Uses the `pl_node` union field. `payload_index` points to a `FnType`.
180358 fn_type,
181359 /// Same as `fn_type` but the function is variadic.
182360 fn_type_var_args,
183361 /// Returns a function type, with a calling convention instruction operand.
362 /// Uses the `pl_node` union field. `payload_index` points to a `FnTypeCc`.
184363 fn_type_cc,
185364 /// Same as `fn_type_cc` but the function is variadic.
186365 fn_type_cc_var_args,
187 /// @import(operand)
366 /// `@import(operand)`.
367 /// Uses the `un_node` field.
188368 import,
189 /// Integer literal.
369 /// Integer literal that fits in a u64. Uses the int union value.
190370 int,
191371 /// Convert an integer value to another integer type, asserting that the destination type
192372 /// can hold the same mathematical value.
373 /// Uses the `pl_node` field. AST is the `@intCast` syntax.
374 /// Payload is `Bin` with lhs as the dest type, rhs the operand.
193375 intcast,
194376 /// Make an integer type out of signedness and bit count.
377 /// Payload is `int_type`
195378 int_type,
379 /// Convert an error type to `u16`
380 error_to_int,
381 /// Convert a `u16` to `anyerror`
382 int_to_error,
196383 /// Return a boolean false if an optional is null. `x != null`
384 /// Uses the `un_node` field.
197385 is_non_null,
198386 /// Return a boolean true if an optional is null. `x == null`
387 /// Uses the `un_node` field.
199388 is_null,
200389 /// Return a boolean false if an optional is null. `x.* != null`
390 /// Uses the `un_node` field.
201391 is_non_null_ptr,
202392 /// Return a boolean true if an optional is null. `x.* == null`
393 /// Uses the `un_node` field.
203394 is_null_ptr,
204395 /// Return a boolean true if value is an error
396 /// Uses the `un_node` field.
205397 is_err,
206398 /// Return a boolean true if dereferenced pointer is an error
399 /// Uses the `un_node` field.
207400 is_err_ptr,
208 /// A labeled block of code that loops forever. At the end of the body it is implied
209 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
401 /// A labeled block of code that loops forever. At the end of the body will have either
402 /// a `repeat` instruction or a `repeat_inline` instruction.
403 /// Uses the `pl_node` field. The AST node is either a for loop or while loop.
404 /// This ZIR instruction is needed because TZIR does not (yet?) match ZIR, and Sema
405 /// needs to emit more than 1 TZIR block for this instruction.
406 /// The payload is `Block`.
210407 loop,
408 /// Sends runtime control flow back to the beginning of the current block.
409 /// Uses the `node` field.
410 repeat,
411 /// Sends comptime control flow back to the beginning of the current block.
412 /// Uses the `node` field.
413 repeat_inline,
211414 /// Merge two error sets into one, `E1 || E2`.
415 /// Uses the `pl_node` field with payload `Bin`.
212416 merge_error_sets,
213417 /// Ambiguously remainder division or modulus. If the computation would possibly have
214418 /// a different value depending on whether the operation is remainder division or modulus,
215419 /// a compile error is emitted. Otherwise the computation is performed.
420 /// Uses the `pl_node` union field. Payload is `Bin`.
216421 mod_rem,
217422 /// Arithmetic multiplication. Asserts no integer overflow.
423 /// Uses the `pl_node` union field. Payload is `Bin`.
218424 mul,
219425 /// Twos complement wrapping integer multiplication.
426 /// Uses the `pl_node` union field. Payload is `Bin`.
220427 mulwrap,
221 /// An await inside a nosuspend scope.
222 nosuspend_await,
223428 /// Given a reference to a function and a parameter index, returns the
224 /// type of the parameter. TODO what happens when the parameter is `anytype`?
429 /// type of the parameter. The only usage of this instruction is for the
430 /// result location of parameters of function calls. In the case of a function's
431 /// parameter type being `anytype`, it is the type coercion's job to detect this
432 /// scenario and skip the coercion, so that semantic analysis of this instruction
433 /// is not in a position where it must create an invalid type.
434 /// Uses the `param_type` union field.
225435 param_type,
226 /// An alternative to using `const` for simple primitive values such as `true` or `u8`.
227 /// TODO flatten so that each primitive has its own ZIR Inst Tag.
228 primitive,
229436 /// Convert a pointer to a `usize` integer.
437 /// Uses the `un_node` field. The AST node is the builtin fn call node.
230438 ptrtoint,
231439 /// Turns an R-Value into a const L-Value. In other words, it takes a value,
232440 /// stores it in a memory location, and returns a const pointer to it. If the value
233441 /// is `comptime`, the memory location is global static constant data. Otherwise,
234442 /// the memory location is in the stack frame, local to the scope containing the
235443 /// instruction.
444 /// Uses the `un_tok` union field.
236445 ref,
237 /// Resume an async function.
238 @"resume",
239446 /// Obtains a pointer to the return value.
447 /// Uses the `node` union field.
240448 ret_ptr,
241449 /// Obtains the return type of the in-scope function.
450 /// Uses the `node` union field.
242451 ret_type,
243 /// Sends control flow back to the function's callee. Takes an operand as the return value.
244 @"return",
245 /// Same as `return` but there is no operand; the operand is implicitly the void value.
246 return_void,
452 /// Sends control flow back to the function's callee.
453 /// Includes an operand as the return value.
454 /// Includes an AST node source location.
455 /// Uses the `un_node` union field.
456 ret_node,
457 /// Sends control flow back to the function's callee.
458 /// Includes an operand as the return value.
459 /// Includes a token source location.
460 /// Uses the `un_tok` union field.
461 ret_tok,
462 /// Same as `ret_tok` except the operand needs to get coerced to the function's
463 /// return type.
464 ret_coerce,
247465 /// Changes the maximum number of backwards branches that compile-time
248466 /// code execution can use before giving up and making a compile error.
467 /// Uses the `un_node` union field.
249468 set_eval_branch_quota,
250469 /// Integer shift-left. Zeroes are shifted in from the right hand side.
470 /// Uses the `pl_node` union field. Payload is `Bin`.
251471 shl,
252472 /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.
473 /// Uses the `pl_node` union field. Payload is `Bin`.
253474 shr,
254 /// Create a const pointer type with element type T. `*const T`
255 single_const_ptr_type,
256 /// Create a mutable pointer type with element type T. `*T`
257 single_mut_ptr_type,
258 /// Create a const pointer type with element type T. `[*]const T`
259 many_const_ptr_type,
260 /// Create a mutable pointer type with element type T. `[*]T`
261 many_mut_ptr_type,
262 /// Create a const pointer type with element type T. `[*c]const T`
263 c_const_ptr_type,
264 /// Create a mutable pointer type with element type T. `[*c]T`
265 c_mut_ptr_type,
266 /// Create a mutable slice type with element type T. `[]T`
267 mut_slice_type,
268 /// Create a const slice type with element type T. `[]T`
269 const_slice_type,
270 /// Create a pointer type with attributes
475 /// Create a pointer type that does not have a sentinel, alignment, or bit range specified.
476 /// Uses the `ptr_type_simple` union field.
477 ptr_type_simple,
478 /// Create a pointer type which can have a sentinel, alignment, and/or bit range.
479 /// Uses the `ptr_type` union field.
271480 ptr_type,
272481 /// Each `store_to_inferred_ptr` puts the type of the stored value into a set,
273482 /// and then `resolve_inferred_alloc` triggers peer type resolution on the set.
274483 /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which
275484 /// is the allocation that needs to have its type inferred.
485 /// Uses the `un_node` field. The AST node is the var decl.
276486 resolve_inferred_alloc,
277 /// Slice operation `array_ptr[start..end:sentinel]`
278 slice,
279 /// Slice operation with just start `lhs[rhs..]`
487 /// Slice operation `lhs[rhs..]`. No sentinel and no end offset.
488 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceStart`.
280489 slice_start,
281 /// Write a value to a pointer. For loading, see `deref`.
490 /// Slice operation `array_ptr[start..end]`. No sentinel.
491 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceEnd`.
492 slice_end,
493 /// Slice operation `array_ptr[start..end:sentinel]`.
494 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
495 slice_sentinel,
496 /// Write a value to a pointer. For loading, see `load`.
497 /// Source location is assumed to be same as previous instruction.
498 /// Uses the `bin` union field.
282499 store,
500 /// Same as `store` except provides a source location.
501 /// Uses the `pl_node` union field. Payload is `Bin`.
502 store_node,
283503 /// Same as `store` but the type of the value being stored will be used to infer
284504 /// the block type. The LHS is the pointer to store to.
505 /// Uses the `bin` union field.
285506 store_to_block_ptr,
286507 /// Same as `store` but the type of the value being stored will be used to infer
287508 /// the pointer type.
509 /// Uses the `bin` union field - Astgen.zig depends on the ability to change
510 /// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr`
511 /// without changing the data.
288512 store_to_inferred_ptr,
289513 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
514 /// Uses the `str` union field.
290515 str,
291 /// Create a struct type.
292 struct_type,
293516 /// Arithmetic subtraction. Asserts no integer overflow.
517 /// Uses the `pl_node` union field. Payload is `Bin`.
294518 sub,
295519 /// Twos complement wrapping integer subtraction.
520 /// Uses the `pl_node` union field. Payload is `Bin`.
296521 subwrap,
522 /// Arithmetic negation. Asserts no integer overflow.
523 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.
524 /// Uses `un_node`.
525 negate,
526 /// Twos complement wrapping integer negation.
527 /// Same as subwrap with a lhs of 0, split into a separate instruction to save memory.
528 /// Uses `un_node`.
529 negate_wrap,
297530 /// Returns the type of a value.
531 /// Uses the `un_tok` field.
298532 typeof,
299 /// Is the builtin @TypeOf which returns the type after peertype resolution of one or more params
533 /// Given a value which is a pointer, returns the element type.
534 /// Uses the `un_node` field.
535 typeof_elem,
536 /// The builtin `@TypeOf` which returns the type after Peer Type Resolution
537 /// of one or more params.
538 /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`.
300539 typeof_peer,
301 /// Asserts control-flow will not reach this instruction. Not safety checked - the compiler
302 /// will assume the correctness of this instruction.
303 unreachable_unsafe,
304 /// Asserts control-flow will not reach this instruction. In safety-checked modes,
305 /// this will generate a call to the panic function unless it can be proven unreachable
306 /// by the compiler.
307 unreachable_safe,
540 /// Asserts control-flow will not reach this instruction (`unreachable`).
541 /// Uses the `unreachable` union field.
542 @"unreachable",
308543 /// Bitwise XOR. `^`
544 /// Uses the `pl_node` union field. Payload is `Bin`.
309545 xor,
310546 /// Create an optional type '?T'
547 /// Uses the `un_node` field.
311548 optional_type,
312549 /// Create an optional type '?T'. The operand is a pointer value. The optional type will
313550 /// be the type of the pointer element, wrapped in an optional.
551 /// Uses the `un_node` field.
314552 optional_type_from_ptr_elem,
315 /// Create a union type.
316 union_type,
317553 /// ?T => T with safety.
318554 /// Given an optional value, returns the payload value, with a safety check that
319555 /// the value is non-null. Used for `orelse`, `if` and `while`.
556 /// Uses the `un_node` field.
320557 optional_payload_safe,
321558 /// ?T => T without safety.
322559 /// Given an optional value, returns the payload value. No safety checks.
560 /// Uses the `un_node` field.
323561 optional_payload_unsafe,
324562 /// *?T => *T with safety.
325563 /// Given a pointer to an optional value, returns a pointer to the payload value,
326564 /// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`.
565 /// Uses the `un_node` field.
327566 optional_payload_safe_ptr,
328567 /// *?T => *T without safety.
329568 /// Given a pointer to an optional value, returns a pointer to the payload value.
330569 /// No safety checks.
570 /// Uses the `un_node` field.
331571 optional_payload_unsafe_ptr,
332572 /// E!T => T with safety.
333573 /// Given an error union value, returns the payload value, with a safety check
334574 /// that the value is not an error. Used for catch, if, and while.
575 /// Uses the `un_node` field.
335576 err_union_payload_safe,
336577 /// E!T => T without safety.
337578 /// Given an error union value, returns the payload value. No safety checks.
579 /// Uses the `un_node` field.
338580 err_union_payload_unsafe,
339581 /// *E!T => *T with safety.
340582 /// Given a pointer to an error union value, returns a pointer to the payload value,
341583 /// with a safety check that the value is not an error. Used for catch, if, and while.
584 /// Uses the `un_node` field.
342585 err_union_payload_safe_ptr,
343586 /// *E!T => *T without safety.
344587 /// Given a pointer to a error union value, returns a pointer to the payload value.
345588 /// No safety checks.
589 /// Uses the `un_node` field.
346590 err_union_payload_unsafe_ptr,
347591 /// E!T => E without safety.
348592 /// Given an error union value, returns the error code. No safety checks.
593 /// Uses the `un_node` field.
349594 err_union_code,
350595 /// *E!T => E without safety.
351596 /// Given a pointer to an error union value, returns the error code. No safety checks.
597 /// Uses the `un_node` field.
352598 err_union_code_ptr,
353599 /// Takes a *E!T and raises a compiler error if T != void
600 /// Uses the `un_tok` field.
354601 ensure_err_payload_void,
355 /// Create a enum literal,
602 /// An enum literal. Uses the `str_tok` union field.
356603 enum_literal,
357 /// Create an enum type.
358 enum_type,
359 /// Does nothing; returns a void value.
360 void_value,
361 /// Suspend an async function.
362 @"suspend",
363 /// Suspend an async function.
364 /// Same as .suspend but with a block.
365 suspend_block,
366 /// A switch expression.
367 switchbr,
368 /// Same as `switchbr` but the target is a pointer to the value being switched on.
369 switchbr_ref,
370 /// A range in a switch case, `lhs...rhs`.
371 /// Only checks that `lhs >= rhs` if they are ints, everything else is
372 /// validated by the .switch instruction.
373 switch_range,
374
375 pub fn Type(tag: Tag) type {
376 return switch (tag) {
377 .alloc_inferred,
378 .alloc_inferred_mut,
379 .breakpoint,
380 .dbg_stmt,
381 .return_void,
382 .ret_ptr,
383 .ret_type,
384 .unreachable_unsafe,
385 .unreachable_safe,
386 .void_value,
387 .@"suspend",
388 => NoOp,
389
390 .alloc,
391 .alloc_mut,
392 .bool_not,
393 .compile_error,
394 .deref,
395 .@"return",
396 .is_null,
397 .is_non_null,
398 .is_null_ptr,
399 .is_non_null_ptr,
400 .is_err,
401 .is_err_ptr,
402 .ptrtoint,
403 .ensure_result_used,
404 .ensure_result_non_error,
405 .bitcast_result_ptr,
406 .ref,
407 .bitcast_ref,
408 .typeof,
409 .resolve_inferred_alloc,
410 .single_const_ptr_type,
411 .single_mut_ptr_type,
412 .many_const_ptr_type,
413 .many_mut_ptr_type,
414 .c_const_ptr_type,
415 .c_mut_ptr_type,
416 .mut_slice_type,
417 .const_slice_type,
418 .optional_type,
419 .optional_type_from_ptr_elem,
420 .optional_payload_safe,
421 .optional_payload_unsafe,
422 .optional_payload_safe_ptr,
423 .optional_payload_unsafe_ptr,
424 .err_union_payload_safe,
425 .err_union_payload_unsafe,
426 .err_union_payload_safe_ptr,
427 .err_union_payload_unsafe_ptr,
428 .err_union_code,
429 .err_union_code_ptr,
430 .ensure_err_payload_void,
431 .anyframe_type,
432 .bit_not,
433 .import,
434 .set_eval_branch_quota,
435 .indexable_ptr_len,
436 .@"resume",
437 .@"await",
438 .nosuspend_await,
439 => UnOp,
440
441 .add,
442 .addwrap,
443 .array_cat,
444 .array_mul,
445 .array_type,
446 .bit_and,
447 .bit_or,
448 .bool_and,
449 .bool_or,
450 .div,
451 .mod_rem,
452 .mul,
453 .mulwrap,
454 .shl,
455 .shr,
456 .store,
457 .store_to_block_ptr,
458 .store_to_inferred_ptr,
459 .sub,
460 .subwrap,
461 .cmp_lt,
462 .cmp_lte,
463 .cmp_eq,
464 .cmp_gte,
465 .cmp_gt,
466 .cmp_neq,
467 .as,
468 .floatcast,
469 .intcast,
470 .bitcast,
471 .coerce_result_ptr,
472 .xor,
473 .error_union_type,
474 .merge_error_sets,
475 .slice_start,
476 .switch_range,
477 => BinOp,
478
479 .block,
480 .block_flat,
481 .block_comptime,
482 .block_comptime_flat,
483 .suspend_block,
484 => Block,
485
486 .switchbr, .switchbr_ref => SwitchBr,
487
488 .arg => Arg,
489 .array_type_sentinel => ArrayTypeSentinel,
490 .@"break" => Break,
491 .break_void => BreakVoid,
492 .call => Call,
493 .decl_ref => DeclRef,
494 .decl_ref_str => DeclRefStr,
495 .decl_val => DeclVal,
496 .compile_log => CompileLog,
497 .loop => Loop,
498 .@"const" => Const,
499 .str => Str,
500 .int => Int,
501 .int_type => IntType,
502 .field_ptr, .field_val => Field,
503 .field_ptr_named, .field_val_named => FieldNamed,
504 .@"asm" => Asm,
505 .@"fn" => Fn,
506 .@"export" => Export,
507 .param_type => ParamType,
508 .primitive => Primitive,
509 .fn_type, .fn_type_var_args => FnType,
510 .fn_type_cc, .fn_type_cc_var_args => FnTypeCc,
511 .elem_ptr, .elem_val => Elem,
512 .condbr => CondBr,
513 .ptr_type => PtrType,
514 .enum_literal => EnumLiteral,
515 .error_set => ErrorSet,
516 .error_value => ErrorValue,
517 .slice => Slice,
518 .typeof_peer => TypeOfPeer,
519 .container_field_named => ContainerFieldNamed,
520 .container_field_typed => ContainerFieldTyped,
521 .container_field => ContainerField,
522 .enum_type => EnumType,
523 .union_type => UnionType,
524 .struct_type => StructType,
525 };
526 }
604 /// An enum literal 8 or fewer bytes. No source location.
605 /// Uses the `small_str` field.
606 enum_literal_small,
607 /// A switch expression. Uses the `pl_node` union field.
608 /// AST node is the switch, payload is `SwitchBlock`.
609 /// All prongs of target handled.
610 switch_block,
611 /// Same as switch_block, except one or more prongs have multiple items.
612 switch_block_multi,
613 /// Same as switch_block, except has an else prong.
614 switch_block_else,
615 /// Same as switch_block_else, except one or more prongs have multiple items.
616 switch_block_else_multi,
617 /// Same as switch_block, except has an underscore prong.
618 switch_block_under,
619 /// Same as switch_block, except one or more prongs have multiple items.
620 switch_block_under_multi,
621 /// Same as `switch_block` but the target is a pointer to the value being switched on.
622 switch_block_ref,
623 /// Same as `switch_block_multi` but the target is a pointer to the value being switched on.
624 switch_block_ref_multi,
625 /// Same as `switch_block_else` but the target is a pointer to the value being switched on.
626 switch_block_ref_else,
627 /// Same as `switch_block_else_multi` but the target is a pointer to the
628 /// value being switched on.
629 switch_block_ref_else_multi,
630 /// Same as `switch_block_under` but the target is a pointer to the value
631 /// being switched on.
632 switch_block_ref_under,
633 /// Same as `switch_block_under_multi` but the target is a pointer to
634 /// the value being switched on.
635 switch_block_ref_under_multi,
636 /// Produces the capture value for a switch prong.
637 /// Uses the `switch_capture` field.
638 switch_capture,
639 /// Produces the capture value for a switch prong.
640 /// Result is a pointer to the value.
641 /// Uses the `switch_capture` field.
642 switch_capture_ref,
643 /// Produces the capture value for a switch prong.
644 /// The prong is one of the multi cases.
645 /// Uses the `switch_capture` field.
646 switch_capture_multi,
647 /// Produces the capture value for a switch prong.
648 /// The prong is one of the multi cases.
649 /// Result is a pointer to the value.
650 /// Uses the `switch_capture` field.
651 switch_capture_multi_ref,
652 /// Produces the capture value for the else/'_' switch prong.
653 /// Uses the `switch_capture` field.
654 switch_capture_else,
655 /// Produces the capture value for the else/'_' switch prong.
656 /// Result is a pointer to the value.
657 /// Uses the `switch_capture` field.
658 switch_capture_else_ref,
659 /// Given a set of `field_ptr` instructions, assumes they are all part of a struct
660 /// initialization expression, and emits compile errors for duplicate fields
661 /// as well as missing fields, if applicable.
662 /// Uses the `pl_node` field. Payload is `Block`.
663 validate_struct_init_ptr,
664 /// A struct literal with a specified type, with no fields.
665 /// Uses the `un_node` field.
666 struct_init_empty,
527667
528668 /// Returns whether the instruction is one of the control flow "noreturn" types.
529669 /// Function calls do not count.
......@@ -540,23 +680,28 @@ pub const Inst = struct {
540680 .array_type,
541681 .array_type_sentinel,
542682 .indexable_ptr_len,
543 .arg,
544683 .as,
684 .as_node,
545685 .@"asm",
686 .asm_volatile,
546687 .bit_and,
547688 .bitcast,
548 .bitcast_ref,
549689 .bitcast_result_ptr,
550690 .bit_or,
551691 .block,
552 .block_flat,
553 .block_comptime,
554 .block_comptime_flat,
692 .block_inline,
693 .loop,
694 .bool_br_and,
695 .bool_br_or,
555696 .bool_not,
556697 .bool_and,
557698 .bool_or,
558699 .breakpoint,
559700 .call,
701 .call_chkused,
702 .call_compile_time,
703 .call_none,
704 .call_none_chkused,
560705 .cmp_lt,
561706 .cmp_lte,
562707 .cmp_eq,
......@@ -565,23 +710,28 @@ pub const Inst = struct {
565710 .cmp_neq,
566711 .coerce_result_ptr,
567712 .@"const",
568 .dbg_stmt,
713 .struct_decl,
714 .struct_decl_packed,
715 .struct_decl_extern,
716 .union_decl,
717 .enum_decl,
718 .opaque_decl,
719 .dbg_stmt_node,
569720 .decl_ref,
570 .decl_ref_str,
571721 .decl_val,
572 .deref,
722 .load,
573723 .div,
574724 .elem_ptr,
575725 .elem_val,
726 .elem_ptr_node,
727 .elem_val_node,
576728 .ensure_result_used,
577729 .ensure_result_non_error,
578 .@"export",
579730 .floatcast,
580731 .field_ptr,
581732 .field_val,
582733 .field_ptr_named,
583734 .field_val_named,
584 .@"fn",
585735 .fn_type,
586736 .fn_type_var_args,
587737 .fn_type_cc,
......@@ -599,28 +749,23 @@ pub const Inst = struct {
599749 .mul,
600750 .mulwrap,
601751 .param_type,
602 .primitive,
603752 .ptrtoint,
604753 .ref,
605754 .ret_ptr,
606755 .ret_type,
607756 .shl,
608757 .shr,
609 .single_const_ptr_type,
610 .single_mut_ptr_type,
611 .many_const_ptr_type,
612 .many_mut_ptr_type,
613 .c_const_ptr_type,
614 .c_mut_ptr_type,
615 .mut_slice_type,
616 .const_slice_type,
617758 .store,
759 .store_node,
618760 .store_to_block_ptr,
619761 .store_to_inferred_ptr,
620762 .str,
621763 .sub,
622764 .subwrap,
765 .negate,
766 .negate_wrap,
623767 .typeof,
768 .typeof_elem,
624769 .xor,
625770 .optional_type,
626771 .optional_type_from_ptr_elem,
......@@ -634,1436 +779,1566 @@ pub const Inst = struct {
634779 .err_union_payload_unsafe_ptr,
635780 .err_union_code,
636781 .err_union_code_ptr,
782 .error_to_int,
783 .int_to_error,
637784 .ptr_type,
785 .ptr_type_simple,
638786 .ensure_err_payload_void,
639787 .enum_literal,
788 .enum_literal_small,
640789 .merge_error_sets,
641 .anyframe_type,
642790 .error_union_type,
643791 .bit_not,
644 .error_set,
645792 .error_value,
646 .slice,
647793 .slice_start,
794 .slice_end,
795 .slice_sentinel,
648796 .import,
649797 .typeof_peer,
650798 .resolve_inferred_alloc,
651799 .set_eval_branch_quota,
652800 .compile_log,
653 .enum_type,
654 .union_type,
655 .struct_type,
656 .void_value,
657 .switch_range,
658 .@"resume",
659 .@"await",
660 .nosuspend_await,
801 .elided,
802 .switch_capture,
803 .switch_capture_ref,
804 .switch_capture_multi,
805 .switch_capture_multi_ref,
806 .switch_capture_else,
807 .switch_capture_else_ref,
808 .switch_block,
809 .switch_block_multi,
810 .switch_block_else,
811 .switch_block_else_multi,
812 .switch_block_under,
813 .switch_block_under_multi,
814 .switch_block_ref,
815 .switch_block_ref_multi,
816 .switch_block_ref_else,
817 .switch_block_ref_else_multi,
818 .switch_block_ref_under,
819 .switch_block_ref_under_multi,
820 .validate_struct_init_ptr,
821 .struct_init_empty,
661822 => false,
662823
663824 .@"break",
664 .break_void,
825 .break_inline,
665826 .condbr,
827 .condbr_inline,
666828 .compile_error,
667 .@"return",
668 .return_void,
669 .unreachable_unsafe,
670 .unreachable_safe,
671 .loop,
672 .container_field_named,
673 .container_field_typed,
674 .container_field,
675 .switchbr,
676 .switchbr_ref,
677 .@"suspend",
678 .suspend_block,
829 .ret_node,
830 .ret_tok,
831 .ret_coerce,
832 .@"unreachable",
833 .repeat,
834 .repeat_inline,
679835 => true,
680836 };
681837 }
682838 };
683839
684 /// Prefer `castTag` to this.
685 pub fn cast(base: *Inst, comptime T: type) ?*T {
686 if (@hasField(T, "base_tag")) {
687 return base.castTag(T.base_tag);
688 }
689 inline for (@typeInfo(Tag).Enum.fields) |field| {
690 const tag = @intToEnum(Tag, field.value);
691 if (base.tag == tag) {
692 if (T == tag.Type()) {
693 return @fieldParentPtr(T, "base", base);
694 }
695 return null;
696 }
697 }
698 unreachable;
699 }
700
701 pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
702 if (base.tag == tag) {
703 return @fieldParentPtr(tag.Type(), "base", base);
704 }
705 return null;
706 }
707
708 pub const NoOp = struct {
709 base: Inst,
840 /// The position of a ZIR instruction within the `Code` instructions array.
841 pub const Index = u32;
842
843 /// A reference to a TypedValue, parameter of the current function,
844 /// or ZIR instruction.
845 ///
846 /// If the Ref has a tag in this enum, it refers to a TypedValue which may be
847 /// retrieved with Ref.toTypedValue().
848 ///
849 /// If the value of a Ref does not have a tag, it referes to either a parameter
850 /// of the current function or a ZIR instruction.
851 ///
852 /// The first values after the the last tag refer to parameters which may be
853 /// derived by subtracting typed_value_map.len.
854 ///
855 /// All further values refer to ZIR instructions which may be derived by
856 /// subtracting typed_value_map.len and the number of parameters.
857 ///
858 /// When adding a tag to this enum, consider adding a corresponding entry to
859 /// `simple_types` in astgen.
860 ///
861 /// The tag type is specified so that it is safe to bitcast between `[]u32`
862 /// and `[]Ref`.
863 pub const Ref = enum(u32) {
864 /// This Ref does not correspond to any ZIR instruction or constant
865 /// value and may instead be used as a sentinel to indicate null.
866 none,
867
868 u8_type,
869 i8_type,
870 u16_type,
871 i16_type,
872 u32_type,
873 i32_type,
874 u64_type,
875 i64_type,
876 usize_type,
877 isize_type,
878 c_short_type,
879 c_ushort_type,
880 c_int_type,
881 c_uint_type,
882 c_long_type,
883 c_ulong_type,
884 c_longlong_type,
885 c_ulonglong_type,
886 c_longdouble_type,
887 f16_type,
888 f32_type,
889 f64_type,
890 f128_type,
891 c_void_type,
892 bool_type,
893 void_type,
894 type_type,
895 anyerror_type,
896 comptime_int_type,
897 comptime_float_type,
898 noreturn_type,
899 null_type,
900 undefined_type,
901 fn_noreturn_no_args_type,
902 fn_void_no_args_type,
903 fn_naked_noreturn_no_args_type,
904 fn_ccc_void_no_args_type,
905 single_const_pointer_to_comptime_int_type,
906 const_slice_u8_type,
907 enum_literal_type,
908
909 /// `undefined` (untyped)
910 undef,
911 /// `0` (comptime_int)
912 zero,
913 /// `1` (comptime_int)
914 one,
915 /// `{}`
916 void_value,
917 /// `unreachable` (noreturn type)
918 unreachable_value,
919 /// `null` (untyped)
920 null_value,
921 /// `true`
922 bool_true,
923 /// `false`
924 bool_false,
925 /// `.{}` (untyped)
926 empty_struct,
927 /// `0` (usize)
928 zero_usize,
929 /// `1` (usize)
930 one_usize,
931
932 _,
933
934 pub const typed_value_map = std.enums.directEnumArray(Ref, TypedValue, 0, .{
935 .none = undefined,
936
937 .u8_type = .{
938 .ty = Type.initTag(.type),
939 .val = Value.initTag(.u8_type),
940 },
941 .i8_type = .{
942 .ty = Type.initTag(.type),
943 .val = Value.initTag(.i8_type),
944 },
945 .u16_type = .{
946 .ty = Type.initTag(.type),
947 .val = Value.initTag(.u16_type),
948 },
949 .i16_type = .{
950 .ty = Type.initTag(.type),
951 .val = Value.initTag(.i16_type),
952 },
953 .u32_type = .{
954 .ty = Type.initTag(.type),
955 .val = Value.initTag(.u32_type),
956 },
957 .i32_type = .{
958 .ty = Type.initTag(.type),
959 .val = Value.initTag(.i32_type),
960 },
961 .u64_type = .{
962 .ty = Type.initTag(.type),
963 .val = Value.initTag(.u64_type),
964 },
965 .i64_type = .{
966 .ty = Type.initTag(.type),
967 .val = Value.initTag(.i64_type),
968 },
969 .usize_type = .{
970 .ty = Type.initTag(.type),
971 .val = Value.initTag(.usize_type),
972 },
973 .isize_type = .{
974 .ty = Type.initTag(.type),
975 .val = Value.initTag(.isize_type),
976 },
977 .c_short_type = .{
978 .ty = Type.initTag(.type),
979 .val = Value.initTag(.c_short_type),
980 },
981 .c_ushort_type = .{
982 .ty = Type.initTag(.type),
983 .val = Value.initTag(.c_ushort_type),
984 },
985 .c_int_type = .{
986 .ty = Type.initTag(.type),
987 .val = Value.initTag(.c_int_type),
988 },
989 .c_uint_type = .{
990 .ty = Type.initTag(.type),
991 .val = Value.initTag(.c_uint_type),
992 },
993 .c_long_type = .{
994 .ty = Type.initTag(.type),
995 .val = Value.initTag(.c_long_type),
996 },
997 .c_ulong_type = .{
998 .ty = Type.initTag(.type),
999 .val = Value.initTag(.c_ulong_type),
1000 },
1001 .c_longlong_type = .{
1002 .ty = Type.initTag(.type),
1003 .val = Value.initTag(.c_longlong_type),
1004 },
1005 .c_ulonglong_type = .{
1006 .ty = Type.initTag(.type),
1007 .val = Value.initTag(.c_ulonglong_type),
1008 },
1009 .c_longdouble_type = .{
1010 .ty = Type.initTag(.type),
1011 .val = Value.initTag(.c_longdouble_type),
1012 },
1013 .f16_type = .{
1014 .ty = Type.initTag(.type),
1015 .val = Value.initTag(.f16_type),
1016 },
1017 .f32_type = .{
1018 .ty = Type.initTag(.type),
1019 .val = Value.initTag(.f32_type),
1020 },
1021 .f64_type = .{
1022 .ty = Type.initTag(.type),
1023 .val = Value.initTag(.f64_type),
1024 },
1025 .f128_type = .{
1026 .ty = Type.initTag(.type),
1027 .val = Value.initTag(.f128_type),
1028 },
1029 .c_void_type = .{
1030 .ty = Type.initTag(.type),
1031 .val = Value.initTag(.c_void_type),
1032 },
1033 .bool_type = .{
1034 .ty = Type.initTag(.type),
1035 .val = Value.initTag(.bool_type),
1036 },
1037 .void_type = .{
1038 .ty = Type.initTag(.type),
1039 .val = Value.initTag(.void_type),
1040 },
1041 .type_type = .{
1042 .ty = Type.initTag(.type),
1043 .val = Value.initTag(.type_type),
1044 },
1045 .anyerror_type = .{
1046 .ty = Type.initTag(.type),
1047 .val = Value.initTag(.anyerror_type),
1048 },
1049 .comptime_int_type = .{
1050 .ty = Type.initTag(.type),
1051 .val = Value.initTag(.comptime_int_type),
1052 },
1053 .comptime_float_type = .{
1054 .ty = Type.initTag(.type),
1055 .val = Value.initTag(.comptime_float_type),
1056 },
1057 .noreturn_type = .{
1058 .ty = Type.initTag(.type),
1059 .val = Value.initTag(.noreturn_type),
1060 },
1061 .null_type = .{
1062 .ty = Type.initTag(.type),
1063 .val = Value.initTag(.null_type),
1064 },
1065 .undefined_type = .{
1066 .ty = Type.initTag(.type),
1067 .val = Value.initTag(.undefined_type),
1068 },
1069 .fn_noreturn_no_args_type = .{
1070 .ty = Type.initTag(.type),
1071 .val = Value.initTag(.fn_noreturn_no_args_type),
1072 },
1073 .fn_void_no_args_type = .{
1074 .ty = Type.initTag(.type),
1075 .val = Value.initTag(.fn_void_no_args_type),
1076 },
1077 .fn_naked_noreturn_no_args_type = .{
1078 .ty = Type.initTag(.type),
1079 .val = Value.initTag(.fn_naked_noreturn_no_args_type),
1080 },
1081 .fn_ccc_void_no_args_type = .{
1082 .ty = Type.initTag(.type),
1083 .val = Value.initTag(.fn_ccc_void_no_args_type),
1084 },
1085 .single_const_pointer_to_comptime_int_type = .{
1086 .ty = Type.initTag(.type),
1087 .val = Value.initTag(.single_const_pointer_to_comptime_int_type),
1088 },
1089 .const_slice_u8_type = .{
1090 .ty = Type.initTag(.type),
1091 .val = Value.initTag(.const_slice_u8_type),
1092 },
1093 .enum_literal_type = .{
1094 .ty = Type.initTag(.type),
1095 .val = Value.initTag(.enum_literal_type),
1096 },
7101097
711 positionals: struct {},
712 kw_args: struct {},
1098 .undef = .{
1099 .ty = Type.initTag(.@"undefined"),
1100 .val = Value.initTag(.undef),
1101 },
1102 .zero = .{
1103 .ty = Type.initTag(.comptime_int),
1104 .val = Value.initTag(.zero),
1105 },
1106 .zero_usize = .{
1107 .ty = Type.initTag(.usize),
1108 .val = Value.initTag(.zero),
1109 },
1110 .one = .{
1111 .ty = Type.initTag(.comptime_int),
1112 .val = Value.initTag(.one),
1113 },
1114 .one_usize = .{
1115 .ty = Type.initTag(.usize),
1116 .val = Value.initTag(.one),
1117 },
1118 .void_value = .{
1119 .ty = Type.initTag(.void),
1120 .val = Value.initTag(.void_value),
1121 },
1122 .unreachable_value = .{
1123 .ty = Type.initTag(.noreturn),
1124 .val = Value.initTag(.unreachable_value),
1125 },
1126 .null_value = .{
1127 .ty = Type.initTag(.@"null"),
1128 .val = Value.initTag(.null_value),
1129 },
1130 .bool_true = .{
1131 .ty = Type.initTag(.bool),
1132 .val = Value.initTag(.bool_true),
1133 },
1134 .bool_false = .{
1135 .ty = Type.initTag(.bool),
1136 .val = Value.initTag(.bool_false),
1137 },
1138 .empty_struct = .{
1139 .ty = Type.initTag(.empty_struct_literal),
1140 .val = Value.initTag(.empty_struct_value),
1141 },
1142 });
7131143 };
7141144
715 pub const UnOp = struct {
716 base: Inst,
717
718 positionals: struct {
719 operand: *Inst,
1145 /// All instructions have an 8-byte payload, which is contained within
1146 /// this union. `Tag` determines which union field is active, as well as
1147 /// how to interpret the data within.
1148 pub const Data = union {
1149 /// Used for unary operators, with an AST node source location.
1150 un_node: struct {
1151 /// Offset from Decl AST node index.
1152 src_node: i32,
1153 /// The meaning of this operand depends on the corresponding `Tag`.
1154 operand: Ref,
1155
1156 pub fn src(self: @This()) LazySrcLoc {
1157 return .{ .node_offset = self.src_node };
1158 }
7201159 },
721 kw_args: struct {},
722 };
723
724 pub const BinOp = struct {
725 base: Inst,
726
727 positionals: struct {
728 lhs: *Inst,
729 rhs: *Inst,
1160 /// Used for unary operators, with a token source location.
1161 un_tok: struct {
1162 /// Offset from Decl AST token index.
1163 src_tok: ast.TokenIndex,
1164 /// The meaning of this operand depends on the corresponding `Tag`.
1165 operand: Ref,
1166
1167 pub fn src(self: @This()) LazySrcLoc {
1168 return .{ .token_offset = self.src_tok };
1169 }
7301170 },
731 kw_args: struct {},
732 };
733
734 pub const Arg = struct {
735 pub const base_tag = Tag.arg;
736 base: Inst,
1171 pl_node: struct {
1172 /// Offset from Decl AST node index.
1173 /// `Tag` determines which kind of AST node this points to.
1174 src_node: i32,
1175 /// index into extra.
1176 /// `Tag` determines what lives there.
1177 payload_index: u32,
1178
1179 pub fn src(self: @This()) LazySrcLoc {
1180 return .{ .node_offset = self.src_node };
1181 }
1182 },
1183 bin: Bin,
1184 @"const": *TypedValue,
1185 /// For strings which may contain null bytes.
1186 str: struct {
1187 /// Offset into `string_bytes`.
1188 start: u32,
1189 /// Number of bytes in the string.
1190 len: u32,
1191
1192 pub fn get(self: @This(), code: Code) []const u8 {
1193 return code.string_bytes[self.start..][0..self.len];
1194 }
1195 },
1196 /// Strings 8 or fewer bytes which may not contain null bytes.
1197 small_str: struct {
1198 bytes: [8]u8,
1199
1200 pub fn get(self: @This()) []const u8 {
1201 const end = for (self.bytes) |byte, i| {
1202 if (byte == 0) break i;
1203 } else self.bytes.len;
1204 return self.bytes[0..end];
1205 }
1206 },
1207 str_tok: struct {
1208 /// Offset into `string_bytes`. Null-terminated.
1209 start: u32,
1210 /// Offset from Decl AST token index.
1211 src_tok: u32,
1212
1213 pub fn get(self: @This(), code: Code) [:0]const u8 {
1214 return code.nullTerminatedString(self.start);
1215 }
7371216
738 positionals: struct {
739 /// This exists to be passed to the arg TZIR instruction, which
740 /// needs it for debug info.
741 name: []const u8,
1217 pub fn src(self: @This()) LazySrcLoc {
1218 return .{ .token_offset = self.src_tok };
1219 }
1220 },
1221 /// Offset from Decl AST token index.
1222 tok: ast.TokenIndex,
1223 /// Offset from Decl AST node index.
1224 node: i32,
1225 int: u64,
1226 array_type_sentinel: struct {
1227 len: Ref,
1228 /// index into extra, points to an `ArrayTypeSentinel`
1229 payload_index: u32,
1230 },
1231 ptr_type_simple: struct {
1232 is_allowzero: bool,
1233 is_mutable: bool,
1234 is_volatile: bool,
1235 size: std.builtin.TypeInfo.Pointer.Size,
1236 elem_type: Ref,
1237 },
1238 ptr_type: struct {
1239 flags: packed struct {
1240 is_allowzero: bool,
1241 is_mutable: bool,
1242 is_volatile: bool,
1243 has_sentinel: bool,
1244 has_align: bool,
1245 has_bit_range: bool,
1246 _: u2 = undefined,
1247 },
1248 size: std.builtin.TypeInfo.Pointer.Size,
1249 /// Index into extra. See `PtrType`.
1250 payload_index: u32,
1251 },
1252 int_type: struct {
1253 /// Offset from Decl AST node index.
1254 /// `Tag` determines which kind of AST node this points to.
1255 src_node: i32,
1256 signedness: std.builtin.Signedness,
1257 bit_count: u16,
1258
1259 pub fn src(self: @This()) LazySrcLoc {
1260 return .{ .node_offset = self.src_node };
1261 }
1262 },
1263 bool_br: struct {
1264 lhs: Ref,
1265 /// Points to a `Block`.
1266 payload_index: u32,
1267 },
1268 param_type: struct {
1269 callee: Ref,
1270 param_index: u32,
1271 },
1272 @"unreachable": struct {
1273 /// Offset from Decl AST node index.
1274 /// `Tag` determines which kind of AST node this points to.
1275 src_node: i32,
1276 /// `false`: Not safety checked - the compiler will assume the
1277 /// correctness of this instruction.
1278 /// `true`: In safety-checked modes, this will generate a call
1279 /// to the panic function unless it can be proven unreachable by the compiler.
1280 safety: bool,
1281
1282 pub fn src(self: @This()) LazySrcLoc {
1283 return .{ .node_offset = self.src_node };
1284 }
1285 },
1286 @"break": struct {
1287 block_inst: Index,
1288 operand: Ref,
1289 },
1290 switch_capture: struct {
1291 switch_inst: Index,
1292 prong_index: u32,
7421293 },
743 kw_args: struct {},
744 };
7451294
746 pub const Block = struct {
747 pub const base_tag = Tag.block;
748 base: Inst,
1295 // Make sure we don't accidentally add a field to make this union
1296 // bigger than expected. Note that in Debug builds, Zig is allowed
1297 // to insert a secret field for safety checks.
1298 comptime {
1299 if (std.builtin.mode != .Debug) {
1300 assert(@sizeOf(Data) == 8);
1301 }
1302 }
1303 };
7491304
750 positionals: struct {
751 body: Body,
752 },
753 kw_args: struct {},
1305 /// Stored in extra. Trailing is:
1306 /// * output_name: u32 // index into string_bytes (null terminated) if output is present
1307 /// * arg: Ref // for every args_len.
1308 /// * constraint: u32 // index into string_bytes (null terminated) for every args_len.
1309 /// * clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
1310 pub const Asm = struct {
1311 asm_source: Ref,
1312 return_type: Ref,
1313 /// May be omitted.
1314 output: Ref,
1315 args_len: u32,
1316 clobbers_len: u32,
7541317 };
7551318
756 pub const Break = struct {
757 pub const base_tag = Tag.@"break";
758 base: Inst,
1319 /// This data is stored inside extra, with trailing parameter type indexes
1320 /// according to `param_types_len`.
1321 /// Each param type is a `Ref`.
1322 pub const FnTypeCc = struct {
1323 return_type: Ref,
1324 cc: Ref,
1325 param_types_len: u32,
1326 };
7591327
760 positionals: struct {
761 block: *Block,
762 operand: *Inst,
763 },
764 kw_args: struct {},
1328 /// This data is stored inside extra, with trailing parameter type indexes
1329 /// according to `param_types_len`.
1330 /// Each param type is a `Ref`.
1331 pub const FnType = struct {
1332 return_type: Ref,
1333 param_types_len: u32,
7651334 };
7661335
767 pub const BreakVoid = struct {
768 pub const base_tag = Tag.break_void;
769 base: Inst,
1336 /// This data is stored inside extra, with trailing operands according to `operands_len`.
1337 /// Each operand is a `Ref`.
1338 pub const MultiOp = struct {
1339 operands_len: u32,
1340 };
7701341
771 positionals: struct {
772 block: *Block,
773 },
774 kw_args: struct {},
1342 /// This data is stored inside extra, with trailing operands according to `body_len`.
1343 /// Each operand is an `Index`.
1344 pub const Block = struct {
1345 body_len: u32,
7751346 };
7761347
777 // TODO break this into multiple call instructions to avoid paying the cost
778 // of the calling convention field most of the time.
1348 /// Stored inside extra, with trailing arguments according to `args_len`.
1349 /// Each argument is a `Ref`.
7791350 pub const Call = struct {
780 pub const base_tag = Tag.call;
781 base: Inst,
782
783 positionals: struct {
784 func: *Inst,
785 args: []*Inst,
786 modifier: std.builtin.CallOptions.Modifier = .auto,
787 },
788 kw_args: struct {},
1351 callee: Ref,
1352 args_len: u32,
7891353 };
7901354
791 pub const DeclRef = struct {
792 pub const base_tag = Tag.decl_ref;
793 base: Inst,
794
795 positionals: struct {
796 decl: *IrModule.Decl,
797 },
798 kw_args: struct {},
1355 /// This data is stored inside extra, with two sets of trailing `Ref`:
1356 /// * 0. the then body, according to `then_body_len`.
1357 /// * 1. the else body, according to `else_body_len`.
1358 pub const CondBr = struct {
1359 condition: Ref,
1360 then_body_len: u32,
1361 else_body_len: u32,
7991362 };
8001363
801 pub const DeclRefStr = struct {
802 pub const base_tag = Tag.decl_ref_str;
803 base: Inst,
804
805 positionals: struct {
806 name: *Inst,
807 },
808 kw_args: struct {},
1364 /// Stored in extra. Depending on the flags in Data, there will be up to 4
1365 /// trailing Ref fields:
1366 /// 0. sentinel: Ref // if `has_sentinel` flag is set
1367 /// 1. align: Ref // if `has_align` flag is set
1368 /// 2. bit_start: Ref // if `has_bit_range` flag is set
1369 /// 3. bit_end: Ref // if `has_bit_range` flag is set
1370 pub const PtrType = struct {
1371 elem_type: Ref,
8091372 };
8101373
811 pub const DeclVal = struct {
812 pub const base_tag = Tag.decl_val;
813 base: Inst,
814
815 positionals: struct {
816 decl: *IrModule.Decl,
817 },
818 kw_args: struct {},
1374 pub const ArrayTypeSentinel = struct {
1375 sentinel: Ref,
1376 elem_type: Ref,
8191377 };
8201378
821 pub const CompileLog = struct {
822 pub const base_tag = Tag.compile_log;
823 base: Inst,
824
825 positionals: struct {
826 to_log: []*Inst,
827 },
828 kw_args: struct {},
1379 pub const SliceStart = struct {
1380 lhs: Ref,
1381 start: Ref,
8291382 };
8301383
831 pub const Const = struct {
832 pub const base_tag = Tag.@"const";
833 base: Inst,
834
835 positionals: struct {
836 typed_value: TypedValue,
837 },
838 kw_args: struct {},
1384 pub const SliceEnd = struct {
1385 lhs: Ref,
1386 start: Ref,
1387 end: Ref,
8391388 };
8401389
841 pub const Str = struct {
842 pub const base_tag = Tag.str;
843 base: Inst,
844
845 positionals: struct {
846 bytes: []const u8,
847 },
848 kw_args: struct {},
1390 pub const SliceSentinel = struct {
1391 lhs: Ref,
1392 start: Ref,
1393 end: Ref,
1394 sentinel: Ref,
8491395 };
8501396
851 pub const Int = struct {
852 pub const base_tag = Tag.int;
853 base: Inst,
854
855 positionals: struct {
856 int: BigIntConst,
857 },
858 kw_args: struct {},
1397 /// The meaning of these operands depends on the corresponding `Tag`.
1398 pub const Bin = struct {
1399 lhs: Ref,
1400 rhs: Ref,
8591401 };
8601402
861 pub const Loop = struct {
862 pub const base_tag = Tag.loop;
863 base: Inst,
1403 /// This form is supported when there are no ranges, and exactly 1 item per block.
1404 /// Depending on zir tag and len fields, extra fields trail
1405 /// this one in the extra array.
1406 /// 0. else_body { // If the tag has "_else" or "_under" in it.
1407 /// body_len: u32,
1408 /// body member Index for every body_len
1409 /// }
1410 /// 1. cases: {
1411 /// item: Ref,
1412 /// body_len: u32,
1413 /// body member Index for every body_len
1414 /// } for every cases_len
1415 pub const SwitchBlock = struct {
1416 operand: Ref,
1417 cases_len: u32,
1418 };
8641419
865 positionals: struct {
866 body: Body,
867 },
868 kw_args: struct {},
1420 /// This form is required when there exists a block which has more than one item,
1421 /// or a range.
1422 /// Depending on zir tag and len fields, extra fields trail
1423 /// this one in the extra array.
1424 /// 0. else_body { // If the tag has "_else" or "_under" in it.
1425 /// body_len: u32,
1426 /// body member Index for every body_len
1427 /// }
1428 /// 1. scalar_cases: { // for every scalar_cases_len
1429 /// item: Ref,
1430 /// body_len: u32,
1431 /// body member Index for every body_len
1432 /// }
1433 /// 2. multi_cases: { // for every multi_cases_len
1434 /// items_len: u32,
1435 /// ranges_len: u32,
1436 /// body_len: u32,
1437 /// item: Ref // for every items_len
1438 /// ranges: { // for every ranges_len
1439 /// item_first: Ref,
1440 /// item_last: Ref,
1441 /// }
1442 /// body member Index for every body_len
1443 /// }
1444 pub const SwitchBlockMulti = struct {
1445 operand: Ref,
1446 scalar_cases_len: u32,
1447 multi_cases_len: u32,
8691448 };
8701449
8711450 pub const Field = struct {
872 base: Inst,
873
874 positionals: struct {
875 object: *Inst,
876 field_name: []const u8,
877 },
878 kw_args: struct {},
1451 lhs: Ref,
1452 /// Offset into `string_bytes`.
1453 field_name_start: u32,
8791454 };
8801455
8811456 pub const FieldNamed = struct {
882 base: Inst,
883
884 positionals: struct {
885 object: *Inst,
886 field_name: *Inst,
887 },
888 kw_args: struct {},
1457 lhs: Ref,
1458 field_name: Ref,
8891459 };
8901460
891 pub const Asm = struct {
892 pub const base_tag = Tag.@"asm";
893 base: Inst,
894
895 positionals: struct {
896 asm_source: *Inst,
897 return_type: *Inst,
898 },
899 kw_args: struct {
900 @"volatile": bool = false,
901 output: ?*Inst = null,
902 inputs: []const []const u8 = &.{},
903 clobbers: []const []const u8 = &.{},
904 args: []*Inst = &[0]*Inst{},
905 },
1461 pub const As = struct {
1462 dest_type: Ref,
1463 operand: Ref,
9061464 };
9071465
908 pub const Fn = struct {
909 pub const base_tag = Tag.@"fn";
910 base: Inst,
911
912 positionals: struct {
913 fn_type: *Inst,
914 body: Body,
915 },
916 kw_args: struct {},
1466 /// Trailing:
1467 /// 0. has_bits: u32 // for every 16 fields
1468 /// - sets of 2 bits:
1469 /// 0b0X: whether corresponding field has an align expression
1470 /// 0bX0: whether corresponding field has a default expression
1471 /// 1. fields: { // for every fields_len
1472 /// field_name: u32,
1473 /// field_type: Ref,
1474 /// align: Ref, // if corresponding bit is set
1475 /// default_value: Ref, // if corresponding bit is set
1476 /// }
1477 pub const StructDecl = struct {
1478 fields_len: u32,
9171479 };
9181480
919 pub const FnType = struct {
920 pub const base_tag = Tag.fn_type;
921 base: Inst,
922
923 positionals: struct {
924 param_types: []*Inst,
925 return_type: *Inst,
926 },
927 kw_args: struct {},
1481 /// Trailing:
1482 /// 0. has_bits: u32 // for every 32 fields
1483 /// - the bit is whether corresponding field has an value expression
1484 /// 1. field_name: u32 // for every field: null terminated string index
1485 /// 2. value: Ref // for every field for which corresponding bit is set
1486 pub const EnumDecl = struct {
1487 /// Can be `Ref.none`.
1488 tag_type: Ref,
1489 fields_len: u32,
9281490 };
9291491
930 pub const FnTypeCc = struct {
931 pub const base_tag = Tag.fn_type_cc;
932 base: Inst,
933
934 positionals: struct {
935 param_types: []*Inst,
936 return_type: *Inst,
937 cc: *Inst,
938 },
939 kw_args: struct {},
1492 /// Trailing:
1493 /// 0. has_bits: u32 // for every 10 fields (+1)
1494 /// - first bit is special: set if and only if auto enum tag is enabled.
1495 /// - sets of 3 bits:
1496 /// 0b00X: whether corresponding field has a type expression
1497 /// 0b0X0: whether corresponding field has a align expression
1498 /// 0bX00: whether corresponding field has a tag value expression
1499 /// 1. field_name: u32 // for every field: null terminated string index
1500 /// 2. opt_exprs // Ref for every field for which corresponding bit is set
1501 /// - interleaved. type if present, align if present, tag value if present.
1502 pub const UnionDecl = struct {
1503 /// Can be `Ref.none`.
1504 tag_type: Ref,
1505 fields_len: u32,
9401506 };
1507};
9411508
942 pub const IntType = struct {
943 pub const base_tag = Tag.int_type;
944 base: Inst,
945
946 positionals: struct {
947 signed: *Inst,
948 bits: *Inst,
949 },
950 kw_args: struct {},
951 };
1509pub const SpecialProng = enum { none, @"else", under };
9521510
953 pub const Export = struct {
954 pub const base_tag = Tag.@"export";
955 base: Inst,
1511const Writer = struct {
1512 gpa: *Allocator,
1513 arena: *Allocator,
1514 scope: *Module.Scope,
1515 code: Code,
1516 indent: usize,
1517 param_count: usize,
9561518
957 positionals: struct {
958 symbol_name: *Inst,
959 decl_name: []const u8,
960 },
961 kw_args: struct {},
962 };
1519 fn writeInstToStream(
1520 self: *Writer,
1521 stream: anytype,
1522 inst: Inst.Index,
1523 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1524 const tags = self.code.instructions.items(.tag);
1525 const tag = tags[inst];
1526 try stream.print("= {s}(", .{@tagName(tags[inst])});
1527 switch (tag) {
1528 .array_type,
1529 .as,
1530 .coerce_result_ptr,
1531 .elem_ptr,
1532 .elem_val,
1533 .intcast,
1534 .store,
1535 .store_to_block_ptr,
1536 => try self.writeBin(stream, inst),
1537
1538 .alloc,
1539 .alloc_mut,
1540 .alloc_inferred,
1541 .alloc_inferred_mut,
1542 .indexable_ptr_len,
1543 .bit_not,
1544 .bool_not,
1545 .negate,
1546 .negate_wrap,
1547 .call_none,
1548 .call_none_chkused,
1549 .compile_error,
1550 .load,
1551 .ensure_result_used,
1552 .ensure_result_non_error,
1553 .import,
1554 .ptrtoint,
1555 .ret_node,
1556 .set_eval_branch_quota,
1557 .resolve_inferred_alloc,
1558 .optional_type,
1559 .optional_type_from_ptr_elem,
1560 .optional_payload_safe,
1561 .optional_payload_unsafe,
1562 .optional_payload_safe_ptr,
1563 .optional_payload_unsafe_ptr,
1564 .err_union_payload_safe,
1565 .err_union_payload_unsafe,
1566 .err_union_payload_safe_ptr,
1567 .err_union_payload_unsafe_ptr,
1568 .err_union_code,
1569 .err_union_code_ptr,
1570 .int_to_error,
1571 .error_to_int,
1572 .is_non_null,
1573 .is_null,
1574 .is_non_null_ptr,
1575 .is_null_ptr,
1576 .is_err,
1577 .is_err_ptr,
1578 .typeof,
1579 .typeof_elem,
1580 .struct_init_empty,
1581 => try self.writeUnNode(stream, inst),
1582
1583 .ref,
1584 .ret_tok,
1585 .ret_coerce,
1586 .ensure_err_payload_void,
1587 => try self.writeUnTok(stream, inst),
1588
1589 .bool_br_and,
1590 .bool_br_or,
1591 => try self.writeBoolBr(stream, inst),
1592
1593 .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
1594 .@"const" => try self.writeConst(stream, inst),
1595 .param_type => try self.writeParamType(stream, inst),
1596 .ptr_type_simple => try self.writePtrTypeSimple(stream, inst),
1597 .ptr_type => try self.writePtrType(stream, inst),
1598 .int => try self.writeInt(stream, inst),
1599 .str => try self.writeStr(stream, inst),
1600 .elided => try stream.writeAll(")"),
1601 .int_type => try self.writeIntType(stream, inst),
1602
1603 .@"break",
1604 .break_inline,
1605 => try self.writeBreak(stream, inst),
1606
1607 .@"asm",
1608 .asm_volatile,
1609 .elem_ptr_node,
1610 .elem_val_node,
1611 .field_ptr_named,
1612 .field_val_named,
1613 .floatcast,
1614 .slice_start,
1615 .slice_end,
1616 .slice_sentinel,
1617 .union_decl,
1618 .enum_decl,
1619 => try self.writePlNode(stream, inst),
1620
1621 .add,
1622 .addwrap,
1623 .array_cat,
1624 .array_mul,
1625 .mul,
1626 .mulwrap,
1627 .sub,
1628 .subwrap,
1629 .bool_and,
1630 .bool_or,
1631 .cmp_lt,
1632 .cmp_lte,
1633 .cmp_eq,
1634 .cmp_gte,
1635 .cmp_gt,
1636 .cmp_neq,
1637 .div,
1638 .mod_rem,
1639 .shl,
1640 .shr,
1641 .xor,
1642 .store_node,
1643 .error_union_type,
1644 .merge_error_sets,
1645 .bit_and,
1646 .bit_or,
1647 => try self.writePlNodeBin(stream, inst),
1648
1649 .call,
1650 .call_chkused,
1651 .call_compile_time,
1652 => try self.writePlNodeCall(stream, inst),
1653
1654 .block,
1655 .block_inline,
1656 .loop,
1657 .validate_struct_init_ptr,
1658 => try self.writePlNodeBlock(stream, inst),
1659
1660 .condbr,
1661 .condbr_inline,
1662 => try self.writePlNodeCondBr(stream, inst),
1663
1664 .struct_decl,
1665 .struct_decl_packed,
1666 .struct_decl_extern,
1667 => try self.writeStructDecl(stream, inst),
1668
1669 .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none),
1670 .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
1671 .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under),
1672 .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none),
1673 .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
1674 .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under),
1675
1676 .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
1677 .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
1678 .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
1679 .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
1680 .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
1681 .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
1682
1683 .compile_log,
1684 .typeof_peer,
1685 => try self.writePlNodeMultiOp(stream, inst),
1686
1687 .decl_ref,
1688 .decl_val,
1689 => try self.writePlNodeDecl(stream, inst),
1690
1691 .field_ptr,
1692 .field_val,
1693 => try self.writePlNodeField(stream, inst),
1694
1695 .as_node => try self.writeAs(stream, inst),
1696
1697 .breakpoint,
1698 .opaque_decl,
1699 .dbg_stmt_node,
1700 .ret_ptr,
1701 .ret_type,
1702 .repeat,
1703 .repeat_inline,
1704 => try self.writeNode(stream, inst),
1705
1706 .error_value,
1707 .enum_literal,
1708 => try self.writeStrTok(stream, inst),
1709
1710 .fn_type => try self.writeFnType(stream, inst, false),
1711 .fn_type_cc => try self.writeFnTypeCc(stream, inst, false),
1712 .fn_type_var_args => try self.writeFnType(stream, inst, true),
1713 .fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true),
1714
1715 .@"unreachable" => try self.writeUnreachable(stream, inst),
1716
1717 .enum_literal_small => try self.writeSmallStr(stream, inst),
1718
1719 .switch_capture,
1720 .switch_capture_ref,
1721 .switch_capture_multi,
1722 .switch_capture_multi_ref,
1723 .switch_capture_else,
1724 .switch_capture_else_ref,
1725 => try self.writeSwitchCapture(stream, inst),
1726
1727 .bitcast,
1728 .bitcast_result_ptr,
1729 .store_to_inferred_ptr,
1730 => try stream.writeAll("TODO)"),
1731 }
1732 }
9631733
964 pub const ParamType = struct {
965 pub const base_tag = Tag.param_type;
966 base: Inst,
967
968 positionals: struct {
969 func: *Inst,
970 arg_index: usize,
971 },
972 kw_args: struct {},
973 };
974
975 pub const Primitive = struct {
976 pub const base_tag = Tag.primitive;
977 base: Inst,
978
979 positionals: struct {
980 tag: Builtin,
981 },
982 kw_args: struct {},
983
984 pub const Builtin = enum {
985 i8,
986 u8,
987 i16,
988 u16,
989 i32,
990 u32,
991 i64,
992 u64,
993 isize,
994 usize,
995 c_short,
996 c_ushort,
997 c_int,
998 c_uint,
999 c_long,
1000 c_ulong,
1001 c_longlong,
1002 c_ulonglong,
1003 c_longdouble,
1004 c_void,
1005 f16,
1006 f32,
1007 f64,
1008 f128,
1009 bool,
1010 void,
1011 noreturn,
1012 type,
1013 anyerror,
1014 comptime_int,
1015 comptime_float,
1016 @"true",
1017 @"false",
1018 @"null",
1019 @"undefined",
1020 void_value,
1021
1022 pub fn toTypedValue(self: Builtin) TypedValue {
1023 return switch (self) {
1024 .i8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i8_type) },
1025 .u8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u8_type) },
1026 .i16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i16_type) },
1027 .u16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u16_type) },
1028 .i32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i32_type) },
1029 .u32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u32_type) },
1030 .i64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i64_type) },
1031 .u64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u64_type) },
1032 .isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) },
1033 .usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) },
1034 .c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) },
1035 .c_ushort => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ushort_type) },
1036 .c_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_int_type) },
1037 .c_uint => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_uint_type) },
1038 .c_long => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_long_type) },
1039 .c_ulong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulong_type) },
1040 .c_longlong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longlong_type) },
1041 .c_ulonglong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulonglong_type) },
1042 .c_longdouble => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longdouble_type) },
1043 .c_void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_void_type) },
1044 .f16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f16_type) },
1045 .f32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f32_type) },
1046 .f64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f64_type) },
1047 .f128 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f128_type) },
1048 .bool => .{ .ty = Type.initTag(.type), .val = Value.initTag(.bool_type) },
1049 .void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.void_type) },
1050 .noreturn => .{ .ty = Type.initTag(.type), .val = Value.initTag(.noreturn_type) },
1051 .type => .{ .ty = Type.initTag(.type), .val = Value.initTag(.type_type) },
1052 .anyerror => .{ .ty = Type.initTag(.type), .val = Value.initTag(.anyerror_type) },
1053 .comptime_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_int_type) },
1054 .comptime_float => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_float_type) },
1055 .@"true" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_true) },
1056 .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },
1057 .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },
1058 .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },
1059 .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.void_value) },
1060 };
1061 }
1062 };
1063 };
1064
1065 pub const Elem = struct {
1066 base: Inst,
1067
1068 positionals: struct {
1069 array: *Inst,
1070 index: *Inst,
1071 },
1072 kw_args: struct {},
1073 };
1074
1075 pub const CondBr = struct {
1076 pub const base_tag = Tag.condbr;
1077 base: Inst,
1078
1079 positionals: struct {
1080 condition: *Inst,
1081 then_body: Body,
1082 else_body: Body,
1083 },
1084 kw_args: struct {},
1085 };
1086
1087 pub const PtrType = struct {
1088 pub const base_tag = Tag.ptr_type;
1089 base: Inst,
1090
1091 positionals: struct {
1092 child_type: *Inst,
1093 },
1094 kw_args: struct {
1095 @"allowzero": bool = false,
1096 @"align": ?*Inst = null,
1097 align_bit_start: ?*Inst = null,
1098 align_bit_end: ?*Inst = null,
1099 mutable: bool = true,
1100 @"volatile": bool = false,
1101 sentinel: ?*Inst = null,
1102 size: std.builtin.TypeInfo.Pointer.Size = .One,
1103 },
1104 };
1105
1106 pub const ArrayTypeSentinel = struct {
1107 pub const base_tag = Tag.array_type_sentinel;
1108 base: Inst,
1109
1110 positionals: struct {
1111 len: *Inst,
1112 sentinel: *Inst,
1113 elem_type: *Inst,
1114 },
1115 kw_args: struct {},
1116 };
1117
1118 pub const EnumLiteral = struct {
1119 pub const base_tag = Tag.enum_literal;
1120 base: Inst,
1121
1122 positionals: struct {
1123 name: []const u8,
1124 },
1125 kw_args: struct {},
1126 };
1127
1128 pub const ErrorSet = struct {
1129 pub const base_tag = Tag.error_set;
1130 base: Inst,
1131
1132 positionals: struct {
1133 fields: [][]const u8,
1134 },
1135 kw_args: struct {},
1136 };
1137
1138 pub const ErrorValue = struct {
1139 pub const base_tag = Tag.error_value;
1140 base: Inst,
1734 fn writeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1735 const inst_data = self.code.instructions.items(.data)[inst].bin;
1736 try self.writeInstRef(stream, inst_data.lhs);
1737 try stream.writeAll(", ");
1738 try self.writeInstRef(stream, inst_data.rhs);
1739 try stream.writeByte(')');
1740 }
11411741
1142 positionals: struct {
1143 name: []const u8,
1144 },
1145 kw_args: struct {},
1146 };
1742 fn writeUnNode(
1743 self: *Writer,
1744 stream: anytype,
1745 inst: Inst.Index,
1746 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1747 const inst_data = self.code.instructions.items(.data)[inst].un_node;
1748 try self.writeInstRef(stream, inst_data.operand);
1749 try stream.writeAll(") ");
1750 try self.writeSrc(stream, inst_data.src());
1751 }
11471752
1148 pub const Slice = struct {
1149 pub const base_tag = Tag.slice;
1150 base: Inst,
1753 fn writeUnTok(
1754 self: *Writer,
1755 stream: anytype,
1756 inst: Inst.Index,
1757 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1758 const inst_data = self.code.instructions.items(.data)[inst].un_tok;
1759 try self.writeInstRef(stream, inst_data.operand);
1760 try stream.writeAll(") ");
1761 try self.writeSrc(stream, inst_data.src());
1762 }
11511763
1152 positionals: struct {
1153 array_ptr: *Inst,
1154 start: *Inst,
1155 },
1156 kw_args: struct {
1157 end: ?*Inst = null,
1158 sentinel: ?*Inst = null,
1159 },
1160 };
1764 fn writeArrayTypeSentinel(
1765 self: *Writer,
1766 stream: anytype,
1767 inst: Inst.Index,
1768 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1769 const inst_data = self.code.instructions.items(.data)[inst].array_type_sentinel;
1770 try stream.writeAll("TODO)");
1771 }
11611772
1162 pub const TypeOfPeer = struct {
1163 pub const base_tag = .typeof_peer;
1164 base: Inst,
1165 positionals: struct {
1166 items: []*Inst,
1167 },
1168 kw_args: struct {},
1169 };
1773 fn writeConst(
1774 self: *Writer,
1775 stream: anytype,
1776 inst: Inst.Index,
1777 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1778 const inst_data = self.code.instructions.items(.data)[inst].@"const";
1779 try stream.writeAll("TODO)");
1780 }
11701781
1171 pub const ContainerFieldNamed = struct {
1172 pub const base_tag = Tag.container_field_named;
1173 base: Inst,
1782 fn writeParamType(
1783 self: *Writer,
1784 stream: anytype,
1785 inst: Inst.Index,
1786 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1787 const inst_data = self.code.instructions.items(.data)[inst].param_type;
1788 try self.writeInstRef(stream, inst_data.callee);
1789 try stream.print(", {d})", .{inst_data.param_index});
1790 }
11741791
1175 positionals: struct {
1176 bytes: []const u8,
1177 },
1178 kw_args: struct {},
1179 };
1792 fn writePtrTypeSimple(
1793 self: *Writer,
1794 stream: anytype,
1795 inst: Inst.Index,
1796 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1797 const inst_data = self.code.instructions.items(.data)[inst].ptr_type_simple;
1798 try stream.writeAll("TODO)");
1799 }
11801800
1181 pub const ContainerFieldTyped = struct {
1182 pub const base_tag = Tag.container_field_typed;
1183 base: Inst,
1801 fn writePtrType(
1802 self: *Writer,
1803 stream: anytype,
1804 inst: Inst.Index,
1805 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1806 const inst_data = self.code.instructions.items(.data)[inst].ptr_type;
1807 try stream.writeAll("TODO)");
1808 }
11841809
1185 positionals: struct {
1186 bytes: []const u8,
1187 ty: *Inst,
1188 },
1189 kw_args: struct {},
1190 };
1810 fn writeInt(
1811 self: *Writer,
1812 stream: anytype,
1813 inst: Inst.Index,
1814 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1815 const inst_data = self.code.instructions.items(.data)[inst].int;
1816 try stream.print("{d})", .{inst_data});
1817 }
11911818
1192 pub const ContainerField = struct {
1193 pub const base_tag = Tag.container_field;
1194 base: Inst,
1819 fn writeStr(
1820 self: *Writer,
1821 stream: anytype,
1822 inst: Inst.Index,
1823 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1824 const inst_data = self.code.instructions.items(.data)[inst].str;
1825 const str = inst_data.get(self.code);
1826 try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
1827 }
11951828
1196 positionals: struct {
1197 bytes: []const u8,
1198 },
1199 kw_args: struct {
1200 ty: ?*Inst = null,
1201 init: ?*Inst = null,
1202 alignment: ?*Inst = null,
1203 is_comptime: bool = false,
1204 },
1205 };
1829 fn writePlNode(
1830 self: *Writer,
1831 stream: anytype,
1832 inst: Inst.Index,
1833 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1834 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1835 try stream.writeAll("TODO) ");
1836 try self.writeSrc(stream, inst_data.src());
1837 }
12061838
1207 pub const EnumType = struct {
1208 pub const base_tag = Tag.enum_type;
1209 base: Inst,
1839 fn writePlNodeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1840 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1841 const extra = self.code.extraData(Inst.Bin, inst_data.payload_index).data;
1842 try self.writeInstRef(stream, extra.lhs);
1843 try stream.writeAll(", ");
1844 try self.writeInstRef(stream, extra.rhs);
1845 try stream.writeAll(") ");
1846 try self.writeSrc(stream, inst_data.src());
1847 }
12101848
1211 positionals: struct {
1212 fields: []*Inst,
1213 },
1214 kw_args: struct {
1215 tag_type: ?*Inst = null,
1216 layout: std.builtin.TypeInfo.ContainerLayout = .Auto,
1217 },
1218 };
1849 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1850 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1851 const extra = self.code.extraData(Inst.Call, inst_data.payload_index);
1852 const args = self.code.refSlice(extra.end, extra.data.args_len);
12191853
1220 pub const StructType = struct {
1221 pub const base_tag = Tag.struct_type;
1222 base: Inst,
1854 try self.writeInstRef(stream, extra.data.callee);
1855 try stream.writeAll(", [");
1856 for (args) |arg, i| {
1857 if (i != 0) try stream.writeAll(", ");
1858 try self.writeInstRef(stream, arg);
1859 }
1860 try stream.writeAll("]) ");
1861 try self.writeSrc(stream, inst_data.src());
1862 }
12231863
1224 positionals: struct {
1225 fields: []*Inst,
1226 },
1227 kw_args: struct {
1228 layout: std.builtin.TypeInfo.ContainerLayout = .Auto,
1229 },
1230 };
1864 fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1865 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1866 const extra = self.code.extraData(Inst.Block, inst_data.payload_index);
1867 const body = self.code.extra[extra.end..][0..extra.data.body_len];
1868 try stream.writeAll("{\n");
1869 self.indent += 2;
1870 try self.writeBody(stream, body);
1871 self.indent -= 2;
1872 try stream.writeByteNTimes(' ', self.indent);
1873 try stream.writeAll("}) ");
1874 try self.writeSrc(stream, inst_data.src());
1875 }
12311876
1232 pub const UnionType = struct {
1233 pub const base_tag = Tag.union_type;
1234 base: Inst,
1877 fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1878 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1879 const extra = self.code.extraData(Inst.CondBr, inst_data.payload_index);
1880 const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len];
1881 const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1882 try self.writeInstRef(stream, extra.data.condition);
1883 try stream.writeAll(", {\n");
1884 self.indent += 2;
1885 try self.writeBody(stream, then_body);
1886 self.indent -= 2;
1887 try stream.writeByteNTimes(' ', self.indent);
1888 try stream.writeAll("}, {\n");
1889 self.indent += 2;
1890 try self.writeBody(stream, else_body);
1891 self.indent -= 2;
1892 try stream.writeByteNTimes(' ', self.indent);
1893 try stream.writeAll("}) ");
1894 try self.writeSrc(stream, inst_data.src());
1895 }
12351896
1236 positionals: struct {
1237 fields: []*Inst,
1238 },
1239 kw_args: struct {
1240 init_inst: ?*Inst = null,
1241 has_enum_token: bool,
1242 layout: std.builtin.TypeInfo.ContainerLayout = .Auto,
1243 },
1244 };
1897 fn writeStructDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1898 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1899 const extra = self.code.extraData(Inst.StructDecl, inst_data.payload_index);
1900 const fields_len = extra.data.fields_len;
1901 const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable;
1902
1903 try stream.writeAll("{\n");
1904 self.indent += 2;
1905
1906 var field_index: usize = extra.end + bit_bags_count;
1907 var bit_bag_index: usize = extra.end;
1908 var cur_bit_bag: u32 = undefined;
1909 var field_i: u32 = 0;
1910 while (field_i < fields_len) : (field_i += 1) {
1911 if (field_i % 16 == 0) {
1912 cur_bit_bag = self.code.extra[bit_bag_index];
1913 bit_bag_index += 1;
1914 }
1915 const has_align = @truncate(u1, cur_bit_bag) != 0;
1916 cur_bit_bag >>= 1;
1917 const has_default = @truncate(u1, cur_bit_bag) != 0;
1918 cur_bit_bag >>= 1;
1919
1920 const field_name = self.code.nullTerminatedString(self.code.extra[field_index]);
1921 field_index += 1;
1922 const field_type = @intToEnum(Inst.Ref, self.code.extra[field_index]);
1923 field_index += 1;
1924
1925 try stream.writeByteNTimes(' ', self.indent);
1926 try stream.print("{}: ", .{std.zig.fmtId(field_name)});
1927 try self.writeInstRef(stream, field_type);
1928
1929 if (has_align) {
1930 const align_ref = @intToEnum(Inst.Ref, self.code.extra[field_index]);
1931 field_index += 1;
1932
1933 try stream.writeAll(" align(");
1934 try self.writeInstRef(stream, align_ref);
1935 try stream.writeAll(")");
1936 }
1937 if (has_default) {
1938 const default_ref = @intToEnum(Inst.Ref, self.code.extra[field_index]);
1939 field_index += 1;
12451940
1246 pub const SwitchBr = struct {
1247 base: Inst,
1248
1249 positionals: struct {
1250 target: *Inst,
1251 /// List of all individual items and ranges
1252 items: []*Inst,
1253 cases: []Case,
1254 else_body: Body,
1255 /// Pointer to first range if such exists.
1256 range: ?*Inst = null,
1257 special_prong: SpecialProng = .none,
1258 },
1259 kw_args: struct {},
1941 try stream.writeAll(" = ");
1942 try self.writeInstRef(stream, default_ref);
1943 }
1944 try stream.writeAll(",\n");
1945 }
12601946
1261 pub const SpecialProng = enum {
1262 none,
1263 @"else",
1264 underscore,
1265 };
1947 self.indent -= 2;
1948 try stream.writeByteNTimes(' ', self.indent);
1949 try stream.writeAll("}) ");
1950 try self.writeSrc(stream, inst_data.src());
1951 }
12661952
1267 pub const Case = struct {
1268 item: *Inst,
1269 body: Body,
1953 fn writePlNodeSwitchBr(
1954 self: *Writer,
1955 stream: anytype,
1956 inst: Inst.Index,
1957 special_prong: SpecialProng,
1958 ) !void {
1959 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1960 const extra = self.code.extraData(Inst.SwitchBlock, inst_data.payload_index);
1961 const special: struct {
1962 body: []const Inst.Index,
1963 end: usize,
1964 } = switch (special_prong) {
1965 .none => .{ .body = &.{}, .end = extra.end },
1966 .under, .@"else" => blk: {
1967 const body_len = self.code.extra[extra.end];
1968 const extra_body_start = extra.end + 1;
1969 break :blk .{
1970 .body = self.code.extra[extra_body_start..][0..body_len],
1971 .end = extra_body_start + body_len,
1972 };
1973 },
12701974 };
1271 };
1272};
12731975
1274pub const ErrorMsg = struct {
1275 byte_offset: usize,
1276 msg: []const u8,
1277};
1278
1279pub const Body = struct {
1280 instructions: []*Inst,
1281};
1976 try self.writeInstRef(stream, extra.data.operand);
12821977
1283pub const Module = struct {
1284 decls: []*Decl,
1285 arena: std.heap.ArenaAllocator,
1286 error_msg: ?ErrorMsg = null,
1287 metadata: std.AutoHashMap(*Inst, MetaData),
1288 body_metadata: std.AutoHashMap(*Body, BodyMetaData),
1289
1290 pub const Decl = struct {
1291 name: []const u8,
1292
1293 /// Hash of slice into the source of the part after the = and before the next instruction.
1294 contents_hash: std.zig.SrcHash,
1978 if (special.body.len != 0) {
1979 const prong_name = switch (special_prong) {
1980 .@"else" => "else",
1981 .under => "_",
1982 else => unreachable,
1983 };
1984 try stream.print(", {s} => {{\n", .{prong_name});
1985 self.indent += 2;
1986 try self.writeBody(stream, special.body);
1987 self.indent -= 2;
1988 try stream.writeByteNTimes(' ', self.indent);
1989 try stream.writeAll("}");
1990 }
12951991
1296 inst: *Inst,
1297 };
1992 var extra_index: usize = special.end;
1993 {
1994 var scalar_i: usize = 0;
1995 while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
1996 const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
1997 extra_index += 1;
1998 const body_len = self.code.extra[extra_index];
1999 extra_index += 1;
2000 const body = self.code.extra[extra_index..][0..body_len];
2001 extra_index += body_len;
12982002
1299 pub const MetaData = struct {
1300 deaths: ir.Inst.DeathsInt,
1301 addr: usize,
1302 };
2003 try stream.writeAll(", ");
2004 try self.writeInstRef(stream, item_ref);
2005 try stream.writeAll(" => {\n");
2006 self.indent += 2;
2007 try self.writeBody(stream, body);
2008 self.indent -= 2;
2009 try stream.writeByteNTimes(' ', self.indent);
2010 try stream.writeAll("}");
2011 }
2012 }
2013 try stream.writeAll(") ");
2014 try self.writeSrc(stream, inst_data.src());
2015 }
13032016
1304 pub const BodyMetaData = struct {
1305 deaths: []*Inst,
1306 };
2017 fn writePlNodeSwitchBlockMulti(
2018 self: *Writer,
2019 stream: anytype,
2020 inst: Inst.Index,
2021 special_prong: SpecialProng,
2022 ) !void {
2023 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2024 const extra = self.code.extraData(Inst.SwitchBlockMulti, inst_data.payload_index);
2025 const special: struct {
2026 body: []const Inst.Index,
2027 end: usize,
2028 } = switch (special_prong) {
2029 .none => .{ .body = &.{}, .end = extra.end },
2030 .under, .@"else" => blk: {
2031 const body_len = self.code.extra[extra.end];
2032 const extra_body_start = extra.end + 1;
2033 break :blk .{
2034 .body = self.code.extra[extra_body_start..][0..body_len],
2035 .end = extra_body_start + body_len,
2036 };
2037 },
2038 };
13072039
1308 pub fn deinit(self: *Module, allocator: *Allocator) void {
1309 self.metadata.deinit();
1310 self.body_metadata.deinit();
1311 allocator.free(self.decls);
1312 self.arena.deinit();
1313 self.* = undefined;
1314 }
2040 try self.writeInstRef(stream, extra.data.operand);
13152041
1316 /// This is a debugging utility for rendering the tree to stderr.
1317 pub fn dump(self: Module) void {
1318 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().writer()) catch {};
1319 }
2042 if (special.body.len != 0) {
2043 const prong_name = switch (special_prong) {
2044 .@"else" => "else",
2045 .under => "_",
2046 else => unreachable,
2047 };
2048 try stream.print(", {s} => {{\n", .{prong_name});
2049 self.indent += 2;
2050 try self.writeBody(stream, special.body);
2051 self.indent -= 2;
2052 try stream.writeByteNTimes(' ', self.indent);
2053 try stream.writeAll("}");
2054 }
13202055
1321 const DeclAndIndex = struct {
1322 decl: *Decl,
1323 index: usize,
1324 };
2056 var extra_index: usize = special.end;
2057 {
2058 var scalar_i: usize = 0;
2059 while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
2060 const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
2061 extra_index += 1;
2062 const body_len = self.code.extra[extra_index];
2063 extra_index += 1;
2064 const body = self.code.extra[extra_index..][0..body_len];
2065 extra_index += body_len;
13252066
1326 /// TODO Look into making a table to speed this up.
1327 pub fn findDecl(self: Module, name: []const u8) ?DeclAndIndex {
1328 for (self.decls) |decl, i| {
1329 if (mem.eql(u8, decl.name, name)) {
1330 return DeclAndIndex{
1331 .decl = decl,
1332 .index = i,
1333 };
2067 try stream.writeAll(", ");
2068 try self.writeInstRef(stream, item_ref);
2069 try stream.writeAll(" => {\n");
2070 self.indent += 2;
2071 try self.writeBody(stream, body);
2072 self.indent -= 2;
2073 try stream.writeByteNTimes(' ', self.indent);
2074 try stream.writeAll("}");
13342075 }
13352076 }
1336 return null;
1337 }
2077 {
2078 var multi_i: usize = 0;
2079 while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) {
2080 const items_len = self.code.extra[extra_index];
2081 extra_index += 1;
2082 const ranges_len = self.code.extra[extra_index];
2083 extra_index += 1;
2084 const body_len = self.code.extra[extra_index];
2085 extra_index += 1;
2086 const items = self.code.refSlice(extra_index, items_len);
2087 extra_index += items_len;
2088
2089 for (items) |item_ref| {
2090 try stream.writeAll(", ");
2091 try self.writeInstRef(stream, item_ref);
2092 }
13382093
1339 pub fn findInstDecl(self: Module, inst: *Inst) ?DeclAndIndex {
1340 for (self.decls) |decl, i| {
1341 if (decl.inst == inst) {
1342 return DeclAndIndex{
1343 .decl = decl,
1344 .index = i,
1345 };
2094 var range_i: usize = 0;
2095 while (range_i < ranges_len) : (range_i += 1) {
2096 const item_first = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
2097 extra_index += 1;
2098 const item_last = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
2099 extra_index += 1;
2100
2101 try stream.writeAll(", ");
2102 try self.writeInstRef(stream, item_first);
2103 try stream.writeAll("...");
2104 try self.writeInstRef(stream, item_last);
2105 }
2106
2107 const body = self.code.extra[extra_index..][0..body_len];
2108 extra_index += body_len;
2109 try stream.writeAll(" => {\n");
2110 self.indent += 2;
2111 try self.writeBody(stream, body);
2112 self.indent -= 2;
2113 try stream.writeByteNTimes(' ', self.indent);
2114 try stream.writeAll("}");
13462115 }
13472116 }
1348 return null;
2117 try stream.writeAll(") ");
2118 try self.writeSrc(stream, inst_data.src());
13492119 }
13502120
1351 /// The allocator is used for temporary storage, but this function always returns
1352 /// with no resources allocated.
1353 pub fn writeToStream(self: Module, allocator: *Allocator, stream: anytype) !void {
1354 var write = Writer{
1355 .module = &self,
1356 .inst_table = InstPtrTable.init(allocator),
1357 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),
1358 .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator),
1359 .arena = std.heap.ArenaAllocator.init(allocator),
1360 .indent = 2,
1361 .next_instr_index = undefined,
1362 };
1363 defer write.arena.deinit();
1364 defer write.inst_table.deinit();
1365 defer write.block_table.deinit();
1366 defer write.loop_table.deinit();
1367
1368 // First, build a map of *Inst to @ or % indexes
1369 try write.inst_table.ensureCapacity(@intCast(u32, self.decls.len));
2121 fn writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2122 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2123 const extra = self.code.extraData(Inst.MultiOp, inst_data.payload_index);
2124 const operands = self.code.refSlice(extra.end, extra.data.operands_len);
13702125
1371 for (self.decls) |decl, decl_i| {
1372 try write.inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name });
2126 for (operands) |operand, i| {
2127 if (i != 0) try stream.writeAll(", ");
2128 try self.writeInstRef(stream, operand);
13732129 }
2130 try stream.writeAll(") ");
2131 try self.writeSrc(stream, inst_data.src());
2132 }
13742133
1375 for (self.decls) |decl, i| {
1376 write.next_instr_index = 0;
1377 try stream.print("@{s} ", .{decl.name});
1378 try write.writeInstToStream(stream, decl.inst);
1379 try stream.writeByte('\n');
1380 }
2134 fn writePlNodeDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2135 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2136 const decl = self.code.decls[inst_data.payload_index];
2137 try stream.print("{s}) ", .{decl.name});
2138 try self.writeSrc(stream, inst_data.src());
13812139 }
1382};
13832140
1384const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 });
2141 fn writePlNodeField(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2142 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2143 const extra = self.code.extraData(Inst.Field, inst_data.payload_index).data;
2144 const name = self.code.nullTerminatedString(extra.field_name_start);
2145 try self.writeInstRef(stream, extra.lhs);
2146 try stream.print(", \"{}\") ", .{std.zig.fmtEscapes(name)});
2147 try self.writeSrc(stream, inst_data.src());
2148 }
13852149
1386const Writer = struct {
1387 module: *const Module,
1388 inst_table: InstPtrTable,
1389 block_table: std.AutoHashMap(*Inst.Block, []const u8),
1390 loop_table: std.AutoHashMap(*Inst.Loop, []const u8),
1391 arena: std.heap.ArenaAllocator,
1392 indent: usize,
1393 next_instr_index: usize,
2150 fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2151 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2152 const extra = self.code.extraData(Inst.As, inst_data.payload_index).data;
2153 try self.writeInstRef(stream, extra.dest_type);
2154 try stream.writeAll(", ");
2155 try self.writeInstRef(stream, extra.operand);
2156 try stream.writeAll(") ");
2157 try self.writeSrc(stream, inst_data.src());
2158 }
13942159
1395 fn writeInstToStream(
2160 fn writeNode(
13962161 self: *Writer,
13972162 stream: anytype,
1398 inst: *Inst,
2163 inst: Inst.Index,
13992164 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1400 inline for (@typeInfo(Inst.Tag).Enum.fields) |enum_field| {
1401 const expected_tag = @field(Inst.Tag, enum_field.name);
1402 if (inst.tag == expected_tag) {
1403 return self.writeInstToStreamGeneric(stream, expected_tag, inst);
1404 }
1405 }
1406 unreachable; // all tags handled
2165 const src_node = self.code.instructions.items(.data)[inst].node;
2166 const src: LazySrcLoc = .{ .node_offset = src_node };
2167 try stream.writeAll(") ");
2168 try self.writeSrc(stream, src);
14072169 }
14082170
1409 fn writeInstToStreamGeneric(
2171 fn writeStrTok(
14102172 self: *Writer,
14112173 stream: anytype,
1412 comptime inst_tag: Inst.Tag,
1413 base: *Inst,
2174 inst: Inst.Index,
14142175 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1415 const SpecificInst = inst_tag.Type();
1416 const inst = @fieldParentPtr(SpecificInst, "base", base);
1417 const Positionals = @TypeOf(inst.positionals);
1418 try stream.writeAll("= " ++ @tagName(inst_tag) ++ "(");
1419 const pos_fields = @typeInfo(Positionals).Struct.fields;
1420 inline for (pos_fields) |arg_field, i| {
1421 if (i != 0) {
1422 try stream.writeAll(", ");
1423 }
1424 try self.writeParamToStream(stream, &@field(inst.positionals, arg_field.name));
1425 }
2176 const inst_data = self.code.instructions.items(.data)[inst].str_tok;
2177 const str = inst_data.get(self.code);
2178 try stream.print("\"{}\") ", .{std.zig.fmtEscapes(str)});
2179 try self.writeSrc(stream, inst_data.src());
2180 }
14262181
1427 comptime var need_comma = pos_fields.len != 0;
1428 const KW_Args = @TypeOf(inst.kw_args);
1429 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
1430 if (@typeInfo(arg_field.field_type) == .Optional) {
1431 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
1432 if (need_comma) try stream.writeAll(", ");
1433 try stream.print("{s}=", .{arg_field.name});
1434 try self.writeParamToStream(stream, &non_optional);
1435 need_comma = true;
1436 }
1437 } else {
1438 if (need_comma) try stream.writeAll(", ");
1439 try stream.print("{s}=", .{arg_field.name});
1440 try self.writeParamToStream(stream, &@field(inst.kw_args, arg_field.name));
1441 need_comma = true;
1442 }
1443 }
2182 fn writeFnType(
2183 self: *Writer,
2184 stream: anytype,
2185 inst: Inst.Index,
2186 var_args: bool,
2187 ) !void {
2188 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2189 const src = inst_data.src();
2190 const extra = self.code.extraData(Inst.FnType, inst_data.payload_index);
2191 const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
2192 return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, .none, src);
2193 }
14442194
1445 try stream.writeByte(')');
2195 fn writeFnTypeCc(
2196 self: *Writer,
2197 stream: anytype,
2198 inst: Inst.Index,
2199 var_args: bool,
2200 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
2201 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2202 const src = inst_data.src();
2203 const extra = self.code.extraData(Inst.FnTypeCc, inst_data.payload_index);
2204 const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
2205 const cc = extra.data.cc;
2206 return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, cc, src);
14462207 }
14472208
1448 fn writeParamToStream(self: *Writer, stream: anytype, param_ptr: anytype) !void {
1449 const param = param_ptr.*;
1450 if (@typeInfo(@TypeOf(param)) == .Enum) {
1451 return stream.writeAll(@tagName(param));
1452 }
1453 switch (@TypeOf(param)) {
1454 *Inst => return self.writeInstParamToStream(stream, param),
1455 ?*Inst => return self.writeInstParamToStream(stream, param.?),
1456 []*Inst => {
1457 try stream.writeByte('[');
1458 for (param) |inst, i| {
1459 if (i != 0) {
1460 try stream.writeAll(", ");
1461 }
1462 try self.writeInstParamToStream(stream, inst);
1463 }
1464 try stream.writeByte(']');
1465 },
1466 Body => {
1467 try stream.writeAll("{\n");
1468 if (self.module.body_metadata.get(param_ptr)) |metadata| {
1469 if (metadata.deaths.len > 0) {
1470 try stream.writeByteNTimes(' ', self.indent);
1471 try stream.writeAll("; deaths={");
1472 for (metadata.deaths) |death, i| {
1473 if (i != 0) try stream.writeAll(", ");
1474 try self.writeInstParamToStream(stream, death);
1475 }
1476 try stream.writeAll("}\n");
1477 }
1478 }
2209 fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2210 const inst_data = self.code.instructions.items(.data)[inst].bool_br;
2211 const extra = self.code.extraData(Inst.Block, inst_data.payload_index);
2212 const body = self.code.extra[extra.end..][0..extra.data.body_len];
2213 try self.writeInstRef(stream, inst_data.lhs);
2214 try stream.writeAll(", {\n");
2215 self.indent += 2;
2216 try self.writeBody(stream, body);
2217 self.indent -= 2;
2218 try stream.writeByteNTimes(' ', self.indent);
2219 try stream.writeAll("})");
2220 }
14792221
1480 for (param.instructions) |inst| {
1481 const my_i = self.next_instr_index;
1482 self.next_instr_index += 1;
1483 try self.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = undefined });
1484 try stream.writeByteNTimes(' ', self.indent);
1485 try stream.print("%{d} ", .{my_i});
1486 if (inst.cast(Inst.Block)) |block| {
1487 const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{d}", .{my_i});
1488 try self.block_table.put(block, name);
1489 } else if (inst.cast(Inst.Loop)) |loop| {
1490 const name = try std.fmt.allocPrint(&self.arena.allocator, "loop_{d}", .{my_i});
1491 try self.loop_table.put(loop, name);
1492 }
1493 self.indent += 2;
1494 try self.writeInstToStream(stream, inst);
1495 if (self.module.metadata.get(inst)) |metadata| {
1496 try stream.print(" ; deaths=0b{b}", .{metadata.deaths});
1497 // This is conditionally compiled in because addresses mess up the tests due
1498 // to Address Space Layout Randomization. It's super useful when debugging
1499 // codegen.zig though.
1500 if (!std.builtin.is_test) {
1501 try stream.print(" 0x{x}", .{metadata.addr});
1502 }
1503 }
1504 self.indent -= 2;
1505 try stream.writeByte('\n');
1506 }
1507 try stream.writeByteNTimes(' ', self.indent - 2);
1508 try stream.writeByte('}');
1509 },
1510 bool => return stream.writeByte("01"[@boolToInt(param)]),
1511 []u8, []const u8 => return stream.print("\"{}\"", .{std.zig.fmtEscapes(param)}),
1512 BigIntConst, usize => return stream.print("{}", .{param}),
1513 TypedValue => return stream.print("TypedValue{{ .ty = {}, .val = {}}}", .{ param.ty, param.val }),
1514 *IrModule.Decl => return stream.print("Decl({s})", .{param.name}),
1515 *Inst.Block => {
1516 const name = self.block_table.get(param) orelse "!BADREF!";
1517 return stream.print("\"{}\"", .{std.zig.fmtEscapes(name)});
1518 },
1519 *Inst.Loop => {
1520 const name = self.loop_table.get(param).?;
1521 return stream.print("\"{}\"", .{std.zig.fmtEscapes(name)});
1522 },
1523 [][]const u8, []const []const u8 => {
1524 try stream.writeByte('[');
1525 for (param) |str, i| {
1526 if (i != 0) {
1527 try stream.writeAll(", ");
1528 }
1529 try stream.print("\"{}\"", .{std.zig.fmtEscapes(str)});
1530 }
1531 try stream.writeByte(']');
1532 },
1533 []Inst.SwitchBr.Case => {
1534 if (param.len == 0) {
1535 return stream.writeAll("{}");
1536 }
1537 try stream.writeAll("{\n");
1538 for (param) |*case, i| {
1539 if (i != 0) {
1540 try stream.writeAll(",\n");
1541 }
1542 try stream.writeByteNTimes(' ', self.indent);
1543 self.indent += 2;
1544 try self.writeParamToStream(stream, &case.item);
1545 try stream.writeAll(" => ");
1546 try self.writeParamToStream(stream, &case.body);
1547 self.indent -= 2;
1548 }
1549 try stream.writeByte('\n');
1550 try stream.writeByteNTimes(' ', self.indent - 2);
1551 try stream.writeByte('}');
1552 },
1553 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
1554 }
2222 fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2223 const int_type = self.code.instructions.items(.data)[inst].int_type;
2224 const prefix: u8 = switch (int_type.signedness) {
2225 .signed => 'i',
2226 .unsigned => 'u',
2227 };
2228 try stream.print("{c}{d}) ", .{ prefix, int_type.bit_count });
2229 try self.writeSrc(stream, int_type.src());
15552230 }
15562231
1557 fn writeInstParamToStream(self: *Writer, stream: anytype, inst: *Inst) !void {
1558 if (self.inst_table.get(inst)) |info| {
1559 if (info.index) |i| {
1560 try stream.print("%{d}", .{info.index});
1561 } else {
1562 try stream.print("@{s}", .{info.name});
1563 }
1564 } else if (inst.cast(Inst.DeclVal)) |decl_val| {
1565 try stream.print("@{s}", .{decl_val.positionals.decl.name});
1566 } else {
1567 // This should be unreachable in theory, but since ZIR is used for debugging the compiler
1568 // we output some debug text instead.
1569 try stream.print("?{s}?", .{@tagName(inst.tag)});
1570 }
2232 fn writeBreak(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2233 const inst_data = self.code.instructions.items(.data)[inst].@"break";
2234
2235 try self.writeInstIndex(stream, inst_data.block_inst);
2236 try stream.writeAll(", ");
2237 try self.writeInstRef(stream, inst_data.operand);
2238 try stream.writeAll(")");
15712239 }
1572};
15732240
1574/// For debugging purposes, prints a function representation to stderr.
1575pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
1576 const allocator = old_module.gpa;
1577 var ctx: DumpTzir = .{
1578 .allocator = allocator,
1579 .arena = std.heap.ArenaAllocator.init(allocator),
1580 .old_module = &old_module,
1581 .module_fn = module_fn,
1582 .indent = 2,
1583 .inst_table = DumpTzir.InstTable.init(allocator),
1584 .partial_inst_table = DumpTzir.InstTable.init(allocator),
1585 .const_table = DumpTzir.InstTable.init(allocator),
1586 };
1587 defer ctx.inst_table.deinit();
1588 defer ctx.partial_inst_table.deinit();
1589 defer ctx.const_table.deinit();
1590 defer ctx.arena.deinit();
1591
1592 switch (module_fn.state) {
1593 .queued => std.debug.print("(queued)", .{}),
1594 .inline_only => std.debug.print("(inline_only)", .{}),
1595 .in_progress => std.debug.print("(in_progress)", .{}),
1596 .sema_failure => std.debug.print("(sema_failure)", .{}),
1597 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
1598 .success => {
1599 const writer = std.io.getStdErr().writer();
1600 ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR");
1601 },
2241 fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2242 const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";
2243 const safety_str = if (inst_data.safety) "safe" else "unsafe";
2244 try stream.print("{s}) ", .{safety_str});
2245 try self.writeSrc(stream, inst_data.src());
16022246 }
1603}
16042247
1605const DumpTzir = struct {
1606 allocator: *Allocator,
1607 arena: std.heap.ArenaAllocator,
1608 old_module: *const IrModule,
1609 module_fn: *IrModule.Fn,
1610 indent: usize,
1611 inst_table: InstTable,
1612 partial_inst_table: InstTable,
1613 const_table: InstTable,
1614 next_index: usize = 0,
1615 next_partial_index: usize = 0,
1616 next_const_index: usize = 0,
1617
1618 const InstTable = std.AutoArrayHashMap(*ir.Inst, usize);
1619
1620 /// TODO: Improve this code to include a stack of ir.Body and store the instructions
1621 /// in there. Now we are putting all the instructions in a function local table,
1622 /// however instructions that are in a Body can be thown away when the Body ends.
1623 fn dump(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void {
1624 // First pass to pre-populate the table so that we can show even invalid references.
1625 // Must iterate the same order we iterate the second time.
1626 // We also look for constants and put them in the const_table.
1627 try dtz.fetchInstsAndResolveConsts(body);
1628
1629 std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
1630
1631 for (dtz.const_table.items()) |entry| {
1632 const constant = entry.key.castTag(.constant).?;
1633 try writer.print(" @{d}: {} = {};\n", .{
1634 entry.value, constant.base.ty, constant.val,
1635 });
2248 fn writeFnTypeCommon(
2249 self: *Writer,
2250 stream: anytype,
2251 param_types: []const Inst.Ref,
2252 ret_ty: Inst.Ref,
2253 var_args: bool,
2254 cc: Inst.Ref,
2255 src: LazySrcLoc,
2256 ) !void {
2257 try stream.writeAll("[");
2258 for (param_types) |param_type, i| {
2259 if (i != 0) try stream.writeAll(", ");
2260 try self.writeInstRef(stream, param_type);
16362261 }
1637
1638 return dtz.dumpBody(body, writer);
2262 try stream.writeAll("], ");
2263 try self.writeInstRef(stream, ret_ty);
2264 try self.writeOptionalInstRef(stream, ", cc=", cc);
2265 try self.writeFlag(stream, ", var_args", var_args);
2266 try stream.writeAll(") ");
2267 try self.writeSrc(stream, src);
16392268 }
16402269
1641 fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: ir.Body) error{OutOfMemory}!void {
1642 for (body.instructions) |inst| {
1643 try dtz.inst_table.put(inst, dtz.next_index);
1644 dtz.next_index += 1;
1645 switch (inst.tag) {
1646 .alloc,
1647 .retvoid,
1648 .unreach,
1649 .breakpoint,
1650 .dbg_stmt,
1651 .arg,
1652 => {},
1653
1654 .ref,
1655 .ret,
1656 .bitcast,
1657 .not,
1658 .is_non_null,
1659 .is_non_null_ptr,
1660 .is_null,
1661 .is_null_ptr,
1662 .is_err,
1663 .is_err_ptr,
1664 .ptrtoint,
1665 .floatcast,
1666 .intcast,
1667 .load,
1668 .optional_payload,
1669 .optional_payload_ptr,
1670 .wrap_optional,
1671 .wrap_errunion_payload,
1672 .wrap_errunion_err,
1673 .unwrap_errunion_payload,
1674 .unwrap_errunion_err,
1675 .unwrap_errunion_payload_ptr,
1676 .unwrap_errunion_err_ptr,
1677 => {
1678 const un_op = inst.cast(ir.Inst.UnOp).?;
1679 try dtz.findConst(un_op.operand);
1680 },
2270 fn writeSmallStr(
2271 self: *Writer,
2272 stream: anytype,
2273 inst: Inst.Index,
2274 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
2275 const str = self.code.instructions.items(.data)[inst].small_str.get();
2276 try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
2277 }
16812278
1682 .add,
1683 .addwrap,
1684 .sub,
1685 .subwrap,
1686 .mul,
1687 .mulwrap,
1688 .cmp_lt,
1689 .cmp_lte,
1690 .cmp_eq,
1691 .cmp_gte,
1692 .cmp_gt,
1693 .cmp_neq,
1694 .store,
1695 .bool_and,
1696 .bool_or,
1697 .bit_and,
1698 .bit_or,
1699 .xor,
1700 => {
1701 const bin_op = inst.cast(ir.Inst.BinOp).?;
1702 try dtz.findConst(bin_op.lhs);
1703 try dtz.findConst(bin_op.rhs);
1704 },
1705
1706 .br => {
1707 const br = inst.castTag(.br).?;
1708 try dtz.findConst(&br.block.base);
1709 try dtz.findConst(br.operand);
1710 },
1711
1712 .br_block_flat => {
1713 const br_block_flat = inst.castTag(.br_block_flat).?;
1714 try dtz.findConst(&br_block_flat.block.base);
1715 try dtz.fetchInstsAndResolveConsts(br_block_flat.body);
1716 },
1717
1718 .br_void => {
1719 const br_void = inst.castTag(.br_void).?;
1720 try dtz.findConst(&br_void.block.base);
1721 },
1722
1723 .block => {
1724 const block = inst.castTag(.block).?;
1725 try dtz.fetchInstsAndResolveConsts(block.body);
1726 },
1727
1728 .condbr => {
1729 const condbr = inst.castTag(.condbr).?;
1730 try dtz.findConst(condbr.condition);
1731 try dtz.fetchInstsAndResolveConsts(condbr.then_body);
1732 try dtz.fetchInstsAndResolveConsts(condbr.else_body);
1733 },
1734
1735 .loop => {
1736 const loop = inst.castTag(.loop).?;
1737 try dtz.fetchInstsAndResolveConsts(loop.body);
1738 },
1739 .call => {
1740 const call = inst.castTag(.call).?;
1741 try dtz.findConst(call.func);
1742 for (call.args) |arg| {
1743 try dtz.findConst(arg);
1744 }
1745 },
1746
1747 // TODO fill out this debug printing
1748 .assembly,
1749 .constant,
1750 .varptr,
1751 .switchbr,
1752 => {},
1753 }
1754 }
2279 fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2280 const inst_data = self.code.instructions.items(.data)[inst].switch_capture;
2281 try self.writeInstIndex(stream, inst_data.switch_inst);
2282 try stream.print(", {d})", .{inst_data.prong_index});
17552283 }
17562284
1757 fn dumpBody(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
1758 for (body.instructions) |inst| {
1759 const my_index = dtz.next_partial_index;
1760 try dtz.partial_inst_table.put(inst, my_index);
1761 dtz.next_partial_index += 1;
1762
1763 try writer.writeByteNTimes(' ', dtz.indent);
1764 try writer.print("%{d}: {} = {s}(", .{
1765 my_index, inst.ty, @tagName(inst.tag),
1766 });
1767 switch (inst.tag) {
1768 .alloc,
1769 .retvoid,
1770 .unreach,
1771 .breakpoint,
1772 .dbg_stmt,
1773 => try writer.writeAll(")\n"),
2285 fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void {
2286 var i: usize = @enumToInt(ref);
17742287
1775 .ref,
1776 .ret,
1777 .bitcast,
1778 .not,
1779 .is_non_null,
1780 .is_null,
1781 .is_non_null_ptr,
1782 .is_null_ptr,
1783 .is_err,
1784 .is_err_ptr,
1785 .ptrtoint,
1786 .floatcast,
1787 .intcast,
1788 .load,
1789 .optional_payload,
1790 .optional_payload_ptr,
1791 .wrap_optional,
1792 .wrap_errunion_err,
1793 .wrap_errunion_payload,
1794 .unwrap_errunion_err,
1795 .unwrap_errunion_payload,
1796 .unwrap_errunion_payload_ptr,
1797 .unwrap_errunion_err_ptr,
1798 => {
1799 const un_op = inst.cast(ir.Inst.UnOp).?;
1800 const kinky = try dtz.writeInst(writer, un_op.operand);
1801 if (kinky != null) {
1802 try writer.writeAll(") // Instruction does not dominate all uses!\n");
1803 } else {
1804 try writer.writeAll(")\n");
1805 }
1806 },
2288 if (i < Inst.Ref.typed_value_map.len) {
2289 return stream.print("@{}", .{ref});
2290 }
2291 i -= Inst.Ref.typed_value_map.len;
18072292
1808 .add,
1809 .addwrap,
1810 .sub,
1811 .subwrap,
1812 .mul,
1813 .mulwrap,
1814 .cmp_lt,
1815 .cmp_lte,
1816 .cmp_eq,
1817 .cmp_gte,
1818 .cmp_gt,
1819 .cmp_neq,
1820 .store,
1821 .bool_and,
1822 .bool_or,
1823 .bit_and,
1824 .bit_or,
1825 .xor,
1826 => {
1827 const bin_op = inst.cast(ir.Inst.BinOp).?;
1828
1829 const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs);
1830 try writer.writeAll(", ");
1831 const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs);
1832
1833 if (lhs_kinky != null or rhs_kinky != null) {
1834 try writer.writeAll(") // Instruction does not dominate all uses!");
1835 if (lhs_kinky) |lhs| {
1836 try writer.print(" %{d}", .{lhs});
1837 }
1838 if (rhs_kinky) |rhs| {
1839 try writer.print(" %{d}", .{rhs});
1840 }
1841 try writer.writeAll("\n");
1842 } else {
1843 try writer.writeAll(")\n");
1844 }
1845 },
1846
1847 .arg => {
1848 const arg = inst.castTag(.arg).?;
1849 try writer.print("{s})\n", .{arg.name});
1850 },
1851
1852 .br => {
1853 const br = inst.castTag(.br).?;
1854
1855 const lhs_kinky = try dtz.writeInst(writer, &br.block.base);
1856 try writer.writeAll(", ");
1857 const rhs_kinky = try dtz.writeInst(writer, br.operand);
1858
1859 if (lhs_kinky != null or rhs_kinky != null) {
1860 try writer.writeAll(") // Instruction does not dominate all uses!");
1861 if (lhs_kinky) |lhs| {
1862 try writer.print(" %{d}", .{lhs});
1863 }
1864 if (rhs_kinky) |rhs| {
1865 try writer.print(" %{d}", .{rhs});
1866 }
1867 try writer.writeAll("\n");
1868 } else {
1869 try writer.writeAll(")\n");
1870 }
1871 },
1872
1873 .br_block_flat => {
1874 const br_block_flat = inst.castTag(.br_block_flat).?;
1875 const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base);
1876 if (block_kinky != null) {
1877 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1878 } else {
1879 try writer.writeAll(", {\n");
1880 }
1881
1882 const old_indent = dtz.indent;
1883 dtz.indent += 2;
1884 try dtz.dumpBody(br_block_flat.body, writer);
1885 dtz.indent = old_indent;
1886
1887 try writer.writeByteNTimes(' ', dtz.indent);
1888 try writer.writeAll("})\n");
1889 },
1890
1891 .br_void => {
1892 const br_void = inst.castTag(.br_void).?;
1893 const kinky = try dtz.writeInst(writer, &br_void.block.base);
1894 if (kinky) |_| {
1895 try writer.writeAll(") // Instruction does not dominate all uses!\n");
1896 } else {
1897 try writer.writeAll(")\n");
1898 }
1899 },
1900
1901 .block => {
1902 const block = inst.castTag(.block).?;
1903
1904 try writer.writeAll("{\n");
1905
1906 const old_indent = dtz.indent;
1907 dtz.indent += 2;
1908 try dtz.dumpBody(block.body, writer);
1909 dtz.indent = old_indent;
1910
1911 try writer.writeByteNTimes(' ', dtz.indent);
1912 try writer.writeAll("})\n");
1913 },
1914
1915 .condbr => {
1916 const condbr = inst.castTag(.condbr).?;
1917
1918 const condition_kinky = try dtz.writeInst(writer, condbr.condition);
1919 if (condition_kinky != null) {
1920 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1921 } else {
1922 try writer.writeAll(", {\n");
1923 }
1924
1925 const old_indent = dtz.indent;
1926 dtz.indent += 2;
1927 try dtz.dumpBody(condbr.then_body, writer);
1928
1929 try writer.writeByteNTimes(' ', old_indent);
1930 try writer.writeAll("}, {\n");
1931
1932 try dtz.dumpBody(condbr.else_body, writer);
1933 dtz.indent = old_indent;
1934
1935 try writer.writeByteNTimes(' ', old_indent);
1936 try writer.writeAll("})\n");
1937 },
1938
1939 .loop => {
1940 const loop = inst.castTag(.loop).?;
1941
1942 try writer.writeAll("{\n");
1943
1944 const old_indent = dtz.indent;
1945 dtz.indent += 2;
1946 try dtz.dumpBody(loop.body, writer);
1947 dtz.indent = old_indent;
1948
1949 try writer.writeByteNTimes(' ', dtz.indent);
1950 try writer.writeAll("})\n");
1951 },
1952
1953 .call => {
1954 const call = inst.castTag(.call).?;
1955
1956 const args_kinky = try dtz.allocator.alloc(?usize, call.args.len);
1957 defer dtz.allocator.free(args_kinky);
1958 std.mem.set(?usize, args_kinky, null);
1959 var any_kinky_args = false;
1960
1961 const func_kinky = try dtz.writeInst(writer, call.func);
1962
1963 for (call.args) |arg, i| {
1964 try writer.writeAll(", ");
1965
1966 args_kinky[i] = try dtz.writeInst(writer, arg);
1967 any_kinky_args = any_kinky_args or args_kinky[i] != null;
1968 }
1969
1970 if (func_kinky != null or any_kinky_args) {
1971 try writer.writeAll(") // Instruction does not dominate all uses!");
1972 if (func_kinky) |func_index| {
1973 try writer.print(" %{d}", .{func_index});
1974 }
1975 for (args_kinky) |arg_kinky| {
1976 if (arg_kinky) |arg_index| {
1977 try writer.print(" %{d}", .{arg_index});
1978 }
1979 }
1980 try writer.writeAll("\n");
1981 } else {
1982 try writer.writeAll(")\n");
1983 }
1984 },
1985
1986 // TODO fill out this debug printing
1987 .assembly,
1988 .constant,
1989 .varptr,
1990 .switchbr,
1991 => {
1992 try writer.writeAll("!TODO!)\n");
1993 },
1994 }
2293 if (i < self.param_count) {
2294 return stream.print("${d}", .{i});
19952295 }
2296 i -= self.param_count;
2297
2298 return self.writeInstIndex(stream, @intCast(Inst.Index, i));
19962299 }
19972300
1998 fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *ir.Inst) !?usize {
1999 if (dtz.partial_inst_table.get(inst)) |operand_index| {
2000 try writer.print("%{d}", .{operand_index});
2001 return null;
2002 } else if (dtz.const_table.get(inst)) |operand_index| {
2003 try writer.print("@{d}", .{operand_index});
2004 return null;
2005 } else if (dtz.inst_table.get(inst)) |operand_index| {
2006 try writer.print("%{d}", .{operand_index});
2007 return operand_index;
2008 } else {
2009 try writer.writeAll("!BADREF!");
2010 return null;
2011 }
2301 fn writeInstIndex(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2302 return stream.print("%{d}", .{inst});
20122303 }
20132304
2014 fn findConst(dtz: *DumpTzir, operand: *ir.Inst) !void {
2015 if (operand.tag == .constant) {
2016 try dtz.const_table.put(operand, dtz.next_const_index);
2017 dtz.next_const_index += 1;
2018 }
2305 fn writeOptionalInstRef(
2306 self: *Writer,
2307 stream: anytype,
2308 prefix: []const u8,
2309 inst: Inst.Ref,
2310 ) !void {
2311 if (inst == .none) return;
2312 try stream.writeAll(prefix);
2313 try self.writeInstRef(stream, inst);
20192314 }
2020};
20212315
2022/// For debugging purposes, like dumpFn but for unanalyzed zir blocks
2023pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8, instructions: []*Inst) !void {
2024 var fib = std.heap.FixedBufferAllocator.init(&[_]u8{});
2025 var module = Module{
2026 .decls = &[_]*Module.Decl{},
2027 .arena = std.heap.ArenaAllocator.init(&fib.allocator),
2028 .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(&fib.allocator),
2029 .body_metadata = std.AutoHashMap(*Body, Module.BodyMetaData).init(&fib.allocator),
2030 };
2031 var write = Writer{
2032 .module = &module,
2033 .inst_table = InstPtrTable.init(allocator),
2034 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),
2035 .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator),
2036 .arena = std.heap.ArenaAllocator.init(allocator),
2037 .indent = 4,
2038 .next_instr_index = 0,
2039 };
2040 defer write.arena.deinit();
2041 defer write.inst_table.deinit();
2042 defer write.block_table.deinit();
2043 defer write.loop_table.deinit();
2044
2045 try write.inst_table.ensureCapacity(@intCast(u32, instructions.len));
2046
2047 const stderr = std.io.getStdErr().writer();
2048 try stderr.print("{s} {s} {{ // unanalyzed\n", .{ kind, decl_name });
2049
2050 for (instructions) |inst| {
2051 const my_i = write.next_instr_index;
2052 write.next_instr_index += 1;
2053
2054 if (inst.cast(Inst.Block)) |block| {
2055 const name = try std.fmt.allocPrint(&write.arena.allocator, "label_{d}", .{my_i});
2056 try write.block_table.put(block, name);
2057 } else if (inst.cast(Inst.Loop)) |loop| {
2058 const name = try std.fmt.allocPrint(&write.arena.allocator, "loop_{d}", .{my_i});
2059 try write.loop_table.put(loop, name);
2060 }
2316 fn writeFlag(
2317 self: *Writer,
2318 stream: anytype,
2319 name: []const u8,
2320 flag: bool,
2321 ) !void {
2322 if (!flag) return;
2323 try stream.writeAll(name);
2324 }
20612325
2062 try write.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = "inst" });
2063 try stderr.print(" %{d} ", .{my_i});
2064 try write.writeInstToStream(stderr, inst);
2065 try stderr.writeByte('\n');
2326 fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void {
2327 const tree = self.scope.tree();
2328 const src_loc = src.toSrcLoc(self.scope);
2329 const abs_byte_off = try src_loc.byteOffset();
2330 const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off);
2331 try stream.print("{s}:{d}:{d}", .{
2332 @tagName(src), delta_line.line + 1, delta_line.column + 1,
2333 });
20662334 }
20672335
2068 try stderr.print("}} // {s} {s}\n\n", .{ kind, decl_name });
2069}
2336 fn writeBody(self: *Writer, stream: anytype, body: []const Inst.Index) !void {
2337 for (body) |inst| {
2338 try stream.writeByteNTimes(' ', self.indent);
2339 try stream.print("%{d} ", .{inst});
2340 try self.writeInstToStream(stream, inst);
2341 try stream.writeByte('\n');
2342 }
2343 }
2344};
src/zir_sema.zig deleted-2597
......@@ -1,2597 +0,0 @@
1//! Semantic analysis of ZIR instructions.
2//! This file operates on a `Module` instance, transforming untyped ZIR
3//! instructions into semantically-analyzed IR instructions. It does type
4//! checking, comptime control flow, and safety-check generation. This is the
5//! the heart of the Zig compiler.
6//! When deciding if something goes into this file or into Module, here is a
7//! guiding principle: if it has to do with (untyped) ZIR instructions, it goes
8//! here. If the analysis operates on typed IR instructions, it goes in Module.
9
10const std = @import("std");
11const mem = std.mem;
12const Allocator = std.mem.Allocator;
13const assert = std.debug.assert;
14const log = std.log.scoped(.sema);
15
16const Value = @import("value.zig").Value;
17const Type = @import("type.zig").Type;
18const TypedValue = @import("TypedValue.zig");
19const ir = @import("ir.zig");
20const zir = @import("zir.zig");
21const Module = @import("Module.zig");
22const Inst = ir.Inst;
23const Body = ir.Body;
24const trace = @import("tracy.zig").trace;
25const Scope = Module.Scope;
26const InnerError = Module.InnerError;
27const Decl = Module.Decl;
28
29pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
30 switch (old_inst.tag) {
31 .alloc => return zirAlloc(mod, scope, old_inst.castTag(.alloc).?),
32 .alloc_mut => return zirAllocMut(mod, scope, old_inst.castTag(.alloc_mut).?),
33 .alloc_inferred => return zirAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred).?, .inferred_alloc_const),
34 .alloc_inferred_mut => return zirAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred_mut).?, .inferred_alloc_mut),
35 .arg => return zirArg(mod, scope, old_inst.castTag(.arg).?),
36 .bitcast_ref => return zirBitcastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
37 .bitcast_result_ptr => return zirBitcastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
38 .block => return zirBlock(mod, scope, old_inst.castTag(.block).?, false),
39 .block_comptime => return zirBlock(mod, scope, old_inst.castTag(.block_comptime).?, true),
40 .block_flat => return zirBlockFlat(mod, scope, old_inst.castTag(.block_flat).?, false),
41 .block_comptime_flat => return zirBlockFlat(mod, scope, old_inst.castTag(.block_comptime_flat).?, true),
42 .@"break" => return zirBreak(mod, scope, old_inst.castTag(.@"break").?),
43 .breakpoint => return zirBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?),
44 .break_void => return zirBreakVoid(mod, scope, old_inst.castTag(.break_void).?),
45 .call => return zirCall(mod, scope, old_inst.castTag(.call).?),
46 .coerce_result_ptr => return zirCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?),
47 .compile_error => return zirCompileError(mod, scope, old_inst.castTag(.compile_error).?),
48 .compile_log => return zirCompileLog(mod, scope, old_inst.castTag(.compile_log).?),
49 .@"const" => return zirConst(mod, scope, old_inst.castTag(.@"const").?),
50 .dbg_stmt => return zirDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?),
51 .decl_ref => return zirDeclRef(mod, scope, old_inst.castTag(.decl_ref).?),
52 .decl_ref_str => return zirDeclRefStr(mod, scope, old_inst.castTag(.decl_ref_str).?),
53 .decl_val => return zirDeclVal(mod, scope, old_inst.castTag(.decl_val).?),
54 .ensure_result_used => return zirEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?),
55 .ensure_result_non_error => return zirEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?),
56 .indexable_ptr_len => return zirIndexablePtrLen(mod, scope, old_inst.castTag(.indexable_ptr_len).?),
57 .ref => return zirRef(mod, scope, old_inst.castTag(.ref).?),
58 .resolve_inferred_alloc => return zirResolveInferredAlloc(mod, scope, old_inst.castTag(.resolve_inferred_alloc).?),
59 .ret_ptr => return zirRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
60 .ret_type => return zirRetType(mod, scope, old_inst.castTag(.ret_type).?),
61 .store_to_block_ptr => return zirStoreToBlockPtr(mod, scope, old_inst.castTag(.store_to_block_ptr).?),
62 .store_to_inferred_ptr => return zirStoreToInferredPtr(mod, scope, old_inst.castTag(.store_to_inferred_ptr).?),
63 .single_const_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?, false, .One),
64 .single_mut_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?, true, .One),
65 .many_const_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.many_const_ptr_type).?, false, .Many),
66 .many_mut_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.many_mut_ptr_type).?, true, .Many),
67 .c_const_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.c_const_ptr_type).?, false, .C),
68 .c_mut_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.c_mut_ptr_type).?, true, .C),
69 .const_slice_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.const_slice_type).?, false, .Slice),
70 .mut_slice_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.mut_slice_type).?, true, .Slice),
71 .ptr_type => return zirPtrType(mod, scope, old_inst.castTag(.ptr_type).?),
72 .store => return zirStore(mod, scope, old_inst.castTag(.store).?),
73 .set_eval_branch_quota => return zirSetEvalBranchQuota(mod, scope, old_inst.castTag(.set_eval_branch_quota).?),
74 .str => return zirStr(mod, scope, old_inst.castTag(.str).?),
75 .int => return zirInt(mod, scope, old_inst.castTag(.int).?),
76 .int_type => return zirIntType(mod, scope, old_inst.castTag(.int_type).?),
77 .loop => return zirLoop(mod, scope, old_inst.castTag(.loop).?),
78 .param_type => return zirParamType(mod, scope, old_inst.castTag(.param_type).?),
79 .ptrtoint => return zirPtrtoint(mod, scope, old_inst.castTag(.ptrtoint).?),
80 .field_ptr => return zirFieldPtr(mod, scope, old_inst.castTag(.field_ptr).?),
81 .field_val => return zirFieldVal(mod, scope, old_inst.castTag(.field_val).?),
82 .field_ptr_named => return zirFieldPtrNamed(mod, scope, old_inst.castTag(.field_ptr_named).?),
83 .field_val_named => return zirFieldValNamed(mod, scope, old_inst.castTag(.field_val_named).?),
84 .deref => return zirDeref(mod, scope, old_inst.castTag(.deref).?),
85 .as => return zirAs(mod, scope, old_inst.castTag(.as).?),
86 .@"asm" => return zirAsm(mod, scope, old_inst.castTag(.@"asm").?),
87 .unreachable_safe => return zirUnreachable(mod, scope, old_inst.castTag(.unreachable_safe).?, true),
88 .unreachable_unsafe => return zirUnreachable(mod, scope, old_inst.castTag(.unreachable_unsafe).?, false),
89 .@"return" => return zirReturn(mod, scope, old_inst.castTag(.@"return").?),
90 .return_void => return zirReturnVoid(mod, scope, old_inst.castTag(.return_void).?),
91 .@"fn" => return zirFn(mod, scope, old_inst.castTag(.@"fn").?),
92 .@"export" => return zirExport(mod, scope, old_inst.castTag(.@"export").?),
93 .primitive => return zirPrimitive(mod, scope, old_inst.castTag(.primitive).?),
94 .fn_type => return zirFnType(mod, scope, old_inst.castTag(.fn_type).?, false),
95 .fn_type_cc => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc).?, false),
96 .fn_type_var_args => return zirFnType(mod, scope, old_inst.castTag(.fn_type_var_args).?, true),
97 .fn_type_cc_var_args => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc_var_args).?, true),
98 .intcast => return zirIntcast(mod, scope, old_inst.castTag(.intcast).?),
99 .bitcast => return zirBitcast(mod, scope, old_inst.castTag(.bitcast).?),
100 .floatcast => return zirFloatcast(mod, scope, old_inst.castTag(.floatcast).?),
101 .elem_ptr => return zirElemPtr(mod, scope, old_inst.castTag(.elem_ptr).?),
102 .elem_val => return zirElemVal(mod, scope, old_inst.castTag(.elem_val).?),
103 .add => return zirArithmetic(mod, scope, old_inst.castTag(.add).?),
104 .addwrap => return zirArithmetic(mod, scope, old_inst.castTag(.addwrap).?),
105 .sub => return zirArithmetic(mod, scope, old_inst.castTag(.sub).?),
106 .subwrap => return zirArithmetic(mod, scope, old_inst.castTag(.subwrap).?),
107 .mul => return zirArithmetic(mod, scope, old_inst.castTag(.mul).?),
108 .mulwrap => return zirArithmetic(mod, scope, old_inst.castTag(.mulwrap).?),
109 .div => return zirArithmetic(mod, scope, old_inst.castTag(.div).?),
110 .mod_rem => return zirArithmetic(mod, scope, old_inst.castTag(.mod_rem).?),
111 .array_cat => return zirArrayCat(mod, scope, old_inst.castTag(.array_cat).?),
112 .array_mul => return zirArrayMul(mod, scope, old_inst.castTag(.array_mul).?),
113 .bit_and => return zirBitwise(mod, scope, old_inst.castTag(.bit_and).?),
114 .bit_not => return zirBitNot(mod, scope, old_inst.castTag(.bit_not).?),
115 .bit_or => return zirBitwise(mod, scope, old_inst.castTag(.bit_or).?),
116 .xor => return zirBitwise(mod, scope, old_inst.castTag(.xor).?),
117 .shl => return zirShl(mod, scope, old_inst.castTag(.shl).?),
118 .shr => return zirShr(mod, scope, old_inst.castTag(.shr).?),
119 .cmp_lt => return zirCmp(mod, scope, old_inst.castTag(.cmp_lt).?, .lt),
120 .cmp_lte => return zirCmp(mod, scope, old_inst.castTag(.cmp_lte).?, .lte),
121 .cmp_eq => return zirCmp(mod, scope, old_inst.castTag(.cmp_eq).?, .eq),
122 .cmp_gte => return zirCmp(mod, scope, old_inst.castTag(.cmp_gte).?, .gte),
123 .cmp_gt => return zirCmp(mod, scope, old_inst.castTag(.cmp_gt).?, .gt),
124 .cmp_neq => return zirCmp(mod, scope, old_inst.castTag(.cmp_neq).?, .neq),
125 .condbr => return zirCondbr(mod, scope, old_inst.castTag(.condbr).?),
126 .is_null => return zirIsNull(mod, scope, old_inst.castTag(.is_null).?, false),
127 .is_non_null => return zirIsNull(mod, scope, old_inst.castTag(.is_non_null).?, true),
128 .is_null_ptr => return zirIsNullPtr(mod, scope, old_inst.castTag(.is_null_ptr).?, false),
129 .is_non_null_ptr => return zirIsNullPtr(mod, scope, old_inst.castTag(.is_non_null_ptr).?, true),
130 .is_err => return zirIsErr(mod, scope, old_inst.castTag(.is_err).?),
131 .is_err_ptr => return zirIsErrPtr(mod, scope, old_inst.castTag(.is_err_ptr).?),
132 .bool_not => return zirBoolNot(mod, scope, old_inst.castTag(.bool_not).?),
133 .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?),
134 .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?),
135 .optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
136 .optional_type_from_ptr_elem => return zirOptionalTypeFromPtrElem(mod, scope, old_inst.castTag(.optional_type_from_ptr_elem).?),
137 .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true),
138 .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false),
139 .optional_payload_safe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true),
140 .optional_payload_unsafe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_unsafe_ptr).?, false),
141 .err_union_payload_safe => return zirErrUnionPayload(mod, scope, old_inst.castTag(.err_union_payload_safe).?, true),
142 .err_union_payload_unsafe => return zirErrUnionPayload(mod, scope, old_inst.castTag(.err_union_payload_unsafe).?, false),
143 .err_union_payload_safe_ptr => return zirErrUnionPayloadPtr(mod, scope, old_inst.castTag(.err_union_payload_safe_ptr).?, true),
144 .err_union_payload_unsafe_ptr => return zirErrUnionPayloadPtr(mod, scope, old_inst.castTag(.err_union_payload_unsafe_ptr).?, false),
145 .err_union_code => return zirErrUnionCode(mod, scope, old_inst.castTag(.err_union_code).?),
146 .err_union_code_ptr => return zirErrUnionCodePtr(mod, scope, old_inst.castTag(.err_union_code_ptr).?),
147 .ensure_err_payload_void => return zirEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?),
148 .array_type => return zirArrayType(mod, scope, old_inst.castTag(.array_type).?),
149 .array_type_sentinel => return zirArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?),
150 .enum_literal => return zirEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?),
151 .merge_error_sets => return zirMergeErrorSets(mod, scope, old_inst.castTag(.merge_error_sets).?),
152 .error_union_type => return zirErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?),
153 .anyframe_type => return zirAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?),
154 .error_set => return zirErrorSet(mod, scope, old_inst.castTag(.error_set).?),
155 .error_value => return zirErrorValue(mod, scope, old_inst.castTag(.error_value).?),
156 .slice => return zirSlice(mod, scope, old_inst.castTag(.slice).?),
157 .slice_start => return zirSliceStart(mod, scope, old_inst.castTag(.slice_start).?),
158 .import => return zirImport(mod, scope, old_inst.castTag(.import).?),
159 .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?),
160 .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?),
161 .void_value => return mod.constVoid(scope, old_inst.src),
162 .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?, false),
163 .switchbr_ref => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr_ref).?, true),
164 .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),
165 .@"await" => return zirAwait(mod, scope, old_inst.castTag(.@"await").?),
166 .nosuspend_await => return zirAwait(mod, scope, old_inst.castTag(.nosuspend_await).?),
167 .@"resume" => return zirResume(mod, scope, old_inst.castTag(.@"resume").?),
168 .@"suspend" => return zirSuspend(mod, scope, old_inst.castTag(.@"suspend").?),
169 .suspend_block => return zirSuspendBlock(mod, scope, old_inst.castTag(.suspend_block).?),
170
171 .container_field_named,
172 .container_field_typed,
173 .container_field,
174 .enum_type,
175 .union_type,
176 .struct_type,
177 => return mod.fail(scope, old_inst.src, "TODO analyze container instructions", .{}),
178 }
179}
180
181pub fn analyzeBody(mod: *Module, block: *Scope.Block, body: zir.Body) !void {
182 const tracy = trace(@src());
183 defer tracy.end();
184
185 for (body.instructions) |src_inst| {
186 const analyzed_inst = try analyzeInst(mod, &block.base, src_inst);
187 try block.inst_table.putNoClobber(src_inst, analyzed_inst);
188 if (analyzed_inst.ty.zigTypeTag() == .NoReturn) {
189 break;
190 }
191 }
192}
193
194pub fn analyzeBodyValueAsType(
195 mod: *Module,
196 block_scope: *Scope.Block,
197 zir_result_inst: *zir.Inst,
198 body: zir.Body,
199) !Type {
200 try analyzeBody(mod, block_scope, body);
201 const result_inst = block_scope.inst_table.get(zir_result_inst).?;
202 const val = try mod.resolveConstValue(&block_scope.base, result_inst);
203 return val.toType(block_scope.base.arena());
204}
205
206pub fn resolveInst(mod: *Module, scope: *Scope, zir_inst: *zir.Inst) InnerError!*Inst {
207 const block = scope.cast(Scope.Block).?;
208 return block.inst_table.get(zir_inst).?; // Instruction does not dominate all uses!
209}
210
211fn resolveConstString(mod: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 {
212 const new_inst = try resolveInst(mod, scope, old_inst);
213 const wanted_type = Type.initTag(.const_slice_u8);
214 const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
215 const val = try mod.resolveConstValue(scope, coerced_inst);
216 return val.toAllocatedBytes(scope.arena());
217}
218
219fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
220 const new_inst = try resolveInst(mod, scope, old_inst);
221 const wanted_type = Type.initTag(.@"type");
222 const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
223 const val = try mod.resolveConstValue(scope, coerced_inst);
224 return val.toType(scope.arena());
225}
226
227/// Appropriate to call when the coercion has already been done by result
228/// location semantics. Asserts the value fits in the provided `Int` type.
229/// Only supports `Int` types 64 bits or less.
230fn resolveAlreadyCoercedInt(
231 mod: *Module,
232 scope: *Scope,
233 old_inst: *zir.Inst,
234 comptime Int: type,
235) !Int {
236 comptime assert(@typeInfo(Int).Int.bits <= 64);
237 const new_inst = try resolveInst(mod, scope, old_inst);
238 const val = try mod.resolveConstValue(scope, new_inst);
239 switch (@typeInfo(Int).Int.signedness) {
240 .signed => return @intCast(Int, val.toSignedInt()),
241 .unsigned => return @intCast(Int, val.toUnsignedInt()),
242 }
243}
244
245fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 {
246 const new_inst = try resolveInst(mod, scope, old_inst);
247 const coerced = try mod.coerce(scope, dest_type, new_inst);
248 const val = try mod.resolveConstValue(scope, coerced);
249
250 return val.toUnsignedInt();
251}
252
253pub fn resolveInstConst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
254 const new_inst = try resolveInst(mod, scope, old_inst);
255 const val = try mod.resolveConstValue(scope, new_inst);
256 return TypedValue{
257 .ty = new_inst.ty,
258 .val = val,
259 };
260}
261
262fn zirConst(mod: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst {
263 const tracy = trace(@src());
264 defer tracy.end();
265 // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
266 // after analysis.
267 const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena());
268 return mod.constInst(scope, const_inst.base.src, typed_value_copy);
269}
270
271fn analyzeConstInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
272 const new_inst = try analyzeInst(mod, scope, old_inst);
273 return TypedValue{
274 .ty = new_inst.ty,
275 .val = try mod.resolveConstValue(scope, new_inst),
276 };
277}
278
279fn zirBitcastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
280 const tracy = trace(@src());
281 defer tracy.end();
282 return mod.fail(scope, inst.base.src, "TODO implement zir_sema.zirBitcastRef", .{});
283}
284
285fn zirBitcastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
286 const tracy = trace(@src());
287 defer tracy.end();
288 return mod.fail(scope, inst.base.src, "TODO implement zir_sema.zirBitcastResultPtr", .{});
289}
290
291fn zirCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
292 const tracy = trace(@src());
293 defer tracy.end();
294 return mod.fail(scope, inst.base.src, "TODO implement zirCoerceResultPtr", .{});
295}
296
297fn zirRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
298 const tracy = trace(@src());
299 defer tracy.end();
300 const b = try mod.requireFunctionBlock(scope, inst.base.src);
301 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
302 const ret_type = fn_ty.fnReturnType();
303 const ptr_type = try mod.simplePtrType(scope, inst.base.src, ret_type, true, .One);
304 return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
305}
306
307fn zirRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
308 const tracy = trace(@src());
309 defer tracy.end();
310
311 const operand = try resolveInst(mod, scope, inst.positionals.operand);
312 return mod.analyzeRef(scope, inst.base.src, operand);
313}
314
315fn zirRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
316 const tracy = trace(@src());
317 defer tracy.end();
318 const b = try mod.requireFunctionBlock(scope, inst.base.src);
319 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
320 const ret_type = fn_ty.fnReturnType();
321 return mod.constType(scope, inst.base.src, ret_type);
322}
323
324fn zirEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
325 const tracy = trace(@src());
326 defer tracy.end();
327 const operand = try resolveInst(mod, scope, inst.positionals.operand);
328 switch (operand.ty.zigTypeTag()) {
329 .Void, .NoReturn => return mod.constVoid(scope, operand.src),
330 else => return mod.fail(scope, operand.src, "expression value is ignored", .{}),
331 }
332}
333
334fn zirEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
335 const tracy = trace(@src());
336 defer tracy.end();
337 const operand = try resolveInst(mod, scope, inst.positionals.operand);
338 switch (operand.ty.zigTypeTag()) {
339 .ErrorSet, .ErrorUnion => return mod.fail(scope, operand.src, "error is discarded", .{}),
340 else => return mod.constVoid(scope, operand.src),
341 }
342}
343
344fn zirIndexablePtrLen(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
345 const tracy = trace(@src());
346 defer tracy.end();
347
348 const array_ptr = try resolveInst(mod, scope, inst.positionals.operand);
349 const elem_ty = array_ptr.ty.elemType();
350 if (!elem_ty.isIndexable()) {
351 const msg = msg: {
352 const msg = try mod.errMsg(
353 scope,
354 inst.base.src,
355 "type '{}' does not support indexing",
356 .{elem_ty},
357 );
358 errdefer msg.destroy(mod.gpa);
359 try mod.errNote(
360 scope,
361 inst.base.src,
362 msg,
363 "for loop operand must be an array, slice, tuple, or vector",
364 .{},
365 );
366 break :msg msg;
367 };
368 return mod.failWithOwnedErrorMsg(scope, msg);
369 }
370 const result_ptr = try mod.namedFieldPtr(scope, inst.base.src, array_ptr, "len", inst.base.src);
371 return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src);
372}
373
374fn zirAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
375 const tracy = trace(@src());
376 defer tracy.end();
377 const var_type = try resolveType(mod, scope, inst.positionals.operand);
378 const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One);
379 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
380 return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
381}
382
383fn zirAllocMut(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
384 const tracy = trace(@src());
385 defer tracy.end();
386 const var_type = try resolveType(mod, scope, inst.positionals.operand);
387 try mod.validateVarType(scope, inst.base.src, var_type);
388 const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One);
389 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
390 return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
391}
392
393fn zirAllocInferred(
394 mod: *Module,
395 scope: *Scope,
396 inst: *zir.Inst.NoOp,
397 mut_tag: Type.Tag,
398) InnerError!*Inst {
399 const tracy = trace(@src());
400 defer tracy.end();
401 const val_payload = try scope.arena().create(Value.Payload.InferredAlloc);
402 val_payload.* = .{
403 .data = .{},
404 };
405 // `Module.constInst` does not add the instruction to the block because it is
406 // not needed in the case of constant values. However here, we plan to "downgrade"
407 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
408 // to the block even though it is currently a `.constant`.
409 const result = try mod.constInst(scope, inst.base.src, .{
410 .ty = switch (mut_tag) {
411 .inferred_alloc_const => Type.initTag(.inferred_alloc_const),
412 .inferred_alloc_mut => Type.initTag(.inferred_alloc_mut),
413 else => unreachable,
414 },
415 .val = Value.initPayload(&val_payload.base),
416 });
417 const block = try mod.requireFunctionBlock(scope, inst.base.src);
418 try block.instructions.append(mod.gpa, result);
419 return result;
420}
421
422fn zirResolveInferredAlloc(
423 mod: *Module,
424 scope: *Scope,
425 inst: *zir.Inst.UnOp,
426) InnerError!*Inst {
427 const tracy = trace(@src());
428 defer tracy.end();
429 const ptr = try resolveInst(mod, scope, inst.positionals.operand);
430 const ptr_val = ptr.castTag(.constant).?.val;
431 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
432 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
433 const final_elem_ty = try mod.resolvePeerTypes(scope, peer_inst_list);
434 const var_is_mut = switch (ptr.ty.tag()) {
435 .inferred_alloc_const => false,
436 .inferred_alloc_mut => true,
437 else => unreachable,
438 };
439 if (var_is_mut) {
440 try mod.validateVarType(scope, inst.base.src, final_elem_ty);
441 }
442 const final_ptr_ty = try mod.simplePtrType(scope, inst.base.src, final_elem_ty, true, .One);
443
444 // Change it to a normal alloc.
445 ptr.ty = final_ptr_ty;
446 ptr.tag = .alloc;
447
448 return mod.constVoid(scope, inst.base.src);
449}
450
451fn zirStoreToBlockPtr(
452 mod: *Module,
453 scope: *Scope,
454 inst: *zir.Inst.BinOp,
455) InnerError!*Inst {
456 const tracy = trace(@src());
457 defer tracy.end();
458
459 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
460 const value = try resolveInst(mod, scope, inst.positionals.rhs);
461 const ptr_ty = try mod.simplePtrType(scope, inst.base.src, value.ty, true, .One);
462 // TODO detect when this store should be done at compile-time. For example,
463 // if expressions should force it when the condition is compile-time known.
464 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
465 const bitcasted_ptr = try mod.addUnOp(b, inst.base.src, ptr_ty, .bitcast, ptr);
466 return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value);
467}
468
469fn zirStoreToInferredPtr(
470 mod: *Module,
471 scope: *Scope,
472 inst: *zir.Inst.BinOp,
473) InnerError!*Inst {
474 const tracy = trace(@src());
475 defer tracy.end();
476
477 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
478 const value = try resolveInst(mod, scope, inst.positionals.rhs);
479 const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?;
480 // Add the stored instruction to the set we will use to resolve peer types
481 // for the inferred allocation.
482 try inferred_alloc.data.stored_inst_list.append(scope.arena(), value);
483 // Create a runtime bitcast instruction with exactly the type the pointer wants.
484 const ptr_ty = try mod.simplePtrType(scope, inst.base.src, value.ty, true, .One);
485 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
486 const bitcasted_ptr = try mod.addUnOp(b, inst.base.src, ptr_ty, .bitcast, ptr);
487 return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value);
488}
489
490fn zirSetEvalBranchQuota(
491 mod: *Module,
492 scope: *Scope,
493 inst: *zir.Inst.UnOp,
494) InnerError!*Inst {
495 const b = try mod.requireFunctionBlock(scope, inst.base.src);
496 const quota = try resolveAlreadyCoercedInt(mod, scope, inst.positionals.operand, u32);
497 if (b.branch_quota.* < quota)
498 b.branch_quota.* = quota;
499 return mod.constVoid(scope, inst.base.src);
500}
501
502fn zirStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
503 const tracy = trace(@src());
504 defer tracy.end();
505
506 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
507 const value = try resolveInst(mod, scope, inst.positionals.rhs);
508 return mod.storePtr(scope, inst.base.src, ptr, value);
509}
510
511fn zirParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {
512 const tracy = trace(@src());
513 defer tracy.end();
514 const fn_inst = try resolveInst(mod, scope, inst.positionals.func);
515 const arg_index = inst.positionals.arg_index;
516
517 const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) {
518 .Fn => fn_inst.ty,
519 .BoundFn => {
520 return mod.fail(scope, fn_inst.src, "TODO implement zirParamType for method call syntax", .{});
521 },
522 else => {
523 return mod.fail(scope, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty});
524 },
525 };
526
527 const param_count = fn_ty.fnParamLen();
528 if (arg_index >= param_count) {
529 if (fn_ty.fnIsVarArgs()) {
530 return mod.constType(scope, inst.base.src, Type.initTag(.var_args_param));
531 }
532 return mod.fail(scope, inst.base.src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{
533 arg_index,
534 fn_ty,
535 param_count,
536 });
537 }
538
539 // TODO support generic functions
540 const param_type = fn_ty.fnParamType(arg_index);
541 return mod.constType(scope, inst.base.src, param_type);
542}
543
544fn zirStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst {
545 const tracy = trace(@src());
546 defer tracy.end();
547 // The bytes references memory inside the ZIR module, which can get deallocated
548 // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.
549 var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
550 errdefer new_decl_arena.deinit();
551 const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);
552
553 const decl_ty = try Type.Tag.array_u8_sentinel_0.create(&new_decl_arena.allocator, arena_bytes.len);
554 const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, arena_bytes);
555
556 const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
557 .ty = decl_ty,
558 .val = decl_val,
559 });
560 return mod.analyzeDeclRef(scope, str_inst.base.src, new_decl);
561}
562
563fn zirInt(mod: *Module, scope: *Scope, inst: *zir.Inst.Int) InnerError!*Inst {
564 const tracy = trace(@src());
565 defer tracy.end();
566
567 return mod.constIntBig(scope, inst.base.src, Type.initTag(.comptime_int), inst.positionals.int);
568}
569
570fn zirExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst {
571 const tracy = trace(@src());
572 defer tracy.end();
573 const symbol_name = try resolveConstString(mod, scope, export_inst.positionals.symbol_name);
574 const exported_decl = mod.lookupDeclName(scope, export_inst.positionals.decl_name) orelse
575 return mod.fail(scope, export_inst.base.src, "decl '{s}' not found", .{export_inst.positionals.decl_name});
576 try mod.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl);
577 return mod.constVoid(scope, export_inst.base.src);
578}
579
580fn zirCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
581 const tracy = trace(@src());
582 defer tracy.end();
583 const msg = try resolveConstString(mod, scope, inst.positionals.operand);
584 return mod.fail(scope, inst.base.src, "{s}", .{msg});
585}
586
587fn zirCompileLog(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileLog) InnerError!*Inst {
588 var managed = mod.compile_log_text.toManaged(mod.gpa);
589 defer mod.compile_log_text = managed.moveToUnmanaged();
590 const writer = managed.writer();
591
592 for (inst.positionals.to_log) |arg_inst, i| {
593 if (i != 0) try writer.print(", ", .{});
594
595 const arg = try resolveInst(mod, scope, arg_inst);
596 if (arg.value()) |val| {
597 try writer.print("@as({}, {})", .{ arg.ty, val });
598 } else {
599 try writer.print("@as({}, [runtime value])", .{arg.ty});
600 }
601 }
602 try writer.print("\n", .{});
603
604 const gop = try mod.compile_log_decls.getOrPut(mod.gpa, scope.ownerDecl().?);
605 if (!gop.found_existing) {
606 gop.entry.value = .{
607 .file_scope = scope.getFileScope(),
608 .byte_offset = inst.base.src,
609 };
610 }
611 return mod.constVoid(scope, inst.base.src);
612}
613
614fn zirArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
615 const tracy = trace(@src());
616 defer tracy.end();
617 const b = try mod.requireFunctionBlock(scope, inst.base.src);
618 if (b.inlining) |inlining| {
619 const param_index = inlining.param_index;
620 inlining.param_index += 1;
621 return inlining.casted_args[param_index];
622 }
623 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
624 const param_index = b.instructions.items.len;
625 const param_count = fn_ty.fnParamLen();
626 if (param_index >= param_count) {
627 return mod.fail(scope, inst.base.src, "parameter index {d} outside list of length {d}", .{
628 param_index,
629 param_count,
630 });
631 }
632 const param_type = fn_ty.fnParamType(param_index);
633 const name = try scope.arena().dupeZ(u8, inst.positionals.name);
634 return mod.addArg(b, inst.base.src, param_type, name);
635}
636
637fn zirLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError!*Inst {
638 const tracy = trace(@src());
639 defer tracy.end();
640 const parent_block = scope.cast(Scope.Block).?;
641
642 // Reserve space for a Loop instruction so that generated Break instructions can
643 // point to it, even if it doesn't end up getting used because the code ends up being
644 // comptime evaluated.
645 const loop_inst = try parent_block.arena.create(Inst.Loop);
646 loop_inst.* = .{
647 .base = .{
648 .tag = Inst.Loop.base_tag,
649 .ty = Type.initTag(.noreturn),
650 .src = inst.base.src,
651 },
652 .body = undefined,
653 };
654
655 var child_block: Scope.Block = .{
656 .parent = parent_block,
657 .inst_table = parent_block.inst_table,
658 .func = parent_block.func,
659 .owner_decl = parent_block.owner_decl,
660 .src_decl = parent_block.src_decl,
661 .instructions = .{},
662 .arena = parent_block.arena,
663 .inlining = parent_block.inlining,
664 .is_comptime = parent_block.is_comptime,
665 .branch_quota = parent_block.branch_quota,
666 };
667 defer child_block.instructions.deinit(mod.gpa);
668
669 try analyzeBody(mod, &child_block, inst.positionals.body);
670
671 // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
672
673 try parent_block.instructions.append(mod.gpa, &loop_inst.base);
674 loop_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
675 return &loop_inst.base;
676}
677
678fn zirBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
679 const tracy = trace(@src());
680 defer tracy.end();
681 const parent_block = scope.cast(Scope.Block).?;
682
683 var child_block = parent_block.makeSubBlock();
684 defer child_block.instructions.deinit(mod.gpa);
685 child_block.is_comptime = child_block.is_comptime or is_comptime;
686
687 try analyzeBody(mod, &child_block, inst.positionals.body);
688
689 // Move the analyzed instructions into the parent block arena.
690 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
691 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
692
693 // The result of a flat block is the last instruction.
694 const zir_inst_list = inst.positionals.body.instructions;
695 const last_zir_inst = zir_inst_list[zir_inst_list.len - 1];
696 return resolveInst(mod, scope, last_zir_inst);
697}
698
699fn zirBlock(
700 mod: *Module,
701 scope: *Scope,
702 inst: *zir.Inst.Block,
703 is_comptime: bool,
704) InnerError!*Inst {
705 const tracy = trace(@src());
706 defer tracy.end();
707
708 const parent_block = scope.cast(Scope.Block).?;
709
710 // Reserve space for a Block instruction so that generated Break instructions can
711 // point to it, even if it doesn't end up getting used because the code ends up being
712 // comptime evaluated.
713 const block_inst = try parent_block.arena.create(Inst.Block);
714 block_inst.* = .{
715 .base = .{
716 .tag = Inst.Block.base_tag,
717 .ty = undefined, // Set after analysis.
718 .src = inst.base.src,
719 },
720 .body = undefined,
721 };
722
723 var child_block: Scope.Block = .{
724 .parent = parent_block,
725 .inst_table = parent_block.inst_table,
726 .func = parent_block.func,
727 .owner_decl = parent_block.owner_decl,
728 .src_decl = parent_block.src_decl,
729 .instructions = .{},
730 .arena = parent_block.arena,
731 // TODO @as here is working around a stage1 miscompilation bug :(
732 .label = @as(?Scope.Block.Label, Scope.Block.Label{
733 .zir_block = inst,
734 .merges = .{
735 .results = .{},
736 .br_list = .{},
737 .block_inst = block_inst,
738 },
739 }),
740 .inlining = parent_block.inlining,
741 .is_comptime = is_comptime or parent_block.is_comptime,
742 .branch_quota = parent_block.branch_quota,
743 };
744 const merges = &child_block.label.?.merges;
745
746 defer child_block.instructions.deinit(mod.gpa);
747 defer merges.results.deinit(mod.gpa);
748 defer merges.br_list.deinit(mod.gpa);
749
750 try analyzeBody(mod, &child_block, inst.positionals.body);
751
752 return analyzeBlockBody(mod, scope, &child_block, merges);
753}
754
755fn analyzeBlockBody(
756 mod: *Module,
757 scope: *Scope,
758 child_block: *Scope.Block,
759 merges: *Scope.Block.Merges,
760) InnerError!*Inst {
761 const tracy = trace(@src());
762 defer tracy.end();
763
764 const parent_block = scope.cast(Scope.Block).?;
765
766 // Blocks must terminate with noreturn instruction.
767 assert(child_block.instructions.items.len != 0);
768 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
769
770 if (merges.results.items.len == 0) {
771 // No need for a block instruction. We can put the new instructions
772 // directly into the parent block.
773 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
774 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
775 return copied_instructions[copied_instructions.len - 1];
776 }
777 if (merges.results.items.len == 1) {
778 const last_inst_index = child_block.instructions.items.len - 1;
779 const last_inst = child_block.instructions.items[last_inst_index];
780 if (last_inst.breakBlock()) |br_block| {
781 if (br_block == merges.block_inst) {
782 // No need for a block instruction. We can put the new instructions directly
783 // into the parent block. Here we omit the break instruction.
784 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
785 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
786 return merges.results.items[0];
787 }
788 }
789 }
790 // It is impossible to have the number of results be > 1 in a comptime scope.
791 assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition.
792
793 // Need to set the type and emit the Block instruction. This allows machine code generation
794 // to emit a jump instruction to after the block when it encounters the break.
795 try parent_block.instructions.append(mod.gpa, &merges.block_inst.base);
796 const resolved_ty = try mod.resolvePeerTypes(scope, merges.results.items);
797 merges.block_inst.base.ty = resolved_ty;
798 merges.block_inst.body = .{
799 .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items),
800 };
801 // Now that the block has its type resolved, we need to go back into all the break
802 // instructions, and insert type coercion on the operands.
803 for (merges.br_list.items) |br| {
804 if (br.operand.ty.eql(resolved_ty)) {
805 // No type coercion needed.
806 continue;
807 }
808 var coerce_block = parent_block.makeSubBlock();
809 defer coerce_block.instructions.deinit(mod.gpa);
810 const coerced_operand = try mod.coerce(&coerce_block.base, resolved_ty, br.operand);
811 // If no instructions were produced, such as in the case of a coercion of a
812 // constant value to a new type, we can simply point the br operand to it.
813 if (coerce_block.instructions.items.len == 0) {
814 br.operand = coerced_operand;
815 continue;
816 }
817 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);
818 // Here we depend on the br instruction having been over-allocated (if necessary)
819 // inide analyzeBreak so that it can be converted into a br_block_flat instruction.
820 const br_src = br.base.src;
821 const br_ty = br.base.ty;
822 const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);
823 br_block_flat.* = .{
824 .base = .{
825 .src = br_src,
826 .ty = br_ty,
827 .tag = .br_block_flat,
828 },
829 .block = merges.block_inst,
830 .body = .{
831 .instructions = try parent_block.arena.dupe(*Inst, coerce_block.instructions.items),
832 },
833 };
834 }
835 return &merges.block_inst.base;
836}
837
838fn zirBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
839 const tracy = trace(@src());
840 defer tracy.end();
841 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
842 return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint);
843}
844
845fn zirBreak(mod: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
846 const tracy = trace(@src());
847 defer tracy.end();
848
849 const operand = try resolveInst(mod, scope, inst.positionals.operand);
850 const block = inst.positionals.block;
851 return analyzeBreak(mod, scope, inst.base.src, block, operand);
852}
853
854fn zirBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst {
855 const tracy = trace(@src());
856 defer tracy.end();
857
858 const block = inst.positionals.block;
859 const void_inst = try mod.constVoid(scope, inst.base.src);
860 return analyzeBreak(mod, scope, inst.base.src, block, void_inst);
861}
862
863fn analyzeBreak(
864 mod: *Module,
865 scope: *Scope,
866 src: usize,
867 zir_block: *zir.Inst.Block,
868 operand: *Inst,
869) InnerError!*Inst {
870 var opt_block = scope.cast(Scope.Block);
871 while (opt_block) |block| {
872 if (block.label) |*label| {
873 if (label.zir_block == zir_block) {
874 const b = try mod.requireFunctionBlock(scope, src);
875 // Here we add a br instruction, but we over-allocate a little bit
876 // (if necessary) to make it possible to convert the instruction into
877 // a br_block_flat instruction later.
878 const br = @ptrCast(*Inst.Br, try b.arena.alignedAlloc(
879 u8,
880 Inst.convertable_br_align,
881 Inst.convertable_br_size,
882 ));
883 br.* = .{
884 .base = .{
885 .tag = .br,
886 .ty = Type.initTag(.noreturn),
887 .src = src,
888 },
889 .operand = operand,
890 .block = label.merges.block_inst,
891 };
892 try b.instructions.append(mod.gpa, &br.base);
893 try label.merges.results.append(mod.gpa, operand);
894 try label.merges.br_list.append(mod.gpa, br);
895 return &br.base;
896 }
897 }
898 opt_block = block.parent;
899 } else unreachable;
900}
901
902fn zirDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
903 const tracy = trace(@src());
904 defer tracy.end();
905 if (scope.cast(Scope.Block)) |b| {
906 if (!b.is_comptime) {
907 return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .dbg_stmt);
908 }
909 }
910 return mod.constVoid(scope, inst.base.src);
911}
912
913fn zirDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {
914 const tracy = trace(@src());
915 defer tracy.end();
916 const decl_name = try resolveConstString(mod, scope, inst.positionals.name);
917 return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name);
918}
919
920fn zirDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
921 const tracy = trace(@src());
922 defer tracy.end();
923 return mod.analyzeDeclRef(scope, inst.base.src, inst.positionals.decl);
924}
925
926fn zirDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst {
927 const tracy = trace(@src());
928 defer tracy.end();
929 return mod.analyzeDeclVal(scope, inst.base.src, inst.positionals.decl);
930}
931
932fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
933 const tracy = trace(@src());
934 defer tracy.end();
935
936 const func = try resolveInst(mod, scope, inst.positionals.func);
937 if (func.ty.zigTypeTag() != .Fn)
938 return mod.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty});
939
940 const cc = func.ty.fnCallingConvention();
941 if (cc == .Naked) {
942 // TODO add error note: declared here
943 return mod.fail(
944 scope,
945 inst.positionals.func.src,
946 "unable to call function with naked calling convention",
947 .{},
948 );
949 }
950 const call_params_len = inst.positionals.args.len;
951 const fn_params_len = func.ty.fnParamLen();
952 if (func.ty.fnIsVarArgs()) {
953 assert(cc == .C);
954 if (call_params_len < fn_params_len) {
955 // TODO add error note: declared here
956 return mod.fail(
957 scope,
958 inst.positionals.func.src,
959 "expected at least {d} argument(s), found {d}",
960 .{ fn_params_len, call_params_len },
961 );
962 }
963 } else if (fn_params_len != call_params_len) {
964 // TODO add error note: declared here
965 return mod.fail(
966 scope,
967 inst.positionals.func.src,
968 "expected {d} argument(s), found {d}",
969 .{ fn_params_len, call_params_len },
970 );
971 }
972
973 if (inst.positionals.modifier == .compile_time) {
974 return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
975 }
976 if (inst.positionals.modifier != .auto) {
977 return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.positionals.modifier});
978 }
979
980 // TODO handle function calls of generic functions
981 const casted_args = try scope.arena().alloc(*Inst, call_params_len);
982 for (inst.positionals.args) |src_arg, i| {
983 // the args are already casted to the result of a param type instruction.
984 casted_args[i] = try resolveInst(mod, scope, src_arg);
985 }
986
987 const ret_type = func.ty.fnReturnType();
988
989 const b = try mod.requireFunctionBlock(scope, inst.base.src);
990 const is_comptime_call = b.is_comptime or inst.positionals.modifier == .compile_time;
991 const is_inline_call = is_comptime_call or inst.positionals.modifier == .always_inline or
992 func.ty.fnCallingConvention() == .Inline;
993 if (is_inline_call) {
994 const func_val = try mod.resolveConstValue(scope, func);
995 const module_fn = switch (func_val.tag()) {
996 .function => func_val.castTag(.function).?.data,
997 .extern_fn => return mod.fail(scope, inst.base.src, "{s} call of extern function", .{
998 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
999 }),
1000 else => unreachable,
1001 };
1002
1003 // Analyze the ZIR. The same ZIR gets analyzed into a runtime function
1004 // or an inlined call depending on what union tag the `label` field is
1005 // set to in the `Scope.Block`.
1006 // This block instruction will be used to capture the return value from the
1007 // inlined function.
1008 const block_inst = try scope.arena().create(Inst.Block);
1009 block_inst.* = .{
1010 .base = .{
1011 .tag = Inst.Block.base_tag,
1012 .ty = ret_type,
1013 .src = inst.base.src,
1014 },
1015 .body = undefined,
1016 };
1017 // If this is the top of the inline/comptime call stack, we use this data.
1018 // Otherwise we pass on the shared data from the parent scope.
1019 var shared_inlining = Scope.Block.Inlining.Shared{
1020 .branch_count = 0,
1021 .caller = b.func,
1022 };
1023 // This one is shared among sub-blocks within the same callee, but not
1024 // shared among the entire inline/comptime call stack.
1025 var inlining = Scope.Block.Inlining{
1026 .shared = if (b.inlining) |inlining| inlining.shared else &shared_inlining,
1027 .param_index = 0,
1028 .casted_args = casted_args,
1029 .merges = .{
1030 .results = .{},
1031 .br_list = .{},
1032 .block_inst = block_inst,
1033 },
1034 };
1035 var inst_table = Scope.Block.InstTable.init(mod.gpa);
1036 defer inst_table.deinit();
1037
1038 var child_block: Scope.Block = .{
1039 .parent = null,
1040 .inst_table = &inst_table,
1041 .func = module_fn,
1042 .owner_decl = scope.ownerDecl().?,
1043 .src_decl = module_fn.owner_decl,
1044 .instructions = .{},
1045 .arena = scope.arena(),
1046 .label = null,
1047 .inlining = &inlining,
1048 .is_comptime = is_comptime_call,
1049 .branch_quota = b.branch_quota,
1050 };
1051
1052 const merges = &child_block.inlining.?.merges;
1053
1054 defer child_block.instructions.deinit(mod.gpa);
1055 defer merges.results.deinit(mod.gpa);
1056 defer merges.br_list.deinit(mod.gpa);
1057
1058 try mod.emitBackwardBranch(&child_block, inst.base.src);
1059
1060 // This will have return instructions analyzed as break instructions to
1061 // the block_inst above.
1062 try analyzeBody(mod, &child_block, module_fn.zir);
1063
1064 return analyzeBlockBody(mod, scope, &child_block, merges);
1065 }
1066
1067 return mod.addCall(b, inst.base.src, ret_type, func, casted_args);
1068}
1069
1070fn zirFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
1071 const tracy = trace(@src());
1072 defer tracy.end();
1073 const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type);
1074 const new_func = try scope.arena().create(Module.Fn);
1075 new_func.* = .{
1076 .state = if (fn_type.fnCallingConvention() == .Inline) .inline_only else .queued,
1077 .zir = fn_inst.positionals.body,
1078 .body = undefined,
1079 .owner_decl = scope.ownerDecl().?,
1080 };
1081 return mod.constInst(scope, fn_inst.base.src, .{
1082 .ty = fn_type,
1083 .val = try Value.Tag.function.create(scope.arena(), new_func),
1084 });
1085}
1086
1087fn zirAwait(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1088 return mod.fail(scope, inst.base.src, "TODO implement await", .{});
1089}
1090
1091fn zirResume(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1092 return mod.fail(scope, inst.base.src, "TODO implement resume", .{});
1093}
1094
1095fn zirSuspend(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
1096 return mod.fail(scope, inst.base.src, "TODO implement suspend", .{});
1097}
1098
1099fn zirSuspendBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
1100 return mod.fail(scope, inst.base.src, "TODO implement suspend", .{});
1101}
1102
1103fn zirIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst {
1104 const tracy = trace(@src());
1105 defer tracy.end();
1106 return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});
1107}
1108
1109fn zirOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst {
1110 const tracy = trace(@src());
1111 defer tracy.end();
1112 const child_type = try resolveType(mod, scope, optional.positionals.operand);
1113
1114 return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));
1115}
1116
1117fn zirOptionalTypeFromPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1118 const tracy = trace(@src());
1119 defer tracy.end();
1120
1121 const ptr = try resolveInst(mod, scope, inst.positionals.operand);
1122 const elem_ty = ptr.ty.elemType();
1123
1124 return mod.constType(scope, inst.base.src, try mod.optionalType(scope, elem_ty));
1125}
1126
1127fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {
1128 const tracy = trace(@src());
1129 defer tracy.end();
1130 // TODO these should be lazily evaluated
1131 const len = try resolveInstConst(mod, scope, array.positionals.lhs);
1132 const elem_type = try resolveType(mod, scope, array.positionals.rhs);
1133
1134 return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), null, elem_type));
1135}
1136
1137fn zirArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.ArrayTypeSentinel) InnerError!*Inst {
1138 const tracy = trace(@src());
1139 defer tracy.end();
1140 // TODO these should be lazily evaluated
1141 const len = try resolveInstConst(mod, scope, array.positionals.len);
1142 const sentinel = try resolveInstConst(mod, scope, array.positionals.sentinel);
1143 const elem_type = try resolveType(mod, scope, array.positionals.elem_type);
1144
1145 return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type));
1146}
1147
1148fn zirErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1149 const tracy = trace(@src());
1150 defer tracy.end();
1151 const error_union = try resolveType(mod, scope, inst.positionals.lhs);
1152 const payload = try resolveType(mod, scope, inst.positionals.rhs);
1153
1154 if (error_union.zigTypeTag() != .ErrorSet) {
1155 return mod.fail(scope, inst.base.src, "expected error set type, found {}", .{error_union.elemType()});
1156 }
1157
1158 return mod.constType(scope, inst.base.src, try mod.errorUnionType(scope, error_union, payload));
1159}
1160
1161fn zirAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1162 const tracy = trace(@src());
1163 defer tracy.end();
1164 const return_type = try resolveType(mod, scope, inst.positionals.operand);
1165
1166 return mod.constType(scope, inst.base.src, try mod.anyframeType(scope, return_type));
1167}
1168
1169fn zirErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError!*Inst {
1170 const tracy = trace(@src());
1171 defer tracy.end();
1172 // The declarations arena will store the hashmap.
1173 var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
1174 errdefer new_decl_arena.deinit();
1175
1176 const payload = try new_decl_arena.allocator.create(Value.Payload.ErrorSet);
1177 payload.* = .{
1178 .base = .{ .tag = .error_set },
1179 .data = .{
1180 .fields = .{},
1181 .decl = undefined, // populated below
1182 },
1183 };
1184 try payload.data.fields.ensureCapacity(&new_decl_arena.allocator, @intCast(u32, inst.positionals.fields.len));
1185
1186 for (inst.positionals.fields) |field_name| {
1187 const entry = try mod.getErrorValue(field_name);
1188 if (payload.data.fields.fetchPutAssumeCapacity(entry.key, {})) |_| {
1189 return mod.fail(scope, inst.base.src, "duplicate error: '{s}'", .{field_name});
1190 }
1191 }
1192 // TODO create name in format "error:line:column"
1193 const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
1194 .ty = Type.initTag(.type),
1195 .val = Value.initPayload(&payload.base),
1196 });
1197 payload.data.decl = new_decl;
1198 return mod.analyzeDeclVal(scope, inst.base.src, new_decl);
1199}
1200
1201fn zirErrorValue(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorValue) InnerError!*Inst {
1202 const tracy = trace(@src());
1203 defer tracy.end();
1204
1205 // Create an anonymous error set type with only this error value, and return the value.
1206 const entry = try mod.getErrorValue(inst.positionals.name);
1207 const result_type = try Type.Tag.error_set_single.create(scope.arena(), entry.key);
1208 return mod.constInst(scope, inst.base.src, .{
1209 .ty = result_type,
1210 .val = try Value.Tag.@"error".create(scope.arena(), .{
1211 .name = entry.key,
1212 }),
1213 });
1214}
1215
1216fn zirMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1217 const tracy = trace(@src());
1218 defer tracy.end();
1219
1220 const rhs_ty = try resolveType(mod, scope, inst.positionals.rhs);
1221 const lhs_ty = try resolveType(mod, scope, inst.positionals.lhs);
1222 if (rhs_ty.zigTypeTag() != .ErrorSet)
1223 return mod.fail(scope, inst.positionals.rhs.src, "expected error set type, found {}", .{rhs_ty});
1224 if (lhs_ty.zigTypeTag() != .ErrorSet)
1225 return mod.fail(scope, inst.positionals.lhs.src, "expected error set type, found {}", .{lhs_ty});
1226
1227 // anything merged with anyerror is anyerror
1228 if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror)
1229 return mod.constInst(scope, inst.base.src, .{
1230 .ty = Type.initTag(.type),
1231 .val = Value.initTag(.anyerror_type),
1232 });
1233 // The declarations arena will store the hashmap.
1234 var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
1235 errdefer new_decl_arena.deinit();
1236
1237 const payload = try new_decl_arena.allocator.create(Value.Payload.ErrorSet);
1238 payload.* = .{
1239 .base = .{ .tag = .error_set },
1240 .data = .{
1241 .fields = .{},
1242 .decl = undefined, // populated below
1243 },
1244 };
1245 try payload.data.fields.ensureCapacity(&new_decl_arena.allocator, @intCast(u32, switch (rhs_ty.tag()) {
1246 .error_set_single => 1,
1247 .error_set => rhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields.size,
1248 else => unreachable,
1249 } + switch (lhs_ty.tag()) {
1250 .error_set_single => 1,
1251 .error_set => lhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields.size,
1252 else => unreachable,
1253 }));
1254
1255 switch (lhs_ty.tag()) {
1256 .error_set_single => {
1257 const name = lhs_ty.castTag(.error_set_single).?.data;
1258 payload.data.fields.putAssumeCapacity(name, {});
1259 },
1260 .error_set => {
1261 var multiple = lhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields;
1262 var it = multiple.iterator();
1263 while (it.next()) |entry| {
1264 payload.data.fields.putAssumeCapacity(entry.key, entry.value);
1265 }
1266 },
1267 else => unreachable,
1268 }
1269
1270 switch (rhs_ty.tag()) {
1271 .error_set_single => {
1272 const name = rhs_ty.castTag(.error_set_single).?.data;
1273 payload.data.fields.putAssumeCapacity(name, {});
1274 },
1275 .error_set => {
1276 var multiple = rhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields;
1277 var it = multiple.iterator();
1278 while (it.next()) |entry| {
1279 payload.data.fields.putAssumeCapacity(entry.key, entry.value);
1280 }
1281 },
1282 else => unreachable,
1283 }
1284 // TODO create name in format "error:line:column"
1285 const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
1286 .ty = Type.initTag(.type),
1287 .val = Value.initPayload(&payload.base),
1288 });
1289 payload.data.decl = new_decl;
1290
1291 return mod.analyzeDeclVal(scope, inst.base.src, new_decl);
1292}
1293
1294fn zirEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst {
1295 const tracy = trace(@src());
1296 defer tracy.end();
1297 const duped_name = try scope.arena().dupe(u8, inst.positionals.name);
1298 return mod.constInst(scope, inst.base.src, .{
1299 .ty = Type.initTag(.enum_literal),
1300 .val = try Value.Tag.enum_literal.create(scope.arena(), duped_name),
1301 });
1302}
1303
1304/// Pointer in, pointer out.
1305fn zirOptionalPayloadPtr(
1306 mod: *Module,
1307 scope: *Scope,
1308 unwrap: *zir.Inst.UnOp,
1309 safety_check: bool,
1310) InnerError!*Inst {
1311 const tracy = trace(@src());
1312 defer tracy.end();
1313
1314 const optional_ptr = try resolveInst(mod, scope, unwrap.positionals.operand);
1315 assert(optional_ptr.ty.zigTypeTag() == .Pointer);
1316
1317 const opt_type = optional_ptr.ty.elemType();
1318 if (opt_type.zigTypeTag() != .Optional) {
1319 return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{opt_type});
1320 }
1321
1322 const child_type = try opt_type.optionalChildAlloc(scope.arena());
1323 const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, !optional_ptr.ty.isConstPtr(), .One);
1324
1325 if (optional_ptr.value()) |pointer_val| {
1326 const val = try pointer_val.pointerDeref(scope.arena());
1327 if (val.isNull()) {
1328 return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{});
1329 }
1330 // The same Value represents the pointer to the optional and the payload.
1331 return mod.constInst(scope, unwrap.base.src, .{
1332 .ty = child_pointer,
1333 .val = pointer_val,
1334 });
1335 }
1336
1337 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1338 if (safety_check and mod.wantSafety(scope)) {
1339 const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .is_non_null_ptr, optional_ptr);
1340 try mod.addSafetyCheck(b, is_non_null, .unwrap_null);
1341 }
1342 return mod.addUnOp(b, unwrap.base.src, child_pointer, .optional_payload_ptr, optional_ptr);
1343}
1344
1345/// Value in, value out.
1346fn zirOptionalPayload(
1347 mod: *Module,
1348 scope: *Scope,
1349 unwrap: *zir.Inst.UnOp,
1350 safety_check: bool,
1351) InnerError!*Inst {
1352 const tracy = trace(@src());
1353 defer tracy.end();
1354
1355 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1356 const opt_type = operand.ty;
1357 if (opt_type.zigTypeTag() != .Optional) {
1358 return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{opt_type});
1359 }
1360
1361 const child_type = try opt_type.optionalChildAlloc(scope.arena());
1362
1363 if (operand.value()) |val| {
1364 if (val.isNull()) {
1365 return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{});
1366 }
1367 return mod.constInst(scope, unwrap.base.src, .{
1368 .ty = child_type,
1369 .val = val,
1370 });
1371 }
1372
1373 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1374 if (safety_check and mod.wantSafety(scope)) {
1375 const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .is_non_null, operand);
1376 try mod.addSafetyCheck(b, is_non_null, .unwrap_null);
1377 }
1378 return mod.addUnOp(b, unwrap.base.src, child_type, .optional_payload, operand);
1379}
1380
1381/// Value in, value out
1382fn zirErrUnionPayload(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
1383 const tracy = trace(@src());
1384 defer tracy.end();
1385
1386 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1387 if (operand.ty.zigTypeTag() != .ErrorUnion)
1388 return mod.fail(scope, operand.src, "expected error union type, found '{}'", .{operand.ty});
1389
1390 if (operand.value()) |val| {
1391 if (val.getError()) |name| {
1392 return mod.fail(scope, unwrap.base.src, "caught unexpected error '{s}'", .{name});
1393 }
1394 const data = val.castTag(.error_union).?.data;
1395 return mod.constInst(scope, unwrap.base.src, .{
1396 .ty = operand.ty.castTag(.error_union).?.data.payload,
1397 .val = data,
1398 });
1399 }
1400 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1401 if (safety_check and mod.wantSafety(scope)) {
1402 const is_non_err = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .is_err, operand);
1403 try mod.addSafetyCheck(b, is_non_err, .unwrap_errunion);
1404 }
1405 return mod.addUnOp(b, unwrap.base.src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_payload, operand);
1406}
1407
1408/// Pointer in, pointer out
1409fn zirErrUnionPayloadPtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
1410 const tracy = trace(@src());
1411 defer tracy.end();
1412
1413 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1414 assert(operand.ty.zigTypeTag() == .Pointer);
1415
1416 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
1417 return mod.fail(scope, unwrap.base.src, "expected error union type, found {}", .{operand.ty.elemType()});
1418
1419 const operand_pointer_ty = try mod.simplePtrType(scope, unwrap.base.src, operand.ty.elemType().castTag(.error_union).?.data.payload, !operand.ty.isConstPtr(), .One);
1420
1421 if (operand.value()) |pointer_val| {
1422 const val = try pointer_val.pointerDeref(scope.arena());
1423 if (val.getError()) |name| {
1424 return mod.fail(scope, unwrap.base.src, "caught unexpected error '{s}'", .{name});
1425 }
1426 const data = val.castTag(.error_union).?.data;
1427 // The same Value represents the pointer to the error union and the payload.
1428 return mod.constInst(scope, unwrap.base.src, .{
1429 .ty = operand_pointer_ty,
1430 .val = try Value.Tag.ref_val.create(
1431 scope.arena(),
1432 data,
1433 ),
1434 });
1435 }
1436
1437 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1438 if (safety_check and mod.wantSafety(scope)) {
1439 const is_non_err = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .is_err, operand);
1440 try mod.addSafetyCheck(b, is_non_err, .unwrap_errunion);
1441 }
1442 return mod.addUnOp(b, unwrap.base.src, operand_pointer_ty, .unwrap_errunion_payload_ptr, operand);
1443}
1444
1445/// Value in, value out
1446fn zirErrUnionCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
1447 const tracy = trace(@src());
1448 defer tracy.end();
1449
1450 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1451 if (operand.ty.zigTypeTag() != .ErrorUnion)
1452 return mod.fail(scope, unwrap.base.src, "expected error union type, found '{}'", .{operand.ty});
1453
1454 if (operand.value()) |val| {
1455 assert(val.getError() != null);
1456 const data = val.castTag(.error_union).?.data;
1457 return mod.constInst(scope, unwrap.base.src, .{
1458 .ty = operand.ty.castTag(.error_union).?.data.error_set,
1459 .val = data,
1460 });
1461 }
1462
1463 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1464 return mod.addUnOp(b, unwrap.base.src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand);
1465}
1466
1467/// Pointer in, value out
1468fn zirErrUnionCodePtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
1469 const tracy = trace(@src());
1470 defer tracy.end();
1471
1472 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1473 assert(operand.ty.zigTypeTag() == .Pointer);
1474
1475 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
1476 return mod.fail(scope, unwrap.base.src, "expected error union type, found {}", .{operand.ty.elemType()});
1477
1478 if (operand.value()) |pointer_val| {
1479 const val = try pointer_val.pointerDeref(scope.arena());
1480 assert(val.getError() != null);
1481 const data = val.castTag(.error_union).?.data;
1482 return mod.constInst(scope, unwrap.base.src, .{
1483 .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set,
1484 .val = data,
1485 });
1486 }
1487
1488 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1489 return mod.addUnOp(b, unwrap.base.src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand);
1490}
1491
1492fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
1493 const tracy = trace(@src());
1494 defer tracy.end();
1495
1496 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1497 if (operand.ty.zigTypeTag() != .ErrorUnion)
1498 return mod.fail(scope, unwrap.base.src, "expected error union type, found '{}'", .{operand.ty});
1499 if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) {
1500 return mod.fail(scope, unwrap.base.src, "expression value is ignored", .{});
1501 }
1502 return mod.constVoid(scope, unwrap.base.src);
1503}
1504
1505fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType, var_args: bool) InnerError!*Inst {
1506 const tracy = trace(@src());
1507 defer tracy.end();
1508
1509 return fnTypeCommon(
1510 mod,
1511 scope,
1512 &fntype.base,
1513 fntype.positionals.param_types,
1514 fntype.positionals.return_type,
1515 .Unspecified,
1516 var_args,
1517 );
1518}
1519
1520fn zirFnTypeCc(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnTypeCc, var_args: bool) InnerError!*Inst {
1521 const tracy = trace(@src());
1522 defer tracy.end();
1523
1524 const cc_tv = try resolveInstConst(mod, scope, fntype.positionals.cc);
1525 // TODO once we're capable of importing and analyzing decls from
1526 // std.builtin, this needs to change
1527 const cc_str = cc_tv.val.castTag(.enum_literal).?.data;
1528 const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse
1529 return mod.fail(scope, fntype.positionals.cc.src, "Unknown calling convention {s}", .{cc_str});
1530 return fnTypeCommon(
1531 mod,
1532 scope,
1533 &fntype.base,
1534 fntype.positionals.param_types,
1535 fntype.positionals.return_type,
1536 cc,
1537 var_args,
1538 );
1539}
1540
1541fn fnTypeCommon(
1542 mod: *Module,
1543 scope: *Scope,
1544 zir_inst: *zir.Inst,
1545 zir_param_types: []*zir.Inst,
1546 zir_return_type: *zir.Inst,
1547 cc: std.builtin.CallingConvention,
1548 var_args: bool,
1549) InnerError!*Inst {
1550 const return_type = try resolveType(mod, scope, zir_return_type);
1551
1552 // Hot path for some common function types.
1553 if (zir_param_types.len == 0 and !var_args) {
1554 if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
1555 return mod.constType(scope, zir_inst.src, Type.initTag(.fn_noreturn_no_args));
1556 }
1557
1558 if (return_type.zigTypeTag() == .Void and cc == .Unspecified) {
1559 return mod.constType(scope, zir_inst.src, Type.initTag(.fn_void_no_args));
1560 }
1561
1562 if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
1563 return mod.constType(scope, zir_inst.src, Type.initTag(.fn_naked_noreturn_no_args));
1564 }
1565
1566 if (return_type.zigTypeTag() == .Void and cc == .C) {
1567 return mod.constType(scope, zir_inst.src, Type.initTag(.fn_ccc_void_no_args));
1568 }
1569 }
1570
1571 const arena = scope.arena();
1572 const param_types = try arena.alloc(Type, zir_param_types.len);
1573 for (zir_param_types) |param_type, i| {
1574 const resolved = try resolveType(mod, scope, param_type);
1575 // TODO skip for comptime params
1576 if (!resolved.isValidVarType(false)) {
1577 return mod.fail(scope, param_type.src, "parameter of type '{}' must be declared comptime", .{resolved});
1578 }
1579 param_types[i] = resolved;
1580 }
1581
1582 const fn_ty = try Type.Tag.function.create(arena, .{
1583 .param_types = param_types,
1584 .return_type = return_type,
1585 .cc = cc,
1586 .is_var_args = var_args,
1587 });
1588 return mod.constType(scope, zir_inst.src, fn_ty);
1589}
1590
1591fn zirPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
1592 const tracy = trace(@src());
1593 defer tracy.end();
1594 return mod.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue());
1595}
1596
1597fn zirAs(mod: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst {
1598 const tracy = trace(@src());
1599 defer tracy.end();
1600 const dest_type = try resolveType(mod, scope, as.positionals.lhs);
1601 const new_inst = try resolveInst(mod, scope, as.positionals.rhs);
1602 return mod.coerce(scope, dest_type, new_inst);
1603}
1604
1605fn zirPtrtoint(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst {
1606 const tracy = trace(@src());
1607 defer tracy.end();
1608 const ptr = try resolveInst(mod, scope, ptrtoint.positionals.operand);
1609 if (ptr.ty.zigTypeTag() != .Pointer) {
1610 return mod.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty});
1611 }
1612 // TODO handle known-pointer-address
1613 const b = try mod.requireRuntimeBlock(scope, ptrtoint.base.src);
1614 const ty = Type.initTag(.usize);
1615 return mod.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr);
1616}
1617
1618fn zirFieldVal(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst {
1619 const tracy = trace(@src());
1620 defer tracy.end();
1621
1622 const object = try resolveInst(mod, scope, inst.positionals.object);
1623 const field_name = inst.positionals.field_name;
1624 const object_ptr = try mod.analyzeRef(scope, inst.base.src, object);
1625 const result_ptr = try mod.namedFieldPtr(scope, inst.base.src, object_ptr, field_name, inst.base.src);
1626 return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src);
1627}
1628
1629fn zirFieldPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst {
1630 const tracy = trace(@src());
1631 defer tracy.end();
1632
1633 const object_ptr = try resolveInst(mod, scope, inst.positionals.object);
1634 const field_name = inst.positionals.field_name;
1635 return mod.namedFieldPtr(scope, inst.base.src, object_ptr, field_name, inst.base.src);
1636}
1637
1638fn zirFieldValNamed(mod: *Module, scope: *Scope, inst: *zir.Inst.FieldNamed) InnerError!*Inst {
1639 const tracy = trace(@src());
1640 defer tracy.end();
1641
1642 const object = try resolveInst(mod, scope, inst.positionals.object);
1643 const field_name = try resolveConstString(mod, scope, inst.positionals.field_name);
1644 const fsrc = inst.positionals.field_name.src;
1645 const object_ptr = try mod.analyzeRef(scope, inst.base.src, object);
1646 const result_ptr = try mod.namedFieldPtr(scope, inst.base.src, object_ptr, field_name, fsrc);
1647 return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src);
1648}
1649
1650fn zirFieldPtrNamed(mod: *Module, scope: *Scope, inst: *zir.Inst.FieldNamed) InnerError!*Inst {
1651 const tracy = trace(@src());
1652 defer tracy.end();
1653
1654 const object_ptr = try resolveInst(mod, scope, inst.positionals.object);
1655 const field_name = try resolveConstString(mod, scope, inst.positionals.field_name);
1656 const fsrc = inst.positionals.field_name.src;
1657 return mod.namedFieldPtr(scope, inst.base.src, object_ptr, field_name, fsrc);
1658}
1659
1660fn zirIntcast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1661 const tracy = trace(@src());
1662 defer tracy.end();
1663 const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
1664 const operand = try resolveInst(mod, scope, inst.positionals.rhs);
1665
1666 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
1667 .ComptimeInt => true,
1668 .Int => false,
1669 else => return mod.fail(
1670 scope,
1671 inst.positionals.lhs.src,
1672 "expected integer type, found '{}'",
1673 .{
1674 dest_type,
1675 },
1676 ),
1677 };
1678
1679 switch (operand.ty.zigTypeTag()) {
1680 .ComptimeInt, .Int => {},
1681 else => return mod.fail(
1682 scope,
1683 inst.positionals.rhs.src,
1684 "expected integer type, found '{}'",
1685 .{operand.ty},
1686 ),
1687 }
1688
1689 if (operand.value() != null) {
1690 return mod.coerce(scope, dest_type, operand);
1691 } else if (dest_is_comptime_int) {
1692 return mod.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{});
1693 }
1694
1695 return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{});
1696}
1697
1698fn zirBitcast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1699 const tracy = trace(@src());
1700 defer tracy.end();
1701 const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
1702 const operand = try resolveInst(mod, scope, inst.positionals.rhs);
1703 return mod.bitcast(scope, dest_type, operand);
1704}
1705
1706fn zirFloatcast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1707 const tracy = trace(@src());
1708 defer tracy.end();
1709 const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
1710 const operand = try resolveInst(mod, scope, inst.positionals.rhs);
1711
1712 const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
1713 .ComptimeFloat => true,
1714 .Float => false,
1715 else => return mod.fail(
1716 scope,
1717 inst.positionals.lhs.src,
1718 "expected float type, found '{}'",
1719 .{
1720 dest_type,
1721 },
1722 ),
1723 };
1724
1725 switch (operand.ty.zigTypeTag()) {
1726 .ComptimeFloat, .Float, .ComptimeInt => {},
1727 else => return mod.fail(
1728 scope,
1729 inst.positionals.rhs.src,
1730 "expected float type, found '{}'",
1731 .{operand.ty},
1732 ),
1733 }
1734
1735 if (operand.value() != null) {
1736 return mod.coerce(scope, dest_type, operand);
1737 } else if (dest_is_comptime_float) {
1738 return mod.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{});
1739 }
1740
1741 return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{});
1742}
1743
1744fn zirElemVal(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst {
1745 const tracy = trace(@src());
1746 defer tracy.end();
1747
1748 const array = try resolveInst(mod, scope, inst.positionals.array);
1749 const array_ptr = try mod.analyzeRef(scope, inst.base.src, array);
1750 const elem_index = try resolveInst(mod, scope, inst.positionals.index);
1751 const result_ptr = try mod.elemPtr(scope, inst.base.src, array_ptr, elem_index);
1752 return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src);
1753}
1754
1755fn zirElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst {
1756 const tracy = trace(@src());
1757 defer tracy.end();
1758
1759 const array_ptr = try resolveInst(mod, scope, inst.positionals.array);
1760 const elem_index = try resolveInst(mod, scope, inst.positionals.index);
1761 return mod.elemPtr(scope, inst.base.src, array_ptr, elem_index);
1762}
1763
1764fn zirSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst {
1765 const tracy = trace(@src());
1766 defer tracy.end();
1767 const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
1768 const start = try resolveInst(mod, scope, inst.positionals.start);
1769 const end = if (inst.kw_args.end) |end| try resolveInst(mod, scope, end) else null;
1770 const sentinel = if (inst.kw_args.sentinel) |sentinel| try resolveInst(mod, scope, sentinel) else null;
1771
1772 return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, end, sentinel);
1773}
1774
1775fn zirSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1776 const tracy = trace(@src());
1777 defer tracy.end();
1778 const array_ptr = try resolveInst(mod, scope, inst.positionals.lhs);
1779 const start = try resolveInst(mod, scope, inst.positionals.rhs);
1780
1781 return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null);
1782}
1783
1784fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1785 const tracy = trace(@src());
1786 defer tracy.end();
1787 const start = try resolveInst(mod, scope, inst.positionals.lhs);
1788 const end = try resolveInst(mod, scope, inst.positionals.rhs);
1789
1790 switch (start.ty.zigTypeTag()) {
1791 .Int, .ComptimeInt => {},
1792 else => return mod.constVoid(scope, inst.base.src),
1793 }
1794 switch (end.ty.zigTypeTag()) {
1795 .Int, .ComptimeInt => {},
1796 else => return mod.constVoid(scope, inst.base.src),
1797 }
1798 // .switch_range must be inside a comptime scope
1799 const start_val = start.value().?;
1800 const end_val = end.value().?;
1801 if (start_val.compare(.gte, end_val)) {
1802 return mod.fail(scope, inst.base.src, "range start value must be smaller than the end value", .{});
1803 }
1804 return mod.constVoid(scope, inst.base.src);
1805}
1806
1807fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr, ref: bool) InnerError!*Inst {
1808 const tracy = trace(@src());
1809 defer tracy.end();
1810
1811 const target_ptr = try resolveInst(mod, scope, inst.positionals.target);
1812 const target = if (ref)
1813 try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target.src)
1814 else
1815 target_ptr;
1816 try validateSwitch(mod, scope, target, inst);
1817
1818 if (try mod.resolveDefinedValue(scope, target)) |target_val| {
1819 for (inst.positionals.cases) |case| {
1820 const resolved = try resolveInst(mod, scope, case.item);
1821 const casted = try mod.coerce(scope, target.ty, resolved);
1822 const item = try mod.resolveConstValue(scope, casted);
1823
1824 if (target_val.eql(item)) {
1825 try analyzeBody(mod, scope.cast(Scope.Block).?, case.body);
1826 return mod.constNoReturn(scope, inst.base.src);
1827 }
1828 }
1829 try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body);
1830 return mod.constNoReturn(scope, inst.base.src);
1831 }
1832
1833 if (inst.positionals.cases.len == 0) {
1834 // no cases just analyze else_branch
1835 try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body);
1836 return mod.constNoReturn(scope, inst.base.src);
1837 }
1838
1839 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
1840 const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len);
1841
1842 var case_block: Scope.Block = .{
1843 .parent = parent_block,
1844 .inst_table = parent_block.inst_table,
1845 .func = parent_block.func,
1846 .owner_decl = parent_block.owner_decl,
1847 .src_decl = parent_block.src_decl,
1848 .instructions = .{},
1849 .arena = parent_block.arena,
1850 .inlining = parent_block.inlining,
1851 .is_comptime = parent_block.is_comptime,
1852 .branch_quota = parent_block.branch_quota,
1853 };
1854 defer case_block.instructions.deinit(mod.gpa);
1855
1856 for (inst.positionals.cases) |case, i| {
1857 // Reset without freeing.
1858 case_block.instructions.items.len = 0;
1859
1860 const resolved = try resolveInst(mod, scope, case.item);
1861 const casted = try mod.coerce(scope, target.ty, resolved);
1862 const item = try mod.resolveConstValue(scope, casted);
1863
1864 try analyzeBody(mod, &case_block, case.body);
1865
1866 cases[i] = .{
1867 .item = item,
1868 .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) },
1869 };
1870 }
1871
1872 case_block.instructions.items.len = 0;
1873 try analyzeBody(mod, &case_block, inst.positionals.else_body);
1874
1875 const else_body: ir.Body = .{
1876 .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
1877 };
1878
1879 return mod.addSwitchBr(parent_block, inst.base.src, target, cases, else_body);
1880}
1881
1882fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {
1883 // validate usage of '_' prongs
1884 if (inst.positionals.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) {
1885 return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});
1886 // TODO notes "'_' prong here" inst.positionals.cases[last].src
1887 }
1888
1889 // check that target type supports ranges
1890 if (inst.positionals.range) |range_inst| {
1891 switch (target.ty.zigTypeTag()) {
1892 .Int, .ComptimeInt => {},
1893 else => {
1894 return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty});
1895 // TODO notes "range used here" range_inst.src
1896 },
1897 }
1898 }
1899
1900 // validate for duplicate items/missing else prong
1901 switch (target.ty.zigTypeTag()) {
1902 .Enum => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Enum", .{}),
1903 .ErrorSet => return mod.fail(scope, inst.base.src, "TODO validateSwitch .ErrorSet", .{}),
1904 .Union => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Union", .{}),
1905 .Int, .ComptimeInt => {
1906 var range_set = @import("RangeSet.zig").init(mod.gpa);
1907 defer range_set.deinit();
1908
1909 for (inst.positionals.items) |item| {
1910 const maybe_src = if (item.castTag(.switch_range)) |range| blk: {
1911 const start_resolved = try resolveInst(mod, scope, range.positionals.lhs);
1912 const start_casted = try mod.coerce(scope, target.ty, start_resolved);
1913 const end_resolved = try resolveInst(mod, scope, range.positionals.rhs);
1914 const end_casted = try mod.coerce(scope, target.ty, end_resolved);
1915
1916 break :blk try range_set.add(
1917 try mod.resolveConstValue(scope, start_casted),
1918 try mod.resolveConstValue(scope, end_casted),
1919 item.src,
1920 );
1921 } else blk: {
1922 const resolved = try resolveInst(mod, scope, item);
1923 const casted = try mod.coerce(scope, target.ty, resolved);
1924 const value = try mod.resolveConstValue(scope, casted);
1925 break :blk try range_set.add(value, value, item.src);
1926 };
1927
1928 if (maybe_src) |previous_src| {
1929 return mod.fail(scope, item.src, "duplicate switch value", .{});
1930 // TODO notes "previous value is here" previous_src
1931 }
1932 }
1933
1934 if (target.ty.zigTypeTag() == .Int) {
1935 var arena = std.heap.ArenaAllocator.init(mod.gpa);
1936 defer arena.deinit();
1937
1938 const start = try target.ty.minInt(&arena, mod.getTarget());
1939 const end = try target.ty.maxInt(&arena, mod.getTarget());
1940 if (try range_set.spans(start, end)) {
1941 if (inst.positionals.special_prong == .@"else") {
1942 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
1943 }
1944 return;
1945 }
1946 }
1947
1948 if (inst.positionals.special_prong != .@"else") {
1949 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
1950 }
1951 },
1952 .Bool => {
1953 var true_count: u8 = 0;
1954 var false_count: u8 = 0;
1955 for (inst.positionals.items) |item| {
1956 const resolved = try resolveInst(mod, scope, item);
1957 const casted = try mod.coerce(scope, Type.initTag(.bool), resolved);
1958 if ((try mod.resolveConstValue(scope, casted)).toBool()) {
1959 true_count += 1;
1960 } else {
1961 false_count += 1;
1962 }
1963
1964 if (true_count + false_count > 2) {
1965 return mod.fail(scope, item.src, "duplicate switch value", .{});
1966 }
1967 }
1968 if ((true_count + false_count < 2) and inst.positionals.special_prong != .@"else") {
1969 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
1970 }
1971 if ((true_count + false_count == 2) and inst.positionals.special_prong == .@"else") {
1972 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
1973 }
1974 },
1975 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
1976 if (inst.positionals.special_prong != .@"else") {
1977 return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty});
1978 }
1979
1980 var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa);
1981 defer seen_values.deinit();
1982
1983 for (inst.positionals.items) |item| {
1984 const resolved = try resolveInst(mod, scope, item);
1985 const casted = try mod.coerce(scope, target.ty, resolved);
1986 const val = try mod.resolveConstValue(scope, casted);
1987
1988 if (try seen_values.fetchPut(val, item.src)) |prev| {
1989 return mod.fail(scope, item.src, "duplicate switch value", .{});
1990 // TODO notes "previous value here" prev.value
1991 }
1992 }
1993 },
1994
1995 .ErrorUnion,
1996 .NoReturn,
1997 .Array,
1998 .Struct,
1999 .Undefined,
2000 .Null,
2001 .Optional,
2002 .BoundFn,
2003 .Opaque,
2004 .Vector,
2005 .Frame,
2006 .AnyFrame,
2007 .ComptimeFloat,
2008 .Float,
2009 => {
2010 return mod.fail(scope, target.src, "invalid switch target type '{}'", .{target.ty});
2011 },
2012 }
2013}
2014
2015fn zirImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2016 const tracy = trace(@src());
2017 defer tracy.end();
2018 const operand = try resolveConstString(mod, scope, inst.positionals.operand);
2019
2020 const file_scope = mod.analyzeImport(scope, inst.base.src, operand) catch |err| switch (err) {
2021 error.ImportOutsidePkgPath => {
2022 return mod.fail(scope, inst.base.src, "import of file outside package path: '{s}'", .{operand});
2023 },
2024 error.FileNotFound => {
2025 return mod.fail(scope, inst.base.src, "unable to find '{s}'", .{operand});
2026 },
2027 else => {
2028 // TODO: make sure this gets retried and not cached
2029 return mod.fail(scope, inst.base.src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });
2030 },
2031 };
2032 return mod.constType(scope, inst.base.src, file_scope.root_container.ty);
2033}
2034
2035fn zirShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2036 const tracy = trace(@src());
2037 defer tracy.end();
2038 return mod.fail(scope, inst.base.src, "TODO implement zirShl", .{});
2039}
2040
2041fn zirShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2042 const tracy = trace(@src());
2043 defer tracy.end();
2044 return mod.fail(scope, inst.base.src, "TODO implement zirShr", .{});
2045}
2046
2047fn zirBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2048 const tracy = trace(@src());
2049 defer tracy.end();
2050
2051 const lhs = try resolveInst(mod, scope, inst.positionals.lhs);
2052 const rhs = try resolveInst(mod, scope, inst.positionals.rhs);
2053
2054 const instructions = &[_]*Inst{ lhs, rhs };
2055 const resolved_type = try mod.resolvePeerTypes(scope, instructions);
2056 const casted_lhs = try mod.coerce(scope, resolved_type, lhs);
2057 const casted_rhs = try mod.coerce(scope, resolved_type, rhs);
2058
2059 const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
2060 resolved_type.elemType()
2061 else
2062 resolved_type;
2063
2064 const scalar_tag = scalar_type.zigTypeTag();
2065
2066 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
2067 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
2068 return mod.fail(scope, inst.base.src, "vector length mismatch: {d} and {d}", .{
2069 lhs.ty.arrayLen(),
2070 rhs.ty.arrayLen(),
2071 });
2072 }
2073 return mod.fail(scope, inst.base.src, "TODO implement support for vectors in zirBitwise", .{});
2074 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
2075 return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
2076 lhs.ty,
2077 rhs.ty,
2078 });
2079 }
2080
2081 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
2082
2083 if (!is_int) {
2084 return mod.fail(scope, inst.base.src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
2085 }
2086
2087 if (casted_lhs.value()) |lhs_val| {
2088 if (casted_rhs.value()) |rhs_val| {
2089 if (lhs_val.isUndef() or rhs_val.isUndef()) {
2090 return mod.constInst(scope, inst.base.src, .{
2091 .ty = resolved_type,
2092 .val = Value.initTag(.undef),
2093 });
2094 }
2095 return mod.fail(scope, inst.base.src, "TODO implement comptime bitwise operations", .{});
2096 }
2097 }
2098
2099 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
2100 const ir_tag = switch (inst.base.tag) {
2101 .bit_and => Inst.Tag.bit_and,
2102 .bit_or => Inst.Tag.bit_or,
2103 .xor => Inst.Tag.xor,
2104 else => unreachable,
2105 };
2106
2107 return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs);
2108}
2109
2110fn zirBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2111 const tracy = trace(@src());
2112 defer tracy.end();
2113 return mod.fail(scope, inst.base.src, "TODO implement zirBitNot", .{});
2114}
2115
2116fn zirArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2117 const tracy = trace(@src());
2118 defer tracy.end();
2119 return mod.fail(scope, inst.base.src, "TODO implement zirArrayCat", .{});
2120}
2121
2122fn zirArrayMul(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2123 const tracy = trace(@src());
2124 defer tracy.end();
2125 return mod.fail(scope, inst.base.src, "TODO implement zirArrayMul", .{});
2126}
2127
2128fn zirArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2129 const tracy = trace(@src());
2130 defer tracy.end();
2131
2132 const lhs = try resolveInst(mod, scope, inst.positionals.lhs);
2133 const rhs = try resolveInst(mod, scope, inst.positionals.rhs);
2134
2135 const instructions = &[_]*Inst{ lhs, rhs };
2136 const resolved_type = try mod.resolvePeerTypes(scope, instructions);
2137 const casted_lhs = try mod.coerce(scope, resolved_type, lhs);
2138 const casted_rhs = try mod.coerce(scope, resolved_type, rhs);
2139
2140 const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
2141 resolved_type.elemType()
2142 else
2143 resolved_type;
2144
2145 const scalar_tag = scalar_type.zigTypeTag();
2146
2147 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
2148 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
2149 return mod.fail(scope, inst.base.src, "vector length mismatch: {d} and {d}", .{
2150 lhs.ty.arrayLen(),
2151 rhs.ty.arrayLen(),
2152 });
2153 }
2154 return mod.fail(scope, inst.base.src, "TODO implement support for vectors in zirBinOp", .{});
2155 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
2156 return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
2157 lhs.ty,
2158 rhs.ty,
2159 });
2160 }
2161
2162 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
2163 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
2164
2165 if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) {
2166 return mod.fail(scope, inst.base.src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
2167 }
2168
2169 if (casted_lhs.value()) |lhs_val| {
2170 if (casted_rhs.value()) |rhs_val| {
2171 if (lhs_val.isUndef() or rhs_val.isUndef()) {
2172 return mod.constInst(scope, inst.base.src, .{
2173 .ty = resolved_type,
2174 .val = Value.initTag(.undef),
2175 });
2176 }
2177 return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val);
2178 }
2179 }
2180
2181 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
2182 const ir_tag: Inst.Tag = switch (inst.base.tag) {
2183 .add => .add,
2184 .addwrap => .addwrap,
2185 .sub => .sub,
2186 .subwrap => .subwrap,
2187 .mul => .mul,
2188 .mulwrap => .mulwrap,
2189 else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{s}''", .{@tagName(inst.base.tag)}),
2190 };
2191
2192 return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs);
2193}
2194
2195/// Analyzes operands that are known at comptime
2196fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst {
2197 // incase rhs is 0, simply return lhs without doing any calculations
2198 // TODO Once division is implemented we should throw an error when dividing by 0.
2199 if (rhs_val.compareWithZero(.eq)) {
2200 return mod.constInst(scope, inst.base.src, .{
2201 .ty = res_type,
2202 .val = lhs_val,
2203 });
2204 }
2205 const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;
2206
2207 const value = switch (inst.base.tag) {
2208 .add => blk: {
2209 const val = if (is_int)
2210 try Module.intAdd(scope.arena(), lhs_val, rhs_val)
2211 else
2212 try mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val);
2213 break :blk val;
2214 },
2215 .sub => blk: {
2216 const val = if (is_int)
2217 try Module.intSub(scope.arena(), lhs_val, rhs_val)
2218 else
2219 try mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val);
2220 break :blk val;
2221 },
2222 else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{s}'", .{@tagName(inst.base.tag)}),
2223 };
2224
2225 log.debug("{s}({}, {}) result: {}", .{ @tagName(inst.base.tag), lhs_val, rhs_val, value });
2226
2227 return mod.constInst(scope, inst.base.src, .{
2228 .ty = res_type,
2229 .val = value,
2230 });
2231}
2232
2233fn zirDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst {
2234 const tracy = trace(@src());
2235 defer tracy.end();
2236 const ptr = try resolveInst(mod, scope, deref.positionals.operand);
2237 return mod.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.operand.src);
2238}
2239
2240fn zirAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst {
2241 const tracy = trace(@src());
2242 defer tracy.end();
2243
2244 const return_type = try resolveType(mod, scope, assembly.positionals.return_type);
2245 const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source);
2246 const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null;
2247
2248 const arena = scope.arena();
2249 const inputs = try arena.alloc([]const u8, assembly.kw_args.inputs.len);
2250 const clobbers = try arena.alloc([]const u8, assembly.kw_args.clobbers.len);
2251 const args = try arena.alloc(*Inst, assembly.kw_args.args.len);
2252
2253 for (inputs) |*elem, i| {
2254 elem.* = try arena.dupe(u8, assembly.kw_args.inputs[i]);
2255 }
2256 for (clobbers) |*elem, i| {
2257 elem.* = try arena.dupe(u8, assembly.kw_args.clobbers[i]);
2258 }
2259 for (args) |*elem, i| {
2260 const arg = try resolveInst(mod, scope, assembly.kw_args.args[i]);
2261 elem.* = try mod.coerce(scope, Type.initTag(.usize), arg);
2262 }
2263
2264 const b = try mod.requireRuntimeBlock(scope, assembly.base.src);
2265 const inst = try b.arena.create(Inst.Assembly);
2266 inst.* = .{
2267 .base = .{
2268 .tag = .assembly,
2269 .ty = return_type,
2270 .src = assembly.base.src,
2271 },
2272 .asm_source = asm_source,
2273 .is_volatile = assembly.kw_args.@"volatile",
2274 .output = output,
2275 .inputs = inputs,
2276 .clobbers = clobbers,
2277 .args = args,
2278 };
2279 try b.instructions.append(mod.gpa, &inst.base);
2280 return &inst.base;
2281}
2282
2283fn zirCmp(
2284 mod: *Module,
2285 scope: *Scope,
2286 inst: *zir.Inst.BinOp,
2287 op: std.math.CompareOperator,
2288) InnerError!*Inst {
2289 const tracy = trace(@src());
2290 defer tracy.end();
2291 const lhs = try resolveInst(mod, scope, inst.positionals.lhs);
2292 const rhs = try resolveInst(mod, scope, inst.positionals.rhs);
2293
2294 const is_equality_cmp = switch (op) {
2295 .eq, .neq => true,
2296 else => false,
2297 };
2298 const lhs_ty_tag = lhs.ty.zigTypeTag();
2299 const rhs_ty_tag = rhs.ty.zigTypeTag();
2300 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
2301 // null == null, null != null
2302 return mod.constBool(scope, inst.base.src, op == .eq);
2303 } else if (is_equality_cmp and
2304 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
2305 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
2306 {
2307 // comparing null with optionals
2308 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
2309 return mod.analyzeIsNull(scope, inst.base.src, opt_operand, op == .neq);
2310 } else if (is_equality_cmp and
2311 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
2312 {
2313 return mod.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{});
2314 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
2315 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
2316 return mod.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type});
2317 } else if (is_equality_cmp and
2318 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
2319 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
2320 {
2321 return mod.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
2322 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
2323 if (!is_equality_cmp) {
2324 return mod.fail(scope, inst.base.src, "{s} operator not allowed for errors", .{@tagName(op)});
2325 }
2326 if (rhs.value()) |rval| {
2327 if (lhs.value()) |lval| {
2328 // TODO optimisation oppurtunity: evaluate if std.mem.eql is faster with the names, or calling to Module.getErrorValue to get the values and then compare them is faster
2329 return mod.constBool(scope, inst.base.src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq));
2330 }
2331 }
2332 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
2333 return mod.addBinOp(b, inst.base.src, Type.initTag(.bool), if (op == .eq) .cmp_eq else .cmp_neq, lhs, rhs);
2334 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
2335 // This operation allows any combination of integer and float types, regardless of the
2336 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
2337 // numeric types.
2338 return mod.cmpNumeric(scope, inst.base.src, lhs, rhs, op);
2339 } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {
2340 if (!is_equality_cmp) {
2341 return mod.fail(scope, inst.base.src, "{s} operator not allowed for types", .{@tagName(op)});
2342 }
2343 return mod.constBool(scope, inst.base.src, lhs.value().?.eql(rhs.value().?) == (op == .eq));
2344 }
2345 return mod.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
2346}
2347
2348fn zirTypeof(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2349 const tracy = trace(@src());
2350 defer tracy.end();
2351 const operand = try resolveInst(mod, scope, inst.positionals.operand);
2352 return mod.constType(scope, inst.base.src, operand.ty);
2353}
2354
2355fn zirTypeofPeer(mod: *Module, scope: *Scope, inst: *zir.Inst.TypeOfPeer) InnerError!*Inst {
2356 const tracy = trace(@src());
2357 defer tracy.end();
2358 var insts_to_res = try mod.gpa.alloc(*ir.Inst, inst.positionals.items.len);
2359 defer mod.gpa.free(insts_to_res);
2360 for (inst.positionals.items) |item, i| {
2361 insts_to_res[i] = try resolveInst(mod, scope, item);
2362 }
2363 const pt_res = try mod.resolvePeerTypes(scope, insts_to_res);
2364 return mod.constType(scope, inst.base.src, pt_res);
2365}
2366
2367fn zirBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2368 const tracy = trace(@src());
2369 defer tracy.end();
2370 const uncasted_operand = try resolveInst(mod, scope, inst.positionals.operand);
2371 const bool_type = Type.initTag(.bool);
2372 const operand = try mod.coerce(scope, bool_type, uncasted_operand);
2373 if (try mod.resolveDefinedValue(scope, operand)) |val| {
2374 return mod.constBool(scope, inst.base.src, !val.toBool());
2375 }
2376 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
2377 return mod.addUnOp(b, inst.base.src, bool_type, .not, operand);
2378}
2379
2380fn zirBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2381 const tracy = trace(@src());
2382 defer tracy.end();
2383 const bool_type = Type.initTag(.bool);
2384 const uncasted_lhs = try resolveInst(mod, scope, inst.positionals.lhs);
2385 const lhs = try mod.coerce(scope, bool_type, uncasted_lhs);
2386 const uncasted_rhs = try resolveInst(mod, scope, inst.positionals.rhs);
2387 const rhs = try mod.coerce(scope, bool_type, uncasted_rhs);
2388
2389 const is_bool_or = inst.base.tag == .bool_or;
2390
2391 if (lhs.value()) |lhs_val| {
2392 if (rhs.value()) |rhs_val| {
2393 if (is_bool_or) {
2394 return mod.constBool(scope, inst.base.src, lhs_val.toBool() or rhs_val.toBool());
2395 } else {
2396 return mod.constBool(scope, inst.base.src, lhs_val.toBool() and rhs_val.toBool());
2397 }
2398 }
2399 }
2400 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
2401 return mod.addBinOp(b, inst.base.src, bool_type, if (is_bool_or) .bool_or else .bool_and, lhs, rhs);
2402}
2403
2404fn zirIsNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
2405 const tracy = trace(@src());
2406 defer tracy.end();
2407 const operand = try resolveInst(mod, scope, inst.positionals.operand);
2408 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);
2409}
2410
2411fn zirIsNullPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
2412 const tracy = trace(@src());
2413 defer tracy.end();
2414 const ptr = try resolveInst(mod, scope, inst.positionals.operand);
2415 const loaded = try mod.analyzeDeref(scope, inst.base.src, ptr, ptr.src);
2416 return mod.analyzeIsNull(scope, inst.base.src, loaded, invert_logic);
2417}
2418
2419fn zirIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2420 const tracy = trace(@src());
2421 defer tracy.end();
2422 const operand = try resolveInst(mod, scope, inst.positionals.operand);
2423 return mod.analyzeIsErr(scope, inst.base.src, operand);
2424}
2425
2426fn zirIsErrPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2427 const tracy = trace(@src());
2428 defer tracy.end();
2429 const ptr = try resolveInst(mod, scope, inst.positionals.operand);
2430 const loaded = try mod.analyzeDeref(scope, inst.base.src, ptr, ptr.src);
2431 return mod.analyzeIsErr(scope, inst.base.src, loaded);
2432}
2433
2434fn zirCondbr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
2435 const tracy = trace(@src());
2436 defer tracy.end();
2437 const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);
2438 const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);
2439
2440 const parent_block = scope.cast(Scope.Block).?;
2441
2442 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
2443 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
2444 try analyzeBody(mod, parent_block, body.*);
2445 return mod.constNoReturn(scope, inst.base.src);
2446 }
2447
2448 var true_block: Scope.Block = .{
2449 .parent = parent_block,
2450 .inst_table = parent_block.inst_table,
2451 .func = parent_block.func,
2452 .owner_decl = parent_block.owner_decl,
2453 .src_decl = parent_block.src_decl,
2454 .instructions = .{},
2455 .arena = parent_block.arena,
2456 .inlining = parent_block.inlining,
2457 .is_comptime = parent_block.is_comptime,
2458 .branch_quota = parent_block.branch_quota,
2459 };
2460 defer true_block.instructions.deinit(mod.gpa);
2461 try analyzeBody(mod, &true_block, inst.positionals.then_body);
2462
2463 var false_block: Scope.Block = .{
2464 .parent = parent_block,
2465 .inst_table = parent_block.inst_table,
2466 .func = parent_block.func,
2467 .owner_decl = parent_block.owner_decl,
2468 .src_decl = parent_block.src_decl,
2469 .instructions = .{},
2470 .arena = parent_block.arena,
2471 .inlining = parent_block.inlining,
2472 .is_comptime = parent_block.is_comptime,
2473 .branch_quota = parent_block.branch_quota,
2474 };
2475 defer false_block.instructions.deinit(mod.gpa);
2476 try analyzeBody(mod, &false_block, inst.positionals.else_body);
2477
2478 const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
2479 const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
2480 return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
2481}
2482
2483fn zirUnreachable(
2484 mod: *Module,
2485 scope: *Scope,
2486 unreach: *zir.Inst.NoOp,
2487 safety_check: bool,
2488) InnerError!*Inst {
2489 const tracy = trace(@src());
2490 defer tracy.end();
2491 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);
2492 // TODO Add compile error for @optimizeFor occurring too late in a scope.
2493 if (safety_check and mod.wantSafety(scope)) {
2494 return mod.safetyPanic(b, unreach.base.src, .unreach);
2495 } else {
2496 return mod.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
2497 }
2498}
2499
2500fn zirReturn(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2501 const tracy = trace(@src());
2502 defer tracy.end();
2503 const operand = try resolveInst(mod, scope, inst.positionals.operand);
2504 const b = try mod.requireFunctionBlock(scope, inst.base.src);
2505
2506 if (b.inlining) |inlining| {
2507 // We are inlining a function call; rewrite the `ret` as a `break`.
2508 try inlining.merges.results.append(mod.gpa, operand);
2509 const br = try mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand);
2510 return &br.base;
2511 }
2512
2513 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
2514}
2515
2516fn zirReturnVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2517 const tracy = trace(@src());
2518 defer tracy.end();
2519 const b = try mod.requireFunctionBlock(scope, inst.base.src);
2520 if (b.inlining) |inlining| {
2521 // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`.
2522 const void_inst = try mod.constVoid(scope, inst.base.src);
2523 try inlining.merges.results.append(mod.gpa, void_inst);
2524 const br = try mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst);
2525 return &br.base;
2526 }
2527
2528 if (b.func) |func| {
2529 // Need to emit a compile error if returning void is not allowed.
2530 const void_inst = try mod.constVoid(scope, inst.base.src);
2531 const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
2532 const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);
2533 if (casted_void.ty.zigTypeTag() != .Void) {
2534 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);
2535 }
2536 }
2537 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
2538}
2539
2540fn floatOpAllowed(tag: zir.Inst.Tag) bool {
2541 // extend this swich as additional operators are implemented
2542 return switch (tag) {
2543 .add, .sub => true,
2544 else => false,
2545 };
2546}
2547
2548fn zirSimplePtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*Inst {
2549 const tracy = trace(@src());
2550 defer tracy.end();
2551 const elem_type = try resolveType(mod, scope, inst.positionals.operand);
2552 const ty = try mod.simplePtrType(scope, inst.base.src, elem_type, mutable, size);
2553 return mod.constType(scope, inst.base.src, ty);
2554}
2555
2556fn zirPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) InnerError!*Inst {
2557 const tracy = trace(@src());
2558 defer tracy.end();
2559 // TODO lazy values
2560 const @"align" = if (inst.kw_args.@"align") |some|
2561 @truncate(u32, try resolveInt(mod, scope, some, Type.initTag(.u32)))
2562 else
2563 0;
2564 const bit_offset = if (inst.kw_args.align_bit_start) |some|
2565 @truncate(u16, try resolveInt(mod, scope, some, Type.initTag(.u16)))
2566 else
2567 0;
2568 const host_size = if (inst.kw_args.align_bit_end) |some|
2569 @truncate(u16, try resolveInt(mod, scope, some, Type.initTag(.u16)))
2570 else
2571 0;
2572
2573 if (host_size != 0 and bit_offset >= host_size * 8)
2574 return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{});
2575
2576 const sentinel = if (inst.kw_args.sentinel) |some|
2577 (try resolveInstConst(mod, scope, some)).val
2578 else
2579 null;
2580
2581 const elem_type = try resolveType(mod, scope, inst.positionals.child_type);
2582
2583 const ty = try mod.ptrType(
2584 scope,
2585 inst.base.src,
2586 elem_type,
2587 sentinel,
2588 @"align",
2589 bit_offset,
2590 host_size,
2591 inst.kw_args.mutable,
2592 inst.kw_args.@"allowzero",
2593 inst.kw_args.@"volatile",
2594 inst.kw_args.size,
2595 );
2596 return mod.constType(scope, inst.base.src, ty);
2597}
test/compile_errors.zig-2
......@@ -1027,9 +1027,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
10271027 \\}
10281028 \\fn foo() void {}
10291029 , &[_][]const u8{
1030 "tmp.zig:3:21: error: async call in nosuspend scope",
10311030 "tmp.zig:4:9: error: suspend in nosuspend scope",
1032 "tmp.zig:5:9: error: resume in nosuspend scope",
10331031 });
10341032
10351033 cases.add("atomicrmw with bool op not .Xchg",
test/stage1/behavior/async_fn.zig+63-1
......@@ -1,5 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
2const builtin = std.builtin;
33const expect = std.testing.expect;
44const expectEqual = std.testing.expectEqual;
55const expectEqualStrings = std.testing.expectEqualStrings;
......@@ -1545,6 +1545,68 @@ test "nosuspend on function calls" {
15451545 expectEqual(@as(i32, 42), (try nosuspend S1.d()).b);
15461546}
15471547
1548test "nosuspend on async function calls" {
1549 const S0 = struct {
1550 b: i32 = 42,
1551 };
1552 const S1 = struct {
1553 fn c() S0 {
1554 return S0{};
1555 }
1556 fn d() !S0 {
1557 return S0{};
1558 }
1559 };
1560 var frame_c = nosuspend async S1.c();
1561 expectEqual(@as(i32, 42), (await frame_c).b);
1562 var frame_d = nosuspend async S1.d();
1563 expectEqual(@as(i32, 42), (try await frame_d).b);
1564}
1565
1566// test "resume nosuspend async function calls" {
1567// const S0 = struct {
1568// b: i32 = 42,
1569// };
1570// const S1 = struct {
1571// fn c() S0 {
1572// suspend;
1573// return S0{};
1574// }
1575// fn d() !S0 {
1576// suspend;
1577// return S0{};
1578// }
1579// };
1580// var frame_c = nosuspend async S1.c();
1581// resume frame_c;
1582// expectEqual(@as(i32, 42), (await frame_c).b);
1583// var frame_d = nosuspend async S1.d();
1584// resume frame_d;
1585// expectEqual(@as(i32, 42), (try await frame_d).b);
1586// }
1587
1588test "nosuspend resume async function calls" {
1589 const S0 = struct {
1590 b: i32 = 42,
1591 };
1592 const S1 = struct {
1593 fn c() S0 {
1594 suspend;
1595 return S0{};
1596 }
1597 fn d() !S0 {
1598 suspend;
1599 return S0{};
1600 }
1601 };
1602 var frame_c = async S1.c();
1603 nosuspend resume frame_c;
1604 expectEqual(@as(i32, 42), (await frame_c).b);
1605 var frame_d = async S1.d();
1606 nosuspend resume frame_d;
1607 expectEqual(@as(i32, 42), (try await frame_d).b);
1608}
1609
15481610test "avoid forcing frame alignment resolution implicit cast to *c_void" {
15491611 const S = struct {
15501612 var x: ?*c_void = null;
test/stage2/arm.zig+41
......@@ -378,4 +378,45 @@ pub fn addCases(ctx: *TestContext) !void {
378378 "",
379379 );
380380 }
381
382 {
383 var case = ctx.exe("save function return values in callee preserved register", linux_arm);
384 // Here, it is necessary to save the result of bar() into a
385 // callee preserved register, otherwise it will be overwritten
386 // by the first parameter to baz.
387 case.addCompareOutput(
388 \\export fn _start() noreturn {
389 \\ assert(foo() == 43);
390 \\ exit();
391 \\}
392 \\
393 \\fn foo() u32 {
394 \\ return bar() + baz(42);
395 \\}
396 \\
397 \\fn bar() u32 {
398 \\ return 1;
399 \\}
400 \\
401 \\fn baz(x: u32) u32 {
402 \\ return x;
403 \\}
404 \\
405 \\fn assert(ok: bool) void {
406 \\ if (!ok) unreachable;
407 \\}
408 \\
409 \\fn exit() noreturn {
410 \\ asm volatile ("svc #0"
411 \\ :
412 \\ : [number] "{r7}" (1),
413 \\ [arg1] "{r0}" (0)
414 \\ : "memory"
415 \\ );
416 \\ unreachable;
417 \\}
418 ,
419 "",
420 );
421 }
381422}
test/stage2/cbe.zig+180
......@@ -39,6 +39,21 @@ pub fn addCases(ctx: *TestContext) !void {
3939 \\}
4040 \\fn unused() void {}
4141 , "yo!" ++ std.cstr.line_sep);
42
43 // Comptime return type and calling convention expected.
44 case.addError(
45 \\var x: i32 = 1234;
46 \\export fn main() x {
47 \\ return 0;
48 \\}
49 \\export fn foo() callconv(y) c_int {
50 \\ return 0;
51 \\}
52 \\var y: i32 = 1234;
53 , &.{
54 ":2:18: error: unable to resolve comptime value",
55 ":5:26: error: unable to resolve comptime value",
56 });
4257 }
4358
4459 {
......@@ -54,6 +69,42 @@ pub fn addCases(ctx: *TestContext) !void {
5469 , "Hello, world!" ++ std.cstr.line_sep);
5570 }
5671
72 {
73 var case = ctx.exeFromCompiledC("@intToError", .{});
74
75 case.addCompareOutput(
76 \\pub export fn main() c_int {
77 \\ // comptime checks
78 \\ const a = error.A;
79 \\ const b = error.B;
80 \\ const c = @intToError(2);
81 \\ const d = @intToError(1);
82 \\ if (!(c == b)) unreachable;
83 \\ if (!(a == d)) unreachable;
84 \\ // runtime checks
85 \\ var x = error.A;
86 \\ var y = error.B;
87 \\ var z = @intToError(2);
88 \\ var f = @intToError(1);
89 \\ if (!(y == z)) unreachable;
90 \\ if (!(x == f)) unreachable;
91 \\ return 0;
92 \\}
93 , "");
94 case.addError(
95 \\pub export fn main() c_int {
96 \\ const c = @intToError(0);
97 \\ return 0;
98 \\}
99 , &.{":2:27: error: integer value 0 represents no error"});
100 case.addError(
101 \\pub export fn main() c_int {
102 \\ const c = @intToError(3);
103 \\ return 0;
104 \\}
105 , &.{":2:27: error: integer value 3 represents no error"});
106 }
107
57108 {
58109 var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64);
59110
......@@ -243,6 +294,134 @@ pub fn addCases(ctx: *TestContext) !void {
243294 \\ return a - 4;
244295 \\}
245296 , "");
297
298 // Switch expression missing else case.
299 case.addError(
300 \\export fn main() c_int {
301 \\ var cond: c_int = 0;
302 \\ const a: c_int = switch (cond) {
303 \\ 1 => 1,
304 \\ 2 => 2,
305 \\ 3 => 3,
306 \\ 4 => 4,
307 \\ };
308 \\ return a - 4;
309 \\}
310 , &.{":3:22: error: switch must handle all possibilities"});
311
312 // Switch expression, has an unreachable prong.
313 case.addCompareOutput(
314 \\export fn main() c_int {
315 \\ var cond: c_int = 0;
316 \\ const a: c_int = switch (cond) {
317 \\ 1 => 1,
318 \\ 2 => 2,
319 \\ 99...300, 12 => 3,
320 \\ 0 => 4,
321 \\ 13 => unreachable,
322 \\ else => 5,
323 \\ };
324 \\ return a - 4;
325 \\}
326 , "");
327
328 // Switch expression, has an unreachable prong and prongs write
329 // to result locations.
330 case.addCompareOutput(
331 \\export fn main() c_int {
332 \\ var cond: c_int = 0;
333 \\ var a: c_int = switch (cond) {
334 \\ 1 => 1,
335 \\ 2 => 2,
336 \\ 99...300, 12 => 3,
337 \\ 0 => 4,
338 \\ 13 => unreachable,
339 \\ else => 5,
340 \\ };
341 \\ return a - 4;
342 \\}
343 , "");
344
345 // Integer switch expression has duplicate case value.
346 case.addError(
347 \\export fn main() c_int {
348 \\ var cond: c_int = 0;
349 \\ const a: c_int = switch (cond) {
350 \\ 1 => 1,
351 \\ 2 => 2,
352 \\ 96, 11...13, 97 => 3,
353 \\ 0 => 4,
354 \\ 90, 12 => 100,
355 \\ else => 5,
356 \\ };
357 \\ return a - 4;
358 \\}
359 , &.{
360 ":8:13: error: duplicate switch value",
361 ":6:15: note: previous value here",
362 });
363
364 // Boolean switch expression has duplicate case value.
365 case.addError(
366 \\export fn main() c_int {
367 \\ var a: bool = false;
368 \\ const b: c_int = switch (a) {
369 \\ false => 1,
370 \\ true => 2,
371 \\ false => 3,
372 \\ };
373 \\}
374 , &.{
375 ":6:9: error: duplicate switch value",
376 });
377
378 // Sparse (no range capable) switch expression has duplicate case value.
379 case.addError(
380 \\export fn main() c_int {
381 \\ const A: type = i32;
382 \\ const b: c_int = switch (A) {
383 \\ i32 => 1,
384 \\ bool => 2,
385 \\ f64, i32 => 3,
386 \\ else => 4,
387 \\ };
388 \\}
389 , &.{
390 ":6:14: error: duplicate switch value",
391 ":4:9: note: previous value here",
392 });
393
394 // Ranges not allowed for some kinds of switches.
395 case.addError(
396 \\export fn main() c_int {
397 \\ const A: type = i32;
398 \\ const b: c_int = switch (A) {
399 \\ i32 => 1,
400 \\ bool => 2,
401 \\ f16...f64 => 3,
402 \\ else => 4,
403 \\ };
404 \\}
405 , &.{
406 ":3:30: error: ranges not allowed when switching on type 'type'",
407 ":6:12: note: range here",
408 });
409
410 // Switch expression has unreachable else prong.
411 case.addError(
412 \\export fn main() c_int {
413 \\ var a: u2 = 0;
414 \\ const b: i32 = switch (a) {
415 \\ 0 => 10,
416 \\ 1 => 20,
417 \\ 2 => 30,
418 \\ 3 => 40,
419 \\ else => 50,
420 \\ };
421 \\}
422 , &.{
423 ":8:14: error: unreachable else prong; all cases already handled",
424 });
246425 }
247426 //{
248427 // var case = ctx.exeFromCompiledC("optionals", .{});
......@@ -271,6 +450,7 @@ pub fn addCases(ctx: *TestContext) !void {
271450 // \\}
272451 // , "");
273452 //}
453
274454 {
275455 var case = ctx.exeFromCompiledC("errors", .{});
276456 case.addCompareOutput(
test/stage2/test.zig+50-10
......@@ -355,7 +355,7 @@ pub fn addCases(ctx: *TestContext) !void {
355355 \\ const z = @TypeOf(true, 1);
356356 \\ unreachable;
357357 \\}
358 , &[_][]const u8{":2:29: error: incompatible types: 'bool' and 'comptime_int'"});
358 , &[_][]const u8{":2:15: error: incompatible types: 'bool' and 'comptime_int'"});
359359 }
360360
361361 {
......@@ -621,6 +621,43 @@ pub fn addCases(ctx: *TestContext) !void {
621621 "hello\nhello\nhello\nhello\n",
622622 );
623623
624 // inline while requires the condition to be comptime known.
625 case.addError(
626 \\export fn _start() noreturn {
627 \\ var i: u32 = 0;
628 \\ inline while (i < 4) : (i += 1) print();
629 \\ assert(i == 4);
630 \\
631 \\ exit();
632 \\}
633 \\
634 \\fn print() void {
635 \\ asm volatile ("syscall"
636 \\ :
637 \\ : [number] "{rax}" (1),
638 \\ [arg1] "{rdi}" (1),
639 \\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
640 \\ [arg3] "{rdx}" (6)
641 \\ : "rcx", "r11", "memory"
642 \\ );
643 \\ return;
644 \\}
645 \\
646 \\pub fn assert(ok: bool) void {
647 \\ if (!ok) unreachable; // assertion failure
648 \\}
649 \\
650 \\fn exit() noreturn {
651 \\ asm volatile ("syscall"
652 \\ :
653 \\ : [number] "{rax}" (231),
654 \\ [arg1] "{rdi}" (0)
655 \\ : "rcx", "r11", "memory"
656 \\ );
657 \\ unreachable;
658 \\}
659 , &[_][]const u8{":3:21: error: unable to resolve comptime value"});
660
624661 // Labeled blocks (no conditional branch)
625662 case.addCompareOutput(
626663 \\export fn _start() noreturn {
......@@ -1070,7 +1107,7 @@ pub fn addCases(ctx: *TestContext) !void {
10701107 \\}
10711108 \\fn x() void {}
10721109 , &[_][]const u8{
1073 ":11:8: error: found compile log statement",
1110 ":9:5: error: found compile log statement",
10741111 ":4:5: note: also here",
10751112 });
10761113 }
......@@ -1294,10 +1331,9 @@ pub fn addCases(ctx: *TestContext) !void {
12941331 ,
12951332 "",
12961333 );
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.
1334 // This additionally tests that the compile error reports the correct source location.
1335 // Without storing source locations relative to the owner decl, the compile error
1336 // here would be off by 2 bytes (from the "7" -> "999").
13011337 case.addError(
13021338 \\export fn _start() noreturn {
13031339 \\ const y = fibonacci(999);
......@@ -1318,7 +1354,7 @@ pub fn addCases(ctx: *TestContext) !void {
13181354 \\ );
13191355 \\ unreachable;
13201356 \\}
1321 , &[_][]const u8{":8:19: error: evaluation exceeded 1000 backwards branches"});
1357 , &[_][]const u8{":8:21: error: evaluation exceeded 1000 backwards branches"});
13221358 }
13231359 {
13241360 var case = ctx.exe("orelse at comptime", linux_x64);
......@@ -1442,6 +1478,7 @@ pub fn addCases(ctx: *TestContext) !void {
14421478 ,
14431479 "",
14441480 );
1481
14451482 case.addCompareOutput(
14461483 \\export fn _start() noreturn {
14471484 \\ const i: anyerror!u64 = error.B;
......@@ -1464,6 +1501,7 @@ pub fn addCases(ctx: *TestContext) !void {
14641501 ,
14651502 "",
14661503 );
1504
14671505 case.addCompareOutput(
14681506 \\export fn _start() noreturn {
14691507 \\ const a: anyerror!comptime_int = 42;
......@@ -1485,11 +1523,12 @@ pub fn addCases(ctx: *TestContext) !void {
14851523 \\ unreachable;
14861524 \\}
14871525 , "");
1526
14881527 case.addCompareOutput(
14891528 \\export fn _start() noreturn {
1490 \\const a: anyerror!u32 = error.B;
1491 \\_ = &(a catch |err| assert(err == error.B));
1492 \\exit();
1529 \\ const a: anyerror!u32 = error.B;
1530 \\ _ = &(a catch |err| assert(err == error.B));
1531 \\ exit();
14931532 \\}
14941533 \\fn assert(b: bool) void {
14951534 \\ if (!b) unreachable;
......@@ -1504,6 +1543,7 @@ pub fn addCases(ctx: *TestContext) !void {
15041543 \\ unreachable;
15051544 \\}
15061545 , "");
1546
15071547 case.addCompareOutput(
15081548 \\export fn _start() noreturn {
15091549 \\ const a: anyerror!u32 = error.Bar;