| author | |
| committer | |
| log | 070a28e493c6994197b2e52f42f3d0020b7395aa |
| tree | 495bf07a61916906a7f1b1d13019be91426ebbdc |
| parent | 1b657e6e41bcaf362d1cc9455c17e06e57973554 |
| parent | c9e31febf811286580792265efe20ccfa76c0fcf |
| signature |
rework ZIR memory layout; overhaul source locations33 files changed, 14719 insertions(+), 11495 deletions(-)
CMakeLists.txt+2-2| ... | ... | @@ -539,7 +539,7 @@ set(ZIG_STAGE2_SOURCES |
| 539 | 539 | "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" |
| 540 | 540 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" |
| 541 | 541 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" |
| 542 | "${CMAKE_SOURCE_DIR}/src/astgen.zig" | |
| 542 | "${CMAKE_SOURCE_DIR}/src/AstGen.zig" | |
| 543 | 543 | "${CMAKE_SOURCE_DIR}/src/clang.zig" |
| 544 | 544 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" |
| 545 | 545 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" |
| ... | ... | @@ -591,7 +591,7 @@ set(ZIG_STAGE2_SOURCES |
| 591 | 591 | "${CMAKE_SOURCE_DIR}/src/value.zig" |
| 592 | 592 | "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig" |
| 593 | 593 | "${CMAKE_SOURCE_DIR}/src/zir.zig" |
| 594 | "${CMAKE_SOURCE_DIR}/src/zir_sema.zig" | |
| 594 | "${CMAKE_SOURCE_DIR}/src/Sema.zig" | |
| 595 | 595 | ) |
| 596 | 596 | |
| 597 | 597 | if(MSVC) |
lib/std/enums.zig+48-41| ... | ... | @@ -32,7 +32,7 @@ pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_def |
| 32 | 32 | .fields = fields, |
| 33 | 33 | .decls = &[_]std.builtin.TypeInfo.Declaration{}, |
| 34 | 34 | .is_tuple = false, |
| 35 | }}); | |
| 35 | } }); | |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | 38 | /// Looks up the supplied fields in the given enum type. |
| ... | ... | @@ -70,7 +70,7 @@ pub fn values(comptime E: type) []const E { |
| 70 | 70 | |
| 71 | 71 | test "std.enum.values" { |
| 72 | 72 | 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)); | |
| 74 | 74 | } |
| 75 | 75 | |
| 76 | 76 | /// 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 { |
| 82 | 82 | |
| 83 | 83 | test "std.enum.uniqueValues" { |
| 84 | 84 | 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)); | |
| 86 | 86 | |
| 87 | 87 | const F = enum { a, b, c }; |
| 88 | testing.expectEqualSlices(F, &.{.a, .b, .c}, uniqueValues(F)); | |
| 88 | testing.expectEqualSlices(F, &.{ .a, .b, .c }, uniqueValues(F)); | |
| 89 | 89 | } |
| 90 | 90 | |
| 91 | 91 | /// 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 { |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | 104 | var unique_fields: []const EnumField = &[_]EnumField{}; |
| 105 | outer: | |
| 106 | for (raw_fields) |candidate| { | |
| 105 | outer: for (raw_fields) |candidate| { | |
| 107 | 106 | for (unique_fields) |u| { |
| 108 | 107 | if (u.value == candidate.value) |
| 109 | 108 | continue :outer; |
| ... | ... | @@ -116,28 +115,25 @@ pub fn uniqueFields(comptime E: type) []const EnumField { |
| 116 | 115 | } |
| 117 | 116 | |
| 118 | 117 | /// 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. | |
| 120 | 121 | /// If the enum contains any fields with values that cannot be represented |
| 121 | 122 | /// by usize, a compile error is issued. The max_unused_slots parameter limits |
| 122 | 123 | /// the total number of items which have no matching enum key (holes in the enum |
| 123 | 124 | /// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots |
| 124 | 125 | /// must be at least 3, to allow unused slots 0, 3, and 4. |
| 125 | 126 | fn 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 | ||
| 131 | 127 | var max_value: comptime_int = -1; |
| 132 | 128 | const max_usize: comptime_int = ~@as(usize, 0); |
| 133 | 129 | const fields = uniqueFields(E); |
| 134 | 130 | for (fields) |f| { |
| 135 | 131 | 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."); | |
| 137 | 133 | } |
| 138 | 134 | if (f.value > max_value) { |
| 139 | 135 | 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."); | |
| 141 | 137 | } |
| 142 | 138 | max_value = f.value; |
| 143 | 139 | } |
| ... | ... | @@ -147,14 +143,16 @@ fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) |
| 147 | 143 | if (unused_slots > max_unused_slots) { |
| 148 | 144 | const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots}); |
| 149 | 145 | 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."); | |
| 151 | 147 | } |
| 152 | 148 | |
| 153 | 149 | return max_value + 1; |
| 154 | 150 | } |
| 155 | 151 | |
| 156 | 152 | /// 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. | |
| 158 | 156 | /// If the enum contains any fields with values that cannot be represented |
| 159 | 157 | /// by usize, a compile error is issued. The max_unused_slots parameter limits |
| 160 | 158 | /// 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 { |
| 243 | 241 | if (@hasField(E, n)) { |
| 244 | 242 | return @field(E, n); |
| 245 | 243 | } |
| 246 | @compileError("Enum "++@typeName(E)++" has no field named "++n); | |
| 244 | @compileError("Enum " ++ @typeName(E) ++ " has no field named " ++ n); | |
| 247 | 245 | } |
| 248 | @compileError("Cannot cast from "++@typeName(@TypeOf(value))++" to "++@typeName(E)); | |
| 246 | @compileError("Cannot cast from " ++ @typeName(@TypeOf(value)) ++ " to " ++ @typeName(E)); | |
| 249 | 247 | } |
| 250 | 248 | } |
| 251 | 249 | |
| ... | ... | @@ -256,7 +254,7 @@ test "std.enums.nameCast" { |
| 256 | 254 | testing.expectEqual(A.a, nameCast(A, A.a)); |
| 257 | 255 | testing.expectEqual(A.a, nameCast(A, B.a)); |
| 258 | 256 | 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"))); | |
| 260 | 258 | testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a"))); |
| 261 | 259 | testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a"))); |
| 262 | 260 | |
| ... | ... | @@ -398,12 +396,12 @@ pub fn EnumArray(comptime E: type, comptime V: type) type { |
| 398 | 396 | pub fn NoExtension(comptime Self: type) type { |
| 399 | 397 | return NoExt; |
| 400 | 398 | } |
| 401 | const NoExt = struct{}; | |
| 399 | const NoExt = struct {}; | |
| 402 | 400 | |
| 403 | 401 | /// A set type with an Indexer mapping from keys to indices. |
| 404 | 402 | /// Presence or absence is stored as a dense bitfield. This |
| 405 | 403 | /// type does no allocation and can be copied by value. |
| 406 | pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type { | |
| 404 | pub fn IndexedSet(comptime I: type, comptime Ext: fn (type) type) type { | |
| 407 | 405 | comptime ensureIndexer(I); |
| 408 | 406 | return struct { |
| 409 | 407 | const Self = @This(); |
| ... | ... | @@ -422,7 +420,7 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type { |
| 422 | 420 | |
| 423 | 421 | bits: BitSet = BitSet.initEmpty(), |
| 424 | 422 | |
| 425 | /// Returns a set containing all possible keys. | |
| 423 | /// Returns a set containing all possible keys. | |
| 426 | 424 | pub fn initFull() Self { |
| 427 | 425 | return .{ .bits = BitSet.initFull() }; |
| 428 | 426 | } |
| ... | ... | @@ -492,7 +490,8 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type { |
| 492 | 490 | pub fn next(self: *Iterator) ?Key { |
| 493 | 491 | return if (self.inner.next()) |index| |
| 494 | 492 | Indexer.keyForIndex(index) |
| 495 | else null; | |
| 493 | else | |
| 494 | null; | |
| 496 | 495 | } |
| 497 | 496 | }; |
| 498 | 497 | }; |
| ... | ... | @@ -501,7 +500,7 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type { |
| 501 | 500 | /// A map from keys to values, using an index lookup. Uses a |
| 502 | 501 | /// bitfield to track presence and a dense array of values. |
| 503 | 502 | /// This type does no allocation and can be copied by value. |
| 504 | pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type { | |
| 503 | pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn (type) type) type { | |
| 505 | 504 | comptime ensureIndexer(I); |
| 506 | 505 | return struct { |
| 507 | 506 | const Self = @This(); |
| ... | ... | @@ -652,7 +651,8 @@ pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type |
| 652 | 651 | .key = Indexer.keyForIndex(index), |
| 653 | 652 | .value = &self.values[index], |
| 654 | 653 | } |
| 655 | else null; | |
| 654 | else | |
| 655 | null; | |
| 656 | 656 | } |
| 657 | 657 | }; |
| 658 | 658 | }; |
| ... | ... | @@ -660,7 +660,7 @@ pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type |
| 660 | 660 | |
| 661 | 661 | /// A dense array of values, using an indexed lookup. |
| 662 | 662 | /// This type does no allocation and can be copied by value. |
| 663 | pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type { | |
| 663 | pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn (type) type) type { | |
| 664 | 664 | comptime ensureIndexer(I); |
| 665 | 665 | return struct { |
| 666 | 666 | const Self = @This(); |
| ... | ... | @@ -769,9 +769,9 @@ pub fn ensureIndexer(comptime T: type) void { |
| 769 | 769 | if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize."); |
| 770 | 770 | if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize."); |
| 771 | 771 | 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."); | |
| 773 | 773 | 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."); | |
| 775 | 775 | } |
| 776 | 776 | } |
| 777 | 777 | |
| ... | ... | @@ -802,14 +802,18 @@ pub fn EnumIndexer(comptime E: type) type { |
| 802 | 802 | return struct { |
| 803 | 803 | pub const Key = E; |
| 804 | 804 | 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 | } | |
| 807 | 811 | }; |
| 808 | 812 | } |
| 809 | 813 | std.sort.sort(EnumField, &fields, {}, ascByValue); |
| 810 | 814 | 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) { | |
| 813 | 817 | return struct { |
| 814 | 818 | pub const Key = E; |
| 815 | 819 | pub const count = fields.len; |
| ... | ... | @@ -844,7 +848,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 844 | 848 | } |
| 845 | 849 | |
| 846 | 850 | test "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 }; | |
| 848 | 852 | const Indexer = EnumIndexer(E); |
| 849 | 853 | ensureIndexer(Indexer); |
| 850 | 854 | testing.expectEqual(E, Indexer.Key); |
| ... | ... | @@ -908,7 +912,7 @@ test "std.enums.EnumIndexer sparse" { |
| 908 | 912 | } |
| 909 | 913 | |
| 910 | 914 | test "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 }; | |
| 912 | 916 | const Indexer = EnumIndexer(E); |
| 913 | 917 | ensureIndexer(Indexer); |
| 914 | 918 | testing.expectEqual(E, Indexer.Key); |
| ... | ... | @@ -957,7 +961,8 @@ test "std.enums.EnumSet" { |
| 957 | 961 | } |
| 958 | 962 | |
| 959 | 963 | var mut = Set.init(.{ |
| 960 | .a=true, .c=true, | |
| 964 | .a = true, | |
| 965 | .c = true, | |
| 961 | 966 | }); |
| 962 | 967 | testing.expectEqual(@as(usize, 2), mut.count()); |
| 963 | 968 | testing.expectEqual(true, mut.contains(.a)); |
| ... | ... | @@ -986,7 +991,7 @@ test "std.enums.EnumSet" { |
| 986 | 991 | testing.expectEqual(@as(?E, null), it.next()); |
| 987 | 992 | } |
| 988 | 993 | |
| 989 | mut.toggleSet(Set.init(.{ .a=true, .b=true })); | |
| 994 | mut.toggleSet(Set.init(.{ .a = true, .b = true })); | |
| 990 | 995 | testing.expectEqual(@as(usize, 2), mut.count()); |
| 991 | 996 | testing.expectEqual(true, mut.contains(.a)); |
| 992 | 997 | testing.expectEqual(false, mut.contains(.b)); |
| ... | ... | @@ -994,7 +999,7 @@ test "std.enums.EnumSet" { |
| 994 | 999 | testing.expectEqual(true, mut.contains(.d)); |
| 995 | 1000 | testing.expectEqual(true, mut.contains(.e)); // aliases a |
| 996 | 1001 | |
| 997 | mut.setUnion(Set.init(.{ .a=true, .b=true })); | |
| 1002 | mut.setUnion(Set.init(.{ .a = true, .b = true })); | |
| 998 | 1003 | testing.expectEqual(@as(usize, 3), mut.count()); |
| 999 | 1004 | testing.expectEqual(true, mut.contains(.a)); |
| 1000 | 1005 | testing.expectEqual(true, mut.contains(.b)); |
| ... | ... | @@ -1009,7 +1014,7 @@ test "std.enums.EnumSet" { |
| 1009 | 1014 | testing.expectEqual(false, mut.contains(.c)); |
| 1010 | 1015 | testing.expectEqual(true, mut.contains(.d)); |
| 1011 | 1016 | |
| 1012 | mut.setIntersection(Set.init(.{ .a=true, .b=true })); | |
| 1017 | mut.setIntersection(Set.init(.{ .a = true, .b = true })); | |
| 1013 | 1018 | testing.expectEqual(@as(usize, 1), mut.count()); |
| 1014 | 1019 | testing.expectEqual(true, mut.contains(.a)); |
| 1015 | 1020 | testing.expectEqual(false, mut.contains(.b)); |
| ... | ... | @@ -1072,7 +1077,7 @@ test "std.enums.EnumArray sized" { |
| 1072 | 1077 | const undef = Array.initUndefined(); |
| 1073 | 1078 | var inst = Array.initFill(5); |
| 1074 | 1079 | 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 }); | |
| 1076 | 1081 | |
| 1077 | 1082 | testing.expectEqual(@as(usize, 5), inst.get(.a)); |
| 1078 | 1083 | testing.expectEqual(@as(usize, 5), inst.get(.b)); |
| ... | ... | @@ -1272,10 +1277,12 @@ test "std.enums.EnumMap sized" { |
| 1272 | 1277 | var iter = a.iterator(); |
| 1273 | 1278 | const Entry = Map.Entry; |
| 1274 | 1279 | testing.expectEqual(@as(?Entry, Entry{ |
| 1275 | .key = .b, .value = &a.values[1], | |
| 1280 | .key = .b, | |
| 1281 | .value = &a.values[1], | |
| 1276 | 1282 | }), iter.next()); |
| 1277 | 1283 | testing.expectEqual(@as(?Entry, Entry{ |
| 1278 | .key = .d, .value = &a.values[3], | |
| 1284 | .key = .d, | |
| 1285 | .value = &a.values[3], | |
| 1279 | 1286 | }), iter.next()); |
| 1280 | 1287 | testing.expectEqual(@as(?Entry, null), iter.next()); |
| 1281 | 1288 | } |
lib/std/zig.zig+1-1| ... | ... | @@ -11,7 +11,7 @@ pub const Tokenizer = tokenizer.Tokenizer; |
| 11 | 11 | pub const fmtId = @import("zig/fmt.zig").fmtId; |
| 12 | 12 | pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes; |
| 13 | 13 | pub const parse = @import("zig/parse.zig").parse; |
| 14 | pub const parseStringLiteral = @import("zig/string_literal.zig").parse; | |
| 14 | pub const string_literal = @import("zig/string_literal.zig"); | |
| 15 | 15 | pub const ast = @import("zig/ast.zig"); |
| 16 | 16 | pub const system = @import("zig/system.zig"); |
| 17 | 17 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; |
lib/std/zig/ast.zig+10-4| ... | ... | @@ -1252,6 +1252,7 @@ pub const Tree = struct { |
| 1252 | 1252 | buffer[0] = data.lhs; |
| 1253 | 1253 | const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1]; |
| 1254 | 1254 | return tree.fullFnProto(.{ |
| 1255 | .proto_node = node, | |
| 1255 | 1256 | .fn_token = tree.nodes.items(.main_token)[node], |
| 1256 | 1257 | .return_type = data.rhs, |
| 1257 | 1258 | .params = params, |
| ... | ... | @@ -1267,6 +1268,7 @@ pub const Tree = struct { |
| 1267 | 1268 | const params_range = tree.extraData(data.lhs, Node.SubRange); |
| 1268 | 1269 | const params = tree.extra_data[params_range.start..params_range.end]; |
| 1269 | 1270 | return tree.fullFnProto(.{ |
| 1271 | .proto_node = node, | |
| 1270 | 1272 | .fn_token = tree.nodes.items(.main_token)[node], |
| 1271 | 1273 | .return_type = data.rhs, |
| 1272 | 1274 | .params = params, |
| ... | ... | @@ -1283,6 +1285,7 @@ pub const Tree = struct { |
| 1283 | 1285 | buffer[0] = extra.param; |
| 1284 | 1286 | const params = if (extra.param == 0) buffer[0..0] else buffer[0..1]; |
| 1285 | 1287 | return tree.fullFnProto(.{ |
| 1288 | .proto_node = node, | |
| 1286 | 1289 | .fn_token = tree.nodes.items(.main_token)[node], |
| 1287 | 1290 | .return_type = data.rhs, |
| 1288 | 1291 | .params = params, |
| ... | ... | @@ -1298,6 +1301,7 @@ pub const Tree = struct { |
| 1298 | 1301 | const extra = tree.extraData(data.lhs, Node.FnProto); |
| 1299 | 1302 | const params = tree.extra_data[extra.params_start..extra.params_end]; |
| 1300 | 1303 | return tree.fullFnProto(.{ |
| 1304 | .proto_node = node, | |
| 1301 | 1305 | .fn_token = tree.nodes.items(.main_token)[node], |
| 1302 | 1306 | .return_type = data.rhs, |
| 1303 | 1307 | .params = params, |
| ... | ... | @@ -1430,7 +1434,7 @@ pub const Tree = struct { |
| 1430 | 1434 | .ast = .{ |
| 1431 | 1435 | .lbracket = tree.nodes.items(.main_token)[node], |
| 1432 | 1436 | .elem_count = data.lhs, |
| 1433 | .sentinel = null, | |
| 1437 | .sentinel = 0, | |
| 1434 | 1438 | .elem_type = data.rhs, |
| 1435 | 1439 | }, |
| 1436 | 1440 | }; |
| ... | ... | @@ -1440,6 +1444,7 @@ pub const Tree = struct { |
| 1440 | 1444 | assert(tree.nodes.items(.tag)[node] == .array_type_sentinel); |
| 1441 | 1445 | const data = tree.nodes.items(.data)[node]; |
| 1442 | 1446 | const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel); |
| 1447 | assert(extra.sentinel != 0); | |
| 1443 | 1448 | return .{ |
| 1444 | 1449 | .ast = .{ |
| 1445 | 1450 | .lbracket = tree.nodes.items(.main_token)[node], |
| ... | ... | @@ -2119,6 +2124,7 @@ pub const full = struct { |
| 2119 | 2124 | ast: Ast, |
| 2120 | 2125 | |
| 2121 | 2126 | pub const Ast = struct { |
| 2127 | proto_node: Node.Index, | |
| 2122 | 2128 | fn_token: TokenIndex, |
| 2123 | 2129 | return_type: Node.Index, |
| 2124 | 2130 | params: []const Node.Index, |
| ... | ... | @@ -2262,7 +2268,7 @@ pub const full = struct { |
| 2262 | 2268 | pub const Ast = struct { |
| 2263 | 2269 | lbracket: TokenIndex, |
| 2264 | 2270 | elem_count: Node.Index, |
| 2265 | sentinel: ?Node.Index, | |
| 2271 | sentinel: Node.Index, | |
| 2266 | 2272 | elem_type: Node.Index, |
| 2267 | 2273 | }; |
| 2268 | 2274 | }; |
| ... | ... | @@ -2549,9 +2555,9 @@ pub const Node = struct { |
| 2549 | 2555 | @"await", |
| 2550 | 2556 | /// `?lhs`. rhs unused. main_token is the `?`. |
| 2551 | 2557 | optional_type, |
| 2552 | /// `[lhs]rhs`. lhs can be omitted to make it a slice. | |
| 2558 | /// `[lhs]rhs`. | |
| 2553 | 2559 | array_type, |
| 2554 | /// `[lhs:a]b`. `array_type_sentinel[rhs]`. | |
| 2560 | /// `[lhs:a]b`. `ArrayTypeSentinel[rhs]`. | |
| 2555 | 2561 | array_type_sentinel, |
| 2556 | 2562 | /// `[*]align(lhs) rhs`. lhs can be omitted. |
| 2557 | 2563 | /// `*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 { |
| 59 | 59 | parser.nodes.appendAssumeCapacity(.{ |
| 60 | 60 | .tag = .root, |
| 61 | 61 | .main_token = 0, |
| 62 | .data = .{ | |
| 63 | .lhs = undefined, | |
| 64 | .rhs = undefined, | |
| 65 | }, | |
| 62 | .data = undefined, | |
| 66 | 63 | }); |
| 67 | 64 | const root_members = try parser.parseContainerMembers(); |
| 68 | 65 | const root_decls = try root_members.toSpan(&parser); |
| ... | ... | @@ -139,6 +136,16 @@ const Parser = struct { |
| 139 | 136 | return result; |
| 140 | 137 | } |
| 141 | 138 | |
| 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 | ||
| 142 | 149 | fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index { |
| 143 | 150 | const fields = std.meta.fields(@TypeOf(extra)); |
| 144 | 151 | try p.extra_data.ensureCapacity(p.gpa, p.extra_data.items.len + fields.len); |
| ... | ... | @@ -554,9 +561,10 @@ const Parser = struct { |
| 554 | 561 | return fn_proto; |
| 555 | 562 | }, |
| 556 | 563 | .l_brace => { |
| 564 | const fn_decl_index = try p.reserveNode(); | |
| 557 | 565 | const body_block = try p.parseBlock(); |
| 558 | 566 | assert(body_block != 0); |
| 559 | return p.addNode(.{ | |
| 567 | return p.setNode(fn_decl_index, .{ | |
| 560 | 568 | .tag = .fn_decl, |
| 561 | 569 | .main_token = p.nodes.items(.main_token)[fn_proto], |
| 562 | 570 | .data = .{ |
| ... | ... | @@ -634,6 +642,10 @@ const Parser = struct { |
| 634 | 642 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr) |
| 635 | 643 | fn parseFnProto(p: *Parser) !Node.Index { |
| 636 | 644 | 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 | ||
| 637 | 649 | _ = p.eatToken(.identifier); |
| 638 | 650 | const params = try p.parseParamDeclList(); |
| 639 | 651 | defer params.deinit(p.gpa); |
| ... | ... | @@ -651,7 +663,7 @@ const Parser = struct { |
| 651 | 663 | |
| 652 | 664 | if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) { |
| 653 | 665 | switch (params) { |
| 654 | .zero_or_one => |param| return p.addNode(.{ | |
| 666 | .zero_or_one => |param| return p.setNode(fn_proto_index, .{ | |
| 655 | 667 | .tag = .fn_proto_simple, |
| 656 | 668 | .main_token = fn_token, |
| 657 | 669 | .data = .{ |
| ... | ... | @@ -661,7 +673,7 @@ const Parser = struct { |
| 661 | 673 | }), |
| 662 | 674 | .multi => |list| { |
| 663 | 675 | const span = try p.listToSpan(list); |
| 664 | return p.addNode(.{ | |
| 676 | return p.setNode(fn_proto_index, .{ | |
| 665 | 677 | .tag = .fn_proto_multi, |
| 666 | 678 | .main_token = fn_token, |
| 667 | 679 | .data = .{ |
| ... | ... | @@ -676,7 +688,7 @@ const Parser = struct { |
| 676 | 688 | } |
| 677 | 689 | } |
| 678 | 690 | switch (params) { |
| 679 | .zero_or_one => |param| return p.addNode(.{ | |
| 691 | .zero_or_one => |param| return p.setNode(fn_proto_index, .{ | |
| 680 | 692 | .tag = .fn_proto_one, |
| 681 | 693 | .main_token = fn_token, |
| 682 | 694 | .data = .{ |
| ... | ... | @@ -691,7 +703,7 @@ const Parser = struct { |
| 691 | 703 | }), |
| 692 | 704 | .multi => |list| { |
| 693 | 705 | const span = try p.listToSpan(list); |
| 694 | return p.addNode(.{ | |
| 706 | return p.setNode(fn_proto_index, .{ | |
| 695 | 707 | .tag = .fn_proto, |
| 696 | 708 | .main_token = fn_token, |
| 697 | 709 | .data = .{ |
lib/std/zig/render.zig+3-3| ... | ... | @@ -717,9 +717,9 @@ fn renderArrayType( |
| 717 | 717 | ais.pushIndentNextLine(); |
| 718 | 718 | try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket |
| 719 | 719 | try renderExpression(gpa, ais, tree, array_type.ast.elem_count, inner_space); |
| 720 | if (array_type.ast.sentinel) |sentinel| { | |
| 721 | try renderToken(ais, tree, tree.firstToken(sentinel) - 1, inner_space); // colon | |
| 722 | try renderExpression(gpa, ais, tree, sentinel, inner_space); | |
| 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); | |
| 723 | 723 | } |
| 724 | 724 | ais.popIndent(); |
| 725 | 725 | try renderToken(ais, tree, rbracket, .none); // rbracket |
lib/std/zig/string_literal.zig+82-52| ... | ... | @@ -6,112 +6,143 @@ |
| 6 | 6 | const std = @import("../std.zig"); |
| 7 | 7 | const assert = std.debug.assert; |
| 8 | 8 | |
| 9 | const State = enum { | |
| 10 | Start, | |
| 11 | Backslash, | |
| 12 | }; | |
| 13 | ||
| 14 | 9 | pub const ParseError = error{ |
| 15 | 10 | OutOfMemory, |
| 11 | InvalidStringLiteral, | |
| 12 | }; | |
| 16 | 13 | |
| 17 | /// When this is returned, index will be the position of the character. | |
| 18 | InvalidCharacter, | |
| 14 | pub 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, | |
| 19 | 28 | }; |
| 20 | 29 | |
| 21 | /// caller owns returned memory | |
| 22 | pub 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. | |
| 32 | pub fn parseAppend(buf: *std.ArrayList(u8), bytes: []const u8) error{OutOfMemory}!Result { | |
| 27 | 33 | assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"'); |
| 34 | const slice = bytes[1..]; | |
| 28 | 35 | |
| 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); | |
| 31 | 39 | |
| 32 | const slice = bytes[1..]; | |
| 33 | try list.ensureCapacity(slice.len - 1); | |
| 40 | const State = enum { | |
| 41 | Start, | |
| 42 | Backslash, | |
| 43 | }; | |
| 34 | 44 | |
| 35 | 45 | var state = State.Start; |
| 36 | 46 | var index: usize = 0; |
| 37 | while (index < slice.len) : (index += 1) { | |
| 47 | while (true) : (index += 1) { | |
| 38 | 48 | const b = slice[index]; |
| 39 | 49 | |
| 40 | 50 | switch (state) { |
| 41 | 51 | State.Start => switch (b) { |
| 42 | 52 | '\\' => state = State.Backslash, |
| 43 | 53 | '\n' => { |
| 44 | bad_index.* = index; | |
| 45 | return error.InvalidCharacter; | |
| 54 | return Result{ .invalid_character = index }; | |
| 46 | 55 | }, |
| 47 | '"' => return list.toOwnedSlice(), | |
| 48 | else => try list.append(b), | |
| 56 | '"' => return Result.success, | |
| 57 | else => try buf.append(b), | |
| 49 | 58 | }, |
| 50 | 59 | State.Backslash => switch (b) { |
| 51 | 60 | 'n' => { |
| 52 | try list.append('\n'); | |
| 61 | try buf.append('\n'); | |
| 53 | 62 | state = State.Start; |
| 54 | 63 | }, |
| 55 | 64 | 'r' => { |
| 56 | try list.append('\r'); | |
| 65 | try buf.append('\r'); | |
| 57 | 66 | state = State.Start; |
| 58 | 67 | }, |
| 59 | 68 | '\\' => { |
| 60 | try list.append('\\'); | |
| 69 | try buf.append('\\'); | |
| 61 | 70 | state = State.Start; |
| 62 | 71 | }, |
| 63 | 72 | 't' => { |
| 64 | try list.append('\t'); | |
| 73 | try buf.append('\t'); | |
| 65 | 74 | state = State.Start; |
| 66 | 75 | }, |
| 67 | 76 | '\'' => { |
| 68 | try list.append('\''); | |
| 77 | try buf.append('\''); | |
| 69 | 78 | state = State.Start; |
| 70 | 79 | }, |
| 71 | 80 | '"' => { |
| 72 | try list.append('"'); | |
| 81 | try buf.append('"'); | |
| 73 | 82 | state = State.Start; |
| 74 | 83 | }, |
| 75 | 84 | 'x' => { |
| 76 | 85 | // TODO: add more/better/broader tests for this. |
| 77 | 86 | 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 | } | |
| 88 | 100 | }, |
| 89 | 101 | 'u' => { |
| 90 | 102 | // 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] == '{') { | |
| 92 | 106 | if (std.mem.indexOfScalarPos(u8, slice[0..std.math.min(index + 9, slice.len)], index + 3, '}')) |index_end| { |
| 93 | 107 | const hex_str = slice[index + 2 .. index_end]; |
| 94 | 108 | if (std.fmt.parseUnsigned(u32, hex_str, 16)) |uint| { |
| 95 | 109 | if (uint <= 0x10ffff) { |
| 96 | try list.appendSlice(std.mem.toBytes(uint)[0..]); | |
| 110 | try buf.appendSlice(std.mem.toBytes(uint)[0..]); | |
| 97 | 111 | state = State.Start; |
| 98 | 112 | index = index_end; // loop-header increments |
| 99 | 113 | continue; |
| 100 | 114 | } |
| 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 | } | |
| 106 | 127 | }, |
| 107 | 128 | else => { |
| 108 | bad_index.* = index; | |
| 109 | return error.InvalidCharacter; | |
| 129 | return Result{ .invalid_character = index }; | |
| 110 | 130 | }, |
| 111 | 131 | }, |
| 112 | 132 | } |
| 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. | |
| 138 | pub 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, | |
| 113 | 145 | } |
| 114 | unreachable; | |
| 115 | 146 | } |
| 116 | 147 | |
| 117 | 148 | test "parse" { |
| ... | ... | @@ -121,9 +152,8 @@ test "parse" { |
| 121 | 152 | var fixed_buf_mem: [32]u8 = undefined; |
| 122 | 153 | var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buf_mem[0..]); |
| 123 | 154 | var alloc = &fixed_buf_alloc.allocator; |
| 124 | var bad_index: usize = undefined; | |
| 125 | 155 | |
| 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}\""))); | |
| 129 | 159 | } |
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 | ||
| 6 | const AstGen = @This(); | |
| 7 | ||
| 8 | const std = @import("std"); | |
| 9 | const ast = std.zig.ast; | |
| 10 | const mem = std.mem; | |
| 11 | const Allocator = std.mem.Allocator; | |
| 12 | const assert = std.debug.assert; | |
| 13 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | |
| 14 | ||
| 15 | const Value = @import("value.zig").Value; | |
| 16 | const Type = @import("type.zig").Type; | |
| 17 | const TypedValue = @import("TypedValue.zig"); | |
| 18 | const zir = @import("zir.zig"); | |
| 19 | const Module = @import("Module.zig"); | |
| 20 | const trace = @import("tracy.zig").trace; | |
| 21 | const Scope = Module.Scope; | |
| 22 | const GenZir = Scope.GenZir; | |
| 23 | const InnerError = Module.InnerError; | |
| 24 | const Decl = Module.Decl; | |
| 25 | const LazySrcLoc = Module.LazySrcLoc; | |
| 26 | const BuiltinFn = @import("BuiltinFn.zig"); | |
| 27 | ||
| 28 | instructions: std.MultiArrayList(zir.Inst) = .{}, | |
| 29 | string_bytes: ArrayListUnmanaged(u8) = .{}, | |
| 30 | extra: ArrayListUnmanaged(u32) = .{}, | |
| 31 | decl_map: std.StringArrayHashMapUnmanaged(void) = .{}, | |
| 32 | decls: 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. | |
| 35 | ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len, | |
| 36 | mod: *Module, | |
| 37 | decl: *Decl, | |
| 38 | arena: *Allocator, | |
| 39 | ||
| 40 | /// Call `deinit` on the result. | |
| 41 | pub 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 | ||
| 58 | pub 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 | ||
| 64 | pub 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 | ||
| 77 | pub 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 | ||
| 82 | pub fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const zir.Inst.Ref) void { | |
| 83 | const coerced = @bitCast([]const u32, refs); | |
| 84 | astgen.extra.appendSliceAssumeCapacity(coerced); | |
| 85 | } | |
| 86 | ||
| 87 | pub 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 | ||
| 95 | pub fn indexToRef(astgen: AstGen, inst: zir.Inst.Index) zir.Inst.Ref { | |
| 96 | return @intToEnum(zir.Inst.Ref, astgen.ref_start_index + inst); | |
| 97 | } | |
| 98 | ||
| 99 | pub 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 | ||
| 108 | pub 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 | ||
| 117 | pub 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 | ||
| 192 | pub 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 | ||
| 196 | fn 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. | |
| 390 | pub 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 | ||
| 791 | pub 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 | ||
| 804 | fn 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 | ||
| 870 | fn 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 | ||
| 915 | pub 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 | ||
| 940 | fn 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 | ||
| 982 | fn 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 | ||
| 1068 | fn 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 | ||
| 1307 | fn 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 | ||
| 1512 | fn 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 | ||
| 1532 | fn 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 | ||
| 1553 | fn 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 | ||
| 1562 | fn 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 | ||
| 1571 | fn 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 | ||
| 1586 | fn 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 | ||
| 1674 | fn 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 | ||
| 1686 | fn 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 | ||
| 1700 | fn 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 | ||
| 1709 | fn 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 | ||
| 1769 | fn 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 | ||
| 1886 | fn 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. | |
| 1950 | fn 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 | ||
| 1956 | pub 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 | ||
| 1986 | fn 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 | ||
| 2009 | fn 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 | ||
| 2026 | fn 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 | ||
| 2043 | fn 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 | ||
| 2069 | fn 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 | ||
| 2165 | fn 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. | |
| 2188 | fn 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 | ||
| 2217 | fn 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 | ||
| 2352 | fn 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 | ||
| 2527 | fn 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 | ||
| 2539 | pub 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 | ||
| 2632 | fn 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 | ||
| 3219 | fn 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 | ||
| 3237 | fn 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 | ||
| 3326 | fn 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 | ||
| 3350 | fn 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 | ||
| 3393 | fn 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 | ||
| 3413 | fn 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 | ||
| 3435 | fn 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 | ||
| 3463 | fn 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 | ||
| 3515 | fn 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 | ||
| 3544 | fn 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 | ||
| 3590 | fn 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 | ||
| 3624 | fn 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 | ||
| 3652 | fn 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 | ||
| 3867 | fn 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 | ||
| 3917 | pub 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 | ||
| 3957 | fn 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. | |
| 4174 | fn 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 { |
| 259 | 259 | /// To support incremental compilation, errors are stored in various places |
| 260 | 260 | /// so that they can be created and destroyed appropriately. This structure |
| 261 | 261 | /// 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 | |
| 263 | 263 | /// and freed all at once. |
| 264 | 264 | pub const AllErrors = struct { |
| 265 | 265 | arena: std.heap.ArenaAllocator.State, |
| ... | ... | @@ -267,11 +267,11 @@ pub const AllErrors = struct { |
| 267 | 267 | |
| 268 | 268 | pub const Message = union(enum) { |
| 269 | 269 | src: struct { |
| 270 | src_path: []const u8, | |
| 271 | line: usize, | |
| 272 | column: usize, | |
| 273 | byte_offset: usize, | |
| 274 | 270 | msg: []const u8, |
| 271 | src_path: []const u8, | |
| 272 | line: u32, | |
| 273 | column: u32, | |
| 274 | byte_offset: u32, | |
| 275 | 275 | notes: []Message = &.{}, |
| 276 | 276 | }, |
| 277 | 277 | plain: struct { |
| ... | ... | @@ -316,29 +316,31 @@ pub const AllErrors = struct { |
| 316 | 316 | const notes = try arena.allocator.alloc(Message, module_err_msg.notes.len); |
| 317 | 317 | for (notes) |*note, i| { |
| 318 | 318 | 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; | |
| 322 | 323 | note.* = .{ |
| 323 | 324 | .src = .{ |
| 324 | 325 | .src_path = try arena.allocator.dupe(u8, sub_file_path), |
| 325 | 326 | .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), | |
| 329 | 330 | }, |
| 330 | 331 | }; |
| 331 | 332 | } |
| 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; | |
| 335 | 337 | try errors.append(.{ |
| 336 | 338 | .src = .{ |
| 337 | 339 | .src_path = try arena.allocator.dupe(u8, sub_file_path), |
| 338 | 340 | .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), | |
| 342 | 344 | .notes = notes, |
| 343 | 345 | }, |
| 344 | 346 | }); |
| ... | ... | @@ -939,6 +941,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 939 | 941 | }; |
| 940 | 942 | |
| 941 | 943 | const module = try arena.create(Module); |
| 944 | errdefer module.deinit(); | |
| 942 | 945 | module.* = .{ |
| 943 | 946 | .gpa = gpa, |
| 944 | 947 | .comp = comp, |
| ... | ... | @@ -946,7 +949,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 946 | 949 | .root_scope = root_scope, |
| 947 | 950 | .zig_cache_artifact_directory = zig_cache_artifact_directory, |
| 948 | 951 | .emit_h = options.emit_h, |
| 952 | .error_name_list = try std.ArrayListUnmanaged([]const u8).initCapacity(gpa, 1), | |
| 949 | 953 | }; |
| 954 | module.error_name_list.appendAssumeCapacity("(no error)"); | |
| 950 | 955 | break :blk module; |
| 951 | 956 | } else blk: { |
| 952 | 957 | if (options.emit_h != null) return error.NoZigModuleForCHeader; |
src/Module.zig+2098-1917| ... | ... | @@ -1,31 +1,32 @@ |
| 1 | const 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 | ||
| 2 | 5 | const std = @import("std"); |
| 3 | const Compilation = @import("Compilation.zig"); | |
| 4 | 6 | const mem = std.mem; |
| 5 | 7 | const Allocator = std.mem.Allocator; |
| 6 | 8 | const ArrayListUnmanaged = std.ArrayListUnmanaged; |
| 7 | const Value = @import("value.zig").Value; | |
| 8 | const Type = @import("type.zig").Type; | |
| 9 | const TypedValue = @import("TypedValue.zig"); | |
| 10 | 9 | const assert = std.debug.assert; |
| 11 | 10 | const log = std.log.scoped(.module); |
| 12 | 11 | const BigIntConst = std.math.big.int.Const; |
| 13 | 12 | const BigIntMutable = std.math.big.int.Mutable; |
| 14 | 13 | const Target = std.Target; |
| 14 | const ast = std.zig.ast; | |
| 15 | ||
| 16 | const Module = @This(); | |
| 17 | const Compilation = @import("Compilation.zig"); | |
| 18 | const Value = @import("value.zig").Value; | |
| 19 | const Type = @import("type.zig").Type; | |
| 20 | const TypedValue = @import("TypedValue.zig"); | |
| 15 | 21 | const Package = @import("Package.zig"); |
| 16 | 22 | const link = @import("link.zig"); |
| 17 | 23 | const ir = @import("ir.zig"); |
| 18 | 24 | const zir = @import("zir.zig"); |
| 19 | const Inst = ir.Inst; | |
| 20 | const Body = ir.Body; | |
| 21 | const ast = std.zig.ast; | |
| 22 | 25 | const trace = @import("tracy.zig").trace; |
| 23 | const astgen = @import("astgen.zig"); | |
| 24 | const zir_sema = @import("zir_sema.zig"); | |
| 26 | const AstGen = @import("AstGen.zig"); | |
| 27 | const Sema = @import("Sema.zig"); | |
| 25 | 28 | const target_util = @import("target.zig"); |
| 26 | 29 | |
| 27 | const default_eval_branch_quota = 1000; | |
| 28 | ||
| 29 | 30 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 30 | 31 | gpa: *Allocator, |
| 31 | 32 | comp: *Compilation, |
| ... | ... | @@ -77,7 +78,12 @@ next_anon_name_index: usize = 0, |
| 77 | 78 | deletion_set: ArrayListUnmanaged(*Decl) = .{}, |
| 78 | 79 | |
| 79 | 80 | /// Error tags and their values, tag names are duped with mod.gpa. |
| 80 | global_error_set: std.StringHashMapUnmanaged(u16) = .{}, | |
| 81 | /// Corresponds with `error_name_list`. | |
| 82 | global_error_set: std.StringHashMapUnmanaged(ErrorInt) = .{}, | |
| 83 | ||
| 84 | /// ErrorInt -> []const u8 for fast lookups for @intToError at comptime | |
| 85 | /// Corresponds with `global_error_set`. | |
| 86 | error_name_list: ArrayListUnmanaged([]const u8) = .{}, | |
| 81 | 87 | |
| 82 | 88 | /// Keys are fully qualified paths |
| 83 | 89 | import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, |
| ... | ... | @@ -102,12 +108,13 @@ stage1_flags: packed struct { |
| 102 | 108 | |
| 103 | 109 | emit_h: ?Compilation.EmitLoc, |
| 104 | 110 | |
| 105 | compile_log_text: std.ArrayListUnmanaged(u8) = .{}, | |
| 111 | compile_log_text: ArrayListUnmanaged(u8) = .{}, | |
| 112 | ||
| 113 | pub const ErrorInt = u32; | |
| 106 | 114 | |
| 107 | 115 | pub const Export = struct { |
| 108 | 116 | options: std.builtin.ExportOptions, |
| 109 | /// Byte offset into the file that contains the export directive. | |
| 110 | src: usize, | |
| 117 | src: LazySrcLoc, | |
| 111 | 118 | /// Represents the position of the export, if any, in the output file. |
| 112 | 119 | link: link.File.Export, |
| 113 | 120 | /// The Decl that performs the export. Note that this is *not* the Decl being exported. |
| ... | ... | @@ -132,11 +139,12 @@ pub const DeclPlusEmitH = struct { |
| 132 | 139 | }; |
| 133 | 140 | |
| 134 | 141 | pub 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. | |
| 140 | 148 | /// Memory owned by this decl, using Module's allocator. |
| 141 | 149 | name: [*:0]const u8, |
| 142 | 150 | /// The direct parent container of the Decl. |
| ... | ... | @@ -219,73 +227,102 @@ pub const Decl = struct { |
| 219 | 227 | /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself` |
| 220 | 228 | pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false); |
| 221 | 229 | |
| 222 | pub fn destroy(self: *Decl, module: *Module) void { | |
| 230 | pub fn destroy(decl: *Decl, module: *Module) void { | |
| 223 | 231 | 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 | } | |
| 226 | 238 | tvm.deinit(gpa); |
| 227 | 239 | } |
| 228 | self.dependants.deinit(gpa); | |
| 229 | self.dependencies.deinit(gpa); | |
| 240 | decl.dependants.deinit(gpa); | |
| 241 | decl.dependencies.deinit(gpa); | |
| 230 | 242 | 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); | |
| 232 | 244 | decl_plus_emit_h.emit_h.fwd_decl.deinit(gpa); |
| 233 | 245 | gpa.destroy(decl_plus_emit_h); |
| 234 | 246 | } else { |
| 235 | gpa.destroy(self); | |
| 247 | gpa.destroy(decl); | |
| 236 | 248 | } |
| 237 | 249 | } |
| 238 | 250 | |
| 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 { | |
| 240 | 268 | return .{ |
| 241 | .byte_offset = self.src(), | |
| 242 | .file_scope = self.getFileScope(), | |
| 269 | .container = .{ .decl = decl }, | |
| 270 | .lazy = .{ .node_offset = 0 }, | |
| 243 | 271 | }; |
| 244 | 272 | } |
| 245 | 273 | |
| 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()]; | |
| 250 | 287 | } |
| 251 | 288 | |
| 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)); | |
| 254 | 291 | } |
| 255 | 292 | |
| 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; | |
| 258 | 295 | return tvm.typed_value; |
| 259 | 296 | } |
| 260 | 297 | |
| 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; | |
| 263 | 300 | } |
| 264 | 301 | |
| 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); | |
| 267 | 304 | std.debug.print("{s}:{d}:{d} name={s} status={s}", .{ |
| 268 | self.scope.sub_file_path, | |
| 305 | decl.scope.sub_file_path, | |
| 269 | 306 | loc.line + 1, |
| 270 | 307 | loc.column + 1, |
| 271 | mem.spanZ(self.name), | |
| 272 | @tagName(self.analysis), | |
| 308 | mem.spanZ(decl.name), | |
| 309 | @tagName(decl.analysis), | |
| 273 | 310 | }); |
| 274 | if (self.typedValueManaged()) |tvm| { | |
| 311 | if (decl.typedValueManaged()) |tvm| { | |
| 275 | 312 | std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val }); |
| 276 | 313 | } |
| 277 | 314 | std.debug.print("\n", .{}); |
| 278 | 315 | } |
| 279 | 316 | |
| 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) { | |
| 282 | 319 | .most_recent => |*x| return x, |
| 283 | 320 | .never_succeeded => return null, |
| 284 | 321 | } |
| 285 | 322 | } |
| 286 | 323 | |
| 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; | |
| 289 | 326 | } |
| 290 | 327 | |
| 291 | 328 | pub fn getEmitH(decl: *Decl, module: *Module) *EmitH { |
| ... | ... | @@ -294,21 +331,32 @@ pub const Decl = struct { |
| 294 | 331 | return &decl_plus_emit_h.emit_h; |
| 295 | 332 | } |
| 296 | 333 | |
| 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); | |
| 299 | 336 | } |
| 300 | 337 | |
| 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); | |
| 303 | 340 | } |
| 304 | 341 | }; |
| 305 | 342 | |
| 306 | 343 | /// This state is attached to every Decl when Module emit_h is non-null. |
| 307 | 344 | pub const EmitH = struct { |
| 308 | fwd_decl: std.ArrayListUnmanaged(u8) = .{}, | |
| 345 | fwd_decl: ArrayListUnmanaged(u8) = .{}, | |
| 309 | 346 | }; |
| 310 | 347 | |
| 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. | |
| 349 | pub 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. | |
| 312 | 360 | /// Extern functions do not have this data structure; they are represented by |
| 313 | 361 | /// the `Decl` only, with a `Value` tag of `extern_fn`. |
| 314 | 362 | pub const Fn = struct { |
| ... | ... | @@ -316,9 +364,15 @@ pub const Fn = struct { |
| 316 | 364 | /// Contains un-analyzed ZIR instructions generated from Zig source AST. |
| 317 | 365 | /// Even after we finish analysis, the ZIR is kept in memory, so that |
| 318 | 366 | /// 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, | |
| 320 | 374 | /// undefined unless analysis state is `success`. |
| 321 | body: Body, | |
| 375 | body: ir.Body, | |
| 322 | 376 | state: Analysis, |
| 323 | 377 | |
| 324 | 378 | pub const Analysis = enum { |
| ... | ... | @@ -336,8 +390,12 @@ pub const Fn = struct { |
| 336 | 390 | }; |
| 337 | 391 | |
| 338 | 392 | /// 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); | |
| 341 | 399 | } |
| 342 | 400 | }; |
| 343 | 401 | |
| ... | ... | @@ -364,103 +422,93 @@ pub const Scope = struct { |
| 364 | 422 | } |
| 365 | 423 | |
| 366 | 424 | /// 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, | |
| 375 | 431 | .file => unreachable, |
| 376 | 432 | .container => unreachable, |
| 433 | .decl_ref => unreachable, | |
| 377 | 434 | } |
| 378 | 435 | } |
| 379 | 436 | |
| 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, | |
| 392 | 443 | .file => null, |
| 393 | 444 | .container => null, |
| 445 | .decl_ref => scope.cast(DeclRef).?.decl, | |
| 394 | 446 | }; |
| 395 | 447 | } |
| 396 | 448 | |
| 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, | |
| 405 | 455 | .file => null, |
| 406 | 456 | .container => null, |
| 457 | .decl_ref => scope.cast(DeclRef).?.decl, | |
| 407 | 458 | }; |
| 408 | 459 | } |
| 409 | 460 | |
| 410 | 461 | /// 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, | |
| 421 | 471 | } |
| 422 | 472 | } |
| 423 | 473 | |
| 424 | 474 | /// Must generate unique bytes with no collisions with other decls. |
| 425 | 475 | /// The point of hashing here is only to limit the number of bytes of |
| 426 | 476 | /// 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) { | |
| 429 | 479 | .block => unreachable, |
| 430 | 480 | .gen_zir => unreachable, |
| 431 | 481 | .local_val => unreachable, |
| 432 | 482 | .local_ptr => unreachable, |
| 433 | .gen_suspend => unreachable, | |
| 434 | .gen_nosuspend => unreachable, | |
| 435 | 483 | .file => unreachable, |
| 436 | .container => return self.cast(Container).?.fullyQualifiedNameHash(name), | |
| 484 | .container => return scope.cast(Container).?.fullyQualifiedNameHash(name), | |
| 485 | .decl_ref => unreachable, | |
| 437 | 486 | } |
| 438 | 487 | } |
| 439 | 488 | |
| 440 | 489 | /// 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, | |
| 451 | 499 | } |
| 452 | 500 | } |
| 453 | 501 | |
| 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) { | |
| 457 | 505 | .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, | |
| 462 | 509 | .file => unreachable, |
| 463 | 510 | .container => unreachable, |
| 511 | .decl_ref => unreachable, | |
| 464 | 512 | }; |
| 465 | 513 | } |
| 466 | 514 | |
| ... | ... | @@ -474,8 +522,7 @@ pub const Scope = struct { |
| 474 | 522 | .gen_zir => unreachable, |
| 475 | 523 | .local_val => unreachable, |
| 476 | 524 | .local_ptr => unreachable, |
| 477 | .gen_suspend => unreachable, | |
| 478 | .gen_nosuspend => unreachable, | |
| 525 | .decl_ref => unreachable, | |
| 479 | 526 | } |
| 480 | 527 | } |
| 481 | 528 | |
| ... | ... | @@ -487,8 +534,7 @@ pub const Scope = struct { |
| 487 | 534 | .local_val => unreachable, |
| 488 | 535 | .local_ptr => unreachable, |
| 489 | 536 | .block => unreachable, |
| 490 | .gen_suspend => unreachable, | |
| 491 | .gen_nosuspend => unreachable, | |
| 537 | .decl_ref => unreachable, | |
| 492 | 538 | } |
| 493 | 539 | } |
| 494 | 540 | |
| ... | ... | @@ -499,40 +545,11 @@ pub const Scope = struct { |
| 499 | 545 | cur = switch (cur.tag) { |
| 500 | 546 | .container => return @fieldParentPtr(Container, "base", cur).file_scope, |
| 501 | 547 | .file => return @fieldParentPtr(File, "base", cur), |
| 502 | .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent, | |
| 548 | .gen_zir => @fieldParentPtr(GenZir, "base", cur).parent, | |
| 503 | 549 | .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, |
| 504 | 550 | .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, |
| 505 | 551 | .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, | |
| 536 | 553 | }; |
| 537 | 554 | } |
| 538 | 555 | } |
| ... | ... | @@ -554,8 +571,10 @@ pub const Scope = struct { |
| 554 | 571 | gen_zir, |
| 555 | 572 | local_val, |
| 556 | 573 | 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, | |
| 559 | 578 | }; |
| 560 | 579 | |
| 561 | 580 | pub const Container = struct { |
| ... | ... | @@ -568,19 +587,19 @@ pub const Scope = struct { |
| 568 | 587 | decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, |
| 569 | 588 | ty: Type, |
| 570 | 589 | |
| 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); | |
| 573 | 592 | // 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; | |
| 577 | 596 | } |
| 578 | 597 | |
| 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); | |
| 581 | 600 | } |
| 582 | 601 | |
| 583 | pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash { | |
| 602 | pub fn fullyQualifiedNameHash(cont: *Container, name: []const u8) NameHash { | |
| 584 | 603 | // TODO container scope qualified names. |
| 585 | 604 | return std.zig.hashSrc(name); |
| 586 | 605 | } |
| ... | ... | @@ -610,55 +629,55 @@ pub const Scope = struct { |
| 610 | 629 | |
| 611 | 630 | root_container: Container, |
| 612 | 631 | |
| 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) { | |
| 615 | 634 | .never_loaded, |
| 616 | 635 | .unloaded_parse_failure, |
| 617 | 636 | .unloaded_success, |
| 618 | 637 | => {}, |
| 619 | 638 | |
| 620 | 639 | .loaded_success => { |
| 621 | self.tree.deinit(gpa); | |
| 622 | self.status = .unloaded_success; | |
| 640 | file.tree.deinit(gpa); | |
| 641 | file.status = .unloaded_success; | |
| 623 | 642 | }, |
| 624 | 643 | } |
| 625 | switch (self.source) { | |
| 644 | switch (file.source) { | |
| 626 | 645 | .bytes => |bytes| { |
| 627 | 646 | gpa.free(bytes); |
| 628 | self.source = .{ .unloaded = {} }; | |
| 647 | file.source = .{ .unloaded = {} }; | |
| 629 | 648 | }, |
| 630 | 649 | .unloaded => {}, |
| 631 | 650 | } |
| 632 | 651 | } |
| 633 | 652 | |
| 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; | |
| 638 | 657 | } |
| 639 | 658 | |
| 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); | |
| 643 | 662 | } |
| 644 | 663 | |
| 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 }); | |
| 648 | 667 | } |
| 649 | 668 | |
| 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) { | |
| 652 | 671 | .unloaded => { |
| 653 | const source = try self.pkg.root_src_directory.handle.readFileAllocOptions( | |
| 672 | const source = try file.pkg.root_src_directory.handle.readFileAllocOptions( | |
| 654 | 673 | module.gpa, |
| 655 | self.sub_file_path, | |
| 674 | file.sub_file_path, | |
| 656 | 675 | std.math.maxInt(u32), |
| 657 | 676 | null, |
| 658 | 677 | 1, |
| 659 | 678 | 0, |
| 660 | 679 | ); |
| 661 | self.source = .{ .bytes = source }; | |
| 680 | file.source = .{ .bytes = source }; | |
| 662 | 681 | return source; |
| 663 | 682 | }, |
| 664 | 683 | .bytes => |bytes| return bytes, |
| ... | ... | @@ -666,37 +685,30 @@ pub const Scope = struct { |
| 666 | 685 | } |
| 667 | 686 | }; |
| 668 | 687 | |
| 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 | |
| 670 | 691 | /// during semantic analysis of the block. |
| 671 | 692 | pub const Block = struct { |
| 672 | 693 | pub const base_tag: Tag = .block; |
| 673 | 694 | |
| 674 | 695 | base: Scope = Scope{ .tag = base_tag }, |
| 675 | 696 | 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, | |
| 683 | 699 | /// 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. | |
| 684 | 702 | src_decl: *Decl, |
| 685 | instructions: ArrayListUnmanaged(*Inst), | |
| 686 | /// Points to the arena allocator of the Decl. | |
| 687 | arena: *Allocator, | |
| 703 | instructions: ArrayListUnmanaged(*ir.Inst), | |
| 688 | 704 | label: ?Label = null, |
| 689 | 705 | inlining: ?*Inlining, |
| 690 | 706 | is_comptime: bool, |
| 691 | /// Shared to sub-blocks. | |
| 692 | branch_quota: *u32, | |
| 693 | ||
| 694 | pub const InstTable = std.AutoHashMap(*zir.Inst, *Inst); | |
| 695 | 707 | |
| 696 | 708 | /// This `Block` maps a block ZIR instruction to the corresponding |
| 697 | 709 | /// TZIR instruction for break instruction analysis. |
| 698 | 710 | pub const Label = struct { |
| 699 | zir_block: *zir.Inst.Block, | |
| 711 | zir_block: zir.Inst.Index, | |
| 700 | 712 | merges: Merges, |
| 701 | 713 | }; |
| 702 | 714 | |
| ... | ... | @@ -706,73 +718,232 @@ pub const Scope = struct { |
| 706 | 718 | /// It is shared among all the blocks in an inline or comptime called |
| 707 | 719 | /// function. |
| 708 | 720 | 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, | |
| 716 | 721 | merges: Merges, |
| 717 | ||
| 718 | pub const Shared = struct { | |
| 719 | caller: ?*Fn, | |
| 720 | branch_count: u32, | |
| 721 | }; | |
| 722 | 722 | }; |
| 723 | 723 | |
| 724 | 724 | pub const Merges = struct { |
| 725 | block_inst: *Inst.Block, | |
| 725 | block_inst: *ir.Inst.Block, | |
| 726 | 726 | /// Separate array list from break_inst_list so that it can be passed directly |
| 727 | 727 | /// to resolvePeerTypes. |
| 728 | results: ArrayListUnmanaged(*Inst), | |
| 728 | results: ArrayListUnmanaged(*ir.Inst), | |
| 729 | 729 | /// Keeps track of the break instructions so that the operand can be replaced |
| 730 | 730 | /// if we need to add type coercion at the end of block analysis. |
| 731 | 731 | /// Same indexes, capacity, length as `results`. |
| 732 | br_list: ArrayListUnmanaged(*Inst.Br), | |
| 732 | br_list: ArrayListUnmanaged(*ir.Inst.Br), | |
| 733 | 733 | }; |
| 734 | 734 | |
| 735 | 735 | /// 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); | |
| 738 | 738 | } |
| 739 | 739 | |
| 740 | 740 | pub fn makeSubBlock(parent: *Block) Block { |
| 741 | 741 | return .{ |
| 742 | 742 | .parent = parent, |
| 743 | .inst_table = parent.inst_table, | |
| 744 | .func = parent.func, | |
| 745 | .owner_decl = parent.owner_decl, | |
| 743 | .sema = parent.sema, | |
| 746 | 744 | .src_decl = parent.src_decl, |
| 747 | 745 | .instructions = .{}, |
| 748 | .arena = parent.arena, | |
| 749 | 746 | .label = null, |
| 750 | 747 | .inlining = parent.inlining, |
| 751 | 748 | .is_comptime = parent.is_comptime, |
| 752 | .branch_quota = parent.branch_quota, | |
| 753 | 749 | }; |
| 754 | 750 | } |
| 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 | } | |
| 755 | 923 | }; |
| 756 | 924 | |
| 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 { | |
| 760 | 928 | pub const base_tag: Tag = .gen_zir; |
| 761 | 929 | base: Scope = Scope{ .tag = base_tag }, |
| 762 | /// Parents can be: `GenZIR`, `File` | |
| 763 | parent: *Scope, | |
| 764 | decl: *Decl, | |
| 765 | arena: *Allocator, | |
| 766 | 930 | 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) = .{}, | |
| 769 | 938 | 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, | |
| 774 | 943 | /// 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, | |
| 776 | 947 | /// Keeps track of how many branches of a block did not actually |
| 777 | 948 | /// consume the result location. astgen uses this to figure out |
| 778 | 949 | /// whether to rely on break instructions or writing to the result |
| ... | ... | @@ -784,19 +955,456 @@ pub const Scope = struct { |
| 784 | 955 | break_count: usize = 0, |
| 785 | 956 | /// Tracks `break :foo bar` instructions so they can possibly be elided later if |
| 786 | 957 | /// 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) = .{}, | |
| 788 | 959 | /// Tracks `store_to_block_ptr` instructions that correspond to break instructions |
| 789 | 960 | /// so they can possibly be elided later if the labeled block ends up not needing |
| 790 | 961 | /// 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) = .{}, | |
| 794 | 963 | |
| 795 | 964 | pub const Label = struct { |
| 796 | 965 | token: ast.TokenIndex, |
| 797 | block_inst: *zir.Inst.Block, | |
| 966 | block_inst: zir.Inst.Index, | |
| 798 | 967 | used: bool = false, |
| 799 | 968 | }; |
| 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 | } | |
| 800 | 1408 | }; |
| 801 | 1409 | |
| 802 | 1410 | /// 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 { |
| 805 | 1413 | pub const LocalVal = struct { |
| 806 | 1414 | pub const base_tag: Tag = .local_val; |
| 807 | 1415 | base: Scope = Scope{ .tag = base_tag }, |
| 808 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | |
| 1416 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`. | |
| 809 | 1417 | parent: *Scope, |
| 810 | gen_zir: *GenZIR, | |
| 1418 | gen_zir: *GenZir, | |
| 811 | 1419 | name: []const u8, |
| 812 | inst: *zir.Inst, | |
| 1420 | inst: zir.Inst.Ref, | |
| 1421 | /// Source location of the corresponding variable declaration. | |
| 1422 | src: LazySrcLoc, | |
| 813 | 1423 | }; |
| 814 | 1424 | |
| 815 | 1425 | /// This could be a `const` or `var` local. It has a pointer instead of a value. |
| ... | ... | @@ -818,21 +1428,19 @@ pub const Scope = struct { |
| 818 | 1428 | pub const LocalPtr = struct { |
| 819 | 1429 | pub const base_tag: Tag = .local_ptr; |
| 820 | 1430 | base: Scope = Scope{ .tag = base_tag }, |
| 821 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | |
| 1431 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`. | |
| 822 | 1432 | parent: *Scope, |
| 823 | gen_zir: *GenZIR, | |
| 1433 | gen_zir: *GenZir, | |
| 824 | 1434 | name: []const u8, |
| 825 | ptr: *zir.Inst, | |
| 1435 | ptr: zir.Inst.Ref, | |
| 1436 | /// Source location of the corresponding variable declaration. | |
| 1437 | src: LazySrcLoc, | |
| 826 | 1438 | }; |
| 827 | 1439 | |
| 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; | |
| 831 | 1442 | 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, | |
| 836 | 1444 | }; |
| 837 | 1445 | }; |
| 838 | 1446 | |
| ... | ... | @@ -855,17 +1463,17 @@ pub const ErrorMsg = struct { |
| 855 | 1463 | comptime format: []const u8, |
| 856 | 1464 | args: anytype, |
| 857 | 1465 | ) !*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; | |
| 862 | 1470 | } |
| 863 | 1471 | |
| 864 | 1472 | /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`, |
| 865 | 1473 | /// 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); | |
| 869 | 1477 | } |
| 870 | 1478 | |
| 871 | 1479 | pub fn init( |
| ... | ... | @@ -880,84 +1488,715 @@ pub const ErrorMsg = struct { |
| 880 | 1488 | }; |
| 881 | 1489 | } |
| 882 | 1490 | |
| 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| { | |
| 885 | 1493 | note.deinit(gpa); |
| 886 | 1494 | } |
| 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; | |
| 890 | 1498 | } |
| 891 | 1499 | }; |
| 892 | 1500 | |
| 893 | 1501 | /// Canonical reference to a position within a source file. |
| 894 | 1502 | pub const SrcLoc = struct { |
| 895 | file_scope: *Scope.File, | |
| 896 | byte_offset: usize, | |
| 897 | }; | |
| 898 | ||
| 899 | pub 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 | } | |
| 900 | 1547 | |
| 901 | pub 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, | |
| 903 | 1551 | |
| 904 | self.compile_log_text.deinit(gpa); | |
| 1552 | .byte_abs => |byte_index| return byte_index, | |
| 905 | 1553 | |
| 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), | |
| 907 | 1685 | |
| 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 | }, | |
| 909 | 1745 | |
| 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 | }, | |
| 914 | 1788 | |
| 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 | }, | |
| 919 | 1800 | |
| 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 | }, | |
| 924 | 1827 | |
| 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. | |
| 1910 | pub 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 | ||
| 2136 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; | |
| 2137 | ||
| 2138 | pub 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); | |
| 927 | 2159 | } |
| 928 | self.failed_files.deinit(gpa); | |
| 2160 | mod.emit_h_failed_decls.deinit(gpa); | |
| 929 | 2161 | |
| 930 | for (self.failed_exports.items()) |entry| { | |
| 2162 | for (mod.failed_files.items()) |entry| { | |
| 931 | 2163 | entry.value.destroy(gpa); |
| 932 | 2164 | } |
| 933 | self.failed_exports.deinit(gpa); | |
| 2165 | mod.failed_files.deinit(gpa); | |
| 934 | 2166 | |
| 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); | |
| 936 | 2171 | |
| 937 | for (self.decl_exports.items()) |entry| { | |
| 2172 | mod.compile_log_decls.deinit(gpa); | |
| 2173 | ||
| 2174 | for (mod.decl_exports.items()) |entry| { | |
| 938 | 2175 | const export_list = entry.value; |
| 939 | 2176 | gpa.free(export_list); |
| 940 | 2177 | } |
| 941 | self.decl_exports.deinit(gpa); | |
| 2178 | mod.decl_exports.deinit(gpa); | |
| 942 | 2179 | |
| 943 | for (self.export_owners.items()) |entry| { | |
| 2180 | for (mod.export_owners.items()) |entry| { | |
| 944 | 2181 | freeExportList(gpa, entry.value); |
| 945 | 2182 | } |
| 946 | self.export_owners.deinit(gpa); | |
| 2183 | mod.export_owners.deinit(gpa); | |
| 947 | 2184 | |
| 948 | self.symbol_exports.deinit(gpa); | |
| 949 | self.root_scope.destroy(gpa); | |
| 2185 | mod.symbol_exports.deinit(gpa); | |
| 2186 | mod.root_scope.destroy(gpa); | |
| 950 | 2187 | |
| 951 | var it = self.global_error_set.iterator(); | |
| 2188 | var it = mod.global_error_set.iterator(); | |
| 952 | 2189 | while (it.next()) |entry| { |
| 953 | 2190 | gpa.free(entry.key); |
| 954 | 2191 | } |
| 955 | self.global_error_set.deinit(gpa); | |
| 2192 | mod.global_error_set.deinit(gpa); | |
| 2193 | ||
| 2194 | mod.error_name_list.deinit(gpa); | |
| 956 | 2195 | |
| 957 | for (self.import_table.items()) |entry| { | |
| 2196 | for (mod.import_table.items()) |entry| { | |
| 958 | 2197 | entry.value.destroy(gpa); |
| 959 | 2198 | } |
| 960 | self.import_table.deinit(gpa); | |
| 2199 | mod.import_table.deinit(gpa); | |
| 961 | 2200 | } |
| 962 | 2201 | |
| 963 | 2202 | fn freeExportList(gpa: *Allocator, export_list: []*Export) void { |
| ... | ... | @@ -1102,42 +2341,51 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { |
| 1102 | 2341 | // A comptime decl does not store any value so we can just deinit this arena after analysis is done. |
| 1103 | 2342 | var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1104 | 2343 | 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); | |
| 1112 | 2344 | |
| 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); | |
| 1118 | 2355 | |
| 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); | |
| 1121 | 2358 | |
| 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); | |
| 1123 | 2366 | |
| 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 | }; | |
| 1124 | 2378 | var block_scope: Scope.Block = .{ |
| 1125 | 2379 | .parent = null, |
| 1126 | .inst_table = &inst_table, | |
| 1127 | .func = null, | |
| 1128 | .owner_decl = decl, | |
| 2380 | .sema = &sema, | |
| 1129 | 2381 | .src_decl = decl, |
| 1130 | 2382 | .instructions = .{}, |
| 1131 | .arena = &analysis_arena.allocator, | |
| 1132 | 2383 | .inlining = null, |
| 1133 | 2384 | .is_comptime = true, |
| 1134 | .branch_quota = &branch_quota, | |
| 1135 | 2385 | }; |
| 1136 | 2386 | defer block_scope.instructions.deinit(mod.gpa); |
| 1137 | 2387 | |
| 1138 | _ = try zir_sema.analyzeBody(mod, &block_scope, .{ | |
| 1139 | .instructions = gen_scope.instructions.items, | |
| 1140 | }); | |
| 2388 | _ = try sema.root(&block_scope); | |
| 1141 | 2389 | |
| 1142 | 2390 | decl.analysis = .complete; |
| 1143 | 2391 | decl.generation = mod.generation; |
| ... | ... | @@ -1160,7 +2408,6 @@ fn astgenAndSemaFn( |
| 1160 | 2408 | |
| 1161 | 2409 | decl.analysis = .in_progress; |
| 1162 | 2410 | |
| 1163 | const token_starts = tree.tokens.items(.start); | |
| 1164 | 2411 | const token_tags = tree.tokens.items(.tag); |
| 1165 | 2412 | |
| 1166 | 2413 | // This arena allocator's memory is discarded at the end of this function. It is used |
| ... | ... | @@ -1168,11 +2415,14 @@ fn astgenAndSemaFn( |
| 1168 | 2415 | // to complete the Decl analysis. |
| 1169 | 2416 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1170 | 2417 | 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 = .{ | |
| 1175 | 2423 | .force_comptime = true, |
| 2424 | .parent = &decl.container.base, | |
| 2425 | .astgen = &fn_type_astgen, | |
| 1176 | 2426 | }; |
| 1177 | 2427 | defer fn_type_scope.instructions.deinit(mod.gpa); |
| 1178 | 2428 | |
| ... | ... | @@ -1189,13 +2439,7 @@ fn astgenAndSemaFn( |
| 1189 | 2439 | } |
| 1190 | 2440 | break :blk count; |
| 1191 | 2441 | }; |
| 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); | |
| 1199 | 2443 | |
| 1200 | 2444 | var is_var_args = false; |
| 1201 | 2445 | { |
| ... | ... | @@ -1220,7 +2464,7 @@ fn astgenAndSemaFn( |
| 1220 | 2464 | const param_type_node = param.type_expr; |
| 1221 | 2465 | assert(param_type_node != 0); |
| 1222 | 2466 | 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); | |
| 1224 | 2468 | } |
| 1225 | 2469 | assert(param_type_i == param_count); |
| 1226 | 2470 | } |
| ... | ... | @@ -1289,10 +2533,10 @@ fn astgenAndSemaFn( |
| 1289 | 2533 | if (token_tags[maybe_bang] == .bang) { |
| 1290 | 2534 | return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); |
| 1291 | 2535 | } |
| 1292 | const return_type_inst = try astgen.expr( | |
| 1293 | mod, | |
| 2536 | const return_type_inst = try AstGen.expr( | |
| 2537 | &fn_type_scope, | |
| 1294 | 2538 | &fn_type_scope.base, |
| 1295 | type_type_rl, | |
| 2539 | .{ .ty = .type_type }, | |
| 1296 | 2540 | fn_proto.ast.return_type, |
| 1297 | 2541 | ); |
| 1298 | 2542 | |
| ... | ... | @@ -1301,73 +2545,72 @@ fn astgenAndSemaFn( |
| 1301 | 2545 | else |
| 1302 | 2546 | false; |
| 1303 | 2547 | |
| 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) | |
| 1305 | 2549 | // TODO instead of enum literal type, this needs to be the |
| 1306 | 2550 | // std.builtin.CallingConvention enum. We need to implement importing other files |
| 1307 | 2551 | // 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, | |
| 1325 | 2568 | .param_types = param_types, |
| 1326 | 2569 | .cc = cc, |
| 1327 | 2570 | }); |
| 1328 | if (is_var_args) fn_type.tag = .fn_type_cc_var_args; | |
| 1329 | break :fn_type fn_type; | |
| 1330 | 2571 | } 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, | |
| 1333 | 2576 | .param_types = param_types, |
| 1334 | 2577 | }); |
| 1335 | if (is_var_args) fn_type.tag = .fn_type_var_args; | |
| 1336 | break :fn_type fn_type; | |
| 1337 | 2578 | }; |
| 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); | |
| 1342 | 2580 | |
| 1343 | 2581 | // We need the memory for the Type to go into the arena for the Decl |
| 1344 | 2582 | var decl_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1345 | 2583 | errdefer decl_arena.deinit(); |
| 1346 | 2584 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1347 | 2585 | |
| 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 | } | |
| 1352 | 2591 | |
| 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 | }; | |
| 1353 | 2603 | var block_scope: Scope.Block = .{ |
| 1354 | 2604 | .parent = null, |
| 1355 | .inst_table = &inst_table, | |
| 1356 | .func = null, | |
| 1357 | .owner_decl = decl, | |
| 2605 | .sema = &fn_type_sema, | |
| 1358 | 2606 | .src_decl = decl, |
| 1359 | 2607 | .instructions = .{}, |
| 1360 | .arena = &decl_arena.allocator, | |
| 1361 | 2608 | .inlining = null, |
| 1362 | .is_comptime = false, | |
| 1363 | .branch_quota = &branch_quota, | |
| 2609 | .is_comptime = true, | |
| 1364 | 2610 | }; |
| 1365 | 2611 | defer block_scope.instructions.deinit(mod.gpa); |
| 1366 | 2612 | |
| 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); | |
| 1371 | 2614 | if (body_node == 0) { |
| 1372 | 2615 | if (!is_extern) { |
| 1373 | 2616 | return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function has no body", .{}); |
| ... | ... | @@ -1409,63 +2652,69 @@ fn astgenAndSemaFn( |
| 1409 | 2652 | const new_func = try decl_arena.allocator.create(Fn); |
| 1410 | 2653 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); |
| 1411 | 2654 | |
| 1412 | const fn_zir: zir.Body = blk: { | |
| 2655 | const fn_zir: zir.Code = blk: { | |
| 1413 | 2656 | // 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 = .{ | |
| 1418 | 2662 | .force_comptime = false, |
| 2663 | .parent = &decl.container.base, | |
| 2664 | .astgen = &astgen, | |
| 1419 | 2665 | }; |
| 1420 | 2666 | defer gen_scope.instructions.deinit(mod.gpa); |
| 1421 | 2667 | |
| 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 | ||
| 1424 | 2673 | var params_scope = &gen_scope.base; |
| 1425 | 2674 | var i: usize = 0; |
| 1426 | 2675 | var it = fn_proto.iterate(tree); |
| 1427 | 2676 | while (it.next()) |param| : (i += 1) { |
| 1428 | 2677 | const name_token = param.name_token.?; |
| 1429 | const src = token_starts[name_token]; | |
| 1430 | 2678 | 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; | |
| 1443 | 2679 | const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); |
| 1444 | 2680 | sub_scope.* = .{ |
| 1445 | 2681 | .parent = params_scope, |
| 1446 | 2682 | .gen_zir = &gen_scope, |
| 1447 | 2683 | .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), | |
| 1449 | 2687 | }; |
| 1450 | 2688 | 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); | |
| 1451 | 2698 | } |
| 1452 | 2699 | |
| 1453 | _ = try astgen.expr(mod, params_scope, .none, body_node); | |
| 2700 | _ = try AstGen.expr(&gen_scope, params_scope, .none, body_node); | |
| 1454 | 2701 | |
| 1455 | 2702 | 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()) | |
| 1457 | 2705 | { |
| 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)); | |
| 1460 | 2710 | } |
| 1461 | 2711 | |
| 2712 | const code = try gen_scope.finish(); | |
| 1462 | 2713 | 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 {}; | |
| 1464 | 2715 | } |
| 1465 | 2716 | |
| 1466 | break :blk .{ | |
| 1467 | .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), | |
| 1468 | }; | |
| 2717 | break :blk code; | |
| 1469 | 2718 | }; |
| 1470 | 2719 | |
| 1471 | 2720 | const is_inline = fn_type.fnCallingConvention() == .Inline; |
| ... | ... | @@ -1492,6 +2741,7 @@ fn astgenAndSemaFn( |
| 1492 | 2741 | if (tvm.typed_value.val.castTag(.function)) |payload| { |
| 1493 | 2742 | const prev_func = payload.data; |
| 1494 | 2743 | prev_is_inline = prev_func.state == .inline_only; |
| 2744 | prev_func.deinit(mod.gpa); | |
| 1495 | 2745 | } |
| 1496 | 2746 | |
| 1497 | 2747 | tvm.deinit(mod.gpa); |
| ... | ... | @@ -1533,7 +2783,7 @@ fn astgenAndSemaFn( |
| 1533 | 2783 | .{}, |
| 1534 | 2784 | ); |
| 1535 | 2785 | } |
| 1536 | const export_src = token_starts[maybe_export_token]; | |
| 2786 | const export_src = decl.tokSrcLoc(maybe_export_token); | |
| 1537 | 2787 | const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString |
| 1538 | 2788 | // The scope needs to have the decl in it. |
| 1539 | 2789 | try mod.analyzeExport(&block_scope.base, export_src, name, decl); |
| ... | ... | @@ -1552,8 +2802,8 @@ fn astgenAndSemaVarDecl( |
| 1552 | 2802 | defer tracy.end(); |
| 1553 | 2803 | |
| 1554 | 2804 | decl.analysis = .in_progress; |
| 2805 | decl.is_pub = var_decl.visib_token != null; | |
| 1555 | 2806 | |
| 1556 | const token_starts = tree.tokens.items(.start); | |
| 1557 | 2807 | const token_tags = tree.tokens.items(.tag); |
| 1558 | 2808 | |
| 1559 | 2809 | // We need the memory for the Type to go into the arena for the Decl |
| ... | ... | @@ -1561,54 +2811,29 @@ fn astgenAndSemaVarDecl( |
| 1561 | 2811 | errdefer decl_arena.deinit(); |
| 1562 | 2812 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1563 | 2813 | |
| 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 }; | |
| 1582 | 2816 | |
| 1583 | decl.is_pub = var_decl.visib_token != null; | |
| 1584 | 2817 | const is_extern = blk: { |
| 1585 | 2818 | 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; | |
| 1596 | 2820 | }; |
| 2821 | ||
| 1597 | 2822 | if (var_decl.lib_name) |lib_name| { |
| 1598 | 2823 | 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", .{}); | |
| 1600 | 2825 | } |
| 1601 | 2826 | const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var; |
| 1602 | 2827 | const is_threadlocal = if (var_decl.threadlocal_token) |some| blk: { |
| 1603 | 2828 | 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", .{}); | |
| 1605 | 2830 | } |
| 1606 | 2831 | break :blk true; |
| 1607 | 2832 | } else false; |
| 1608 | 2833 | assert(var_decl.comptime_token == null); |
| 1609 | 2834 | if (var_decl.ast.align_node != 0) { |
| 1610 | 2835 | return mod.failNode( |
| 1611 | &block_scope.base, | |
| 2836 | &decl_scope.base, | |
| 1612 | 2837 | var_decl.ast.align_node, |
| 1613 | 2838 | "TODO implement function align expression", |
| 1614 | 2839 | .{}, |
| ... | ... | @@ -1616,7 +2841,7 @@ fn astgenAndSemaVarDecl( |
| 1616 | 2841 | } |
| 1617 | 2842 | if (var_decl.ast.section_node != 0) { |
| 1618 | 2843 | return mod.failNode( |
| 1619 | &block_scope.base, | |
| 2844 | &decl_scope.base, | |
| 1620 | 2845 | var_decl.ast.section_node, |
| 1621 | 2846 | "TODO implement function section expression", |
| 1622 | 2847 | .{}, |
| ... | ... | @@ -1624,103 +2849,136 @@ fn astgenAndSemaVarDecl( |
| 1624 | 2849 | } |
| 1625 | 2850 | |
| 1626 | 2851 | 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 | ||
| 1627 | 2861 | var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1628 | 2862 | 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 = .{ | |
| 1633 | 2868 | .force_comptime = true, |
| 2869 | .parent = &decl.container.base, | |
| 2870 | .astgen = &astgen, | |
| 1634 | 2871 | }; |
| 1635 | 2872 | defer gen_scope.instructions.deinit(mod.gpa); |
| 1636 | 2873 | |
| 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), | |
| 1646 | 2876 | } else .none; |
| 1647 | 2877 | |
| 1648 | const init_inst = try astgen.comptimeExpr( | |
| 1649 | mod, | |
| 2878 | const init_inst = try AstGen.comptimeExpr( | |
| 2879 | &gen_scope, | |
| 1650 | 2880 | &gen_scope.base, |
| 1651 | 2881 | init_result_loc, |
| 1652 | 2882 | var_decl.ast.init_node, |
| 1653 | 2883 | ); |
| 2884 | _ = try gen_scope.addBreak(.break_inline, 0, init_inst); | |
| 2885 | var code = try gen_scope.finish(); | |
| 2886 | defer code.deinit(mod.gpa); | |
| 1654 | 2887 | 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 {}; | |
| 1656 | 2889 | } |
| 1657 | 2890 | |
| 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), | |
| 1666 | 2897 | .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, | |
| 1667 | 2905 | .src_decl = decl, |
| 1668 | 2906 | .instructions = .{}, |
| 1669 | .arena = &gen_scope_arena.allocator, | |
| 1670 | 2907 | .inlining = null, |
| 1671 | 2908 | .is_comptime = true, |
| 1672 | .branch_quota = &branch_quota_vi, | |
| 1673 | 2909 | }; |
| 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); | |
| 1678 | 2911 | |
| 2912 | const init_inst_zir_ref = try sema.rootAsRef(&block_scope); | |
| 1679 | 2913 | // 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); | |
| 1681 | 2915 | // The is_comptime in the Scope.Block guarantees the result is comptime-known. |
| 1682 | 2916 | const val = analyzed_init_inst.value().?; |
| 1683 | 2917 | |
| 1684 | const ty = try analyzed_init_inst.ty.copy(block_scope.arena); | |
| 1685 | 2918 | 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), | |
| 1688 | 2921 | }; |
| 1689 | 2922 | } else if (!is_extern) { |
| 1690 | 2923 | return mod.failTok( |
| 1691 | &block_scope.base, | |
| 2924 | &decl_scope.base, | |
| 1692 | 2925 | var_decl.ast.mut_token, |
| 1693 | 2926 | "variables must be initialized", |
| 1694 | 2927 | .{}, |
| 1695 | 2928 | ); |
| 1696 | 2929 | } 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. | |
| 1699 | 2930 | var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1700 | 2931 | 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 = .{ | |
| 1705 | 2937 | .force_comptime = true, |
| 2938 | .parent = &decl.container.base, | |
| 2939 | .astgen = &astgen, | |
| 1706 | 2940 | }; |
| 1707 | 2941 | defer type_scope.instructions.deinit(mod.gpa); |
| 1708 | 2942 | |
| 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); | |
| 1710 | 2948 | 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 {}; | |
| 1712 | 2950 | } |
| 1713 | 2951 | |
| 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 | ||
| 1717 | 2975 | break :vi .{ |
| 1718 | .ty = ty, | |
| 2976 | .ty = try ty.copy(&decl_arena.allocator), | |
| 1719 | 2977 | .val = null, |
| 1720 | 2978 | }; |
| 1721 | 2979 | } else { |
| 1722 | 2980 | return mod.failTok( |
| 1723 | &block_scope.base, | |
| 2981 | &decl_scope.base, | |
| 1724 | 2982 | var_decl.ast.mut_token, |
| 1725 | 2983 | "unable to infer variable type", |
| 1726 | 2984 | .{}, |
| ... | ... | @@ -1729,7 +2987,7 @@ fn astgenAndSemaVarDecl( |
| 1729 | 2987 | |
| 1730 | 2988 | if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { |
| 1731 | 2989 | return mod.failTok( |
| 1732 | &block_scope.base, | |
| 2990 | &decl_scope.base, | |
| 1733 | 2991 | var_decl.ast.mut_token, |
| 1734 | 2992 | "variable of type '{}' must be const", |
| 1735 | 2993 | .{var_info.ty}, |
| ... | ... | @@ -1768,57 +3026,57 @@ fn astgenAndSemaVarDecl( |
| 1768 | 3026 | |
| 1769 | 3027 | if (var_decl.extern_export_token) |maybe_export_token| { |
| 1770 | 3028 | 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); | |
| 1772 | 3030 | const name_token = var_decl.ast.mut_token + 1; |
| 1773 | 3031 | const name = tree.tokenSlice(name_token); // TODO identifierTokenString |
| 1774 | 3032 | // 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); | |
| 1776 | 3034 | } |
| 1777 | 3035 | } |
| 1778 | 3036 | return type_changed; |
| 1779 | 3037 | } |
| 1780 | 3038 | |
| 1781 | fn 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); | |
| 3039 | pub 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); | |
| 1784 | 3042 | |
| 1785 | 3043 | depender.dependencies.putAssumeCapacity(dependee, {}); |
| 1786 | 3044 | dependee.dependants.putAssumeCapacity(depender, {}); |
| 1787 | 3045 | } |
| 1788 | 3046 | |
| 1789 | pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree { | |
| 3047 | pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree { | |
| 1790 | 3048 | const tracy = trace(@src()); |
| 1791 | 3049 | defer tracy.end(); |
| 1792 | 3050 | |
| 1793 | 3051 | switch (root_scope.status) { |
| 1794 | 3052 | .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); | |
| 1796 | 3054 | |
| 1797 | const source = try root_scope.getSource(self); | |
| 3055 | const source = try root_scope.getSource(mod); | |
| 1798 | 3056 | |
| 1799 | 3057 | 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); | |
| 1802 | 3060 | |
| 1803 | 3061 | const tree = &root_scope.tree; |
| 1804 | 3062 | |
| 1805 | 3063 | if (tree.errors.len != 0) { |
| 1806 | 3064 | const parse_err = tree.errors[0]; |
| 1807 | 3065 | |
| 1808 | var msg = std.ArrayList(u8).init(self.gpa); | |
| 3066 | var msg = std.ArrayList(u8).init(mod.gpa); | |
| 1809 | 3067 | defer msg.deinit(); |
| 1810 | 3068 | |
| 1811 | 3069 | 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); | |
| 1813 | 3071 | err_msg.* = .{ |
| 1814 | 3072 | .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 }, | |
| 1817 | 3075 | }, |
| 1818 | 3076 | .msg = msg.toOwnedSlice(), |
| 1819 | 3077 | }; |
| 1820 | 3078 | |
| 1821 | self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg); | |
| 3079 | mod.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg); | |
| 1822 | 3080 | root_scope.status = .unloaded_parse_failure; |
| 1823 | 3081 | return error.AnalysisFail; |
| 1824 | 3082 | } |
| ... | ... | @@ -2051,11 +3309,9 @@ fn semaContainerFn( |
| 2051 | 3309 | const tracy = trace(@src()); |
| 2052 | 3310 | defer tracy.end(); |
| 2053 | 3311 | |
| 2054 | const token_starts = tree.tokens.items(.start); | |
| 2055 | const token_tags = tree.tokens.items(.tag); | |
| 2056 | ||
| 2057 | 3312 | // We will create a Decl for it regardless of analysis status. |
| 2058 | 3313 | const name_tok = fn_proto.name_token orelse { |
| 3314 | // This problem will go away with #1717. | |
| 2059 | 3315 | @panic("TODO missing function name"); |
| 2060 | 3316 | }; |
| 2061 | 3317 | const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString |
| ... | ... | @@ -2068,8 +3324,8 @@ fn semaContainerFn( |
| 2068 | 3324 | if (deleted_decls.swapRemove(decl) == null) { |
| 2069 | 3325 | decl.analysis = .sema_failure; |
| 2070 | 3326 | 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 }, | |
| 2073 | 3329 | }, "redefinition of '{s}'", .{decl.name}); |
| 2074 | 3330 | errdefer msg.destroy(mod.gpa); |
| 2075 | 3331 | try mod.failed_decls.putNoClobber(mod.gpa, decl, msg); |
| ... | ... | @@ -2098,6 +3354,7 @@ fn semaContainerFn( |
| 2098 | 3354 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| 2099 | 3355 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| 2100 | 3356 | if (fn_proto.extern_export_token) |maybe_export_token| { |
| 3357 | const token_tags = tree.tokens.items(.tag); | |
| 2101 | 3358 | if (token_tags[maybe_export_token] == .keyword_export) { |
| 2102 | 3359 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 2103 | 3360 | } |
| ... | ... | @@ -2117,11 +3374,7 @@ fn semaContainerVar( |
| 2117 | 3374 | const tracy = trace(@src()); |
| 2118 | 3375 | defer tracy.end(); |
| 2119 | 3376 | |
| 2120 | const token_starts = tree.tokens.items(.start); | |
| 2121 | const token_tags = tree.tokens.items(.tag); | |
| 2122 | ||
| 2123 | 3377 | const name_token = var_decl.ast.mut_token + 1; |
| 2124 | const name_src = token_starts[name_token]; | |
| 2125 | 3378 | const name = tree.tokenSlice(name_token); // TODO identifierTokenString |
| 2126 | 3379 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| 2127 | 3380 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); |
| ... | ... | @@ -2132,8 +3385,8 @@ fn semaContainerVar( |
| 2132 | 3385 | if (deleted_decls.swapRemove(decl) == null) { |
| 2133 | 3386 | decl.analysis = .sema_failure; |
| 2134 | 3387 | 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 }, | |
| 2137 | 3390 | }, "redefinition of '{s}'", .{decl.name}); |
| 2138 | 3391 | errdefer err_msg.destroy(mod.gpa); |
| 2139 | 3392 | try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg); |
| ... | ... | @@ -2145,6 +3398,7 @@ fn semaContainerVar( |
| 2145 | 3398 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| 2146 | 3399 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| 2147 | 3400 | if (var_decl.extern_export_token) |maybe_export_token| { |
| 3401 | const token_tags = tree.tokens.items(.tag); | |
| 2148 | 3402 | if (token_tags[maybe_export_token] == .keyword_export) { |
| 2149 | 3403 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 2150 | 3404 | } |
| ... | ... | @@ -2167,11 +3421,11 @@ fn semaContainerField( |
| 2167 | 3421 | log.err("TODO: analyze container field", .{}); |
| 2168 | 3422 | } |
| 2169 | 3423 | |
| 2170 | pub fn deleteDecl(self: *Module, decl: *Decl) !void { | |
| 3424 | pub fn deleteDecl(mod: *Module, decl: *Decl) !void { | |
| 2171 | 3425 | const tracy = trace(@src()); |
| 2172 | 3426 | defer tracy.end(); |
| 2173 | 3427 | |
| 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); | |
| 2175 | 3429 | |
| 2176 | 3430 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will |
| 2177 | 3431 | // not be present in the set, and this does nothing. |
| ... | ... | @@ -2179,7 +3433,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 2179 | 3433 | |
| 2180 | 3434 | log.debug("deleting decl '{s}'", .{decl.name}); |
| 2181 | 3435 | const name_hash = decl.fullyQualifiedNameHash(); |
| 2182 | self.decl_table.removeAssertDiscard(name_hash); | |
| 3436 | mod.decl_table.removeAssertDiscard(name_hash); | |
| 2183 | 3437 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. |
| 2184 | 3438 | for (decl.dependencies.items()) |entry| { |
| 2185 | 3439 | const dep = entry.key; |
| ... | ... | @@ -2188,7 +3442,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 2188 | 3442 | // We don't recursively perform a deletion here, because during the update, |
| 2189 | 3443 | // another reference to it may turn up. |
| 2190 | 3444 | dep.deletion_flag = true; |
| 2191 | self.deletion_set.appendAssumeCapacity(dep); | |
| 3445 | mod.deletion_set.appendAssumeCapacity(dep); | |
| 2192 | 3446 | } |
| 2193 | 3447 | } |
| 2194 | 3448 | // 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 { |
| 2197 | 3451 | dep.removeDependency(decl); |
| 2198 | 3452 | if (dep.analysis != .outdated) { |
| 2199 | 3453 | // TODO Move this failure possibility to the top of the function. |
| 2200 | try self.markOutdatedDecl(dep); | |
| 3454 | try mod.markOutdatedDecl(dep); | |
| 2201 | 3455 | } |
| 2202 | 3456 | } |
| 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); | |
| 2205 | 3459 | } |
| 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); | |
| 2208 | 3462 | } |
| 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); | |
| 2212 | 3466 | |
| 2213 | decl.destroy(self); | |
| 3467 | decl.destroy(mod); | |
| 2214 | 3468 | } |
| 2215 | 3469 | |
| 2216 | 3470 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of |
| 2217 | 3471 | /// this Decl will cause them to be re-created (or not). |
| 2218 | fn deleteDeclExports(self: *Module, decl: *Decl) void { | |
| 2219 | const kv = self.export_owners.swapRemove(decl) orelse return; | |
| 3472 | fn deleteDeclExports(mod: *Module, decl: *Decl) void { | |
| 3473 | const kv = mod.export_owners.swapRemove(decl) orelse return; | |
| 2220 | 3474 | |
| 2221 | 3475 | 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| { | |
| 2223 | 3477 | // Remove exports with owner_decl matching the regenerating decl. |
| 2224 | 3478 | const list = decl_exports_kv.value; |
| 2225 | 3479 | var i: usize = 0; |
| ... | ... | @@ -2232,73 +3486,101 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { |
| 2232 | 3486 | i += 1; |
| 2233 | 3487 | } |
| 2234 | 3488 | } |
| 2235 | decl_exports_kv.value = self.gpa.shrink(list, new_len); | |
| 3489 | decl_exports_kv.value = mod.gpa.shrink(list, new_len); | |
| 2236 | 3490 | if (new_len == 0) { |
| 2237 | self.decl_exports.removeAssertDiscard(exp.exported_decl); | |
| 3491 | mod.decl_exports.removeAssertDiscard(exp.exported_decl); | |
| 2238 | 3492 | } |
| 2239 | 3493 | } |
| 2240 | if (self.comp.bin_file.cast(link.File.Elf)) |elf| { | |
| 3494 | if (mod.comp.bin_file.cast(link.File.Elf)) |elf| { | |
| 2241 | 3495 | elf.deleteExport(exp.link.elf); |
| 2242 | 3496 | } |
| 2243 | if (self.comp.bin_file.cast(link.File.MachO)) |macho| { | |
| 3497 | if (mod.comp.bin_file.cast(link.File.MachO)) |macho| { | |
| 2244 | 3498 | macho.deleteExport(exp.link.macho); |
| 2245 | 3499 | } |
| 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); | |
| 2248 | 3502 | } |
| 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); | |
| 2252 | 3506 | } |
| 2253 | self.gpa.free(kv.value); | |
| 3507 | mod.gpa.free(kv.value); | |
| 2254 | 3508 | } |
| 2255 | 3509 | |
| 2256 | pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { | |
| 3510 | pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void { | |
| 2257 | 3511 | const tracy = trace(@src()); |
| 2258 | 3512 | defer tracy.end(); |
| 2259 | 3513 | |
| 2260 | 3514 | // 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); | |
| 2262 | 3516 | 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); | |
| 2266 | 3549 | |
| 2267 | 3550 | var inner_block: Scope.Block = .{ |
| 2268 | 3551 | .parent = null, |
| 2269 | .inst_table = &inst_table, | |
| 2270 | .func = func, | |
| 2271 | .owner_decl = decl, | |
| 3552 | .sema = &sema, | |
| 2272 | 3553 | .src_decl = decl, |
| 2273 | 3554 | .instructions = .{}, |
| 2274 | .arena = &arena.allocator, | |
| 2275 | 3555 | .inlining = null, |
| 2276 | 3556 | .is_comptime = false, |
| 2277 | .branch_quota = &branch_quota, | |
| 2278 | 3557 | }; |
| 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); | |
| 2280 | 3562 | |
| 2281 | 3563 | func.state = .in_progress; |
| 2282 | 3564 | log.debug("set {s} to in_progress", .{decl.name}); |
| 2283 | 3565 | |
| 2284 | try zir_sema.analyzeBody(self, &inner_block, func.zir); | |
| 3566 | _ = try sema.root(&inner_block); | |
| 2285 | 3567 | |
| 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); | |
| 2287 | 3569 | func.state = .success; |
| 2288 | 3570 | func.body = .{ .instructions = instructions }; |
| 2289 | 3571 | log.debug("set {s} to success", .{decl.name}); |
| 2290 | 3572 | } |
| 2291 | 3573 | |
| 2292 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | |
| 3574 | fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { | |
| 2293 | 3575 | 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); | |
| 2297 | 3579 | } |
| 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); | |
| 2300 | 3582 | } |
| 2301 | _ = self.compile_log_decls.swapRemove(decl); | |
| 3583 | _ = mod.compile_log_decls.swapRemove(decl); | |
| 2302 | 3584 | decl.analysis = .outdated; |
| 2303 | 3585 | } |
| 2304 | 3586 | |
| ... | ... | @@ -2349,65 +3631,39 @@ fn allocateNewDecl( |
| 2349 | 3631 | } |
| 2350 | 3632 | |
| 2351 | 3633 | fn createNewDecl( |
| 2352 | self: *Module, | |
| 3634 | mod: *Module, | |
| 2353 | 3635 | scope: *Scope, |
| 2354 | 3636 | decl_name: []const u8, |
| 2355 | 3637 | src_index: usize, |
| 2356 | 3638 | name_hash: Scope.NameHash, |
| 2357 | 3639 | contents_hash: std.zig.SrcHash, |
| 2358 | 3640 | ) !*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); | |
| 2364 | 3646 | return new_decl; |
| 2365 | 3647 | } |
| 2366 | 3648 | |
| 2367 | 3649 | /// Get error value for error tag `name`. |
| 2368 | pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry { | |
| 2369 | const gop = try self.global_error_set.getOrPut(self.gpa, name); | |
| 3650 | pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged(ErrorInt).Entry { | |
| 3651 | const gop = try mod.global_error_set.getOrPut(mod.gpa, name); | |
| 2370 | 3652 | if (gop.found_existing) |
| 2371 | 3653 | return gop.entry.*; |
| 2372 | errdefer self.global_error_set.removeAssertDiscard(name); | |
| 2373 | 3654 | |
| 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); | |
| 2376 | 3660 | return gop.entry.*; |
| 2377 | 3661 | } |
| 2378 | 3662 | |
| 2379 | pub 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 | ||
| 2384 | pub 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 | ||
| 2392 | pub 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 | ||
| 2397 | pub 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 | ||
| 2407 | 3663 | pub fn analyzeExport( |
| 2408 | 3664 | mod: *Module, |
| 2409 | 3665 | scope: *Scope, |
| 2410 | src: usize, | |
| 3666 | src: LazySrcLoc, | |
| 2411 | 3667 | borrowed_symbol_name: []const u8, |
| 2412 | 3668 | exported_decl: *Decl, |
| 2413 | 3669 | ) !void { |
| ... | ... | @@ -2496,178 +3752,11 @@ pub fn analyzeExport( |
| 2496 | 3752 | }, |
| 2497 | 3753 | }; |
| 2498 | 3754 | } |
| 2499 | ||
| 2500 | pub 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 | ||
| 2519 | pub 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 | ||
| 2540 | pub 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 | ||
| 2563 | pub 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 | ||
| 2577 | pub 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 | ||
| 2598 | pub 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 | ||
| 2621 | pub 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 | ||
| 2643 | pub 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 | ||
| 2666 | pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst { | |
| 2667 | const const_inst = try scope.arena().create(Inst.Constant); | |
| 3755 | pub fn constInst(mod: *Module, arena: *Allocator, src: LazySrcLoc, typed_value: TypedValue) !*ir.Inst { | |
| 3756 | const const_inst = try arena.create(ir.Inst.Constant); | |
| 2668 | 3757 | const_inst.* = .{ |
| 2669 | 3758 | .base = .{ |
| 2670 | .tag = Inst.Constant.base_tag, | |
| 3759 | .tag = ir.Inst.Constant.base_tag, | |
| 2671 | 3760 | .ty = typed_value.ty, |
| 2672 | 3761 | .src = src, |
| 2673 | 3762 | }, |
| ... | ... | @@ -2676,94 +3765,94 @@ pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedVal |
| 2676 | 3765 | return &const_inst.base; |
| 2677 | 3766 | } |
| 2678 | 3767 | |
| 2679 | pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | |
| 2680 | return self.constInst(scope, src, .{ | |
| 3768 | pub fn constType(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type) !*ir.Inst { | |
| 3769 | return mod.constInst(arena, src, .{ | |
| 2681 | 3770 | .ty = Type.initTag(.type), |
| 2682 | .val = try ty.toValue(scope.arena()), | |
| 3771 | .val = try ty.toValue(arena), | |
| 2683 | 3772 | }); |
| 2684 | 3773 | } |
| 2685 | 3774 | |
| 2686 | pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst { | |
| 2687 | return self.constInst(scope, src, .{ | |
| 3775 | pub fn constVoid(mod: *Module, arena: *Allocator, src: LazySrcLoc) !*ir.Inst { | |
| 3776 | return mod.constInst(arena, src, .{ | |
| 2688 | 3777 | .ty = Type.initTag(.void), |
| 2689 | 3778 | .val = Value.initTag(.void_value), |
| 2690 | 3779 | }); |
| 2691 | 3780 | } |
| 2692 | 3781 | |
| 2693 | pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst { | |
| 2694 | return self.constInst(scope, src, .{ | |
| 3782 | pub fn constNoReturn(mod: *Module, arena: *Allocator, src: LazySrcLoc) !*ir.Inst { | |
| 3783 | return mod.constInst(arena, src, .{ | |
| 2695 | 3784 | .ty = Type.initTag(.noreturn), |
| 2696 | 3785 | .val = Value.initTag(.unreachable_value), |
| 2697 | 3786 | }); |
| 2698 | 3787 | } |
| 2699 | 3788 | |
| 2700 | pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | |
| 2701 | return self.constInst(scope, src, .{ | |
| 3789 | pub fn constUndef(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type) !*ir.Inst { | |
| 3790 | return mod.constInst(arena, src, .{ | |
| 2702 | 3791 | .ty = ty, |
| 2703 | 3792 | .val = Value.initTag(.undef), |
| 2704 | 3793 | }); |
| 2705 | 3794 | } |
| 2706 | 3795 | |
| 2707 | pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst { | |
| 2708 | return self.constInst(scope, src, .{ | |
| 3796 | pub fn constBool(mod: *Module, arena: *Allocator, src: LazySrcLoc, v: bool) !*ir.Inst { | |
| 3797 | return mod.constInst(arena, src, .{ | |
| 2709 | 3798 | .ty = Type.initTag(.bool), |
| 2710 | 3799 | .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)], |
| 2711 | 3800 | }); |
| 2712 | 3801 | } |
| 2713 | 3802 | |
| 2714 | pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst { | |
| 2715 | return self.constInst(scope, src, .{ | |
| 3803 | pub fn constIntUnsigned(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, int: u64) !*ir.Inst { | |
| 3804 | return mod.constInst(arena, src, .{ | |
| 2716 | 3805 | .ty = ty, |
| 2717 | .val = try Value.Tag.int_u64.create(scope.arena(), int), | |
| 3806 | .val = try Value.Tag.int_u64.create(arena, int), | |
| 2718 | 3807 | }); |
| 2719 | 3808 | } |
| 2720 | 3809 | |
| 2721 | pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst { | |
| 2722 | return self.constInst(scope, src, .{ | |
| 3810 | pub fn constIntSigned(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, int: i64) !*ir.Inst { | |
| 3811 | return mod.constInst(arena, src, .{ | |
| 2723 | 3812 | .ty = ty, |
| 2724 | .val = try Value.Tag.int_i64.create(scope.arena(), int), | |
| 3813 | .val = try Value.Tag.int_i64.create(arena, int), | |
| 2725 | 3814 | }); |
| 2726 | 3815 | } |
| 2727 | 3816 | |
| 2728 | pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst { | |
| 3817 | pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, big_int: BigIntConst) !*ir.Inst { | |
| 2729 | 3818 | if (big_int.positive) { |
| 2730 | 3819 | if (big_int.to(u64)) |x| { |
| 2731 | return self.constIntUnsigned(scope, src, ty, x); | |
| 3820 | return mod.constIntUnsigned(arena, src, ty, x); | |
| 2732 | 3821 | } else |err| switch (err) { |
| 2733 | 3822 | error.NegativeIntoUnsigned => unreachable, |
| 2734 | 3823 | error.TargetTooSmall => {}, // handled below |
| 2735 | 3824 | } |
| 2736 | return self.constInst(scope, src, .{ | |
| 3825 | return mod.constInst(arena, src, .{ | |
| 2737 | 3826 | .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), | |
| 2739 | 3828 | }); |
| 2740 | 3829 | } else { |
| 2741 | 3830 | if (big_int.to(i64)) |x| { |
| 2742 | return self.constIntSigned(scope, src, ty, x); | |
| 3831 | return mod.constIntSigned(arena, src, ty, x); | |
| 2743 | 3832 | } else |err| switch (err) { |
| 2744 | 3833 | error.NegativeIntoUnsigned => unreachable, |
| 2745 | 3834 | error.TargetTooSmall => {}, // handled below |
| 2746 | 3835 | } |
| 2747 | return self.constInst(scope, src, .{ | |
| 3836 | return mod.constInst(arena, src, .{ | |
| 2748 | 3837 | .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), | |
| 2750 | 3839 | }); |
| 2751 | 3840 | } |
| 2752 | 3841 | } |
| 2753 | 3842 | |
| 2754 | 3843 | pub fn createAnonymousDecl( |
| 2755 | self: *Module, | |
| 3844 | mod: *Module, | |
| 2756 | 3845 | scope: *Scope, |
| 2757 | 3846 | decl_arena: *std.heap.ArenaAllocator, |
| 2758 | 3847 | typed_value: TypedValue, |
| 2759 | 3848 | ) !*Decl { |
| 2760 | const name_index = self.getNextAnonNameIndex(); | |
| 3849 | const name_index = mod.getNextAnonNameIndex(); | |
| 2761 | 3850 | 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); | |
| 2764 | 3853 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); |
| 2765 | 3854 | 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); | |
| 2767 | 3856 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 2768 | 3857 | |
| 2769 | 3858 | decl_arena_state.* = decl_arena.state; |
| ... | ... | @@ -2774,32 +3863,32 @@ pub fn createAnonymousDecl( |
| 2774 | 3863 | }, |
| 2775 | 3864 | }; |
| 2776 | 3865 | new_decl.analysis = .complete; |
| 2777 | new_decl.generation = self.generation; | |
| 3866 | new_decl.generation = mod.generation; | |
| 2778 | 3867 | |
| 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. | |
| 2782 | 3871 | 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 }); | |
| 2785 | 3874 | } |
| 2786 | 3875 | |
| 2787 | 3876 | return new_decl; |
| 2788 | 3877 | } |
| 2789 | 3878 | |
| 2790 | 3879 | pub fn createContainerDecl( |
| 2791 | self: *Module, | |
| 3880 | mod: *Module, | |
| 2792 | 3881 | scope: *Scope, |
| 2793 | 3882 | base_token: std.zig.ast.TokenIndex, |
| 2794 | 3883 | decl_arena: *std.heap.ArenaAllocator, |
| 2795 | 3884 | typed_value: TypedValue, |
| 2796 | 3885 | ) !*Decl { |
| 2797 | 3886 | 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); | |
| 2800 | 3889 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); |
| 2801 | 3890 | 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); | |
| 2803 | 3892 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 2804 | 3893 | |
| 2805 | 3894 | decl_arena_state.* = decl_arena.state; |
| ... | ... | @@ -2810,12 +3899,12 @@ pub fn createContainerDecl( |
| 2810 | 3899 | }, |
| 2811 | 3900 | }; |
| 2812 | 3901 | new_decl.analysis = .complete; |
| 2813 | new_decl.generation = self.generation; | |
| 3902 | new_decl.generation = mod.generation; | |
| 2814 | 3903 | |
| 2815 | 3904 | return new_decl; |
| 2816 | 3905 | } |
| 2817 | 3906 | |
| 2818 | fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 { | |
| 3907 | fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 { | |
| 2819 | 3908 | // TODO add namespaces, generic function signatrues |
| 2820 | 3909 | const tree = scope.tree(); |
| 2821 | 3910 | const token_tags = tree.tokens.items(.tag); |
| ... | ... | @@ -2827,775 +3916,39 @@ fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIn |
| 2827 | 3916 | else => unreachable, |
| 2828 | 3917 | }; |
| 2829 | 3918 | 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 }); | |
| 2831 | 3920 | } |
| 2832 | 3921 | |
| 2833 | fn getNextAnonNameIndex(self: *Module) usize { | |
| 2834 | return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic); | |
| 3922 | fn getNextAnonNameIndex(mod: *Module) usize { | |
| 3923 | return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); | |
| 2835 | 3924 | } |
| 2836 | 3925 | |
| 2837 | pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { | |
| 3926 | pub fn lookupDeclName(mod: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { | |
| 2838 | 3927 | const namespace = scope.namespace(); |
| 2839 | 3928 | const name_hash = namespace.fullyQualifiedNameHash(ident_name); |
| 2840 | return self.decl_table.get(name_hash); | |
| 2841 | } | |
| 2842 | ||
| 2843 | pub 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); | |
| 2846 | 3930 | } |
| 2847 | 3931 | |
| 2848 | pub 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 | ||
| 2874 | fn 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 | ||
| 2899 | pub 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 | ||
| 2913 | pub 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 | ||
| 2929 | pub 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 | ||
| 2935 | pub 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 | ||
| 2945 | pub 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 | ||
| 2962 | pub 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 | ||
| 2974 | pub 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 | ||
| 3040 | pub 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. | |
| 3094 | pub 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 | ||
| 3265 | fn 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 | ||
| 3274 | fn 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 | ||
| 3320 | fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type { | |
| 3321 | const int_payload = try scope.arena().create(Type.Payload.Bits); | |
| 3932 | pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type { | |
| 3933 | const int_payload = try arena.create(Type.Payload.Bits); | |
| 3322 | 3934 | int_payload.* = .{ |
| 3323 | 3935 | .base = .{ |
| 3324 | .tag = if (signed) .int_signed else .int_unsigned, | |
| 3936 | .tag = switch (signedness) { | |
| 3937 | .signed => .int_signed, | |
| 3938 | .unsigned => .int_unsigned, | |
| 3939 | }, | |
| 3325 | 3940 | }, |
| 3326 | 3941 | .data = bits, |
| 3327 | 3942 | }; |
| 3328 | 3943 | return Type.initPayload(&int_payload.base); |
| 3329 | 3944 | } |
| 3330 | 3945 | |
| 3331 | pub 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 | ||
| 3386 | pub 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 | ||
| 3506 | pub 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 | ||
| 3542 | pub 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 | ||
| 3551 | pub 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 | ||
| 3567 | pub 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 | ||
| 3577 | fn 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 | ||
| 3585 | fn 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 | ||
| 3593 | 3946 | /// We don't return a pointer to the new error note because the pointer |
| 3594 | 3947 | /// becomes invalid when you add another one. |
| 3595 | 3948 | pub fn errNote( |
| 3596 | 3949 | mod: *Module, |
| 3597 | 3950 | scope: *Scope, |
| 3598 | src: usize, | |
| 3951 | src: LazySrcLoc, | |
| 3599 | 3952 | parent: *ErrorMsg, |
| 3600 | 3953 | comptime format: []const u8, |
| 3601 | 3954 | args: anytype, |
| ... | ... | @@ -3605,10 +3958,7 @@ pub fn errNote( |
| 3605 | 3958 | |
| 3606 | 3959 | parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1); |
| 3607 | 3960 | 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), | |
| 3612 | 3962 | .msg = msg, |
| 3613 | 3963 | }; |
| 3614 | 3964 | } |
| ... | ... | @@ -3616,121 +3966,112 @@ pub fn errNote( |
| 3616 | 3966 | pub fn errMsg( |
| 3617 | 3967 | mod: *Module, |
| 3618 | 3968 | scope: *Scope, |
| 3619 | src_byte_offset: usize, | |
| 3969 | src: LazySrcLoc, | |
| 3620 | 3970 | comptime format: []const u8, |
| 3621 | 3971 | args: anytype, |
| 3622 | 3972 | ) 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); | |
| 3627 | 3974 | } |
| 3628 | 3975 | |
| 3629 | 3976 | pub fn fail( |
| 3630 | 3977 | mod: *Module, |
| 3631 | 3978 | scope: *Scope, |
| 3632 | src_byte_offset: usize, | |
| 3979 | src: LazySrcLoc, | |
| 3633 | 3980 | comptime format: []const u8, |
| 3634 | 3981 | args: anytype, |
| 3635 | 3982 | ) 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); | |
| 3637 | 3984 | return mod.failWithOwnedErrorMsg(scope, err_msg); |
| 3638 | 3985 | } |
| 3639 | 3986 | |
| 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`. | |
| 3989 | pub 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`. | |
| 3640 | 4003 | pub fn failTok( |
| 3641 | self: *Module, | |
| 4004 | mod: *Module, | |
| 3642 | 4005 | scope: *Scope, |
| 3643 | 4006 | token_index: ast.TokenIndex, |
| 3644 | 4007 | comptime format: []const u8, |
| 3645 | 4008 | args: anytype, |
| 3646 | 4009 | ) 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); | |
| 3649 | 4012 | } |
| 3650 | 4013 | |
| 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`. | |
| 3651 | 4016 | pub fn failNode( |
| 3652 | self: *Module, | |
| 4017 | mod: *Module, | |
| 3653 | 4018 | scope: *Scope, |
| 3654 | ast_node: ast.Node.Index, | |
| 4019 | node_index: ast.Node.Index, | |
| 3655 | 4020 | comptime format: []const u8, |
| 3656 | 4021 | args: anytype, |
| 3657 | 4022 | ) 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); | |
| 3661 | 4025 | } |
| 3662 | 4026 | |
| 3663 | pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError { | |
| 4027 | pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError { | |
| 3664 | 4028 | @setCold(true); |
| 3665 | 4029 | { |
| 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); | |
| 3669 | 4033 | } |
| 3670 | 4034 | switch (scope.tag) { |
| 3671 | 4035 | .block => { |
| 3672 | 4036 | 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; | |
| 3680 | 4039 | } 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; | |
| 3687 | 4042 | } |
| 3688 | self.failed_decls.putAssumeCapacityNoClobber(block.owner_decl, err_msg); | |
| 4043 | mod.failed_decls.putAssumeCapacityNoClobber(block.sema.owner_decl, err_msg); | |
| 3689 | 4044 | }, |
| 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); | |
| 3695 | 4050 | }, |
| 3696 | 4051 | .local_val => { |
| 3697 | 4052 | 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); | |
| 3701 | 4056 | }, |
| 3702 | 4057 | .local_ptr => { |
| 3703 | 4058 | 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); | |
| 3713 | 4062 | }, |
| 3714 | 4063 | .file => unreachable, |
| 3715 | 4064 | .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 | }, | |
| 3716 | 4071 | } |
| 3717 | 4072 | return error.AnalysisFail; |
| 3718 | 4073 | } |
| 3719 | 4074 | |
| 3720 | const InMemoryCoercionResult = enum { | |
| 3721 | ok, | |
| 3722 | no_match, | |
| 3723 | }; | |
| 3724 | ||
| 3725 | fn 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 | ||
| 3734 | 4075 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { |
| 3735 | 4076 | return @bitCast(u128, a) == @bitCast(u128, b); |
| 3736 | 4077 | } |
| ... | ... | @@ -3780,14 +4121,12 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { |
| 3780 | 4121 | } |
| 3781 | 4122 | |
| 3782 | 4123 | pub fn floatAdd( |
| 3783 | self: *Module, | |
| 3784 | scope: *Scope, | |
| 4124 | arena: *Allocator, | |
| 3785 | 4125 | float_type: Type, |
| 3786 | src: usize, | |
| 4126 | src: LazySrcLoc, | |
| 3787 | 4127 | lhs: Value, |
| 3788 | 4128 | rhs: Value, |
| 3789 | 4129 | ) !Value { |
| 3790 | const arena = scope.arena(); | |
| 3791 | 4130 | switch (float_type.tag()) { |
| 3792 | 4131 | .f16 => { |
| 3793 | 4132 | @panic("TODO add __trunctfhf2 to compiler-rt"); |
| ... | ... | @@ -3815,14 +4154,12 @@ pub fn floatAdd( |
| 3815 | 4154 | } |
| 3816 | 4155 | |
| 3817 | 4156 | pub fn floatSub( |
| 3818 | self: *Module, | |
| 3819 | scope: *Scope, | |
| 4157 | arena: *Allocator, | |
| 3820 | 4158 | float_type: Type, |
| 3821 | src: usize, | |
| 4159 | src: LazySrcLoc, | |
| 3822 | 4160 | lhs: Value, |
| 3823 | 4161 | rhs: Value, |
| 3824 | 4162 | ) !Value { |
| 3825 | const arena = scope.arena(); | |
| 3826 | 4163 | switch (float_type.tag()) { |
| 3827 | 4164 | .f16 => { |
| 3828 | 4165 | @panic("TODO add __trunctfhf2 to compiler-rt"); |
| ... | ... | @@ -3850,9 +4187,8 @@ pub fn floatSub( |
| 3850 | 4187 | } |
| 3851 | 4188 | |
| 3852 | 4189 | pub fn simplePtrType( |
| 3853 | self: *Module, | |
| 3854 | scope: *Scope, | |
| 3855 | src: usize, | |
| 4190 | mod: *Module, | |
| 4191 | arena: *Allocator, | |
| 3856 | 4192 | elem_ty: Type, |
| 3857 | 4193 | mutable: bool, |
| 3858 | 4194 | size: std.builtin.TypeInfo.Pointer.Size, |
| ... | ... | @@ -3863,7 +4199,7 @@ pub fn simplePtrType( |
| 3863 | 4199 | // TODO stage1 type inference bug |
| 3864 | 4200 | const T = Type.Tag; |
| 3865 | 4201 | |
| 3866 | const type_payload = try scope.arena().create(Type.Payload.ElemType); | |
| 4202 | const type_payload = try arena.create(Type.Payload.ElemType); | |
| 3867 | 4203 | type_payload.* = .{ |
| 3868 | 4204 | .base = .{ |
| 3869 | 4205 | .tag = switch (size) { |
| ... | ... | @@ -3879,9 +4215,8 @@ pub fn simplePtrType( |
| 3879 | 4215 | } |
| 3880 | 4216 | |
| 3881 | 4217 | pub fn ptrType( |
| 3882 | self: *Module, | |
| 3883 | scope: *Scope, | |
| 3884 | src: usize, | |
| 4218 | mod: *Module, | |
| 4219 | arena: *Allocator, | |
| 3885 | 4220 | elem_ty: Type, |
| 3886 | 4221 | sentinel: ?Value, |
| 3887 | 4222 | @"align": u32, |
| ... | ... | @@ -3895,7 +4230,7 @@ pub fn ptrType( |
| 3895 | 4230 | assert(host_size == 0 or bit_offset < host_size * 8); |
| 3896 | 4231 | |
| 3897 | 4232 | // TODO check if type can be represented by simplePtrType |
| 3898 | return Type.Tag.pointer.create(scope.arena(), .{ | |
| 4233 | return Type.Tag.pointer.create(arena, .{ | |
| 3899 | 4234 | .pointee_type = elem_ty, |
| 3900 | 4235 | .sentinel = sentinel, |
| 3901 | 4236 | .@"align" = @"align", |
| ... | ... | @@ -3908,23 +4243,23 @@ pub fn ptrType( |
| 3908 | 4243 | }); |
| 3909 | 4244 | } |
| 3910 | 4245 | |
| 3911 | pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type { | |
| 4246 | pub fn optionalType(mod: *Module, arena: *Allocator, child_type: Type) Allocator.Error!Type { | |
| 3912 | 4247 | switch (child_type.tag()) { |
| 3913 | 4248 | .single_const_pointer => return Type.Tag.optional_single_const_pointer.create( |
| 3914 | scope.arena(), | |
| 4249 | arena, | |
| 3915 | 4250 | child_type.elemType(), |
| 3916 | 4251 | ), |
| 3917 | 4252 | .single_mut_pointer => return Type.Tag.optional_single_mut_pointer.create( |
| 3918 | scope.arena(), | |
| 4253 | arena, | |
| 3919 | 4254 | child_type.elemType(), |
| 3920 | 4255 | ), |
| 3921 | else => return Type.Tag.optional.create(scope.arena(), child_type), | |
| 4256 | else => return Type.Tag.optional.create(arena, child_type), | |
| 3922 | 4257 | } |
| 3923 | 4258 | } |
| 3924 | 4259 | |
| 3925 | 4260 | pub fn arrayType( |
| 3926 | self: *Module, | |
| 3927 | scope: *Scope, | |
| 4261 | mod: *Module, | |
| 4262 | arena: *Allocator, | |
| 3928 | 4263 | len: u64, |
| 3929 | 4264 | sentinel: ?Value, |
| 3930 | 4265 | elem_type: Type, |
| ... | ... | @@ -3932,30 +4267,30 @@ pub fn arrayType( |
| 3932 | 4267 | if (elem_type.eql(Type.initTag(.u8))) { |
| 3933 | 4268 | if (sentinel) |some| { |
| 3934 | 4269 | 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); | |
| 3936 | 4271 | } |
| 3937 | 4272 | } else { |
| 3938 | return Type.Tag.array_u8.create(scope.arena(), len); | |
| 4273 | return Type.Tag.array_u8.create(arena, len); | |
| 3939 | 4274 | } |
| 3940 | 4275 | } |
| 3941 | 4276 | |
| 3942 | 4277 | if (sentinel) |some| { |
| 3943 | return Type.Tag.array_sentinel.create(scope.arena(), .{ | |
| 4278 | return Type.Tag.array_sentinel.create(arena, .{ | |
| 3944 | 4279 | .len = len, |
| 3945 | 4280 | .sentinel = some, |
| 3946 | 4281 | .elem_type = elem_type, |
| 3947 | 4282 | }); |
| 3948 | 4283 | } |
| 3949 | 4284 | |
| 3950 | return Type.Tag.array.create(scope.arena(), .{ | |
| 4285 | return Type.Tag.array.create(arena, .{ | |
| 3951 | 4286 | .len = len, |
| 3952 | 4287 | .elem_type = elem_type, |
| 3953 | 4288 | }); |
| 3954 | 4289 | } |
| 3955 | 4290 | |
| 3956 | 4291 | pub fn errorUnionType( |
| 3957 | self: *Module, | |
| 3958 | scope: *Scope, | |
| 4292 | mod: *Module, | |
| 4293 | arena: *Allocator, | |
| 3959 | 4294 | error_set: Type, |
| 3960 | 4295 | payload: Type, |
| 3961 | 4296 | ) Allocator.Error!Type { |
| ... | ... | @@ -3964,19 +4299,15 @@ pub fn errorUnionType( |
| 3964 | 4299 | return Type.initTag(.anyerror_void_error_union); |
| 3965 | 4300 | } |
| 3966 | 4301 | |
| 3967 | return Type.Tag.error_union.create(scope.arena(), .{ | |
| 4302 | return Type.Tag.error_union.create(arena, .{ | |
| 3968 | 4303 | .error_set = error_set, |
| 3969 | 4304 | .payload = payload, |
| 3970 | 4305 | }); |
| 3971 | 4306 | } |
| 3972 | 4307 | |
| 3973 | pub 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 | ||
| 3977 | pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { | |
| 4308 | pub fn dumpInst(mod: *Module, scope: *Scope, inst: *ir.Inst) void { | |
| 3978 | 4309 | 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"); | |
| 3980 | 4311 | const loc = std.zig.findLineColumn(source, inst.src); |
| 3981 | 4312 | if (inst.tag == .constant) { |
| 3982 | 4313 | 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 { |
| 4006 | 4337 | } |
| 4007 | 4338 | } |
| 4008 | 4339 | |
| 4009 | pub const PanicId = enum { | |
| 4010 | unreach, | |
| 4011 | unwrap_null, | |
| 4012 | unwrap_errunion, | |
| 4013 | }; | |
| 4014 | ||
| 4015 | pub 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 | ||
| 4077 | pub 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); | |
| 4340 | pub fn getTarget(mod: Module) Target { | |
| 4341 | return mod.comp.bin_file.options.target; | |
| 4081 | 4342 | } |
| 4082 | 4343 | |
| 4083 | pub fn getTarget(self: Module) Target { | |
| 4084 | return self.comp.bin_file.options.target; | |
| 4085 | } | |
| 4086 | ||
| 4087 | pub fn optimizeMode(self: Module) std.builtin.Mode { | |
| 4088 | return self.comp.bin_file.options.optimize_mode; | |
| 4089 | } | |
| 4090 | ||
| 4091 | pub 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 | } | |
| 4344 | pub fn optimizeMode(mod: Module) std.builtin.Mode { | |
| 4345 | return mod.comp.bin_file.options.optimize_mode; | |
| 4095 | 4346 | } |
| 4096 | 4347 | |
| 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`. | |
| 4098 | 4353 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { |
| 4099 | 4354 | const tree = scope.tree(); |
| 4100 | 4355 | const token_tags = tree.tokens.items(.tag); |
| 4101 | const token_starts = tree.tokens.items(.start); | |
| 4102 | 4356 | assert(token_tags[token] == .identifier); |
| 4103 | ||
| 4104 | 4357 | 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; | |
| 4116 | 4360 | } |
| 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); | |
| 4118 | 4365 | } |
| 4119 | 4366 | |
| 4120 | pub 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`. | |
| 4370 | pub 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); | |
| 4128 | 4384 | } |
| 4129 | 4385 | } |
| 4130 | 4386 | |
| 4131 | pub fn namedFieldPtr( | |
| 4387 | /// Appends the result to `buf`. | |
| 4388 | pub fn parseStrLit( | |
| 4132 | 4389 | mod: *Module, |
| 4133 | 4390 | 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 | ); | |
| 4161 | 4411 | }, |
| 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 | ); | |
| 4185 | 4419 | }, |
| 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 | ); | |
| 4230 | 4451 | }, |
| 4231 | else => {}, | |
| 4232 | } | |
| 4233 | return mod.fail(scope, src, "type '{}' does not support field access", .{elem_ty}); | |
| 4234 | } | |
| 4235 | ||
| 4236 | pub 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 | } | |
| 4269 | 4452 | } |
| 4270 | ||
| 4271 | return mod.fail(scope, src, "TODO implement more analyze elemptr", .{}); | |
| 4272 | 4453 | } |
src/RangeSet.zig+19-18| ... | ... | @@ -2,13 +2,14 @@ const std = @import("std"); |
| 2 | 2 | const Order = std.math.Order; |
| 3 | 3 | const Value = @import("value.zig").Value; |
| 4 | 4 | const RangeSet = @This(); |
| 5 | const SwitchProngSrc = @import("AstGen.zig").SwitchProngSrc; | |
| 5 | 6 | |
| 6 | 7 | ranges: std.ArrayList(Range), |
| 7 | 8 | |
| 8 | 9 | pub const Range = struct { |
| 9 | start: Value, | |
| 10 | end: Value, | |
| 11 | src: usize, | |
| 10 | first: Value, | |
| 11 | last: Value, | |
| 12 | src: SwitchProngSrc, | |
| 12 | 13 | }; |
| 13 | 14 | |
| 14 | 15 | pub fn init(allocator: *std.mem.Allocator) RangeSet { |
| ... | ... | @@ -21,18 +22,15 @@ pub fn deinit(self: *RangeSet) void { |
| 21 | 22 | self.ranges.deinit(); |
| 22 | 23 | } |
| 23 | 24 | |
| 24 | pub fn add(self: *RangeSet, start: Value, end: Value, src: usize) !?usize { | |
| 25 | pub fn add(self: *RangeSet, first: Value, last: Value, src: SwitchProngSrc) !?SwitchProngSrc { | |
| 25 | 26 | 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. | |
| 31 | 29 | } |
| 32 | 30 | } |
| 33 | 31 | try self.ranges.append(.{ |
| 34 | .start = start, | |
| 35 | .end = end, | |
| 32 | .first = first, | |
| 33 | .last = last, | |
| 36 | 34 | .src = src, |
| 37 | 35 | }); |
| 38 | 36 | return null; |
| ... | ... | @@ -40,14 +38,17 @@ pub fn add(self: *RangeSet, start: Value, end: Value, src: usize) !?usize { |
| 40 | 38 | |
| 41 | 39 | /// Assumes a and b do not overlap |
| 42 | 40 | fn lessThan(_: void, a: Range, b: Range) bool { |
| 43 | return a.start.compare(.lt, b.start); | |
| 41 | return a.first.compare(.lt, b.first); | |
| 44 | 42 | } |
| 45 | 43 | |
| 46 | pub fn spans(self: *RangeSet, start: Value, end: Value) !bool { | |
| 44 | pub fn spans(self: *RangeSet, first: Value, last: Value) !bool { | |
| 45 | if (self.ranges.items.len == 0) | |
| 46 | return false; | |
| 47 | ||
| 47 | 48 | std.sort.sort(Range, self.ranges.items, {}, lessThan); |
| 48 | 49 | |
| 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)) | |
| 51 | 52 | { |
| 52 | 53 | return false; |
| 53 | 54 | } |
| ... | ... | @@ -62,11 +63,11 @@ pub fn spans(self: *RangeSet, start: Value, end: Value) !bool { |
| 62 | 63 | // i starts counting from the second item. |
| 63 | 64 | const prev = self.ranges.items[i]; |
| 64 | 65 | |
| 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)); | |
| 67 | 68 | try counter.addScalar(counter.toConst(), 1); |
| 68 | 69 | |
| 69 | const cur_start_int = cur.start.toBigInt(&space); | |
| 70 | const cur_start_int = cur.first.toBigInt(&space); | |
| 70 | 71 | if (!cur_start_int.eq(counter.toConst())) { |
| 71 | 72 | return false; |
| 72 | 73 | } |
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 | ||
| 8 | mod: *Module, | |
| 9 | /// Alias to `mod.gpa`. | |
| 10 | gpa: *Allocator, | |
| 11 | /// Points to the arena allocator of the Decl. | |
| 12 | arena: *Allocator, | |
| 13 | code: zir.Code, | |
| 14 | /// Maps ZIR to TZIR. | |
| 15 | inst_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`. | |
| 19 | owner_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`. | |
| 22 | owner_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. | |
| 26 | func: ?*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 | |
| 33 | param_inst_list: []const *ir.Inst, | |
| 34 | branch_quota: u32 = 1000, | |
| 35 | branch_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. | |
| 40 | src: LazySrcLoc = .{ .token_offset = 0 }, | |
| 41 | ||
| 42 | const std = @import("std"); | |
| 43 | const mem = std.mem; | |
| 44 | const Allocator = std.mem.Allocator; | |
| 45 | const assert = std.debug.assert; | |
| 46 | const log = std.log.scoped(.sema); | |
| 47 | ||
| 48 | const Sema = @This(); | |
| 49 | const Value = @import("value.zig").Value; | |
| 50 | const Type = @import("type.zig").Type; | |
| 51 | const TypedValue = @import("TypedValue.zig"); | |
| 52 | const ir = @import("ir.zig"); | |
| 53 | const zir = @import("zir.zig"); | |
| 54 | const Module = @import("Module.zig"); | |
| 55 | const Inst = ir.Inst; | |
| 56 | const Body = ir.Body; | |
| 57 | const trace = @import("tracy.zig").trace; | |
| 58 | const Scope = Module.Scope; | |
| 59 | const InnerError = Module.InnerError; | |
| 60 | const Decl = Module.Decl; | |
| 61 | const LazySrcLoc = Module.LazySrcLoc; | |
| 62 | const RangeSet = @import("RangeSet.zig"); | |
| 63 | const AstGen = @import("AstGen.zig"); | |
| 64 | ||
| 65 | pub 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 | ||
| 72 | pub 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`. | |
| 78 | pub 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. | |
| 90 | fn 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. | |
| 100 | const 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. | |
| 111 | pub 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. | |
| 374 | pub 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 | ||
| 398 | fn 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 | ||
| 411 | fn 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 | ||
| 419 | fn 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 | ||
| 424 | fn 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 | ||
| 434 | fn failWithNeededComptime(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError { | |
| 435 | return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{}); | |
| 436 | } | |
| 437 | ||
| 438 | fn 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. | |
| 445 | fn 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 | ||
| 461 | fn 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 | ||
| 475 | fn 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 | ||
| 489 | fn 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 | ||
| 501 | fn 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 | ||
| 507 | fn 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 | ||
| 513 | fn 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 | ||
| 525 | fn 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 | ||
| 534 | fn 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 | ||
| 545 | fn 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 | ||
| 556 | fn 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 | ||
| 568 | fn 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 | ||
| 581 | fn 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 | ||
| 615 | fn 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 | ||
| 628 | fn 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 | ||
| 642 | fn 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 | ||
| 671 | fn 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 | ||
| 697 | fn 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 | ||
| 713 | fn 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 | ||
| 732 | fn 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 | ||
| 741 | fn 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 | ||
| 751 | fn 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 | ||
| 763 | fn 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 | ||
| 799 | fn 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 | ||
| 825 | fn 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 | ||
| 833 | fn 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 | ||
| 844 | fn 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 | ||
| 871 | fn 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 | ||
| 881 | fn 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 | ||
| 942 | fn 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 | ||
| 992 | fn 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 | ||
| 1074 | fn 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 | ||
| 1084 | fn 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 | ||
| 1124 | fn 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 | ||
| 1142 | fn 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 | ||
| 1152 | fn 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 | ||
| 1162 | fn 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 | ||
| 1177 | fn 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 | ||
| 1196 | fn 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 | ||
| 1349 | fn 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 | ||
| 1360 | fn 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 | ||
| 1372 | fn 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 | ||
| 1384 | fn 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 | ||
| 1397 | fn 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 | ||
| 1412 | fn 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 | ||
| 1433 | fn 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 | ||
| 1451 | fn 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 | ||
| 1477 | fn 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 | ||
| 1510 | fn 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 | ||
| 1589 | fn 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 | ||
| 1602 | fn 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. | |
| 1616 | fn 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. | |
| 1659 | fn 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 | |
| 1697 | fn 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. | |
| 1731 | fn 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 | |
| 1775 | fn 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 | |
| 1799 | fn 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 | ||
| 1825 | fn 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 | ||
| 1839 | fn 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 | ||
| 1858 | fn 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 | ||
| 1884 | fn 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 | ||
| 1934 | fn 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 | ||
| 1942 | fn 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 | ||
| 1952 | fn 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 | ||
| 1964 | fn 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 | ||
| 1981 | fn 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 | ||
| 1996 | fn 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 | ||
| 2009 | fn 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 | ||
| 2024 | fn 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 | ||
| 2037 | fn 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 | ||
| 2080 | fn 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 | ||
| 2095 | fn 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 | ||
| 2138 | fn 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 | ||
| 2150 | fn 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 | ||
| 2165 | fn 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 | ||
| 2175 | fn 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 | ||
| 2188 | fn 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 | ||
| 2201 | fn 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 | ||
| 2215 | fn 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 | ||
| 2231 | fn 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 | ||
| 2249 | fn 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 | ||
| 2266 | fn 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 | ||
| 2299 | fn 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 | ||
| 2332 | fn 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 | ||
| 2899 | fn 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 | ||
| 2922 | fn 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 | ||
| 2937 | fn 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 | ||
| 2950 | fn 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 | ||
| 2980 | fn 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 | ||
| 3001 | const ValueSrcMap = std.HashMap(Value, AstGen.SwitchProngSrc, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage); | |
| 3002 | ||
| 3003 | fn 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 | ||
| 3016 | fn 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 | ||
| 3049 | fn 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 | ||
| 3073 | fn 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 | ||
| 3079 | fn 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 | ||
| 3085 | fn 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 | ||
| 3151 | fn 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 | ||
| 3157 | fn 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 | ||
| 3163 | fn 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 | ||
| 3169 | fn 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 | ||
| 3188 | fn 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 | ||
| 3204 | fn 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 | ||
| 3306 | fn 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 | ||
| 3317 | fn 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 | ||
| 3382 | fn 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 | ||
| 3451 | fn 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 | ||
| 3458 | fn 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 | ||
| 3466 | fn 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 | ||
| 3486 | fn 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 | ||
| 3503 | fn 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 | ||
| 3534 | fn 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 | ||
| 3602 | fn 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 | ||
| 3617 | fn 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 | ||
| 3633 | fn 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 | ||
| 3642 | fn 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 | ||
| 3653 | fn 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 | ||
| 3697 | fn 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 | ||
| 3714 | fn 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 | ||
| 3730 | fn 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 | ||
| 3741 | fn 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 | ||
| 3771 | fn 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 | ||
| 3779 | fn 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 | ||
| 3800 | fn 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 | ||
| 3854 | fn 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 | ||
| 3860 | fn 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 | ||
| 3867 | fn 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 | ||
| 3873 | pub const PanicId = enum { | |
| 3874 | unreach, | |
| 3875 | unwrap_null, | |
| 3876 | unwrap_errunion, | |
| 3877 | invalid_error_code, | |
| 3878 | }; | |
| 3879 | ||
| 3880 | fn 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 | ||
| 3938 | fn 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 | ||
| 3945 | fn 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 | ||
| 3953 | fn 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 | ||
| 4063 | fn 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 | ||
| 4102 | fn 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 | ||
| 4236 | const InMemoryCoercionResult = enum { | |
| 4237 | ok, | |
| 4238 | no_match, | |
| 4239 | }; | |
| 4240 | ||
| 4241 | fn 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 | ||
| 4250 | fn 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 | ||
| 4288 | fn 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 | ||
| 4297 | fn 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 | ||
| 4319 | fn 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 | ||
| 4329 | fn 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 | ||
| 4337 | fn 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 | ||
| 4345 | fn 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 | ||
| 4350 | fn 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 | ||
| 4371 | fn 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 | ||
| 4396 | fn 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 | ||
| 4415 | fn 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 | ||
| 4437 | fn 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 | ||
| 4454 | fn 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 | ||
| 4466 | fn 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 | ||
| 4540 | fn 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. | |
| 4594 | fn 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 | ||
| 4767 | fn 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 | ||
| 4776 | fn 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 | ||
| 4842 | fn 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const Allocator = std.mem.Allocator; | |
| 4 | const assert = std.debug.assert; | |
| 5 | ||
| 6 | const Value = @import("value.zig").Value; | |
| 7 | const Type = @import("type.zig").Type; | |
| 8 | const TypedValue = @import("TypedValue.zig"); | |
| 9 | const zir = @import("zir.zig"); | |
| 10 | const Module = @import("Module.zig"); | |
| 11 | const ast = std.zig.ast; | |
| 12 | const trace = @import("tracy.zig").trace; | |
| 13 | const Scope = Module.Scope; | |
| 14 | const InnerError = Module.InnerError; | |
| 15 | const BuiltinFn = @import("BuiltinFn.zig"); | |
| 16 | ||
| 17 | pub 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 | ||
| 60 | pub 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 | ||
| 73 | fn 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. | |
| 267 | pub 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 | ||
| 667 | pub 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 | ||
| 703 | fn 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 | ||
| 781 | fn 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 | ||
| 836 | pub 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 | ||
| 861 | fn 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 | ||
| 909 | fn 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 | ||
| 1006 | fn 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 | ||
| 1055 | fn 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 | ||
| 1259 | fn 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 | ||
| 1279 | fn 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 | ||
| 1299 | fn 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 | ||
| 1314 | fn 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 | ||
| 1325 | fn 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 | ||
| 1345 | fn 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 | ||
| 1394 | fn 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 | ||
| 1421 | fn 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 | ||
| 1449 | fn 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 | ||
| 1490 | fn 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 | ||
| 1648 | fn 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 | ||
| 1695 | fn 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 | ||
| 1803 | fn 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. | |
| 1883 | fn 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 | ||
| 1889 | pub 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 | ||
| 1912 | fn 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 | ||
| 1941 | fn 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 | ||
| 1989 | fn 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 | ||
| 2008 | fn 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 | ||
| 2097 | fn 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. | |
| 2204 | fn 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 | ||
| 2218 | fn 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 | ||
| 2224 | fn 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 | ||
| 2380 | fn 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 | ||
| 2577 | fn 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 | ||
| 2592 | fn 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 | ||
| 2885 | fn 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 | ||
| 2926 | fn 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 | ||
| 2949 | fn 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 | ||
| 3053 | fn 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 | ||
| 3074 | fn 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 | ||
| 3091 | fn 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 | ||
| 3140 | fn 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 | ||
| 3163 | fn 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 | ||
| 3202 | fn 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 | ||
| 3229 | fn 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 | ||
| 3273 | fn 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 | ||
| 3307 | fn 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 | ||
| 3351 | fn 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 | ||
| 3399 | fn 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 | ||
| 3420 | fn 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 | ||
| 3621 | fn 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 | ||
| 3660 | fn 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 | ||
| 3710 | fn 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 | ||
| 3721 | fn 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 | ||
| 3743 | fn 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 | ||
| 3751 | pub 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 | ||
| 3779 | fn 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. | |
| 3997 | fn 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. | |
| 4031 | fn 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 | ||
| 4048 | fn 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. | |
| 4087 | fn 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 | ||
| 4102 | fn 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 | ||
| 4133 | pub 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 | ||
| 4155 | pub 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 | ||
| 4178 | pub 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 | ||
| 4201 | pub 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 | ||
| 4217 | pub 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 | ||
| 4222 | pub 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 | ||
| 4246 | pub 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 | ||
| 4272 | pub 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 | ||
| 4296 | pub 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. | |
| 4309 | pub 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. | |
| 4315 | pub 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; |
| 17 | 17 | const leb128 = std.leb; |
| 18 | 18 | const log = std.log.scoped(.codegen); |
| 19 | 19 | const build_options = @import("build_options"); |
| 20 | const LazySrcLoc = Module.LazySrcLoc; | |
| 20 | 21 | |
| 21 | 22 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. |
| 22 | 23 | pub const BlockData = struct { |
| ... | ... | @@ -498,7 +499,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 498 | 499 | defer function.stack.deinit(bin_file.allocator); |
| 499 | 500 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 500 | 501 | |
| 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) { | |
| 502 | 503 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 503 | 504 | else => |e| return e, |
| 504 | 505 | }; |
| ... | ... | @@ -791,8 +792,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 791 | 792 | } |
| 792 | 793 | } |
| 793 | 794 | |
| 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; | |
| 796 | 797 | self.prev_di_pc = self.code.items.len; |
| 797 | 798 | switch (self.debug_output) { |
| 798 | 799 | .dwarf => |dbg_out| { |
| ... | ... | @@ -800,7 +801,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 800 | 801 | // lookup table, and changing ir.Inst from storing byte offset to token. Currently |
| 801 | 802 | // this involves scanning over the source code for newlines |
| 802 | 803 | // (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); | |
| 804 | 805 | const delta_pc = self.code.items.len - self.prev_di_pc; |
| 805 | 806 | // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit |
| 806 | 807 | // 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 { |
| 897 | 898 | .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?), |
| 898 | 899 | .is_err => return self.genIsErr(inst.castTag(.is_err).?), |
| 899 | 900 | .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).?), | |
| 900 | 903 | .load => return self.genLoad(inst.castTag(.load).?), |
| 901 | 904 | .loop => return self.genLoop(inst.castTag(.loop).?), |
| 902 | 905 | .not => return self.genNot(inst.castTag(.not).?), |
| ... | ... | @@ -978,7 +981,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 978 | 981 | /// Copies a value to a register without tracking the register. The register is not considered |
| 979 | 982 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 980 | 983 | /// 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 { | |
| 982 | 985 | const reg = self.findUnusedReg() orelse b: { |
| 983 | 986 | // We'll take over the first register. Move the instruction that was previously |
| 984 | 987 | // there to a stack allocation. |
| ... | ... | @@ -1457,7 +1460,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1457 | 1460 | |
| 1458 | 1461 | fn genArmBinOpCode( |
| 1459 | 1462 | self: *Self, |
| 1460 | src: usize, | |
| 1463 | src: LazySrcLoc, | |
| 1461 | 1464 | dst_reg: Register, |
| 1462 | 1465 | lhs_mcv: MCValue, |
| 1463 | 1466 | rhs_mcv: MCValue, |
| ... | ... | @@ -1620,7 +1623,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1620 | 1623 | |
| 1621 | 1624 | fn genX8664BinMathCode( |
| 1622 | 1625 | self: *Self, |
| 1623 | src: usize, | |
| 1626 | src: LazySrcLoc, | |
| 1624 | 1627 | dst_ty: Type, |
| 1625 | 1628 | dst_mcv: MCValue, |
| 1626 | 1629 | src_mcv: MCValue, |
| ... | ... | @@ -1706,7 +1709,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1706 | 1709 | } |
| 1707 | 1710 | } |
| 1708 | 1711 | |
| 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 { | |
| 1710 | 1713 | const abi_size = ty.abiSize(self.target.*); |
| 1711 | 1714 | const adj_off = off + abi_size; |
| 1712 | 1715 | try self.code.ensureCapacity(self.code.items.len + 7); |
| ... | ... | @@ -1807,7 +1810,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1807 | 1810 | return result; |
| 1808 | 1811 | } |
| 1809 | 1812 | |
| 1810 | fn genBreakpoint(self: *Self, src: usize) !MCValue { | |
| 1813 | fn genBreakpoint(self: *Self, src: LazySrcLoc) !MCValue { | |
| 1811 | 1814 | switch (arch) { |
| 1812 | 1815 | .i386, .x86_64 => { |
| 1813 | 1816 | try self.code.append(0xcc); // int3 |
| ... | ... | @@ -2234,7 +2237,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2234 | 2237 | } |
| 2235 | 2238 | } |
| 2236 | 2239 | |
| 2237 | fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue { | |
| 2240 | fn ret(self: *Self, src: LazySrcLoc, mcv: MCValue) !MCValue { | |
| 2238 | 2241 | const ret_ty = self.fn_type.fnReturnType(); |
| 2239 | 2242 | try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv); |
| 2240 | 2243 | switch (arch) { |
| ... | ... | @@ -2324,8 +2327,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2324 | 2327 | } |
| 2325 | 2328 | } |
| 2326 | 2329 | |
| 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); | |
| 2329 | 2336 | assert(inst.base.isUnused()); |
| 2330 | 2337 | return MCValue.dead; |
| 2331 | 2338 | } |
| ... | ... | @@ -2562,6 +2569,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2562 | 2569 | return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{}); |
| 2563 | 2570 | } |
| 2564 | 2571 | |
| 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 | ||
| 2565 | 2580 | fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue { |
| 2566 | 2581 | // A loop is a setup to be able to jump back to the beginning. |
| 2567 | 2582 | const start_index = self.code.items.len; |
| ... | ... | @@ -2571,7 +2586,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2571 | 2586 | } |
| 2572 | 2587 | |
| 2573 | 2588 | /// 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 { | |
| 2575 | 2590 | switch (arch) { |
| 2576 | 2591 | .i386, .x86_64 => { |
| 2577 | 2592 | try self.code.ensureCapacity(self.code.items.len + 5); |
| ... | ... | @@ -2628,7 +2643,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2628 | 2643 | } |
| 2629 | 2644 | } |
| 2630 | 2645 | |
| 2631 | fn performReloc(self: *Self, src: usize, reloc: Reloc) !void { | |
| 2646 | fn performReloc(self: *Self, src: LazySrcLoc, reloc: Reloc) !void { | |
| 2632 | 2647 | switch (reloc) { |
| 2633 | 2648 | .rel32 => |pos| { |
| 2634 | 2649 | const amt = self.code.items.len - (pos + 4); |
| ... | ... | @@ -2692,7 +2707,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2692 | 2707 | } |
| 2693 | 2708 | } |
| 2694 | 2709 | |
| 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 { | |
| 2696 | 2711 | if (operand.ty.hasCodeGenBits()) { |
| 2697 | 2712 | const operand_mcv = try self.resolveInst(operand); |
| 2698 | 2713 | const block_mcv = @bitCast(MCValue, block.codegen.mcv); |
| ... | ... | @@ -2705,7 +2720,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2705 | 2720 | return self.brVoid(src, block); |
| 2706 | 2721 | } |
| 2707 | 2722 | |
| 2708 | fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue { | |
| 2723 | fn brVoid(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block) !MCValue { | |
| 2709 | 2724 | // Emit a jump with a relocation. It will be patched up after the block ends. |
| 2710 | 2725 | try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1); |
| 2711 | 2726 | |
| ... | ... | @@ -2767,7 +2782,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2767 | 2782 | return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{}); |
| 2768 | 2783 | } |
| 2769 | 2784 | |
| 2770 | if (inst.output) |output| { | |
| 2785 | if (inst.output_name) |output| { | |
| 2771 | 2786 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2772 | 2787 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2773 | 2788 | } |
| ... | ... | @@ -2799,7 +2814,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2799 | 2814 | return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{}); |
| 2800 | 2815 | } |
| 2801 | 2816 | |
| 2802 | if (inst.output) |output| { | |
| 2817 | if (inst.output_name) |output| { | |
| 2803 | 2818 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2804 | 2819 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2805 | 2820 | } |
| ... | ... | @@ -2829,7 +2844,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2829 | 2844 | return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{}); |
| 2830 | 2845 | } |
| 2831 | 2846 | |
| 2832 | if (inst.output) |output| { | |
| 2847 | if (inst.output_name) |output| { | |
| 2833 | 2848 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2834 | 2849 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2835 | 2850 | } |
| ... | ... | @@ -2859,7 +2874,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2859 | 2874 | return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); |
| 2860 | 2875 | } |
| 2861 | 2876 | |
| 2862 | if (inst.output) |output| { | |
| 2877 | if (inst.output_name) |output| { | |
| 2863 | 2878 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2864 | 2879 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2865 | 2880 | } |
| ... | ... | @@ -2909,7 +2924,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2909 | 2924 | } |
| 2910 | 2925 | |
| 2911 | 2926 | /// 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 { | |
| 2913 | 2928 | switch (loc) { |
| 2914 | 2929 | .none => return, |
| 2915 | 2930 | .register => |reg| return self.genSetReg(src, ty, reg, val), |
| ... | ... | @@ -2921,7 +2936,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2921 | 2936 | } |
| 2922 | 2937 | } |
| 2923 | 2938 | |
| 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 { | |
| 2925 | 2940 | switch (arch) { |
| 2926 | 2941 | .arm, .armeb => switch (mcv) { |
| 2927 | 2942 | .dead => unreachable, |
| ... | ... | @@ -3160,7 +3175,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3160 | 3175 | } |
| 3161 | 3176 | } |
| 3162 | 3177 | |
| 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 { | |
| 3164 | 3179 | switch (arch) { |
| 3165 | 3180 | .arm, .armeb => switch (mcv) { |
| 3166 | 3181 | .dead => unreachable, |
| ... | ... | @@ -3703,7 +3718,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3703 | 3718 | return mcv; |
| 3704 | 3719 | } |
| 3705 | 3720 | |
| 3706 | fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) InnerError!MCValue { | |
| 3721 | fn genTypedValue(self: *Self, src: LazySrcLoc, typed_value: TypedValue) InnerError!MCValue { | |
| 3707 | 3722 | if (typed_value.val.isUndef()) |
| 3708 | 3723 | return MCValue{ .undef = {} }; |
| 3709 | 3724 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| ... | ... | @@ -3778,7 +3793,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3778 | 3793 | }; |
| 3779 | 3794 | |
| 3780 | 3795 | /// 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 { | |
| 3782 | 3797 | const cc = fn_ty.fnCallingConvention(); |
| 3783 | 3798 | const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); |
| 3784 | 3799 | defer self.gpa.free(param_types); |
| ... | ... | @@ -3992,13 +4007,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3992 | 4007 | }; |
| 3993 | 4008 | } |
| 3994 | 4009 | |
| 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 { | |
| 3996 | 4011 | @setCold(true); |
| 3997 | 4012 | 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); | |
| 4002 | 4018 | return error.CodegenFail; |
| 4003 | 4019 | } |
| 4004 | 4020 |
src/codegen/c.zig+27-17| ... | ... | @@ -14,6 +14,7 @@ const TypedValue = @import("../TypedValue.zig"); |
| 14 | 14 | const C = link.File.C; |
| 15 | 15 | const Decl = Module.Decl; |
| 16 | 16 | const trace = @import("../tracy.zig").trace; |
| 17 | const LazySrcLoc = Module.LazySrcLoc; | |
| 17 | 18 | |
| 18 | 19 | const Mutability = enum { Const, Mut }; |
| 19 | 20 | |
| ... | ... | @@ -145,11 +146,10 @@ pub const DeclGen = struct { |
| 145 | 146 | error_msg: ?*Module.ErrorMsg, |
| 146 | 147 | typedefs: TypedefMap, |
| 147 | 148 | |
| 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); | |
| 153 | 153 | return error.AnalysisFail; |
| 154 | 154 | } |
| 155 | 155 | |
| ... | ... | @@ -160,7 +160,7 @@ pub const DeclGen = struct { |
| 160 | 160 | val: Value, |
| 161 | 161 | ) error{ OutOfMemory, AnalysisFail }!void { |
| 162 | 162 | 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?)", .{}); | |
| 164 | 164 | } |
| 165 | 165 | switch (t.zigTypeTag()) { |
| 166 | 166 | .Int => { |
| ... | ... | @@ -193,7 +193,7 @@ pub const DeclGen = struct { |
| 193 | 193 | try writer.print("{s}", .{decl.name}); |
| 194 | 194 | }, |
| 195 | 195 | else => |e| return dg.fail( |
| 196 | dg.decl.src(), | |
| 196 | .{ .node_offset = 0 }, | |
| 197 | 197 | "TODO: C backend: implement Pointer value {s}", |
| 198 | 198 | .{@tagName(e)}, |
| 199 | 199 | ), |
| ... | ... | @@ -276,7 +276,7 @@ pub const DeclGen = struct { |
| 276 | 276 | try writer.writeAll(", .error = 0 }"); |
| 277 | 277 | } |
| 278 | 278 | }, |
| 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}", .{ | |
| 280 | 280 | @tagName(e), |
| 281 | 281 | }), |
| 282 | 282 | } |
| ... | ... | @@ -350,7 +350,7 @@ pub const DeclGen = struct { |
| 350 | 350 | break; |
| 351 | 351 | } |
| 352 | 352 | } 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", .{}); | |
| 354 | 354 | } |
| 355 | 355 | }, |
| 356 | 356 | else => unreachable, |
| ... | ... | @@ -358,7 +358,7 @@ pub const DeclGen = struct { |
| 358 | 358 | }, |
| 359 | 359 | .Pointer => { |
| 360 | 360 | 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", .{}); | |
| 362 | 362 | } else { |
| 363 | 363 | try dg.renderType(w, t.elemType()); |
| 364 | 364 | try w.writeAll(" *"); |
| ... | ... | @@ -431,7 +431,7 @@ pub const DeclGen = struct { |
| 431 | 431 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 432 | 432 | }, |
| 433 | 433 | .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}", .{ | |
| 435 | 435 | @tagName(e), |
| 436 | 436 | }), |
| 437 | 437 | } |
| ... | ... | @@ -569,13 +569,15 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 569 | 569 | .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?), |
| 570 | 570 | .is_err => try genIsErr(o, inst.castTag(.is_err).?), |
| 571 | 571 | .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).?), | |
| 572 | 574 | .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), |
| 573 | 575 | .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), |
| 574 | 576 | .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?), |
| 575 | 577 | .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?), |
| 576 | 578 | .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?), |
| 577 | 579 | .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}), | |
| 579 | 581 | }; |
| 580 | 582 | switch (result_value) { |
| 581 | 583 | .none => {}, |
| ... | ... | @@ -756,11 +758,11 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue { |
| 756 | 758 | try writer.writeAll(");\n"); |
| 757 | 759 | return result_local; |
| 758 | 760 | } 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", .{}); | |
| 760 | 762 | } |
| 761 | 763 | } |
| 762 | 764 | |
| 763 | fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue { | |
| 765 | fn genDbgStmt(o: *Object, inst: *Inst.DbgStmt) !CValue { | |
| 764 | 766 | // TODO emit #line directive here with line number and filename |
| 765 | 767 | return CValue.none; |
| 766 | 768 | } |
| ... | ... | @@ -913,13 +915,13 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { |
| 913 | 915 | try o.writeCValue(writer, arg_c_value); |
| 914 | 916 | try writer.writeAll(";\n"); |
| 915 | 917 | } 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", .{}); | |
| 917 | 919 | } |
| 918 | 920 | } |
| 919 | 921 | const volatile_string: []const u8 = if (as.is_volatile) "volatile " else ""; |
| 920 | 922 | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source }); |
| 921 | 923 | 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", .{}); | |
| 923 | 925 | } |
| 924 | 926 | if (as.inputs.len > 0) { |
| 925 | 927 | if (as.output == null) { |
| ... | ... | @@ -945,7 +947,7 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { |
| 945 | 947 | if (as.base.isUnused()) |
| 946 | 948 | return CValue.none; |
| 947 | 949 | |
| 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", .{}); | |
| 949 | 951 | } |
| 950 | 952 | |
| 951 | 953 | fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue { |
| ... | ... | @@ -1072,6 +1074,14 @@ fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1072 | 1074 | return local; |
| 1073 | 1075 | } |
| 1074 | 1076 | |
| 1077 | fn genIntToError(o: *Object, inst: *Inst.UnOp) !CValue { | |
| 1078 | return o.resolveInst(inst.operand); | |
| 1079 | } | |
| 1080 | ||
| 1081 | fn genErrorToInt(o: *Object, inst: *Inst.UnOp) !CValue { | |
| 1082 | return o.resolveInst(inst.operand); | |
| 1083 | } | |
| 1084 | ||
| 1075 | 1085 | fn IndentWriter(comptime UnderlyingWriter: type) type { |
| 1076 | 1086 | return struct { |
| 1077 | 1087 | const Self = @This(); |
src/codegen/llvm.zig+400-361| ... | ... | @@ -15,6 +15,8 @@ const Inst = ir.Inst; |
| 15 | 15 | const Value = @import("../value.zig").Value; |
| 16 | 16 | const Type = @import("../type.zig").Type; |
| 17 | 17 | |
| 18 | const LazySrcLoc = Module.LazySrcLoc; | |
| 19 | ||
| 18 | 20 | pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 19 | 21 | const llvm_arch = switch (target.cpu.arch) { |
| 20 | 22 | .arm => "arm", |
| ... | ... | @@ -143,79 +145,42 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 143 | 145 | return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi }); |
| 144 | 146 | } |
| 145 | 147 | |
| 146 | pub const LLVMIRModule = struct { | |
| 147 | module: *Module, | |
| 148 | pub const Object = struct { | |
| 148 | 149 | llvm_module: *const llvm.Module, |
| 149 | 150 | context: *const llvm.Context, |
| 150 | 151 | 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, | |
| 175 | 153 | |
| 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); | |
| 192 | 156 | errdefer allocator.destroy(self); |
| 193 | 157 | |
| 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, .{ | |
| 197 | 159 | .root_name = options.root_name, |
| 198 | 160 | .target = options.target, |
| 199 | 161 | .output_mode = .Obj, |
| 200 | 162 | }); |
| 201 | defer gpa.free(obj_basename); | |
| 163 | defer allocator.free(obj_basename); | |
| 202 | 164 | |
| 203 | 165 | 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); | |
| 206 | 171 | |
| 207 | 172 | const context = llvm.Context.create(); |
| 208 | 173 | errdefer context.dispose(); |
| 209 | 174 | |
| 210 | 175 | initializeLLVMTargets(); |
| 211 | 176 | |
| 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); | |
| 214 | 179 | const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context); |
| 215 | 180 | errdefer llvm_module.dispose(); |
| 216 | 181 | |
| 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); | |
| 219 | 184 | |
| 220 | 185 | var error_message: [*:0]const u8 = undefined; |
| 221 | 186 | var target: *const llvm.Target = undefined; |
| ... | ... | @@ -250,34 +215,21 @@ pub const LLVMIRModule = struct { |
| 250 | 215 | ); |
| 251 | 216 | errdefer target_machine.dispose(); |
| 252 | 217 | |
| 253 | const builder = context.createBuilder(); | |
| 254 | errdefer builder.dispose(); | |
| 255 | ||
| 256 | 218 | self.* = .{ |
| 257 | .module = options.module.?, | |
| 258 | 219 | .llvm_module = llvm_module, |
| 259 | 220 | .context = context, |
| 260 | 221 | .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, | |
| 266 | 223 | }; |
| 267 | 224 | return self; |
| 268 | 225 | } |
| 269 | 226 | |
| 270 | pub fn deinit(self: *LLVMIRModule, allocator: *Allocator) void { | |
| 271 | self.builder.dispose(); | |
| 227 | pub fn deinit(self: *Object, allocator: *Allocator) void { | |
| 272 | 228 | self.target_machine.dispose(); |
| 273 | 229 | self.llvm_module.dispose(); |
| 274 | 230 | self.context.dispose(); |
| 275 | 231 | |
| 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); | |
| 281 | 233 | allocator.destroy(self); |
| 282 | 234 | } |
| 283 | 235 | |
| ... | ... | @@ -289,7 +241,7 @@ pub const LLVMIRModule = struct { |
| 289 | 241 | llvm.initializeAllAsmParsers(); |
| 290 | 242 | } |
| 291 | 243 | |
| 292 | pub fn flushModule(self: *LLVMIRModule, comp: *Compilation) !void { | |
| 244 | pub fn flushModule(self: *Object, comp: *Compilation) !void { | |
| 293 | 245 | if (comp.verbose_llvm_ir) { |
| 294 | 246 | const dump = self.llvm_module.printToString(); |
| 295 | 247 | defer llvm.disposeMessage(dump); |
| ... | ... | @@ -310,13 +262,10 @@ pub const LLVMIRModule = struct { |
| 310 | 262 | } |
| 311 | 263 | } |
| 312 | 264 | |
| 313 | const object_pathZ = try self.gpa.dupeZ(u8, self.object_path); | |
| 314 | defer self.gpa.free(object_pathZ); | |
| 315 | ||
| 316 | 265 | var error_message: [*:0]const u8 = undefined; |
| 317 | 266 | if (self.target_machine.emitToFile( |
| 318 | 267 | self.llvm_module, |
| 319 | object_pathZ.ptr, | |
| 268 | self.object_pathZ.ptr, | |
| 320 | 269 | .ObjectFile, |
| 321 | 270 | &error_message, |
| 322 | 271 | ).toBool()) { |
| ... | ... | @@ -328,44 +277,68 @@ pub const LLVMIRModule = struct { |
| 328 | 277 | } |
| 329 | 278 | } |
| 330 | 279 | |
| 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) { | |
| 333 | 289 | error.CodegenFail => { |
| 334 | 290 | 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; | |
| 337 | 293 | return; |
| 338 | 294 | }, |
| 339 | 295 | else => |e| return e, |
| 340 | 296 | }; |
| 341 | 297 | } |
| 298 | }; | |
| 342 | 299 | |
| 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(); | |
| 300 | pub const DeclGen = struct { | |
| 301 | object: *Object, | |
| 302 | module: *Module, | |
| 303 | decl: *Module.Decl, | |
| 304 | err_msg: ?*Module.ErrorMsg, | |
| 346 | 305 | |
| 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; | |
| 348 | 327 | |
| 349 | 328 | log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val }); |
| 350 | 329 | |
| 351 | 330 | if (typed_value.val.castTag(.function)) |func_payload| { |
| 352 | 331 | const func = func_payload.data; |
| 353 | 332 | |
| 354 | const llvm_func = try self.resolveLLVMFunction(func.owner_decl, src); | |
| 333 | const llvm_func = try self.resolveLLVMFunction(func.owner_decl); | |
| 355 | 334 | |
| 356 | 335 | // This gets the LLVM values from the function and stores them in `self.args`. |
| 357 | 336 | const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen(); |
| 358 | 337 | var args = try self.gpa.alloc(*const llvm.Value, fn_param_len); |
| 359 | defer self.gpa.free(args); | |
| 360 | 338 | |
| 361 | 339 | for (args) |*arg, i| { |
| 362 | 340 | arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); |
| 363 | 341 | } |
| 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(); | |
| 369 | 342 | |
| 370 | 343 | // We remove all the basic blocks of a function to support incremental |
| 371 | 344 | // compilation! |
| ... | ... | @@ -374,20 +347,293 @@ pub const LLVMIRModule = struct { |
| 374 | 347 | bb.deleteBasicBlock(); |
| 375 | 348 | } |
| 376 | 349 | |
| 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(); | |
| 381 | 367 | |
| 382 | try self.genBody(func.body); | |
| 368 | try fg.genBody(func.body); | |
| 383 | 369 | } 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); | |
| 385 | 371 | } 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 | ||
| 571 | pub 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); | |
| 387 | 630 | } |
| 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)", .{}); | |
| 388 | 634 | } |
| 389 | 635 | |
| 390 | fn genBody(self: *LLVMIRModule, body: ir.Body) error{ OutOfMemory, CodegenFail }!void { | |
| 636 | fn genBody(self: *FuncGen, body: ir.Body) error{ OutOfMemory, CodegenFail }!void { | |
| 391 | 637 | for (body.instructions) |inst| { |
| 392 | 638 | const opt_value = switch (inst.tag) { |
| 393 | 639 | .add => try self.genAdd(inst.castTag(.add).?), |
| ... | ... | @@ -425,13 +671,13 @@ pub const LLVMIRModule = struct { |
| 425 | 671 | // TODO: implement debug info |
| 426 | 672 | break :blk null; |
| 427 | 673 | }, |
| 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}), | |
| 429 | 675 | }; |
| 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); | |
| 431 | 677 | } |
| 432 | 678 | } |
| 433 | 679 | |
| 434 | fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value { | |
| 680 | fn genCall(self: *FuncGen, inst: *Inst.Call) !?*const llvm.Value { | |
| 435 | 681 | if (inst.func.value()) |func_value| { |
| 436 | 682 | const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| |
| 437 | 683 | extern_fn.data |
| ... | ... | @@ -441,12 +687,12 @@ pub const LLVMIRModule = struct { |
| 441 | 687 | unreachable; |
| 442 | 688 | |
| 443 | 689 | 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); | |
| 445 | 691 | |
| 446 | 692 | const num_args = inst.args.len; |
| 447 | 693 | |
| 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); | |
| 450 | 696 | |
| 451 | 697 | for (inst.args) |arg, i| { |
| 452 | 698 | llvm_param_vals[i] = try self.resolveInst(arg); |
| ... | ... | @@ -471,27 +717,32 @@ pub const LLVMIRModule = struct { |
| 471 | 717 | |
| 472 | 718 | return call; |
| 473 | 719 | } 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", .{}); | |
| 475 | 721 | } |
| 476 | 722 | } |
| 477 | 723 | |
| 478 | fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value { | |
| 724 | fn genRetVoid(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value { | |
| 479 | 725 | _ = self.builder.buildRetVoid(); |
| 480 | 726 | return null; |
| 481 | 727 | } |
| 482 | 728 | |
| 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 | } | |
| 484 | 735 | _ = self.builder.buildRet(try self.resolveInst(inst.operand)); |
| 485 | 736 | return null; |
| 486 | 737 | } |
| 487 | 738 | |
| 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 { | |
| 489 | 740 | const lhs = try self.resolveInst(inst.lhs); |
| 490 | 741 | const rhs = try self.resolveInst(inst.rhs); |
| 491 | 742 | |
| 492 | 743 | if (!inst.base.ty.isInt()) |
| 493 | 744 | 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}); | |
| 495 | 746 | |
| 496 | 747 | const is_signed = inst.base.ty.isSignedInt(); |
| 497 | 748 | const operation = switch (op) { |
| ... | ... | @@ -506,21 +757,21 @@ pub const LLVMIRModule = struct { |
| 506 | 757 | return self.builder.buildICmp(operation, lhs, rhs, ""); |
| 507 | 758 | } |
| 508 | 759 | |
| 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"); | |
| 511 | 762 | |
| 512 | 763 | // 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, .{ | |
| 516 | 767 | .parent_bb = parent_bb, |
| 517 | 768 | .break_bbs = &break_bbs, |
| 518 | 769 | .break_vals = &break_vals, |
| 519 | 770 | }); |
| 520 | 771 | defer { |
| 521 | 772 | 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()); | |
| 524 | 775 | } |
| 525 | 776 | |
| 526 | 777 | try self.genBody(inst.body); |
| ... | ... | @@ -531,7 +782,7 @@ pub const LLVMIRModule = struct { |
| 531 | 782 | // If the block does not return a value, we dont have to create a phi node. |
| 532 | 783 | if (!inst.base.ty.hasCodeGenBits()) return null; |
| 533 | 784 | |
| 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), ""); | |
| 535 | 786 | phi_node.addIncoming( |
| 536 | 787 | break_vals.items.ptr, |
| 537 | 788 | break_bbs.items.ptr, |
| ... | ... | @@ -540,7 +791,7 @@ pub const LLVMIRModule = struct { |
| 540 | 791 | return phi_node; |
| 541 | 792 | } |
| 542 | 793 | |
| 543 | fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value { | |
| 794 | fn genBr(self: *FuncGen, inst: *Inst.Br) !?*const llvm.Value { | |
| 544 | 795 | var block = self.blocks.get(inst.block).?; |
| 545 | 796 | |
| 546 | 797 | // If the break doesn't break a value, then we don't have to add |
| ... | ... | @@ -553,25 +804,25 @@ pub const LLVMIRModule = struct { |
| 553 | 804 | |
| 554 | 805 | // For the phi node, we need the basic blocks and the values of the |
| 555 | 806 | // 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); | |
| 558 | 809 | |
| 559 | 810 | _ = self.builder.buildBr(block.parent_bb); |
| 560 | 811 | } |
| 561 | 812 | return null; |
| 562 | 813 | } |
| 563 | 814 | |
| 564 | fn genBrVoid(self: *LLVMIRModule, inst: *Inst.BrVoid) !?*const llvm.Value { | |
| 815 | fn genBrVoid(self: *FuncGen, inst: *Inst.BrVoid) !?*const llvm.Value { | |
| 565 | 816 | var block = self.blocks.get(inst.block).?; |
| 566 | 817 | _ = self.builder.buildBr(block.parent_bb); |
| 567 | 818 | return null; |
| 568 | 819 | } |
| 569 | 820 | |
| 570 | fn genCondBr(self: *LLVMIRModule, inst: *Inst.CondBr) !?*const llvm.Value { | |
| 821 | fn genCondBr(self: *FuncGen, inst: *Inst.CondBr) !?*const llvm.Value { | |
| 571 | 822 | const condition_value = try self.resolveInst(inst.condition); |
| 572 | 823 | |
| 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"); | |
| 575 | 826 | { |
| 576 | 827 | const prev_block = self.builder.getInsertBlock(); |
| 577 | 828 | defer self.builder.positionBuilderAtEnd(prev_block); |
| ... | ... | @@ -586,8 +837,8 @@ pub const LLVMIRModule = struct { |
| 586 | 837 | return null; |
| 587 | 838 | } |
| 588 | 839 | |
| 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"); | |
| 591 | 842 | _ = self.builder.buildBr(loop_block); |
| 592 | 843 | |
| 593 | 844 | self.builder.positionBuilderAtEnd(loop_block); |
| ... | ... | @@ -597,20 +848,20 @@ pub const LLVMIRModule = struct { |
| 597 | 848 | return null; |
| 598 | 849 | } |
| 599 | 850 | |
| 600 | fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 851 | fn genNot(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 601 | 852 | return self.builder.buildNot(try self.resolveInst(inst.operand), ""); |
| 602 | 853 | } |
| 603 | 854 | |
| 604 | fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value { | |
| 855 | fn genUnreach(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value { | |
| 605 | 856 | _ = self.builder.buildUnreachable(); |
| 606 | 857 | return null; |
| 607 | 858 | } |
| 608 | 859 | |
| 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 { | |
| 610 | 861 | const operand = try self.resolveInst(inst.operand); |
| 611 | 862 | |
| 612 | 863 | if (operand_is_ptr) { |
| 613 | const index_type = self.context.intType(32); | |
| 864 | const index_type = self.context().intType(32); | |
| 614 | 865 | |
| 615 | 866 | var indices: [2]*const llvm.Value = .{ |
| 616 | 867 | index_type.constNull(), |
| ... | ... | @@ -623,15 +874,15 @@ pub const LLVMIRModule = struct { |
| 623 | 874 | } |
| 624 | 875 | } |
| 625 | 876 | |
| 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 { | |
| 627 | 878 | return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, ""); |
| 628 | 879 | } |
| 629 | 880 | |
| 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 { | |
| 631 | 882 | const operand = try self.resolveInst(inst.operand); |
| 632 | 883 | |
| 633 | 884 | if (operand_is_ptr) { |
| 634 | const index_type = self.context.intType(32); | |
| 885 | const index_type = self.context().intType(32); | |
| 635 | 886 | |
| 636 | 887 | var indices: [2]*const llvm.Value = .{ |
| 637 | 888 | index_type.constNull(), |
| ... | ... | @@ -644,12 +895,12 @@ pub const LLVMIRModule = struct { |
| 644 | 895 | } |
| 645 | 896 | } |
| 646 | 897 | |
| 647 | fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { | |
| 898 | fn genAdd(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { | |
| 648 | 899 | const lhs = try self.resolveInst(inst.lhs); |
| 649 | 900 | const rhs = try self.resolveInst(inst.rhs); |
| 650 | 901 | |
| 651 | 902 | 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}); | |
| 653 | 904 | |
| 654 | 905 | return if (inst.base.ty.isSignedInt()) |
| 655 | 906 | self.builder.buildNSWAdd(lhs, rhs, "") |
| ... | ... | @@ -657,12 +908,12 @@ pub const LLVMIRModule = struct { |
| 657 | 908 | self.builder.buildNUWAdd(lhs, rhs, ""); |
| 658 | 909 | } |
| 659 | 910 | |
| 660 | fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { | |
| 911 | fn genSub(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { | |
| 661 | 912 | const lhs = try self.resolveInst(inst.lhs); |
| 662 | 913 | const rhs = try self.resolveInst(inst.rhs); |
| 663 | 914 | |
| 664 | 915 | 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}); | |
| 666 | 917 | |
| 667 | 918 | return if (inst.base.ty.isSignedInt()) |
| 668 | 919 | self.builder.buildNSWSub(lhs, rhs, "") |
| ... | ... | @@ -670,44 +921,44 @@ pub const LLVMIRModule = struct { |
| 670 | 921 | self.builder.buildNUWSub(lhs, rhs, ""); |
| 671 | 922 | } |
| 672 | 923 | |
| 673 | fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 924 | fn genIntCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 674 | 925 | const val = try self.resolveInst(inst.operand); |
| 675 | 926 | |
| 676 | 927 | const signed = inst.base.ty.isSignedInt(); |
| 677 | 928 | // TODO: Should we use intcast here or just a simple bitcast? |
| 678 | 929 | // 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), ""); | |
| 680 | 931 | } |
| 681 | 932 | |
| 682 | fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 933 | fn genBitCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 683 | 934 | 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); | |
| 685 | 936 | |
| 686 | 937 | return self.builder.buildBitCast(val, dest_type, ""); |
| 687 | 938 | } |
| 688 | 939 | |
| 689 | fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.Value { | |
| 940 | fn genArg(self: *FuncGen, inst: *Inst.Arg) !?*const llvm.Value { | |
| 690 | 941 | const arg_val = self.args[self.arg_index]; |
| 691 | 942 | self.arg_index += 1; |
| 692 | 943 | |
| 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)); | |
| 694 | 945 | _ = self.builder.buildStore(arg_val, ptr_val); |
| 695 | 946 | return self.builder.buildLoad(ptr_val, ""); |
| 696 | 947 | } |
| 697 | 948 | |
| 698 | fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value { | |
| 949 | fn genAlloc(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value { | |
| 699 | 950 | // buildAlloca expects the pointee type, not the pointer type, so assert that |
| 700 | 951 | // a Payload.PointerSimple is passed to the alloc instruction. |
| 701 | 952 | const pointee_type = inst.base.ty.castPointer().?.data; |
| 702 | 953 | |
| 703 | 954 | // TODO: figure out a way to get the name of the var decl. |
| 704 | 955 | // 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)); | |
| 706 | 957 | } |
| 707 | 958 | |
| 708 | 959 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| 709 | 960 | /// 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 { | |
| 711 | 962 | const prev_block = self.builder.getInsertBlock(); |
| 712 | 963 | defer self.builder.positionBuilderAtEnd(prev_block); |
| 713 | 964 | |
| ... | ... | @@ -729,242 +980,30 @@ pub const LLVMIRModule = struct { |
| 729 | 980 | return val; |
| 730 | 981 | } |
| 731 | 982 | |
| 732 | fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { | |
| 983 | fn genStore(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { | |
| 733 | 984 | const val = try self.resolveInst(inst.rhs); |
| 734 | 985 | const ptr = try self.resolveInst(inst.lhs); |
| 735 | 986 | _ = self.builder.buildStore(val, ptr); |
| 736 | 987 | return null; |
| 737 | 988 | } |
| 738 | 989 | |
| 739 | fn genLoad(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 990 | fn genLoad(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 740 | 991 | const ptr_val = try self.resolveInst(inst.operand); |
| 741 | 992 | return self.builder.buildLoad(ptr_val, ""); |
| 742 | 993 | } |
| 743 | 994 | |
| 744 | fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value { | |
| 995 | fn genBreakpoint(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value { | |
| 745 | 996 | const llvn_fn = self.getIntrinsic("llvm.debugtrap"); |
| 746 | 997 | _ = self.builder.buildCall(llvn_fn, null, 0, ""); |
| 747 | 998 | return null; |
| 748 | 999 | } |
| 749 | 1000 | |
| 750 | fn getIntrinsic(self: *LLVMIRModule, name: []const u8) *const llvm.Value { | |
| 1001 | fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value { | |
| 751 | 1002 | const id = llvm.lookupIntrinsicID(name.ptr, name.len); |
| 752 | 1003 | assert(id != 0); |
| 753 | 1004 | // TODO: add support for overload intrinsics by passing the prefix of the intrinsic |
| 754 | 1005 | // to `lookupIntrinsicID` and then passing the correct types to |
| 755 | 1006 | // `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); | |
| 969 | 1008 | } |
| 970 | 1009 | }; |
src/codegen/wasm.zig+9-10| ... | ... | @@ -14,6 +14,7 @@ const Type = @import("../type.zig").Type; |
| 14 | 14 | const Value = @import("../value.zig").Value; |
| 15 | 15 | const Compilation = @import("../Compilation.zig"); |
| 16 | 16 | const AnyMCValue = @import("../codegen.zig").AnyMCValue; |
| 17 | const LazySrcLoc = Module.LazySrcLoc; | |
| 17 | 18 | |
| 18 | 19 | /// Wasm Value, created when generating an instruction |
| 19 | 20 | const WValue = union(enum) { |
| ... | ... | @@ -70,11 +71,9 @@ pub const Context = struct { |
| 70 | 71 | } |
| 71 | 72 | |
| 72 | 73 | /// 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); | |
| 78 | 77 | return error.CodegenFail; |
| 79 | 78 | } |
| 80 | 79 | |
| ... | ... | @@ -91,7 +90,7 @@ pub const Context = struct { |
| 91 | 90 | } |
| 92 | 91 | |
| 93 | 92 | /// 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 { | |
| 95 | 94 | return switch (ty.tag()) { |
| 96 | 95 | .f32 => wasm.valtype(.f32), |
| 97 | 96 | .f64 => wasm.valtype(.f64), |
| ... | ... | @@ -104,7 +103,7 @@ pub const Context = struct { |
| 104 | 103 | /// Using a given `Type`, returns the corresponding wasm value type |
| 105 | 104 | /// Differently from `genValtype` this also allows `void` to create a block |
| 106 | 105 | /// 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 { | |
| 108 | 107 | return switch (ty.tag()) { |
| 109 | 108 | .void, .noreturn => wasm.block_empty, |
| 110 | 109 | else => self.genValtype(src, ty), |
| ... | ... | @@ -139,7 +138,7 @@ pub const Context = struct { |
| 139 | 138 | ty.fnParamTypes(params); |
| 140 | 139 | for (params) |param_type| { |
| 141 | 140 | // 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); | |
| 143 | 142 | try writer.writeByte(val_type); |
| 144 | 143 | } |
| 145 | 144 | } |
| ... | ... | @@ -151,7 +150,7 @@ pub const Context = struct { |
| 151 | 150 | else => |ret_type| { |
| 152 | 151 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 153 | 152 | // 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); | |
| 155 | 154 | try writer.writeByte(val_type); |
| 156 | 155 | }, |
| 157 | 156 | } |
| ... | ... | @@ -168,7 +167,7 @@ pub const Context = struct { |
| 168 | 167 | const mod_fn = blk: { |
| 169 | 168 | if (tv.val.castTag(.function)) |func| break :blk func.data; |
| 170 | 169 | 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()}); | |
| 172 | 171 | }; |
| 173 | 172 | |
| 174 | 173 | // 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 { |
| 25 | 25 | /// lifetimes of operands are encoded elsewhere. |
| 26 | 26 | deaths: DeathsInt = undefined, |
| 27 | 27 | ty: Type, |
| 28 | /// Byte offset into the source. | |
| 29 | src: usize, | |
| 28 | src: Module.LazySrcLoc, | |
| 30 | 29 | |
| 31 | 30 | pub const DeathsInt = u16; |
| 32 | 31 | pub const DeathsBitIndex = std.math.Log2Int(DeathsInt); |
| ... | ... | @@ -81,22 +80,28 @@ pub const Inst = struct { |
| 81 | 80 | condbr, |
| 82 | 81 | constant, |
| 83 | 82 | dbg_stmt, |
| 84 | // ?T => bool | |
| 83 | /// ?T => bool | |
| 85 | 84 | is_null, |
| 86 | // ?T => bool (inverted logic) | |
| 85 | /// ?T => bool (inverted logic) | |
| 87 | 86 | is_non_null, |
| 88 | // *?T => bool | |
| 87 | /// *?T => bool | |
| 89 | 88 | is_null_ptr, |
| 90 | // *?T => bool (inverted logic) | |
| 89 | /// *?T => bool (inverted logic) | |
| 91 | 90 | is_non_null_ptr, |
| 92 | // E!T => bool | |
| 91 | /// E!T => bool | |
| 93 | 92 | is_err, |
| 94 | // *E!T => bool | |
| 93 | /// *E!T => bool | |
| 95 | 94 | is_err_ptr, |
| 95 | /// E => u16 | |
| 96 | error_to_int, | |
| 97 | /// u16 => E | |
| 98 | int_to_error, | |
| 96 | 99 | bool_and, |
| 97 | 100 | bool_or, |
| 98 | 101 | /// Read a value from a pointer. |
| 99 | 102 | 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. | |
| 100 | 105 | loop, |
| 101 | 106 | ptrtoint, |
| 102 | 107 | ref, |
| ... | ... | @@ -113,9 +118,9 @@ pub const Inst = struct { |
| 113 | 118 | not, |
| 114 | 119 | floatcast, |
| 115 | 120 | intcast, |
| 116 | // ?T => T | |
| 121 | /// ?T => T | |
| 117 | 122 | optional_payload, |
| 118 | // *?T => *T | |
| 123 | /// *?T => *T | |
| 119 | 124 | optional_payload_ptr, |
| 120 | 125 | wrap_optional, |
| 121 | 126 | /// E!T -> T |
| ... | ... | @@ -139,7 +144,6 @@ pub const Inst = struct { |
| 139 | 144 | .retvoid, |
| 140 | 145 | .unreach, |
| 141 | 146 | .breakpoint, |
| 142 | .dbg_stmt, | |
| 143 | 147 | => NoOp, |
| 144 | 148 | |
| 145 | 149 | .ref, |
| ... | ... | @@ -152,6 +156,8 @@ pub const Inst = struct { |
| 152 | 156 | .is_null_ptr, |
| 153 | 157 | .is_err, |
| 154 | 158 | .is_err_ptr, |
| 159 | .int_to_error, | |
| 160 | .error_to_int, | |
| 155 | 161 | .ptrtoint, |
| 156 | 162 | .floatcast, |
| 157 | 163 | .intcast, |
| ... | ... | @@ -199,6 +205,7 @@ pub const Inst = struct { |
| 199 | 205 | .loop => Loop, |
| 200 | 206 | .varptr => VarPtr, |
| 201 | 207 | .switchbr => SwitchBr, |
| 208 | .dbg_stmt => DbgStmt, | |
| 202 | 209 | }; |
| 203 | 210 | } |
| 204 | 211 | |
| ... | ... | @@ -360,7 +367,8 @@ pub const Inst = struct { |
| 360 | 367 | base: Inst, |
| 361 | 368 | asm_source: []const u8, |
| 362 | 369 | is_volatile: bool, |
| 363 | output: ?[]const u8, | |
| 370 | output: ?*Inst, | |
| 371 | output_name: ?[]const u8, | |
| 364 | 372 | inputs: []const []const u8, |
| 365 | 373 | clobbers: []const []const u8, |
| 366 | 374 | args: []const *Inst, |
| ... | ... | @@ -584,8 +592,512 @@ pub const Inst = struct { |
| 584 | 592 | return (self.deaths + self.else_index)[0..self.else_deaths]; |
| 585 | 593 | } |
| 586 | 594 | }; |
| 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 | }; | |
| 587 | 609 | }; |
| 588 | 610 | |
| 589 | 611 | pub const Body = struct { |
| 590 | 612 | instructions: []*Inst, |
| 591 | 613 | }; |
| 614 | ||
| 615 | /// For debugging purposes, prints a function representation to stderr. | |
| 616 | pub 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 | ||
| 646 | const 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 { |
| 185 | 185 | if (module.global_error_set.size == 0) break :render_errors; |
| 186 | 186 | var it = module.global_error_set.iterator(); |
| 187 | 187 | 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 }); | |
| 190 | 189 | } |
| 191 | 190 | try err_typedef_writer.writeByte('\n'); |
| 192 | 191 | } |
src/link/Coff.zig+10-10| ... | ... | @@ -34,7 +34,7 @@ pub const base_tag: link.File.Tag = .coff; |
| 34 | 34 | const msdos_stub = @embedFile("msdos-stub.bin"); |
| 35 | 35 | |
| 36 | 36 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 37 | llvm_ir_module: ?*llvm_backend.LLVMIRModule = null, | |
| 37 | llvm_object: ?*llvm_backend.Object = null, | |
| 38 | 38 | |
| 39 | 39 | base: link.File, |
| 40 | 40 | ptr_width: PtrWidth, |
| ... | ... | @@ -129,7 +129,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 129 | 129 | const self = try createEmpty(allocator, options); |
| 130 | 130 | errdefer self.base.destroy(); |
| 131 | 131 | |
| 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); | |
| 133 | 133 | return self; |
| 134 | 134 | } |
| 135 | 135 | |
| ... | ... | @@ -413,7 +413,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff { |
| 413 | 413 | } |
| 414 | 414 | |
| 415 | 415 | pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { |
| 416 | if (self.llvm_ir_module) |_| return; | |
| 416 | if (self.llvm_object) |_| return; | |
| 417 | 417 | |
| 418 | 418 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 419 | 419 | |
| ... | ... | @@ -660,7 +660,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { |
| 660 | 660 | defer tracy.end(); |
| 661 | 661 | |
| 662 | 662 | 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); | |
| 664 | 664 | |
| 665 | 665 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 666 | 666 | if (typed_value.val.tag() == .extern_fn) { |
| ... | ... | @@ -720,15 +720,15 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { |
| 720 | 720 | } |
| 721 | 721 | |
| 722 | 722 | pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { |
| 723 | if (self.llvm_ir_module) |_| return; | |
| 723 | if (self.llvm_object) |_| return; | |
| 724 | 724 | |
| 725 | 725 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 726 | 726 | self.freeTextBlock(&decl.link.coff); |
| 727 | 727 | self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {}; |
| 728 | 728 | } |
| 729 | 729 | |
| 730 | pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void { | |
| 731 | if (self.llvm_ir_module) |_| return; | |
| 730 | pub fn updateDeclExports(self: *Coff, module: *Module, decl: *Module.Decl, exports: []const *Module.Export) !void { | |
| 731 | if (self.llvm_object) |_| return; | |
| 732 | 732 | |
| 733 | 733 | for (exports) |exp| { |
| 734 | 734 | if (exp.options.section) |section_name| { |
| ... | ... | @@ -771,7 +771,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void { |
| 771 | 771 | defer tracy.end(); |
| 772 | 772 | |
| 773 | 773 | 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); | |
| 775 | 775 | |
| 776 | 776 | if (self.text_section_size_dirty) { |
| 777 | 777 | // Write the new raw size in the .text header |
| ... | ... | @@ -1308,7 +1308,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1308 | 1308 | } |
| 1309 | 1309 | |
| 1310 | 1310 | pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { |
| 1311 | assert(self.llvm_ir_module == null); | |
| 1311 | assert(self.llvm_object == null); | |
| 1312 | 1312 | return self.text_section_virtual_address + decl.link.coff.text_offset; |
| 1313 | 1313 | } |
| 1314 | 1314 | |
| ... | ... | @@ -1318,7 +1318,7 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v |
| 1318 | 1318 | |
| 1319 | 1319 | pub fn deinit(self: *Coff) void { |
| 1320 | 1320 | 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); | |
| 1322 | 1322 | |
| 1323 | 1323 | self.text_block_free_list.deinit(self.base.allocator); |
| 1324 | 1324 | self.offset_table.deinit(self.base.allocator); |
src/link/Elf.zig+13-13| ... | ... | @@ -35,7 +35,7 @@ base: File, |
| 35 | 35 | ptr_width: PtrWidth, |
| 36 | 36 | |
| 37 | 37 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 38 | llvm_ir_module: ?*llvm_backend.LLVMIRModule = null, | |
| 38 | llvm_object: ?*llvm_backend.Object = null, | |
| 39 | 39 | |
| 40 | 40 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 41 | 41 | /// Same order as in the file. |
| ... | ... | @@ -232,7 +232,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 232 | 232 | const self = try createEmpty(allocator, options); |
| 233 | 233 | errdefer self.base.destroy(); |
| 234 | 234 | |
| 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); | |
| 236 | 236 | return self; |
| 237 | 237 | } |
| 238 | 238 | |
| ... | ... | @@ -299,7 +299,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { |
| 299 | 299 | |
| 300 | 300 | pub fn deinit(self: *Elf) void { |
| 301 | 301 | if (build_options.have_llvm) |
| 302 | if (self.llvm_ir_module) |ir_module| | |
| 302 | if (self.llvm_object) |ir_module| | |
| 303 | 303 | ir_module.deinit(self.base.allocator); |
| 304 | 304 | |
| 305 | 305 | self.sections.deinit(self.base.allocator); |
| ... | ... | @@ -318,7 +318,7 @@ pub fn deinit(self: *Elf) void { |
| 318 | 318 | } |
| 319 | 319 | |
| 320 | 320 | pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 { |
| 321 | assert(self.llvm_ir_module == null); | |
| 321 | assert(self.llvm_object == null); | |
| 322 | 322 | assert(decl.link.elf.local_sym_index != 0); |
| 323 | 323 | return self.local_symbols.items[decl.link.elf.local_sym_index].st_value; |
| 324 | 324 | } |
| ... | ... | @@ -438,7 +438,7 @@ fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 { |
| 438 | 438 | } |
| 439 | 439 | |
| 440 | 440 | pub fn populateMissingMetadata(self: *Elf) !void { |
| 441 | assert(self.llvm_ir_module == null); | |
| 441 | assert(self.llvm_object == null); | |
| 442 | 442 | |
| 443 | 443 | const small_ptr = switch (self.ptr_width) { |
| 444 | 444 | .p32 => true, |
| ... | ... | @@ -745,7 +745,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 745 | 745 | defer tracy.end(); |
| 746 | 746 | |
| 747 | 747 | 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); | |
| 749 | 749 | |
| 750 | 750 | // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the |
| 751 | 751 | // Zig source code. |
| ... | ... | @@ -2111,7 +2111,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al |
| 2111 | 2111 | } |
| 2112 | 2112 | |
| 2113 | 2113 | pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { |
| 2114 | if (self.llvm_ir_module) |_| return; | |
| 2114 | if (self.llvm_object) |_| return; | |
| 2115 | 2115 | |
| 2116 | 2116 | if (decl.link.elf.local_sym_index != 0) return; |
| 2117 | 2117 | |
| ... | ... | @@ -2149,7 +2149,7 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { |
| 2149 | 2149 | } |
| 2150 | 2150 | |
| 2151 | 2151 | pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { |
| 2152 | if (self.llvm_ir_module) |_| return; | |
| 2152 | if (self.llvm_object) |_| return; | |
| 2153 | 2153 | |
| 2154 | 2154 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 2155 | 2155 | self.freeTextBlock(&decl.link.elf); |
| ... | ... | @@ -2189,7 +2189,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { |
| 2189 | 2189 | defer tracy.end(); |
| 2190 | 2190 | |
| 2191 | 2191 | 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); | |
| 2193 | 2193 | |
| 2194 | 2194 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 2195 | 2195 | if (typed_value.val.tag() == .extern_fn) { |
| ... | ... | @@ -2670,10 +2670,10 @@ fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const |
| 2670 | 2670 | pub fn updateDeclExports( |
| 2671 | 2671 | self: *Elf, |
| 2672 | 2672 | module: *Module, |
| 2673 | decl: *const Module.Decl, | |
| 2673 | decl: *Module.Decl, | |
| 2674 | 2674 | exports: []const *Module.Export, |
| 2675 | 2675 | ) !void { |
| 2676 | if (self.llvm_ir_module) |_| return; | |
| 2676 | if (self.llvm_object) |_| return; | |
| 2677 | 2677 | |
| 2678 | 2678 | const tracy = trace(@src()); |
| 2679 | 2679 | defer tracy.end(); |
| ... | ... | @@ -2748,7 +2748,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec |
| 2748 | 2748 | const tracy = trace(@src()); |
| 2749 | 2749 | defer tracy.end(); |
| 2750 | 2750 | |
| 2751 | if (self.llvm_ir_module) |_| return; | |
| 2751 | if (self.llvm_object) |_| return; | |
| 2752 | 2752 | |
| 2753 | 2753 | const tree = decl.container.file_scope.tree; |
| 2754 | 2754 | const node_tags = tree.nodes.items(.tag); |
| ... | ... | @@ -2773,7 +2773,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec |
| 2773 | 2773 | } |
| 2774 | 2774 | |
| 2775 | 2775 | pub fn deleteExport(self: *Elf, exp: Export) void { |
| 2776 | if (self.llvm_ir_module) |_| return; | |
| 2776 | if (self.llvm_object) |_| return; | |
| 2777 | 2777 | |
| 2778 | 2778 | const sym_index = exp.sym_index orelse return; |
| 2779 | 2779 | 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 |
| 1340 | 1340 | pub fn updateDeclExports( |
| 1341 | 1341 | self: *MachO, |
| 1342 | 1342 | module: *Module, |
| 1343 | decl: *const Module.Decl, | |
| 1343 | decl: *Module.Decl, | |
| 1344 | 1344 | exports: []const *Module.Export, |
| 1345 | 1345 | ) !void { |
| 1346 | 1346 | const tracy = trace(@src()); |
src/main.zig+15-22| ... | ... | @@ -1487,7 +1487,7 @@ fn buildOutputType( |
| 1487 | 1487 | for (diags.arch.?.allCpuModels()) |cpu| { |
| 1488 | 1488 | help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help; |
| 1489 | 1489 | } |
| 1490 | std.log.info("Available CPUs for architecture '{s}': {s}", .{ | |
| 1490 | std.log.info("Available CPUs for architecture '{s}':\n{s}", .{ | |
| 1491 | 1491 | @tagName(diags.arch.?), help_text.items, |
| 1492 | 1492 | }); |
| 1493 | 1493 | } |
| ... | ... | @@ -1499,7 +1499,7 @@ fn buildOutputType( |
| 1499 | 1499 | for (diags.arch.?.allFeaturesList()) |feature| { |
| 1500 | 1500 | help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help; |
| 1501 | 1501 | } |
| 1502 | std.log.info("Available CPU features for architecture '{s}': {s}", .{ | |
| 1502 | std.log.info("Available CPU features for architecture '{s}':\n{s}", .{ | |
| 1503 | 1503 | @tagName(diags.arch.?), help_text.items, |
| 1504 | 1504 | }); |
| 1505 | 1505 | } |
| ... | ... | @@ -1750,15 +1750,12 @@ fn buildOutputType( |
| 1750 | 1750 | } |
| 1751 | 1751 | |
| 1752 | 1752 | 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 | }; | |
| 1762 | 1759 | defer zig_lib_directory.handle.close(); |
| 1763 | 1760 | |
| 1764 | 1761 | var thread_pool: ThreadPool = undefined; |
| ... | ... | @@ -2461,15 +2458,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v |
| 2461 | 2458 | } |
| 2462 | 2459 | } |
| 2463 | 2460 | |
| 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 | }; | |
| 2473 | 2467 | defer zig_lib_directory.handle.close(); |
| 2474 | 2468 | |
| 2475 | 2469 | const std_special = "std" ++ fs.path.sep_str ++ "special"; |
| ... | ... | @@ -3281,8 +3275,7 @@ pub const ClangArgIterator = struct { |
| 3281 | 3275 | self.zig_equivalent = clang_arg.zig_equivalent; |
| 3282 | 3276 | break :find_clang_arg; |
| 3283 | 3277 | }, |
| 3284 | } | |
| 3285 | else { | |
| 3278 | } else { | |
| 3286 | 3279 | fatal("Unknown Clang option: '{s}'", .{arg}); |
| 3287 | 3280 | } |
| 3288 | 3281 | } |
src/translate_c.zig+1-1| ... | ... | @@ -4343,7 +4343,7 @@ fn isZigPrimitiveType(name: []const u8) bool { |
| 4343 | 4343 | } |
| 4344 | 4344 | return true; |
| 4345 | 4345 | } |
| 4346 | return @import("astgen.zig").simple_types.has(name); | |
| 4346 | return @import("AstGen.zig").simple_types.has(name); | |
| 4347 | 4347 | } |
| 4348 | 4348 | |
| 4349 | 4349 | const MacroCtx = struct { |
src/type.zig+203-199| ... | ... | @@ -92,11 +92,7 @@ pub const Type = extern union { |
| 92 | 92 | |
| 93 | 93 | .anyerror_void_error_union, .error_union => return .ErrorUnion, |
| 94 | 94 | |
| 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, | |
| 100 | 96 | |
| 101 | 97 | .var_args_param => unreachable, // can be any type |
| 102 | 98 | } |
| ... | ... | @@ -173,6 +169,125 @@ pub const Type = extern union { |
| 173 | 169 | }; |
| 174 | 170 | } |
| 175 | 171 | |
| 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 | ||
| 176 | 291 | pub fn eql(a: Type, b: Type) bool { |
| 177 | 292 | // As a shortcut, if the small tags / addresses match, we're done. |
| 178 | 293 | if (a.tag_if_small_enough == b.tag_if_small_enough) |
| ... | ... | @@ -195,25 +310,38 @@ pub const Type = extern union { |
| 195 | 310 | return a.elemType().eql(b.elemType()); |
| 196 | 311 | }, |
| 197 | 312 | .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)) | |
| 207 | 316 | 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) | |
| 212 | 328 | return false; |
| 213 | 329 | |
| 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; | |
| 217 | 345 | }, |
| 218 | 346 | .Int => { |
| 219 | 347 | // Detect that e.g. u64 != usize, even if the bits match on a particular target. |
| ... | ... | @@ -399,7 +527,6 @@ pub const Type = extern union { |
| 399 | 527 | .const_slice_u8, |
| 400 | 528 | .enum_literal, |
| 401 | 529 | .anyerror_void_error_union, |
| 402 | .@"anyframe", | |
| 403 | 530 | .inferred_alloc_const, |
| 404 | 531 | .inferred_alloc_mut, |
| 405 | 532 | .var_args_param, |
| ... | ... | @@ -420,7 +547,6 @@ pub const Type = extern union { |
| 420 | 547 | .optional, |
| 421 | 548 | .optional_single_mut_pointer, |
| 422 | 549 | .optional_single_const_pointer, |
| 423 | .anyframe_T, | |
| 424 | 550 | => return self.copyPayloadShallow(allocator, Payload.ElemType), |
| 425 | 551 | |
| 426 | 552 | .int_signed, |
| ... | ... | @@ -480,13 +606,10 @@ pub const Type = extern union { |
| 480 | 606 | .payload = try payload.payload.copy(allocator), |
| 481 | 607 | }); |
| 482 | 608 | }, |
| 483 | .error_set => return self.copyPayloadShallow(allocator, Payload.Decl), | |
| 609 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), | |
| 484 | 610 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), |
| 485 | 611 | .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), |
| 486 | 612 | |
| 487 | .@"enum" => return self.copyPayloadShallow(allocator, Payload.Enum), | |
| 488 | .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), | |
| 489 | .@"union" => return self.copyPayloadShallow(allocator, Payload.Union), | |
| 490 | 613 | .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque), |
| 491 | 614 | } |
| 492 | 615 | } |
| ... | ... | @@ -551,7 +674,6 @@ pub const Type = extern union { |
| 551 | 674 | |
| 552 | 675 | // TODO this should print the structs name |
| 553 | 676 | .empty_struct => return out_stream.writeAll("struct {}"), |
| 554 | .@"anyframe" => return out_stream.writeAll("anyframe"), | |
| 555 | 677 | .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"), |
| 556 | 678 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 557 | 679 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), |
| ... | ... | @@ -579,12 +701,6 @@ pub const Type = extern union { |
| 579 | 701 | continue; |
| 580 | 702 | }, |
| 581 | 703 | |
| 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 | }, | |
| 588 | 704 | .array_u8 => { |
| 589 | 705 | const len = ty.castTag(.array_u8).?.data; |
| 590 | 706 | return out_stream.print("[{d}]u8", .{len}); |
| ... | ... | @@ -715,8 +831,8 @@ pub const Type = extern union { |
| 715 | 831 | continue; |
| 716 | 832 | }, |
| 717 | 833 | .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)); | |
| 720 | 836 | }, |
| 721 | 837 | .error_set_single => { |
| 722 | 838 | const name = ty.castTag(.error_set_single).?.data; |
| ... | ... | @@ -725,9 +841,6 @@ pub const Type = extern union { |
| 725 | 841 | .inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"), |
| 726 | 842 | .inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"), |
| 727 | 843 | // 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 {}"), | |
| 731 | 844 | .@"opaque" => return out_stream.writeAll("opaque {}"), |
| 732 | 845 | } |
| 733 | 846 | unreachable; |
| ... | ... | @@ -822,8 +935,6 @@ pub const Type = extern union { |
| 822 | 935 | .optional, |
| 823 | 936 | .optional_single_mut_pointer, |
| 824 | 937 | .optional_single_const_pointer, |
| 825 | .@"anyframe", | |
| 826 | .anyframe_T, | |
| 827 | 938 | .anyerror_void_error_union, |
| 828 | 939 | .error_set, |
| 829 | 940 | .error_set_single, |
| ... | ... | @@ -839,10 +950,6 @@ pub const Type = extern union { |
| 839 | 950 | return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits(); |
| 840 | 951 | }, |
| 841 | 952 | |
| 842 | .@"enum" => @panic("TODO"), | |
| 843 | .@"struct" => @panic("TODO"), | |
| 844 | .@"union" => @panic("TODO"), | |
| 845 | ||
| 846 | 953 | .c_void, |
| 847 | 954 | .void, |
| 848 | 955 | .type, |
| ... | ... | @@ -863,7 +970,39 @@ pub const Type = extern union { |
| 863 | 970 | } |
| 864 | 971 | |
| 865 | 972 | 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 | } | |
| 867 | 1006 | } |
| 868 | 1007 | |
| 869 | 1008 | /// Asserts that hasCodeGenBits() is true. |
| ... | ... | @@ -907,17 +1046,9 @@ pub const Type = extern union { |
| 907 | 1046 | .mut_slice, |
| 908 | 1047 | .optional_single_const_pointer, |
| 909 | 1048 | .optional_single_mut_pointer, |
| 910 | .@"anyframe", | |
| 911 | .anyframe_T, | |
| 1049 | .pointer, | |
| 912 | 1050 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 913 | 1051 | |
| 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 | ||
| 921 | 1052 | .c_short => return @divExact(CType.short.sizeInBits(target), 8), |
| 922 | 1053 | .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8), |
| 923 | 1054 | .c_int => return @divExact(CType.int.sizeInBits(target), 8), |
| ... | ... | @@ -967,10 +1098,6 @@ pub const Type = extern union { |
| 967 | 1098 | @panic("TODO abiAlignment error union"); |
| 968 | 1099 | }, |
| 969 | 1100 | |
| 970 | .@"enum" => self.cast(Payload.Enum).?.abiAlignment(target), | |
| 971 | .@"struct" => @panic("TODO"), | |
| 972 | .@"union" => @panic("TODO"), | |
| 973 | ||
| 974 | 1101 | .c_void, |
| 975 | 1102 | .void, |
| 976 | 1103 | .type, |
| ... | ... | @@ -1038,7 +1165,7 @@ pub const Type = extern union { |
| 1038 | 1165 | .i64, .u64 => return 8, |
| 1039 | 1166 | .u128, .i128 => return 16, |
| 1040 | 1167 | |
| 1041 | .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), | |
| 1168 | .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), | |
| 1042 | 1169 | |
| 1043 | 1170 | .const_slice, |
| 1044 | 1171 | .mut_slice, |
| ... | ... | @@ -1119,10 +1246,6 @@ pub const Type = extern union { |
| 1119 | 1246 | } |
| 1120 | 1247 | @panic("TODO abiSize error union"); |
| 1121 | 1248 | }, |
| 1122 | ||
| 1123 | .@"enum" => @panic("TODO"), | |
| 1124 | .@"struct" => @panic("TODO"), | |
| 1125 | .@"union" => @panic("TODO"), | |
| 1126 | 1249 | }; |
| 1127 | 1250 | } |
| 1128 | 1251 | |
| ... | ... | @@ -1186,15 +1309,10 @@ pub const Type = extern union { |
| 1186 | 1309 | .const_slice, |
| 1187 | 1310 | .mut_slice, |
| 1188 | 1311 | .error_union, |
| 1189 | .@"anyframe", | |
| 1190 | .anyframe_T, | |
| 1191 | 1312 | .anyerror_void_error_union, |
| 1192 | 1313 | .error_set, |
| 1193 | 1314 | .error_set_single, |
| 1194 | 1315 | .empty_struct, |
| 1195 | .@"enum", | |
| 1196 | .@"struct", | |
| 1197 | .@"union", | |
| 1198 | 1316 | .@"opaque", |
| 1199 | 1317 | .var_args_param, |
| 1200 | 1318 | => false, |
| ... | ... | @@ -1264,15 +1382,10 @@ pub const Type = extern union { |
| 1264 | 1382 | .optional_single_const_pointer, |
| 1265 | 1383 | .enum_literal, |
| 1266 | 1384 | .error_union, |
| 1267 | .@"anyframe", | |
| 1268 | .anyframe_T, | |
| 1269 | 1385 | .anyerror_void_error_union, |
| 1270 | 1386 | .error_set, |
| 1271 | 1387 | .error_set_single, |
| 1272 | 1388 | .empty_struct, |
| 1273 | .@"enum", | |
| 1274 | .@"struct", | |
| 1275 | .@"union", | |
| 1276 | 1389 | .@"opaque", |
| 1277 | 1390 | .var_args_param, |
| 1278 | 1391 | => unreachable, |
| ... | ... | @@ -1361,17 +1474,12 @@ pub const Type = extern union { |
| 1361 | 1474 | .optional_single_const_pointer, |
| 1362 | 1475 | .enum_literal, |
| 1363 | 1476 | .error_union, |
| 1364 | .@"anyframe", | |
| 1365 | .anyframe_T, | |
| 1366 | 1477 | .anyerror_void_error_union, |
| 1367 | 1478 | .error_set, |
| 1368 | 1479 | .error_set_single, |
| 1369 | 1480 | .empty_struct, |
| 1370 | 1481 | .inferred_alloc_const, |
| 1371 | 1482 | .inferred_alloc_mut, |
| 1372 | .@"enum", | |
| 1373 | .@"struct", | |
| 1374 | .@"union", | |
| 1375 | 1483 | .@"opaque", |
| 1376 | 1484 | .var_args_param, |
| 1377 | 1485 | => false, |
| ... | ... | @@ -1442,17 +1550,12 @@ pub const Type = extern union { |
| 1442 | 1550 | .enum_literal, |
| 1443 | 1551 | .mut_slice, |
| 1444 | 1552 | .error_union, |
| 1445 | .@"anyframe", | |
| 1446 | .anyframe_T, | |
| 1447 | 1553 | .anyerror_void_error_union, |
| 1448 | 1554 | .error_set, |
| 1449 | 1555 | .error_set_single, |
| 1450 | 1556 | .empty_struct, |
| 1451 | 1557 | .inferred_alloc_const, |
| 1452 | 1558 | .inferred_alloc_mut, |
| 1453 | .@"enum", | |
| 1454 | .@"struct", | |
| 1455 | .@"union", | |
| 1456 | 1559 | .@"opaque", |
| 1457 | 1560 | .var_args_param, |
| 1458 | 1561 | => false, |
| ... | ... | @@ -1532,17 +1635,12 @@ pub const Type = extern union { |
| 1532 | 1635 | .optional_single_const_pointer, |
| 1533 | 1636 | .enum_literal, |
| 1534 | 1637 | .error_union, |
| 1535 | .@"anyframe", | |
| 1536 | .anyframe_T, | |
| 1537 | 1638 | .anyerror_void_error_union, |
| 1538 | 1639 | .error_set, |
| 1539 | 1640 | .error_set_single, |
| 1540 | 1641 | .empty_struct, |
| 1541 | 1642 | .inferred_alloc_const, |
| 1542 | 1643 | .inferred_alloc_mut, |
| 1543 | .@"enum", | |
| 1544 | .@"struct", | |
| 1545 | .@"union", | |
| 1546 | 1644 | .@"opaque", |
| 1547 | 1645 | .var_args_param, |
| 1548 | 1646 | => false, |
| ... | ... | @@ -1617,17 +1715,12 @@ pub const Type = extern union { |
| 1617 | 1715 | .optional_single_const_pointer, |
| 1618 | 1716 | .enum_literal, |
| 1619 | 1717 | .error_union, |
| 1620 | .@"anyframe", | |
| 1621 | .anyframe_T, | |
| 1622 | 1718 | .anyerror_void_error_union, |
| 1623 | 1719 | .error_set, |
| 1624 | 1720 | .error_set_single, |
| 1625 | 1721 | .empty_struct, |
| 1626 | 1722 | .inferred_alloc_const, |
| 1627 | 1723 | .inferred_alloc_mut, |
| 1628 | .@"enum", | |
| 1629 | .@"struct", | |
| 1630 | .@"union", | |
| 1631 | 1724 | .@"opaque", |
| 1632 | 1725 | .var_args_param, |
| 1633 | 1726 | => false, |
| ... | ... | @@ -1744,17 +1837,12 @@ pub const Type = extern union { |
| 1744 | 1837 | .optional_single_mut_pointer => unreachable, |
| 1745 | 1838 | .enum_literal => unreachable, |
| 1746 | 1839 | .error_union => unreachable, |
| 1747 | .@"anyframe" => unreachable, | |
| 1748 | .anyframe_T => unreachable, | |
| 1749 | 1840 | .anyerror_void_error_union => unreachable, |
| 1750 | 1841 | .error_set => unreachable, |
| 1751 | 1842 | .error_set_single => unreachable, |
| 1752 | 1843 | .empty_struct => unreachable, |
| 1753 | 1844 | .inferred_alloc_const => unreachable, |
| 1754 | 1845 | .inferred_alloc_mut => unreachable, |
| 1755 | .@"enum" => unreachable, | |
| 1756 | .@"struct" => unreachable, | |
| 1757 | .@"union" => unreachable, | |
| 1758 | 1846 | .@"opaque" => unreachable, |
| 1759 | 1847 | .var_args_param => unreachable, |
| 1760 | 1848 | |
| ... | ... | @@ -1897,17 +1985,12 @@ pub const Type = extern union { |
| 1897 | 1985 | .optional_single_const_pointer, |
| 1898 | 1986 | .enum_literal, |
| 1899 | 1987 | .error_union, |
| 1900 | .@"anyframe", | |
| 1901 | .anyframe_T, | |
| 1902 | 1988 | .anyerror_void_error_union, |
| 1903 | 1989 | .error_set, |
| 1904 | 1990 | .error_set_single, |
| 1905 | 1991 | .empty_struct, |
| 1906 | 1992 | .inferred_alloc_const, |
| 1907 | 1993 | .inferred_alloc_mut, |
| 1908 | .@"enum", | |
| 1909 | .@"struct", | |
| 1910 | .@"union", | |
| 1911 | 1994 | .@"opaque", |
| 1912 | 1995 | .var_args_param, |
| 1913 | 1996 | => unreachable, |
| ... | ... | @@ -1972,17 +2055,12 @@ pub const Type = extern union { |
| 1972 | 2055 | .optional_single_const_pointer, |
| 1973 | 2056 | .enum_literal, |
| 1974 | 2057 | .error_union, |
| 1975 | .@"anyframe", | |
| 1976 | .anyframe_T, | |
| 1977 | 2058 | .anyerror_void_error_union, |
| 1978 | 2059 | .error_set, |
| 1979 | 2060 | .error_set_single, |
| 1980 | 2061 | .empty_struct, |
| 1981 | 2062 | .inferred_alloc_const, |
| 1982 | 2063 | .inferred_alloc_mut, |
| 1983 | .@"enum", | |
| 1984 | .@"struct", | |
| 1985 | .@"union", | |
| 1986 | 2064 | .@"opaque", |
| 1987 | 2065 | .var_args_param, |
| 1988 | 2066 | => unreachable, |
| ... | ... | @@ -2062,17 +2140,12 @@ pub const Type = extern union { |
| 2062 | 2140 | .optional_single_const_pointer, |
| 2063 | 2141 | .enum_literal, |
| 2064 | 2142 | .error_union, |
| 2065 | .@"anyframe", | |
| 2066 | .anyframe_T, | |
| 2067 | 2143 | .anyerror_void_error_union, |
| 2068 | 2144 | .error_set, |
| 2069 | 2145 | .error_set_single, |
| 2070 | 2146 | .empty_struct, |
| 2071 | 2147 | .inferred_alloc_const, |
| 2072 | 2148 | .inferred_alloc_mut, |
| 2073 | .@"enum", | |
| 2074 | .@"struct", | |
| 2075 | .@"union", | |
| 2076 | 2149 | .@"opaque", |
| 2077 | 2150 | .var_args_param, |
| 2078 | 2151 | => false, |
| ... | ... | @@ -2148,17 +2221,12 @@ pub const Type = extern union { |
| 2148 | 2221 | .optional_single_const_pointer, |
| 2149 | 2222 | .enum_literal, |
| 2150 | 2223 | .error_union, |
| 2151 | .@"anyframe", | |
| 2152 | .anyframe_T, | |
| 2153 | 2224 | .anyerror_void_error_union, |
| 2154 | 2225 | .error_set, |
| 2155 | 2226 | .error_set_single, |
| 2156 | 2227 | .empty_struct, |
| 2157 | 2228 | .inferred_alloc_const, |
| 2158 | 2229 | .inferred_alloc_mut, |
| 2159 | .@"enum", | |
| 2160 | .@"struct", | |
| 2161 | .@"union", | |
| 2162 | 2230 | .@"opaque", |
| 2163 | 2231 | .var_args_param, |
| 2164 | 2232 | => false, |
| ... | ... | @@ -2220,17 +2288,12 @@ pub const Type = extern union { |
| 2220 | 2288 | .optional_single_const_pointer, |
| 2221 | 2289 | .enum_literal, |
| 2222 | 2290 | .error_union, |
| 2223 | .@"anyframe", | |
| 2224 | .anyframe_T, | |
| 2225 | 2291 | .anyerror_void_error_union, |
| 2226 | 2292 | .error_set, |
| 2227 | 2293 | .error_set_single, |
| 2228 | 2294 | .empty_struct, |
| 2229 | 2295 | .inferred_alloc_const, |
| 2230 | 2296 | .inferred_alloc_mut, |
| 2231 | .@"enum", | |
| 2232 | .@"struct", | |
| 2233 | .@"union", | |
| 2234 | 2297 | .@"opaque", |
| 2235 | 2298 | .var_args_param, |
| 2236 | 2299 | => unreachable, |
| ... | ... | @@ -2320,17 +2383,12 @@ pub const Type = extern union { |
| 2320 | 2383 | .optional_single_const_pointer, |
| 2321 | 2384 | .enum_literal, |
| 2322 | 2385 | .error_union, |
| 2323 | .@"anyframe", | |
| 2324 | .anyframe_T, | |
| 2325 | 2386 | .anyerror_void_error_union, |
| 2326 | 2387 | .error_set, |
| 2327 | 2388 | .error_set_single, |
| 2328 | 2389 | .empty_struct, |
| 2329 | 2390 | .inferred_alloc_const, |
| 2330 | 2391 | .inferred_alloc_mut, |
| 2331 | .@"enum", | |
| 2332 | .@"struct", | |
| 2333 | .@"union", | |
| 2334 | 2392 | .@"opaque", |
| 2335 | 2393 | .var_args_param, |
| 2336 | 2394 | => false, |
| ... | ... | @@ -2441,17 +2499,12 @@ pub const Type = extern union { |
| 2441 | 2499 | .optional_single_const_pointer, |
| 2442 | 2500 | .enum_literal, |
| 2443 | 2501 | .error_union, |
| 2444 | .@"anyframe", | |
| 2445 | .anyframe_T, | |
| 2446 | 2502 | .anyerror_void_error_union, |
| 2447 | 2503 | .error_set, |
| 2448 | 2504 | .error_set_single, |
| 2449 | 2505 | .empty_struct, |
| 2450 | 2506 | .inferred_alloc_const, |
| 2451 | 2507 | .inferred_alloc_mut, |
| 2452 | .@"enum", | |
| 2453 | .@"struct", | |
| 2454 | .@"union", | |
| 2455 | 2508 | .@"opaque", |
| 2456 | 2509 | .var_args_param, |
| 2457 | 2510 | => unreachable, |
| ... | ... | @@ -2528,17 +2581,12 @@ pub const Type = extern union { |
| 2528 | 2581 | .optional_single_const_pointer, |
| 2529 | 2582 | .enum_literal, |
| 2530 | 2583 | .error_union, |
| 2531 | .@"anyframe", | |
| 2532 | .anyframe_T, | |
| 2533 | 2584 | .anyerror_void_error_union, |
| 2534 | 2585 | .error_set, |
| 2535 | 2586 | .error_set_single, |
| 2536 | 2587 | .empty_struct, |
| 2537 | 2588 | .inferred_alloc_const, |
| 2538 | 2589 | .inferred_alloc_mut, |
| 2539 | .@"enum", | |
| 2540 | .@"struct", | |
| 2541 | .@"union", | |
| 2542 | 2590 | .@"opaque", |
| 2543 | 2591 | .var_args_param, |
| 2544 | 2592 | => unreachable, |
| ... | ... | @@ -2614,17 +2662,12 @@ pub const Type = extern union { |
| 2614 | 2662 | .optional_single_const_pointer, |
| 2615 | 2663 | .enum_literal, |
| 2616 | 2664 | .error_union, |
| 2617 | .@"anyframe", | |
| 2618 | .anyframe_T, | |
| 2619 | 2665 | .anyerror_void_error_union, |
| 2620 | 2666 | .error_set, |
| 2621 | 2667 | .error_set_single, |
| 2622 | 2668 | .empty_struct, |
| 2623 | 2669 | .inferred_alloc_const, |
| 2624 | 2670 | .inferred_alloc_mut, |
| 2625 | .@"enum", | |
| 2626 | .@"struct", | |
| 2627 | .@"union", | |
| 2628 | 2671 | .@"opaque", |
| 2629 | 2672 | .var_args_param, |
| 2630 | 2673 | => unreachable, |
| ... | ... | @@ -2700,17 +2743,12 @@ pub const Type = extern union { |
| 2700 | 2743 | .optional_single_const_pointer, |
| 2701 | 2744 | .enum_literal, |
| 2702 | 2745 | .error_union, |
| 2703 | .@"anyframe", | |
| 2704 | .anyframe_T, | |
| 2705 | 2746 | .anyerror_void_error_union, |
| 2706 | 2747 | .error_set, |
| 2707 | 2748 | .error_set_single, |
| 2708 | 2749 | .empty_struct, |
| 2709 | 2750 | .inferred_alloc_const, |
| 2710 | 2751 | .inferred_alloc_mut, |
| 2711 | .@"enum", | |
| 2712 | .@"struct", | |
| 2713 | .@"union", | |
| 2714 | 2752 | .@"opaque", |
| 2715 | 2753 | .var_args_param, |
| 2716 | 2754 | => unreachable, |
| ... | ... | @@ -2783,17 +2821,12 @@ pub const Type = extern union { |
| 2783 | 2821 | .optional_single_const_pointer, |
| 2784 | 2822 | .enum_literal, |
| 2785 | 2823 | .error_union, |
| 2786 | .@"anyframe", | |
| 2787 | .anyframe_T, | |
| 2788 | 2824 | .anyerror_void_error_union, |
| 2789 | 2825 | .error_set, |
| 2790 | 2826 | .error_set_single, |
| 2791 | 2827 | .empty_struct, |
| 2792 | 2828 | .inferred_alloc_const, |
| 2793 | 2829 | .inferred_alloc_mut, |
| 2794 | .@"enum", | |
| 2795 | .@"struct", | |
| 2796 | .@"union", | |
| 2797 | 2830 | .@"opaque", |
| 2798 | 2831 | .var_args_param, |
| 2799 | 2832 | => unreachable, |
| ... | ... | @@ -2866,17 +2899,12 @@ pub const Type = extern union { |
| 2866 | 2899 | .optional_single_const_pointer, |
| 2867 | 2900 | .enum_literal, |
| 2868 | 2901 | .error_union, |
| 2869 | .@"anyframe", | |
| 2870 | .anyframe_T, | |
| 2871 | 2902 | .anyerror_void_error_union, |
| 2872 | 2903 | .error_set, |
| 2873 | 2904 | .error_set_single, |
| 2874 | 2905 | .empty_struct, |
| 2875 | 2906 | .inferred_alloc_const, |
| 2876 | 2907 | .inferred_alloc_mut, |
| 2877 | .@"enum", | |
| 2878 | .@"struct", | |
| 2879 | .@"union", | |
| 2880 | 2908 | .@"opaque", |
| 2881 | 2909 | .var_args_param, |
| 2882 | 2910 | => unreachable, |
| ... | ... | @@ -2949,17 +2977,12 @@ pub const Type = extern union { |
| 2949 | 2977 | .optional_single_const_pointer, |
| 2950 | 2978 | .enum_literal, |
| 2951 | 2979 | .error_union, |
| 2952 | .@"anyframe", | |
| 2953 | .anyframe_T, | |
| 2954 | 2980 | .anyerror_void_error_union, |
| 2955 | 2981 | .error_set, |
| 2956 | 2982 | .error_set_single, |
| 2957 | 2983 | .empty_struct, |
| 2958 | 2984 | .inferred_alloc_const, |
| 2959 | 2985 | .inferred_alloc_mut, |
| 2960 | .@"enum", | |
| 2961 | .@"struct", | |
| 2962 | .@"union", | |
| 2963 | 2986 | .@"opaque", |
| 2964 | 2987 | .var_args_param, |
| 2965 | 2988 | => false, |
| ... | ... | @@ -3016,8 +3039,6 @@ pub const Type = extern union { |
| 3016 | 3039 | .optional_single_const_pointer, |
| 3017 | 3040 | .enum_literal, |
| 3018 | 3041 | .anyerror_void_error_union, |
| 3019 | .anyframe_T, | |
| 3020 | .@"anyframe", | |
| 3021 | 3042 | .error_union, |
| 3022 | 3043 | .error_set, |
| 3023 | 3044 | .error_set_single, |
| ... | ... | @@ -3025,10 +3046,6 @@ pub const Type = extern union { |
| 3025 | 3046 | .var_args_param, |
| 3026 | 3047 | => return null, |
| 3027 | 3048 | |
| 3028 | .@"enum" => @panic("TODO onePossibleValue enum"), | |
| 3029 | .@"struct" => @panic("TODO onePossibleValue struct"), | |
| 3030 | .@"union" => @panic("TODO onePossibleValue union"), | |
| 3031 | ||
| 3032 | 3049 | .empty_struct => return Value.initTag(.empty_struct_value), |
| 3033 | 3050 | .void => return Value.initTag(.void_value), |
| 3034 | 3051 | .noreturn => return Value.initTag(.unreachable_value), |
| ... | ... | @@ -3128,17 +3145,12 @@ pub const Type = extern union { |
| 3128 | 3145 | .optional_single_const_pointer, |
| 3129 | 3146 | .enum_literal, |
| 3130 | 3147 | .error_union, |
| 3131 | .@"anyframe", | |
| 3132 | .anyframe_T, | |
| 3133 | 3148 | .anyerror_void_error_union, |
| 3134 | 3149 | .error_set, |
| 3135 | 3150 | .error_set_single, |
| 3136 | 3151 | .empty_struct, |
| 3137 | 3152 | .inferred_alloc_const, |
| 3138 | 3153 | .inferred_alloc_mut, |
| 3139 | .@"enum", | |
| 3140 | .@"struct", | |
| 3141 | .@"union", | |
| 3142 | 3154 | .@"opaque", |
| 3143 | 3155 | .var_args_param, |
| 3144 | 3156 | => return false, |
| ... | ... | @@ -3220,8 +3232,6 @@ pub const Type = extern union { |
| 3220 | 3232 | .optional_single_const_pointer, |
| 3221 | 3233 | .enum_literal, |
| 3222 | 3234 | .error_union, |
| 3223 | .@"anyframe", | |
| 3224 | .anyframe_T, | |
| 3225 | 3235 | .anyerror_void_error_union, |
| 3226 | 3236 | .error_set, |
| 3227 | 3237 | .error_set_single, |
| ... | ... | @@ -3234,10 +3244,7 @@ pub const Type = extern union { |
| 3234 | 3244 | => unreachable, |
| 3235 | 3245 | |
| 3236 | 3246 | .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, | |
| 3241 | 3248 | }; |
| 3242 | 3249 | } |
| 3243 | 3250 | |
| ... | ... | @@ -3296,6 +3303,10 @@ pub const Type = extern union { |
| 3296 | 3303 | } |
| 3297 | 3304 | } |
| 3298 | 3305 | |
| 3306 | pub fn isExhaustiveEnum(ty: Type) bool { | |
| 3307 | return false; // TODO | |
| 3308 | } | |
| 3309 | ||
| 3299 | 3310 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| 3300 | 3311 | /// it has extra enum tags in it, as a way of using less memory. For example, |
| 3301 | 3312 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types |
| ... | ... | @@ -3346,7 +3357,6 @@ pub const Type = extern union { |
| 3346 | 3357 | fn_ccc_void_no_args, |
| 3347 | 3358 | single_const_pointer_to_comptime_int, |
| 3348 | 3359 | anyerror_void_error_union, |
| 3349 | @"anyframe", | |
| 3350 | 3360 | const_slice_u8, |
| 3351 | 3361 | /// This is a special type for variadic parameters of a function call. |
| 3352 | 3362 | /// 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 { |
| 3379 | 3389 | optional_single_mut_pointer, |
| 3380 | 3390 | optional_single_const_pointer, |
| 3381 | 3391 | error_union, |
| 3382 | anyframe_T, | |
| 3383 | 3392 | error_set, |
| 3384 | 3393 | error_set_single, |
| 3385 | 3394 | empty_struct, |
| 3386 | @"enum", | |
| 3387 | @"struct", | |
| 3388 | @"union", | |
| 3389 | 3395 | @"opaque", |
| 3390 | 3396 | |
| 3391 | 3397 | pub const last_no_payload_tag = Tag.inferred_alloc_const; |
| ... | ... | @@ -3435,7 +3441,6 @@ pub const Type = extern union { |
| 3435 | 3441 | .fn_ccc_void_no_args, |
| 3436 | 3442 | .single_const_pointer_to_comptime_int, |
| 3437 | 3443 | .anyerror_void_error_union, |
| 3438 | .@"anyframe", | |
| 3439 | 3444 | .const_slice_u8, |
| 3440 | 3445 | .inferred_alloc_const, |
| 3441 | 3446 | .inferred_alloc_mut, |
| ... | ... | @@ -3457,25 +3462,22 @@ pub const Type = extern union { |
| 3457 | 3462 | .optional, |
| 3458 | 3463 | .optional_single_mut_pointer, |
| 3459 | 3464 | .optional_single_const_pointer, |
| 3460 | .anyframe_T, | |
| 3461 | 3465 | => Payload.ElemType, |
| 3462 | 3466 | |
| 3463 | 3467 | .int_signed, |
| 3464 | 3468 | .int_unsigned, |
| 3465 | 3469 | => Payload.Bits, |
| 3466 | 3470 | |
| 3471 | .error_set => Payload.ErrorSet, | |
| 3472 | ||
| 3467 | 3473 | .array => Payload.Array, |
| 3468 | 3474 | .array_sentinel => Payload.ArraySentinel, |
| 3469 | 3475 | .pointer => Payload.Pointer, |
| 3470 | 3476 | .function => Payload.Function, |
| 3471 | 3477 | .error_union => Payload.ErrorUnion, |
| 3472 | .error_set => Payload.Decl, | |
| 3473 | 3478 | .error_set_single => Payload.Name, |
| 3474 | .empty_struct => Payload.ContainerScope, | |
| 3475 | .@"enum" => Payload.Enum, | |
| 3476 | .@"struct" => Payload.Struct, | |
| 3477 | .@"union" => Payload.Union, | |
| 3478 | 3479 | .@"opaque" => Payload.Opaque, |
| 3480 | .empty_struct => Payload.ContainerScope, | |
| 3479 | 3481 | }; |
| 3480 | 3482 | } |
| 3481 | 3483 | |
| ... | ... | @@ -3550,6 +3552,13 @@ pub const Type = extern union { |
| 3550 | 3552 | }, |
| 3551 | 3553 | }; |
| 3552 | 3554 | |
| 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 | ||
| 3553 | 3562 | pub const Pointer = struct { |
| 3554 | 3563 | pub const base_tag = Tag.pointer; |
| 3555 | 3564 | |
| ... | ... | @@ -3598,13 +3607,8 @@ pub const Type = extern union { |
| 3598 | 3607 | |
| 3599 | 3608 | pub const Opaque = struct { |
| 3600 | 3609 | base: Payload = .{ .tag = .@"opaque" }, |
| 3601 | ||
| 3602 | scope: Module.Scope.Container, | |
| 3610 | data: Module.Scope.Container, | |
| 3603 | 3611 | }; |
| 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"); | |
| 3608 | 3612 | }; |
| 3609 | 3613 | }; |
| 3610 | 3614 |
src/type/Enum.zig deleted-55| ... | ... | @@ -1,55 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const zir = @import("../zir.zig"); | |
| 3 | const Value = @import("../value.zig").Value; | |
| 4 | const Type = @import("../type.zig").Type; | |
| 5 | const Module = @import("../Module.zig"); | |
| 6 | const Scope = Module.Scope; | |
| 7 | const Enum = @This(); | |
| 8 | ||
| 9 | base: Type.Payload = .{ .tag = .@"enum" }, | |
| 10 | ||
| 11 | analysis: union(enum) { | |
| 12 | queued: Zir, | |
| 13 | in_progress, | |
| 14 | resolved: Size, | |
| 15 | failed, | |
| 16 | }, | |
| 17 | scope: Scope.Container, | |
| 18 | ||
| 19 | pub const Field = struct { | |
| 20 | value: Value, | |
| 21 | }; | |
| 22 | ||
| 23 | pub const Zir = struct { | |
| 24 | body: zir.Body, | |
| 25 | inst: *zir.Inst, | |
| 26 | }; | |
| 27 | ||
| 28 | pub const Size = struct { | |
| 29 | tag_type: Type, | |
| 30 | fields: std.StringArrayHashMapUnmanaged(Field), | |
| 31 | }; | |
| 32 | ||
| 33 | pub 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? | |
| 48 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const zir = @import("../zir.zig"); | |
| 3 | const Value = @import("../value.zig").Value; | |
| 4 | const Type = @import("../type.zig").Type; | |
| 5 | const Module = @import("../Module.zig"); | |
| 6 | const Scope = Module.Scope; | |
| 7 | const Struct = @This(); | |
| 8 | ||
| 9 | base: Type.Payload = .{ .tag = .@"struct" }, | |
| 10 | ||
| 11 | analysis: 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 | }, | |
| 20 | scope: Scope.Container, | |
| 21 | ||
| 22 | pub const Field = struct { | |
| 23 | value: Value, | |
| 24 | }; | |
| 25 | ||
| 26 | pub const Zir = struct { | |
| 27 | body: zir.Body, | |
| 28 | inst: *zir.Inst, | |
| 29 | }; | |
| 30 | ||
| 31 | pub const Zero = struct { | |
| 32 | is_zero_bits: bool, | |
| 33 | fields: std.StringArrayHashMapUnmanaged(Field), | |
| 34 | }; | |
| 35 | ||
| 36 | pub const Size = struct { | |
| 37 | is_zero_bits: bool, | |
| 38 | alignment: u32, | |
| 39 | size: u32, | |
| 40 | fields: std.StringArrayHashMapUnmanaged(Field), | |
| 41 | }; | |
| 42 | ||
| 43 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const zir = @import("../zir.zig"); | |
| 3 | const Value = @import("../value.zig").Value; | |
| 4 | const Type = @import("../type.zig").Type; | |
| 5 | const Module = @import("../Module.zig"); | |
| 6 | const Scope = Module.Scope; | |
| 7 | const Union = @This(); | |
| 8 | ||
| 9 | base: Type.Payload = .{ .tag = .@"struct" }, | |
| 10 | ||
| 11 | analysis: 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 | }, | |
| 20 | scope: Scope.Container, | |
| 21 | ||
| 22 | pub const Field = struct { | |
| 23 | value: Value, | |
| 24 | }; | |
| 25 | ||
| 26 | pub const Zir = struct { | |
| 27 | body: zir.Body, | |
| 28 | inst: *zir.Inst, | |
| 29 | }; | |
| 30 | ||
| 31 | pub const Zero = struct { | |
| 32 | is_zero_bits: bool, | |
| 33 | fields: std.StringArrayHashMapUnmanaged(Field), | |
| 34 | }; | |
| 35 | ||
| 36 | pub const Size = struct { | |
| 37 | is_zero_bits: bool, | |
| 38 | alignment: u32, | |
| 39 | size: u32, | |
| 40 | fields: std.StringArrayHashMapUnmanaged(Field), | |
| 41 | }; | |
| 42 | ||
| 43 | pub 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 { |
| 30 | 30 | i32_type, |
| 31 | 31 | u64_type, |
| 32 | 32 | i64_type, |
| 33 | u128_type, | |
| 34 | i128_type, | |
| 33 | 35 | usize_type, |
| 34 | 36 | isize_type, |
| 35 | 37 | c_short_type, |
| ... | ... | @@ -62,18 +64,18 @@ pub const Value = extern union { |
| 62 | 64 | single_const_pointer_to_comptime_int_type, |
| 63 | 65 | const_slice_u8_type, |
| 64 | 66 | enum_literal_type, |
| 65 | anyframe_type, | |
| 66 | 67 | |
| 67 | 68 | undef, |
| 68 | 69 | zero, |
| 69 | 70 | one, |
| 70 | 71 | void_value, |
| 71 | 72 | unreachable_value, |
| 72 | empty_struct_value, | |
| 73 | empty_array, | |
| 74 | 73 | null_value, |
| 75 | 74 | 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. | |
| 77 | 79 | // After this, the tag requires a payload. |
| 78 | 80 | |
| 79 | 81 | ty, |
| ... | ... | @@ -100,14 +102,13 @@ pub const Value = extern union { |
| 100 | 102 | float_64, |
| 101 | 103 | float_128, |
| 102 | 104 | enum_literal, |
| 103 | error_set, | |
| 104 | 105 | @"error", |
| 105 | 106 | error_union, |
| 106 | 107 | /// This is a special value that tracks a set of types that have been stored |
| 107 | 108 | /// to an inferred allocation. It does not support any of the normal value queries. |
| 108 | 109 | inferred_alloc, |
| 109 | 110 | |
| 110 | pub const last_no_payload_tag = Tag.bool_false; | |
| 111 | pub const last_no_payload_tag = Tag.empty_array; | |
| 111 | 112 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| 112 | 113 | |
| 113 | 114 | pub fn Type(comptime t: Tag) type { |
| ... | ... | @@ -120,6 +121,8 @@ pub const Value = extern union { |
| 120 | 121 | .i32_type, |
| 121 | 122 | .u64_type, |
| 122 | 123 | .i64_type, |
| 124 | .u128_type, | |
| 125 | .i128_type, | |
| 123 | 126 | .usize_type, |
| 124 | 127 | .isize_type, |
| 125 | 128 | .c_short_type, |
| ... | ... | @@ -152,7 +155,6 @@ pub const Value = extern union { |
| 152 | 155 | .single_const_pointer_to_comptime_int_type, |
| 153 | 156 | .const_slice_u8_type, |
| 154 | 157 | .enum_literal_type, |
| 155 | .anyframe_type, | |
| 156 | 158 | .undef, |
| 157 | 159 | .zero, |
| 158 | 160 | .one, |
| ... | ... | @@ -193,7 +195,6 @@ pub const Value = extern union { |
| 193 | 195 | .float_32 => Payload.Float_32, |
| 194 | 196 | .float_64 => Payload.Float_64, |
| 195 | 197 | .float_128 => Payload.Float_128, |
| 196 | .error_set => Payload.ErrorSet, | |
| 197 | 198 | .@"error" => Payload.Error, |
| 198 | 199 | .inferred_alloc => Payload.InferredAlloc, |
| 199 | 200 | }; |
| ... | ... | @@ -275,6 +276,8 @@ pub const Value = extern union { |
| 275 | 276 | .i32_type, |
| 276 | 277 | .u64_type, |
| 277 | 278 | .i64_type, |
| 279 | .u128_type, | |
| 280 | .i128_type, | |
| 278 | 281 | .usize_type, |
| 279 | 282 | .isize_type, |
| 280 | 283 | .c_short_type, |
| ... | ... | @@ -307,7 +310,6 @@ pub const Value = extern union { |
| 307 | 310 | .single_const_pointer_to_comptime_int_type, |
| 308 | 311 | .const_slice_u8_type, |
| 309 | 312 | .enum_literal_type, |
| 310 | .anyframe_type, | |
| 311 | 313 | .undef, |
| 312 | 314 | .zero, |
| 313 | 315 | .one, |
| ... | ... | @@ -400,7 +402,6 @@ pub const Value = extern union { |
| 400 | 402 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 401 | 403 | }, |
| 402 | 404 | |
| 403 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), | |
| 404 | 405 | .inferred_alloc => unreachable, |
| 405 | 406 | } |
| 406 | 407 | } |
| ... | ... | @@ -429,6 +430,8 @@ pub const Value = extern union { |
| 429 | 430 | .i32_type => return out_stream.writeAll("i32"), |
| 430 | 431 | .u64_type => return out_stream.writeAll("u64"), |
| 431 | 432 | .i64_type => return out_stream.writeAll("i64"), |
| 433 | .u128_type => return out_stream.writeAll("u128"), | |
| 434 | .i128_type => return out_stream.writeAll("i128"), | |
| 432 | 435 | .isize_type => return out_stream.writeAll("isize"), |
| 433 | 436 | .usize_type => return out_stream.writeAll("usize"), |
| 434 | 437 | .c_short_type => return out_stream.writeAll("c_short"), |
| ... | ... | @@ -461,7 +464,6 @@ pub const Value = extern union { |
| 461 | 464 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), |
| 462 | 465 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), |
| 463 | 466 | .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"), |
| 464 | .anyframe_type => return out_stream.writeAll("anyframe"), | |
| 465 | 467 | |
| 466 | 468 | // TODO this should print `NAME{}` |
| 467 | 469 | .empty_struct_value => return out_stream.writeAll("struct {}{}"), |
| ... | ... | @@ -510,15 +512,6 @@ pub const Value = extern union { |
| 510 | 512 | .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), |
| 511 | 513 | .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}), |
| 512 | 514 | .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 | }, | |
| 522 | 515 | .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}), |
| 523 | 516 | // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that |
| 524 | 517 | .error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}), |
| ... | ... | @@ -557,6 +550,8 @@ pub const Value = extern union { |
| 557 | 550 | .i32_type => Type.initTag(.i32), |
| 558 | 551 | .u64_type => Type.initTag(.u64), |
| 559 | 552 | .i64_type => Type.initTag(.i64), |
| 553 | .u128_type => Type.initTag(.u128), | |
| 554 | .i128_type => Type.initTag(.i128), | |
| 560 | 555 | .usize_type => Type.initTag(.usize), |
| 561 | 556 | .isize_type => Type.initTag(.isize), |
| 562 | 557 | .c_short_type => Type.initTag(.c_short), |
| ... | ... | @@ -589,7 +584,6 @@ pub const Value = extern union { |
| 589 | 584 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), |
| 590 | 585 | .const_slice_u8_type => Type.initTag(.const_slice_u8), |
| 591 | 586 | .enum_literal_type => Type.initTag(.enum_literal), |
| 592 | .anyframe_type => Type.initTag(.@"anyframe"), | |
| 593 | 587 | |
| 594 | 588 | .int_type => { |
| 595 | 589 | const payload = self.castTag(.int_type).?.data; |
| ... | ... | @@ -602,10 +596,6 @@ pub const Value = extern union { |
| 602 | 596 | }; |
| 603 | 597 | return Type.initPayload(&new.base); |
| 604 | 598 | }, |
| 605 | .error_set => { | |
| 606 | const payload = self.castTag(.error_set).?.data; | |
| 607 | return Type.Tag.error_set.create(allocator, payload.decl); | |
| 608 | }, | |
| 609 | 599 | |
| 610 | 600 | .undef, |
| 611 | 601 | .zero, |
| ... | ... | @@ -654,6 +644,8 @@ pub const Value = extern union { |
| 654 | 644 | .i32_type, |
| 655 | 645 | .u64_type, |
| 656 | 646 | .i64_type, |
| 647 | .u128_type, | |
| 648 | .i128_type, | |
| 657 | 649 | .usize_type, |
| 658 | 650 | .isize_type, |
| 659 | 651 | .c_short_type, |
| ... | ... | @@ -686,7 +678,6 @@ pub const Value = extern union { |
| 686 | 678 | .single_const_pointer_to_comptime_int_type, |
| 687 | 679 | .const_slice_u8_type, |
| 688 | 680 | .enum_literal_type, |
| 689 | .anyframe_type, | |
| 690 | 681 | .null_value, |
| 691 | 682 | .function, |
| 692 | 683 | .extern_fn, |
| ... | ... | @@ -704,7 +695,6 @@ pub const Value = extern union { |
| 704 | 695 | .unreachable_value, |
| 705 | 696 | .empty_array, |
| 706 | 697 | .enum_literal, |
| 707 | .error_set, | |
| 708 | 698 | .error_union, |
| 709 | 699 | .@"error", |
| 710 | 700 | .empty_struct_value, |
| ... | ... | @@ -741,6 +731,8 @@ pub const Value = extern union { |
| 741 | 731 | .i32_type, |
| 742 | 732 | .u64_type, |
| 743 | 733 | .i64_type, |
| 734 | .u128_type, | |
| 735 | .i128_type, | |
| 744 | 736 | .usize_type, |
| 745 | 737 | .isize_type, |
| 746 | 738 | .c_short_type, |
| ... | ... | @@ -773,7 +765,6 @@ pub const Value = extern union { |
| 773 | 765 | .single_const_pointer_to_comptime_int_type, |
| 774 | 766 | .const_slice_u8_type, |
| 775 | 767 | .enum_literal_type, |
| 776 | .anyframe_type, | |
| 777 | 768 | .null_value, |
| 778 | 769 | .function, |
| 779 | 770 | .extern_fn, |
| ... | ... | @@ -791,7 +782,6 @@ pub const Value = extern union { |
| 791 | 782 | .unreachable_value, |
| 792 | 783 | .empty_array, |
| 793 | 784 | .enum_literal, |
| 794 | .error_set, | |
| 795 | 785 | .@"error", |
| 796 | 786 | .error_union, |
| 797 | 787 | .empty_struct_value, |
| ... | ... | @@ -828,6 +818,8 @@ pub const Value = extern union { |
| 828 | 818 | .i32_type, |
| 829 | 819 | .u64_type, |
| 830 | 820 | .i64_type, |
| 821 | .u128_type, | |
| 822 | .i128_type, | |
| 831 | 823 | .usize_type, |
| 832 | 824 | .isize_type, |
| 833 | 825 | .c_short_type, |
| ... | ... | @@ -860,7 +852,6 @@ pub const Value = extern union { |
| 860 | 852 | .single_const_pointer_to_comptime_int_type, |
| 861 | 853 | .const_slice_u8_type, |
| 862 | 854 | .enum_literal_type, |
| 863 | .anyframe_type, | |
| 864 | 855 | .null_value, |
| 865 | 856 | .function, |
| 866 | 857 | .extern_fn, |
| ... | ... | @@ -878,7 +869,6 @@ pub const Value = extern union { |
| 878 | 869 | .unreachable_value, |
| 879 | 870 | .empty_array, |
| 880 | 871 | .enum_literal, |
| 881 | .error_set, | |
| 882 | 872 | .@"error", |
| 883 | 873 | .error_union, |
| 884 | 874 | .empty_struct_value, |
| ... | ... | @@ -942,6 +932,8 @@ pub const Value = extern union { |
| 942 | 932 | .i32_type, |
| 943 | 933 | .u64_type, |
| 944 | 934 | .i64_type, |
| 935 | .u128_type, | |
| 936 | .i128_type, | |
| 945 | 937 | .usize_type, |
| 946 | 938 | .isize_type, |
| 947 | 939 | .c_short_type, |
| ... | ... | @@ -974,7 +966,6 @@ pub const Value = extern union { |
| 974 | 966 | .single_const_pointer_to_comptime_int_type, |
| 975 | 967 | .const_slice_u8_type, |
| 976 | 968 | .enum_literal_type, |
| 977 | .anyframe_type, | |
| 978 | 969 | .null_value, |
| 979 | 970 | .function, |
| 980 | 971 | .extern_fn, |
| ... | ... | @@ -993,7 +984,6 @@ pub const Value = extern union { |
| 993 | 984 | .unreachable_value, |
| 994 | 985 | .empty_array, |
| 995 | 986 | .enum_literal, |
| 996 | .error_set, | |
| 997 | 987 | .@"error", |
| 998 | 988 | .error_union, |
| 999 | 989 | .empty_struct_value, |
| ... | ... | @@ -1034,6 +1024,8 @@ pub const Value = extern union { |
| 1034 | 1024 | .i32_type, |
| 1035 | 1025 | .u64_type, |
| 1036 | 1026 | .i64_type, |
| 1027 | .u128_type, | |
| 1028 | .i128_type, | |
| 1037 | 1029 | .usize_type, |
| 1038 | 1030 | .isize_type, |
| 1039 | 1031 | .c_short_type, |
| ... | ... | @@ -1066,7 +1058,6 @@ pub const Value = extern union { |
| 1066 | 1058 | .single_const_pointer_to_comptime_int_type, |
| 1067 | 1059 | .const_slice_u8_type, |
| 1068 | 1060 | .enum_literal_type, |
| 1069 | .anyframe_type, | |
| 1070 | 1061 | .null_value, |
| 1071 | 1062 | .function, |
| 1072 | 1063 | .extern_fn, |
| ... | ... | @@ -1084,7 +1075,6 @@ pub const Value = extern union { |
| 1084 | 1075 | .unreachable_value, |
| 1085 | 1076 | .empty_array, |
| 1086 | 1077 | .enum_literal, |
| 1087 | .error_set, | |
| 1088 | 1078 | .@"error", |
| 1089 | 1079 | .error_union, |
| 1090 | 1080 | .empty_struct_value, |
| ... | ... | @@ -1191,6 +1181,8 @@ pub const Value = extern union { |
| 1191 | 1181 | .i32_type, |
| 1192 | 1182 | .u64_type, |
| 1193 | 1183 | .i64_type, |
| 1184 | .u128_type, | |
| 1185 | .i128_type, | |
| 1194 | 1186 | .usize_type, |
| 1195 | 1187 | .isize_type, |
| 1196 | 1188 | .c_short_type, |
| ... | ... | @@ -1223,7 +1215,6 @@ pub const Value = extern union { |
| 1223 | 1215 | .single_const_pointer_to_comptime_int_type, |
| 1224 | 1216 | .const_slice_u8_type, |
| 1225 | 1217 | .enum_literal_type, |
| 1226 | .anyframe_type, | |
| 1227 | 1218 | .bool_true, |
| 1228 | 1219 | .bool_false, |
| 1229 | 1220 | .null_value, |
| ... | ... | @@ -1244,7 +1235,6 @@ pub const Value = extern union { |
| 1244 | 1235 | .void_value, |
| 1245 | 1236 | .unreachable_value, |
| 1246 | 1237 | .enum_literal, |
| 1247 | .error_set, | |
| 1248 | 1238 | .@"error", |
| 1249 | 1239 | .error_union, |
| 1250 | 1240 | .empty_struct_value, |
| ... | ... | @@ -1275,6 +1265,8 @@ pub const Value = extern union { |
| 1275 | 1265 | .i32_type, |
| 1276 | 1266 | .u64_type, |
| 1277 | 1267 | .i64_type, |
| 1268 | .u128_type, | |
| 1269 | .i128_type, | |
| 1278 | 1270 | .usize_type, |
| 1279 | 1271 | .isize_type, |
| 1280 | 1272 | .c_short_type, |
| ... | ... | @@ -1307,7 +1299,6 @@ pub const Value = extern union { |
| 1307 | 1299 | .single_const_pointer_to_comptime_int_type, |
| 1308 | 1300 | .const_slice_u8_type, |
| 1309 | 1301 | .enum_literal_type, |
| 1310 | .anyframe_type, | |
| 1311 | 1302 | .null_value, |
| 1312 | 1303 | .function, |
| 1313 | 1304 | .extern_fn, |
| ... | ... | @@ -1322,7 +1313,6 @@ pub const Value = extern union { |
| 1322 | 1313 | .unreachable_value, |
| 1323 | 1314 | .empty_array, |
| 1324 | 1315 | .enum_literal, |
| 1325 | .error_set, | |
| 1326 | 1316 | .@"error", |
| 1327 | 1317 | .error_union, |
| 1328 | 1318 | .empty_struct_value, |
| ... | ... | @@ -1427,6 +1417,8 @@ pub const Value = extern union { |
| 1427 | 1417 | .i32_type, |
| 1428 | 1418 | .u64_type, |
| 1429 | 1419 | .i64_type, |
| 1420 | .u128_type, | |
| 1421 | .i128_type, | |
| 1430 | 1422 | .usize_type, |
| 1431 | 1423 | .isize_type, |
| 1432 | 1424 | .c_short_type, |
| ... | ... | @@ -1459,18 +1451,12 @@ pub const Value = extern union { |
| 1459 | 1451 | .single_const_pointer_to_comptime_int_type, |
| 1460 | 1452 | .const_slice_u8_type, |
| 1461 | 1453 | .enum_literal_type, |
| 1462 | .anyframe_type, | |
| 1463 | 1454 | .ty, |
| 1464 | 1455 | => { |
| 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. | |
| 1466 | 1457 | var allocator = std.heap.FixedBufferAllocator.init(&[_]u8{}); |
| 1467 | 1458 | return (self.toType(&allocator.allocator) catch unreachable).hash(); |
| 1468 | 1459 | }, |
| 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 | }, | |
| 1474 | 1460 | .int_type => { |
| 1475 | 1461 | const payload = self.castTag(.int_type).?.data; |
| 1476 | 1462 | var int_payload = Type.Payload.Bits{ |
| ... | ... | @@ -1585,6 +1571,8 @@ pub const Value = extern union { |
| 1585 | 1571 | .i32_type, |
| 1586 | 1572 | .u64_type, |
| 1587 | 1573 | .i64_type, |
| 1574 | .u128_type, | |
| 1575 | .i128_type, | |
| 1588 | 1576 | .usize_type, |
| 1589 | 1577 | .isize_type, |
| 1590 | 1578 | .c_short_type, |
| ... | ... | @@ -1617,7 +1605,6 @@ pub const Value = extern union { |
| 1617 | 1605 | .single_const_pointer_to_comptime_int_type, |
| 1618 | 1606 | .const_slice_u8_type, |
| 1619 | 1607 | .enum_literal_type, |
| 1620 | .anyframe_type, | |
| 1621 | 1608 | .zero, |
| 1622 | 1609 | .one, |
| 1623 | 1610 | .bool_true, |
| ... | ... | @@ -1641,7 +1628,6 @@ pub const Value = extern union { |
| 1641 | 1628 | .unreachable_value, |
| 1642 | 1629 | .empty_array, |
| 1643 | 1630 | .enum_literal, |
| 1644 | .error_set, | |
| 1645 | 1631 | .@"error", |
| 1646 | 1632 | .error_union, |
| 1647 | 1633 | .empty_struct_value, |
| ... | ... | @@ -1672,6 +1658,8 @@ pub const Value = extern union { |
| 1672 | 1658 | .i32_type, |
| 1673 | 1659 | .u64_type, |
| 1674 | 1660 | .i64_type, |
| 1661 | .u128_type, | |
| 1662 | .i128_type, | |
| 1675 | 1663 | .usize_type, |
| 1676 | 1664 | .isize_type, |
| 1677 | 1665 | .c_short_type, |
| ... | ... | @@ -1704,7 +1692,6 @@ pub const Value = extern union { |
| 1704 | 1692 | .single_const_pointer_to_comptime_int_type, |
| 1705 | 1693 | .const_slice_u8_type, |
| 1706 | 1694 | .enum_literal_type, |
| 1707 | .anyframe_type, | |
| 1708 | 1695 | .zero, |
| 1709 | 1696 | .one, |
| 1710 | 1697 | .bool_true, |
| ... | ... | @@ -1728,7 +1715,6 @@ pub const Value = extern union { |
| 1728 | 1715 | .void_value, |
| 1729 | 1716 | .unreachable_value, |
| 1730 | 1717 | .enum_literal, |
| 1731 | .error_set, | |
| 1732 | 1718 | .@"error", |
| 1733 | 1719 | .error_union, |
| 1734 | 1720 | .empty_struct_value, |
| ... | ... | @@ -1776,6 +1762,8 @@ pub const Value = extern union { |
| 1776 | 1762 | .i32_type, |
| 1777 | 1763 | .u64_type, |
| 1778 | 1764 | .i64_type, |
| 1765 | .u128_type, | |
| 1766 | .i128_type, | |
| 1779 | 1767 | .usize_type, |
| 1780 | 1768 | .isize_type, |
| 1781 | 1769 | .c_short_type, |
| ... | ... | @@ -1808,7 +1796,6 @@ pub const Value = extern union { |
| 1808 | 1796 | .single_const_pointer_to_comptime_int_type, |
| 1809 | 1797 | .const_slice_u8_type, |
| 1810 | 1798 | .enum_literal_type, |
| 1811 | .anyframe_type, | |
| 1812 | 1799 | .zero, |
| 1813 | 1800 | .one, |
| 1814 | 1801 | .empty_array, |
| ... | ... | @@ -1832,7 +1819,6 @@ pub const Value = extern union { |
| 1832 | 1819 | .float_128, |
| 1833 | 1820 | .void_value, |
| 1834 | 1821 | .enum_literal, |
| 1835 | .error_set, | |
| 1836 | 1822 | .@"error", |
| 1837 | 1823 | .error_union, |
| 1838 | 1824 | .empty_struct_value, |
| ... | ... | @@ -1858,6 +1844,8 @@ pub const Value = extern union { |
| 1858 | 1844 | .i32_type, |
| 1859 | 1845 | .u64_type, |
| 1860 | 1846 | .i64_type, |
| 1847 | .u128_type, | |
| 1848 | .i128_type, | |
| 1861 | 1849 | .usize_type, |
| 1862 | 1850 | .isize_type, |
| 1863 | 1851 | .c_short_type, |
| ... | ... | @@ -1890,7 +1878,6 @@ pub const Value = extern union { |
| 1890 | 1878 | .single_const_pointer_to_comptime_int_type, |
| 1891 | 1879 | .const_slice_u8_type, |
| 1892 | 1880 | .enum_literal_type, |
| 1893 | .anyframe_type, | |
| 1894 | 1881 | .zero, |
| 1895 | 1882 | .one, |
| 1896 | 1883 | .null_value, |
| ... | ... | @@ -1915,7 +1902,6 @@ pub const Value = extern union { |
| 1915 | 1902 | .float_128, |
| 1916 | 1903 | .void_value, |
| 1917 | 1904 | .enum_literal, |
| 1918 | .error_set, | |
| 1919 | 1905 | .empty_struct_value, |
| 1920 | 1906 | => null, |
| 1921 | 1907 | |
| ... | ... | @@ -1960,6 +1946,8 @@ pub const Value = extern union { |
| 1960 | 1946 | .i32_type, |
| 1961 | 1947 | .u64_type, |
| 1962 | 1948 | .i64_type, |
| 1949 | .u128_type, | |
| 1950 | .i128_type, | |
| 1963 | 1951 | .usize_type, |
| 1964 | 1952 | .isize_type, |
| 1965 | 1953 | .c_short_type, |
| ... | ... | @@ -1992,8 +1980,6 @@ pub const Value = extern union { |
| 1992 | 1980 | .single_const_pointer_to_comptime_int_type, |
| 1993 | 1981 | .const_slice_u8_type, |
| 1994 | 1982 | .enum_literal_type, |
| 1995 | .anyframe_type, | |
| 1996 | .error_set, | |
| 1997 | 1983 | => true, |
| 1998 | 1984 | |
| 1999 | 1985 | .zero, |
| ... | ... | @@ -2137,18 +2123,6 @@ pub const Value = extern union { |
| 2137 | 2123 | data: f128, |
| 2138 | 2124 | }; |
| 2139 | 2125 | |
| 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 | ||
| 2152 | 2126 | pub const Error = struct { |
| 2153 | 2127 | base: Payload = .{ .tag = .@"error" }, |
| 2154 | 2128 | 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. | |
| 2 | 3 | |
| 3 | 4 | const std = @import("std"); |
| 4 | 5 | const mem = std.mem; |
| ... | ... | @@ -6,524 +7,636 @@ const Allocator = std.mem.Allocator; |
| 6 | 7 | const assert = std.debug.assert; |
| 7 | 8 | const BigIntConst = std.math.big.int.Const; |
| 8 | 9 | const BigIntMutable = std.math.big.int.Mutable; |
| 10 | const ast = std.zig.ast; | |
| 11 | ||
| 9 | 12 | const Type = @import("type.zig").Type; |
| 10 | 13 | const Value = @import("value.zig").Value; |
| 11 | 14 | const TypedValue = @import("TypedValue.zig"); |
| 12 | 15 | const ir = @import("ir.zig"); |
| 13 | const IrModule = @import("Module.zig"); | |
| 16 | const Module = @import("Module.zig"); | |
| 17 | const 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. | |
| 28 | pub 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 | }; | |
| 14 | 112 | |
| 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. | |
| 20 | 116 | pub const Inst = struct { |
| 21 | 117 | tag: Tag, |
| 22 | /// Byte offset into the source. | |
| 23 | src: usize, | |
| 118 | data: Data, | |
| 24 | 119 | |
| 25 | 120 | /// These names are used directly as the instruction names in the text format. |
| 26 | pub const Tag = enum { | |
| 121 | pub const Tag = enum(u8) { | |
| 27 | 122 | /// Arithmetic addition, asserts no integer overflow. |
| 123 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 28 | 124 | add, |
| 29 | 125 | /// Twos complement wrapping integer addition. |
| 126 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 30 | 127 | 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. | |
| 33 | 132 | alloc, |
| 34 | 133 | /// Same as `alloc` except mutable. |
| 35 | 134 | alloc_mut, |
| 36 | 135 | /// Same as `alloc` except the type is inferred. |
| 136 | /// The operand is unused. | |
| 37 | 137 | alloc_inferred, |
| 38 | 138 | /// Same as `alloc_inferred` except mutable. |
| 39 | 139 | alloc_inferred_mut, |
| 40 | /// Create an `anyframe->T`. | |
| 41 | anyframe_type, | |
| 42 | 140 | /// Array concatenation. `a ++ b` |
| 141 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 43 | 142 | array_cat, |
| 44 | 143 | /// Array multiplication `a ** b` |
| 144 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 45 | 145 | 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. | |
| 47 | 148 | 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. | |
| 49 | 151 | array_type_sentinel, |
| 50 | 152 | /// 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. | |
| 53 | 156 | 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. | |
| 61 | 159 | 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. | |
| 63 | 165 | @"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, | |
| 66 | 169 | /// Bitwise AND. `&` |
| 67 | 170 | 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`. | |
| 69 | 173 | 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, | |
| 75 | 174 | /// A typed result location pointer is bitcasted to a new result location pointer. |
| 76 | 175 | /// The new result location pointer has an inferred type. |
| 176 | /// Uses the un_node field. | |
| 77 | 177 | bitcast_result_ptr, |
| 78 | 178 | /// Bitwise NOT. `~` |
| 179 | /// Uses `un_node`. | |
| 79 | 180 | bit_not, |
| 80 | 181 | /// Bitwise OR. `|` |
| 81 | 182 | bit_or, |
| 82 | 183 | /// A labeled block of code, which can return a value. |
| 184 | /// Uses the `pl_node` union field. Payload is `Block`. | |
| 83 | 185 | 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, | |
| 91 | 191 | /// Boolean AND. See also `bit_and`. |
| 192 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 92 | 193 | bool_and, |
| 93 | 194 | /// Boolean NOT. See also `bit_not`. |
| 195 | /// Uses the `un_node` field. | |
| 94 | 196 | bool_not, |
| 95 | 197 | /// Boolean OR. See also `bit_or`. |
| 198 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 96 | 199 | 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. | |
| 98 | 211 | @"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. | |
| 99 | 219 | 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`. | |
| 103 | 222 | 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, | |
| 104 | 232 | /// `<` |
| 233 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 105 | 234 | cmp_lt, |
| 106 | 235 | /// `<=` |
| 236 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 107 | 237 | cmp_lte, |
| 108 | 238 | /// `==` |
| 239 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 109 | 240 | cmp_eq, |
| 110 | 241 | /// `>=` |
| 242 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 111 | 243 | cmp_gte, |
| 112 | 244 | /// `>` |
| 245 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 113 | 246 | cmp_gt, |
| 114 | 247 | /// `!=` |
| 248 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 115 | 249 | cmp_neq, |
| 116 | 250 | /// Coerces a result location pointer to a new element type. It is evaluated "backwards"- |
| 117 | 251 | /// as type coercion from the new element type to the old element type. |
| 252 | /// Uses the `bin` union field. | |
| 118 | 253 | /// LHS is destination element type, RHS is result pointer. |
| 119 | 254 | coerce_result_ptr, |
| 120 | 255 | /// Emit an error message and fail compilation. |
| 256 | /// Uses the `un_node` field. | |
| 121 | 257 | compile_error, |
| 122 | 258 | /// 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`. | |
| 123 | 261 | compile_log, |
| 124 | 262 | /// 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`. | |
| 125 | 265 | 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. | |
| 127 | 272 | @"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, | |
| 134 | 273 | /// Declares the beginning of a statement. Used for debug info. |
| 135 | dbg_stmt, | |
| 274 | /// Uses the `node` union field. | |
| 275 | dbg_stmt_node, | |
| 136 | 276 | /// Represents a pointer to a global decl. |
| 277 | /// Uses the `pl_node` union field. `payload_index` is into `decls`. | |
| 137 | 278 | 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`. | |
| 141 | 281 | 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, | |
| 144 | 285 | /// Arithmetic division. Asserts no integer overflow. |
| 286 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 145 | 287 | div, |
| 146 | 288 | /// 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. | |
| 148 | 291 | 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, | |
| 149 | 295 | /// 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. | |
| 150 | 298 | 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, | |
| 151 | 305 | /// Emits a compile error if the operand is not `void`. |
| 306 | /// Uses the `un_node` field. | |
| 152 | 307 | ensure_result_used, |
| 153 | 308 | /// Emits a compile error if an error is ignored. |
| 309 | /// Uses the `un_node` field. | |
| 154 | 310 | ensure_result_non_error, |
| 155 | 311 | /// Create a `E!T` type. |
| 312 | /// Uses the `pl_node` field with `Bin` payload. | |
| 156 | 313 | 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. | |
| 160 | 315 | error_value, |
| 161 | /// Export the provided Decl as the provided name in the compilation's output object file. | |
| 162 | @"export", | |
| 163 | 316 | /// 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. | |
| 165 | 319 | field_ptr, |
| 166 | 320 | /// 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. | |
| 168 | 323 | field_val, |
| 169 | 324 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer |
| 170 | 325 | /// 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. | |
| 171 | 327 | field_ptr_named, |
| 172 | 328 | /// Given a struct or object that contains virtual fields, returns the named field. |
| 173 | 329 | /// 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. | |
| 174 | 331 | 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. | |
| 176 | 336 | floatcast, |
| 177 | /// Declare a function body. | |
| 178 | @"fn", | |
| 179 | 337 | /// Returns a function type, assuming unspecified calling convention. |
| 338 | /// Uses the `pl_node` union field. `payload_index` points to a `FnType`. | |
| 180 | 339 | fn_type, |
| 181 | 340 | /// Same as `fn_type` but the function is variadic. |
| 182 | 341 | fn_type_var_args, |
| 183 | 342 | /// Returns a function type, with a calling convention instruction operand. |
| 343 | /// Uses the `pl_node` union field. `payload_index` points to a `FnTypeCc`. | |
| 184 | 344 | fn_type_cc, |
| 185 | 345 | /// Same as `fn_type_cc` but the function is variadic. |
| 186 | 346 | fn_type_cc_var_args, |
| 187 | /// @import(operand) | |
| 347 | /// `@import(operand)`. | |
| 348 | /// Uses the `un_node` field. | |
| 188 | 349 | import, |
| 189 | /// Integer literal. | |
| 350 | /// Integer literal that fits in a u64. Uses the int union value. | |
| 190 | 351 | int, |
| 191 | 352 | /// Convert an integer value to another integer type, asserting that the destination type |
| 192 | 353 | /// 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. | |
| 193 | 356 | intcast, |
| 194 | 357 | /// Make an integer type out of signedness and bit count. |
| 358 | /// Payload is `int_type` | |
| 195 | 359 | int_type, |
| 360 | /// Convert an error type to `u16` | |
| 361 | error_to_int, | |
| 362 | /// Convert a `u16` to `anyerror` | |
| 363 | int_to_error, | |
| 196 | 364 | /// Return a boolean false if an optional is null. `x != null` |
| 365 | /// Uses the `un_node` field. | |
| 197 | 366 | is_non_null, |
| 198 | 367 | /// Return a boolean true if an optional is null. `x == null` |
| 368 | /// Uses the `un_node` field. | |
| 199 | 369 | is_null, |
| 200 | 370 | /// Return a boolean false if an optional is null. `x.* != null` |
| 371 | /// Uses the `un_node` field. | |
| 201 | 372 | is_non_null_ptr, |
| 202 | 373 | /// Return a boolean true if an optional is null. `x.* == null` |
| 374 | /// Uses the `un_node` field. | |
| 203 | 375 | is_null_ptr, |
| 204 | 376 | /// Return a boolean true if value is an error |
| 377 | /// Uses the `un_node` field. | |
| 205 | 378 | is_err, |
| 206 | 379 | /// Return a boolean true if dereferenced pointer is an error |
| 380 | /// Uses the `un_node` field. | |
| 207 | 381 | 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`. | |
| 210 | 388 | 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, | |
| 211 | 395 | /// Merge two error sets into one, `E1 || E2`. |
| 396 | /// Uses the `pl_node` field with payload `Bin`. | |
| 212 | 397 | merge_error_sets, |
| 213 | 398 | /// Ambiguously remainder division or modulus. If the computation would possibly have |
| 214 | 399 | /// a different value depending on whether the operation is remainder division or modulus, |
| 215 | 400 | /// a compile error is emitted. Otherwise the computation is performed. |
| 401 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 216 | 402 | mod_rem, |
| 217 | 403 | /// Arithmetic multiplication. Asserts no integer overflow. |
| 404 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 218 | 405 | mul, |
| 219 | 406 | /// Twos complement wrapping integer multiplication. |
| 407 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 220 | 408 | mulwrap, |
| 221 | /// An await inside a nosuspend scope. | |
| 222 | nosuspend_await, | |
| 223 | 409 | /// 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. | |
| 225 | 416 | 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, | |
| 229 | 417 | /// Convert a pointer to a `usize` integer. |
| 418 | /// Uses the `un_node` field. The AST node is the builtin fn call node. | |
| 230 | 419 | ptrtoint, |
| 231 | 420 | /// Turns an R-Value into a const L-Value. In other words, it takes a value, |
| 232 | 421 | /// stores it in a memory location, and returns a const pointer to it. If the value |
| 233 | 422 | /// is `comptime`, the memory location is global static constant data. Otherwise, |
| 234 | 423 | /// the memory location is in the stack frame, local to the scope containing the |
| 235 | 424 | /// instruction. |
| 425 | /// Uses the `un_tok` union field. | |
| 236 | 426 | ref, |
| 237 | /// Resume an async function. | |
| 238 | @"resume", | |
| 239 | 427 | /// Obtains a pointer to the return value. |
| 428 | /// Uses the `node` union field. | |
| 240 | 429 | ret_ptr, |
| 241 | 430 | /// Obtains the return type of the in-scope function. |
| 431 | /// Uses the `node` union field. | |
| 242 | 432 | 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, | |
| 247 | 446 | /// Changes the maximum number of backwards branches that compile-time |
| 248 | 447 | /// code execution can use before giving up and making a compile error. |
| 448 | /// Uses the `un_node` union field. | |
| 249 | 449 | set_eval_branch_quota, |
| 250 | 450 | /// Integer shift-left. Zeroes are shifted in from the right hand side. |
| 451 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 251 | 452 | shl, |
| 252 | 453 | /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type. |
| 454 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 253 | 455 | 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. | |
| 271 | 461 | ptr_type, |
| 272 | 462 | /// Each `store_to_inferred_ptr` puts the type of the stored value into a set, |
| 273 | 463 | /// and then `resolve_inferred_alloc` triggers peer type resolution on the set. |
| 274 | 464 | /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which |
| 275 | 465 | /// is the allocation that needs to have its type inferred. |
| 466 | /// Uses the `un_node` field. The AST node is the var decl. | |
| 276 | 467 | 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`. | |
| 280 | 470 | 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. | |
| 282 | 480 | store, |
| 481 | /// Same as `store` except provides a source location. | |
| 482 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 483 | store_node, | |
| 283 | 484 | /// Same as `store` but the type of the value being stored will be used to infer |
| 284 | 485 | /// the block type. The LHS is the pointer to store to. |
| 486 | /// Uses the `bin` union field. | |
| 285 | 487 | store_to_block_ptr, |
| 286 | 488 | /// Same as `store` but the type of the value being stored will be used to infer |
| 287 | 489 | /// 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. | |
| 288 | 493 | store_to_inferred_ptr, |
| 289 | 494 | /// String Literal. Makes an anonymous Decl and then takes a pointer to it. |
| 495 | /// Uses the `str` union field. | |
| 290 | 496 | str, |
| 291 | /// Create a struct type. | |
| 292 | struct_type, | |
| 293 | 497 | /// Arithmetic subtraction. Asserts no integer overflow. |
| 498 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 294 | 499 | sub, |
| 295 | 500 | /// Twos complement wrapping integer subtraction. |
| 501 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 296 | 502 | 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, | |
| 297 | 511 | /// Returns the type of a value. |
| 512 | /// Uses the `un_tok` field. | |
| 298 | 513 | 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`. | |
| 300 | 520 | 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", | |
| 308 | 524 | /// Bitwise XOR. `^` |
| 525 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 309 | 526 | xor, |
| 310 | 527 | /// Create an optional type '?T' |
| 528 | /// Uses the `un_node` field. | |
| 311 | 529 | optional_type, |
| 312 | 530 | /// Create an optional type '?T'. The operand is a pointer value. The optional type will |
| 313 | 531 | /// be the type of the pointer element, wrapped in an optional. |
| 532 | /// Uses the `un_node` field. | |
| 314 | 533 | optional_type_from_ptr_elem, |
| 315 | /// Create a union type. | |
| 316 | union_type, | |
| 317 | 534 | /// ?T => T with safety. |
| 318 | 535 | /// Given an optional value, returns the payload value, with a safety check that |
| 319 | 536 | /// the value is non-null. Used for `orelse`, `if` and `while`. |
| 537 | /// Uses the `un_node` field. | |
| 320 | 538 | optional_payload_safe, |
| 321 | 539 | /// ?T => T without safety. |
| 322 | 540 | /// Given an optional value, returns the payload value. No safety checks. |
| 541 | /// Uses the `un_node` field. | |
| 323 | 542 | optional_payload_unsafe, |
| 324 | 543 | /// *?T => *T with safety. |
| 325 | 544 | /// Given a pointer to an optional value, returns a pointer to the payload value, |
| 326 | 545 | /// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`. |
| 546 | /// Uses the `un_node` field. | |
| 327 | 547 | optional_payload_safe_ptr, |
| 328 | 548 | /// *?T => *T without safety. |
| 329 | 549 | /// Given a pointer to an optional value, returns a pointer to the payload value. |
| 330 | 550 | /// No safety checks. |
| 551 | /// Uses the `un_node` field. | |
| 331 | 552 | optional_payload_unsafe_ptr, |
| 332 | 553 | /// E!T => T with safety. |
| 333 | 554 | /// Given an error union value, returns the payload value, with a safety check |
| 334 | 555 | /// that the value is not an error. Used for catch, if, and while. |
| 556 | /// Uses the `un_node` field. | |
| 335 | 557 | err_union_payload_safe, |
| 336 | 558 | /// E!T => T without safety. |
| 337 | 559 | /// Given an error union value, returns the payload value. No safety checks. |
| 560 | /// Uses the `un_node` field. | |
| 338 | 561 | err_union_payload_unsafe, |
| 339 | 562 | /// *E!T => *T with safety. |
| 340 | 563 | /// Given a pointer to an error union value, returns a pointer to the payload value, |
| 341 | 564 | /// with a safety check that the value is not an error. Used for catch, if, and while. |
| 565 | /// Uses the `un_node` field. | |
| 342 | 566 | err_union_payload_safe_ptr, |
| 343 | 567 | /// *E!T => *T without safety. |
| 344 | 568 | /// Given a pointer to a error union value, returns a pointer to the payload value. |
| 345 | 569 | /// No safety checks. |
| 570 | /// Uses the `un_node` field. | |
| 346 | 571 | err_union_payload_unsafe_ptr, |
| 347 | 572 | /// E!T => E without safety. |
| 348 | 573 | /// Given an error union value, returns the error code. No safety checks. |
| 574 | /// Uses the `un_node` field. | |
| 349 | 575 | err_union_code, |
| 350 | 576 | /// *E!T => E without safety. |
| 351 | 577 | /// Given a pointer to an error union value, returns the error code. No safety checks. |
| 578 | /// Uses the `un_node` field. | |
| 352 | 579 | err_union_code_ptr, |
| 353 | 580 | /// Takes a *E!T and raises a compiler error if T != void |
| 581 | /// Uses the `un_tok` field. | |
| 354 | 582 | ensure_err_payload_void, |
| 355 | /// Create a enum literal, | |
| 583 | /// An enum literal. Uses the `str_tok` union field. | |
| 356 | 584 | 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, | |
| 527 | 640 | |
| 528 | 641 | /// Returns whether the instruction is one of the control flow "noreturn" types. |
| 529 | 642 | /// Function calls do not count. |
| ... | ... | @@ -540,23 +653,28 @@ pub const Inst = struct { |
| 540 | 653 | .array_type, |
| 541 | 654 | .array_type_sentinel, |
| 542 | 655 | .indexable_ptr_len, |
| 543 | .arg, | |
| 544 | 656 | .as, |
| 657 | .as_node, | |
| 545 | 658 | .@"asm", |
| 659 | .asm_volatile, | |
| 546 | 660 | .bit_and, |
| 547 | 661 | .bitcast, |
| 548 | .bitcast_ref, | |
| 549 | 662 | .bitcast_result_ptr, |
| 550 | 663 | .bit_or, |
| 551 | 664 | .block, |
| 552 | .block_flat, | |
| 553 | .block_comptime, | |
| 554 | .block_comptime_flat, | |
| 665 | .block_inline, | |
| 666 | .loop, | |
| 667 | .bool_br_and, | |
| 668 | .bool_br_or, | |
| 555 | 669 | .bool_not, |
| 556 | 670 | .bool_and, |
| 557 | 671 | .bool_or, |
| 558 | 672 | .breakpoint, |
| 559 | 673 | .call, |
| 674 | .call_chkused, | |
| 675 | .call_compile_time, | |
| 676 | .call_none, | |
| 677 | .call_none_chkused, | |
| 560 | 678 | .cmp_lt, |
| 561 | 679 | .cmp_lte, |
| 562 | 680 | .cmp_eq, |
| ... | ... | @@ -565,23 +683,22 @@ pub const Inst = struct { |
| 565 | 683 | .cmp_neq, |
| 566 | 684 | .coerce_result_ptr, |
| 567 | 685 | .@"const", |
| 568 | .dbg_stmt, | |
| 686 | .dbg_stmt_node, | |
| 569 | 687 | .decl_ref, |
| 570 | .decl_ref_str, | |
| 571 | 688 | .decl_val, |
| 572 | .deref, | |
| 689 | .load, | |
| 573 | 690 | .div, |
| 574 | 691 | .elem_ptr, |
| 575 | 692 | .elem_val, |
| 693 | .elem_ptr_node, | |
| 694 | .elem_val_node, | |
| 576 | 695 | .ensure_result_used, |
| 577 | 696 | .ensure_result_non_error, |
| 578 | .@"export", | |
| 579 | 697 | .floatcast, |
| 580 | 698 | .field_ptr, |
| 581 | 699 | .field_val, |
| 582 | 700 | .field_ptr_named, |
| 583 | 701 | .field_val_named, |
| 584 | .@"fn", | |
| 585 | 702 | .fn_type, |
| 586 | 703 | .fn_type_var_args, |
| 587 | 704 | .fn_type_cc, |
| ... | ... | @@ -599,28 +716,23 @@ pub const Inst = struct { |
| 599 | 716 | .mul, |
| 600 | 717 | .mulwrap, |
| 601 | 718 | .param_type, |
| 602 | .primitive, | |
| 603 | 719 | .ptrtoint, |
| 604 | 720 | .ref, |
| 605 | 721 | .ret_ptr, |
| 606 | 722 | .ret_type, |
| 607 | 723 | .shl, |
| 608 | 724 | .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, | |
| 617 | 725 | .store, |
| 726 | .store_node, | |
| 618 | 727 | .store_to_block_ptr, |
| 619 | 728 | .store_to_inferred_ptr, |
| 620 | 729 | .str, |
| 621 | 730 | .sub, |
| 622 | 731 | .subwrap, |
| 732 | .negate, | |
| 733 | .negate_wrap, | |
| 623 | 734 | .typeof, |
| 735 | .typeof_elem, | |
| 624 | 736 | .xor, |
| 625 | 737 | .optional_type, |
| 626 | 738 | .optional_type_from_ptr_elem, |
| ... | ... | @@ -634,1436 +746,1450 @@ pub const Inst = struct { |
| 634 | 746 | .err_union_payload_unsafe_ptr, |
| 635 | 747 | .err_union_code, |
| 636 | 748 | .err_union_code_ptr, |
| 749 | .error_to_int, | |
| 750 | .int_to_error, | |
| 637 | 751 | .ptr_type, |
| 752 | .ptr_type_simple, | |
| 638 | 753 | .ensure_err_payload_void, |
| 639 | 754 | .enum_literal, |
| 755 | .enum_literal_small, | |
| 640 | 756 | .merge_error_sets, |
| 641 | .anyframe_type, | |
| 642 | 757 | .error_union_type, |
| 643 | 758 | .bit_not, |
| 644 | .error_set, | |
| 645 | 759 | .error_value, |
| 646 | .slice, | |
| 647 | 760 | .slice_start, |
| 761 | .slice_end, | |
| 762 | .slice_sentinel, | |
| 648 | 763 | .import, |
| 649 | 764 | .typeof_peer, |
| 650 | 765 | .resolve_inferred_alloc, |
| 651 | 766 | .set_eval_branch_quota, |
| 652 | 767 | .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, | |
| 661 | 787 | => false, |
| 662 | 788 | |
| 663 | 789 | .@"break", |
| 664 | .break_void, | |
| 790 | .break_inline, | |
| 665 | 791 | .condbr, |
| 792 | .condbr_inline, | |
| 666 | 793 | .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, | |
| 679 | 800 | => true, |
| 680 | 801 | }; |
| 681 | 802 | } |
| 682 | 803 | }; |
| 683 | 804 | |
| 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 | }, | |
| 710 | 1060 | |
| 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 | }); | |
| 713 | 1102 | }; |
| 714 | 1103 | |
| 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 | } | |
| 720 | 1118 | }, |
| 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 | } | |
| 730 | 1129 | }, |
| 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 | } | |
| 737 | 1175 | |
| 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, | |
| 742 | 1252 | }, |
| 743 | kw_args: struct {}, | |
| 744 | }; | |
| 745 | 1253 | |
| 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 | }; | |
| 749 | 1263 | |
| 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, | |
| 754 | 1276 | }; |
| 755 | 1277 | |
| 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 | }; | |
| 759 | 1286 | |
| 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, | |
| 765 | 1293 | }; |
| 766 | 1294 | |
| 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 | }; | |
| 770 | 1300 | |
| 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, | |
| 775 | 1305 | }; |
| 776 | 1306 | |
| 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`. | |
| 779 | 1309 | 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, | |
| 789 | 1312 | }; |
| 790 | 1313 | |
| 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, | |
| 799 | 1321 | }; |
| 800 | 1322 | |
| 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, | |
| 809 | 1331 | }; |
| 810 | 1332 | |
| 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, | |
| 819 | 1336 | }; |
| 820 | 1337 | |
| 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, | |
| 829 | 1341 | }; |
| 830 | 1342 | |
| 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, | |
| 839 | 1347 | }; |
| 840 | 1348 | |
| 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, | |
| 849 | 1354 | }; |
| 850 | 1355 | |
| 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, | |
| 859 | 1360 | }; |
| 860 | 1361 | |
| 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 | }; | |
| 864 | 1378 | |
| 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, | |
| 869 | 1407 | }; |
| 870 | 1408 | |
| 871 | 1409 | 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, | |
| 879 | 1413 | }; |
| 880 | 1414 | |
| 881 | 1415 | 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, | |
| 889 | 1418 | }; |
| 890 | 1419 | |
| 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, | |
| 906 | 1423 | }; |
| 1424 | }; | |
| 907 | 1425 | |
| 908 | pub const Fn = struct { | |
| 909 | pub const base_tag = Tag.@"fn"; | |
| 910 | base: Inst, | |
| 1426 | pub const SpecialProng = enum { none, @"else", under }; | |
| 911 | 1427 | |
| 912 | positionals: struct { | |
| 913 | fn_type: *Inst, | |
| 914 | body: Body, | |
| 915 | }, | |
| 916 | kw_args: struct {}, | |
| 917 | }; | |
| 1428 | const Writer = struct { | |
| 1429 | gpa: *Allocator, | |
| 1430 | arena: *Allocator, | |
| 1431 | scope: *Module.Scope, | |
| 1432 | code: Code, | |
| 1433 | indent: usize, | |
| 1434 | param_count: usize, | |
| 918 | 1435 | |
| 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 | } | |
| 922 | 1640 | |
| 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 | } | |
| 929 | 1648 | |
| 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 | } | |
| 933 | 1659 | |
| 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 | } | |
| 1174 | 1670 | |
| 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 | } | |
| 1180 | 1679 | |
| 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 | } | |
| 1184 | 1688 | |
| 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 | } | |
| 1191 | 1698 | |
| 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 | } | |
| 1195 | 1707 | |
| 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 | } | |
| 1206 | 1716 | |
| 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 | } | |
| 1210 | 1725 | |
| 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 | } | |
| 1219 | 1735 | |
| 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 | } | |
| 1223 | 1745 | |
| 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 | } | |
| 1231 | 1755 | |
| 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); | |
| 1235 | 1760 | |
| 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 | } | |
| 1245 | 1770 | |
| 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 | } | |
| 1260 | 1783 | |
| 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 | } | |
| 1266 | 1803 | |
| 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 | }, | |
| 1270 | 1825 | }; |
| 1271 | }; | |
| 1272 | }; | |
| 1273 | 1826 | |
| 1274 | pub const ErrorMsg = struct { | |
| 1275 | byte_offset: usize, | |
| 1276 | msg: []const u8, | |
| 1277 | }; | |
| 1278 | ||
| 1279 | pub const Body = struct { | |
| 1280 | instructions: []*Inst, | |
| 1281 | }; | |
| 1827 | try self.writeInstRef(stream, extra.data.operand); | |
| 1282 | 1828 | |
| 1283 | pub 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 | } | |
| 1295 | 1842 | |
| 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; | |
| 1298 | 1853 | |
| 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 | } | |
| 1303 | 1867 | |
| 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 | }; | |
| 1307 | 1890 | |
| 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); | |
| 1315 | 1892 | |
| 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 | } | |
| 1320 | 1906 | |
| 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; | |
| 1325 | 1917 | |
| 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("}"); | |
| 1334 | 1926 | } |
| 1335 | 1927 | } |
| 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 | } | |
| 1338 | 1944 | |
| 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("}"); | |
| 1346 | 1966 | } |
| 1347 | 1967 | } |
| 1348 | return null; | |
| 1968 | try stream.writeAll(") "); | |
| 1969 | try self.writeSrc(stream, inst_data.src()); | |
| 1349 | 1970 | } |
| 1350 | 1971 | |
| 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); | |
| 1367 | 1976 | |
| 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); | |
| 1373 | 1980 | } |
| 1981 | try stream.writeAll(") "); | |
| 1982 | try self.writeSrc(stream, inst_data.src()); | |
| 1983 | } | |
| 1374 | 1984 | |
| 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()); | |
| 1381 | 1990 | } |
| 1382 | }; | |
| 1383 | 1991 | |
| 1384 | const 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 | } | |
| 1385 | 2000 | |
| 1386 | const 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 | } | |
| 1394 | 2010 | |
| 1395 | fn writeInstToStream( | |
| 2011 | fn writeNode( | |
| 1396 | 2012 | self: *Writer, |
| 1397 | 2013 | stream: anytype, |
| 1398 | inst: *Inst, | |
| 2014 | inst: Inst.Index, | |
| 1399 | 2015 | ) (@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); | |
| 1407 | 2020 | } |
| 1408 | 2021 | |
| 1409 | fn writeInstToStreamGeneric( | |
| 2022 | fn writeStrTok( | |
| 1410 | 2023 | self: *Writer, |
| 1411 | 2024 | stream: anytype, |
| 1412 | comptime inst_tag: Inst.Tag, | |
| 1413 | base: *Inst, | |
| 2025 | inst: Inst.Index, | |
| 1414 | 2026 | ) (@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 | } | |
| 1426 | 2032 | |
| 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 | } | |
| 1444 | 2045 | |
| 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); | |
| 1446 | 2058 | } |
| 1447 | 2059 | |
| 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 | } | |
| 1479 | 2072 | |
| 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()); | |
| 1555 | 2081 | } |
| 1556 | 2082 | |
| 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(")"); | |
| 1571 | 2090 | } |
| 1572 | }; | |
| 1573 | 2091 | |
| 1574 | /// For debugging purposes, prints a function representation to stderr. | |
| 1575 | pub 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()); | |
| 1602 | 2097 | } |
| 1603 | } | |
| 1604 | 2098 | |
| 1605 | const 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); | |
| 1636 | 2112 | } |
| 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); | |
| 1639 | 2119 | } |
| 1640 | 2120 | |
| 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 | } | |
| 1681 | 2129 | |
| 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}); | |
| 1755 | 2134 | } |
| 1756 | 2135 | |
| 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); | |
| 1774 | 2138 | |
| 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; | |
| 1807 | 2143 | |
| 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}); | |
| 1995 | 2146 | } |
| 2147 | i -= self.param_count; | |
| 2148 | ||
| 2149 | return self.writeInstIndex(stream, @intCast(Inst.Index, i)); | |
| 1996 | 2150 | } |
| 1997 | 2151 | |
| 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}); | |
| 2012 | 2154 | } |
| 2013 | 2155 | |
| 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); | |
| 2019 | 2165 | } |
| 2020 | }; | |
| 2021 | 2166 | |
| 2022 | /// For debugging purposes, like dumpFn but for unanalyzed zir blocks | |
| 2023 | pub 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 | } | |
| 2061 | 2176 | |
| 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 | }); | |
| 2066 | 2185 | } |
| 2067 | 2186 | |
| 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 | ||
| 10 | const std = @import("std"); | |
| 11 | const mem = std.mem; | |
| 12 | const Allocator = std.mem.Allocator; | |
| 13 | const assert = std.debug.assert; | |
| 14 | const log = std.log.scoped(.sema); | |
| 15 | ||
| 16 | const Value = @import("value.zig").Value; | |
| 17 | const Type = @import("type.zig").Type; | |
| 18 | const TypedValue = @import("TypedValue.zig"); | |
| 19 | const ir = @import("ir.zig"); | |
| 20 | const zir = @import("zir.zig"); | |
| 21 | const Module = @import("Module.zig"); | |
| 22 | const Inst = ir.Inst; | |
| 23 | const Body = ir.Body; | |
| 24 | const trace = @import("tracy.zig").trace; | |
| 25 | const Scope = Module.Scope; | |
| 26 | const InnerError = Module.InnerError; | |
| 27 | const Decl = Module.Decl; | |
| 28 | ||
| 29 | pub 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 | ||
| 181 | pub 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 | ||
| 194 | pub 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 | ||
| 206 | pub 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 | ||
| 211 | fn 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 | ||
| 219 | fn 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. | |
| 230 | fn 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 | ||
| 245 | fn 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 | ||
| 253 | pub 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 | ||
| 262 | fn 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 | ||
| 271 | fn 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 | ||
| 279 | fn 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 | ||
| 285 | fn 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 | ||
| 291 | fn 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 | ||
| 297 | fn 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 | ||
| 307 | fn 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 | ||
| 315 | fn 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 | ||
| 324 | fn 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 | ||
| 334 | fn 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 | ||
| 344 | fn 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 | ||
| 374 | fn 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 | ||
| 383 | fn 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 | ||
| 393 | fn 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 | ||
| 422 | fn 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 | ||
| 451 | fn 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 | ||
| 469 | fn 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 | ||
| 490 | fn 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 | ||
| 502 | fn 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 | ||
| 511 | fn 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 | ||
| 544 | fn 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 | ||
| 563 | fn 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 | ||
| 570 | fn 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 | ||
| 580 | fn 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 | ||
| 587 | fn 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 | ||
| 614 | fn 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 | ||
| 637 | fn 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 | ||
| 678 | fn 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 | ||
| 699 | fn 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 | ||
| 755 | fn 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 | ||
| 838 | fn 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 | ||
| 845 | fn 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 | ||
| 854 | fn 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 | ||
| 863 | fn 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 | ||
| 902 | fn 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 | ||
| 913 | fn 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 | ||
| 920 | fn 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 | ||
| 926 | fn 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 | ||
| 932 | fn 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 | ||
| 1070 | fn 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 | ||
| 1087 | fn zirAwait(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 1088 | return mod.fail(scope, inst.base.src, "TODO implement await", .{}); | |
| 1089 | } | |
| 1090 | ||
| 1091 | fn zirResume(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 1092 | return mod.fail(scope, inst.base.src, "TODO implement resume", .{}); | |
| 1093 | } | |
| 1094 | ||
| 1095 | fn zirSuspend(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 1096 | return mod.fail(scope, inst.base.src, "TODO implement suspend", .{}); | |
| 1097 | } | |
| 1098 | ||
| 1099 | fn zirSuspendBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst { | |
| 1100 | return mod.fail(scope, inst.base.src, "TODO implement suspend", .{}); | |
| 1101 | } | |
| 1102 | ||
| 1103 | fn 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 | ||
| 1109 | fn 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 | ||
| 1117 | fn 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 | ||
| 1127 | fn 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 | ||
| 1137 | fn 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 | ||
| 1148 | fn 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 | ||
| 1161 | fn 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 | ||
| 1169 | fn 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 | ||
| 1201 | fn 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 | ||
| 1216 | fn 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 | ||
| 1294 | fn 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. | |
| 1305 | fn 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. | |
| 1346 | fn 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 | |
| 1382 | fn 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 | |
| 1409 | fn 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 | |
| 1446 | fn 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 | |
| 1468 | fn 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 | ||
| 1492 | fn 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 | ||
| 1505 | fn 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 | ||
| 1520 | fn 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 | ||
| 1541 | fn 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 | ||
| 1591 | fn 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 | ||
| 1597 | fn 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 | ||
| 1605 | fn 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 | ||
| 1618 | fn 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 | ||
| 1629 | fn 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 | ||
| 1638 | fn 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 | ||
| 1650 | fn 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 | ||
| 1660 | fn 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 | ||
| 1698 | fn 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 | ||
| 1706 | fn 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 | ||
| 1744 | fn 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 | ||
| 1755 | fn 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 | ||
| 1764 | fn 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 | ||
| 1775 | fn 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 | ||
| 1784 | fn 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 | ||
| 1807 | fn 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 | ||
| 1882 | fn 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 | ||
| 2015 | fn 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 | ||
| 2035 | fn 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 | ||
| 2041 | fn 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 | ||
| 2047 | fn 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 | ||
| 2110 | fn 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 | ||
| 2116 | fn 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 | ||
| 2122 | fn 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 | ||
| 2128 | fn 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 | |
| 2196 | fn 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 | ||
| 2233 | fn 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 | ||
| 2240 | fn 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 | ||
| 2283 | fn 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 | ||
| 2348 | fn 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 | ||
| 2355 | fn 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 | ||
| 2367 | fn 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 | ||
| 2380 | fn 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 | ||
| 2404 | fn 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 | ||
| 2411 | fn 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 | ||
| 2419 | fn 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 | ||
| 2426 | fn 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 | ||
| 2434 | fn 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 | ||
| 2483 | fn 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 | ||
| 2500 | fn 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 | ||
| 2516 | fn 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 | ||
| 2540 | fn 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 | ||
| 2548 | fn 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 | ||
| 2556 | fn 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 { |
| 39 | 39 | \\} |
| 40 | 40 | \\fn unused() void {} |
| 41 | 41 | , "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 | }); | |
| 42 | 57 | } |
| 43 | 58 | |
| 44 | 59 | { |
| ... | ... | @@ -54,6 +69,42 @@ pub fn addCases(ctx: *TestContext) !void { |
| 54 | 69 | , "Hello, world!" ++ std.cstr.line_sep); |
| 55 | 70 | } |
| 56 | 71 | |
| 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 | ||
| 57 | 108 | { |
| 58 | 109 | var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64); |
| 59 | 110 | |
| ... | ... | @@ -243,6 +294,134 @@ pub fn addCases(ctx: *TestContext) !void { |
| 243 | 294 | \\ return a - 4; |
| 244 | 295 | \\} |
| 245 | 296 | , ""); |
| 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 | }); | |
| 246 | 425 | } |
| 247 | 426 | //{ |
| 248 | 427 | // var case = ctx.exeFromCompiledC("optionals", .{}); |
| ... | ... | @@ -271,6 +450,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 271 | 450 | // \\} |
| 272 | 451 | // , ""); |
| 273 | 452 | //} |
| 453 | ||
| 274 | 454 | { |
| 275 | 455 | var case = ctx.exeFromCompiledC("errors", .{}); |
| 276 | 456 | case.addCompareOutput( |
test/stage2/test.zig+50-10| ... | ... | @@ -355,7 +355,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 355 | 355 | \\ const z = @TypeOf(true, 1); |
| 356 | 356 | \\ unreachable; |
| 357 | 357 | \\} |
| 358 | , &[_][]const u8{":2:29: error: incompatible types: 'bool' and 'comptime_int'"}); | |
| 358 | , &[_][]const u8{":2:15: error: incompatible types: 'bool' and 'comptime_int'"}); | |
| 359 | 359 | } |
| 360 | 360 | |
| 361 | 361 | { |
| ... | ... | @@ -621,6 +621,43 @@ pub fn addCases(ctx: *TestContext) !void { |
| 621 | 621 | "hello\nhello\nhello\nhello\n", |
| 622 | 622 | ); |
| 623 | 623 | |
| 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 | ||
| 624 | 661 | // Labeled blocks (no conditional branch) |
| 625 | 662 | case.addCompareOutput( |
| 626 | 663 | \\export fn _start() noreturn { |
| ... | ... | @@ -1070,7 +1107,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1070 | 1107 | \\} |
| 1071 | 1108 | \\fn x() void {} |
| 1072 | 1109 | , &[_][]const u8{ |
| 1073 | ":11:8: error: found compile log statement", | |
| 1110 | ":9:5: error: found compile log statement", | |
| 1074 | 1111 | ":4:5: note: also here", |
| 1075 | 1112 | }); |
| 1076 | 1113 | } |
| ... | ... | @@ -1294,10 +1331,9 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1294 | 1331 | , |
| 1295 | 1332 | "", |
| 1296 | 1333 | ); |
| 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"). | |
| 1301 | 1337 | case.addError( |
| 1302 | 1338 | \\export fn _start() noreturn { |
| 1303 | 1339 | \\ const y = fibonacci(999); |
| ... | ... | @@ -1318,7 +1354,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1318 | 1354 | \\ ); |
| 1319 | 1355 | \\ unreachable; |
| 1320 | 1356 | \\} |
| 1321 | , &[_][]const u8{":8:19: error: evaluation exceeded 1000 backwards branches"}); | |
| 1357 | , &[_][]const u8{":8:21: error: evaluation exceeded 1000 backwards branches"}); | |
| 1322 | 1358 | } |
| 1323 | 1359 | { |
| 1324 | 1360 | var case = ctx.exe("orelse at comptime", linux_x64); |
| ... | ... | @@ -1442,6 +1478,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1442 | 1478 | , |
| 1443 | 1479 | "", |
| 1444 | 1480 | ); |
| 1481 | ||
| 1445 | 1482 | case.addCompareOutput( |
| 1446 | 1483 | \\export fn _start() noreturn { |
| 1447 | 1484 | \\ const i: anyerror!u64 = error.B; |
| ... | ... | @@ -1464,6 +1501,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1464 | 1501 | , |
| 1465 | 1502 | "", |
| 1466 | 1503 | ); |
| 1504 | ||
| 1467 | 1505 | case.addCompareOutput( |
| 1468 | 1506 | \\export fn _start() noreturn { |
| 1469 | 1507 | \\ const a: anyerror!comptime_int = 42; |
| ... | ... | @@ -1485,11 +1523,12 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1485 | 1523 | \\ unreachable; |
| 1486 | 1524 | \\} |
| 1487 | 1525 | , ""); |
| 1526 | ||
| 1488 | 1527 | case.addCompareOutput( |
| 1489 | 1528 | \\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(); | |
| 1493 | 1532 | \\} |
| 1494 | 1533 | \\fn assert(b: bool) void { |
| 1495 | 1534 | \\ if (!b) unreachable; |
| ... | ... | @@ -1504,6 +1543,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1504 | 1543 | \\ unreachable; |
| 1505 | 1544 | \\} |
| 1506 | 1545 | , ""); |
| 1546 | ||
| 1507 | 1547 | case.addCompareOutput( |
| 1508 | 1548 | \\export fn _start() noreturn { |
| 1509 | 1549 | \\ const a: anyerror!u32 = error.Bar; |