authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-31 23:11:15-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-03-31 23:11:15-07:00
log070a28e493c6994197b2e52f42f3d0020b7395aa
tree495bf07a61916906a7f1b1d13019be91426ebbdc
parent1b657e6e41bcaf362d1cc9455c17e06e57973554
parentc9e31febf811286580792265efe20ccfa76c0fcf
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8266 from ziglang/zir-memory-layout

rework ZIR memory layout; overhaul source locations

33 files changed, 14719 insertions(+), 11495 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)
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/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+4275
......@@ -0,0 +1,4275 @@
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, 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, tree.containerDeclTwo(&buffer, node));
726 },
727 .container_decl_arg,
728 .container_decl_arg_trailing,
729 => return containerDecl(gz, scope, rl, tree.containerDeclArg(node)),
730
731 .tagged_union,
732 .tagged_union_trailing,
733 => return containerDecl(gz, scope, rl, 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, tree.taggedUnionTwo(&buffer, node));
737 },
738 .tagged_union_enum_tag,
739 .tagged_union_enum_tag_trailing,
740 => return containerDecl(gz, scope, rl, 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,
773 .struct_init_one_comma,
774 .struct_init_dot_two,
775 .struct_init_dot_two_comma,
776 .struct_init_dot,
777 .struct_init_dot_comma,
778 .struct_init,
779 .struct_init_comma,
780 => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}),
781
782 .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}),
783 .fn_proto_simple,
784 .fn_proto_multi,
785 .fn_proto_one,
786 .fn_proto,
787 => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}),
788 }
789}
790
791pub fn comptimeExpr(
792 gz: *GenZir,
793 scope: *Scope,
794 rl: ResultLoc,
795 node: ast.Node.Index,
796) InnerError!zir.Inst.Ref {
797 const prev_force_comptime = gz.force_comptime;
798 gz.force_comptime = true;
799 const result = try expr(gz, scope, rl, node);
800 gz.force_comptime = prev_force_comptime;
801 return result;
802}
803
804fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
805 const mod = parent_gz.astgen.mod;
806 const tree = parent_gz.tree();
807 const node_datas = tree.nodes.items(.data);
808 const break_label = node_datas[node].lhs;
809 const rhs = node_datas[node].rhs;
810
811 // Look for the label in the scope.
812 var scope = parent_scope;
813 while (true) {
814 switch (scope.tag) {
815 .gen_zir => {
816 const block_gz = scope.cast(GenZir).?;
817
818 const block_inst = blk: {
819 if (break_label != 0) {
820 if (block_gz.label) |*label| {
821 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
822 label.used = true;
823 break :blk label.block_inst;
824 }
825 }
826 } else if (block_gz.break_block != 0) {
827 break :blk block_gz.break_block;
828 }
829 scope = block_gz.parent;
830 continue;
831 };
832
833 if (rhs == 0) {
834 _ = try parent_gz.addBreak(.@"break", block_inst, .void_value);
835 return zir.Inst.Ref.unreachable_value;
836 }
837 block_gz.break_count += 1;
838 const prev_rvalue_rl_count = block_gz.rvalue_rl_count;
839 const operand = try expr(parent_gz, parent_scope, block_gz.break_result_loc, rhs);
840 const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count;
841
842 const br = try parent_gz.addBreak(.@"break", block_inst, operand);
843
844 if (block_gz.break_result_loc == .block_ptr) {
845 try block_gz.labeled_breaks.append(mod.gpa, br);
846
847 if (have_store_to_block) {
848 const zir_tags = parent_gz.astgen.instructions.items(.tag);
849 const zir_datas = parent_gz.astgen.instructions.items(.data);
850 const store_inst = @intCast(u32, zir_tags.len - 2);
851 assert(zir_tags[store_inst] == .store_to_block_ptr);
852 assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr);
853 try block_gz.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst);
854 }
855 }
856 return zir.Inst.Ref.unreachable_value;
857 },
858 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
859 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
860 else => if (break_label != 0) {
861 const label_name = try mod.identifierTokenString(parent_scope, break_label);
862 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
863 } else {
864 return mod.failNode(parent_scope, node, "break expression outside loop", .{});
865 },
866 }
867 }
868}
869
870fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
871 const mod = parent_gz.astgen.mod;
872 const tree = parent_gz.tree();
873 const node_datas = tree.nodes.items(.data);
874 const break_label = node_datas[node].lhs;
875
876 // Look for the label in the scope.
877 var scope = parent_scope;
878 while (true) {
879 switch (scope.tag) {
880 .gen_zir => {
881 const gen_zir = scope.cast(GenZir).?;
882 const continue_block = gen_zir.continue_block;
883 if (continue_block == 0) {
884 scope = gen_zir.parent;
885 continue;
886 }
887 if (break_label != 0) blk: {
888 if (gen_zir.label) |*label| {
889 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
890 label.used = true;
891 break :blk;
892 }
893 }
894 // found continue but either it has a different label, or no label
895 scope = gen_zir.parent;
896 continue;
897 }
898
899 // TODO emit a break_inline if the loop being continued is inline
900 _ = try parent_gz.addBreak(.@"break", continue_block, .void_value);
901 return zir.Inst.Ref.unreachable_value;
902 },
903 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
904 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
905 else => if (break_label != 0) {
906 const label_name = try mod.identifierTokenString(parent_scope, break_label);
907 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
908 } else {
909 return mod.failNode(parent_scope, node, "continue expression outside loop", .{});
910 },
911 }
912 }
913}
914
915pub fn blockExpr(
916 gz: *GenZir,
917 scope: *Scope,
918 rl: ResultLoc,
919 block_node: ast.Node.Index,
920 statements: []const ast.Node.Index,
921) InnerError!zir.Inst.Ref {
922 const tracy = trace(@src());
923 defer tracy.end();
924
925 const tree = gz.tree();
926 const main_tokens = tree.nodes.items(.main_token);
927 const token_tags = tree.tokens.items(.tag);
928
929 const lbrace = main_tokens[block_node];
930 if (token_tags[lbrace - 1] == .colon and
931 token_tags[lbrace - 2] == .identifier)
932 {
933 return labeledBlockExpr(gz, scope, rl, block_node, statements, .block);
934 }
935
936 try blockExprStmts(gz, scope, block_node, statements);
937 return rvalue(gz, scope, rl, .void_value, block_node);
938}
939
940fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {
941 // Look for the label in the scope.
942 var scope = parent_scope;
943 while (true) {
944 switch (scope.tag) {
945 .gen_zir => {
946 const gen_zir = scope.cast(GenZir).?;
947 if (gen_zir.label) |prev_label| {
948 if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {
949 const tree = parent_scope.tree();
950 const main_tokens = tree.nodes.items(.main_token);
951
952 const label_name = try mod.identifierTokenString(parent_scope, label);
953 const msg = msg: {
954 const msg = try mod.errMsg(
955 parent_scope,
956 gen_zir.tokSrcLoc(label),
957 "redefinition of label '{s}'",
958 .{label_name},
959 );
960 errdefer msg.destroy(mod.gpa);
961 try mod.errNote(
962 parent_scope,
963 gen_zir.tokSrcLoc(prev_label.token),
964 msg,
965 "previous definition is here",
966 .{},
967 );
968 break :msg msg;
969 };
970 return mod.failWithOwnedErrorMsg(parent_scope, msg);
971 }
972 }
973 scope = gen_zir.parent;
974 },
975 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
976 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
977 else => return,
978 }
979 }
980}
981
982fn labeledBlockExpr(
983 gz: *GenZir,
984 parent_scope: *Scope,
985 rl: ResultLoc,
986 block_node: ast.Node.Index,
987 statements: []const ast.Node.Index,
988 zir_tag: zir.Inst.Tag,
989) InnerError!zir.Inst.Ref {
990 const tracy = trace(@src());
991 defer tracy.end();
992
993 assert(zir_tag == .block);
994
995 const mod = gz.astgen.mod;
996 const tree = gz.tree();
997 const main_tokens = tree.nodes.items(.main_token);
998 const token_tags = tree.tokens.items(.tag);
999
1000 const lbrace = main_tokens[block_node];
1001 const label_token = lbrace - 2;
1002 assert(token_tags[label_token] == .identifier);
1003
1004 try checkLabelRedefinition(mod, parent_scope, label_token);
1005
1006 // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
1007 // so that break statements can reference it.
1008 const block_inst = try gz.addBlock(zir_tag, block_node);
1009 try gz.instructions.append(mod.gpa, block_inst);
1010
1011 var block_scope: GenZir = .{
1012 .parent = parent_scope,
1013 .astgen = gz.astgen,
1014 .force_comptime = gz.force_comptime,
1015 .instructions = .{},
1016 // TODO @as here is working around a stage1 miscompilation bug :(
1017 .label = @as(?GenZir.Label, GenZir.Label{
1018 .token = label_token,
1019 .block_inst = block_inst,
1020 }),
1021 };
1022 block_scope.setBreakResultLoc(rl);
1023 defer block_scope.instructions.deinit(mod.gpa);
1024 defer block_scope.labeled_breaks.deinit(mod.gpa);
1025 defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa);
1026
1027 try blockExprStmts(&block_scope, &block_scope.base, block_node, statements);
1028
1029 if (!block_scope.label.?.used) {
1030 return mod.failTok(parent_scope, label_token, "unused block label", .{});
1031 }
1032
1033 const zir_tags = gz.astgen.instructions.items(.tag);
1034 const zir_datas = gz.astgen.instructions.items(.data);
1035
1036 const strat = rl.strategy(&block_scope);
1037 switch (strat.tag) {
1038 .break_void => {
1039 // The code took advantage of the result location as a pointer.
1040 // Turn the break instruction operands into void.
1041 for (block_scope.labeled_breaks.items) |br| {
1042 zir_datas[br].@"break".operand = .void_value;
1043 }
1044 try block_scope.setBlockBody(block_inst);
1045
1046 return gz.astgen.indexToRef(block_inst);
1047 },
1048 .break_operand => {
1049 // All break operands are values that did not use the result location pointer.
1050 if (strat.elide_store_to_block_ptr_instructions) {
1051 for (block_scope.labeled_store_to_block_ptr_list.items) |inst| {
1052 zir_tags[inst] = .elided;
1053 zir_datas[inst] = undefined;
1054 }
1055 // TODO technically not needed since we changed the tag to elided but
1056 // would be better still to elide the ones that are in this list.
1057 }
1058 try block_scope.setBlockBody(block_inst);
1059 const block_ref = gz.astgen.indexToRef(block_inst);
1060 switch (rl) {
1061 .ref => return block_ref,
1062 else => return rvalue(gz, parent_scope, rl, block_ref, block_node),
1063 }
1064 },
1065 }
1066}
1067
1068fn blockExprStmts(
1069 gz: *GenZir,
1070 parent_scope: *Scope,
1071 node: ast.Node.Index,
1072 statements: []const ast.Node.Index,
1073) !void {
1074 const tree = gz.tree();
1075 const main_tokens = tree.nodes.items(.main_token);
1076 const node_tags = tree.nodes.items(.tag);
1077
1078 var block_arena = std.heap.ArenaAllocator.init(gz.astgen.mod.gpa);
1079 defer block_arena.deinit();
1080
1081 var scope = parent_scope;
1082 for (statements) |statement| {
1083 if (!gz.force_comptime) {
1084 _ = try gz.addNode(.dbg_stmt_node, statement);
1085 }
1086 switch (node_tags[statement]) {
1087 .global_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.globalVarDecl(statement)),
1088 .local_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.localVarDecl(statement)),
1089 .simple_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.simpleVarDecl(statement)),
1090 .aligned_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.alignedVarDecl(statement)),
1091
1092 .assign => try assign(gz, scope, statement),
1093 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),
1094 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or),
1095 .assign_bit_shift_left => try assignOp(gz, scope, statement, .shl),
1096 .assign_bit_shift_right => try assignOp(gz, scope, statement, .shr),
1097 .assign_bit_xor => try assignOp(gz, scope, statement, .xor),
1098 .assign_div => try assignOp(gz, scope, statement, .div),
1099 .assign_sub => try assignOp(gz, scope, statement, .sub),
1100 .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap),
1101 .assign_mod => try assignOp(gz, scope, statement, .mod_rem),
1102 .assign_add => try assignOp(gz, scope, statement, .add),
1103 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
1104 .assign_mul => try assignOp(gz, scope, statement, .mul),
1105 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
1106
1107 else => {
1108 // We need to emit an error if the result is not `noreturn` or `void`, but
1109 // we want to avoid adding the ZIR instruction if possible for performance.
1110 const maybe_unused_result = try expr(gz, scope, .none, statement);
1111 const elide_check = if (gz.astgen.refToIndex(maybe_unused_result)) |inst| b: {
1112 // Note that this array becomes invalid after appending more items to it
1113 // in the above while loop.
1114 const zir_tags = gz.astgen.instructions.items(.tag);
1115 switch (zir_tags[inst]) {
1116 .@"const" => {
1117 const tv = gz.astgen.instructions.items(.data)[inst].@"const";
1118 break :b switch (tv.ty.zigTypeTag()) {
1119 .NoReturn, .Void => true,
1120 else => false,
1121 };
1122 },
1123 // For some instructions, swap in a slightly different ZIR tag
1124 // so we can avoid a separate ensure_result_used instruction.
1125 .call_none_chkused => unreachable,
1126 .call_none => {
1127 zir_tags[inst] = .call_none_chkused;
1128 break :b true;
1129 },
1130 .call_chkused => unreachable,
1131 .call => {
1132 zir_tags[inst] = .call_chkused;
1133 break :b true;
1134 },
1135
1136 // ZIR instructions that might be a type other than `noreturn` or `void`.
1137 .add,
1138 .addwrap,
1139 .alloc,
1140 .alloc_mut,
1141 .alloc_inferred,
1142 .alloc_inferred_mut,
1143 .array_cat,
1144 .array_mul,
1145 .array_type,
1146 .array_type_sentinel,
1147 .indexable_ptr_len,
1148 .as,
1149 .as_node,
1150 .@"asm",
1151 .asm_volatile,
1152 .bit_and,
1153 .bitcast,
1154 .bitcast_result_ptr,
1155 .bit_or,
1156 .block,
1157 .block_inline,
1158 .loop,
1159 .bool_br_and,
1160 .bool_br_or,
1161 .bool_not,
1162 .bool_and,
1163 .bool_or,
1164 .call_compile_time,
1165 .cmp_lt,
1166 .cmp_lte,
1167 .cmp_eq,
1168 .cmp_gte,
1169 .cmp_gt,
1170 .cmp_neq,
1171 .coerce_result_ptr,
1172 .decl_ref,
1173 .decl_val,
1174 .load,
1175 .div,
1176 .elem_ptr,
1177 .elem_val,
1178 .elem_ptr_node,
1179 .elem_val_node,
1180 .floatcast,
1181 .field_ptr,
1182 .field_val,
1183 .field_ptr_named,
1184 .field_val_named,
1185 .fn_type,
1186 .fn_type_var_args,
1187 .fn_type_cc,
1188 .fn_type_cc_var_args,
1189 .int,
1190 .intcast,
1191 .int_type,
1192 .is_non_null,
1193 .is_null,
1194 .is_non_null_ptr,
1195 .is_null_ptr,
1196 .is_err,
1197 .is_err_ptr,
1198 .mod_rem,
1199 .mul,
1200 .mulwrap,
1201 .param_type,
1202 .ptrtoint,
1203 .ref,
1204 .ret_ptr,
1205 .ret_type,
1206 .shl,
1207 .shr,
1208 .str,
1209 .sub,
1210 .subwrap,
1211 .negate,
1212 .negate_wrap,
1213 .typeof,
1214 .typeof_elem,
1215 .xor,
1216 .optional_type,
1217 .optional_type_from_ptr_elem,
1218 .optional_payload_safe,
1219 .optional_payload_unsafe,
1220 .optional_payload_safe_ptr,
1221 .optional_payload_unsafe_ptr,
1222 .err_union_payload_safe,
1223 .err_union_payload_unsafe,
1224 .err_union_payload_safe_ptr,
1225 .err_union_payload_unsafe_ptr,
1226 .err_union_code,
1227 .err_union_code_ptr,
1228 .ptr_type,
1229 .ptr_type_simple,
1230 .enum_literal,
1231 .enum_literal_small,
1232 .merge_error_sets,
1233 .error_union_type,
1234 .bit_not,
1235 .error_value,
1236 .error_to_int,
1237 .int_to_error,
1238 .slice_start,
1239 .slice_end,
1240 .slice_sentinel,
1241 .import,
1242 .typeof_peer,
1243 .switch_block,
1244 .switch_block_multi,
1245 .switch_block_else,
1246 .switch_block_else_multi,
1247 .switch_block_under,
1248 .switch_block_under_multi,
1249 .switch_block_ref,
1250 .switch_block_ref_multi,
1251 .switch_block_ref_else,
1252 .switch_block_ref_else_multi,
1253 .switch_block_ref_under,
1254 .switch_block_ref_under_multi,
1255 .switch_capture,
1256 .switch_capture_ref,
1257 .switch_capture_multi,
1258 .switch_capture_multi_ref,
1259 .switch_capture_else,
1260 .switch_capture_else_ref,
1261 => break :b false,
1262
1263 // ZIR instructions that are always either `noreturn` or `void`.
1264 .breakpoint,
1265 .dbg_stmt_node,
1266 .ensure_result_used,
1267 .ensure_result_non_error,
1268 .set_eval_branch_quota,
1269 .compile_log,
1270 .ensure_err_payload_void,
1271 .@"break",
1272 .break_inline,
1273 .condbr,
1274 .condbr_inline,
1275 .compile_error,
1276 .ret_node,
1277 .ret_tok,
1278 .ret_coerce,
1279 .@"unreachable",
1280 .elided,
1281 .store,
1282 .store_node,
1283 .store_to_block_ptr,
1284 .store_to_inferred_ptr,
1285 .resolve_inferred_alloc,
1286 .repeat,
1287 .repeat_inline,
1288 => break :b true,
1289 }
1290 } else switch (maybe_unused_result) {
1291 .none => unreachable,
1292
1293 .void_value,
1294 .unreachable_value,
1295 => true,
1296
1297 else => false,
1298 };
1299 if (!elide_check) {
1300 _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
1301 }
1302 },
1303 }
1304 }
1305}
1306
1307fn varDecl(
1308 gz: *GenZir,
1309 scope: *Scope,
1310 node: ast.Node.Index,
1311 block_arena: *Allocator,
1312 var_decl: ast.full.VarDecl,
1313) InnerError!*Scope {
1314 const mod = gz.astgen.mod;
1315 if (var_decl.comptime_token) |comptime_token| {
1316 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
1317 }
1318 if (var_decl.ast.align_node != 0) {
1319 return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{});
1320 }
1321 const astgen = gz.astgen;
1322 const tree = gz.tree();
1323 const token_tags = tree.tokens.items(.tag);
1324
1325 const name_token = var_decl.ast.mut_token + 1;
1326 const name_src = gz.tokSrcLoc(name_token);
1327 const ident_name = try mod.identifierTokenString(scope, name_token);
1328
1329 // Local variables shadowing detection, including function parameters.
1330 {
1331 var s = scope;
1332 while (true) switch (s.tag) {
1333 .local_val => {
1334 const local_val = s.cast(Scope.LocalVal).?;
1335 if (mem.eql(u8, local_val.name, ident_name)) {
1336 const msg = msg: {
1337 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
1338 ident_name,
1339 });
1340 errdefer msg.destroy(mod.gpa);
1341 try mod.errNote(scope, local_val.src, msg, "previous definition is here", .{});
1342 break :msg msg;
1343 };
1344 return mod.failWithOwnedErrorMsg(scope, msg);
1345 }
1346 s = local_val.parent;
1347 },
1348 .local_ptr => {
1349 const local_ptr = s.cast(Scope.LocalPtr).?;
1350 if (mem.eql(u8, local_ptr.name, ident_name)) {
1351 const msg = msg: {
1352 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
1353 ident_name,
1354 });
1355 errdefer msg.destroy(mod.gpa);
1356 try mod.errNote(scope, local_ptr.src, msg, "previous definition is here", .{});
1357 break :msg msg;
1358 };
1359 return mod.failWithOwnedErrorMsg(scope, msg);
1360 }
1361 s = local_ptr.parent;
1362 },
1363 .gen_zir => s = s.cast(GenZir).?.parent,
1364 else => break,
1365 };
1366 }
1367
1368 // Namespace vars shadowing detection
1369 if (mod.lookupDeclName(scope, ident_name)) |_| {
1370 // TODO add note for other definition
1371 return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
1372 }
1373 if (var_decl.ast.init_node == 0) {
1374 return mod.fail(scope, name_src, "variables must be initialized", .{});
1375 }
1376
1377 switch (token_tags[var_decl.ast.mut_token]) {
1378 .keyword_const => {
1379 // Depending on the type of AST the initialization expression is, we may need an lvalue
1380 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
1381 // the variable, no memory location needed.
1382 if (!nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node)) {
1383 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{
1384 .ty = try typeExpr(gz, scope, var_decl.ast.type_node),
1385 } else .none;
1386 const init_inst = try expr(gz, scope, result_loc, var_decl.ast.init_node);
1387 const sub_scope = try block_arena.create(Scope.LocalVal);
1388 sub_scope.* = .{
1389 .parent = scope,
1390 .gen_zir = gz,
1391 .name = ident_name,
1392 .inst = init_inst,
1393 .src = name_src,
1394 };
1395 return &sub_scope.base;
1396 }
1397
1398 // Detect whether the initialization expression actually uses the
1399 // result location pointer.
1400 var init_scope: GenZir = .{
1401 .parent = scope,
1402 .force_comptime = gz.force_comptime,
1403 .astgen = astgen,
1404 };
1405 defer init_scope.instructions.deinit(mod.gpa);
1406
1407 var resolve_inferred_alloc: zir.Inst.Ref = .none;
1408 var opt_type_inst: zir.Inst.Ref = .none;
1409 if (var_decl.ast.type_node != 0) {
1410 const type_inst = try typeExpr(gz, &init_scope.base, var_decl.ast.type_node);
1411 opt_type_inst = type_inst;
1412 init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node);
1413 init_scope.rl_ty_inst = type_inst;
1414 } else {
1415 const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node);
1416 resolve_inferred_alloc = alloc;
1417 init_scope.rl_ptr = alloc;
1418 }
1419 const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
1420 const init_inst = try expr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node);
1421 const zir_tags = astgen.instructions.items(.tag);
1422 const zir_datas = astgen.instructions.items(.data);
1423
1424 const parent_zir = &gz.instructions;
1425 if (init_scope.rvalue_rl_count == 1) {
1426 // Result location pointer not used. We don't need an alloc for this
1427 // const local, and type inference becomes trivial.
1428 // Move the init_scope instructions into the parent scope, eliding
1429 // the alloc instruction and the store_to_block_ptr instruction.
1430 const expected_len = parent_zir.items.len + init_scope.instructions.items.len - 2;
1431 try parent_zir.ensureCapacity(mod.gpa, expected_len);
1432 for (init_scope.instructions.items) |src_inst| {
1433 if (astgen.indexToRef(src_inst) == init_scope.rl_ptr) continue;
1434 if (zir_tags[src_inst] == .store_to_block_ptr) {
1435 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue;
1436 }
1437 parent_zir.appendAssumeCapacity(src_inst);
1438 }
1439 assert(parent_zir.items.len == expected_len);
1440
1441 const sub_scope = try block_arena.create(Scope.LocalVal);
1442 sub_scope.* = .{
1443 .parent = scope,
1444 .gen_zir = gz,
1445 .name = ident_name,
1446 .inst = init_inst,
1447 .src = name_src,
1448 };
1449 return &sub_scope.base;
1450 }
1451 // The initialization expression took advantage of the result location
1452 // of the const local. In this case we will create an alloc and a LocalPtr for it.
1453 // Move the init_scope instructions into the parent scope, swapping
1454 // store_to_block_ptr for store_to_inferred_ptr.
1455 const expected_len = parent_zir.items.len + init_scope.instructions.items.len;
1456 try parent_zir.ensureCapacity(mod.gpa, expected_len);
1457 for (init_scope.instructions.items) |src_inst| {
1458 if (zir_tags[src_inst] == .store_to_block_ptr) {
1459 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) {
1460 zir_tags[src_inst] = .store_to_inferred_ptr;
1461 }
1462 }
1463 parent_zir.appendAssumeCapacity(src_inst);
1464 }
1465 assert(parent_zir.items.len == expected_len);
1466 if (resolve_inferred_alloc != .none) {
1467 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
1468 }
1469 const sub_scope = try block_arena.create(Scope.LocalPtr);
1470 sub_scope.* = .{
1471 .parent = scope,
1472 .gen_zir = gz,
1473 .name = ident_name,
1474 .ptr = init_scope.rl_ptr,
1475 .src = name_src,
1476 };
1477 return &sub_scope.base;
1478 },
1479 .keyword_var => {
1480 var resolve_inferred_alloc: zir.Inst.Ref = .none;
1481 const var_data: struct {
1482 result_loc: ResultLoc,
1483 alloc: zir.Inst.Ref,
1484 } = if (var_decl.ast.type_node != 0) a: {
1485 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
1486
1487 const alloc = try gz.addUnNode(.alloc_mut, type_inst, node);
1488 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
1489 } else a: {
1490 const alloc = try gz.addUnNode(.alloc_inferred_mut, undefined, node);
1491 resolve_inferred_alloc = alloc;
1492 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } };
1493 };
1494 const init_inst = try expr(gz, scope, var_data.result_loc, var_decl.ast.init_node);
1495 if (resolve_inferred_alloc != .none) {
1496 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
1497 }
1498 const sub_scope = try block_arena.create(Scope.LocalPtr);
1499 sub_scope.* = .{
1500 .parent = scope,
1501 .gen_zir = gz,
1502 .name = ident_name,
1503 .ptr = var_data.alloc,
1504 .src = name_src,
1505 };
1506 return &sub_scope.base;
1507 },
1508 else => unreachable,
1509 }
1510}
1511
1512fn assign(gz: *GenZir, scope: *Scope, infix_node: ast.Node.Index) InnerError!void {
1513 const tree = gz.tree();
1514 const node_datas = tree.nodes.items(.data);
1515 const main_tokens = tree.nodes.items(.main_token);
1516 const node_tags = tree.nodes.items(.tag);
1517
1518 const lhs = node_datas[infix_node].lhs;
1519 const rhs = node_datas[infix_node].rhs;
1520 if (node_tags[lhs] == .identifier) {
1521 // This intentionally does not support `@"_"` syntax.
1522 const ident_name = tree.tokenSlice(main_tokens[lhs]);
1523 if (mem.eql(u8, ident_name, "_")) {
1524 _ = try expr(gz, scope, .discard, rhs);
1525 return;
1526 }
1527 }
1528 const lvalue = try lvalExpr(gz, scope, lhs);
1529 _ = try expr(gz, scope, .{ .ptr = lvalue }, rhs);
1530}
1531
1532fn assignOp(
1533 gz: *GenZir,
1534 scope: *Scope,
1535 infix_node: ast.Node.Index,
1536 op_inst_tag: zir.Inst.Tag,
1537) InnerError!void {
1538 const tree = gz.tree();
1539 const node_datas = tree.nodes.items(.data);
1540
1541 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
1542 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
1543 const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
1544 const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
1545
1546 const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{
1547 .lhs = lhs,
1548 .rhs = rhs,
1549 });
1550 _ = try gz.addBin(.store, lhs_ptr, result);
1551}
1552
1553fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
1554 const tree = gz.tree();
1555 const node_datas = tree.nodes.items(.data);
1556
1557 const operand = try expr(gz, scope, .{ .ty = .bool_type }, node_datas[node].lhs);
1558 const result = try gz.addUnNode(.bool_not, operand, node);
1559 return rvalue(gz, scope, rl, result, node);
1560}
1561
1562fn bitNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
1563 const tree = gz.tree();
1564 const node_datas = tree.nodes.items(.data);
1565
1566 const operand = try expr(gz, scope, .none, node_datas[node].lhs);
1567 const result = try gz.addUnNode(.bit_not, operand, node);
1568 return rvalue(gz, scope, rl, result, node);
1569}
1570
1571fn negation(
1572 gz: *GenZir,
1573 scope: *Scope,
1574 rl: ResultLoc,
1575 node: ast.Node.Index,
1576 tag: zir.Inst.Tag,
1577) InnerError!zir.Inst.Ref {
1578 const tree = gz.tree();
1579 const node_datas = tree.nodes.items(.data);
1580
1581 const operand = try expr(gz, scope, .none, node_datas[node].lhs);
1582 const result = try gz.addUnNode(tag, operand, node);
1583 return rvalue(gz, scope, rl, result, node);
1584}
1585
1586fn ptrType(
1587 gz: *GenZir,
1588 scope: *Scope,
1589 rl: ResultLoc,
1590 node: ast.Node.Index,
1591 ptr_info: ast.full.PtrType,
1592) InnerError!zir.Inst.Ref {
1593 const tree = gz.tree();
1594
1595 const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
1596
1597 const simple = ptr_info.ast.align_node == 0 and
1598 ptr_info.ast.sentinel == 0 and
1599 ptr_info.ast.bit_range_start == 0;
1600
1601 if (simple) {
1602 const result = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
1603 .ptr_type_simple = .{
1604 .is_allowzero = ptr_info.allowzero_token != null,
1605 .is_mutable = ptr_info.const_token == null,
1606 .is_volatile = ptr_info.volatile_token != null,
1607 .size = ptr_info.size,
1608 .elem_type = elem_type,
1609 },
1610 } });
1611 return rvalue(gz, scope, rl, result, node);
1612 }
1613
1614 var sentinel_ref: zir.Inst.Ref = .none;
1615 var align_ref: zir.Inst.Ref = .none;
1616 var bit_start_ref: zir.Inst.Ref = .none;
1617 var bit_end_ref: zir.Inst.Ref = .none;
1618 var trailing_count: u32 = 0;
1619
1620 if (ptr_info.ast.sentinel != 0) {
1621 sentinel_ref = try expr(gz, scope, .{ .ty = elem_type }, ptr_info.ast.sentinel);
1622 trailing_count += 1;
1623 }
1624 if (ptr_info.ast.align_node != 0) {
1625 align_ref = try expr(gz, scope, .none, ptr_info.ast.align_node);
1626 trailing_count += 1;
1627 }
1628 if (ptr_info.ast.bit_range_start != 0) {
1629 assert(ptr_info.ast.bit_range_end != 0);
1630 bit_start_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_start);
1631 bit_end_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_end);
1632 trailing_count += 2;
1633 }
1634
1635 const gpa = gz.astgen.mod.gpa;
1636 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1637 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1638 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1639 @typeInfo(zir.Inst.PtrType).Struct.fields.len + trailing_count);
1640
1641 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.PtrType{ .elem_type = elem_type });
1642 if (sentinel_ref != .none) {
1643 gz.astgen.extra.appendAssumeCapacity(@enumToInt(sentinel_ref));
1644 }
1645 if (align_ref != .none) {
1646 gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref));
1647 }
1648 if (bit_start_ref != .none) {
1649 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
1650 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
1651 }
1652
1653 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1654 const result = gz.astgen.indexToRef(new_index);
1655 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
1656 .ptr_type = .{
1657 .flags = .{
1658 .is_allowzero = ptr_info.allowzero_token != null,
1659 .is_mutable = ptr_info.const_token == null,
1660 .is_volatile = ptr_info.volatile_token != null,
1661 .has_sentinel = sentinel_ref != .none,
1662 .has_align = align_ref != .none,
1663 .has_bit_range = bit_start_ref != .none,
1664 },
1665 .size = ptr_info.size,
1666 .payload_index = payload_index,
1667 },
1668 } });
1669 gz.instructions.appendAssumeCapacity(new_index);
1670
1671 return rvalue(gz, scope, rl, result, node);
1672}
1673
1674fn arrayType(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
1675 const tree = gz.tree();
1676 const node_datas = tree.nodes.items(.data);
1677
1678 // TODO check for [_]T
1679 const len = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].lhs);
1680 const elem_type = try typeExpr(gz, scope, node_datas[node].rhs);
1681
1682 const result = try gz.addBin(.array_type, len, elem_type);
1683 return rvalue(gz, scope, rl, result, node);
1684}
1685
1686fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
1687 const tree = gz.tree();
1688 const node_datas = tree.nodes.items(.data);
1689 const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel);
1690
1691 // TODO check for [_]T
1692 const len = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].lhs);
1693 const elem_type = try typeExpr(gz, scope, extra.elem_type);
1694 const sentinel = try expr(gz, scope, .{ .ty = elem_type }, extra.sentinel);
1695
1696 const result = try gz.addArrayTypeSentinel(len, elem_type, sentinel);
1697 return rvalue(gz, scope, rl, result, node);
1698}
1699
1700fn containerDecl(
1701 gz: *GenZir,
1702 scope: *Scope,
1703 rl: ResultLoc,
1704 container_decl: ast.full.ContainerDecl,
1705) InnerError!zir.Inst.Ref {
1706 return gz.astgen.mod.failTok(scope, container_decl.ast.main_token, "TODO implement container decls", .{});
1707}
1708
1709fn errorSetDecl(
1710 gz: *GenZir,
1711 scope: *Scope,
1712 rl: ResultLoc,
1713 node: ast.Node.Index,
1714) InnerError!zir.Inst.Ref {
1715 const mod = gz.astgen.mod;
1716 const tree = gz.tree();
1717 const main_tokens = tree.nodes.items(.main_token);
1718 const token_tags = tree.tokens.items(.tag);
1719 const arena = gz.astgen.arena;
1720
1721 // Count how many fields there are.
1722 const error_token = main_tokens[node];
1723 const count: usize = count: {
1724 var tok_i = error_token + 2;
1725 var count: usize = 0;
1726 while (true) : (tok_i += 1) {
1727 switch (token_tags[tok_i]) {
1728 .doc_comment, .comma => {},
1729 .identifier => count += 1,
1730 .r_brace => break :count count,
1731 else => unreachable,
1732 }
1733 } else unreachable; // TODO should not need else unreachable here
1734 };
1735
1736 const fields = try arena.alloc([]const u8, count);
1737 {
1738 var tok_i = error_token + 2;
1739 var field_i: usize = 0;
1740 while (true) : (tok_i += 1) {
1741 switch (token_tags[tok_i]) {
1742 .doc_comment, .comma => {},
1743 .identifier => {
1744 fields[field_i] = try mod.identifierTokenString(scope, tok_i);
1745 field_i += 1;
1746 },
1747 .r_brace => break,
1748 else => unreachable,
1749 }
1750 }
1751 }
1752 const error_set = try arena.create(Module.ErrorSet);
1753 error_set.* = .{
1754 .owner_decl = gz.astgen.decl,
1755 .node_offset = gz.astgen.decl.nodeIndexToRelative(node),
1756 .names_ptr = fields.ptr,
1757 .names_len = @intCast(u32, fields.len),
1758 };
1759 const error_set_ty = try Type.Tag.error_set.create(arena, error_set);
1760 const typed_value = try arena.create(TypedValue);
1761 typed_value.* = .{
1762 .ty = Type.initTag(.type),
1763 .val = try Value.Tag.ty.create(arena, error_set_ty),
1764 };
1765 const result = try gz.addConst(typed_value);
1766 return rvalue(gz, scope, rl, result, node);
1767}
1768
1769fn orelseCatchExpr(
1770 parent_gz: *GenZir,
1771 scope: *Scope,
1772 rl: ResultLoc,
1773 node: ast.Node.Index,
1774 lhs: ast.Node.Index,
1775 cond_op: zir.Inst.Tag,
1776 unwrap_op: zir.Inst.Tag,
1777 unwrap_code_op: zir.Inst.Tag,
1778 rhs: ast.Node.Index,
1779 payload_token: ?ast.TokenIndex,
1780) InnerError!zir.Inst.Ref {
1781 const mod = parent_gz.astgen.mod;
1782 const tree = parent_gz.tree();
1783
1784 var block_scope: GenZir = .{
1785 .parent = scope,
1786 .astgen = parent_gz.astgen,
1787 .force_comptime = parent_gz.force_comptime,
1788 .instructions = .{},
1789 };
1790 block_scope.setBreakResultLoc(rl);
1791 defer block_scope.instructions.deinit(mod.gpa);
1792
1793 // This could be a pointer or value depending on the `operand_rl` parameter.
1794 // We cannot use `block_scope.break_result_loc` because that has the bare
1795 // type, whereas this expression has the optional type. Later we make
1796 // up for this fact by calling rvalue on the else branch.
1797 block_scope.break_count += 1;
1798
1799 // TODO handle catch
1800 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
1801 .ref => .ref,
1802 .discard, .none, .block_ptr, .inferred_ptr => .none,
1803 .ty => |elem_ty| blk: {
1804 const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node);
1805 break :blk .{ .ty = wrapped_ty };
1806 },
1807 .ptr => |ptr_ty| blk: {
1808 const wrapped_ty = try block_scope.addUnNode(.optional_type_from_ptr_elem, ptr_ty, node);
1809 break :blk .{ .ty = wrapped_ty };
1810 },
1811 };
1812 const operand = try expr(&block_scope, &block_scope.base, operand_rl, lhs);
1813 const cond = try block_scope.addUnNode(cond_op, operand, node);
1814 const condbr = try block_scope.addCondBr(.condbr, node);
1815
1816 const block = try parent_gz.addBlock(.block, node);
1817 try parent_gz.instructions.append(mod.gpa, block);
1818 try block_scope.setBlockBody(block);
1819
1820 var then_scope: GenZir = .{
1821 .parent = scope,
1822 .astgen = parent_gz.astgen,
1823 .force_comptime = block_scope.force_comptime,
1824 .instructions = .{},
1825 };
1826 defer then_scope.instructions.deinit(mod.gpa);
1827
1828 var err_val_scope: Scope.LocalVal = undefined;
1829 const then_sub_scope = blk: {
1830 const payload = payload_token orelse break :blk &then_scope.base;
1831 if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
1832 return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{});
1833 }
1834 const err_name = try mod.identifierTokenString(scope, payload);
1835 err_val_scope = .{
1836 .parent = &then_scope.base,
1837 .gen_zir = &then_scope,
1838 .name = err_name,
1839 .inst = try then_scope.addUnNode(unwrap_code_op, operand, node),
1840 .src = parent_gz.tokSrcLoc(payload),
1841 };
1842 break :blk &err_val_scope.base;
1843 };
1844
1845 block_scope.break_count += 1;
1846 const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, rhs);
1847 // We hold off on the break instructions as well as copying the then/else
1848 // instructions into place until we know whether to keep store_to_block_ptr
1849 // instructions or not.
1850
1851 var else_scope: GenZir = .{
1852 .parent = scope,
1853 .astgen = parent_gz.astgen,
1854 .force_comptime = block_scope.force_comptime,
1855 .instructions = .{},
1856 };
1857 defer else_scope.instructions.deinit(mod.gpa);
1858
1859 // This could be a pointer or value depending on `unwrap_op`.
1860 const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node);
1861 const else_result = switch (rl) {
1862 .ref => unwrapped_payload,
1863 else => try rvalue(&else_scope, &else_scope.base, block_scope.break_result_loc, unwrapped_payload, node),
1864 };
1865
1866 return finishThenElseBlock(
1867 parent_gz,
1868 scope,
1869 rl,
1870 node,
1871 &block_scope,
1872 &then_scope,
1873 &else_scope,
1874 condbr,
1875 cond,
1876 node,
1877 node,
1878 then_result,
1879 else_result,
1880 block,
1881 block,
1882 .@"break",
1883 );
1884}
1885
1886fn finishThenElseBlock(
1887 parent_gz: *GenZir,
1888 parent_scope: *Scope,
1889 rl: ResultLoc,
1890 node: ast.Node.Index,
1891 block_scope: *GenZir,
1892 then_scope: *GenZir,
1893 else_scope: *GenZir,
1894 condbr: zir.Inst.Index,
1895 cond: zir.Inst.Ref,
1896 then_src: ast.Node.Index,
1897 else_src: ast.Node.Index,
1898 then_result: zir.Inst.Ref,
1899 else_result: zir.Inst.Ref,
1900 main_block: zir.Inst.Index,
1901 then_break_block: zir.Inst.Index,
1902 break_tag: zir.Inst.Tag,
1903) InnerError!zir.Inst.Ref {
1904 // We now have enough information to decide whether the result instruction should
1905 // be communicated via result location pointer or break instructions.
1906 const strat = rl.strategy(block_scope);
1907 const astgen = block_scope.astgen;
1908 switch (strat.tag) {
1909 .break_void => {
1910 if (!astgen.refIsNoReturn(then_result)) {
1911 _ = try then_scope.addBreak(break_tag, then_break_block, .void_value);
1912 }
1913 const elide_else = if (else_result != .none) astgen.refIsNoReturn(else_result) else false;
1914 if (!elide_else) {
1915 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
1916 }
1917 assert(!strat.elide_store_to_block_ptr_instructions);
1918 try setCondBrPayload(condbr, cond, then_scope, else_scope);
1919 return astgen.indexToRef(main_block);
1920 },
1921 .break_operand => {
1922 if (!astgen.refIsNoReturn(then_result)) {
1923 _ = try then_scope.addBreak(break_tag, then_break_block, then_result);
1924 }
1925 if (else_result != .none) {
1926 if (!astgen.refIsNoReturn(else_result)) {
1927 _ = try else_scope.addBreak(break_tag, main_block, else_result);
1928 }
1929 } else {
1930 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
1931 }
1932 if (strat.elide_store_to_block_ptr_instructions) {
1933 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope);
1934 } else {
1935 try setCondBrPayload(condbr, cond, then_scope, else_scope);
1936 }
1937 const block_ref = astgen.indexToRef(main_block);
1938 switch (rl) {
1939 .ref => return block_ref,
1940 else => return rvalue(parent_gz, parent_scope, rl, block_ref, node),
1941 }
1942 },
1943 }
1944}
1945
1946/// Return whether the identifier names of two tokens are equal. Resolves @""
1947/// tokens without allocating.
1948/// OK in theory it could do it without allocating. This implementation
1949/// allocates when the @"" form is used.
1950fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
1951 const ident_name_1 = try mod.identifierTokenString(scope, token1);
1952 const ident_name_2 = try mod.identifierTokenString(scope, token2);
1953 return mem.eql(u8, ident_name_1, ident_name_2);
1954}
1955
1956pub fn fieldAccess(
1957 gz: *GenZir,
1958 scope: *Scope,
1959 rl: ResultLoc,
1960 node: ast.Node.Index,
1961) InnerError!zir.Inst.Ref {
1962 const mod = gz.astgen.mod;
1963 const tree = gz.tree();
1964 const main_tokens = tree.nodes.items(.main_token);
1965 const node_datas = tree.nodes.items(.data);
1966
1967 const object_node = node_datas[node].lhs;
1968 const dot_token = main_tokens[node];
1969 const field_ident = dot_token + 1;
1970 const string_bytes = &gz.astgen.string_bytes;
1971 const str_index = @intCast(u32, string_bytes.items.len);
1972 try mod.appendIdentStr(scope, field_ident, string_bytes);
1973 try string_bytes.append(mod.gpa, 0);
1974 switch (rl) {
1975 .ref => return gz.addPlNode(.field_ptr, node, zir.Inst.Field{
1976 .lhs = try expr(gz, scope, .ref, object_node),
1977 .field_name_start = str_index,
1978 }),
1979 else => return rvalue(gz, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{
1980 .lhs = try expr(gz, scope, .none, object_node),
1981 .field_name_start = str_index,
1982 }), node),
1983 }
1984}
1985
1986fn arrayAccess(
1987 gz: *GenZir,
1988 scope: *Scope,
1989 rl: ResultLoc,
1990 node: ast.Node.Index,
1991) InnerError!zir.Inst.Ref {
1992 const tree = gz.tree();
1993 const main_tokens = tree.nodes.items(.main_token);
1994 const node_datas = tree.nodes.items(.data);
1995 switch (rl) {
1996 .ref => return gz.addBin(
1997 .elem_ptr,
1998 try expr(gz, scope, .ref, node_datas[node].lhs),
1999 try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
2000 ),
2001 else => return rvalue(gz, scope, rl, try gz.addBin(
2002 .elem_val,
2003 try expr(gz, scope, .none, node_datas[node].lhs),
2004 try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
2005 ), node),
2006 }
2007}
2008
2009fn simpleBinOp(
2010 gz: *GenZir,
2011 scope: *Scope,
2012 rl: ResultLoc,
2013 node: ast.Node.Index,
2014 op_inst_tag: zir.Inst.Tag,
2015) InnerError!zir.Inst.Ref {
2016 const tree = gz.tree();
2017 const node_datas = tree.nodes.items(.data);
2018
2019 const result = try gz.addPlNode(op_inst_tag, node, zir.Inst.Bin{
2020 .lhs = try expr(gz, scope, .none, node_datas[node].lhs),
2021 .rhs = try expr(gz, scope, .none, node_datas[node].rhs),
2022 });
2023 return rvalue(gz, scope, rl, result, node);
2024}
2025
2026fn simpleStrTok(
2027 gz: *GenZir,
2028 scope: *Scope,
2029 rl: ResultLoc,
2030 ident_token: ast.TokenIndex,
2031 node: ast.Node.Index,
2032 op_inst_tag: zir.Inst.Tag,
2033) InnerError!zir.Inst.Ref {
2034 const mod = gz.astgen.mod;
2035 const string_bytes = &gz.astgen.string_bytes;
2036 const str_index = @intCast(u32, string_bytes.items.len);
2037 try mod.appendIdentStr(scope, ident_token, string_bytes);
2038 try string_bytes.append(mod.gpa, 0);
2039 const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
2040 return rvalue(gz, scope, rl, result, node);
2041}
2042
2043fn boolBinOp(
2044 gz: *GenZir,
2045 scope: *Scope,
2046 rl: ResultLoc,
2047 node: ast.Node.Index,
2048 zir_tag: zir.Inst.Tag,
2049) InnerError!zir.Inst.Ref {
2050 const node_datas = gz.tree().nodes.items(.data);
2051
2052 const lhs = try expr(gz, scope, .{ .ty = .bool_type }, node_datas[node].lhs);
2053 const bool_br = try gz.addBoolBr(zir_tag, lhs);
2054
2055 var rhs_scope: GenZir = .{
2056 .parent = scope,
2057 .astgen = gz.astgen,
2058 .force_comptime = gz.force_comptime,
2059 };
2060 defer rhs_scope.instructions.deinit(gz.astgen.mod.gpa);
2061 const rhs = try expr(&rhs_scope, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs);
2062 _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
2063 try rhs_scope.setBoolBrBody(bool_br);
2064
2065 const block_ref = gz.astgen.indexToRef(bool_br);
2066 return rvalue(gz, scope, rl, block_ref, node);
2067}
2068
2069fn ifExpr(
2070 parent_gz: *GenZir,
2071 scope: *Scope,
2072 rl: ResultLoc,
2073 node: ast.Node.Index,
2074 if_full: ast.full.If,
2075) InnerError!zir.Inst.Ref {
2076 const mod = parent_gz.astgen.mod;
2077
2078 var block_scope: GenZir = .{
2079 .parent = scope,
2080 .astgen = parent_gz.astgen,
2081 .force_comptime = parent_gz.force_comptime,
2082 .instructions = .{},
2083 };
2084 block_scope.setBreakResultLoc(rl);
2085 defer block_scope.instructions.deinit(mod.gpa);
2086
2087 const cond = c: {
2088 // TODO https://github.com/ziglang/zig/issues/7929
2089 if (if_full.error_token) |error_token| {
2090 return mod.failTok(scope, error_token, "TODO implement if error union", .{});
2091 } else if (if_full.payload_token) |payload_token| {
2092 return mod.failTok(scope, payload_token, "TODO implement if optional", .{});
2093 } else {
2094 break :c try expr(&block_scope, &block_scope.base, .{ .ty = .bool_type }, if_full.ast.cond_expr);
2095 }
2096 };
2097
2098 const condbr = try block_scope.addCondBr(.condbr, node);
2099
2100 const block = try parent_gz.addBlock(.block, node);
2101 try parent_gz.instructions.append(mod.gpa, block);
2102 try block_scope.setBlockBody(block);
2103
2104 var then_scope: GenZir = .{
2105 .parent = scope,
2106 .astgen = parent_gz.astgen,
2107 .force_comptime = block_scope.force_comptime,
2108 .instructions = .{},
2109 };
2110 defer then_scope.instructions.deinit(mod.gpa);
2111
2112 // declare payload to the then_scope
2113 const then_sub_scope = &then_scope.base;
2114
2115 block_scope.break_count += 1;
2116 const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);
2117 // We hold off on the break instructions as well as copying the then/else
2118 // instructions into place until we know whether to keep store_to_block_ptr
2119 // instructions or not.
2120
2121 var else_scope: GenZir = .{
2122 .parent = scope,
2123 .astgen = parent_gz.astgen,
2124 .force_comptime = block_scope.force_comptime,
2125 .instructions = .{},
2126 };
2127 defer else_scope.instructions.deinit(mod.gpa);
2128
2129 const else_node = if_full.ast.else_expr;
2130 const else_info: struct {
2131 src: ast.Node.Index,
2132 result: zir.Inst.Ref,
2133 } = if (else_node != 0) blk: {
2134 block_scope.break_count += 1;
2135 const sub_scope = &else_scope.base;
2136 break :blk .{
2137 .src = else_node,
2138 .result = try expr(&else_scope, sub_scope, block_scope.break_result_loc, else_node),
2139 };
2140 } else .{
2141 .src = if_full.ast.then_expr,
2142 .result = .none,
2143 };
2144
2145 return finishThenElseBlock(
2146 parent_gz,
2147 scope,
2148 rl,
2149 node,
2150 &block_scope,
2151 &then_scope,
2152 &else_scope,
2153 condbr,
2154 cond,
2155 if_full.ast.then_expr,
2156 else_info.src,
2157 then_result,
2158 else_info.result,
2159 block,
2160 block,
2161 .@"break",
2162 );
2163}
2164
2165fn setCondBrPayload(
2166 condbr: zir.Inst.Index,
2167 cond: zir.Inst.Ref,
2168 then_scope: *GenZir,
2169 else_scope: *GenZir,
2170) !void {
2171 const astgen = then_scope.astgen;
2172
2173 try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len +
2174 @typeInfo(zir.Inst.CondBr).Struct.fields.len +
2175 then_scope.instructions.items.len + else_scope.instructions.items.len);
2176
2177 const zir_datas = astgen.instructions.items(.data);
2178 zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{
2179 .condition = cond,
2180 .then_body_len = @intCast(u32, then_scope.instructions.items.len),
2181 .else_body_len = @intCast(u32, else_scope.instructions.items.len),
2182 });
2183 astgen.extra.appendSliceAssumeCapacity(then_scope.instructions.items);
2184 astgen.extra.appendSliceAssumeCapacity(else_scope.instructions.items);
2185}
2186
2187/// If `elide_block_store_ptr` is set, expects to find exactly 1 .store_to_block_ptr instruction.
2188fn setCondBrPayloadElideBlockStorePtr(
2189 condbr: zir.Inst.Index,
2190 cond: zir.Inst.Ref,
2191 then_scope: *GenZir,
2192 else_scope: *GenZir,
2193) !void {
2194 const astgen = then_scope.astgen;
2195
2196 try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len +
2197 @typeInfo(zir.Inst.CondBr).Struct.fields.len +
2198 then_scope.instructions.items.len + else_scope.instructions.items.len - 2);
2199
2200 const zir_datas = astgen.instructions.items(.data);
2201 zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{
2202 .condition = cond,
2203 .then_body_len = @intCast(u32, then_scope.instructions.items.len - 1),
2204 .else_body_len = @intCast(u32, else_scope.instructions.items.len - 1),
2205 });
2206
2207 const zir_tags = astgen.instructions.items(.tag);
2208 for ([_]*GenZir{ then_scope, else_scope }) |scope| {
2209 for (scope.instructions.items) |src_inst| {
2210 if (zir_tags[src_inst] != .store_to_block_ptr) {
2211 astgen.extra.appendAssumeCapacity(src_inst);
2212 }
2213 }
2214 }
2215}
2216
2217fn whileExpr(
2218 parent_gz: *GenZir,
2219 scope: *Scope,
2220 rl: ResultLoc,
2221 node: ast.Node.Index,
2222 while_full: ast.full.While,
2223) InnerError!zir.Inst.Ref {
2224 const mod = parent_gz.astgen.mod;
2225 if (while_full.label_token) |label_token| {
2226 try checkLabelRedefinition(mod, scope, label_token);
2227 }
2228
2229 const is_inline = parent_gz.force_comptime or while_full.inline_token != null;
2230 const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
2231 const loop_block = try parent_gz.addBlock(loop_tag, node);
2232 try parent_gz.instructions.append(mod.gpa, loop_block);
2233
2234 var loop_scope: GenZir = .{
2235 .parent = scope,
2236 .astgen = parent_gz.astgen,
2237 .force_comptime = parent_gz.force_comptime,
2238 .instructions = .{},
2239 };
2240 loop_scope.setBreakResultLoc(rl);
2241 defer loop_scope.instructions.deinit(mod.gpa);
2242
2243 var continue_scope: GenZir = .{
2244 .parent = &loop_scope.base,
2245 .astgen = parent_gz.astgen,
2246 .force_comptime = loop_scope.force_comptime,
2247 .instructions = .{},
2248 };
2249 defer continue_scope.instructions.deinit(mod.gpa);
2250
2251 const cond = c: {
2252 // TODO https://github.com/ziglang/zig/issues/7929
2253 if (while_full.error_token) |error_token| {
2254 return mod.failTok(scope, error_token, "TODO implement while error union", .{});
2255 } else if (while_full.payload_token) |payload_token| {
2256 return mod.failTok(scope, payload_token, "TODO implement while optional", .{});
2257 } else {
2258 const bool_type_rl: ResultLoc = .{ .ty = .bool_type };
2259 break :c try expr(&continue_scope, &continue_scope.base, bool_type_rl, while_full.ast.cond_expr);
2260 }
2261 };
2262
2263 const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
2264 const condbr = try continue_scope.addCondBr(condbr_tag, node);
2265 const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
2266 const cond_block = try loop_scope.addBlock(block_tag, node);
2267 try loop_scope.instructions.append(mod.gpa, cond_block);
2268 try continue_scope.setBlockBody(cond_block);
2269
2270 // TODO avoid emitting the continue expr when there
2271 // are no jumps to it. This happens when the last statement of a while body is noreturn
2272 // and there are no `continue` statements.
2273 if (while_full.ast.cont_expr != 0) {
2274 _ = try expr(&loop_scope, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr);
2275 }
2276 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
2277 _ = try loop_scope.addNode(repeat_tag, node);
2278
2279 try loop_scope.setBlockBody(loop_block);
2280 loop_scope.break_block = loop_block;
2281 loop_scope.continue_block = cond_block;
2282 if (while_full.label_token) |label_token| {
2283 loop_scope.label = @as(?GenZir.Label, GenZir.Label{
2284 .token = label_token,
2285 .block_inst = loop_block,
2286 });
2287 }
2288
2289 var then_scope: GenZir = .{
2290 .parent = &continue_scope.base,
2291 .astgen = parent_gz.astgen,
2292 .force_comptime = continue_scope.force_comptime,
2293 .instructions = .{},
2294 };
2295 defer then_scope.instructions.deinit(mod.gpa);
2296
2297 const then_sub_scope = &then_scope.base;
2298
2299 loop_scope.break_count += 1;
2300 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
2301
2302 var else_scope: GenZir = .{
2303 .parent = &continue_scope.base,
2304 .astgen = parent_gz.astgen,
2305 .force_comptime = continue_scope.force_comptime,
2306 .instructions = .{},
2307 };
2308 defer else_scope.instructions.deinit(mod.gpa);
2309
2310 const else_node = while_full.ast.else_expr;
2311 const else_info: struct {
2312 src: ast.Node.Index,
2313 result: zir.Inst.Ref,
2314 } = if (else_node != 0) blk: {
2315 loop_scope.break_count += 1;
2316 const sub_scope = &else_scope.base;
2317 break :blk .{
2318 .src = else_node,
2319 .result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node),
2320 };
2321 } else .{
2322 .src = while_full.ast.then_expr,
2323 .result = .none,
2324 };
2325
2326 if (loop_scope.label) |some| {
2327 if (!some.used) {
2328 return mod.failTok(scope, some.token, "unused while loop label", .{});
2329 }
2330 }
2331 const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
2332 return finishThenElseBlock(
2333 parent_gz,
2334 scope,
2335 rl,
2336 node,
2337 &loop_scope,
2338 &then_scope,
2339 &else_scope,
2340 condbr,
2341 cond,
2342 while_full.ast.then_expr,
2343 else_info.src,
2344 then_result,
2345 else_info.result,
2346 loop_block,
2347 cond_block,
2348 break_tag,
2349 );
2350}
2351
2352fn forExpr(
2353 parent_gz: *GenZir,
2354 scope: *Scope,
2355 rl: ResultLoc,
2356 node: ast.Node.Index,
2357 for_full: ast.full.While,
2358) InnerError!zir.Inst.Ref {
2359 const mod = parent_gz.astgen.mod;
2360 if (for_full.label_token) |label_token| {
2361 try checkLabelRedefinition(mod, scope, label_token);
2362 }
2363 // Set up variables and constants.
2364 const is_inline = parent_gz.force_comptime or for_full.inline_token != null;
2365 const tree = parent_gz.tree();
2366 const token_tags = tree.tokens.items(.tag);
2367
2368 const array_ptr = try expr(parent_gz, scope, .ref, for_full.ast.cond_expr);
2369 const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
2370
2371 const index_ptr = blk: {
2372 const index_ptr = try parent_gz.addUnNode(.alloc, .usize_type, node);
2373 // initialize to zero
2374 _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
2375 break :blk index_ptr;
2376 };
2377
2378 const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
2379 const loop_block = try parent_gz.addBlock(loop_tag, node);
2380 try parent_gz.instructions.append(mod.gpa, loop_block);
2381
2382 var loop_scope: GenZir = .{
2383 .parent = scope,
2384 .astgen = parent_gz.astgen,
2385 .force_comptime = parent_gz.force_comptime,
2386 .instructions = .{},
2387 };
2388 loop_scope.setBreakResultLoc(rl);
2389 defer loop_scope.instructions.deinit(mod.gpa);
2390
2391 var cond_scope: GenZir = .{
2392 .parent = &loop_scope.base,
2393 .astgen = parent_gz.astgen,
2394 .force_comptime = loop_scope.force_comptime,
2395 .instructions = .{},
2396 };
2397 defer cond_scope.instructions.deinit(mod.gpa);
2398
2399 // check condition i < array_expr.len
2400 const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
2401 const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, zir.Inst.Bin{
2402 .lhs = index,
2403 .rhs = len,
2404 });
2405
2406 const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
2407 const condbr = try cond_scope.addCondBr(condbr_tag, node);
2408 const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
2409 const cond_block = try loop_scope.addBlock(block_tag, node);
2410 try loop_scope.instructions.append(mod.gpa, cond_block);
2411 try cond_scope.setBlockBody(cond_block);
2412
2413 // Increment the index variable.
2414 const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
2415 const index_plus_one = try loop_scope.addPlNode(.add, node, zir.Inst.Bin{
2416 .lhs = index_2,
2417 .rhs = .one_usize,
2418 });
2419 _ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
2420 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
2421 _ = try loop_scope.addNode(repeat_tag, node);
2422
2423 try loop_scope.setBlockBody(loop_block);
2424 loop_scope.break_block = loop_block;
2425 loop_scope.continue_block = cond_block;
2426 if (for_full.label_token) |label_token| {
2427 loop_scope.label = @as(?GenZir.Label, GenZir.Label{
2428 .token = label_token,
2429 .block_inst = loop_block,
2430 });
2431 }
2432
2433 var then_scope: GenZir = .{
2434 .parent = &cond_scope.base,
2435 .astgen = parent_gz.astgen,
2436 .force_comptime = cond_scope.force_comptime,
2437 .instructions = .{},
2438 };
2439 defer then_scope.instructions.deinit(mod.gpa);
2440
2441 var index_scope: Scope.LocalPtr = undefined;
2442 const then_sub_scope = blk: {
2443 const payload_token = for_full.payload_token.?;
2444 const ident = if (token_tags[payload_token] == .asterisk)
2445 payload_token + 1
2446 else
2447 payload_token;
2448 const is_ptr = ident != payload_token;
2449 const value_name = tree.tokenSlice(ident);
2450 if (!mem.eql(u8, value_name, "_")) {
2451 return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{});
2452 } else if (is_ptr) {
2453 return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{});
2454 }
2455
2456 const index_token = if (token_tags[ident + 1] == .comma)
2457 ident + 2
2458 else
2459 break :blk &then_scope.base;
2460 if (mem.eql(u8, tree.tokenSlice(index_token), "_")) {
2461 return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{});
2462 }
2463 const index_name = try mod.identifierTokenString(&then_scope.base, index_token);
2464 index_scope = .{
2465 .parent = &then_scope.base,
2466 .gen_zir = &then_scope,
2467 .name = index_name,
2468 .ptr = index_ptr,
2469 .src = parent_gz.tokSrcLoc(index_token),
2470 };
2471 break :blk &index_scope.base;
2472 };
2473
2474 loop_scope.break_count += 1;
2475 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
2476
2477 var else_scope: GenZir = .{
2478 .parent = &cond_scope.base,
2479 .astgen = parent_gz.astgen,
2480 .force_comptime = cond_scope.force_comptime,
2481 .instructions = .{},
2482 };
2483 defer else_scope.instructions.deinit(mod.gpa);
2484
2485 const else_node = for_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 = for_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 for 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 for_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 getRangeNode(
2528 node_tags: []const ast.Node.Tag,
2529 node_datas: []const ast.Node.Data,
2530 node: ast.Node.Index,
2531) ?ast.Node.Index {
2532 switch (node_tags[node]) {
2533 .switch_range => return node,
2534 .grouped_expression => unreachable,
2535 else => return null,
2536 }
2537}
2538
2539pub const SwitchProngSrc = union(enum) {
2540 scalar: u32,
2541 multi: Multi,
2542 range: Multi,
2543
2544 pub const Multi = struct {
2545 prong: u32,
2546 item: u32,
2547 };
2548
2549 pub const RangeExpand = enum { none, first, last };
2550
2551 /// This function is intended to be called only when it is certain that we need
2552 /// the LazySrcLoc in order to emit a compile error.
2553 pub fn resolve(
2554 prong_src: SwitchProngSrc,
2555 decl: *Decl,
2556 switch_node_offset: i32,
2557 range_expand: RangeExpand,
2558 ) LazySrcLoc {
2559 @setCold(true);
2560 const switch_node = decl.relativeToNodeIndex(switch_node_offset);
2561 const tree = decl.container.file_scope.base.tree();
2562 const main_tokens = tree.nodes.items(.main_token);
2563 const node_datas = tree.nodes.items(.data);
2564 const node_tags = tree.nodes.items(.tag);
2565 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2566 const case_nodes = tree.extra_data[extra.start..extra.end];
2567
2568 var multi_i: u32 = 0;
2569 var scalar_i: u32 = 0;
2570 for (case_nodes) |case_node| {
2571 const case = switch (node_tags[case_node]) {
2572 .switch_case_one => tree.switchCaseOne(case_node),
2573 .switch_case => tree.switchCase(case_node),
2574 else => unreachable,
2575 };
2576 if (case.ast.values.len == 0)
2577 continue;
2578 if (case.ast.values.len == 1 and
2579 node_tags[case.ast.values[0]] == .identifier and
2580 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2581 {
2582 continue;
2583 }
2584 const is_multi = case.ast.values.len != 1 or
2585 getRangeNode(node_tags, node_datas, case.ast.values[0]) != null;
2586
2587 switch (prong_src) {
2588 .scalar => |i| if (!is_multi and i == scalar_i) return LazySrcLoc{
2589 .node_offset = decl.nodeIndexToRelative(case.ast.values[0]),
2590 },
2591 .multi => |s| if (is_multi and s.prong == multi_i) {
2592 var item_i: u32 = 0;
2593 for (case.ast.values) |item_node| {
2594 if (getRangeNode(node_tags, node_datas, item_node) != null)
2595 continue;
2596
2597 if (item_i == s.item) return LazySrcLoc{
2598 .node_offset = decl.nodeIndexToRelative(item_node),
2599 };
2600 item_i += 1;
2601 } else unreachable;
2602 },
2603 .range => |s| if (is_multi and s.prong == multi_i) {
2604 var range_i: u32 = 0;
2605 for (case.ast.values) |item_node| {
2606 const range = getRangeNode(node_tags, node_datas, item_node) orelse continue;
2607
2608 if (range_i == s.item) switch (range_expand) {
2609 .none => return LazySrcLoc{
2610 .node_offset = decl.nodeIndexToRelative(item_node),
2611 },
2612 .first => return LazySrcLoc{
2613 .node_offset = decl.nodeIndexToRelative(node_datas[range].lhs),
2614 },
2615 .last => return LazySrcLoc{
2616 .node_offset = decl.nodeIndexToRelative(node_datas[range].rhs),
2617 },
2618 };
2619 range_i += 1;
2620 } else unreachable;
2621 },
2622 }
2623 if (is_multi) {
2624 multi_i += 1;
2625 } else {
2626 scalar_i += 1;
2627 }
2628 } else unreachable;
2629 }
2630};
2631
2632fn switchExpr(
2633 parent_gz: *GenZir,
2634 scope: *Scope,
2635 rl: ResultLoc,
2636 switch_node: ast.Node.Index,
2637) InnerError!zir.Inst.Ref {
2638 const astgen = parent_gz.astgen;
2639 const mod = astgen.mod;
2640 const gpa = mod.gpa;
2641 const tree = parent_gz.tree();
2642 const node_datas = tree.nodes.items(.data);
2643 const node_tags = tree.nodes.items(.tag);
2644 const main_tokens = tree.nodes.items(.main_token);
2645 const token_tags = tree.tokens.items(.tag);
2646 const operand_node = node_datas[switch_node].lhs;
2647 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2648 const case_nodes = tree.extra_data[extra.start..extra.end];
2649
2650 // We perform two passes over the AST. This first pass is to collect information
2651 // for the following variables, make note of the special prong AST node index,
2652 // and bail out with a compile error if there are multiple special prongs present.
2653 var any_payload_is_ref = false;
2654 var scalar_cases_len: u32 = 0;
2655 var multi_cases_len: u32 = 0;
2656 var special_prong: zir.SpecialProng = .none;
2657 var special_node: ast.Node.Index = 0;
2658 var else_src: ?LazySrcLoc = null;
2659 var underscore_src: ?LazySrcLoc = null;
2660 for (case_nodes) |case_node| {
2661 const case = switch (node_tags[case_node]) {
2662 .switch_case_one => tree.switchCaseOne(case_node),
2663 .switch_case => tree.switchCase(case_node),
2664 else => unreachable,
2665 };
2666 if (case.payload_token) |payload_token| {
2667 if (token_tags[payload_token] == .asterisk) {
2668 any_payload_is_ref = true;
2669 }
2670 }
2671 // Check for else/`_` prong.
2672 if (case.ast.values.len == 0) {
2673 const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1);
2674 if (else_src) |src| {
2675 const msg = msg: {
2676 const msg = try mod.errMsg(
2677 scope,
2678 case_src,
2679 "multiple else prongs in switch expression",
2680 .{},
2681 );
2682 errdefer msg.destroy(gpa);
2683 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2684 break :msg msg;
2685 };
2686 return mod.failWithOwnedErrorMsg(scope, msg);
2687 } else if (underscore_src) |some_underscore| {
2688 const msg = msg: {
2689 const msg = try mod.errMsg(
2690 scope,
2691 parent_gz.nodeSrcLoc(switch_node),
2692 "else and '_' prong in switch expression",
2693 .{},
2694 );
2695 errdefer msg.destroy(gpa);
2696 try mod.errNote(scope, case_src, msg, "else prong is here", .{});
2697 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
2698 break :msg msg;
2699 };
2700 return mod.failWithOwnedErrorMsg(scope, msg);
2701 }
2702 special_node = case_node;
2703 special_prong = .@"else";
2704 else_src = case_src;
2705 continue;
2706 } else if (case.ast.values.len == 1 and
2707 node_tags[case.ast.values[0]] == .identifier and
2708 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2709 {
2710 const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1);
2711 if (underscore_src) |src| {
2712 const msg = msg: {
2713 const msg = try mod.errMsg(
2714 scope,
2715 case_src,
2716 "multiple '_' prongs in switch expression",
2717 .{},
2718 );
2719 errdefer msg.destroy(gpa);
2720 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2721 break :msg msg;
2722 };
2723 return mod.failWithOwnedErrorMsg(scope, msg);
2724 } else if (else_src) |some_else| {
2725 const msg = msg: {
2726 const msg = try mod.errMsg(
2727 scope,
2728 parent_gz.nodeSrcLoc(switch_node),
2729 "else and '_' prong in switch expression",
2730 .{},
2731 );
2732 errdefer msg.destroy(gpa);
2733 try mod.errNote(scope, some_else, msg, "else prong is here", .{});
2734 try mod.errNote(scope, case_src, msg, "'_' prong is here", .{});
2735 break :msg msg;
2736 };
2737 return mod.failWithOwnedErrorMsg(scope, msg);
2738 }
2739 special_node = case_node;
2740 special_prong = .under;
2741 underscore_src = case_src;
2742 continue;
2743 }
2744
2745 if (case.ast.values.len == 1 and
2746 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2747 {
2748 scalar_cases_len += 1;
2749 } else {
2750 multi_cases_len += 1;
2751 }
2752 }
2753
2754 const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
2755 const operand = try expr(parent_gz, scope, operand_rl, operand_node);
2756 // We need the type of the operand to use as the result location for all the prong items.
2757 const typeof_tag: zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof;
2758 const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node);
2759 const item_rl: ResultLoc = .{ .ty = operand_ty_inst };
2760
2761 // Contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti.
2762 // This is the header as well as the optional else prong body, as well as all the
2763 // scalar cases.
2764 // At the end we will memcpy this into place.
2765 var scalar_cases_payload = std.ArrayListUnmanaged(u32){};
2766 defer scalar_cases_payload.deinit(gpa);
2767 // Same deal, but this is only the `extra` data for the multi cases.
2768 var multi_cases_payload = std.ArrayListUnmanaged(u32){};
2769 defer multi_cases_payload.deinit(gpa);
2770
2771 var block_scope: GenZir = .{
2772 .parent = scope,
2773 .astgen = astgen,
2774 .force_comptime = parent_gz.force_comptime,
2775 .instructions = .{},
2776 };
2777 block_scope.setBreakResultLoc(rl);
2778 defer block_scope.instructions.deinit(gpa);
2779
2780 // This gets added to the parent block later, after the item expressions.
2781 const switch_block = try parent_gz.addBlock(undefined, switch_node);
2782
2783 // We re-use this same scope for all cases, including the special prong, if any.
2784 var case_scope: GenZir = .{
2785 .parent = &block_scope.base,
2786 .astgen = astgen,
2787 .force_comptime = parent_gz.force_comptime,
2788 .instructions = .{},
2789 };
2790 defer case_scope.instructions.deinit(gpa);
2791
2792 // Do the else/`_` first because it goes first in the payload.
2793 var capture_val_scope: Scope.LocalVal = undefined;
2794 if (special_node != 0) {
2795 const case = switch (node_tags[special_node]) {
2796 .switch_case_one => tree.switchCaseOne(special_node),
2797 .switch_case => tree.switchCase(special_node),
2798 else => unreachable,
2799 };
2800 const sub_scope = blk: {
2801 const payload_token = case.payload_token orelse break :blk &case_scope.base;
2802 const ident = if (token_tags[payload_token] == .asterisk)
2803 payload_token + 1
2804 else
2805 payload_token;
2806 const is_ptr = ident != payload_token;
2807 if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
2808 if (is_ptr) {
2809 return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{});
2810 }
2811 break :blk &case_scope.base;
2812 }
2813 const capture_tag: zir.Inst.Tag = if (is_ptr)
2814 .switch_capture_else_ref
2815 else
2816 .switch_capture_else;
2817 const capture = try case_scope.add(.{
2818 .tag = capture_tag,
2819 .data = .{ .switch_capture = .{
2820 .switch_inst = switch_block,
2821 .prong_index = undefined,
2822 } },
2823 });
2824 const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token);
2825 capture_val_scope = .{
2826 .parent = &case_scope.base,
2827 .gen_zir = &case_scope,
2828 .name = capture_name,
2829 .inst = capture,
2830 .src = parent_gz.tokSrcLoc(payload_token),
2831 };
2832 break :blk &capture_val_scope.base;
2833 };
2834 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
2835 if (!astgen.refIsNoReturn(case_result)) {
2836 block_scope.break_count += 1;
2837 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
2838 }
2839 // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`.
2840 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
2841 3 + // operand, scalar_cases_len, else body len
2842 @boolToInt(multi_cases_len != 0) +
2843 case_scope.instructions.items.len);
2844 scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
2845 scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
2846 if (multi_cases_len != 0) {
2847 scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
2848 }
2849 scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
2850 scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
2851 } else {
2852 // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`.
2853 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
2854 2 + // operand, scalar_cases_len
2855 @boolToInt(multi_cases_len != 0));
2856 scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
2857 scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
2858 if (multi_cases_len != 0) {
2859 scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
2860 }
2861 }
2862
2863 // In this pass we generate all the item and prong expressions except the special case.
2864 var multi_case_index: u32 = 0;
2865 var scalar_case_index: u32 = 0;
2866 for (case_nodes) |case_node| {
2867 if (case_node == special_node)
2868 continue;
2869 const case = switch (node_tags[case_node]) {
2870 .switch_case_one => tree.switchCaseOne(case_node),
2871 .switch_case => tree.switchCase(case_node),
2872 else => unreachable,
2873 };
2874
2875 // Reset the scope.
2876 case_scope.instructions.shrinkRetainingCapacity(0);
2877
2878 const is_multi_case = case.ast.values.len != 1 or
2879 getRangeNode(node_tags, node_datas, case.ast.values[0]) != null;
2880
2881 const sub_scope = blk: {
2882 const payload_token = case.payload_token orelse break :blk &case_scope.base;
2883 const ident = if (token_tags[payload_token] == .asterisk)
2884 payload_token + 1
2885 else
2886 payload_token;
2887 const is_ptr = ident != payload_token;
2888 if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
2889 if (is_ptr) {
2890 return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{});
2891 }
2892 break :blk &case_scope.base;
2893 }
2894 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
2895 const is_ptr_bits: u2 = @boolToInt(is_ptr);
2896 const capture_tag: zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
2897 0b00 => .switch_capture,
2898 0b01 => .switch_capture_ref,
2899 0b10 => .switch_capture_multi,
2900 0b11 => .switch_capture_multi_ref,
2901 };
2902 const capture_index = if (is_multi_case) ci: {
2903 multi_case_index += 1;
2904 break :ci multi_case_index - 1;
2905 } else ci: {
2906 scalar_case_index += 1;
2907 break :ci scalar_case_index - 1;
2908 };
2909 const capture = try case_scope.add(.{
2910 .tag = capture_tag,
2911 .data = .{ .switch_capture = .{
2912 .switch_inst = switch_block,
2913 .prong_index = capture_index,
2914 } },
2915 });
2916 const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token);
2917 capture_val_scope = .{
2918 .parent = &case_scope.base,
2919 .gen_zir = &case_scope,
2920 .name = capture_name,
2921 .inst = capture,
2922 .src = parent_gz.tokSrcLoc(payload_token),
2923 };
2924 break :blk &capture_val_scope.base;
2925 };
2926
2927 if (is_multi_case) {
2928 // items_len, ranges_len, body_len
2929 const header_index = multi_cases_payload.items.len;
2930 try multi_cases_payload.resize(gpa, multi_cases_payload.items.len + 3);
2931
2932 // items
2933 var items_len: u32 = 0;
2934 for (case.ast.values) |item_node| {
2935 if (getRangeNode(node_tags, node_datas, item_node) != null) continue;
2936 items_len += 1;
2937
2938 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
2939 try multi_cases_payload.append(gpa, @enumToInt(item_inst));
2940 }
2941
2942 // ranges
2943 var ranges_len: u32 = 0;
2944 for (case.ast.values) |item_node| {
2945 const range = getRangeNode(node_tags, node_datas, item_node) orelse continue;
2946 ranges_len += 1;
2947
2948 const first = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].lhs);
2949 const last = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].rhs);
2950 try multi_cases_payload.appendSlice(gpa, &[_]u32{
2951 @enumToInt(first), @enumToInt(last),
2952 });
2953 }
2954
2955 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
2956 if (!astgen.refIsNoReturn(case_result)) {
2957 block_scope.break_count += 1;
2958 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
2959 }
2960
2961 multi_cases_payload.items[header_index + 0] = items_len;
2962 multi_cases_payload.items[header_index + 1] = ranges_len;
2963 multi_cases_payload.items[header_index + 2] = @intCast(u32, case_scope.instructions.items.len);
2964 try multi_cases_payload.appendSlice(gpa, case_scope.instructions.items);
2965 } else {
2966 const item_node = case.ast.values[0];
2967 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
2968 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
2969 if (!astgen.refIsNoReturn(case_result)) {
2970 block_scope.break_count += 1;
2971 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
2972 }
2973 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
2974 2 + case_scope.instructions.items.len);
2975 scalar_cases_payload.appendAssumeCapacity(@enumToInt(item_inst));
2976 scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
2977 scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
2978 }
2979 }
2980 // Now that the item expressions are generated we can add this.
2981 try parent_gz.instructions.append(gpa, switch_block);
2982
2983 const ref_bit: u4 = @boolToInt(any_payload_is_ref);
2984 const multi_bit: u4 = @boolToInt(multi_cases_len != 0);
2985 const special_prong_bits: u4 = @enumToInt(special_prong);
2986 comptime {
2987 assert(@enumToInt(zir.SpecialProng.none) == 0b00);
2988 assert(@enumToInt(zir.SpecialProng.@"else") == 0b01);
2989 assert(@enumToInt(zir.SpecialProng.under) == 0b10);
2990 }
2991 const zir_tags = astgen.instructions.items(.tag);
2992 zir_tags[switch_block] = switch ((ref_bit << 3) | (special_prong_bits << 1) | multi_bit) {
2993 0b0_00_0 => .switch_block,
2994 0b0_00_1 => .switch_block_multi,
2995 0b0_01_0 => .switch_block_else,
2996 0b0_01_1 => .switch_block_else_multi,
2997 0b0_10_0 => .switch_block_under,
2998 0b0_10_1 => .switch_block_under_multi,
2999 0b1_00_0 => .switch_block_ref,
3000 0b1_00_1 => .switch_block_ref_multi,
3001 0b1_01_0 => .switch_block_ref_else,
3002 0b1_01_1 => .switch_block_ref_else_multi,
3003 0b1_10_0 => .switch_block_ref_under,
3004 0b1_10_1 => .switch_block_ref_under_multi,
3005 else => unreachable,
3006 };
3007 const payload_index = astgen.extra.items.len;
3008 const zir_datas = astgen.instructions.items(.data);
3009 zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index);
3010 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
3011 scalar_cases_payload.items.len + multi_cases_payload.items.len);
3012 const strat = rl.strategy(&block_scope);
3013 switch (strat.tag) {
3014 .break_operand => {
3015 // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus
3016 // this is always true.
3017 assert(strat.elide_store_to_block_ptr_instructions);
3018
3019 // There will necessarily be a store_to_block_ptr for
3020 // all prongs, except for prongs that ended with a noreturn instruction.
3021 // Elide all the `store_to_block_ptr` instructions.
3022
3023 // The break instructions need to have their operands coerced if the
3024 // switch's result location is a `ty`. In this case we overwrite the
3025 // `store_to_block_ptr` instruction with an `as` instruction and repurpose
3026 // it as the break operand.
3027
3028 var extra_index: usize = 0;
3029 extra_index += 2;
3030 extra_index += @boolToInt(multi_cases_len != 0);
3031 if (special_prong != .none) special_prong: {
3032 const body_len_index = extra_index;
3033 const body_len = scalar_cases_payload.items[extra_index];
3034 extra_index += 1;
3035 if (body_len < 2) {
3036 extra_index += body_len;
3037 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3038 break :special_prong;
3039 }
3040 extra_index += body_len - 2;
3041 const store_inst = scalar_cases_payload.items[extra_index];
3042 if (zir_tags[store_inst] != .store_to_block_ptr) {
3043 extra_index += 2;
3044 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3045 break :special_prong;
3046 }
3047 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
3048 if (block_scope.rl_ty_inst != .none) {
3049 extra_index += 1;
3050 const break_inst = scalar_cases_payload.items[extra_index];
3051 extra_index += 1;
3052 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3053 zir_tags[store_inst] = .as;
3054 zir_datas[store_inst].bin = .{
3055 .lhs = block_scope.rl_ty_inst,
3056 .rhs = zir_datas[break_inst].@"break".operand,
3057 };
3058 zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
3059 } else {
3060 scalar_cases_payload.items[body_len_index] -= 1;
3061 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3062 extra_index += 1;
3063 astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
3064 extra_index += 1;
3065 }
3066 } else {
3067 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3068 }
3069 var scalar_i: u32 = 0;
3070 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
3071 const start_index = extra_index;
3072 extra_index += 1;
3073 const body_len_index = extra_index;
3074 const body_len = scalar_cases_payload.items[extra_index];
3075 extra_index += 1;
3076 if (body_len < 2) {
3077 extra_index += body_len;
3078 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
3079 continue;
3080 }
3081 extra_index += body_len - 2;
3082 const store_inst = scalar_cases_payload.items[extra_index];
3083 if (zir_tags[store_inst] != .store_to_block_ptr) {
3084 extra_index += 2;
3085 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
3086 continue;
3087 }
3088 if (block_scope.rl_ty_inst != .none) {
3089 extra_index += 1;
3090 const break_inst = scalar_cases_payload.items[extra_index];
3091 extra_index += 1;
3092 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
3093 zir_tags[store_inst] = .as;
3094 zir_datas[store_inst].bin = .{
3095 .lhs = block_scope.rl_ty_inst,
3096 .rhs = zir_datas[break_inst].@"break".operand,
3097 };
3098 zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
3099 } else {
3100 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
3101 scalar_cases_payload.items[body_len_index] -= 1;
3102 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
3103 extra_index += 1;
3104 astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
3105 extra_index += 1;
3106 }
3107 }
3108 extra_index = 0;
3109 var multi_i: u32 = 0;
3110 while (multi_i < multi_cases_len) : (multi_i += 1) {
3111 const start_index = extra_index;
3112 const items_len = multi_cases_payload.items[extra_index];
3113 extra_index += 1;
3114 const ranges_len = multi_cases_payload.items[extra_index];
3115 extra_index += 1;
3116 const body_len_index = extra_index;
3117 const body_len = multi_cases_payload.items[extra_index];
3118 extra_index += 1;
3119 extra_index += items_len;
3120 extra_index += 2 * ranges_len;
3121 if (body_len < 2) {
3122 extra_index += body_len;
3123 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3124 continue;
3125 }
3126 extra_index += body_len - 2;
3127 const store_inst = multi_cases_payload.items[extra_index];
3128 if (zir_tags[store_inst] != .store_to_block_ptr) {
3129 extra_index += 2;
3130 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3131 continue;
3132 }
3133 if (block_scope.rl_ty_inst != .none) {
3134 extra_index += 1;
3135 const break_inst = multi_cases_payload.items[extra_index];
3136 extra_index += 1;
3137 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3138 zir_tags[store_inst] = .as;
3139 zir_datas[store_inst].bin = .{
3140 .lhs = block_scope.rl_ty_inst,
3141 .rhs = zir_datas[break_inst].@"break".operand,
3142 };
3143 zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
3144 } else {
3145 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
3146 multi_cases_payload.items[body_len_index] -= 1;
3147 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3148 extra_index += 1;
3149 astgen.extra.appendAssumeCapacity(multi_cases_payload.items[extra_index]);
3150 extra_index += 1;
3151 }
3152 }
3153
3154 const block_ref = astgen.indexToRef(switch_block);
3155 switch (rl) {
3156 .ref => return block_ref,
3157 else => return rvalue(parent_gz, scope, rl, block_ref, switch_node),
3158 }
3159 },
3160 .break_void => {
3161 assert(!strat.elide_store_to_block_ptr_instructions);
3162 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
3163 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
3164 // Modify all the terminating instruction tags to become `break` variants.
3165 var extra_index: usize = payload_index;
3166 extra_index += 2;
3167 extra_index += @boolToInt(multi_cases_len != 0);
3168 if (special_prong != .none) {
3169 const body_len = astgen.extra.items[extra_index];
3170 extra_index += 1;
3171 const body = astgen.extra.items[extra_index..][0..body_len];
3172 extra_index += body_len;
3173 const last = body[body.len - 1];
3174 if (zir_tags[last] == .@"break" and
3175 zir_datas[last].@"break".block_inst == switch_block)
3176 {
3177 zir_datas[last].@"break".operand = .void_value;
3178 }
3179 }
3180 var scalar_i: u32 = 0;
3181 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
3182 extra_index += 1;
3183 const body_len = astgen.extra.items[extra_index];
3184 extra_index += 1;
3185 const body = astgen.extra.items[extra_index..][0..body_len];
3186 extra_index += body_len;
3187 const last = body[body.len - 1];
3188 if (zir_tags[last] == .@"break" and
3189 zir_datas[last].@"break".block_inst == switch_block)
3190 {
3191 zir_datas[last].@"break".operand = .void_value;
3192 }
3193 }
3194 var multi_i: u32 = 0;
3195 while (multi_i < multi_cases_len) : (multi_i += 1) {
3196 const items_len = astgen.extra.items[extra_index];
3197 extra_index += 1;
3198 const ranges_len = astgen.extra.items[extra_index];
3199 extra_index += 1;
3200 const body_len = astgen.extra.items[extra_index];
3201 extra_index += 1;
3202 extra_index += items_len;
3203 extra_index += 2 * ranges_len;
3204 const body = astgen.extra.items[extra_index..][0..body_len];
3205 extra_index += body_len;
3206 const last = body[body.len - 1];
3207 if (zir_tags[last] == .@"break" and
3208 zir_datas[last].@"break".block_inst == switch_block)
3209 {
3210 zir_datas[last].@"break".operand = .void_value;
3211 }
3212 }
3213
3214 return astgen.indexToRef(switch_block);
3215 },
3216 }
3217}
3218
3219fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
3220 const tree = gz.tree();
3221 const node_datas = tree.nodes.items(.data);
3222 const main_tokens = tree.nodes.items(.main_token);
3223
3224 const operand_node = node_datas[node].lhs;
3225 const operand: zir.Inst.Ref = if (operand_node != 0) operand: {
3226 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{
3227 .ptr = try gz.addNode(.ret_ptr, node),
3228 } else .{
3229 .ty = try gz.addNode(.ret_type, node),
3230 };
3231 break :operand try expr(gz, scope, rl, operand_node);
3232 } else .void_value;
3233 _ = try gz.addUnNode(.ret_node, operand, node);
3234 return zir.Inst.Ref.unreachable_value;
3235}
3236
3237fn identifier(
3238 gz: *GenZir,
3239 scope: *Scope,
3240 rl: ResultLoc,
3241 ident: ast.Node.Index,
3242) InnerError!zir.Inst.Ref {
3243 const tracy = trace(@src());
3244 defer tracy.end();
3245
3246 const mod = gz.astgen.mod;
3247 const tree = gz.tree();
3248 const main_tokens = tree.nodes.items(.main_token);
3249
3250 const ident_token = main_tokens[ident];
3251 const ident_name = try mod.identifierTokenString(scope, ident_token);
3252 if (mem.eql(u8, ident_name, "_")) {
3253 return mod.failNode(scope, ident, "TODO implement '_' identifier", .{});
3254 }
3255
3256 if (simple_types.get(ident_name)) |zir_const_ref| {
3257 return rvalue(gz, scope, rl, zir_const_ref, ident);
3258 }
3259
3260 if (ident_name.len >= 2) integer: {
3261 const first_c = ident_name[0];
3262 if (first_c == 'i' or first_c == 'u') {
3263 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
3264 true => .signed,
3265 false => .unsigned,
3266 };
3267 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
3268 error.Overflow => return mod.failNode(
3269 scope,
3270 ident,
3271 "primitive integer type '{s}' exceeds maximum bit width of 65535",
3272 .{ident_name},
3273 ),
3274 error.InvalidCharacter => break :integer,
3275 };
3276 const result = try gz.add(.{
3277 .tag = .int_type,
3278 .data = .{ .int_type = .{
3279 .src_node = gz.astgen.decl.nodeIndexToRelative(ident),
3280 .signedness = signedness,
3281 .bit_count = bit_count,
3282 } },
3283 });
3284 return rvalue(gz, scope, rl, result, ident);
3285 }
3286 }
3287
3288 // Local variables, including function parameters.
3289 {
3290 var s = scope;
3291 while (true) switch (s.tag) {
3292 .local_val => {
3293 const local_val = s.cast(Scope.LocalVal).?;
3294 if (mem.eql(u8, local_val.name, ident_name)) {
3295 return rvalue(gz, scope, rl, local_val.inst, ident);
3296 }
3297 s = local_val.parent;
3298 },
3299 .local_ptr => {
3300 const local_ptr = s.cast(Scope.LocalPtr).?;
3301 if (mem.eql(u8, local_ptr.name, ident_name)) {
3302 if (rl == .ref) return local_ptr.ptr;
3303 const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident);
3304 return rvalue(gz, scope, rl, loaded, ident);
3305 }
3306 s = local_ptr.parent;
3307 },
3308 .gen_zir => s = s.cast(GenZir).?.parent,
3309 else => break,
3310 };
3311 }
3312
3313 const gop = try gz.astgen.decl_map.getOrPut(mod.gpa, ident_name);
3314 if (!gop.found_existing) {
3315 const decl = mod.lookupDeclName(scope, ident_name) orelse
3316 return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name});
3317 try gz.astgen.decls.append(mod.gpa, decl);
3318 }
3319 const decl_index = @intCast(u32, gop.index);
3320 switch (rl) {
3321 .ref => return gz.addDecl(.decl_ref, decl_index, ident),
3322 else => return rvalue(gz, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident),
3323 }
3324}
3325
3326fn stringLiteral(
3327 gz: *GenZir,
3328 scope: *Scope,
3329 rl: ResultLoc,
3330 node: ast.Node.Index,
3331) InnerError!zir.Inst.Ref {
3332 const tree = gz.tree();
3333 const main_tokens = tree.nodes.items(.main_token);
3334 const string_bytes = &gz.astgen.string_bytes;
3335 const str_index = string_bytes.items.len;
3336 const str_lit_token = main_tokens[node];
3337 const token_bytes = tree.tokenSlice(str_lit_token);
3338 try gz.astgen.mod.parseStrLit(scope, str_lit_token, string_bytes, token_bytes, 0);
3339 const str_len = string_bytes.items.len - str_index;
3340 const result = try gz.add(.{
3341 .tag = .str,
3342 .data = .{ .str = .{
3343 .start = @intCast(u32, str_index),
3344 .len = @intCast(u32, str_len),
3345 } },
3346 });
3347 return rvalue(gz, scope, rl, result, node);
3348}
3349
3350fn multilineStringLiteral(
3351 gz: *GenZir,
3352 scope: *Scope,
3353 rl: ResultLoc,
3354 node: ast.Node.Index,
3355) InnerError!zir.Inst.Ref {
3356 const tree = gz.tree();
3357 const node_datas = tree.nodes.items(.data);
3358 const main_tokens = tree.nodes.items(.main_token);
3359
3360 const start = node_datas[node].lhs;
3361 const end = node_datas[node].rhs;
3362
3363 const gpa = gz.astgen.mod.gpa;
3364 const string_bytes = &gz.astgen.string_bytes;
3365 const str_index = string_bytes.items.len;
3366
3367 // First line: do not append a newline.
3368 var tok_i = start;
3369 {
3370 const slice = tree.tokenSlice(tok_i);
3371 const line_bytes = slice[2 .. slice.len - 1];
3372 try string_bytes.appendSlice(gpa, line_bytes);
3373 tok_i += 1;
3374 }
3375 // Following lines: each line prepends a newline.
3376 while (tok_i <= end) : (tok_i += 1) {
3377 const slice = tree.tokenSlice(tok_i);
3378 const line_bytes = slice[2 .. slice.len - 1];
3379 try string_bytes.ensureCapacity(gpa, string_bytes.items.len + line_bytes.len + 1);
3380 string_bytes.appendAssumeCapacity('\n');
3381 string_bytes.appendSliceAssumeCapacity(line_bytes);
3382 }
3383 const result = try gz.add(.{
3384 .tag = .str,
3385 .data = .{ .str = .{
3386 .start = @intCast(u32, str_index),
3387 .len = @intCast(u32, string_bytes.items.len - str_index),
3388 } },
3389 });
3390 return rvalue(gz, scope, rl, result, node);
3391}
3392
3393fn charLiteral(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
3394 const mod = gz.astgen.mod;
3395 const tree = gz.tree();
3396 const main_tokens = tree.nodes.items(.main_token);
3397 const main_token = main_tokens[node];
3398 const slice = tree.tokenSlice(main_token);
3399
3400 var bad_index: usize = undefined;
3401 const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
3402 error.InvalidCharacter => {
3403 const bad_byte = slice[bad_index];
3404 const token_starts = tree.tokens.items(.start);
3405 const src_off = @intCast(u32, token_starts[main_token] + bad_index);
3406 return mod.failOff(scope, src_off, "invalid character: '{c}'\n", .{bad_byte});
3407 },
3408 };
3409 const result = try gz.addInt(value);
3410 return rvalue(gz, scope, rl, result, node);
3411}
3412
3413fn integerLiteral(
3414 gz: *GenZir,
3415 scope: *Scope,
3416 rl: ResultLoc,
3417 node: ast.Node.Index,
3418) InnerError!zir.Inst.Ref {
3419 const tree = gz.tree();
3420 const main_tokens = tree.nodes.items(.main_token);
3421 const int_token = main_tokens[node];
3422 const prefixed_bytes = tree.tokenSlice(int_token);
3423 if (std.fmt.parseInt(u64, prefixed_bytes, 0)) |small_int| {
3424 const result: zir.Inst.Ref = switch (small_int) {
3425 0 => .zero,
3426 1 => .one,
3427 else => try gz.addInt(small_int),
3428 };
3429 return rvalue(gz, scope, rl, result, node);
3430 } else |err| {
3431 return gz.astgen.mod.failNode(scope, node, "TODO implement int literals that don't fit in a u64", .{});
3432 }
3433}
3434
3435fn floatLiteral(
3436 gz: *GenZir,
3437 scope: *Scope,
3438 rl: ResultLoc,
3439 node: ast.Node.Index,
3440) InnerError!zir.Inst.Ref {
3441 const arena = gz.astgen.arena;
3442 const tree = gz.tree();
3443 const main_tokens = tree.nodes.items(.main_token);
3444
3445 const main_token = main_tokens[node];
3446 const bytes = tree.tokenSlice(main_token);
3447 if (bytes.len > 2 and bytes[1] == 'x') {
3448 assert(bytes[0] == '0'); // validated by tokenizer
3449 return gz.astgen.mod.failTok(scope, main_token, "TODO implement hex floats", .{});
3450 }
3451 const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
3452 error.InvalidCharacter => unreachable, // validated by tokenizer
3453 };
3454 const typed_value = try arena.create(TypedValue);
3455 typed_value.* = .{
3456 .ty = Type.initTag(.comptime_float),
3457 .val = try Value.Tag.float_128.create(arena, float_number),
3458 };
3459 const result = try gz.addConst(typed_value);
3460 return rvalue(gz, scope, rl, result, node);
3461}
3462
3463fn asmExpr(
3464 gz: *GenZir,
3465 scope: *Scope,
3466 rl: ResultLoc,
3467 node: ast.Node.Index,
3468 full: ast.full.Asm,
3469) InnerError!zir.Inst.Ref {
3470 const mod = gz.astgen.mod;
3471 const arena = gz.astgen.arena;
3472 const tree = gz.tree();
3473 const main_tokens = tree.nodes.items(.main_token);
3474 const node_datas = tree.nodes.items(.data);
3475
3476 const asm_source = try expr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);
3477
3478 if (full.outputs.len != 0) {
3479 // when implementing this be sure to add test coverage for the asm return type
3480 // not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc)
3481 return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{});
3482 }
3483
3484 const constraints = try arena.alloc(u32, full.inputs.len);
3485 const args = try arena.alloc(zir.Inst.Ref, full.inputs.len);
3486
3487 for (full.inputs) |input, i| {
3488 const constraint_token = main_tokens[input] + 2;
3489 const string_bytes = &gz.astgen.string_bytes;
3490 constraints[i] = @intCast(u32, string_bytes.items.len);
3491 const token_bytes = tree.tokenSlice(constraint_token);
3492 try mod.parseStrLit(scope, constraint_token, string_bytes, token_bytes, 0);
3493 try string_bytes.append(mod.gpa, 0);
3494
3495 args[i] = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input].lhs);
3496 }
3497
3498 const tag: zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm";
3499 const result = try gz.addPlNode(tag, node, zir.Inst.Asm{
3500 .asm_source = asm_source,
3501 .return_type = .void_type,
3502 .output = .none,
3503 .args_len = @intCast(u32, full.inputs.len),
3504 .clobbers_len = 0, // TODO implement asm clobbers
3505 });
3506
3507 try gz.astgen.extra.ensureCapacity(mod.gpa, gz.astgen.extra.items.len +
3508 args.len + constraints.len);
3509 gz.astgen.appendRefsAssumeCapacity(args);
3510 gz.astgen.extra.appendSliceAssumeCapacity(constraints);
3511
3512 return rvalue(gz, scope, rl, result, node);
3513}
3514
3515fn as(
3516 gz: *GenZir,
3517 scope: *Scope,
3518 rl: ResultLoc,
3519 node: ast.Node.Index,
3520 lhs: ast.Node.Index,
3521 rhs: ast.Node.Index,
3522) InnerError!zir.Inst.Ref {
3523 const dest_type = try typeExpr(gz, scope, lhs);
3524 switch (rl) {
3525 .none, .discard, .ref, .ty => {
3526 const result = try expr(gz, scope, .{ .ty = dest_type }, rhs);
3527 return rvalue(gz, scope, rl, result, node);
3528 },
3529
3530 .ptr => |result_ptr| {
3531 return asRlPtr(gz, scope, rl, result_ptr, rhs, dest_type);
3532 },
3533 .block_ptr => |block_scope| {
3534 return asRlPtr(gz, scope, rl, block_scope.rl_ptr, rhs, dest_type);
3535 },
3536
3537 .inferred_ptr => |result_alloc| {
3538 // TODO here we should be able to resolve the inference; we now have a type for the result.
3539 return gz.astgen.mod.failNode(scope, node, "TODO implement @as with inferred-type result location pointer", .{});
3540 },
3541 }
3542}
3543
3544fn asRlPtr(
3545 parent_gz: *GenZir,
3546 scope: *Scope,
3547 rl: ResultLoc,
3548 result_ptr: zir.Inst.Ref,
3549 operand_node: ast.Node.Index,
3550 dest_type: zir.Inst.Ref,
3551) InnerError!zir.Inst.Ref {
3552 // Detect whether this expr() call goes into rvalue() to store the result into the
3553 // result location. If it does, elide the coerce_result_ptr instruction
3554 // as well as the store instruction, instead passing the result as an rvalue.
3555 const astgen = parent_gz.astgen;
3556
3557 var as_scope: GenZir = .{
3558 .parent = scope,
3559 .astgen = astgen,
3560 .force_comptime = parent_gz.force_comptime,
3561 .instructions = .{},
3562 };
3563 defer as_scope.instructions.deinit(astgen.mod.gpa);
3564
3565 as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
3566 const result = try expr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);
3567 const parent_zir = &parent_gz.instructions;
3568 if (as_scope.rvalue_rl_count == 1) {
3569 // Busted! This expression didn't actually need a pointer.
3570 const zir_tags = astgen.instructions.items(.tag);
3571 const zir_datas = astgen.instructions.items(.data);
3572 const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2;
3573 try parent_zir.ensureCapacity(astgen.mod.gpa, expected_len);
3574 for (as_scope.instructions.items) |src_inst| {
3575 if (astgen.indexToRef(src_inst) == as_scope.rl_ptr) continue;
3576 if (zir_tags[src_inst] == .store_to_block_ptr) {
3577 if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
3578 }
3579 parent_zir.appendAssumeCapacity(src_inst);
3580 }
3581 assert(parent_zir.items.len == expected_len);
3582 const casted_result = try parent_gz.addBin(.as, dest_type, result);
3583 return rvalue(parent_gz, scope, rl, casted_result, operand_node);
3584 } else {
3585 try parent_zir.appendSlice(astgen.mod.gpa, as_scope.instructions.items);
3586 return result;
3587 }
3588}
3589
3590fn bitCast(
3591 gz: *GenZir,
3592 scope: *Scope,
3593 rl: ResultLoc,
3594 node: ast.Node.Index,
3595 lhs: ast.Node.Index,
3596 rhs: ast.Node.Index,
3597) InnerError!zir.Inst.Ref {
3598 const mod = gz.astgen.mod;
3599 const dest_type = try typeExpr(gz, scope, lhs);
3600 switch (rl) {
3601 .none, .discard, .ty => {
3602 const operand = try expr(gz, scope, .none, rhs);
3603 const result = try gz.addPlNode(.bitcast, node, zir.Inst.Bin{
3604 .lhs = dest_type,
3605 .rhs = operand,
3606 });
3607 return rvalue(gz, scope, rl, result, node);
3608 },
3609 .ref => unreachable, // `@bitCast` is not allowed as an r-value.
3610 .ptr => |result_ptr| {
3611 const casted_result_ptr = try gz.addUnNode(.bitcast_result_ptr, result_ptr, node);
3612 return expr(gz, scope, .{ .ptr = casted_result_ptr }, rhs);
3613 },
3614 .block_ptr => |block_ptr| {
3615 return mod.failNode(scope, node, "TODO implement @bitCast with result location inferred peer types", .{});
3616 },
3617 .inferred_ptr => |result_alloc| {
3618 // TODO here we should be able to resolve the inference; we now have a type for the result.
3619 return mod.failNode(scope, node, "TODO implement @bitCast with inferred-type result location pointer", .{});
3620 },
3621 }
3622}
3623
3624fn typeOf(
3625 gz: *GenZir,
3626 scope: *Scope,
3627 rl: ResultLoc,
3628 node: ast.Node.Index,
3629 params: []const ast.Node.Index,
3630) InnerError!zir.Inst.Ref {
3631 if (params.len < 1) {
3632 return gz.astgen.mod.failNode(scope, node, "expected at least 1 argument, found 0", .{});
3633 }
3634 if (params.len == 1) {
3635 const result = try gz.addUnNode(.typeof, try expr(gz, scope, .none, params[0]), node);
3636 return rvalue(gz, scope, rl, result, node);
3637 }
3638 const arena = gz.astgen.arena;
3639 var items = try arena.alloc(zir.Inst.Ref, params.len);
3640 for (params) |param, param_i| {
3641 items[param_i] = try expr(gz, scope, .none, param);
3642 }
3643
3644 const result = try gz.addPlNode(.typeof_peer, node, zir.Inst.MultiOp{
3645 .operands_len = @intCast(u32, params.len),
3646 });
3647 try gz.astgen.appendRefs(items);
3648
3649 return rvalue(gz, scope, rl, result, node);
3650}
3651
3652fn builtinCall(
3653 gz: *GenZir,
3654 scope: *Scope,
3655 rl: ResultLoc,
3656 node: ast.Node.Index,
3657 params: []const ast.Node.Index,
3658) InnerError!zir.Inst.Ref {
3659 const mod = gz.astgen.mod;
3660 const tree = gz.tree();
3661 const main_tokens = tree.nodes.items(.main_token);
3662
3663 const builtin_token = main_tokens[node];
3664 const builtin_name = tree.tokenSlice(builtin_token);
3665
3666 // We handle the different builtins manually because they have different semantics depending
3667 // on the function. For example, `@as` and others participate in result location semantics,
3668 // and `@cImport` creates a special scope that collects a .c source code text buffer.
3669 // Also, some builtins have a variable number of parameters.
3670
3671 const info = BuiltinFn.list.get(builtin_name) orelse {
3672 return mod.failNode(scope, node, "invalid builtin function: '{s}'", .{
3673 builtin_name,
3674 });
3675 };
3676 if (info.param_count) |expected| {
3677 if (expected != params.len) {
3678 const s = if (expected == 1) "" else "s";
3679 return mod.failNode(scope, node, "expected {d} parameter{s}, found {d}", .{
3680 expected, s, params.len,
3681 });
3682 }
3683 }
3684
3685 switch (info.tag) {
3686 .ptr_to_int => {
3687 const operand = try expr(gz, scope, .none, params[0]);
3688 const result = try gz.addUnNode(.ptrtoint, operand, node);
3689 return rvalue(gz, scope, rl, result, node);
3690 },
3691 .float_cast => {
3692 const dest_type = try typeExpr(gz, scope, params[0]);
3693 const rhs = try expr(gz, scope, .none, params[1]);
3694 const result = try gz.addPlNode(.floatcast, node, zir.Inst.Bin{
3695 .lhs = dest_type,
3696 .rhs = rhs,
3697 });
3698 return rvalue(gz, scope, rl, result, node);
3699 },
3700 .int_cast => {
3701 const dest_type = try typeExpr(gz, scope, params[0]);
3702 const rhs = try expr(gz, scope, .none, params[1]);
3703 const result = try gz.addPlNode(.intcast, node, zir.Inst.Bin{
3704 .lhs = dest_type,
3705 .rhs = rhs,
3706 });
3707 return rvalue(gz, scope, rl, result, node);
3708 },
3709 .breakpoint => {
3710 const result = try gz.add(.{
3711 .tag = .breakpoint,
3712 .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(node) },
3713 });
3714 return rvalue(gz, scope, rl, result, node);
3715 },
3716 .import => {
3717 const target = try expr(gz, scope, .none, params[0]);
3718 const result = try gz.addUnNode(.import, target, node);
3719 return rvalue(gz, scope, rl, result, node);
3720 },
3721 .error_to_int => {
3722 const target = try expr(gz, scope, .none, params[0]);
3723 const result = try gz.addUnNode(.error_to_int, target, node);
3724 return rvalue(gz, scope, rl, result, node);
3725 },
3726 .int_to_error => {
3727 const target = try expr(gz, scope, .{ .ty = .u16_type }, params[0]);
3728 const result = try gz.addUnNode(.int_to_error, target, node);
3729 return rvalue(gz, scope, rl, result, node);
3730 },
3731 .compile_error => {
3732 const target = try expr(gz, scope, .none, params[0]);
3733 const result = try gz.addUnNode(.compile_error, target, node);
3734 return rvalue(gz, scope, rl, result, node);
3735 },
3736 .set_eval_branch_quota => {
3737 const quota = try expr(gz, scope, .{ .ty = .u32_type }, params[0]);
3738 const result = try gz.addUnNode(.set_eval_branch_quota, quota, node);
3739 return rvalue(gz, scope, rl, result, node);
3740 },
3741 .compile_log => {
3742 const arg_refs = try mod.gpa.alloc(zir.Inst.Ref, params.len);
3743 defer mod.gpa.free(arg_refs);
3744
3745 for (params) |param, i| arg_refs[i] = try expr(gz, scope, .none, param);
3746
3747 const result = try gz.addPlNode(.compile_log, node, zir.Inst.MultiOp{
3748 .operands_len = @intCast(u32, params.len),
3749 });
3750 try gz.astgen.appendRefs(arg_refs);
3751 return rvalue(gz, scope, rl, result, node);
3752 },
3753 .field => {
3754 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
3755 if (rl == .ref) {
3756 return try gz.addPlNode(.field_ptr_named, node, zir.Inst.FieldNamed{
3757 .lhs = try expr(gz, scope, .ref, params[0]),
3758 .field_name = field_name,
3759 });
3760 }
3761 const result = try gz.addPlNode(.field_val_named, node, zir.Inst.FieldNamed{
3762 .lhs = try expr(gz, scope, .none, params[0]),
3763 .field_name = field_name,
3764 });
3765 return rvalue(gz, scope, rl, result, node);
3766 },
3767 .as => return as(gz, scope, rl, node, params[0], params[1]),
3768 .bit_cast => return bitCast(gz, scope, rl, node, params[0], params[1]),
3769 .TypeOf => return typeOf(gz, scope, rl, node, params),
3770
3771 .add_with_overflow,
3772 .align_cast,
3773 .align_of,
3774 .atomic_load,
3775 .atomic_rmw,
3776 .atomic_store,
3777 .bit_offset_of,
3778 .bool_to_int,
3779 .bit_size_of,
3780 .mul_add,
3781 .byte_swap,
3782 .bit_reverse,
3783 .byte_offset_of,
3784 .call,
3785 .c_define,
3786 .c_import,
3787 .c_include,
3788 .clz,
3789 .cmpxchg_strong,
3790 .cmpxchg_weak,
3791 .ctz,
3792 .c_undef,
3793 .div_exact,
3794 .div_floor,
3795 .div_trunc,
3796 .embed_file,
3797 .enum_to_int,
3798 .error_name,
3799 .error_return_trace,
3800 .err_set_cast,
3801 .@"export",
3802 .fence,
3803 .field_parent_ptr,
3804 .float_to_int,
3805 .has_decl,
3806 .has_field,
3807 .int_to_enum,
3808 .int_to_float,
3809 .int_to_ptr,
3810 .memcpy,
3811 .memset,
3812 .wasm_memory_size,
3813 .wasm_memory_grow,
3814 .mod,
3815 .mul_with_overflow,
3816 .panic,
3817 .pop_count,
3818 .ptr_cast,
3819 .rem,
3820 .return_address,
3821 .set_align_stack,
3822 .set_cold,
3823 .set_float_mode,
3824 .set_runtime_safety,
3825 .shl_exact,
3826 .shl_with_overflow,
3827 .shr_exact,
3828 .shuffle,
3829 .size_of,
3830 .splat,
3831 .reduce,
3832 .src,
3833 .sqrt,
3834 .sin,
3835 .cos,
3836 .exp,
3837 .exp2,
3838 .log,
3839 .log2,
3840 .log10,
3841 .fabs,
3842 .floor,
3843 .ceil,
3844 .trunc,
3845 .round,
3846 .sub_with_overflow,
3847 .tag_name,
3848 .This,
3849 .truncate,
3850 .Type,
3851 .type_info,
3852 .type_name,
3853 .union_init,
3854 => return mod.failNode(scope, node, "TODO: implement builtin function {s}", .{
3855 builtin_name,
3856 }),
3857
3858 .async_call,
3859 .frame,
3860 .Frame,
3861 .frame_address,
3862 .frame_size,
3863 => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
3864 }
3865}
3866
3867fn callExpr(
3868 gz: *GenZir,
3869 scope: *Scope,
3870 rl: ResultLoc,
3871 node: ast.Node.Index,
3872 call: ast.full.Call,
3873) InnerError!zir.Inst.Ref {
3874 const mod = gz.astgen.mod;
3875 if (call.async_token) |async_token| {
3876 return mod.failTok(scope, async_token, "async and related features are not yet supported", .{});
3877 }
3878 const lhs = try expr(gz, scope, .none, call.ast.fn_expr);
3879
3880 const args = try mod.gpa.alloc(zir.Inst.Ref, call.ast.params.len);
3881 defer mod.gpa.free(args);
3882
3883 for (call.ast.params) |param_node, i| {
3884 const param_type = try gz.add(.{
3885 .tag = .param_type,
3886 .data = .{ .param_type = .{
3887 .callee = lhs,
3888 .param_index = @intCast(u32, i),
3889 } },
3890 });
3891 args[i] = try expr(gz, scope, .{ .ty = param_type }, param_node);
3892 }
3893
3894 const modifier: std.builtin.CallOptions.Modifier = switch (call.async_token != null) {
3895 true => .async_kw,
3896 false => .auto,
3897 };
3898 const result: zir.Inst.Ref = res: {
3899 const tag: zir.Inst.Tag = switch (modifier) {
3900 .auto => switch (args.len == 0) {
3901 true => break :res try gz.addUnNode(.call_none, lhs, node),
3902 false => .call,
3903 },
3904 .async_kw => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
3905 .never_tail => unreachable,
3906 .never_inline => unreachable,
3907 .no_async => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
3908 .always_tail => unreachable,
3909 .always_inline => unreachable,
3910 .compile_time => .call_compile_time,
3911 };
3912 break :res try gz.addCall(tag, lhs, args, node);
3913 };
3914 return rvalue(gz, scope, rl, result, node); // TODO function call with result location
3915}
3916
3917pub const simple_types = std.ComptimeStringMap(zir.Inst.Ref, .{
3918 .{ "u8", .u8_type },
3919 .{ "i8", .i8_type },
3920 .{ "u16", .u16_type },
3921 .{ "i16", .i16_type },
3922 .{ "u32", .u32_type },
3923 .{ "i32", .i32_type },
3924 .{ "u64", .u64_type },
3925 .{ "i64", .i64_type },
3926 .{ "usize", .usize_type },
3927 .{ "isize", .isize_type },
3928 .{ "c_short", .c_short_type },
3929 .{ "c_ushort", .c_ushort_type },
3930 .{ "c_int", .c_int_type },
3931 .{ "c_uint", .c_uint_type },
3932 .{ "c_long", .c_long_type },
3933 .{ "c_ulong", .c_ulong_type },
3934 .{ "c_longlong", .c_longlong_type },
3935 .{ "c_ulonglong", .c_ulonglong_type },
3936 .{ "c_longdouble", .c_longdouble_type },
3937 .{ "f16", .f16_type },
3938 .{ "f32", .f32_type },
3939 .{ "f64", .f64_type },
3940 .{ "f128", .f128_type },
3941 .{ "c_void", .c_void_type },
3942 .{ "bool", .bool_type },
3943 .{ "void", .void_type },
3944 .{ "type", .type_type },
3945 .{ "anyerror", .anyerror_type },
3946 .{ "comptime_int", .comptime_int_type },
3947 .{ "comptime_float", .comptime_float_type },
3948 .{ "noreturn", .noreturn_type },
3949 .{ "null", .null_type },
3950 .{ "undefined", .undefined_type },
3951 .{ "undefined", .undef },
3952 .{ "null", .null_value },
3953 .{ "true", .bool_true },
3954 .{ "false", .bool_false },
3955});
3956
3957fn nodeMayNeedMemoryLocation(tree: *const ast.Tree, start_node: ast.Node.Index) bool {
3958 const node_tags = tree.nodes.items(.tag);
3959 const node_datas = tree.nodes.items(.data);
3960 const main_tokens = tree.nodes.items(.main_token);
3961 const token_tags = tree.tokens.items(.tag);
3962
3963 var node = start_node;
3964 while (true) {
3965 switch (node_tags[node]) {
3966 .root,
3967 .@"usingnamespace",
3968 .test_decl,
3969 .switch_case,
3970 .switch_case_one,
3971 .container_field_init,
3972 .container_field_align,
3973 .container_field,
3974 .asm_output,
3975 .asm_input,
3976 => unreachable,
3977
3978 .@"return",
3979 .@"break",
3980 .@"continue",
3981 .bit_not,
3982 .bool_not,
3983 .global_var_decl,
3984 .local_var_decl,
3985 .simple_var_decl,
3986 .aligned_var_decl,
3987 .@"defer",
3988 .@"errdefer",
3989 .address_of,
3990 .optional_type,
3991 .negation,
3992 .negation_wrap,
3993 .@"resume",
3994 .array_type,
3995 .array_type_sentinel,
3996 .ptr_type_aligned,
3997 .ptr_type_sentinel,
3998 .ptr_type,
3999 .ptr_type_bit_range,
4000 .@"suspend",
4001 .@"anytype",
4002 .fn_proto_simple,
4003 .fn_proto_multi,
4004 .fn_proto_one,
4005 .fn_proto,
4006 .fn_decl,
4007 .anyframe_type,
4008 .anyframe_literal,
4009 .integer_literal,
4010 .float_literal,
4011 .enum_literal,
4012 .string_literal,
4013 .multiline_string_literal,
4014 .char_literal,
4015 .true_literal,
4016 .false_literal,
4017 .null_literal,
4018 .undefined_literal,
4019 .unreachable_literal,
4020 .identifier,
4021 .error_set_decl,
4022 .container_decl,
4023 .container_decl_trailing,
4024 .container_decl_two,
4025 .container_decl_two_trailing,
4026 .container_decl_arg,
4027 .container_decl_arg_trailing,
4028 .tagged_union,
4029 .tagged_union_trailing,
4030 .tagged_union_two,
4031 .tagged_union_two_trailing,
4032 .tagged_union_enum_tag,
4033 .tagged_union_enum_tag_trailing,
4034 .@"asm",
4035 .asm_simple,
4036 .add,
4037 .add_wrap,
4038 .array_cat,
4039 .array_mult,
4040 .assign,
4041 .assign_bit_and,
4042 .assign_bit_or,
4043 .assign_bit_shift_left,
4044 .assign_bit_shift_right,
4045 .assign_bit_xor,
4046 .assign_div,
4047 .assign_sub,
4048 .assign_sub_wrap,
4049 .assign_mod,
4050 .assign_add,
4051 .assign_add_wrap,
4052 .assign_mul,
4053 .assign_mul_wrap,
4054 .bang_equal,
4055 .bit_and,
4056 .bit_or,
4057 .bit_shift_left,
4058 .bit_shift_right,
4059 .bit_xor,
4060 .bool_and,
4061 .bool_or,
4062 .div,
4063 .equal_equal,
4064 .error_union,
4065 .greater_or_equal,
4066 .greater_than,
4067 .less_or_equal,
4068 .less_than,
4069 .merge_error_sets,
4070 .mod,
4071 .mul,
4072 .mul_wrap,
4073 .switch_range,
4074 .field_access,
4075 .sub,
4076 .sub_wrap,
4077 .slice,
4078 .slice_open,
4079 .slice_sentinel,
4080 .deref,
4081 .array_access,
4082 .error_value,
4083 .while_simple, // This variant cannot have an else expression.
4084 .while_cont, // This variant cannot have an else expression.
4085 .for_simple, // This variant cannot have an else expression.
4086 .if_simple, // This variant cannot have an else expression.
4087 => return false,
4088
4089 // Forward the question to the LHS sub-expression.
4090 .grouped_expression,
4091 .@"try",
4092 .@"await",
4093 .@"comptime",
4094 .@"nosuspend",
4095 .unwrap_optional,
4096 => node = node_datas[node].lhs,
4097
4098 // Forward the question to the RHS sub-expression.
4099 .@"catch",
4100 .@"orelse",
4101 => node = node_datas[node].rhs,
4102
4103 // True because these are exactly the expressions we need memory locations for.
4104 .array_init_one,
4105 .array_init_one_comma,
4106 .array_init_dot_two,
4107 .array_init_dot_two_comma,
4108 .array_init_dot,
4109 .array_init_dot_comma,
4110 .array_init,
4111 .array_init_comma,
4112 .struct_init_one,
4113 .struct_init_one_comma,
4114 .struct_init_dot_two,
4115 .struct_init_dot_two_comma,
4116 .struct_init_dot,
4117 .struct_init_dot_comma,
4118 .struct_init,
4119 .struct_init_comma,
4120 => return true,
4121
4122 // True because depending on comptime conditions, sub-expressions
4123 // may be the kind that need memory locations.
4124 .@"while", // This variant always has an else expression.
4125 .@"if", // This variant always has an else expression.
4126 .@"for", // This variant always has an else expression.
4127 .@"switch",
4128 .switch_comma,
4129 .call_one,
4130 .call_one_comma,
4131 .async_call_one,
4132 .async_call_one_comma,
4133 .call,
4134 .call_comma,
4135 .async_call,
4136 .async_call_comma,
4137 => return true,
4138
4139 .block_two,
4140 .block_two_semicolon,
4141 .block,
4142 .block_semicolon,
4143 => {
4144 const lbrace = main_tokens[node];
4145 if (token_tags[lbrace - 1] == .colon) {
4146 // Labeled blocks may need a memory location to forward
4147 // to their break statements.
4148 return true;
4149 } else {
4150 return false;
4151 }
4152 },
4153
4154 .builtin_call,
4155 .builtin_call_comma,
4156 .builtin_call_two,
4157 .builtin_call_two_comma,
4158 => {
4159 const builtin_token = main_tokens[node];
4160 const builtin_name = tree.tokenSlice(builtin_token);
4161 // If the builtin is an invalid name, we don't cause an error here; instead
4162 // let it pass, and the error will be "invalid builtin function" later.
4163 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
4164 return builtin_info.needs_mem_loc;
4165 },
4166 }
4167 }
4168}
4169
4170/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
4171/// result locations must call this function on their result.
4172/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
4173/// If the `ResultLoc` is `ty`, it will coerce the result to the type.
4174fn rvalue(
4175 gz: *GenZir,
4176 scope: *Scope,
4177 rl: ResultLoc,
4178 result: zir.Inst.Ref,
4179 src_node: ast.Node.Index,
4180) InnerError!zir.Inst.Ref {
4181 switch (rl) {
4182 .none => return result,
4183 .discard => {
4184 // Emit a compile error for discarding error values.
4185 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
4186 return result;
4187 },
4188 .ref => {
4189 // We need a pointer but we have a value.
4190 const tree = gz.tree();
4191 const src_token = tree.firstToken(src_node);
4192 return gz.addUnTok(.ref, result, src_token);
4193 },
4194 .ty => |ty_inst| {
4195 // Quickly eliminate some common, unnecessary type coercion.
4196 const as_ty = @as(u64, @enumToInt(zir.Inst.Ref.type_type)) << 32;
4197 const as_comptime_int = @as(u64, @enumToInt(zir.Inst.Ref.comptime_int_type)) << 32;
4198 const as_bool = @as(u64, @enumToInt(zir.Inst.Ref.bool_type)) << 32;
4199 const as_usize = @as(u64, @enumToInt(zir.Inst.Ref.usize_type)) << 32;
4200 const as_void = @as(u64, @enumToInt(zir.Inst.Ref.void_type)) << 32;
4201 switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) {
4202 as_ty | @enumToInt(zir.Inst.Ref.u8_type),
4203 as_ty | @enumToInt(zir.Inst.Ref.i8_type),
4204 as_ty | @enumToInt(zir.Inst.Ref.u16_type),
4205 as_ty | @enumToInt(zir.Inst.Ref.i16_type),
4206 as_ty | @enumToInt(zir.Inst.Ref.u32_type),
4207 as_ty | @enumToInt(zir.Inst.Ref.i32_type),
4208 as_ty | @enumToInt(zir.Inst.Ref.u64_type),
4209 as_ty | @enumToInt(zir.Inst.Ref.i64_type),
4210 as_ty | @enumToInt(zir.Inst.Ref.usize_type),
4211 as_ty | @enumToInt(zir.Inst.Ref.isize_type),
4212 as_ty | @enumToInt(zir.Inst.Ref.c_short_type),
4213 as_ty | @enumToInt(zir.Inst.Ref.c_ushort_type),
4214 as_ty | @enumToInt(zir.Inst.Ref.c_int_type),
4215 as_ty | @enumToInt(zir.Inst.Ref.c_uint_type),
4216 as_ty | @enumToInt(zir.Inst.Ref.c_long_type),
4217 as_ty | @enumToInt(zir.Inst.Ref.c_ulong_type),
4218 as_ty | @enumToInt(zir.Inst.Ref.c_longlong_type),
4219 as_ty | @enumToInt(zir.Inst.Ref.c_ulonglong_type),
4220 as_ty | @enumToInt(zir.Inst.Ref.c_longdouble_type),
4221 as_ty | @enumToInt(zir.Inst.Ref.f16_type),
4222 as_ty | @enumToInt(zir.Inst.Ref.f32_type),
4223 as_ty | @enumToInt(zir.Inst.Ref.f64_type),
4224 as_ty | @enumToInt(zir.Inst.Ref.f128_type),
4225 as_ty | @enumToInt(zir.Inst.Ref.c_void_type),
4226 as_ty | @enumToInt(zir.Inst.Ref.bool_type),
4227 as_ty | @enumToInt(zir.Inst.Ref.void_type),
4228 as_ty | @enumToInt(zir.Inst.Ref.type_type),
4229 as_ty | @enumToInt(zir.Inst.Ref.anyerror_type),
4230 as_ty | @enumToInt(zir.Inst.Ref.comptime_int_type),
4231 as_ty | @enumToInt(zir.Inst.Ref.comptime_float_type),
4232 as_ty | @enumToInt(zir.Inst.Ref.noreturn_type),
4233 as_ty | @enumToInt(zir.Inst.Ref.null_type),
4234 as_ty | @enumToInt(zir.Inst.Ref.undefined_type),
4235 as_ty | @enumToInt(zir.Inst.Ref.fn_noreturn_no_args_type),
4236 as_ty | @enumToInt(zir.Inst.Ref.fn_void_no_args_type),
4237 as_ty | @enumToInt(zir.Inst.Ref.fn_naked_noreturn_no_args_type),
4238 as_ty | @enumToInt(zir.Inst.Ref.fn_ccc_void_no_args_type),
4239 as_ty | @enumToInt(zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
4240 as_ty | @enumToInt(zir.Inst.Ref.const_slice_u8_type),
4241 as_ty | @enumToInt(zir.Inst.Ref.enum_literal_type),
4242 as_comptime_int | @enumToInt(zir.Inst.Ref.zero),
4243 as_comptime_int | @enumToInt(zir.Inst.Ref.one),
4244 as_bool | @enumToInt(zir.Inst.Ref.bool_true),
4245 as_bool | @enumToInt(zir.Inst.Ref.bool_false),
4246 as_usize | @enumToInt(zir.Inst.Ref.zero_usize),
4247 as_usize | @enumToInt(zir.Inst.Ref.one_usize),
4248 as_void | @enumToInt(zir.Inst.Ref.void_value),
4249 => return result, // type of result is already correct
4250
4251 // Need an explicit type coercion instruction.
4252 else => return gz.addPlNode(.as_node, src_node, zir.Inst.As{
4253 .dest_type = ty_inst,
4254 .operand = result,
4255 }),
4256 }
4257 },
4258 .ptr => |ptr_inst| {
4259 _ = try gz.addPlNode(.store_node, src_node, zir.Inst.Bin{
4260 .lhs = ptr_inst,
4261 .rhs = result,
4262 });
4263 return result;
4264 },
4265 .inferred_ptr => |alloc| {
4266 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
4267 return result;
4268 },
4269 .block_ptr => |block_scope| {
4270 block_scope.rvalue_rl_count += 1;
4271 _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
4272 return result;
4273 },
4274 }
4275}
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+2098-1917
......@@ -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,32 @@ 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/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
312360/// Extern functions do not have this data structure; they are represented by
313361/// the `Decl` only, with a `Value` tag of `extern_fn`.
314362pub const Fn = struct {
......@@ -316,9 +364,15 @@ pub const Fn = struct {
316364 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
317365 /// Even after we finish analysis, the ZIR is kept in memory, so that
318366 /// comptime and inline function calls can happen.
319 zir: zir.Body,
367 /// Parameter names are stored here so that they may be referenced for debug info,
368 /// without having source code bytes loaded into memory.
369 /// The number of parameters is determined by referring to the type.
370 /// The first N elements of `extra` are indexes into `string_bytes` to
371 /// a null-terminated string.
372 /// This memory is managed with gpa, must be freed when the function is freed.
373 zir: zir.Code,
320374 /// undefined unless analysis state is `success`.
321 body: Body,
375 body: ir.Body,
322376 state: Analysis,
323377
324378 pub const Analysis = enum {
......@@ -336,8 +390,12 @@ pub const Fn = struct {
336390 };
337391
338392 /// For debugging purposes.
339 pub fn dump(self: *Fn, mod: Module) void {
340 zir.dumpFn(mod, self);
393 pub fn dump(func: *Fn, mod: Module) void {
394 ir.dumpFn(mod, func);
395 }
396
397 pub fn deinit(func: *Fn, gpa: *Allocator) void {
398 func.zir.deinit(gpa);
341399 }
342400};
343401
......@@ -364,103 +422,93 @@ pub const Scope = struct {
364422 }
365423
366424 /// 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,
425 pub fn arena(scope: *Scope) *Allocator {
426 switch (scope.tag) {
427 .block => return scope.cast(Block).?.sema.arena,
428 .gen_zir => return scope.cast(GenZir).?.astgen.arena,
429 .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.arena,
430 .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.arena,
375431 .file => unreachable,
376432 .container => unreachable,
433 .decl_ref => unreachable,
377434 }
378435 }
379436
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,
437 pub fn ownerDecl(scope: *Scope) ?*Decl {
438 return switch (scope.tag) {
439 .block => scope.cast(Block).?.sema.owner_decl,
440 .gen_zir => scope.cast(GenZir).?.astgen.decl,
441 .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl,
442 .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl,
392443 .file => null,
393444 .container => null,
445 .decl_ref => scope.cast(DeclRef).?.decl,
394446 };
395447 }
396448
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,
449 pub fn srcDecl(scope: *Scope) ?*Decl {
450 return switch (scope.tag) {
451 .block => scope.cast(Block).?.src_decl,
452 .gen_zir => scope.cast(GenZir).?.astgen.decl,
453 .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl,
454 .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl,
405455 .file => null,
406456 .container => null,
457 .decl_ref => scope.cast(DeclRef).?.decl,
407458 };
408459 }
409460
410461 /// 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,
462 pub fn namespace(scope: *Scope) *Container {
463 switch (scope.tag) {
464 .block => return scope.cast(Block).?.sema.owner_decl.container,
465 .gen_zir => return scope.cast(GenZir).?.astgen.decl.container,
466 .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.decl.container,
467 .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.decl.container,
468 .file => return &scope.cast(File).?.root_container,
469 .container => return scope.cast(Container).?,
470 .decl_ref => return scope.cast(DeclRef).?.decl.container,
421471 }
422472 }
423473
424474 /// Must generate unique bytes with no collisions with other decls.
425475 /// The point of hashing here is only to limit the number of bytes of
426476 /// the unique identifier to a fixed size (16 bytes).
427 pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
428 switch (self.tag) {
477 pub fn fullyQualifiedNameHash(scope: *Scope, name: []const u8) NameHash {
478 switch (scope.tag) {
429479 .block => unreachable,
430480 .gen_zir => unreachable,
431481 .local_val => unreachable,
432482 .local_ptr => unreachable,
433 .gen_suspend => unreachable,
434 .gen_nosuspend => unreachable,
435483 .file => unreachable,
436 .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
484 .container => return scope.cast(Container).?.fullyQualifiedNameHash(name),
485 .decl_ref => unreachable,
437486 }
438487 }
439488
440489 /// 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,
490 pub fn tree(scope: *Scope) *const ast.Tree {
491 switch (scope.tag) {
492 .file => return &scope.cast(File).?.tree,
493 .block => return &scope.cast(Block).?.src_decl.container.file_scope.tree,
494 .gen_zir => return scope.cast(GenZir).?.tree(),
495 .local_val => return &scope.cast(LocalVal).?.gen_zir.astgen.decl.container.file_scope.tree,
496 .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.astgen.decl.container.file_scope.tree,
497 .container => return &scope.cast(Container).?.file_scope.tree,
498 .decl_ref => return &scope.cast(DeclRef).?.decl.container.file_scope.tree,
451499 }
452500 }
453501
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) {
502 /// Asserts the scope is a child of a `GenZir` and returns it.
503 pub fn getGenZir(scope: *Scope) *GenZir {
504 return switch (scope.tag) {
457505 .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,
506 .gen_zir => scope.cast(GenZir).?,
507 .local_val => return scope.cast(LocalVal).?.gen_zir,
508 .local_ptr => return scope.cast(LocalPtr).?.gen_zir,
462509 .file => unreachable,
463510 .container => unreachable,
511 .decl_ref => unreachable,
464512 };
465513 }
466514
......@@ -474,8 +522,7 @@ pub const Scope = struct {
474522 .gen_zir => unreachable,
475523 .local_val => unreachable,
476524 .local_ptr => unreachable,
477 .gen_suspend => unreachable,
478 .gen_nosuspend => unreachable,
525 .decl_ref => unreachable,
479526 }
480527 }
481528
......@@ -487,8 +534,7 @@ pub const Scope = struct {
487534 .local_val => unreachable,
488535 .local_ptr => unreachable,
489536 .block => unreachable,
490 .gen_suspend => unreachable,
491 .gen_nosuspend => unreachable,
537 .decl_ref => unreachable,
492538 }
493539 }
494540
......@@ -499,40 +545,11 @@ pub const Scope = struct {
499545 cur = switch (cur.tag) {
500546 .container => return @fieldParentPtr(Container, "base", cur).file_scope,
501547 .file => return @fieldParentPtr(File, "base", cur),
502 .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent,
548 .gen_zir => @fieldParentPtr(GenZir, "base", cur).parent,
503549 .local_val => @fieldParentPtr(LocalVal, "base", cur).parent,
504550 .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent,
505551 .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,
552 .decl_ref => return @fieldParentPtr(DeclRef, "base", cur).decl.container.file_scope,
536553 };
537554 }
538555 }
......@@ -554,8 +571,10 @@ pub const Scope = struct {
554571 gen_zir,
555572 local_val,
556573 local_ptr,
557 gen_suspend,
558 gen_nosuspend,
574 /// Used for simple error reporting. Only contains a reference to a
575 /// `Decl` for use with `srcDecl` and `ownerDecl`.
576 /// Has no parents or children.
577 decl_ref,
559578 };
560579
561580 pub const Container = struct {
......@@ -568,19 +587,19 @@ pub const Scope = struct {
568587 decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
569588 ty: Type,
570589
571 pub fn deinit(self: *Container, gpa: *Allocator) void {
572 self.decls.deinit(gpa);
590 pub fn deinit(cont: *Container, gpa: *Allocator) void {
591 cont.decls.deinit(gpa);
573592 // 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;
593 gpa.destroy(cont.ty.castTag(.empty_struct).?);
594 gpa.free(cont.file_scope.sub_file_path);
595 cont.* = undefined;
577596 }
578597
579 pub fn removeDecl(self: *Container, child: *Decl) void {
580 _ = self.decls.swapRemove(child);
598 pub fn removeDecl(cont: *Container, child: *Decl) void {
599 _ = cont.decls.swapRemove(child);
581600 }
582601
583 pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
602 pub fn fullyQualifiedNameHash(cont: *Container, name: []const u8) NameHash {
584603 // TODO container scope qualified names.
585604 return std.zig.hashSrc(name);
586605 }
......@@ -610,55 +629,55 @@ pub const Scope = struct {
610629
611630 root_container: Container,
612631
613 pub fn unload(self: *File, gpa: *Allocator) void {
614 switch (self.status) {
632 pub fn unload(file: *File, gpa: *Allocator) void {
633 switch (file.status) {
615634 .never_loaded,
616635 .unloaded_parse_failure,
617636 .unloaded_success,
618637 => {},
619638
620639 .loaded_success => {
621 self.tree.deinit(gpa);
622 self.status = .unloaded_success;
640 file.tree.deinit(gpa);
641 file.status = .unloaded_success;
623642 },
624643 }
625 switch (self.source) {
644 switch (file.source) {
626645 .bytes => |bytes| {
627646 gpa.free(bytes);
628 self.source = .{ .unloaded = {} };
647 file.source = .{ .unloaded = {} };
629648 },
630649 .unloaded => {},
631650 }
632651 }
633652
634 pub fn deinit(self: *File, gpa: *Allocator) void {
635 self.root_container.deinit(gpa);
636 self.unload(gpa);
637 self.* = undefined;
653 pub fn deinit(file: *File, gpa: *Allocator) void {
654 file.root_container.deinit(gpa);
655 file.unload(gpa);
656 file.* = undefined;
638657 }
639658
640 pub fn destroy(self: *File, gpa: *Allocator) void {
641 self.deinit(gpa);
642 gpa.destroy(self);
659 pub fn destroy(file: *File, gpa: *Allocator) void {
660 file.deinit(gpa);
661 gpa.destroy(file);
643662 }
644663
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 });
664 pub fn dumpSrc(file: *File, src: LazySrcLoc) void {
665 const loc = std.zig.findLineColumn(file.source.bytes, src);
666 std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 });
648667 }
649668
650 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
651 switch (self.source) {
669 pub fn getSource(file: *File, module: *Module) ![:0]const u8 {
670 switch (file.source) {
652671 .unloaded => {
653 const source = try self.pkg.root_src_directory.handle.readFileAllocOptions(
672 const source = try file.pkg.root_src_directory.handle.readFileAllocOptions(
654673 module.gpa,
655 self.sub_file_path,
674 file.sub_file_path,
656675 std.math.maxInt(u32),
657676 null,
658677 1,
659678 0,
660679 );
661 self.source = .{ .bytes = source };
680 file.source = .{ .bytes = source };
662681 return source;
663682 },
664683 .bytes => |bytes| return bytes,
......@@ -666,37 +685,30 @@ pub const Scope = struct {
666685 }
667686 };
668687
669 /// This is a temporary structure, references to it are valid only
688 /// This is the context needed to semantically analyze ZIR instructions and
689 /// produce TZIR instructions.
690 /// This is a temporary structure stored on the stack; references to it are valid only
670691 /// during semantic analysis of the block.
671692 pub const Block = struct {
672693 pub const base_tag: Tag = .block;
673694
674695 base: Scope = Scope{ .tag = base_tag },
675696 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,
697 /// Shared among all child blocks.
698 sema: *Sema,
683699 /// This Decl is the Decl according to the Zig source code corresponding to this Block.
700 /// This can vary during inline or comptime function calls. See `Sema.owner_decl`
701 /// for the one that will be the same for all Block instances.
684702 src_decl: *Decl,
685 instructions: ArrayListUnmanaged(*Inst),
686 /// Points to the arena allocator of the Decl.
687 arena: *Allocator,
703 instructions: ArrayListUnmanaged(*ir.Inst),
688704 label: ?Label = null,
689705 inlining: ?*Inlining,
690706 is_comptime: bool,
691 /// Shared to sub-blocks.
692 branch_quota: *u32,
693
694 pub const InstTable = std.AutoHashMap(*zir.Inst, *Inst);
695707
696708 /// This `Block` maps a block ZIR instruction to the corresponding
697709 /// TZIR instruction for break instruction analysis.
698710 pub const Label = struct {
699 zir_block: *zir.Inst.Block,
711 zir_block: zir.Inst.Index,
700712 merges: Merges,
701713 };
702714
......@@ -706,73 +718,232 @@ pub const Scope = struct {
706718 /// It is shared among all the blocks in an inline or comptime called
707719 /// function.
708720 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,
716721 merges: Merges,
717
718 pub const Shared = struct {
719 caller: ?*Fn,
720 branch_count: u32,
721 };
722722 };
723723
724724 pub const Merges = struct {
725 block_inst: *Inst.Block,
725 block_inst: *ir.Inst.Block,
726726 /// Separate array list from break_inst_list so that it can be passed directly
727727 /// to resolvePeerTypes.
728 results: ArrayListUnmanaged(*Inst),
728 results: ArrayListUnmanaged(*ir.Inst),
729729 /// Keeps track of the break instructions so that the operand can be replaced
730730 /// if we need to add type coercion at the end of block analysis.
731731 /// Same indexes, capacity, length as `results`.
732 br_list: ArrayListUnmanaged(*Inst.Br),
732 br_list: ArrayListUnmanaged(*ir.Inst.Br),
733733 };
734734
735735 /// For debugging purposes.
736 pub fn dump(self: *Block, mod: Module) void {
737 zir.dumpBlock(mod, self);
736 pub fn dump(block: *Block, mod: Module) void {
737 zir.dumpBlock(mod, block);
738738 }
739739
740740 pub fn makeSubBlock(parent: *Block) Block {
741741 return .{
742742 .parent = parent,
743 .inst_table = parent.inst_table,
744 .func = parent.func,
745 .owner_decl = parent.owner_decl,
743 .sema = parent.sema,
746744 .src_decl = parent.src_decl,
747745 .instructions = .{},
748 .arena = parent.arena,
749746 .label = null,
750747 .inlining = parent.inlining,
751748 .is_comptime = parent.is_comptime,
752 .branch_quota = parent.branch_quota,
753749 };
754750 }
751
752 pub fn wantSafety(block: *const Block) bool {
753 // TODO take into account scope's safety overrides
754 return switch (block.sema.mod.optimizeMode()) {
755 .Debug => true,
756 .ReleaseSafe => true,
757 .ReleaseFast => false,
758 .ReleaseSmall => false,
759 };
760 }
761
762 pub fn getFileScope(block: *Block) *Scope.File {
763 return block.src_decl.container.file_scope;
764 }
765
766 pub fn addNoOp(
767 block: *Scope.Block,
768 src: LazySrcLoc,
769 ty: Type,
770 comptime tag: ir.Inst.Tag,
771 ) !*ir.Inst {
772 const inst = try block.sema.arena.create(tag.Type());
773 inst.* = .{
774 .base = .{
775 .tag = tag,
776 .ty = ty,
777 .src = src,
778 },
779 };
780 try block.instructions.append(block.sema.gpa, &inst.base);
781 return &inst.base;
782 }
783
784 pub fn addUnOp(
785 block: *Scope.Block,
786 src: LazySrcLoc,
787 ty: Type,
788 tag: ir.Inst.Tag,
789 operand: *ir.Inst,
790 ) !*ir.Inst {
791 const inst = try block.sema.arena.create(ir.Inst.UnOp);
792 inst.* = .{
793 .base = .{
794 .tag = tag,
795 .ty = ty,
796 .src = src,
797 },
798 .operand = operand,
799 };
800 try block.instructions.append(block.sema.gpa, &inst.base);
801 return &inst.base;
802 }
803
804 pub fn addBinOp(
805 block: *Scope.Block,
806 src: LazySrcLoc,
807 ty: Type,
808 tag: ir.Inst.Tag,
809 lhs: *ir.Inst,
810 rhs: *ir.Inst,
811 ) !*ir.Inst {
812 const inst = try block.sema.arena.create(ir.Inst.BinOp);
813 inst.* = .{
814 .base = .{
815 .tag = tag,
816 .ty = ty,
817 .src = src,
818 },
819 .lhs = lhs,
820 .rhs = rhs,
821 };
822 try block.instructions.append(block.sema.gpa, &inst.base);
823 return &inst.base;
824 }
825 pub fn addBr(
826 scope_block: *Scope.Block,
827 src: LazySrcLoc,
828 target_block: *ir.Inst.Block,
829 operand: *ir.Inst,
830 ) !*ir.Inst.Br {
831 const inst = try scope_block.sema.arena.create(ir.Inst.Br);
832 inst.* = .{
833 .base = .{
834 .tag = .br,
835 .ty = Type.initTag(.noreturn),
836 .src = src,
837 },
838 .operand = operand,
839 .block = target_block,
840 };
841 try scope_block.instructions.append(scope_block.sema.gpa, &inst.base);
842 return inst;
843 }
844
845 pub fn addCondBr(
846 block: *Scope.Block,
847 src: LazySrcLoc,
848 condition: *ir.Inst,
849 then_body: ir.Body,
850 else_body: ir.Body,
851 ) !*ir.Inst {
852 const inst = try block.sema.arena.create(ir.Inst.CondBr);
853 inst.* = .{
854 .base = .{
855 .tag = .condbr,
856 .ty = Type.initTag(.noreturn),
857 .src = src,
858 },
859 .condition = condition,
860 .then_body = then_body,
861 .else_body = else_body,
862 };
863 try block.instructions.append(block.sema.gpa, &inst.base);
864 return &inst.base;
865 }
866
867 pub fn addCall(
868 block: *Scope.Block,
869 src: LazySrcLoc,
870 ty: Type,
871 func: *ir.Inst,
872 args: []const *ir.Inst,
873 ) !*ir.Inst {
874 const inst = try block.sema.arena.create(ir.Inst.Call);
875 inst.* = .{
876 .base = .{
877 .tag = .call,
878 .ty = ty,
879 .src = src,
880 },
881 .func = func,
882 .args = args,
883 };
884 try block.instructions.append(block.sema.gpa, &inst.base);
885 return &inst.base;
886 }
887
888 pub fn addSwitchBr(
889 block: *Scope.Block,
890 src: LazySrcLoc,
891 operand: *ir.Inst,
892 cases: []ir.Inst.SwitchBr.Case,
893 else_body: ir.Body,
894 ) !*ir.Inst {
895 const inst = try block.sema.arena.create(ir.Inst.SwitchBr);
896 inst.* = .{
897 .base = .{
898 .tag = .switchbr,
899 .ty = Type.initTag(.noreturn),
900 .src = src,
901 },
902 .target = operand,
903 .cases = cases,
904 .else_body = else_body,
905 };
906 try block.instructions.append(block.sema.gpa, &inst.base);
907 return &inst.base;
908 }
909
910 pub fn addDbgStmt(block: *Scope.Block, src: LazySrcLoc, abs_byte_off: u32) !*ir.Inst {
911 const inst = try block.sema.arena.create(ir.Inst.DbgStmt);
912 inst.* = .{
913 .base = .{
914 .tag = .dbg_stmt,
915 .ty = Type.initTag(.void),
916 .src = src,
917 },
918 .byte_offset = abs_byte_off,
919 };
920 try block.instructions.append(block.sema.gpa, &inst.base);
921 return &inst.base;
922 }
755923 };
756924
757 /// This is a temporary structure, references to it are valid only
758 /// during semantic analysis of the decl.
759 pub const GenZIR = struct {
925 /// This is a temporary structure; references to it are valid only
926 /// while constructing a `zir.Code`.
927 pub const GenZir = struct {
760928 pub const base_tag: Tag = .gen_zir;
761929 base: Scope = Scope{ .tag = base_tag },
762 /// Parents can be: `GenZIR`, `File`
763 parent: *Scope,
764 decl: *Decl,
765 arena: *Allocator,
766930 force_comptime: bool,
767 /// The first N instructions in a function body ZIR are arg instructions.
768 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
931 /// Parents can be: `GenZir`, `File`
932 parent: *Scope,
933 /// All `GenZir` scopes for the same ZIR share this.
934 astgen: *AstGen,
935 /// Keeps track of the list of instructions in this scope only. Indexes
936 /// to instructions in `astgen`.
937 instructions: ArrayListUnmanaged(zir.Inst.Index) = .{},
769938 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,
939 break_block: zir.Inst.Index = 0,
940 continue_block: zir.Inst.Index = 0,
941 /// Only valid when setBreakResultLoc is called.
942 break_result_loc: AstGen.ResultLoc = undefined,
774943 /// When a block has a pointer result location, here it is.
775 rl_ptr: ?*zir.Inst = null,
944 rl_ptr: zir.Inst.Ref = .none,
945 /// When a block has a type result location, here it is.
946 rl_ty_inst: zir.Inst.Ref = .none,
776947 /// Keeps track of how many branches of a block did not actually
777948 /// consume the result location. astgen uses this to figure out
778949 /// whether to rely on break instructions or writing to the result
......@@ -784,19 +955,456 @@ pub const Scope = struct {
784955 break_count: usize = 0,
785956 /// Tracks `break :foo bar` instructions so they can possibly be elided later if
786957 /// the labeled block ends up not needing a result location pointer.
787 labeled_breaks: std.ArrayListUnmanaged(*zir.Inst.Break) = .{},
958 labeled_breaks: ArrayListUnmanaged(zir.Inst.Index) = .{},
788959 /// Tracks `store_to_block_ptr` instructions that correspond to break instructions
789960 /// so they can possibly be elided later if the labeled block ends up not needing
790961 /// 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,
962 labeled_store_to_block_ptr_list: ArrayListUnmanaged(zir.Inst.Index) = .{},
794963
795964 pub const Label = struct {
796965 token: ast.TokenIndex,
797 block_inst: *zir.Inst.Block,
966 block_inst: zir.Inst.Index,
798967 used: bool = false,
799968 };
969
970 /// Only valid to call on the top of the `GenZir` stack. Completes the
971 /// `AstGen` into a `zir.Code`. Leaves the `AstGen` in an
972 /// initialized, but empty, state.
973 pub fn finish(gz: *GenZir) !zir.Code {
974 const gpa = gz.astgen.mod.gpa;
975 try gz.setBlockBody(0);
976 return zir.Code{
977 .instructions = gz.astgen.instructions.toOwnedSlice(),
978 .string_bytes = gz.astgen.string_bytes.toOwnedSlice(gpa),
979 .extra = gz.astgen.extra.toOwnedSlice(gpa),
980 .decls = gz.astgen.decls.toOwnedSlice(gpa),
981 };
982 }
983
984 pub fn tokSrcLoc(gz: GenZir, token_index: ast.TokenIndex) LazySrcLoc {
985 return gz.astgen.decl.tokSrcLoc(token_index);
986 }
987
988 pub fn nodeSrcLoc(gz: GenZir, node_index: ast.Node.Index) LazySrcLoc {
989 return gz.astgen.decl.nodeSrcLoc(node_index);
990 }
991
992 pub fn tree(gz: *const GenZir) *const ast.Tree {
993 return &gz.astgen.decl.container.file_scope.tree;
994 }
995
996 pub fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void {
997 // Depending on whether the result location is a pointer or value, different
998 // ZIR needs to be generated. In the former case we rely on storing to the
999 // pointer to communicate the result, and use breakvoid; in the latter case
1000 // the block break instructions will have the result values.
1001 // One more complication: when the result location is a pointer, we detect
1002 // the scenario where the result location is not consumed. In this case
1003 // we emit ZIR for the block break instructions to have the result values,
1004 // and then rvalue() on that to pass the value to the result location.
1005 switch (parent_rl) {
1006 .ty => |ty_inst| {
1007 gz.rl_ty_inst = ty_inst;
1008 gz.break_result_loc = parent_rl;
1009 },
1010 .discard, .none, .ptr, .ref => {
1011 gz.break_result_loc = parent_rl;
1012 },
1013
1014 .inferred_ptr => |ptr| {
1015 gz.rl_ptr = ptr;
1016 gz.break_result_loc = .{ .block_ptr = gz };
1017 },
1018
1019 .block_ptr => |parent_block_scope| {
1020 gz.rl_ty_inst = parent_block_scope.rl_ty_inst;
1021 gz.rl_ptr = parent_block_scope.rl_ptr;
1022 gz.break_result_loc = .{ .block_ptr = gz };
1023 },
1024 }
1025 }
1026
1027 pub fn setBoolBrBody(gz: GenZir, inst: zir.Inst.Index) !void {
1028 const gpa = gz.astgen.mod.gpa;
1029 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1030 @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len);
1031 const zir_datas = gz.astgen.instructions.items(.data);
1032 zir_datas[inst].bool_br.payload_index = gz.astgen.addExtraAssumeCapacity(
1033 zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) },
1034 );
1035 gz.astgen.extra.appendSliceAssumeCapacity(gz.instructions.items);
1036 }
1037
1038 pub fn setBlockBody(gz: GenZir, inst: zir.Inst.Index) !void {
1039 const gpa = gz.astgen.mod.gpa;
1040 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1041 @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len);
1042 const zir_datas = gz.astgen.instructions.items(.data);
1043 zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity(
1044 zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) },
1045 );
1046 gz.astgen.extra.appendSliceAssumeCapacity(gz.instructions.items);
1047 }
1048
1049 pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct {
1050 src_node: ast.Node.Index,
1051 param_types: []const zir.Inst.Ref,
1052 ret_ty: zir.Inst.Ref,
1053 cc: zir.Inst.Ref,
1054 }) !zir.Inst.Ref {
1055 assert(args.src_node != 0);
1056 assert(args.ret_ty != .none);
1057 assert(args.cc != .none);
1058 const gpa = gz.astgen.mod.gpa;
1059 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1060 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1061 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1062 @typeInfo(zir.Inst.FnTypeCc).Struct.fields.len + args.param_types.len);
1063
1064 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnTypeCc{
1065 .return_type = args.ret_ty,
1066 .cc = args.cc,
1067 .param_types_len = @intCast(u32, args.param_types.len),
1068 });
1069 gz.astgen.appendRefsAssumeCapacity(args.param_types);
1070
1071 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1072 gz.astgen.instructions.appendAssumeCapacity(.{
1073 .tag = tag,
1074 .data = .{ .pl_node = .{
1075 .src_node = gz.astgen.decl.nodeIndexToRelative(args.src_node),
1076 .payload_index = payload_index,
1077 } },
1078 });
1079 gz.instructions.appendAssumeCapacity(new_index);
1080 return gz.astgen.indexToRef(new_index);
1081 }
1082
1083 pub fn addFnType(gz: *GenZir, tag: zir.Inst.Tag, args: struct {
1084 src_node: ast.Node.Index,
1085 ret_ty: zir.Inst.Ref,
1086 param_types: []const zir.Inst.Ref,
1087 }) !zir.Inst.Ref {
1088 assert(args.src_node != 0);
1089 assert(args.ret_ty != .none);
1090 const gpa = gz.astgen.mod.gpa;
1091 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1092 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1093 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1094 @typeInfo(zir.Inst.FnType).Struct.fields.len + args.param_types.len);
1095
1096 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnType{
1097 .return_type = args.ret_ty,
1098 .param_types_len = @intCast(u32, args.param_types.len),
1099 });
1100 gz.astgen.appendRefsAssumeCapacity(args.param_types);
1101
1102 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1103 gz.astgen.instructions.appendAssumeCapacity(.{
1104 .tag = tag,
1105 .data = .{ .pl_node = .{
1106 .src_node = gz.astgen.decl.nodeIndexToRelative(args.src_node),
1107 .payload_index = payload_index,
1108 } },
1109 });
1110 gz.instructions.appendAssumeCapacity(new_index);
1111 return gz.astgen.indexToRef(new_index);
1112 }
1113
1114 pub fn addCall(
1115 gz: *GenZir,
1116 tag: zir.Inst.Tag,
1117 callee: zir.Inst.Ref,
1118 args: []const zir.Inst.Ref,
1119 /// Absolute node index. This function does the conversion to offset from Decl.
1120 src_node: ast.Node.Index,
1121 ) !zir.Inst.Ref {
1122 assert(callee != .none);
1123 assert(src_node != 0);
1124 const gpa = gz.astgen.mod.gpa;
1125 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1126 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1127 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1128 @typeInfo(zir.Inst.Call).Struct.fields.len + args.len);
1129
1130 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.Call{
1131 .callee = callee,
1132 .args_len = @intCast(u32, args.len),
1133 });
1134 gz.astgen.appendRefsAssumeCapacity(args);
1135
1136 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1137 gz.astgen.instructions.appendAssumeCapacity(.{
1138 .tag = tag,
1139 .data = .{ .pl_node = .{
1140 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
1141 .payload_index = payload_index,
1142 } },
1143 });
1144 gz.instructions.appendAssumeCapacity(new_index);
1145 return gz.astgen.indexToRef(new_index);
1146 }
1147
1148 /// Note that this returns a `zir.Inst.Index` not a ref.
1149 /// Leaves the `payload_index` field undefined.
1150 pub fn addBoolBr(
1151 gz: *GenZir,
1152 tag: zir.Inst.Tag,
1153 lhs: zir.Inst.Ref,
1154 ) !zir.Inst.Index {
1155 assert(lhs != .none);
1156 const gpa = gz.astgen.mod.gpa;
1157 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1158 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1159
1160 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1161 gz.astgen.instructions.appendAssumeCapacity(.{
1162 .tag = tag,
1163 .data = .{ .bool_br = .{
1164 .lhs = lhs,
1165 .payload_index = undefined,
1166 } },
1167 });
1168 gz.instructions.appendAssumeCapacity(new_index);
1169 return new_index;
1170 }
1171
1172 pub fn addInt(gz: *GenZir, integer: u64) !zir.Inst.Ref {
1173 return gz.add(.{
1174 .tag = .int,
1175 .data = .{ .int = integer },
1176 });
1177 }
1178
1179 pub fn addUnNode(
1180 gz: *GenZir,
1181 tag: zir.Inst.Tag,
1182 operand: zir.Inst.Ref,
1183 /// Absolute node index. This function does the conversion to offset from Decl.
1184 src_node: ast.Node.Index,
1185 ) !zir.Inst.Ref {
1186 assert(operand != .none);
1187 return gz.add(.{
1188 .tag = tag,
1189 .data = .{ .un_node = .{
1190 .operand = operand,
1191 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
1192 } },
1193 });
1194 }
1195
1196 pub fn addPlNode(
1197 gz: *GenZir,
1198 tag: zir.Inst.Tag,
1199 /// Absolute node index. This function does the conversion to offset from Decl.
1200 src_node: ast.Node.Index,
1201 extra: anytype,
1202 ) !zir.Inst.Ref {
1203 const gpa = gz.astgen.mod.gpa;
1204 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1205 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1206
1207 const payload_index = try gz.astgen.addExtra(extra);
1208 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1209 gz.astgen.instructions.appendAssumeCapacity(.{
1210 .tag = tag,
1211 .data = .{ .pl_node = .{
1212 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
1213 .payload_index = payload_index,
1214 } },
1215 });
1216 gz.instructions.appendAssumeCapacity(new_index);
1217 return gz.astgen.indexToRef(new_index);
1218 }
1219
1220 pub fn addArrayTypeSentinel(
1221 gz: *GenZir,
1222 len: zir.Inst.Ref,
1223 sentinel: zir.Inst.Ref,
1224 elem_type: zir.Inst.Ref,
1225 ) !zir.Inst.Ref {
1226 const gpa = gz.astgen.mod.gpa;
1227 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1228 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1229
1230 const payload_index = try gz.astgen.addExtra(zir.Inst.ArrayTypeSentinel{
1231 .sentinel = sentinel,
1232 .elem_type = elem_type,
1233 });
1234 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1235 gz.astgen.instructions.appendAssumeCapacity(.{
1236 .tag = .array_type_sentinel,
1237 .data = .{ .array_type_sentinel = .{
1238 .len = len,
1239 .payload_index = payload_index,
1240 } },
1241 });
1242 gz.instructions.appendAssumeCapacity(new_index);
1243 return gz.astgen.indexToRef(new_index);
1244 }
1245
1246 pub fn addUnTok(
1247 gz: *GenZir,
1248 tag: zir.Inst.Tag,
1249 operand: zir.Inst.Ref,
1250 /// Absolute token index. This function does the conversion to Decl offset.
1251 abs_tok_index: ast.TokenIndex,
1252 ) !zir.Inst.Ref {
1253 assert(operand != .none);
1254 return gz.add(.{
1255 .tag = tag,
1256 .data = .{ .un_tok = .{
1257 .operand = operand,
1258 .src_tok = abs_tok_index - gz.astgen.decl.srcToken(),
1259 } },
1260 });
1261 }
1262
1263 pub fn addStrTok(
1264 gz: *GenZir,
1265 tag: zir.Inst.Tag,
1266 str_index: u32,
1267 /// Absolute token index. This function does the conversion to Decl offset.
1268 abs_tok_index: ast.TokenIndex,
1269 ) !zir.Inst.Ref {
1270 return gz.add(.{
1271 .tag = tag,
1272 .data = .{ .str_tok = .{
1273 .start = str_index,
1274 .src_tok = abs_tok_index - gz.astgen.decl.srcToken(),
1275 } },
1276 });
1277 }
1278
1279 pub fn addBreak(
1280 gz: *GenZir,
1281 tag: zir.Inst.Tag,
1282 break_block: zir.Inst.Index,
1283 operand: zir.Inst.Ref,
1284 ) !zir.Inst.Index {
1285 return gz.addAsIndex(.{
1286 .tag = tag,
1287 .data = .{ .@"break" = .{
1288 .block_inst = break_block,
1289 .operand = operand,
1290 } },
1291 });
1292 }
1293
1294 pub fn addBin(
1295 gz: *GenZir,
1296 tag: zir.Inst.Tag,
1297 lhs: zir.Inst.Ref,
1298 rhs: zir.Inst.Ref,
1299 ) !zir.Inst.Ref {
1300 assert(lhs != .none);
1301 assert(rhs != .none);
1302 return gz.add(.{
1303 .tag = tag,
1304 .data = .{ .bin = .{
1305 .lhs = lhs,
1306 .rhs = rhs,
1307 } },
1308 });
1309 }
1310
1311 pub fn addDecl(
1312 gz: *GenZir,
1313 tag: zir.Inst.Tag,
1314 decl_index: u32,
1315 src_node: ast.Node.Index,
1316 ) !zir.Inst.Ref {
1317 return gz.add(.{
1318 .tag = tag,
1319 .data = .{ .pl_node = .{
1320 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
1321 .payload_index = decl_index,
1322 } },
1323 });
1324 }
1325
1326 pub fn addNode(
1327 gz: *GenZir,
1328 tag: zir.Inst.Tag,
1329 /// Absolute node index. This function does the conversion to offset from Decl.
1330 src_node: ast.Node.Index,
1331 ) !zir.Inst.Ref {
1332 return gz.add(.{
1333 .tag = tag,
1334 .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(src_node) },
1335 });
1336 }
1337
1338 /// Asserts that `str` is 8 or fewer bytes.
1339 pub fn addSmallStr(
1340 gz: *GenZir,
1341 tag: zir.Inst.Tag,
1342 str: []const u8,
1343 ) !zir.Inst.Ref {
1344 var buf: [9]u8 = undefined;
1345 mem.copy(u8, &buf, str);
1346 buf[str.len] = 0;
1347
1348 return gz.add(.{
1349 .tag = tag,
1350 .data = .{ .small_str = .{ .bytes = buf[0..8].* } },
1351 });
1352 }
1353
1354 /// Note that this returns a `zir.Inst.Index` not a ref.
1355 /// Does *not* append the block instruction to the scope.
1356 /// Leaves the `payload_index` field undefined.
1357 pub fn addBlock(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index {
1358 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1359 const gpa = gz.astgen.mod.gpa;
1360 try gz.astgen.instructions.append(gpa, .{
1361 .tag = tag,
1362 .data = .{ .pl_node = .{
1363 .src_node = gz.astgen.decl.nodeIndexToRelative(node),
1364 .payload_index = undefined,
1365 } },
1366 });
1367 return new_index;
1368 }
1369
1370 /// Note that this returns a `zir.Inst.Index` not a ref.
1371 /// Leaves the `payload_index` field undefined.
1372 pub fn addCondBr(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index {
1373 const gpa = gz.astgen.mod.gpa;
1374 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1375 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1376 try gz.astgen.instructions.append(gpa, .{
1377 .tag = tag,
1378 .data = .{ .pl_node = .{
1379 .src_node = gz.astgen.decl.nodeIndexToRelative(node),
1380 .payload_index = undefined,
1381 } },
1382 });
1383 gz.instructions.appendAssumeCapacity(new_index);
1384 return new_index;
1385 }
1386
1387 pub fn addConst(gz: *GenZir, typed_value: *TypedValue) !zir.Inst.Ref {
1388 return gz.add(.{
1389 .tag = .@"const",
1390 .data = .{ .@"const" = typed_value },
1391 });
1392 }
1393
1394 pub fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref {
1395 return gz.astgen.indexToRef(try gz.addAsIndex(inst));
1396 }
1397
1398 pub fn addAsIndex(gz: *GenZir, inst: zir.Inst) !zir.Inst.Index {
1399 const gpa = gz.astgen.mod.gpa;
1400 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1401 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1402
1403 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1404 gz.astgen.instructions.appendAssumeCapacity(inst);
1405 gz.instructions.appendAssumeCapacity(new_index);
1406 return new_index;
1407 }
8001408 };
8011409
8021410 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
......@@ -805,11 +1413,13 @@ pub const Scope = struct {
8051413 pub const LocalVal = struct {
8061414 pub const base_tag: Tag = .local_val;
8071415 base: Scope = Scope{ .tag = base_tag },
808 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
1416 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`.
8091417 parent: *Scope,
810 gen_zir: *GenZIR,
1418 gen_zir: *GenZir,
8111419 name: []const u8,
812 inst: *zir.Inst,
1420 inst: zir.Inst.Ref,
1421 /// Source location of the corresponding variable declaration.
1422 src: LazySrcLoc,
8131423 };
8141424
8151425 /// This could be a `const` or `var` local. It has a pointer instead of a value.
......@@ -818,21 +1428,19 @@ pub const Scope = struct {
8181428 pub const LocalPtr = struct {
8191429 pub const base_tag: Tag = .local_ptr;
8201430 base: Scope = Scope{ .tag = base_tag },
821 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
1431 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`.
8221432 parent: *Scope,
823 gen_zir: *GenZIR,
1433 gen_zir: *GenZir,
8241434 name: []const u8,
825 ptr: *zir.Inst,
1435 ptr: zir.Inst.Ref,
1436 /// Source location of the corresponding variable declaration.
1437 src: LazySrcLoc,
8261438 };
8271439
828 pub const Nosuspend = struct {
829 pub const base_tag: Tag = .gen_nosuspend;
830
1440 pub const DeclRef = struct {
1441 pub const base_tag: Tag = .decl_ref;
8311442 base: Scope = Scope{ .tag = base_tag },
832 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
833 parent: *Scope,
834 gen_zir: *GenZIR,
835 src: usize,
1443 decl: *Decl,
8361444 };
8371445};
8381446
......@@ -855,17 +1463,17 @@ pub const ErrorMsg = struct {
8551463 comptime format: []const u8,
8561464 args: anytype,
8571465 ) !*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;
1466 const err_msg = try gpa.create(ErrorMsg);
1467 errdefer gpa.destroy(err_msg);
1468 err_msg.* = try init(gpa, src_loc, format, args);
1469 return err_msg;
8621470 }
8631471
8641472 /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`,
8651473 /// as well as all notes.
866 pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
867 self.deinit(gpa);
868 gpa.destroy(self);
1474 pub fn destroy(err_msg: *ErrorMsg, gpa: *Allocator) void {
1475 err_msg.deinit(gpa);
1476 gpa.destroy(err_msg);
8691477 }
8701478
8711479 pub fn init(
......@@ -880,84 +1488,715 @@ pub const ErrorMsg = struct {
8801488 };
8811489 }
8821490
883 pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void {
884 for (self.notes) |*note| {
1491 pub fn deinit(err_msg: *ErrorMsg, gpa: *Allocator) void {
1492 for (err_msg.notes) |*note| {
8851493 note.deinit(gpa);
8861494 }
887 gpa.free(self.notes);
888 gpa.free(self.msg);
889 self.* = undefined;
1495 gpa.free(err_msg.notes);
1496 gpa.free(err_msg.msg);
1497 err_msg.* = undefined;
8901498 }
8911499};
8921500
8931501/// Canonical reference to a position within a source file.
8941502pub const SrcLoc = struct {
895 file_scope: *Scope.File,
896 byte_offset: usize,
897};
898
899pub const InnerError = error{ OutOfMemory, AnalysisFail };
1503 /// The active field is determined by tag of `lazy`.
1504 container: union {
1505 /// The containing `Decl` according to the source code.
1506 decl: *Decl,
1507 file_scope: *Scope.File,
1508 },
1509 /// Relative to `decl`.
1510 lazy: LazySrcLoc,
1511
1512 pub fn fileScope(src_loc: SrcLoc) *Scope.File {
1513 return switch (src_loc.lazy) {
1514 .unneeded => unreachable,
1515
1516 .byte_abs,
1517 .token_abs,
1518 .node_abs,
1519 => src_loc.container.file_scope,
1520
1521 .byte_offset,
1522 .token_offset,
1523 .node_offset,
1524 .node_offset_var_decl_ty,
1525 .node_offset_for_cond,
1526 .node_offset_builtin_call_arg0,
1527 .node_offset_builtin_call_arg1,
1528 .node_offset_array_access_index,
1529 .node_offset_slice_sentinel,
1530 .node_offset_call_func,
1531 .node_offset_field_name,
1532 .node_offset_deref_ptr,
1533 .node_offset_asm_source,
1534 .node_offset_asm_ret_ty,
1535 .node_offset_if_cond,
1536 .node_offset_bin_op,
1537 .node_offset_bin_lhs,
1538 .node_offset_bin_rhs,
1539 .node_offset_switch_operand,
1540 .node_offset_switch_special_prong,
1541 .node_offset_switch_range,
1542 .node_offset_fn_type_cc,
1543 .node_offset_fn_type_ret_ty,
1544 => src_loc.container.decl.container.file_scope,
1545 };
1546 }
9001547
901pub fn deinit(self: *Module) void {
902 const gpa = self.gpa;
1548 pub fn byteOffset(src_loc: SrcLoc) !u32 {
1549 switch (src_loc.lazy) {
1550 .unneeded => unreachable,
9031551
904 self.compile_log_text.deinit(gpa);
1552 .byte_abs => |byte_index| return byte_index,
9051553
906 self.zig_cache_artifact_directory.handle.close();
1554 .token_abs => |tok_index| {
1555 const tree = src_loc.container.file_scope.base.tree();
1556 const token_starts = tree.tokens.items(.start);
1557 return token_starts[tok_index];
1558 },
1559 .node_abs => |node| {
1560 const tree = src_loc.container.file_scope.base.tree();
1561 const token_starts = tree.tokens.items(.start);
1562 const tok_index = tree.firstToken(node);
1563 return token_starts[tok_index];
1564 },
1565 .byte_offset => |byte_off| {
1566 const decl = src_loc.container.decl;
1567 return decl.srcByteOffset() + byte_off;
1568 },
1569 .token_offset => |tok_off| {
1570 const decl = src_loc.container.decl;
1571 const tok_index = decl.srcToken() + tok_off;
1572 const tree = decl.container.file_scope.base.tree();
1573 const token_starts = tree.tokens.items(.start);
1574 return token_starts[tok_index];
1575 },
1576 .node_offset, .node_offset_bin_op => |node_off| {
1577 const decl = src_loc.container.decl;
1578 const node = decl.relativeToNodeIndex(node_off);
1579 const tree = decl.container.file_scope.base.tree();
1580 const main_tokens = tree.nodes.items(.main_token);
1581 const tok_index = main_tokens[node];
1582 const token_starts = tree.tokens.items(.start);
1583 return token_starts[tok_index];
1584 },
1585 .node_offset_var_decl_ty => |node_off| {
1586 const decl = src_loc.container.decl;
1587 const node = decl.relativeToNodeIndex(node_off);
1588 const tree = decl.container.file_scope.base.tree();
1589 const node_tags = tree.nodes.items(.tag);
1590 const full = switch (node_tags[node]) {
1591 .global_var_decl => tree.globalVarDecl(node),
1592 .local_var_decl => tree.localVarDecl(node),
1593 .simple_var_decl => tree.simpleVarDecl(node),
1594 .aligned_var_decl => tree.alignedVarDecl(node),
1595 else => unreachable,
1596 };
1597 const tok_index = if (full.ast.type_node != 0) blk: {
1598 const main_tokens = tree.nodes.items(.main_token);
1599 break :blk main_tokens[full.ast.type_node];
1600 } else blk: {
1601 break :blk full.ast.mut_token + 1; // the name token
1602 };
1603 const token_starts = tree.tokens.items(.start);
1604 return token_starts[tok_index];
1605 },
1606 .node_offset_builtin_call_arg0 => |node_off| {
1607 const decl = src_loc.container.decl;
1608 const tree = decl.container.file_scope.base.tree();
1609 const node_datas = tree.nodes.items(.data);
1610 const node_tags = tree.nodes.items(.tag);
1611 const node = decl.relativeToNodeIndex(node_off);
1612 const param = switch (node_tags[node]) {
1613 .builtin_call_two, .builtin_call_two_comma => node_datas[node].lhs,
1614 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs],
1615 else => unreachable,
1616 };
1617 const main_tokens = tree.nodes.items(.main_token);
1618 const tok_index = main_tokens[param];
1619 const token_starts = tree.tokens.items(.start);
1620 return token_starts[tok_index];
1621 },
1622 .node_offset_builtin_call_arg1 => |node_off| {
1623 const decl = src_loc.container.decl;
1624 const tree = decl.container.file_scope.base.tree();
1625 const node_datas = tree.nodes.items(.data);
1626 const node_tags = tree.nodes.items(.tag);
1627 const node = decl.relativeToNodeIndex(node_off);
1628 const param = switch (node_tags[node]) {
1629 .builtin_call_two, .builtin_call_two_comma => node_datas[node].rhs,
1630 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + 1],
1631 else => unreachable,
1632 };
1633 const main_tokens = tree.nodes.items(.main_token);
1634 const tok_index = main_tokens[param];
1635 const token_starts = tree.tokens.items(.start);
1636 return token_starts[tok_index];
1637 },
1638 .node_offset_array_access_index => |node_off| {
1639 const decl = src_loc.container.decl;
1640 const tree = decl.container.file_scope.base.tree();
1641 const node_datas = tree.nodes.items(.data);
1642 const node_tags = tree.nodes.items(.tag);
1643 const node = decl.relativeToNodeIndex(node_off);
1644 const main_tokens = tree.nodes.items(.main_token);
1645 const tok_index = main_tokens[node_datas[node].rhs];
1646 const token_starts = tree.tokens.items(.start);
1647 return token_starts[tok_index];
1648 },
1649 .node_offset_slice_sentinel => |node_off| {
1650 const decl = src_loc.container.decl;
1651 const tree = decl.container.file_scope.base.tree();
1652 const node_datas = tree.nodes.items(.data);
1653 const node_tags = tree.nodes.items(.tag);
1654 const node = decl.relativeToNodeIndex(node_off);
1655 const full = switch (node_tags[node]) {
1656 .slice_open => tree.sliceOpen(node),
1657 .slice => tree.slice(node),
1658 .slice_sentinel => tree.sliceSentinel(node),
1659 else => unreachable,
1660 };
1661 const main_tokens = tree.nodes.items(.main_token);
1662 const tok_index = main_tokens[full.ast.sentinel];
1663 const token_starts = tree.tokens.items(.start);
1664 return token_starts[tok_index];
1665 },
1666 .node_offset_call_func => |node_off| {
1667 const decl = src_loc.container.decl;
1668 const tree = decl.container.file_scope.base.tree();
1669 const node_datas = tree.nodes.items(.data);
1670 const node_tags = tree.nodes.items(.tag);
1671 const node = decl.relativeToNodeIndex(node_off);
1672 var params: [1]ast.Node.Index = undefined;
1673 const full = switch (node_tags[node]) {
1674 .call_one,
1675 .call_one_comma,
1676 .async_call_one,
1677 .async_call_one_comma,
1678 => tree.callOne(&params, node),
1679
1680 .call,
1681 .call_comma,
1682 .async_call,
1683 .async_call_comma,
1684 => tree.callFull(node),
9071685
908 self.deletion_set.deinit(gpa);
1686 else => unreachable,
1687 };
1688 const main_tokens = tree.nodes.items(.main_token);
1689 const tok_index = main_tokens[full.ast.fn_expr];
1690 const token_starts = tree.tokens.items(.start);
1691 return token_starts[tok_index];
1692 },
1693 .node_offset_field_name => |node_off| {
1694 const decl = src_loc.container.decl;
1695 const tree = decl.container.file_scope.base.tree();
1696 const node_datas = tree.nodes.items(.data);
1697 const node_tags = tree.nodes.items(.tag);
1698 const node = decl.relativeToNodeIndex(node_off);
1699 const tok_index = node_datas[node].rhs;
1700 const token_starts = tree.tokens.items(.start);
1701 return token_starts[tok_index];
1702 },
1703 .node_offset_deref_ptr => |node_off| {
1704 const decl = src_loc.container.decl;
1705 const tree = decl.container.file_scope.base.tree();
1706 const node_datas = tree.nodes.items(.data);
1707 const node_tags = tree.nodes.items(.tag);
1708 const node = decl.relativeToNodeIndex(node_off);
1709 const tok_index = node_datas[node].lhs;
1710 const token_starts = tree.tokens.items(.start);
1711 return token_starts[tok_index];
1712 },
1713 .node_offset_asm_source => |node_off| {
1714 const decl = src_loc.container.decl;
1715 const tree = decl.container.file_scope.base.tree();
1716 const node_datas = tree.nodes.items(.data);
1717 const node_tags = tree.nodes.items(.tag);
1718 const node = decl.relativeToNodeIndex(node_off);
1719 const full = switch (node_tags[node]) {
1720 .asm_simple => tree.asmSimple(node),
1721 .@"asm" => tree.asmFull(node),
1722 else => unreachable,
1723 };
1724 const main_tokens = tree.nodes.items(.main_token);
1725 const tok_index = main_tokens[full.ast.template];
1726 const token_starts = tree.tokens.items(.start);
1727 return token_starts[tok_index];
1728 },
1729 .node_offset_asm_ret_ty => |node_off| {
1730 const decl = src_loc.container.decl;
1731 const tree = decl.container.file_scope.base.tree();
1732 const node_datas = tree.nodes.items(.data);
1733 const node_tags = tree.nodes.items(.tag);
1734 const node = decl.relativeToNodeIndex(node_off);
1735 const full = switch (node_tags[node]) {
1736 .asm_simple => tree.asmSimple(node),
1737 .@"asm" => tree.asmFull(node),
1738 else => unreachable,
1739 };
1740 const main_tokens = tree.nodes.items(.main_token);
1741 const tok_index = main_tokens[full.outputs[0]];
1742 const token_starts = tree.tokens.items(.start);
1743 return token_starts[tok_index];
1744 },
9091745
910 for (self.decl_table.items()) |entry| {
911 entry.value.destroy(self);
912 }
913 self.decl_table.deinit(gpa);
1746 .node_offset_for_cond, .node_offset_if_cond => |node_off| {
1747 const decl = src_loc.container.decl;
1748 const node = decl.relativeToNodeIndex(node_off);
1749 const tree = decl.container.file_scope.base.tree();
1750 const node_tags = tree.nodes.items(.tag);
1751 const src_node = switch (node_tags[node]) {
1752 .if_simple => tree.ifSimple(node).ast.cond_expr,
1753 .@"if" => tree.ifFull(node).ast.cond_expr,
1754 .while_simple => tree.whileSimple(node).ast.cond_expr,
1755 .while_cont => tree.whileCont(node).ast.cond_expr,
1756 .@"while" => tree.whileFull(node).ast.cond_expr,
1757 .for_simple => tree.forSimple(node).ast.cond_expr,
1758 .@"for" => tree.forFull(node).ast.cond_expr,
1759 else => unreachable,
1760 };
1761 const main_tokens = tree.nodes.items(.main_token);
1762 const tok_index = main_tokens[src_node];
1763 const token_starts = tree.tokens.items(.start);
1764 return token_starts[tok_index];
1765 },
1766 .node_offset_bin_lhs => |node_off| {
1767 const decl = src_loc.container.decl;
1768 const node = decl.relativeToNodeIndex(node_off);
1769 const tree = decl.container.file_scope.base.tree();
1770 const node_datas = tree.nodes.items(.data);
1771 const src_node = node_datas[node].lhs;
1772 const main_tokens = tree.nodes.items(.main_token);
1773 const tok_index = main_tokens[src_node];
1774 const token_starts = tree.tokens.items(.start);
1775 return token_starts[tok_index];
1776 },
1777 .node_offset_bin_rhs => |node_off| {
1778 const decl = src_loc.container.decl;
1779 const node = decl.relativeToNodeIndex(node_off);
1780 const tree = decl.container.file_scope.base.tree();
1781 const node_datas = tree.nodes.items(.data);
1782 const src_node = node_datas[node].rhs;
1783 const main_tokens = tree.nodes.items(.main_token);
1784 const tok_index = main_tokens[src_node];
1785 const token_starts = tree.tokens.items(.start);
1786 return token_starts[tok_index];
1787 },
9141788
915 for (self.failed_decls.items()) |entry| {
916 entry.value.destroy(gpa);
917 }
918 self.failed_decls.deinit(gpa);
1789 .node_offset_switch_operand => |node_off| {
1790 const decl = src_loc.container.decl;
1791 const node = decl.relativeToNodeIndex(node_off);
1792 const tree = decl.container.file_scope.base.tree();
1793 const node_datas = tree.nodes.items(.data);
1794 const src_node = node_datas[node].lhs;
1795 const main_tokens = tree.nodes.items(.main_token);
1796 const tok_index = main_tokens[src_node];
1797 const token_starts = tree.tokens.items(.start);
1798 return token_starts[tok_index];
1799 },
9191800
920 for (self.emit_h_failed_decls.items()) |entry| {
921 entry.value.destroy(gpa);
922 }
923 self.emit_h_failed_decls.deinit(gpa);
1801 .node_offset_switch_special_prong => |node_off| {
1802 const decl = src_loc.container.decl;
1803 const switch_node = decl.relativeToNodeIndex(node_off);
1804 const tree = decl.container.file_scope.base.tree();
1805 const node_datas = tree.nodes.items(.data);
1806 const node_tags = tree.nodes.items(.tag);
1807 const main_tokens = tree.nodes.items(.main_token);
1808 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
1809 const case_nodes = tree.extra_data[extra.start..extra.end];
1810 for (case_nodes) |case_node| {
1811 const case = switch (node_tags[case_node]) {
1812 .switch_case_one => tree.switchCaseOne(case_node),
1813 .switch_case => tree.switchCase(case_node),
1814 else => unreachable,
1815 };
1816 const is_special = (case.ast.values.len == 0) or
1817 (case.ast.values.len == 1 and
1818 node_tags[case.ast.values[0]] == .identifier and
1819 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));
1820 if (!is_special) continue;
1821
1822 const tok_index = main_tokens[case_node];
1823 const token_starts = tree.tokens.items(.start);
1824 return token_starts[tok_index];
1825 } else unreachable;
1826 },
9241827
925 for (self.failed_files.items()) |entry| {
926 entry.value.destroy(gpa);
1828 .node_offset_switch_range => |node_off| {
1829 const decl = src_loc.container.decl;
1830 const switch_node = decl.relativeToNodeIndex(node_off);
1831 const tree = decl.container.file_scope.base.tree();
1832 const node_datas = tree.nodes.items(.data);
1833 const node_tags = tree.nodes.items(.tag);
1834 const main_tokens = tree.nodes.items(.main_token);
1835 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
1836 const case_nodes = tree.extra_data[extra.start..extra.end];
1837 for (case_nodes) |case_node| {
1838 const case = switch (node_tags[case_node]) {
1839 .switch_case_one => tree.switchCaseOne(case_node),
1840 .switch_case => tree.switchCase(case_node),
1841 else => unreachable,
1842 };
1843 const is_special = (case.ast.values.len == 0) or
1844 (case.ast.values.len == 1 and
1845 node_tags[case.ast.values[0]] == .identifier and
1846 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));
1847 if (is_special) continue;
1848
1849 for (case.ast.values) |item_node| {
1850 if (node_tags[item_node] == .switch_range) {
1851 const tok_index = main_tokens[item_node];
1852 const token_starts = tree.tokens.items(.start);
1853 return token_starts[tok_index];
1854 }
1855 }
1856 } else unreachable;
1857 },
1858
1859 .node_offset_fn_type_cc => |node_off| {
1860 const decl = src_loc.container.decl;
1861 const tree = decl.container.file_scope.base.tree();
1862 const node_datas = tree.nodes.items(.data);
1863 const node_tags = tree.nodes.items(.tag);
1864 const node = decl.relativeToNodeIndex(node_off);
1865 var params: [1]ast.Node.Index = undefined;
1866 const full = switch (node_tags[node]) {
1867 .fn_proto_simple => tree.fnProtoSimple(&params, node),
1868 .fn_proto_multi => tree.fnProtoMulti(node),
1869 .fn_proto_one => tree.fnProtoOne(&params, node),
1870 .fn_proto => tree.fnProto(node),
1871 else => unreachable,
1872 };
1873 const main_tokens = tree.nodes.items(.main_token);
1874 const tok_index = main_tokens[full.ast.callconv_expr];
1875 const token_starts = tree.tokens.items(.start);
1876 return token_starts[tok_index];
1877 },
1878
1879 .node_offset_fn_type_ret_ty => |node_off| {
1880 const decl = src_loc.container.decl;
1881 const tree = decl.container.file_scope.base.tree();
1882 const node_datas = tree.nodes.items(.data);
1883 const node_tags = tree.nodes.items(.tag);
1884 const node = decl.relativeToNodeIndex(node_off);
1885 var params: [1]ast.Node.Index = undefined;
1886 const full = switch (node_tags[node]) {
1887 .fn_proto_simple => tree.fnProtoSimple(&params, node),
1888 .fn_proto_multi => tree.fnProtoMulti(node),
1889 .fn_proto_one => tree.fnProtoOne(&params, node),
1890 .fn_proto => tree.fnProto(node),
1891 else => unreachable,
1892 };
1893 const main_tokens = tree.nodes.items(.main_token);
1894 const tok_index = main_tokens[full.ast.return_type];
1895 const token_starts = tree.tokens.items(.start);
1896 return token_starts[tok_index];
1897 },
1898 }
1899 }
1900};
1901
1902/// Resolving a source location into a byte offset may require doing work
1903/// that we would rather not do unless the error actually occurs.
1904/// Therefore we need a data structure that contains the information necessary
1905/// to lazily produce a `SrcLoc` as required.
1906/// Most of the offsets in this data structure are relative to the containing Decl.
1907/// This makes the source location resolve properly even when a Decl gets
1908/// shifted up or down in the file, as long as the Decl's contents itself
1909/// do not change.
1910pub const LazySrcLoc = union(enum) {
1911 /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting
1912 /// that all code paths which would need to resolve the source location are
1913 /// unreachable. If you are debugging this tag incorrectly being this value,
1914 /// look into using reverse-continue with a memory watchpoint to see where the
1915 /// value is being set to this tag.
1916 unneeded,
1917 /// The source location points to a byte offset within a source file,
1918 /// offset from 0. The source file is determined contextually.
1919 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
1920 byte_abs: u32,
1921 /// The source location points to a token within a source file,
1922 /// offset from 0. The source file is determined contextually.
1923 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
1924 token_abs: u32,
1925 /// The source location points to an AST node within a source file,
1926 /// offset from 0. The source file is determined contextually.
1927 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
1928 node_abs: u32,
1929 /// The source location points to a byte offset within a source file,
1930 /// offset from the byte offset of the Decl within the file.
1931 /// The Decl is determined contextually.
1932 byte_offset: u32,
1933 /// This data is the offset into the token list from the Decl token.
1934 /// The Decl is determined contextually.
1935 token_offset: u32,
1936 /// The source location points to an AST node, which is this value offset
1937 /// from its containing Decl node AST index.
1938 /// The Decl is determined contextually.
1939 node_offset: i32,
1940 /// The source location points to a variable declaration type expression,
1941 /// found by taking this AST node index offset from the containing
1942 /// Decl AST node, which points to a variable declaration AST node. Next, navigate
1943 /// to the type expression.
1944 /// The Decl is determined contextually.
1945 node_offset_var_decl_ty: i32,
1946 /// The source location points to a for loop condition expression,
1947 /// found by taking this AST node index offset from the containing
1948 /// Decl AST node, which points to a for loop AST node. Next, navigate
1949 /// to the condition expression.
1950 /// The Decl is determined contextually.
1951 node_offset_for_cond: i32,
1952 /// The source location points to the first parameter of a builtin
1953 /// function call, found by taking this AST node index offset from the containing
1954 /// Decl AST node, which points to a builtin call AST node. Next, navigate
1955 /// to the first parameter.
1956 /// The Decl is determined contextually.
1957 node_offset_builtin_call_arg0: i32,
1958 /// Same as `node_offset_builtin_call_arg0` except arg index 1.
1959 node_offset_builtin_call_arg1: i32,
1960 /// The source location points to the index expression of an array access
1961 /// expression, found by taking this AST node index offset from the containing
1962 /// Decl AST node, which points to an array access AST node. Next, navigate
1963 /// to the index expression.
1964 /// The Decl is determined contextually.
1965 node_offset_array_access_index: i32,
1966 /// The source location points to the sentinel expression of a slice
1967 /// expression, found by taking this AST node index offset from the containing
1968 /// Decl AST node, which points to a slice AST node. Next, navigate
1969 /// to the sentinel expression.
1970 /// The Decl is determined contextually.
1971 node_offset_slice_sentinel: i32,
1972 /// The source location points to the callee expression of a function
1973 /// call expression, found by taking this AST node index offset from the containing
1974 /// Decl AST node, which points to a function call AST node. Next, navigate
1975 /// to the callee expression.
1976 /// The Decl is determined contextually.
1977 node_offset_call_func: i32,
1978 /// The source location points to the field name of a field access expression,
1979 /// found by taking this AST node index offset from the containing
1980 /// Decl AST node, which points to a field access AST node. Next, navigate
1981 /// to the field name token.
1982 /// The Decl is determined contextually.
1983 node_offset_field_name: i32,
1984 /// The source location points to the pointer of a pointer deref expression,
1985 /// found by taking this AST node index offset from the containing
1986 /// Decl AST node, which points to a pointer deref AST node. Next, navigate
1987 /// to the pointer expression.
1988 /// The Decl is determined contextually.
1989 node_offset_deref_ptr: i32,
1990 /// The source location points to the assembly source code of an inline assembly
1991 /// expression, found by taking this AST node index offset from the containing
1992 /// Decl AST node, which points to inline assembly AST node. Next, navigate
1993 /// to the asm template source code.
1994 /// The Decl is determined contextually.
1995 node_offset_asm_source: i32,
1996 /// The source location points to the return type of an inline assembly
1997 /// expression, found by taking this AST node index offset from the containing
1998 /// Decl AST node, which points to inline assembly AST node. Next, navigate
1999 /// to the return type expression.
2000 /// The Decl is determined contextually.
2001 node_offset_asm_ret_ty: i32,
2002 /// The source location points to the condition expression of an if
2003 /// expression, found by taking this AST node index offset from the containing
2004 /// Decl AST node, which points to an if expression AST node. Next, navigate
2005 /// to the condition expression.
2006 /// The Decl is determined contextually.
2007 node_offset_if_cond: i32,
2008 /// The source location points to a binary expression, such as `a + b`, found
2009 /// by taking this AST node index offset from the containing Decl AST node.
2010 /// The Decl is determined contextually.
2011 node_offset_bin_op: i32,
2012 /// The source location points to the LHS of a binary expression, found
2013 /// by taking this AST node index offset from the containing Decl AST node,
2014 /// which points to a binary expression AST node. Next, nagivate to the LHS.
2015 /// The Decl is determined contextually.
2016 node_offset_bin_lhs: i32,
2017 /// The source location points to the RHS of a binary expression, found
2018 /// by taking this AST node index offset from the containing Decl AST node,
2019 /// which points to a binary expression AST node. Next, nagivate to the RHS.
2020 /// The Decl is determined contextually.
2021 node_offset_bin_rhs: i32,
2022 /// The source location points to the operand of a switch expression, found
2023 /// by taking this AST node index offset from the containing Decl AST node,
2024 /// which points to a switch expression AST node. Next, nagivate to the operand.
2025 /// The Decl is determined contextually.
2026 node_offset_switch_operand: i32,
2027 /// The source location points to the else/`_` prong of a switch expression, found
2028 /// by taking this AST node index offset from the containing Decl AST node,
2029 /// which points to a switch expression AST node. Next, nagivate to the else/`_` prong.
2030 /// The Decl is determined contextually.
2031 node_offset_switch_special_prong: i32,
2032 /// The source location points to all the ranges of a switch expression, found
2033 /// by taking this AST node index offset from the containing Decl AST node,
2034 /// which points to a switch expression AST node. Next, nagivate to any of the
2035 /// range nodes. The error applies to all of them.
2036 /// The Decl is determined contextually.
2037 node_offset_switch_range: i32,
2038 /// The source location points to the calling convention of a function type
2039 /// expression, found by taking this AST node index offset from the containing
2040 /// Decl AST node, which points to a function type AST node. Next, nagivate to
2041 /// the calling convention node.
2042 /// The Decl is determined contextually.
2043 node_offset_fn_type_cc: i32,
2044 /// The source location points to the return type of a function type
2045 /// expression, found by taking this AST node index offset from the containing
2046 /// Decl AST node, which points to a function type AST node. Next, nagivate to
2047 /// the return type node.
2048 /// The Decl is determined contextually.
2049 node_offset_fn_type_ret_ty: i32,
2050
2051 /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.
2052 pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {
2053 return switch (lazy) {
2054 .unneeded,
2055 .byte_abs,
2056 .token_abs,
2057 .node_abs,
2058 => .{
2059 .container = .{ .file_scope = scope.getFileScope() },
2060 .lazy = lazy,
2061 },
2062
2063 .byte_offset,
2064 .token_offset,
2065 .node_offset,
2066 .node_offset_var_decl_ty,
2067 .node_offset_for_cond,
2068 .node_offset_builtin_call_arg0,
2069 .node_offset_builtin_call_arg1,
2070 .node_offset_array_access_index,
2071 .node_offset_slice_sentinel,
2072 .node_offset_call_func,
2073 .node_offset_field_name,
2074 .node_offset_deref_ptr,
2075 .node_offset_asm_source,
2076 .node_offset_asm_ret_ty,
2077 .node_offset_if_cond,
2078 .node_offset_bin_op,
2079 .node_offset_bin_lhs,
2080 .node_offset_bin_rhs,
2081 .node_offset_switch_operand,
2082 .node_offset_switch_special_prong,
2083 .node_offset_switch_range,
2084 .node_offset_fn_type_cc,
2085 .node_offset_fn_type_ret_ty,
2086 => .{
2087 .container = .{ .decl = scope.srcDecl().? },
2088 .lazy = lazy,
2089 },
2090 };
2091 }
2092
2093 /// Upgrade to a `SrcLoc` based on the `Decl` provided.
2094 pub fn toSrcLocWithDecl(lazy: LazySrcLoc, decl: *Decl) SrcLoc {
2095 return switch (lazy) {
2096 .unneeded,
2097 .byte_abs,
2098 .token_abs,
2099 .node_abs,
2100 => .{
2101 .container = .{ .file_scope = decl.getFileScope() },
2102 .lazy = lazy,
2103 },
2104
2105 .byte_offset,
2106 .token_offset,
2107 .node_offset,
2108 .node_offset_var_decl_ty,
2109 .node_offset_for_cond,
2110 .node_offset_builtin_call_arg0,
2111 .node_offset_builtin_call_arg1,
2112 .node_offset_array_access_index,
2113 .node_offset_slice_sentinel,
2114 .node_offset_call_func,
2115 .node_offset_field_name,
2116 .node_offset_deref_ptr,
2117 .node_offset_asm_source,
2118 .node_offset_asm_ret_ty,
2119 .node_offset_if_cond,
2120 .node_offset_bin_op,
2121 .node_offset_bin_lhs,
2122 .node_offset_bin_rhs,
2123 .node_offset_switch_operand,
2124 .node_offset_switch_special_prong,
2125 .node_offset_switch_range,
2126 .node_offset_fn_type_cc,
2127 .node_offset_fn_type_ret_ty,
2128 => .{
2129 .container = .{ .decl = decl },
2130 .lazy = lazy,
2131 },
2132 };
2133 }
2134};
2135
2136pub const InnerError = error{ OutOfMemory, AnalysisFail };
2137
2138pub fn deinit(mod: *Module) void {
2139 const gpa = mod.gpa;
2140
2141 mod.compile_log_text.deinit(gpa);
2142
2143 mod.zig_cache_artifact_directory.handle.close();
2144
2145 mod.deletion_set.deinit(gpa);
2146
2147 for (mod.decl_table.items()) |entry| {
2148 entry.value.destroy(mod);
2149 }
2150 mod.decl_table.deinit(gpa);
2151
2152 for (mod.failed_decls.items()) |entry| {
2153 entry.value.destroy(gpa);
2154 }
2155 mod.failed_decls.deinit(gpa);
2156
2157 for (mod.emit_h_failed_decls.items()) |entry| {
2158 entry.value.destroy(gpa);
9272159 }
928 self.failed_files.deinit(gpa);
2160 mod.emit_h_failed_decls.deinit(gpa);
9292161
930 for (self.failed_exports.items()) |entry| {
2162 for (mod.failed_files.items()) |entry| {
9312163 entry.value.destroy(gpa);
9322164 }
933 self.failed_exports.deinit(gpa);
2165 mod.failed_files.deinit(gpa);
9342166
935 self.compile_log_decls.deinit(gpa);
2167 for (mod.failed_exports.items()) |entry| {
2168 entry.value.destroy(gpa);
2169 }
2170 mod.failed_exports.deinit(gpa);
9362171
937 for (self.decl_exports.items()) |entry| {
2172 mod.compile_log_decls.deinit(gpa);
2173
2174 for (mod.decl_exports.items()) |entry| {
9382175 const export_list = entry.value;
9392176 gpa.free(export_list);
9402177 }
941 self.decl_exports.deinit(gpa);
2178 mod.decl_exports.deinit(gpa);
9422179
943 for (self.export_owners.items()) |entry| {
2180 for (mod.export_owners.items()) |entry| {
9442181 freeExportList(gpa, entry.value);
9452182 }
946 self.export_owners.deinit(gpa);
2183 mod.export_owners.deinit(gpa);
9472184
948 self.symbol_exports.deinit(gpa);
949 self.root_scope.destroy(gpa);
2185 mod.symbol_exports.deinit(gpa);
2186 mod.root_scope.destroy(gpa);
9502187
951 var it = self.global_error_set.iterator();
2188 var it = mod.global_error_set.iterator();
9522189 while (it.next()) |entry| {
9532190 gpa.free(entry.key);
9542191 }
955 self.global_error_set.deinit(gpa);
2192 mod.global_error_set.deinit(gpa);
2193
2194 mod.error_name_list.deinit(gpa);
9562195
957 for (self.import_table.items()) |entry| {
2196 for (mod.import_table.items()) |entry| {
9582197 entry.value.destroy(gpa);
9592198 }
960 self.import_table.deinit(gpa);
2199 mod.import_table.deinit(gpa);
9612200}
9622201
9632202fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
......@@ -1102,42 +2341,51 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
11022341 // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
11032342 var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa);
11042343 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);
11122344
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 }
2345 var code: zir.Code = blk: {
2346 var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator);
2347 defer astgen.deinit();
2348
2349 var gen_scope: Scope.GenZir = .{
2350 .force_comptime = true,
2351 .parent = &decl.container.base,
2352 .astgen = &astgen,
2353 };
2354 defer gen_scope.instructions.deinit(mod.gpa);
11182355
1119 var inst_table = Scope.Block.InstTable.init(mod.gpa);
1120 defer inst_table.deinit();
2356 const block_expr = node_datas[decl_node].lhs;
2357 _ = try AstGen.comptimeExpr(&gen_scope, &gen_scope.base, .none, block_expr);
11212358
1122 var branch_quota: u32 = default_eval_branch_quota;
2359 const code = try gen_scope.finish();
2360 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
2361 code.dump(mod.gpa, "comptime_block", &gen_scope.base, 0) catch {};
2362 }
2363 break :blk code;
2364 };
2365 defer code.deinit(mod.gpa);
11232366
2367 var sema: Sema = .{
2368 .mod = mod,
2369 .gpa = mod.gpa,
2370 .arena = &analysis_arena.allocator,
2371 .code = code,
2372 .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, code.instructions.len),
2373 .owner_decl = decl,
2374 .func = null,
2375 .owner_func = null,
2376 .param_inst_list = &.{},
2377 };
11242378 var block_scope: Scope.Block = .{
11252379 .parent = null,
1126 .inst_table = &inst_table,
1127 .func = null,
1128 .owner_decl = decl,
2380 .sema = &sema,
11292381 .src_decl = decl,
11302382 .instructions = .{},
1131 .arena = &analysis_arena.allocator,
11322383 .inlining = null,
11332384 .is_comptime = true,
1134 .branch_quota = &branch_quota,
11352385 };
11362386 defer block_scope.instructions.deinit(mod.gpa);
11372387
1138 _ = try zir_sema.analyzeBody(mod, &block_scope, .{
1139 .instructions = gen_scope.instructions.items,
1140 });
2388 _ = try sema.root(&block_scope);
11412389
11422390 decl.analysis = .complete;
11432391 decl.generation = mod.generation;
......@@ -1160,7 +2408,6 @@ fn astgenAndSemaFn(
11602408
11612409 decl.analysis = .in_progress;
11622410
1163 const token_starts = tree.tokens.items(.start);
11642411 const token_tags = tree.tokens.items(.tag);
11652412
11662413 // This arena allocator's memory is discarded at the end of this function. It is used
......@@ -1168,11 +2415,14 @@ fn astgenAndSemaFn(
11682415 // to complete the Decl analysis.
11692416 var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
11702417 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,
2418
2419 var fn_type_astgen = try AstGen.init(mod, decl, &fn_type_scope_arena.allocator);
2420 defer fn_type_astgen.deinit();
2421
2422 var fn_type_scope: Scope.GenZir = .{
11752423 .force_comptime = true,
2424 .parent = &decl.container.base,
2425 .astgen = &fn_type_astgen,
11762426 };
11772427 defer fn_type_scope.instructions.deinit(mod.gpa);
11782428
......@@ -1189,13 +2439,7 @@ fn astgenAndSemaFn(
11892439 }
11902440 break :blk count;
11912441 };
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 };
2442 const param_types = try fn_type_scope_arena.allocator.alloc(zir.Inst.Ref, param_count);
11992443
12002444 var is_var_args = false;
12012445 {
......@@ -1220,7 +2464,7 @@ fn astgenAndSemaFn(
12202464 const param_type_node = param.type_expr;
12212465 assert(param_type_node != 0);
12222466 param_types[param_type_i] =
1223 try astgen.expr(mod, &fn_type_scope.base, type_type_rl, param_type_node);
2467 try AstGen.expr(&fn_type_scope, &fn_type_scope.base, .{ .ty = .type_type }, param_type_node);
12242468 }
12252469 assert(param_type_i == param_count);
12262470 }
......@@ -1289,10 +2533,10 @@ fn astgenAndSemaFn(
12892533 if (token_tags[maybe_bang] == .bang) {
12902534 return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{});
12912535 }
1292 const return_type_inst = try astgen.expr(
1293 mod,
2536 const return_type_inst = try AstGen.expr(
2537 &fn_type_scope,
12942538 &fn_type_scope.base,
1295 type_type_rl,
2539 .{ .ty = .type_type },
12962540 fn_proto.ast.return_type,
12972541 );
12982542
......@@ -1301,73 +2545,72 @@ fn astgenAndSemaFn(
13012545 else
13022546 false;
13032547
1304 const cc_inst = if (fn_proto.ast.callconv_expr != 0) cc: {
2548 const cc: zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
13052549 // TODO instead of enum literal type, this needs to be the
13062550 // std.builtin.CallingConvention enum. We need to implement importing other files
13072551 // 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,
2552 try AstGen.comptimeExpr(
2553 &fn_type_scope,
2554 &fn_type_scope.base,
2555 .{ .ty = .enum_literal_type },
2556 fn_proto.ast.callconv_expr,
2557 )
2558 else if (is_extern) // note: https://github.com/ziglang/zig/issues/5269
2559 try fn_type_scope.addSmallStr(.enum_literal_small, "C")
2560 else
2561 .none;
2562
2563 const fn_type_inst: zir.Inst.Ref = if (cc != .none) fn_type: {
2564 const tag: zir.Inst.Tag = if (is_var_args) .fn_type_cc_var_args else .fn_type_cc;
2565 break :fn_type try fn_type_scope.addFnTypeCc(tag, .{
2566 .src_node = fn_proto.ast.proto_node,
2567 .ret_ty = return_type_inst,
13252568 .param_types = param_types,
13262569 .cc = cc,
13272570 });
1328 if (is_var_args) fn_type.tag = .fn_type_cc_var_args;
1329 break :fn_type fn_type;
13302571 } 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,
2572 const tag: zir.Inst.Tag = if (is_var_args) .fn_type_var_args else .fn_type;
2573 break :fn_type try fn_type_scope.addFnType(tag, .{
2574 .src_node = fn_proto.ast.proto_node,
2575 .ret_ty = return_type_inst,
13332576 .param_types = param_types,
13342577 });
1335 if (is_var_args) fn_type.tag = .fn_type_var_args;
1336 break :fn_type fn_type;
13372578 };
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 }
2579 _ = try fn_type_scope.addBreak(.break_inline, 0, fn_type_inst);
13422580
13432581 // We need the memory for the Type to go into the arena for the Decl
13442582 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
13452583 errdefer decl_arena.deinit();
13462584 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
13472585
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;
2586 var fn_type_code = try fn_type_scope.finish();
2587 defer fn_type_code.deinit(mod.gpa);
2588 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
2589 fn_type_code.dump(mod.gpa, "fn_type", &fn_type_scope.base, 0) catch {};
2590 }
13522591
2592 var fn_type_sema: Sema = .{
2593 .mod = mod,
2594 .gpa = mod.gpa,
2595 .arena = &decl_arena.allocator,
2596 .code = fn_type_code,
2597 .inst_map = try fn_type_scope_arena.allocator.alloc(*ir.Inst, fn_type_code.instructions.len),
2598 .owner_decl = decl,
2599 .func = null,
2600 .owner_func = null,
2601 .param_inst_list = &.{},
2602 };
13532603 var block_scope: Scope.Block = .{
13542604 .parent = null,
1355 .inst_table = &inst_table,
1356 .func = null,
1357 .owner_decl = decl,
2605 .sema = &fn_type_sema,
13582606 .src_decl = decl,
13592607 .instructions = .{},
1360 .arena = &decl_arena.allocator,
13612608 .inlining = null,
1362 .is_comptime = false,
1363 .branch_quota = &branch_quota,
2609 .is_comptime = true,
13642610 };
13652611 defer block_scope.instructions.deinit(mod.gpa);
13662612
1367 const fn_type = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, fn_type_inst, .{
1368 .instructions = fn_type_scope.instructions.items,
1369 });
1370
2613 const fn_type = try fn_type_sema.rootAsType(&block_scope);
13712614 if (body_node == 0) {
13722615 if (!is_extern) {
13732616 return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function has no body", .{});
......@@ -1409,63 +2652,69 @@ fn astgenAndSemaFn(
14092652 const new_func = try decl_arena.allocator.create(Fn);
14102653 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
14112654
1412 const fn_zir: zir.Body = blk: {
2655 const fn_zir: zir.Code = blk: {
14132656 // 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,
2657 var astgen = try AstGen.init(mod, decl, &decl_arena.allocator);
2658 astgen.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count);
2659 defer astgen.deinit();
2660
2661 var gen_scope: Scope.GenZir = .{
14182662 .force_comptime = false,
2663 .parent = &decl.container.base,
2664 .astgen = &astgen,
14192665 };
14202666 defer gen_scope.instructions.deinit(mod.gpa);
14212667
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);
2668 // Iterate over the parameters. We put the param names as the first N
2669 // items inside `extra` so that debug info later can refer to the parameter names
2670 // even while the respective source code is unloaded.
2671 try astgen.extra.ensureCapacity(mod.gpa, param_count);
2672
14242673 var params_scope = &gen_scope.base;
14252674 var i: usize = 0;
14262675 var it = fn_proto.iterate(tree);
14272676 while (it.next()) |param| : (i += 1) {
14282677 const name_token = param.name_token.?;
1429 const src = token_starts[name_token];
14302678 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;
14432679 const sub_scope = try decl_arena.allocator.create(Scope.LocalVal);
14442680 sub_scope.* = .{
14452681 .parent = params_scope,
14462682 .gen_zir = &gen_scope,
14472683 .name = param_name,
1448 .inst = &arg.base,
2684 // Implicit const list first, then implicit arg list.
2685 .inst = @intToEnum(zir.Inst.Ref, @intCast(u32, zir.Inst.Ref.typed_value_map.len + i)),
2686 .src = decl.tokSrcLoc(name_token),
14492687 };
14502688 params_scope = &sub_scope.base;
2689
2690 // Additionally put the param name into `string_bytes` and reference it with
2691 // `extra` so that we have access to the data in codegen, for debug info.
2692 const str_index = @intCast(u32, astgen.string_bytes.items.len);
2693 astgen.extra.appendAssumeCapacity(str_index);
2694 const used_bytes = astgen.string_bytes.items.len;
2695 try astgen.string_bytes.ensureCapacity(mod.gpa, used_bytes + param_name.len + 1);
2696 astgen.string_bytes.appendSliceAssumeCapacity(param_name);
2697 astgen.string_bytes.appendAssumeCapacity(0);
14512698 }
14522699
1453 _ = try astgen.expr(mod, params_scope, .none, body_node);
2700 _ = try AstGen.expr(&gen_scope, params_scope, .none, body_node);
14542701
14552702 if (gen_scope.instructions.items.len == 0 or
1456 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
2703 !astgen.instructions.items(.tag)[gen_scope.instructions.items.len - 1]
2704 .isNoReturn())
14572705 {
1458 const src = token_starts[tree.lastToken(body_node)];
1459 _ = try astgen.addZIRNoOp(mod, &gen_scope.base, src, .return_void);
2706 // astgen uses result location semantics to coerce return operands.
2707 // Since we are adding the return instruction here, we must handle the coercion.
2708 // We do this by using the `ret_coerce` instruction.
2709 _ = try gen_scope.addUnTok(.ret_coerce, .void_value, tree.lastToken(body_node));
14602710 }
14612711
2712 const code = try gen_scope.finish();
14622713 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1463 zir.dumpZir(mod.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};
2714 code.dump(mod.gpa, "fn_body", &gen_scope.base, param_count) catch {};
14642715 }
14652716
1466 break :blk .{
1467 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1468 };
2717 break :blk code;
14692718 };
14702719
14712720 const is_inline = fn_type.fnCallingConvention() == .Inline;
......@@ -1492,6 +2741,7 @@ fn astgenAndSemaFn(
14922741 if (tvm.typed_value.val.castTag(.function)) |payload| {
14932742 const prev_func = payload.data;
14942743 prev_is_inline = prev_func.state == .inline_only;
2744 prev_func.deinit(mod.gpa);
14952745 }
14962746
14972747 tvm.deinit(mod.gpa);
......@@ -1533,7 +2783,7 @@ fn astgenAndSemaFn(
15332783 .{},
15342784 );
15352785 }
1536 const export_src = token_starts[maybe_export_token];
2786 const export_src = decl.tokSrcLoc(maybe_export_token);
15372787 const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString
15382788 // The scope needs to have the decl in it.
15392789 try mod.analyzeExport(&block_scope.base, export_src, name, decl);
......@@ -1552,8 +2802,8 @@ fn astgenAndSemaVarDecl(
15522802 defer tracy.end();
15532803
15542804 decl.analysis = .in_progress;
2805 decl.is_pub = var_decl.visib_token != null;
15552806
1556 const token_starts = tree.tokens.items(.start);
15572807 const token_tags = tree.tokens.items(.tag);
15582808
15592809 // We need the memory for the Type to go into the arena for the Decl
......@@ -1561,54 +2811,29 @@ fn astgenAndSemaVarDecl(
15612811 errdefer decl_arena.deinit();
15622812 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
15632813
1564 var decl_inst_table = Scope.Block.InstTable.init(mod.gpa);
1565 defer decl_inst_table.deinit();
1566
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);
2814 // Used for simple error reporting.
2815 var decl_scope: Scope.DeclRef = .{ .decl = decl };
15822816
1583 decl.is_pub = var_decl.visib_token != null;
15842817 const is_extern = blk: {
15852818 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;
2819 break :blk token_tags[maybe_extern_token] == .keyword_extern;
15962820 };
2821
15972822 if (var_decl.lib_name) |lib_name| {
15982823 assert(is_extern);
1599 return mod.failTok(&block_scope.base, lib_name, "TODO implement function library name", .{});
2824 return mod.failTok(&decl_scope.base, lib_name, "TODO implement function library name", .{});
16002825 }
16012826 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
16022827 const is_threadlocal = if (var_decl.threadlocal_token) |some| blk: {
16032828 if (!is_mutable) {
1604 return mod.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
2829 return mod.failTok(&decl_scope.base, some, "threadlocal variable cannot be constant", .{});
16052830 }
16062831 break :blk true;
16072832 } else false;
16082833 assert(var_decl.comptime_token == null);
16092834 if (var_decl.ast.align_node != 0) {
16102835 return mod.failNode(
1611 &block_scope.base,
2836 &decl_scope.base,
16122837 var_decl.ast.align_node,
16132838 "TODO implement function align expression",
16142839 .{},
......@@ -1616,7 +2841,7 @@ fn astgenAndSemaVarDecl(
16162841 }
16172842 if (var_decl.ast.section_node != 0) {
16182843 return mod.failNode(
1619 &block_scope.base,
2844 &decl_scope.base,
16202845 var_decl.ast.section_node,
16212846 "TODO implement function section expression",
16222847 .{},
......@@ -1624,103 +2849,136 @@ fn astgenAndSemaVarDecl(
16242849 }
16252850
16262851 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.ast.init_node != 0) vi: {
2852 if (is_extern) {
2853 return mod.failNode(
2854 &decl_scope.base,
2855 var_decl.ast.init_node,
2856 "extern variables have no initializers",
2857 .{},
2858 );
2859 }
2860
16272861 var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
16282862 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,
2863
2864 var astgen = try AstGen.init(mod, decl, &gen_scope_arena.allocator);
2865 defer astgen.deinit();
2866
2867 var gen_scope: Scope.GenZir = .{
16332868 .force_comptime = true,
2869 .parent = &decl.container.base,
2870 .astgen = &astgen,
16342871 };
16352872 defer gen_scope.instructions.deinit(mod.gpa);
16362873
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 };
2874 const init_result_loc: AstGen.ResultLoc = if (var_decl.ast.type_node != 0) .{
2875 .ty = try AstGen.expr(&gen_scope, &gen_scope.base, .{ .ty = .type_type }, var_decl.ast.type_node),
16462876 } else .none;
16472877
1648 const init_inst = try astgen.comptimeExpr(
1649 mod,
2878 const init_inst = try AstGen.comptimeExpr(
2879 &gen_scope,
16502880 &gen_scope.base,
16512881 init_result_loc,
16522882 var_decl.ast.init_node,
16532883 );
2884 _ = try gen_scope.addBreak(.break_inline, 0, init_inst);
2885 var code = try gen_scope.finish();
2886 defer code.deinit(mod.gpa);
16542887 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1655 zir.dumpZir(mod.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};
2888 code.dump(mod.gpa, "var_init", &gen_scope.base, 0) catch {};
16562889 }
16572890
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,
2891 var sema: Sema = .{
2892 .mod = mod,
2893 .gpa = mod.gpa,
2894 .arena = &gen_scope_arena.allocator,
2895 .code = code,
2896 .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
16662897 .owner_decl = decl,
2898 .func = null,
2899 .owner_func = null,
2900 .param_inst_list = &.{},
2901 };
2902 var block_scope: Scope.Block = .{
2903 .parent = null,
2904 .sema = &sema,
16672905 .src_decl = decl,
16682906 .instructions = .{},
1669 .arena = &gen_scope_arena.allocator,
16702907 .inlining = null,
16712908 .is_comptime = true,
1672 .branch_quota = &branch_quota_vi,
16732909 };
1674 defer inner_block.instructions.deinit(mod.gpa);
1675 try zir_sema.analyzeBody(mod, &inner_block, .{
1676 .instructions = gen_scope.instructions.items,
1677 });
2910 defer block_scope.instructions.deinit(mod.gpa);
16782911
2912 const init_inst_zir_ref = try sema.rootAsRef(&block_scope);
16792913 // The result location guarantees the type coercion.
1680 const analyzed_init_inst = var_inst_table.get(init_inst).?;
2914 const analyzed_init_inst = try sema.resolveInst(init_inst_zir_ref);
16812915 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
16822916 const val = analyzed_init_inst.value().?;
16832917
1684 const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
16852918 break :vi .{
1686 .ty = ty,
1687 .val = try val.copy(block_scope.arena),
2919 .ty = try analyzed_init_inst.ty.copy(&decl_arena.allocator),
2920 .val = try val.copy(&decl_arena.allocator),
16882921 };
16892922 } else if (!is_extern) {
16902923 return mod.failTok(
1691 &block_scope.base,
2924 &decl_scope.base,
16922925 var_decl.ast.mut_token,
16932926 "variables must be initialized",
16942927 .{},
16952928 );
16962929 } 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.
16992930 var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
17002931 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,
2932
2933 var astgen = try AstGen.init(mod, decl, &type_scope_arena.allocator);
2934 defer astgen.deinit();
2935
2936 var type_scope: Scope.GenZir = .{
17052937 .force_comptime = true,
2938 .parent = &decl.container.base,
2939 .astgen = &astgen,
17062940 };
17072941 defer type_scope.instructions.deinit(mod.gpa);
17082942
1709 const var_type = try astgen.typeExpr(mod, &type_scope.base, type_node);
2943 const var_type = try AstGen.typeExpr(&type_scope, &type_scope.base, var_decl.ast.type_node);
2944 _ = try type_scope.addBreak(.break_inline, 0, var_type);
2945
2946 var code = try type_scope.finish();
2947 defer code.deinit(mod.gpa);
17102948 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1711 zir.dumpZir(mod.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};
2949 code.dump(mod.gpa, "var_type", &type_scope.base, 0) catch {};
17122950 }
17132951
1714 const ty = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, var_type, .{
1715 .instructions = type_scope.instructions.items,
1716 });
2952 var sema: Sema = .{
2953 .mod = mod,
2954 .gpa = mod.gpa,
2955 .arena = &type_scope_arena.allocator,
2956 .code = code,
2957 .inst_map = try type_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
2958 .owner_decl = decl,
2959 .func = null,
2960 .owner_func = null,
2961 .param_inst_list = &.{},
2962 };
2963 var block_scope: Scope.Block = .{
2964 .parent = null,
2965 .sema = &sema,
2966 .src_decl = decl,
2967 .instructions = .{},
2968 .inlining = null,
2969 .is_comptime = true,
2970 };
2971 defer block_scope.instructions.deinit(mod.gpa);
2972
2973 const ty = try sema.rootAsType(&block_scope);
2974
17172975 break :vi .{
1718 .ty = ty,
2976 .ty = try ty.copy(&decl_arena.allocator),
17192977 .val = null,
17202978 };
17212979 } else {
17222980 return mod.failTok(
1723 &block_scope.base,
2981 &decl_scope.base,
17242982 var_decl.ast.mut_token,
17252983 "unable to infer variable type",
17262984 .{},
......@@ -1729,7 +2987,7 @@ fn astgenAndSemaVarDecl(
17292987
17302988 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
17312989 return mod.failTok(
1732 &block_scope.base,
2990 &decl_scope.base,
17332991 var_decl.ast.mut_token,
17342992 "variable of type '{}' must be const",
17352993 .{var_info.ty},
......@@ -1768,57 +3026,57 @@ fn astgenAndSemaVarDecl(
17683026
17693027 if (var_decl.extern_export_token) |maybe_export_token| {
17703028 if (token_tags[maybe_export_token] == .keyword_export) {
1771 const export_src = token_starts[maybe_export_token];
3029 const export_src = decl.tokSrcLoc(maybe_export_token);
17723030 const name_token = var_decl.ast.mut_token + 1;
17733031 const name = tree.tokenSlice(name_token); // TODO identifierTokenString
17743032 // The scope needs to have the decl in it.
1775 try mod.analyzeExport(&block_scope.base, export_src, name, decl);
3033 try mod.analyzeExport(&decl_scope.base, export_src, name, decl);
17763034 }
17773035 }
17783036 return type_changed;
17793037}
17803038
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);
3039pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !void {
3040 try depender.dependencies.ensureCapacity(mod.gpa, depender.dependencies.items().len + 1);
3041 try dependee.dependants.ensureCapacity(mod.gpa, dependee.dependants.items().len + 1);
17843042
17853043 depender.dependencies.putAssumeCapacity(dependee, {});
17863044 dependee.dependants.putAssumeCapacity(depender, {});
17873045}
17883046
1789pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree {
3047pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree {
17903048 const tracy = trace(@src());
17913049 defer tracy.end();
17923050
17933051 switch (root_scope.status) {
17943052 .never_loaded, .unloaded_success => {
1795 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
3053 try mod.failed_files.ensureCapacity(mod.gpa, mod.failed_files.items().len + 1);
17963054
1797 const source = try root_scope.getSource(self);
3055 const source = try root_scope.getSource(mod);
17983056
17993057 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);
3058 root_scope.tree = try std.zig.parse(mod.gpa, source);
3059 defer if (!keep_tree) root_scope.tree.deinit(mod.gpa);
18023060
18033061 const tree = &root_scope.tree;
18043062
18053063 if (tree.errors.len != 0) {
18063064 const parse_err = tree.errors[0];
18073065
1808 var msg = std.ArrayList(u8).init(self.gpa);
3066 var msg = std.ArrayList(u8).init(mod.gpa);
18093067 defer msg.deinit();
18103068
18113069 try tree.renderError(parse_err, msg.writer());
1812 const err_msg = try self.gpa.create(ErrorMsg);
3070 const err_msg = try mod.gpa.create(ErrorMsg);
18133071 err_msg.* = .{
18143072 .src_loc = .{
1815 .file_scope = root_scope,
1816 .byte_offset = tree.tokens.items(.start)[parse_err.token],
3073 .container = .{ .file_scope = root_scope },
3074 .lazy = .{ .token_abs = parse_err.token },
18173075 },
18183076 .msg = msg.toOwnedSlice(),
18193077 };
18203078
1821 self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
3079 mod.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
18223080 root_scope.status = .unloaded_parse_failure;
18233081 return error.AnalysisFail;
18243082 }
......@@ -2051,11 +3309,9 @@ fn semaContainerFn(
20513309 const tracy = trace(@src());
20523310 defer tracy.end();
20533311
2054 const token_starts = tree.tokens.items(.start);
2055 const token_tags = tree.tokens.items(.tag);
2056
20573312 // We will create a Decl for it regardless of analysis status.
20583313 const name_tok = fn_proto.name_token orelse {
3314 // This problem will go away with #1717.
20593315 @panic("TODO missing function name");
20603316 };
20613317 const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString
......@@ -2068,8 +3324,8 @@ fn semaContainerFn(
20683324 if (deleted_decls.swapRemove(decl) == null) {
20693325 decl.analysis = .sema_failure;
20703326 const msg = try ErrorMsg.create(mod.gpa, .{
2071 .file_scope = container_scope.file_scope,
2072 .byte_offset = token_starts[name_tok],
3327 .container = .{ .file_scope = container_scope.file_scope },
3328 .lazy = .{ .token_abs = name_tok },
20733329 }, "redefinition of '{s}'", .{decl.name});
20743330 errdefer msg.destroy(mod.gpa);
20753331 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
......@@ -2098,6 +3354,7 @@ fn semaContainerFn(
20983354 const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
20993355 container_scope.decls.putAssumeCapacity(new_decl, {});
21003356 if (fn_proto.extern_export_token) |maybe_export_token| {
3357 const token_tags = tree.tokens.items(.tag);
21013358 if (token_tags[maybe_export_token] == .keyword_export) {
21023359 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
21033360 }
......@@ -2117,11 +3374,7 @@ fn semaContainerVar(
21173374 const tracy = trace(@src());
21183375 defer tracy.end();
21193376
2120 const token_starts = tree.tokens.items(.start);
2121 const token_tags = tree.tokens.items(.tag);
2122
21233377 const name_token = var_decl.ast.mut_token + 1;
2124 const name_src = token_starts[name_token];
21253378 const name = tree.tokenSlice(name_token); // TODO identifierTokenString
21263379 const name_hash = container_scope.fullyQualifiedNameHash(name);
21273380 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
......@@ -2132,8 +3385,8 @@ fn semaContainerVar(
21323385 if (deleted_decls.swapRemove(decl) == null) {
21333386 decl.analysis = .sema_failure;
21343387 const err_msg = try ErrorMsg.create(mod.gpa, .{
2135 .file_scope = container_scope.file_scope,
2136 .byte_offset = name_src,
3388 .container = .{ .file_scope = container_scope.file_scope },
3389 .lazy = .{ .token_abs = name_token },
21373390 }, "redefinition of '{s}'", .{decl.name});
21383391 errdefer err_msg.destroy(mod.gpa);
21393392 try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg);
......@@ -2145,6 +3398,7 @@ fn semaContainerVar(
21453398 const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
21463399 container_scope.decls.putAssumeCapacity(new_decl, {});
21473400 if (var_decl.extern_export_token) |maybe_export_token| {
3401 const token_tags = tree.tokens.items(.tag);
21483402 if (token_tags[maybe_export_token] == .keyword_export) {
21493403 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
21503404 }
......@@ -2167,11 +3421,11 @@ fn semaContainerField(
21673421 log.err("TODO: analyze container field", .{});
21683422}
21693423
2170pub fn deleteDecl(self: *Module, decl: *Decl) !void {
3424pub fn deleteDecl(mod: *Module, decl: *Decl) !void {
21713425 const tracy = trace(@src());
21723426 defer tracy.end();
21733427
2174 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
3428 try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.items.len + decl.dependencies.items().len);
21753429
21763430 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
21773431 // not be present in the set, and this does nothing.
......@@ -2179,7 +3433,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {
21793433
21803434 log.debug("deleting decl '{s}'", .{decl.name});
21813435 const name_hash = decl.fullyQualifiedNameHash();
2182 self.decl_table.removeAssertDiscard(name_hash);
3436 mod.decl_table.removeAssertDiscard(name_hash);
21833437 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
21843438 for (decl.dependencies.items()) |entry| {
21853439 const dep = entry.key;
......@@ -2188,7 +3442,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {
21883442 // We don't recursively perform a deletion here, because during the update,
21893443 // another reference to it may turn up.
21903444 dep.deletion_flag = true;
2191 self.deletion_set.appendAssumeCapacity(dep);
3445 mod.deletion_set.appendAssumeCapacity(dep);
21923446 }
21933447 }
21943448 // Anything that depends on this deleted decl certainly needs to be re-analyzed.
......@@ -2197,29 +3451,29 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {
21973451 dep.removeDependency(decl);
21983452 if (dep.analysis != .outdated) {
21993453 // TODO Move this failure possibility to the top of the function.
2200 try self.markOutdatedDecl(dep);
3454 try mod.markOutdatedDecl(dep);
22013455 }
22023456 }
2203 if (self.failed_decls.swapRemove(decl)) |entry| {
2204 entry.value.destroy(self.gpa);
3457 if (mod.failed_decls.swapRemove(decl)) |entry| {
3458 entry.value.destroy(mod.gpa);
22053459 }
2206 if (self.emit_h_failed_decls.swapRemove(decl)) |entry| {
2207 entry.value.destroy(self.gpa);
3460 if (mod.emit_h_failed_decls.swapRemove(decl)) |entry| {
3461 entry.value.destroy(mod.gpa);
22083462 }
2209 _ = self.compile_log_decls.swapRemove(decl);
2210 self.deleteDeclExports(decl);
2211 self.comp.bin_file.freeDecl(decl);
3463 _ = mod.compile_log_decls.swapRemove(decl);
3464 mod.deleteDeclExports(decl);
3465 mod.comp.bin_file.freeDecl(decl);
22123466
2213 decl.destroy(self);
3467 decl.destroy(mod);
22143468}
22153469
22163470/// Delete all the Export objects that are caused by this Decl. Re-analysis of
22173471/// 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;
3472fn deleteDeclExports(mod: *Module, decl: *Decl) void {
3473 const kv = mod.export_owners.swapRemove(decl) orelse return;
22203474
22213475 for (kv.value) |exp| {
2222 if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
3476 if (mod.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
22233477 // Remove exports with owner_decl matching the regenerating decl.
22243478 const list = decl_exports_kv.value;
22253479 var i: usize = 0;
......@@ -2232,73 +3486,101 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
22323486 i += 1;
22333487 }
22343488 }
2235 decl_exports_kv.value = self.gpa.shrink(list, new_len);
3489 decl_exports_kv.value = mod.gpa.shrink(list, new_len);
22363490 if (new_len == 0) {
2237 self.decl_exports.removeAssertDiscard(exp.exported_decl);
3491 mod.decl_exports.removeAssertDiscard(exp.exported_decl);
22383492 }
22393493 }
2240 if (self.comp.bin_file.cast(link.File.Elf)) |elf| {
3494 if (mod.comp.bin_file.cast(link.File.Elf)) |elf| {
22413495 elf.deleteExport(exp.link.elf);
22423496 }
2243 if (self.comp.bin_file.cast(link.File.MachO)) |macho| {
3497 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {
22443498 macho.deleteExport(exp.link.macho);
22453499 }
2246 if (self.failed_exports.swapRemove(exp)) |entry| {
2247 entry.value.destroy(self.gpa);
3500 if (mod.failed_exports.swapRemove(exp)) |entry| {
3501 entry.value.destroy(mod.gpa);
22483502 }
2249 _ = self.symbol_exports.swapRemove(exp.options.name);
2250 self.gpa.free(exp.options.name);
2251 self.gpa.destroy(exp);
3503 _ = mod.symbol_exports.swapRemove(exp.options.name);
3504 mod.gpa.free(exp.options.name);
3505 mod.gpa.destroy(exp);
22523506 }
2253 self.gpa.free(kv.value);
3507 mod.gpa.free(kv.value);
22543508}
22553509
2256pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
3510pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
22573511 const tracy = trace(@src());
22583512 defer tracy.end();
22593513
22603514 // Use the Decl's arena for function memory.
2261 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
3515 var arena = decl.typed_value.most_recent.arena.?.promote(mod.gpa);
22623516 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;
3517
3518 const fn_ty = decl.typed_value.most_recent.typed_value.ty;
3519 const param_inst_list = try mod.gpa.alloc(*ir.Inst, fn_ty.fnParamLen());
3520 defer mod.gpa.free(param_inst_list);
3521
3522 for (param_inst_list) |*param_inst, param_index| {
3523 const param_type = fn_ty.fnParamType(param_index);
3524 const name = func.zir.nullTerminatedString(func.zir.extra[param_index]);
3525 const arg_inst = try arena.allocator.create(ir.Inst.Arg);
3526 arg_inst.* = .{
3527 .base = .{
3528 .tag = .arg,
3529 .ty = param_type,
3530 .src = .unneeded,
3531 },
3532 .name = name,
3533 };
3534 param_inst.* = &arg_inst.base;
3535 }
3536
3537 var sema: Sema = .{
3538 .mod = mod,
3539 .gpa = mod.gpa,
3540 .arena = &arena.allocator,
3541 .code = func.zir,
3542 .inst_map = try mod.gpa.alloc(*ir.Inst, func.zir.instructions.len),
3543 .owner_decl = decl,
3544 .func = func,
3545 .owner_func = func,
3546 .param_inst_list = param_inst_list,
3547 };
3548 defer mod.gpa.free(sema.inst_map);
22663549
22673550 var inner_block: Scope.Block = .{
22683551 .parent = null,
2269 .inst_table = &inst_table,
2270 .func = func,
2271 .owner_decl = decl,
3552 .sema = &sema,
22723553 .src_decl = decl,
22733554 .instructions = .{},
2274 .arena = &arena.allocator,
22753555 .inlining = null,
22763556 .is_comptime = false,
2277 .branch_quota = &branch_quota,
22783557 };
2279 defer inner_block.instructions.deinit(self.gpa);
3558 defer inner_block.instructions.deinit(mod.gpa);
3559
3560 // TZIR currently requires the arg parameters to be the first N instructions
3561 try inner_block.instructions.appendSlice(mod.gpa, param_inst_list);
22803562
22813563 func.state = .in_progress;
22823564 log.debug("set {s} to in_progress", .{decl.name});
22833565
2284 try zir_sema.analyzeBody(self, &inner_block, func.zir);
3566 _ = try sema.root(&inner_block);
22853567
2286 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
3568 const instructions = try arena.allocator.dupe(*ir.Inst, inner_block.instructions.items);
22873569 func.state = .success;
22883570 func.body = .{ .instructions = instructions };
22893571 log.debug("set {s} to success", .{decl.name});
22903572}
22913573
2292fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
3574fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
22933575 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);
3576 try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl });
3577 if (mod.failed_decls.swapRemove(decl)) |entry| {
3578 entry.value.destroy(mod.gpa);
22973579 }
2298 if (self.emit_h_failed_decls.swapRemove(decl)) |entry| {
2299 entry.value.destroy(self.gpa);
3580 if (mod.emit_h_failed_decls.swapRemove(decl)) |entry| {
3581 entry.value.destroy(mod.gpa);
23003582 }
2301 _ = self.compile_log_decls.swapRemove(decl);
3583 _ = mod.compile_log_decls.swapRemove(decl);
23023584 decl.analysis = .outdated;
23033585}
23043586
......@@ -2349,65 +3631,39 @@ fn allocateNewDecl(
23493631}
23503632
23513633fn createNewDecl(
2352 self: *Module,
3634 mod: *Module,
23533635 scope: *Scope,
23543636 decl_name: []const u8,
23553637 src_index: usize,
23563638 name_hash: Scope.NameHash,
23573639 contents_hash: std.zig.SrcHash,
23583640) !*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);
3641 try mod.decl_table.ensureCapacity(mod.gpa, mod.decl_table.items().len + 1);
3642 const new_decl = try mod.allocateNewDecl(scope, src_index, contents_hash);
3643 errdefer mod.gpa.destroy(new_decl);
3644 new_decl.name = try mem.dupeZ(mod.gpa, u8, decl_name);
3645 mod.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
23643646 return new_decl;
23653647}
23663648
23673649/// 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);
3650pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged(ErrorInt).Entry {
3651 const gop = try mod.global_error_set.getOrPut(mod.gpa, name);
23703652 if (gop.found_existing)
23713653 return gop.entry.*;
2372 errdefer self.global_error_set.removeAssertDiscard(name);
23733654
2374 gop.entry.key = try self.gpa.dupe(u8, name);
2375 gop.entry.value = @intCast(u16, self.global_error_set.count() - 1);
3655 errdefer mod.global_error_set.removeAssertDiscard(name);
3656 try mod.error_name_list.ensureCapacity(mod.gpa, mod.error_name_list.items.len + 1);
3657 gop.entry.key = try mod.gpa.dupe(u8, name);
3658 gop.entry.value = @intCast(ErrorInt, mod.error_name_list.items.len);
3659 mod.error_name_list.appendAssumeCapacity(gop.entry.key);
23763660 return gop.entry.*;
23773661}
23783662
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
24073663pub fn analyzeExport(
24083664 mod: *Module,
24093665 scope: *Scope,
2410 src: usize,
3666 src: LazySrcLoc,
24113667 borrowed_symbol_name: []const u8,
24123668 exported_decl: *Decl,
24133669) !void {
......@@ -2496,178 +3752,11 @@ pub fn analyzeExport(
24963752 },
24973753 };
24983754}
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);
3755pub fn constInst(mod: *Module, arena: *Allocator, src: LazySrcLoc, typed_value: TypedValue) !*ir.Inst {
3756 const const_inst = try arena.create(ir.Inst.Constant);
26683757 const_inst.* = .{
26693758 .base = .{
2670 .tag = Inst.Constant.base_tag,
3759 .tag = ir.Inst.Constant.base_tag,
26713760 .ty = typed_value.ty,
26723761 .src = src,
26733762 },
......@@ -2676,94 +3765,94 @@ pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedVal
26763765 return &const_inst.base;
26773766}
26783767
2679pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2680 return self.constInst(scope, src, .{
3768pub fn constType(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type) !*ir.Inst {
3769 return mod.constInst(arena, src, .{
26813770 .ty = Type.initTag(.type),
2682 .val = try ty.toValue(scope.arena()),
3771 .val = try ty.toValue(arena),
26833772 });
26843773}
26853774
2686pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
2687 return self.constInst(scope, src, .{
3775pub fn constVoid(mod: *Module, arena: *Allocator, src: LazySrcLoc) !*ir.Inst {
3776 return mod.constInst(arena, src, .{
26883777 .ty = Type.initTag(.void),
26893778 .val = Value.initTag(.void_value),
26903779 });
26913780}
26923781
2693pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
2694 return self.constInst(scope, src, .{
3782pub fn constNoReturn(mod: *Module, arena: *Allocator, src: LazySrcLoc) !*ir.Inst {
3783 return mod.constInst(arena, src, .{
26953784 .ty = Type.initTag(.noreturn),
26963785 .val = Value.initTag(.unreachable_value),
26973786 });
26983787}
26993788
2700pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2701 return self.constInst(scope, src, .{
3789pub fn constUndef(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type) !*ir.Inst {
3790 return mod.constInst(arena, src, .{
27023791 .ty = ty,
27033792 .val = Value.initTag(.undef),
27043793 });
27053794}
27063795
2707pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
2708 return self.constInst(scope, src, .{
3796pub fn constBool(mod: *Module, arena: *Allocator, src: LazySrcLoc, v: bool) !*ir.Inst {
3797 return mod.constInst(arena, src, .{
27093798 .ty = Type.initTag(.bool),
27103799 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
27113800 });
27123801}
27133802
2714pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
2715 return self.constInst(scope, src, .{
3803pub fn constIntUnsigned(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, int: u64) !*ir.Inst {
3804 return mod.constInst(arena, src, .{
27163805 .ty = ty,
2717 .val = try Value.Tag.int_u64.create(scope.arena(), int),
3806 .val = try Value.Tag.int_u64.create(arena, int),
27183807 });
27193808}
27203809
2721pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
2722 return self.constInst(scope, src, .{
3810pub fn constIntSigned(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, int: i64) !*ir.Inst {
3811 return mod.constInst(arena, src, .{
27233812 .ty = ty,
2724 .val = try Value.Tag.int_i64.create(scope.arena(), int),
3813 .val = try Value.Tag.int_i64.create(arena, int),
27253814 });
27263815}
27273816
2728pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
3817pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, big_int: BigIntConst) !*ir.Inst {
27293818 if (big_int.positive) {
27303819 if (big_int.to(u64)) |x| {
2731 return self.constIntUnsigned(scope, src, ty, x);
3820 return mod.constIntUnsigned(arena, src, ty, x);
27323821 } else |err| switch (err) {
27333822 error.NegativeIntoUnsigned => unreachable,
27343823 error.TargetTooSmall => {}, // handled below
27353824 }
2736 return self.constInst(scope, src, .{
3825 return mod.constInst(arena, src, .{
27373826 .ty = ty,
2738 .val = try Value.Tag.int_big_positive.create(scope.arena(), big_int.limbs),
3827 .val = try Value.Tag.int_big_positive.create(arena, big_int.limbs),
27393828 });
27403829 } else {
27413830 if (big_int.to(i64)) |x| {
2742 return self.constIntSigned(scope, src, ty, x);
3831 return mod.constIntSigned(arena, src, ty, x);
27433832 } else |err| switch (err) {
27443833 error.NegativeIntoUnsigned => unreachable,
27453834 error.TargetTooSmall => {}, // handled below
27463835 }
2747 return self.constInst(scope, src, .{
3836 return mod.constInst(arena, src, .{
27483837 .ty = ty,
2749 .val = try Value.Tag.int_big_negative.create(scope.arena(), big_int.limbs),
3838 .val = try Value.Tag.int_big_negative.create(arena, big_int.limbs),
27503839 });
27513840 }
27523841}
27533842
27543843pub fn createAnonymousDecl(
2755 self: *Module,
3844 mod: *Module,
27563845 scope: *Scope,
27573846 decl_arena: *std.heap.ArenaAllocator,
27583847 typed_value: TypedValue,
27593848) !*Decl {
2760 const name_index = self.getNextAnonNameIndex();
3849 const name_index = mod.getNextAnonNameIndex();
27613850 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);
3851 const name = try std.fmt.allocPrint(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index });
3852 defer mod.gpa.free(name);
27643853 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
27653854 const src_hash: std.zig.SrcHash = undefined;
2766 const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
3855 const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
27673856 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
27683857
27693858 decl_arena_state.* = decl_arena.state;
......@@ -2774,32 +3863,32 @@ pub fn createAnonymousDecl(
27743863 },
27753864 };
27763865 new_decl.analysis = .complete;
2777 new_decl.generation = self.generation;
3866 new_decl.generation = mod.generation;
27783867
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.
3868 // TODO: This generates the Decl into the machine code file if it is of a
3869 // type that is non-zero size. We should be able to further improve the
3870 // compiler to omit Decls which are only referenced at compile-time and not runtime.
27823871 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 });
3872 try mod.comp.bin_file.allocateDeclIndexes(new_decl);
3873 try mod.comp.work_queue.writeItem(.{ .codegen_decl = new_decl });
27853874 }
27863875
27873876 return new_decl;
27883877}
27893878
27903879pub fn createContainerDecl(
2791 self: *Module,
3880 mod: *Module,
27923881 scope: *Scope,
27933882 base_token: std.zig.ast.TokenIndex,
27943883 decl_arena: *std.heap.ArenaAllocator,
27953884 typed_value: TypedValue,
27963885) !*Decl {
27973886 const scope_decl = scope.ownerDecl().?;
2798 const name = try self.getAnonTypeName(scope, base_token);
2799 defer self.gpa.free(name);
3887 const name = try mod.getAnonTypeName(scope, base_token);
3888 defer mod.gpa.free(name);
28003889 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
28013890 const src_hash: std.zig.SrcHash = undefined;
2802 const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
3891 const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
28033892 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
28043893
28053894 decl_arena_state.* = decl_arena.state;
......@@ -2810,12 +3899,12 @@ pub fn createContainerDecl(
28103899 },
28113900 };
28123901 new_decl.analysis = .complete;
2813 new_decl.generation = self.generation;
3902 new_decl.generation = mod.generation;
28143903
28153904 return new_decl;
28163905}
28173906
2818fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
3907fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
28193908 // TODO add namespaces, generic function signatrues
28203909 const tree = scope.tree();
28213910 const token_tags = tree.tokens.items(.tag);
......@@ -2827,775 +3916,39 @@ fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIn
28273916 else => unreachable,
28283917 };
28293918 const loc = tree.tokenLocation(0, base_token);
2830 return std.fmt.allocPrint(self.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column });
3919 return std.fmt.allocPrint(mod.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column });
28313920}
28323921
2833fn getNextAnonNameIndex(self: *Module) usize {
2834 return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
3922fn getNextAnonNameIndex(mod: *Module) usize {
3923 return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic);
28353924}
28363925
2837pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
3926pub fn lookupDeclName(mod: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
28383927 const namespace = scope.namespace();
28393928 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);
3929 return mod.decl_table.get(name_hash);
28463930}
28473931
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);
3932pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type {
3933 const int_payload = try arena.create(Type.Payload.Bits);
33223934 int_payload.* = .{
33233935 .base = .{
3324 .tag = if (signed) .int_signed else .int_unsigned,
3936 .tag = switch (signedness) {
3937 .signed => .int_signed,
3938 .unsigned => .int_unsigned,
3939 },
33253940 },
33263941 .data = bits,
33273942 };
33283943 return Type.initPayload(&int_payload.base);
33293944}
33303945
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
35933946/// We don't return a pointer to the new error note because the pointer
35943947/// becomes invalid when you add another one.
35953948pub fn errNote(
35963949 mod: *Module,
35973950 scope: *Scope,
3598 src: usize,
3951 src: LazySrcLoc,
35993952 parent: *ErrorMsg,
36003953 comptime format: []const u8,
36013954 args: anytype,
......@@ -3605,10 +3958,7 @@ pub fn errNote(
36053958
36063959 parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1);
36073960 parent.notes[parent.notes.len - 1] = .{
3608 .src_loc = .{
3609 .file_scope = scope.getFileScope(),
3610 .byte_offset = src,
3611 },
3961 .src_loc = src.toSrcLoc(scope),
36123962 .msg = msg,
36133963 };
36143964}
......@@ -3616,121 +3966,112 @@ pub fn errNote(
36163966pub fn errMsg(
36173967 mod: *Module,
36183968 scope: *Scope,
3619 src_byte_offset: usize,
3969 src: LazySrcLoc,
36203970 comptime format: []const u8,
36213971 args: anytype,
36223972) error{OutOfMemory}!*ErrorMsg {
3623 return ErrorMsg.create(mod.gpa, .{
3624 .file_scope = scope.getFileScope(),
3625 .byte_offset = src_byte_offset,
3626 }, format, args);
3973 return ErrorMsg.create(mod.gpa, src.toSrcLoc(scope), format, args);
36273974}
36283975
36293976pub fn fail(
36303977 mod: *Module,
36313978 scope: *Scope,
3632 src_byte_offset: usize,
3979 src: LazySrcLoc,
36333980 comptime format: []const u8,
36343981 args: anytype,
36353982) InnerError {
3636 const err_msg = try mod.errMsg(scope, src_byte_offset, format, args);
3983 const err_msg = try mod.errMsg(scope, src, format, args);
36373984 return mod.failWithOwnedErrorMsg(scope, err_msg);
36383985}
36393986
3987/// Same as `fail`, except given an absolute byte offset, and the function sets up the `LazySrcLoc`
3988/// for pointing at it relatively by subtracting from the containing `Decl`.
3989pub fn failOff(
3990 mod: *Module,
3991 scope: *Scope,
3992 byte_offset: u32,
3993 comptime format: []const u8,
3994 args: anytype,
3995) InnerError {
3996 const decl_byte_offset = scope.srcDecl().?.srcByteOffset();
3997 const src: LazySrcLoc = .{ .byte_offset = byte_offset - decl_byte_offset };
3998 return mod.fail(scope, src, format, args);
3999}
4000
4001/// Same as `fail`, except given a token index, and the function sets up the `LazySrcLoc`
4002/// for pointing at it relatively by subtracting from the containing `Decl`.
36404003pub fn failTok(
3641 self: *Module,
4004 mod: *Module,
36424005 scope: *Scope,
36434006 token_index: ast.TokenIndex,
36444007 comptime format: []const u8,
36454008 args: anytype,
36464009) InnerError {
3647 const src = scope.tree().tokens.items(.start)[token_index];
3648 return self.fail(scope, src, format, args);
4010 const src = scope.srcDecl().?.tokSrcLoc(token_index);
4011 return mod.fail(scope, src, format, args);
36494012}
36504013
4014/// Same as `fail`, except given an AST node index, and the function sets up the `LazySrcLoc`
4015/// for pointing at it relatively by subtracting from the containing `Decl`.
36514016pub fn failNode(
3652 self: *Module,
4017 mod: *Module,
36534018 scope: *Scope,
3654 ast_node: ast.Node.Index,
4019 node_index: ast.Node.Index,
36554020 comptime format: []const u8,
36564021 args: anytype,
36574022) 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);
4023 const src = scope.srcDecl().?.nodeSrcLoc(node_index);
4024 return mod.fail(scope, src, format, args);
36614025}
36624026
3663pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError {
4027pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError {
36644028 @setCold(true);
36654029 {
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);
4030 errdefer err_msg.destroy(mod.gpa);
4031 try mod.failed_decls.ensureCapacity(mod.gpa, mod.failed_decls.items().len + 1);
4032 try mod.failed_files.ensureCapacity(mod.gpa, mod.failed_files.items().len + 1);
36694033 }
36704034 switch (scope.tag) {
36714035 .block => {
36724036 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 }
4037 if (block.sema.owner_func) |func| {
4038 func.state = .sema_failure;
36804039 } 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 }
4040 block.sema.owner_decl.analysis = .sema_failure;
4041 block.sema.owner_decl.generation = mod.generation;
36874042 }
3688 self.failed_decls.putAssumeCapacityNoClobber(block.owner_decl, err_msg);
4043 mod.failed_decls.putAssumeCapacityNoClobber(block.sema.owner_decl, err_msg);
36894044 },
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);
4045 .gen_zir => {
4046 const gen_zir = scope.cast(Scope.GenZir).?;
4047 gen_zir.astgen.decl.analysis = .sema_failure;
4048 gen_zir.astgen.decl.generation = mod.generation;
4049 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg);
36954050 },
36964051 .local_val => {
36974052 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);
4053 gen_zir.astgen.decl.analysis = .sema_failure;
4054 gen_zir.astgen.decl.generation = mod.generation;
4055 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg);
37014056 },
37024057 .local_ptr => {
37034058 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);
4059 gen_zir.astgen.decl.analysis = .sema_failure;
4060 gen_zir.astgen.decl.generation = mod.generation;
4061 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg);
37134062 },
37144063 .file => unreachable,
37154064 .container => unreachable,
4065 .decl_ref => {
4066 const decl_ref = scope.cast(Scope.DeclRef).?;
4067 decl_ref.decl.analysis = .sema_failure;
4068 decl_ref.decl.generation = mod.generation;
4069 mod.failed_decls.putAssumeCapacityNoClobber(decl_ref.decl, err_msg);
4070 },
37164071 }
37174072 return error.AnalysisFail;
37184073}
37194074
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
37344075fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
37354076 return @bitCast(u128, a) == @bitCast(u128, b);
37364077}
......@@ -3780,14 +4121,12 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
37804121}
37814122
37824123pub fn floatAdd(
3783 self: *Module,
3784 scope: *Scope,
4124 arena: *Allocator,
37854125 float_type: Type,
3786 src: usize,
4126 src: LazySrcLoc,
37874127 lhs: Value,
37884128 rhs: Value,
37894129) !Value {
3790 const arena = scope.arena();
37914130 switch (float_type.tag()) {
37924131 .f16 => {
37934132 @panic("TODO add __trunctfhf2 to compiler-rt");
......@@ -3815,14 +4154,12 @@ pub fn floatAdd(
38154154}
38164155
38174156pub fn floatSub(
3818 self: *Module,
3819 scope: *Scope,
4157 arena: *Allocator,
38204158 float_type: Type,
3821 src: usize,
4159 src: LazySrcLoc,
38224160 lhs: Value,
38234161 rhs: Value,
38244162) !Value {
3825 const arena = scope.arena();
38264163 switch (float_type.tag()) {
38274164 .f16 => {
38284165 @panic("TODO add __trunctfhf2 to compiler-rt");
......@@ -3850,9 +4187,8 @@ pub fn floatSub(
38504187}
38514188
38524189pub fn simplePtrType(
3853 self: *Module,
3854 scope: *Scope,
3855 src: usize,
4190 mod: *Module,
4191 arena: *Allocator,
38564192 elem_ty: Type,
38574193 mutable: bool,
38584194 size: std.builtin.TypeInfo.Pointer.Size,
......@@ -3863,7 +4199,7 @@ pub fn simplePtrType(
38634199 // TODO stage1 type inference bug
38644200 const T = Type.Tag;
38654201
3866 const type_payload = try scope.arena().create(Type.Payload.ElemType);
4202 const type_payload = try arena.create(Type.Payload.ElemType);
38674203 type_payload.* = .{
38684204 .base = .{
38694205 .tag = switch (size) {
......@@ -3879,9 +4215,8 @@ pub fn simplePtrType(
38794215}
38804216
38814217pub fn ptrType(
3882 self: *Module,
3883 scope: *Scope,
3884 src: usize,
4218 mod: *Module,
4219 arena: *Allocator,
38854220 elem_ty: Type,
38864221 sentinel: ?Value,
38874222 @"align": u32,
......@@ -3895,7 +4230,7 @@ pub fn ptrType(
38954230 assert(host_size == 0 or bit_offset < host_size * 8);
38964231
38974232 // TODO check if type can be represented by simplePtrType
3898 return Type.Tag.pointer.create(scope.arena(), .{
4233 return Type.Tag.pointer.create(arena, .{
38994234 .pointee_type = elem_ty,
39004235 .sentinel = sentinel,
39014236 .@"align" = @"align",
......@@ -3908,23 +4243,23 @@ pub fn ptrType(
39084243 });
39094244}
39104245
3911pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type {
4246pub fn optionalType(mod: *Module, arena: *Allocator, child_type: Type) Allocator.Error!Type {
39124247 switch (child_type.tag()) {
39134248 .single_const_pointer => return Type.Tag.optional_single_const_pointer.create(
3914 scope.arena(),
4249 arena,
39154250 child_type.elemType(),
39164251 ),
39174252 .single_mut_pointer => return Type.Tag.optional_single_mut_pointer.create(
3918 scope.arena(),
4253 arena,
39194254 child_type.elemType(),
39204255 ),
3921 else => return Type.Tag.optional.create(scope.arena(), child_type),
4256 else => return Type.Tag.optional.create(arena, child_type),
39224257 }
39234258}
39244259
39254260pub fn arrayType(
3926 self: *Module,
3927 scope: *Scope,
4261 mod: *Module,
4262 arena: *Allocator,
39284263 len: u64,
39294264 sentinel: ?Value,
39304265 elem_type: Type,
......@@ -3932,30 +4267,30 @@ pub fn arrayType(
39324267 if (elem_type.eql(Type.initTag(.u8))) {
39334268 if (sentinel) |some| {
39344269 if (some.eql(Value.initTag(.zero))) {
3935 return Type.Tag.array_u8_sentinel_0.create(scope.arena(), len);
4270 return Type.Tag.array_u8_sentinel_0.create(arena, len);
39364271 }
39374272 } else {
3938 return Type.Tag.array_u8.create(scope.arena(), len);
4273 return Type.Tag.array_u8.create(arena, len);
39394274 }
39404275 }
39414276
39424277 if (sentinel) |some| {
3943 return Type.Tag.array_sentinel.create(scope.arena(), .{
4278 return Type.Tag.array_sentinel.create(arena, .{
39444279 .len = len,
39454280 .sentinel = some,
39464281 .elem_type = elem_type,
39474282 });
39484283 }
39494284
3950 return Type.Tag.array.create(scope.arena(), .{
4285 return Type.Tag.array.create(arena, .{
39514286 .len = len,
39524287 .elem_type = elem_type,
39534288 });
39544289}
39554290
39564291pub fn errorUnionType(
3957 self: *Module,
3958 scope: *Scope,
4292 mod: *Module,
4293 arena: *Allocator,
39594294 error_set: Type,
39604295 payload: Type,
39614296) Allocator.Error!Type {
......@@ -3964,19 +4299,15 @@ pub fn errorUnionType(
39644299 return Type.initTag(.anyerror_void_error_union);
39654300 }
39664301
3967 return Type.Tag.error_union.create(scope.arena(), .{
4302 return Type.Tag.error_union.create(arena, .{
39684303 .error_set = error_set,
39694304 .payload = payload,
39704305 });
39714306}
39724307
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 {
4308pub fn dumpInst(mod: *Module, scope: *Scope, inst: *ir.Inst) void {
39784309 const zir_module = scope.namespace();
3979 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
4310 const source = zir_module.getSource(mod) catch @panic("dumpInst failed to get source");
39804311 const loc = std.zig.findLineColumn(source, inst.src);
39814312 if (inst.tag == .constant) {
39824313 std.debug.print("constant ty={} val={} src={s}:{d}:{d}\n", .{
......@@ -4006,267 +4337,117 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
40064337 }
40074338}
40084339
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);
4340pub fn getTarget(mod: Module) Target {
4341 return mod.comp.bin_file.options.target;
40814342}
40824343
4083pub fn getTarget(self: Module) Target {
4084 return self.comp.bin_file.options.target;
4085}
4086
4087pub fn optimizeMode(self: Module) std.builtin.Mode {
4088 return self.comp.bin_file.options.optimize_mode;
4089}
4090
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 }
4344pub fn optimizeMode(mod: Module) std.builtin.Mode {
4345 return mod.comp.bin_file.options.optimize_mode;
40954346}
40964347
4097/// Identifier token -> String (allocated in scope.arena())
4348/// Given an identifier token, obtain the string for it.
4349/// If the token uses @"" syntax, parses as a string, reports errors if applicable,
4350/// and allocates the result within `scope.arena()`.
4351/// Otherwise, returns a reference to the source code bytes directly.
4352/// See also `appendIdentStr` and `parseStrLit`.
40984353pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
40994354 const tree = scope.tree();
41004355 const token_tags = tree.tokens.items(.tag);
4101 const token_starts = tree.tokens.items(.start);
41024356 assert(token_tags[token] == .identifier);
4103
41044357 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 };
4358 if (!mem.startsWith(u8, ident_name, "@")) {
4359 return ident_name;
41164360 }
4117 return ident_name;
4361 var buf: ArrayListUnmanaged(u8) = .{};
4362 defer buf.deinit(mod.gpa);
4363 try parseStrLit(mod, scope, token, &buf, ident_name, 1);
4364 return buf.toOwnedSlice(mod.gpa);
41184365}
41194366
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 });
4367/// Given an identifier token, obtain the string for it (possibly parsing as a string
4368/// literal if it is @"" syntax), and append the string to `buf`.
4369/// See also `identifierTokenString` and `parseStrLit`.
4370pub fn appendIdentStr(
4371 mod: *Module,
4372 scope: *Scope,
4373 token: ast.TokenIndex,
4374 buf: *ArrayListUnmanaged(u8),
4375) InnerError!void {
4376 const tree = scope.tree();
4377 const token_tags = tree.tokens.items(.tag);
4378 assert(token_tags[token] == .identifier);
4379 const ident_name = tree.tokenSlice(token);
4380 if (!mem.startsWith(u8, ident_name, "@")) {
4381 return buf.appendSlice(mod.gpa, ident_name);
4382 } else {
4383 return mod.parseStrLit(scope, token, buf, ident_name, 1);
41284384 }
41294385}
41304386
4131pub fn namedFieldPtr(
4387/// Appends the result to `buf`.
4388pub fn parseStrLit(
41324389 mod: *Module,
41334390 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 }
4391 token: ast.TokenIndex,
4392 buf: *ArrayListUnmanaged(u8),
4393 bytes: []const u8,
4394 offset: u32,
4395) InnerError!void {
4396 const tree = scope.tree();
4397 const token_starts = tree.tokens.items(.start);
4398 const raw_string = bytes[offset..];
4399 var buf_managed = buf.toManaged(mod.gpa);
4400 const result = std.zig.string_literal.parseAppend(&buf_managed, raw_string);
4401 buf.* = buf_managed.toUnmanaged();
4402 switch (try result) {
4403 .success => return,
4404 .invalid_character => |bad_index| {
4405 return mod.failOff(
4406 scope,
4407 token_starts[token] + offset + @intCast(u32, bad_index),
4408 "invalid string literal character: '{c}'",
4409 .{raw_string[bad_index]},
4410 );
41614411 },
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 }
4412 .expected_hex_digits => |bad_index| {
4413 return mod.failOff(
4414 scope,
4415 token_starts[token] + offset + @intCast(u32, bad_index),
4416 "expected hex digits after '\\x'",
4417 .{},
4418 );
41854419 },
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 }
4420 .invalid_hex_escape => |bad_index| {
4421 return mod.failOff(
4422 scope,
4423 token_starts[token] + offset + @intCast(u32, bad_index),
4424 "invalid hex digit: '{c}'",
4425 .{raw_string[bad_index]},
4426 );
4427 },
4428 .invalid_unicode_escape => |bad_index| {
4429 return mod.failOff(
4430 scope,
4431 token_starts[token] + offset + @intCast(u32, bad_index),
4432 "invalid unicode digit: '{c}'",
4433 .{raw_string[bad_index]},
4434 );
4435 },
4436 .missing_matching_rbrace => |bad_index| {
4437 return mod.failOff(
4438 scope,
4439 token_starts[token] + offset + @intCast(u32, bad_index),
4440 "missing matching '}}' character",
4441 .{},
4442 );
4443 },
4444 .expected_unicode_digits => |bad_index| {
4445 return mod.failOff(
4446 scope,
4447 token_starts[token] + offset + @intCast(u32, bad_index),
4448 "expected unicode digits after '\\u'",
4449 .{},
4450 );
42304451 },
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 }
42694452 }
4270
4271 return mod.fail(scope, src, "TODO implement more analyze elemptr", .{});
42724453}
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+4897
......@@ -0,0 +1,4897 @@
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
263 // Instructions that we know to *always* be noreturn based solely on their tag.
264 // These functions match the return type of analyzeBody so that we can
265 // tail call them here.
266 .condbr => return sema.zirCondbr(block, inst),
267 .@"break" => return sema.zirBreak(block, inst),
268 .break_inline => return inst,
269 .compile_error => return sema.zirCompileError(block, inst),
270 .ret_coerce => return sema.zirRetTok(block, inst, true),
271 .ret_node => return sema.zirRetNode(block, inst),
272 .ret_tok => return sema.zirRetTok(block, inst, false),
273 .@"unreachable" => return sema.zirUnreachable(block, inst),
274 .repeat => return sema.zirRepeat(block, inst),
275
276 // Instructions that we know can *never* be noreturn based solely on
277 // their tag. We avoid needlessly checking if they are noreturn and
278 // continue the loop.
279 // We also know that they cannot be referenced later, so we avoid
280 // putting them into the map.
281 .breakpoint => {
282 try sema.zirBreakpoint(block, inst);
283 continue;
284 },
285 .dbg_stmt_node => {
286 try sema.zirDbgStmtNode(block, inst);
287 continue;
288 },
289 .ensure_err_payload_void => {
290 try sema.zirEnsureErrPayloadVoid(block, inst);
291 continue;
292 },
293 .ensure_result_non_error => {
294 try sema.zirEnsureResultNonError(block, inst);
295 continue;
296 },
297 .ensure_result_used => {
298 try sema.zirEnsureResultUsed(block, inst);
299 continue;
300 },
301 .compile_log => {
302 try sema.zirCompileLog(block, inst);
303 continue;
304 },
305 .set_eval_branch_quota => {
306 try sema.zirSetEvalBranchQuota(block, inst);
307 continue;
308 },
309 .store => {
310 try sema.zirStore(block, inst);
311 continue;
312 },
313 .store_node => {
314 try sema.zirStoreNode(block, inst);
315 continue;
316 },
317 .store_to_block_ptr => {
318 try sema.zirStoreToBlockPtr(block, inst);
319 continue;
320 },
321 .store_to_inferred_ptr => {
322 try sema.zirStoreToInferredPtr(block, inst);
323 continue;
324 },
325 .resolve_inferred_alloc => {
326 try sema.zirResolveInferredAlloc(block, inst);
327 continue;
328 },
329
330 // Special case instructions to handle comptime control flow.
331 .repeat_inline => {
332 // Send comptime control flow back to the beginning of this block.
333 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
334 try sema.emitBackwardBranch(block, src);
335 i = 0;
336 continue;
337 },
338 .block_inline => blk: {
339 // Directly analyze the block body without introducing a new block.
340 const inst_data = datas[inst].pl_node;
341 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
342 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
343 const break_inst = try sema.analyzeBody(block, inline_body);
344 const break_data = datas[break_inst].@"break";
345 if (inst == break_data.block_inst) {
346 break :blk try sema.resolveInst(break_data.operand);
347 } else {
348 return break_inst;
349 }
350 },
351 .condbr_inline => blk: {
352 const inst_data = datas[inst].pl_node;
353 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
354 const extra = sema.code.extraData(zir.Inst.CondBr, inst_data.payload_index);
355 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
356 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
357 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition);
358 const inline_body = if (cond.val.toBool()) then_body else else_body;
359 const break_inst = try sema.analyzeBody(block, inline_body);
360 const break_data = datas[break_inst].@"break";
361 if (inst == break_data.block_inst) {
362 break :blk try sema.resolveInst(break_data.operand);
363 } else {
364 return break_inst;
365 }
366 },
367 };
368 if (map[inst].ty.isNoReturn())
369 return always_noreturn;
370 }
371}
372
373/// TODO when we rework TZIR memory layout, this function will no longer have a possible error.
374pub fn resolveInst(sema: *Sema, zir_ref: zir.Inst.Ref) error{OutOfMemory}!*ir.Inst {
375 var i: usize = @enumToInt(zir_ref);
376
377 // First section of indexes correspond to a set number of constant values.
378 if (i < zir.Inst.Ref.typed_value_map.len) {
379 // TODO when we rework TZIR memory layout, this function can be as simple as:
380 // if (zir_ref < zir.const_inst_list.len + sema.param_count)
381 // return zir_ref;
382 // Until then we allocate memory for a new, mutable `ir.Inst` to match what
383 // TZIR expects.
384 return sema.mod.constInst(sema.arena, .unneeded, zir.Inst.Ref.typed_value_map[i]);
385 }
386 i -= zir.Inst.Ref.typed_value_map.len;
387
388 // Next section of indexes correspond to function parameters, if any.
389 if (i < sema.param_inst_list.len) {
390 return sema.param_inst_list[i];
391 }
392 i -= sema.param_inst_list.len;
393
394 // Finally, the last section of indexes refers to the map of ZIR=>TZIR.
395 return sema.inst_map[i];
396}
397
398fn resolveConstString(
399 sema: *Sema,
400 block: *Scope.Block,
401 src: LazySrcLoc,
402 zir_ref: zir.Inst.Ref,
403) ![]u8 {
404 const tzir_inst = try sema.resolveInst(zir_ref);
405 const wanted_type = Type.initTag(.const_slice_u8);
406 const coerced_inst = try sema.coerce(block, wanted_type, tzir_inst, src);
407 const val = try sema.resolveConstValue(block, src, coerced_inst);
408 return val.toAllocatedBytes(sema.arena);
409}
410
411fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: zir.Inst.Ref) !Type {
412 const tzir_inst = try sema.resolveInst(zir_ref);
413 const wanted_type = Type.initTag(.@"type");
414 const coerced_inst = try sema.coerce(block, wanted_type, tzir_inst, src);
415 const val = try sema.resolveConstValue(block, src, coerced_inst);
416 return val.toType(sema.arena);
417}
418
419fn resolveConstValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !Value {
420 return (try sema.resolveDefinedValue(block, src, base)) orelse
421 return sema.failWithNeededComptime(block, src);
422}
423
424fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !?Value {
425 if (base.value()) |val| {
426 if (val.isUndef()) {
427 return sema.failWithUseOfUndef(block, src);
428 }
429 return val;
430 }
431 return null;
432}
433
434fn failWithNeededComptime(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError {
435 return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{});
436}
437
438fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError {
439 return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{});
440}
441
442/// Appropriate to call when the coercion has already been done by result
443/// location semantics. Asserts the value fits in the provided `Int` type.
444/// Only supports `Int` types 64 bits or less.
445fn resolveAlreadyCoercedInt(
446 sema: *Sema,
447 block: *Scope.Block,
448 src: LazySrcLoc,
449 zir_ref: zir.Inst.Ref,
450 comptime Int: type,
451) !Int {
452 comptime assert(@typeInfo(Int).Int.bits <= 64);
453 const tzir_inst = try sema.resolveInst(zir_ref);
454 const val = try sema.resolveConstValue(block, src, tzir_inst);
455 switch (@typeInfo(Int).Int.signedness) {
456 .signed => return @intCast(Int, val.toSignedInt()),
457 .unsigned => return @intCast(Int, val.toUnsignedInt()),
458 }
459}
460
461fn resolveInt(
462 sema: *Sema,
463 block: *Scope.Block,
464 src: LazySrcLoc,
465 zir_ref: zir.Inst.Ref,
466 dest_type: Type,
467) !u64 {
468 const tzir_inst = try sema.resolveInst(zir_ref);
469 const coerced = try sema.coerce(block, dest_type, tzir_inst, src);
470 const val = try sema.resolveConstValue(block, src, coerced);
471
472 return val.toUnsignedInt();
473}
474
475fn resolveInstConst(
476 sema: *Sema,
477 block: *Scope.Block,
478 src: LazySrcLoc,
479 zir_ref: zir.Inst.Ref,
480) InnerError!TypedValue {
481 const tzir_inst = try sema.resolveInst(zir_ref);
482 const val = try sema.resolveConstValue(block, src, tzir_inst);
483 return TypedValue{
484 .ty = tzir_inst.ty,
485 .val = val,
486 };
487}
488
489fn zirConst(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
490 const tracy = trace(@src());
491 defer tracy.end();
492
493 const tv_ptr = sema.code.instructions.items(.data)[inst].@"const";
494 // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
495 // after analysis. This happens, for example, with variable declaration initialization
496 // expressions.
497 const typed_value_copy = try tv_ptr.copy(sema.arena);
498 return sema.mod.constInst(sema.arena, .unneeded, typed_value_copy);
499}
500
501fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
502 const tracy = trace(@src());
503 defer tracy.end();
504 return sema.mod.fail(&block.base, sema.src, "TODO implement zir_sema.zirBitcastResultPtr", .{});
505}
506
507fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
508 const tracy = trace(@src());
509 defer tracy.end();
510 return sema.mod.fail(&block.base, sema.src, "TODO implement zirCoerceResultPtr", .{});
511}
512
513fn zirRetPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
514 const tracy = trace(@src());
515 defer tracy.end();
516
517 const src: LazySrcLoc = .unneeded;
518 try sema.requireFunctionBlock(block, src);
519 const fn_ty = sema.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
520 const ret_type = fn_ty.fnReturnType();
521 const ptr_type = try sema.mod.simplePtrType(sema.arena, ret_type, true, .One);
522 return block.addNoOp(src, ptr_type, .alloc);
523}
524
525fn zirRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
526 const tracy = trace(@src());
527 defer tracy.end();
528
529 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
530 const operand = try sema.resolveInst(inst_data.operand);
531 return sema.analyzeRef(block, inst_data.src(), operand);
532}
533
534fn zirRetType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
535 const tracy = trace(@src());
536 defer tracy.end();
537
538 const src: LazySrcLoc = .unneeded;
539 try sema.requireFunctionBlock(block, src);
540 const fn_ty = sema.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
541 const ret_type = fn_ty.fnReturnType();
542 return sema.mod.constType(sema.arena, src, ret_type);
543}
544
545fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
546 const tracy = trace(@src());
547 defer tracy.end();
548
549 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
550 const operand = try sema.resolveInst(inst_data.operand);
551 const src = inst_data.src();
552
553 return sema.ensureResultUsed(block, operand, src);
554}
555
556fn ensureResultUsed(
557 sema: *Sema,
558 block: *Scope.Block,
559 operand: *Inst,
560 src: LazySrcLoc,
561) InnerError!void {
562 switch (operand.ty.zigTypeTag()) {
563 .Void, .NoReturn => return,
564 else => return sema.mod.fail(&block.base, src, "expression value is ignored", .{}),
565 }
566}
567
568fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
569 const tracy = trace(@src());
570 defer tracy.end();
571
572 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
573 const operand = try sema.resolveInst(inst_data.operand);
574 const src = inst_data.src();
575 switch (operand.ty.zigTypeTag()) {
576 .ErrorSet, .ErrorUnion => return sema.mod.fail(&block.base, src, "error is discarded", .{}),
577 else => return,
578 }
579}
580
581fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
582 const tracy = trace(@src());
583 defer tracy.end();
584
585 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
586 const src = inst_data.src();
587 const array_ptr = try sema.resolveInst(inst_data.operand);
588
589 const elem_ty = array_ptr.ty.elemType();
590 if (!elem_ty.isIndexable()) {
591 const cond_src: LazySrcLoc = .{ .node_offset_for_cond = inst_data.src_node };
592 const msg = msg: {
593 const msg = try sema.mod.errMsg(
594 &block.base,
595 cond_src,
596 "type '{}' does not support indexing",
597 .{elem_ty},
598 );
599 errdefer msg.destroy(sema.gpa);
600 try sema.mod.errNote(
601 &block.base,
602 cond_src,
603 msg,
604 "for loop operand must be an array, slice, tuple, or vector",
605 .{},
606 );
607 break :msg msg;
608 };
609 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
610 }
611 const result_ptr = try sema.namedFieldPtr(block, src, array_ptr, "len", src);
612 return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
613}
614
615fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
616 const tracy = trace(@src());
617 defer tracy.end();
618
619 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
620 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
621 const var_decl_src = inst_data.src();
622 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
623 const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One);
624 try sema.requireRuntimeBlock(block, var_decl_src);
625 return block.addNoOp(var_decl_src, ptr_type, .alloc);
626}
627
628fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
629 const tracy = trace(@src());
630 defer tracy.end();
631
632 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
633 const var_decl_src = inst_data.src();
634 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
635 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
636 try sema.validateVarType(block, ty_src, var_type);
637 const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One);
638 try sema.requireRuntimeBlock(block, var_decl_src);
639 return block.addNoOp(var_decl_src, ptr_type, .alloc);
640}
641
642fn zirAllocInferred(
643 sema: *Sema,
644 block: *Scope.Block,
645 inst: zir.Inst.Index,
646 inferred_alloc_ty: Type,
647) InnerError!*Inst {
648 const tracy = trace(@src());
649 defer tracy.end();
650
651 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
652 const src = inst_data.src();
653
654 const val_payload = try sema.arena.create(Value.Payload.InferredAlloc);
655 val_payload.* = .{
656 .data = .{},
657 };
658 // `Module.constInst` does not add the instruction to the block because it is
659 // not needed in the case of constant values. However here, we plan to "downgrade"
660 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
661 // to the block even though it is currently a `.constant`.
662 const result = try sema.mod.constInst(sema.arena, src, .{
663 .ty = inferred_alloc_ty,
664 .val = Value.initPayload(&val_payload.base),
665 });
666 try sema.requireFunctionBlock(block, src);
667 try block.instructions.append(sema.gpa, result);
668 return result;
669}
670
671fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
672 const tracy = trace(@src());
673 defer tracy.end();
674
675 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
676 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
677 const ptr = try sema.resolveInst(inst_data.operand);
678 const ptr_val = ptr.castTag(.constant).?.val;
679 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
680 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
681 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list);
682 const var_is_mut = switch (ptr.ty.tag()) {
683 .inferred_alloc_const => false,
684 .inferred_alloc_mut => true,
685 else => unreachable,
686 };
687 if (var_is_mut) {
688 try sema.validateVarType(block, ty_src, final_elem_ty);
689 }
690 const final_ptr_ty = try sema.mod.simplePtrType(sema.arena, final_elem_ty, true, .One);
691
692 // Change it to a normal alloc.
693 ptr.ty = final_ptr_ty;
694 ptr.tag = .alloc;
695}
696
697fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
698 const tracy = trace(@src());
699 defer tracy.end();
700
701 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
702 const ptr = try sema.resolveInst(bin_inst.lhs);
703 const value = try sema.resolveInst(bin_inst.rhs);
704 const ptr_ty = try sema.mod.simplePtrType(sema.arena, value.ty, true, .One);
705 // TODO detect when this store should be done at compile-time. For example,
706 // if expressions should force it when the condition is compile-time known.
707 const src: LazySrcLoc = .unneeded;
708 try sema.requireRuntimeBlock(block, src);
709 const bitcasted_ptr = try block.addUnOp(src, ptr_ty, .bitcast, ptr);
710 return sema.storePtr(block, src, bitcasted_ptr, value);
711}
712
713fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
714 const tracy = trace(@src());
715 defer tracy.end();
716
717 const src: LazySrcLoc = .unneeded;
718 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
719 const ptr = try sema.resolveInst(bin_inst.lhs);
720 const value = try sema.resolveInst(bin_inst.rhs);
721 const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?;
722 // Add the stored instruction to the set we will use to resolve peer types
723 // for the inferred allocation.
724 try inferred_alloc.data.stored_inst_list.append(sema.arena, value);
725 // Create a runtime bitcast instruction with exactly the type the pointer wants.
726 const ptr_ty = try sema.mod.simplePtrType(sema.arena, value.ty, true, .One);
727 try sema.requireRuntimeBlock(block, src);
728 const bitcasted_ptr = try block.addUnOp(src, ptr_ty, .bitcast, ptr);
729 return sema.storePtr(block, src, bitcasted_ptr, value);
730}
731
732fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
733 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
734 const src = inst_data.src();
735 try sema.requireFunctionBlock(block, src);
736 const quota = try sema.resolveAlreadyCoercedInt(block, src, inst_data.operand, u32);
737 if (sema.branch_quota < quota)
738 sema.branch_quota = quota;
739}
740
741fn zirStore(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
742 const tracy = trace(@src());
743 defer tracy.end();
744
745 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
746 const ptr = try sema.resolveInst(bin_inst.lhs);
747 const value = try sema.resolveInst(bin_inst.rhs);
748 return sema.storePtr(block, sema.src, ptr, value);
749}
750
751fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
752 const tracy = trace(@src());
753 defer tracy.end();
754
755 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
756 const src = inst_data.src();
757 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
758 const ptr = try sema.resolveInst(extra.lhs);
759 const value = try sema.resolveInst(extra.rhs);
760 return sema.storePtr(block, src, ptr, value);
761}
762
763fn zirParamType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
764 const tracy = trace(@src());
765 defer tracy.end();
766
767 const src: LazySrcLoc = .unneeded;
768 const inst_data = sema.code.instructions.items(.data)[inst].param_type;
769 const fn_inst = try sema.resolveInst(inst_data.callee);
770 const param_index = inst_data.param_index;
771
772 const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) {
773 .Fn => fn_inst.ty,
774 .BoundFn => {
775 return sema.mod.fail(&block.base, fn_inst.src, "TODO implement zirParamType for method call syntax", .{});
776 },
777 else => {
778 return sema.mod.fail(&block.base, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty});
779 },
780 };
781
782 const param_count = fn_ty.fnParamLen();
783 if (param_index >= param_count) {
784 if (fn_ty.fnIsVarArgs()) {
785 return sema.mod.constType(sema.arena, src, Type.initTag(.var_args_param));
786 }
787 return sema.mod.fail(&block.base, src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{
788 param_index,
789 fn_ty,
790 param_count,
791 });
792 }
793
794 // TODO support generic functions
795 const param_type = fn_ty.fnParamType(param_index);
796 return sema.mod.constType(sema.arena, src, param_type);
797}
798
799fn zirStr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
800 const tracy = trace(@src());
801 defer tracy.end();
802
803 const zir_bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code);
804
805 // `zir_bytes` references memory inside the ZIR module, which can get deallocated
806 // after semantic analysis is complete, for example in the case of the initialization
807 // expression of a variable declaration. We need the memory to be in the new
808 // anonymous Decl's arena.
809
810 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
811 errdefer new_decl_arena.deinit();
812
813 const bytes = try new_decl_arena.allocator.dupe(u8, zir_bytes);
814
815 const decl_ty = try Type.Tag.array_u8_sentinel_0.create(&new_decl_arena.allocator, bytes.len);
816 const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, bytes);
817
818 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{
819 .ty = decl_ty,
820 .val = decl_val,
821 });
822 return sema.analyzeDeclRef(block, .unneeded, new_decl);
823}
824
825fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
826 const tracy = trace(@src());
827 defer tracy.end();
828
829 const int = sema.code.instructions.items(.data)[inst].int;
830 return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int);
831}
832
833fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
834 const tracy = trace(@src());
835 defer tracy.end();
836
837 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
838 const src = inst_data.src();
839 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
840 const msg = try sema.resolveConstString(block, operand_src, inst_data.operand);
841 return sema.mod.fail(&block.base, src, "{s}", .{msg});
842}
843
844fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
845 var managed = sema.mod.compile_log_text.toManaged(sema.gpa);
846 defer sema.mod.compile_log_text = managed.moveToUnmanaged();
847 const writer = managed.writer();
848
849 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
850 const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index);
851 const args = sema.code.refSlice(extra.end, extra.data.operands_len);
852
853 for (args) |arg_ref, i| {
854 if (i != 0) try writer.print(", ", .{});
855
856 const arg = try sema.resolveInst(arg_ref);
857 if (arg.value()) |val| {
858 try writer.print("@as({}, {})", .{ arg.ty, val });
859 } else {
860 try writer.print("@as({}, [runtime value])", .{arg.ty});
861 }
862 }
863 try writer.print("\n", .{});
864
865 const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, sema.owner_decl);
866 if (!gop.found_existing) {
867 gop.entry.value = inst_data.src().toSrcLoc(&block.base);
868 }
869}
870
871fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
872 const tracy = trace(@src());
873 defer tracy.end();
874
875 const src_node = sema.code.instructions.items(.data)[inst].node;
876 const src: LazySrcLoc = .{ .node_offset = src_node };
877 try sema.requireRuntimeBlock(block, src);
878 return always_noreturn;
879}
880
881fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
882 const tracy = trace(@src());
883 defer tracy.end();
884
885 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
886 const src = inst_data.src();
887 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
888 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
889
890 // TZIR expects a block outside the loop block too.
891 const block_inst = try sema.arena.create(Inst.Block);
892 block_inst.* = .{
893 .base = .{
894 .tag = Inst.Block.base_tag,
895 .ty = undefined,
896 .src = src,
897 },
898 .body = undefined,
899 };
900
901 var child_block = parent_block.makeSubBlock();
902 child_block.label = Scope.Block.Label{
903 .zir_block = inst,
904 .merges = .{
905 .results = .{},
906 .br_list = .{},
907 .block_inst = block_inst,
908 },
909 };
910 const merges = &child_block.label.?.merges;
911
912 defer child_block.instructions.deinit(sema.gpa);
913 defer merges.results.deinit(sema.gpa);
914 defer merges.br_list.deinit(sema.gpa);
915
916 // Reserve space for a Loop instruction so that generated Break instructions can
917 // point to it, even if it doesn't end up getting used because the code ends up being
918 // comptime evaluated.
919 const loop_inst = try sema.arena.create(Inst.Loop);
920 loop_inst.* = .{
921 .base = .{
922 .tag = Inst.Loop.base_tag,
923 .ty = Type.initTag(.noreturn),
924 .src = src,
925 },
926 .body = undefined,
927 };
928
929 var loop_block = child_block.makeSubBlock();
930 defer loop_block.instructions.deinit(sema.gpa);
931
932 _ = try sema.analyzeBody(&loop_block, body);
933
934 // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
935
936 try child_block.instructions.append(sema.gpa, &loop_inst.base);
937 loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, loop_block.instructions.items) };
938
939 return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
940}
941
942fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
943 const tracy = trace(@src());
944 defer tracy.end();
945
946 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
947 const src = inst_data.src();
948 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
949 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
950
951 // Reserve space for a Block instruction so that generated Break instructions can
952 // point to it, even if it doesn't end up getting used because the code ends up being
953 // comptime evaluated.
954 const block_inst = try sema.arena.create(Inst.Block);
955 block_inst.* = .{
956 .base = .{
957 .tag = Inst.Block.base_tag,
958 .ty = undefined, // Set after analysis.
959 .src = src,
960 },
961 .body = undefined,
962 };
963
964 var child_block: Scope.Block = .{
965 .parent = parent_block,
966 .sema = sema,
967 .src_decl = parent_block.src_decl,
968 .instructions = .{},
969 // TODO @as here is working around a stage1 miscompilation bug :(
970 .label = @as(?Scope.Block.Label, Scope.Block.Label{
971 .zir_block = inst,
972 .merges = .{
973 .results = .{},
974 .br_list = .{},
975 .block_inst = block_inst,
976 },
977 }),
978 .inlining = parent_block.inlining,
979 .is_comptime = parent_block.is_comptime,
980 };
981 const merges = &child_block.label.?.merges;
982
983 defer child_block.instructions.deinit(sema.gpa);
984 defer merges.results.deinit(sema.gpa);
985 defer merges.br_list.deinit(sema.gpa);
986
987 _ = try sema.analyzeBody(&child_block, body);
988
989 return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
990}
991
992fn analyzeBlockBody(
993 sema: *Sema,
994 parent_block: *Scope.Block,
995 src: LazySrcLoc,
996 child_block: *Scope.Block,
997 merges: *Scope.Block.Merges,
998) InnerError!*Inst {
999 const tracy = trace(@src());
1000 defer tracy.end();
1001
1002 // Blocks must terminate with noreturn instruction.
1003 assert(child_block.instructions.items.len != 0);
1004 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
1005
1006 if (merges.results.items.len == 0) {
1007 // No need for a block instruction. We can put the new instructions
1008 // directly into the parent block.
1009 const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items);
1010 try parent_block.instructions.appendSlice(sema.gpa, copied_instructions);
1011 return copied_instructions[copied_instructions.len - 1];
1012 }
1013 if (merges.results.items.len == 1) {
1014 const last_inst_index = child_block.instructions.items.len - 1;
1015 const last_inst = child_block.instructions.items[last_inst_index];
1016 if (last_inst.breakBlock()) |br_block| {
1017 if (br_block == merges.block_inst) {
1018 // No need for a block instruction. We can put the new instructions directly
1019 // into the parent block. Here we omit the break instruction.
1020 const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
1021 try parent_block.instructions.appendSlice(sema.gpa, copied_instructions);
1022 return merges.results.items[0];
1023 }
1024 }
1025 }
1026 // It is impossible to have the number of results be > 1 in a comptime scope.
1027 assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition.
1028
1029 // Need to set the type and emit the Block instruction. This allows machine code generation
1030 // to emit a jump instruction to after the block when it encounters the break.
1031 try parent_block.instructions.append(sema.gpa, &merges.block_inst.base);
1032 const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items);
1033 merges.block_inst.base.ty = resolved_ty;
1034 merges.block_inst.body = .{
1035 .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items),
1036 };
1037 // Now that the block has its type resolved, we need to go back into all the break
1038 // instructions, and insert type coercion on the operands.
1039 for (merges.br_list.items) |br| {
1040 if (br.operand.ty.eql(resolved_ty)) {
1041 // No type coercion needed.
1042 continue;
1043 }
1044 var coerce_block = parent_block.makeSubBlock();
1045 defer coerce_block.instructions.deinit(sema.gpa);
1046 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br.operand, br.operand.src);
1047 // If no instructions were produced, such as in the case of a coercion of a
1048 // constant value to a new type, we can simply point the br operand to it.
1049 if (coerce_block.instructions.items.len == 0) {
1050 br.operand = coerced_operand;
1051 continue;
1052 }
1053 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);
1054 // Here we depend on the br instruction having been over-allocated (if necessary)
1055 // inside zirBreak so that it can be converted into a br_block_flat instruction.
1056 const br_src = br.base.src;
1057 const br_ty = br.base.ty;
1058 const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);
1059 br_block_flat.* = .{
1060 .base = .{
1061 .src = br_src,
1062 .ty = br_ty,
1063 .tag = .br_block_flat,
1064 },
1065 .block = merges.block_inst,
1066 .body = .{
1067 .instructions = try sema.arena.dupe(*Inst, coerce_block.instructions.items),
1068 },
1069 };
1070 }
1071 return &merges.block_inst.base;
1072}
1073
1074fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
1075 const tracy = trace(@src());
1076 defer tracy.end();
1077
1078 const src_node = sema.code.instructions.items(.data)[inst].node;
1079 const src: LazySrcLoc = .{ .node_offset = src_node };
1080 try sema.requireRuntimeBlock(block, src);
1081 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
1082}
1083
1084fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
1085 const tracy = trace(@src());
1086 defer tracy.end();
1087
1088 const inst_data = sema.code.instructions.items(.data)[inst].@"break";
1089 const src = sema.src;
1090 const operand = try sema.resolveInst(inst_data.operand);
1091 const zir_block = inst_data.block_inst;
1092
1093 var block = start_block;
1094 while (true) {
1095 if (block.label) |*label| {
1096 if (label.zir_block == zir_block) {
1097 // Here we add a br instruction, but we over-allocate a little bit
1098 // (if necessary) to make it possible to convert the instruction into
1099 // a br_block_flat instruction later.
1100 const br = @ptrCast(*Inst.Br, try sema.arena.alignedAlloc(
1101 u8,
1102 Inst.convertable_br_align,
1103 Inst.convertable_br_size,
1104 ));
1105 br.* = .{
1106 .base = .{
1107 .tag = .br,
1108 .ty = Type.initTag(.noreturn),
1109 .src = src,
1110 },
1111 .operand = operand,
1112 .block = label.merges.block_inst,
1113 };
1114 try start_block.instructions.append(sema.gpa, &br.base);
1115 try label.merges.results.append(sema.gpa, operand);
1116 try label.merges.br_list.append(sema.gpa, br);
1117 return inst;
1118 }
1119 }
1120 block = block.parent.?;
1121 }
1122}
1123
1124fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
1125 const tracy = trace(@src());
1126 defer tracy.end();
1127
1128 // We do not set sema.src here because dbg_stmt instructions are only emitted for
1129 // ZIR code that possibly will need to generate runtime code. So error messages
1130 // and other source locations must not rely on sema.src being set from dbg_stmt
1131 // instructions.
1132 if (block.is_comptime) return;
1133
1134 const src_node = sema.code.instructions.items(.data)[inst].node;
1135 const src: LazySrcLoc = .{ .node_offset = src_node };
1136
1137 const src_loc = src.toSrcLoc(&block.base);
1138 const abs_byte_off = try src_loc.byteOffset();
1139 _ = try block.addDbgStmt(src, abs_byte_off);
1140}
1141
1142fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1143 const tracy = trace(@src());
1144 defer tracy.end();
1145
1146 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1147 const src = inst_data.src();
1148 const decl = sema.code.decls[inst_data.payload_index];
1149 return sema.analyzeDeclRef(block, src, decl);
1150}
1151
1152fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1153 const tracy = trace(@src());
1154 defer tracy.end();
1155
1156 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1157 const src = inst_data.src();
1158 const decl = sema.code.decls[inst_data.payload_index];
1159 return sema.analyzeDeclVal(block, src, decl);
1160}
1161
1162fn zirCallNone(
1163 sema: *Sema,
1164 block: *Scope.Block,
1165 inst: zir.Inst.Index,
1166 ensure_result_used: bool,
1167) InnerError!*Inst {
1168 const tracy = trace(@src());
1169 defer tracy.end();
1170
1171 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1172 const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
1173
1174 return sema.analyzeCall(block, inst_data.operand, func_src, inst_data.src(), .auto, ensure_result_used, &.{});
1175}
1176
1177fn zirCall(
1178 sema: *Sema,
1179 block: *Scope.Block,
1180 inst: zir.Inst.Index,
1181 modifier: std.builtin.CallOptions.Modifier,
1182 ensure_result_used: bool,
1183) InnerError!*Inst {
1184 const tracy = trace(@src());
1185 defer tracy.end();
1186
1187 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1188 const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
1189 const call_src = inst_data.src();
1190 const extra = sema.code.extraData(zir.Inst.Call, inst_data.payload_index);
1191 const args = sema.code.refSlice(extra.end, extra.data.args_len);
1192
1193 return sema.analyzeCall(block, extra.data.callee, func_src, call_src, modifier, ensure_result_used, args);
1194}
1195
1196fn analyzeCall(
1197 sema: *Sema,
1198 block: *Scope.Block,
1199 zir_func: zir.Inst.Ref,
1200 func_src: LazySrcLoc,
1201 call_src: LazySrcLoc,
1202 modifier: std.builtin.CallOptions.Modifier,
1203 ensure_result_used: bool,
1204 zir_args: []const zir.Inst.Ref,
1205) InnerError!*ir.Inst {
1206 const func = try sema.resolveInst(zir_func);
1207
1208 if (func.ty.zigTypeTag() != .Fn)
1209 return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty});
1210
1211 const cc = func.ty.fnCallingConvention();
1212 if (cc == .Naked) {
1213 // TODO add error note: declared here
1214 return sema.mod.fail(
1215 &block.base,
1216 func_src,
1217 "unable to call function with naked calling convention",
1218 .{},
1219 );
1220 }
1221 const fn_params_len = func.ty.fnParamLen();
1222 if (func.ty.fnIsVarArgs()) {
1223 assert(cc == .C);
1224 if (zir_args.len < fn_params_len) {
1225 // TODO add error note: declared here
1226 return sema.mod.fail(
1227 &block.base,
1228 func_src,
1229 "expected at least {d} argument(s), found {d}",
1230 .{ fn_params_len, zir_args.len },
1231 );
1232 }
1233 } else if (fn_params_len != zir_args.len) {
1234 // TODO add error note: declared here
1235 return sema.mod.fail(
1236 &block.base,
1237 func_src,
1238 "expected {d} argument(s), found {d}",
1239 .{ fn_params_len, zir_args.len },
1240 );
1241 }
1242
1243 if (modifier == .compile_time) {
1244 return sema.mod.fail(&block.base, call_src, "TODO implement comptime function calls", .{});
1245 }
1246 if (modifier != .auto) {
1247 return sema.mod.fail(&block.base, call_src, "TODO implement call with modifier {}", .{modifier});
1248 }
1249
1250 // TODO handle function calls of generic functions
1251 const casted_args = try sema.arena.alloc(*Inst, zir_args.len);
1252 for (zir_args) |zir_arg, i| {
1253 // the args are already casted to the result of a param type instruction.
1254 casted_args[i] = try sema.resolveInst(zir_arg);
1255 }
1256
1257 const ret_type = func.ty.fnReturnType();
1258
1259 const is_comptime_call = block.is_comptime or modifier == .compile_time;
1260 const is_inline_call = is_comptime_call or modifier == .always_inline or
1261 func.ty.fnCallingConvention() == .Inline;
1262 const result: *Inst = if (is_inline_call) res: {
1263 const func_val = try sema.resolveConstValue(block, func_src, func);
1264 const module_fn = switch (func_val.tag()) {
1265 .function => func_val.castTag(.function).?.data,
1266 .extern_fn => return sema.mod.fail(&block.base, call_src, "{s} call of extern function", .{
1267 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
1268 }),
1269 else => unreachable,
1270 };
1271
1272 // Analyze the ZIR. The same ZIR gets analyzed into a runtime function
1273 // or an inlined call depending on what union tag the `label` field is
1274 // set to in the `Scope.Block`.
1275 // This block instruction will be used to capture the return value from the
1276 // inlined function.
1277 const block_inst = try sema.arena.create(Inst.Block);
1278 block_inst.* = .{
1279 .base = .{
1280 .tag = Inst.Block.base_tag,
1281 .ty = ret_type,
1282 .src = call_src,
1283 },
1284 .body = undefined,
1285 };
1286 // This one is shared among sub-blocks within the same callee, but not
1287 // shared among the entire inline/comptime call stack.
1288 var inlining: Scope.Block.Inlining = .{
1289 .merges = .{
1290 .results = .{},
1291 .br_list = .{},
1292 .block_inst = block_inst,
1293 },
1294 };
1295 var inline_sema: Sema = .{
1296 .mod = sema.mod,
1297 .gpa = sema.mod.gpa,
1298 .arena = sema.arena,
1299 .code = module_fn.zir,
1300 .inst_map = try sema.gpa.alloc(*ir.Inst, module_fn.zir.instructions.len),
1301 .owner_decl = sema.owner_decl,
1302 .owner_func = sema.owner_func,
1303 .func = module_fn,
1304 .param_inst_list = casted_args,
1305 .branch_quota = sema.branch_quota,
1306 .branch_count = sema.branch_count,
1307 };
1308 defer sema.gpa.free(inline_sema.inst_map);
1309
1310 var child_block: Scope.Block = .{
1311 .parent = null,
1312 .sema = &inline_sema,
1313 .src_decl = module_fn.owner_decl,
1314 .instructions = .{},
1315 .label = null,
1316 .inlining = &inlining,
1317 .is_comptime = is_comptime_call,
1318 };
1319
1320 const merges = &child_block.inlining.?.merges;
1321
1322 defer child_block.instructions.deinit(sema.gpa);
1323 defer merges.results.deinit(sema.gpa);
1324 defer merges.br_list.deinit(sema.gpa);
1325
1326 try inline_sema.emitBackwardBranch(&child_block, call_src);
1327
1328 // This will have return instructions analyzed as break instructions to
1329 // the block_inst above.
1330 _ = try inline_sema.root(&child_block);
1331
1332 const result = try inline_sema.analyzeBlockBody(block, call_src, &child_block, merges);
1333
1334 sema.branch_quota = inline_sema.branch_quota;
1335 sema.branch_count = inline_sema.branch_count;
1336
1337 break :res result;
1338 } else res: {
1339 try sema.requireRuntimeBlock(block, call_src);
1340 break :res try block.addCall(call_src, ret_type, func, casted_args);
1341 };
1342
1343 if (ensure_result_used) {
1344 try sema.ensureResultUsed(block, result, call_src);
1345 }
1346 return result;
1347}
1348
1349fn zirIntType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1350 const tracy = trace(@src());
1351 defer tracy.end();
1352
1353 const int_type = sema.code.instructions.items(.data)[inst].int_type;
1354 const src = int_type.src();
1355 const ty = try Module.makeIntType(sema.arena, int_type.signedness, int_type.bit_count);
1356
1357 return sema.mod.constType(sema.arena, src, ty);
1358}
1359
1360fn zirOptionalType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1361 const tracy = trace(@src());
1362 defer tracy.end();
1363
1364 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1365 const src = inst_data.src();
1366 const child_type = try sema.resolveType(block, src, inst_data.operand);
1367 const opt_type = try sema.mod.optionalType(sema.arena, child_type);
1368
1369 return sema.mod.constType(sema.arena, src, opt_type);
1370}
1371
1372fn zirOptionalTypeFromPtrElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1373 const tracy = trace(@src());
1374 defer tracy.end();
1375
1376 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1377 const ptr = try sema.resolveInst(inst_data.operand);
1378 const elem_ty = ptr.ty.elemType();
1379 const opt_ty = try sema.mod.optionalType(sema.arena, elem_ty);
1380
1381 return sema.mod.constType(sema.arena, inst_data.src(), opt_ty);
1382}
1383
1384fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1385 const tracy = trace(@src());
1386 defer tracy.end();
1387
1388 // TODO these should be lazily evaluated
1389 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
1390 const len = try sema.resolveInstConst(block, .unneeded, bin_inst.lhs);
1391 const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs);
1392 const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), null, elem_type);
1393
1394 return sema.mod.constType(sema.arena, .unneeded, array_ty);
1395}
1396
1397fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1398 const tracy = trace(@src());
1399 defer tracy.end();
1400
1401 // TODO these should be lazily evaluated
1402 const inst_data = sema.code.instructions.items(.data)[inst].array_type_sentinel;
1403 const len = try sema.resolveInstConst(block, .unneeded, inst_data.len);
1404 const extra = sema.code.extraData(zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data;
1405 const sentinel = try sema.resolveInstConst(block, .unneeded, extra.sentinel);
1406 const elem_type = try sema.resolveType(block, .unneeded, extra.elem_type);
1407 const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), sentinel.val, elem_type);
1408
1409 return sema.mod.constType(sema.arena, .unneeded, array_ty);
1410}
1411
1412fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1413 const tracy = trace(@src());
1414 defer tracy.end();
1415
1416 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1417 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
1418 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
1419 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
1420 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
1421 const error_union = try sema.resolveType(block, lhs_src, extra.lhs);
1422 const payload = try sema.resolveType(block, rhs_src, extra.rhs);
1423
1424 if (error_union.zigTypeTag() != .ErrorSet) {
1425 return sema.mod.fail(&block.base, lhs_src, "expected error set type, found {}", .{
1426 error_union.elemType(),
1427 });
1428 }
1429 const err_union_ty = try sema.mod.errorUnionType(sema.arena, error_union, payload);
1430 return sema.mod.constType(sema.arena, src, err_union_ty);
1431}
1432
1433fn zirErrorValue(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1434 const tracy = trace(@src());
1435 defer tracy.end();
1436
1437 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
1438 const src = inst_data.src();
1439
1440 // Create an anonymous error set type with only this error value, and return the value.
1441 const entry = try sema.mod.getErrorValue(inst_data.get(sema.code));
1442 const result_type = try Type.Tag.error_set_single.create(sema.arena, entry.key);
1443 return sema.mod.constInst(sema.arena, src, .{
1444 .ty = result_type,
1445 .val = try Value.Tag.@"error".create(sema.arena, .{
1446 .name = entry.key,
1447 }),
1448 });
1449}
1450
1451fn zirErrorToInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1452 const tracy = trace(@src());
1453 defer tracy.end();
1454
1455 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1456 const src = inst_data.src();
1457 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1458 const op = try sema.resolveInst(inst_data.operand);
1459 const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);
1460
1461 if (op_coerced.value()) |val| {
1462 const payload = try sema.arena.create(Value.Payload.U64);
1463 payload.* = .{
1464 .base = .{ .tag = .int_u64 },
1465 .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
1466 };
1467 return sema.mod.constInst(sema.arena, src, .{
1468 .ty = Type.initTag(.u16),
1469 .val = Value.initPayload(&payload.base),
1470 });
1471 }
1472
1473 try sema.requireRuntimeBlock(block, src);
1474 return block.addUnOp(src, Type.initTag(.u16), .error_to_int, op_coerced);
1475}
1476
1477fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1478 const tracy = trace(@src());
1479 defer tracy.end();
1480
1481 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1482 const src = inst_data.src();
1483 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1484
1485 const op = try sema.resolveInst(inst_data.operand);
1486
1487 if (try sema.resolveDefinedValue(block, operand_src, op)) |value| {
1488 const int = value.toUnsignedInt();
1489 if (int > sema.mod.global_error_set.count() or int == 0)
1490 return sema.mod.fail(&block.base, operand_src, "integer value {d} represents no error", .{int});
1491 const payload = try sema.arena.create(Value.Payload.Error);
1492 payload.* = .{
1493 .base = .{ .tag = .@"error" },
1494 .data = .{ .name = sema.mod.error_name_list.items[int] },
1495 };
1496 return sema.mod.constInst(sema.arena, src, .{
1497 .ty = Type.initTag(.anyerror),
1498 .val = Value.initPayload(&payload.base),
1499 });
1500 }
1501 try sema.requireRuntimeBlock(block, src);
1502 if (block.wantSafety()) {
1503 return sema.mod.fail(&block.base, src, "TODO: get max errors in compilation", .{});
1504 // const is_gt_max = @panic("TODO get max errors in compilation");
1505 // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
1506 }
1507 return block.addUnOp(src, Type.initTag(.anyerror), .int_to_error, op);
1508}
1509
1510fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1511 const tracy = trace(@src());
1512 defer tracy.end();
1513
1514 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1515 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
1516 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
1517 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
1518 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
1519 const lhs_ty = try sema.resolveType(block, lhs_src, extra.lhs);
1520 const rhs_ty = try sema.resolveType(block, rhs_src, extra.rhs);
1521 if (rhs_ty.zigTypeTag() != .ErrorSet)
1522 return sema.mod.fail(&block.base, rhs_src, "expected error set type, found {}", .{rhs_ty});
1523 if (lhs_ty.zigTypeTag() != .ErrorSet)
1524 return sema.mod.fail(&block.base, lhs_src, "expected error set type, found {}", .{lhs_ty});
1525
1526 // Anything merged with anyerror is anyerror.
1527 if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {
1528 return sema.mod.constInst(sema.arena, src, .{
1529 .ty = Type.initTag(.type),
1530 .val = Value.initTag(.anyerror_type),
1531 });
1532 }
1533 // When we support inferred error sets, we'll want to use a data structure that can
1534 // represent a merged set of errors without forcing them to be resolved here. Until then
1535 // we re-use the same data structure that is used for explicit error set declarations.
1536 var set: std.StringHashMapUnmanaged(void) = .{};
1537 defer set.deinit(sema.gpa);
1538
1539 switch (lhs_ty.tag()) {
1540 .error_set_single => {
1541 const name = lhs_ty.castTag(.error_set_single).?.data;
1542 try set.put(sema.gpa, name, {});
1543 },
1544 .error_set => {
1545 const lhs_set = lhs_ty.castTag(.error_set).?.data;
1546 try set.ensureCapacity(sema.gpa, set.count() + lhs_set.names_len);
1547 for (lhs_set.names_ptr[0..lhs_set.names_len]) |name| {
1548 set.putAssumeCapacityNoClobber(name, {});
1549 }
1550 },
1551 else => unreachable,
1552 }
1553 switch (rhs_ty.tag()) {
1554 .error_set_single => {
1555 const name = rhs_ty.castTag(.error_set_single).?.data;
1556 try set.put(sema.gpa, name, {});
1557 },
1558 .error_set => {
1559 const rhs_set = rhs_ty.castTag(.error_set).?.data;
1560 try set.ensureCapacity(sema.gpa, set.count() + rhs_set.names_len);
1561 for (rhs_set.names_ptr[0..rhs_set.names_len]) |name| {
1562 set.putAssumeCapacity(name, {});
1563 }
1564 },
1565 else => unreachable,
1566 }
1567
1568 const new_names = try sema.arena.alloc([]const u8, set.count());
1569 var it = set.iterator();
1570 var i: usize = 0;
1571 while (it.next()) |entry| : (i += 1) {
1572 new_names[i] = entry.key;
1573 }
1574
1575 const new_error_set = try sema.arena.create(Module.ErrorSet);
1576 new_error_set.* = .{
1577 .owner_decl = sema.owner_decl,
1578 .node_offset = inst_data.src_node,
1579 .names_ptr = new_names.ptr,
1580 .names_len = @intCast(u32, new_names.len),
1581 };
1582 const error_set_ty = try Type.Tag.error_set.create(sema.arena, new_error_set);
1583 return sema.mod.constInst(sema.arena, src, .{
1584 .ty = Type.initTag(.type),
1585 .val = try Value.Tag.ty.create(sema.arena, error_set_ty),
1586 });
1587}
1588
1589fn zirEnumLiteral(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1590 const tracy = trace(@src());
1591 defer tracy.end();
1592
1593 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
1594 const src = inst_data.src();
1595 const duped_name = try sema.arena.dupe(u8, inst_data.get(sema.code));
1596 return sema.mod.constInst(sema.arena, src, .{
1597 .ty = Type.initTag(.enum_literal),
1598 .val = try Value.Tag.enum_literal.create(sema.arena, duped_name),
1599 });
1600}
1601
1602fn zirEnumLiteralSmall(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1603 const tracy = trace(@src());
1604 defer tracy.end();
1605
1606 const name = sema.code.instructions.items(.data)[inst].small_str.get();
1607 const src: LazySrcLoc = .unneeded;
1608 const duped_name = try sema.arena.dupe(u8, name);
1609 return sema.mod.constInst(sema.arena, src, .{
1610 .ty = Type.initTag(.enum_literal),
1611 .val = try Value.Tag.enum_literal.create(sema.arena, duped_name),
1612 });
1613}
1614
1615/// Pointer in, pointer out.
1616fn zirOptionalPayloadPtr(
1617 sema: *Sema,
1618 block: *Scope.Block,
1619 inst: zir.Inst.Index,
1620 safety_check: bool,
1621) InnerError!*Inst {
1622 const tracy = trace(@src());
1623 defer tracy.end();
1624
1625 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1626 const optional_ptr = try sema.resolveInst(inst_data.operand);
1627 assert(optional_ptr.ty.zigTypeTag() == .Pointer);
1628 const src = inst_data.src();
1629
1630 const opt_type = optional_ptr.ty.elemType();
1631 if (opt_type.zigTypeTag() != .Optional) {
1632 return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type});
1633 }
1634
1635 const child_type = try opt_type.optionalChildAlloc(sema.arena);
1636 const child_pointer = try sema.mod.simplePtrType(sema.arena, child_type, !optional_ptr.ty.isConstPtr(), .One);
1637
1638 if (optional_ptr.value()) |pointer_val| {
1639 const val = try pointer_val.pointerDeref(sema.arena);
1640 if (val.isNull()) {
1641 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
1642 }
1643 // The same Value represents the pointer to the optional and the payload.
1644 return sema.mod.constInst(sema.arena, src, .{
1645 .ty = child_pointer,
1646 .val = pointer_val,
1647 });
1648 }
1649
1650 try sema.requireRuntimeBlock(block, src);
1651 if (safety_check and block.wantSafety()) {
1652 const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null_ptr, optional_ptr);
1653 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
1654 }
1655 return block.addUnOp(src, child_pointer, .optional_payload_ptr, optional_ptr);
1656}
1657
1658/// Value in, value out.
1659fn zirOptionalPayload(
1660 sema: *Sema,
1661 block: *Scope.Block,
1662 inst: zir.Inst.Index,
1663 safety_check: bool,
1664) InnerError!*Inst {
1665 const tracy = trace(@src());
1666 defer tracy.end();
1667
1668 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1669 const src = inst_data.src();
1670 const operand = try sema.resolveInst(inst_data.operand);
1671 const opt_type = operand.ty;
1672 if (opt_type.zigTypeTag() != .Optional) {
1673 return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type});
1674 }
1675
1676 const child_type = try opt_type.optionalChildAlloc(sema.arena);
1677
1678 if (operand.value()) |val| {
1679 if (val.isNull()) {
1680 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
1681 }
1682 return sema.mod.constInst(sema.arena, src, .{
1683 .ty = child_type,
1684 .val = val,
1685 });
1686 }
1687
1688 try sema.requireRuntimeBlock(block, src);
1689 if (safety_check and block.wantSafety()) {
1690 const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null, operand);
1691 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
1692 }
1693 return block.addUnOp(src, child_type, .optional_payload, operand);
1694}
1695
1696/// Value in, value out
1697fn zirErrUnionPayload(
1698 sema: *Sema,
1699 block: *Scope.Block,
1700 inst: zir.Inst.Index,
1701 safety_check: bool,
1702) InnerError!*Inst {
1703 const tracy = trace(@src());
1704 defer tracy.end();
1705
1706 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1707 const src = inst_data.src();
1708 const operand = try sema.resolveInst(inst_data.operand);
1709 if (operand.ty.zigTypeTag() != .ErrorUnion)
1710 return sema.mod.fail(&block.base, operand.src, "expected error union type, found '{}'", .{operand.ty});
1711
1712 if (operand.value()) |val| {
1713 if (val.getError()) |name| {
1714 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
1715 }
1716 const data = val.castTag(.error_union).?.data;
1717 return sema.mod.constInst(sema.arena, src, .{
1718 .ty = operand.ty.castTag(.error_union).?.data.payload,
1719 .val = data,
1720 });
1721 }
1722 try sema.requireRuntimeBlock(block, src);
1723 if (safety_check and block.wantSafety()) {
1724 const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand);
1725 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
1726 }
1727 return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_payload, operand);
1728}
1729
1730/// Pointer in, pointer out.
1731fn zirErrUnionPayloadPtr(
1732 sema: *Sema,
1733 block: *Scope.Block,
1734 inst: zir.Inst.Index,
1735 safety_check: bool,
1736) InnerError!*Inst {
1737 const tracy = trace(@src());
1738 defer tracy.end();
1739
1740 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1741 const src = inst_data.src();
1742 const operand = try sema.resolveInst(inst_data.operand);
1743 assert(operand.ty.zigTypeTag() == .Pointer);
1744
1745 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
1746 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});
1747
1748 const operand_pointer_ty = try sema.mod.simplePtrType(sema.arena, operand.ty.elemType().castTag(.error_union).?.data.payload, !operand.ty.isConstPtr(), .One);
1749
1750 if (operand.value()) |pointer_val| {
1751 const val = try pointer_val.pointerDeref(sema.arena);
1752 if (val.getError()) |name| {
1753 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
1754 }
1755 const data = val.castTag(.error_union).?.data;
1756 // The same Value represents the pointer to the error union and the payload.
1757 return sema.mod.constInst(sema.arena, src, .{
1758 .ty = operand_pointer_ty,
1759 .val = try Value.Tag.ref_val.create(
1760 sema.arena,
1761 data,
1762 ),
1763 });
1764 }
1765
1766 try sema.requireRuntimeBlock(block, src);
1767 if (safety_check and block.wantSafety()) {
1768 const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand);
1769 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
1770 }
1771 return block.addUnOp(src, operand_pointer_ty, .unwrap_errunion_payload_ptr, operand);
1772}
1773
1774/// Value in, value out
1775fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1776 const tracy = trace(@src());
1777 defer tracy.end();
1778
1779 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1780 const src = inst_data.src();
1781 const operand = try sema.resolveInst(inst_data.operand);
1782 if (operand.ty.zigTypeTag() != .ErrorUnion)
1783 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});
1784
1785 if (operand.value()) |val| {
1786 assert(val.getError() != null);
1787 const data = val.castTag(.error_union).?.data;
1788 return sema.mod.constInst(sema.arena, src, .{
1789 .ty = operand.ty.castTag(.error_union).?.data.error_set,
1790 .val = data,
1791 });
1792 }
1793
1794 try sema.requireRuntimeBlock(block, src);
1795 return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand);
1796}
1797
1798/// Pointer in, value out
1799fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1800 const tracy = trace(@src());
1801 defer tracy.end();
1802
1803 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1804 const src = inst_data.src();
1805 const operand = try sema.resolveInst(inst_data.operand);
1806 assert(operand.ty.zigTypeTag() == .Pointer);
1807
1808 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
1809 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});
1810
1811 if (operand.value()) |pointer_val| {
1812 const val = try pointer_val.pointerDeref(sema.arena);
1813 assert(val.getError() != null);
1814 const data = val.castTag(.error_union).?.data;
1815 return sema.mod.constInst(sema.arena, src, .{
1816 .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set,
1817 .val = data,
1818 });
1819 }
1820
1821 try sema.requireRuntimeBlock(block, src);
1822 return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand);
1823}
1824
1825fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
1826 const tracy = trace(@src());
1827 defer tracy.end();
1828
1829 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
1830 const src = inst_data.src();
1831 const operand = try sema.resolveInst(inst_data.operand);
1832 if (operand.ty.zigTypeTag() != .ErrorUnion)
1833 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});
1834 if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) {
1835 return sema.mod.fail(&block.base, src, "expression value is ignored", .{});
1836 }
1837}
1838
1839fn zirFnType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: bool) InnerError!*Inst {
1840 const tracy = trace(@src());
1841 defer tracy.end();
1842
1843 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1844 const src = inst_data.src();
1845 const extra = sema.code.extraData(zir.Inst.FnType, inst_data.payload_index);
1846 const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len);
1847
1848 return sema.fnTypeCommon(
1849 block,
1850 inst_data.src_node,
1851 param_types,
1852 extra.data.return_type,
1853 .Unspecified,
1854 var_args,
1855 );
1856}
1857
1858fn zirFnTypeCc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: bool) InnerError!*Inst {
1859 const tracy = trace(@src());
1860 defer tracy.end();
1861
1862 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1863 const src = inst_data.src();
1864 const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node };
1865 const extra = sema.code.extraData(zir.Inst.FnTypeCc, inst_data.payload_index);
1866 const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len);
1867
1868 const cc_tv = try sema.resolveInstConst(block, cc_src, extra.data.cc);
1869 // TODO once we're capable of importing and analyzing decls from
1870 // std.builtin, this needs to change
1871 const cc_str = cc_tv.val.castTag(.enum_literal).?.data;
1872 const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse
1873 return sema.mod.fail(&block.base, cc_src, "Unknown calling convention {s}", .{cc_str});
1874 return sema.fnTypeCommon(
1875 block,
1876 inst_data.src_node,
1877 param_types,
1878 extra.data.return_type,
1879 cc,
1880 var_args,
1881 );
1882}
1883
1884fn fnTypeCommon(
1885 sema: *Sema,
1886 block: *Scope.Block,
1887 src_node_offset: i32,
1888 zir_param_types: []const zir.Inst.Ref,
1889 zir_return_type: zir.Inst.Ref,
1890 cc: std.builtin.CallingConvention,
1891 var_args: bool,
1892) InnerError!*Inst {
1893 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
1894 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
1895 const return_type = try sema.resolveType(block, ret_ty_src, zir_return_type);
1896
1897 // Hot path for some common function types.
1898 if (zir_param_types.len == 0 and !var_args) {
1899 if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
1900 return sema.mod.constType(sema.arena, src, Type.initTag(.fn_noreturn_no_args));
1901 }
1902
1903 if (return_type.zigTypeTag() == .Void and cc == .Unspecified) {
1904 return sema.mod.constType(sema.arena, src, Type.initTag(.fn_void_no_args));
1905 }
1906
1907 if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
1908 return sema.mod.constType(sema.arena, src, Type.initTag(.fn_naked_noreturn_no_args));
1909 }
1910
1911 if (return_type.zigTypeTag() == .Void and cc == .C) {
1912 return sema.mod.constType(sema.arena, src, Type.initTag(.fn_ccc_void_no_args));
1913 }
1914 }
1915
1916 const param_types = try sema.arena.alloc(Type, zir_param_types.len);
1917 for (zir_param_types) |param_type, i| {
1918 // TODO make a compile error from `resolveType` report the source location
1919 // of the specific parameter. Will need to take a similar strategy as
1920 // `resolveSwitchItemVal` to avoid resolving the source location unless
1921 // we actually need to report an error.
1922 param_types[i] = try sema.resolveType(block, src, param_type);
1923 }
1924
1925 const fn_ty = try Type.Tag.function.create(sema.arena, .{
1926 .param_types = param_types,
1927 .return_type = return_type,
1928 .cc = cc,
1929 .is_var_args = var_args,
1930 });
1931 return sema.mod.constType(sema.arena, src, fn_ty);
1932}
1933
1934fn zirAs(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1935 const tracy = trace(@src());
1936 defer tracy.end();
1937
1938 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
1939 return sema.analyzeAs(block, .unneeded, bin_inst.lhs, bin_inst.rhs);
1940}
1941
1942fn zirAsNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1943 const tracy = trace(@src());
1944 defer tracy.end();
1945
1946 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1947 const src = inst_data.src();
1948 const extra = sema.code.extraData(zir.Inst.As, inst_data.payload_index).data;
1949 return sema.analyzeAs(block, src, extra.dest_type, extra.operand);
1950}
1951
1952fn analyzeAs(
1953 sema: *Sema,
1954 block: *Scope.Block,
1955 src: LazySrcLoc,
1956 zir_dest_type: zir.Inst.Ref,
1957 zir_operand: zir.Inst.Ref,
1958) InnerError!*Inst {
1959 const dest_type = try sema.resolveType(block, src, zir_dest_type);
1960 const operand = try sema.resolveInst(zir_operand);
1961 return sema.coerce(block, dest_type, operand, src);
1962}
1963
1964fn zirPtrtoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1965 const tracy = trace(@src());
1966 defer tracy.end();
1967
1968 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1969 const ptr = try sema.resolveInst(inst_data.operand);
1970 if (ptr.ty.zigTypeTag() != .Pointer) {
1971 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1972 return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty});
1973 }
1974 // TODO handle known-pointer-address
1975 const src = inst_data.src();
1976 try sema.requireRuntimeBlock(block, src);
1977 const ty = Type.initTag(.usize);
1978 return block.addUnOp(src, ty, .ptrtoint, ptr);
1979}
1980
1981fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1982 const tracy = trace(@src());
1983 defer tracy.end();
1984
1985 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1986 const src = inst_data.src();
1987 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
1988 const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data;
1989 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
1990 const object = try sema.resolveInst(extra.lhs);
1991 const object_ptr = try sema.analyzeRef(block, src, object);
1992 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
1993 return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
1994}
1995
1996fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1997 const tracy = trace(@src());
1998 defer tracy.end();
1999
2000 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2001 const src = inst_data.src();
2002 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
2003 const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data;
2004 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
2005 const object_ptr = try sema.resolveInst(extra.lhs);
2006 return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
2007}
2008
2009fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) 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 field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2016 const extra = sema.code.extraData(zir.Inst.FieldNamed, inst_data.payload_index).data;
2017 const object = try sema.resolveInst(extra.lhs);
2018 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
2019 const object_ptr = try sema.analyzeRef(block, src, object);
2020 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
2021 return sema.analyzeLoad(block, src, result_ptr, src);
2022}
2023
2024fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2025 const tracy = trace(@src());
2026 defer tracy.end();
2027
2028 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2029 const src = inst_data.src();
2030 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2031 const extra = sema.code.extraData(zir.Inst.FieldNamed, inst_data.payload_index).data;
2032 const object_ptr = try sema.resolveInst(extra.lhs);
2033 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
2034 return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
2035}
2036
2037fn zirIntcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2038 const tracy = trace(@src());
2039 defer tracy.end();
2040
2041 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2042 const src = inst_data.src();
2043 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2044 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2045 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2046
2047 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
2048 const operand = try sema.resolveInst(extra.rhs);
2049
2050 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
2051 .ComptimeInt => true,
2052 .Int => false,
2053 else => return sema.mod.fail(
2054 &block.base,
2055 dest_ty_src,
2056 "expected integer type, found '{}'",
2057 .{dest_type},
2058 ),
2059 };
2060
2061 switch (operand.ty.zigTypeTag()) {
2062 .ComptimeInt, .Int => {},
2063 else => return sema.mod.fail(
2064 &block.base,
2065 operand_src,
2066 "expected integer type, found '{}'",
2067 .{operand.ty},
2068 ),
2069 }
2070
2071 if (operand.value() != null) {
2072 return sema.coerce(block, dest_type, operand, operand_src);
2073 } else if (dest_is_comptime_int) {
2074 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_int'", .{});
2075 }
2076
2077 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten int", .{});
2078}
2079
2080fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2081 const tracy = trace(@src());
2082 defer tracy.end();
2083
2084 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2085 const src = inst_data.src();
2086 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2087 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2088 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2089
2090 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
2091 const operand = try sema.resolveInst(extra.rhs);
2092 return sema.bitcast(block, dest_type, operand);
2093}
2094
2095fn zirFloatcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2096 const tracy = trace(@src());
2097 defer tracy.end();
2098
2099 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2100 const src = inst_data.src();
2101 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2102 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2103 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2104
2105 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
2106 const operand = try sema.resolveInst(extra.rhs);
2107
2108 const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
2109 .ComptimeFloat => true,
2110 .Float => false,
2111 else => return sema.mod.fail(
2112 &block.base,
2113 dest_ty_src,
2114 "expected float type, found '{}'",
2115 .{dest_type},
2116 ),
2117 };
2118
2119 switch (operand.ty.zigTypeTag()) {
2120 .ComptimeFloat, .Float, .ComptimeInt => {},
2121 else => return sema.mod.fail(
2122 &block.base,
2123 operand_src,
2124 "expected float type, found '{}'",
2125 .{operand.ty},
2126 ),
2127 }
2128
2129 if (operand.value() != null) {
2130 return sema.coerce(block, dest_type, operand, operand_src);
2131 } else if (dest_is_comptime_float) {
2132 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_float'", .{});
2133 }
2134
2135 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten float", .{});
2136}
2137
2138fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2139 const tracy = trace(@src());
2140 defer tracy.end();
2141
2142 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
2143 const array = try sema.resolveInst(bin_inst.lhs);
2144 const array_ptr = try sema.analyzeRef(block, sema.src, array);
2145 const elem_index = try sema.resolveInst(bin_inst.rhs);
2146 const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
2147 return sema.analyzeLoad(block, sema.src, result_ptr, sema.src);
2148}
2149
2150fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2151 const tracy = trace(@src());
2152 defer tracy.end();
2153
2154 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2155 const src = inst_data.src();
2156 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
2157 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2158 const array = try sema.resolveInst(extra.lhs);
2159 const array_ptr = try sema.analyzeRef(block, src, array);
2160 const elem_index = try sema.resolveInst(extra.rhs);
2161 const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
2162 return sema.analyzeLoad(block, src, result_ptr, src);
2163}
2164
2165fn zirElemPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2166 const tracy = trace(@src());
2167 defer tracy.end();
2168
2169 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
2170 const array_ptr = try sema.resolveInst(bin_inst.lhs);
2171 const elem_index = try sema.resolveInst(bin_inst.rhs);
2172 return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
2173}
2174
2175fn zirElemPtrNode(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 elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
2182 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2183 const array_ptr = try sema.resolveInst(extra.lhs);
2184 const elem_index = try sema.resolveInst(extra.rhs);
2185 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
2186}
2187
2188fn zirSliceStart(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 extra = sema.code.extraData(zir.Inst.SliceStart, inst_data.payload_index).data;
2195 const array_ptr = try sema.resolveInst(extra.lhs);
2196 const start = try sema.resolveInst(extra.start);
2197
2198 return sema.analyzeSlice(block, src, array_ptr, start, null, null, .unneeded);
2199}
2200
2201fn zirSliceEnd(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2202 const tracy = trace(@src());
2203 defer tracy.end();
2204
2205 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2206 const src = inst_data.src();
2207 const extra = sema.code.extraData(zir.Inst.SliceEnd, inst_data.payload_index).data;
2208 const array_ptr = try sema.resolveInst(extra.lhs);
2209 const start = try sema.resolveInst(extra.start);
2210 const end = try sema.resolveInst(extra.end);
2211
2212 return sema.analyzeSlice(block, src, array_ptr, start, end, null, .unneeded);
2213}
2214
2215fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2216 const tracy = trace(@src());
2217 defer tracy.end();
2218
2219 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2220 const src = inst_data.src();
2221 const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node };
2222 const extra = sema.code.extraData(zir.Inst.SliceSentinel, inst_data.payload_index).data;
2223 const array_ptr = try sema.resolveInst(extra.lhs);
2224 const start = try sema.resolveInst(extra.start);
2225 const end = try sema.resolveInst(extra.end);
2226 const sentinel = try sema.resolveInst(extra.sentinel);
2227
2228 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
2229}
2230
2231fn zirSwitchCapture(
2232 sema: *Sema,
2233 block: *Scope.Block,
2234 inst: zir.Inst.Index,
2235 is_multi: bool,
2236 is_ref: bool,
2237) InnerError!*Inst {
2238 const tracy = trace(@src());
2239 defer tracy.end();
2240
2241 const zir_datas = sema.code.instructions.items(.data);
2242 const capture_info = zir_datas[inst].switch_capture;
2243 const switch_info = zir_datas[capture_info.switch_inst].pl_node;
2244 const src = switch_info.src();
2245
2246 return sema.mod.fail(&block.base, src, "TODO implement Sema for zirSwitchCapture", .{});
2247}
2248
2249fn zirSwitchCaptureElse(
2250 sema: *Sema,
2251 block: *Scope.Block,
2252 inst: zir.Inst.Index,
2253 is_ref: bool,
2254) InnerError!*Inst {
2255 const tracy = trace(@src());
2256 defer tracy.end();
2257
2258 const zir_datas = sema.code.instructions.items(.data);
2259 const capture_info = zir_datas[inst].switch_capture;
2260 const switch_info = zir_datas[capture_info.switch_inst].pl_node;
2261 const src = switch_info.src();
2262
2263 return sema.mod.fail(&block.base, src, "TODO implement Sema for zirSwitchCaptureElse", .{});
2264}
2265
2266fn zirSwitchBlock(
2267 sema: *Sema,
2268 block: *Scope.Block,
2269 inst: zir.Inst.Index,
2270 is_ref: bool,
2271 special_prong: zir.SpecialProng,
2272) InnerError!*Inst {
2273 const tracy = trace(@src());
2274 defer tracy.end();
2275
2276 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2277 const src = inst_data.src();
2278 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2279 const extra = sema.code.extraData(zir.Inst.SwitchBlock, inst_data.payload_index);
2280
2281 const operand_ptr = try sema.resolveInst(extra.data.operand);
2282 const operand = if (is_ref)
2283 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
2284 else
2285 operand_ptr;
2286
2287 return sema.analyzeSwitch(
2288 block,
2289 operand,
2290 extra.end,
2291 special_prong,
2292 extra.data.cases_len,
2293 0,
2294 inst,
2295 inst_data.src_node,
2296 );
2297}
2298
2299fn zirSwitchBlockMulti(
2300 sema: *Sema,
2301 block: *Scope.Block,
2302 inst: zir.Inst.Index,
2303 is_ref: bool,
2304 special_prong: zir.SpecialProng,
2305) InnerError!*Inst {
2306 const tracy = trace(@src());
2307 defer tracy.end();
2308
2309 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2310 const src = inst_data.src();
2311 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2312 const extra = sema.code.extraData(zir.Inst.SwitchBlockMulti, inst_data.payload_index);
2313
2314 const operand_ptr = try sema.resolveInst(extra.data.operand);
2315 const operand = if (is_ref)
2316 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
2317 else
2318 operand_ptr;
2319
2320 return sema.analyzeSwitch(
2321 block,
2322 operand,
2323 extra.end,
2324 special_prong,
2325 extra.data.scalar_cases_len,
2326 extra.data.multi_cases_len,
2327 inst,
2328 inst_data.src_node,
2329 );
2330}
2331
2332fn analyzeSwitch(
2333 sema: *Sema,
2334 block: *Scope.Block,
2335 operand: *Inst,
2336 extra_end: usize,
2337 special_prong: zir.SpecialProng,
2338 scalar_cases_len: usize,
2339 multi_cases_len: usize,
2340 switch_inst: zir.Inst.Index,
2341 src_node_offset: i32,
2342) InnerError!*Inst {
2343 const gpa = sema.gpa;
2344 const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {
2345 .none => .{ .body = &.{}, .end = extra_end },
2346 .under, .@"else" => blk: {
2347 const body_len = sema.code.extra[extra_end];
2348 const extra_body_start = extra_end + 1;
2349 break :blk .{
2350 .body = sema.code.extra[extra_body_start..][0..body_len],
2351 .end = extra_body_start + body_len,
2352 };
2353 },
2354 };
2355
2356 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
2357 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
2358 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
2359
2360 // Validate usage of '_' prongs.
2361 if (special_prong == .under and !operand.ty.isExhaustiveEnum()) {
2362 const msg = msg: {
2363 const msg = try sema.mod.errMsg(
2364 &block.base,
2365 src,
2366 "'_' prong only allowed when switching on non-exhaustive enums",
2367 .{},
2368 );
2369 errdefer msg.destroy(gpa);
2370 try sema.mod.errNote(
2371 &block.base,
2372 special_prong_src,
2373 msg,
2374 "'_' prong here",
2375 .{},
2376 );
2377 break :msg msg;
2378 };
2379 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
2380 }
2381
2382 // Validate for duplicate items, missing else prong, and invalid range.
2383 switch (operand.ty.zigTypeTag()) {
2384 .Enum => return sema.mod.fail(&block.base, src, "TODO validate switch .Enum", .{}),
2385 .ErrorSet => return sema.mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}),
2386 .Union => return sema.mod.fail(&block.base, src, "TODO validate switch .Union", .{}),
2387 .Int, .ComptimeInt => {
2388 var range_set = RangeSet.init(gpa);
2389 defer range_set.deinit();
2390
2391 var extra_index: usize = special.end;
2392 {
2393 var scalar_i: u32 = 0;
2394 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2395 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2396 extra_index += 1;
2397 const body_len = sema.code.extra[extra_index];
2398 extra_index += 1;
2399 const body = sema.code.extra[extra_index..][0..body_len];
2400 extra_index += body_len;
2401
2402 try sema.validateSwitchItem(
2403 block,
2404 &range_set,
2405 item_ref,
2406 src_node_offset,
2407 .{ .scalar = scalar_i },
2408 );
2409 }
2410 }
2411 {
2412 var multi_i: u32 = 0;
2413 while (multi_i < multi_cases_len) : (multi_i += 1) {
2414 const items_len = sema.code.extra[extra_index];
2415 extra_index += 1;
2416 const ranges_len = sema.code.extra[extra_index];
2417 extra_index += 1;
2418 const body_len = sema.code.extra[extra_index];
2419 extra_index += 1;
2420 const items = sema.code.refSlice(extra_index, items_len);
2421 extra_index += items_len;
2422
2423 for (items) |item_ref, item_i| {
2424 try sema.validateSwitchItem(
2425 block,
2426 &range_set,
2427 item_ref,
2428 src_node_offset,
2429 .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
2430 );
2431 }
2432
2433 var range_i: u32 = 0;
2434 while (range_i < ranges_len) : (range_i += 1) {
2435 const item_first = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2436 extra_index += 1;
2437 const item_last = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2438 extra_index += 1;
2439
2440 try sema.validateSwitchRange(
2441 block,
2442 &range_set,
2443 item_first,
2444 item_last,
2445 src_node_offset,
2446 .{ .range = .{ .prong = multi_i, .item = range_i } },
2447 );
2448 }
2449
2450 extra_index += body_len;
2451 }
2452 }
2453
2454 check_range: {
2455 if (operand.ty.zigTypeTag() == .Int) {
2456 var arena = std.heap.ArenaAllocator.init(gpa);
2457 defer arena.deinit();
2458
2459 const min_int = try operand.ty.minInt(&arena, sema.mod.getTarget());
2460 const max_int = try operand.ty.maxInt(&arena, sema.mod.getTarget());
2461 if (try range_set.spans(min_int, max_int)) {
2462 if (special_prong == .@"else") {
2463 return sema.mod.fail(
2464 &block.base,
2465 special_prong_src,
2466 "unreachable else prong; all cases already handled",
2467 .{},
2468 );
2469 }
2470 break :check_range;
2471 }
2472 }
2473 if (special_prong != .@"else") {
2474 return sema.mod.fail(
2475 &block.base,
2476 src,
2477 "switch must handle all possibilities",
2478 .{},
2479 );
2480 }
2481 }
2482 },
2483 .Bool => {
2484 var true_count: u8 = 0;
2485 var false_count: u8 = 0;
2486
2487 var extra_index: usize = special.end;
2488 {
2489 var scalar_i: u32 = 0;
2490 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2491 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2492 extra_index += 1;
2493 const body_len = sema.code.extra[extra_index];
2494 extra_index += 1;
2495 const body = sema.code.extra[extra_index..][0..body_len];
2496 extra_index += body_len;
2497
2498 try sema.validateSwitchItemBool(
2499 block,
2500 &true_count,
2501 &false_count,
2502 item_ref,
2503 src_node_offset,
2504 .{ .scalar = scalar_i },
2505 );
2506 }
2507 }
2508 {
2509 var multi_i: u32 = 0;
2510 while (multi_i < multi_cases_len) : (multi_i += 1) {
2511 const items_len = sema.code.extra[extra_index];
2512 extra_index += 1;
2513 const ranges_len = sema.code.extra[extra_index];
2514 extra_index += 1;
2515 const body_len = sema.code.extra[extra_index];
2516 extra_index += 1;
2517 const items = sema.code.refSlice(extra_index, items_len);
2518 extra_index += items_len + body_len;
2519
2520 for (items) |item_ref, item_i| {
2521 try sema.validateSwitchItemBool(
2522 block,
2523 &true_count,
2524 &false_count,
2525 item_ref,
2526 src_node_offset,
2527 .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
2528 );
2529 }
2530
2531 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
2532 }
2533 }
2534 switch (special_prong) {
2535 .@"else" => {
2536 if (true_count + false_count == 2) {
2537 return sema.mod.fail(
2538 &block.base,
2539 src,
2540 "unreachable else prong; all cases already handled",
2541 .{},
2542 );
2543 }
2544 },
2545 .under, .none => {
2546 if (true_count + false_count < 2) {
2547 return sema.mod.fail(
2548 &block.base,
2549 src,
2550 "switch must handle all possibilities",
2551 .{},
2552 );
2553 }
2554 },
2555 }
2556 },
2557 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
2558 if (special_prong != .@"else") {
2559 return sema.mod.fail(
2560 &block.base,
2561 src,
2562 "else prong required when switching on type '{}'",
2563 .{operand.ty},
2564 );
2565 }
2566
2567 var seen_values = ValueSrcMap.init(gpa);
2568 defer seen_values.deinit();
2569
2570 var extra_index: usize = special.end;
2571 {
2572 var scalar_i: u32 = 0;
2573 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2574 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2575 extra_index += 1;
2576 const body_len = sema.code.extra[extra_index];
2577 extra_index += 1;
2578 const body = sema.code.extra[extra_index..][0..body_len];
2579 extra_index += body_len;
2580
2581 try sema.validateSwitchItemSparse(
2582 block,
2583 &seen_values,
2584 item_ref,
2585 src_node_offset,
2586 .{ .scalar = scalar_i },
2587 );
2588 }
2589 }
2590 {
2591 var multi_i: u32 = 0;
2592 while (multi_i < multi_cases_len) : (multi_i += 1) {
2593 const items_len = sema.code.extra[extra_index];
2594 extra_index += 1;
2595 const ranges_len = sema.code.extra[extra_index];
2596 extra_index += 1;
2597 const body_len = sema.code.extra[extra_index];
2598 extra_index += 1;
2599 const items = sema.code.refSlice(extra_index, items_len);
2600 extra_index += items_len + body_len;
2601
2602 for (items) |item_ref, item_i| {
2603 try sema.validateSwitchItemSparse(
2604 block,
2605 &seen_values,
2606 item_ref,
2607 src_node_offset,
2608 .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
2609 );
2610 }
2611
2612 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
2613 }
2614 }
2615 },
2616
2617 .ErrorUnion,
2618 .NoReturn,
2619 .Array,
2620 .Struct,
2621 .Undefined,
2622 .Null,
2623 .Optional,
2624 .BoundFn,
2625 .Opaque,
2626 .Vector,
2627 .Frame,
2628 .AnyFrame,
2629 .ComptimeFloat,
2630 .Float,
2631 => return sema.mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{
2632 operand.ty,
2633 }),
2634 }
2635
2636 if (try sema.resolveDefinedValue(block, src, operand)) |operand_val| {
2637 var extra_index: usize = special.end;
2638 {
2639 var scalar_i: usize = 0;
2640 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2641 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2642 extra_index += 1;
2643 const body_len = sema.code.extra[extra_index];
2644 extra_index += 1;
2645 const body = sema.code.extra[extra_index..][0..body_len];
2646 extra_index += body_len;
2647
2648 // Validation above ensured these will succeed.
2649 const item = sema.resolveInst(item_ref) catch unreachable;
2650 const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable;
2651 if (operand_val.eql(item_val)) {
2652 return sema.resolveBody(block, body);
2653 }
2654 }
2655 }
2656 {
2657 var multi_i: usize = 0;
2658 while (multi_i < multi_cases_len) : (multi_i += 1) {
2659 const items_len = sema.code.extra[extra_index];
2660 extra_index += 1;
2661 const ranges_len = sema.code.extra[extra_index];
2662 extra_index += 1;
2663 const body_len = sema.code.extra[extra_index];
2664 extra_index += 1;
2665 const items = sema.code.refSlice(extra_index, items_len);
2666 extra_index += items_len;
2667 const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len];
2668
2669 for (items) |item_ref| {
2670 // Validation above ensured these will succeed.
2671 const item = sema.resolveInst(item_ref) catch unreachable;
2672 const item_val = sema.resolveConstValue(block, item.src, item) catch unreachable;
2673 if (operand_val.eql(item_val)) {
2674 return sema.resolveBody(block, body);
2675 }
2676 }
2677
2678 var range_i: usize = 0;
2679 while (range_i < ranges_len) : (range_i += 1) {
2680 const item_first = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2681 extra_index += 1;
2682 const item_last = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2683 extra_index += 1;
2684
2685 // Validation above ensured these will succeed.
2686 const first_tv = sema.resolveInstConst(block, .unneeded, item_first) catch unreachable;
2687 const last_tv = sema.resolveInstConst(block, .unneeded, item_last) catch unreachable;
2688 if (Value.compare(operand_val, .gte, first_tv.val) and
2689 Value.compare(operand_val, .lte, last_tv.val))
2690 {
2691 return sema.resolveBody(block, body);
2692 }
2693 }
2694
2695 extra_index += body_len;
2696 }
2697 }
2698 return sema.resolveBody(block, special.body);
2699 }
2700
2701 if (scalar_cases_len + multi_cases_len == 0) {
2702 return sema.resolveBody(block, special.body);
2703 }
2704
2705 try sema.requireRuntimeBlock(block, src);
2706
2707 const block_inst = try sema.arena.create(Inst.Block);
2708 block_inst.* = .{
2709 .base = .{
2710 .tag = Inst.Block.base_tag,
2711 .ty = undefined, // Set after analysis.
2712 .src = src,
2713 },
2714 .body = undefined,
2715 };
2716
2717 var child_block: Scope.Block = .{
2718 .parent = block,
2719 .sema = sema,
2720 .src_decl = block.src_decl,
2721 .instructions = .{},
2722 // TODO @as here is working around a stage1 miscompilation bug :(
2723 .label = @as(?Scope.Block.Label, Scope.Block.Label{
2724 .zir_block = switch_inst,
2725 .merges = .{
2726 .results = .{},
2727 .br_list = .{},
2728 .block_inst = block_inst,
2729 },
2730 }),
2731 .inlining = block.inlining,
2732 .is_comptime = block.is_comptime,
2733 };
2734 const merges = &child_block.label.?.merges;
2735 defer child_block.instructions.deinit(gpa);
2736 defer merges.results.deinit(gpa);
2737 defer merges.br_list.deinit(gpa);
2738
2739 // TODO when reworking TZIR memory layout make multi cases get generated as cases,
2740 // not as part of the "else" block.
2741 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
2742
2743 var case_block = child_block.makeSubBlock();
2744 defer case_block.instructions.deinit(gpa);
2745
2746 var extra_index: usize = special.end;
2747
2748 var scalar_i: usize = 0;
2749 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2750 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2751 extra_index += 1;
2752 const body_len = sema.code.extra[extra_index];
2753 extra_index += 1;
2754 const body = sema.code.extra[extra_index..][0..body_len];
2755 extra_index += body_len;
2756
2757 case_block.instructions.shrinkRetainingCapacity(0);
2758 // We validate these above; these two calls are guaranteed to succeed.
2759 const item = sema.resolveInst(item_ref) catch unreachable;
2760 const item_val = sema.resolveConstValue(&case_block, .unneeded, item) catch unreachable;
2761
2762 _ = try sema.analyzeBody(&case_block, body);
2763
2764 cases[scalar_i] = .{
2765 .item = item_val,
2766 .body = .{ .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items) },
2767 };
2768 }
2769
2770 var first_else_body: Body = undefined;
2771 var prev_condbr: ?*Inst.CondBr = null;
2772
2773 var multi_i: usize = 0;
2774 while (multi_i < multi_cases_len) : (multi_i += 1) {
2775 const items_len = sema.code.extra[extra_index];
2776 extra_index += 1;
2777 const ranges_len = sema.code.extra[extra_index];
2778 extra_index += 1;
2779 const body_len = sema.code.extra[extra_index];
2780 extra_index += 1;
2781 const items = sema.code.refSlice(extra_index, items_len);
2782 extra_index += items_len;
2783
2784 case_block.instructions.shrinkRetainingCapacity(0);
2785
2786 var any_ok: ?*Inst = null;
2787 const bool_ty = comptime Type.initTag(.bool);
2788
2789 for (items) |item_ref| {
2790 const item = try sema.resolveInst(item_ref);
2791 _ = try sema.resolveConstValue(&child_block, item.src, item);
2792
2793 const cmp_ok = try case_block.addBinOp(item.src, bool_ty, .cmp_eq, operand, item);
2794 if (any_ok) |some| {
2795 any_ok = try case_block.addBinOp(item.src, bool_ty, .bool_or, some, cmp_ok);
2796 } else {
2797 any_ok = cmp_ok;
2798 }
2799 }
2800
2801 var range_i: usize = 0;
2802 while (range_i < ranges_len) : (range_i += 1) {
2803 const first_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2804 extra_index += 1;
2805 const last_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2806 extra_index += 1;
2807
2808 const item_first = try sema.resolveInst(first_ref);
2809 const item_last = try sema.resolveInst(last_ref);
2810
2811 _ = try sema.resolveConstValue(&child_block, item_first.src, item_first);
2812 _ = try sema.resolveConstValue(&child_block, item_last.src, item_last);
2813
2814 const range_src = item_first.src;
2815
2816 // operand >= first and operand <= last
2817 const range_first_ok = try case_block.addBinOp(
2818 item_first.src,
2819 bool_ty,
2820 .cmp_gte,
2821 operand,
2822 item_first,
2823 );
2824 const range_last_ok = try case_block.addBinOp(
2825 item_last.src,
2826 bool_ty,
2827 .cmp_lte,
2828 operand,
2829 item_last,
2830 );
2831 const range_ok = try case_block.addBinOp(
2832 range_src,
2833 bool_ty,
2834 .bool_and,
2835 range_first_ok,
2836 range_last_ok,
2837 );
2838 if (any_ok) |some| {
2839 any_ok = try case_block.addBinOp(range_src, bool_ty, .bool_or, some, range_ok);
2840 } else {
2841 any_ok = range_ok;
2842 }
2843 }
2844
2845 const new_condbr = try sema.arena.create(Inst.CondBr);
2846 new_condbr.* = .{
2847 .base = .{
2848 .tag = .condbr,
2849 .ty = Type.initTag(.noreturn),
2850 .src = src,
2851 },
2852 .condition = any_ok.?,
2853 .then_body = undefined,
2854 .else_body = undefined,
2855 };
2856 try case_block.instructions.append(gpa, &new_condbr.base);
2857
2858 const cond_body: Body = .{
2859 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
2860 };
2861
2862 case_block.instructions.shrinkRetainingCapacity(0);
2863 const body = sema.code.extra[extra_index..][0..body_len];
2864 extra_index += body_len;
2865 _ = try sema.analyzeBody(&case_block, body);
2866 new_condbr.then_body = .{
2867 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
2868 };
2869 if (prev_condbr) |condbr| {
2870 condbr.else_body = cond_body;
2871 } else {
2872 first_else_body = cond_body;
2873 }
2874 prev_condbr = new_condbr;
2875 }
2876
2877 const final_else_body: Body = blk: {
2878 if (special.body.len != 0) {
2879 case_block.instructions.shrinkRetainingCapacity(0);
2880 _ = try sema.analyzeBody(&case_block, special.body);
2881 const else_body: Body = .{
2882 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
2883 };
2884 if (prev_condbr) |condbr| {
2885 condbr.else_body = else_body;
2886 break :blk first_else_body;
2887 } else {
2888 break :blk else_body;
2889 }
2890 } else {
2891 break :blk .{ .instructions = &.{} };
2892 }
2893 };
2894
2895 _ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
2896 return sema.analyzeBlockBody(block, src, &child_block, merges);
2897}
2898
2899fn resolveSwitchItemVal(
2900 sema: *Sema,
2901 block: *Scope.Block,
2902 item_ref: zir.Inst.Ref,
2903 switch_node_offset: i32,
2904 switch_prong_src: AstGen.SwitchProngSrc,
2905 range_expand: AstGen.SwitchProngSrc.RangeExpand,
2906) InnerError!Value {
2907 const item = try sema.resolveInst(item_ref);
2908 // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc
2909 // because we only have the switch AST node. Only if we know for sure we need to report
2910 // a compile error do we resolve the full source locations.
2911 if (item.value()) |val| {
2912 if (val.isUndef()) {
2913 const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand);
2914 return sema.failWithUseOfUndef(block, src);
2915 }
2916 return val;
2917 }
2918 const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand);
2919 return sema.failWithNeededComptime(block, src);
2920}
2921
2922fn validateSwitchRange(
2923 sema: *Sema,
2924 block: *Scope.Block,
2925 range_set: *RangeSet,
2926 first_ref: zir.Inst.Ref,
2927 last_ref: zir.Inst.Ref,
2928 src_node_offset: i32,
2929 switch_prong_src: AstGen.SwitchProngSrc,
2930) InnerError!void {
2931 const first_val = try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first);
2932 const last_val = try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last);
2933 const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src);
2934 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
2935}
2936
2937fn validateSwitchItem(
2938 sema: *Sema,
2939 block: *Scope.Block,
2940 range_set: *RangeSet,
2941 item_ref: zir.Inst.Ref,
2942 src_node_offset: i32,
2943 switch_prong_src: AstGen.SwitchProngSrc,
2944) InnerError!void {
2945 const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
2946 const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src);
2947 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
2948}
2949
2950fn validateSwitchDupe(
2951 sema: *Sema,
2952 block: *Scope.Block,
2953 maybe_prev_src: ?AstGen.SwitchProngSrc,
2954 switch_prong_src: AstGen.SwitchProngSrc,
2955 src_node_offset: i32,
2956) InnerError!void {
2957 const prev_prong_src = maybe_prev_src orelse return;
2958 const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none);
2959 const prev_src = prev_prong_src.resolve(block.src_decl, src_node_offset, .none);
2960 const msg = msg: {
2961 const msg = try sema.mod.errMsg(
2962 &block.base,
2963 src,
2964 "duplicate switch value",
2965 .{},
2966 );
2967 errdefer msg.destroy(sema.gpa);
2968 try sema.mod.errNote(
2969 &block.base,
2970 prev_src,
2971 msg,
2972 "previous value here",
2973 .{},
2974 );
2975 break :msg msg;
2976 };
2977 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
2978}
2979
2980fn validateSwitchItemBool(
2981 sema: *Sema,
2982 block: *Scope.Block,
2983 true_count: *u8,
2984 false_count: *u8,
2985 item_ref: zir.Inst.Ref,
2986 src_node_offset: i32,
2987 switch_prong_src: AstGen.SwitchProngSrc,
2988) InnerError!void {
2989 const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
2990 if (item_val.toBool()) {
2991 true_count.* += 1;
2992 } else {
2993 false_count.* += 1;
2994 }
2995 if (true_count.* + false_count.* > 2) {
2996 const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none);
2997 return sema.mod.fail(&block.base, src, "duplicate switch value", .{});
2998 }
2999}
3000
3001const ValueSrcMap = std.HashMap(Value, AstGen.SwitchProngSrc, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage);
3002
3003fn validateSwitchItemSparse(
3004 sema: *Sema,
3005 block: *Scope.Block,
3006 seen_values: *ValueSrcMap,
3007 item_ref: zir.Inst.Ref,
3008 src_node_offset: i32,
3009 switch_prong_src: AstGen.SwitchProngSrc,
3010) InnerError!void {
3011 const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
3012 const entry = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return;
3013 return sema.validateSwitchDupe(block, entry.value, switch_prong_src, src_node_offset);
3014}
3015
3016fn validateSwitchNoRange(
3017 sema: *Sema,
3018 block: *Scope.Block,
3019 ranges_len: u32,
3020 operand_ty: Type,
3021 src_node_offset: i32,
3022) InnerError!void {
3023 if (ranges_len == 0)
3024 return;
3025
3026 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
3027 const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset };
3028
3029 const msg = msg: {
3030 const msg = try sema.mod.errMsg(
3031 &block.base,
3032 operand_src,
3033 "ranges not allowed when switching on type '{}'",
3034 .{operand_ty},
3035 );
3036 errdefer msg.destroy(sema.gpa);
3037 try sema.mod.errNote(
3038 &block.base,
3039 range_src,
3040 msg,
3041 "range here",
3042 .{},
3043 );
3044 break :msg msg;
3045 };
3046 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
3047}
3048
3049fn zirImport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3050 const tracy = trace(@src());
3051 defer tracy.end();
3052
3053 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3054 const src = inst_data.src();
3055 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
3056 const operand = try sema.resolveConstString(block, operand_src, inst_data.operand);
3057
3058 const file_scope = sema.analyzeImport(block, src, operand) catch |err| switch (err) {
3059 error.ImportOutsidePkgPath => {
3060 return sema.mod.fail(&block.base, src, "import of file outside package path: '{s}'", .{operand});
3061 },
3062 error.FileNotFound => {
3063 return sema.mod.fail(&block.base, src, "unable to find '{s}'", .{operand});
3064 },
3065 else => {
3066 // TODO: make sure this gets retried and not cached
3067 return sema.mod.fail(&block.base, src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });
3068 },
3069 };
3070 return sema.mod.constType(sema.arena, src, file_scope.root_container.ty);
3071}
3072
3073fn zirShl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3074 const tracy = trace(@src());
3075 defer tracy.end();
3076 return sema.mod.fail(&block.base, sema.src, "TODO implement zirShl", .{});
3077}
3078
3079fn zirShr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3080 const tracy = trace(@src());
3081 defer tracy.end();
3082 return sema.mod.fail(&block.base, sema.src, "TODO implement zirShr", .{});
3083}
3084
3085fn zirBitwise(
3086 sema: *Sema,
3087 block: *Scope.Block,
3088 inst: zir.Inst.Index,
3089 ir_tag: ir.Inst.Tag,
3090) InnerError!*Inst {
3091 const tracy = trace(@src());
3092 defer tracy.end();
3093
3094 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3095 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
3096 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
3097 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
3098 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
3099 const lhs = try sema.resolveInst(extra.lhs);
3100 const rhs = try sema.resolveInst(extra.rhs);
3101
3102 const instructions = &[_]*Inst{ lhs, rhs };
3103 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
3104 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
3105 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
3106
3107 const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
3108 resolved_type.elemType()
3109 else
3110 resolved_type;
3111
3112 const scalar_tag = scalar_type.zigTypeTag();
3113
3114 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
3115 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
3116 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
3117 lhs.ty.arrayLen(),
3118 rhs.ty.arrayLen(),
3119 });
3120 }
3121 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBitwise", .{});
3122 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
3123 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
3124 lhs.ty,
3125 rhs.ty,
3126 });
3127 }
3128
3129 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
3130
3131 if (!is_int) {
3132 return sema.mod.fail(&block.base, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
3133 }
3134
3135 if (casted_lhs.value()) |lhs_val| {
3136 if (casted_rhs.value()) |rhs_val| {
3137 if (lhs_val.isUndef() or rhs_val.isUndef()) {
3138 return sema.mod.constInst(sema.arena, src, .{
3139 .ty = resolved_type,
3140 .val = Value.initTag(.undef),
3141 });
3142 }
3143 return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});
3144 }
3145 }
3146
3147 try sema.requireRuntimeBlock(block, src);
3148 return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);
3149}
3150
3151fn zirBitNot(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3152 const tracy = trace(@src());
3153 defer tracy.end();
3154 return sema.mod.fail(&block.base, sema.src, "TODO implement zirBitNot", .{});
3155}
3156
3157fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3158 const tracy = trace(@src());
3159 defer tracy.end();
3160 return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayCat", .{});
3161}
3162
3163fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3164 const tracy = trace(@src());
3165 defer tracy.end();
3166 return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayMul", .{});
3167}
3168
3169fn zirNegate(
3170 sema: *Sema,
3171 block: *Scope.Block,
3172 inst: zir.Inst.Index,
3173 tag_override: zir.Inst.Tag,
3174) InnerError!*Inst {
3175 const tracy = trace(@src());
3176 defer tracy.end();
3177
3178 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3179 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
3180 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
3181 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
3182 const lhs = try sema.resolveInst(.zero);
3183 const rhs = try sema.resolveInst(inst_data.operand);
3184
3185 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
3186}
3187
3188fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3189 const tracy = trace(@src());
3190 defer tracy.end();
3191
3192 const tag_override = block.sema.code.instructions.items(.tag)[inst];
3193 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3194 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
3195 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
3196 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
3197 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
3198 const lhs = try sema.resolveInst(extra.lhs);
3199 const rhs = try sema.resolveInst(extra.rhs);
3200
3201 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
3202}
3203
3204fn analyzeArithmetic(
3205 sema: *Sema,
3206 block: *Scope.Block,
3207 zir_tag: zir.Inst.Tag,
3208 lhs: *Inst,
3209 rhs: *Inst,
3210 src: LazySrcLoc,
3211 lhs_src: LazySrcLoc,
3212 rhs_src: LazySrcLoc,
3213) InnerError!*Inst {
3214 const instructions = &[_]*Inst{ lhs, rhs };
3215 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
3216 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
3217 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
3218
3219 const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
3220 resolved_type.elemType()
3221 else
3222 resolved_type;
3223
3224 const scalar_tag = scalar_type.zigTypeTag();
3225
3226 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
3227 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
3228 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
3229 lhs.ty.arrayLen(),
3230 rhs.ty.arrayLen(),
3231 });
3232 }
3233 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBinOp", .{});
3234 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
3235 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
3236 lhs.ty,
3237 rhs.ty,
3238 });
3239 }
3240
3241 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
3242 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
3243
3244 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
3245 return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
3246 }
3247
3248 if (casted_lhs.value()) |lhs_val| {
3249 if (casted_rhs.value()) |rhs_val| {
3250 if (lhs_val.isUndef() or rhs_val.isUndef()) {
3251 return sema.mod.constInst(sema.arena, src, .{
3252 .ty = resolved_type,
3253 .val = Value.initTag(.undef),
3254 });
3255 }
3256 // incase rhs is 0, simply return lhs without doing any calculations
3257 // TODO Once division is implemented we should throw an error when dividing by 0.
3258 if (rhs_val.compareWithZero(.eq)) {
3259 return sema.mod.constInst(sema.arena, src, .{
3260 .ty = scalar_type,
3261 .val = lhs_val,
3262 });
3263 }
3264
3265 const value = switch (zir_tag) {
3266 .add => blk: {
3267 const val = if (is_int)
3268 try Module.intAdd(sema.arena, lhs_val, rhs_val)
3269 else
3270 try Module.floatAdd(sema.arena, scalar_type, src, lhs_val, rhs_val);
3271 break :blk val;
3272 },
3273 .sub => blk: {
3274 const val = if (is_int)
3275 try Module.intSub(sema.arena, lhs_val, rhs_val)
3276 else
3277 try Module.floatSub(sema.arena, scalar_type, src, lhs_val, rhs_val);
3278 break :blk val;
3279 },
3280 else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tag)}),
3281 };
3282
3283 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });
3284
3285 return sema.mod.constInst(sema.arena, src, .{
3286 .ty = scalar_type,
3287 .val = value,
3288 });
3289 }
3290 }
3291
3292 try sema.requireRuntimeBlock(block, src);
3293 const ir_tag: Inst.Tag = switch (zir_tag) {
3294 .add => .add,
3295 .addwrap => .addwrap,
3296 .sub => .sub,
3297 .subwrap => .subwrap,
3298 .mul => .mul,
3299 .mulwrap => .mulwrap,
3300 else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tag)}),
3301 };
3302
3303 return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);
3304}
3305
3306fn zirLoad(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3307 const tracy = trace(@src());
3308 defer tracy.end();
3309
3310 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3311 const src = inst_data.src();
3312 const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node };
3313 const ptr = try sema.resolveInst(inst_data.operand);
3314 return sema.analyzeLoad(block, src, ptr, ptr_src);
3315}
3316
3317fn zirAsm(
3318 sema: *Sema,
3319 block: *Scope.Block,
3320 inst: zir.Inst.Index,
3321 is_volatile: bool,
3322) InnerError!*Inst {
3323 const tracy = trace(@src());
3324 defer tracy.end();
3325
3326 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3327 const src = inst_data.src();
3328 const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = inst_data.src_node };
3329 const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = inst_data.src_node };
3330 const extra = sema.code.extraData(zir.Inst.Asm, inst_data.payload_index);
3331 const return_type = try sema.resolveType(block, ret_ty_src, extra.data.return_type);
3332 const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source);
3333
3334 var extra_i = extra.end;
3335 const Output = struct { name: []const u8, inst: *Inst };
3336 const output: ?Output = if (extra.data.output != .none) blk: {
3337 const name = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
3338 extra_i += 1;
3339 break :blk Output{
3340 .name = name,
3341 .inst = try sema.resolveInst(extra.data.output),
3342 };
3343 } else null;
3344
3345 const args = try sema.arena.alloc(*Inst, extra.data.args_len);
3346 const inputs = try sema.arena.alloc([]const u8, extra.data.args_len);
3347 const clobbers = try sema.arena.alloc([]const u8, extra.data.clobbers_len);
3348
3349 for (args) |*arg| {
3350 arg.* = try sema.resolveInst(@intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]));
3351 extra_i += 1;
3352 }
3353 for (inputs) |*name| {
3354 name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
3355 extra_i += 1;
3356 }
3357 for (clobbers) |*name| {
3358 name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
3359 extra_i += 1;
3360 }
3361
3362 try sema.requireRuntimeBlock(block, src);
3363 const asm_tzir = try sema.arena.create(Inst.Assembly);
3364 asm_tzir.* = .{
3365 .base = .{
3366 .tag = .assembly,
3367 .ty = return_type,
3368 .src = src,
3369 },
3370 .asm_source = asm_source,
3371 .is_volatile = is_volatile,
3372 .output = if (output) |o| o.inst else null,
3373 .output_name = if (output) |o| o.name else null,
3374 .inputs = inputs,
3375 .clobbers = clobbers,
3376 .args = args,
3377 };
3378 try block.instructions.append(sema.gpa, &asm_tzir.base);
3379 return &asm_tzir.base;
3380}
3381
3382fn zirCmp(
3383 sema: *Sema,
3384 block: *Scope.Block,
3385 inst: zir.Inst.Index,
3386 op: std.math.CompareOperator,
3387) InnerError!*Inst {
3388 const tracy = trace(@src());
3389 defer tracy.end();
3390
3391 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3392 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
3393 const src: LazySrcLoc = inst_data.src();
3394 const lhs = try sema.resolveInst(extra.lhs);
3395 const rhs = try sema.resolveInst(extra.rhs);
3396
3397 const is_equality_cmp = switch (op) {
3398 .eq, .neq => true,
3399 else => false,
3400 };
3401 const lhs_ty_tag = lhs.ty.zigTypeTag();
3402 const rhs_ty_tag = rhs.ty.zigTypeTag();
3403 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
3404 // null == null, null != null
3405 return sema.mod.constBool(sema.arena, src, op == .eq);
3406 } else if (is_equality_cmp and
3407 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
3408 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
3409 {
3410 // comparing null with optionals
3411 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
3412 return sema.analyzeIsNull(block, src, opt_operand, op == .neq);
3413 } else if (is_equality_cmp and
3414 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
3415 {
3416 return sema.mod.fail(&block.base, src, "TODO implement C pointer cmp", .{});
3417 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
3418 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
3419 return sema.mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type});
3420 } else if (is_equality_cmp and
3421 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
3422 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
3423 {
3424 return sema.mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
3425 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
3426 if (!is_equality_cmp) {
3427 return sema.mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)});
3428 }
3429 if (rhs.value()) |rval| {
3430 if (lhs.value()) |lval| {
3431 // 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
3432 return sema.mod.constBool(sema.arena, src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq));
3433 }
3434 }
3435 try sema.requireRuntimeBlock(block, src);
3436 return block.addBinOp(src, Type.initTag(.bool), if (op == .eq) .cmp_eq else .cmp_neq, lhs, rhs);
3437 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
3438 // This operation allows any combination of integer and float types, regardless of the
3439 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
3440 // numeric types.
3441 return sema.cmpNumeric(block, src, lhs, rhs, op);
3442 } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {
3443 if (!is_equality_cmp) {
3444 return sema.mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)});
3445 }
3446 return sema.mod.constBool(sema.arena, src, lhs.value().?.eql(rhs.value().?) == (op == .eq));
3447 }
3448 return sema.mod.fail(&block.base, src, "TODO implement more cmp analysis", .{});
3449}
3450
3451fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3452 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3453 const src = inst_data.src();
3454 const operand = try sema.resolveInst(inst_data.operand);
3455 return sema.mod.constType(sema.arena, src, operand.ty);
3456}
3457
3458fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3459 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3460 const src = inst_data.src();
3461 const operand_ptr = try sema.resolveInst(inst_data.operand);
3462 const elem_ty = operand_ptr.ty.elemType();
3463 return sema.mod.constType(sema.arena, src, elem_ty);
3464}
3465
3466fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3467 const tracy = trace(@src());
3468 defer tracy.end();
3469
3470 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3471 const src = inst_data.src();
3472 const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index);
3473 const args = sema.code.refSlice(extra.end, extra.data.operands_len);
3474
3475 const inst_list = try sema.gpa.alloc(*ir.Inst, extra.data.operands_len);
3476 defer sema.gpa.free(inst_list);
3477
3478 for (args) |arg_ref, i| {
3479 inst_list[i] = try sema.resolveInst(arg_ref);
3480 }
3481
3482 const result_type = try sema.resolvePeerTypes(block, src, inst_list);
3483 return sema.mod.constType(sema.arena, src, result_type);
3484}
3485
3486fn zirBoolNot(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3487 const tracy = trace(@src());
3488 defer tracy.end();
3489
3490 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3491 const src = inst_data.src();
3492 const uncasted_operand = try sema.resolveInst(inst_data.operand);
3493
3494 const bool_type = Type.initTag(.bool);
3495 const operand = try sema.coerce(block, bool_type, uncasted_operand, uncasted_operand.src);
3496 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
3497 return sema.mod.constBool(sema.arena, src, !val.toBool());
3498 }
3499 try sema.requireRuntimeBlock(block, src);
3500 return block.addUnOp(src, bool_type, .not, operand);
3501}
3502
3503fn zirBoolOp(
3504 sema: *Sema,
3505 block: *Scope.Block,
3506 inst: zir.Inst.Index,
3507 comptime is_bool_or: bool,
3508) InnerError!*Inst {
3509 const tracy = trace(@src());
3510 defer tracy.end();
3511
3512 const src: LazySrcLoc = .unneeded;
3513 const bool_type = Type.initTag(.bool);
3514 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
3515 const uncasted_lhs = try sema.resolveInst(bin_inst.lhs);
3516 const lhs = try sema.coerce(block, bool_type, uncasted_lhs, uncasted_lhs.src);
3517 const uncasted_rhs = try sema.resolveInst(bin_inst.rhs);
3518 const rhs = try sema.coerce(block, bool_type, uncasted_rhs, uncasted_rhs.src);
3519
3520 if (lhs.value()) |lhs_val| {
3521 if (rhs.value()) |rhs_val| {
3522 if (is_bool_or) {
3523 return sema.mod.constBool(sema.arena, src, lhs_val.toBool() or rhs_val.toBool());
3524 } else {
3525 return sema.mod.constBool(sema.arena, src, lhs_val.toBool() and rhs_val.toBool());
3526 }
3527 }
3528 }
3529 try sema.requireRuntimeBlock(block, src);
3530 const tag: ir.Inst.Tag = if (is_bool_or) .bool_or else .bool_and;
3531 return block.addBinOp(src, bool_type, tag, lhs, rhs);
3532}
3533
3534fn zirBoolBr(
3535 sema: *Sema,
3536 parent_block: *Scope.Block,
3537 inst: zir.Inst.Index,
3538 is_bool_or: bool,
3539) InnerError!*Inst {
3540 const tracy = trace(@src());
3541 defer tracy.end();
3542
3543 const datas = sema.code.instructions.items(.data);
3544 const inst_data = datas[inst].bool_br;
3545 const src: LazySrcLoc = .unneeded;
3546 const lhs = try sema.resolveInst(inst_data.lhs);
3547 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
3548 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
3549
3550 if (try sema.resolveDefinedValue(parent_block, src, lhs)) |lhs_val| {
3551 if (lhs_val.toBool() == is_bool_or) {
3552 return sema.mod.constBool(sema.arena, src, is_bool_or);
3553 }
3554 // comptime-known left-hand side. No need for a block here; the result
3555 // is simply the rhs expression. Here we rely on there only being 1
3556 // break instruction (`break_inline`).
3557 return sema.resolveBody(parent_block, body);
3558 }
3559
3560 const block_inst = try sema.arena.create(Inst.Block);
3561 block_inst.* = .{
3562 .base = .{
3563 .tag = Inst.Block.base_tag,
3564 .ty = Type.initTag(.bool),
3565 .src = src,
3566 },
3567 .body = undefined,
3568 };
3569
3570 var child_block = parent_block.makeSubBlock();
3571 defer child_block.instructions.deinit(sema.gpa);
3572
3573 var then_block = child_block.makeSubBlock();
3574 defer then_block.instructions.deinit(sema.gpa);
3575
3576 var else_block = child_block.makeSubBlock();
3577 defer else_block.instructions.deinit(sema.gpa);
3578
3579 const lhs_block = if (is_bool_or) &then_block else &else_block;
3580 const rhs_block = if (is_bool_or) &else_block else &then_block;
3581
3582 const lhs_result = try sema.mod.constInst(sema.arena, src, .{
3583 .ty = Type.initTag(.bool),
3584 .val = if (is_bool_or) Value.initTag(.bool_true) else Value.initTag(.bool_false),
3585 });
3586 _ = try lhs_block.addBr(src, block_inst, lhs_result);
3587
3588 const rhs_result = try sema.resolveBody(rhs_block, body);
3589 _ = try rhs_block.addBr(src, block_inst, rhs_result);
3590
3591 const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };
3592 const tzir_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, rhs_block.instructions.items) };
3593 _ = try child_block.addCondBr(src, lhs, tzir_then_body, tzir_else_body);
3594
3595 block_inst.body = .{
3596 .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items),
3597 };
3598 try parent_block.instructions.append(sema.gpa, &block_inst.base);
3599 return &block_inst.base;
3600}
3601
3602fn zirIsNull(
3603 sema: *Sema,
3604 block: *Scope.Block,
3605 inst: zir.Inst.Index,
3606 invert_logic: bool,
3607) InnerError!*Inst {
3608 const tracy = trace(@src());
3609 defer tracy.end();
3610
3611 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3612 const src = inst_data.src();
3613 const operand = try sema.resolveInst(inst_data.operand);
3614 return sema.analyzeIsNull(block, src, operand, invert_logic);
3615}
3616
3617fn zirIsNullPtr(
3618 sema: *Sema,
3619 block: *Scope.Block,
3620 inst: zir.Inst.Index,
3621 invert_logic: bool,
3622) InnerError!*Inst {
3623 const tracy = trace(@src());
3624 defer tracy.end();
3625
3626 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3627 const src = inst_data.src();
3628 const ptr = try sema.resolveInst(inst_data.operand);
3629 const loaded = try sema.analyzeLoad(block, src, ptr, src);
3630 return sema.analyzeIsNull(block, src, loaded, invert_logic);
3631}
3632
3633fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3634 const tracy = trace(@src());
3635 defer tracy.end();
3636
3637 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3638 const operand = try sema.resolveInst(inst_data.operand);
3639 return sema.analyzeIsErr(block, inst_data.src(), operand);
3640}
3641
3642fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3643 const tracy = trace(@src());
3644 defer tracy.end();
3645
3646 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3647 const src = inst_data.src();
3648 const ptr = try sema.resolveInst(inst_data.operand);
3649 const loaded = try sema.analyzeLoad(block, src, ptr, src);
3650 return sema.analyzeIsErr(block, src, loaded);
3651}
3652
3653fn zirCondbr(
3654 sema: *Sema,
3655 parent_block: *Scope.Block,
3656 inst: zir.Inst.Index,
3657) InnerError!zir.Inst.Index {
3658 const tracy = trace(@src());
3659 defer tracy.end();
3660
3661 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3662 const src = inst_data.src();
3663 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
3664 const extra = sema.code.extraData(zir.Inst.CondBr, inst_data.payload_index);
3665
3666 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
3667 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
3668
3669 const uncasted_cond = try sema.resolveInst(extra.data.condition);
3670 const cond = try sema.coerce(parent_block, Type.initTag(.bool), uncasted_cond, cond_src);
3671
3672 if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| {
3673 const body = if (cond_val.toBool()) then_body else else_body;
3674 _ = try sema.analyzeBody(parent_block, body);
3675 return always_noreturn;
3676 }
3677
3678 var sub_block = parent_block.makeSubBlock();
3679 defer sub_block.instructions.deinit(sema.gpa);
3680
3681 _ = try sema.analyzeBody(&sub_block, then_body);
3682 const tzir_then_body: ir.Body = .{
3683 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),
3684 };
3685
3686 sub_block.instructions.shrinkRetainingCapacity(0);
3687
3688 _ = try sema.analyzeBody(&sub_block, else_body);
3689 const tzir_else_body: ir.Body = .{
3690 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),
3691 };
3692
3693 _ = try parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body);
3694 return always_noreturn;
3695}
3696
3697fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
3698 const tracy = trace(@src());
3699 defer tracy.end();
3700
3701 const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
3702 const src = inst_data.src();
3703 const safety_check = inst_data.safety;
3704 try sema.requireRuntimeBlock(block, src);
3705 // TODO Add compile error for @optimizeFor occurring too late in a scope.
3706 if (safety_check and block.wantSafety()) {
3707 return sema.safetyPanic(block, src, .unreach);
3708 } else {
3709 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
3710 return always_noreturn;
3711 }
3712}
3713
3714fn zirRetTok(
3715 sema: *Sema,
3716 block: *Scope.Block,
3717 inst: zir.Inst.Index,
3718 need_coercion: bool,
3719) InnerError!zir.Inst.Index {
3720 const tracy = trace(@src());
3721 defer tracy.end();
3722
3723 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
3724 const operand = try sema.resolveInst(inst_data.operand);
3725 const src = inst_data.src();
3726
3727 return sema.analyzeRet(block, operand, src, need_coercion);
3728}
3729
3730fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
3731 const tracy = trace(@src());
3732 defer tracy.end();
3733
3734 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3735 const operand = try sema.resolveInst(inst_data.operand);
3736 const src = inst_data.src();
3737
3738 return sema.analyzeRet(block, operand, src, false);
3739}
3740
3741fn analyzeRet(
3742 sema: *Sema,
3743 block: *Scope.Block,
3744 operand: *Inst,
3745 src: LazySrcLoc,
3746 need_coercion: bool,
3747) InnerError!zir.Inst.Index {
3748 if (block.inlining) |inlining| {
3749 // We are inlining a function call; rewrite the `ret` as a `break`.
3750 try inlining.merges.results.append(sema.gpa, operand);
3751 _ = try block.addBr(src, inlining.merges.block_inst, operand);
3752 return always_noreturn;
3753 }
3754
3755 if (need_coercion) {
3756 if (sema.func) |func| {
3757 const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
3758 const fn_ret_ty = fn_ty.fnReturnType();
3759 const casted_operand = try sema.coerce(block, fn_ret_ty, operand, src);
3760 if (fn_ret_ty.zigTypeTag() == .Void)
3761 _ = try block.addNoOp(src, Type.initTag(.noreturn), .retvoid)
3762 else
3763 _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, casted_operand);
3764 return always_noreturn;
3765 }
3766 }
3767 _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, operand);
3768 return always_noreturn;
3769}
3770
3771fn floatOpAllowed(tag: zir.Inst.Tag) bool {
3772 // extend this swich as additional operators are implemented
3773 return switch (tag) {
3774 .add, .sub => true,
3775 else => false,
3776 };
3777}
3778
3779fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3780 const tracy = trace(@src());
3781 defer tracy.end();
3782
3783 const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple;
3784 const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type);
3785 const ty = try sema.mod.ptrType(
3786 sema.arena,
3787 elem_type,
3788 null,
3789 0,
3790 0,
3791 0,
3792 inst_data.is_mutable,
3793 inst_data.is_allowzero,
3794 inst_data.is_volatile,
3795 inst_data.size,
3796 );
3797 return sema.mod.constType(sema.arena, .unneeded, ty);
3798}
3799
3800fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3801 const tracy = trace(@src());
3802 defer tracy.end();
3803
3804 const src: LazySrcLoc = .unneeded;
3805 const inst_data = sema.code.instructions.items(.data)[inst].ptr_type;
3806 const extra = sema.code.extraData(zir.Inst.PtrType, inst_data.payload_index);
3807
3808 var extra_i = extra.end;
3809
3810 const sentinel = if (inst_data.flags.has_sentinel) blk: {
3811 const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]);
3812 extra_i += 1;
3813 break :blk (try sema.resolveInstConst(block, .unneeded, ref)).val;
3814 } else null;
3815
3816 const abi_align = if (inst_data.flags.has_align) blk: {
3817 const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]);
3818 extra_i += 1;
3819 break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32);
3820 } else 0;
3821
3822 const bit_start = if (inst_data.flags.has_bit_range) blk: {
3823 const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]);
3824 extra_i += 1;
3825 break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16);
3826 } else 0;
3827
3828 const bit_end = if (inst_data.flags.has_bit_range) blk: {
3829 const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]);
3830 extra_i += 1;
3831 break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16);
3832 } else 0;
3833
3834 if (bit_end != 0 and bit_start >= bit_end * 8)
3835 return sema.mod.fail(&block.base, src, "bit offset starts after end of host integer", .{});
3836
3837 const elem_type = try sema.resolveType(block, .unneeded, extra.data.elem_type);
3838
3839 const ty = try sema.mod.ptrType(
3840 sema.arena,
3841 elem_type,
3842 sentinel,
3843 abi_align,
3844 bit_start,
3845 bit_end,
3846 inst_data.flags.is_mutable,
3847 inst_data.flags.is_allowzero,
3848 inst_data.flags.is_volatile,
3849 inst_data.size,
3850 );
3851 return sema.mod.constType(sema.arena, src, ty);
3852}
3853
3854fn requireFunctionBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
3855 if (sema.func == null) {
3856 return sema.mod.fail(&block.base, src, "instruction illegal outside function body", .{});
3857 }
3858}
3859
3860fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
3861 if (block.is_comptime) {
3862 return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{});
3863 }
3864 try sema.requireFunctionBlock(block, src);
3865}
3866
3867fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {
3868 if (!ty.isValidVarType(false)) {
3869 return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty});
3870 }
3871}
3872
3873pub const PanicId = enum {
3874 unreach,
3875 unwrap_null,
3876 unwrap_errunion,
3877 invalid_error_code,
3878};
3879
3880fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
3881 const block_inst = try sema.arena.create(Inst.Block);
3882 block_inst.* = .{
3883 .base = .{
3884 .tag = Inst.Block.base_tag,
3885 .ty = Type.initTag(.void),
3886 .src = ok.src,
3887 },
3888 .body = .{
3889 .instructions = try sema.arena.alloc(*Inst, 1), // Only need space for the condbr.
3890 },
3891 };
3892
3893 const ok_body: ir.Body = .{
3894 .instructions = try sema.arena.alloc(*Inst, 1), // Only need space for the br_void.
3895 };
3896 const br_void = try sema.arena.create(Inst.BrVoid);
3897 br_void.* = .{
3898 .base = .{
3899 .tag = .br_void,
3900 .ty = Type.initTag(.noreturn),
3901 .src = ok.src,
3902 },
3903 .block = block_inst,
3904 };
3905 ok_body.instructions[0] = &br_void.base;
3906
3907 var fail_block: Scope.Block = .{
3908 .parent = parent_block,
3909 .sema = sema,
3910 .src_decl = parent_block.src_decl,
3911 .instructions = .{},
3912 .inlining = parent_block.inlining,
3913 .is_comptime = parent_block.is_comptime,
3914 };
3915
3916 defer fail_block.instructions.deinit(sema.gpa);
3917
3918 _ = try sema.safetyPanic(&fail_block, ok.src, panic_id);
3919
3920 const fail_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, fail_block.instructions.items) };
3921
3922 const condbr = try sema.arena.create(Inst.CondBr);
3923 condbr.* = .{
3924 .base = .{
3925 .tag = .condbr,
3926 .ty = Type.initTag(.noreturn),
3927 .src = ok.src,
3928 },
3929 .condition = ok,
3930 .then_body = ok_body,
3931 .else_body = fail_body,
3932 };
3933 block_inst.body.instructions[0] = &condbr.base;
3934
3935 try parent_block.instructions.append(sema.gpa, &block_inst.base);
3936}
3937
3938fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Index {
3939 // TODO Once we have a panic function to call, call it here instead of breakpoint.
3940 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
3941 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
3942 return always_noreturn;
3943}
3944
3945fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
3946 sema.branch_count += 1;
3947 if (sema.branch_count > sema.branch_quota) {
3948 // TODO show the "called from here" stack
3949 return sema.mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{sema.branch_quota});
3950 }
3951}
3952
3953fn namedFieldPtr(
3954 sema: *Sema,
3955 block: *Scope.Block,
3956 src: LazySrcLoc,
3957 object_ptr: *Inst,
3958 field_name: []const u8,
3959 field_name_src: LazySrcLoc,
3960) InnerError!*Inst {
3961 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
3962 .Pointer => object_ptr.ty.elemType(),
3963 else => return sema.mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
3964 };
3965 switch (elem_ty.zigTypeTag()) {
3966 .Array => {
3967 if (mem.eql(u8, field_name, "len")) {
3968 return sema.mod.constInst(sema.arena, src, .{
3969 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
3970 .val = try Value.Tag.ref_val.create(
3971 sema.arena,
3972 try Value.Tag.int_u64.create(sema.arena, elem_ty.arrayLen()),
3973 ),
3974 });
3975 } else {
3976 return sema.mod.fail(
3977 &block.base,
3978 field_name_src,
3979 "no member named '{s}' in '{}'",
3980 .{ field_name, elem_ty },
3981 );
3982 }
3983 },
3984 .Pointer => {
3985 const ptr_child = elem_ty.elemType();
3986 switch (ptr_child.zigTypeTag()) {
3987 .Array => {
3988 if (mem.eql(u8, field_name, "len")) {
3989 return sema.mod.constInst(sema.arena, src, .{
3990 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
3991 .val = try Value.Tag.ref_val.create(
3992 sema.arena,
3993 try Value.Tag.int_u64.create(sema.arena, ptr_child.arrayLen()),
3994 ),
3995 });
3996 } else {
3997 return sema.mod.fail(
3998 &block.base,
3999 field_name_src,
4000 "no member named '{s}' in '{}'",
4001 .{ field_name, elem_ty },
4002 );
4003 }
4004 },
4005 else => {},
4006 }
4007 },
4008 .Type => {
4009 _ = try sema.resolveConstValue(block, object_ptr.src, object_ptr);
4010 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src);
4011 const val = result.value().?;
4012 const child_type = try val.toType(sema.arena);
4013 switch (child_type.zigTypeTag()) {
4014 .ErrorSet => {
4015 // TODO resolve inferred error sets
4016 const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {
4017 const error_set = payload.data;
4018 // TODO this is O(N). I'm putting off solving this until we solve inferred
4019 // error sets at the same time.
4020 const names = error_set.names_ptr[0..error_set.names_len];
4021 for (names) |name| {
4022 if (mem.eql(u8, field_name, name)) {
4023 break :blk name;
4024 }
4025 }
4026 return sema.mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{
4027 field_name,
4028 child_type,
4029 });
4030 } else (try sema.mod.getErrorValue(field_name)).key;
4031
4032 return sema.mod.constInst(sema.arena, src, .{
4033 .ty = try sema.mod.simplePtrType(sema.arena, child_type, false, .One),
4034 .val = try Value.Tag.ref_val.create(
4035 sema.arena,
4036 try Value.Tag.@"error".create(sema.arena, .{
4037 .name = name,
4038 }),
4039 ),
4040 });
4041 },
4042 .Struct => {
4043 const container_scope = child_type.getContainerScope();
4044 if (sema.mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
4045 // TODO if !decl.is_pub and inDifferentFiles() "{} is private"
4046 return sema.analyzeDeclRef(block, src, decl);
4047 }
4048
4049 if (container_scope.file_scope == sema.mod.root_scope) {
4050 return sema.mod.fail(&block.base, src, "root source file has no member called '{s}'", .{field_name});
4051 } else {
4052 return sema.mod.fail(&block.base, src, "container '{}' has no member called '{s}'", .{ child_type, field_name });
4053 }
4054 },
4055 else => return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{child_type}),
4056 }
4057 },
4058 else => {},
4059 }
4060 return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty});
4061}
4062
4063fn elemPtr(
4064 sema: *Sema,
4065 block: *Scope.Block,
4066 src: LazySrcLoc,
4067 array_ptr: *Inst,
4068 elem_index: *Inst,
4069 elem_index_src: LazySrcLoc,
4070) InnerError!*Inst {
4071 const elem_ty = switch (array_ptr.ty.zigTypeTag()) {
4072 .Pointer => array_ptr.ty.elemType(),
4073 else => return sema.mod.fail(&block.base, array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
4074 };
4075 if (!elem_ty.isIndexable()) {
4076 return sema.mod.fail(&block.base, src, "array access of non-array type '{}'", .{elem_ty});
4077 }
4078
4079 if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) {
4080 // we have to deref the ptr operand to get the actual array pointer
4081 const array_ptr_deref = try sema.analyzeLoad(block, src, array_ptr, array_ptr.src);
4082 if (array_ptr_deref.value()) |array_ptr_val| {
4083 if (elem_index.value()) |index_val| {
4084 // Both array pointer and index are compile-time known.
4085 const index_u64 = index_val.toUnsignedInt();
4086 // @intCast here because it would have been impossible to construct a value that
4087 // required a larger index.
4088 const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64));
4089 const pointee_type = elem_ty.elemType().elemType();
4090
4091 return sema.mod.constInst(sema.arena, src, .{
4092 .ty = try Type.Tag.single_const_pointer.create(sema.arena, pointee_type),
4093 .val = elem_ptr,
4094 });
4095 }
4096 }
4097 }
4098
4099 return sema.mod.fail(&block.base, src, "TODO implement more analyze elemptr", .{});
4100}
4101
4102fn coerce(
4103 sema: *Sema,
4104 block: *Scope.Block,
4105 dest_type: Type,
4106 inst: *Inst,
4107 inst_src: LazySrcLoc,
4108) InnerError!*Inst {
4109 if (dest_type.tag() == .var_args_param) {
4110 return sema.coerceVarArgParam(block, inst);
4111 }
4112 // If the types are the same, we can return the operand.
4113 if (dest_type.eql(inst.ty))
4114 return inst;
4115
4116 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
4117 if (in_memory_result == .ok) {
4118 return sema.bitcast(block, dest_type, inst);
4119 }
4120
4121 // undefined to anything
4122 if (inst.value()) |val| {
4123 if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
4124 return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = val });
4125 }
4126 }
4127 assert(inst.ty.zigTypeTag() != .Undefined);
4128
4129 // T to E!T or E to E!T
4130 if (dest_type.tag() == .error_union) {
4131 return try sema.wrapErrorUnion(block, dest_type, inst);
4132 }
4133
4134 // comptime known number to other number
4135 if (try sema.coerceNum(block, dest_type, inst)) |some|
4136 return some;
4137
4138 const target = sema.mod.getTarget();
4139
4140 switch (dest_type.zigTypeTag()) {
4141 .Optional => {
4142 // null to ?T
4143 if (inst.ty.zigTypeTag() == .Null) {
4144 return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
4145 }
4146
4147 // T to ?T
4148 var buf: Type.Payload.ElemType = undefined;
4149 const child_type = dest_type.optionalChild(&buf);
4150 if (child_type.eql(inst.ty)) {
4151 return sema.wrapOptional(block, dest_type, inst);
4152 } else if (try sema.coerceNum(block, child_type, inst)) |some| {
4153 return sema.wrapOptional(block, dest_type, some);
4154 }
4155 },
4156 .Pointer => {
4157 // Coercions where the source is a single pointer to an array.
4158 src_array_ptr: {
4159 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
4160 const array_type = inst.ty.elemType();
4161 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
4162 const array_elem_type = array_type.elemType();
4163 if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
4164 if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
4165
4166 const dst_elem_type = dest_type.elemType();
4167 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
4168 .ok => {},
4169 .no_match => break :src_array_ptr,
4170 }
4171
4172 switch (dest_type.ptrSize()) {
4173 .Slice => {
4174 // *[N]T to []T
4175 return sema.coerceArrayPtrToSlice(block, dest_type, inst);
4176 },
4177 .C => {
4178 // *[N]T to [*c]T
4179 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4180 },
4181 .Many => {
4182 // *[N]T to [*]T
4183 // *[N:s]T to [*:s]T
4184 const src_sentinel = array_type.sentinel();
4185 const dst_sentinel = dest_type.sentinel();
4186 if (src_sentinel == null and dst_sentinel == null)
4187 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4188
4189 if (src_sentinel) |src_s| {
4190 if (dst_sentinel) |dst_s| {
4191 if (src_s.eql(dst_s)) {
4192 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4193 }
4194 }
4195 }
4196 },
4197 .One => {},
4198 }
4199 }
4200 },
4201 .Int => {
4202 // integer widening
4203 if (inst.ty.zigTypeTag() == .Int) {
4204 assert(inst.value() == null); // handled above
4205
4206 const dst_info = dest_type.intInfo(target);
4207 const src_info = inst.ty.intInfo(target);
4208 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
4209 // small enough unsigned ints can get casted to large enough signed ints
4210 (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))
4211 {
4212 try sema.requireRuntimeBlock(block, inst_src);
4213 return block.addUnOp(inst_src, dest_type, .intcast, inst);
4214 }
4215 }
4216 },
4217 .Float => {
4218 // float widening
4219 if (inst.ty.zigTypeTag() == .Float) {
4220 assert(inst.value() == null); // handled above
4221
4222 const src_bits = inst.ty.floatBits(target);
4223 const dst_bits = dest_type.floatBits(target);
4224 if (dst_bits >= src_bits) {
4225 try sema.requireRuntimeBlock(block, inst_src);
4226 return block.addUnOp(inst_src, dest_type, .floatcast, inst);
4227 }
4228 }
4229 },
4230 else => {},
4231 }
4232
4233 return sema.mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });
4234}
4235
4236const InMemoryCoercionResult = enum {
4237 ok,
4238 no_match,
4239};
4240
4241fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
4242 if (dest_type.eql(src_type))
4243 return .ok;
4244
4245 // TODO: implement more of this function
4246
4247 return .no_match;
4248}
4249
4250fn coerceNum(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) InnerError!?*Inst {
4251 const val = inst.value() orelse return null;
4252 const src_zig_tag = inst.ty.zigTypeTag();
4253 const dst_zig_tag = dest_type.zigTypeTag();
4254
4255 const target = sema.mod.getTarget();
4256
4257 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
4258 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
4259 if (val.floatHasFraction()) {
4260 return sema.mod.fail(&block.base, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
4261 }
4262 return sema.mod.fail(&block.base, inst.src, "TODO float to int", .{});
4263 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
4264 if (!val.intFitsInType(dest_type, target)) {
4265 return sema.mod.fail(&block.base, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
4266 }
4267 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
4268 }
4269 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
4270 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
4271 const res = val.floatCast(sema.arena, dest_type, target) catch |err| switch (err) {
4272 error.Overflow => return sema.mod.fail(
4273 &block.base,
4274 inst.src,
4275 "cast of value {} to type '{}' loses information",
4276 .{ val, dest_type },
4277 ),
4278 error.OutOfMemory => return error.OutOfMemory,
4279 };
4280 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = res });
4281 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
4282 return sema.mod.fail(&block.base, inst.src, "TODO int to float", .{});
4283 }
4284 }
4285 return null;
4286}
4287
4288fn coerceVarArgParam(sema: *Sema, block: *Scope.Block, inst: *Inst) !*Inst {
4289 switch (inst.ty.zigTypeTag()) {
4290 .ComptimeInt, .ComptimeFloat => return sema.mod.fail(&block.base, inst.src, "integer and float literals in var args function must be casted", .{}),
4291 else => {},
4292 }
4293 // TODO implement more of this function.
4294 return inst;
4295}
4296
4297fn storePtr(
4298 sema: *Sema,
4299 block: *Scope.Block,
4300 src: LazySrcLoc,
4301 ptr: *Inst,
4302 uncasted_value: *Inst,
4303) !void {
4304 if (ptr.ty.isConstPtr())
4305 return sema.mod.fail(&block.base, src, "cannot assign to constant", .{});
4306
4307 const elem_ty = ptr.ty.elemType();
4308 const value = try sema.coerce(block, elem_ty, uncasted_value, src);
4309 if (elem_ty.onePossibleValue() != null)
4310 return;
4311
4312 // TODO handle comptime pointer writes
4313 // TODO handle if the element type requires comptime
4314
4315 try sema.requireRuntimeBlock(block, src);
4316 _ = try block.addBinOp(src, Type.initTag(.void), .store, ptr, value);
4317}
4318
4319fn bitcast(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
4320 if (inst.value()) |val| {
4321 // Keep the comptime Value representation; take the new type.
4322 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
4323 }
4324 // TODO validate the type size and other compile errors
4325 try sema.requireRuntimeBlock(block, inst.src);
4326 return block.addUnOp(inst.src, dest_type, .bitcast, inst);
4327}
4328
4329fn coerceArrayPtrToSlice(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
4330 if (inst.value()) |val| {
4331 // The comptime Value representation is compatible with both types.
4332 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
4333 }
4334 return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
4335}
4336
4337fn coerceArrayPtrToMany(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
4338 if (inst.value()) |val| {
4339 // The comptime Value representation is compatible with both types.
4340 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
4341 }
4342 return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{});
4343}
4344
4345fn analyzeDeclVal(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
4346 const decl_ref = try sema.analyzeDeclRef(block, src, decl);
4347 return sema.analyzeLoad(block, src, decl_ref, src);
4348}
4349
4350fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
4351 try sema.mod.declareDeclDependency(sema.owner_decl, decl);
4352 sema.mod.ensureDeclAnalyzed(decl) catch |err| {
4353 if (sema.func) |func| {
4354 func.state = .dependency_failure;
4355 } else {
4356 sema.owner_decl.analysis = .dependency_failure;
4357 }
4358 return err;
4359 };
4360
4361 const decl_tv = try decl.typedValue();
4362 if (decl_tv.val.tag() == .variable) {
4363 return sema.analyzeVarRef(block, src, decl_tv);
4364 }
4365 return sema.mod.constInst(sema.arena, src, .{
4366 .ty = try sema.mod.simplePtrType(sema.arena, decl_tv.ty, false, .One),
4367 .val = try Value.Tag.decl_ref.create(sema.arena, decl),
4368 });
4369}
4370
4371fn analyzeVarRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, tv: TypedValue) InnerError!*Inst {
4372 const variable = tv.val.castTag(.variable).?.data;
4373
4374 const ty = try sema.mod.simplePtrType(sema.arena, tv.ty, variable.is_mutable, .One);
4375 if (!variable.is_mutable and !variable.is_extern) {
4376 return sema.mod.constInst(sema.arena, src, .{
4377 .ty = ty,
4378 .val = try Value.Tag.ref_val.create(sema.arena, variable.init),
4379 });
4380 }
4381
4382 try sema.requireRuntimeBlock(block, src);
4383 const inst = try sema.arena.create(Inst.VarPtr);
4384 inst.* = .{
4385 .base = .{
4386 .tag = .varptr,
4387 .ty = ty,
4388 .src = src,
4389 },
4390 .variable = variable,
4391 };
4392 try block.instructions.append(sema.gpa, &inst.base);
4393 return &inst.base;
4394}
4395
4396fn analyzeRef(
4397 sema: *Sema,
4398 block: *Scope.Block,
4399 src: LazySrcLoc,
4400 operand: *Inst,
4401) InnerError!*Inst {
4402 const ptr_type = try sema.mod.simplePtrType(sema.arena, operand.ty, false, .One);
4403
4404 if (operand.value()) |val| {
4405 return sema.mod.constInst(sema.arena, src, .{
4406 .ty = ptr_type,
4407 .val = try Value.Tag.ref_val.create(sema.arena, val),
4408 });
4409 }
4410
4411 try sema.requireRuntimeBlock(block, src);
4412 return block.addUnOp(src, ptr_type, .ref, operand);
4413}
4414
4415fn analyzeLoad(
4416 sema: *Sema,
4417 block: *Scope.Block,
4418 src: LazySrcLoc,
4419 ptr: *Inst,
4420 ptr_src: LazySrcLoc,
4421) InnerError!*Inst {
4422 const elem_ty = switch (ptr.ty.zigTypeTag()) {
4423 .Pointer => ptr.ty.elemType(),
4424 else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
4425 };
4426 if (ptr.value()) |val| {
4427 return sema.mod.constInst(sema.arena, src, .{
4428 .ty = elem_ty,
4429 .val = try val.pointerDeref(sema.arena),
4430 });
4431 }
4432
4433 try sema.requireRuntimeBlock(block, src);
4434 return block.addUnOp(src, elem_ty, .load, ptr);
4435}
4436
4437fn analyzeIsNull(
4438 sema: *Sema,
4439 block: *Scope.Block,
4440 src: LazySrcLoc,
4441 operand: *Inst,
4442 invert_logic: bool,
4443) InnerError!*Inst {
4444 if (operand.value()) |opt_val| {
4445 const is_null = opt_val.isNull();
4446 const bool_value = if (invert_logic) !is_null else is_null;
4447 return sema.mod.constBool(sema.arena, src, bool_value);
4448 }
4449 try sema.requireRuntimeBlock(block, src);
4450 const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null;
4451 return block.addUnOp(src, Type.initTag(.bool), inst_tag, operand);
4452}
4453
4454fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst {
4455 const ot = operand.ty.zigTypeTag();
4456 if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false);
4457 if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true);
4458 assert(ot == .ErrorUnion);
4459 if (operand.value()) |err_union| {
4460 return sema.mod.constBool(sema.arena, src, err_union.getError() != null);
4461 }
4462 try sema.requireRuntimeBlock(block, src);
4463 return block.addUnOp(src, Type.initTag(.bool), .is_err, operand);
4464}
4465
4466fn analyzeSlice(
4467 sema: *Sema,
4468 block: *Scope.Block,
4469 src: LazySrcLoc,
4470 array_ptr: *Inst,
4471 start: *Inst,
4472 end_opt: ?*Inst,
4473 sentinel_opt: ?*Inst,
4474 sentinel_src: LazySrcLoc,
4475) InnerError!*Inst {
4476 const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
4477 .Pointer => array_ptr.ty.elemType(),
4478 else => return sema.mod.fail(&block.base, src, "expected pointer, found '{}'", .{array_ptr.ty}),
4479 };
4480
4481 var array_type = ptr_child;
4482 const elem_type = switch (ptr_child.zigTypeTag()) {
4483 .Array => ptr_child.elemType(),
4484 .Pointer => blk: {
4485 if (ptr_child.isSinglePointer()) {
4486 if (ptr_child.elemType().zigTypeTag() == .Array) {
4487 array_type = ptr_child.elemType();
4488 break :blk ptr_child.elemType().elemType();
4489 }
4490
4491 return sema.mod.fail(&block.base, src, "slice of single-item pointer", .{});
4492 }
4493 break :blk ptr_child.elemType();
4494 },
4495 else => return sema.mod.fail(&block.base, src, "slice of non-array type '{}'", .{ptr_child}),
4496 };
4497
4498 const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
4499 const casted = try sema.coerce(block, elem_type, sentinel, sentinel.src);
4500 break :blk try sema.resolveConstValue(block, sentinel_src, casted);
4501 } else null;
4502
4503 var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
4504 var return_elem_type = elem_type;
4505 if (end_opt) |end| {
4506 if (end.value()) |end_val| {
4507 if (start.value()) |start_val| {
4508 const start_u64 = start_val.toUnsignedInt();
4509 const end_u64 = end_val.toUnsignedInt();
4510 if (start_u64 > end_u64) {
4511 return sema.mod.fail(&block.base, src, "out of bounds slice", .{});
4512 }
4513
4514 const len = end_u64 - start_u64;
4515 const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
4516 array_type.sentinel()
4517 else
4518 slice_sentinel;
4519 return_elem_type = try sema.mod.arrayType(sema.arena, len, array_sentinel, elem_type);
4520 return_ptr_size = .One;
4521 }
4522 }
4523 }
4524 const return_type = try sema.mod.ptrType(
4525 sema.arena,
4526 return_elem_type,
4527 if (end_opt == null) slice_sentinel else null,
4528 0, // TODO alignment
4529 0,
4530 0,
4531 !ptr_child.isConstPtr(),
4532 ptr_child.isAllowzeroPtr(),
4533 ptr_child.isVolatilePtr(),
4534 return_ptr_size,
4535 );
4536
4537 return sema.mod.fail(&block.base, src, "TODO implement analysis of slice", .{});
4538}
4539
4540fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_string: []const u8) !*Scope.File {
4541 const cur_pkg = block.getFileScope().pkg;
4542 const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse ".";
4543 const found_pkg = cur_pkg.table.get(target_string);
4544
4545 const resolved_path = if (found_pkg) |pkg|
4546 try std.fs.path.resolve(sema.gpa, &[_][]const u8{ pkg.root_src_directory.path orelse ".", pkg.root_src_path })
4547 else
4548 try std.fs.path.resolve(sema.gpa, &[_][]const u8{ cur_pkg_dir_path, target_string });
4549 errdefer sema.gpa.free(resolved_path);
4550
4551 if (sema.mod.import_table.get(resolved_path)) |some| {
4552 sema.gpa.free(resolved_path);
4553 return some;
4554 }
4555
4556 if (found_pkg == null) {
4557 const resolved_root_path = try std.fs.path.resolve(sema.gpa, &[_][]const u8{cur_pkg_dir_path});
4558 defer sema.gpa.free(resolved_root_path);
4559
4560 if (!mem.startsWith(u8, resolved_path, resolved_root_path)) {
4561 return error.ImportOutsidePkgPath;
4562 }
4563 }
4564
4565 // TODO Scope.Container arena for ty and sub_file_path
4566 const file_scope = try sema.gpa.create(Scope.File);
4567 errdefer sema.gpa.destroy(file_scope);
4568 const struct_ty = try Type.Tag.empty_struct.create(sema.gpa, &file_scope.root_container);
4569 errdefer sema.gpa.destroy(struct_ty.castTag(.empty_struct).?);
4570
4571 file_scope.* = .{
4572 .sub_file_path = resolved_path,
4573 .source = .{ .unloaded = {} },
4574 .tree = undefined,
4575 .status = .never_loaded,
4576 .pkg = found_pkg orelse cur_pkg,
4577 .root_container = .{
4578 .file_scope = file_scope,
4579 .decls = .{},
4580 .ty = struct_ty,
4581 },
4582 };
4583 sema.mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
4584 error.AnalysisFail => {
4585 assert(sema.mod.comp.totalErrorCount() != 0);
4586 },
4587 else => |e| return e,
4588 };
4589 try sema.mod.import_table.put(sema.gpa, file_scope.sub_file_path, file_scope);
4590 return file_scope;
4591}
4592
4593/// Asserts that lhs and rhs types are both numeric.
4594fn cmpNumeric(
4595 sema: *Sema,
4596 block: *Scope.Block,
4597 src: LazySrcLoc,
4598 lhs: *Inst,
4599 rhs: *Inst,
4600 op: std.math.CompareOperator,
4601) InnerError!*Inst {
4602 assert(lhs.ty.isNumeric());
4603 assert(rhs.ty.isNumeric());
4604
4605 const lhs_ty_tag = lhs.ty.zigTypeTag();
4606 const rhs_ty_tag = rhs.ty.zigTypeTag();
4607
4608 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
4609 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
4610 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
4611 lhs.ty.arrayLen(),
4612 rhs.ty.arrayLen(),
4613 });
4614 }
4615 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{});
4616 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
4617 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
4618 lhs.ty,
4619 rhs.ty,
4620 });
4621 }
4622
4623 if (lhs.value()) |lhs_val| {
4624 if (rhs.value()) |rhs_val| {
4625 return sema.mod.constBool(sema.arena, src, Value.compare(lhs_val, op, rhs_val));
4626 }
4627 }
4628
4629 // TODO handle comparisons against lazy zero values
4630 // Some values can be compared against zero without being runtime known or without forcing
4631 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
4632 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
4633 // of this function if we don't need to.
4634
4635 // It must be a runtime comparison.
4636 try sema.requireRuntimeBlock(block, src);
4637 // For floats, emit a float comparison instruction.
4638 const lhs_is_float = switch (lhs_ty_tag) {
4639 .Float, .ComptimeFloat => true,
4640 else => false,
4641 };
4642 const rhs_is_float = switch (rhs_ty_tag) {
4643 .Float, .ComptimeFloat => true,
4644 else => false,
4645 };
4646 const target = sema.mod.getTarget();
4647 if (lhs_is_float and rhs_is_float) {
4648 // Implicit cast the smaller one to the larger one.
4649 const dest_type = x: {
4650 if (lhs_ty_tag == .ComptimeFloat) {
4651 break :x rhs.ty;
4652 } else if (rhs_ty_tag == .ComptimeFloat) {
4653 break :x lhs.ty;
4654 }
4655 if (lhs.ty.floatBits(target) >= rhs.ty.floatBits(target)) {
4656 break :x lhs.ty;
4657 } else {
4658 break :x rhs.ty;
4659 }
4660 };
4661 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs.src);
4662 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs.src);
4663 return block.addBinOp(src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
4664 }
4665 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
4666 // For mixed signed and unsigned integers, implicit cast both operands to a signed
4667 // integer with + 1 bit.
4668 // For mixed floats and integers, extract the integer part from the float, cast that to
4669 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
4670 // add/subtract 1.
4671 const lhs_is_signed = if (lhs.value()) |lhs_val|
4672 lhs_val.compareWithZero(.lt)
4673 else
4674 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
4675 const rhs_is_signed = if (rhs.value()) |rhs_val|
4676 rhs_val.compareWithZero(.lt)
4677 else
4678 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
4679 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
4680
4681 var dest_float_type: ?Type = null;
4682
4683 var lhs_bits: usize = undefined;
4684 if (lhs.value()) |lhs_val| {
4685 if (lhs_val.isUndef())
4686 return sema.mod.constUndef(sema.arena, src, Type.initTag(.bool));
4687 const is_unsigned = if (lhs_is_float) x: {
4688 var bigint_space: Value.BigIntSpace = undefined;
4689 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
4690 defer bigint.deinit();
4691 const zcmp = lhs_val.orderAgainstZero();
4692 if (lhs_val.floatHasFraction()) {
4693 switch (op) {
4694 .eq => return sema.mod.constBool(sema.arena, src, false),
4695 .neq => return sema.mod.constBool(sema.arena, src, true),
4696 else => {},
4697 }
4698 if (zcmp == .lt) {
4699 try bigint.addScalar(bigint.toConst(), -1);
4700 } else {
4701 try bigint.addScalar(bigint.toConst(), 1);
4702 }
4703 }
4704 lhs_bits = bigint.toConst().bitCountTwosComp();
4705 break :x (zcmp != .lt);
4706 } else x: {
4707 lhs_bits = lhs_val.intBitCountTwosComp();
4708 break :x (lhs_val.orderAgainstZero() != .lt);
4709 };
4710 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
4711 } else if (lhs_is_float) {
4712 dest_float_type = lhs.ty;
4713 } else {
4714 const int_info = lhs.ty.intInfo(target);
4715 lhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
4716 }
4717
4718 var rhs_bits: usize = undefined;
4719 if (rhs.value()) |rhs_val| {
4720 if (rhs_val.isUndef())
4721 return sema.mod.constUndef(sema.arena, src, Type.initTag(.bool));
4722 const is_unsigned = if (rhs_is_float) x: {
4723 var bigint_space: Value.BigIntSpace = undefined;
4724 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
4725 defer bigint.deinit();
4726 const zcmp = rhs_val.orderAgainstZero();
4727 if (rhs_val.floatHasFraction()) {
4728 switch (op) {
4729 .eq => return sema.mod.constBool(sema.arena, src, false),
4730 .neq => return sema.mod.constBool(sema.arena, src, true),
4731 else => {},
4732 }
4733 if (zcmp == .lt) {
4734 try bigint.addScalar(bigint.toConst(), -1);
4735 } else {
4736 try bigint.addScalar(bigint.toConst(), 1);
4737 }
4738 }
4739 rhs_bits = bigint.toConst().bitCountTwosComp();
4740 break :x (zcmp != .lt);
4741 } else x: {
4742 rhs_bits = rhs_val.intBitCountTwosComp();
4743 break :x (rhs_val.orderAgainstZero() != .lt);
4744 };
4745 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
4746 } else if (rhs_is_float) {
4747 dest_float_type = rhs.ty;
4748 } else {
4749 const int_info = rhs.ty.intInfo(target);
4750 rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
4751 }
4752
4753 const dest_type = if (dest_float_type) |ft| ft else blk: {
4754 const max_bits = std.math.max(lhs_bits, rhs_bits);
4755 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
4756 error.Overflow => return sema.mod.fail(&block.base, src, "{d} exceeds maximum integer bit count", .{max_bits}),
4757 };
4758 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
4759 break :blk try Module.makeIntType(sema.arena, signedness, casted_bits);
4760 };
4761 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs.src);
4762 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs.src);
4763
4764 return block.addBinOp(src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
4765}
4766
4767fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
4768 if (inst.value()) |val| {
4769 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
4770 }
4771
4772 try sema.requireRuntimeBlock(block, inst.src);
4773 return block.addUnOp(inst.src, dest_type, .wrap_optional, inst);
4774}
4775
4776fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
4777 // TODO deal with inferred error sets
4778 const err_union = dest_type.castTag(.error_union).?;
4779 if (inst.value()) |val| {
4780 const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: {
4781 _ = try sema.coerce(block, err_union.data.payload, inst, inst.src);
4782 break :blk val;
4783 } else switch (err_union.data.error_set.tag()) {
4784 .anyerror => val,
4785 .error_set_single => blk: {
4786 const expected_name = val.castTag(.@"error").?.data.name;
4787 const n = err_union.data.error_set.castTag(.error_set_single).?.data;
4788 if (!mem.eql(u8, expected_name, n)) {
4789 return sema.mod.fail(
4790 &block.base,
4791 inst.src,
4792 "expected type '{}', found type '{}'",
4793 .{ err_union.data.error_set, inst.ty },
4794 );
4795 }
4796 break :blk val;
4797 },
4798 .error_set => blk: {
4799 const expected_name = val.castTag(.@"error").?.data.name;
4800 const error_set = err_union.data.error_set.castTag(.error_set).?.data;
4801 const names = error_set.names_ptr[0..error_set.names_len];
4802 // TODO this is O(N). I'm putting off solving this until we solve inferred
4803 // error sets at the same time.
4804 const found = for (names) |name| {
4805 if (mem.eql(u8, expected_name, name)) break true;
4806 } else false;
4807 if (!found) {
4808 return sema.mod.fail(
4809 &block.base,
4810 inst.src,
4811 "expected type '{}', found type '{}'",
4812 .{ err_union.data.error_set, inst.ty },
4813 );
4814 }
4815 break :blk val;
4816 },
4817 else => unreachable,
4818 };
4819
4820 return sema.mod.constInst(sema.arena, inst.src, .{
4821 .ty = dest_type,
4822 // creating a SubValue for the error_union payload
4823 .val = try Value.Tag.error_union.create(
4824 sema.arena,
4825 to_wrap,
4826 ),
4827 });
4828 }
4829
4830 try sema.requireRuntimeBlock(block, inst.src);
4831
4832 // we are coercing from E to E!T
4833 if (inst.ty.zigTypeTag() == .ErrorSet) {
4834 var coerced = try sema.coerce(block, err_union.data.error_set, inst, inst.src);
4835 return block.addUnOp(inst.src, dest_type, .wrap_errunion_err, coerced);
4836 } else {
4837 var coerced = try sema.coerce(block, err_union.data.payload, inst, inst.src);
4838 return block.addUnOp(inst.src, dest_type, .wrap_errunion_payload, coerced);
4839 }
4840}
4841
4842fn resolvePeerTypes(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, instructions: []*Inst) !Type {
4843 if (instructions.len == 0)
4844 return Type.initTag(.noreturn);
4845
4846 if (instructions.len == 1)
4847 return instructions[0].ty;
4848
4849 const target = sema.mod.getTarget();
4850
4851 var chosen = instructions[0];
4852 for (instructions[1..]) |candidate| {
4853 if (candidate.ty.eql(chosen.ty))
4854 continue;
4855 if (candidate.ty.zigTypeTag() == .NoReturn)
4856 continue;
4857 if (chosen.ty.zigTypeTag() == .NoReturn) {
4858 chosen = candidate;
4859 continue;
4860 }
4861 if (candidate.ty.zigTypeTag() == .Undefined)
4862 continue;
4863 if (chosen.ty.zigTypeTag() == .Undefined) {
4864 chosen = candidate;
4865 continue;
4866 }
4867 if (chosen.ty.isInt() and
4868 candidate.ty.isInt() and
4869 chosen.ty.isSignedInt() == candidate.ty.isSignedInt())
4870 {
4871 if (chosen.ty.intInfo(target).bits < candidate.ty.intInfo(target).bits) {
4872 chosen = candidate;
4873 }
4874 continue;
4875 }
4876 if (chosen.ty.isFloat() and candidate.ty.isFloat()) {
4877 if (chosen.ty.floatBits(target) < candidate.ty.floatBits(target)) {
4878 chosen = candidate;
4879 }
4880 continue;
4881 }
4882
4883 if (chosen.ty.zigTypeTag() == .ComptimeInt and candidate.ty.isInt()) {
4884 chosen = candidate;
4885 continue;
4886 }
4887
4888 if (chosen.ty.isInt() and candidate.ty.zigTypeTag() == .ComptimeInt) {
4889 continue;
4890 }
4891
4892 // TODO error notes pointing out each type
4893 return sema.mod.fail(&block.base, src, "incompatible types: '{}' and '{}'", .{ chosen.ty, candidate.ty });
4894 }
4895
4896 return chosen.ty;
4897}
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+46-30
......@@ -17,6 +17,7 @@ const DW = std.dwarf;
1717const leb128 = std.leb;
1818const log = std.log.scoped(.codegen);
1919const build_options = @import("build_options");
20const LazySrcLoc = Module.LazySrcLoc;
2021
2122/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
2223pub const BlockData = struct {
......@@ -498,7 +499,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
498499 defer function.stack.deinit(bin_file.allocator);
499500 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
500501
501 var call_info = function.resolveCallingConventionValues(src_loc.byte_offset, fn_type) catch |err| switch (err) {
502 var call_info = function.resolveCallingConventionValues(src_loc.lazy, fn_type) catch |err| switch (err) {
502503 error.CodegenFail => return Result{ .fail = function.err_msg.? },
503504 else => |e| return e,
504505 };
......@@ -791,8 +792,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
791792 }
792793 }
793794
794 fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {
795 self.prev_di_src = src;
795 fn dbgAdvancePCAndLine(self: *Self, abs_byte_off: usize) InnerError!void {
796 self.prev_di_src = abs_byte_off;
796797 self.prev_di_pc = self.code.items.len;
797798 switch (self.debug_output) {
798799 .dwarf => |dbg_out| {
......@@ -800,7 +801,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
800801 // lookup table, and changing ir.Inst from storing byte offset to token. Currently
801802 // this involves scanning over the source code for newlines
802803 // (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);
804 const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, abs_byte_off);
804805 const delta_pc = self.code.items.len - self.prev_di_pc;
805806 // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
806807 // single-byte opcodes that add different numbers to both the PC and the line number
......@@ -897,6 +898,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
897898 .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
898899 .is_err => return self.genIsErr(inst.castTag(.is_err).?),
899900 .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
901 .error_to_int => return self.genErrorToInt(inst.castTag(.error_to_int).?),
902 .int_to_error => return self.genIntToError(inst.castTag(.int_to_error).?),
900903 .load => return self.genLoad(inst.castTag(.load).?),
901904 .loop => return self.genLoop(inst.castTag(.loop).?),
902905 .not => return self.genNot(inst.castTag(.not).?),
......@@ -978,7 +981,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
978981 /// Copies a value to a register without tracking the register. The register is not considered
979982 /// allocated. A second call to `copyToTmpRegister` may return the same register.
980983 /// 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 {
984 fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register {
982985 const reg = self.findUnusedReg() orelse b: {
983986 // We'll take over the first register. Move the instruction that was previously
984987 // there to a stack allocation.
......@@ -1457,7 +1460,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14571460
14581461 fn genArmBinOpCode(
14591462 self: *Self,
1460 src: usize,
1463 src: LazySrcLoc,
14611464 dst_reg: Register,
14621465 lhs_mcv: MCValue,
14631466 rhs_mcv: MCValue,
......@@ -1620,7 +1623,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16201623
16211624 fn genX8664BinMathCode(
16221625 self: *Self,
1623 src: usize,
1626 src: LazySrcLoc,
16241627 dst_ty: Type,
16251628 dst_mcv: MCValue,
16261629 src_mcv: MCValue,
......@@ -1706,7 +1709,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17061709 }
17071710 }
17081711
1709 fn genX8664ModRMRegToStack(self: *Self, src: usize, ty: Type, off: u32, reg: Register, opcode: u8) !void {
1712 fn genX8664ModRMRegToStack(self: *Self, src: LazySrcLoc, ty: Type, off: u32, reg: Register, opcode: u8) !void {
17101713 const abi_size = ty.abiSize(self.target.*);
17111714 const adj_off = off + abi_size;
17121715 try self.code.ensureCapacity(self.code.items.len + 7);
......@@ -1807,7 +1810,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18071810 return result;
18081811 }
18091812
1810 fn genBreakpoint(self: *Self, src: usize) !MCValue {
1813 fn genBreakpoint(self: *Self, src: LazySrcLoc) !MCValue {
18111814 switch (arch) {
18121815 .i386, .x86_64 => {
18131816 try self.code.append(0xcc); // int3
......@@ -2234,7 +2237,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22342237 }
22352238 }
22362239
2237 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
2240 fn ret(self: *Self, src: LazySrcLoc, mcv: MCValue) !MCValue {
22382241 const ret_ty = self.fn_type.fnReturnType();
22392242 try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);
22402243 switch (arch) {
......@@ -2324,8 +2327,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23242327 }
23252328 }
23262329
2327 fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
2328 try self.dbgAdvancePCAndLine(inst.base.src);
2330 fn genDbgStmt(self: *Self, inst: *ir.Inst.DbgStmt) !MCValue {
2331 // TODO when reworking tzir memory layout, rework source locations here as
2332 // well to be more efficient, as well as support inlined function calls correctly.
2333 // For now we convert LazySrcLoc to absolute byte offset, to match what the
2334 // existing codegen code expects.
2335 try self.dbgAdvancePCAndLine(inst.byte_offset);
23292336 assert(inst.base.isUnused());
23302337 return MCValue.dead;
23312338 }
......@@ -2562,6 +2569,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25622569 return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});
25632570 }
25642571
2572 fn genErrorToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
2573 return self.resolveInst(inst.operand);
2574 }
2575
2576 fn genIntToError(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
2577 return self.resolveInst(inst.operand);
2578 }
2579
25652580 fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
25662581 // A loop is a setup to be able to jump back to the beginning.
25672582 const start_index = self.code.items.len;
......@@ -2571,7 +2586,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25712586 }
25722587
25732588 /// Send control flow to the `index` of `self.code`.
2574 fn jump(self: *Self, src: usize, index: usize) !void {
2589 fn jump(self: *Self, src: LazySrcLoc, index: usize) !void {
25752590 switch (arch) {
25762591 .i386, .x86_64 => {
25772592 try self.code.ensureCapacity(self.code.items.len + 5);
......@@ -2628,7 +2643,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26282643 }
26292644 }
26302645
2631 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
2646 fn performReloc(self: *Self, src: LazySrcLoc, reloc: Reloc) !void {
26322647 switch (reloc) {
26332648 .rel32 => |pos| {
26342649 const amt = self.code.items.len - (pos + 4);
......@@ -2692,7 +2707,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26922707 }
26932708 }
26942709
2695 fn br(self: *Self, src: usize, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue {
2710 fn br(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue {
26962711 if (operand.ty.hasCodeGenBits()) {
26972712 const operand_mcv = try self.resolveInst(operand);
26982713 const block_mcv = @bitCast(MCValue, block.codegen.mcv);
......@@ -2705,7 +2720,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27052720 return self.brVoid(src, block);
27062721 }
27072722
2708 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
2723 fn brVoid(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block) !MCValue {
27092724 // Emit a jump with a relocation. It will be patched up after the block ends.
27102725 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
27112726
......@@ -2767,7 +2782,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27672782 return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
27682783 }
27692784
2770 if (inst.output) |output| {
2785 if (inst.output_name) |output| {
27712786 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
27722787 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
27732788 }
......@@ -2799,7 +2814,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27992814 return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});
28002815 }
28012816
2802 if (inst.output) |output| {
2817 if (inst.output_name) |output| {
28032818 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
28042819 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
28052820 }
......@@ -2829,7 +2844,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28292844 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
28302845 }
28312846
2832 if (inst.output) |output| {
2847 if (inst.output_name) |output| {
28332848 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
28342849 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
28352850 }
......@@ -2859,7 +2874,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28592874 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
28602875 }
28612876
2862 if (inst.output) |output| {
2877 if (inst.output_name) |output| {
28632878 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
28642879 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
28652880 }
......@@ -2909,7 +2924,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29092924 }
29102925
29112926 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
2912 fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void {
2927 fn setRegOrMem(self: *Self, src: LazySrcLoc, ty: Type, loc: MCValue, val: MCValue) !void {
29132928 switch (loc) {
29142929 .none => return,
29152930 .register => |reg| return self.genSetReg(src, ty, reg, val),
......@@ -2921,7 +2936,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29212936 }
29222937 }
29232938
2924 fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
2939 fn genSetStack(self: *Self, src: LazySrcLoc, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
29252940 switch (arch) {
29262941 .arm, .armeb => switch (mcv) {
29272942 .dead => unreachable,
......@@ -3160,7 +3175,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31603175 }
31613176 }
31623177
3163 fn genSetReg(self: *Self, src: usize, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
3178 fn genSetReg(self: *Self, src: LazySrcLoc, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
31643179 switch (arch) {
31653180 .arm, .armeb => switch (mcv) {
31663181 .dead => unreachable,
......@@ -3703,7 +3718,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37033718 return mcv;
37043719 }
37053720
3706 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) InnerError!MCValue {
3721 fn genTypedValue(self: *Self, src: LazySrcLoc, typed_value: TypedValue) InnerError!MCValue {
37073722 if (typed_value.val.isUndef())
37083723 return MCValue{ .undef = {} };
37093724 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
......@@ -3778,7 +3793,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37783793 };
37793794
37803795 /// Caller must call `CallMCValues.deinit`.
3781 fn resolveCallingConventionValues(self: *Self, src: usize, fn_ty: Type) !CallMCValues {
3796 fn resolveCallingConventionValues(self: *Self, src: LazySrcLoc, fn_ty: Type) !CallMCValues {
37823797 const cc = fn_ty.fnCallingConvention();
37833798 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
37843799 defer self.gpa.free(param_types);
......@@ -3992,13 +4007,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
39924007 };
39934008 }
39944009
3995 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) InnerError {
4010 fn fail(self: *Self, src: LazySrcLoc, comptime format: []const u8, args: anytype) InnerError {
39964011 @setCold(true);
39974012 assert(self.err_msg == null);
3998 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, .{
3999 .file_scope = self.src_loc.file_scope,
4000 .byte_offset = src,
4001 }, format, args);
4013 const src_loc = if (src != .unneeded)
4014 src.toSrcLocWithDecl(self.mod_fn.owner_decl)
4015 else
4016 self.src_loc;
4017 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src_loc, format, args);
40024018 return error.CodegenFail;
40034019 }
40044020
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",
......@@ -143,79 +145,42 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
143145 return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi });
144146}
145147
146pub const LLVMIRModule = struct {
147 module: *Module,
148pub const Object = struct {
148149 llvm_module: *const llvm.Module,
149150 context: *const llvm.Context,
150151 target_machine: *const llvm.TargetMachine,
151 builder: *const llvm.Builder,
152
153 object_path: []const u8,
154
155 gpa: *Allocator,
156 err_msg: ?*Module.ErrorMsg = null,
157
158 // TODO: The fields below should really move into a different struct,
159 // because they are only valid when generating a function
160
161 /// This stores the LLVM values used in a function, such that they can be
162 /// referred to in other instructions. This table is cleared before every function is generated.
163 /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks
164 /// in here, however if a block ends, the instructions can be thrown away.
165 func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{},
166
167 /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction.
168 args: []*const llvm.Value = &[_]*const llvm.Value{},
169 arg_index: usize = 0,
170
171 entry_block: *const llvm.BasicBlock = undefined,
172 /// This fields stores the last alloca instruction, such that we can append more alloca instructions
173 /// to the top of the function.
174 latest_alloca_inst: ?*const llvm.Value = null,
152 object_pathZ: [:0]const u8,
175153
176 llvm_func: *const llvm.Value = undefined,
177
178 /// This data structure is used to implement breaking to blocks.
179 blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct {
180 parent_bb: *const llvm.BasicBlock,
181 break_bbs: *BreakBasicBlocks,
182 break_vals: *BreakValues,
183 }) = .{},
184
185 src_loc: Module.SrcLoc,
186
187 const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock);
188 const BreakValues = std.ArrayListUnmanaged(*const llvm.Value);
189
190 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {
191 const self = try allocator.create(LLVMIRModule);
154 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Object {
155 const self = try allocator.create(Object);
192156 errdefer allocator.destroy(self);
193157
194 const gpa = options.module.?.gpa;
195
196 const obj_basename = try std.zig.binNameAlloc(gpa, .{
158 const obj_basename = try std.zig.binNameAlloc(allocator, .{
197159 .root_name = options.root_name,
198160 .target = options.target,
199161 .output_mode = .Obj,
200162 });
201 defer gpa.free(obj_basename);
163 defer allocator.free(obj_basename);
202164
203165 const o_directory = options.module.?.zig_cache_artifact_directory;
204 const object_path = try o_directory.join(gpa, &[_][]const u8{obj_basename});
205 errdefer gpa.free(object_path);
166 const object_path = try o_directory.join(allocator, &[_][]const u8{obj_basename});
167 defer allocator.free(object_path);
168
169 const object_pathZ = try allocator.dupeZ(u8, object_path);
170 errdefer allocator.free(object_pathZ);
206171
207172 const context = llvm.Context.create();
208173 errdefer context.dispose();
209174
210175 initializeLLVMTargets();
211176
212 const root_nameZ = try gpa.dupeZ(u8, options.root_name);
213 defer gpa.free(root_nameZ);
177 const root_nameZ = try allocator.dupeZ(u8, options.root_name);
178 defer allocator.free(root_nameZ);
214179 const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context);
215180 errdefer llvm_module.dispose();
216181
217 const llvm_target_triple = try targetTriple(gpa, options.target);
218 defer gpa.free(llvm_target_triple);
182 const llvm_target_triple = try targetTriple(allocator, options.target);
183 defer allocator.free(llvm_target_triple);
219184
220185 var error_message: [*:0]const u8 = undefined;
221186 var target: *const llvm.Target = undefined;
......@@ -250,34 +215,21 @@ pub const LLVMIRModule = struct {
250215 );
251216 errdefer target_machine.dispose();
252217
253 const builder = context.createBuilder();
254 errdefer builder.dispose();
255
256218 self.* = .{
257 .module = options.module.?,
258219 .llvm_module = llvm_module,
259220 .context = context,
260221 .target_machine = target_machine,
261 .builder = builder,
262 .object_path = object_path,
263 .gpa = gpa,
264 // TODO move this field into a struct that is only instantiated per gen() call
265 .src_loc = undefined,
222 .object_pathZ = object_pathZ,
266223 };
267224 return self;
268225 }
269226
270 pub fn deinit(self: *LLVMIRModule, allocator: *Allocator) void {
271 self.builder.dispose();
227 pub fn deinit(self: *Object, allocator: *Allocator) void {
272228 self.target_machine.dispose();
273229 self.llvm_module.dispose();
274230 self.context.dispose();
275231
276 self.func_inst_table.deinit(self.gpa);
277 self.gpa.free(self.object_path);
278
279 self.blocks.deinit(self.gpa);
280
232 allocator.free(self.object_pathZ);
281233 allocator.destroy(self);
282234 }
283235
......@@ -289,7 +241,7 @@ pub const LLVMIRModule = struct {
289241 llvm.initializeAllAsmParsers();
290242 }
291243
292 pub fn flushModule(self: *LLVMIRModule, comp: *Compilation) !void {
244 pub fn flushModule(self: *Object, comp: *Compilation) !void {
293245 if (comp.verbose_llvm_ir) {
294246 const dump = self.llvm_module.printToString();
295247 defer llvm.disposeMessage(dump);
......@@ -310,13 +262,10 @@ pub const LLVMIRModule = struct {
310262 }
311263 }
312264
313 const object_pathZ = try self.gpa.dupeZ(u8, self.object_path);
314 defer self.gpa.free(object_pathZ);
315
316265 var error_message: [*:0]const u8 = undefined;
317266 if (self.target_machine.emitToFile(
318267 self.llvm_module,
319 object_pathZ.ptr,
268 self.object_pathZ.ptr,
320269 .ObjectFile,
321270 &error_message,
322271 ).toBool()) {
......@@ -328,44 +277,68 @@ pub const LLVMIRModule = struct {
328277 }
329278 }
330279
331 pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
332 self.gen(module, decl) catch |err| switch (err) {
280 pub fn updateDecl(self: *Object, module: *Module, decl: *Module.Decl) !void {
281 var dg: DeclGen = .{
282 .object = self,
283 .module = module,
284 .decl = decl,
285 .err_msg = null,
286 .gpa = module.gpa,
287 };
288 dg.genDecl() catch |err| switch (err) {
333289 error.CodegenFail => {
334290 decl.analysis = .codegen_failure;
335 try module.failed_decls.put(module.gpa, decl, self.err_msg.?);
336 self.err_msg = null;
291 try module.failed_decls.put(module.gpa, decl, dg.err_msg.?);
292 dg.err_msg = null;
337293 return;
338294 },
339295 else => |e| return e,
340296 };
341297 }
298};
342299
343 fn gen(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
344 const typed_value = decl.typed_value.most_recent.typed_value;
345 const src = decl.src();
300pub const DeclGen = struct {
301 object: *Object,
302 module: *Module,
303 decl: *Module.Decl,
304 err_msg: ?*Module.ErrorMsg,
346305
347 self.src_loc = decl.srcLoc();
306 gpa: *Allocator,
307
308 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
309 @setCold(true);
310 assert(self.err_msg == null);
311 const src_loc = @as(LazySrcLoc, .{ .node_offset = 0 }).toSrcLocWithDecl(self.decl);
312 self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, "TODO (LLVM): " ++ format, args);
313 return error.CodegenFail;
314 }
315
316 fn llvmModule(self: *DeclGen) *const llvm.Module {
317 return self.object.llvm_module;
318 }
319
320 fn context(self: *DeclGen) *const llvm.Context {
321 return self.object.context;
322 }
323
324 fn genDecl(self: *DeclGen) !void {
325 const decl = self.decl;
326 const typed_value = decl.typed_value.most_recent.typed_value;
348327
349328 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val });
350329
351330 if (typed_value.val.castTag(.function)) |func_payload| {
352331 const func = func_payload.data;
353332
354 const llvm_func = try self.resolveLLVMFunction(func.owner_decl, src);
333 const llvm_func = try self.resolveLLVMFunction(func.owner_decl);
355334
356335 // This gets the LLVM values from the function and stores them in `self.args`.
357336 const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen();
358337 var args = try self.gpa.alloc(*const llvm.Value, fn_param_len);
359 defer self.gpa.free(args);
360338
361339 for (args) |*arg, i| {
362340 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));
363341 }
364 self.args = args;
365 self.arg_index = 0;
366
367 // Make sure no other LLVM values from other functions can be referenced
368 self.func_inst_table.clearRetainingCapacity();
369342
370343 // We remove all the basic blocks of a function to support incremental
371344 // compilation!
......@@ -374,20 +347,293 @@ pub const LLVMIRModule = struct {
374347 bb.deleteBasicBlock();
375348 }
376349
377 self.entry_block = self.context.appendBasicBlock(llvm_func, "Entry");
378 self.builder.positionBuilderAtEnd(self.entry_block);
379 self.latest_alloca_inst = null;
380 self.llvm_func = llvm_func;
350 const builder = self.context().createBuilder();
351
352 const entry_block = self.context().appendBasicBlock(llvm_func, "Entry");
353 builder.positionBuilderAtEnd(entry_block);
354
355 var fg: FuncGen = .{
356 .dg = self,
357 .builder = builder,
358 .args = args,
359 .arg_index = 0,
360 .func_inst_table = .{},
361 .entry_block = entry_block,
362 .latest_alloca_inst = null,
363 .llvm_func = llvm_func,
364 .blocks = .{},
365 };
366 defer fg.deinit();
381367
382 try self.genBody(func.body);
368 try fg.genBody(func.body);
383369 } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| {
384 _ = try self.resolveLLVMFunction(extern_fn.data, src);
370 _ = try self.resolveLLVMFunction(extern_fn.data);
385371 } else {
386 _ = try self.resolveGlobalDecl(decl, src);
372 _ = try self.resolveGlobalDecl(decl);
373 }
374 }
375
376 /// If the llvm function does not exist, create it
377 fn resolveLLVMFunction(self: *DeclGen, func: *Module.Decl) !*const llvm.Value {
378 // TODO: do we want to store this in our own datastructure?
379 if (self.llvmModule().getNamedFunction(func.name)) |llvm_fn| return llvm_fn;
380
381 const zig_fn_type = func.typed_value.most_recent.typed_value.ty;
382 const return_type = zig_fn_type.fnReturnType();
383
384 const fn_param_len = zig_fn_type.fnParamLen();
385
386 const fn_param_types = try self.gpa.alloc(Type, fn_param_len);
387 defer self.gpa.free(fn_param_types);
388 zig_fn_type.fnParamTypes(fn_param_types);
389
390 const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len);
391 defer self.gpa.free(llvm_param);
392
393 for (fn_param_types) |fn_param, i| {
394 llvm_param[i] = try self.getLLVMType(fn_param);
395 }
396
397 const fn_type = llvm.Type.functionType(
398 try self.getLLVMType(return_type),
399 if (fn_param_len == 0) null else llvm_param.ptr,
400 @intCast(c_uint, fn_param_len),
401 .False,
402 );
403 const llvm_fn = self.llvmModule().addFunction(func.name, fn_type);
404
405 if (return_type.tag() == .noreturn) {
406 self.addFnAttr(llvm_fn, "noreturn");
407 }
408
409 return llvm_fn;
410 }
411
412 fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
413 // TODO: do we want to store this in our own datastructure?
414 if (self.llvmModule().getNamedGlobal(decl.name)) |val| return val;
415
416 const typed_value = decl.typed_value.most_recent.typed_value;
417
418 // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`.
419 const llvm_type = try self.getLLVMType(typed_value.ty);
420 const val = try self.genTypedValue(typed_value, null);
421 const global = self.llvmModule().addGlobal(llvm_type, decl.name);
422 llvm.setInitializer(global, val);
423
424 // TODO ask the Decl if it is const
425 // https://github.com/ziglang/zig/issues/7582
426
427 return global;
428 }
429
430 fn getLLVMType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
431 switch (t.zigTypeTag()) {
432 .Void => return self.context().voidType(),
433 .NoReturn => return self.context().voidType(),
434 .Int => {
435 const info = t.intInfo(self.module.getTarget());
436 return self.context().intType(info.bits);
437 },
438 .Bool => return self.context().intType(1),
439 .Pointer => {
440 if (t.isSlice()) {
441 return self.todo("implement slices", .{});
442 } else {
443 const elem_type = try self.getLLVMType(t.elemType());
444 return elem_type.pointerType(0);
445 }
446 },
447 .Array => {
448 const elem_type = try self.getLLVMType(t.elemType());
449 return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
450 },
451 .Optional => {
452 if (!t.isPtrLikeOptional()) {
453 var buf: Type.Payload.ElemType = undefined;
454 const child_type = t.optionalChild(&buf);
455
456 var optional_types: [2]*const llvm.Type = .{
457 try self.getLLVMType(child_type),
458 self.context().intType(1),
459 };
460 return self.context().structType(&optional_types, 2, .False);
461 } else {
462 return self.todo("implement optional pointers as actual pointers", .{});
463 }
464 },
465 else => return self.todo("implement getLLVMType for type '{}'", .{t}),
466 }
467 }
468
469 // TODO: figure out a way to remove the FuncGen argument
470 fn genTypedValue(self: *DeclGen, tv: TypedValue, fg: ?*FuncGen) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
471 const llvm_type = try self.getLLVMType(tv.ty);
472
473 if (tv.val.isUndef())
474 return llvm_type.getUndef();
475
476 switch (tv.ty.zigTypeTag()) {
477 .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
478 .Int => {
479 var bigint_space: Value.BigIntSpace = undefined;
480 const bigint = tv.val.toBigInt(&bigint_space);
481
482 if (bigint.eqZero()) return llvm_type.constNull();
483
484 if (bigint.limbs.len != 1) {
485 return self.todo("implement bigger bigint", .{});
486 }
487 const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
488 if (!bigint.positive) {
489 return llvm.constNeg(llvm_int);
490 }
491 return llvm_int;
492 },
493 .Pointer => switch (tv.val.tag()) {
494 .decl_ref => {
495 const decl = tv.val.castTag(.decl_ref).?.data;
496 const val = try self.resolveGlobalDecl(decl);
497
498 const usize_type = try self.getLLVMType(Type.initTag(.usize));
499
500 // TODO: second index should be the index into the memory!
501 var indices: [2]*const llvm.Value = .{
502 usize_type.constNull(),
503 usize_type.constNull(),
504 };
505
506 // TODO: consider using buildInBoundsGEP2 for opaque pointers
507 return fg.?.builder.buildInBoundsGEP(val, &indices, 2, "");
508 },
509 .ref_val => {
510 const elem_value = tv.val.castTag(.ref_val).?.data;
511 const elem_type = tv.ty.castPointer().?.data;
512 const alloca = fg.?.buildAlloca(try self.getLLVMType(elem_type));
513 _ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca);
514 return alloca;
515 },
516 else => return self.todo("implement const of pointer type '{}'", .{tv.ty}),
517 },
518 .Array => {
519 if (tv.val.castTag(.bytes)) |payload| {
520 const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: {
521 if (sentinel.tag() == .zero) break :blk true;
522 return self.todo("handle other sentinel values", .{});
523 } else false;
524
525 return self.context().constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel));
526 } else {
527 return self.todo("handle more array values", .{});
528 }
529 },
530 .Optional => {
531 if (!tv.ty.isPtrLikeOptional()) {
532 var buf: Type.Payload.ElemType = undefined;
533 const child_type = tv.ty.optionalChild(&buf);
534 const llvm_child_type = try self.getLLVMType(child_type);
535
536 if (tv.val.tag() == .null_value) {
537 var optional_values: [2]*const llvm.Value = .{
538 llvm_child_type.constNull(),
539 self.context().intType(1).constNull(),
540 };
541 return self.context().constStruct(&optional_values, 2, .False);
542 } else {
543 var optional_values: [2]*const llvm.Value = .{
544 try self.genTypedValue(.{ .ty = child_type, .val = tv.val }, fg),
545 self.context().intType(1).constAllOnes(),
546 };
547 return self.context().constStruct(&optional_values, 2, .False);
548 }
549 } else {
550 return self.todo("implement const of optional pointer", .{});
551 }
552 },
553 else => return self.todo("implement const of type '{}'", .{tv.ty}),
554 }
555 }
556
557 // Helper functions
558 fn addAttr(self: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
559 const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);
560 assert(kind_id != 0);
561 const llvm_attr = self.context().createEnumAttribute(kind_id, 0);
562 val.addAttributeAtIndex(index, llvm_attr);
563 }
564
565 fn addFnAttr(self: *DeclGen, val: *const llvm.Value, attr_name: []const u8) void {
566 // TODO: improve this API, `addAttr(-1, attr_name)`
567 self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name);
568 }
569};
570
571pub const FuncGen = struct {
572 dg: *DeclGen,
573
574 builder: *const llvm.Builder,
575
576 /// This stores the LLVM values used in a function, such that they can be
577 /// referred to in other instructions. This table is cleared before every function is generated.
578 /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks
579 /// in here, however if a block ends, the instructions can be thrown away.
580 func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value),
581
582 /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction.
583 args: []*const llvm.Value,
584 arg_index: usize,
585
586 entry_block: *const llvm.BasicBlock,
587 /// This fields stores the last alloca instruction, such that we can append more alloca instructions
588 /// to the top of the function.
589 latest_alloca_inst: ?*const llvm.Value,
590
591 llvm_func: *const llvm.Value,
592
593 /// This data structure is used to implement breaking to blocks.
594 blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct {
595 parent_bb: *const llvm.BasicBlock,
596 break_bbs: *BreakBasicBlocks,
597 break_vals: *BreakValues,
598 }),
599
600 const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock);
601 const BreakValues = std.ArrayListUnmanaged(*const llvm.Value);
602
603 fn deinit(self: *FuncGen) void {
604 self.builder.dispose();
605 self.func_inst_table.deinit(self.gpa());
606 self.gpa().free(self.args);
607 self.blocks.deinit(self.gpa());
608 }
609
610 fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
611 @setCold(true);
612 return self.dg.todo(format, args);
613 }
614
615 fn llvmModule(self: *FuncGen) *const llvm.Module {
616 return self.dg.object.llvm_module;
617 }
618
619 fn context(self: *FuncGen) *const llvm.Context {
620 return self.dg.object.context;
621 }
622
623 fn gpa(self: *FuncGen) *Allocator {
624 return self.dg.gpa;
625 }
626
627 fn resolveInst(self: *FuncGen, inst: *ir.Inst) !*const llvm.Value {
628 if (inst.value()) |val| {
629 return self.dg.genTypedValue(.{ .ty = inst.ty, .val = val }, self);
387630 }
631 if (self.func_inst_table.get(inst)) |value| return value;
632
633 return self.todo("implement global llvm values (or the value is not in the func_inst_table table)", .{});
388634 }
389635
390 fn genBody(self: *LLVMIRModule, body: ir.Body) error{ OutOfMemory, CodegenFail }!void {
636 fn genBody(self: *FuncGen, body: ir.Body) error{ OutOfMemory, CodegenFail }!void {
391637 for (body.instructions) |inst| {
392638 const opt_value = switch (inst.tag) {
393639 .add => try self.genAdd(inst.castTag(.add).?),
......@@ -425,13 +671,13 @@ pub const LLVMIRModule = struct {
425671 // TODO: implement debug info
426672 break :blk null;
427673 },
428 else => |tag| return self.fail(inst.src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),
674 else => |tag| return self.todo("implement TZIR instruction: {}", .{tag}),
429675 };
430 if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val);
676 if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa(), inst, val);
431677 }
432678 }
433679
434 fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value {
680 fn genCall(self: *FuncGen, inst: *Inst.Call) !?*const llvm.Value {
435681 if (inst.func.value()) |func_value| {
436682 const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn|
437683 extern_fn.data
......@@ -441,12 +687,12 @@ pub const LLVMIRModule = struct {
441687 unreachable;
442688
443689 const zig_fn_type = fn_decl.typed_value.most_recent.typed_value.ty;
444 const llvm_fn = try self.resolveLLVMFunction(fn_decl, inst.base.src);
690 const llvm_fn = try self.dg.resolveLLVMFunction(fn_decl);
445691
446692 const num_args = inst.args.len;
447693
448 const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, num_args);
449 defer self.gpa.free(llvm_param_vals);
694 const llvm_param_vals = try self.gpa().alloc(*const llvm.Value, num_args);
695 defer self.gpa().free(llvm_param_vals);
450696
451697 for (inst.args) |arg, i| {
452698 llvm_param_vals[i] = try self.resolveInst(arg);
......@@ -471,27 +717,32 @@ pub const LLVMIRModule = struct {
471717
472718 return call;
473719 } else {
474 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
720 return self.todo("implement calling runtime known function pointer", .{});
475721 }
476722 }
477723
478 fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value {
724 fn genRetVoid(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value {
479725 _ = self.builder.buildRetVoid();
480726 return null;
481727 }
482728
483 fn genRet(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
729 fn genRet(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
730 if (!inst.operand.ty.hasCodeGenBits()) {
731 // TODO: in astgen these instructions should turn into `retvoid` instructions.
732 _ = self.builder.buildRetVoid();
733 return null;
734 }
484735 _ = self.builder.buildRet(try self.resolveInst(inst.operand));
485736 return null;
486737 }
487738
488 fn genCmp(self: *LLVMIRModule, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value {
739 fn genCmp(self: *FuncGen, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value {
489740 const lhs = try self.resolveInst(inst.lhs);
490741 const rhs = try self.resolveInst(inst.rhs);
491742
492743 if (!inst.base.ty.isInt())
493744 if (inst.base.ty.tag() != .bool)
494 return self.fail(inst.base.src, "TODO implement 'genCmp' for type {}", .{inst.base.ty});
745 return self.todo("implement 'genCmp' for type {}", .{inst.base.ty});
495746
496747 const is_signed = inst.base.ty.isSignedInt();
497748 const operation = switch (op) {
......@@ -506,21 +757,21 @@ pub const LLVMIRModule = struct {
506757 return self.builder.buildICmp(operation, lhs, rhs, "");
507758 }
508759
509 fn genBlock(self: *LLVMIRModule, inst: *Inst.Block) !?*const llvm.Value {
510 const parent_bb = self.context.createBasicBlock("Block");
760 fn genBlock(self: *FuncGen, inst: *Inst.Block) !?*const llvm.Value {
761 const parent_bb = self.context().createBasicBlock("Block");
511762
512763 // 5 breaks to a block seems like a reasonable default.
513 var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5);
514 var break_vals = try BreakValues.initCapacity(self.gpa, 5);
515 try self.blocks.putNoClobber(self.gpa, inst, .{
764 var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa(), 5);
765 var break_vals = try BreakValues.initCapacity(self.gpa(), 5);
766 try self.blocks.putNoClobber(self.gpa(), inst, .{
516767 .parent_bb = parent_bb,
517768 .break_bbs = &break_bbs,
518769 .break_vals = &break_vals,
519770 });
520771 defer {
521772 self.blocks.removeAssertDiscard(inst);
522 break_bbs.deinit(self.gpa);
523 break_vals.deinit(self.gpa);
773 break_bbs.deinit(self.gpa());
774 break_vals.deinit(self.gpa());
524775 }
525776
526777 try self.genBody(inst.body);
......@@ -531,7 +782,7 @@ pub const LLVMIRModule = struct {
531782 // If the block does not return a value, we dont have to create a phi node.
532783 if (!inst.base.ty.hasCodeGenBits()) return null;
533784
534 const phi_node = self.builder.buildPhi(try self.getLLVMType(inst.base.ty, inst.base.src), "");
785 const phi_node = self.builder.buildPhi(try self.dg.getLLVMType(inst.base.ty), "");
535786 phi_node.addIncoming(
536787 break_vals.items.ptr,
537788 break_bbs.items.ptr,
......@@ -540,7 +791,7 @@ pub const LLVMIRModule = struct {
540791 return phi_node;
541792 }
542793
543 fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value {
794 fn genBr(self: *FuncGen, inst: *Inst.Br) !?*const llvm.Value {
544795 var block = self.blocks.get(inst.block).?;
545796
546797 // If the break doesn't break a value, then we don't have to add
......@@ -553,25 +804,25 @@ pub const LLVMIRModule = struct {
553804
554805 // For the phi node, we need the basic blocks and the values of the
555806 // break instructions.
556 try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());
557 try block.break_vals.append(self.gpa, val);
807 try block.break_bbs.append(self.gpa(), self.builder.getInsertBlock());
808 try block.break_vals.append(self.gpa(), val);
558809
559810 _ = self.builder.buildBr(block.parent_bb);
560811 }
561812 return null;
562813 }
563814
564 fn genBrVoid(self: *LLVMIRModule, inst: *Inst.BrVoid) !?*const llvm.Value {
815 fn genBrVoid(self: *FuncGen, inst: *Inst.BrVoid) !?*const llvm.Value {
565816 var block = self.blocks.get(inst.block).?;
566817 _ = self.builder.buildBr(block.parent_bb);
567818 return null;
568819 }
569820
570 fn genCondBr(self: *LLVMIRModule, inst: *Inst.CondBr) !?*const llvm.Value {
821 fn genCondBr(self: *FuncGen, inst: *Inst.CondBr) !?*const llvm.Value {
571822 const condition_value = try self.resolveInst(inst.condition);
572823
573 const then_block = self.context.appendBasicBlock(self.llvm_func, "Then");
574 const else_block = self.context.appendBasicBlock(self.llvm_func, "Else");
824 const then_block = self.context().appendBasicBlock(self.llvm_func, "Then");
825 const else_block = self.context().appendBasicBlock(self.llvm_func, "Else");
575826 {
576827 const prev_block = self.builder.getInsertBlock();
577828 defer self.builder.positionBuilderAtEnd(prev_block);
......@@ -586,8 +837,8 @@ pub const LLVMIRModule = struct {
586837 return null;
587838 }
588839
589 fn genLoop(self: *LLVMIRModule, inst: *Inst.Loop) !?*const llvm.Value {
590 const loop_block = self.context.appendBasicBlock(self.llvm_func, "Loop");
840 fn genLoop(self: *FuncGen, inst: *Inst.Loop) !?*const llvm.Value {
841 const loop_block = self.context().appendBasicBlock(self.llvm_func, "Loop");
591842 _ = self.builder.buildBr(loop_block);
592843
593844 self.builder.positionBuilderAtEnd(loop_block);
......@@ -597,20 +848,20 @@ pub const LLVMIRModule = struct {
597848 return null;
598849 }
599850
600 fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
851 fn genNot(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
601852 return self.builder.buildNot(try self.resolveInst(inst.operand), "");
602853 }
603854
604 fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value {
855 fn genUnreach(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value {
605856 _ = self.builder.buildUnreachable();
606857 return null;
607858 }
608859
609 fn genIsNonNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
860 fn genIsNonNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
610861 const operand = try self.resolveInst(inst.operand);
611862
612863 if (operand_is_ptr) {
613 const index_type = self.context.intType(32);
864 const index_type = self.context().intType(32);
614865
615866 var indices: [2]*const llvm.Value = .{
616867 index_type.constNull(),
......@@ -623,15 +874,15 @@ pub const LLVMIRModule = struct {
623874 }
624875 }
625876
626 fn genIsNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
877 fn genIsNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
627878 return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, "");
628879 }
629880
630 fn genOptionalPayload(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
881 fn genOptionalPayload(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
631882 const operand = try self.resolveInst(inst.operand);
632883
633884 if (operand_is_ptr) {
634 const index_type = self.context.intType(32);
885 const index_type = self.context().intType(32);
635886
636887 var indices: [2]*const llvm.Value = .{
637888 index_type.constNull(),
......@@ -644,12 +895,12 @@ pub const LLVMIRModule = struct {
644895 }
645896 }
646897
647 fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
898 fn genAdd(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {
648899 const lhs = try self.resolveInst(inst.lhs);
649900 const rhs = try self.resolveInst(inst.rhs);
650901
651902 if (!inst.base.ty.isInt())
652 return self.fail(inst.base.src, "TODO implement 'genAdd' for type {}", .{inst.base.ty});
903 return self.todo("implement 'genAdd' for type {}", .{inst.base.ty});
653904
654905 return if (inst.base.ty.isSignedInt())
655906 self.builder.buildNSWAdd(lhs, rhs, "")
......@@ -657,12 +908,12 @@ pub const LLVMIRModule = struct {
657908 self.builder.buildNUWAdd(lhs, rhs, "");
658909 }
659910
660 fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
911 fn genSub(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {
661912 const lhs = try self.resolveInst(inst.lhs);
662913 const rhs = try self.resolveInst(inst.rhs);
663914
664915 if (!inst.base.ty.isInt())
665 return self.fail(inst.base.src, "TODO implement 'genSub' for type {}", .{inst.base.ty});
916 return self.todo("implement 'genSub' for type {}", .{inst.base.ty});
666917
667918 return if (inst.base.ty.isSignedInt())
668919 self.builder.buildNSWSub(lhs, rhs, "")
......@@ -670,44 +921,44 @@ pub const LLVMIRModule = struct {
670921 self.builder.buildNUWSub(lhs, rhs, "");
671922 }
672923
673 fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
924 fn genIntCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
674925 const val = try self.resolveInst(inst.operand);
675926
676927 const signed = inst.base.ty.isSignedInt();
677928 // TODO: Should we use intcast here or just a simple bitcast?
678929 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes
679 return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), llvm.Bool.fromBool(signed), "");
930 return self.builder.buildIntCast2(val, try self.dg.getLLVMType(inst.base.ty), llvm.Bool.fromBool(signed), "");
680931 }
681932
682 fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
933 fn genBitCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
683934 const val = try self.resolveInst(inst.operand);
684 const dest_type = try self.getLLVMType(inst.base.ty, inst.base.src);
935 const dest_type = try self.dg.getLLVMType(inst.base.ty);
685936
686937 return self.builder.buildBitCast(val, dest_type, "");
687938 }
688939
689 fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.Value {
940 fn genArg(self: *FuncGen, inst: *Inst.Arg) !?*const llvm.Value {
690941 const arg_val = self.args[self.arg_index];
691942 self.arg_index += 1;
692943
693 const ptr_val = self.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src));
944 const ptr_val = self.buildAlloca(try self.dg.getLLVMType(inst.base.ty));
694945 _ = self.builder.buildStore(arg_val, ptr_val);
695946 return self.builder.buildLoad(ptr_val, "");
696947 }
697948
698 fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value {
949 fn genAlloc(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value {
699950 // buildAlloca expects the pointee type, not the pointer type, so assert that
700951 // a Payload.PointerSimple is passed to the alloc instruction.
701952 const pointee_type = inst.base.ty.castPointer().?.data;
702953
703954 // TODO: figure out a way to get the name of the var decl.
704955 // TODO: set alignment and volatile
705 return self.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src));
956 return self.buildAlloca(try self.dg.getLLVMType(pointee_type));
706957 }
707958
708959 /// Use this instead of builder.buildAlloca, because this function makes sure to
709960 /// put the alloca instruction at the top of the function!
710 fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value {
961 fn buildAlloca(self: *FuncGen, t: *const llvm.Type) *const llvm.Value {
711962 const prev_block = self.builder.getInsertBlock();
712963 defer self.builder.positionBuilderAtEnd(prev_block);
713964
......@@ -729,242 +980,30 @@ pub const LLVMIRModule = struct {
729980 return val;
730981 }
731982
732 fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
983 fn genStore(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {
733984 const val = try self.resolveInst(inst.rhs);
734985 const ptr = try self.resolveInst(inst.lhs);
735986 _ = self.builder.buildStore(val, ptr);
736987 return null;
737988 }
738989
739 fn genLoad(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
990 fn genLoad(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
740991 const ptr_val = try self.resolveInst(inst.operand);
741992 return self.builder.buildLoad(ptr_val, "");
742993 }
743994
744 fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value {
995 fn genBreakpoint(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value {
745996 const llvn_fn = self.getIntrinsic("llvm.debugtrap");
746997 _ = self.builder.buildCall(llvn_fn, null, 0, "");
747998 return null;
748999 }
7491000
750 fn getIntrinsic(self: *LLVMIRModule, name: []const u8) *const llvm.Value {
1001 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
7511002 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
7521003 assert(id != 0);
7531004 // TODO: add support for overload intrinsics by passing the prefix of the intrinsic
7541005 // to `lookupIntrinsicID` and then passing the correct types to
7551006 // `getIntrinsicDeclaration`
756 return self.llvm_module.getIntrinsicDeclaration(id, null, 0);
757 }
758
759 fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.Value {
760 if (inst.value()) |val| {
761 return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val });
762 }
763 if (self.func_inst_table.get(inst)) |value| return value;
764
765 return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{});
766 }
767
768 fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
769 const llvm_type = try self.getLLVMType(tv.ty, src);
770
771 if (tv.val.isUndef())
772 return llvm_type.getUndef();
773
774 switch (tv.ty.zigTypeTag()) {
775 .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
776 .Int => {
777 var bigint_space: Value.BigIntSpace = undefined;
778 const bigint = tv.val.toBigInt(&bigint_space);
779
780 if (bigint.eqZero()) return llvm_type.constNull();
781
782 if (bigint.limbs.len != 1) {
783 return self.fail(src, "TODO implement bigger bigint", .{});
784 }
785 const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
786 if (!bigint.positive) {
787 return llvm.constNeg(llvm_int);
788 }
789 return llvm_int;
790 },
791 .Pointer => switch (tv.val.tag()) {
792 .decl_ref => {
793 const decl = tv.val.castTag(.decl_ref).?.data;
794 const val = try self.resolveGlobalDecl(decl, src);
795
796 const usize_type = try self.getLLVMType(Type.initTag(.usize), src);
797
798 // TODO: second index should be the index into the memory!
799 var indices: [2]*const llvm.Value = .{
800 usize_type.constNull(),
801 usize_type.constNull(),
802 };
803
804 // TODO: consider using buildInBoundsGEP2 for opaque pointers
805 return self.builder.buildInBoundsGEP(val, &indices, 2, "");
806 },
807 .ref_val => {
808 const elem_value = tv.val.castTag(.ref_val).?.data;
809 const elem_type = tv.ty.castPointer().?.data;
810 const alloca = self.buildAlloca(try self.getLLVMType(elem_type, src));
811 _ = self.builder.buildStore(try self.genTypedValue(src, .{ .ty = elem_type, .val = elem_value }), alloca);
812 return alloca;
813 },
814 else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}),
815 },
816 .Array => {
817 if (tv.val.castTag(.bytes)) |payload| {
818 const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: {
819 if (sentinel.tag() == .zero) break :blk true;
820 return self.fail(src, "TODO handle other sentinel values", .{});
821 } else false;
822
823 return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel));
824 } else {
825 return self.fail(src, "TODO handle more array values", .{});
826 }
827 },
828 .Optional => {
829 if (!tv.ty.isPtrLikeOptional()) {
830 var buf: Type.Payload.ElemType = undefined;
831 const child_type = tv.ty.optionalChild(&buf);
832 const llvm_child_type = try self.getLLVMType(child_type, src);
833
834 if (tv.val.tag() == .null_value) {
835 var optional_values: [2]*const llvm.Value = .{
836 llvm_child_type.constNull(),
837 self.context.intType(1).constNull(),
838 };
839 return self.context.constStruct(&optional_values, 2, .False);
840 } else {
841 var optional_values: [2]*const llvm.Value = .{
842 try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),
843 self.context.intType(1).constAllOnes(),
844 };
845 return self.context.constStruct(&optional_values, 2, .False);
846 }
847 } else {
848 return self.fail(src, "TODO implement const of optional pointer", .{});
849 }
850 },
851 else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),
852 }
853 }
854
855 fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
856 switch (t.zigTypeTag()) {
857 .Void => return self.context.voidType(),
858 .NoReturn => return self.context.voidType(),
859 .Int => {
860 const info = t.intInfo(self.module.getTarget());
861 return self.context.intType(info.bits);
862 },
863 .Bool => return self.context.intType(1),
864 .Pointer => {
865 if (t.isSlice()) {
866 return self.fail(src, "TODO: LLVM backend: implement slices", .{});
867 } else {
868 const elem_type = try self.getLLVMType(t.elemType(), src);
869 return elem_type.pointerType(0);
870 }
871 },
872 .Array => {
873 const elem_type = try self.getLLVMType(t.elemType(), src);
874 return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
875 },
876 .Optional => {
877 if (!t.isPtrLikeOptional()) {
878 var buf: Type.Payload.ElemType = undefined;
879 const child_type = t.optionalChild(&buf);
880
881 var optional_types: [2]*const llvm.Type = .{
882 try self.getLLVMType(child_type, src),
883 self.context.intType(1),
884 };
885 return self.context.structType(&optional_types, 2, .False);
886 } else {
887 return self.fail(src, "TODO implement optional pointers as actual pointers", .{});
888 }
889 },
890 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
891 }
892 }
893
894 fn resolveGlobalDecl(self: *LLVMIRModule, decl: *Module.Decl, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
895 // TODO: do we want to store this in our own datastructure?
896 if (self.llvm_module.getNamedGlobal(decl.name)) |val| return val;
897
898 const typed_value = decl.typed_value.most_recent.typed_value;
899
900 // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`.
901 const llvm_type = try self.getLLVMType(typed_value.ty, src);
902 const val = try self.genTypedValue(src, typed_value);
903 const global = self.llvm_module.addGlobal(llvm_type, decl.name);
904 llvm.setInitializer(global, val);
905
906 // TODO ask the Decl if it is const
907 // https://github.com/ziglang/zig/issues/7582
908
909 return global;
910 }
911
912 /// If the llvm function does not exist, create it
913 fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Decl, src: usize) !*const llvm.Value {
914 // TODO: do we want to store this in our own datastructure?
915 if (self.llvm_module.getNamedFunction(func.name)) |llvm_fn| return llvm_fn;
916
917 const zig_fn_type = func.typed_value.most_recent.typed_value.ty;
918 const return_type = zig_fn_type.fnReturnType();
919
920 const fn_param_len = zig_fn_type.fnParamLen();
921
922 const fn_param_types = try self.gpa.alloc(Type, fn_param_len);
923 defer self.gpa.free(fn_param_types);
924 zig_fn_type.fnParamTypes(fn_param_types);
925
926 const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len);
927 defer self.gpa.free(llvm_param);
928
929 for (fn_param_types) |fn_param, i| {
930 llvm_param[i] = try self.getLLVMType(fn_param, src);
931 }
932
933 const fn_type = llvm.Type.functionType(
934 try self.getLLVMType(return_type, src),
935 if (fn_param_len == 0) null else llvm_param.ptr,
936 @intCast(c_uint, fn_param_len),
937 .False,
938 );
939 const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);
940
941 if (return_type.tag() == .noreturn) {
942 self.addFnAttr(llvm_fn, "noreturn");
943 }
944
945 return llvm_fn;
946 }
947
948 // Helper functions
949 fn addAttr(self: LLVMIRModule, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
950 const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);
951 assert(kind_id != 0);
952 const llvm_attr = self.context.createEnumAttribute(kind_id, 0);
953 val.addAttributeAtIndex(index, llvm_attr);
954 }
955
956 fn addFnAttr(self: *LLVMIRModule, val: *const llvm.Value, attr_name: []const u8) void {
957 // TODO: improve this API, `addAttr(-1, attr_name)`
958 self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name);
959 }
960
961 pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
962 @setCold(true);
963 assert(self.err_msg == null);
964 self.err_msg = try Module.ErrorMsg.create(self.gpa, .{
965 .file_scope = self.src_loc.file_scope,
966 .byte_offset = src,
967 }, format, args);
968 return error.CodegenFail;
1007 return self.llvmModule().getIntrinsicDeclaration(id, null, 0);
9691008 }
9701009};
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+524-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
......@@ -139,7 +144,6 @@ pub const Inst = struct {
139144 .retvoid,
140145 .unreach,
141146 .breakpoint,
142 .dbg_stmt,
143147 => NoOp,
144148
145149 .ref,
......@@ -152,6 +156,8 @@ pub const Inst = struct {
152156 .is_null_ptr,
153157 .is_err,
154158 .is_err_ptr,
159 .int_to_error,
160 .error_to_int,
155161 .ptrtoint,
156162 .floatcast,
157163 .intcast,
......@@ -199,6 +205,7 @@ pub const Inst = struct {
199205 .loop => Loop,
200206 .varptr => VarPtr,
201207 .switchbr => SwitchBr,
208 .dbg_stmt => DbgStmt,
202209 };
203210 }
204211
......@@ -360,7 +367,8 @@ pub const Inst = struct {
360367 base: Inst,
361368 asm_source: []const u8,
362369 is_volatile: bool,
363 output: ?[]const u8,
370 output: ?*Inst,
371 output_name: ?[]const u8,
364372 inputs: []const []const u8,
365373 clobbers: []const []const u8,
366374 args: []const *Inst,
......@@ -584,8 +592,512 @@ pub const Inst = struct {
584592 return (self.deaths + self.else_index)[0..self.else_deaths];
585593 }
586594 };
595
596 pub const DbgStmt = struct {
597 pub const base_tag = Tag.dbg_stmt;
598
599 base: Inst,
600 byte_offset: u32,
601
602 pub fn operandCount(self: *const DbgStmt) usize {
603 return 0;
604 }
605 pub fn getOperand(self: *const DbgStmt, index: usize) ?*Inst {
606 return null;
607 }
608 };
587609};
588610
589611pub const Body = struct {
590612 instructions: []*Inst,
591613};
614
615/// For debugging purposes, prints a function representation to stderr.
616pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
617 const allocator = old_module.gpa;
618 var ctx: DumpTzir = .{
619 .allocator = allocator,
620 .arena = std.heap.ArenaAllocator.init(allocator),
621 .old_module = &old_module,
622 .module_fn = module_fn,
623 .indent = 2,
624 .inst_table = DumpTzir.InstTable.init(allocator),
625 .partial_inst_table = DumpTzir.InstTable.init(allocator),
626 .const_table = DumpTzir.InstTable.init(allocator),
627 };
628 defer ctx.inst_table.deinit();
629 defer ctx.partial_inst_table.deinit();
630 defer ctx.const_table.deinit();
631 defer ctx.arena.deinit();
632
633 switch (module_fn.state) {
634 .queued => std.debug.print("(queued)", .{}),
635 .inline_only => std.debug.print("(inline_only)", .{}),
636 .in_progress => std.debug.print("(in_progress)", .{}),
637 .sema_failure => std.debug.print("(sema_failure)", .{}),
638 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
639 .success => {
640 const writer = std.io.getStdErr().writer();
641 ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR");
642 },
643 }
644}
645
646const DumpTzir = struct {
647 allocator: *std.mem.Allocator,
648 arena: std.heap.ArenaAllocator,
649 old_module: *const Module,
650 module_fn: *Module.Fn,
651 indent: usize,
652 inst_table: InstTable,
653 partial_inst_table: InstTable,
654 const_table: InstTable,
655 next_index: usize = 0,
656 next_partial_index: usize = 0,
657 next_const_index: usize = 0,
658
659 const InstTable = std.AutoArrayHashMap(*Inst, usize);
660
661 /// TODO: Improve this code to include a stack of Body and store the instructions
662 /// in there. Now we are putting all the instructions in a function local table,
663 /// however instructions that are in a Body can be thown away when the Body ends.
664 fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void {
665 // First pass to pre-populate the table so that we can show even invalid references.
666 // Must iterate the same order we iterate the second time.
667 // We also look for constants and put them in the const_table.
668 try dtz.fetchInstsAndResolveConsts(body);
669
670 std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
671
672 for (dtz.const_table.items()) |entry| {
673 const constant = entry.key.castTag(.constant).?;
674 try writer.print(" @{d}: {} = {};\n", .{
675 entry.value, constant.base.ty, constant.val,
676 });
677 }
678
679 return dtz.dumpBody(body, writer);
680 }
681
682 fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void {
683 for (body.instructions) |inst| {
684 try dtz.inst_table.put(inst, dtz.next_index);
685 dtz.next_index += 1;
686 switch (inst.tag) {
687 .alloc,
688 .retvoid,
689 .unreach,
690 .breakpoint,
691 .dbg_stmt,
692 .arg,
693 => {},
694
695 .ref,
696 .ret,
697 .bitcast,
698 .not,
699 .is_non_null,
700 .is_non_null_ptr,
701 .is_null,
702 .is_null_ptr,
703 .is_err,
704 .is_err_ptr,
705 .error_to_int,
706 .int_to_error,
707 .ptrtoint,
708 .floatcast,
709 .intcast,
710 .load,
711 .optional_payload,
712 .optional_payload_ptr,
713 .wrap_optional,
714 .wrap_errunion_payload,
715 .wrap_errunion_err,
716 .unwrap_errunion_payload,
717 .unwrap_errunion_err,
718 .unwrap_errunion_payload_ptr,
719 .unwrap_errunion_err_ptr,
720 => {
721 const un_op = inst.cast(Inst.UnOp).?;
722 try dtz.findConst(un_op.operand);
723 },
724
725 .add,
726 .addwrap,
727 .sub,
728 .subwrap,
729 .mul,
730 .mulwrap,
731 .cmp_lt,
732 .cmp_lte,
733 .cmp_eq,
734 .cmp_gte,
735 .cmp_gt,
736 .cmp_neq,
737 .store,
738 .bool_and,
739 .bool_or,
740 .bit_and,
741 .bit_or,
742 .xor,
743 => {
744 const bin_op = inst.cast(Inst.BinOp).?;
745 try dtz.findConst(bin_op.lhs);
746 try dtz.findConst(bin_op.rhs);
747 },
748
749 .br => {
750 const br = inst.castTag(.br).?;
751 try dtz.findConst(&br.block.base);
752 try dtz.findConst(br.operand);
753 },
754
755 .br_block_flat => {
756 const br_block_flat = inst.castTag(.br_block_flat).?;
757 try dtz.findConst(&br_block_flat.block.base);
758 try dtz.fetchInstsAndResolveConsts(br_block_flat.body);
759 },
760
761 .br_void => {
762 const br_void = inst.castTag(.br_void).?;
763 try dtz.findConst(&br_void.block.base);
764 },
765
766 .block => {
767 const block = inst.castTag(.block).?;
768 try dtz.fetchInstsAndResolveConsts(block.body);
769 },
770
771 .condbr => {
772 const condbr = inst.castTag(.condbr).?;
773 try dtz.findConst(condbr.condition);
774 try dtz.fetchInstsAndResolveConsts(condbr.then_body);
775 try dtz.fetchInstsAndResolveConsts(condbr.else_body);
776 },
777 .switchbr => {
778 const switchbr = inst.castTag(.switchbr).?;
779 try dtz.findConst(switchbr.target);
780 try dtz.fetchInstsAndResolveConsts(switchbr.else_body);
781 for (switchbr.cases) |case| {
782 try dtz.fetchInstsAndResolveConsts(case.body);
783 }
784 },
785
786 .loop => {
787 const loop = inst.castTag(.loop).?;
788 try dtz.fetchInstsAndResolveConsts(loop.body);
789 },
790 .call => {
791 const call = inst.castTag(.call).?;
792 try dtz.findConst(call.func);
793 for (call.args) |arg| {
794 try dtz.findConst(arg);
795 }
796 },
797
798 // TODO fill out this debug printing
799 .assembly,
800 .constant,
801 .varptr,
802 => {},
803 }
804 }
805 }
806
807 fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
808 for (body.instructions) |inst| {
809 const my_index = dtz.next_partial_index;
810 try dtz.partial_inst_table.put(inst, my_index);
811 dtz.next_partial_index += 1;
812
813 try writer.writeByteNTimes(' ', dtz.indent);
814 try writer.print("%{d}: {} = {s}(", .{
815 my_index, inst.ty, @tagName(inst.tag),
816 });
817 switch (inst.tag) {
818 .alloc,
819 .retvoid,
820 .unreach,
821 .breakpoint,
822 .dbg_stmt,
823 => try writer.writeAll(")\n"),
824
825 .ref,
826 .ret,
827 .bitcast,
828 .not,
829 .is_non_null,
830 .is_null,
831 .is_non_null_ptr,
832 .is_null_ptr,
833 .is_err,
834 .is_err_ptr,
835 .error_to_int,
836 .int_to_error,
837 .ptrtoint,
838 .floatcast,
839 .intcast,
840 .load,
841 .optional_payload,
842 .optional_payload_ptr,
843 .wrap_optional,
844 .wrap_errunion_err,
845 .wrap_errunion_payload,
846 .unwrap_errunion_err,
847 .unwrap_errunion_payload,
848 .unwrap_errunion_payload_ptr,
849 .unwrap_errunion_err_ptr,
850 => {
851 const un_op = inst.cast(Inst.UnOp).?;
852 const kinky = try dtz.writeInst(writer, un_op.operand);
853 if (kinky != null) {
854 try writer.writeAll(") // Instruction does not dominate all uses!\n");
855 } else {
856 try writer.writeAll(")\n");
857 }
858 },
859
860 .add,
861 .addwrap,
862 .sub,
863 .subwrap,
864 .mul,
865 .mulwrap,
866 .cmp_lt,
867 .cmp_lte,
868 .cmp_eq,
869 .cmp_gte,
870 .cmp_gt,
871 .cmp_neq,
872 .store,
873 .bool_and,
874 .bool_or,
875 .bit_and,
876 .bit_or,
877 .xor,
878 => {
879 const bin_op = inst.cast(Inst.BinOp).?;
880
881 const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs);
882 try writer.writeAll(", ");
883 const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs);
884
885 if (lhs_kinky != null or rhs_kinky != null) {
886 try writer.writeAll(") // Instruction does not dominate all uses!");
887 if (lhs_kinky) |lhs| {
888 try writer.print(" %{d}", .{lhs});
889 }
890 if (rhs_kinky) |rhs| {
891 try writer.print(" %{d}", .{rhs});
892 }
893 try writer.writeAll("\n");
894 } else {
895 try writer.writeAll(")\n");
896 }
897 },
898
899 .arg => {
900 const arg = inst.castTag(.arg).?;
901 try writer.print("{s})\n", .{arg.name});
902 },
903
904 .br => {
905 const br = inst.castTag(.br).?;
906
907 const lhs_kinky = try dtz.writeInst(writer, &br.block.base);
908 try writer.writeAll(", ");
909 const rhs_kinky = try dtz.writeInst(writer, br.operand);
910
911 if (lhs_kinky != null or rhs_kinky != null) {
912 try writer.writeAll(") // Instruction does not dominate all uses!");
913 if (lhs_kinky) |lhs| {
914 try writer.print(" %{d}", .{lhs});
915 }
916 if (rhs_kinky) |rhs| {
917 try writer.print(" %{d}", .{rhs});
918 }
919 try writer.writeAll("\n");
920 } else {
921 try writer.writeAll(")\n");
922 }
923 },
924
925 .br_block_flat => {
926 const br_block_flat = inst.castTag(.br_block_flat).?;
927 const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base);
928 if (block_kinky != null) {
929 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
930 } else {
931 try writer.writeAll(", {\n");
932 }
933
934 const old_indent = dtz.indent;
935 dtz.indent += 2;
936 try dtz.dumpBody(br_block_flat.body, writer);
937 dtz.indent = old_indent;
938
939 try writer.writeByteNTimes(' ', dtz.indent);
940 try writer.writeAll("})\n");
941 },
942
943 .br_void => {
944 const br_void = inst.castTag(.br_void).?;
945 const kinky = try dtz.writeInst(writer, &br_void.block.base);
946 if (kinky) |_| {
947 try writer.writeAll(") // Instruction does not dominate all uses!\n");
948 } else {
949 try writer.writeAll(")\n");
950 }
951 },
952
953 .block => {
954 const block = inst.castTag(.block).?;
955
956 try writer.writeAll("{\n");
957
958 const old_indent = dtz.indent;
959 dtz.indent += 2;
960 try dtz.dumpBody(block.body, writer);
961 dtz.indent = old_indent;
962
963 try writer.writeByteNTimes(' ', dtz.indent);
964 try writer.writeAll("})\n");
965 },
966
967 .condbr => {
968 const condbr = inst.castTag(.condbr).?;
969
970 const condition_kinky = try dtz.writeInst(writer, condbr.condition);
971 if (condition_kinky != null) {
972 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
973 } else {
974 try writer.writeAll(", {\n");
975 }
976
977 const old_indent = dtz.indent;
978 dtz.indent += 2;
979 try dtz.dumpBody(condbr.then_body, writer);
980
981 try writer.writeByteNTimes(' ', old_indent);
982 try writer.writeAll("}, {\n");
983
984 try dtz.dumpBody(condbr.else_body, writer);
985 dtz.indent = old_indent;
986
987 try writer.writeByteNTimes(' ', old_indent);
988 try writer.writeAll("})\n");
989 },
990
991 .switchbr => {
992 const switchbr = inst.castTag(.switchbr).?;
993
994 const condition_kinky = try dtz.writeInst(writer, switchbr.target);
995 if (condition_kinky != null) {
996 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
997 } else {
998 try writer.writeAll(", {\n");
999 }
1000 const old_indent = dtz.indent;
1001
1002 if (switchbr.else_body.instructions.len != 0) {
1003 dtz.indent += 2;
1004 try dtz.dumpBody(switchbr.else_body, writer);
1005
1006 try writer.writeByteNTimes(' ', old_indent);
1007 try writer.writeAll("}, {\n");
1008 dtz.indent = old_indent;
1009 }
1010 for (switchbr.cases) |case| {
1011 dtz.indent += 2;
1012 try dtz.dumpBody(case.body, writer);
1013
1014 try writer.writeByteNTimes(' ', old_indent);
1015 try writer.writeAll("}, {\n");
1016 dtz.indent = old_indent;
1017 }
1018
1019 try writer.writeByteNTimes(' ', old_indent);
1020 try writer.writeAll("})\n");
1021 },
1022
1023 .loop => {
1024 const loop = inst.castTag(.loop).?;
1025
1026 try writer.writeAll("{\n");
1027
1028 const old_indent = dtz.indent;
1029 dtz.indent += 2;
1030 try dtz.dumpBody(loop.body, writer);
1031 dtz.indent = old_indent;
1032
1033 try writer.writeByteNTimes(' ', dtz.indent);
1034 try writer.writeAll("})\n");
1035 },
1036
1037 .call => {
1038 const call = inst.castTag(.call).?;
1039
1040 const args_kinky = try dtz.allocator.alloc(?usize, call.args.len);
1041 defer dtz.allocator.free(args_kinky);
1042 std.mem.set(?usize, args_kinky, null);
1043 var any_kinky_args = false;
1044
1045 const func_kinky = try dtz.writeInst(writer, call.func);
1046
1047 for (call.args) |arg, i| {
1048 try writer.writeAll(", ");
1049
1050 args_kinky[i] = try dtz.writeInst(writer, arg);
1051 any_kinky_args = any_kinky_args or args_kinky[i] != null;
1052 }
1053
1054 if (func_kinky != null or any_kinky_args) {
1055 try writer.writeAll(") // Instruction does not dominate all uses!");
1056 if (func_kinky) |func_index| {
1057 try writer.print(" %{d}", .{func_index});
1058 }
1059 for (args_kinky) |arg_kinky| {
1060 if (arg_kinky) |arg_index| {
1061 try writer.print(" %{d}", .{arg_index});
1062 }
1063 }
1064 try writer.writeAll("\n");
1065 } else {
1066 try writer.writeAll(")\n");
1067 }
1068 },
1069
1070 // TODO fill out this debug printing
1071 .assembly,
1072 .constant,
1073 .varptr,
1074 => {
1075 try writer.writeAll("!TODO!)\n");
1076 },
1077 }
1078 }
1079 }
1080
1081 fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize {
1082 if (dtz.partial_inst_table.get(inst)) |operand_index| {
1083 try writer.print("%{d}", .{operand_index});
1084 return null;
1085 } else if (dtz.const_table.get(inst)) |operand_index| {
1086 try writer.print("@{d}", .{operand_index});
1087 return null;
1088 } else if (dtz.inst_table.get(inst)) |operand_index| {
1089 try writer.print("%{d}", .{operand_index});
1090 return operand_index;
1091 } else {
1092 try writer.writeAll("!BADREF!");
1093 return null;
1094 }
1095 }
1096
1097 fn findConst(dtz: *DumpTzir, operand: *Inst) !void {
1098 if (operand.tag == .constant) {
1099 try dtz.const_table.put(operand, dtz.next_const_index);
1100 dtz.next_const_index += 1;
1101 }
1102 }
1103};
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+15-22
......@@ -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;
......@@ -2461,15 +2458,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
24612458 }
24622459 }
24632460
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 };
2461 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
2462 .path = lib_dir,
2463 .handle = try fs.cwd().openDir(lib_dir, .{}),
2464 } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
2465 fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
2466 };
24732467 defer zig_lib_directory.handle.close();
24742468
24752469 const std_special = "std" ++ fs.path.sep_str ++ "special";
......@@ -3281,8 +3275,7 @@ pub const ClangArgIterator = struct {
32813275 self.zig_equivalent = clang_arg.zig_equivalent;
32823276 break :find_clang_arg;
32833277 },
3284 }
3285 else {
3278 } else {
32863279 fatal("Unknown Clang option: '{s}'", .{arg});
32873280 }
32883281 }
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+203-199
......@@ -92,11 +92,7 @@ pub const Type = extern union {
9292
9393 .anyerror_void_error_union, .error_union => return .ErrorUnion,
9494
95 .anyframe_T, .@"anyframe" => return .AnyFrame,
96
97 .@"struct", .empty_struct => return .Struct,
98 .@"enum" => return .Enum,
99 .@"union" => return .Union,
95 .empty_struct => return .Struct,
10096
10197 .var_args_param => unreachable, // can be any type
10298 }
......@@ -173,6 +169,125 @@ pub const Type = extern union {
173169 };
174170 }
175171
172 pub fn ptrInfo(self: Type) Payload.Pointer {
173 switch (self.tag()) {
174 .single_const_pointer_to_comptime_int => return .{ .data = .{
175 .pointee_type = Type.initTag(.comptime_int),
176 .sentinel = null,
177 .@"align" = 0,
178 .bit_offset = 0,
179 .host_size = 0,
180 .@"allowzero" = false,
181 .mutable = false,
182 .@"volatile" = false,
183 .size = .One,
184 } },
185 .const_slice_u8 => return .{ .data = .{
186 .pointee_type = Type.initTag(.u8),
187 .sentinel = null,
188 .@"align" = 0,
189 .bit_offset = 0,
190 .host_size = 0,
191 .@"allowzero" = false,
192 .mutable = false,
193 .@"volatile" = false,
194 .size = .Slice,
195 } },
196 .single_const_pointer => return .{ .data = .{
197 .pointee_type = self.castPointer().?.data,
198 .sentinel = null,
199 .@"align" = 0,
200 .bit_offset = 0,
201 .host_size = 0,
202 .@"allowzero" = false,
203 .mutable = false,
204 .@"volatile" = false,
205 .size = .One,
206 } },
207 .single_mut_pointer => return .{ .data = .{
208 .pointee_type = self.castPointer().?.data,
209 .sentinel = null,
210 .@"align" = 0,
211 .bit_offset = 0,
212 .host_size = 0,
213 .@"allowzero" = false,
214 .mutable = true,
215 .@"volatile" = false,
216 .size = .One,
217 } },
218 .many_const_pointer => return .{ .data = .{
219 .pointee_type = self.castPointer().?.data,
220 .sentinel = null,
221 .@"align" = 0,
222 .bit_offset = 0,
223 .host_size = 0,
224 .@"allowzero" = false,
225 .mutable = false,
226 .@"volatile" = false,
227 .size = .Many,
228 } },
229 .many_mut_pointer => return .{ .data = .{
230 .pointee_type = self.castPointer().?.data,
231 .sentinel = null,
232 .@"align" = 0,
233 .bit_offset = 0,
234 .host_size = 0,
235 .@"allowzero" = false,
236 .mutable = true,
237 .@"volatile" = false,
238 .size = .Many,
239 } },
240 .c_const_pointer => return .{ .data = .{
241 .pointee_type = self.castPointer().?.data,
242 .sentinel = null,
243 .@"align" = 0,
244 .bit_offset = 0,
245 .host_size = 0,
246 .@"allowzero" = false,
247 .mutable = false,
248 .@"volatile" = false,
249 .size = .C,
250 } },
251 .c_mut_pointer => return .{ .data = .{
252 .pointee_type = self.castPointer().?.data,
253 .sentinel = null,
254 .@"align" = 0,
255 .bit_offset = 0,
256 .host_size = 0,
257 .@"allowzero" = false,
258 .mutable = true,
259 .@"volatile" = false,
260 .size = .C,
261 } },
262 .const_slice => return .{ .data = .{
263 .pointee_type = self.castPointer().?.data,
264 .sentinel = null,
265 .@"align" = 0,
266 .bit_offset = 0,
267 .host_size = 0,
268 .@"allowzero" = false,
269 .mutable = false,
270 .@"volatile" = false,
271 .size = .Slice,
272 } },
273 .mut_slice => return .{ .data = .{
274 .pointee_type = self.castPointer().?.data,
275 .sentinel = null,
276 .@"align" = 0,
277 .bit_offset = 0,
278 .host_size = 0,
279 .@"allowzero" = false,
280 .mutable = true,
281 .@"volatile" = false,
282 .size = .Slice,
283 } },
284
285 .pointer => return self.castTag(.pointer).?.*,
286
287 else => unreachable,
288 }
289 }
290
176291 pub fn eql(a: Type, b: Type) bool {
177292 // As a shortcut, if the small tags / addresses match, we're done.
178293 if (a.tag_if_small_enough == b.tag_if_small_enough)
......@@ -195,25 +310,38 @@ pub const Type = extern union {
195310 return a.elemType().eql(b.elemType());
196311 },
197312 .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)
313 const info_a = a.ptrInfo().data;
314 const info_b = b.ptrInfo().data;
315 if (!info_a.pointee_type.eql(info_b.pointee_type))
207316 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)
317 if (info_a.size != info_b.size)
318 return false;
319 if (info_a.mutable != info_b.mutable)
320 return false;
321 if (info_a.@"volatile" != info_b.@"volatile")
322 return false;
323 if (info_a.@"allowzero" != info_b.@"allowzero")
324 return false;
325 if (info_a.bit_offset != info_b.bit_offset)
326 return false;
327 if (info_a.host_size != info_b.host_size)
212328 return false;
213329
214 std.debug.panic("TODO implement more pointer Type equality comparison: {} and {}", .{
215 a, b,
216 });
330 const sentinel_a = info_a.sentinel;
331 const sentinel_b = info_b.sentinel;
332 if (sentinel_a) |sa| {
333 if (sentinel_b) |sb| {
334 if (!sa.eql(sb))
335 return false;
336 } else {
337 return false;
338 }
339 } else {
340 if (sentinel_b != null)
341 return false;
342 }
343
344 return true;
217345 },
218346 .Int => {
219347 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
......@@ -399,7 +527,6 @@ pub const Type = extern union {
399527 .const_slice_u8,
400528 .enum_literal,
401529 .anyerror_void_error_union,
402 .@"anyframe",
403530 .inferred_alloc_const,
404531 .inferred_alloc_mut,
405532 .var_args_param,
......@@ -420,7 +547,6 @@ pub const Type = extern union {
420547 .optional,
421548 .optional_single_mut_pointer,
422549 .optional_single_const_pointer,
423 .anyframe_T,
424550 => return self.copyPayloadShallow(allocator, Payload.ElemType),
425551
426552 .int_signed,
......@@ -480,13 +606,10 @@ pub const Type = extern union {
480606 .payload = try payload.payload.copy(allocator),
481607 });
482608 },
483 .error_set => return self.copyPayloadShallow(allocator, Payload.Decl),
609 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
484610 .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
485611 .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
486612
487 .@"enum" => return self.copyPayloadShallow(allocator, Payload.Enum),
488 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
489 .@"union" => return self.copyPayloadShallow(allocator, Payload.Union),
490613 .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
491614 }
492615 }
......@@ -551,7 +674,6 @@ pub const Type = extern union {
551674
552675 // TODO this should print the structs name
553676 .empty_struct => return out_stream.writeAll("struct {}"),
554 .@"anyframe" => return out_stream.writeAll("anyframe"),
555677 .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
556678 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
557679 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
......@@ -579,12 +701,6 @@ pub const Type = extern union {
579701 continue;
580702 },
581703
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 },
588704 .array_u8 => {
589705 const len = ty.castTag(.array_u8).?.data;
590706 return out_stream.print("[{d}]u8", .{len});
......@@ -715,8 +831,8 @@ pub const Type = extern union {
715831 continue;
716832 },
717833 .error_set => {
718 const decl = ty.castTag(.error_set).?.data;
719 return out_stream.writeAll(std.mem.spanZ(decl.name));
834 const error_set = ty.castTag(.error_set).?.data;
835 return out_stream.writeAll(std.mem.spanZ(error_set.owner_decl.name));
720836 },
721837 .error_set_single => {
722838 const name = ty.castTag(.error_set_single).?.data;
......@@ -725,9 +841,6 @@ pub const Type = extern union {
725841 .inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"),
726842 .inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"),
727843 // 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 {}"),
731844 .@"opaque" => return out_stream.writeAll("opaque {}"),
732845 }
733846 unreachable;
......@@ -822,8 +935,6 @@ pub const Type = extern union {
822935 .optional,
823936 .optional_single_mut_pointer,
824937 .optional_single_const_pointer,
825 .@"anyframe",
826 .anyframe_T,
827938 .anyerror_void_error_union,
828939 .error_set,
829940 .error_set_single,
......@@ -839,10 +950,6 @@ pub const Type = extern union {
839950 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
840951 },
841952
842 .@"enum" => @panic("TODO"),
843 .@"struct" => @panic("TODO"),
844 .@"union" => @panic("TODO"),
845
846953 .c_void,
847954 .void,
848955 .type,
......@@ -863,7 +970,39 @@ pub const Type = extern union {
863970 }
864971
865972 pub fn isNoReturn(self: Type) bool {
866 return self.zigTypeTag() == .NoReturn;
973 const definitely_correct_result = self.zigTypeTag() == .NoReturn;
974 const fast_result = self.tag_if_small_enough == @enumToInt(Tag.noreturn);
975 assert(fast_result == definitely_correct_result);
976 return fast_result;
977 }
978
979 pub fn ptrAlignment(self: Type, target: Target) u32 {
980 switch (self.tag()) {
981 .single_const_pointer,
982 .single_mut_pointer,
983 .many_const_pointer,
984 .many_mut_pointer,
985 .c_const_pointer,
986 .c_mut_pointer,
987 .const_slice,
988 .mut_slice,
989 .optional_single_const_pointer,
990 .optional_single_mut_pointer,
991 => return self.cast(Payload.ElemType).?.data.abiAlignment(target),
992
993 .const_slice_u8 => return 1,
994
995 .pointer => {
996 const ptr_info = self.castTag(.pointer).?.data;
997 if (ptr_info.@"align" != 0) {
998 return ptr_info.@"align";
999 } else {
1000 return ptr_info.pointee_type.abiAlignment();
1001 }
1002 },
1003
1004 else => unreachable,
1005 }
8671006 }
8681007
8691008 /// Asserts that hasCodeGenBits() is true.
......@@ -907,17 +1046,9 @@ pub const Type = extern union {
9071046 .mut_slice,
9081047 .optional_single_const_pointer,
9091048 .optional_single_mut_pointer,
910 .@"anyframe",
911 .anyframe_T,
1049 .pointer,
9121050 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
9131051
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
9211052 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
9221053 .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
9231054 .c_int => return @divExact(CType.int.sizeInBits(target), 8),
......@@ -967,10 +1098,6 @@ pub const Type = extern union {
9671098 @panic("TODO abiAlignment error union");
9681099 },
9691100
970 .@"enum" => self.cast(Payload.Enum).?.abiAlignment(target),
971 .@"struct" => @panic("TODO"),
972 .@"union" => @panic("TODO"),
973
9741101 .c_void,
9751102 .void,
9761103 .type,
......@@ -1038,7 +1165,7 @@ pub const Type = extern union {
10381165 .i64, .u64 => return 8,
10391166 .u128, .i128 => return 16,
10401167
1041 .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
1168 .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
10421169
10431170 .const_slice,
10441171 .mut_slice,
......@@ -1119,10 +1246,6 @@ pub const Type = extern union {
11191246 }
11201247 @panic("TODO abiSize error union");
11211248 },
1122
1123 .@"enum" => @panic("TODO"),
1124 .@"struct" => @panic("TODO"),
1125 .@"union" => @panic("TODO"),
11261249 };
11271250 }
11281251
......@@ -1186,15 +1309,10 @@ pub const Type = extern union {
11861309 .const_slice,
11871310 .mut_slice,
11881311 .error_union,
1189 .@"anyframe",
1190 .anyframe_T,
11911312 .anyerror_void_error_union,
11921313 .error_set,
11931314 .error_set_single,
11941315 .empty_struct,
1195 .@"enum",
1196 .@"struct",
1197 .@"union",
11981316 .@"opaque",
11991317 .var_args_param,
12001318 => false,
......@@ -1264,15 +1382,10 @@ pub const Type = extern union {
12641382 .optional_single_const_pointer,
12651383 .enum_literal,
12661384 .error_union,
1267 .@"anyframe",
1268 .anyframe_T,
12691385 .anyerror_void_error_union,
12701386 .error_set,
12711387 .error_set_single,
12721388 .empty_struct,
1273 .@"enum",
1274 .@"struct",
1275 .@"union",
12761389 .@"opaque",
12771390 .var_args_param,
12781391 => unreachable,
......@@ -1361,17 +1474,12 @@ pub const Type = extern union {
13611474 .optional_single_const_pointer,
13621475 .enum_literal,
13631476 .error_union,
1364 .@"anyframe",
1365 .anyframe_T,
13661477 .anyerror_void_error_union,
13671478 .error_set,
13681479 .error_set_single,
13691480 .empty_struct,
13701481 .inferred_alloc_const,
13711482 .inferred_alloc_mut,
1372 .@"enum",
1373 .@"struct",
1374 .@"union",
13751483 .@"opaque",
13761484 .var_args_param,
13771485 => false,
......@@ -1442,17 +1550,12 @@ pub const Type = extern union {
14421550 .enum_literal,
14431551 .mut_slice,
14441552 .error_union,
1445 .@"anyframe",
1446 .anyframe_T,
14471553 .anyerror_void_error_union,
14481554 .error_set,
14491555 .error_set_single,
14501556 .empty_struct,
14511557 .inferred_alloc_const,
14521558 .inferred_alloc_mut,
1453 .@"enum",
1454 .@"struct",
1455 .@"union",
14561559 .@"opaque",
14571560 .var_args_param,
14581561 => false,
......@@ -1532,17 +1635,12 @@ pub const Type = extern union {
15321635 .optional_single_const_pointer,
15331636 .enum_literal,
15341637 .error_union,
1535 .@"anyframe",
1536 .anyframe_T,
15371638 .anyerror_void_error_union,
15381639 .error_set,
15391640 .error_set_single,
15401641 .empty_struct,
15411642 .inferred_alloc_const,
15421643 .inferred_alloc_mut,
1543 .@"enum",
1544 .@"struct",
1545 .@"union",
15461644 .@"opaque",
15471645 .var_args_param,
15481646 => false,
......@@ -1617,17 +1715,12 @@ pub const Type = extern union {
16171715 .optional_single_const_pointer,
16181716 .enum_literal,
16191717 .error_union,
1620 .@"anyframe",
1621 .anyframe_T,
16221718 .anyerror_void_error_union,
16231719 .error_set,
16241720 .error_set_single,
16251721 .empty_struct,
16261722 .inferred_alloc_const,
16271723 .inferred_alloc_mut,
1628 .@"enum",
1629 .@"struct",
1630 .@"union",
16311724 .@"opaque",
16321725 .var_args_param,
16331726 => false,
......@@ -1744,17 +1837,12 @@ pub const Type = extern union {
17441837 .optional_single_mut_pointer => unreachable,
17451838 .enum_literal => unreachable,
17461839 .error_union => unreachable,
1747 .@"anyframe" => unreachable,
1748 .anyframe_T => unreachable,
17491840 .anyerror_void_error_union => unreachable,
17501841 .error_set => unreachable,
17511842 .error_set_single => unreachable,
17521843 .empty_struct => unreachable,
17531844 .inferred_alloc_const => unreachable,
17541845 .inferred_alloc_mut => unreachable,
1755 .@"enum" => unreachable,
1756 .@"struct" => unreachable,
1757 .@"union" => unreachable,
17581846 .@"opaque" => unreachable,
17591847 .var_args_param => unreachable,
17601848
......@@ -1897,17 +1985,12 @@ pub const Type = extern union {
18971985 .optional_single_const_pointer,
18981986 .enum_literal,
18991987 .error_union,
1900 .@"anyframe",
1901 .anyframe_T,
19021988 .anyerror_void_error_union,
19031989 .error_set,
19041990 .error_set_single,
19051991 .empty_struct,
19061992 .inferred_alloc_const,
19071993 .inferred_alloc_mut,
1908 .@"enum",
1909 .@"struct",
1910 .@"union",
19111994 .@"opaque",
19121995 .var_args_param,
19131996 => unreachable,
......@@ -1972,17 +2055,12 @@ pub const Type = extern union {
19722055 .optional_single_const_pointer,
19732056 .enum_literal,
19742057 .error_union,
1975 .@"anyframe",
1976 .anyframe_T,
19772058 .anyerror_void_error_union,
19782059 .error_set,
19792060 .error_set_single,
19802061 .empty_struct,
19812062 .inferred_alloc_const,
19822063 .inferred_alloc_mut,
1983 .@"enum",
1984 .@"struct",
1985 .@"union",
19862064 .@"opaque",
19872065 .var_args_param,
19882066 => unreachable,
......@@ -2062,17 +2140,12 @@ pub const Type = extern union {
20622140 .optional_single_const_pointer,
20632141 .enum_literal,
20642142 .error_union,
2065 .@"anyframe",
2066 .anyframe_T,
20672143 .anyerror_void_error_union,
20682144 .error_set,
20692145 .error_set_single,
20702146 .empty_struct,
20712147 .inferred_alloc_const,
20722148 .inferred_alloc_mut,
2073 .@"enum",
2074 .@"struct",
2075 .@"union",
20762149 .@"opaque",
20772150 .var_args_param,
20782151 => false,
......@@ -2148,17 +2221,12 @@ pub const Type = extern union {
21482221 .optional_single_const_pointer,
21492222 .enum_literal,
21502223 .error_union,
2151 .@"anyframe",
2152 .anyframe_T,
21532224 .anyerror_void_error_union,
21542225 .error_set,
21552226 .error_set_single,
21562227 .empty_struct,
21572228 .inferred_alloc_const,
21582229 .inferred_alloc_mut,
2159 .@"enum",
2160 .@"struct",
2161 .@"union",
21622230 .@"opaque",
21632231 .var_args_param,
21642232 => false,
......@@ -2220,17 +2288,12 @@ pub const Type = extern union {
22202288 .optional_single_const_pointer,
22212289 .enum_literal,
22222290 .error_union,
2223 .@"anyframe",
2224 .anyframe_T,
22252291 .anyerror_void_error_union,
22262292 .error_set,
22272293 .error_set_single,
22282294 .empty_struct,
22292295 .inferred_alloc_const,
22302296 .inferred_alloc_mut,
2231 .@"enum",
2232 .@"struct",
2233 .@"union",
22342297 .@"opaque",
22352298 .var_args_param,
22362299 => unreachable,
......@@ -2320,17 +2383,12 @@ pub const Type = extern union {
23202383 .optional_single_const_pointer,
23212384 .enum_literal,
23222385 .error_union,
2323 .@"anyframe",
2324 .anyframe_T,
23252386 .anyerror_void_error_union,
23262387 .error_set,
23272388 .error_set_single,
23282389 .empty_struct,
23292390 .inferred_alloc_const,
23302391 .inferred_alloc_mut,
2331 .@"enum",
2332 .@"struct",
2333 .@"union",
23342392 .@"opaque",
23352393 .var_args_param,
23362394 => false,
......@@ -2441,17 +2499,12 @@ pub const Type = extern union {
24412499 .optional_single_const_pointer,
24422500 .enum_literal,
24432501 .error_union,
2444 .@"anyframe",
2445 .anyframe_T,
24462502 .anyerror_void_error_union,
24472503 .error_set,
24482504 .error_set_single,
24492505 .empty_struct,
24502506 .inferred_alloc_const,
24512507 .inferred_alloc_mut,
2452 .@"enum",
2453 .@"struct",
2454 .@"union",
24552508 .@"opaque",
24562509 .var_args_param,
24572510 => unreachable,
......@@ -2528,17 +2581,12 @@ pub const Type = extern union {
25282581 .optional_single_const_pointer,
25292582 .enum_literal,
25302583 .error_union,
2531 .@"anyframe",
2532 .anyframe_T,
25332584 .anyerror_void_error_union,
25342585 .error_set,
25352586 .error_set_single,
25362587 .empty_struct,
25372588 .inferred_alloc_const,
25382589 .inferred_alloc_mut,
2539 .@"enum",
2540 .@"struct",
2541 .@"union",
25422590 .@"opaque",
25432591 .var_args_param,
25442592 => unreachable,
......@@ -2614,17 +2662,12 @@ pub const Type = extern union {
26142662 .optional_single_const_pointer,
26152663 .enum_literal,
26162664 .error_union,
2617 .@"anyframe",
2618 .anyframe_T,
26192665 .anyerror_void_error_union,
26202666 .error_set,
26212667 .error_set_single,
26222668 .empty_struct,
26232669 .inferred_alloc_const,
26242670 .inferred_alloc_mut,
2625 .@"enum",
2626 .@"struct",
2627 .@"union",
26282671 .@"opaque",
26292672 .var_args_param,
26302673 => unreachable,
......@@ -2700,17 +2743,12 @@ pub const Type = extern union {
27002743 .optional_single_const_pointer,
27012744 .enum_literal,
27022745 .error_union,
2703 .@"anyframe",
2704 .anyframe_T,
27052746 .anyerror_void_error_union,
27062747 .error_set,
27072748 .error_set_single,
27082749 .empty_struct,
27092750 .inferred_alloc_const,
27102751 .inferred_alloc_mut,
2711 .@"enum",
2712 .@"struct",
2713 .@"union",
27142752 .@"opaque",
27152753 .var_args_param,
27162754 => unreachable,
......@@ -2783,17 +2821,12 @@ pub const Type = extern union {
27832821 .optional_single_const_pointer,
27842822 .enum_literal,
27852823 .error_union,
2786 .@"anyframe",
2787 .anyframe_T,
27882824 .anyerror_void_error_union,
27892825 .error_set,
27902826 .error_set_single,
27912827 .empty_struct,
27922828 .inferred_alloc_const,
27932829 .inferred_alloc_mut,
2794 .@"enum",
2795 .@"struct",
2796 .@"union",
27972830 .@"opaque",
27982831 .var_args_param,
27992832 => unreachable,
......@@ -2866,17 +2899,12 @@ pub const Type = extern union {
28662899 .optional_single_const_pointer,
28672900 .enum_literal,
28682901 .error_union,
2869 .@"anyframe",
2870 .anyframe_T,
28712902 .anyerror_void_error_union,
28722903 .error_set,
28732904 .error_set_single,
28742905 .empty_struct,
28752906 .inferred_alloc_const,
28762907 .inferred_alloc_mut,
2877 .@"enum",
2878 .@"struct",
2879 .@"union",
28802908 .@"opaque",
28812909 .var_args_param,
28822910 => unreachable,
......@@ -2949,17 +2977,12 @@ pub const Type = extern union {
29492977 .optional_single_const_pointer,
29502978 .enum_literal,
29512979 .error_union,
2952 .@"anyframe",
2953 .anyframe_T,
29542980 .anyerror_void_error_union,
29552981 .error_set,
29562982 .error_set_single,
29572983 .empty_struct,
29582984 .inferred_alloc_const,
29592985 .inferred_alloc_mut,
2960 .@"enum",
2961 .@"struct",
2962 .@"union",
29632986 .@"opaque",
29642987 .var_args_param,
29652988 => false,
......@@ -3016,8 +3039,6 @@ pub const Type = extern union {
30163039 .optional_single_const_pointer,
30173040 .enum_literal,
30183041 .anyerror_void_error_union,
3019 .anyframe_T,
3020 .@"anyframe",
30213042 .error_union,
30223043 .error_set,
30233044 .error_set_single,
......@@ -3025,10 +3046,6 @@ pub const Type = extern union {
30253046 .var_args_param,
30263047 => return null,
30273048
3028 .@"enum" => @panic("TODO onePossibleValue enum"),
3029 .@"struct" => @panic("TODO onePossibleValue struct"),
3030 .@"union" => @panic("TODO onePossibleValue union"),
3031
30323049 .empty_struct => return Value.initTag(.empty_struct_value),
30333050 .void => return Value.initTag(.void_value),
30343051 .noreturn => return Value.initTag(.unreachable_value),
......@@ -3128,17 +3145,12 @@ pub const Type = extern union {
31283145 .optional_single_const_pointer,
31293146 .enum_literal,
31303147 .error_union,
3131 .@"anyframe",
3132 .anyframe_T,
31333148 .anyerror_void_error_union,
31343149 .error_set,
31353150 .error_set_single,
31363151 .empty_struct,
31373152 .inferred_alloc_const,
31383153 .inferred_alloc_mut,
3139 .@"enum",
3140 .@"struct",
3141 .@"union",
31423154 .@"opaque",
31433155 .var_args_param,
31443156 => return false,
......@@ -3220,8 +3232,6 @@ pub const Type = extern union {
32203232 .optional_single_const_pointer,
32213233 .enum_literal,
32223234 .error_union,
3223 .@"anyframe",
3224 .anyframe_T,
32253235 .anyerror_void_error_union,
32263236 .error_set,
32273237 .error_set_single,
......@@ -3234,10 +3244,7 @@ pub const Type = extern union {
32343244 => unreachable,
32353245
32363246 .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,
3247 .@"opaque" => &self.castTag(.@"opaque").?.data,
32413248 };
32423249 }
32433250
......@@ -3296,6 +3303,10 @@ pub const Type = extern union {
32963303 }
32973304 }
32983305
3306 pub fn isExhaustiveEnum(ty: Type) bool {
3307 return false; // TODO
3308 }
3309
32993310 /// This enum does not directly correspond to `std.builtin.TypeId` because
33003311 /// it has extra enum tags in it, as a way of using less memory. For example,
33013312 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
......@@ -3346,7 +3357,6 @@ pub const Type = extern union {
33463357 fn_ccc_void_no_args,
33473358 single_const_pointer_to_comptime_int,
33483359 anyerror_void_error_union,
3349 @"anyframe",
33503360 const_slice_u8,
33513361 /// This is a special type for variadic parameters of a function call.
33523362 /// Casts to it will validate that the type can be passed to a c calling convetion function.
......@@ -3379,13 +3389,9 @@ pub const Type = extern union {
33793389 optional_single_mut_pointer,
33803390 optional_single_const_pointer,
33813391 error_union,
3382 anyframe_T,
33833392 error_set,
33843393 error_set_single,
33853394 empty_struct,
3386 @"enum",
3387 @"struct",
3388 @"union",
33893395 @"opaque",
33903396
33913397 pub const last_no_payload_tag = Tag.inferred_alloc_const;
......@@ -3435,7 +3441,6 @@ pub const Type = extern union {
34353441 .fn_ccc_void_no_args,
34363442 .single_const_pointer_to_comptime_int,
34373443 .anyerror_void_error_union,
3438 .@"anyframe",
34393444 .const_slice_u8,
34403445 .inferred_alloc_const,
34413446 .inferred_alloc_mut,
......@@ -3457,25 +3462,22 @@ pub const Type = extern union {
34573462 .optional,
34583463 .optional_single_mut_pointer,
34593464 .optional_single_const_pointer,
3460 .anyframe_T,
34613465 => Payload.ElemType,
34623466
34633467 .int_signed,
34643468 .int_unsigned,
34653469 => Payload.Bits,
34663470
3471 .error_set => Payload.ErrorSet,
3472
34673473 .array => Payload.Array,
34683474 .array_sentinel => Payload.ArraySentinel,
34693475 .pointer => Payload.Pointer,
34703476 .function => Payload.Function,
34713477 .error_union => Payload.ErrorUnion,
3472 .error_set => Payload.Decl,
34733478 .error_set_single => Payload.Name,
3474 .empty_struct => Payload.ContainerScope,
3475 .@"enum" => Payload.Enum,
3476 .@"struct" => Payload.Struct,
3477 .@"union" => Payload.Union,
34783479 .@"opaque" => Payload.Opaque,
3480 .empty_struct => Payload.ContainerScope,
34793481 };
34803482 }
34813483
......@@ -3550,6 +3552,13 @@ pub const Type = extern union {
35503552 },
35513553 };
35523554
3555 pub const ErrorSet = struct {
3556 pub const base_tag = Tag.error_set;
3557
3558 base: Payload = Payload{ .tag = base_tag },
3559 data: *Module.ErrorSet,
3560 };
3561
35533562 pub const Pointer = struct {
35543563 pub const base_tag = Tag.pointer;
35553564
......@@ -3598,13 +3607,8 @@ pub const Type = extern union {
35983607
35993608 pub const Opaque = struct {
36003609 base: Payload = .{ .tag = .@"opaque" },
3601
3602 scope: Module.Scope.Container,
3610 data: Module.Scope.Container,
36033611 };
3604
3605 pub const Enum = @import("type/Enum.zig");
3606 pub const Struct = @import("type/Struct.zig");
3607 pub const Union = @import("type/Union.zig");
36083612 };
36093613};
36103614
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+42-68
......@@ -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,18 @@ 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 empty_struct_value,
78 empty_array, // See last_no_payload_tag below.
7779 // After this, the tag requires a payload.
7880
7981 ty,
......@@ -100,14 +102,13 @@ pub const Value = extern union {
100102 float_64,
101103 float_128,
102104 enum_literal,
103 error_set,
104105 @"error",
105106 error_union,
106107 /// This is a special value that tracks a set of types that have been stored
107108 /// to an inferred allocation. It does not support any of the normal value queries.
108109 inferred_alloc,
109110
110 pub const last_no_payload_tag = Tag.bool_false;
111 pub const last_no_payload_tag = Tag.empty_array;
111112 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
112113
113114 pub fn Type(comptime t: Tag) type {
......@@ -120,6 +121,8 @@ pub const Value = extern union {
120121 .i32_type,
121122 .u64_type,
122123 .i64_type,
124 .u128_type,
125 .i128_type,
123126 .usize_type,
124127 .isize_type,
125128 .c_short_type,
......@@ -152,7 +155,6 @@ pub const Value = extern union {
152155 .single_const_pointer_to_comptime_int_type,
153156 .const_slice_u8_type,
154157 .enum_literal_type,
155 .anyframe_type,
156158 .undef,
157159 .zero,
158160 .one,
......@@ -193,7 +195,6 @@ pub const Value = extern union {
193195 .float_32 => Payload.Float_32,
194196 .float_64 => Payload.Float_64,
195197 .float_128 => Payload.Float_128,
196 .error_set => Payload.ErrorSet,
197198 .@"error" => Payload.Error,
198199 .inferred_alloc => Payload.InferredAlloc,
199200 };
......@@ -275,6 +276,8 @@ pub const Value = extern union {
275276 .i32_type,
276277 .u64_type,
277278 .i64_type,
279 .u128_type,
280 .i128_type,
278281 .usize_type,
279282 .isize_type,
280283 .c_short_type,
......@@ -307,7 +310,6 @@ pub const Value = extern union {
307310 .single_const_pointer_to_comptime_int_type,
308311 .const_slice_u8_type,
309312 .enum_literal_type,
310 .anyframe_type,
311313 .undef,
312314 .zero,
313315 .one,
......@@ -400,7 +402,6 @@ pub const Value = extern union {
400402 return Value{ .ptr_otherwise = &new_payload.base };
401403 },
402404
403 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
404405 .inferred_alloc => unreachable,
405406 }
406407 }
......@@ -429,6 +430,8 @@ pub const Value = extern union {
429430 .i32_type => return out_stream.writeAll("i32"),
430431 .u64_type => return out_stream.writeAll("u64"),
431432 .i64_type => return out_stream.writeAll("i64"),
433 .u128_type => return out_stream.writeAll("u128"),
434 .i128_type => return out_stream.writeAll("i128"),
432435 .isize_type => return out_stream.writeAll("isize"),
433436 .usize_type => return out_stream.writeAll("usize"),
434437 .c_short_type => return out_stream.writeAll("c_short"),
......@@ -461,7 +464,6 @@ pub const Value = extern union {
461464 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
462465 .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
463466 .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
464 .anyframe_type => return out_stream.writeAll("anyframe"),
465467
466468 // TODO this should print `NAME{}`
467469 .empty_struct_value => return out_stream.writeAll("struct {}{}"),
......@@ -510,15 +512,6 @@ pub const Value = extern union {
510512 .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}),
511513 .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}),
512514 .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 },
522515 .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}),
523516 // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that
524517 .error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}),
......@@ -557,6 +550,8 @@ pub const Value = extern union {
557550 .i32_type => Type.initTag(.i32),
558551 .u64_type => Type.initTag(.u64),
559552 .i64_type => Type.initTag(.i64),
553 .u128_type => Type.initTag(.u128),
554 .i128_type => Type.initTag(.i128),
560555 .usize_type => Type.initTag(.usize),
561556 .isize_type => Type.initTag(.isize),
562557 .c_short_type => Type.initTag(.c_short),
......@@ -589,7 +584,6 @@ pub const Value = extern union {
589584 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
590585 .const_slice_u8_type => Type.initTag(.const_slice_u8),
591586 .enum_literal_type => Type.initTag(.enum_literal),
592 .anyframe_type => Type.initTag(.@"anyframe"),
593587
594588 .int_type => {
595589 const payload = self.castTag(.int_type).?.data;
......@@ -602,10 +596,6 @@ pub const Value = extern union {
602596 };
603597 return Type.initPayload(&new.base);
604598 },
605 .error_set => {
606 const payload = self.castTag(.error_set).?.data;
607 return Type.Tag.error_set.create(allocator, payload.decl);
608 },
609599
610600 .undef,
611601 .zero,
......@@ -654,6 +644,8 @@ pub const Value = extern union {
654644 .i32_type,
655645 .u64_type,
656646 .i64_type,
647 .u128_type,
648 .i128_type,
657649 .usize_type,
658650 .isize_type,
659651 .c_short_type,
......@@ -686,7 +678,6 @@ pub const Value = extern union {
686678 .single_const_pointer_to_comptime_int_type,
687679 .const_slice_u8_type,
688680 .enum_literal_type,
689 .anyframe_type,
690681 .null_value,
691682 .function,
692683 .extern_fn,
......@@ -704,7 +695,6 @@ pub const Value = extern union {
704695 .unreachable_value,
705696 .empty_array,
706697 .enum_literal,
707 .error_set,
708698 .error_union,
709699 .@"error",
710700 .empty_struct_value,
......@@ -741,6 +731,8 @@ pub const Value = extern union {
741731 .i32_type,
742732 .u64_type,
743733 .i64_type,
734 .u128_type,
735 .i128_type,
744736 .usize_type,
745737 .isize_type,
746738 .c_short_type,
......@@ -773,7 +765,6 @@ pub const Value = extern union {
773765 .single_const_pointer_to_comptime_int_type,
774766 .const_slice_u8_type,
775767 .enum_literal_type,
776 .anyframe_type,
777768 .null_value,
778769 .function,
779770 .extern_fn,
......@@ -791,7 +782,6 @@ pub const Value = extern union {
791782 .unreachable_value,
792783 .empty_array,
793784 .enum_literal,
794 .error_set,
795785 .@"error",
796786 .error_union,
797787 .empty_struct_value,
......@@ -828,6 +818,8 @@ pub const Value = extern union {
828818 .i32_type,
829819 .u64_type,
830820 .i64_type,
821 .u128_type,
822 .i128_type,
831823 .usize_type,
832824 .isize_type,
833825 .c_short_type,
......@@ -860,7 +852,6 @@ pub const Value = extern union {
860852 .single_const_pointer_to_comptime_int_type,
861853 .const_slice_u8_type,
862854 .enum_literal_type,
863 .anyframe_type,
864855 .null_value,
865856 .function,
866857 .extern_fn,
......@@ -878,7 +869,6 @@ pub const Value = extern union {
878869 .unreachable_value,
879870 .empty_array,
880871 .enum_literal,
881 .error_set,
882872 .@"error",
883873 .error_union,
884874 .empty_struct_value,
......@@ -942,6 +932,8 @@ pub const Value = extern union {
942932 .i32_type,
943933 .u64_type,
944934 .i64_type,
935 .u128_type,
936 .i128_type,
945937 .usize_type,
946938 .isize_type,
947939 .c_short_type,
......@@ -974,7 +966,6 @@ pub const Value = extern union {
974966 .single_const_pointer_to_comptime_int_type,
975967 .const_slice_u8_type,
976968 .enum_literal_type,
977 .anyframe_type,
978969 .null_value,
979970 .function,
980971 .extern_fn,
......@@ -993,7 +984,6 @@ pub const Value = extern union {
993984 .unreachable_value,
994985 .empty_array,
995986 .enum_literal,
996 .error_set,
997987 .@"error",
998988 .error_union,
999989 .empty_struct_value,
......@@ -1034,6 +1024,8 @@ pub const Value = extern union {
10341024 .i32_type,
10351025 .u64_type,
10361026 .i64_type,
1027 .u128_type,
1028 .i128_type,
10371029 .usize_type,
10381030 .isize_type,
10391031 .c_short_type,
......@@ -1066,7 +1058,6 @@ pub const Value = extern union {
10661058 .single_const_pointer_to_comptime_int_type,
10671059 .const_slice_u8_type,
10681060 .enum_literal_type,
1069 .anyframe_type,
10701061 .null_value,
10711062 .function,
10721063 .extern_fn,
......@@ -1084,7 +1075,6 @@ pub const Value = extern union {
10841075 .unreachable_value,
10851076 .empty_array,
10861077 .enum_literal,
1087 .error_set,
10881078 .@"error",
10891079 .error_union,
10901080 .empty_struct_value,
......@@ -1191,6 +1181,8 @@ pub const Value = extern union {
11911181 .i32_type,
11921182 .u64_type,
11931183 .i64_type,
1184 .u128_type,
1185 .i128_type,
11941186 .usize_type,
11951187 .isize_type,
11961188 .c_short_type,
......@@ -1223,7 +1215,6 @@ pub const Value = extern union {
12231215 .single_const_pointer_to_comptime_int_type,
12241216 .const_slice_u8_type,
12251217 .enum_literal_type,
1226 .anyframe_type,
12271218 .bool_true,
12281219 .bool_false,
12291220 .null_value,
......@@ -1244,7 +1235,6 @@ pub const Value = extern union {
12441235 .void_value,
12451236 .unreachable_value,
12461237 .enum_literal,
1247 .error_set,
12481238 .@"error",
12491239 .error_union,
12501240 .empty_struct_value,
......@@ -1275,6 +1265,8 @@ pub const Value = extern union {
12751265 .i32_type,
12761266 .u64_type,
12771267 .i64_type,
1268 .u128_type,
1269 .i128_type,
12781270 .usize_type,
12791271 .isize_type,
12801272 .c_short_type,
......@@ -1307,7 +1299,6 @@ pub const Value = extern union {
13071299 .single_const_pointer_to_comptime_int_type,
13081300 .const_slice_u8_type,
13091301 .enum_literal_type,
1310 .anyframe_type,
13111302 .null_value,
13121303 .function,
13131304 .extern_fn,
......@@ -1322,7 +1313,6 @@ pub const Value = extern union {
13221313 .unreachable_value,
13231314 .empty_array,
13241315 .enum_literal,
1325 .error_set,
13261316 .@"error",
13271317 .error_union,
13281318 .empty_struct_value,
......@@ -1427,6 +1417,8 @@ pub const Value = extern union {
14271417 .i32_type,
14281418 .u64_type,
14291419 .i64_type,
1420 .u128_type,
1421 .i128_type,
14301422 .usize_type,
14311423 .isize_type,
14321424 .c_short_type,
......@@ -1459,18 +1451,12 @@ pub const Value = extern union {
14591451 .single_const_pointer_to_comptime_int_type,
14601452 .const_slice_u8_type,
14611453 .enum_literal_type,
1462 .anyframe_type,
14631454 .ty,
14641455 => {
1465 // Directly return Type.hash, toType can only fail for .int_type and .error_set.
1456 // Directly return Type.hash, toType can only fail for .int_type.
14661457 var allocator = std.heap.FixedBufferAllocator.init(&[_]u8{});
14671458 return (self.toType(&allocator.allocator) catch unreachable).hash();
14681459 },
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 },
14741460 .int_type => {
14751461 const payload = self.castTag(.int_type).?.data;
14761462 var int_payload = Type.Payload.Bits{
......@@ -1585,6 +1571,8 @@ pub const Value = extern union {
15851571 .i32_type,
15861572 .u64_type,
15871573 .i64_type,
1574 .u128_type,
1575 .i128_type,
15881576 .usize_type,
15891577 .isize_type,
15901578 .c_short_type,
......@@ -1617,7 +1605,6 @@ pub const Value = extern union {
16171605 .single_const_pointer_to_comptime_int_type,
16181606 .const_slice_u8_type,
16191607 .enum_literal_type,
1620 .anyframe_type,
16211608 .zero,
16221609 .one,
16231610 .bool_true,
......@@ -1641,7 +1628,6 @@ pub const Value = extern union {
16411628 .unreachable_value,
16421629 .empty_array,
16431630 .enum_literal,
1644 .error_set,
16451631 .@"error",
16461632 .error_union,
16471633 .empty_struct_value,
......@@ -1672,6 +1658,8 @@ pub const Value = extern union {
16721658 .i32_type,
16731659 .u64_type,
16741660 .i64_type,
1661 .u128_type,
1662 .i128_type,
16751663 .usize_type,
16761664 .isize_type,
16771665 .c_short_type,
......@@ -1704,7 +1692,6 @@ pub const Value = extern union {
17041692 .single_const_pointer_to_comptime_int_type,
17051693 .const_slice_u8_type,
17061694 .enum_literal_type,
1707 .anyframe_type,
17081695 .zero,
17091696 .one,
17101697 .bool_true,
......@@ -1728,7 +1715,6 @@ pub const Value = extern union {
17281715 .void_value,
17291716 .unreachable_value,
17301717 .enum_literal,
1731 .error_set,
17321718 .@"error",
17331719 .error_union,
17341720 .empty_struct_value,
......@@ -1776,6 +1762,8 @@ pub const Value = extern union {
17761762 .i32_type,
17771763 .u64_type,
17781764 .i64_type,
1765 .u128_type,
1766 .i128_type,
17791767 .usize_type,
17801768 .isize_type,
17811769 .c_short_type,
......@@ -1808,7 +1796,6 @@ pub const Value = extern union {
18081796 .single_const_pointer_to_comptime_int_type,
18091797 .const_slice_u8_type,
18101798 .enum_literal_type,
1811 .anyframe_type,
18121799 .zero,
18131800 .one,
18141801 .empty_array,
......@@ -1832,7 +1819,6 @@ pub const Value = extern union {
18321819 .float_128,
18331820 .void_value,
18341821 .enum_literal,
1835 .error_set,
18361822 .@"error",
18371823 .error_union,
18381824 .empty_struct_value,
......@@ -1858,6 +1844,8 @@ pub const Value = extern union {
18581844 .i32_type,
18591845 .u64_type,
18601846 .i64_type,
1847 .u128_type,
1848 .i128_type,
18611849 .usize_type,
18621850 .isize_type,
18631851 .c_short_type,
......@@ -1890,7 +1878,6 @@ pub const Value = extern union {
18901878 .single_const_pointer_to_comptime_int_type,
18911879 .const_slice_u8_type,
18921880 .enum_literal_type,
1893 .anyframe_type,
18941881 .zero,
18951882 .one,
18961883 .null_value,
......@@ -1915,7 +1902,6 @@ pub const Value = extern union {
19151902 .float_128,
19161903 .void_value,
19171904 .enum_literal,
1918 .error_set,
19191905 .empty_struct_value,
19201906 => null,
19211907
......@@ -1960,6 +1946,8 @@ pub const Value = extern union {
19601946 .i32_type,
19611947 .u64_type,
19621948 .i64_type,
1949 .u128_type,
1950 .i128_type,
19631951 .usize_type,
19641952 .isize_type,
19651953 .c_short_type,
......@@ -1992,8 +1980,6 @@ pub const Value = extern union {
19921980 .single_const_pointer_to_comptime_int_type,
19931981 .const_slice_u8_type,
19941982 .enum_literal_type,
1995 .anyframe_type,
1996 .error_set,
19971983 => true,
19981984
19991985 .zero,
......@@ -2137,18 +2123,6 @@ pub const Value = extern union {
21372123 data: f128,
21382124 };
21392125
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
21522126 pub const Error = struct {
21532127 base: Payload = .{ .tag = .@"error" },
21542128 data: struct {
src/zir.zig+1719-1593
......@@ -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,636 @@ 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,
134273 /// Declares the beginning of a statement. Used for debug info.
135 dbg_stmt,
274 /// Uses the `node` union field.
275 dbg_stmt_node,
136276 /// Represents a pointer to a global decl.
277 /// Uses the `pl_node` union field. `payload_index` is into `decls`.
137278 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.
279 /// Equivalent to a decl_ref followed by load.
280 /// Uses the `pl_node` union field. `payload_index` is into `decls`.
141281 decl_val,
142 /// Load the value from a pointer.
143 deref,
282 /// Load the value from a pointer. Assumes `x.*` syntax.
283 /// Uses `un_node` field. AST node is the `x.*` syntax.
284 load,
144285 /// Arithmetic division. Asserts no integer overflow.
286 /// Uses the `pl_node` union field. Payload is `Bin`.
145287 div,
146288 /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at
147 /// the provided index.
289 /// the provided index. Uses the `bin` union field. Source location is implied
290 /// to be the same as the previous instruction.
148291 elem_ptr,
292 /// Same as `elem_ptr` except also stores a source location node.
293 /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
294 elem_ptr_node,
149295 /// Given an array, slice, or pointer, returns the element at the provided index.
296 /// Uses the `bin` union field. Source location is implied to be the same
297 /// as the previous instruction.
150298 elem_val,
299 /// Same as `elem_val` except also stores a source location node.
300 /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
301 elem_val_node,
302 /// This instruction has been deleted late in the astgen phase. It must
303 /// be ignored, and the corresponding `Data` is undefined.
304 elided,
151305 /// Emits a compile error if the operand is not `void`.
306 /// Uses the `un_node` field.
152307 ensure_result_used,
153308 /// Emits a compile error if an error is ignored.
309 /// Uses the `un_node` field.
154310 ensure_result_non_error,
155311 /// Create a `E!T` type.
312 /// Uses the `pl_node` field with `Bin` payload.
156313 error_union_type,
157 /// Create an error set.
158 error_set,
159 /// `error.Foo` syntax.
314 /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
160315 error_value,
161 /// Export the provided Decl as the provided name in the compilation's output object file.
162 @"export",
163316 /// 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.
317 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
318 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
165319 field_ptr,
166320 /// 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.
321 /// The field name is stored in string_bytes. Used by a.b syntax.
322 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
168323 field_val,
169324 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
170325 /// to the named field. The field name is a comptime instruction. Used by @field.
326 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
171327 field_ptr_named,
172328 /// Given a struct or object that contains virtual fields, returns the named field.
173329 /// The field name is a comptime instruction. Used by @field.
330 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
174331 field_val_named,
175 /// Convert a larger float type to any other float type, possibly causing a loss of precision.
332 /// Convert a larger float type to any other float type, possibly causing
333 /// a loss of precision.
334 /// Uses the `pl_node` field. AST is the `@floatCast` syntax.
335 /// Payload is `Bin` with lhs as the dest type, rhs the operand.
176336 floatcast,
177 /// Declare a function body.
178 @"fn",
179337 /// Returns a function type, assuming unspecified calling convention.
338 /// Uses the `pl_node` union field. `payload_index` points to a `FnType`.
180339 fn_type,
181340 /// Same as `fn_type` but the function is variadic.
182341 fn_type_var_args,
183342 /// Returns a function type, with a calling convention instruction operand.
343 /// Uses the `pl_node` union field. `payload_index` points to a `FnTypeCc`.
184344 fn_type_cc,
185345 /// Same as `fn_type_cc` but the function is variadic.
186346 fn_type_cc_var_args,
187 /// @import(operand)
347 /// `@import(operand)`.
348 /// Uses the `un_node` field.
188349 import,
189 /// Integer literal.
350 /// Integer literal that fits in a u64. Uses the int union value.
190351 int,
191352 /// Convert an integer value to another integer type, asserting that the destination type
192353 /// can hold the same mathematical value.
354 /// Uses the `pl_node` field. AST is the `@intCast` syntax.
355 /// Payload is `Bin` with lhs as the dest type, rhs the operand.
193356 intcast,
194357 /// Make an integer type out of signedness and bit count.
358 /// Payload is `int_type`
195359 int_type,
360 /// Convert an error type to `u16`
361 error_to_int,
362 /// Convert a `u16` to `anyerror`
363 int_to_error,
196364 /// Return a boolean false if an optional is null. `x != null`
365 /// Uses the `un_node` field.
197366 is_non_null,
198367 /// Return a boolean true if an optional is null. `x == null`
368 /// Uses the `un_node` field.
199369 is_null,
200370 /// Return a boolean false if an optional is null. `x.* != null`
371 /// Uses the `un_node` field.
201372 is_non_null_ptr,
202373 /// Return a boolean true if an optional is null. `x.* == null`
374 /// Uses the `un_node` field.
203375 is_null_ptr,
204376 /// Return a boolean true if value is an error
377 /// Uses the `un_node` field.
205378 is_err,
206379 /// Return a boolean true if dereferenced pointer is an error
380 /// Uses the `un_node` field.
207381 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.
382 /// A labeled block of code that loops forever. At the end of the body will have either
383 /// a `repeat` instruction or a `repeat_inline` instruction.
384 /// Uses the `pl_node` field. The AST node is either a for loop or while loop.
385 /// This ZIR instruction is needed because TZIR does not (yet?) match ZIR, and Sema
386 /// needs to emit more than 1 TZIR block for this instruction.
387 /// The payload is `Block`.
210388 loop,
389 /// Sends runtime control flow back to the beginning of the current block.
390 /// Uses the `node` field.
391 repeat,
392 /// Sends comptime control flow back to the beginning of the current block.
393 /// Uses the `node` field.
394 repeat_inline,
211395 /// Merge two error sets into one, `E1 || E2`.
396 /// Uses the `pl_node` field with payload `Bin`.
212397 merge_error_sets,
213398 /// Ambiguously remainder division or modulus. If the computation would possibly have
214399 /// a different value depending on whether the operation is remainder division or modulus,
215400 /// a compile error is emitted. Otherwise the computation is performed.
401 /// Uses the `pl_node` union field. Payload is `Bin`.
216402 mod_rem,
217403 /// Arithmetic multiplication. Asserts no integer overflow.
404 /// Uses the `pl_node` union field. Payload is `Bin`.
218405 mul,
219406 /// Twos complement wrapping integer multiplication.
407 /// Uses the `pl_node` union field. Payload is `Bin`.
220408 mulwrap,
221 /// An await inside a nosuspend scope.
222 nosuspend_await,
223409 /// 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`?
410 /// type of the parameter. The only usage of this instruction is for the
411 /// result location of parameters of function calls. In the case of a function's
412 /// parameter type being `anytype`, it is the type coercion's job to detect this
413 /// scenario and skip the coercion, so that semantic analysis of this instruction
414 /// is not in a position where it must create an invalid type.
415 /// Uses the `param_type` union field.
225416 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,
229417 /// Convert a pointer to a `usize` integer.
418 /// Uses the `un_node` field. The AST node is the builtin fn call node.
230419 ptrtoint,
231420 /// Turns an R-Value into a const L-Value. In other words, it takes a value,
232421 /// stores it in a memory location, and returns a const pointer to it. If the value
233422 /// is `comptime`, the memory location is global static constant data. Otherwise,
234423 /// the memory location is in the stack frame, local to the scope containing the
235424 /// instruction.
425 /// Uses the `un_tok` union field.
236426 ref,
237 /// Resume an async function.
238 @"resume",
239427 /// Obtains a pointer to the return value.
428 /// Uses the `node` union field.
240429 ret_ptr,
241430 /// Obtains the return type of the in-scope function.
431 /// Uses the `node` union field.
242432 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,
433 /// Sends control flow back to the function's callee.
434 /// Includes an operand as the return value.
435 /// Includes an AST node source location.
436 /// Uses the `un_node` union field.
437 ret_node,
438 /// Sends control flow back to the function's callee.
439 /// Includes an operand as the return value.
440 /// Includes a token source location.
441 /// Uses the `un_tok` union field.
442 ret_tok,
443 /// Same as `ret_tok` except the operand needs to get coerced to the function's
444 /// return type.
445 ret_coerce,
247446 /// Changes the maximum number of backwards branches that compile-time
248447 /// code execution can use before giving up and making a compile error.
448 /// Uses the `un_node` union field.
249449 set_eval_branch_quota,
250450 /// Integer shift-left. Zeroes are shifted in from the right hand side.
451 /// Uses the `pl_node` union field. Payload is `Bin`.
251452 shl,
252453 /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.
454 /// Uses the `pl_node` union field. Payload is `Bin`.
253455 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
456 /// Create a pointer type that does not have a sentinel, alignment, or bit range specified.
457 /// Uses the `ptr_type_simple` union field.
458 ptr_type_simple,
459 /// Create a pointer type which can have a sentinel, alignment, and/or bit range.
460 /// Uses the `ptr_type` union field.
271461 ptr_type,
272462 /// Each `store_to_inferred_ptr` puts the type of the stored value into a set,
273463 /// and then `resolve_inferred_alloc` triggers peer type resolution on the set.
274464 /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which
275465 /// is the allocation that needs to have its type inferred.
466 /// Uses the `un_node` field. The AST node is the var decl.
276467 resolve_inferred_alloc,
277 /// Slice operation `array_ptr[start..end:sentinel]`
278 slice,
279 /// Slice operation with just start `lhs[rhs..]`
468 /// Slice operation `lhs[rhs..]`. No sentinel and no end offset.
469 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceStart`.
280470 slice_start,
281 /// Write a value to a pointer. For loading, see `deref`.
471 /// Slice operation `array_ptr[start..end]`. No sentinel.
472 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceEnd`.
473 slice_end,
474 /// Slice operation `array_ptr[start..end:sentinel]`.
475 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
476 slice_sentinel,
477 /// Write a value to a pointer. For loading, see `load`.
478 /// Source location is assumed to be same as previous instruction.
479 /// Uses the `bin` union field.
282480 store,
481 /// Same as `store` except provides a source location.
482 /// Uses the `pl_node` union field. Payload is `Bin`.
483 store_node,
283484 /// Same as `store` but the type of the value being stored will be used to infer
284485 /// the block type. The LHS is the pointer to store to.
486 /// Uses the `bin` union field.
285487 store_to_block_ptr,
286488 /// Same as `store` but the type of the value being stored will be used to infer
287489 /// the pointer type.
490 /// Uses the `bin` union field - Astgen.zig depends on the ability to change
491 /// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr`
492 /// without changing the data.
288493 store_to_inferred_ptr,
289494 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
495 /// Uses the `str` union field.
290496 str,
291 /// Create a struct type.
292 struct_type,
293497 /// Arithmetic subtraction. Asserts no integer overflow.
498 /// Uses the `pl_node` union field. Payload is `Bin`.
294499 sub,
295500 /// Twos complement wrapping integer subtraction.
501 /// Uses the `pl_node` union field. Payload is `Bin`.
296502 subwrap,
503 /// Arithmetic negation. Asserts no integer overflow.
504 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.
505 /// Uses `un_node`.
506 negate,
507 /// Twos complement wrapping integer negation.
508 /// Same as subwrap with a lhs of 0, split into a separate instruction to save memory.
509 /// Uses `un_node`.
510 negate_wrap,
297511 /// Returns the type of a value.
512 /// Uses the `un_tok` field.
298513 typeof,
299 /// Is the builtin @TypeOf which returns the type after peertype resolution of one or more params
514 /// Given a value which is a pointer, returns the element type.
515 /// Uses the `un_node` field.
516 typeof_elem,
517 /// The builtin `@TypeOf` which returns the type after Peer Type Resolution
518 /// of one or more params.
519 /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`.
300520 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,
521 /// Asserts control-flow will not reach this instruction (`unreachable`).
522 /// Uses the `unreachable` union field.
523 @"unreachable",
308524 /// Bitwise XOR. `^`
525 /// Uses the `pl_node` union field. Payload is `Bin`.
309526 xor,
310527 /// Create an optional type '?T'
528 /// Uses the `un_node` field.
311529 optional_type,
312530 /// Create an optional type '?T'. The operand is a pointer value. The optional type will
313531 /// be the type of the pointer element, wrapped in an optional.
532 /// Uses the `un_node` field.
314533 optional_type_from_ptr_elem,
315 /// Create a union type.
316 union_type,
317534 /// ?T => T with safety.
318535 /// Given an optional value, returns the payload value, with a safety check that
319536 /// the value is non-null. Used for `orelse`, `if` and `while`.
537 /// Uses the `un_node` field.
320538 optional_payload_safe,
321539 /// ?T => T without safety.
322540 /// Given an optional value, returns the payload value. No safety checks.
541 /// Uses the `un_node` field.
323542 optional_payload_unsafe,
324543 /// *?T => *T with safety.
325544 /// Given a pointer to an optional value, returns a pointer to the payload value,
326545 /// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`.
546 /// Uses the `un_node` field.
327547 optional_payload_safe_ptr,
328548 /// *?T => *T without safety.
329549 /// Given a pointer to an optional value, returns a pointer to the payload value.
330550 /// No safety checks.
551 /// Uses the `un_node` field.
331552 optional_payload_unsafe_ptr,
332553 /// E!T => T with safety.
333554 /// Given an error union value, returns the payload value, with a safety check
334555 /// that the value is not an error. Used for catch, if, and while.
556 /// Uses the `un_node` field.
335557 err_union_payload_safe,
336558 /// E!T => T without safety.
337559 /// Given an error union value, returns the payload value. No safety checks.
560 /// Uses the `un_node` field.
338561 err_union_payload_unsafe,
339562 /// *E!T => *T with safety.
340563 /// Given a pointer to an error union value, returns a pointer to the payload value,
341564 /// with a safety check that the value is not an error. Used for catch, if, and while.
565 /// Uses the `un_node` field.
342566 err_union_payload_safe_ptr,
343567 /// *E!T => *T without safety.
344568 /// Given a pointer to a error union value, returns a pointer to the payload value.
345569 /// No safety checks.
570 /// Uses the `un_node` field.
346571 err_union_payload_unsafe_ptr,
347572 /// E!T => E without safety.
348573 /// Given an error union value, returns the error code. No safety checks.
574 /// Uses the `un_node` field.
349575 err_union_code,
350576 /// *E!T => E without safety.
351577 /// Given a pointer to an error union value, returns the error code. No safety checks.
578 /// Uses the `un_node` field.
352579 err_union_code_ptr,
353580 /// Takes a *E!T and raises a compiler error if T != void
581 /// Uses the `un_tok` field.
354582 ensure_err_payload_void,
355 /// Create a enum literal,
583 /// An enum literal. Uses the `str_tok` union field.
356584 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 }
585 /// An enum literal 8 or fewer bytes. No source location.
586 /// Uses the `small_str` field.
587 enum_literal_small,
588 /// A switch expression. Uses the `pl_node` union field.
589 /// AST node is the switch, payload is `SwitchBlock`.
590 /// All prongs of target handled.
591 switch_block,
592 /// Same as switch_block, except one or more prongs have multiple items.
593 switch_block_multi,
594 /// Same as switch_block, except has an else prong.
595 switch_block_else,
596 /// Same as switch_block_else, except one or more prongs have multiple items.
597 switch_block_else_multi,
598 /// Same as switch_block, except has an underscore prong.
599 switch_block_under,
600 /// Same as switch_block, except one or more prongs have multiple items.
601 switch_block_under_multi,
602 /// Same as `switch_block` but the target is a pointer to the value being switched on.
603 switch_block_ref,
604 /// Same as `switch_block_multi` but the target is a pointer to the value being switched on.
605 switch_block_ref_multi,
606 /// Same as `switch_block_else` but the target is a pointer to the value being switched on.
607 switch_block_ref_else,
608 /// Same as `switch_block_else_multi` but the target is a pointer to the
609 /// value being switched on.
610 switch_block_ref_else_multi,
611 /// Same as `switch_block_under` but the target is a pointer to the value
612 /// being switched on.
613 switch_block_ref_under,
614 /// Same as `switch_block_under_multi` but the target is a pointer to
615 /// the value being switched on.
616 switch_block_ref_under_multi,
617 /// Produces the capture value for a switch prong.
618 /// Uses the `switch_capture` field.
619 switch_capture,
620 /// Produces the capture value for a switch prong.
621 /// Result is a pointer to the value.
622 /// Uses the `switch_capture` field.
623 switch_capture_ref,
624 /// Produces the capture value for a switch prong.
625 /// The prong is one of the multi cases.
626 /// Uses the `switch_capture` field.
627 switch_capture_multi,
628 /// Produces the capture value for a switch prong.
629 /// The prong is one of the multi cases.
630 /// Result is a pointer to the value.
631 /// Uses the `switch_capture` field.
632 switch_capture_multi_ref,
633 /// Produces the capture value for the else/'_' switch prong.
634 /// Uses the `switch_capture` field.
635 switch_capture_else,
636 /// Produces the capture value for the else/'_' switch prong.
637 /// Result is a pointer to the value.
638 /// Uses the `switch_capture` field.
639 switch_capture_else_ref,
527640
528641 /// Returns whether the instruction is one of the control flow "noreturn" types.
529642 /// Function calls do not count.
......@@ -540,23 +653,28 @@ pub const Inst = struct {
540653 .array_type,
541654 .array_type_sentinel,
542655 .indexable_ptr_len,
543 .arg,
544656 .as,
657 .as_node,
545658 .@"asm",
659 .asm_volatile,
546660 .bit_and,
547661 .bitcast,
548 .bitcast_ref,
549662 .bitcast_result_ptr,
550663 .bit_or,
551664 .block,
552 .block_flat,
553 .block_comptime,
554 .block_comptime_flat,
665 .block_inline,
666 .loop,
667 .bool_br_and,
668 .bool_br_or,
555669 .bool_not,
556670 .bool_and,
557671 .bool_or,
558672 .breakpoint,
559673 .call,
674 .call_chkused,
675 .call_compile_time,
676 .call_none,
677 .call_none_chkused,
560678 .cmp_lt,
561679 .cmp_lte,
562680 .cmp_eq,
......@@ -565,23 +683,22 @@ pub const Inst = struct {
565683 .cmp_neq,
566684 .coerce_result_ptr,
567685 .@"const",
568 .dbg_stmt,
686 .dbg_stmt_node,
569687 .decl_ref,
570 .decl_ref_str,
571688 .decl_val,
572 .deref,
689 .load,
573690 .div,
574691 .elem_ptr,
575692 .elem_val,
693 .elem_ptr_node,
694 .elem_val_node,
576695 .ensure_result_used,
577696 .ensure_result_non_error,
578 .@"export",
579697 .floatcast,
580698 .field_ptr,
581699 .field_val,
582700 .field_ptr_named,
583701 .field_val_named,
584 .@"fn",
585702 .fn_type,
586703 .fn_type_var_args,
587704 .fn_type_cc,
......@@ -599,28 +716,23 @@ pub const Inst = struct {
599716 .mul,
600717 .mulwrap,
601718 .param_type,
602 .primitive,
603719 .ptrtoint,
604720 .ref,
605721 .ret_ptr,
606722 .ret_type,
607723 .shl,
608724 .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,
617725 .store,
726 .store_node,
618727 .store_to_block_ptr,
619728 .store_to_inferred_ptr,
620729 .str,
621730 .sub,
622731 .subwrap,
732 .negate,
733 .negate_wrap,
623734 .typeof,
735 .typeof_elem,
624736 .xor,
625737 .optional_type,
626738 .optional_type_from_ptr_elem,
......@@ -634,1436 +746,1450 @@ pub const Inst = struct {
634746 .err_union_payload_unsafe_ptr,
635747 .err_union_code,
636748 .err_union_code_ptr,
749 .error_to_int,
750 .int_to_error,
637751 .ptr_type,
752 .ptr_type_simple,
638753 .ensure_err_payload_void,
639754 .enum_literal,
755 .enum_literal_small,
640756 .merge_error_sets,
641 .anyframe_type,
642757 .error_union_type,
643758 .bit_not,
644 .error_set,
645759 .error_value,
646 .slice,
647760 .slice_start,
761 .slice_end,
762 .slice_sentinel,
648763 .import,
649764 .typeof_peer,
650765 .resolve_inferred_alloc,
651766 .set_eval_branch_quota,
652767 .compile_log,
653 .enum_type,
654 .union_type,
655 .struct_type,
656 .void_value,
657 .switch_range,
658 .@"resume",
659 .@"await",
660 .nosuspend_await,
768 .elided,
769 .switch_capture,
770 .switch_capture_ref,
771 .switch_capture_multi,
772 .switch_capture_multi_ref,
773 .switch_capture_else,
774 .switch_capture_else_ref,
775 .switch_block,
776 .switch_block_multi,
777 .switch_block_else,
778 .switch_block_else_multi,
779 .switch_block_under,
780 .switch_block_under_multi,
781 .switch_block_ref,
782 .switch_block_ref_multi,
783 .switch_block_ref_else,
784 .switch_block_ref_else_multi,
785 .switch_block_ref_under,
786 .switch_block_ref_under_multi,
661787 => false,
662788
663789 .@"break",
664 .break_void,
790 .break_inline,
665791 .condbr,
792 .condbr_inline,
666793 .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,
794 .ret_node,
795 .ret_tok,
796 .ret_coerce,
797 .@"unreachable",
798 .repeat,
799 .repeat_inline,
679800 => true,
680801 };
681802 }
682803 };
683804
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,
805 /// The position of a ZIR instruction within the `Code` instructions array.
806 pub const Index = u32;
807
808 /// A reference to a TypedValue, parameter of the current function,
809 /// or ZIR instruction.
810 ///
811 /// If the Ref has a tag in this enum, it refers to a TypedValue which may be
812 /// retrieved with Ref.toTypedValue().
813 ///
814 /// If the value of a Ref does not have a tag, it referes to either a parameter
815 /// of the current function or a ZIR instruction.
816 ///
817 /// The first values after the the last tag refer to parameters which may be
818 /// derived by subtracting typed_value_map.len.
819 ///
820 /// All further values refer to ZIR instructions which may be derived by
821 /// subtracting typed_value_map.len and the number of parameters.
822 ///
823 /// When adding a tag to this enum, consider adding a corresponding entry to
824 /// `simple_types` in astgen.
825 ///
826 /// The tag type is specified so that it is safe to bitcast between `[]u32`
827 /// and `[]Ref`.
828 pub const Ref = enum(u32) {
829 /// This Ref does not correspond to any ZIR instruction or constant
830 /// value and may instead be used as a sentinel to indicate null.
831 none,
832
833 u8_type,
834 i8_type,
835 u16_type,
836 i16_type,
837 u32_type,
838 i32_type,
839 u64_type,
840 i64_type,
841 usize_type,
842 isize_type,
843 c_short_type,
844 c_ushort_type,
845 c_int_type,
846 c_uint_type,
847 c_long_type,
848 c_ulong_type,
849 c_longlong_type,
850 c_ulonglong_type,
851 c_longdouble_type,
852 f16_type,
853 f32_type,
854 f64_type,
855 f128_type,
856 c_void_type,
857 bool_type,
858 void_type,
859 type_type,
860 anyerror_type,
861 comptime_int_type,
862 comptime_float_type,
863 noreturn_type,
864 null_type,
865 undefined_type,
866 fn_noreturn_no_args_type,
867 fn_void_no_args_type,
868 fn_naked_noreturn_no_args_type,
869 fn_ccc_void_no_args_type,
870 single_const_pointer_to_comptime_int_type,
871 const_slice_u8_type,
872 enum_literal_type,
873
874 /// `undefined` (untyped)
875 undef,
876 /// `0` (comptime_int)
877 zero,
878 /// `1` (comptime_int)
879 one,
880 /// `{}`
881 void_value,
882 /// `unreachable` (noreturn type)
883 unreachable_value,
884 /// `null` (untyped)
885 null_value,
886 /// `true`
887 bool_true,
888 /// `false`
889 bool_false,
890 /// `0` (usize)
891 zero_usize,
892 /// `1` (usize)
893 one_usize,
894
895 _,
896
897 pub const typed_value_map = std.enums.directEnumArray(Ref, TypedValue, 0, .{
898 .none = undefined,
899
900 .u8_type = .{
901 .ty = Type.initTag(.type),
902 .val = Value.initTag(.u8_type),
903 },
904 .i8_type = .{
905 .ty = Type.initTag(.type),
906 .val = Value.initTag(.i8_type),
907 },
908 .u16_type = .{
909 .ty = Type.initTag(.type),
910 .val = Value.initTag(.u16_type),
911 },
912 .i16_type = .{
913 .ty = Type.initTag(.type),
914 .val = Value.initTag(.i16_type),
915 },
916 .u32_type = .{
917 .ty = Type.initTag(.type),
918 .val = Value.initTag(.u32_type),
919 },
920 .i32_type = .{
921 .ty = Type.initTag(.type),
922 .val = Value.initTag(.i32_type),
923 },
924 .u64_type = .{
925 .ty = Type.initTag(.type),
926 .val = Value.initTag(.u64_type),
927 },
928 .i64_type = .{
929 .ty = Type.initTag(.type),
930 .val = Value.initTag(.i64_type),
931 },
932 .usize_type = .{
933 .ty = Type.initTag(.type),
934 .val = Value.initTag(.usize_type),
935 },
936 .isize_type = .{
937 .ty = Type.initTag(.type),
938 .val = Value.initTag(.isize_type),
939 },
940 .c_short_type = .{
941 .ty = Type.initTag(.type),
942 .val = Value.initTag(.c_short_type),
943 },
944 .c_ushort_type = .{
945 .ty = Type.initTag(.type),
946 .val = Value.initTag(.c_ushort_type),
947 },
948 .c_int_type = .{
949 .ty = Type.initTag(.type),
950 .val = Value.initTag(.c_int_type),
951 },
952 .c_uint_type = .{
953 .ty = Type.initTag(.type),
954 .val = Value.initTag(.c_uint_type),
955 },
956 .c_long_type = .{
957 .ty = Type.initTag(.type),
958 .val = Value.initTag(.c_long_type),
959 },
960 .c_ulong_type = .{
961 .ty = Type.initTag(.type),
962 .val = Value.initTag(.c_ulong_type),
963 },
964 .c_longlong_type = .{
965 .ty = Type.initTag(.type),
966 .val = Value.initTag(.c_longlong_type),
967 },
968 .c_ulonglong_type = .{
969 .ty = Type.initTag(.type),
970 .val = Value.initTag(.c_ulonglong_type),
971 },
972 .c_longdouble_type = .{
973 .ty = Type.initTag(.type),
974 .val = Value.initTag(.c_longdouble_type),
975 },
976 .f16_type = .{
977 .ty = Type.initTag(.type),
978 .val = Value.initTag(.f16_type),
979 },
980 .f32_type = .{
981 .ty = Type.initTag(.type),
982 .val = Value.initTag(.f32_type),
983 },
984 .f64_type = .{
985 .ty = Type.initTag(.type),
986 .val = Value.initTag(.f64_type),
987 },
988 .f128_type = .{
989 .ty = Type.initTag(.type),
990 .val = Value.initTag(.f128_type),
991 },
992 .c_void_type = .{
993 .ty = Type.initTag(.type),
994 .val = Value.initTag(.c_void_type),
995 },
996 .bool_type = .{
997 .ty = Type.initTag(.type),
998 .val = Value.initTag(.bool_type),
999 },
1000 .void_type = .{
1001 .ty = Type.initTag(.type),
1002 .val = Value.initTag(.void_type),
1003 },
1004 .type_type = .{
1005 .ty = Type.initTag(.type),
1006 .val = Value.initTag(.type_type),
1007 },
1008 .anyerror_type = .{
1009 .ty = Type.initTag(.type),
1010 .val = Value.initTag(.anyerror_type),
1011 },
1012 .comptime_int_type = .{
1013 .ty = Type.initTag(.type),
1014 .val = Value.initTag(.comptime_int_type),
1015 },
1016 .comptime_float_type = .{
1017 .ty = Type.initTag(.type),
1018 .val = Value.initTag(.comptime_float_type),
1019 },
1020 .noreturn_type = .{
1021 .ty = Type.initTag(.type),
1022 .val = Value.initTag(.noreturn_type),
1023 },
1024 .null_type = .{
1025 .ty = Type.initTag(.type),
1026 .val = Value.initTag(.null_type),
1027 },
1028 .undefined_type = .{
1029 .ty = Type.initTag(.type),
1030 .val = Value.initTag(.undefined_type),
1031 },
1032 .fn_noreturn_no_args_type = .{
1033 .ty = Type.initTag(.type),
1034 .val = Value.initTag(.fn_noreturn_no_args_type),
1035 },
1036 .fn_void_no_args_type = .{
1037 .ty = Type.initTag(.type),
1038 .val = Value.initTag(.fn_void_no_args_type),
1039 },
1040 .fn_naked_noreturn_no_args_type = .{
1041 .ty = Type.initTag(.type),
1042 .val = Value.initTag(.fn_naked_noreturn_no_args_type),
1043 },
1044 .fn_ccc_void_no_args_type = .{
1045 .ty = Type.initTag(.type),
1046 .val = Value.initTag(.fn_ccc_void_no_args_type),
1047 },
1048 .single_const_pointer_to_comptime_int_type = .{
1049 .ty = Type.initTag(.type),
1050 .val = Value.initTag(.single_const_pointer_to_comptime_int_type),
1051 },
1052 .const_slice_u8_type = .{
1053 .ty = Type.initTag(.type),
1054 .val = Value.initTag(.const_slice_u8_type),
1055 },
1056 .enum_literal_type = .{
1057 .ty = Type.initTag(.type),
1058 .val = Value.initTag(.enum_literal_type),
1059 },
7101060
711 positionals: struct {},
712 kw_args: struct {},
1061 .undef = .{
1062 .ty = Type.initTag(.@"undefined"),
1063 .val = Value.initTag(.undef),
1064 },
1065 .zero = .{
1066 .ty = Type.initTag(.comptime_int),
1067 .val = Value.initTag(.zero),
1068 },
1069 .zero_usize = .{
1070 .ty = Type.initTag(.usize),
1071 .val = Value.initTag(.zero),
1072 },
1073 .one = .{
1074 .ty = Type.initTag(.comptime_int),
1075 .val = Value.initTag(.one),
1076 },
1077 .one_usize = .{
1078 .ty = Type.initTag(.usize),
1079 .val = Value.initTag(.one),
1080 },
1081 .void_value = .{
1082 .ty = Type.initTag(.void),
1083 .val = Value.initTag(.void_value),
1084 },
1085 .unreachable_value = .{
1086 .ty = Type.initTag(.noreturn),
1087 .val = Value.initTag(.unreachable_value),
1088 },
1089 .null_value = .{
1090 .ty = Type.initTag(.@"null"),
1091 .val = Value.initTag(.null_value),
1092 },
1093 .bool_true = .{
1094 .ty = Type.initTag(.bool),
1095 .val = Value.initTag(.bool_true),
1096 },
1097 .bool_false = .{
1098 .ty = Type.initTag(.bool),
1099 .val = Value.initTag(.bool_false),
1100 },
1101 });
7131102 };
7141103
715 pub const UnOp = struct {
716 base: Inst,
717
718 positionals: struct {
719 operand: *Inst,
1104 /// All instructions have an 8-byte payload, which is contained within
1105 /// this union. `Tag` determines which union field is active, as well as
1106 /// how to interpret the data within.
1107 pub const Data = union {
1108 /// Used for unary operators, with an AST node source location.
1109 un_node: struct {
1110 /// Offset from Decl AST node index.
1111 src_node: i32,
1112 /// The meaning of this operand depends on the corresponding `Tag`.
1113 operand: Ref,
1114
1115 pub fn src(self: @This()) LazySrcLoc {
1116 return .{ .node_offset = self.src_node };
1117 }
7201118 },
721 kw_args: struct {},
722 };
723
724 pub const BinOp = struct {
725 base: Inst,
726
727 positionals: struct {
728 lhs: *Inst,
729 rhs: *Inst,
1119 /// Used for unary operators, with a token source location.
1120 un_tok: struct {
1121 /// Offset from Decl AST token index.
1122 src_tok: ast.TokenIndex,
1123 /// The meaning of this operand depends on the corresponding `Tag`.
1124 operand: Ref,
1125
1126 pub fn src(self: @This()) LazySrcLoc {
1127 return .{ .token_offset = self.src_tok };
1128 }
7301129 },
731 kw_args: struct {},
732 };
733
734 pub const Arg = struct {
735 pub const base_tag = Tag.arg;
736 base: Inst,
1130 pl_node: struct {
1131 /// Offset from Decl AST node index.
1132 /// `Tag` determines which kind of AST node this points to.
1133 src_node: i32,
1134 /// index into extra.
1135 /// `Tag` determines what lives there.
1136 payload_index: u32,
1137
1138 pub fn src(self: @This()) LazySrcLoc {
1139 return .{ .node_offset = self.src_node };
1140 }
1141 },
1142 bin: Bin,
1143 @"const": *TypedValue,
1144 /// For strings which may contain null bytes.
1145 str: struct {
1146 /// Offset into `string_bytes`.
1147 start: u32,
1148 /// Number of bytes in the string.
1149 len: u32,
1150
1151 pub fn get(self: @This(), code: Code) []const u8 {
1152 return code.string_bytes[self.start..][0..self.len];
1153 }
1154 },
1155 /// Strings 8 or fewer bytes which may not contain null bytes.
1156 small_str: struct {
1157 bytes: [8]u8,
1158
1159 pub fn get(self: @This()) []const u8 {
1160 const end = for (self.bytes) |byte, i| {
1161 if (byte == 0) break i;
1162 } else self.bytes.len;
1163 return self.bytes[0..end];
1164 }
1165 },
1166 str_tok: struct {
1167 /// Offset into `string_bytes`. Null-terminated.
1168 start: u32,
1169 /// Offset from Decl AST token index.
1170 src_tok: u32,
1171
1172 pub fn get(self: @This(), code: Code) [:0]const u8 {
1173 return code.nullTerminatedString(self.start);
1174 }
7371175
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,
1176 pub fn src(self: @This()) LazySrcLoc {
1177 return .{ .token_offset = self.src_tok };
1178 }
1179 },
1180 /// Offset from Decl AST token index.
1181 tok: ast.TokenIndex,
1182 /// Offset from Decl AST node index.
1183 node: i32,
1184 int: u64,
1185 array_type_sentinel: struct {
1186 len: Ref,
1187 /// index into extra, points to an `ArrayTypeSentinel`
1188 payload_index: u32,
1189 },
1190 ptr_type_simple: struct {
1191 is_allowzero: bool,
1192 is_mutable: bool,
1193 is_volatile: bool,
1194 size: std.builtin.TypeInfo.Pointer.Size,
1195 elem_type: Ref,
1196 },
1197 ptr_type: struct {
1198 flags: packed struct {
1199 is_allowzero: bool,
1200 is_mutable: bool,
1201 is_volatile: bool,
1202 has_sentinel: bool,
1203 has_align: bool,
1204 has_bit_range: bool,
1205 _: u2 = undefined,
1206 },
1207 size: std.builtin.TypeInfo.Pointer.Size,
1208 /// Index into extra. See `PtrType`.
1209 payload_index: u32,
1210 },
1211 int_type: struct {
1212 /// Offset from Decl AST node index.
1213 /// `Tag` determines which kind of AST node this points to.
1214 src_node: i32,
1215 signedness: std.builtin.Signedness,
1216 bit_count: u16,
1217
1218 pub fn src(self: @This()) LazySrcLoc {
1219 return .{ .node_offset = self.src_node };
1220 }
1221 },
1222 bool_br: struct {
1223 lhs: Ref,
1224 /// Points to a `Block`.
1225 payload_index: u32,
1226 },
1227 param_type: struct {
1228 callee: Ref,
1229 param_index: u32,
1230 },
1231 @"unreachable": struct {
1232 /// Offset from Decl AST node index.
1233 /// `Tag` determines which kind of AST node this points to.
1234 src_node: i32,
1235 /// `false`: Not safety checked - the compiler will assume the
1236 /// correctness of this instruction.
1237 /// `true`: In safety-checked modes, this will generate a call
1238 /// to the panic function unless it can be proven unreachable by the compiler.
1239 safety: bool,
1240
1241 pub fn src(self: @This()) LazySrcLoc {
1242 return .{ .node_offset = self.src_node };
1243 }
1244 },
1245 @"break": struct {
1246 block_inst: Index,
1247 operand: Ref,
1248 },
1249 switch_capture: struct {
1250 switch_inst: Index,
1251 prong_index: u32,
7421252 },
743 kw_args: struct {},
744 };
7451253
746 pub const Block = struct {
747 pub const base_tag = Tag.block;
748 base: Inst,
1254 // Make sure we don't accidentally add a field to make this union
1255 // bigger than expected. Note that in Debug builds, Zig is allowed
1256 // to insert a secret field for safety checks.
1257 comptime {
1258 if (std.builtin.mode != .Debug) {
1259 assert(@sizeOf(Data) == 8);
1260 }
1261 }
1262 };
7491263
750 positionals: struct {
751 body: Body,
752 },
753 kw_args: struct {},
1264 /// Stored in extra. Trailing is:
1265 /// * output_name: u32 // index into string_bytes (null terminated) if output is present
1266 /// * arg: Ref // for every args_len.
1267 /// * constraint: u32 // index into string_bytes (null terminated) for every args_len.
1268 /// * clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
1269 pub const Asm = struct {
1270 asm_source: Ref,
1271 return_type: Ref,
1272 /// May be omitted.
1273 output: Ref,
1274 args_len: u32,
1275 clobbers_len: u32,
7541276 };
7551277
756 pub const Break = struct {
757 pub const base_tag = Tag.@"break";
758 base: Inst,
1278 /// This data is stored inside extra, with trailing parameter type indexes
1279 /// according to `param_types_len`.
1280 /// Each param type is a `Ref`.
1281 pub const FnTypeCc = struct {
1282 return_type: Ref,
1283 cc: Ref,
1284 param_types_len: u32,
1285 };
7591286
760 positionals: struct {
761 block: *Block,
762 operand: *Inst,
763 },
764 kw_args: struct {},
1287 /// This data is stored inside extra, with trailing parameter type indexes
1288 /// according to `param_types_len`.
1289 /// Each param type is a `Ref`.
1290 pub const FnType = struct {
1291 return_type: Ref,
1292 param_types_len: u32,
7651293 };
7661294
767 pub const BreakVoid = struct {
768 pub const base_tag = Tag.break_void;
769 base: Inst,
1295 /// This data is stored inside extra, with trailing operands according to `operands_len`.
1296 /// Each operand is a `Ref`.
1297 pub const MultiOp = struct {
1298 operands_len: u32,
1299 };
7701300
771 positionals: struct {
772 block: *Block,
773 },
774 kw_args: struct {},
1301 /// This data is stored inside extra, with trailing operands according to `body_len`.
1302 /// Each operand is an `Index`.
1303 pub const Block = struct {
1304 body_len: u32,
7751305 };
7761306
777 // TODO break this into multiple call instructions to avoid paying the cost
778 // of the calling convention field most of the time.
1307 /// Stored inside extra, with trailing arguments according to `args_len`.
1308 /// Each argument is a `Ref`.
7791309 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 {},
1310 callee: Ref,
1311 args_len: u32,
7891312 };
7901313
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 {},
1314 /// This data is stored inside extra, with two sets of trailing `Ref`:
1315 /// * 0. the then body, according to `then_body_len`.
1316 /// * 1. the else body, according to `else_body_len`.
1317 pub const CondBr = struct {
1318 condition: Ref,
1319 then_body_len: u32,
1320 else_body_len: u32,
7991321 };
8001322
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 {},
1323 /// Stored in extra. Depending on the flags in Data, there will be up to 4
1324 /// trailing Ref fields:
1325 /// 0. sentinel: Ref // if `has_sentinel` flag is set
1326 /// 1. align: Ref // if `has_align` flag is set
1327 /// 2. bit_start: Ref // if `has_bit_range` flag is set
1328 /// 3. bit_end: Ref // if `has_bit_range` flag is set
1329 pub const PtrType = struct {
1330 elem_type: Ref,
8091331 };
8101332
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 {},
1333 pub const ArrayTypeSentinel = struct {
1334 sentinel: Ref,
1335 elem_type: Ref,
8191336 };
8201337
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 {},
1338 pub const SliceStart = struct {
1339 lhs: Ref,
1340 start: Ref,
8291341 };
8301342
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 {},
1343 pub const SliceEnd = struct {
1344 lhs: Ref,
1345 start: Ref,
1346 end: Ref,
8391347 };
8401348
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 {},
1349 pub const SliceSentinel = struct {
1350 lhs: Ref,
1351 start: Ref,
1352 end: Ref,
1353 sentinel: Ref,
8491354 };
8501355
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 {},
1356 /// The meaning of these operands depends on the corresponding `Tag`.
1357 pub const Bin = struct {
1358 lhs: Ref,
1359 rhs: Ref,
8591360 };
8601361
861 pub const Loop = struct {
862 pub const base_tag = Tag.loop;
863 base: Inst,
1362 /// This form is supported when there are no ranges, and exactly 1 item per block.
1363 /// Depending on zir tag and len fields, extra fields trail
1364 /// this one in the extra array.
1365 /// 0. else_body { // If the tag has "_else" or "_under" in it.
1366 /// body_len: u32,
1367 /// body member Index for every body_len
1368 /// }
1369 /// 1. cases: {
1370 /// item: Ref,
1371 /// body_len: u32,
1372 /// body member Index for every body_len
1373 /// } for every cases_len
1374 pub const SwitchBlock = struct {
1375 operand: Ref,
1376 cases_len: u32,
1377 };
8641378
865 positionals: struct {
866 body: Body,
867 },
868 kw_args: struct {},
1379 /// This form is required when there exists a block which has more than one item,
1380 /// or a range.
1381 /// Depending on zir tag and len fields, extra fields trail
1382 /// this one in the extra array.
1383 /// 0. else_body { // If the tag has "_else" or "_under" in it.
1384 /// body_len: u32,
1385 /// body member Index for every body_len
1386 /// }
1387 /// 1. scalar_cases: { // for every scalar_cases_len
1388 /// item: Ref,
1389 /// body_len: u32,
1390 /// body member Index for every body_len
1391 /// }
1392 /// 2. multi_cases: { // for every multi_cases_len
1393 /// items_len: u32,
1394 /// ranges_len: u32,
1395 /// body_len: u32,
1396 /// item: Ref // for every items_len
1397 /// ranges: { // for every ranges_len
1398 /// item_first: Ref,
1399 /// item_last: Ref,
1400 /// }
1401 /// body member Index for every body_len
1402 /// }
1403 pub const SwitchBlockMulti = struct {
1404 operand: Ref,
1405 scalar_cases_len: u32,
1406 multi_cases_len: u32,
8691407 };
8701408
8711409 pub const Field = struct {
872 base: Inst,
873
874 positionals: struct {
875 object: *Inst,
876 field_name: []const u8,
877 },
878 kw_args: struct {},
1410 lhs: Ref,
1411 /// Offset into `string_bytes`.
1412 field_name_start: u32,
8791413 };
8801414
8811415 pub const FieldNamed = struct {
882 base: Inst,
883
884 positionals: struct {
885 object: *Inst,
886 field_name: *Inst,
887 },
888 kw_args: struct {},
1416 lhs: Ref,
1417 field_name: Ref,
8891418 };
8901419
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 },
1420 pub const As = struct {
1421 dest_type: Ref,
1422 operand: Ref,
9061423 };
1424};
9071425
908 pub const Fn = struct {
909 pub const base_tag = Tag.@"fn";
910 base: Inst,
1426pub const SpecialProng = enum { none, @"else", under };
9111427
912 positionals: struct {
913 fn_type: *Inst,
914 body: Body,
915 },
916 kw_args: struct {},
917 };
1428const Writer = struct {
1429 gpa: *Allocator,
1430 arena: *Allocator,
1431 scope: *Module.Scope,
1432 code: Code,
1433 indent: usize,
1434 param_count: usize,
9181435
919 pub const FnType = struct {
920 pub const base_tag = Tag.fn_type;
921 base: Inst,
1436 fn writeInstToStream(
1437 self: *Writer,
1438 stream: anytype,
1439 inst: Inst.Index,
1440 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1441 const tags = self.code.instructions.items(.tag);
1442 const tag = tags[inst];
1443 try stream.print("= {s}(", .{@tagName(tags[inst])});
1444 switch (tag) {
1445 .array_type,
1446 .as,
1447 .coerce_result_ptr,
1448 .elem_ptr,
1449 .elem_val,
1450 .intcast,
1451 .store,
1452 .store_to_block_ptr,
1453 => try self.writeBin(stream, inst),
1454
1455 .alloc,
1456 .alloc_mut,
1457 .alloc_inferred,
1458 .alloc_inferred_mut,
1459 .indexable_ptr_len,
1460 .bit_not,
1461 .bool_not,
1462 .negate,
1463 .negate_wrap,
1464 .call_none,
1465 .call_none_chkused,
1466 .compile_error,
1467 .load,
1468 .ensure_result_used,
1469 .ensure_result_non_error,
1470 .import,
1471 .ptrtoint,
1472 .ret_node,
1473 .set_eval_branch_quota,
1474 .resolve_inferred_alloc,
1475 .optional_type,
1476 .optional_type_from_ptr_elem,
1477 .optional_payload_safe,
1478 .optional_payload_unsafe,
1479 .optional_payload_safe_ptr,
1480 .optional_payload_unsafe_ptr,
1481 .err_union_payload_safe,
1482 .err_union_payload_unsafe,
1483 .err_union_payload_safe_ptr,
1484 .err_union_payload_unsafe_ptr,
1485 .err_union_code,
1486 .err_union_code_ptr,
1487 .int_to_error,
1488 .error_to_int,
1489 .is_non_null,
1490 .is_null,
1491 .is_non_null_ptr,
1492 .is_null_ptr,
1493 .is_err,
1494 .is_err_ptr,
1495 .typeof,
1496 .typeof_elem,
1497 => try self.writeUnNode(stream, inst),
1498
1499 .ref,
1500 .ret_tok,
1501 .ret_coerce,
1502 .ensure_err_payload_void,
1503 => try self.writeUnTok(stream, inst),
1504
1505 .bool_br_and,
1506 .bool_br_or,
1507 => try self.writeBoolBr(stream, inst),
1508
1509 .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
1510 .@"const" => try self.writeConst(stream, inst),
1511 .param_type => try self.writeParamType(stream, inst),
1512 .ptr_type_simple => try self.writePtrTypeSimple(stream, inst),
1513 .ptr_type => try self.writePtrType(stream, inst),
1514 .int => try self.writeInt(stream, inst),
1515 .str => try self.writeStr(stream, inst),
1516 .elided => try stream.writeAll(")"),
1517 .int_type => try self.writeIntType(stream, inst),
1518
1519 .@"break",
1520 .break_inline,
1521 => try self.writeBreak(stream, inst),
1522
1523 .@"asm",
1524 .asm_volatile,
1525 .elem_ptr_node,
1526 .elem_val_node,
1527 .field_ptr_named,
1528 .field_val_named,
1529 .floatcast,
1530 .slice_start,
1531 .slice_end,
1532 .slice_sentinel,
1533 => try self.writePlNode(stream, inst),
1534
1535 .add,
1536 .addwrap,
1537 .array_cat,
1538 .array_mul,
1539 .mul,
1540 .mulwrap,
1541 .sub,
1542 .subwrap,
1543 .bool_and,
1544 .bool_or,
1545 .cmp_lt,
1546 .cmp_lte,
1547 .cmp_eq,
1548 .cmp_gte,
1549 .cmp_gt,
1550 .cmp_neq,
1551 .div,
1552 .mod_rem,
1553 .shl,
1554 .shr,
1555 .xor,
1556 .store_node,
1557 .error_union_type,
1558 .merge_error_sets,
1559 .bit_and,
1560 .bit_or,
1561 => try self.writePlNodeBin(stream, inst),
1562
1563 .call,
1564 .call_chkused,
1565 .call_compile_time,
1566 => try self.writePlNodeCall(stream, inst),
1567
1568 .block,
1569 .block_inline,
1570 .loop,
1571 => try self.writePlNodeBlock(stream, inst),
1572
1573 .condbr,
1574 .condbr_inline,
1575 => try self.writePlNodeCondBr(stream, inst),
1576
1577 .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none),
1578 .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
1579 .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under),
1580 .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none),
1581 .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
1582 .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under),
1583
1584 .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
1585 .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
1586 .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
1587 .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
1588 .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
1589 .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
1590
1591 .compile_log,
1592 .typeof_peer,
1593 => try self.writePlNodeMultiOp(stream, inst),
1594
1595 .decl_ref,
1596 .decl_val,
1597 => try self.writePlNodeDecl(stream, inst),
1598
1599 .field_ptr,
1600 .field_val,
1601 => try self.writePlNodeField(stream, inst),
1602
1603 .as_node => try self.writeAs(stream, inst),
1604
1605 .breakpoint,
1606 .dbg_stmt_node,
1607 .ret_ptr,
1608 .ret_type,
1609 .repeat,
1610 .repeat_inline,
1611 => try self.writeNode(stream, inst),
1612
1613 .error_value,
1614 .enum_literal,
1615 => try self.writeStrTok(stream, inst),
1616
1617 .fn_type => try self.writeFnType(stream, inst, false),
1618 .fn_type_cc => try self.writeFnTypeCc(stream, inst, false),
1619 .fn_type_var_args => try self.writeFnType(stream, inst, true),
1620 .fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true),
1621
1622 .@"unreachable" => try self.writeUnreachable(stream, inst),
1623
1624 .enum_literal_small => try self.writeSmallStr(stream, inst),
1625
1626 .switch_capture,
1627 .switch_capture_ref,
1628 .switch_capture_multi,
1629 .switch_capture_multi_ref,
1630 .switch_capture_else,
1631 .switch_capture_else_ref,
1632 => try self.writeSwitchCapture(stream, inst),
1633
1634 .bitcast,
1635 .bitcast_result_ptr,
1636 .store_to_inferred_ptr,
1637 => try stream.writeAll("TODO)"),
1638 }
1639 }
9221640
923 positionals: struct {
924 param_types: []*Inst,
925 return_type: *Inst,
926 },
927 kw_args: struct {},
928 };
1641 fn writeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1642 const inst_data = self.code.instructions.items(.data)[inst].bin;
1643 try self.writeInstRef(stream, inst_data.lhs);
1644 try stream.writeAll(", ");
1645 try self.writeInstRef(stream, inst_data.rhs);
1646 try stream.writeByte(')');
1647 }
9291648
930 pub const FnTypeCc = struct {
931 pub const base_tag = Tag.fn_type_cc;
932 base: Inst,
1649 fn writeUnNode(
1650 self: *Writer,
1651 stream: anytype,
1652 inst: Inst.Index,
1653 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1654 const inst_data = self.code.instructions.items(.data)[inst].un_node;
1655 try self.writeInstRef(stream, inst_data.operand);
1656 try stream.writeAll(") ");
1657 try self.writeSrc(stream, inst_data.src());
1658 }
9331659
934 positionals: struct {
935 param_types: []*Inst,
936 return_type: *Inst,
937 cc: *Inst,
938 },
939 kw_args: struct {},
940 };
941
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 };
952
953 pub const Export = struct {
954 pub const base_tag = Tag.@"export";
955 base: Inst,
956
957 positionals: struct {
958 symbol_name: *Inst,
959 decl_name: []const u8,
960 },
961 kw_args: struct {},
962 };
963
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,
1141
1142 positionals: struct {
1143 name: []const u8,
1144 },
1145 kw_args: struct {},
1146 };
1147
1148 pub const Slice = struct {
1149 pub const base_tag = Tag.slice;
1150 base: Inst,
1151
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 };
1161
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 };
1170
1171 pub const ContainerFieldNamed = struct {
1172 pub const base_tag = Tag.container_field_named;
1173 base: Inst,
1660 fn writeUnTok(
1661 self: *Writer,
1662 stream: anytype,
1663 inst: Inst.Index,
1664 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1665 const inst_data = self.code.instructions.items(.data)[inst].un_tok;
1666 try self.writeInstRef(stream, inst_data.operand);
1667 try stream.writeAll(") ");
1668 try self.writeSrc(stream, inst_data.src());
1669 }
11741670
1175 positionals: struct {
1176 bytes: []const u8,
1177 },
1178 kw_args: struct {},
1179 };
1671 fn writeArrayTypeSentinel(
1672 self: *Writer,
1673 stream: anytype,
1674 inst: Inst.Index,
1675 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1676 const inst_data = self.code.instructions.items(.data)[inst].array_type_sentinel;
1677 try stream.writeAll("TODO)");
1678 }
11801679
1181 pub const ContainerFieldTyped = struct {
1182 pub const base_tag = Tag.container_field_typed;
1183 base: Inst,
1680 fn writeConst(
1681 self: *Writer,
1682 stream: anytype,
1683 inst: Inst.Index,
1684 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1685 const inst_data = self.code.instructions.items(.data)[inst].@"const";
1686 try stream.writeAll("TODO)");
1687 }
11841688
1185 positionals: struct {
1186 bytes: []const u8,
1187 ty: *Inst,
1188 },
1189 kw_args: struct {},
1190 };
1689 fn writeParamType(
1690 self: *Writer,
1691 stream: anytype,
1692 inst: Inst.Index,
1693 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1694 const inst_data = self.code.instructions.items(.data)[inst].param_type;
1695 try self.writeInstRef(stream, inst_data.callee);
1696 try stream.print(", {d})", .{inst_data.param_index});
1697 }
11911698
1192 pub const ContainerField = struct {
1193 pub const base_tag = Tag.container_field;
1194 base: Inst,
1699 fn writePtrTypeSimple(
1700 self: *Writer,
1701 stream: anytype,
1702 inst: Inst.Index,
1703 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1704 const inst_data = self.code.instructions.items(.data)[inst].ptr_type_simple;
1705 try stream.writeAll("TODO)");
1706 }
11951707
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 };
1708 fn writePtrType(
1709 self: *Writer,
1710 stream: anytype,
1711 inst: Inst.Index,
1712 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1713 const inst_data = self.code.instructions.items(.data)[inst].ptr_type;
1714 try stream.writeAll("TODO)");
1715 }
12061716
1207 pub const EnumType = struct {
1208 pub const base_tag = Tag.enum_type;
1209 base: Inst,
1717 fn writeInt(
1718 self: *Writer,
1719 stream: anytype,
1720 inst: Inst.Index,
1721 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1722 const inst_data = self.code.instructions.items(.data)[inst].int;
1723 try stream.print("{d})", .{inst_data});
1724 }
12101725
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 };
1726 fn writeStr(
1727 self: *Writer,
1728 stream: anytype,
1729 inst: Inst.Index,
1730 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1731 const inst_data = self.code.instructions.items(.data)[inst].str;
1732 const str = inst_data.get(self.code);
1733 try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
1734 }
12191735
1220 pub const StructType = struct {
1221 pub const base_tag = Tag.struct_type;
1222 base: Inst,
1736 fn writePlNode(
1737 self: *Writer,
1738 stream: anytype,
1739 inst: Inst.Index,
1740 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1741 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1742 try stream.writeAll("TODO) ");
1743 try self.writeSrc(stream, inst_data.src());
1744 }
12231745
1224 positionals: struct {
1225 fields: []*Inst,
1226 },
1227 kw_args: struct {
1228 layout: std.builtin.TypeInfo.ContainerLayout = .Auto,
1229 },
1230 };
1746 fn writePlNodeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1747 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1748 const extra = self.code.extraData(Inst.Bin, inst_data.payload_index).data;
1749 try self.writeInstRef(stream, extra.lhs);
1750 try stream.writeAll(", ");
1751 try self.writeInstRef(stream, extra.rhs);
1752 try stream.writeAll(") ");
1753 try self.writeSrc(stream, inst_data.src());
1754 }
12311755
1232 pub const UnionType = struct {
1233 pub const base_tag = Tag.union_type;
1234 base: Inst,
1756 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1757 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1758 const extra = self.code.extraData(Inst.Call, inst_data.payload_index);
1759 const args = self.code.refSlice(extra.end, extra.data.args_len);
12351760
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 };
1761 try self.writeInstRef(stream, extra.data.callee);
1762 try stream.writeAll(", [");
1763 for (args) |arg, i| {
1764 if (i != 0) try stream.writeAll(", ");
1765 try self.writeInstRef(stream, arg);
1766 }
1767 try stream.writeAll("]) ");
1768 try self.writeSrc(stream, inst_data.src());
1769 }
12451770
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 {},
1771 fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1772 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1773 const extra = self.code.extraData(Inst.Block, inst_data.payload_index);
1774 const body = self.code.extra[extra.end..][0..extra.data.body_len];
1775 try stream.writeAll("{\n");
1776 self.indent += 2;
1777 try self.writeBody(stream, body);
1778 self.indent -= 2;
1779 try stream.writeByteNTimes(' ', self.indent);
1780 try stream.writeAll("}) ");
1781 try self.writeSrc(stream, inst_data.src());
1782 }
12601783
1261 pub const SpecialProng = enum {
1262 none,
1263 @"else",
1264 underscore,
1265 };
1784 fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1785 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1786 const extra = self.code.extraData(Inst.CondBr, inst_data.payload_index);
1787 const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len];
1788 const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1789 try self.writeInstRef(stream, extra.data.condition);
1790 try stream.writeAll(", {\n");
1791 self.indent += 2;
1792 try self.writeBody(stream, then_body);
1793 self.indent -= 2;
1794 try stream.writeByteNTimes(' ', self.indent);
1795 try stream.writeAll("}, {\n");
1796 self.indent += 2;
1797 try self.writeBody(stream, else_body);
1798 self.indent -= 2;
1799 try stream.writeByteNTimes(' ', self.indent);
1800 try stream.writeAll("}) ");
1801 try self.writeSrc(stream, inst_data.src());
1802 }
12661803
1267 pub const Case = struct {
1268 item: *Inst,
1269 body: Body,
1804 fn writePlNodeSwitchBr(
1805 self: *Writer,
1806 stream: anytype,
1807 inst: Inst.Index,
1808 special_prong: SpecialProng,
1809 ) !void {
1810 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1811 const extra = self.code.extraData(Inst.SwitchBlock, inst_data.payload_index);
1812 const special: struct {
1813 body: []const Inst.Index,
1814 end: usize,
1815 } = switch (special_prong) {
1816 .none => .{ .body = &.{}, .end = extra.end },
1817 .under, .@"else" => blk: {
1818 const body_len = self.code.extra[extra.end];
1819 const extra_body_start = extra.end + 1;
1820 break :blk .{
1821 .body = self.code.extra[extra_body_start..][0..body_len],
1822 .end = extra_body_start + body_len,
1823 };
1824 },
12701825 };
1271 };
1272};
12731826
1274pub const ErrorMsg = struct {
1275 byte_offset: usize,
1276 msg: []const u8,
1277};
1278
1279pub const Body = struct {
1280 instructions: []*Inst,
1281};
1827 try self.writeInstRef(stream, extra.data.operand);
12821828
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,
1829 if (special.body.len != 0) {
1830 const prong_name = switch (special_prong) {
1831 .@"else" => "else",
1832 .under => "_",
1833 else => unreachable,
1834 };
1835 try stream.print(", {s} => {{\n", .{prong_name});
1836 self.indent += 2;
1837 try self.writeBody(stream, special.body);
1838 self.indent -= 2;
1839 try stream.writeByteNTimes(' ', self.indent);
1840 try stream.writeAll("}");
1841 }
12951842
1296 inst: *Inst,
1297 };
1843 var extra_index: usize = special.end;
1844 {
1845 var scalar_i: usize = 0;
1846 while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
1847 const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
1848 extra_index += 1;
1849 const body_len = self.code.extra[extra_index];
1850 extra_index += 1;
1851 const body = self.code.extra[extra_index..][0..body_len];
1852 extra_index += body_len;
12981853
1299 pub const MetaData = struct {
1300 deaths: ir.Inst.DeathsInt,
1301 addr: usize,
1302 };
1854 try stream.writeAll(", ");
1855 try self.writeInstRef(stream, item_ref);
1856 try stream.writeAll(" => {\n");
1857 self.indent += 2;
1858 try self.writeBody(stream, body);
1859 self.indent -= 2;
1860 try stream.writeByteNTimes(' ', self.indent);
1861 try stream.writeAll("}");
1862 }
1863 }
1864 try stream.writeAll(") ");
1865 try self.writeSrc(stream, inst_data.src());
1866 }
13031867
1304 pub const BodyMetaData = struct {
1305 deaths: []*Inst,
1306 };
1868 fn writePlNodeSwitchBlockMulti(
1869 self: *Writer,
1870 stream: anytype,
1871 inst: Inst.Index,
1872 special_prong: SpecialProng,
1873 ) !void {
1874 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1875 const extra = self.code.extraData(Inst.SwitchBlockMulti, inst_data.payload_index);
1876 const special: struct {
1877 body: []const Inst.Index,
1878 end: usize,
1879 } = switch (special_prong) {
1880 .none => .{ .body = &.{}, .end = extra.end },
1881 .under, .@"else" => blk: {
1882 const body_len = self.code.extra[extra.end];
1883 const extra_body_start = extra.end + 1;
1884 break :blk .{
1885 .body = self.code.extra[extra_body_start..][0..body_len],
1886 .end = extra_body_start + body_len,
1887 };
1888 },
1889 };
13071890
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 }
1891 try self.writeInstRef(stream, extra.data.operand);
13151892
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 }
1893 if (special.body.len != 0) {
1894 const prong_name = switch (special_prong) {
1895 .@"else" => "else",
1896 .under => "_",
1897 else => unreachable,
1898 };
1899 try stream.print(", {s} => {{\n", .{prong_name});
1900 self.indent += 2;
1901 try self.writeBody(stream, special.body);
1902 self.indent -= 2;
1903 try stream.writeByteNTimes(' ', self.indent);
1904 try stream.writeAll("}");
1905 }
13201906
1321 const DeclAndIndex = struct {
1322 decl: *Decl,
1323 index: usize,
1324 };
1907 var extra_index: usize = special.end;
1908 {
1909 var scalar_i: usize = 0;
1910 while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
1911 const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
1912 extra_index += 1;
1913 const body_len = self.code.extra[extra_index];
1914 extra_index += 1;
1915 const body = self.code.extra[extra_index..][0..body_len];
1916 extra_index += body_len;
13251917
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 };
1918 try stream.writeAll(", ");
1919 try self.writeInstRef(stream, item_ref);
1920 try stream.writeAll(" => {\n");
1921 self.indent += 2;
1922 try self.writeBody(stream, body);
1923 self.indent -= 2;
1924 try stream.writeByteNTimes(' ', self.indent);
1925 try stream.writeAll("}");
13341926 }
13351927 }
1336 return null;
1337 }
1928 {
1929 var multi_i: usize = 0;
1930 while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) {
1931 const items_len = self.code.extra[extra_index];
1932 extra_index += 1;
1933 const ranges_len = self.code.extra[extra_index];
1934 extra_index += 1;
1935 const body_len = self.code.extra[extra_index];
1936 extra_index += 1;
1937 const items = self.code.refSlice(extra_index, items_len);
1938 extra_index += items_len;
1939
1940 for (items) |item_ref| {
1941 try stream.writeAll(", ");
1942 try self.writeInstRef(stream, item_ref);
1943 }
13381944
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 };
1945 var range_i: usize = 0;
1946 while (range_i < ranges_len) : (range_i += 1) {
1947 const item_first = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
1948 extra_index += 1;
1949 const item_last = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
1950 extra_index += 1;
1951
1952 try stream.writeAll(", ");
1953 try self.writeInstRef(stream, item_first);
1954 try stream.writeAll("...");
1955 try self.writeInstRef(stream, item_last);
1956 }
1957
1958 const body = self.code.extra[extra_index..][0..body_len];
1959 extra_index += body_len;
1960 try stream.writeAll(" => {\n");
1961 self.indent += 2;
1962 try self.writeBody(stream, body);
1963 self.indent -= 2;
1964 try stream.writeByteNTimes(' ', self.indent);
1965 try stream.writeAll("}");
13461966 }
13471967 }
1348 return null;
1968 try stream.writeAll(") ");
1969 try self.writeSrc(stream, inst_data.src());
13491970 }
13501971
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();
1972 fn writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1973 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1974 const extra = self.code.extraData(Inst.MultiOp, inst_data.payload_index);
1975 const operands = self.code.refSlice(extra.end, extra.data.operands_len);
13671976
1368 // First, build a map of *Inst to @ or % indexes
1369 try write.inst_table.ensureCapacity(@intCast(u32, self.decls.len));
1370
1371 for (self.decls) |decl, decl_i| {
1372 try write.inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name });
1977 for (operands) |operand, i| {
1978 if (i != 0) try stream.writeAll(", ");
1979 try self.writeInstRef(stream, operand);
13731980 }
1981 try stream.writeAll(") ");
1982 try self.writeSrc(stream, inst_data.src());
1983 }
13741984
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 }
1985 fn writePlNodeDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1986 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1987 const decl = self.code.decls[inst_data.payload_index];
1988 try stream.print("{s}) ", .{decl.name});
1989 try self.writeSrc(stream, inst_data.src());
13811990 }
1382};
13831991
1384const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 });
1992 fn writePlNodeField(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1993 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1994 const extra = self.code.extraData(Inst.Field, inst_data.payload_index).data;
1995 const name = self.code.nullTerminatedString(extra.field_name_start);
1996 try self.writeInstRef(stream, extra.lhs);
1997 try stream.print(", \"{}\") ", .{std.zig.fmtEscapes(name)});
1998 try self.writeSrc(stream, inst_data.src());
1999 }
13852000
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,
2001 fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2002 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2003 const extra = self.code.extraData(Inst.As, inst_data.payload_index).data;
2004 try self.writeInstRef(stream, extra.dest_type);
2005 try stream.writeAll(", ");
2006 try self.writeInstRef(stream, extra.operand);
2007 try stream.writeAll(") ");
2008 try self.writeSrc(stream, inst_data.src());
2009 }
13942010
1395 fn writeInstToStream(
2011 fn writeNode(
13962012 self: *Writer,
13972013 stream: anytype,
1398 inst: *Inst,
2014 inst: Inst.Index,
13992015 ) (@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
2016 const src_node = self.code.instructions.items(.data)[inst].node;
2017 const src: LazySrcLoc = .{ .node_offset = src_node };
2018 try stream.writeAll(") ");
2019 try self.writeSrc(stream, src);
14072020 }
14082021
1409 fn writeInstToStreamGeneric(
2022 fn writeStrTok(
14102023 self: *Writer,
14112024 stream: anytype,
1412 comptime inst_tag: Inst.Tag,
1413 base: *Inst,
2025 inst: Inst.Index,
14142026 ) (@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 }
2027 const inst_data = self.code.instructions.items(.data)[inst].str_tok;
2028 const str = inst_data.get(self.code);
2029 try stream.print("\"{}\") ", .{std.zig.fmtEscapes(str)});
2030 try self.writeSrc(stream, inst_data.src());
2031 }
14262032
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 }
2033 fn writeFnType(
2034 self: *Writer,
2035 stream: anytype,
2036 inst: Inst.Index,
2037 var_args: bool,
2038 ) !void {
2039 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2040 const src = inst_data.src();
2041 const extra = self.code.extraData(Inst.FnType, inst_data.payload_index);
2042 const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
2043 return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, .none, src);
2044 }
14442045
1445 try stream.writeByte(')');
2046 fn writeFnTypeCc(
2047 self: *Writer,
2048 stream: anytype,
2049 inst: Inst.Index,
2050 var_args: bool,
2051 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
2052 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2053 const src = inst_data.src();
2054 const extra = self.code.extraData(Inst.FnTypeCc, inst_data.payload_index);
2055 const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
2056 const cc = extra.data.cc;
2057 return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, cc, src);
14462058 }
14472059
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 }
2060 fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2061 const inst_data = self.code.instructions.items(.data)[inst].bool_br;
2062 const extra = self.code.extraData(Inst.Block, inst_data.payload_index);
2063 const body = self.code.extra[extra.end..][0..extra.data.body_len];
2064 try self.writeInstRef(stream, inst_data.lhs);
2065 try stream.writeAll(", {\n");
2066 self.indent += 2;
2067 try self.writeBody(stream, body);
2068 self.indent -= 2;
2069 try stream.writeByteNTimes(' ', self.indent);
2070 try stream.writeAll("})");
2071 }
14792072
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 }
2073 fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2074 const int_type = self.code.instructions.items(.data)[inst].int_type;
2075 const prefix: u8 = switch (int_type.signedness) {
2076 .signed => 'i',
2077 .unsigned => 'u',
2078 };
2079 try stream.print("{c}{d}) ", .{ prefix, int_type.bit_count });
2080 try self.writeSrc(stream, int_type.src());
15552081 }
15562082
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 }
2083 fn writeBreak(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2084 const inst_data = self.code.instructions.items(.data)[inst].@"break";
2085
2086 try self.writeInstIndex(stream, inst_data.block_inst);
2087 try stream.writeAll(", ");
2088 try self.writeInstRef(stream, inst_data.operand);
2089 try stream.writeAll(")");
15712090 }
1572};
15732091
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 },
2092 fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2093 const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";
2094 const safety_str = if (inst_data.safety) "safe" else "unsafe";
2095 try stream.print("{s}) ", .{safety_str});
2096 try self.writeSrc(stream, inst_data.src());
16022097 }
1603}
16042098
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 });
2099 fn writeFnTypeCommon(
2100 self: *Writer,
2101 stream: anytype,
2102 param_types: []const Inst.Ref,
2103 ret_ty: Inst.Ref,
2104 var_args: bool,
2105 cc: Inst.Ref,
2106 src: LazySrcLoc,
2107 ) !void {
2108 try stream.writeAll("[");
2109 for (param_types) |param_type, i| {
2110 if (i != 0) try stream.writeAll(", ");
2111 try self.writeInstRef(stream, param_type);
16362112 }
1637
1638 return dtz.dumpBody(body, writer);
2113 try stream.writeAll("], ");
2114 try self.writeInstRef(stream, ret_ty);
2115 try self.writeOptionalInstRef(stream, ", cc=", cc);
2116 try self.writeFlag(stream, ", var_args", var_args);
2117 try stream.writeAll(") ");
2118 try self.writeSrc(stream, src);
16392119 }
16402120
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 },
2121 fn writeSmallStr(
2122 self: *Writer,
2123 stream: anytype,
2124 inst: Inst.Index,
2125 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
2126 const str = self.code.instructions.items(.data)[inst].small_str.get();
2127 try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
2128 }
16812129
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 }
2130 fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2131 const inst_data = self.code.instructions.items(.data)[inst].switch_capture;
2132 try self.writeInstIndex(stream, inst_data.switch_inst);
2133 try stream.print(", {d})", .{inst_data.prong_index});
17552134 }
17562135
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"),
2136 fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void {
2137 var i: usize = @enumToInt(ref);
17742138
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 },
2139 if (i < Inst.Ref.typed_value_map.len) {
2140 return stream.print("@{}", .{ref});
2141 }
2142 i -= Inst.Ref.typed_value_map.len;
18072143
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 }
2144 if (i < self.param_count) {
2145 return stream.print("${d}", .{i});
19952146 }
2147 i -= self.param_count;
2148
2149 return self.writeInstIndex(stream, @intCast(Inst.Index, i));
19962150 }
19972151
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 }
2152 fn writeInstIndex(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2153 return stream.print("%{d}", .{inst});
20122154 }
20132155
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 }
2156 fn writeOptionalInstRef(
2157 self: *Writer,
2158 stream: anytype,
2159 prefix: []const u8,
2160 inst: Inst.Ref,
2161 ) !void {
2162 if (inst == .none) return;
2163 try stream.writeAll(prefix);
2164 try self.writeInstRef(stream, inst);
20192165 }
2020};
20212166
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 }
2167 fn writeFlag(
2168 self: *Writer,
2169 stream: anytype,
2170 name: []const u8,
2171 flag: bool,
2172 ) !void {
2173 if (!flag) return;
2174 try stream.writeAll(name);
2175 }
20612176
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');
2177 fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void {
2178 const tree = self.scope.tree();
2179 const src_loc = src.toSrcLoc(self.scope);
2180 const abs_byte_off = try src_loc.byteOffset();
2181 const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off);
2182 try stream.print("{s}:{d}:{d}", .{
2183 @tagName(src), delta_line.line + 1, delta_line.column + 1,
2184 });
20662185 }
20672186
2068 try stderr.print("}} // {s} {s}\n\n", .{ kind, decl_name });
2069}
2187 fn writeBody(self: *Writer, stream: anytype, body: []const Inst.Index) !void {
2188 for (body) |inst| {
2189 try stream.writeByteNTimes(' ', self.indent);
2190 try stream.print("%{d} ", .{inst});
2191 try self.writeInstToStream(stream, inst);
2192 try stream.writeByte('\n');
2193 }
2194 }
2195};
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/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;