| author | |
| committer | |
| log | a0e89c9b46fc91d9b1dfebd02eaae233802e3cbc |
| tree | 6a1ee37f130b4cb69204158ff915ecc2c6bb002b |
| parent | 94383d14df77fa638dac14f4b2bda5a2e3f21c5c |
| parent | 228a1ce3e8d112a7710fa47c6b9486cf320b5d6f |
52 files changed, 17120 insertions(+), 11701 deletions(-)
CMakeLists.txt+2-2| ... | @@ -539,7 +539,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -539,7 +539,7 @@ set(ZIG_STAGE2_SOURCES |
| 539 | "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" | 539 | "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" |
| 540 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" | 540 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" |
| 541 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" | 541 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" |
| 542 | "${CMAKE_SOURCE_DIR}/src/astgen.zig" | 542 | "${CMAKE_SOURCE_DIR}/src/AstGen.zig" |
| 543 | "${CMAKE_SOURCE_DIR}/src/clang.zig" | 543 | "${CMAKE_SOURCE_DIR}/src/clang.zig" |
| 544 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" | 544 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" |
| 545 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" | 545 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" |
| ... | @@ -591,7 +591,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -591,7 +591,7 @@ set(ZIG_STAGE2_SOURCES |
| 591 | "${CMAKE_SOURCE_DIR}/src/value.zig" | 591 | "${CMAKE_SOURCE_DIR}/src/value.zig" |
| 592 | "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig" | 592 | "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig" |
| 593 | "${CMAKE_SOURCE_DIR}/src/zir.zig" | 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 | if(MSVC) | 597 | if(MSVC) |
doc/docgen.zig+1-1| ... | @@ -1349,7 +1349,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any | ... | @@ -1349,7 +1349,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any |
| 1349 | } | 1349 | } |
| 1350 | const escaped_stderr = try escapeHtml(allocator, result.stderr); | 1350 | const escaped_stderr = try escapeHtml(allocator, result.stderr); |
| 1351 | const colored_stderr = try termColor(allocator, escaped_stderr); | 1351 | const colored_stderr = try termColor(allocator, escaped_stderr); |
| 1352 | try out.print("<pre><code class=\"shell\">$ zig test {s}.zig{s}\n{s}</code></pre>\n", .{ | 1352 | try out.print("<pre><code class=\"shell\">$ zig test {s}.zig {s}\n{s}</code></pre>\n", .{ |
| 1353 | code.name, | 1353 | code.name, |
| 1354 | mode_arg, | 1354 | mode_arg, |
| 1355 | colored_stderr, | 1355 | colored_stderr, |
doc/langref.html.in+14-9| ... | @@ -6594,13 +6594,11 @@ const std = @import("std"); | ... | @@ -6594,13 +6594,11 @@ const std = @import("std"); |
| 6594 | const expect = std.testing.expect; | 6594 | const expect = std.testing.expect; |
| 6595 | 6595 | ||
| 6596 | test "async and await" { | 6596 | test "async and await" { |
| 6597 | // Here we have an exception where we do not match an async | 6597 | // The test block is not async and so cannot have a suspend |
| 6598 | // with an await. The test block is not async and so cannot | 6598 | // point in it. By using the nosuspend keyword, we promise that |
| 6599 | // have a suspend point in it. | 6599 | // the code in amain will finish executing without suspending |
| 6600 | // This is well-defined behavior, and everything is OK here. | 6600 | // back to the test block. |
| 6601 | // Note however that there would be no way to collect the | 6601 | nosuspend amain(); |
| 6602 | // return value of amain, if it were something other than void. | ||
| 6603 | _ = async amain(); | ||
| 6604 | } | 6602 | } |
| 6605 | 6603 | ||
| 6606 | fn amain() void { | 6604 | fn amain() void { |
| ... | @@ -10799,9 +10797,16 @@ fn readU32Be() u32 {} | ... | @@ -10799,9 +10797,16 @@ fn readU32Be() u32 {} |
| 10799 | <pre>{#syntax#}nosuspend{#endsyntax#}</pre> | 10797 | <pre>{#syntax#}nosuspend{#endsyntax#}</pre> |
| 10800 | </td> | 10798 | </td> |
| 10801 | <td> | 10799 | <td> |
| 10802 | The {#syntax#}nosuspend{#endsyntax#} keyword. | 10800 | The {#syntax#}nosuspend{#endsyntax#} keyword can be used in front of a block, statement or expression, to mark a scope where no suspension points are reached. |
| 10801 | In particular, inside a {#syntax#}nosuspend{#endsyntax#} scope: | ||
| 10802 | <ul> | ||
| 10803 | <li>Using the {#syntax#}suspend{#endsyntax#} keyword results in a compile error.</li> | ||
| 10804 | <li>Using {#syntax#}await{#endsyntax#} on a function frame which hasn't completed yet results in safety-checked {#link|Undefined Behavior#}.</li> | ||
| 10805 | <li>Calling an async function may result in safety-checked {#link|Undefined Behavior#}, because it's equivalent to <code>await async some_async_fn()</code>, which contains an {#syntax#}await{#endsyntax#}.</li> | ||
| 10806 | </ul> | ||
| 10807 | Code inside a {#syntax#}nosuspend{#endsyntax#} scope does not cause the enclosing function to become an {#link|async function|Async Functions#}. | ||
| 10803 | <ul> | 10808 | <ul> |
| 10804 | <li>TODO add documentation for nosuspend</li> | 10809 | <li>See also {#link|Async Functions#}</li> |
| 10805 | </ul> | 10810 | </ul> |
| 10806 | </td> | 10811 | </td> |
| 10807 | </tr> | 10812 | </tr> |
lib/std/enums.zig+48-41| ... | @@ -32,7 +32,7 @@ pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_def | ... | @@ -32,7 +32,7 @@ pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_def |
| 32 | .fields = fields, | 32 | .fields = fields, |
| 33 | .decls = &[_]std.builtin.TypeInfo.Declaration{}, | 33 | .decls = &[_]std.builtin.TypeInfo.Declaration{}, |
| 34 | .is_tuple = false, | 34 | .is_tuple = false, |
| 35 | }}); | 35 | } }); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | /// Looks up the supplied fields in the given enum type. | 38 | /// Looks up the supplied fields in the given enum type. |
| ... | @@ -70,7 +70,7 @@ pub fn values(comptime E: type) []const E { | ... | @@ -70,7 +70,7 @@ pub fn values(comptime E: type) []const E { |
| 70 | 70 | ||
| 71 | test "std.enum.values" { | 71 | test "std.enum.values" { |
| 72 | const E = extern enum { a, b, c, d = 0 }; | 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 | /// Returns the set of all unique named values in the given enum, in | 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,10 +82,10 @@ pub fn uniqueValues(comptime E: type) []const E { |
| 82 | 82 | ||
| 83 | test "std.enum.uniqueValues" { | 83 | test "std.enum.uniqueValues" { |
| 84 | const E = extern enum { a, b, c, d = 0, e, f = 3 }; | 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 | const F = enum { a, b, c }; | 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 | /// Returns the set of all unique field values in the given enum, in | 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,8 +102,7 @@ pub fn uniqueFields(comptime E: type) []const EnumField { |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | var unique_fields: []const EnumField = &[_]EnumField{}; | 104 | var unique_fields: []const EnumField = &[_]EnumField{}; |
| 105 | outer: | 105 | outer: for (raw_fields) |candidate| { |
| 106 | for (raw_fields) |candidate| { | ||
| 107 | for (unique_fields) |u| { | 106 | for (unique_fields) |u| { |
| 108 | if (u.value == candidate.value) | 107 | if (u.value == candidate.value) |
| 109 | continue :outer; | 108 | continue :outer; |
| ... | @@ -116,28 +115,25 @@ pub fn uniqueFields(comptime E: type) []const EnumField { | ... | @@ -116,28 +115,25 @@ pub fn uniqueFields(comptime E: type) []const EnumField { |
| 116 | } | 115 | } |
| 117 | 116 | ||
| 118 | /// Determines the length of a direct-mapped enum array, indexed by | 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 | /// If the enum contains any fields with values that cannot be represented | 121 | /// If the enum contains any fields with values that cannot be represented |
| 121 | /// by usize, a compile error is issued. The max_unused_slots parameter limits | 122 | /// by usize, a compile error is issued. The max_unused_slots parameter limits |
| 122 | /// the total number of items which have no matching enum key (holes in the enum | 123 | /// the total number of items which have no matching enum key (holes in the enum |
| 123 | /// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots | 124 | /// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots |
| 124 | /// must be at least 3, to allow unused slots 0, 3, and 4. | 125 | /// must be at least 3, to allow unused slots 0, 3, and 4. |
| 125 | fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int { | 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 | var max_value: comptime_int = -1; | 127 | var max_value: comptime_int = -1; |
| 132 | const max_usize: comptime_int = ~@as(usize, 0); | 128 | const max_usize: comptime_int = ~@as(usize, 0); |
| 133 | const fields = uniqueFields(E); | 129 | const fields = uniqueFields(E); |
| 134 | for (fields) |f| { | 130 | for (fields) |f| { |
| 135 | if (f.value < 0) { | 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 | if (f.value > max_value) { | 134 | if (f.value > max_value) { |
| 139 | if (f.value > max_usize) { | 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 | max_value = f.value; | 138 | max_value = f.value; |
| 143 | } | 139 | } |
| ... | @@ -147,14 +143,16 @@ fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) | ... | @@ -147,14 +143,16 @@ fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) |
| 147 | if (unused_slots > max_unused_slots) { | 143 | if (unused_slots > max_unused_slots) { |
| 148 | const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots}); | 144 | const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots}); |
| 149 | const allowed_str = std.fmt.comptimePrint("{d}", .{max_unused_slots}); | 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 | return max_value + 1; | 149 | return max_value + 1; |
| 154 | } | 150 | } |
| 155 | 151 | ||
| 156 | /// Initializes an array of Data which can be indexed by | 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 | /// If the enum contains any fields with values that cannot be represented | 156 | /// If the enum contains any fields with values that cannot be represented |
| 159 | /// by usize, a compile error is issued. The max_unused_slots parameter limits | 157 | /// by usize, a compile error is issued. The max_unused_slots parameter limits |
| 160 | /// the total number of items which have no matching enum key (holes in the enum | 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,9 +241,9 @@ pub fn nameCast(comptime E: type, comptime value: anytype) E { |
| 243 | if (@hasField(E, n)) { | 241 | if (@hasField(E, n)) { |
| 244 | return @field(E, n); | 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,7 +254,7 @@ test "std.enums.nameCast" { |
| 256 | testing.expectEqual(A.a, nameCast(A, A.a)); | 254 | testing.expectEqual(A.a, nameCast(A, A.a)); |
| 257 | testing.expectEqual(A.a, nameCast(A, B.a)); | 255 | testing.expectEqual(A.a, nameCast(A, B.a)); |
| 258 | testing.expectEqual(A.a, nameCast(A, "a")); | 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 | testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a"))); | 258 | testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a"))); |
| 261 | testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a"))); | 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,12 +396,12 @@ pub fn EnumArray(comptime E: type, comptime V: type) type { |
| 398 | pub fn NoExtension(comptime Self: type) type { | 396 | pub fn NoExtension(comptime Self: type) type { |
| 399 | return NoExt; | 397 | return NoExt; |
| 400 | } | 398 | } |
| 401 | const NoExt = struct{}; | 399 | const NoExt = struct {}; |
| 402 | 400 | ||
| 403 | /// A set type with an Indexer mapping from keys to indices. | 401 | /// A set type with an Indexer mapping from keys to indices. |
| 404 | /// Presence or absence is stored as a dense bitfield. This | 402 | /// Presence or absence is stored as a dense bitfield. This |
| 405 | /// type does no allocation and can be copied by value. | 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 | comptime ensureIndexer(I); | 405 | comptime ensureIndexer(I); |
| 408 | return struct { | 406 | return struct { |
| 409 | const Self = @This(); | 407 | const Self = @This(); |
| ... | @@ -422,7 +420,7 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type { | ... | @@ -422,7 +420,7 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type { |
| 422 | 420 | ||
| 423 | bits: BitSet = BitSet.initEmpty(), | 421 | bits: BitSet = BitSet.initEmpty(), |
| 424 | 422 | ||
| 425 | /// Returns a set containing all possible keys. | 423 | /// Returns a set containing all possible keys. |
| 426 | pub fn initFull() Self { | 424 | pub fn initFull() Self { |
| 427 | return .{ .bits = BitSet.initFull() }; | 425 | return .{ .bits = BitSet.initFull() }; |
| 428 | } | 426 | } |
| ... | @@ -492,7 +490,8 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type { | ... | @@ -492,7 +490,8 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type { |
| 492 | pub fn next(self: *Iterator) ?Key { | 490 | pub fn next(self: *Iterator) ?Key { |
| 493 | return if (self.inner.next()) |index| | 491 | return if (self.inner.next()) |index| |
| 494 | Indexer.keyForIndex(index) | 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,7 +500,7 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type { |
| 501 | /// A map from keys to values, using an index lookup. Uses a | 500 | /// A map from keys to values, using an index lookup. Uses a |
| 502 | /// bitfield to track presence and a dense array of values. | 501 | /// bitfield to track presence and a dense array of values. |
| 503 | /// This type does no allocation and can be copied by value. | 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 | comptime ensureIndexer(I); | 504 | comptime ensureIndexer(I); |
| 506 | return struct { | 505 | return struct { |
| 507 | const Self = @This(); | 506 | const Self = @This(); |
| ... | @@ -652,7 +651,8 @@ pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type | ... | @@ -652,7 +651,8 @@ pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type |
| 652 | .key = Indexer.keyForIndex(index), | 651 | .key = Indexer.keyForIndex(index), |
| 653 | .value = &self.values[index], | 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,7 +660,7 @@ pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type |
| 660 | 660 | ||
| 661 | /// A dense array of values, using an indexed lookup. | 661 | /// A dense array of values, using an indexed lookup. |
| 662 | /// This type does no allocation and can be copied by value. | 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 | comptime ensureIndexer(I); | 664 | comptime ensureIndexer(I); |
| 665 | return struct { | 665 | return struct { |
| 666 | const Self = @This(); | 666 | const Self = @This(); |
| ... | @@ -769,9 +769,9 @@ pub fn ensureIndexer(comptime T: type) void { | ... | @@ -769,9 +769,9 @@ pub fn ensureIndexer(comptime T: type) void { |
| 769 | if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize."); | 769 | if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize."); |
| 770 | if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize."); | 770 | if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize."); |
| 771 | if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn(Key)usize."); | 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 | if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn(usize)Key."); | 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,14 +802,18 @@ pub fn EnumIndexer(comptime E: type) type { |
| 802 | return struct { | 802 | return struct { |
| 803 | pub const Key = E; | 803 | pub const Key = E; |
| 804 | pub const count: usize = 0; | 804 | pub const count: usize = 0; |
| 805 | pub fn indexOf(e: E) usize { unreachable; } | 805 | pub fn indexOf(e: E) usize { |
| 806 | pub fn keyForIndex(i: usize) E { unreachable; } | 806 | unreachable; |
| 807 | } | ||
| 808 | pub fn keyForIndex(i: usize) E { | ||
| 809 | unreachable; | ||
| 810 | } | ||
| 807 | }; | 811 | }; |
| 808 | } | 812 | } |
| 809 | std.sort.sort(EnumField, &fields, {}, ascByValue); | 813 | std.sort.sort(EnumField, &fields, {}, ascByValue); |
| 810 | const min = fields[0].value; | 814 | const min = fields[0].value; |
| 811 | const max = fields[fields.len-1].value; | 815 | const max = fields[fields.len - 1].value; |
| 812 | if (max - min == fields.len-1) { | 816 | if (max - min == fields.len - 1) { |
| 813 | return struct { | 817 | return struct { |
| 814 | pub const Key = E; | 818 | pub const Key = E; |
| 815 | pub const count = fields.len; | 819 | pub const count = fields.len; |
| ... | @@ -844,7 +848,7 @@ pub fn EnumIndexer(comptime E: type) type { | ... | @@ -844,7 +848,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 844 | } | 848 | } |
| 845 | 849 | ||
| 846 | test "std.enums.EnumIndexer dense zeroed" { | 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 | const Indexer = EnumIndexer(E); | 852 | const Indexer = EnumIndexer(E); |
| 849 | ensureIndexer(Indexer); | 853 | ensureIndexer(Indexer); |
| 850 | testing.expectEqual(E, Indexer.Key); | 854 | testing.expectEqual(E, Indexer.Key); |
| ... | @@ -908,7 +912,7 @@ test "std.enums.EnumIndexer sparse" { | ... | @@ -908,7 +912,7 @@ test "std.enums.EnumIndexer sparse" { |
| 908 | } | 912 | } |
| 909 | 913 | ||
| 910 | test "std.enums.EnumIndexer repeats" { | 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 | const Indexer = EnumIndexer(E); | 916 | const Indexer = EnumIndexer(E); |
| 913 | ensureIndexer(Indexer); | 917 | ensureIndexer(Indexer); |
| 914 | testing.expectEqual(E, Indexer.Key); | 918 | testing.expectEqual(E, Indexer.Key); |
| ... | @@ -957,7 +961,8 @@ test "std.enums.EnumSet" { | ... | @@ -957,7 +961,8 @@ test "std.enums.EnumSet" { |
| 957 | } | 961 | } |
| 958 | 962 | ||
| 959 | var mut = Set.init(.{ | 963 | var mut = Set.init(.{ |
| 960 | .a=true, .c=true, | 964 | .a = true, |
| 965 | .c = true, | ||
| 961 | }); | 966 | }); |
| 962 | testing.expectEqual(@as(usize, 2), mut.count()); | 967 | testing.expectEqual(@as(usize, 2), mut.count()); |
| 963 | testing.expectEqual(true, mut.contains(.a)); | 968 | testing.expectEqual(true, mut.contains(.a)); |
| ... | @@ -986,7 +991,7 @@ test "std.enums.EnumSet" { | ... | @@ -986,7 +991,7 @@ test "std.enums.EnumSet" { |
| 986 | testing.expectEqual(@as(?E, null), it.next()); | 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 | testing.expectEqual(@as(usize, 2), mut.count()); | 995 | testing.expectEqual(@as(usize, 2), mut.count()); |
| 991 | testing.expectEqual(true, mut.contains(.a)); | 996 | testing.expectEqual(true, mut.contains(.a)); |
| 992 | testing.expectEqual(false, mut.contains(.b)); | 997 | testing.expectEqual(false, mut.contains(.b)); |
| ... | @@ -994,7 +999,7 @@ test "std.enums.EnumSet" { | ... | @@ -994,7 +999,7 @@ test "std.enums.EnumSet" { |
| 994 | testing.expectEqual(true, mut.contains(.d)); | 999 | testing.expectEqual(true, mut.contains(.d)); |
| 995 | testing.expectEqual(true, mut.contains(.e)); // aliases a | 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 | testing.expectEqual(@as(usize, 3), mut.count()); | 1003 | testing.expectEqual(@as(usize, 3), mut.count()); |
| 999 | testing.expectEqual(true, mut.contains(.a)); | 1004 | testing.expectEqual(true, mut.contains(.a)); |
| 1000 | testing.expectEqual(true, mut.contains(.b)); | 1005 | testing.expectEqual(true, mut.contains(.b)); |
| ... | @@ -1009,7 +1014,7 @@ test "std.enums.EnumSet" { | ... | @@ -1009,7 +1014,7 @@ test "std.enums.EnumSet" { |
| 1009 | testing.expectEqual(false, mut.contains(.c)); | 1014 | testing.expectEqual(false, mut.contains(.c)); |
| 1010 | testing.expectEqual(true, mut.contains(.d)); | 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 | testing.expectEqual(@as(usize, 1), mut.count()); | 1018 | testing.expectEqual(@as(usize, 1), mut.count()); |
| 1014 | testing.expectEqual(true, mut.contains(.a)); | 1019 | testing.expectEqual(true, mut.contains(.a)); |
| 1015 | testing.expectEqual(false, mut.contains(.b)); | 1020 | testing.expectEqual(false, mut.contains(.b)); |
| ... | @@ -1072,7 +1077,7 @@ test "std.enums.EnumArray sized" { | ... | @@ -1072,7 +1077,7 @@ test "std.enums.EnumArray sized" { |
| 1072 | const undef = Array.initUndefined(); | 1077 | const undef = Array.initUndefined(); |
| 1073 | var inst = Array.initFill(5); | 1078 | var inst = Array.initFill(5); |
| 1074 | const inst2 = Array.init(.{ .a = 1, .b = 2, .c = 3, .d = 4 }); | 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 | testing.expectEqual(@as(usize, 5), inst.get(.a)); | 1082 | testing.expectEqual(@as(usize, 5), inst.get(.a)); |
| 1078 | testing.expectEqual(@as(usize, 5), inst.get(.b)); | 1083 | testing.expectEqual(@as(usize, 5), inst.get(.b)); |
| ... | @@ -1272,10 +1277,12 @@ test "std.enums.EnumMap sized" { | ... | @@ -1272,10 +1277,12 @@ test "std.enums.EnumMap sized" { |
| 1272 | var iter = a.iterator(); | 1277 | var iter = a.iterator(); |
| 1273 | const Entry = Map.Entry; | 1278 | const Entry = Map.Entry; |
| 1274 | testing.expectEqual(@as(?Entry, Entry{ | 1279 | testing.expectEqual(@as(?Entry, Entry{ |
| 1275 | .key = .b, .value = &a.values[1], | 1280 | .key = .b, |
| 1281 | .value = &a.values[1], | ||
| 1276 | }), iter.next()); | 1282 | }), iter.next()); |
| 1277 | testing.expectEqual(@as(?Entry, Entry{ | 1283 | testing.expectEqual(@as(?Entry, Entry{ |
| 1278 | .key = .d, .value = &a.values[3], | 1284 | .key = .d, |
| 1285 | .value = &a.values[3], | ||
| 1279 | }), iter.next()); | 1286 | }), iter.next()); |
| 1280 | testing.expectEqual(@as(?Entry, null), iter.next()); | 1287 | testing.expectEqual(@as(?Entry, null), iter.next()); |
| 1281 | } | 1288 | } |
lib/std/os.zig+2| ... | @@ -3267,6 +3267,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne | ... | @@ -3267,6 +3267,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne |
| 3267 | .WSAEADDRINUSE => return error.AddressInUse, | 3267 | .WSAEADDRINUSE => return error.AddressInUse, |
| 3268 | .WSAEADDRNOTAVAIL => return error.AddressNotAvailable, | 3268 | .WSAEADDRNOTAVAIL => return error.AddressNotAvailable, |
| 3269 | .WSAECONNREFUSED => return error.ConnectionRefused, | 3269 | .WSAECONNREFUSED => return error.ConnectionRefused, |
| 3270 | .WSAECONNRESET => return error.ConnectionResetByPeer, | ||
| 3270 | .WSAETIMEDOUT => return error.ConnectionTimedOut, | 3271 | .WSAETIMEDOUT => return error.ConnectionTimedOut, |
| 3271 | .WSAEHOSTUNREACH, // TODO: should we return NetworkUnreachable in this case as well? | 3272 | .WSAEHOSTUNREACH, // TODO: should we return NetworkUnreachable in this case as well? |
| 3272 | .WSAENETUNREACH, | 3273 | .WSAENETUNREACH, |
| ... | @@ -3296,6 +3297,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne | ... | @@ -3296,6 +3297,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne |
| 3296 | EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | 3297 | EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. |
| 3297 | EBADF => unreachable, // sockfd is not a valid open file descriptor. | 3298 | EBADF => unreachable, // sockfd is not a valid open file descriptor. |
| 3298 | ECONNREFUSED => return error.ConnectionRefused, | 3299 | ECONNREFUSED => return error.ConnectionRefused, |
| 3300 | ECONNRESET => return error.ConnectionResetByPeer, | ||
| 3299 | EFAULT => unreachable, // The socket structure address is outside the user's address space. | 3301 | EFAULT => unreachable, // The socket structure address is outside the user's address space. |
| 3300 | EINTR => continue, | 3302 | EINTR => continue, |
| 3301 | EISCONN => unreachable, // The socket is already connected. | 3303 | EISCONN => unreachable, // The socket is already connected. |
lib/std/os/linux/io_uring.zig+3-1| ... | @@ -1353,7 +1353,9 @@ test "timeout (after a relative time)" { | ... | @@ -1353,7 +1353,9 @@ test "timeout (after a relative time)" { |
| 1353 | .res = -linux.ETIME, | 1353 | .res = -linux.ETIME, |
| 1354 | .flags = 0, | 1354 | .flags = 0, |
| 1355 | }, cqe); | 1355 | }, cqe); |
| 1356 | testing.expectApproxEqAbs(@intToFloat(f64, ms), @intToFloat(f64, stopped - started), margin); | 1356 | |
| 1357 | // Tests should not depend on timings: skip test (result) if outside margin. | ||
| 1358 | if (!std.math.approxEqAbs(f64, ms, @intToFloat(f64, stopped - started), margin)) return error.SkipZigTest; | ||
| 1357 | } | 1359 | } |
| 1358 | 1360 | ||
| 1359 | test "timeout (after a number of completions)" { | 1361 | test "timeout (after a number of completions)" { |
lib/std/priority_dequeue.zig created+972| ... | @@ -0,0 +1,972 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("std.zig"); | ||
| 7 | const Allocator = std.mem.Allocator; | ||
| 8 | const assert = std.debug.assert; | ||
| 9 | const warn = std.debug.warn; | ||
| 10 | const Order = std.math.Order; | ||
| 11 | const testing = std.testing; | ||
| 12 | const expect = testing.expect; | ||
| 13 | const expectEqual = testing.expectEqual; | ||
| 14 | const expectError = testing.expectError; | ||
| 15 | |||
| 16 | /// Priority Dequeue for storing generic data. Initialize with `init`. | ||
| 17 | pub fn PriorityDequeue(comptime T: type) type { | ||
| 18 | return struct { | ||
| 19 | const Self = @This(); | ||
| 20 | |||
| 21 | items: []T, | ||
| 22 | len: usize, | ||
| 23 | allocator: *Allocator, | ||
| 24 | compareFn: fn (a: T, b: T) Order, | ||
| 25 | |||
| 26 | /// Initialize and return a new priority dequeue. Provide `compareFn` | ||
| 27 | /// that returns `Order.lt` when its first argument should | ||
| 28 | /// get min-popped before its second argument, `Order.eq` if the | ||
| 29 | /// arguments are of equal priority, or `Order.gt` if the second | ||
| 30 | /// argument should be min-popped first. Popping the max element works | ||
| 31 | /// in reverse. For example, to make `popMin` return the smallest | ||
| 32 | /// number, provide | ||
| 33 | /// | ||
| 34 | /// `fn lessThan(a: T, b: T) Order { return std.math.order(a, b); }` | ||
| 35 | pub fn init(allocator: *Allocator, compareFn: fn (T, T) Order) Self { | ||
| 36 | return Self{ | ||
| 37 | .items = &[_]T{}, | ||
| 38 | .len = 0, | ||
| 39 | .allocator = allocator, | ||
| 40 | .compareFn = compareFn, | ||
| 41 | }; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Free memory used by the dequeue. | ||
| 45 | pub fn deinit(self: Self) void { | ||
| 46 | self.allocator.free(self.items); | ||
| 47 | } | ||
| 48 | |||
| 49 | /// Insert a new element, maintaining priority. | ||
| 50 | pub fn add(self: *Self, elem: T) !void { | ||
| 51 | try ensureCapacity(self, self.len + 1); | ||
| 52 | addUnchecked(self, elem); | ||
| 53 | } | ||
| 54 | |||
| 55 | /// Add each element in `items` to the dequeue. | ||
| 56 | pub fn addSlice(self: *Self, items: []const T) !void { | ||
| 57 | try self.ensureCapacity(self.len + items.len); | ||
| 58 | for (items) |e| { | ||
| 59 | self.addUnchecked(e); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | fn addUnchecked(self: *Self, elem: T) void { | ||
| 64 | self.items[self.len] = elem; | ||
| 65 | |||
| 66 | if (self.len > 0) { | ||
| 67 | const start = self.getStartForSiftUp(elem, self.len); | ||
| 68 | self.siftUp(start); | ||
| 69 | } | ||
| 70 | |||
| 71 | self.len += 1; | ||
| 72 | } | ||
| 73 | |||
| 74 | fn isMinLayer(index: usize) bool { | ||
| 75 | // In the min-max heap structure: | ||
| 76 | // The first element is on a min layer; | ||
| 77 | // next two are on a max layer; | ||
| 78 | // next four are on a min layer, and so on. | ||
| 79 | const leading_zeros = @clz(usize, index + 1); | ||
| 80 | const highest_set_bit = @bitSizeOf(usize) - 1 - leading_zeros; | ||
| 81 | return (highest_set_bit & 1) == 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | fn nextIsMinLayer(self: Self) bool { | ||
| 85 | return isMinLayer(self.len); | ||
| 86 | } | ||
| 87 | |||
| 88 | const StartIndexAndLayer = struct { | ||
| 89 | index: usize, | ||
| 90 | min_layer: bool, | ||
| 91 | }; | ||
| 92 | |||
| 93 | fn getStartForSiftUp(self: Self, child: T, index: usize) StartIndexAndLayer { | ||
| 94 | var child_index = index; | ||
| 95 | var parent_index = parentIndex(child_index); | ||
| 96 | const parent = self.items[parent_index]; | ||
| 97 | |||
| 98 | const min_layer = self.nextIsMinLayer(); | ||
| 99 | const order = self.compareFn(child, parent); | ||
| 100 | if ((min_layer and order == .gt) or (!min_layer and order == .lt)) { | ||
| 101 | // We must swap the item with it's parent if it is on the "wrong" layer | ||
| 102 | self.items[parent_index] = child; | ||
| 103 | self.items[child_index] = parent; | ||
| 104 | return .{ | ||
| 105 | .index = parent_index, | ||
| 106 | .min_layer = !min_layer, | ||
| 107 | }; | ||
| 108 | } else { | ||
| 109 | return .{ | ||
| 110 | .index = child_index, | ||
| 111 | .min_layer = min_layer, | ||
| 112 | }; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | fn siftUp(self: *Self, start: StartIndexAndLayer) void { | ||
| 117 | if (start.min_layer) { | ||
| 118 | doSiftUp(self, start.index, .lt); | ||
| 119 | } else { | ||
| 120 | doSiftUp(self, start.index, .gt); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | fn doSiftUp(self: *Self, start_index: usize, target_order: Order) void { | ||
| 125 | var child_index = start_index; | ||
| 126 | while (child_index > 2) { | ||
| 127 | var grandparent_index = grandparentIndex(child_index); | ||
| 128 | const child = self.items[child_index]; | ||
| 129 | const grandparent = self.items[grandparent_index]; | ||
| 130 | |||
| 131 | // If the grandparent is already better or equal, we have gone as far as we need to | ||
| 132 | if (self.compareFn(child, grandparent) != target_order) break; | ||
| 133 | |||
| 134 | // Otherwise swap the item with it's grandparent | ||
| 135 | self.items[grandparent_index] = child; | ||
| 136 | self.items[child_index] = grandparent; | ||
| 137 | child_index = grandparent_index; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Look at the smallest element in the dequeue. Returns | ||
| 142 | /// `null` if empty. | ||
| 143 | pub fn peekMin(self: *Self) ?T { | ||
| 144 | return if (self.len > 0) self.items[0] else null; | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Look at the largest element in the dequeue. Returns | ||
| 148 | /// `null` if empty. | ||
| 149 | pub fn peekMax(self: *Self) ?T { | ||
| 150 | if (self.len == 0) return null; | ||
| 151 | if (self.len == 1) return self.items[0]; | ||
| 152 | if (self.len == 2) return self.items[1]; | ||
| 153 | return self.bestItemAtIndices(1, 2, .gt).item; | ||
| 154 | } | ||
| 155 | |||
| 156 | fn maxIndex(self: Self) ?usize { | ||
| 157 | if (self.len == 0) return null; | ||
| 158 | if (self.len == 1) return 0; | ||
| 159 | if (self.len == 2) return 1; | ||
| 160 | return self.bestItemAtIndices(1, 2, .gt).index; | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Pop the smallest element from the dequeue. Returns | ||
| 164 | /// `null` if empty. | ||
| 165 | pub fn removeMinOrNull(self: *Self) ?T { | ||
| 166 | return if (self.len > 0) self.removeMin() else null; | ||
| 167 | } | ||
| 168 | |||
| 169 | /// Remove and return the smallest element from the | ||
| 170 | /// dequeue. | ||
| 171 | pub fn removeMin(self: *Self) T { | ||
| 172 | return self.removeIndex(0); | ||
| 173 | } | ||
| 174 | |||
| 175 | /// Pop the largest element from the dequeue. Returns | ||
| 176 | /// `null` if empty. | ||
| 177 | pub fn removeMaxOrNull(self: *Self) ?T { | ||
| 178 | return if (self.len > 0) self.removeMax() else null; | ||
| 179 | } | ||
| 180 | |||
| 181 | /// Remove and return the largest element from the | ||
| 182 | /// dequeue. | ||
| 183 | pub fn removeMax(self: *Self) T { | ||
| 184 | return self.removeIndex(self.maxIndex().?); | ||
| 185 | } | ||
| 186 | |||
| 187 | /// Remove and return element at index. Indices are in the | ||
| 188 | /// same order as iterator, which is not necessarily priority | ||
| 189 | /// order. | ||
| 190 | pub fn removeIndex(self: *Self, index: usize) T { | ||
| 191 | assert(self.len > index); | ||
| 192 | const item = self.items[index]; | ||
| 193 | const last = self.items[self.len - 1]; | ||
| 194 | |||
| 195 | self.items[index] = last; | ||
| 196 | self.len -= 1; | ||
| 197 | siftDown(self, index); | ||
| 198 | |||
| 199 | return item; | ||
| 200 | } | ||
| 201 | |||
| 202 | fn siftDown(self: *Self, index: usize) void { | ||
| 203 | if (isMinLayer(index)) { | ||
| 204 | self.doSiftDown(index, .lt); | ||
| 205 | } else { | ||
| 206 | self.doSiftDown(index, .gt); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | fn doSiftDown(self: *Self, start_index: usize, target_order: Order) void { | ||
| 211 | var index = start_index; | ||
| 212 | const half = self.len >> 1; | ||
| 213 | while (true) { | ||
| 214 | const first_grandchild_index = firstGrandchildIndex(index); | ||
| 215 | const last_grandchild_index = first_grandchild_index + 3; | ||
| 216 | |||
| 217 | const elem = self.items[index]; | ||
| 218 | |||
| 219 | if (last_grandchild_index < self.len) { | ||
| 220 | // All four grandchildren exist | ||
| 221 | const index2 = first_grandchild_index + 1; | ||
| 222 | const index3 = index2 + 1; | ||
| 223 | |||
| 224 | // Find the best grandchild | ||
| 225 | const best_left = self.bestItemAtIndices(first_grandchild_index, index2, target_order); | ||
| 226 | const best_right = self.bestItemAtIndices(index3, last_grandchild_index, target_order); | ||
| 227 | const best_grandchild = self.bestItem(best_left, best_right, target_order); | ||
| 228 | |||
| 229 | // If the item is better than or equal to its best grandchild, we are done | ||
| 230 | if (self.compareFn(best_grandchild.item, elem) != target_order) return; | ||
| 231 | |||
| 232 | // Otherwise, swap them | ||
| 233 | self.items[best_grandchild.index] = elem; | ||
| 234 | self.items[index] = best_grandchild.item; | ||
| 235 | index = best_grandchild.index; | ||
| 236 | |||
| 237 | // We might need to swap the element with it's parent | ||
| 238 | self.swapIfParentIsBetter(elem, index, target_order); | ||
| 239 | } else { | ||
| 240 | // The children or grandchildren are the last layer | ||
| 241 | const first_child_index = firstChildIndex(index); | ||
| 242 | if (first_child_index > self.len) return; | ||
| 243 | |||
| 244 | const best_descendent = self.bestDescendent(first_child_index, first_grandchild_index, target_order); | ||
| 245 | |||
| 246 | // If the item is better than or equal to its best descendant, we are done | ||
| 247 | if (self.compareFn(best_descendent.item, elem) != target_order) return; | ||
| 248 | |||
| 249 | // Otherwise swap them | ||
| 250 | self.items[best_descendent.index] = elem; | ||
| 251 | self.items[index] = best_descendent.item; | ||
| 252 | index = best_descendent.index; | ||
| 253 | |||
| 254 | // If we didn't swap a grandchild, we are done | ||
| 255 | if (index < first_grandchild_index) return; | ||
| 256 | |||
| 257 | // We might need to swap the element with it's parent | ||
| 258 | self.swapIfParentIsBetter(elem, index, target_order); | ||
| 259 | return; | ||
| 260 | } | ||
| 261 | |||
| 262 | // If we are now in the last layer, we are done | ||
| 263 | if (index >= half) return; | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | fn swapIfParentIsBetter(self: *Self, child: T, child_index: usize, target_order: Order) void { | ||
| 268 | const parent_index = parentIndex(child_index); | ||
| 269 | const parent = self.items[parent_index]; | ||
| 270 | |||
| 271 | if (self.compareFn(parent, child) == target_order) { | ||
| 272 | self.items[parent_index] = child; | ||
| 273 | self.items[child_index] = parent; | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | const ItemAndIndex = struct { | ||
| 278 | item: T, | ||
| 279 | index: usize, | ||
| 280 | }; | ||
| 281 | |||
| 282 | fn getItem(self: Self, index: usize) ItemAndIndex { | ||
| 283 | return .{ | ||
| 284 | .item = self.items[index], | ||
| 285 | .index = index, | ||
| 286 | }; | ||
| 287 | } | ||
| 288 | |||
| 289 | fn bestItem(self: Self, item1: ItemAndIndex, item2: ItemAndIndex, target_order: Order) ItemAndIndex { | ||
| 290 | if (self.compareFn(item1.item, item2.item) == target_order) { | ||
| 291 | return item1; | ||
| 292 | } else { | ||
| 293 | return item2; | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | fn bestItemAtIndices(self: Self, index1: usize, index2: usize, target_order: Order) ItemAndIndex { | ||
| 298 | var item1 = self.getItem(index1); | ||
| 299 | var item2 = self.getItem(index2); | ||
| 300 | return self.bestItem(item1, item2, target_order); | ||
| 301 | } | ||
| 302 | |||
| 303 | fn bestDescendent(self: Self, first_child_index: usize, first_grandchild_index: usize, target_order: Order) ItemAndIndex { | ||
| 304 | const second_child_index = first_child_index + 1; | ||
| 305 | if (first_grandchild_index >= self.len) { | ||
| 306 | // No grandchildren, find the best child (second may not exist) | ||
| 307 | if (second_child_index >= self.len) { | ||
| 308 | return .{ | ||
| 309 | .item = self.items[first_child_index], | ||
| 310 | .index = first_child_index, | ||
| 311 | }; | ||
| 312 | } else { | ||
| 313 | return self.bestItemAtIndices(first_child_index, second_child_index, target_order); | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | const second_grandchild_index = first_grandchild_index + 1; | ||
| 318 | if (second_grandchild_index >= self.len) { | ||
| 319 | // One grandchild, so we know there is a second child. Compare first grandchild and second child | ||
| 320 | return self.bestItemAtIndices(first_grandchild_index, second_child_index, target_order); | ||
| 321 | } | ||
| 322 | |||
| 323 | const best_left_grandchild_index = self.bestItemAtIndices(first_grandchild_index, second_grandchild_index, target_order).index; | ||
| 324 | const third_grandchild_index = second_grandchild_index + 1; | ||
| 325 | if (third_grandchild_index >= self.len) { | ||
| 326 | // Two grandchildren, and we know the best. Compare this to second child. | ||
| 327 | return self.bestItemAtIndices(best_left_grandchild_index, second_child_index, target_order); | ||
| 328 | } else { | ||
| 329 | // Three grandchildren, compare the min of the first two with the third | ||
| 330 | return self.bestItemAtIndices(best_left_grandchild_index, third_grandchild_index, target_order); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | /// Return the number of elements remaining in the dequeue | ||
| 335 | pub fn count(self: Self) usize { | ||
| 336 | return self.len; | ||
| 337 | } | ||
| 338 | |||
| 339 | /// Return the number of elements that can be added to the | ||
| 340 | /// dequeue before more memory is allocated. | ||
| 341 | pub fn capacity(self: Self) usize { | ||
| 342 | return self.items.len; | ||
| 343 | } | ||
| 344 | |||
| 345 | /// Dequeue takes ownership of the passed in slice. The slice must have been | ||
| 346 | /// allocated with `allocator`. | ||
| 347 | /// De-initialize with `deinit`. | ||
| 348 | pub fn fromOwnedSlice(allocator: *Allocator, compareFn: fn (T, T) Order, items: []T) Self { | ||
| 349 | var queue = Self{ | ||
| 350 | .items = items, | ||
| 351 | .len = items.len, | ||
| 352 | .allocator = allocator, | ||
| 353 | .compareFn = compareFn, | ||
| 354 | }; | ||
| 355 | |||
| 356 | if (queue.len <= 1) return queue; | ||
| 357 | |||
| 358 | const half = (queue.len >> 1) - 1; | ||
| 359 | var i: usize = 0; | ||
| 360 | while (i <= half) : (i += 1) { | ||
| 361 | const index = half - i; | ||
| 362 | queue.siftDown(index); | ||
| 363 | } | ||
| 364 | return queue; | ||
| 365 | } | ||
| 366 | |||
| 367 | pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { | ||
| 368 | var better_capacity = self.capacity(); | ||
| 369 | if (better_capacity >= new_capacity) return; | ||
| 370 | while (true) { | ||
| 371 | better_capacity += better_capacity / 2 + 8; | ||
| 372 | if (better_capacity >= new_capacity) break; | ||
| 373 | } | ||
| 374 | self.items = try self.allocator.realloc(self.items, better_capacity); | ||
| 375 | } | ||
| 376 | |||
| 377 | /// Reduce allocated capacity to `new_len`. | ||
| 378 | pub fn shrinkAndFree(self: *Self, new_len: usize) void { | ||
| 379 | assert(new_len <= self.items.len); | ||
| 380 | |||
| 381 | // Cannot shrink to smaller than the current queue size without invalidating the heap property | ||
| 382 | assert(new_len >= self.len); | ||
| 383 | |||
| 384 | self.items = self.allocator.realloc(self.items[0..], new_len) catch |e| switch (e) { | ||
| 385 | error.OutOfMemory => { // no problem, capacity is still correct then. | ||
| 386 | self.items.len = new_len; | ||
| 387 | return; | ||
| 388 | }, | ||
| 389 | }; | ||
| 390 | self.len = new_len; | ||
| 391 | } | ||
| 392 | |||
| 393 | /// Reduce length to `new_len`. | ||
| 394 | pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { | ||
| 395 | assert(new_len <= self.items.len); | ||
| 396 | |||
| 397 | // Cannot shrink to smaller than the current queue size without invalidating the heap property | ||
| 398 | assert(new_len >= self.len); | ||
| 399 | |||
| 400 | self.len = new_len; | ||
| 401 | } | ||
| 402 | |||
| 403 | pub fn update(self: *Self, elem: T, new_elem: T) !void { | ||
| 404 | var old_index: usize = std.mem.indexOfScalar(T, self.items[0..self.len], elem) orelse return error.ElementNotFound; | ||
| 405 | _ = self.removeIndex(old_index); | ||
| 406 | self.addUnchecked(new_elem); | ||
| 407 | } | ||
| 408 | |||
| 409 | pub const Iterator = struct { | ||
| 410 | queue: *PriorityDequeue(T), | ||
| 411 | count: usize, | ||
| 412 | |||
| 413 | pub fn next(it: *Iterator) ?T { | ||
| 414 | if (it.count >= it.queue.len) return null; | ||
| 415 | const out = it.count; | ||
| 416 | it.count += 1; | ||
| 417 | return it.queue.items[out]; | ||
| 418 | } | ||
| 419 | |||
| 420 | pub fn reset(it: *Iterator) void { | ||
| 421 | it.count = 0; | ||
| 422 | } | ||
| 423 | }; | ||
| 424 | |||
| 425 | /// Return an iterator that walks the queue without consuming | ||
| 426 | /// it. Invalidated if the queue is modified. | ||
| 427 | pub fn iterator(self: *Self) Iterator { | ||
| 428 | return Iterator{ | ||
| 429 | .queue = self, | ||
| 430 | .count = 0, | ||
| 431 | }; | ||
| 432 | } | ||
| 433 | |||
| 434 | fn dump(self: *Self) void { | ||
| 435 | warn("{{ ", .{}); | ||
| 436 | warn("items: ", .{}); | ||
| 437 | for (self.items) |e, i| { | ||
| 438 | if (i >= self.len) break; | ||
| 439 | warn("{}, ", .{e}); | ||
| 440 | } | ||
| 441 | warn("array: ", .{}); | ||
| 442 | for (self.items) |e, i| { | ||
| 443 | warn("{}, ", .{e}); | ||
| 444 | } | ||
| 445 | warn("len: {} ", .{self.len}); | ||
| 446 | warn("capacity: {}", .{self.capacity()}); | ||
| 447 | warn(" }}\n", .{}); | ||
| 448 | } | ||
| 449 | |||
| 450 | fn parentIndex(index: usize) usize { | ||
| 451 | return (index - 1) >> 1; | ||
| 452 | } | ||
| 453 | |||
| 454 | fn grandparentIndex(index: usize) usize { | ||
| 455 | return parentIndex(parentIndex(index)); | ||
| 456 | } | ||
| 457 | |||
| 458 | fn firstChildIndex(index: usize) usize { | ||
| 459 | return (index << 1) + 1; | ||
| 460 | } | ||
| 461 | |||
| 462 | fn firstGrandchildIndex(index: usize) usize { | ||
| 463 | return firstChildIndex(firstChildIndex(index)); | ||
| 464 | } | ||
| 465 | }; | ||
| 466 | } | ||
| 467 | |||
| 468 | fn lessThanComparison(a: u32, b: u32) Order { | ||
| 469 | return std.math.order(a, b); | ||
| 470 | } | ||
| 471 | |||
| 472 | const PDQ = PriorityDequeue(u32); | ||
| 473 | |||
| 474 | test "std.PriorityDequeue: add and remove min" { | ||
| 475 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 476 | defer queue.deinit(); | ||
| 477 | |||
| 478 | try queue.add(54); | ||
| 479 | try queue.add(12); | ||
| 480 | try queue.add(7); | ||
| 481 | try queue.add(23); | ||
| 482 | try queue.add(25); | ||
| 483 | try queue.add(13); | ||
| 484 | |||
| 485 | expectEqual(@as(u32, 7), queue.removeMin()); | ||
| 486 | expectEqual(@as(u32, 12), queue.removeMin()); | ||
| 487 | expectEqual(@as(u32, 13), queue.removeMin()); | ||
| 488 | expectEqual(@as(u32, 23), queue.removeMin()); | ||
| 489 | expectEqual(@as(u32, 25), queue.removeMin()); | ||
| 490 | expectEqual(@as(u32, 54), queue.removeMin()); | ||
| 491 | } | ||
| 492 | |||
| 493 | test "std.PriorityDequeue: add and remove min structs" { | ||
| 494 | const S = struct { | ||
| 495 | size: u32, | ||
| 496 | }; | ||
| 497 | var queue = PriorityDequeue(S).init(testing.allocator, struct { | ||
| 498 | fn order(a: S, b: S) Order { | ||
| 499 | return std.math.order(a.size, b.size); | ||
| 500 | } | ||
| 501 | }.order); | ||
| 502 | defer queue.deinit(); | ||
| 503 | |||
| 504 | try queue.add(.{ .size = 54 }); | ||
| 505 | try queue.add(.{ .size = 12 }); | ||
| 506 | try queue.add(.{ .size = 7 }); | ||
| 507 | try queue.add(.{ .size = 23 }); | ||
| 508 | try queue.add(.{ .size = 25 }); | ||
| 509 | try queue.add(.{ .size = 13 }); | ||
| 510 | |||
| 511 | expectEqual(@as(u32, 7), queue.removeMin().size); | ||
| 512 | expectEqual(@as(u32, 12), queue.removeMin().size); | ||
| 513 | expectEqual(@as(u32, 13), queue.removeMin().size); | ||
| 514 | expectEqual(@as(u32, 23), queue.removeMin().size); | ||
| 515 | expectEqual(@as(u32, 25), queue.removeMin().size); | ||
| 516 | expectEqual(@as(u32, 54), queue.removeMin().size); | ||
| 517 | } | ||
| 518 | |||
| 519 | test "std.PriorityDequeue: add and remove max" { | ||
| 520 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 521 | defer queue.deinit(); | ||
| 522 | |||
| 523 | try queue.add(54); | ||
| 524 | try queue.add(12); | ||
| 525 | try queue.add(7); | ||
| 526 | try queue.add(23); | ||
| 527 | try queue.add(25); | ||
| 528 | try queue.add(13); | ||
| 529 | |||
| 530 | expectEqual(@as(u32, 54), queue.removeMax()); | ||
| 531 | expectEqual(@as(u32, 25), queue.removeMax()); | ||
| 532 | expectEqual(@as(u32, 23), queue.removeMax()); | ||
| 533 | expectEqual(@as(u32, 13), queue.removeMax()); | ||
| 534 | expectEqual(@as(u32, 12), queue.removeMax()); | ||
| 535 | expectEqual(@as(u32, 7), queue.removeMax()); | ||
| 536 | } | ||
| 537 | |||
| 538 | test "std.PriorityDequeue: add and remove same min" { | ||
| 539 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 540 | defer queue.deinit(); | ||
| 541 | |||
| 542 | try queue.add(1); | ||
| 543 | try queue.add(1); | ||
| 544 | try queue.add(2); | ||
| 545 | try queue.add(2); | ||
| 546 | try queue.add(1); | ||
| 547 | try queue.add(1); | ||
| 548 | |||
| 549 | expectEqual(@as(u32, 1), queue.removeMin()); | ||
| 550 | expectEqual(@as(u32, 1), queue.removeMin()); | ||
| 551 | expectEqual(@as(u32, 1), queue.removeMin()); | ||
| 552 | expectEqual(@as(u32, 1), queue.removeMin()); | ||
| 553 | expectEqual(@as(u32, 2), queue.removeMin()); | ||
| 554 | expectEqual(@as(u32, 2), queue.removeMin()); | ||
| 555 | } | ||
| 556 | |||
| 557 | test "std.PriorityDequeue: add and remove same max" { | ||
| 558 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 559 | defer queue.deinit(); | ||
| 560 | |||
| 561 | try queue.add(1); | ||
| 562 | try queue.add(1); | ||
| 563 | try queue.add(2); | ||
| 564 | try queue.add(2); | ||
| 565 | try queue.add(1); | ||
| 566 | try queue.add(1); | ||
| 567 | |||
| 568 | expectEqual(@as(u32, 2), queue.removeMax()); | ||
| 569 | expectEqual(@as(u32, 2), queue.removeMax()); | ||
| 570 | expectEqual(@as(u32, 1), queue.removeMax()); | ||
| 571 | expectEqual(@as(u32, 1), queue.removeMax()); | ||
| 572 | expectEqual(@as(u32, 1), queue.removeMax()); | ||
| 573 | expectEqual(@as(u32, 1), queue.removeMax()); | ||
| 574 | } | ||
| 575 | |||
| 576 | test "std.PriorityDequeue: removeOrNull empty" { | ||
| 577 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 578 | defer queue.deinit(); | ||
| 579 | |||
| 580 | expect(queue.removeMinOrNull() == null); | ||
| 581 | expect(queue.removeMaxOrNull() == null); | ||
| 582 | } | ||
| 583 | |||
| 584 | test "std.PriorityDequeue: edge case 3 elements" { | ||
| 585 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 586 | defer queue.deinit(); | ||
| 587 | |||
| 588 | try queue.add(9); | ||
| 589 | try queue.add(3); | ||
| 590 | try queue.add(2); | ||
| 591 | |||
| 592 | expectEqual(@as(u32, 2), queue.removeMin()); | ||
| 593 | expectEqual(@as(u32, 3), queue.removeMin()); | ||
| 594 | expectEqual(@as(u32, 9), queue.removeMin()); | ||
| 595 | } | ||
| 596 | |||
| 597 | test "std.PriorityDequeue: edge case 3 elements max" { | ||
| 598 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 599 | defer queue.deinit(); | ||
| 600 | |||
| 601 | try queue.add(9); | ||
| 602 | try queue.add(3); | ||
| 603 | try queue.add(2); | ||
| 604 | |||
| 605 | expectEqual(@as(u32, 9), queue.removeMax()); | ||
| 606 | expectEqual(@as(u32, 3), queue.removeMax()); | ||
| 607 | expectEqual(@as(u32, 2), queue.removeMax()); | ||
| 608 | } | ||
| 609 | |||
| 610 | test "std.PriorityDequeue: peekMin" { | ||
| 611 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 612 | defer queue.deinit(); | ||
| 613 | |||
| 614 | expect(queue.peekMin() == null); | ||
| 615 | |||
| 616 | try queue.add(9); | ||
| 617 | try queue.add(3); | ||
| 618 | try queue.add(2); | ||
| 619 | |||
| 620 | expect(queue.peekMin().? == 2); | ||
| 621 | expect(queue.peekMin().? == 2); | ||
| 622 | } | ||
| 623 | |||
| 624 | test "std.PriorityDequeue: peekMax" { | ||
| 625 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 626 | defer queue.deinit(); | ||
| 627 | |||
| 628 | expect(queue.peekMin() == null); | ||
| 629 | |||
| 630 | try queue.add(9); | ||
| 631 | try queue.add(3); | ||
| 632 | try queue.add(2); | ||
| 633 | |||
| 634 | expect(queue.peekMax().? == 9); | ||
| 635 | expect(queue.peekMax().? == 9); | ||
| 636 | } | ||
| 637 | |||
| 638 | test "std.PriorityDequeue: sift up with odd indices" { | ||
| 639 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 640 | defer queue.deinit(); | ||
| 641 | const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; | ||
| 642 | for (items) |e| { | ||
| 643 | try queue.add(e); | ||
| 644 | } | ||
| 645 | |||
| 646 | const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 }; | ||
| 647 | for (sorted_items) |e| { | ||
| 648 | expectEqual(e, queue.removeMin()); | ||
| 649 | } | ||
| 650 | } | ||
| 651 | |||
| 652 | test "std.PriorityDequeue: sift up with odd indices" { | ||
| 653 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 654 | defer queue.deinit(); | ||
| 655 | const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; | ||
| 656 | for (items) |e| { | ||
| 657 | try queue.add(e); | ||
| 658 | } | ||
| 659 | |||
| 660 | const sorted_items = [_]u32{ 25, 24, 24, 22, 21, 16, 15, 15, 14, 13, 12, 11, 7, 7, 6, 5, 2, 1 }; | ||
| 661 | for (sorted_items) |e| { | ||
| 662 | expectEqual(e, queue.removeMax()); | ||
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 666 | test "std.PriorityDequeue: addSlice min" { | ||
| 667 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 668 | defer queue.deinit(); | ||
| 669 | const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; | ||
| 670 | try queue.addSlice(items[0..]); | ||
| 671 | |||
| 672 | const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 }; | ||
| 673 | for (sorted_items) |e| { | ||
| 674 | expectEqual(e, queue.removeMin()); | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | test "std.PriorityDequeue: addSlice max" { | ||
| 679 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 680 | defer queue.deinit(); | ||
| 681 | const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; | ||
| 682 | try queue.addSlice(items[0..]); | ||
| 683 | |||
| 684 | const sorted_items = [_]u32{ 25, 24, 24, 22, 21, 16, 15, 15, 14, 13, 12, 11, 7, 7, 6, 5, 2, 1 }; | ||
| 685 | for (sorted_items) |e| { | ||
| 686 | expectEqual(e, queue.removeMax()); | ||
| 687 | } | ||
| 688 | } | ||
| 689 | |||
| 690 | test "std.PriorityDequeue: fromOwnedSlice trivial case 0" { | ||
| 691 | const items = [0]u32{}; | ||
| 692 | const queue_items = try testing.allocator.dupe(u32, &items); | ||
| 693 | var queue = PDQ.fromOwnedSlice(testing.allocator, lessThanComparison, queue_items[0..]); | ||
| 694 | defer queue.deinit(); | ||
| 695 | expectEqual(@as(usize, 0), queue.len); | ||
| 696 | expect(queue.removeMinOrNull() == null); | ||
| 697 | } | ||
| 698 | |||
| 699 | test "std.PriorityDequeue: fromOwnedSlice trivial case 1" { | ||
| 700 | const items = [1]u32{1}; | ||
| 701 | const queue_items = try testing.allocator.dupe(u32, &items); | ||
| 702 | var queue = PDQ.fromOwnedSlice(testing.allocator, lessThanComparison, queue_items[0..]); | ||
| 703 | defer queue.deinit(); | ||
| 704 | |||
| 705 | expectEqual(@as(usize, 1), queue.len); | ||
| 706 | expectEqual(items[0], queue.removeMin()); | ||
| 707 | expect(queue.removeMinOrNull() == null); | ||
| 708 | } | ||
| 709 | |||
| 710 | test "std.PriorityDequeue: fromOwnedSlice" { | ||
| 711 | const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; | ||
| 712 | const queue_items = try testing.allocator.dupe(u32, items[0..]); | ||
| 713 | var queue = PDQ.fromOwnedSlice(testing.allocator, lessThanComparison, queue_items[0..]); | ||
| 714 | defer queue.deinit(); | ||
| 715 | |||
| 716 | const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 }; | ||
| 717 | for (sorted_items) |e| { | ||
| 718 | expectEqual(e, queue.removeMin()); | ||
| 719 | } | ||
| 720 | } | ||
| 721 | |||
| 722 | test "std.PriorityDequeue: update min queue" { | ||
| 723 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 724 | defer queue.deinit(); | ||
| 725 | |||
| 726 | try queue.add(55); | ||
| 727 | try queue.add(44); | ||
| 728 | try queue.add(11); | ||
| 729 | try queue.update(55, 5); | ||
| 730 | try queue.update(44, 4); | ||
| 731 | try queue.update(11, 1); | ||
| 732 | expectEqual(@as(u32, 1), queue.removeMin()); | ||
| 733 | expectEqual(@as(u32, 4), queue.removeMin()); | ||
| 734 | expectEqual(@as(u32, 5), queue.removeMin()); | ||
| 735 | } | ||
| 736 | |||
| 737 | test "std.PriorityDequeue: update same min queue" { | ||
| 738 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 739 | defer queue.deinit(); | ||
| 740 | |||
| 741 | try queue.add(1); | ||
| 742 | try queue.add(1); | ||
| 743 | try queue.add(2); | ||
| 744 | try queue.add(2); | ||
| 745 | try queue.update(1, 5); | ||
| 746 | try queue.update(2, 4); | ||
| 747 | expectEqual(@as(u32, 1), queue.removeMin()); | ||
| 748 | expectEqual(@as(u32, 2), queue.removeMin()); | ||
| 749 | expectEqual(@as(u32, 4), queue.removeMin()); | ||
| 750 | expectEqual(@as(u32, 5), queue.removeMin()); | ||
| 751 | } | ||
| 752 | |||
| 753 | test "std.PriorityDequeue: update max queue" { | ||
| 754 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 755 | defer queue.deinit(); | ||
| 756 | |||
| 757 | try queue.add(55); | ||
| 758 | try queue.add(44); | ||
| 759 | try queue.add(11); | ||
| 760 | try queue.update(55, 5); | ||
| 761 | try queue.update(44, 1); | ||
| 762 | try queue.update(11, 4); | ||
| 763 | |||
| 764 | expectEqual(@as(u32, 5), queue.removeMax()); | ||
| 765 | expectEqual(@as(u32, 4), queue.removeMax()); | ||
| 766 | expectEqual(@as(u32, 1), queue.removeMax()); | ||
| 767 | } | ||
| 768 | |||
| 769 | test "std.PriorityDequeue: update same max queue" { | ||
| 770 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 771 | defer queue.deinit(); | ||
| 772 | |||
| 773 | try queue.add(1); | ||
| 774 | try queue.add(1); | ||
| 775 | try queue.add(2); | ||
| 776 | try queue.add(2); | ||
| 777 | try queue.update(1, 5); | ||
| 778 | try queue.update(2, 4); | ||
| 779 | expectEqual(@as(u32, 5), queue.removeMax()); | ||
| 780 | expectEqual(@as(u32, 4), queue.removeMax()); | ||
| 781 | expectEqual(@as(u32, 2), queue.removeMax()); | ||
| 782 | expectEqual(@as(u32, 1), queue.removeMax()); | ||
| 783 | } | ||
| 784 | |||
| 785 | test "std.PriorityDequeue: iterator" { | ||
| 786 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 787 | var map = std.AutoHashMap(u32, void).init(testing.allocator); | ||
| 788 | defer { | ||
| 789 | queue.deinit(); | ||
| 790 | map.deinit(); | ||
| 791 | } | ||
| 792 | |||
| 793 | const items = [_]u32{ 54, 12, 7, 23, 25, 13 }; | ||
| 794 | for (items) |e| { | ||
| 795 | _ = try queue.add(e); | ||
| 796 | _ = try map.put(e, {}); | ||
| 797 | } | ||
| 798 | |||
| 799 | var it = queue.iterator(); | ||
| 800 | while (it.next()) |e| { | ||
| 801 | _ = map.remove(e); | ||
| 802 | } | ||
| 803 | |||
| 804 | expectEqual(@as(usize, 0), map.count()); | ||
| 805 | } | ||
| 806 | |||
| 807 | test "std.PriorityDequeue: remove at index" { | ||
| 808 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 809 | defer queue.deinit(); | ||
| 810 | |||
| 811 | try queue.add(3); | ||
| 812 | try queue.add(2); | ||
| 813 | try queue.add(1); | ||
| 814 | |||
| 815 | var it = queue.iterator(); | ||
| 816 | var elem = it.next(); | ||
| 817 | var idx: usize = 0; | ||
| 818 | const two_idx = while (elem != null) : (elem = it.next()) { | ||
| 819 | if (elem.? == 2) | ||
| 820 | break idx; | ||
| 821 | idx += 1; | ||
| 822 | } else unreachable; | ||
| 823 | |||
| 824 | expectEqual(queue.removeIndex(two_idx), 2); | ||
| 825 | expectEqual(queue.removeMin(), 1); | ||
| 826 | expectEqual(queue.removeMin(), 3); | ||
| 827 | expectEqual(queue.removeMinOrNull(), null); | ||
| 828 | } | ||
| 829 | |||
| 830 | test "std.PriorityDequeue: iterator while empty" { | ||
| 831 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 832 | defer queue.deinit(); | ||
| 833 | |||
| 834 | var it = queue.iterator(); | ||
| 835 | |||
| 836 | expectEqual(it.next(), null); | ||
| 837 | } | ||
| 838 | |||
| 839 | test "std.PriorityDequeue: shrinkRetainingCapacity and shrinkAndFree" { | ||
| 840 | var queue = PDQ.init(testing.allocator, lessThanComparison); | ||
| 841 | defer queue.deinit(); | ||
| 842 | |||
| 843 | try queue.ensureCapacity(4); | ||
| 844 | expect(queue.capacity() >= 4); | ||
| 845 | |||
| 846 | try queue.add(1); | ||
| 847 | try queue.add(2); | ||
| 848 | try queue.add(3); | ||
| 849 | expect(queue.capacity() >= 4); | ||
| 850 | expectEqual(@as(usize, 3), queue.len); | ||
| 851 | |||
| 852 | queue.shrinkRetainingCapacity(3); | ||
| 853 | expect(queue.capacity() >= 4); | ||
| 854 | expectEqual(@as(usize, 3), queue.len); | ||
| 855 | |||
| 856 | queue.shrinkAndFree(3); | ||
| 857 | expectEqual(@as(usize, 3), queue.capacity()); | ||
| 858 | expectEqual(@as(usize, 3), queue.len); | ||
| 859 | |||
| 860 | expectEqual(@as(u32, 3), queue.removeMax()); | ||
| 861 | expectEqual(@as(u32, 2), queue.removeMax()); | ||
| 862 | expectEqual(@as(u32, 1), queue.removeMax()); | ||
| 863 | expect(queue.removeMaxOrNull() == null); | ||
| 864 | } | ||
| 865 | |||
| 866 | test "std.PriorityDequeue: fuzz testing min" { | ||
| 867 | var prng = std.rand.DefaultPrng.init(0x12345678); | ||
| 868 | |||
| 869 | const test_case_count = 100; | ||
| 870 | const queue_size = 1_000; | ||
| 871 | |||
| 872 | var i: usize = 0; | ||
| 873 | while (i < test_case_count) : (i += 1) { | ||
| 874 | try fuzzTestMin(&prng.random, queue_size); | ||
| 875 | } | ||
| 876 | } | ||
| 877 | |||
| 878 | fn fuzzTestMin(rng: *std.rand.Random, comptime queue_size: usize) !void { | ||
| 879 | const allocator = testing.allocator; | ||
| 880 | const items = try generateRandomSlice(allocator, rng, queue_size); | ||
| 881 | |||
| 882 | var queue = PDQ.fromOwnedSlice(allocator, lessThanComparison, items); | ||
| 883 | defer queue.deinit(); | ||
| 884 | |||
| 885 | var last_removed: ?u32 = null; | ||
| 886 | while (queue.removeMinOrNull()) |next| { | ||
| 887 | if (last_removed) |last| { | ||
| 888 | expect(last <= next); | ||
| 889 | } | ||
| 890 | last_removed = next; | ||
| 891 | } | ||
| 892 | } | ||
| 893 | |||
| 894 | test "std.PriorityDequeue: fuzz testing max" { | ||
| 895 | var prng = std.rand.DefaultPrng.init(0x87654321); | ||
| 896 | |||
| 897 | const test_case_count = 100; | ||
| 898 | const queue_size = 1_000; | ||
| 899 | |||
| 900 | var i: usize = 0; | ||
| 901 | while (i < test_case_count) : (i += 1) { | ||
| 902 | try fuzzTestMax(&prng.random, queue_size); | ||
| 903 | } | ||
| 904 | } | ||
| 905 | |||
| 906 | fn fuzzTestMax(rng: *std.rand.Random, queue_size: usize) !void { | ||
| 907 | const allocator = testing.allocator; | ||
| 908 | const items = try generateRandomSlice(allocator, rng, queue_size); | ||
| 909 | |||
| 910 | var queue = PDQ.fromOwnedSlice(testing.allocator, lessThanComparison, items); | ||
| 911 | defer queue.deinit(); | ||
| 912 | |||
| 913 | var last_removed: ?u32 = null; | ||
| 914 | while (queue.removeMaxOrNull()) |next| { | ||
| 915 | if (last_removed) |last| { | ||
| 916 | expect(last >= next); | ||
| 917 | } | ||
| 918 | last_removed = next; | ||
| 919 | } | ||
| 920 | } | ||
| 921 | |||
| 922 | test "std.PriorityDequeue: fuzz testing min and max" { | ||
| 923 | var prng = std.rand.DefaultPrng.init(0x87654321); | ||
| 924 | |||
| 925 | const test_case_count = 100; | ||
| 926 | const queue_size = 1_000; | ||
| 927 | |||
| 928 | var i: usize = 0; | ||
| 929 | while (i < test_case_count) : (i += 1) { | ||
| 930 | try fuzzTestMinMax(&prng.random, queue_size); | ||
| 931 | } | ||
| 932 | } | ||
| 933 | |||
| 934 | fn fuzzTestMinMax(rng: *std.rand.Random, queue_size: usize) !void { | ||
| 935 | const allocator = testing.allocator; | ||
| 936 | const items = try generateRandomSlice(allocator, rng, queue_size); | ||
| 937 | |||
| 938 | var queue = PDQ.fromOwnedSlice(allocator, lessThanComparison, items); | ||
| 939 | defer queue.deinit(); | ||
| 940 | |||
| 941 | var last_min: ?u32 = null; | ||
| 942 | var last_max: ?u32 = null; | ||
| 943 | var i: usize = 0; | ||
| 944 | while (i < queue_size) : (i += 1) { | ||
| 945 | if (i % 2 == 0) { | ||
| 946 | const next = queue.removeMin(); | ||
| 947 | if (last_min) |last| { | ||
| 948 | expect(last <= next); | ||
| 949 | } | ||
| 950 | last_min = next; | ||
| 951 | } else { | ||
| 952 | const next = queue.removeMax(); | ||
| 953 | if (last_max) |last| { | ||
| 954 | expect(last >= next); | ||
| 955 | } | ||
| 956 | last_max = next; | ||
| 957 | } | ||
| 958 | } | ||
| 959 | } | ||
| 960 | |||
| 961 | fn generateRandomSlice(allocator: *std.mem.Allocator, rng: *std.rand.Random, size: usize) ![]u32 { | ||
| 962 | var array = std.ArrayList(u32).init(allocator); | ||
| 963 | try array.ensureCapacity(size); | ||
| 964 | |||
| 965 | var i: usize = 0; | ||
| 966 | while (i < size) : (i += 1) { | ||
| 967 | const elem = rng.int(u32); | ||
| 968 | try array.append(elem); | ||
| 969 | } | ||
| 970 | |||
| 971 | return array.toOwnedSlice(); | ||
| 972 | } | ||
lib/std/priority_queue.zig+95-26| ... | @@ -6,6 +6,8 @@ | ... | @@ -6,6 +6,8 @@ |
| 6 | const std = @import("std.zig"); | 6 | const std = @import("std.zig"); |
| 7 | const Allocator = std.mem.Allocator; | 7 | const Allocator = std.mem.Allocator; |
| 8 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 9 | const warn = std.debug.warn; | ||
| 10 | const Order = std.math.Order; | ||
| 9 | const testing = std.testing; | 11 | const testing = std.testing; |
| 10 | const expect = testing.expect; | 12 | const expect = testing.expect; |
| 11 | const expectEqual = testing.expectEqual; | 13 | const expectEqual = testing.expectEqual; |
| ... | @@ -19,15 +21,17 @@ pub fn PriorityQueue(comptime T: type) type { | ... | @@ -19,15 +21,17 @@ pub fn PriorityQueue(comptime T: type) type { |
| 19 | items: []T, | 21 | items: []T, |
| 20 | len: usize, | 22 | len: usize, |
| 21 | allocator: *Allocator, | 23 | allocator: *Allocator, |
| 22 | compareFn: fn (a: T, b: T) bool, | 24 | compareFn: fn (a: T, b: T) Order, |
| 23 | 25 | ||
| 24 | /// Initialize and return a priority queue. Provide | 26 | /// Initialize and return a priority queue. Provide `compareFn` |
| 25 | /// `compareFn` that returns `true` when its first argument | 27 | /// that returns `Order.lt` when its first argument should |
| 26 | /// should get popped before its second argument. For example, | 28 | /// get popped before its second argument, `Order.eq` if the |
| 27 | /// to make `pop` return the minimum value, provide | 29 | /// arguments are of equal priority, or `Order.gt` if the second |
| 30 | /// argument should be popped first. For example, to make `pop` | ||
| 31 | /// return the smallest number, provide | ||
| 28 | /// | 32 | /// |
| 29 | /// `fn lessThan(a: T, b: T) bool { return a < b; }` | 33 | /// `fn lessThan(a: T, b: T) Order { return std.math.order(a, b); }` |
| 30 | pub fn init(allocator: *Allocator, compareFn: fn (a: T, b: T) bool) Self { | 34 | pub fn init(allocator: *Allocator, compareFn: fn (a: T, b: T) Order) Self { |
| 31 | return Self{ | 35 | return Self{ |
| 32 | .items = &[_]T{}, | 36 | .items = &[_]T{}, |
| 33 | .len = 0, | 37 | .len = 0, |
| ... | @@ -60,7 +64,7 @@ pub fn PriorityQueue(comptime T: type) type { | ... | @@ -60,7 +64,7 @@ pub fn PriorityQueue(comptime T: type) type { |
| 60 | const child = self.items[child_index]; | 64 | const child = self.items[child_index]; |
| 61 | const parent = self.items[parent_index]; | 65 | const parent = self.items[parent_index]; |
| 62 | 66 | ||
| 63 | if (!self.compareFn(child, parent)) break; | 67 | if (self.compareFn(child, parent) != .lt) break; |
| 64 | 68 | ||
| 65 | self.items[parent_index] = child; | 69 | self.items[parent_index] = child; |
| 66 | self.items[child_index] = parent; | 70 | self.items[child_index] = parent; |
| ... | @@ -132,14 +136,14 @@ pub fn PriorityQueue(comptime T: type) type { | ... | @@ -132,14 +136,14 @@ pub fn PriorityQueue(comptime T: type) type { |
| 132 | var smallest = self.items[index]; | 136 | var smallest = self.items[index]; |
| 133 | 137 | ||
| 134 | if (left) |e| { | 138 | if (left) |e| { |
| 135 | if (self.compareFn(e, smallest)) { | 139 | if (self.compareFn(e, smallest) == .lt) { |
| 136 | smallest_index = left_index; | 140 | smallest_index = left_index; |
| 137 | smallest = e; | 141 | smallest = e; |
| 138 | } | 142 | } |
| 139 | } | 143 | } |
| 140 | 144 | ||
| 141 | if (right) |e| { | 145 | if (right) |e| { |
| 142 | if (self.compareFn(e, smallest)) { | 146 | if (self.compareFn(e, smallest) == .lt) { |
| 143 | smallest_index = right_index; | 147 | smallest_index = right_index; |
| 144 | smallest = e; | 148 | smallest = e; |
| 145 | } | 149 | } |
| ... | @@ -158,13 +162,16 @@ pub fn PriorityQueue(comptime T: type) type { | ... | @@ -158,13 +162,16 @@ pub fn PriorityQueue(comptime T: type) type { |
| 158 | /// PriorityQueue takes ownership of the passed in slice. The slice must have been | 162 | /// PriorityQueue takes ownership of the passed in slice. The slice must have been |
| 159 | /// allocated with `allocator`. | 163 | /// allocated with `allocator`. |
| 160 | /// Deinitialize with `deinit`. | 164 | /// Deinitialize with `deinit`. |
| 161 | pub fn fromOwnedSlice(allocator: *Allocator, compareFn: fn (a: T, b: T) bool, items: []T) Self { | 165 | pub fn fromOwnedSlice(allocator: *Allocator, compareFn: fn (a: T, b: T) Order, items: []T) Self { |
| 162 | var queue = Self{ | 166 | var queue = Self{ |
| 163 | .items = items, | 167 | .items = items, |
| 164 | .len = items.len, | 168 | .len = items.len, |
| 165 | .allocator = allocator, | 169 | .allocator = allocator, |
| 166 | .compareFn = compareFn, | 170 | .compareFn = compareFn, |
| 167 | }; | 171 | }; |
| 172 | |||
| 173 | if (queue.len <= 1) return queue; | ||
| 174 | |||
| 168 | const half = (queue.len >> 1) - 1; | 175 | const half = (queue.len >> 1) - 1; |
| 169 | var i: usize = 0; | 176 | var i: usize = 0; |
| 170 | while (i <= half) : (i += 1) { | 177 | while (i <= half) : (i += 1) { |
| ... | @@ -183,25 +190,40 @@ pub fn PriorityQueue(comptime T: type) type { | ... | @@ -183,25 +190,40 @@ pub fn PriorityQueue(comptime T: type) type { |
| 183 | self.items = try self.allocator.realloc(self.items, better_capacity); | 190 | self.items = try self.allocator.realloc(self.items, better_capacity); |
| 184 | } | 191 | } |
| 185 | 192 | ||
| 186 | pub fn resize(self: *Self, new_len: usize) !void { | 193 | /// Reduce allocated capacity to `new_len`. |
| 187 | try self.ensureCapacity(new_len); | 194 | pub fn shrinkAndFree(self: *Self, new_len: usize) void { |
| 195 | assert(new_len <= self.items.len); | ||
| 196 | |||
| 197 | // Cannot shrink to smaller than the current queue size without invalidating the heap property | ||
| 198 | assert(new_len >= self.len); | ||
| 199 | |||
| 200 | self.items = self.allocator.realloc(self.items[0..], new_len) catch |e| switch (e) { | ||
| 201 | error.OutOfMemory => { // no problem, capacity is still correct then. | ||
| 202 | self.items.len = new_len; | ||
| 203 | return; | ||
| 204 | }, | ||
| 205 | }; | ||
| 188 | self.len = new_len; | 206 | self.len = new_len; |
| 189 | } | 207 | } |
| 190 | 208 | ||
| 191 | pub fn shrink(self: *Self, new_len: usize) void { | 209 | /// Reduce length to `new_len`. |
| 192 | // TODO take advantage of the new realloc semantics | 210 | pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { |
| 193 | assert(new_len <= self.len); | 211 | assert(new_len <= self.items.len); |
| 212 | |||
| 213 | // Cannot shrink to smaller than the current queue size without invalidating the heap property | ||
| 214 | assert(new_len >= self.len); | ||
| 215 | |||
| 194 | self.len = new_len; | 216 | self.len = new_len; |
| 195 | } | 217 | } |
| 196 | 218 | ||
| 197 | pub fn update(self: *Self, elem: T, new_elem: T) !void { | 219 | pub fn update(self: *Self, elem: T, new_elem: T) !void { |
| 198 | var update_index: usize = std.mem.indexOfScalar(T, self.items, elem) orelse return error.ElementNotFound; | 220 | var update_index: usize = std.mem.indexOfScalar(T, self.items[0..self.len], elem) orelse return error.ElementNotFound; |
| 199 | const old_elem: T = self.items[update_index]; | 221 | const old_elem: T = self.items[update_index]; |
| 200 | self.items[update_index] = new_elem; | 222 | self.items[update_index] = new_elem; |
| 201 | if (self.compareFn(new_elem, old_elem)) { | 223 | switch (self.compareFn(new_elem, old_elem)) { |
| 202 | siftUp(self, update_index); | 224 | .lt => siftUp(self, update_index), |
| 203 | } else { | 225 | .gt => siftDown(self, update_index), |
| 204 | siftDown(self, update_index); | 226 | .eq => {}, // Nothing to do as the items have equal priority |
| 205 | } | 227 | } |
| 206 | } | 228 | } |
| 207 | 229 | ||
| ... | @@ -248,12 +270,12 @@ pub fn PriorityQueue(comptime T: type) type { | ... | @@ -248,12 +270,12 @@ pub fn PriorityQueue(comptime T: type) type { |
| 248 | }; | 270 | }; |
| 249 | } | 271 | } |
| 250 | 272 | ||
| 251 | fn lessThan(a: u32, b: u32) bool { | 273 | fn lessThan(a: u32, b: u32) Order { |
| 252 | return a < b; | 274 | return std.math.order(a, b); |
| 253 | } | 275 | } |
| 254 | 276 | ||
| 255 | fn greaterThan(a: u32, b: u32) bool { | 277 | fn greaterThan(a: u32, b: u32) Order { |
| 256 | return a > b; | 278 | return lessThan(a, b).invert(); |
| 257 | } | 279 | } |
| 258 | 280 | ||
| 259 | const PQ = PriorityQueue(u32); | 281 | const PQ = PriorityQueue(u32); |
| ... | @@ -351,6 +373,26 @@ test "std.PriorityQueue: addSlice" { | ... | @@ -351,6 +373,26 @@ test "std.PriorityQueue: addSlice" { |
| 351 | } | 373 | } |
| 352 | } | 374 | } |
| 353 | 375 | ||
| 376 | test "std.PriorityQueue: fromOwnedSlice trivial case 0" { | ||
| 377 | const items = [0]u32{}; | ||
| 378 | const queue_items = try testing.allocator.dupe(u32, &items); | ||
| 379 | var queue = PQ.fromOwnedSlice(testing.allocator, lessThan, queue_items[0..]); | ||
| 380 | defer queue.deinit(); | ||
| 381 | expectEqual(@as(usize, 0), queue.len); | ||
| 382 | expect(queue.removeOrNull() == null); | ||
| 383 | } | ||
| 384 | |||
| 385 | test "std.PriorityQueue: fromOwnedSlice trivial case 1" { | ||
| 386 | const items = [1]u32{1}; | ||
| 387 | const queue_items = try testing.allocator.dupe(u32, &items); | ||
| 388 | var queue = PQ.fromOwnedSlice(testing.allocator, lessThan, queue_items[0..]); | ||
| 389 | defer queue.deinit(); | ||
| 390 | |||
| 391 | expectEqual(@as(usize, 1), queue.len); | ||
| 392 | expectEqual(items[0], queue.remove()); | ||
| 393 | expect(queue.removeOrNull() == null); | ||
| 394 | } | ||
| 395 | |||
| 354 | test "std.PriorityQueue: fromOwnedSlice" { | 396 | test "std.PriorityQueue: fromOwnedSlice" { |
| 355 | const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; | 397 | const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; |
| 356 | const heap_items = try testing.allocator.dupe(u32, items[0..]); | 398 | const heap_items = try testing.allocator.dupe(u32, items[0..]); |
| ... | @@ -453,6 +495,33 @@ test "std.PriorityQueue: iterator while empty" { | ... | @@ -453,6 +495,33 @@ test "std.PriorityQueue: iterator while empty" { |
| 453 | expectEqual(it.next(), null); | 495 | expectEqual(it.next(), null); |
| 454 | } | 496 | } |
| 455 | 497 | ||
| 498 | test "std.PriorityQueue: shrinkRetainingCapacity and shrinkAndFree" { | ||
| 499 | var queue = PQ.init(testing.allocator, lessThan); | ||
| 500 | defer queue.deinit(); | ||
| 501 | |||
| 502 | try queue.ensureCapacity(4); | ||
| 503 | expect(queue.capacity() >= 4); | ||
| 504 | |||
| 505 | try queue.add(1); | ||
| 506 | try queue.add(2); | ||
| 507 | try queue.add(3); | ||
| 508 | expect(queue.capacity() >= 4); | ||
| 509 | expectEqual(@as(usize, 3), queue.len); | ||
| 510 | |||
| 511 | queue.shrinkRetainingCapacity(3); | ||
| 512 | expect(queue.capacity() >= 4); | ||
| 513 | expectEqual(@as(usize, 3), queue.len); | ||
| 514 | |||
| 515 | queue.shrinkAndFree(3); | ||
| 516 | expectEqual(@as(usize, 3), queue.capacity()); | ||
| 517 | expectEqual(@as(usize, 3), queue.len); | ||
| 518 | |||
| 519 | expectEqual(@as(u32, 1), queue.remove()); | ||
| 520 | expectEqual(@as(u32, 2), queue.remove()); | ||
| 521 | expectEqual(@as(u32, 3), queue.remove()); | ||
| 522 | expect(queue.removeOrNull() == null); | ||
| 523 | } | ||
| 524 | |||
| 456 | test "std.PriorityQueue: update min heap" { | 525 | test "std.PriorityQueue: update min heap" { |
| 457 | var queue = PQ.init(testing.allocator, lessThan); | 526 | var queue = PQ.init(testing.allocator, lessThan); |
| 458 | defer queue.deinit(); | 527 | defer queue.deinit(); |
lib/std/rand/Isaac64.zig+32| ... | @@ -208,3 +208,35 @@ test "isaac64 sequence" { | ... | @@ -208,3 +208,35 @@ test "isaac64 sequence" { |
| 208 | std.testing.expect(s == r.next()); | 208 | std.testing.expect(s == r.next()); |
| 209 | } | 209 | } |
| 210 | } | 210 | } |
| 211 | |||
| 212 | test "isaac64 fill" { | ||
| 213 | var r = Isaac64.init(0); | ||
| 214 | |||
| 215 | // from reference implementation | ||
| 216 | const seq = [_]u64{ | ||
| 217 | 0xf67dfba498e4937c, | ||
| 218 | 0x84a5066a9204f380, | ||
| 219 | 0xfee34bd5f5514dbb, | ||
| 220 | 0x4d1664739b8f80d6, | ||
| 221 | 0x8607459ab52a14aa, | ||
| 222 | 0x0e78bc5a98529e49, | ||
| 223 | 0xfe5332822ad13777, | ||
| 224 | 0x556c27525e33d01a, | ||
| 225 | 0x08643ca615f3149f, | ||
| 226 | 0xd0771faf3cb04714, | ||
| 227 | 0x30e86f68a37b008d, | ||
| 228 | 0x3074ebc0488a3adf, | ||
| 229 | 0x270645ea7a2790bc, | ||
| 230 | 0x5601a0a8d3763c6a, | ||
| 231 | 0x2f83071f53f325dd, | ||
| 232 | 0xb9090f3d42d2d2ea, | ||
| 233 | }; | ||
| 234 | |||
| 235 | for (seq) |s| { | ||
| 236 | var buf0: [8]u8 = undefined; | ||
| 237 | var buf1: [7]u8 = undefined; | ||
| 238 | std.mem.writeIntLittle(u64, &buf0, s); | ||
| 239 | Isaac64.fill(&r.random, &buf1); | ||
| 240 | std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | ||
| 241 | } | ||
| 242 | } |
lib/std/rand/Pcg.zig+25-1| ... | @@ -75,7 +75,7 @@ fn fill(r: *Random, buf: []u8) void { | ... | @@ -75,7 +75,7 @@ fn fill(r: *Random, buf: []u8) void { |
| 75 | var n = self.next(); | 75 | var n = self.next(); |
| 76 | while (i < buf.len) : (i += 1) { | 76 | while (i < buf.len) : (i += 1) { |
| 77 | buf[i] = @truncate(u8, n); | 77 | buf[i] = @truncate(u8, n); |
| 78 | n >>= 4; | 78 | n >>= 8; |
| 79 | } | 79 | } |
| 80 | } | 80 | } |
| 81 | } | 81 | } |
| ... | @@ -99,3 +99,27 @@ test "pcg sequence" { | ... | @@ -99,3 +99,27 @@ test "pcg sequence" { |
| 99 | std.testing.expect(s == r.next()); | 99 | std.testing.expect(s == r.next()); |
| 100 | } | 100 | } |
| 101 | } | 101 | } |
| 102 | |||
| 103 | test "pcg fill" { | ||
| 104 | var r = Pcg.init(0); | ||
| 105 | const s0: u64 = 0x9394bf54ce5d79de; | ||
| 106 | const s1: u64 = 0x84e9c579ef59bbf7; | ||
| 107 | r.seedTwo(s0, s1); | ||
| 108 | |||
| 109 | const seq = [_]u32{ | ||
| 110 | 2881561918, | ||
| 111 | 3063928540, | ||
| 112 | 1199791034, | ||
| 113 | 2487695858, | ||
| 114 | 1479648952, | ||
| 115 | 3247963454, | ||
| 116 | }; | ||
| 117 | |||
| 118 | for (seq) |s| { | ||
| 119 | var buf0: [4]u8 = undefined; | ||
| 120 | var buf1: [3]u8 = undefined; | ||
| 121 | std.mem.writeIntLittle(u32, &buf0, s); | ||
| 122 | Pcg.fill(&r.random, &buf1); | ||
| 123 | std.testing.expect(std.mem.eql(u8, buf0[0..3], buf1[0..])); | ||
| 124 | } | ||
| 125 | } |
lib/std/rand/Sfc64.zig+32| ... | @@ -106,3 +106,35 @@ test "Sfc64 sequence" { | ... | @@ -106,3 +106,35 @@ test "Sfc64 sequence" { |
| 106 | std.testing.expectEqual(s, r.next()); | 106 | std.testing.expectEqual(s, r.next()); |
| 107 | } | 107 | } |
| 108 | } | 108 | } |
| 109 | |||
| 110 | test "Sfc64 fill" { | ||
| 111 | // Unfortunately there does not seem to be an official test sequence. | ||
| 112 | var r = Sfc64.init(0); | ||
| 113 | |||
| 114 | const seq = [_]u64{ | ||
| 115 | 0x3acfa029e3cc6041, | ||
| 116 | 0xf5b6515bf2ee419c, | ||
| 117 | 0x1259635894a29b61, | ||
| 118 | 0xb6ae75395f8ebd6, | ||
| 119 | 0x225622285ce302e2, | ||
| 120 | 0x520d28611395cb21, | ||
| 121 | 0xdb909c818901599d, | ||
| 122 | 0x8ffd195365216f57, | ||
| 123 | 0xe8c4ad5e258ac04a, | ||
| 124 | 0x8f8ef2c89fdb63ca, | ||
| 125 | 0xf9865b01d98d8e2f, | ||
| 126 | 0x46555871a65d08ba, | ||
| 127 | 0x66868677c6298fcd, | ||
| 128 | 0x2ce15a7e6329f57d, | ||
| 129 | 0xb2f1833ca91ca79, | ||
| 130 | 0x4b0890ac9bf453ca, | ||
| 131 | }; | ||
| 132 | |||
| 133 | for (seq) |s| { | ||
| 134 | var buf0: [8]u8 = undefined; | ||
| 135 | var buf1: [7]u8 = undefined; | ||
| 136 | std.mem.writeIntLittle(u64, &buf0, s); | ||
| 137 | Sfc64.fill(&r.random, &buf1); | ||
| 138 | std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | ||
| 139 | } | ||
| 140 | } |
lib/std/rand/Xoroshiro128.zig+23| ... | @@ -131,3 +131,26 @@ test "xoroshiro sequence" { | ... | @@ -131,3 +131,26 @@ test "xoroshiro sequence" { |
| 131 | std.testing.expect(s == r.next()); | 131 | std.testing.expect(s == r.next()); |
| 132 | } | 132 | } |
| 133 | } | 133 | } |
| 134 | |||
| 135 | test "xoroshiro fill" { | ||
| 136 | var r = Xoroshiro128.init(0); | ||
| 137 | r.s[0] = 0xaeecf86f7878dd75; | ||
| 138 | r.s[1] = 0x01cd153642e72622; | ||
| 139 | |||
| 140 | const seq = [_]u64{ | ||
| 141 | 0xb0ba0da5bb600397, | ||
| 142 | 0x18a08afde614dccc, | ||
| 143 | 0xa2635b956a31b929, | ||
| 144 | 0xabe633c971efa045, | ||
| 145 | 0x9ac19f9706ca3cac, | ||
| 146 | 0xf62b426578c1e3fb, | ||
| 147 | }; | ||
| 148 | |||
| 149 | for (seq) |s| { | ||
| 150 | var buf0: [8]u8 = undefined; | ||
| 151 | var buf1: [7]u8 = undefined; | ||
| 152 | std.mem.writeIntLittle(u64, &buf0, s); | ||
| 153 | Xoroshiro128.fill(&r.random, &buf1); | ||
| 154 | std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | ||
| 155 | } | ||
| 156 | } |
lib/std/special/docs/index.html+1| ... | @@ -515,6 +515,7 @@ | ... | @@ -515,6 +515,7 @@ |
| 515 | </style> | 515 | </style> |
| 516 | </head> | 516 | </head> |
| 517 | <body class="canvas"> | 517 | <body class="canvas"> |
| 518 | <div style="background-color: darkred; width: 100vw; text-align: center; color: white; padding: 15px 5px;">These docs are experimental. <a style="color: bisque;text-decoration: underline;" href="https://kristoff.it/blog/zig-new-relationship-llvm/">Progress depends on the self-hosted compiler</a>, <a style="color: bisque;text-decoration: underline;" href="https://github.com/ziglang/zig/wiki/How-to-read-the-standard-library-source-code">consider reading the stlib source in the meantime</a>.</div> | ||
| 518 | <div class="flex-main"> | 519 | <div class="flex-main"> |
| 519 | <div class="flex-filler"></div> | 520 | <div class="flex-filler"></div> |
| 520 | <div class="flex-left sidebar"> | 521 | <div class="flex-left sidebar"> |
lib/std/std.zig+1| ... | @@ -31,6 +31,7 @@ pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayE | ... | @@ -31,6 +31,7 @@ pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayE |
| 31 | pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice; | 31 | pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice; |
| 32 | pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian; | 32 | pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian; |
| 33 | pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; | 33 | pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; |
| 34 | pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue; | ||
| 34 | pub const Progress = @import("Progress.zig"); | 35 | pub const Progress = @import("Progress.zig"); |
| 35 | pub const SemanticVersion = @import("SemanticVersion.zig"); | 36 | pub const SemanticVersion = @import("SemanticVersion.zig"); |
| 36 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; | 37 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; |
lib/std/zig.zig+1-1| ... | @@ -11,7 +11,7 @@ pub const Tokenizer = tokenizer.Tokenizer; | ... | @@ -11,7 +11,7 @@ pub const Tokenizer = tokenizer.Tokenizer; |
| 11 | pub const fmtId = @import("zig/fmt.zig").fmtId; | 11 | pub const fmtId = @import("zig/fmt.zig").fmtId; |
| 12 | pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes; | 12 | pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes; |
| 13 | pub const parse = @import("zig/parse.zig").parse; | 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 | pub const ast = @import("zig/ast.zig"); | 15 | pub const ast = @import("zig/ast.zig"); |
| 16 | pub const system = @import("zig/system.zig"); | 16 | pub const system = @import("zig/system.zig"); |
| 17 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; | 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,6 +1252,7 @@ pub const Tree = struct { |
| 1252 | buffer[0] = data.lhs; | 1252 | buffer[0] = data.lhs; |
| 1253 | const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1]; | 1253 | const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1]; |
| 1254 | return tree.fullFnProto(.{ | 1254 | return tree.fullFnProto(.{ |
| 1255 | .proto_node = node, | ||
| 1255 | .fn_token = tree.nodes.items(.main_token)[node], | 1256 | .fn_token = tree.nodes.items(.main_token)[node], |
| 1256 | .return_type = data.rhs, | 1257 | .return_type = data.rhs, |
| 1257 | .params = params, | 1258 | .params = params, |
| ... | @@ -1267,6 +1268,7 @@ pub const Tree = struct { | ... | @@ -1267,6 +1268,7 @@ pub const Tree = struct { |
| 1267 | const params_range = tree.extraData(data.lhs, Node.SubRange); | 1268 | const params_range = tree.extraData(data.lhs, Node.SubRange); |
| 1268 | const params = tree.extra_data[params_range.start..params_range.end]; | 1269 | const params = tree.extra_data[params_range.start..params_range.end]; |
| 1269 | return tree.fullFnProto(.{ | 1270 | return tree.fullFnProto(.{ |
| 1271 | .proto_node = node, | ||
| 1270 | .fn_token = tree.nodes.items(.main_token)[node], | 1272 | .fn_token = tree.nodes.items(.main_token)[node], |
| 1271 | .return_type = data.rhs, | 1273 | .return_type = data.rhs, |
| 1272 | .params = params, | 1274 | .params = params, |
| ... | @@ -1283,6 +1285,7 @@ pub const Tree = struct { | ... | @@ -1283,6 +1285,7 @@ pub const Tree = struct { |
| 1283 | buffer[0] = extra.param; | 1285 | buffer[0] = extra.param; |
| 1284 | const params = if (extra.param == 0) buffer[0..0] else buffer[0..1]; | 1286 | const params = if (extra.param == 0) buffer[0..0] else buffer[0..1]; |
| 1285 | return tree.fullFnProto(.{ | 1287 | return tree.fullFnProto(.{ |
| 1288 | .proto_node = node, | ||
| 1286 | .fn_token = tree.nodes.items(.main_token)[node], | 1289 | .fn_token = tree.nodes.items(.main_token)[node], |
| 1287 | .return_type = data.rhs, | 1290 | .return_type = data.rhs, |
| 1288 | .params = params, | 1291 | .params = params, |
| ... | @@ -1298,6 +1301,7 @@ pub const Tree = struct { | ... | @@ -1298,6 +1301,7 @@ pub const Tree = struct { |
| 1298 | const extra = tree.extraData(data.lhs, Node.FnProto); | 1301 | const extra = tree.extraData(data.lhs, Node.FnProto); |
| 1299 | const params = tree.extra_data[extra.params_start..extra.params_end]; | 1302 | const params = tree.extra_data[extra.params_start..extra.params_end]; |
| 1300 | return tree.fullFnProto(.{ | 1303 | return tree.fullFnProto(.{ |
| 1304 | .proto_node = node, | ||
| 1301 | .fn_token = tree.nodes.items(.main_token)[node], | 1305 | .fn_token = tree.nodes.items(.main_token)[node], |
| 1302 | .return_type = data.rhs, | 1306 | .return_type = data.rhs, |
| 1303 | .params = params, | 1307 | .params = params, |
| ... | @@ -1430,7 +1434,7 @@ pub const Tree = struct { | ... | @@ -1430,7 +1434,7 @@ pub const Tree = struct { |
| 1430 | .ast = .{ | 1434 | .ast = .{ |
| 1431 | .lbracket = tree.nodes.items(.main_token)[node], | 1435 | .lbracket = tree.nodes.items(.main_token)[node], |
| 1432 | .elem_count = data.lhs, | 1436 | .elem_count = data.lhs, |
| 1433 | .sentinel = null, | 1437 | .sentinel = 0, |
| 1434 | .elem_type = data.rhs, | 1438 | .elem_type = data.rhs, |
| 1435 | }, | 1439 | }, |
| 1436 | }; | 1440 | }; |
| ... | @@ -1440,6 +1444,7 @@ pub const Tree = struct { | ... | @@ -1440,6 +1444,7 @@ pub const Tree = struct { |
| 1440 | assert(tree.nodes.items(.tag)[node] == .array_type_sentinel); | 1444 | assert(tree.nodes.items(.tag)[node] == .array_type_sentinel); |
| 1441 | const data = tree.nodes.items(.data)[node]; | 1445 | const data = tree.nodes.items(.data)[node]; |
| 1442 | const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel); | 1446 | const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel); |
| 1447 | assert(extra.sentinel != 0); | ||
| 1443 | return .{ | 1448 | return .{ |
| 1444 | .ast = .{ | 1449 | .ast = .{ |
| 1445 | .lbracket = tree.nodes.items(.main_token)[node], | 1450 | .lbracket = tree.nodes.items(.main_token)[node], |
| ... | @@ -2119,6 +2124,7 @@ pub const full = struct { | ... | @@ -2119,6 +2124,7 @@ pub const full = struct { |
| 2119 | ast: Ast, | 2124 | ast: Ast, |
| 2120 | 2125 | ||
| 2121 | pub const Ast = struct { | 2126 | pub const Ast = struct { |
| 2127 | proto_node: Node.Index, | ||
| 2122 | fn_token: TokenIndex, | 2128 | fn_token: TokenIndex, |
| 2123 | return_type: Node.Index, | 2129 | return_type: Node.Index, |
| 2124 | params: []const Node.Index, | 2130 | params: []const Node.Index, |
| ... | @@ -2262,7 +2268,7 @@ pub const full = struct { | ... | @@ -2262,7 +2268,7 @@ pub const full = struct { |
| 2262 | pub const Ast = struct { | 2268 | pub const Ast = struct { |
| 2263 | lbracket: TokenIndex, | 2269 | lbracket: TokenIndex, |
| 2264 | elem_count: Node.Index, | 2270 | elem_count: Node.Index, |
| 2265 | sentinel: ?Node.Index, | 2271 | sentinel: Node.Index, |
| 2266 | elem_type: Node.Index, | 2272 | elem_type: Node.Index, |
| 2267 | }; | 2273 | }; |
| 2268 | }; | 2274 | }; |
| ... | @@ -2549,9 +2555,9 @@ pub const Node = struct { | ... | @@ -2549,9 +2555,9 @@ pub const Node = struct { |
| 2549 | @"await", | 2555 | @"await", |
| 2550 | /// `?lhs`. rhs unused. main_token is the `?`. | 2556 | /// `?lhs`. rhs unused. main_token is the `?`. |
| 2551 | optional_type, | 2557 | optional_type, |
| 2552 | /// `[lhs]rhs`. lhs can be omitted to make it a slice. | 2558 | /// `[lhs]rhs`. |
| 2553 | array_type, | 2559 | array_type, |
| 2554 | /// `[lhs:a]b`. `array_type_sentinel[rhs]`. | 2560 | /// `[lhs:a]b`. `ArrayTypeSentinel[rhs]`. |
| 2555 | array_type_sentinel, | 2561 | array_type_sentinel, |
| 2556 | /// `[*]align(lhs) rhs`. lhs can be omitted. | 2562 | /// `[*]align(lhs) rhs`. lhs can be omitted. |
| 2557 | /// `*align(lhs) rhs`. lhs can be omitted. | 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,10 +59,7 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree { |
| 59 | parser.nodes.appendAssumeCapacity(.{ | 59 | parser.nodes.appendAssumeCapacity(.{ |
| 60 | .tag = .root, | 60 | .tag = .root, |
| 61 | .main_token = 0, | 61 | .main_token = 0, |
| 62 | .data = .{ | 62 | .data = undefined, |
| 63 | .lhs = undefined, | ||
| 64 | .rhs = undefined, | ||
| 65 | }, | ||
| 66 | }); | 63 | }); |
| 67 | const root_members = try parser.parseContainerMembers(); | 64 | const root_members = try parser.parseContainerMembers(); |
| 68 | const root_decls = try root_members.toSpan(&parser); | 65 | const root_decls = try root_members.toSpan(&parser); |
| ... | @@ -139,6 +136,16 @@ const Parser = struct { | ... | @@ -139,6 +136,16 @@ const Parser = struct { |
| 139 | return result; | 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 | fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index { | 149 | fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index { |
| 143 | const fields = std.meta.fields(@TypeOf(extra)); | 150 | const fields = std.meta.fields(@TypeOf(extra)); |
| 144 | try p.extra_data.ensureCapacity(p.gpa, p.extra_data.items.len + fields.len); | 151 | try p.extra_data.ensureCapacity(p.gpa, p.extra_data.items.len + fields.len); |
| ... | @@ -554,9 +561,10 @@ const Parser = struct { | ... | @@ -554,9 +561,10 @@ const Parser = struct { |
| 554 | return fn_proto; | 561 | return fn_proto; |
| 555 | }, | 562 | }, |
| 556 | .l_brace => { | 563 | .l_brace => { |
| 564 | const fn_decl_index = try p.reserveNode(); | ||
| 557 | const body_block = try p.parseBlock(); | 565 | const body_block = try p.parseBlock(); |
| 558 | assert(body_block != 0); | 566 | assert(body_block != 0); |
| 559 | return p.addNode(.{ | 567 | return p.setNode(fn_decl_index, .{ |
| 560 | .tag = .fn_decl, | 568 | .tag = .fn_decl, |
| 561 | .main_token = p.nodes.items(.main_token)[fn_proto], | 569 | .main_token = p.nodes.items(.main_token)[fn_proto], |
| 562 | .data = .{ | 570 | .data = .{ |
| ... | @@ -634,6 +642,10 @@ const Parser = struct { | ... | @@ -634,6 +642,10 @@ const Parser = struct { |
| 634 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr) | 642 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr) |
| 635 | fn parseFnProto(p: *Parser) !Node.Index { | 643 | fn parseFnProto(p: *Parser) !Node.Index { |
| 636 | const fn_token = p.eatToken(.keyword_fn) orelse return null_node; | 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 | _ = p.eatToken(.identifier); | 649 | _ = p.eatToken(.identifier); |
| 638 | const params = try p.parseParamDeclList(); | 650 | const params = try p.parseParamDeclList(); |
| 639 | defer params.deinit(p.gpa); | 651 | defer params.deinit(p.gpa); |
| ... | @@ -651,7 +663,7 @@ const Parser = struct { | ... | @@ -651,7 +663,7 @@ const Parser = struct { |
| 651 | 663 | ||
| 652 | if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) { | 664 | if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) { |
| 653 | switch (params) { | 665 | switch (params) { |
| 654 | .zero_or_one => |param| return p.addNode(.{ | 666 | .zero_or_one => |param| return p.setNode(fn_proto_index, .{ |
| 655 | .tag = .fn_proto_simple, | 667 | .tag = .fn_proto_simple, |
| 656 | .main_token = fn_token, | 668 | .main_token = fn_token, |
| 657 | .data = .{ | 669 | .data = .{ |
| ... | @@ -661,7 +673,7 @@ const Parser = struct { | ... | @@ -661,7 +673,7 @@ const Parser = struct { |
| 661 | }), | 673 | }), |
| 662 | .multi => |list| { | 674 | .multi => |list| { |
| 663 | const span = try p.listToSpan(list); | 675 | const span = try p.listToSpan(list); |
| 664 | return p.addNode(.{ | 676 | return p.setNode(fn_proto_index, .{ |
| 665 | .tag = .fn_proto_multi, | 677 | .tag = .fn_proto_multi, |
| 666 | .main_token = fn_token, | 678 | .main_token = fn_token, |
| 667 | .data = .{ | 679 | .data = .{ |
| ... | @@ -676,7 +688,7 @@ const Parser = struct { | ... | @@ -676,7 +688,7 @@ const Parser = struct { |
| 676 | } | 688 | } |
| 677 | } | 689 | } |
| 678 | switch (params) { | 690 | switch (params) { |
| 679 | .zero_or_one => |param| return p.addNode(.{ | 691 | .zero_or_one => |param| return p.setNode(fn_proto_index, .{ |
| 680 | .tag = .fn_proto_one, | 692 | .tag = .fn_proto_one, |
| 681 | .main_token = fn_token, | 693 | .main_token = fn_token, |
| 682 | .data = .{ | 694 | .data = .{ |
| ... | @@ -691,7 +703,7 @@ const Parser = struct { | ... | @@ -691,7 +703,7 @@ const Parser = struct { |
| 691 | }), | 703 | }), |
| 692 | .multi => |list| { | 704 | .multi => |list| { |
| 693 | const span = try p.listToSpan(list); | 705 | const span = try p.listToSpan(list); |
| 694 | return p.addNode(.{ | 706 | return p.setNode(fn_proto_index, .{ |
| 695 | .tag = .fn_proto, | 707 | .tag = .fn_proto, |
| 696 | .main_token = fn_token, | 708 | .main_token = fn_token, |
| 697 | .data = .{ | 709 | .data = .{ |
lib/std/zig/render.zig+3-3| ... | @@ -717,9 +717,9 @@ fn renderArrayType( | ... | @@ -717,9 +717,9 @@ fn renderArrayType( |
| 717 | ais.pushIndentNextLine(); | 717 | ais.pushIndentNextLine(); |
| 718 | try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket | 718 | try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket |
| 719 | try renderExpression(gpa, ais, tree, array_type.ast.elem_count, inner_space); | 719 | try renderExpression(gpa, ais, tree, array_type.ast.elem_count, inner_space); |
| 720 | if (array_type.ast.sentinel) |sentinel| { | 720 | if (array_type.ast.sentinel != 0) { |
| 721 | try renderToken(ais, tree, tree.firstToken(sentinel) - 1, inner_space); // colon | 721 | try renderToken(ais, tree, tree.firstToken(array_type.ast.sentinel) - 1, inner_space); // colon |
| 722 | try renderExpression(gpa, ais, tree, sentinel, inner_space); | 722 | try renderExpression(gpa, ais, tree, array_type.ast.sentinel, inner_space); |
| 723 | } | 723 | } |
| 724 | ais.popIndent(); | 724 | ais.popIndent(); |
| 725 | try renderToken(ais, tree, rbracket, .none); // rbracket | 725 | try renderToken(ais, tree, rbracket, .none); // rbracket |
lib/std/zig/string_literal.zig+82-52| ... | @@ -6,112 +6,143 @@ | ... | @@ -6,112 +6,143 @@ |
| 6 | const std = @import("../std.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 8 | 8 | ||
| 9 | const State = enum { | ||
| 10 | Start, | ||
| 11 | Backslash, | ||
| 12 | }; | ||
| 13 | |||
| 14 | pub const ParseError = error{ | 9 | pub const ParseError = error{ |
| 15 | OutOfMemory, | 10 | OutOfMemory, |
| 11 | InvalidStringLiteral, | ||
| 12 | }; | ||
| 16 | 13 | ||
| 17 | /// When this is returned, index will be the position of the character. | 14 | pub const Result = union(enum) { |
| 18 | InvalidCharacter, | 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 | 30 | /// Parses `bytes` as a Zig string literal and appends the result to `buf`. |
| 22 | pub fn parse( | 31 | /// Asserts `bytes` has '"' at beginning and end. |
| 23 | allocator: *std.mem.Allocator, | 32 | pub fn parseAppend(buf: *std.ArrayList(u8), bytes: []const u8) error{OutOfMemory}!Result { |
| 24 | bytes: []const u8, | ||
| 25 | bad_index: *usize, // populated if error.InvalidCharacter is returned | ||
| 26 | ) ParseError![]u8 { | ||
| 27 | assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"'); | 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); | 36 | const prev_len = buf.items.len; |
| 30 | errdefer list.deinit(); | 37 | try buf.ensureCapacity(prev_len + slice.len - 1); |
| 38 | errdefer buf.shrinkRetainingCapacity(prev_len); | ||
| 31 | 39 | ||
| 32 | const slice = bytes[1..]; | 40 | const State = enum { |
| 33 | try list.ensureCapacity(slice.len - 1); | 41 | Start, |
| 42 | Backslash, | ||
| 43 | }; | ||
| 34 | 44 | ||
| 35 | var state = State.Start; | 45 | var state = State.Start; |
| 36 | var index: usize = 0; | 46 | var index: usize = 0; |
| 37 | while (index < slice.len) : (index += 1) { | 47 | while (true) : (index += 1) { |
| 38 | const b = slice[index]; | 48 | const b = slice[index]; |
| 39 | 49 | ||
| 40 | switch (state) { | 50 | switch (state) { |
| 41 | State.Start => switch (b) { | 51 | State.Start => switch (b) { |
| 42 | '\\' => state = State.Backslash, | 52 | '\\' => state = State.Backslash, |
| 43 | '\n' => { | 53 | '\n' => { |
| 44 | bad_index.* = index; | 54 | return Result{ .invalid_character = index }; |
| 45 | return error.InvalidCharacter; | ||
| 46 | }, | 55 | }, |
| 47 | '"' => return list.toOwnedSlice(), | 56 | '"' => return Result.success, |
| 48 | else => try list.append(b), | 57 | else => try buf.append(b), |
| 49 | }, | 58 | }, |
| 50 | State.Backslash => switch (b) { | 59 | State.Backslash => switch (b) { |
| 51 | 'n' => { | 60 | 'n' => { |
| 52 | try list.append('\n'); | 61 | try buf.append('\n'); |
| 53 | state = State.Start; | 62 | state = State.Start; |
| 54 | }, | 63 | }, |
| 55 | 'r' => { | 64 | 'r' => { |
| 56 | try list.append('\r'); | 65 | try buf.append('\r'); |
| 57 | state = State.Start; | 66 | state = State.Start; |
| 58 | }, | 67 | }, |
| 59 | '\\' => { | 68 | '\\' => { |
| 60 | try list.append('\\'); | 69 | try buf.append('\\'); |
| 61 | state = State.Start; | 70 | state = State.Start; |
| 62 | }, | 71 | }, |
| 63 | 't' => { | 72 | 't' => { |
| 64 | try list.append('\t'); | 73 | try buf.append('\t'); |
| 65 | state = State.Start; | 74 | state = State.Start; |
| 66 | }, | 75 | }, |
| 67 | '\'' => { | 76 | '\'' => { |
| 68 | try list.append('\''); | 77 | try buf.append('\''); |
| 69 | state = State.Start; | 78 | state = State.Start; |
| 70 | }, | 79 | }, |
| 71 | '"' => { | 80 | '"' => { |
| 72 | try list.append('"'); | 81 | try buf.append('"'); |
| 73 | state = State.Start; | 82 | state = State.Start; |
| 74 | }, | 83 | }, |
| 75 | 'x' => { | 84 | 'x' => { |
| 76 | // TODO: add more/better/broader tests for this. | 85 | // TODO: add more/better/broader tests for this. |
| 77 | const index_continue = index + 3; | 86 | const index_continue = index + 3; |
| 78 | if (slice.len >= index_continue) | 87 | if (slice.len < index_continue) { |
| 79 | if (std.fmt.parseUnsigned(u8, slice[index + 1 .. index_continue], 16)) |char| { | 88 | return Result{ .expected_hex_digits = index }; |
| 80 | try list.append(char); | 89 | } |
| 81 | state = State.Start; | 90 | if (std.fmt.parseUnsigned(u8, slice[index + 1 .. index_continue], 16)) |byte| { |
| 82 | index = index_continue - 1; // loop-header increments again | 91 | try buf.append(byte); |
| 83 | continue; | 92 | state = State.Start; |
| 84 | } else |_| {}; | 93 | index = index_continue - 1; // loop-header increments again |
| 85 | 94 | } else |err| switch (err) { | |
| 86 | bad_index.* = index; | 95 | error.Overflow => unreachable, // 2 digits base 16 fits in a u8. |
| 87 | return error.InvalidCharacter; | 96 | error.InvalidCharacter => { |
| 97 | return Result{ .invalid_hex_escape = index + 1 }; | ||
| 98 | }, | ||
| 99 | } | ||
| 88 | }, | 100 | }, |
| 89 | 'u' => { | 101 | 'u' => { |
| 90 | // TODO: add more/better/broader tests for this. | 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 | if (std.mem.indexOfScalarPos(u8, slice[0..std.math.min(index + 9, slice.len)], index + 3, '}')) |index_end| { | 106 | if (std.mem.indexOfScalarPos(u8, slice[0..std.math.min(index + 9, slice.len)], index + 3, '}')) |index_end| { |
| 93 | const hex_str = slice[index + 2 .. index_end]; | 107 | const hex_str = slice[index + 2 .. index_end]; |
| 94 | if (std.fmt.parseUnsigned(u32, hex_str, 16)) |uint| { | 108 | if (std.fmt.parseUnsigned(u32, hex_str, 16)) |uint| { |
| 95 | if (uint <= 0x10ffff) { | 109 | if (uint <= 0x10ffff) { |
| 96 | try list.appendSlice(std.mem.toBytes(uint)[0..]); | 110 | try buf.appendSlice(std.mem.toBytes(uint)[0..]); |
| 97 | state = State.Start; | 111 | state = State.Start; |
| 98 | index = index_end; // loop-header increments | 112 | index = index_end; // loop-header increments |
| 99 | continue; | 113 | continue; |
| 100 | } | 114 | } |
| 101 | } else |_| {} | 115 | } else |err| switch (err) { |
| 102 | }; | 116 | error.Overflow => unreachable, |
| 103 | 117 | error.InvalidCharacter => { | |
| 104 | bad_index.* = index; | 118 | return Result{ .invalid_unicode_escape = index + 1 }; |
| 105 | return error.InvalidCharacter; | 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 | else => { | 128 | else => { |
| 108 | bad_index.* = index; | 129 | return Result{ .invalid_character = index }; |
| 109 | return error.InvalidCharacter; | ||
| 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 | test "parse" { | 148 | test "parse" { |
| ... | @@ -121,9 +152,8 @@ test "parse" { | ... | @@ -121,9 +152,8 @@ test "parse" { |
| 121 | var fixed_buf_mem: [32]u8 = undefined; | 152 | var fixed_buf_mem: [32]u8 = undefined; |
| 122 | var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buf_mem[0..]); | 153 | var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buf_mem[0..]); |
| 123 | var alloc = &fixed_buf_alloc.allocator; | 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))); | 156 | expect(eql(u8, "foo", try parseAlloc(alloc, "\"foo\""))); |
| 127 | expect(eql(u8, "foo", try parse(alloc, "\"f\x6f\x6f\"", &bad_index))); | 157 | expect(eql(u8, "foo", try parseAlloc(alloc, "\"f\x6f\x6f\""))); |
| 128 | expect(eql(u8, "f💯", try parse(alloc, "\"f\u{1f4af}\"", &bad_index))); | 158 | expect(eql(u8, "f💯", try parseAlloc(alloc, "\"f\u{1f4af}\""))); |
| 129 | } | 159 | } |
src/AstGen.zig created+4450| ... | @@ -0,0 +1,4450 @@ | ||
| 1 | //! A Work-In-Progress `zir.Code`. This is a shared parent of all | ||
| 2 | //! `GenZir` scopes. Once the `zir.Code` is produced, this struct | ||
| 3 | //! is deinitialized. | ||
| 4 | //! The `GenZir.finish` function converts this to a `zir.Code`. | ||
| 5 | |||
| 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, node, tree.containerDecl(node)), | ||
| 723 | .container_decl_two, .container_decl_two_trailing => { | ||
| 724 | var buffer: [2]ast.Node.Index = undefined; | ||
| 725 | return containerDecl(gz, scope, rl, node, tree.containerDeclTwo(&buffer, node)); | ||
| 726 | }, | ||
| 727 | .container_decl_arg, | ||
| 728 | .container_decl_arg_trailing, | ||
| 729 | => return containerDecl(gz, scope, rl, node, tree.containerDeclArg(node)), | ||
| 730 | |||
| 731 | .tagged_union, | ||
| 732 | .tagged_union_trailing, | ||
| 733 | => return containerDecl(gz, scope, rl, node, tree.taggedUnion(node)), | ||
| 734 | .tagged_union_two, .tagged_union_two_trailing => { | ||
| 735 | var buffer: [2]ast.Node.Index = undefined; | ||
| 736 | return containerDecl(gz, scope, rl, node, tree.taggedUnionTwo(&buffer, node)); | ||
| 737 | }, | ||
| 738 | .tagged_union_enum_tag, | ||
| 739 | .tagged_union_enum_tag_trailing, | ||
| 740 | => return containerDecl(gz, scope, rl, node, tree.taggedUnionEnumTag(node)), | ||
| 741 | |||
| 742 | .@"break" => return breakExpr(gz, scope, node), | ||
| 743 | .@"continue" => return continueExpr(gz, scope, node), | ||
| 744 | .grouped_expression => return expr(gz, scope, rl, node_datas[node].lhs), | ||
| 745 | .array_type => return arrayType(gz, scope, rl, node), | ||
| 746 | .array_type_sentinel => return arrayTypeSentinel(gz, scope, rl, node), | ||
| 747 | .char_literal => return charLiteral(gz, scope, rl, node), | ||
| 748 | .error_set_decl => return errorSetDecl(gz, scope, rl, node), | ||
| 749 | .array_access => return arrayAccess(gz, scope, rl, node), | ||
| 750 | .@"comptime" => return comptimeExpr(gz, scope, rl, node_datas[node].lhs), | ||
| 751 | .@"switch", .switch_comma => return switchExpr(gz, scope, rl, node), | ||
| 752 | |||
| 753 | .@"nosuspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 754 | .@"suspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 755 | .@"await" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 756 | .@"resume" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 757 | |||
| 758 | .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}), | ||
| 759 | .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}), | ||
| 760 | .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), | ||
| 761 | |||
| 762 | .array_init_one, | ||
| 763 | .array_init_one_comma, | ||
| 764 | .array_init_dot_two, | ||
| 765 | .array_init_dot_two_comma, | ||
| 766 | .array_init_dot, | ||
| 767 | .array_init_dot_comma, | ||
| 768 | .array_init, | ||
| 769 | .array_init_comma, | ||
| 770 | => return mod.failNode(scope, node, "TODO implement astgen.expr for array literals", .{}), | ||
| 771 | |||
| 772 | .struct_init_one, .struct_init_one_comma => { | ||
| 773 | var fields: [1]ast.Node.Index = undefined; | ||
| 774 | return structInitExpr(gz, scope, rl, node, tree.structInitOne(&fields, node)); | ||
| 775 | }, | ||
| 776 | .struct_init_dot_two, .struct_init_dot_two_comma => { | ||
| 777 | var fields: [2]ast.Node.Index = undefined; | ||
| 778 | return structInitExpr(gz, scope, rl, node, tree.structInitDotTwo(&fields, node)); | ||
| 779 | }, | ||
| 780 | .struct_init_dot, | ||
| 781 | .struct_init_dot_comma, | ||
| 782 | => return structInitExpr(gz, scope, rl, node, tree.structInitDot(node)), | ||
| 783 | .struct_init, | ||
| 784 | .struct_init_comma, | ||
| 785 | => return structInitExpr(gz, scope, rl, node, tree.structInit(node)), | ||
| 786 | |||
| 787 | .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}), | ||
| 788 | .fn_proto_simple, | ||
| 789 | .fn_proto_multi, | ||
| 790 | .fn_proto_one, | ||
| 791 | .fn_proto, | ||
| 792 | => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}), | ||
| 793 | } | ||
| 794 | } | ||
| 795 | |||
| 796 | pub fn structInitExpr( | ||
| 797 | gz: *GenZir, | ||
| 798 | scope: *Scope, | ||
| 799 | rl: ResultLoc, | ||
| 800 | node: ast.Node.Index, | ||
| 801 | struct_init: ast.full.StructInit, | ||
| 802 | ) InnerError!zir.Inst.Ref { | ||
| 803 | const tree = gz.tree(); | ||
| 804 | const astgen = gz.astgen; | ||
| 805 | const mod = astgen.mod; | ||
| 806 | const gpa = mod.gpa; | ||
| 807 | |||
| 808 | if (struct_init.ast.fields.len == 0) { | ||
| 809 | if (struct_init.ast.type_expr == 0) { | ||
| 810 | return rvalue(gz, scope, rl, .empty_struct, node); | ||
| 811 | } else { | ||
| 812 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); | ||
| 813 | const result = try gz.addUnNode(.struct_init_empty, ty_inst, node); | ||
| 814 | return rvalue(gz, scope, rl, result, node); | ||
| 815 | } | ||
| 816 | } | ||
| 817 | switch (rl) { | ||
| 818 | .discard => return mod.failNode(scope, node, "TODO implement structInitExpr discard", .{}), | ||
| 819 | .none => return mod.failNode(scope, node, "TODO implement structInitExpr none", .{}), | ||
| 820 | .ref => unreachable, // struct literal not valid as l-value | ||
| 821 | .ty => |ty_inst| { | ||
| 822 | return mod.failNode(scope, node, "TODO implement structInitExpr ty", .{}); | ||
| 823 | }, | ||
| 824 | .ptr => |ptr_inst| { | ||
| 825 | const field_ptr_list = try gpa.alloc(zir.Inst.Index, struct_init.ast.fields.len); | ||
| 826 | defer gpa.free(field_ptr_list); | ||
| 827 | |||
| 828 | for (struct_init.ast.fields) |field_init, i| { | ||
| 829 | const name_token = tree.firstToken(field_init) - 2; | ||
| 830 | const str_index = try gz.identAsString(name_token); | ||
| 831 | const field_ptr = try gz.addPlNode(.field_ptr, field_init, zir.Inst.Field{ | ||
| 832 | .lhs = ptr_inst, | ||
| 833 | .field_name_start = str_index, | ||
| 834 | }); | ||
| 835 | field_ptr_list[i] = astgen.refToIndex(field_ptr).?; | ||
| 836 | _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init); | ||
| 837 | } | ||
| 838 | const validate_inst = try gz.addPlNode(.validate_struct_init_ptr, node, zir.Inst.Block{ | ||
| 839 | .body_len = @intCast(u32, field_ptr_list.len), | ||
| 840 | }); | ||
| 841 | try astgen.extra.appendSlice(gpa, field_ptr_list); | ||
| 842 | return validate_inst; | ||
| 843 | }, | ||
| 844 | .inferred_ptr => |ptr_inst| { | ||
| 845 | return mod.failNode(scope, node, "TODO implement structInitExpr inferred_ptr", .{}); | ||
| 846 | }, | ||
| 847 | .block_ptr => |block_gz| { | ||
| 848 | return mod.failNode(scope, node, "TODO implement structInitExpr block", .{}); | ||
| 849 | }, | ||
| 850 | } | ||
| 851 | } | ||
| 852 | |||
| 853 | pub fn comptimeExpr( | ||
| 854 | gz: *GenZir, | ||
| 855 | scope: *Scope, | ||
| 856 | rl: ResultLoc, | ||
| 857 | node: ast.Node.Index, | ||
| 858 | ) InnerError!zir.Inst.Ref { | ||
| 859 | const prev_force_comptime = gz.force_comptime; | ||
| 860 | gz.force_comptime = true; | ||
| 861 | const result = try expr(gz, scope, rl, node); | ||
| 862 | gz.force_comptime = prev_force_comptime; | ||
| 863 | return result; | ||
| 864 | } | ||
| 865 | |||
| 866 | fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 867 | const mod = parent_gz.astgen.mod; | ||
| 868 | const tree = parent_gz.tree(); | ||
| 869 | const node_datas = tree.nodes.items(.data); | ||
| 870 | const break_label = node_datas[node].lhs; | ||
| 871 | const rhs = node_datas[node].rhs; | ||
| 872 | |||
| 873 | // Look for the label in the scope. | ||
| 874 | var scope = parent_scope; | ||
| 875 | while (true) { | ||
| 876 | switch (scope.tag) { | ||
| 877 | .gen_zir => { | ||
| 878 | const block_gz = scope.cast(GenZir).?; | ||
| 879 | |||
| 880 | const block_inst = blk: { | ||
| 881 | if (break_label != 0) { | ||
| 882 | if (block_gz.label) |*label| { | ||
| 883 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { | ||
| 884 | label.used = true; | ||
| 885 | break :blk label.block_inst; | ||
| 886 | } | ||
| 887 | } | ||
| 888 | } else if (block_gz.break_block != 0) { | ||
| 889 | break :blk block_gz.break_block; | ||
| 890 | } | ||
| 891 | scope = block_gz.parent; | ||
| 892 | continue; | ||
| 893 | }; | ||
| 894 | |||
| 895 | if (rhs == 0) { | ||
| 896 | _ = try parent_gz.addBreak(.@"break", block_inst, .void_value); | ||
| 897 | return zir.Inst.Ref.unreachable_value; | ||
| 898 | } | ||
| 899 | block_gz.break_count += 1; | ||
| 900 | const prev_rvalue_rl_count = block_gz.rvalue_rl_count; | ||
| 901 | const operand = try expr(parent_gz, parent_scope, block_gz.break_result_loc, rhs); | ||
| 902 | const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count; | ||
| 903 | |||
| 904 | const br = try parent_gz.addBreak(.@"break", block_inst, operand); | ||
| 905 | |||
| 906 | if (block_gz.break_result_loc == .block_ptr) { | ||
| 907 | try block_gz.labeled_breaks.append(mod.gpa, br); | ||
| 908 | |||
| 909 | if (have_store_to_block) { | ||
| 910 | const zir_tags = parent_gz.astgen.instructions.items(.tag); | ||
| 911 | const zir_datas = parent_gz.astgen.instructions.items(.data); | ||
| 912 | const store_inst = @intCast(u32, zir_tags.len - 2); | ||
| 913 | assert(zir_tags[store_inst] == .store_to_block_ptr); | ||
| 914 | assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr); | ||
| 915 | try block_gz.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst); | ||
| 916 | } | ||
| 917 | } | ||
| 918 | return zir.Inst.Ref.unreachable_value; | ||
| 919 | }, | ||
| 920 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 921 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 922 | else => if (break_label != 0) { | ||
| 923 | const label_name = try mod.identifierTokenString(parent_scope, break_label); | ||
| 924 | return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); | ||
| 925 | } else { | ||
| 926 | return mod.failNode(parent_scope, node, "break expression outside loop", .{}); | ||
| 927 | }, | ||
| 928 | } | ||
| 929 | } | ||
| 930 | } | ||
| 931 | |||
| 932 | fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 933 | const mod = parent_gz.astgen.mod; | ||
| 934 | const tree = parent_gz.tree(); | ||
| 935 | const node_datas = tree.nodes.items(.data); | ||
| 936 | const break_label = node_datas[node].lhs; | ||
| 937 | |||
| 938 | // Look for the label in the scope. | ||
| 939 | var scope = parent_scope; | ||
| 940 | while (true) { | ||
| 941 | switch (scope.tag) { | ||
| 942 | .gen_zir => { | ||
| 943 | const gen_zir = scope.cast(GenZir).?; | ||
| 944 | const continue_block = gen_zir.continue_block; | ||
| 945 | if (continue_block == 0) { | ||
| 946 | scope = gen_zir.parent; | ||
| 947 | continue; | ||
| 948 | } | ||
| 949 | if (break_label != 0) blk: { | ||
| 950 | if (gen_zir.label) |*label| { | ||
| 951 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { | ||
| 952 | label.used = true; | ||
| 953 | break :blk; | ||
| 954 | } | ||
| 955 | } | ||
| 956 | // found continue but either it has a different label, or no label | ||
| 957 | scope = gen_zir.parent; | ||
| 958 | continue; | ||
| 959 | } | ||
| 960 | |||
| 961 | // TODO emit a break_inline if the loop being continued is inline | ||
| 962 | _ = try parent_gz.addBreak(.@"break", continue_block, .void_value); | ||
| 963 | return zir.Inst.Ref.unreachable_value; | ||
| 964 | }, | ||
| 965 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 966 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 967 | else => if (break_label != 0) { | ||
| 968 | const label_name = try mod.identifierTokenString(parent_scope, break_label); | ||
| 969 | return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); | ||
| 970 | } else { | ||
| 971 | return mod.failNode(parent_scope, node, "continue expression outside loop", .{}); | ||
| 972 | }, | ||
| 973 | } | ||
| 974 | } | ||
| 975 | } | ||
| 976 | |||
| 977 | pub fn blockExpr( | ||
| 978 | gz: *GenZir, | ||
| 979 | scope: *Scope, | ||
| 980 | rl: ResultLoc, | ||
| 981 | block_node: ast.Node.Index, | ||
| 982 | statements: []const ast.Node.Index, | ||
| 983 | ) InnerError!zir.Inst.Ref { | ||
| 984 | const tracy = trace(@src()); | ||
| 985 | defer tracy.end(); | ||
| 986 | |||
| 987 | const tree = gz.tree(); | ||
| 988 | const main_tokens = tree.nodes.items(.main_token); | ||
| 989 | const token_tags = tree.tokens.items(.tag); | ||
| 990 | |||
| 991 | const lbrace = main_tokens[block_node]; | ||
| 992 | if (token_tags[lbrace - 1] == .colon and | ||
| 993 | token_tags[lbrace - 2] == .identifier) | ||
| 994 | { | ||
| 995 | return labeledBlockExpr(gz, scope, rl, block_node, statements, .block); | ||
| 996 | } | ||
| 997 | |||
| 998 | try blockExprStmts(gz, scope, block_node, statements); | ||
| 999 | return rvalue(gz, scope, rl, .void_value, block_node); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void { | ||
| 1003 | // Look for the label in the scope. | ||
| 1004 | var scope = parent_scope; | ||
| 1005 | while (true) { | ||
| 1006 | switch (scope.tag) { | ||
| 1007 | .gen_zir => { | ||
| 1008 | const gen_zir = scope.cast(GenZir).?; | ||
| 1009 | if (gen_zir.label) |prev_label| { | ||
| 1010 | if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) { | ||
| 1011 | const tree = parent_scope.tree(); | ||
| 1012 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1013 | |||
| 1014 | const label_name = try mod.identifierTokenString(parent_scope, label); | ||
| 1015 | const msg = msg: { | ||
| 1016 | const msg = try mod.errMsg( | ||
| 1017 | parent_scope, | ||
| 1018 | gen_zir.tokSrcLoc(label), | ||
| 1019 | "redefinition of label '{s}'", | ||
| 1020 | .{label_name}, | ||
| 1021 | ); | ||
| 1022 | errdefer msg.destroy(mod.gpa); | ||
| 1023 | try mod.errNote( | ||
| 1024 | parent_scope, | ||
| 1025 | gen_zir.tokSrcLoc(prev_label.token), | ||
| 1026 | msg, | ||
| 1027 | "previous definition is here", | ||
| 1028 | .{}, | ||
| 1029 | ); | ||
| 1030 | break :msg msg; | ||
| 1031 | }; | ||
| 1032 | return mod.failWithOwnedErrorMsg(parent_scope, msg); | ||
| 1033 | } | ||
| 1034 | } | ||
| 1035 | scope = gen_zir.parent; | ||
| 1036 | }, | ||
| 1037 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 1038 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 1039 | else => return, | ||
| 1040 | } | ||
| 1041 | } | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | fn labeledBlockExpr( | ||
| 1045 | gz: *GenZir, | ||
| 1046 | parent_scope: *Scope, | ||
| 1047 | rl: ResultLoc, | ||
| 1048 | block_node: ast.Node.Index, | ||
| 1049 | statements: []const ast.Node.Index, | ||
| 1050 | zir_tag: zir.Inst.Tag, | ||
| 1051 | ) InnerError!zir.Inst.Ref { | ||
| 1052 | const tracy = trace(@src()); | ||
| 1053 | defer tracy.end(); | ||
| 1054 | |||
| 1055 | assert(zir_tag == .block); | ||
| 1056 | |||
| 1057 | const mod = gz.astgen.mod; | ||
| 1058 | const tree = gz.tree(); | ||
| 1059 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1060 | const token_tags = tree.tokens.items(.tag); | ||
| 1061 | |||
| 1062 | const lbrace = main_tokens[block_node]; | ||
| 1063 | const label_token = lbrace - 2; | ||
| 1064 | assert(token_tags[label_token] == .identifier); | ||
| 1065 | |||
| 1066 | try checkLabelRedefinition(mod, parent_scope, label_token); | ||
| 1067 | |||
| 1068 | // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct | ||
| 1069 | // so that break statements can reference it. | ||
| 1070 | const block_inst = try gz.addBlock(zir_tag, block_node); | ||
| 1071 | try gz.instructions.append(mod.gpa, block_inst); | ||
| 1072 | |||
| 1073 | var block_scope: GenZir = .{ | ||
| 1074 | .parent = parent_scope, | ||
| 1075 | .astgen = gz.astgen, | ||
| 1076 | .force_comptime = gz.force_comptime, | ||
| 1077 | .instructions = .{}, | ||
| 1078 | // TODO @as here is working around a stage1 miscompilation bug :( | ||
| 1079 | .label = @as(?GenZir.Label, GenZir.Label{ | ||
| 1080 | .token = label_token, | ||
| 1081 | .block_inst = block_inst, | ||
| 1082 | }), | ||
| 1083 | }; | ||
| 1084 | block_scope.setBreakResultLoc(rl); | ||
| 1085 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 1086 | defer block_scope.labeled_breaks.deinit(mod.gpa); | ||
| 1087 | defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); | ||
| 1088 | |||
| 1089 | try blockExprStmts(&block_scope, &block_scope.base, block_node, statements); | ||
| 1090 | |||
| 1091 | if (!block_scope.label.?.used) { | ||
| 1092 | return mod.failTok(parent_scope, label_token, "unused block label", .{}); | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | const zir_tags = gz.astgen.instructions.items(.tag); | ||
| 1096 | const zir_datas = gz.astgen.instructions.items(.data); | ||
| 1097 | |||
| 1098 | const strat = rl.strategy(&block_scope); | ||
| 1099 | switch (strat.tag) { | ||
| 1100 | .break_void => { | ||
| 1101 | // The code took advantage of the result location as a pointer. | ||
| 1102 | // Turn the break instruction operands into void. | ||
| 1103 | for (block_scope.labeled_breaks.items) |br| { | ||
| 1104 | zir_datas[br].@"break".operand = .void_value; | ||
| 1105 | } | ||
| 1106 | try block_scope.setBlockBody(block_inst); | ||
| 1107 | |||
| 1108 | return gz.astgen.indexToRef(block_inst); | ||
| 1109 | }, | ||
| 1110 | .break_operand => { | ||
| 1111 | // All break operands are values that did not use the result location pointer. | ||
| 1112 | if (strat.elide_store_to_block_ptr_instructions) { | ||
| 1113 | for (block_scope.labeled_store_to_block_ptr_list.items) |inst| { | ||
| 1114 | zir_tags[inst] = .elided; | ||
| 1115 | zir_datas[inst] = undefined; | ||
| 1116 | } | ||
| 1117 | // TODO technically not needed since we changed the tag to elided but | ||
| 1118 | // would be better still to elide the ones that are in this list. | ||
| 1119 | } | ||
| 1120 | try block_scope.setBlockBody(block_inst); | ||
| 1121 | const block_ref = gz.astgen.indexToRef(block_inst); | ||
| 1122 | switch (rl) { | ||
| 1123 | .ref => return block_ref, | ||
| 1124 | else => return rvalue(gz, parent_scope, rl, block_ref, block_node), | ||
| 1125 | } | ||
| 1126 | }, | ||
| 1127 | } | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | fn blockExprStmts( | ||
| 1131 | gz: *GenZir, | ||
| 1132 | parent_scope: *Scope, | ||
| 1133 | node: ast.Node.Index, | ||
| 1134 | statements: []const ast.Node.Index, | ||
| 1135 | ) !void { | ||
| 1136 | const tree = gz.tree(); | ||
| 1137 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1138 | const node_tags = tree.nodes.items(.tag); | ||
| 1139 | |||
| 1140 | var block_arena = std.heap.ArenaAllocator.init(gz.astgen.mod.gpa); | ||
| 1141 | defer block_arena.deinit(); | ||
| 1142 | |||
| 1143 | var scope = parent_scope; | ||
| 1144 | for (statements) |statement| { | ||
| 1145 | if (!gz.force_comptime) { | ||
| 1146 | _ = try gz.addNode(.dbg_stmt_node, statement); | ||
| 1147 | } | ||
| 1148 | switch (node_tags[statement]) { | ||
| 1149 | .global_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.globalVarDecl(statement)), | ||
| 1150 | .local_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.localVarDecl(statement)), | ||
| 1151 | .simple_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.simpleVarDecl(statement)), | ||
| 1152 | .aligned_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.alignedVarDecl(statement)), | ||
| 1153 | |||
| 1154 | .assign => try assign(gz, scope, statement), | ||
| 1155 | .assign_bit_and => try assignOp(gz, scope, statement, .bit_and), | ||
| 1156 | .assign_bit_or => try assignOp(gz, scope, statement, .bit_or), | ||
| 1157 | .assign_bit_shift_left => try assignOp(gz, scope, statement, .shl), | ||
| 1158 | .assign_bit_shift_right => try assignOp(gz, scope, statement, .shr), | ||
| 1159 | .assign_bit_xor => try assignOp(gz, scope, statement, .xor), | ||
| 1160 | .assign_div => try assignOp(gz, scope, statement, .div), | ||
| 1161 | .assign_sub => try assignOp(gz, scope, statement, .sub), | ||
| 1162 | .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap), | ||
| 1163 | .assign_mod => try assignOp(gz, scope, statement, .mod_rem), | ||
| 1164 | .assign_add => try assignOp(gz, scope, statement, .add), | ||
| 1165 | .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap), | ||
| 1166 | .assign_mul => try assignOp(gz, scope, statement, .mul), | ||
| 1167 | .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap), | ||
| 1168 | |||
| 1169 | else => { | ||
| 1170 | // We need to emit an error if the result is not `noreturn` or `void`, but | ||
| 1171 | // we want to avoid adding the ZIR instruction if possible for performance. | ||
| 1172 | const maybe_unused_result = try expr(gz, scope, .none, statement); | ||
| 1173 | const elide_check = if (gz.astgen.refToIndex(maybe_unused_result)) |inst| b: { | ||
| 1174 | // Note that this array becomes invalid after appending more items to it | ||
| 1175 | // in the above while loop. | ||
| 1176 | const zir_tags = gz.astgen.instructions.items(.tag); | ||
| 1177 | switch (zir_tags[inst]) { | ||
| 1178 | .@"const" => { | ||
| 1179 | const tv = gz.astgen.instructions.items(.data)[inst].@"const"; | ||
| 1180 | break :b switch (tv.ty.zigTypeTag()) { | ||
| 1181 | .NoReturn, .Void => true, | ||
| 1182 | else => false, | ||
| 1183 | }; | ||
| 1184 | }, | ||
| 1185 | // For some instructions, swap in a slightly different ZIR tag | ||
| 1186 | // so we can avoid a separate ensure_result_used instruction. | ||
| 1187 | .call_none_chkused => unreachable, | ||
| 1188 | .call_none => { | ||
| 1189 | zir_tags[inst] = .call_none_chkused; | ||
| 1190 | break :b true; | ||
| 1191 | }, | ||
| 1192 | .call_chkused => unreachable, | ||
| 1193 | .call => { | ||
| 1194 | zir_tags[inst] = .call_chkused; | ||
| 1195 | break :b true; | ||
| 1196 | }, | ||
| 1197 | |||
| 1198 | // ZIR instructions that might be a type other than `noreturn` or `void`. | ||
| 1199 | .add, | ||
| 1200 | .addwrap, | ||
| 1201 | .alloc, | ||
| 1202 | .alloc_mut, | ||
| 1203 | .alloc_inferred, | ||
| 1204 | .alloc_inferred_mut, | ||
| 1205 | .array_cat, | ||
| 1206 | .array_mul, | ||
| 1207 | .array_type, | ||
| 1208 | .array_type_sentinel, | ||
| 1209 | .indexable_ptr_len, | ||
| 1210 | .as, | ||
| 1211 | .as_node, | ||
| 1212 | .@"asm", | ||
| 1213 | .asm_volatile, | ||
| 1214 | .bit_and, | ||
| 1215 | .bitcast, | ||
| 1216 | .bitcast_result_ptr, | ||
| 1217 | .bit_or, | ||
| 1218 | .block, | ||
| 1219 | .block_inline, | ||
| 1220 | .loop, | ||
| 1221 | .bool_br_and, | ||
| 1222 | .bool_br_or, | ||
| 1223 | .bool_not, | ||
| 1224 | .bool_and, | ||
| 1225 | .bool_or, | ||
| 1226 | .call_compile_time, | ||
| 1227 | .cmp_lt, | ||
| 1228 | .cmp_lte, | ||
| 1229 | .cmp_eq, | ||
| 1230 | .cmp_gte, | ||
| 1231 | .cmp_gt, | ||
| 1232 | .cmp_neq, | ||
| 1233 | .coerce_result_ptr, | ||
| 1234 | .decl_ref, | ||
| 1235 | .decl_val, | ||
| 1236 | .load, | ||
| 1237 | .div, | ||
| 1238 | .elem_ptr, | ||
| 1239 | .elem_val, | ||
| 1240 | .elem_ptr_node, | ||
| 1241 | .elem_val_node, | ||
| 1242 | .floatcast, | ||
| 1243 | .field_ptr, | ||
| 1244 | .field_val, | ||
| 1245 | .field_ptr_named, | ||
| 1246 | .field_val_named, | ||
| 1247 | .fn_type, | ||
| 1248 | .fn_type_var_args, | ||
| 1249 | .fn_type_cc, | ||
| 1250 | .fn_type_cc_var_args, | ||
| 1251 | .int, | ||
| 1252 | .intcast, | ||
| 1253 | .int_type, | ||
| 1254 | .is_non_null, | ||
| 1255 | .is_null, | ||
| 1256 | .is_non_null_ptr, | ||
| 1257 | .is_null_ptr, | ||
| 1258 | .is_err, | ||
| 1259 | .is_err_ptr, | ||
| 1260 | .mod_rem, | ||
| 1261 | .mul, | ||
| 1262 | .mulwrap, | ||
| 1263 | .param_type, | ||
| 1264 | .ptrtoint, | ||
| 1265 | .ref, | ||
| 1266 | .ret_ptr, | ||
| 1267 | .ret_type, | ||
| 1268 | .shl, | ||
| 1269 | .shr, | ||
| 1270 | .str, | ||
| 1271 | .sub, | ||
| 1272 | .subwrap, | ||
| 1273 | .negate, | ||
| 1274 | .negate_wrap, | ||
| 1275 | .typeof, | ||
| 1276 | .typeof_elem, | ||
| 1277 | .xor, | ||
| 1278 | .optional_type, | ||
| 1279 | .optional_type_from_ptr_elem, | ||
| 1280 | .optional_payload_safe, | ||
| 1281 | .optional_payload_unsafe, | ||
| 1282 | .optional_payload_safe_ptr, | ||
| 1283 | .optional_payload_unsafe_ptr, | ||
| 1284 | .err_union_payload_safe, | ||
| 1285 | .err_union_payload_unsafe, | ||
| 1286 | .err_union_payload_safe_ptr, | ||
| 1287 | .err_union_payload_unsafe_ptr, | ||
| 1288 | .err_union_code, | ||
| 1289 | .err_union_code_ptr, | ||
| 1290 | .ptr_type, | ||
| 1291 | .ptr_type_simple, | ||
| 1292 | .enum_literal, | ||
| 1293 | .enum_literal_small, | ||
| 1294 | .merge_error_sets, | ||
| 1295 | .error_union_type, | ||
| 1296 | .bit_not, | ||
| 1297 | .error_value, | ||
| 1298 | .error_to_int, | ||
| 1299 | .int_to_error, | ||
| 1300 | .slice_start, | ||
| 1301 | .slice_end, | ||
| 1302 | .slice_sentinel, | ||
| 1303 | .import, | ||
| 1304 | .typeof_peer, | ||
| 1305 | .switch_block, | ||
| 1306 | .switch_block_multi, | ||
| 1307 | .switch_block_else, | ||
| 1308 | .switch_block_else_multi, | ||
| 1309 | .switch_block_under, | ||
| 1310 | .switch_block_under_multi, | ||
| 1311 | .switch_block_ref, | ||
| 1312 | .switch_block_ref_multi, | ||
| 1313 | .switch_block_ref_else, | ||
| 1314 | .switch_block_ref_else_multi, | ||
| 1315 | .switch_block_ref_under, | ||
| 1316 | .switch_block_ref_under_multi, | ||
| 1317 | .switch_capture, | ||
| 1318 | .switch_capture_ref, | ||
| 1319 | .switch_capture_multi, | ||
| 1320 | .switch_capture_multi_ref, | ||
| 1321 | .switch_capture_else, | ||
| 1322 | .switch_capture_else_ref, | ||
| 1323 | .struct_init_empty, | ||
| 1324 | .struct_decl, | ||
| 1325 | .struct_decl_packed, | ||
| 1326 | .struct_decl_extern, | ||
| 1327 | .union_decl, | ||
| 1328 | .enum_decl, | ||
| 1329 | .opaque_decl, | ||
| 1330 | => break :b false, | ||
| 1331 | |||
| 1332 | // ZIR instructions that are always either `noreturn` or `void`. | ||
| 1333 | .breakpoint, | ||
| 1334 | .dbg_stmt_node, | ||
| 1335 | .ensure_result_used, | ||
| 1336 | .ensure_result_non_error, | ||
| 1337 | .set_eval_branch_quota, | ||
| 1338 | .compile_log, | ||
| 1339 | .ensure_err_payload_void, | ||
| 1340 | .@"break", | ||
| 1341 | .break_inline, | ||
| 1342 | .condbr, | ||
| 1343 | .condbr_inline, | ||
| 1344 | .compile_error, | ||
| 1345 | .ret_node, | ||
| 1346 | .ret_tok, | ||
| 1347 | .ret_coerce, | ||
| 1348 | .@"unreachable", | ||
| 1349 | .elided, | ||
| 1350 | .store, | ||
| 1351 | .store_node, | ||
| 1352 | .store_to_block_ptr, | ||
| 1353 | .store_to_inferred_ptr, | ||
| 1354 | .resolve_inferred_alloc, | ||
| 1355 | .repeat, | ||
| 1356 | .repeat_inline, | ||
| 1357 | .validate_struct_init_ptr, | ||
| 1358 | => break :b true, | ||
| 1359 | } | ||
| 1360 | } else switch (maybe_unused_result) { | ||
| 1361 | .none => unreachable, | ||
| 1362 | |||
| 1363 | .void_value, | ||
| 1364 | .unreachable_value, | ||
| 1365 | => true, | ||
| 1366 | |||
| 1367 | else => false, | ||
| 1368 | }; | ||
| 1369 | if (!elide_check) { | ||
| 1370 | _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement); | ||
| 1371 | } | ||
| 1372 | }, | ||
| 1373 | } | ||
| 1374 | } | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | fn varDecl( | ||
| 1378 | gz: *GenZir, | ||
| 1379 | scope: *Scope, | ||
| 1380 | node: ast.Node.Index, | ||
| 1381 | block_arena: *Allocator, | ||
| 1382 | var_decl: ast.full.VarDecl, | ||
| 1383 | ) InnerError!*Scope { | ||
| 1384 | const mod = gz.astgen.mod; | ||
| 1385 | if (var_decl.comptime_token) |comptime_token| { | ||
| 1386 | return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{}); | ||
| 1387 | } | ||
| 1388 | if (var_decl.ast.align_node != 0) { | ||
| 1389 | return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{}); | ||
| 1390 | } | ||
| 1391 | const astgen = gz.astgen; | ||
| 1392 | const tree = gz.tree(); | ||
| 1393 | const token_tags = tree.tokens.items(.tag); | ||
| 1394 | |||
| 1395 | const name_token = var_decl.ast.mut_token + 1; | ||
| 1396 | const name_src = gz.tokSrcLoc(name_token); | ||
| 1397 | const ident_name = try mod.identifierTokenString(scope, name_token); | ||
| 1398 | |||
| 1399 | // Local variables shadowing detection, including function parameters. | ||
| 1400 | { | ||
| 1401 | var s = scope; | ||
| 1402 | while (true) switch (s.tag) { | ||
| 1403 | .local_val => { | ||
| 1404 | const local_val = s.cast(Scope.LocalVal).?; | ||
| 1405 | if (mem.eql(u8, local_val.name, ident_name)) { | ||
| 1406 | const msg = msg: { | ||
| 1407 | const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{ | ||
| 1408 | ident_name, | ||
| 1409 | }); | ||
| 1410 | errdefer msg.destroy(mod.gpa); | ||
| 1411 | try mod.errNote(scope, local_val.src, msg, "previous definition is here", .{}); | ||
| 1412 | break :msg msg; | ||
| 1413 | }; | ||
| 1414 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 1415 | } | ||
| 1416 | s = local_val.parent; | ||
| 1417 | }, | ||
| 1418 | .local_ptr => { | ||
| 1419 | const local_ptr = s.cast(Scope.LocalPtr).?; | ||
| 1420 | if (mem.eql(u8, local_ptr.name, ident_name)) { | ||
| 1421 | const msg = msg: { | ||
| 1422 | const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{ | ||
| 1423 | ident_name, | ||
| 1424 | }); | ||
| 1425 | errdefer msg.destroy(mod.gpa); | ||
| 1426 | try mod.errNote(scope, local_ptr.src, msg, "previous definition is here", .{}); | ||
| 1427 | break :msg msg; | ||
| 1428 | }; | ||
| 1429 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 1430 | } | ||
| 1431 | s = local_ptr.parent; | ||
| 1432 | }, | ||
| 1433 | .gen_zir => s = s.cast(GenZir).?.parent, | ||
| 1434 | else => break, | ||
| 1435 | }; | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | // Namespace vars shadowing detection | ||
| 1439 | if (mod.lookupDeclName(scope, ident_name)) |_| { | ||
| 1440 | // TODO add note for other definition | ||
| 1441 | return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); | ||
| 1442 | } | ||
| 1443 | if (var_decl.ast.init_node == 0) { | ||
| 1444 | return mod.fail(scope, name_src, "variables must be initialized", .{}); | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | switch (token_tags[var_decl.ast.mut_token]) { | ||
| 1448 | .keyword_const => { | ||
| 1449 | // Depending on the type of AST the initialization expression is, we may need an lvalue | ||
| 1450 | // or an rvalue as a result location. If it is an rvalue, we can use the instruction as | ||
| 1451 | // the variable, no memory location needed. | ||
| 1452 | if (!nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node)) { | ||
| 1453 | const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{ | ||
| 1454 | .ty = try typeExpr(gz, scope, var_decl.ast.type_node), | ||
| 1455 | } else .none; | ||
| 1456 | const init_inst = try expr(gz, scope, result_loc, var_decl.ast.init_node); | ||
| 1457 | const sub_scope = try block_arena.create(Scope.LocalVal); | ||
| 1458 | sub_scope.* = .{ | ||
| 1459 | .parent = scope, | ||
| 1460 | .gen_zir = gz, | ||
| 1461 | .name = ident_name, | ||
| 1462 | .inst = init_inst, | ||
| 1463 | .src = name_src, | ||
| 1464 | }; | ||
| 1465 | return &sub_scope.base; | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | // Detect whether the initialization expression actually uses the | ||
| 1469 | // result location pointer. | ||
| 1470 | var init_scope: GenZir = .{ | ||
| 1471 | .parent = scope, | ||
| 1472 | .force_comptime = gz.force_comptime, | ||
| 1473 | .astgen = astgen, | ||
| 1474 | }; | ||
| 1475 | defer init_scope.instructions.deinit(mod.gpa); | ||
| 1476 | |||
| 1477 | var resolve_inferred_alloc: zir.Inst.Ref = .none; | ||
| 1478 | var opt_type_inst: zir.Inst.Ref = .none; | ||
| 1479 | if (var_decl.ast.type_node != 0) { | ||
| 1480 | const type_inst = try typeExpr(gz, &init_scope.base, var_decl.ast.type_node); | ||
| 1481 | opt_type_inst = type_inst; | ||
| 1482 | init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node); | ||
| 1483 | init_scope.rl_ty_inst = type_inst; | ||
| 1484 | } else { | ||
| 1485 | const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node); | ||
| 1486 | resolve_inferred_alloc = alloc; | ||
| 1487 | init_scope.rl_ptr = alloc; | ||
| 1488 | } | ||
| 1489 | const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope }; | ||
| 1490 | const init_inst = try expr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node); | ||
| 1491 | const zir_tags = astgen.instructions.items(.tag); | ||
| 1492 | const zir_datas = astgen.instructions.items(.data); | ||
| 1493 | |||
| 1494 | const parent_zir = &gz.instructions; | ||
| 1495 | if (init_scope.rvalue_rl_count == 1) { | ||
| 1496 | // Result location pointer not used. We don't need an alloc for this | ||
| 1497 | // const local, and type inference becomes trivial. | ||
| 1498 | // Move the init_scope instructions into the parent scope, eliding | ||
| 1499 | // the alloc instruction and the store_to_block_ptr instruction. | ||
| 1500 | const expected_len = parent_zir.items.len + init_scope.instructions.items.len - 2; | ||
| 1501 | try parent_zir.ensureCapacity(mod.gpa, expected_len); | ||
| 1502 | for (init_scope.instructions.items) |src_inst| { | ||
| 1503 | if (astgen.indexToRef(src_inst) == init_scope.rl_ptr) continue; | ||
| 1504 | if (zir_tags[src_inst] == .store_to_block_ptr) { | ||
| 1505 | if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue; | ||
| 1506 | } | ||
| 1507 | parent_zir.appendAssumeCapacity(src_inst); | ||
| 1508 | } | ||
| 1509 | assert(parent_zir.items.len == expected_len); | ||
| 1510 | |||
| 1511 | const sub_scope = try block_arena.create(Scope.LocalVal); | ||
| 1512 | sub_scope.* = .{ | ||
| 1513 | .parent = scope, | ||
| 1514 | .gen_zir = gz, | ||
| 1515 | .name = ident_name, | ||
| 1516 | .inst = init_inst, | ||
| 1517 | .src = name_src, | ||
| 1518 | }; | ||
| 1519 | return &sub_scope.base; | ||
| 1520 | } | ||
| 1521 | // The initialization expression took advantage of the result location | ||
| 1522 | // of the const local. In this case we will create an alloc and a LocalPtr for it. | ||
| 1523 | // Move the init_scope instructions into the parent scope, swapping | ||
| 1524 | // store_to_block_ptr for store_to_inferred_ptr. | ||
| 1525 | const expected_len = parent_zir.items.len + init_scope.instructions.items.len; | ||
| 1526 | try parent_zir.ensureCapacity(mod.gpa, expected_len); | ||
| 1527 | for (init_scope.instructions.items) |src_inst| { | ||
| 1528 | if (zir_tags[src_inst] == .store_to_block_ptr) { | ||
| 1529 | if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) { | ||
| 1530 | zir_tags[src_inst] = .store_to_inferred_ptr; | ||
| 1531 | } | ||
| 1532 | } | ||
| 1533 | parent_zir.appendAssumeCapacity(src_inst); | ||
| 1534 | } | ||
| 1535 | assert(parent_zir.items.len == expected_len); | ||
| 1536 | if (resolve_inferred_alloc != .none) { | ||
| 1537 | _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); | ||
| 1538 | } | ||
| 1539 | const sub_scope = try block_arena.create(Scope.LocalPtr); | ||
| 1540 | sub_scope.* = .{ | ||
| 1541 | .parent = scope, | ||
| 1542 | .gen_zir = gz, | ||
| 1543 | .name = ident_name, | ||
| 1544 | .ptr = init_scope.rl_ptr, | ||
| 1545 | .src = name_src, | ||
| 1546 | }; | ||
| 1547 | return &sub_scope.base; | ||
| 1548 | }, | ||
| 1549 | .keyword_var => { | ||
| 1550 | var resolve_inferred_alloc: zir.Inst.Ref = .none; | ||
| 1551 | const var_data: struct { | ||
| 1552 | result_loc: ResultLoc, | ||
| 1553 | alloc: zir.Inst.Ref, | ||
| 1554 | } = if (var_decl.ast.type_node != 0) a: { | ||
| 1555 | const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node); | ||
| 1556 | |||
| 1557 | const alloc = try gz.addUnNode(.alloc_mut, type_inst, node); | ||
| 1558 | break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } }; | ||
| 1559 | } else a: { | ||
| 1560 | const alloc = try gz.addUnNode(.alloc_inferred_mut, undefined, node); | ||
| 1561 | resolve_inferred_alloc = alloc; | ||
| 1562 | break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } }; | ||
| 1563 | }; | ||
| 1564 | const init_inst = try expr(gz, scope, var_data.result_loc, var_decl.ast.init_node); | ||
| 1565 | if (resolve_inferred_alloc != .none) { | ||
| 1566 | _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); | ||
| 1567 | } | ||
| 1568 | const sub_scope = try block_arena.create(Scope.LocalPtr); | ||
| 1569 | sub_scope.* = .{ | ||
| 1570 | .parent = scope, | ||
| 1571 | .gen_zir = gz, | ||
| 1572 | .name = ident_name, | ||
| 1573 | .ptr = var_data.alloc, | ||
| 1574 | .src = name_src, | ||
| 1575 | }; | ||
| 1576 | return &sub_scope.base; | ||
| 1577 | }, | ||
| 1578 | else => unreachable, | ||
| 1579 | } | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | fn assign(gz: *GenZir, scope: *Scope, infix_node: ast.Node.Index) InnerError!void { | ||
| 1583 | const tree = gz.tree(); | ||
| 1584 | const node_datas = tree.nodes.items(.data); | ||
| 1585 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1586 | const node_tags = tree.nodes.items(.tag); | ||
| 1587 | |||
| 1588 | const lhs = node_datas[infix_node].lhs; | ||
| 1589 | const rhs = node_datas[infix_node].rhs; | ||
| 1590 | if (node_tags[lhs] == .identifier) { | ||
| 1591 | // This intentionally does not support `@"_"` syntax. | ||
| 1592 | const ident_name = tree.tokenSlice(main_tokens[lhs]); | ||
| 1593 | if (mem.eql(u8, ident_name, "_")) { | ||
| 1594 | _ = try expr(gz, scope, .discard, rhs); | ||
| 1595 | return; | ||
| 1596 | } | ||
| 1597 | } | ||
| 1598 | const lvalue = try lvalExpr(gz, scope, lhs); | ||
| 1599 | _ = try expr(gz, scope, .{ .ptr = lvalue }, rhs); | ||
| 1600 | } | ||
| 1601 | |||
| 1602 | fn assignOp( | ||
| 1603 | gz: *GenZir, | ||
| 1604 | scope: *Scope, | ||
| 1605 | infix_node: ast.Node.Index, | ||
| 1606 | op_inst_tag: zir.Inst.Tag, | ||
| 1607 | ) InnerError!void { | ||
| 1608 | const tree = gz.tree(); | ||
| 1609 | const node_datas = tree.nodes.items(.data); | ||
| 1610 | |||
| 1611 | const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); | ||
| 1612 | const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); | ||
| 1613 | const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node); | ||
| 1614 | const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs); | ||
| 1615 | |||
| 1616 | const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{ | ||
| 1617 | .lhs = lhs, | ||
| 1618 | .rhs = rhs, | ||
| 1619 | }); | ||
| 1620 | _ = try gz.addBin(.store, lhs_ptr, result); | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 1624 | const tree = gz.tree(); | ||
| 1625 | const node_datas = tree.nodes.items(.data); | ||
| 1626 | |||
| 1627 | const operand = try expr(gz, scope, .{ .ty = .bool_type }, node_datas[node].lhs); | ||
| 1628 | const result = try gz.addUnNode(.bool_not, operand, node); | ||
| 1629 | return rvalue(gz, scope, rl, result, node); | ||
| 1630 | } | ||
| 1631 | |||
| 1632 | fn bitNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 1633 | const tree = gz.tree(); | ||
| 1634 | const node_datas = tree.nodes.items(.data); | ||
| 1635 | |||
| 1636 | const operand = try expr(gz, scope, .none, node_datas[node].lhs); | ||
| 1637 | const result = try gz.addUnNode(.bit_not, operand, node); | ||
| 1638 | return rvalue(gz, scope, rl, result, node); | ||
| 1639 | } | ||
| 1640 | |||
| 1641 | fn negation( | ||
| 1642 | gz: *GenZir, | ||
| 1643 | scope: *Scope, | ||
| 1644 | rl: ResultLoc, | ||
| 1645 | node: ast.Node.Index, | ||
| 1646 | tag: zir.Inst.Tag, | ||
| 1647 | ) InnerError!zir.Inst.Ref { | ||
| 1648 | const tree = gz.tree(); | ||
| 1649 | const node_datas = tree.nodes.items(.data); | ||
| 1650 | |||
| 1651 | const operand = try expr(gz, scope, .none, node_datas[node].lhs); | ||
| 1652 | const result = try gz.addUnNode(tag, operand, node); | ||
| 1653 | return rvalue(gz, scope, rl, result, node); | ||
| 1654 | } | ||
| 1655 | |||
| 1656 | fn ptrType( | ||
| 1657 | gz: *GenZir, | ||
| 1658 | scope: *Scope, | ||
| 1659 | rl: ResultLoc, | ||
| 1660 | node: ast.Node.Index, | ||
| 1661 | ptr_info: ast.full.PtrType, | ||
| 1662 | ) InnerError!zir.Inst.Ref { | ||
| 1663 | const tree = gz.tree(); | ||
| 1664 | |||
| 1665 | const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type); | ||
| 1666 | |||
| 1667 | const simple = ptr_info.ast.align_node == 0 and | ||
| 1668 | ptr_info.ast.sentinel == 0 and | ||
| 1669 | ptr_info.ast.bit_range_start == 0; | ||
| 1670 | |||
| 1671 | if (simple) { | ||
| 1672 | const result = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ | ||
| 1673 | .ptr_type_simple = .{ | ||
| 1674 | .is_allowzero = ptr_info.allowzero_token != null, | ||
| 1675 | .is_mutable = ptr_info.const_token == null, | ||
| 1676 | .is_volatile = ptr_info.volatile_token != null, | ||
| 1677 | .size = ptr_info.size, | ||
| 1678 | .elem_type = elem_type, | ||
| 1679 | }, | ||
| 1680 | } }); | ||
| 1681 | return rvalue(gz, scope, rl, result, node); | ||
| 1682 | } | ||
| 1683 | |||
| 1684 | var sentinel_ref: zir.Inst.Ref = .none; | ||
| 1685 | var align_ref: zir.Inst.Ref = .none; | ||
| 1686 | var bit_start_ref: zir.Inst.Ref = .none; | ||
| 1687 | var bit_end_ref: zir.Inst.Ref = .none; | ||
| 1688 | var trailing_count: u32 = 0; | ||
| 1689 | |||
| 1690 | if (ptr_info.ast.sentinel != 0) { | ||
| 1691 | sentinel_ref = try expr(gz, scope, .{ .ty = elem_type }, ptr_info.ast.sentinel); | ||
| 1692 | trailing_count += 1; | ||
| 1693 | } | ||
| 1694 | if (ptr_info.ast.align_node != 0) { | ||
| 1695 | align_ref = try expr(gz, scope, .none, ptr_info.ast.align_node); | ||
| 1696 | trailing_count += 1; | ||
| 1697 | } | ||
| 1698 | if (ptr_info.ast.bit_range_start != 0) { | ||
| 1699 | assert(ptr_info.ast.bit_range_end != 0); | ||
| 1700 | bit_start_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_start); | ||
| 1701 | bit_end_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_end); | ||
| 1702 | trailing_count += 2; | ||
| 1703 | } | ||
| 1704 | |||
| 1705 | const gpa = gz.astgen.mod.gpa; | ||
| 1706 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1707 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | ||
| 1708 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | ||
| 1709 | @typeInfo(zir.Inst.PtrType).Struct.fields.len + trailing_count); | ||
| 1710 | |||
| 1711 | const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.PtrType{ .elem_type = elem_type }); | ||
| 1712 | if (sentinel_ref != .none) { | ||
| 1713 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(sentinel_ref)); | ||
| 1714 | } | ||
| 1715 | if (align_ref != .none) { | ||
| 1716 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref)); | ||
| 1717 | } | ||
| 1718 | if (bit_start_ref != .none) { | ||
| 1719 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref)); | ||
| 1720 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref)); | ||
| 1721 | } | ||
| 1722 | |||
| 1723 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1724 | const result = gz.astgen.indexToRef(new_index); | ||
| 1725 | gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{ | ||
| 1726 | .ptr_type = .{ | ||
| 1727 | .flags = .{ | ||
| 1728 | .is_allowzero = ptr_info.allowzero_token != null, | ||
| 1729 | .is_mutable = ptr_info.const_token == null, | ||
| 1730 | .is_volatile = ptr_info.volatile_token != null, | ||
| 1731 | .has_sentinel = sentinel_ref != .none, | ||
| 1732 | .has_align = align_ref != .none, | ||
| 1733 | .has_bit_range = bit_start_ref != .none, | ||
| 1734 | }, | ||
| 1735 | .size = ptr_info.size, | ||
| 1736 | .payload_index = payload_index, | ||
| 1737 | }, | ||
| 1738 | } }); | ||
| 1739 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1740 | |||
| 1741 | return rvalue(gz, scope, rl, result, node); | ||
| 1742 | } | ||
| 1743 | |||
| 1744 | fn arrayType(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { | ||
| 1745 | const tree = gz.tree(); | ||
| 1746 | const node_datas = tree.nodes.items(.data); | ||
| 1747 | |||
| 1748 | // TODO check for [_]T | ||
| 1749 | const len = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].lhs); | ||
| 1750 | const elem_type = try typeExpr(gz, scope, node_datas[node].rhs); | ||
| 1751 | |||
| 1752 | const result = try gz.addBin(.array_type, len, elem_type); | ||
| 1753 | return rvalue(gz, scope, rl, result, node); | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { | ||
| 1757 | const tree = gz.tree(); | ||
| 1758 | const node_datas = tree.nodes.items(.data); | ||
| 1759 | const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel); | ||
| 1760 | |||
| 1761 | // TODO check for [_]T | ||
| 1762 | const len = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].lhs); | ||
| 1763 | const elem_type = try typeExpr(gz, scope, extra.elem_type); | ||
| 1764 | const sentinel = try expr(gz, scope, .{ .ty = elem_type }, extra.sentinel); | ||
| 1765 | |||
| 1766 | const result = try gz.addArrayTypeSentinel(len, elem_type, sentinel); | ||
| 1767 | return rvalue(gz, scope, rl, result, node); | ||
| 1768 | } | ||
| 1769 | |||
| 1770 | fn containerDecl( | ||
| 1771 | gz: *GenZir, | ||
| 1772 | scope: *Scope, | ||
| 1773 | rl: ResultLoc, | ||
| 1774 | node: ast.Node.Index, | ||
| 1775 | container_decl: ast.full.ContainerDecl, | ||
| 1776 | ) InnerError!zir.Inst.Ref { | ||
| 1777 | const astgen = gz.astgen; | ||
| 1778 | const mod = astgen.mod; | ||
| 1779 | const gpa = mod.gpa; | ||
| 1780 | const tree = gz.tree(); | ||
| 1781 | const token_tags = tree.tokens.items(.tag); | ||
| 1782 | const node_tags = tree.nodes.items(.tag); | ||
| 1783 | |||
| 1784 | // We must not create any types until Sema. Here the goal is only to generate | ||
| 1785 | // ZIR for all the field types, alignments, and default value expressions. | ||
| 1786 | |||
| 1787 | const arg_inst: zir.Inst.Ref = if (container_decl.ast.arg != 0) | ||
| 1788 | try comptimeExpr(gz, scope, .none, container_decl.ast.arg) | ||
| 1789 | else | ||
| 1790 | .none; | ||
| 1791 | |||
| 1792 | switch (token_tags[container_decl.ast.main_token]) { | ||
| 1793 | .keyword_struct => { | ||
| 1794 | const tag = if (container_decl.layout_token) |t| switch (token_tags[t]) { | ||
| 1795 | .keyword_packed => zir.Inst.Tag.struct_decl_packed, | ||
| 1796 | .keyword_extern => zir.Inst.Tag.struct_decl_extern, | ||
| 1797 | else => unreachable, | ||
| 1798 | } else zir.Inst.Tag.struct_decl; | ||
| 1799 | if (container_decl.ast.members.len == 0) { | ||
| 1800 | const result = try gz.addPlNode(tag, node, zir.Inst.StructDecl{ | ||
| 1801 | .fields_len = 0, | ||
| 1802 | }); | ||
| 1803 | return rvalue(gz, scope, rl, result, node); | ||
| 1804 | } | ||
| 1805 | |||
| 1806 | assert(arg_inst == .none); | ||
| 1807 | var fields_data = ArrayListUnmanaged(u32){}; | ||
| 1808 | defer fields_data.deinit(gpa); | ||
| 1809 | |||
| 1810 | // field_name and field_type are both mandatory | ||
| 1811 | try fields_data.ensureCapacity(gpa, container_decl.ast.members.len * 2); | ||
| 1812 | |||
| 1813 | // We only need this if there are greater than 16 fields. | ||
| 1814 | var bit_bag = ArrayListUnmanaged(u32){}; | ||
| 1815 | defer bit_bag.deinit(gpa); | ||
| 1816 | |||
| 1817 | var cur_bit_bag: u32 = 0; | ||
| 1818 | var member_index: usize = 0; | ||
| 1819 | while (true) { | ||
| 1820 | const member_node = container_decl.ast.members[member_index]; | ||
| 1821 | const member = switch (node_tags[member_node]) { | ||
| 1822 | .container_field_init => tree.containerFieldInit(member_node), | ||
| 1823 | .container_field_align => tree.containerFieldAlign(member_node), | ||
| 1824 | .container_field => tree.containerField(member_node), | ||
| 1825 | else => unreachable, | ||
| 1826 | }; | ||
| 1827 | if (member.comptime_token) |comptime_token| { | ||
| 1828 | return mod.failTok(scope, comptime_token, "TODO implement comptime struct fields", .{}); | ||
| 1829 | } | ||
| 1830 | try fields_data.ensureCapacity(gpa, fields_data.items.len + 4); | ||
| 1831 | |||
| 1832 | const field_name = try gz.identAsString(member.ast.name_token); | ||
| 1833 | fields_data.appendAssumeCapacity(field_name); | ||
| 1834 | |||
| 1835 | const field_type = try typeExpr(gz, scope, member.ast.type_expr); | ||
| 1836 | fields_data.appendAssumeCapacity(@enumToInt(field_type)); | ||
| 1837 | |||
| 1838 | const have_align = member.ast.align_expr != 0; | ||
| 1839 | const have_value = member.ast.value_expr != 0; | ||
| 1840 | cur_bit_bag = (cur_bit_bag >> 2) | | ||
| 1841 | (@as(u32, @boolToInt(have_align)) << 30) | | ||
| 1842 | (@as(u32, @boolToInt(have_value)) << 31); | ||
| 1843 | |||
| 1844 | if (have_align) { | ||
| 1845 | const align_inst = try comptimeExpr(gz, scope, .{ .ty = .u32_type }, member.ast.align_expr); | ||
| 1846 | fields_data.appendAssumeCapacity(@enumToInt(align_inst)); | ||
| 1847 | } | ||
| 1848 | if (have_value) { | ||
| 1849 | const default_inst = try comptimeExpr(gz, scope, .{ .ty = field_type }, member.ast.value_expr); | ||
| 1850 | fields_data.appendAssumeCapacity(@enumToInt(default_inst)); | ||
| 1851 | } | ||
| 1852 | |||
| 1853 | member_index += 1; | ||
| 1854 | if (member_index < container_decl.ast.members.len) { | ||
| 1855 | if (member_index % 16 == 0) { | ||
| 1856 | try bit_bag.append(gpa, cur_bit_bag); | ||
| 1857 | cur_bit_bag = 0; | ||
| 1858 | } | ||
| 1859 | } else { | ||
| 1860 | break; | ||
| 1861 | } | ||
| 1862 | } | ||
| 1863 | const empty_slot_count = 16 - ((member_index - 1) % 16); | ||
| 1864 | cur_bit_bag >>= @intCast(u5, empty_slot_count * 2); | ||
| 1865 | |||
| 1866 | const result = try gz.addPlNode(tag, node, zir.Inst.StructDecl{ | ||
| 1867 | .fields_len = @intCast(u32, container_decl.ast.members.len), | ||
| 1868 | }); | ||
| 1869 | try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + | ||
| 1870 | bit_bag.items.len + 1 + fields_data.items.len); | ||
| 1871 | astgen.extra.appendSliceAssumeCapacity(bit_bag.items); // Likely empty. | ||
| 1872 | astgen.extra.appendAssumeCapacity(cur_bit_bag); | ||
| 1873 | astgen.extra.appendSliceAssumeCapacity(fields_data.items); | ||
| 1874 | return rvalue(gz, scope, rl, result, node); | ||
| 1875 | }, | ||
| 1876 | .keyword_union => { | ||
| 1877 | return mod.failTok(scope, container_decl.ast.main_token, "TODO AstGen for union decl", .{}); | ||
| 1878 | }, | ||
| 1879 | .keyword_enum => { | ||
| 1880 | return mod.failTok(scope, container_decl.ast.main_token, "TODO AstGen for enum decl", .{}); | ||
| 1881 | }, | ||
| 1882 | .keyword_opaque => { | ||
| 1883 | const result = try gz.addNode(.opaque_decl, node); | ||
| 1884 | return rvalue(gz, scope, rl, result, node); | ||
| 1885 | }, | ||
| 1886 | else => unreachable, | ||
| 1887 | } | ||
| 1888 | } | ||
| 1889 | |||
| 1890 | fn errorSetDecl( | ||
| 1891 | gz: *GenZir, | ||
| 1892 | scope: *Scope, | ||
| 1893 | rl: ResultLoc, | ||
| 1894 | node: ast.Node.Index, | ||
| 1895 | ) InnerError!zir.Inst.Ref { | ||
| 1896 | const mod = gz.astgen.mod; | ||
| 1897 | const tree = gz.tree(); | ||
| 1898 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1899 | const token_tags = tree.tokens.items(.tag); | ||
| 1900 | const arena = gz.astgen.arena; | ||
| 1901 | |||
| 1902 | // Count how many fields there are. | ||
| 1903 | const error_token = main_tokens[node]; | ||
| 1904 | const count: usize = count: { | ||
| 1905 | var tok_i = error_token + 2; | ||
| 1906 | var count: usize = 0; | ||
| 1907 | while (true) : (tok_i += 1) { | ||
| 1908 | switch (token_tags[tok_i]) { | ||
| 1909 | .doc_comment, .comma => {}, | ||
| 1910 | .identifier => count += 1, | ||
| 1911 | .r_brace => break :count count, | ||
| 1912 | else => unreachable, | ||
| 1913 | } | ||
| 1914 | } else unreachable; // TODO should not need else unreachable here | ||
| 1915 | }; | ||
| 1916 | |||
| 1917 | const fields = try arena.alloc([]const u8, count); | ||
| 1918 | { | ||
| 1919 | var tok_i = error_token + 2; | ||
| 1920 | var field_i: usize = 0; | ||
| 1921 | while (true) : (tok_i += 1) { | ||
| 1922 | switch (token_tags[tok_i]) { | ||
| 1923 | .doc_comment, .comma => {}, | ||
| 1924 | .identifier => { | ||
| 1925 | fields[field_i] = try mod.identifierTokenString(scope, tok_i); | ||
| 1926 | field_i += 1; | ||
| 1927 | }, | ||
| 1928 | .r_brace => break, | ||
| 1929 | else => unreachable, | ||
| 1930 | } | ||
| 1931 | } | ||
| 1932 | } | ||
| 1933 | const error_set = try arena.create(Module.ErrorSet); | ||
| 1934 | error_set.* = .{ | ||
| 1935 | .owner_decl = gz.astgen.decl, | ||
| 1936 | .node_offset = gz.astgen.decl.nodeIndexToRelative(node), | ||
| 1937 | .names_ptr = fields.ptr, | ||
| 1938 | .names_len = @intCast(u32, fields.len), | ||
| 1939 | }; | ||
| 1940 | const error_set_ty = try Type.Tag.error_set.create(arena, error_set); | ||
| 1941 | const typed_value = try arena.create(TypedValue); | ||
| 1942 | typed_value.* = .{ | ||
| 1943 | .ty = Type.initTag(.type), | ||
| 1944 | .val = try Value.Tag.ty.create(arena, error_set_ty), | ||
| 1945 | }; | ||
| 1946 | const result = try gz.addConst(typed_value); | ||
| 1947 | return rvalue(gz, scope, rl, result, node); | ||
| 1948 | } | ||
| 1949 | |||
| 1950 | fn orelseCatchExpr( | ||
| 1951 | parent_gz: *GenZir, | ||
| 1952 | scope: *Scope, | ||
| 1953 | rl: ResultLoc, | ||
| 1954 | node: ast.Node.Index, | ||
| 1955 | lhs: ast.Node.Index, | ||
| 1956 | cond_op: zir.Inst.Tag, | ||
| 1957 | unwrap_op: zir.Inst.Tag, | ||
| 1958 | unwrap_code_op: zir.Inst.Tag, | ||
| 1959 | rhs: ast.Node.Index, | ||
| 1960 | payload_token: ?ast.TokenIndex, | ||
| 1961 | ) InnerError!zir.Inst.Ref { | ||
| 1962 | const mod = parent_gz.astgen.mod; | ||
| 1963 | const tree = parent_gz.tree(); | ||
| 1964 | |||
| 1965 | var block_scope: GenZir = .{ | ||
| 1966 | .parent = scope, | ||
| 1967 | .astgen = parent_gz.astgen, | ||
| 1968 | .force_comptime = parent_gz.force_comptime, | ||
| 1969 | .instructions = .{}, | ||
| 1970 | }; | ||
| 1971 | block_scope.setBreakResultLoc(rl); | ||
| 1972 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 1973 | |||
| 1974 | // This could be a pointer or value depending on the `operand_rl` parameter. | ||
| 1975 | // We cannot use `block_scope.break_result_loc` because that has the bare | ||
| 1976 | // type, whereas this expression has the optional type. Later we make | ||
| 1977 | // up for this fact by calling rvalue on the else branch. | ||
| 1978 | block_scope.break_count += 1; | ||
| 1979 | |||
| 1980 | // TODO handle catch | ||
| 1981 | const operand_rl: ResultLoc = switch (block_scope.break_result_loc) { | ||
| 1982 | .ref => .ref, | ||
| 1983 | .discard, .none, .block_ptr, .inferred_ptr => .none, | ||
| 1984 | .ty => |elem_ty| blk: { | ||
| 1985 | const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node); | ||
| 1986 | break :blk .{ .ty = wrapped_ty }; | ||
| 1987 | }, | ||
| 1988 | .ptr => |ptr_ty| blk: { | ||
| 1989 | const wrapped_ty = try block_scope.addUnNode(.optional_type_from_ptr_elem, ptr_ty, node); | ||
| 1990 | break :blk .{ .ty = wrapped_ty }; | ||
| 1991 | }, | ||
| 1992 | }; | ||
| 1993 | const operand = try expr(&block_scope, &block_scope.base, operand_rl, lhs); | ||
| 1994 | const cond = try block_scope.addUnNode(cond_op, operand, node); | ||
| 1995 | const condbr = try block_scope.addCondBr(.condbr, node); | ||
| 1996 | |||
| 1997 | const block = try parent_gz.addBlock(.block, node); | ||
| 1998 | try parent_gz.instructions.append(mod.gpa, block); | ||
| 1999 | try block_scope.setBlockBody(block); | ||
| 2000 | |||
| 2001 | var then_scope: GenZir = .{ | ||
| 2002 | .parent = scope, | ||
| 2003 | .astgen = parent_gz.astgen, | ||
| 2004 | .force_comptime = block_scope.force_comptime, | ||
| 2005 | .instructions = .{}, | ||
| 2006 | }; | ||
| 2007 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 2008 | |||
| 2009 | var err_val_scope: Scope.LocalVal = undefined; | ||
| 2010 | const then_sub_scope = blk: { | ||
| 2011 | const payload = payload_token orelse break :blk &then_scope.base; | ||
| 2012 | if (mem.eql(u8, tree.tokenSlice(payload), "_")) { | ||
| 2013 | return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{}); | ||
| 2014 | } | ||
| 2015 | const err_name = try mod.identifierTokenString(scope, payload); | ||
| 2016 | err_val_scope = .{ | ||
| 2017 | .parent = &then_scope.base, | ||
| 2018 | .gen_zir = &then_scope, | ||
| 2019 | .name = err_name, | ||
| 2020 | .inst = try then_scope.addUnNode(unwrap_code_op, operand, node), | ||
| 2021 | .src = parent_gz.tokSrcLoc(payload), | ||
| 2022 | }; | ||
| 2023 | break :blk &err_val_scope.base; | ||
| 2024 | }; | ||
| 2025 | |||
| 2026 | block_scope.break_count += 1; | ||
| 2027 | const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, rhs); | ||
| 2028 | // We hold off on the break instructions as well as copying the then/else | ||
| 2029 | // instructions into place until we know whether to keep store_to_block_ptr | ||
| 2030 | // instructions or not. | ||
| 2031 | |||
| 2032 | var else_scope: GenZir = .{ | ||
| 2033 | .parent = scope, | ||
| 2034 | .astgen = parent_gz.astgen, | ||
| 2035 | .force_comptime = block_scope.force_comptime, | ||
| 2036 | .instructions = .{}, | ||
| 2037 | }; | ||
| 2038 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2039 | |||
| 2040 | // This could be a pointer or value depending on `unwrap_op`. | ||
| 2041 | const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node); | ||
| 2042 | const else_result = switch (rl) { | ||
| 2043 | .ref => unwrapped_payload, | ||
| 2044 | else => try rvalue(&else_scope, &else_scope.base, block_scope.break_result_loc, unwrapped_payload, node), | ||
| 2045 | }; | ||
| 2046 | |||
| 2047 | return finishThenElseBlock( | ||
| 2048 | parent_gz, | ||
| 2049 | scope, | ||
| 2050 | rl, | ||
| 2051 | node, | ||
| 2052 | &block_scope, | ||
| 2053 | &then_scope, | ||
| 2054 | &else_scope, | ||
| 2055 | condbr, | ||
| 2056 | cond, | ||
| 2057 | node, | ||
| 2058 | node, | ||
| 2059 | then_result, | ||
| 2060 | else_result, | ||
| 2061 | block, | ||
| 2062 | block, | ||
| 2063 | .@"break", | ||
| 2064 | ); | ||
| 2065 | } | ||
| 2066 | |||
| 2067 | fn finishThenElseBlock( | ||
| 2068 | parent_gz: *GenZir, | ||
| 2069 | parent_scope: *Scope, | ||
| 2070 | rl: ResultLoc, | ||
| 2071 | node: ast.Node.Index, | ||
| 2072 | block_scope: *GenZir, | ||
| 2073 | then_scope: *GenZir, | ||
| 2074 | else_scope: *GenZir, | ||
| 2075 | condbr: zir.Inst.Index, | ||
| 2076 | cond: zir.Inst.Ref, | ||
| 2077 | then_src: ast.Node.Index, | ||
| 2078 | else_src: ast.Node.Index, | ||
| 2079 | then_result: zir.Inst.Ref, | ||
| 2080 | else_result: zir.Inst.Ref, | ||
| 2081 | main_block: zir.Inst.Index, | ||
| 2082 | then_break_block: zir.Inst.Index, | ||
| 2083 | break_tag: zir.Inst.Tag, | ||
| 2084 | ) InnerError!zir.Inst.Ref { | ||
| 2085 | // We now have enough information to decide whether the result instruction should | ||
| 2086 | // be communicated via result location pointer or break instructions. | ||
| 2087 | const strat = rl.strategy(block_scope); | ||
| 2088 | const astgen = block_scope.astgen; | ||
| 2089 | switch (strat.tag) { | ||
| 2090 | .break_void => { | ||
| 2091 | if (!astgen.refIsNoReturn(then_result)) { | ||
| 2092 | _ = try then_scope.addBreak(break_tag, then_break_block, .void_value); | ||
| 2093 | } | ||
| 2094 | const elide_else = if (else_result != .none) astgen.refIsNoReturn(else_result) else false; | ||
| 2095 | if (!elide_else) { | ||
| 2096 | _ = try else_scope.addBreak(break_tag, main_block, .void_value); | ||
| 2097 | } | ||
| 2098 | assert(!strat.elide_store_to_block_ptr_instructions); | ||
| 2099 | try setCondBrPayload(condbr, cond, then_scope, else_scope); | ||
| 2100 | return astgen.indexToRef(main_block); | ||
| 2101 | }, | ||
| 2102 | .break_operand => { | ||
| 2103 | if (!astgen.refIsNoReturn(then_result)) { | ||
| 2104 | _ = try then_scope.addBreak(break_tag, then_break_block, then_result); | ||
| 2105 | } | ||
| 2106 | if (else_result != .none) { | ||
| 2107 | if (!astgen.refIsNoReturn(else_result)) { | ||
| 2108 | _ = try else_scope.addBreak(break_tag, main_block, else_result); | ||
| 2109 | } | ||
| 2110 | } else { | ||
| 2111 | _ = try else_scope.addBreak(break_tag, main_block, .void_value); | ||
| 2112 | } | ||
| 2113 | if (strat.elide_store_to_block_ptr_instructions) { | ||
| 2114 | try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope); | ||
| 2115 | } else { | ||
| 2116 | try setCondBrPayload(condbr, cond, then_scope, else_scope); | ||
| 2117 | } | ||
| 2118 | const block_ref = astgen.indexToRef(main_block); | ||
| 2119 | switch (rl) { | ||
| 2120 | .ref => return block_ref, | ||
| 2121 | else => return rvalue(parent_gz, parent_scope, rl, block_ref, node), | ||
| 2122 | } | ||
| 2123 | }, | ||
| 2124 | } | ||
| 2125 | } | ||
| 2126 | |||
| 2127 | /// Return whether the identifier names of two tokens are equal. Resolves @"" | ||
| 2128 | /// tokens without allocating. | ||
| 2129 | /// OK in theory it could do it without allocating. This implementation | ||
| 2130 | /// allocates when the @"" form is used. | ||
| 2131 | fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { | ||
| 2132 | const ident_name_1 = try mod.identifierTokenString(scope, token1); | ||
| 2133 | const ident_name_2 = try mod.identifierTokenString(scope, token2); | ||
| 2134 | return mem.eql(u8, ident_name_1, ident_name_2); | ||
| 2135 | } | ||
| 2136 | |||
| 2137 | pub fn fieldAccess( | ||
| 2138 | gz: *GenZir, | ||
| 2139 | scope: *Scope, | ||
| 2140 | rl: ResultLoc, | ||
| 2141 | node: ast.Node.Index, | ||
| 2142 | ) InnerError!zir.Inst.Ref { | ||
| 2143 | const astgen = gz.astgen; | ||
| 2144 | const mod = astgen.mod; | ||
| 2145 | const tree = gz.tree(); | ||
| 2146 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2147 | const node_datas = tree.nodes.items(.data); | ||
| 2148 | |||
| 2149 | const object_node = node_datas[node].lhs; | ||
| 2150 | const dot_token = main_tokens[node]; | ||
| 2151 | const field_ident = dot_token + 1; | ||
| 2152 | const str_index = try gz.identAsString(field_ident); | ||
| 2153 | switch (rl) { | ||
| 2154 | .ref => return gz.addPlNode(.field_ptr, node, zir.Inst.Field{ | ||
| 2155 | .lhs = try expr(gz, scope, .ref, object_node), | ||
| 2156 | .field_name_start = str_index, | ||
| 2157 | }), | ||
| 2158 | else => return rvalue(gz, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{ | ||
| 2159 | .lhs = try expr(gz, scope, .none, object_node), | ||
| 2160 | .field_name_start = str_index, | ||
| 2161 | }), node), | ||
| 2162 | } | ||
| 2163 | } | ||
| 2164 | |||
| 2165 | fn arrayAccess( | ||
| 2166 | gz: *GenZir, | ||
| 2167 | scope: *Scope, | ||
| 2168 | rl: ResultLoc, | ||
| 2169 | node: ast.Node.Index, | ||
| 2170 | ) InnerError!zir.Inst.Ref { | ||
| 2171 | const tree = gz.tree(); | ||
| 2172 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2173 | const node_datas = tree.nodes.items(.data); | ||
| 2174 | switch (rl) { | ||
| 2175 | .ref => return gz.addBin( | ||
| 2176 | .elem_ptr, | ||
| 2177 | try expr(gz, scope, .ref, node_datas[node].lhs), | ||
| 2178 | try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs), | ||
| 2179 | ), | ||
| 2180 | else => return rvalue(gz, scope, rl, try gz.addBin( | ||
| 2181 | .elem_val, | ||
| 2182 | try expr(gz, scope, .none, node_datas[node].lhs), | ||
| 2183 | try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs), | ||
| 2184 | ), node), | ||
| 2185 | } | ||
| 2186 | } | ||
| 2187 | |||
| 2188 | fn simpleBinOp( | ||
| 2189 | gz: *GenZir, | ||
| 2190 | scope: *Scope, | ||
| 2191 | rl: ResultLoc, | ||
| 2192 | node: ast.Node.Index, | ||
| 2193 | op_inst_tag: zir.Inst.Tag, | ||
| 2194 | ) InnerError!zir.Inst.Ref { | ||
| 2195 | const tree = gz.tree(); | ||
| 2196 | const node_datas = tree.nodes.items(.data); | ||
| 2197 | |||
| 2198 | const result = try gz.addPlNode(op_inst_tag, node, zir.Inst.Bin{ | ||
| 2199 | .lhs = try expr(gz, scope, .none, node_datas[node].lhs), | ||
| 2200 | .rhs = try expr(gz, scope, .none, node_datas[node].rhs), | ||
| 2201 | }); | ||
| 2202 | return rvalue(gz, scope, rl, result, node); | ||
| 2203 | } | ||
| 2204 | |||
| 2205 | fn simpleStrTok( | ||
| 2206 | gz: *GenZir, | ||
| 2207 | scope: *Scope, | ||
| 2208 | rl: ResultLoc, | ||
| 2209 | ident_token: ast.TokenIndex, | ||
| 2210 | node: ast.Node.Index, | ||
| 2211 | op_inst_tag: zir.Inst.Tag, | ||
| 2212 | ) InnerError!zir.Inst.Ref { | ||
| 2213 | const str_index = try gz.identAsString(ident_token); | ||
| 2214 | const result = try gz.addStrTok(op_inst_tag, str_index, ident_token); | ||
| 2215 | return rvalue(gz, scope, rl, result, node); | ||
| 2216 | } | ||
| 2217 | |||
| 2218 | fn boolBinOp( | ||
| 2219 | gz: *GenZir, | ||
| 2220 | scope: *Scope, | ||
| 2221 | rl: ResultLoc, | ||
| 2222 | node: ast.Node.Index, | ||
| 2223 | zir_tag: zir.Inst.Tag, | ||
| 2224 | ) InnerError!zir.Inst.Ref { | ||
| 2225 | const node_datas = gz.tree().nodes.items(.data); | ||
| 2226 | |||
| 2227 | const lhs = try expr(gz, scope, .{ .ty = .bool_type }, node_datas[node].lhs); | ||
| 2228 | const bool_br = try gz.addBoolBr(zir_tag, lhs); | ||
| 2229 | |||
| 2230 | var rhs_scope: GenZir = .{ | ||
| 2231 | .parent = scope, | ||
| 2232 | .astgen = gz.astgen, | ||
| 2233 | .force_comptime = gz.force_comptime, | ||
| 2234 | }; | ||
| 2235 | defer rhs_scope.instructions.deinit(gz.astgen.mod.gpa); | ||
| 2236 | const rhs = try expr(&rhs_scope, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs); | ||
| 2237 | _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs); | ||
| 2238 | try rhs_scope.setBoolBrBody(bool_br); | ||
| 2239 | |||
| 2240 | const block_ref = gz.astgen.indexToRef(bool_br); | ||
| 2241 | return rvalue(gz, scope, rl, block_ref, node); | ||
| 2242 | } | ||
| 2243 | |||
| 2244 | fn ifExpr( | ||
| 2245 | parent_gz: *GenZir, | ||
| 2246 | scope: *Scope, | ||
| 2247 | rl: ResultLoc, | ||
| 2248 | node: ast.Node.Index, | ||
| 2249 | if_full: ast.full.If, | ||
| 2250 | ) InnerError!zir.Inst.Ref { | ||
| 2251 | const mod = parent_gz.astgen.mod; | ||
| 2252 | |||
| 2253 | var block_scope: GenZir = .{ | ||
| 2254 | .parent = scope, | ||
| 2255 | .astgen = parent_gz.astgen, | ||
| 2256 | .force_comptime = parent_gz.force_comptime, | ||
| 2257 | .instructions = .{}, | ||
| 2258 | }; | ||
| 2259 | block_scope.setBreakResultLoc(rl); | ||
| 2260 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 2261 | |||
| 2262 | const cond = c: { | ||
| 2263 | // TODO https://github.com/ziglang/zig/issues/7929 | ||
| 2264 | if (if_full.error_token) |error_token| { | ||
| 2265 | return mod.failTok(scope, error_token, "TODO implement if error union", .{}); | ||
| 2266 | } else if (if_full.payload_token) |payload_token| { | ||
| 2267 | return mod.failTok(scope, payload_token, "TODO implement if optional", .{}); | ||
| 2268 | } else { | ||
| 2269 | break :c try expr(&block_scope, &block_scope.base, .{ .ty = .bool_type }, if_full.ast.cond_expr); | ||
| 2270 | } | ||
| 2271 | }; | ||
| 2272 | |||
| 2273 | const condbr = try block_scope.addCondBr(.condbr, node); | ||
| 2274 | |||
| 2275 | const block = try parent_gz.addBlock(.block, node); | ||
| 2276 | try parent_gz.instructions.append(mod.gpa, block); | ||
| 2277 | try block_scope.setBlockBody(block); | ||
| 2278 | |||
| 2279 | var then_scope: GenZir = .{ | ||
| 2280 | .parent = scope, | ||
| 2281 | .astgen = parent_gz.astgen, | ||
| 2282 | .force_comptime = block_scope.force_comptime, | ||
| 2283 | .instructions = .{}, | ||
| 2284 | }; | ||
| 2285 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 2286 | |||
| 2287 | // declare payload to the then_scope | ||
| 2288 | const then_sub_scope = &then_scope.base; | ||
| 2289 | |||
| 2290 | block_scope.break_count += 1; | ||
| 2291 | const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr); | ||
| 2292 | // We hold off on the break instructions as well as copying the then/else | ||
| 2293 | // instructions into place until we know whether to keep store_to_block_ptr | ||
| 2294 | // instructions or not. | ||
| 2295 | |||
| 2296 | var else_scope: GenZir = .{ | ||
| 2297 | .parent = scope, | ||
| 2298 | .astgen = parent_gz.astgen, | ||
| 2299 | .force_comptime = block_scope.force_comptime, | ||
| 2300 | .instructions = .{}, | ||
| 2301 | }; | ||
| 2302 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2303 | |||
| 2304 | const else_node = if_full.ast.else_expr; | ||
| 2305 | const else_info: struct { | ||
| 2306 | src: ast.Node.Index, | ||
| 2307 | result: zir.Inst.Ref, | ||
| 2308 | } = if (else_node != 0) blk: { | ||
| 2309 | block_scope.break_count += 1; | ||
| 2310 | const sub_scope = &else_scope.base; | ||
| 2311 | break :blk .{ | ||
| 2312 | .src = else_node, | ||
| 2313 | .result = try expr(&else_scope, sub_scope, block_scope.break_result_loc, else_node), | ||
| 2314 | }; | ||
| 2315 | } else .{ | ||
| 2316 | .src = if_full.ast.then_expr, | ||
| 2317 | .result = .none, | ||
| 2318 | }; | ||
| 2319 | |||
| 2320 | return finishThenElseBlock( | ||
| 2321 | parent_gz, | ||
| 2322 | scope, | ||
| 2323 | rl, | ||
| 2324 | node, | ||
| 2325 | &block_scope, | ||
| 2326 | &then_scope, | ||
| 2327 | &else_scope, | ||
| 2328 | condbr, | ||
| 2329 | cond, | ||
| 2330 | if_full.ast.then_expr, | ||
| 2331 | else_info.src, | ||
| 2332 | then_result, | ||
| 2333 | else_info.result, | ||
| 2334 | block, | ||
| 2335 | block, | ||
| 2336 | .@"break", | ||
| 2337 | ); | ||
| 2338 | } | ||
| 2339 | |||
| 2340 | fn setCondBrPayload( | ||
| 2341 | condbr: zir.Inst.Index, | ||
| 2342 | cond: zir.Inst.Ref, | ||
| 2343 | then_scope: *GenZir, | ||
| 2344 | else_scope: *GenZir, | ||
| 2345 | ) !void { | ||
| 2346 | const astgen = then_scope.astgen; | ||
| 2347 | |||
| 2348 | try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len + | ||
| 2349 | @typeInfo(zir.Inst.CondBr).Struct.fields.len + | ||
| 2350 | then_scope.instructions.items.len + else_scope.instructions.items.len); | ||
| 2351 | |||
| 2352 | const zir_datas = astgen.instructions.items(.data); | ||
| 2353 | zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{ | ||
| 2354 | .condition = cond, | ||
| 2355 | .then_body_len = @intCast(u32, then_scope.instructions.items.len), | ||
| 2356 | .else_body_len = @intCast(u32, else_scope.instructions.items.len), | ||
| 2357 | }); | ||
| 2358 | astgen.extra.appendSliceAssumeCapacity(then_scope.instructions.items); | ||
| 2359 | astgen.extra.appendSliceAssumeCapacity(else_scope.instructions.items); | ||
| 2360 | } | ||
| 2361 | |||
| 2362 | /// If `elide_block_store_ptr` is set, expects to find exactly 1 .store_to_block_ptr instruction. | ||
| 2363 | fn setCondBrPayloadElideBlockStorePtr( | ||
| 2364 | condbr: zir.Inst.Index, | ||
| 2365 | cond: zir.Inst.Ref, | ||
| 2366 | then_scope: *GenZir, | ||
| 2367 | else_scope: *GenZir, | ||
| 2368 | ) !void { | ||
| 2369 | const astgen = then_scope.astgen; | ||
| 2370 | |||
| 2371 | try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len + | ||
| 2372 | @typeInfo(zir.Inst.CondBr).Struct.fields.len + | ||
| 2373 | then_scope.instructions.items.len + else_scope.instructions.items.len - 2); | ||
| 2374 | |||
| 2375 | const zir_datas = astgen.instructions.items(.data); | ||
| 2376 | zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{ | ||
| 2377 | .condition = cond, | ||
| 2378 | .then_body_len = @intCast(u32, then_scope.instructions.items.len - 1), | ||
| 2379 | .else_body_len = @intCast(u32, else_scope.instructions.items.len - 1), | ||
| 2380 | }); | ||
| 2381 | |||
| 2382 | const zir_tags = astgen.instructions.items(.tag); | ||
| 2383 | for ([_]*GenZir{ then_scope, else_scope }) |scope| { | ||
| 2384 | for (scope.instructions.items) |src_inst| { | ||
| 2385 | if (zir_tags[src_inst] != .store_to_block_ptr) { | ||
| 2386 | astgen.extra.appendAssumeCapacity(src_inst); | ||
| 2387 | } | ||
| 2388 | } | ||
| 2389 | } | ||
| 2390 | } | ||
| 2391 | |||
| 2392 | fn whileExpr( | ||
| 2393 | parent_gz: *GenZir, | ||
| 2394 | scope: *Scope, | ||
| 2395 | rl: ResultLoc, | ||
| 2396 | node: ast.Node.Index, | ||
| 2397 | while_full: ast.full.While, | ||
| 2398 | ) InnerError!zir.Inst.Ref { | ||
| 2399 | const mod = parent_gz.astgen.mod; | ||
| 2400 | if (while_full.label_token) |label_token| { | ||
| 2401 | try checkLabelRedefinition(mod, scope, label_token); | ||
| 2402 | } | ||
| 2403 | |||
| 2404 | const is_inline = parent_gz.force_comptime or while_full.inline_token != null; | ||
| 2405 | const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop; | ||
| 2406 | const loop_block = try parent_gz.addBlock(loop_tag, node); | ||
| 2407 | try parent_gz.instructions.append(mod.gpa, loop_block); | ||
| 2408 | |||
| 2409 | var loop_scope: GenZir = .{ | ||
| 2410 | .parent = scope, | ||
| 2411 | .astgen = parent_gz.astgen, | ||
| 2412 | .force_comptime = parent_gz.force_comptime, | ||
| 2413 | .instructions = .{}, | ||
| 2414 | }; | ||
| 2415 | loop_scope.setBreakResultLoc(rl); | ||
| 2416 | defer loop_scope.instructions.deinit(mod.gpa); | ||
| 2417 | |||
| 2418 | var continue_scope: GenZir = .{ | ||
| 2419 | .parent = &loop_scope.base, | ||
| 2420 | .astgen = parent_gz.astgen, | ||
| 2421 | .force_comptime = loop_scope.force_comptime, | ||
| 2422 | .instructions = .{}, | ||
| 2423 | }; | ||
| 2424 | defer continue_scope.instructions.deinit(mod.gpa); | ||
| 2425 | |||
| 2426 | const cond = c: { | ||
| 2427 | // TODO https://github.com/ziglang/zig/issues/7929 | ||
| 2428 | if (while_full.error_token) |error_token| { | ||
| 2429 | return mod.failTok(scope, error_token, "TODO implement while error union", .{}); | ||
| 2430 | } else if (while_full.payload_token) |payload_token| { | ||
| 2431 | return mod.failTok(scope, payload_token, "TODO implement while optional", .{}); | ||
| 2432 | } else { | ||
| 2433 | const bool_type_rl: ResultLoc = .{ .ty = .bool_type }; | ||
| 2434 | break :c try expr(&continue_scope, &continue_scope.base, bool_type_rl, while_full.ast.cond_expr); | ||
| 2435 | } | ||
| 2436 | }; | ||
| 2437 | |||
| 2438 | const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; | ||
| 2439 | const condbr = try continue_scope.addCondBr(condbr_tag, node); | ||
| 2440 | const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block; | ||
| 2441 | const cond_block = try loop_scope.addBlock(block_tag, node); | ||
| 2442 | try loop_scope.instructions.append(mod.gpa, cond_block); | ||
| 2443 | try continue_scope.setBlockBody(cond_block); | ||
| 2444 | |||
| 2445 | // TODO avoid emitting the continue expr when there | ||
| 2446 | // are no jumps to it. This happens when the last statement of a while body is noreturn | ||
| 2447 | // and there are no `continue` statements. | ||
| 2448 | if (while_full.ast.cont_expr != 0) { | ||
| 2449 | _ = try expr(&loop_scope, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr); | ||
| 2450 | } | ||
| 2451 | const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; | ||
| 2452 | _ = try loop_scope.addNode(repeat_tag, node); | ||
| 2453 | |||
| 2454 | try loop_scope.setBlockBody(loop_block); | ||
| 2455 | loop_scope.break_block = loop_block; | ||
| 2456 | loop_scope.continue_block = cond_block; | ||
| 2457 | if (while_full.label_token) |label_token| { | ||
| 2458 | loop_scope.label = @as(?GenZir.Label, GenZir.Label{ | ||
| 2459 | .token = label_token, | ||
| 2460 | .block_inst = loop_block, | ||
| 2461 | }); | ||
| 2462 | } | ||
| 2463 | |||
| 2464 | var then_scope: GenZir = .{ | ||
| 2465 | .parent = &continue_scope.base, | ||
| 2466 | .astgen = parent_gz.astgen, | ||
| 2467 | .force_comptime = continue_scope.force_comptime, | ||
| 2468 | .instructions = .{}, | ||
| 2469 | }; | ||
| 2470 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 2471 | |||
| 2472 | const then_sub_scope = &then_scope.base; | ||
| 2473 | |||
| 2474 | loop_scope.break_count += 1; | ||
| 2475 | const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr); | ||
| 2476 | |||
| 2477 | var else_scope: GenZir = .{ | ||
| 2478 | .parent = &continue_scope.base, | ||
| 2479 | .astgen = parent_gz.astgen, | ||
| 2480 | .force_comptime = continue_scope.force_comptime, | ||
| 2481 | .instructions = .{}, | ||
| 2482 | }; | ||
| 2483 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2484 | |||
| 2485 | const else_node = while_full.ast.else_expr; | ||
| 2486 | const else_info: struct { | ||
| 2487 | src: ast.Node.Index, | ||
| 2488 | result: zir.Inst.Ref, | ||
| 2489 | } = if (else_node != 0) blk: { | ||
| 2490 | loop_scope.break_count += 1; | ||
| 2491 | const sub_scope = &else_scope.base; | ||
| 2492 | break :blk .{ | ||
| 2493 | .src = else_node, | ||
| 2494 | .result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node), | ||
| 2495 | }; | ||
| 2496 | } else .{ | ||
| 2497 | .src = while_full.ast.then_expr, | ||
| 2498 | .result = .none, | ||
| 2499 | }; | ||
| 2500 | |||
| 2501 | if (loop_scope.label) |some| { | ||
| 2502 | if (!some.used) { | ||
| 2503 | return mod.failTok(scope, some.token, "unused while loop label", .{}); | ||
| 2504 | } | ||
| 2505 | } | ||
| 2506 | const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; | ||
| 2507 | return finishThenElseBlock( | ||
| 2508 | parent_gz, | ||
| 2509 | scope, | ||
| 2510 | rl, | ||
| 2511 | node, | ||
| 2512 | &loop_scope, | ||
| 2513 | &then_scope, | ||
| 2514 | &else_scope, | ||
| 2515 | condbr, | ||
| 2516 | cond, | ||
| 2517 | while_full.ast.then_expr, | ||
| 2518 | else_info.src, | ||
| 2519 | then_result, | ||
| 2520 | else_info.result, | ||
| 2521 | loop_block, | ||
| 2522 | cond_block, | ||
| 2523 | break_tag, | ||
| 2524 | ); | ||
| 2525 | } | ||
| 2526 | |||
| 2527 | fn forExpr( | ||
| 2528 | parent_gz: *GenZir, | ||
| 2529 | scope: *Scope, | ||
| 2530 | rl: ResultLoc, | ||
| 2531 | node: ast.Node.Index, | ||
| 2532 | for_full: ast.full.While, | ||
| 2533 | ) InnerError!zir.Inst.Ref { | ||
| 2534 | const mod = parent_gz.astgen.mod; | ||
| 2535 | if (for_full.label_token) |label_token| { | ||
| 2536 | try checkLabelRedefinition(mod, scope, label_token); | ||
| 2537 | } | ||
| 2538 | // Set up variables and constants. | ||
| 2539 | const is_inline = parent_gz.force_comptime or for_full.inline_token != null; | ||
| 2540 | const tree = parent_gz.tree(); | ||
| 2541 | const token_tags = tree.tokens.items(.tag); | ||
| 2542 | |||
| 2543 | const array_ptr = try expr(parent_gz, scope, .ref, for_full.ast.cond_expr); | ||
| 2544 | const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr); | ||
| 2545 | |||
| 2546 | const index_ptr = blk: { | ||
| 2547 | const index_ptr = try parent_gz.addUnNode(.alloc, .usize_type, node); | ||
| 2548 | // initialize to zero | ||
| 2549 | _ = try parent_gz.addBin(.store, index_ptr, .zero_usize); | ||
| 2550 | break :blk index_ptr; | ||
| 2551 | }; | ||
| 2552 | |||
| 2553 | const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop; | ||
| 2554 | const loop_block = try parent_gz.addBlock(loop_tag, node); | ||
| 2555 | try parent_gz.instructions.append(mod.gpa, loop_block); | ||
| 2556 | |||
| 2557 | var loop_scope: GenZir = .{ | ||
| 2558 | .parent = scope, | ||
| 2559 | .astgen = parent_gz.astgen, | ||
| 2560 | .force_comptime = parent_gz.force_comptime, | ||
| 2561 | .instructions = .{}, | ||
| 2562 | }; | ||
| 2563 | loop_scope.setBreakResultLoc(rl); | ||
| 2564 | defer loop_scope.instructions.deinit(mod.gpa); | ||
| 2565 | |||
| 2566 | var cond_scope: GenZir = .{ | ||
| 2567 | .parent = &loop_scope.base, | ||
| 2568 | .astgen = parent_gz.astgen, | ||
| 2569 | .force_comptime = loop_scope.force_comptime, | ||
| 2570 | .instructions = .{}, | ||
| 2571 | }; | ||
| 2572 | defer cond_scope.instructions.deinit(mod.gpa); | ||
| 2573 | |||
| 2574 | // check condition i < array_expr.len | ||
| 2575 | const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr); | ||
| 2576 | const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, zir.Inst.Bin{ | ||
| 2577 | .lhs = index, | ||
| 2578 | .rhs = len, | ||
| 2579 | }); | ||
| 2580 | |||
| 2581 | const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; | ||
| 2582 | const condbr = try cond_scope.addCondBr(condbr_tag, node); | ||
| 2583 | const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block; | ||
| 2584 | const cond_block = try loop_scope.addBlock(block_tag, node); | ||
| 2585 | try loop_scope.instructions.append(mod.gpa, cond_block); | ||
| 2586 | try cond_scope.setBlockBody(cond_block); | ||
| 2587 | |||
| 2588 | // Increment the index variable. | ||
| 2589 | const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr); | ||
| 2590 | const index_plus_one = try loop_scope.addPlNode(.add, node, zir.Inst.Bin{ | ||
| 2591 | .lhs = index_2, | ||
| 2592 | .rhs = .one_usize, | ||
| 2593 | }); | ||
| 2594 | _ = try loop_scope.addBin(.store, index_ptr, index_plus_one); | ||
| 2595 | const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; | ||
| 2596 | _ = try loop_scope.addNode(repeat_tag, node); | ||
| 2597 | |||
| 2598 | try loop_scope.setBlockBody(loop_block); | ||
| 2599 | loop_scope.break_block = loop_block; | ||
| 2600 | loop_scope.continue_block = cond_block; | ||
| 2601 | if (for_full.label_token) |label_token| { | ||
| 2602 | loop_scope.label = @as(?GenZir.Label, GenZir.Label{ | ||
| 2603 | .token = label_token, | ||
| 2604 | .block_inst = loop_block, | ||
| 2605 | }); | ||
| 2606 | } | ||
| 2607 | |||
| 2608 | var then_scope: GenZir = .{ | ||
| 2609 | .parent = &cond_scope.base, | ||
| 2610 | .astgen = parent_gz.astgen, | ||
| 2611 | .force_comptime = cond_scope.force_comptime, | ||
| 2612 | .instructions = .{}, | ||
| 2613 | }; | ||
| 2614 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 2615 | |||
| 2616 | var index_scope: Scope.LocalPtr = undefined; | ||
| 2617 | const then_sub_scope = blk: { | ||
| 2618 | const payload_token = for_full.payload_token.?; | ||
| 2619 | const ident = if (token_tags[payload_token] == .asterisk) | ||
| 2620 | payload_token + 1 | ||
| 2621 | else | ||
| 2622 | payload_token; | ||
| 2623 | const is_ptr = ident != payload_token; | ||
| 2624 | const value_name = tree.tokenSlice(ident); | ||
| 2625 | if (!mem.eql(u8, value_name, "_")) { | ||
| 2626 | return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{}); | ||
| 2627 | } else if (is_ptr) { | ||
| 2628 | return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{}); | ||
| 2629 | } | ||
| 2630 | |||
| 2631 | const index_token = if (token_tags[ident + 1] == .comma) | ||
| 2632 | ident + 2 | ||
| 2633 | else | ||
| 2634 | break :blk &then_scope.base; | ||
| 2635 | if (mem.eql(u8, tree.tokenSlice(index_token), "_")) { | ||
| 2636 | return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{}); | ||
| 2637 | } | ||
| 2638 | const index_name = try mod.identifierTokenString(&then_scope.base, index_token); | ||
| 2639 | index_scope = .{ | ||
| 2640 | .parent = &then_scope.base, | ||
| 2641 | .gen_zir = &then_scope, | ||
| 2642 | .name = index_name, | ||
| 2643 | .ptr = index_ptr, | ||
| 2644 | .src = parent_gz.tokSrcLoc(index_token), | ||
| 2645 | }; | ||
| 2646 | break :blk &index_scope.base; | ||
| 2647 | }; | ||
| 2648 | |||
| 2649 | loop_scope.break_count += 1; | ||
| 2650 | const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr); | ||
| 2651 | |||
| 2652 | var else_scope: GenZir = .{ | ||
| 2653 | .parent = &cond_scope.base, | ||
| 2654 | .astgen = parent_gz.astgen, | ||
| 2655 | .force_comptime = cond_scope.force_comptime, | ||
| 2656 | .instructions = .{}, | ||
| 2657 | }; | ||
| 2658 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2659 | |||
| 2660 | const else_node = for_full.ast.else_expr; | ||
| 2661 | const else_info: struct { | ||
| 2662 | src: ast.Node.Index, | ||
| 2663 | result: zir.Inst.Ref, | ||
| 2664 | } = if (else_node != 0) blk: { | ||
| 2665 | loop_scope.break_count += 1; | ||
| 2666 | const sub_scope = &else_scope.base; | ||
| 2667 | break :blk .{ | ||
| 2668 | .src = else_node, | ||
| 2669 | .result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node), | ||
| 2670 | }; | ||
| 2671 | } else .{ | ||
| 2672 | .src = for_full.ast.then_expr, | ||
| 2673 | .result = .none, | ||
| 2674 | }; | ||
| 2675 | |||
| 2676 | if (loop_scope.label) |some| { | ||
| 2677 | if (!some.used) { | ||
| 2678 | return mod.failTok(scope, some.token, "unused for loop label", .{}); | ||
| 2679 | } | ||
| 2680 | } | ||
| 2681 | const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; | ||
| 2682 | return finishThenElseBlock( | ||
| 2683 | parent_gz, | ||
| 2684 | scope, | ||
| 2685 | rl, | ||
| 2686 | node, | ||
| 2687 | &loop_scope, | ||
| 2688 | &then_scope, | ||
| 2689 | &else_scope, | ||
| 2690 | condbr, | ||
| 2691 | cond, | ||
| 2692 | for_full.ast.then_expr, | ||
| 2693 | else_info.src, | ||
| 2694 | then_result, | ||
| 2695 | else_info.result, | ||
| 2696 | loop_block, | ||
| 2697 | cond_block, | ||
| 2698 | break_tag, | ||
| 2699 | ); | ||
| 2700 | } | ||
| 2701 | |||
| 2702 | fn getRangeNode( | ||
| 2703 | node_tags: []const ast.Node.Tag, | ||
| 2704 | node_datas: []const ast.Node.Data, | ||
| 2705 | node: ast.Node.Index, | ||
| 2706 | ) ?ast.Node.Index { | ||
| 2707 | switch (node_tags[node]) { | ||
| 2708 | .switch_range => return node, | ||
| 2709 | .grouped_expression => unreachable, | ||
| 2710 | else => return null, | ||
| 2711 | } | ||
| 2712 | } | ||
| 2713 | |||
| 2714 | pub const SwitchProngSrc = union(enum) { | ||
| 2715 | scalar: u32, | ||
| 2716 | multi: Multi, | ||
| 2717 | range: Multi, | ||
| 2718 | |||
| 2719 | pub const Multi = struct { | ||
| 2720 | prong: u32, | ||
| 2721 | item: u32, | ||
| 2722 | }; | ||
| 2723 | |||
| 2724 | pub const RangeExpand = enum { none, first, last }; | ||
| 2725 | |||
| 2726 | /// This function is intended to be called only when it is certain that we need | ||
| 2727 | /// the LazySrcLoc in order to emit a compile error. | ||
| 2728 | pub fn resolve( | ||
| 2729 | prong_src: SwitchProngSrc, | ||
| 2730 | decl: *Decl, | ||
| 2731 | switch_node_offset: i32, | ||
| 2732 | range_expand: RangeExpand, | ||
| 2733 | ) LazySrcLoc { | ||
| 2734 | @setCold(true); | ||
| 2735 | const switch_node = decl.relativeToNodeIndex(switch_node_offset); | ||
| 2736 | const tree = decl.container.file_scope.base.tree(); | ||
| 2737 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2738 | const node_datas = tree.nodes.items(.data); | ||
| 2739 | const node_tags = tree.nodes.items(.tag); | ||
| 2740 | const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); | ||
| 2741 | const case_nodes = tree.extra_data[extra.start..extra.end]; | ||
| 2742 | |||
| 2743 | var multi_i: u32 = 0; | ||
| 2744 | var scalar_i: u32 = 0; | ||
| 2745 | for (case_nodes) |case_node| { | ||
| 2746 | const case = switch (node_tags[case_node]) { | ||
| 2747 | .switch_case_one => tree.switchCaseOne(case_node), | ||
| 2748 | .switch_case => tree.switchCase(case_node), | ||
| 2749 | else => unreachable, | ||
| 2750 | }; | ||
| 2751 | if (case.ast.values.len == 0) | ||
| 2752 | continue; | ||
| 2753 | if (case.ast.values.len == 1 and | ||
| 2754 | node_tags[case.ast.values[0]] == .identifier and | ||
| 2755 | mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) | ||
| 2756 | { | ||
| 2757 | continue; | ||
| 2758 | } | ||
| 2759 | const is_multi = case.ast.values.len != 1 or | ||
| 2760 | getRangeNode(node_tags, node_datas, case.ast.values[0]) != null; | ||
| 2761 | |||
| 2762 | switch (prong_src) { | ||
| 2763 | .scalar => |i| if (!is_multi and i == scalar_i) return LazySrcLoc{ | ||
| 2764 | .node_offset = decl.nodeIndexToRelative(case.ast.values[0]), | ||
| 2765 | }, | ||
| 2766 | .multi => |s| if (is_multi and s.prong == multi_i) { | ||
| 2767 | var item_i: u32 = 0; | ||
| 2768 | for (case.ast.values) |item_node| { | ||
| 2769 | if (getRangeNode(node_tags, node_datas, item_node) != null) | ||
| 2770 | continue; | ||
| 2771 | |||
| 2772 | if (item_i == s.item) return LazySrcLoc{ | ||
| 2773 | .node_offset = decl.nodeIndexToRelative(item_node), | ||
| 2774 | }; | ||
| 2775 | item_i += 1; | ||
| 2776 | } else unreachable; | ||
| 2777 | }, | ||
| 2778 | .range => |s| if (is_multi and s.prong == multi_i) { | ||
| 2779 | var range_i: u32 = 0; | ||
| 2780 | for (case.ast.values) |item_node| { | ||
| 2781 | const range = getRangeNode(node_tags, node_datas, item_node) orelse continue; | ||
| 2782 | |||
| 2783 | if (range_i == s.item) switch (range_expand) { | ||
| 2784 | .none => return LazySrcLoc{ | ||
| 2785 | .node_offset = decl.nodeIndexToRelative(item_node), | ||
| 2786 | }, | ||
| 2787 | .first => return LazySrcLoc{ | ||
| 2788 | .node_offset = decl.nodeIndexToRelative(node_datas[range].lhs), | ||
| 2789 | }, | ||
| 2790 | .last => return LazySrcLoc{ | ||
| 2791 | .node_offset = decl.nodeIndexToRelative(node_datas[range].rhs), | ||
| 2792 | }, | ||
| 2793 | }; | ||
| 2794 | range_i += 1; | ||
| 2795 | } else unreachable; | ||
| 2796 | }, | ||
| 2797 | } | ||
| 2798 | if (is_multi) { | ||
| 2799 | multi_i += 1; | ||
| 2800 | } else { | ||
| 2801 | scalar_i += 1; | ||
| 2802 | } | ||
| 2803 | } else unreachable; | ||
| 2804 | } | ||
| 2805 | }; | ||
| 2806 | |||
| 2807 | fn switchExpr( | ||
| 2808 | parent_gz: *GenZir, | ||
| 2809 | scope: *Scope, | ||
| 2810 | rl: ResultLoc, | ||
| 2811 | switch_node: ast.Node.Index, | ||
| 2812 | ) InnerError!zir.Inst.Ref { | ||
| 2813 | const astgen = parent_gz.astgen; | ||
| 2814 | const mod = astgen.mod; | ||
| 2815 | const gpa = mod.gpa; | ||
| 2816 | const tree = parent_gz.tree(); | ||
| 2817 | const node_datas = tree.nodes.items(.data); | ||
| 2818 | const node_tags = tree.nodes.items(.tag); | ||
| 2819 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2820 | const token_tags = tree.tokens.items(.tag); | ||
| 2821 | const operand_node = node_datas[switch_node].lhs; | ||
| 2822 | const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); | ||
| 2823 | const case_nodes = tree.extra_data[extra.start..extra.end]; | ||
| 2824 | |||
| 2825 | // We perform two passes over the AST. This first pass is to collect information | ||
| 2826 | // for the following variables, make note of the special prong AST node index, | ||
| 2827 | // and bail out with a compile error if there are multiple special prongs present. | ||
| 2828 | var any_payload_is_ref = false; | ||
| 2829 | var scalar_cases_len: u32 = 0; | ||
| 2830 | var multi_cases_len: u32 = 0; | ||
| 2831 | var special_prong: zir.SpecialProng = .none; | ||
| 2832 | var special_node: ast.Node.Index = 0; | ||
| 2833 | var else_src: ?LazySrcLoc = null; | ||
| 2834 | var underscore_src: ?LazySrcLoc = null; | ||
| 2835 | for (case_nodes) |case_node| { | ||
| 2836 | const case = switch (node_tags[case_node]) { | ||
| 2837 | .switch_case_one => tree.switchCaseOne(case_node), | ||
| 2838 | .switch_case => tree.switchCase(case_node), | ||
| 2839 | else => unreachable, | ||
| 2840 | }; | ||
| 2841 | if (case.payload_token) |payload_token| { | ||
| 2842 | if (token_tags[payload_token] == .asterisk) { | ||
| 2843 | any_payload_is_ref = true; | ||
| 2844 | } | ||
| 2845 | } | ||
| 2846 | // Check for else/`_` prong. | ||
| 2847 | if (case.ast.values.len == 0) { | ||
| 2848 | const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1); | ||
| 2849 | if (else_src) |src| { | ||
| 2850 | const msg = msg: { | ||
| 2851 | const msg = try mod.errMsg( | ||
| 2852 | scope, | ||
| 2853 | case_src, | ||
| 2854 | "multiple else prongs in switch expression", | ||
| 2855 | .{}, | ||
| 2856 | ); | ||
| 2857 | errdefer msg.destroy(gpa); | ||
| 2858 | try mod.errNote(scope, src, msg, "previous else prong is here", .{}); | ||
| 2859 | break :msg msg; | ||
| 2860 | }; | ||
| 2861 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 2862 | } else if (underscore_src) |some_underscore| { | ||
| 2863 | const msg = msg: { | ||
| 2864 | const msg = try mod.errMsg( | ||
| 2865 | scope, | ||
| 2866 | parent_gz.nodeSrcLoc(switch_node), | ||
| 2867 | "else and '_' prong in switch expression", | ||
| 2868 | .{}, | ||
| 2869 | ); | ||
| 2870 | errdefer msg.destroy(gpa); | ||
| 2871 | try mod.errNote(scope, case_src, msg, "else prong is here", .{}); | ||
| 2872 | try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{}); | ||
| 2873 | break :msg msg; | ||
| 2874 | }; | ||
| 2875 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 2876 | } | ||
| 2877 | special_node = case_node; | ||
| 2878 | special_prong = .@"else"; | ||
| 2879 | else_src = case_src; | ||
| 2880 | continue; | ||
| 2881 | } else if (case.ast.values.len == 1 and | ||
| 2882 | node_tags[case.ast.values[0]] == .identifier and | ||
| 2883 | mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) | ||
| 2884 | { | ||
| 2885 | const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1); | ||
| 2886 | if (underscore_src) |src| { | ||
| 2887 | const msg = msg: { | ||
| 2888 | const msg = try mod.errMsg( | ||
| 2889 | scope, | ||
| 2890 | case_src, | ||
| 2891 | "multiple '_' prongs in switch expression", | ||
| 2892 | .{}, | ||
| 2893 | ); | ||
| 2894 | errdefer msg.destroy(gpa); | ||
| 2895 | try mod.errNote(scope, src, msg, "previous '_' prong is here", .{}); | ||
| 2896 | break :msg msg; | ||
| 2897 | }; | ||
| 2898 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 2899 | } else if (else_src) |some_else| { | ||
| 2900 | const msg = msg: { | ||
| 2901 | const msg = try mod.errMsg( | ||
| 2902 | scope, | ||
| 2903 | parent_gz.nodeSrcLoc(switch_node), | ||
| 2904 | "else and '_' prong in switch expression", | ||
| 2905 | .{}, | ||
| 2906 | ); | ||
| 2907 | errdefer msg.destroy(gpa); | ||
| 2908 | try mod.errNote(scope, some_else, msg, "else prong is here", .{}); | ||
| 2909 | try mod.errNote(scope, case_src, msg, "'_' prong is here", .{}); | ||
| 2910 | break :msg msg; | ||
| 2911 | }; | ||
| 2912 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 2913 | } | ||
| 2914 | special_node = case_node; | ||
| 2915 | special_prong = .under; | ||
| 2916 | underscore_src = case_src; | ||
| 2917 | continue; | ||
| 2918 | } | ||
| 2919 | |||
| 2920 | if (case.ast.values.len == 1 and | ||
| 2921 | getRangeNode(node_tags, node_datas, case.ast.values[0]) == null) | ||
| 2922 | { | ||
| 2923 | scalar_cases_len += 1; | ||
| 2924 | } else { | ||
| 2925 | multi_cases_len += 1; | ||
| 2926 | } | ||
| 2927 | } | ||
| 2928 | |||
| 2929 | const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none; | ||
| 2930 | const operand = try expr(parent_gz, scope, operand_rl, operand_node); | ||
| 2931 | // We need the type of the operand to use as the result location for all the prong items. | ||
| 2932 | const typeof_tag: zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof; | ||
| 2933 | const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node); | ||
| 2934 | const item_rl: ResultLoc = .{ .ty = operand_ty_inst }; | ||
| 2935 | |||
| 2936 | // Contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti. | ||
| 2937 | // This is the header as well as the optional else prong body, as well as all the | ||
| 2938 | // scalar cases. | ||
| 2939 | // At the end we will memcpy this into place. | ||
| 2940 | var scalar_cases_payload = ArrayListUnmanaged(u32){}; | ||
| 2941 | defer scalar_cases_payload.deinit(gpa); | ||
| 2942 | // Same deal, but this is only the `extra` data for the multi cases. | ||
| 2943 | var multi_cases_payload = ArrayListUnmanaged(u32){}; | ||
| 2944 | defer multi_cases_payload.deinit(gpa); | ||
| 2945 | |||
| 2946 | var block_scope: GenZir = .{ | ||
| 2947 | .parent = scope, | ||
| 2948 | .astgen = astgen, | ||
| 2949 | .force_comptime = parent_gz.force_comptime, | ||
| 2950 | .instructions = .{}, | ||
| 2951 | }; | ||
| 2952 | block_scope.setBreakResultLoc(rl); | ||
| 2953 | defer block_scope.instructions.deinit(gpa); | ||
| 2954 | |||
| 2955 | // This gets added to the parent block later, after the item expressions. | ||
| 2956 | const switch_block = try parent_gz.addBlock(undefined, switch_node); | ||
| 2957 | |||
| 2958 | // We re-use this same scope for all cases, including the special prong, if any. | ||
| 2959 | var case_scope: GenZir = .{ | ||
| 2960 | .parent = &block_scope.base, | ||
| 2961 | .astgen = astgen, | ||
| 2962 | .force_comptime = parent_gz.force_comptime, | ||
| 2963 | .instructions = .{}, | ||
| 2964 | }; | ||
| 2965 | defer case_scope.instructions.deinit(gpa); | ||
| 2966 | |||
| 2967 | // Do the else/`_` first because it goes first in the payload. | ||
| 2968 | var capture_val_scope: Scope.LocalVal = undefined; | ||
| 2969 | if (special_node != 0) { | ||
| 2970 | const case = switch (node_tags[special_node]) { | ||
| 2971 | .switch_case_one => tree.switchCaseOne(special_node), | ||
| 2972 | .switch_case => tree.switchCase(special_node), | ||
| 2973 | else => unreachable, | ||
| 2974 | }; | ||
| 2975 | const sub_scope = blk: { | ||
| 2976 | const payload_token = case.payload_token orelse break :blk &case_scope.base; | ||
| 2977 | const ident = if (token_tags[payload_token] == .asterisk) | ||
| 2978 | payload_token + 1 | ||
| 2979 | else | ||
| 2980 | payload_token; | ||
| 2981 | const is_ptr = ident != payload_token; | ||
| 2982 | if (mem.eql(u8, tree.tokenSlice(ident), "_")) { | ||
| 2983 | if (is_ptr) { | ||
| 2984 | return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{}); | ||
| 2985 | } | ||
| 2986 | break :blk &case_scope.base; | ||
| 2987 | } | ||
| 2988 | const capture_tag: zir.Inst.Tag = if (is_ptr) | ||
| 2989 | .switch_capture_else_ref | ||
| 2990 | else | ||
| 2991 | .switch_capture_else; | ||
| 2992 | const capture = try case_scope.add(.{ | ||
| 2993 | .tag = capture_tag, | ||
| 2994 | .data = .{ .switch_capture = .{ | ||
| 2995 | .switch_inst = switch_block, | ||
| 2996 | .prong_index = undefined, | ||
| 2997 | } }, | ||
| 2998 | }); | ||
| 2999 | const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token); | ||
| 3000 | capture_val_scope = .{ | ||
| 3001 | .parent = &case_scope.base, | ||
| 3002 | .gen_zir = &case_scope, | ||
| 3003 | .name = capture_name, | ||
| 3004 | .inst = capture, | ||
| 3005 | .src = parent_gz.tokSrcLoc(payload_token), | ||
| 3006 | }; | ||
| 3007 | break :blk &capture_val_scope.base; | ||
| 3008 | }; | ||
| 3009 | const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr); | ||
| 3010 | if (!astgen.refIsNoReturn(case_result)) { | ||
| 3011 | block_scope.break_count += 1; | ||
| 3012 | _ = try case_scope.addBreak(.@"break", switch_block, case_result); | ||
| 3013 | } | ||
| 3014 | // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`. | ||
| 3015 | try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + | ||
| 3016 | 3 + // operand, scalar_cases_len, else body len | ||
| 3017 | @boolToInt(multi_cases_len != 0) + | ||
| 3018 | case_scope.instructions.items.len); | ||
| 3019 | scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand)); | ||
| 3020 | scalar_cases_payload.appendAssumeCapacity(scalar_cases_len); | ||
| 3021 | if (multi_cases_len != 0) { | ||
| 3022 | scalar_cases_payload.appendAssumeCapacity(multi_cases_len); | ||
| 3023 | } | ||
| 3024 | scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len)); | ||
| 3025 | scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items); | ||
| 3026 | } else { | ||
| 3027 | // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`. | ||
| 3028 | try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + | ||
| 3029 | 2 + // operand, scalar_cases_len | ||
| 3030 | @boolToInt(multi_cases_len != 0)); | ||
| 3031 | scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand)); | ||
| 3032 | scalar_cases_payload.appendAssumeCapacity(scalar_cases_len); | ||
| 3033 | if (multi_cases_len != 0) { | ||
| 3034 | scalar_cases_payload.appendAssumeCapacity(multi_cases_len); | ||
| 3035 | } | ||
| 3036 | } | ||
| 3037 | |||
| 3038 | // In this pass we generate all the item and prong expressions except the special case. | ||
| 3039 | var multi_case_index: u32 = 0; | ||
| 3040 | var scalar_case_index: u32 = 0; | ||
| 3041 | for (case_nodes) |case_node| { | ||
| 3042 | if (case_node == special_node) | ||
| 3043 | continue; | ||
| 3044 | const case = switch (node_tags[case_node]) { | ||
| 3045 | .switch_case_one => tree.switchCaseOne(case_node), | ||
| 3046 | .switch_case => tree.switchCase(case_node), | ||
| 3047 | else => unreachable, | ||
| 3048 | }; | ||
| 3049 | |||
| 3050 | // Reset the scope. | ||
| 3051 | case_scope.instructions.shrinkRetainingCapacity(0); | ||
| 3052 | |||
| 3053 | const is_multi_case = case.ast.values.len != 1 or | ||
| 3054 | getRangeNode(node_tags, node_datas, case.ast.values[0]) != null; | ||
| 3055 | |||
| 3056 | const sub_scope = blk: { | ||
| 3057 | const payload_token = case.payload_token orelse break :blk &case_scope.base; | ||
| 3058 | const ident = if (token_tags[payload_token] == .asterisk) | ||
| 3059 | payload_token + 1 | ||
| 3060 | else | ||
| 3061 | payload_token; | ||
| 3062 | const is_ptr = ident != payload_token; | ||
| 3063 | if (mem.eql(u8, tree.tokenSlice(ident), "_")) { | ||
| 3064 | if (is_ptr) { | ||
| 3065 | return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{}); | ||
| 3066 | } | ||
| 3067 | break :blk &case_scope.base; | ||
| 3068 | } | ||
| 3069 | const is_multi_case_bits: u2 = @boolToInt(is_multi_case); | ||
| 3070 | const is_ptr_bits: u2 = @boolToInt(is_ptr); | ||
| 3071 | const capture_tag: zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) { | ||
| 3072 | 0b00 => .switch_capture, | ||
| 3073 | 0b01 => .switch_capture_ref, | ||
| 3074 | 0b10 => .switch_capture_multi, | ||
| 3075 | 0b11 => .switch_capture_multi_ref, | ||
| 3076 | }; | ||
| 3077 | const capture_index = if (is_multi_case) ci: { | ||
| 3078 | multi_case_index += 1; | ||
| 3079 | break :ci multi_case_index - 1; | ||
| 3080 | } else ci: { | ||
| 3081 | scalar_case_index += 1; | ||
| 3082 | break :ci scalar_case_index - 1; | ||
| 3083 | }; | ||
| 3084 | const capture = try case_scope.add(.{ | ||
| 3085 | .tag = capture_tag, | ||
| 3086 | .data = .{ .switch_capture = .{ | ||
| 3087 | .switch_inst = switch_block, | ||
| 3088 | .prong_index = capture_index, | ||
| 3089 | } }, | ||
| 3090 | }); | ||
| 3091 | const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token); | ||
| 3092 | capture_val_scope = .{ | ||
| 3093 | .parent = &case_scope.base, | ||
| 3094 | .gen_zir = &case_scope, | ||
| 3095 | .name = capture_name, | ||
| 3096 | .inst = capture, | ||
| 3097 | .src = parent_gz.tokSrcLoc(payload_token), | ||
| 3098 | }; | ||
| 3099 | break :blk &capture_val_scope.base; | ||
| 3100 | }; | ||
| 3101 | |||
| 3102 | if (is_multi_case) { | ||
| 3103 | // items_len, ranges_len, body_len | ||
| 3104 | const header_index = multi_cases_payload.items.len; | ||
| 3105 | try multi_cases_payload.resize(gpa, multi_cases_payload.items.len + 3); | ||
| 3106 | |||
| 3107 | // items | ||
| 3108 | var items_len: u32 = 0; | ||
| 3109 | for (case.ast.values) |item_node| { | ||
| 3110 | if (getRangeNode(node_tags, node_datas, item_node) != null) continue; | ||
| 3111 | items_len += 1; | ||
| 3112 | |||
| 3113 | const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node); | ||
| 3114 | try multi_cases_payload.append(gpa, @enumToInt(item_inst)); | ||
| 3115 | } | ||
| 3116 | |||
| 3117 | // ranges | ||
| 3118 | var ranges_len: u32 = 0; | ||
| 3119 | for (case.ast.values) |item_node| { | ||
| 3120 | const range = getRangeNode(node_tags, node_datas, item_node) orelse continue; | ||
| 3121 | ranges_len += 1; | ||
| 3122 | |||
| 3123 | const first = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].lhs); | ||
| 3124 | const last = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].rhs); | ||
| 3125 | try multi_cases_payload.appendSlice(gpa, &[_]u32{ | ||
| 3126 | @enumToInt(first), @enumToInt(last), | ||
| 3127 | }); | ||
| 3128 | } | ||
| 3129 | |||
| 3130 | const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr); | ||
| 3131 | if (!astgen.refIsNoReturn(case_result)) { | ||
| 3132 | block_scope.break_count += 1; | ||
| 3133 | _ = try case_scope.addBreak(.@"break", switch_block, case_result); | ||
| 3134 | } | ||
| 3135 | |||
| 3136 | multi_cases_payload.items[header_index + 0] = items_len; | ||
| 3137 | multi_cases_payload.items[header_index + 1] = ranges_len; | ||
| 3138 | multi_cases_payload.items[header_index + 2] = @intCast(u32, case_scope.instructions.items.len); | ||
| 3139 | try multi_cases_payload.appendSlice(gpa, case_scope.instructions.items); | ||
| 3140 | } else { | ||
| 3141 | const item_node = case.ast.values[0]; | ||
| 3142 | const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node); | ||
| 3143 | const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr); | ||
| 3144 | if (!astgen.refIsNoReturn(case_result)) { | ||
| 3145 | block_scope.break_count += 1; | ||
| 3146 | _ = try case_scope.addBreak(.@"break", switch_block, case_result); | ||
| 3147 | } | ||
| 3148 | try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + | ||
| 3149 | 2 + case_scope.instructions.items.len); | ||
| 3150 | scalar_cases_payload.appendAssumeCapacity(@enumToInt(item_inst)); | ||
| 3151 | scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len)); | ||
| 3152 | scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items); | ||
| 3153 | } | ||
| 3154 | } | ||
| 3155 | // Now that the item expressions are generated we can add this. | ||
| 3156 | try parent_gz.instructions.append(gpa, switch_block); | ||
| 3157 | |||
| 3158 | const ref_bit: u4 = @boolToInt(any_payload_is_ref); | ||
| 3159 | const multi_bit: u4 = @boolToInt(multi_cases_len != 0); | ||
| 3160 | const special_prong_bits: u4 = @enumToInt(special_prong); | ||
| 3161 | comptime { | ||
| 3162 | assert(@enumToInt(zir.SpecialProng.none) == 0b00); | ||
| 3163 | assert(@enumToInt(zir.SpecialProng.@"else") == 0b01); | ||
| 3164 | assert(@enumToInt(zir.SpecialProng.under) == 0b10); | ||
| 3165 | } | ||
| 3166 | const zir_tags = astgen.instructions.items(.tag); | ||
| 3167 | zir_tags[switch_block] = switch ((ref_bit << 3) | (special_prong_bits << 1) | multi_bit) { | ||
| 3168 | 0b0_00_0 => .switch_block, | ||
| 3169 | 0b0_00_1 => .switch_block_multi, | ||
| 3170 | 0b0_01_0 => .switch_block_else, | ||
| 3171 | 0b0_01_1 => .switch_block_else_multi, | ||
| 3172 | 0b0_10_0 => .switch_block_under, | ||
| 3173 | 0b0_10_1 => .switch_block_under_multi, | ||
| 3174 | 0b1_00_0 => .switch_block_ref, | ||
| 3175 | 0b1_00_1 => .switch_block_ref_multi, | ||
| 3176 | 0b1_01_0 => .switch_block_ref_else, | ||
| 3177 | 0b1_01_1 => .switch_block_ref_else_multi, | ||
| 3178 | 0b1_10_0 => .switch_block_ref_under, | ||
| 3179 | 0b1_10_1 => .switch_block_ref_under_multi, | ||
| 3180 | else => unreachable, | ||
| 3181 | }; | ||
| 3182 | const payload_index = astgen.extra.items.len; | ||
| 3183 | const zir_datas = astgen.instructions.items(.data); | ||
| 3184 | zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index); | ||
| 3185 | try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + | ||
| 3186 | scalar_cases_payload.items.len + multi_cases_payload.items.len); | ||
| 3187 | const strat = rl.strategy(&block_scope); | ||
| 3188 | switch (strat.tag) { | ||
| 3189 | .break_operand => { | ||
| 3190 | // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus | ||
| 3191 | // this is always true. | ||
| 3192 | assert(strat.elide_store_to_block_ptr_instructions); | ||
| 3193 | |||
| 3194 | // There will necessarily be a store_to_block_ptr for | ||
| 3195 | // all prongs, except for prongs that ended with a noreturn instruction. | ||
| 3196 | // Elide all the `store_to_block_ptr` instructions. | ||
| 3197 | |||
| 3198 | // The break instructions need to have their operands coerced if the | ||
| 3199 | // switch's result location is a `ty`. In this case we overwrite the | ||
| 3200 | // `store_to_block_ptr` instruction with an `as` instruction and repurpose | ||
| 3201 | // it as the break operand. | ||
| 3202 | |||
| 3203 | var extra_index: usize = 0; | ||
| 3204 | extra_index += 2; | ||
| 3205 | extra_index += @boolToInt(multi_cases_len != 0); | ||
| 3206 | if (special_prong != .none) special_prong: { | ||
| 3207 | const body_len_index = extra_index; | ||
| 3208 | const body_len = scalar_cases_payload.items[extra_index]; | ||
| 3209 | extra_index += 1; | ||
| 3210 | if (body_len < 2) { | ||
| 3211 | extra_index += body_len; | ||
| 3212 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]); | ||
| 3213 | break :special_prong; | ||
| 3214 | } | ||
| 3215 | extra_index += body_len - 2; | ||
| 3216 | const store_inst = scalar_cases_payload.items[extra_index]; | ||
| 3217 | if (zir_tags[store_inst] != .store_to_block_ptr) { | ||
| 3218 | extra_index += 2; | ||
| 3219 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]); | ||
| 3220 | break :special_prong; | ||
| 3221 | } | ||
| 3222 | assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr); | ||
| 3223 | if (block_scope.rl_ty_inst != .none) { | ||
| 3224 | extra_index += 1; | ||
| 3225 | const break_inst = scalar_cases_payload.items[extra_index]; | ||
| 3226 | extra_index += 1; | ||
| 3227 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]); | ||
| 3228 | zir_tags[store_inst] = .as; | ||
| 3229 | zir_datas[store_inst].bin = .{ | ||
| 3230 | .lhs = block_scope.rl_ty_inst, | ||
| 3231 | .rhs = zir_datas[break_inst].@"break".operand, | ||
| 3232 | }; | ||
| 3233 | zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst); | ||
| 3234 | } else { | ||
| 3235 | scalar_cases_payload.items[body_len_index] -= 1; | ||
| 3236 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]); | ||
| 3237 | extra_index += 1; | ||
| 3238 | astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]); | ||
| 3239 | extra_index += 1; | ||
| 3240 | } | ||
| 3241 | } else { | ||
| 3242 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]); | ||
| 3243 | } | ||
| 3244 | var scalar_i: u32 = 0; | ||
| 3245 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | ||
| 3246 | const start_index = extra_index; | ||
| 3247 | extra_index += 1; | ||
| 3248 | const body_len_index = extra_index; | ||
| 3249 | const body_len = scalar_cases_payload.items[extra_index]; | ||
| 3250 | extra_index += 1; | ||
| 3251 | if (body_len < 2) { | ||
| 3252 | extra_index += body_len; | ||
| 3253 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]); | ||
| 3254 | continue; | ||
| 3255 | } | ||
| 3256 | extra_index += body_len - 2; | ||
| 3257 | const store_inst = scalar_cases_payload.items[extra_index]; | ||
| 3258 | if (zir_tags[store_inst] != .store_to_block_ptr) { | ||
| 3259 | extra_index += 2; | ||
| 3260 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]); | ||
| 3261 | continue; | ||
| 3262 | } | ||
| 3263 | if (block_scope.rl_ty_inst != .none) { | ||
| 3264 | extra_index += 1; | ||
| 3265 | const break_inst = scalar_cases_payload.items[extra_index]; | ||
| 3266 | extra_index += 1; | ||
| 3267 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]); | ||
| 3268 | zir_tags[store_inst] = .as; | ||
| 3269 | zir_datas[store_inst].bin = .{ | ||
| 3270 | .lhs = block_scope.rl_ty_inst, | ||
| 3271 | .rhs = zir_datas[break_inst].@"break".operand, | ||
| 3272 | }; | ||
| 3273 | zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst); | ||
| 3274 | } else { | ||
| 3275 | assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr); | ||
| 3276 | scalar_cases_payload.items[body_len_index] -= 1; | ||
| 3277 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]); | ||
| 3278 | extra_index += 1; | ||
| 3279 | astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]); | ||
| 3280 | extra_index += 1; | ||
| 3281 | } | ||
| 3282 | } | ||
| 3283 | extra_index = 0; | ||
| 3284 | var multi_i: u32 = 0; | ||
| 3285 | while (multi_i < multi_cases_len) : (multi_i += 1) { | ||
| 3286 | const start_index = extra_index; | ||
| 3287 | const items_len = multi_cases_payload.items[extra_index]; | ||
| 3288 | extra_index += 1; | ||
| 3289 | const ranges_len = multi_cases_payload.items[extra_index]; | ||
| 3290 | extra_index += 1; | ||
| 3291 | const body_len_index = extra_index; | ||
| 3292 | const body_len = multi_cases_payload.items[extra_index]; | ||
| 3293 | extra_index += 1; | ||
| 3294 | extra_index += items_len; | ||
| 3295 | extra_index += 2 * ranges_len; | ||
| 3296 | if (body_len < 2) { | ||
| 3297 | extra_index += body_len; | ||
| 3298 | astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]); | ||
| 3299 | continue; | ||
| 3300 | } | ||
| 3301 | extra_index += body_len - 2; | ||
| 3302 | const store_inst = multi_cases_payload.items[extra_index]; | ||
| 3303 | if (zir_tags[store_inst] != .store_to_block_ptr) { | ||
| 3304 | extra_index += 2; | ||
| 3305 | astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]); | ||
| 3306 | continue; | ||
| 3307 | } | ||
| 3308 | if (block_scope.rl_ty_inst != .none) { | ||
| 3309 | extra_index += 1; | ||
| 3310 | const break_inst = multi_cases_payload.items[extra_index]; | ||
| 3311 | extra_index += 1; | ||
| 3312 | astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]); | ||
| 3313 | zir_tags[store_inst] = .as; | ||
| 3314 | zir_datas[store_inst].bin = .{ | ||
| 3315 | .lhs = block_scope.rl_ty_inst, | ||
| 3316 | .rhs = zir_datas[break_inst].@"break".operand, | ||
| 3317 | }; | ||
| 3318 | zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst); | ||
| 3319 | } else { | ||
| 3320 | assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr); | ||
| 3321 | multi_cases_payload.items[body_len_index] -= 1; | ||
| 3322 | astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]); | ||
| 3323 | extra_index += 1; | ||
| 3324 | astgen.extra.appendAssumeCapacity(multi_cases_payload.items[extra_index]); | ||
| 3325 | extra_index += 1; | ||
| 3326 | } | ||
| 3327 | } | ||
| 3328 | |||
| 3329 | const block_ref = astgen.indexToRef(switch_block); | ||
| 3330 | switch (rl) { | ||
| 3331 | .ref => return block_ref, | ||
| 3332 | else => return rvalue(parent_gz, scope, rl, block_ref, switch_node), | ||
| 3333 | } | ||
| 3334 | }, | ||
| 3335 | .break_void => { | ||
| 3336 | assert(!strat.elide_store_to_block_ptr_instructions); | ||
| 3337 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items); | ||
| 3338 | astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items); | ||
| 3339 | // Modify all the terminating instruction tags to become `break` variants. | ||
| 3340 | var extra_index: usize = payload_index; | ||
| 3341 | extra_index += 2; | ||
| 3342 | extra_index += @boolToInt(multi_cases_len != 0); | ||
| 3343 | if (special_prong != .none) { | ||
| 3344 | const body_len = astgen.extra.items[extra_index]; | ||
| 3345 | extra_index += 1; | ||
| 3346 | const body = astgen.extra.items[extra_index..][0..body_len]; | ||
| 3347 | extra_index += body_len; | ||
| 3348 | const last = body[body.len - 1]; | ||
| 3349 | if (zir_tags[last] == .@"break" and | ||
| 3350 | zir_datas[last].@"break".block_inst == switch_block) | ||
| 3351 | { | ||
| 3352 | zir_datas[last].@"break".operand = .void_value; | ||
| 3353 | } | ||
| 3354 | } | ||
| 3355 | var scalar_i: u32 = 0; | ||
| 3356 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | ||
| 3357 | extra_index += 1; | ||
| 3358 | const body_len = astgen.extra.items[extra_index]; | ||
| 3359 | extra_index += 1; | ||
| 3360 | const body = astgen.extra.items[extra_index..][0..body_len]; | ||
| 3361 | extra_index += body_len; | ||
| 3362 | const last = body[body.len - 1]; | ||
| 3363 | if (zir_tags[last] == .@"break" and | ||
| 3364 | zir_datas[last].@"break".block_inst == switch_block) | ||
| 3365 | { | ||
| 3366 | zir_datas[last].@"break".operand = .void_value; | ||
| 3367 | } | ||
| 3368 | } | ||
| 3369 | var multi_i: u32 = 0; | ||
| 3370 | while (multi_i < multi_cases_len) : (multi_i += 1) { | ||
| 3371 | const items_len = astgen.extra.items[extra_index]; | ||
| 3372 | extra_index += 1; | ||
| 3373 | const ranges_len = astgen.extra.items[extra_index]; | ||
| 3374 | extra_index += 1; | ||
| 3375 | const body_len = astgen.extra.items[extra_index]; | ||
| 3376 | extra_index += 1; | ||
| 3377 | extra_index += items_len; | ||
| 3378 | extra_index += 2 * ranges_len; | ||
| 3379 | const body = astgen.extra.items[extra_index..][0..body_len]; | ||
| 3380 | extra_index += body_len; | ||
| 3381 | const last = body[body.len - 1]; | ||
| 3382 | if (zir_tags[last] == .@"break" and | ||
| 3383 | zir_datas[last].@"break".block_inst == switch_block) | ||
| 3384 | { | ||
| 3385 | zir_datas[last].@"break".operand = .void_value; | ||
| 3386 | } | ||
| 3387 | } | ||
| 3388 | |||
| 3389 | return astgen.indexToRef(switch_block); | ||
| 3390 | }, | ||
| 3391 | } | ||
| 3392 | } | ||
| 3393 | |||
| 3394 | fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 3395 | const tree = gz.tree(); | ||
| 3396 | const node_datas = tree.nodes.items(.data); | ||
| 3397 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3398 | |||
| 3399 | const operand_node = node_datas[node].lhs; | ||
| 3400 | const operand: zir.Inst.Ref = if (operand_node != 0) operand: { | ||
| 3401 | const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{ | ||
| 3402 | .ptr = try gz.addNode(.ret_ptr, node), | ||
| 3403 | } else .{ | ||
| 3404 | .ty = try gz.addNode(.ret_type, node), | ||
| 3405 | }; | ||
| 3406 | break :operand try expr(gz, scope, rl, operand_node); | ||
| 3407 | } else .void_value; | ||
| 3408 | _ = try gz.addUnNode(.ret_node, operand, node); | ||
| 3409 | return zir.Inst.Ref.unreachable_value; | ||
| 3410 | } | ||
| 3411 | |||
| 3412 | fn identifier( | ||
| 3413 | gz: *GenZir, | ||
| 3414 | scope: *Scope, | ||
| 3415 | rl: ResultLoc, | ||
| 3416 | ident: ast.Node.Index, | ||
| 3417 | ) InnerError!zir.Inst.Ref { | ||
| 3418 | const tracy = trace(@src()); | ||
| 3419 | defer tracy.end(); | ||
| 3420 | |||
| 3421 | const mod = gz.astgen.mod; | ||
| 3422 | const tree = gz.tree(); | ||
| 3423 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3424 | |||
| 3425 | const ident_token = main_tokens[ident]; | ||
| 3426 | const ident_name = try mod.identifierTokenString(scope, ident_token); | ||
| 3427 | if (mem.eql(u8, ident_name, "_")) { | ||
| 3428 | return mod.failNode(scope, ident, "TODO implement '_' identifier", .{}); | ||
| 3429 | } | ||
| 3430 | |||
| 3431 | if (simple_types.get(ident_name)) |zir_const_ref| { | ||
| 3432 | return rvalue(gz, scope, rl, zir_const_ref, ident); | ||
| 3433 | } | ||
| 3434 | |||
| 3435 | if (ident_name.len >= 2) integer: { | ||
| 3436 | const first_c = ident_name[0]; | ||
| 3437 | if (first_c == 'i' or first_c == 'u') { | ||
| 3438 | const signedness: std.builtin.Signedness = switch (first_c == 'i') { | ||
| 3439 | true => .signed, | ||
| 3440 | false => .unsigned, | ||
| 3441 | }; | ||
| 3442 | const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) { | ||
| 3443 | error.Overflow => return mod.failNode( | ||
| 3444 | scope, | ||
| 3445 | ident, | ||
| 3446 | "primitive integer type '{s}' exceeds maximum bit width of 65535", | ||
| 3447 | .{ident_name}, | ||
| 3448 | ), | ||
| 3449 | error.InvalidCharacter => break :integer, | ||
| 3450 | }; | ||
| 3451 | const result = try gz.add(.{ | ||
| 3452 | .tag = .int_type, | ||
| 3453 | .data = .{ .int_type = .{ | ||
| 3454 | .src_node = gz.astgen.decl.nodeIndexToRelative(ident), | ||
| 3455 | .signedness = signedness, | ||
| 3456 | .bit_count = bit_count, | ||
| 3457 | } }, | ||
| 3458 | }); | ||
| 3459 | return rvalue(gz, scope, rl, result, ident); | ||
| 3460 | } | ||
| 3461 | } | ||
| 3462 | |||
| 3463 | // Local variables, including function parameters. | ||
| 3464 | { | ||
| 3465 | var s = scope; | ||
| 3466 | while (true) switch (s.tag) { | ||
| 3467 | .local_val => { | ||
| 3468 | const local_val = s.cast(Scope.LocalVal).?; | ||
| 3469 | if (mem.eql(u8, local_val.name, ident_name)) { | ||
| 3470 | return rvalue(gz, scope, rl, local_val.inst, ident); | ||
| 3471 | } | ||
| 3472 | s = local_val.parent; | ||
| 3473 | }, | ||
| 3474 | .local_ptr => { | ||
| 3475 | const local_ptr = s.cast(Scope.LocalPtr).?; | ||
| 3476 | if (mem.eql(u8, local_ptr.name, ident_name)) { | ||
| 3477 | if (rl == .ref) return local_ptr.ptr; | ||
| 3478 | const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident); | ||
| 3479 | return rvalue(gz, scope, rl, loaded, ident); | ||
| 3480 | } | ||
| 3481 | s = local_ptr.parent; | ||
| 3482 | }, | ||
| 3483 | .gen_zir => s = s.cast(GenZir).?.parent, | ||
| 3484 | else => break, | ||
| 3485 | }; | ||
| 3486 | } | ||
| 3487 | |||
| 3488 | const gop = try gz.astgen.decl_map.getOrPut(mod.gpa, ident_name); | ||
| 3489 | if (!gop.found_existing) { | ||
| 3490 | const decl = mod.lookupDeclName(scope, ident_name) orelse | ||
| 3491 | return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name}); | ||
| 3492 | try gz.astgen.decls.append(mod.gpa, decl); | ||
| 3493 | } | ||
| 3494 | const decl_index = @intCast(u32, gop.index); | ||
| 3495 | switch (rl) { | ||
| 3496 | .ref => return gz.addDecl(.decl_ref, decl_index, ident), | ||
| 3497 | else => return rvalue(gz, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident), | ||
| 3498 | } | ||
| 3499 | } | ||
| 3500 | |||
| 3501 | fn stringLiteral( | ||
| 3502 | gz: *GenZir, | ||
| 3503 | scope: *Scope, | ||
| 3504 | rl: ResultLoc, | ||
| 3505 | node: ast.Node.Index, | ||
| 3506 | ) InnerError!zir.Inst.Ref { | ||
| 3507 | const tree = gz.tree(); | ||
| 3508 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3509 | const string_bytes = &gz.astgen.string_bytes; | ||
| 3510 | const str_index = string_bytes.items.len; | ||
| 3511 | const str_lit_token = main_tokens[node]; | ||
| 3512 | const token_bytes = tree.tokenSlice(str_lit_token); | ||
| 3513 | try gz.astgen.mod.parseStrLit(scope, str_lit_token, string_bytes, token_bytes, 0); | ||
| 3514 | const str_len = string_bytes.items.len - str_index; | ||
| 3515 | const result = try gz.add(.{ | ||
| 3516 | .tag = .str, | ||
| 3517 | .data = .{ .str = .{ | ||
| 3518 | .start = @intCast(u32, str_index), | ||
| 3519 | .len = @intCast(u32, str_len), | ||
| 3520 | } }, | ||
| 3521 | }); | ||
| 3522 | return rvalue(gz, scope, rl, result, node); | ||
| 3523 | } | ||
| 3524 | |||
| 3525 | fn multilineStringLiteral( | ||
| 3526 | gz: *GenZir, | ||
| 3527 | scope: *Scope, | ||
| 3528 | rl: ResultLoc, | ||
| 3529 | node: ast.Node.Index, | ||
| 3530 | ) InnerError!zir.Inst.Ref { | ||
| 3531 | const tree = gz.tree(); | ||
| 3532 | const node_datas = tree.nodes.items(.data); | ||
| 3533 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3534 | |||
| 3535 | const start = node_datas[node].lhs; | ||
| 3536 | const end = node_datas[node].rhs; | ||
| 3537 | |||
| 3538 | const gpa = gz.astgen.mod.gpa; | ||
| 3539 | const string_bytes = &gz.astgen.string_bytes; | ||
| 3540 | const str_index = string_bytes.items.len; | ||
| 3541 | |||
| 3542 | // First line: do not append a newline. | ||
| 3543 | var tok_i = start; | ||
| 3544 | { | ||
| 3545 | const slice = tree.tokenSlice(tok_i); | ||
| 3546 | const line_bytes = slice[2 .. slice.len - 1]; | ||
| 3547 | try string_bytes.appendSlice(gpa, line_bytes); | ||
| 3548 | tok_i += 1; | ||
| 3549 | } | ||
| 3550 | // Following lines: each line prepends a newline. | ||
| 3551 | while (tok_i <= end) : (tok_i += 1) { | ||
| 3552 | const slice = tree.tokenSlice(tok_i); | ||
| 3553 | const line_bytes = slice[2 .. slice.len - 1]; | ||
| 3554 | try string_bytes.ensureCapacity(gpa, string_bytes.items.len + line_bytes.len + 1); | ||
| 3555 | string_bytes.appendAssumeCapacity('\n'); | ||
| 3556 | string_bytes.appendSliceAssumeCapacity(line_bytes); | ||
| 3557 | } | ||
| 3558 | const result = try gz.add(.{ | ||
| 3559 | .tag = .str, | ||
| 3560 | .data = .{ .str = .{ | ||
| 3561 | .start = @intCast(u32, str_index), | ||
| 3562 | .len = @intCast(u32, string_bytes.items.len - str_index), | ||
| 3563 | } }, | ||
| 3564 | }); | ||
| 3565 | return rvalue(gz, scope, rl, result, node); | ||
| 3566 | } | ||
| 3567 | |||
| 3568 | fn charLiteral(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { | ||
| 3569 | const mod = gz.astgen.mod; | ||
| 3570 | const tree = gz.tree(); | ||
| 3571 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3572 | const main_token = main_tokens[node]; | ||
| 3573 | const slice = tree.tokenSlice(main_token); | ||
| 3574 | |||
| 3575 | var bad_index: usize = undefined; | ||
| 3576 | const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) { | ||
| 3577 | error.InvalidCharacter => { | ||
| 3578 | const bad_byte = slice[bad_index]; | ||
| 3579 | const token_starts = tree.tokens.items(.start); | ||
| 3580 | const src_off = @intCast(u32, token_starts[main_token] + bad_index); | ||
| 3581 | return mod.failOff(scope, src_off, "invalid character: '{c}'\n", .{bad_byte}); | ||
| 3582 | }, | ||
| 3583 | }; | ||
| 3584 | const result = try gz.addInt(value); | ||
| 3585 | return rvalue(gz, scope, rl, result, node); | ||
| 3586 | } | ||
| 3587 | |||
| 3588 | fn integerLiteral( | ||
| 3589 | gz: *GenZir, | ||
| 3590 | scope: *Scope, | ||
| 3591 | rl: ResultLoc, | ||
| 3592 | node: ast.Node.Index, | ||
| 3593 | ) InnerError!zir.Inst.Ref { | ||
| 3594 | const tree = gz.tree(); | ||
| 3595 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3596 | const int_token = main_tokens[node]; | ||
| 3597 | const prefixed_bytes = tree.tokenSlice(int_token); | ||
| 3598 | if (std.fmt.parseInt(u64, prefixed_bytes, 0)) |small_int| { | ||
| 3599 | const result: zir.Inst.Ref = switch (small_int) { | ||
| 3600 | 0 => .zero, | ||
| 3601 | 1 => .one, | ||
| 3602 | else => try gz.addInt(small_int), | ||
| 3603 | }; | ||
| 3604 | return rvalue(gz, scope, rl, result, node); | ||
| 3605 | } else |err| { | ||
| 3606 | return gz.astgen.mod.failNode(scope, node, "TODO implement int literals that don't fit in a u64", .{}); | ||
| 3607 | } | ||
| 3608 | } | ||
| 3609 | |||
| 3610 | fn floatLiteral( | ||
| 3611 | gz: *GenZir, | ||
| 3612 | scope: *Scope, | ||
| 3613 | rl: ResultLoc, | ||
| 3614 | node: ast.Node.Index, | ||
| 3615 | ) InnerError!zir.Inst.Ref { | ||
| 3616 | const arena = gz.astgen.arena; | ||
| 3617 | const tree = gz.tree(); | ||
| 3618 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3619 | |||
| 3620 | const main_token = main_tokens[node]; | ||
| 3621 | const bytes = tree.tokenSlice(main_token); | ||
| 3622 | if (bytes.len > 2 and bytes[1] == 'x') { | ||
| 3623 | assert(bytes[0] == '0'); // validated by tokenizer | ||
| 3624 | return gz.astgen.mod.failTok(scope, main_token, "TODO implement hex floats", .{}); | ||
| 3625 | } | ||
| 3626 | const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) { | ||
| 3627 | error.InvalidCharacter => unreachable, // validated by tokenizer | ||
| 3628 | }; | ||
| 3629 | const typed_value = try arena.create(TypedValue); | ||
| 3630 | typed_value.* = .{ | ||
| 3631 | .ty = Type.initTag(.comptime_float), | ||
| 3632 | .val = try Value.Tag.float_128.create(arena, float_number), | ||
| 3633 | }; | ||
| 3634 | const result = try gz.addConst(typed_value); | ||
| 3635 | return rvalue(gz, scope, rl, result, node); | ||
| 3636 | } | ||
| 3637 | |||
| 3638 | fn asmExpr( | ||
| 3639 | gz: *GenZir, | ||
| 3640 | scope: *Scope, | ||
| 3641 | rl: ResultLoc, | ||
| 3642 | node: ast.Node.Index, | ||
| 3643 | full: ast.full.Asm, | ||
| 3644 | ) InnerError!zir.Inst.Ref { | ||
| 3645 | const mod = gz.astgen.mod; | ||
| 3646 | const arena = gz.astgen.arena; | ||
| 3647 | const tree = gz.tree(); | ||
| 3648 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3649 | const node_datas = tree.nodes.items(.data); | ||
| 3650 | |||
| 3651 | const asm_source = try expr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template); | ||
| 3652 | |||
| 3653 | if (full.outputs.len != 0) { | ||
| 3654 | // when implementing this be sure to add test coverage for the asm return type | ||
| 3655 | // not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc) | ||
| 3656 | return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{}); | ||
| 3657 | } | ||
| 3658 | |||
| 3659 | const constraints = try arena.alloc(u32, full.inputs.len); | ||
| 3660 | const args = try arena.alloc(zir.Inst.Ref, full.inputs.len); | ||
| 3661 | |||
| 3662 | for (full.inputs) |input, i| { | ||
| 3663 | const constraint_token = main_tokens[input] + 2; | ||
| 3664 | const string_bytes = &gz.astgen.string_bytes; | ||
| 3665 | constraints[i] = @intCast(u32, string_bytes.items.len); | ||
| 3666 | const token_bytes = tree.tokenSlice(constraint_token); | ||
| 3667 | try mod.parseStrLit(scope, constraint_token, string_bytes, token_bytes, 0); | ||
| 3668 | try string_bytes.append(mod.gpa, 0); | ||
| 3669 | |||
| 3670 | args[i] = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input].lhs); | ||
| 3671 | } | ||
| 3672 | |||
| 3673 | const tag: zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm"; | ||
| 3674 | const result = try gz.addPlNode(tag, node, zir.Inst.Asm{ | ||
| 3675 | .asm_source = asm_source, | ||
| 3676 | .return_type = .void_type, | ||
| 3677 | .output = .none, | ||
| 3678 | .args_len = @intCast(u32, full.inputs.len), | ||
| 3679 | .clobbers_len = 0, // TODO implement asm clobbers | ||
| 3680 | }); | ||
| 3681 | |||
| 3682 | try gz.astgen.extra.ensureCapacity(mod.gpa, gz.astgen.extra.items.len + | ||
| 3683 | args.len + constraints.len); | ||
| 3684 | gz.astgen.appendRefsAssumeCapacity(args); | ||
| 3685 | gz.astgen.extra.appendSliceAssumeCapacity(constraints); | ||
| 3686 | |||
| 3687 | return rvalue(gz, scope, rl, result, node); | ||
| 3688 | } | ||
| 3689 | |||
| 3690 | fn as( | ||
| 3691 | gz: *GenZir, | ||
| 3692 | scope: *Scope, | ||
| 3693 | rl: ResultLoc, | ||
| 3694 | node: ast.Node.Index, | ||
| 3695 | lhs: ast.Node.Index, | ||
| 3696 | rhs: ast.Node.Index, | ||
| 3697 | ) InnerError!zir.Inst.Ref { | ||
| 3698 | const dest_type = try typeExpr(gz, scope, lhs); | ||
| 3699 | switch (rl) { | ||
| 3700 | .none, .discard, .ref, .ty => { | ||
| 3701 | const result = try expr(gz, scope, .{ .ty = dest_type }, rhs); | ||
| 3702 | return rvalue(gz, scope, rl, result, node); | ||
| 3703 | }, | ||
| 3704 | |||
| 3705 | .ptr => |result_ptr| { | ||
| 3706 | return asRlPtr(gz, scope, rl, result_ptr, rhs, dest_type); | ||
| 3707 | }, | ||
| 3708 | .block_ptr => |block_scope| { | ||
| 3709 | return asRlPtr(gz, scope, rl, block_scope.rl_ptr, rhs, dest_type); | ||
| 3710 | }, | ||
| 3711 | |||
| 3712 | .inferred_ptr => |result_alloc| { | ||
| 3713 | // TODO here we should be able to resolve the inference; we now have a type for the result. | ||
| 3714 | return gz.astgen.mod.failNode(scope, node, "TODO implement @as with inferred-type result location pointer", .{}); | ||
| 3715 | }, | ||
| 3716 | } | ||
| 3717 | } | ||
| 3718 | |||
| 3719 | fn asRlPtr( | ||
| 3720 | parent_gz: *GenZir, | ||
| 3721 | scope: *Scope, | ||
| 3722 | rl: ResultLoc, | ||
| 3723 | result_ptr: zir.Inst.Ref, | ||
| 3724 | operand_node: ast.Node.Index, | ||
| 3725 | dest_type: zir.Inst.Ref, | ||
| 3726 | ) InnerError!zir.Inst.Ref { | ||
| 3727 | // Detect whether this expr() call goes into rvalue() to store the result into the | ||
| 3728 | // result location. If it does, elide the coerce_result_ptr instruction | ||
| 3729 | // as well as the store instruction, instead passing the result as an rvalue. | ||
| 3730 | const astgen = parent_gz.astgen; | ||
| 3731 | |||
| 3732 | var as_scope: GenZir = .{ | ||
| 3733 | .parent = scope, | ||
| 3734 | .astgen = astgen, | ||
| 3735 | .force_comptime = parent_gz.force_comptime, | ||
| 3736 | .instructions = .{}, | ||
| 3737 | }; | ||
| 3738 | defer as_scope.instructions.deinit(astgen.mod.gpa); | ||
| 3739 | |||
| 3740 | as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); | ||
| 3741 | const result = try expr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node); | ||
| 3742 | const parent_zir = &parent_gz.instructions; | ||
| 3743 | if (as_scope.rvalue_rl_count == 1) { | ||
| 3744 | // Busted! This expression didn't actually need a pointer. | ||
| 3745 | const zir_tags = astgen.instructions.items(.tag); | ||
| 3746 | const zir_datas = astgen.instructions.items(.data); | ||
| 3747 | const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2; | ||
| 3748 | try parent_zir.ensureCapacity(astgen.mod.gpa, expected_len); | ||
| 3749 | for (as_scope.instructions.items) |src_inst| { | ||
| 3750 | if (astgen.indexToRef(src_inst) == as_scope.rl_ptr) continue; | ||
| 3751 | if (zir_tags[src_inst] == .store_to_block_ptr) { | ||
| 3752 | if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; | ||
| 3753 | } | ||
| 3754 | parent_zir.appendAssumeCapacity(src_inst); | ||
| 3755 | } | ||
| 3756 | assert(parent_zir.items.len == expected_len); | ||
| 3757 | const casted_result = try parent_gz.addBin(.as, dest_type, result); | ||
| 3758 | return rvalue(parent_gz, scope, rl, casted_result, operand_node); | ||
| 3759 | } else { | ||
| 3760 | try parent_zir.appendSlice(astgen.mod.gpa, as_scope.instructions.items); | ||
| 3761 | return result; | ||
| 3762 | } | ||
| 3763 | } | ||
| 3764 | |||
| 3765 | fn bitCast( | ||
| 3766 | gz: *GenZir, | ||
| 3767 | scope: *Scope, | ||
| 3768 | rl: ResultLoc, | ||
| 3769 | node: ast.Node.Index, | ||
| 3770 | lhs: ast.Node.Index, | ||
| 3771 | rhs: ast.Node.Index, | ||
| 3772 | ) InnerError!zir.Inst.Ref { | ||
| 3773 | const mod = gz.astgen.mod; | ||
| 3774 | const dest_type = try typeExpr(gz, scope, lhs); | ||
| 3775 | switch (rl) { | ||
| 3776 | .none, .discard, .ty => { | ||
| 3777 | const operand = try expr(gz, scope, .none, rhs); | ||
| 3778 | const result = try gz.addPlNode(.bitcast, node, zir.Inst.Bin{ | ||
| 3779 | .lhs = dest_type, | ||
| 3780 | .rhs = operand, | ||
| 3781 | }); | ||
| 3782 | return rvalue(gz, scope, rl, result, node); | ||
| 3783 | }, | ||
| 3784 | .ref => unreachable, // `@bitCast` is not allowed as an r-value. | ||
| 3785 | .ptr => |result_ptr| { | ||
| 3786 | const casted_result_ptr = try gz.addUnNode(.bitcast_result_ptr, result_ptr, node); | ||
| 3787 | return expr(gz, scope, .{ .ptr = casted_result_ptr }, rhs); | ||
| 3788 | }, | ||
| 3789 | .block_ptr => |block_ptr| { | ||
| 3790 | return mod.failNode(scope, node, "TODO implement @bitCast with result location inferred peer types", .{}); | ||
| 3791 | }, | ||
| 3792 | .inferred_ptr => |result_alloc| { | ||
| 3793 | // TODO here we should be able to resolve the inference; we now have a type for the result. | ||
| 3794 | return mod.failNode(scope, node, "TODO implement @bitCast with inferred-type result location pointer", .{}); | ||
| 3795 | }, | ||
| 3796 | } | ||
| 3797 | } | ||
| 3798 | |||
| 3799 | fn typeOf( | ||
| 3800 | gz: *GenZir, | ||
| 3801 | scope: *Scope, | ||
| 3802 | rl: ResultLoc, | ||
| 3803 | node: ast.Node.Index, | ||
| 3804 | params: []const ast.Node.Index, | ||
| 3805 | ) InnerError!zir.Inst.Ref { | ||
| 3806 | if (params.len < 1) { | ||
| 3807 | return gz.astgen.mod.failNode(scope, node, "expected at least 1 argument, found 0", .{}); | ||
| 3808 | } | ||
| 3809 | if (params.len == 1) { | ||
| 3810 | const result = try gz.addUnNode(.typeof, try expr(gz, scope, .none, params[0]), node); | ||
| 3811 | return rvalue(gz, scope, rl, result, node); | ||
| 3812 | } | ||
| 3813 | const arena = gz.astgen.arena; | ||
| 3814 | var items = try arena.alloc(zir.Inst.Ref, params.len); | ||
| 3815 | for (params) |param, param_i| { | ||
| 3816 | items[param_i] = try expr(gz, scope, .none, param); | ||
| 3817 | } | ||
| 3818 | |||
| 3819 | const result = try gz.addPlNode(.typeof_peer, node, zir.Inst.MultiOp{ | ||
| 3820 | .operands_len = @intCast(u32, params.len), | ||
| 3821 | }); | ||
| 3822 | try gz.astgen.appendRefs(items); | ||
| 3823 | |||
| 3824 | return rvalue(gz, scope, rl, result, node); | ||
| 3825 | } | ||
| 3826 | |||
| 3827 | fn builtinCall( | ||
| 3828 | gz: *GenZir, | ||
| 3829 | scope: *Scope, | ||
| 3830 | rl: ResultLoc, | ||
| 3831 | node: ast.Node.Index, | ||
| 3832 | params: []const ast.Node.Index, | ||
| 3833 | ) InnerError!zir.Inst.Ref { | ||
| 3834 | const mod = gz.astgen.mod; | ||
| 3835 | const tree = gz.tree(); | ||
| 3836 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3837 | |||
| 3838 | const builtin_token = main_tokens[node]; | ||
| 3839 | const builtin_name = tree.tokenSlice(builtin_token); | ||
| 3840 | |||
| 3841 | // We handle the different builtins manually because they have different semantics depending | ||
| 3842 | // on the function. For example, `@as` and others participate in result location semantics, | ||
| 3843 | // and `@cImport` creates a special scope that collects a .c source code text buffer. | ||
| 3844 | // Also, some builtins have a variable number of parameters. | ||
| 3845 | |||
| 3846 | const info = BuiltinFn.list.get(builtin_name) orelse { | ||
| 3847 | return mod.failNode(scope, node, "invalid builtin function: '{s}'", .{ | ||
| 3848 | builtin_name, | ||
| 3849 | }); | ||
| 3850 | }; | ||
| 3851 | if (info.param_count) |expected| { | ||
| 3852 | if (expected != params.len) { | ||
| 3853 | const s = if (expected == 1) "" else "s"; | ||
| 3854 | return mod.failNode(scope, node, "expected {d} parameter{s}, found {d}", .{ | ||
| 3855 | expected, s, params.len, | ||
| 3856 | }); | ||
| 3857 | } | ||
| 3858 | } | ||
| 3859 | |||
| 3860 | switch (info.tag) { | ||
| 3861 | .ptr_to_int => { | ||
| 3862 | const operand = try expr(gz, scope, .none, params[0]); | ||
| 3863 | const result = try gz.addUnNode(.ptrtoint, operand, node); | ||
| 3864 | return rvalue(gz, scope, rl, result, node); | ||
| 3865 | }, | ||
| 3866 | .float_cast => { | ||
| 3867 | const dest_type = try typeExpr(gz, scope, params[0]); | ||
| 3868 | const rhs = try expr(gz, scope, .none, params[1]); | ||
| 3869 | const result = try gz.addPlNode(.floatcast, node, zir.Inst.Bin{ | ||
| 3870 | .lhs = dest_type, | ||
| 3871 | .rhs = rhs, | ||
| 3872 | }); | ||
| 3873 | return rvalue(gz, scope, rl, result, node); | ||
| 3874 | }, | ||
| 3875 | .int_cast => { | ||
| 3876 | const dest_type = try typeExpr(gz, scope, params[0]); | ||
| 3877 | const rhs = try expr(gz, scope, .none, params[1]); | ||
| 3878 | const result = try gz.addPlNode(.intcast, node, zir.Inst.Bin{ | ||
| 3879 | .lhs = dest_type, | ||
| 3880 | .rhs = rhs, | ||
| 3881 | }); | ||
| 3882 | return rvalue(gz, scope, rl, result, node); | ||
| 3883 | }, | ||
| 3884 | .breakpoint => { | ||
| 3885 | const result = try gz.add(.{ | ||
| 3886 | .tag = .breakpoint, | ||
| 3887 | .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(node) }, | ||
| 3888 | }); | ||
| 3889 | return rvalue(gz, scope, rl, result, node); | ||
| 3890 | }, | ||
| 3891 | .import => { | ||
| 3892 | const target = try expr(gz, scope, .none, params[0]); | ||
| 3893 | const result = try gz.addUnNode(.import, target, node); | ||
| 3894 | return rvalue(gz, scope, rl, result, node); | ||
| 3895 | }, | ||
| 3896 | .error_to_int => { | ||
| 3897 | const target = try expr(gz, scope, .none, params[0]); | ||
| 3898 | const result = try gz.addUnNode(.error_to_int, target, node); | ||
| 3899 | return rvalue(gz, scope, rl, result, node); | ||
| 3900 | }, | ||
| 3901 | .int_to_error => { | ||
| 3902 | const target = try expr(gz, scope, .{ .ty = .u16_type }, params[0]); | ||
| 3903 | const result = try gz.addUnNode(.int_to_error, target, node); | ||
| 3904 | return rvalue(gz, scope, rl, result, node); | ||
| 3905 | }, | ||
| 3906 | .compile_error => { | ||
| 3907 | const target = try expr(gz, scope, .none, params[0]); | ||
| 3908 | const result = try gz.addUnNode(.compile_error, target, node); | ||
| 3909 | return rvalue(gz, scope, rl, result, node); | ||
| 3910 | }, | ||
| 3911 | .set_eval_branch_quota => { | ||
| 3912 | const quota = try expr(gz, scope, .{ .ty = .u32_type }, params[0]); | ||
| 3913 | const result = try gz.addUnNode(.set_eval_branch_quota, quota, node); | ||
| 3914 | return rvalue(gz, scope, rl, result, node); | ||
| 3915 | }, | ||
| 3916 | .compile_log => { | ||
| 3917 | const arg_refs = try mod.gpa.alloc(zir.Inst.Ref, params.len); | ||
| 3918 | defer mod.gpa.free(arg_refs); | ||
| 3919 | |||
| 3920 | for (params) |param, i| arg_refs[i] = try expr(gz, scope, .none, param); | ||
| 3921 | |||
| 3922 | const result = try gz.addPlNode(.compile_log, node, zir.Inst.MultiOp{ | ||
| 3923 | .operands_len = @intCast(u32, params.len), | ||
| 3924 | }); | ||
| 3925 | try gz.astgen.appendRefs(arg_refs); | ||
| 3926 | return rvalue(gz, scope, rl, result, node); | ||
| 3927 | }, | ||
| 3928 | .field => { | ||
| 3929 | const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); | ||
| 3930 | if (rl == .ref) { | ||
| 3931 | return try gz.addPlNode(.field_ptr_named, node, zir.Inst.FieldNamed{ | ||
| 3932 | .lhs = try expr(gz, scope, .ref, params[0]), | ||
| 3933 | .field_name = field_name, | ||
| 3934 | }); | ||
| 3935 | } | ||
| 3936 | const result = try gz.addPlNode(.field_val_named, node, zir.Inst.FieldNamed{ | ||
| 3937 | .lhs = try expr(gz, scope, .none, params[0]), | ||
| 3938 | .field_name = field_name, | ||
| 3939 | }); | ||
| 3940 | return rvalue(gz, scope, rl, result, node); | ||
| 3941 | }, | ||
| 3942 | .as => return as(gz, scope, rl, node, params[0], params[1]), | ||
| 3943 | .bit_cast => return bitCast(gz, scope, rl, node, params[0], params[1]), | ||
| 3944 | .TypeOf => return typeOf(gz, scope, rl, node, params), | ||
| 3945 | |||
| 3946 | .add_with_overflow, | ||
| 3947 | .align_cast, | ||
| 3948 | .align_of, | ||
| 3949 | .atomic_load, | ||
| 3950 | .atomic_rmw, | ||
| 3951 | .atomic_store, | ||
| 3952 | .bit_offset_of, | ||
| 3953 | .bool_to_int, | ||
| 3954 | .bit_size_of, | ||
| 3955 | .mul_add, | ||
| 3956 | .byte_swap, | ||
| 3957 | .bit_reverse, | ||
| 3958 | .byte_offset_of, | ||
| 3959 | .call, | ||
| 3960 | .c_define, | ||
| 3961 | .c_import, | ||
| 3962 | .c_include, | ||
| 3963 | .clz, | ||
| 3964 | .cmpxchg_strong, | ||
| 3965 | .cmpxchg_weak, | ||
| 3966 | .ctz, | ||
| 3967 | .c_undef, | ||
| 3968 | .div_exact, | ||
| 3969 | .div_floor, | ||
| 3970 | .div_trunc, | ||
| 3971 | .embed_file, | ||
| 3972 | .enum_to_int, | ||
| 3973 | .error_name, | ||
| 3974 | .error_return_trace, | ||
| 3975 | .err_set_cast, | ||
| 3976 | .@"export", | ||
| 3977 | .fence, | ||
| 3978 | .field_parent_ptr, | ||
| 3979 | .float_to_int, | ||
| 3980 | .has_decl, | ||
| 3981 | .has_field, | ||
| 3982 | .int_to_enum, | ||
| 3983 | .int_to_float, | ||
| 3984 | .int_to_ptr, | ||
| 3985 | .memcpy, | ||
| 3986 | .memset, | ||
| 3987 | .wasm_memory_size, | ||
| 3988 | .wasm_memory_grow, | ||
| 3989 | .mod, | ||
| 3990 | .mul_with_overflow, | ||
| 3991 | .panic, | ||
| 3992 | .pop_count, | ||
| 3993 | .ptr_cast, | ||
| 3994 | .rem, | ||
| 3995 | .return_address, | ||
| 3996 | .set_align_stack, | ||
| 3997 | .set_cold, | ||
| 3998 | .set_float_mode, | ||
| 3999 | .set_runtime_safety, | ||
| 4000 | .shl_exact, | ||
| 4001 | .shl_with_overflow, | ||
| 4002 | .shr_exact, | ||
| 4003 | .shuffle, | ||
| 4004 | .size_of, | ||
| 4005 | .splat, | ||
| 4006 | .reduce, | ||
| 4007 | .src, | ||
| 4008 | .sqrt, | ||
| 4009 | .sin, | ||
| 4010 | .cos, | ||
| 4011 | .exp, | ||
| 4012 | .exp2, | ||
| 4013 | .log, | ||
| 4014 | .log2, | ||
| 4015 | .log10, | ||
| 4016 | .fabs, | ||
| 4017 | .floor, | ||
| 4018 | .ceil, | ||
| 4019 | .trunc, | ||
| 4020 | .round, | ||
| 4021 | .sub_with_overflow, | ||
| 4022 | .tag_name, | ||
| 4023 | .This, | ||
| 4024 | .truncate, | ||
| 4025 | .Type, | ||
| 4026 | .type_info, | ||
| 4027 | .type_name, | ||
| 4028 | .union_init, | ||
| 4029 | => return mod.failNode(scope, node, "TODO: implement builtin function {s}", .{ | ||
| 4030 | builtin_name, | ||
| 4031 | }), | ||
| 4032 | |||
| 4033 | .async_call, | ||
| 4034 | .frame, | ||
| 4035 | .Frame, | ||
| 4036 | .frame_address, | ||
| 4037 | .frame_size, | ||
| 4038 | => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 4039 | } | ||
| 4040 | } | ||
| 4041 | |||
| 4042 | fn callExpr( | ||
| 4043 | gz: *GenZir, | ||
| 4044 | scope: *Scope, | ||
| 4045 | rl: ResultLoc, | ||
| 4046 | node: ast.Node.Index, | ||
| 4047 | call: ast.full.Call, | ||
| 4048 | ) InnerError!zir.Inst.Ref { | ||
| 4049 | const mod = gz.astgen.mod; | ||
| 4050 | if (call.async_token) |async_token| { | ||
| 4051 | return mod.failTok(scope, async_token, "async and related features are not yet supported", .{}); | ||
| 4052 | } | ||
| 4053 | const lhs = try expr(gz, scope, .none, call.ast.fn_expr); | ||
| 4054 | |||
| 4055 | const args = try mod.gpa.alloc(zir.Inst.Ref, call.ast.params.len); | ||
| 4056 | defer mod.gpa.free(args); | ||
| 4057 | |||
| 4058 | for (call.ast.params) |param_node, i| { | ||
| 4059 | const param_type = try gz.add(.{ | ||
| 4060 | .tag = .param_type, | ||
| 4061 | .data = .{ .param_type = .{ | ||
| 4062 | .callee = lhs, | ||
| 4063 | .param_index = @intCast(u32, i), | ||
| 4064 | } }, | ||
| 4065 | }); | ||
| 4066 | args[i] = try expr(gz, scope, .{ .ty = param_type }, param_node); | ||
| 4067 | } | ||
| 4068 | |||
| 4069 | const modifier: std.builtin.CallOptions.Modifier = switch (call.async_token != null) { | ||
| 4070 | true => .async_kw, | ||
| 4071 | false => .auto, | ||
| 4072 | }; | ||
| 4073 | const result: zir.Inst.Ref = res: { | ||
| 4074 | const tag: zir.Inst.Tag = switch (modifier) { | ||
| 4075 | .auto => switch (args.len == 0) { | ||
| 4076 | true => break :res try gz.addUnNode(.call_none, lhs, node), | ||
| 4077 | false => .call, | ||
| 4078 | }, | ||
| 4079 | .async_kw => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 4080 | .never_tail => unreachable, | ||
| 4081 | .never_inline => unreachable, | ||
| 4082 | .no_async => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 4083 | .always_tail => unreachable, | ||
| 4084 | .always_inline => unreachable, | ||
| 4085 | .compile_time => .call_compile_time, | ||
| 4086 | }; | ||
| 4087 | break :res try gz.addCall(tag, lhs, args, node); | ||
| 4088 | }; | ||
| 4089 | return rvalue(gz, scope, rl, result, node); // TODO function call with result location | ||
| 4090 | } | ||
| 4091 | |||
| 4092 | pub const simple_types = std.ComptimeStringMap(zir.Inst.Ref, .{ | ||
| 4093 | .{ "u8", .u8_type }, | ||
| 4094 | .{ "i8", .i8_type }, | ||
| 4095 | .{ "u16", .u16_type }, | ||
| 4096 | .{ "i16", .i16_type }, | ||
| 4097 | .{ "u32", .u32_type }, | ||
| 4098 | .{ "i32", .i32_type }, | ||
| 4099 | .{ "u64", .u64_type }, | ||
| 4100 | .{ "i64", .i64_type }, | ||
| 4101 | .{ "usize", .usize_type }, | ||
| 4102 | .{ "isize", .isize_type }, | ||
| 4103 | .{ "c_short", .c_short_type }, | ||
| 4104 | .{ "c_ushort", .c_ushort_type }, | ||
| 4105 | .{ "c_int", .c_int_type }, | ||
| 4106 | .{ "c_uint", .c_uint_type }, | ||
| 4107 | .{ "c_long", .c_long_type }, | ||
| 4108 | .{ "c_ulong", .c_ulong_type }, | ||
| 4109 | .{ "c_longlong", .c_longlong_type }, | ||
| 4110 | .{ "c_ulonglong", .c_ulonglong_type }, | ||
| 4111 | .{ "c_longdouble", .c_longdouble_type }, | ||
| 4112 | .{ "f16", .f16_type }, | ||
| 4113 | .{ "f32", .f32_type }, | ||
| 4114 | .{ "f64", .f64_type }, | ||
| 4115 | .{ "f128", .f128_type }, | ||
| 4116 | .{ "c_void", .c_void_type }, | ||
| 4117 | .{ "bool", .bool_type }, | ||
| 4118 | .{ "void", .void_type }, | ||
| 4119 | .{ "type", .type_type }, | ||
| 4120 | .{ "anyerror", .anyerror_type }, | ||
| 4121 | .{ "comptime_int", .comptime_int_type }, | ||
| 4122 | .{ "comptime_float", .comptime_float_type }, | ||
| 4123 | .{ "noreturn", .noreturn_type }, | ||
| 4124 | .{ "null", .null_type }, | ||
| 4125 | .{ "undefined", .undefined_type }, | ||
| 4126 | .{ "undefined", .undef }, | ||
| 4127 | .{ "null", .null_value }, | ||
| 4128 | .{ "true", .bool_true }, | ||
| 4129 | .{ "false", .bool_false }, | ||
| 4130 | }); | ||
| 4131 | |||
| 4132 | fn nodeMayNeedMemoryLocation(tree: *const ast.Tree, start_node: ast.Node.Index) bool { | ||
| 4133 | const node_tags = tree.nodes.items(.tag); | ||
| 4134 | const node_datas = tree.nodes.items(.data); | ||
| 4135 | const main_tokens = tree.nodes.items(.main_token); | ||
| 4136 | const token_tags = tree.tokens.items(.tag); | ||
| 4137 | |||
| 4138 | var node = start_node; | ||
| 4139 | while (true) { | ||
| 4140 | switch (node_tags[node]) { | ||
| 4141 | .root, | ||
| 4142 | .@"usingnamespace", | ||
| 4143 | .test_decl, | ||
| 4144 | .switch_case, | ||
| 4145 | .switch_case_one, | ||
| 4146 | .container_field_init, | ||
| 4147 | .container_field_align, | ||
| 4148 | .container_field, | ||
| 4149 | .asm_output, | ||
| 4150 | .asm_input, | ||
| 4151 | => unreachable, | ||
| 4152 | |||
| 4153 | .@"return", | ||
| 4154 | .@"break", | ||
| 4155 | .@"continue", | ||
| 4156 | .bit_not, | ||
| 4157 | .bool_not, | ||
| 4158 | .global_var_decl, | ||
| 4159 | .local_var_decl, | ||
| 4160 | .simple_var_decl, | ||
| 4161 | .aligned_var_decl, | ||
| 4162 | .@"defer", | ||
| 4163 | .@"errdefer", | ||
| 4164 | .address_of, | ||
| 4165 | .optional_type, | ||
| 4166 | .negation, | ||
| 4167 | .negation_wrap, | ||
| 4168 | .@"resume", | ||
| 4169 | .array_type, | ||
| 4170 | .array_type_sentinel, | ||
| 4171 | .ptr_type_aligned, | ||
| 4172 | .ptr_type_sentinel, | ||
| 4173 | .ptr_type, | ||
| 4174 | .ptr_type_bit_range, | ||
| 4175 | .@"suspend", | ||
| 4176 | .@"anytype", | ||
| 4177 | .fn_proto_simple, | ||
| 4178 | .fn_proto_multi, | ||
| 4179 | .fn_proto_one, | ||
| 4180 | .fn_proto, | ||
| 4181 | .fn_decl, | ||
| 4182 | .anyframe_type, | ||
| 4183 | .anyframe_literal, | ||
| 4184 | .integer_literal, | ||
| 4185 | .float_literal, | ||
| 4186 | .enum_literal, | ||
| 4187 | .string_literal, | ||
| 4188 | .multiline_string_literal, | ||
| 4189 | .char_literal, | ||
| 4190 | .true_literal, | ||
| 4191 | .false_literal, | ||
| 4192 | .null_literal, | ||
| 4193 | .undefined_literal, | ||
| 4194 | .unreachable_literal, | ||
| 4195 | .identifier, | ||
| 4196 | .error_set_decl, | ||
| 4197 | .container_decl, | ||
| 4198 | .container_decl_trailing, | ||
| 4199 | .container_decl_two, | ||
| 4200 | .container_decl_two_trailing, | ||
| 4201 | .container_decl_arg, | ||
| 4202 | .container_decl_arg_trailing, | ||
| 4203 | .tagged_union, | ||
| 4204 | .tagged_union_trailing, | ||
| 4205 | .tagged_union_two, | ||
| 4206 | .tagged_union_two_trailing, | ||
| 4207 | .tagged_union_enum_tag, | ||
| 4208 | .tagged_union_enum_tag_trailing, | ||
| 4209 | .@"asm", | ||
| 4210 | .asm_simple, | ||
| 4211 | .add, | ||
| 4212 | .add_wrap, | ||
| 4213 | .array_cat, | ||
| 4214 | .array_mult, | ||
| 4215 | .assign, | ||
| 4216 | .assign_bit_and, | ||
| 4217 | .assign_bit_or, | ||
| 4218 | .assign_bit_shift_left, | ||
| 4219 | .assign_bit_shift_right, | ||
| 4220 | .assign_bit_xor, | ||
| 4221 | .assign_div, | ||
| 4222 | .assign_sub, | ||
| 4223 | .assign_sub_wrap, | ||
| 4224 | .assign_mod, | ||
| 4225 | .assign_add, | ||
| 4226 | .assign_add_wrap, | ||
| 4227 | .assign_mul, | ||
| 4228 | .assign_mul_wrap, | ||
| 4229 | .bang_equal, | ||
| 4230 | .bit_and, | ||
| 4231 | .bit_or, | ||
| 4232 | .bit_shift_left, | ||
| 4233 | .bit_shift_right, | ||
| 4234 | .bit_xor, | ||
| 4235 | .bool_and, | ||
| 4236 | .bool_or, | ||
| 4237 | .div, | ||
| 4238 | .equal_equal, | ||
| 4239 | .error_union, | ||
| 4240 | .greater_or_equal, | ||
| 4241 | .greater_than, | ||
| 4242 | .less_or_equal, | ||
| 4243 | .less_than, | ||
| 4244 | .merge_error_sets, | ||
| 4245 | .mod, | ||
| 4246 | .mul, | ||
| 4247 | .mul_wrap, | ||
| 4248 | .switch_range, | ||
| 4249 | .field_access, | ||
| 4250 | .sub, | ||
| 4251 | .sub_wrap, | ||
| 4252 | .slice, | ||
| 4253 | .slice_open, | ||
| 4254 | .slice_sentinel, | ||
| 4255 | .deref, | ||
| 4256 | .array_access, | ||
| 4257 | .error_value, | ||
| 4258 | .while_simple, // This variant cannot have an else expression. | ||
| 4259 | .while_cont, // This variant cannot have an else expression. | ||
| 4260 | .for_simple, // This variant cannot have an else expression. | ||
| 4261 | .if_simple, // This variant cannot have an else expression. | ||
| 4262 | => return false, | ||
| 4263 | |||
| 4264 | // Forward the question to the LHS sub-expression. | ||
| 4265 | .grouped_expression, | ||
| 4266 | .@"try", | ||
| 4267 | .@"await", | ||
| 4268 | .@"comptime", | ||
| 4269 | .@"nosuspend", | ||
| 4270 | .unwrap_optional, | ||
| 4271 | => node = node_datas[node].lhs, | ||
| 4272 | |||
| 4273 | // Forward the question to the RHS sub-expression. | ||
| 4274 | .@"catch", | ||
| 4275 | .@"orelse", | ||
| 4276 | => node = node_datas[node].rhs, | ||
| 4277 | |||
| 4278 | // True because these are exactly the expressions we need memory locations for. | ||
| 4279 | .array_init_one, | ||
| 4280 | .array_init_one_comma, | ||
| 4281 | .array_init_dot_two, | ||
| 4282 | .array_init_dot_two_comma, | ||
| 4283 | .array_init_dot, | ||
| 4284 | .array_init_dot_comma, | ||
| 4285 | .array_init, | ||
| 4286 | .array_init_comma, | ||
| 4287 | .struct_init_one, | ||
| 4288 | .struct_init_one_comma, | ||
| 4289 | .struct_init_dot_two, | ||
| 4290 | .struct_init_dot_two_comma, | ||
| 4291 | .struct_init_dot, | ||
| 4292 | .struct_init_dot_comma, | ||
| 4293 | .struct_init, | ||
| 4294 | .struct_init_comma, | ||
| 4295 | => return true, | ||
| 4296 | |||
| 4297 | // True because depending on comptime conditions, sub-expressions | ||
| 4298 | // may be the kind that need memory locations. | ||
| 4299 | .@"while", // This variant always has an else expression. | ||
| 4300 | .@"if", // This variant always has an else expression. | ||
| 4301 | .@"for", // This variant always has an else expression. | ||
| 4302 | .@"switch", | ||
| 4303 | .switch_comma, | ||
| 4304 | .call_one, | ||
| 4305 | .call_one_comma, | ||
| 4306 | .async_call_one, | ||
| 4307 | .async_call_one_comma, | ||
| 4308 | .call, | ||
| 4309 | .call_comma, | ||
| 4310 | .async_call, | ||
| 4311 | .async_call_comma, | ||
| 4312 | => return true, | ||
| 4313 | |||
| 4314 | .block_two, | ||
| 4315 | .block_two_semicolon, | ||
| 4316 | .block, | ||
| 4317 | .block_semicolon, | ||
| 4318 | => { | ||
| 4319 | const lbrace = main_tokens[node]; | ||
| 4320 | if (token_tags[lbrace - 1] == .colon) { | ||
| 4321 | // Labeled blocks may need a memory location to forward | ||
| 4322 | // to their break statements. | ||
| 4323 | return true; | ||
| 4324 | } else { | ||
| 4325 | return false; | ||
| 4326 | } | ||
| 4327 | }, | ||
| 4328 | |||
| 4329 | .builtin_call, | ||
| 4330 | .builtin_call_comma, | ||
| 4331 | .builtin_call_two, | ||
| 4332 | .builtin_call_two_comma, | ||
| 4333 | => { | ||
| 4334 | const builtin_token = main_tokens[node]; | ||
| 4335 | const builtin_name = tree.tokenSlice(builtin_token); | ||
| 4336 | // If the builtin is an invalid name, we don't cause an error here; instead | ||
| 4337 | // let it pass, and the error will be "invalid builtin function" later. | ||
| 4338 | const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false; | ||
| 4339 | return builtin_info.needs_mem_loc; | ||
| 4340 | }, | ||
| 4341 | } | ||
| 4342 | } | ||
| 4343 | } | ||
| 4344 | |||
| 4345 | /// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of | ||
| 4346 | /// result locations must call this function on their result. | ||
| 4347 | /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer. | ||
| 4348 | /// If the `ResultLoc` is `ty`, it will coerce the result to the type. | ||
| 4349 | fn rvalue( | ||
| 4350 | gz: *GenZir, | ||
| 4351 | scope: *Scope, | ||
| 4352 | rl: ResultLoc, | ||
| 4353 | result: zir.Inst.Ref, | ||
| 4354 | src_node: ast.Node.Index, | ||
| 4355 | ) InnerError!zir.Inst.Ref { | ||
| 4356 | switch (rl) { | ||
| 4357 | .none => return result, | ||
| 4358 | .discard => { | ||
| 4359 | // Emit a compile error for discarding error values. | ||
| 4360 | _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); | ||
| 4361 | return result; | ||
| 4362 | }, | ||
| 4363 | .ref => { | ||
| 4364 | // We need a pointer but we have a value. | ||
| 4365 | const tree = gz.tree(); | ||
| 4366 | const src_token = tree.firstToken(src_node); | ||
| 4367 | return gz.addUnTok(.ref, result, src_token); | ||
| 4368 | }, | ||
| 4369 | .ty => |ty_inst| { | ||
| 4370 | // Quickly eliminate some common, unnecessary type coercion. | ||
| 4371 | const as_ty = @as(u64, @enumToInt(zir.Inst.Ref.type_type)) << 32; | ||
| 4372 | const as_comptime_int = @as(u64, @enumToInt(zir.Inst.Ref.comptime_int_type)) << 32; | ||
| 4373 | const as_bool = @as(u64, @enumToInt(zir.Inst.Ref.bool_type)) << 32; | ||
| 4374 | const as_usize = @as(u64, @enumToInt(zir.Inst.Ref.usize_type)) << 32; | ||
| 4375 | const as_void = @as(u64, @enumToInt(zir.Inst.Ref.void_type)) << 32; | ||
| 4376 | switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) { | ||
| 4377 | as_ty | @enumToInt(zir.Inst.Ref.u8_type), | ||
| 4378 | as_ty | @enumToInt(zir.Inst.Ref.i8_type), | ||
| 4379 | as_ty | @enumToInt(zir.Inst.Ref.u16_type), | ||
| 4380 | as_ty | @enumToInt(zir.Inst.Ref.i16_type), | ||
| 4381 | as_ty | @enumToInt(zir.Inst.Ref.u32_type), | ||
| 4382 | as_ty | @enumToInt(zir.Inst.Ref.i32_type), | ||
| 4383 | as_ty | @enumToInt(zir.Inst.Ref.u64_type), | ||
| 4384 | as_ty | @enumToInt(zir.Inst.Ref.i64_type), | ||
| 4385 | as_ty | @enumToInt(zir.Inst.Ref.usize_type), | ||
| 4386 | as_ty | @enumToInt(zir.Inst.Ref.isize_type), | ||
| 4387 | as_ty | @enumToInt(zir.Inst.Ref.c_short_type), | ||
| 4388 | as_ty | @enumToInt(zir.Inst.Ref.c_ushort_type), | ||
| 4389 | as_ty | @enumToInt(zir.Inst.Ref.c_int_type), | ||
| 4390 | as_ty | @enumToInt(zir.Inst.Ref.c_uint_type), | ||
| 4391 | as_ty | @enumToInt(zir.Inst.Ref.c_long_type), | ||
| 4392 | as_ty | @enumToInt(zir.Inst.Ref.c_ulong_type), | ||
| 4393 | as_ty | @enumToInt(zir.Inst.Ref.c_longlong_type), | ||
| 4394 | as_ty | @enumToInt(zir.Inst.Ref.c_ulonglong_type), | ||
| 4395 | as_ty | @enumToInt(zir.Inst.Ref.c_longdouble_type), | ||
| 4396 | as_ty | @enumToInt(zir.Inst.Ref.f16_type), | ||
| 4397 | as_ty | @enumToInt(zir.Inst.Ref.f32_type), | ||
| 4398 | as_ty | @enumToInt(zir.Inst.Ref.f64_type), | ||
| 4399 | as_ty | @enumToInt(zir.Inst.Ref.f128_type), | ||
| 4400 | as_ty | @enumToInt(zir.Inst.Ref.c_void_type), | ||
| 4401 | as_ty | @enumToInt(zir.Inst.Ref.bool_type), | ||
| 4402 | as_ty | @enumToInt(zir.Inst.Ref.void_type), | ||
| 4403 | as_ty | @enumToInt(zir.Inst.Ref.type_type), | ||
| 4404 | as_ty | @enumToInt(zir.Inst.Ref.anyerror_type), | ||
| 4405 | as_ty | @enumToInt(zir.Inst.Ref.comptime_int_type), | ||
| 4406 | as_ty | @enumToInt(zir.Inst.Ref.comptime_float_type), | ||
| 4407 | as_ty | @enumToInt(zir.Inst.Ref.noreturn_type), | ||
| 4408 | as_ty | @enumToInt(zir.Inst.Ref.null_type), | ||
| 4409 | as_ty | @enumToInt(zir.Inst.Ref.undefined_type), | ||
| 4410 | as_ty | @enumToInt(zir.Inst.Ref.fn_noreturn_no_args_type), | ||
| 4411 | as_ty | @enumToInt(zir.Inst.Ref.fn_void_no_args_type), | ||
| 4412 | as_ty | @enumToInt(zir.Inst.Ref.fn_naked_noreturn_no_args_type), | ||
| 4413 | as_ty | @enumToInt(zir.Inst.Ref.fn_ccc_void_no_args_type), | ||
| 4414 | as_ty | @enumToInt(zir.Inst.Ref.single_const_pointer_to_comptime_int_type), | ||
| 4415 | as_ty | @enumToInt(zir.Inst.Ref.const_slice_u8_type), | ||
| 4416 | as_ty | @enumToInt(zir.Inst.Ref.enum_literal_type), | ||
| 4417 | as_comptime_int | @enumToInt(zir.Inst.Ref.zero), | ||
| 4418 | as_comptime_int | @enumToInt(zir.Inst.Ref.one), | ||
| 4419 | as_bool | @enumToInt(zir.Inst.Ref.bool_true), | ||
| 4420 | as_bool | @enumToInt(zir.Inst.Ref.bool_false), | ||
| 4421 | as_usize | @enumToInt(zir.Inst.Ref.zero_usize), | ||
| 4422 | as_usize | @enumToInt(zir.Inst.Ref.one_usize), | ||
| 4423 | as_void | @enumToInt(zir.Inst.Ref.void_value), | ||
| 4424 | => return result, // type of result is already correct | ||
| 4425 | |||
| 4426 | // Need an explicit type coercion instruction. | ||
| 4427 | else => return gz.addPlNode(.as_node, src_node, zir.Inst.As{ | ||
| 4428 | .dest_type = ty_inst, | ||
| 4429 | .operand = result, | ||
| 4430 | }), | ||
| 4431 | } | ||
| 4432 | }, | ||
| 4433 | .ptr => |ptr_inst| { | ||
| 4434 | _ = try gz.addPlNode(.store_node, src_node, zir.Inst.Bin{ | ||
| 4435 | .lhs = ptr_inst, | ||
| 4436 | .rhs = result, | ||
| 4437 | }); | ||
| 4438 | return result; | ||
| 4439 | }, | ||
| 4440 | .inferred_ptr => |alloc| { | ||
| 4441 | _ = try gz.addBin(.store_to_inferred_ptr, alloc, result); | ||
| 4442 | return result; | ||
| 4443 | }, | ||
| 4444 | .block_ptr => |block_scope| { | ||
| 4445 | block_scope.rvalue_rl_count += 1; | ||
| 4446 | _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result); | ||
| 4447 | return result; | ||
| 4448 | }, | ||
| 4449 | } | ||
| 4450 | } | ||
src/Compilation.zig+22-17| ... | @@ -259,7 +259,7 @@ pub const CObject = struct { | ... | @@ -259,7 +259,7 @@ pub const CObject = struct { |
| 259 | /// To support incremental compilation, errors are stored in various places | 259 | /// To support incremental compilation, errors are stored in various places |
| 260 | /// so that they can be created and destroyed appropriately. This structure | 260 | /// so that they can be created and destroyed appropriately. This structure |
| 261 | /// is used to collect all the errors from the various places into one | 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 | /// and freed all at once. | 263 | /// and freed all at once. |
| 264 | pub const AllErrors = struct { | 264 | pub const AllErrors = struct { |
| 265 | arena: std.heap.ArenaAllocator.State, | 265 | arena: std.heap.ArenaAllocator.State, |
| ... | @@ -267,11 +267,11 @@ pub const AllErrors = struct { | ... | @@ -267,11 +267,11 @@ pub const AllErrors = struct { |
| 267 | 267 | ||
| 268 | pub const Message = union(enum) { | 268 | pub const Message = union(enum) { |
| 269 | src: struct { | 269 | src: struct { |
| 270 | src_path: []const u8, | ||
| 271 | line: usize, | ||
| 272 | column: usize, | ||
| 273 | byte_offset: usize, | ||
| 274 | msg: []const u8, | 270 | msg: []const u8, |
| 271 | src_path: []const u8, | ||
| 272 | line: u32, | ||
| 273 | column: u32, | ||
| 274 | byte_offset: u32, | ||
| 275 | notes: []Message = &.{}, | 275 | notes: []Message = &.{}, |
| 276 | }, | 276 | }, |
| 277 | plain: struct { | 277 | plain: struct { |
| ... | @@ -316,29 +316,31 @@ pub const AllErrors = struct { | ... | @@ -316,29 +316,31 @@ pub const AllErrors = struct { |
| 316 | const notes = try arena.allocator.alloc(Message, module_err_msg.notes.len); | 316 | const notes = try arena.allocator.alloc(Message, module_err_msg.notes.len); |
| 317 | for (notes) |*note, i| { | 317 | for (notes) |*note, i| { |
| 318 | const module_note = module_err_msg.notes[i]; | 318 | const module_note = module_err_msg.notes[i]; |
| 319 | const source = try module_note.src_loc.file_scope.getSource(module); | 319 | const source = try module_note.src_loc.fileScope().getSource(module); |
| 320 | const loc = std.zig.findLineColumn(source, module_note.src_loc.byte_offset); | 320 | const byte_offset = try module_note.src_loc.byteOffset(); |
| 321 | const sub_file_path = module_note.src_loc.file_scope.sub_file_path; | 321 | const loc = std.zig.findLineColumn(source, byte_offset); |
| 322 | const sub_file_path = module_note.src_loc.fileScope().sub_file_path; | ||
| 322 | note.* = .{ | 323 | note.* = .{ |
| 323 | .src = .{ | 324 | .src = .{ |
| 324 | .src_path = try arena.allocator.dupe(u8, sub_file_path), | 325 | .src_path = try arena.allocator.dupe(u8, sub_file_path), |
| 325 | .msg = try arena.allocator.dupe(u8, module_note.msg), | 326 | .msg = try arena.allocator.dupe(u8, module_note.msg), |
| 326 | .byte_offset = module_note.src_loc.byte_offset, | 327 | .byte_offset = byte_offset, |
| 327 | .line = loc.line, | 328 | .line = @intCast(u32, loc.line), |
| 328 | .column = loc.column, | 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 source = try module_err_msg.src_loc.fileScope().getSource(module); |
| 333 | const loc = std.zig.findLineColumn(source, module_err_msg.src_loc.byte_offset); | 334 | const byte_offset = try module_err_msg.src_loc.byteOffset(); |
| 334 | const sub_file_path = module_err_msg.src_loc.file_scope.sub_file_path; | 335 | const loc = std.zig.findLineColumn(source, byte_offset); |
| 336 | const sub_file_path = module_err_msg.src_loc.fileScope().sub_file_path; | ||
| 335 | try errors.append(.{ | 337 | try errors.append(.{ |
| 336 | .src = .{ | 338 | .src = .{ |
| 337 | .src_path = try arena.allocator.dupe(u8, sub_file_path), | 339 | .src_path = try arena.allocator.dupe(u8, sub_file_path), |
| 338 | .msg = try arena.allocator.dupe(u8, module_err_msg.msg), | 340 | .msg = try arena.allocator.dupe(u8, module_err_msg.msg), |
| 339 | .byte_offset = module_err_msg.src_loc.byte_offset, | 341 | .byte_offset = byte_offset, |
| 340 | .line = loc.line, | 342 | .line = @intCast(u32, loc.line), |
| 341 | .column = loc.column, | 343 | .column = @intCast(u32, loc.column), |
| 342 | .notes = notes, | 344 | .notes = notes, |
| 343 | }, | 345 | }, |
| 344 | }); | 346 | }); |
| ... | @@ -939,6 +941,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -939,6 +941,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 939 | }; | 941 | }; |
| 940 | 942 | ||
| 941 | const module = try arena.create(Module); | 943 | const module = try arena.create(Module); |
| 944 | errdefer module.deinit(); | ||
| 942 | module.* = .{ | 945 | module.* = .{ |
| 943 | .gpa = gpa, | 946 | .gpa = gpa, |
| 944 | .comp = comp, | 947 | .comp = comp, |
| ... | @@ -946,7 +949,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -946,7 +949,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 946 | .root_scope = root_scope, | 949 | .root_scope = root_scope, |
| 947 | .zig_cache_artifact_directory = zig_cache_artifact_directory, | 950 | .zig_cache_artifact_directory = zig_cache_artifact_directory, |
| 948 | .emit_h = options.emit_h, | 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 | break :blk module; | 955 | break :blk module; |
| 951 | } else blk: { | 956 | } else blk: { |
| 952 | if (options.emit_h != null) return error.NoZigModuleForCHeader; | 957 | if (options.emit_h != null) return error.NoZigModuleForCHeader; |
src/Module.zig+2171-1939| ... | @@ -1,31 +1,32 @@ | ... | @@ -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 | const std = @import("std"); | 5 | const std = @import("std"); |
| 3 | const Compilation = @import("Compilation.zig"); | ||
| 4 | const mem = std.mem; | 6 | const mem = std.mem; |
| 5 | const Allocator = std.mem.Allocator; | 7 | const Allocator = std.mem.Allocator; |
| 6 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | 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 | const assert = std.debug.assert; | 9 | const assert = std.debug.assert; |
| 11 | const log = std.log.scoped(.module); | 10 | const log = std.log.scoped(.module); |
| 12 | const BigIntConst = std.math.big.int.Const; | 11 | const BigIntConst = std.math.big.int.Const; |
| 13 | const BigIntMutable = std.math.big.int.Mutable; | 12 | const BigIntMutable = std.math.big.int.Mutable; |
| 14 | const Target = std.Target; | 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 | const Package = @import("Package.zig"); | 21 | const Package = @import("Package.zig"); |
| 16 | const link = @import("link.zig"); | 22 | const link = @import("link.zig"); |
| 17 | const ir = @import("ir.zig"); | 23 | const ir = @import("ir.zig"); |
| 18 | const zir = @import("zir.zig"); | 24 | const zir = @import("zir.zig"); |
| 19 | const Inst = ir.Inst; | ||
| 20 | const Body = ir.Body; | ||
| 21 | const ast = std.zig.ast; | ||
| 22 | const trace = @import("tracy.zig").trace; | 25 | const trace = @import("tracy.zig").trace; |
| 23 | const astgen = @import("astgen.zig"); | 26 | const AstGen = @import("AstGen.zig"); |
| 24 | const zir_sema = @import("zir_sema.zig"); | 27 | const Sema = @import("Sema.zig"); |
| 25 | const target_util = @import("target.zig"); | 28 | const target_util = @import("target.zig"); |
| 26 | 29 | ||
| 27 | const default_eval_branch_quota = 1000; | ||
| 28 | |||
| 29 | /// General-purpose allocator. Used for both temporary and long-term storage. | 30 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 30 | gpa: *Allocator, | 31 | gpa: *Allocator, |
| 31 | comp: *Compilation, | 32 | comp: *Compilation, |
| ... | @@ -77,7 +78,12 @@ next_anon_name_index: usize = 0, | ... | @@ -77,7 +78,12 @@ next_anon_name_index: usize = 0, |
| 77 | deletion_set: ArrayListUnmanaged(*Decl) = .{}, | 78 | deletion_set: ArrayListUnmanaged(*Decl) = .{}, |
| 78 | 79 | ||
| 79 | /// Error tags and their values, tag names are duped with mod.gpa. | 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 | /// Keys are fully qualified paths | 88 | /// Keys are fully qualified paths |
| 83 | import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, | 89 | import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, |
| ... | @@ -102,12 +108,13 @@ stage1_flags: packed struct { | ... | @@ -102,12 +108,13 @@ stage1_flags: packed struct { |
| 102 | 108 | ||
| 103 | emit_h: ?Compilation.EmitLoc, | 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 | pub const Export = struct { | 115 | pub const Export = struct { |
| 108 | options: std.builtin.ExportOptions, | 116 | options: std.builtin.ExportOptions, |
| 109 | /// Byte offset into the file that contains the export directive. | 117 | src: LazySrcLoc, |
| 110 | src: usize, | ||
| 111 | /// Represents the position of the export, if any, in the output file. | 118 | /// Represents the position of the export, if any, in the output file. |
| 112 | link: link.File.Export, | 119 | link: link.File.Export, |
| 113 | /// The Decl that performs the export. Note that this is *not* the Decl being exported. | 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,11 +139,12 @@ pub const DeclPlusEmitH = struct { |
| 132 | }; | 139 | }; |
| 133 | 140 | ||
| 134 | pub const Decl = struct { | 141 | pub const Decl = struct { |
| 135 | /// This name is relative to the containing namespace of the decl. It uses a null-termination | 142 | /// This name is relative to the containing namespace of the decl. It uses |
| 136 | /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed | 143 | /// null-termination to save bytes, since there can be a lot of decls in a |
| 137 | /// in symbol names, because executable file formats use null-terminated strings for symbol names. | 144 | /// compilation. The null byte is not allowed in symbol names, because |
| 138 | /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for | 145 | /// executable file formats use null-terminated strings for symbol names. |
| 139 | /// mapping them to an address in the output file. | 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 | /// Memory owned by this decl, using Module's allocator. | 148 | /// Memory owned by this decl, using Module's allocator. |
| 141 | name: [*:0]const u8, | 149 | name: [*:0]const u8, |
| 142 | /// The direct parent container of the Decl. | 150 | /// The direct parent container of the Decl. |
| ... | @@ -219,73 +227,102 @@ pub const Decl = struct { | ... | @@ -219,73 +227,102 @@ pub const Decl = struct { |
| 219 | /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself` | 227 | /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself` |
| 220 | pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false); | 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 | const gpa = module.gpa; | 231 | const gpa = module.gpa; |
| 224 | gpa.free(mem.spanZ(self.name)); | 232 | gpa.free(mem.spanZ(decl.name)); |
| 225 | if (self.typedValueManaged()) |tvm| { | 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 | tvm.deinit(gpa); | 238 | tvm.deinit(gpa); |
| 227 | } | 239 | } |
| 228 | self.dependants.deinit(gpa); | 240 | decl.dependants.deinit(gpa); |
| 229 | self.dependencies.deinit(gpa); | 241 | decl.dependencies.deinit(gpa); |
| 230 | if (module.emit_h != null) { | 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 | decl_plus_emit_h.emit_h.fwd_decl.deinit(gpa); | 244 | decl_plus_emit_h.emit_h.fwd_decl.deinit(gpa); |
| 233 | gpa.destroy(decl_plus_emit_h); | 245 | gpa.destroy(decl_plus_emit_h); |
| 234 | } else { | 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 | return .{ | 268 | return .{ |
| 241 | .byte_offset = self.src(), | 269 | .container = .{ .decl = decl }, |
| 242 | .file_scope = self.getFileScope(), | 270 | .lazy = .{ .node_offset = 0 }, |
| 243 | }; | 271 | }; |
| 244 | } | 272 | } |
| 245 | 273 | ||
| 246 | pub fn src(self: Decl) usize { | 274 | pub fn srcNode(decl: Decl) u32 { |
| 247 | const tree = &self.container.file_scope.tree; | 275 | const tree = &decl.container.file_scope.tree; |
| 248 | const decl_node = tree.rootDecls()[self.src_index]; | 276 | return tree.rootDecls()[decl.src_index]; |
| 249 | return tree.tokens.items(.start)[tree.firstToken(decl_node)]; | 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 { | 289 | pub fn fullyQualifiedNameHash(decl: Decl) Scope.NameHash { |
| 253 | return self.container.fullyQualifiedNameHash(mem.spanZ(self.name)); | 290 | return decl.container.fullyQualifiedNameHash(mem.spanZ(decl.name)); |
| 254 | } | 291 | } |
| 255 | 292 | ||
| 256 | pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue { | 293 | pub fn typedValue(decl: *Decl) error{AnalysisFail}!TypedValue { |
| 257 | const tvm = self.typedValueManaged() orelse return error.AnalysisFail; | 294 | const tvm = decl.typedValueManaged() orelse return error.AnalysisFail; |
| 258 | return tvm.typed_value; | 295 | return tvm.typed_value; |
| 259 | } | 296 | } |
| 260 | 297 | ||
| 261 | pub fn value(self: *Decl) error{AnalysisFail}!Value { | 298 | pub fn value(decl: *Decl) error{AnalysisFail}!Value { |
| 262 | return (try self.typedValue()).val; | 299 | return (try decl.typedValue()).val; |
| 263 | } | 300 | } |
| 264 | 301 | ||
| 265 | pub fn dump(self: *Decl) void { | 302 | pub fn dump(decl: *Decl) void { |
| 266 | const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src); | 303 | const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src); |
| 267 | std.debug.print("{s}:{d}:{d} name={s} status={s}", .{ | 304 | std.debug.print("{s}:{d}:{d} name={s} status={s}", .{ |
| 268 | self.scope.sub_file_path, | 305 | decl.scope.sub_file_path, |
| 269 | loc.line + 1, | 306 | loc.line + 1, |
| 270 | loc.column + 1, | 307 | loc.column + 1, |
| 271 | mem.spanZ(self.name), | 308 | mem.spanZ(decl.name), |
| 272 | @tagName(self.analysis), | 309 | @tagName(decl.analysis), |
| 273 | }); | 310 | }); |
| 274 | if (self.typedValueManaged()) |tvm| { | 311 | if (decl.typedValueManaged()) |tvm| { |
| 275 | std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val }); | 312 | std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val }); |
| 276 | } | 313 | } |
| 277 | std.debug.print("\n", .{}); | 314 | std.debug.print("\n", .{}); |
| 278 | } | 315 | } |
| 279 | 316 | ||
| 280 | pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed { | 317 | pub fn typedValueManaged(decl: *Decl) ?*TypedValue.Managed { |
| 281 | switch (self.typed_value) { | 318 | switch (decl.typed_value) { |
| 282 | .most_recent => |*x| return x, | 319 | .most_recent => |*x| return x, |
| 283 | .never_succeeded => return null, | 320 | .never_succeeded => return null, |
| 284 | } | 321 | } |
| 285 | } | 322 | } |
| 286 | 323 | ||
| 287 | pub fn getFileScope(self: Decl) *Scope.File { | 324 | pub fn getFileScope(decl: Decl) *Scope.File { |
| 288 | return self.container.file_scope; | 325 | return decl.container.file_scope; |
| 289 | } | 326 | } |
| 290 | 327 | ||
| 291 | pub fn getEmitH(decl: *Decl, module: *Module) *EmitH { | 328 | pub fn getEmitH(decl: *Decl, module: *Module) *EmitH { |
| ... | @@ -294,21 +331,51 @@ pub const Decl = struct { | ... | @@ -294,21 +331,51 @@ pub const Decl = struct { |
| 294 | return &decl_plus_emit_h.emit_h; | 331 | return &decl_plus_emit_h.emit_h; |
| 295 | } | 332 | } |
| 296 | 333 | ||
| 297 | fn removeDependant(self: *Decl, other: *Decl) void { | 334 | fn removeDependant(decl: *Decl, other: *Decl) void { |
| 298 | self.dependants.removeAssertDiscard(other); | 335 | decl.dependants.removeAssertDiscard(other); |
| 299 | } | 336 | } |
| 300 | 337 | ||
| 301 | fn removeDependency(self: *Decl, other: *Decl) void { | 338 | fn removeDependency(decl: *Decl, other: *Decl) void { |
| 302 | self.dependencies.removeAssertDiscard(other); | 339 | decl.dependencies.removeAssertDiscard(other); |
| 303 | } | 340 | } |
| 304 | }; | 341 | }; |
| 305 | 342 | ||
| 306 | /// This state is attached to every Decl when Module emit_h is non-null. | 343 | /// This state is attached to every Decl when Module emit_h is non-null. |
| 307 | pub const EmitH = struct { | 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 | /// Represents the data that a struct declaration provides. | ||
| 360 | pub const Struct = struct { | ||
| 361 | owner_decl: *Decl, | ||
| 362 | /// Set of field names in declaration order. | ||
| 363 | fields: std.StringArrayHashMapUnmanaged(Field), | ||
| 364 | /// Represents the declarations inside this struct. | ||
| 365 | container: Scope.Container, | ||
| 366 | |||
| 367 | /// Offset from Decl node index, points to the struct AST node. | ||
| 368 | node_offset: i32, | ||
| 369 | |||
| 370 | pub const Field = struct { | ||
| 371 | ty: Type, | ||
| 372 | abi_align: Value, | ||
| 373 | /// Uses `unreachable_value` to indicate no default. | ||
| 374 | default_val: Value, | ||
| 375 | }; | ||
| 376 | }; | ||
| 377 | |||
| 378 | /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. | ||
| 312 | /// Extern functions do not have this data structure; they are represented by | 379 | /// Extern functions do not have this data structure; they are represented by |
| 313 | /// the `Decl` only, with a `Value` tag of `extern_fn`. | 380 | /// the `Decl` only, with a `Value` tag of `extern_fn`. |
| 314 | pub const Fn = struct { | 381 | pub const Fn = struct { |
| ... | @@ -316,9 +383,15 @@ pub const Fn = struct { | ... | @@ -316,9 +383,15 @@ pub const Fn = struct { |
| 316 | /// Contains un-analyzed ZIR instructions generated from Zig source AST. | 383 | /// Contains un-analyzed ZIR instructions generated from Zig source AST. |
| 317 | /// Even after we finish analysis, the ZIR is kept in memory, so that | 384 | /// Even after we finish analysis, the ZIR is kept in memory, so that |
| 318 | /// comptime and inline function calls can happen. | 385 | /// comptime and inline function calls can happen. |
| 319 | zir: zir.Body, | 386 | /// Parameter names are stored here so that they may be referenced for debug info, |
| 387 | /// without having source code bytes loaded into memory. | ||
| 388 | /// The number of parameters is determined by referring to the type. | ||
| 389 | /// The first N elements of `extra` are indexes into `string_bytes` to | ||
| 390 | /// a null-terminated string. | ||
| 391 | /// This memory is managed with gpa, must be freed when the function is freed. | ||
| 392 | zir: zir.Code, | ||
| 320 | /// undefined unless analysis state is `success`. | 393 | /// undefined unless analysis state is `success`. |
| 321 | body: Body, | 394 | body: ir.Body, |
| 322 | state: Analysis, | 395 | state: Analysis, |
| 323 | 396 | ||
| 324 | pub const Analysis = enum { | 397 | pub const Analysis = enum { |
| ... | @@ -336,8 +409,12 @@ pub const Fn = struct { | ... | @@ -336,8 +409,12 @@ pub const Fn = struct { |
| 336 | }; | 409 | }; |
| 337 | 410 | ||
| 338 | /// For debugging purposes. | 411 | /// For debugging purposes. |
| 339 | pub fn dump(self: *Fn, mod: Module) void { | 412 | pub fn dump(func: *Fn, mod: Module) void { |
| 340 | zir.dumpFn(mod, self); | 413 | ir.dumpFn(mod, func); |
| 414 | } | ||
| 415 | |||
| 416 | pub fn deinit(func: *Fn, gpa: *Allocator) void { | ||
| 417 | func.zir.deinit(gpa); | ||
| 341 | } | 418 | } |
| 342 | }; | 419 | }; |
| 343 | 420 | ||
| ... | @@ -364,103 +441,93 @@ pub const Scope = struct { | ... | @@ -364,103 +441,93 @@ pub const Scope = struct { |
| 364 | } | 441 | } |
| 365 | 442 | ||
| 366 | /// Returns the arena Allocator associated with the Decl of the Scope. | 443 | /// Returns the arena Allocator associated with the Decl of the Scope. |
| 367 | pub fn arena(self: *Scope) *Allocator { | 444 | pub fn arena(scope: *Scope) *Allocator { |
| 368 | switch (self.tag) { | 445 | switch (scope.tag) { |
| 369 | .block => return self.cast(Block).?.arena, | 446 | .block => return scope.cast(Block).?.sema.arena, |
| 370 | .gen_zir => return self.cast(GenZIR).?.arena, | 447 | .gen_zir => return scope.cast(GenZir).?.astgen.arena, |
| 371 | .local_val => return self.cast(LocalVal).?.gen_zir.arena, | 448 | .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.arena, |
| 372 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena, | 449 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.arena, |
| 373 | .gen_suspend => return self.cast(GenZIR).?.arena, | ||
| 374 | .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.arena, | ||
| 375 | .file => unreachable, | 450 | .file => unreachable, |
| 376 | .container => unreachable, | 451 | .container => unreachable, |
| 452 | .decl_ref => unreachable, | ||
| 377 | } | 453 | } |
| 378 | } | 454 | } |
| 379 | 455 | ||
| 380 | pub fn isComptime(self: *Scope) bool { | 456 | pub fn ownerDecl(scope: *Scope) ?*Decl { |
| 381 | return self.getGenZIR().force_comptime; | 457 | return switch (scope.tag) { |
| 382 | } | 458 | .block => scope.cast(Block).?.sema.owner_decl, |
| 383 | 459 | .gen_zir => scope.cast(GenZir).?.astgen.decl, | |
| 384 | pub fn ownerDecl(self: *Scope) ?*Decl { | 460 | .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl, |
| 385 | return switch (self.tag) { | 461 | .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl, |
| 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, | ||
| 392 | .file => null, | 462 | .file => null, |
| 393 | .container => null, | 463 | .container => null, |
| 464 | .decl_ref => scope.cast(DeclRef).?.decl, | ||
| 394 | }; | 465 | }; |
| 395 | } | 466 | } |
| 396 | 467 | ||
| 397 | pub fn srcDecl(self: *Scope) ?*Decl { | 468 | pub fn srcDecl(scope: *Scope) ?*Decl { |
| 398 | return switch (self.tag) { | 469 | return switch (scope.tag) { |
| 399 | .block => self.cast(Block).?.src_decl, | 470 | .block => scope.cast(Block).?.src_decl, |
| 400 | .gen_zir => self.cast(GenZIR).?.decl, | 471 | .gen_zir => scope.cast(GenZir).?.astgen.decl, |
| 401 | .local_val => self.cast(LocalVal).?.gen_zir.decl, | 472 | .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl, |
| 402 | .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, | 473 | .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl, |
| 403 | .gen_suspend => return self.cast(GenZIR).?.decl, | ||
| 404 | .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl, | ||
| 405 | .file => null, | 474 | .file => null, |
| 406 | .container => null, | 475 | .container => null, |
| 476 | .decl_ref => scope.cast(DeclRef).?.decl, | ||
| 407 | }; | 477 | }; |
| 408 | } | 478 | } |
| 409 | 479 | ||
| 410 | /// Asserts the scope has a parent which is a Container and returns it. | 480 | /// Asserts the scope has a parent which is a Container and returns it. |
| 411 | pub fn namespace(self: *Scope) *Container { | 481 | pub fn namespace(scope: *Scope) *Container { |
| 412 | switch (self.tag) { | 482 | switch (scope.tag) { |
| 413 | .block => return self.cast(Block).?.owner_decl.container, | 483 | .block => return scope.cast(Block).?.sema.owner_decl.container, |
| 414 | .gen_zir => return self.cast(GenZIR).?.decl.container, | 484 | .gen_zir => return scope.cast(GenZir).?.astgen.decl.container, |
| 415 | .local_val => return self.cast(LocalVal).?.gen_zir.decl.container, | 485 | .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.decl.container, |
| 416 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container, | 486 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.decl.container, |
| 417 | .file => return &self.cast(File).?.root_container, | 487 | .file => return &scope.cast(File).?.root_container, |
| 418 | .container => return self.cast(Container).?, | 488 | .container => return scope.cast(Container).?, |
| 419 | .gen_suspend => return self.cast(GenZIR).?.decl.container, | 489 | .decl_ref => return scope.cast(DeclRef).?.decl.container, |
| 420 | .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl.container, | ||
| 421 | } | 490 | } |
| 422 | } | 491 | } |
| 423 | 492 | ||
| 424 | /// Must generate unique bytes with no collisions with other decls. | 493 | /// Must generate unique bytes with no collisions with other decls. |
| 425 | /// The point of hashing here is only to limit the number of bytes of | 494 | /// The point of hashing here is only to limit the number of bytes of |
| 426 | /// the unique identifier to a fixed size (16 bytes). | 495 | /// the unique identifier to a fixed size (16 bytes). |
| 427 | pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash { | 496 | pub fn fullyQualifiedNameHash(scope: *Scope, name: []const u8) NameHash { |
| 428 | switch (self.tag) { | 497 | switch (scope.tag) { |
| 429 | .block => unreachable, | 498 | .block => unreachable, |
| 430 | .gen_zir => unreachable, | 499 | .gen_zir => unreachable, |
| 431 | .local_val => unreachable, | 500 | .local_val => unreachable, |
| 432 | .local_ptr => unreachable, | 501 | .local_ptr => unreachable, |
| 433 | .gen_suspend => unreachable, | ||
| 434 | .gen_nosuspend => unreachable, | ||
| 435 | .file => unreachable, | 502 | .file => unreachable, |
| 436 | .container => return self.cast(Container).?.fullyQualifiedNameHash(name), | 503 | .container => return scope.cast(Container).?.fullyQualifiedNameHash(name), |
| 504 | .decl_ref => unreachable, | ||
| 437 | } | 505 | } |
| 438 | } | 506 | } |
| 439 | 507 | ||
| 440 | /// Asserts the scope is a child of a File and has an AST tree and returns the tree. | 508 | /// 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 { | 509 | pub fn tree(scope: *Scope) *const ast.Tree { |
| 442 | switch (self.tag) { | 510 | switch (scope.tag) { |
| 443 | .file => return &self.cast(File).?.tree, | 511 | .file => return &scope.cast(File).?.tree, |
| 444 | .block => return &self.cast(Block).?.src_decl.container.file_scope.tree, | 512 | .block => return &scope.cast(Block).?.src_decl.container.file_scope.tree, |
| 445 | .gen_zir => return &self.cast(GenZIR).?.decl.container.file_scope.tree, | 513 | .gen_zir => return scope.cast(GenZir).?.tree(), |
| 446 | .local_val => return &self.cast(LocalVal).?.gen_zir.decl.container.file_scope.tree, | 514 | .local_val => return &scope.cast(LocalVal).?.gen_zir.astgen.decl.container.file_scope.tree, |
| 447 | .local_ptr => return &self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree, | 515 | .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.astgen.decl.container.file_scope.tree, |
| 448 | .container => return &self.cast(Container).?.file_scope.tree, | 516 | .container => return &scope.cast(Container).?.file_scope.tree, |
| 449 | .gen_suspend => return &self.cast(GenZIR).?.decl.container.file_scope.tree, | 517 | .decl_ref => return &scope.cast(DeclRef).?.decl.container.file_scope.tree, |
| 450 | .gen_nosuspend => return &self.cast(Nosuspend).?.gen_zir.decl.container.file_scope.tree, | ||
| 451 | } | 518 | } |
| 452 | } | 519 | } |
| 453 | 520 | ||
| 454 | /// Asserts the scope is a child of a `GenZIR` and returns it. | 521 | /// Asserts the scope is a child of a `GenZir` and returns it. |
| 455 | pub fn getGenZIR(self: *Scope) *GenZIR { | 522 | pub fn getGenZir(scope: *Scope) *GenZir { |
| 456 | return switch (self.tag) { | 523 | return switch (scope.tag) { |
| 457 | .block => unreachable, | 524 | .block => unreachable, |
| 458 | .gen_zir, .gen_suspend => self.cast(GenZIR).?, | 525 | .gen_zir => scope.cast(GenZir).?, |
| 459 | .local_val => return self.cast(LocalVal).?.gen_zir, | 526 | .local_val => return scope.cast(LocalVal).?.gen_zir, |
| 460 | .local_ptr => return self.cast(LocalPtr).?.gen_zir, | 527 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir, |
| 461 | .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir, | ||
| 462 | .file => unreachable, | 528 | .file => unreachable, |
| 463 | .container => unreachable, | 529 | .container => unreachable, |
| 530 | .decl_ref => unreachable, | ||
| 464 | }; | 531 | }; |
| 465 | } | 532 | } |
| 466 | 533 | ||
| ... | @@ -474,8 +541,7 @@ pub const Scope = struct { | ... | @@ -474,8 +541,7 @@ pub const Scope = struct { |
| 474 | .gen_zir => unreachable, | 541 | .gen_zir => unreachable, |
| 475 | .local_val => unreachable, | 542 | .local_val => unreachable, |
| 476 | .local_ptr => unreachable, | 543 | .local_ptr => unreachable, |
| 477 | .gen_suspend => unreachable, | 544 | .decl_ref => unreachable, |
| 478 | .gen_nosuspend => unreachable, | ||
| 479 | } | 545 | } |
| 480 | } | 546 | } |
| 481 | 547 | ||
| ... | @@ -487,8 +553,7 @@ pub const Scope = struct { | ... | @@ -487,8 +553,7 @@ pub const Scope = struct { |
| 487 | .local_val => unreachable, | 553 | .local_val => unreachable, |
| 488 | .local_ptr => unreachable, | 554 | .local_ptr => unreachable, |
| 489 | .block => unreachable, | 555 | .block => unreachable, |
| 490 | .gen_suspend => unreachable, | 556 | .decl_ref => unreachable, |
| 491 | .gen_nosuspend => unreachable, | ||
| 492 | } | 557 | } |
| 493 | } | 558 | } |
| 494 | 559 | ||
| ... | @@ -499,40 +564,11 @@ pub const Scope = struct { | ... | @@ -499,40 +564,11 @@ pub const Scope = struct { |
| 499 | cur = switch (cur.tag) { | 564 | cur = switch (cur.tag) { |
| 500 | .container => return @fieldParentPtr(Container, "base", cur).file_scope, | 565 | .container => return @fieldParentPtr(Container, "base", cur).file_scope, |
| 501 | .file => return @fieldParentPtr(File, "base", cur), | 566 | .file => return @fieldParentPtr(File, "base", cur), |
| 502 | .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent, | 567 | .gen_zir => @fieldParentPtr(GenZir, "base", cur).parent, |
| 503 | .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, | 568 | .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, |
| 504 | .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, | 569 | .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, |
| 505 | .block => return @fieldParentPtr(Block, "base", cur).src_decl.container.file_scope, | 570 | .block => return @fieldParentPtr(Block, "base", cur).src_decl.container.file_scope, |
| 506 | .gen_suspend => @fieldParentPtr(GenZIR, "base", cur).parent, | 571 | .decl_ref => return @fieldParentPtr(DeclRef, "base", cur).decl.container.file_scope, |
| 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, | ||
| 536 | }; | 572 | }; |
| 537 | } | 573 | } |
| 538 | } | 574 | } |
| ... | @@ -554,8 +590,10 @@ pub const Scope = struct { | ... | @@ -554,8 +590,10 @@ pub const Scope = struct { |
| 554 | gen_zir, | 590 | gen_zir, |
| 555 | local_val, | 591 | local_val, |
| 556 | local_ptr, | 592 | local_ptr, |
| 557 | gen_suspend, | 593 | /// Used for simple error reporting. Only contains a reference to a |
| 558 | gen_nosuspend, | 594 | /// `Decl` for use with `srcDecl` and `ownerDecl`. |
| 595 | /// Has no parents or children. | ||
| 596 | decl_ref, | ||
| 559 | }; | 597 | }; |
| 560 | 598 | ||
| 561 | pub const Container = struct { | 599 | pub const Container = struct { |
| ... | @@ -568,19 +606,19 @@ pub const Scope = struct { | ... | @@ -568,19 +606,19 @@ pub const Scope = struct { |
| 568 | decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, | 606 | decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, |
| 569 | ty: Type, | 607 | ty: Type, |
| 570 | 608 | ||
| 571 | pub fn deinit(self: *Container, gpa: *Allocator) void { | 609 | pub fn deinit(cont: *Container, gpa: *Allocator) void { |
| 572 | self.decls.deinit(gpa); | 610 | cont.decls.deinit(gpa); |
| 573 | // TODO either Container of File should have an arena for sub_file_path and ty | 611 | // TODO either Container of File should have an arena for sub_file_path and ty |
| 574 | gpa.destroy(self.ty.castTag(.empty_struct).?); | 612 | gpa.destroy(cont.ty.castTag(.empty_struct).?); |
| 575 | gpa.free(self.file_scope.sub_file_path); | 613 | gpa.free(cont.file_scope.sub_file_path); |
| 576 | self.* = undefined; | 614 | cont.* = undefined; |
| 577 | } | 615 | } |
| 578 | 616 | ||
| 579 | pub fn removeDecl(self: *Container, child: *Decl) void { | 617 | pub fn removeDecl(cont: *Container, child: *Decl) void { |
| 580 | _ = self.decls.swapRemove(child); | 618 | _ = cont.decls.swapRemove(child); |
| 581 | } | 619 | } |
| 582 | 620 | ||
| 583 | pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash { | 621 | pub fn fullyQualifiedNameHash(cont: *Container, name: []const u8) NameHash { |
| 584 | // TODO container scope qualified names. | 622 | // TODO container scope qualified names. |
| 585 | return std.zig.hashSrc(name); | 623 | return std.zig.hashSrc(name); |
| 586 | } | 624 | } |
| ... | @@ -610,55 +648,55 @@ pub const Scope = struct { | ... | @@ -610,55 +648,55 @@ pub const Scope = struct { |
| 610 | 648 | ||
| 611 | root_container: Container, | 649 | root_container: Container, |
| 612 | 650 | ||
| 613 | pub fn unload(self: *File, gpa: *Allocator) void { | 651 | pub fn unload(file: *File, gpa: *Allocator) void { |
| 614 | switch (self.status) { | 652 | switch (file.status) { |
| 615 | .never_loaded, | 653 | .never_loaded, |
| 616 | .unloaded_parse_failure, | 654 | .unloaded_parse_failure, |
| 617 | .unloaded_success, | 655 | .unloaded_success, |
| 618 | => {}, | 656 | => {}, |
| 619 | 657 | ||
| 620 | .loaded_success => { | 658 | .loaded_success => { |
| 621 | self.tree.deinit(gpa); | 659 | file.tree.deinit(gpa); |
| 622 | self.status = .unloaded_success; | 660 | file.status = .unloaded_success; |
| 623 | }, | 661 | }, |
| 624 | } | 662 | } |
| 625 | switch (self.source) { | 663 | switch (file.source) { |
| 626 | .bytes => |bytes| { | 664 | .bytes => |bytes| { |
| 627 | gpa.free(bytes); | 665 | gpa.free(bytes); |
| 628 | self.source = .{ .unloaded = {} }; | 666 | file.source = .{ .unloaded = {} }; |
| 629 | }, | 667 | }, |
| 630 | .unloaded => {}, | 668 | .unloaded => {}, |
| 631 | } | 669 | } |
| 632 | } | 670 | } |
| 633 | 671 | ||
| 634 | pub fn deinit(self: *File, gpa: *Allocator) void { | 672 | pub fn deinit(file: *File, gpa: *Allocator) void { |
| 635 | self.root_container.deinit(gpa); | 673 | file.root_container.deinit(gpa); |
| 636 | self.unload(gpa); | 674 | file.unload(gpa); |
| 637 | self.* = undefined; | 675 | file.* = undefined; |
| 638 | } | 676 | } |
| 639 | 677 | ||
| 640 | pub fn destroy(self: *File, gpa: *Allocator) void { | 678 | pub fn destroy(file: *File, gpa: *Allocator) void { |
| 641 | self.deinit(gpa); | 679 | file.deinit(gpa); |
| 642 | gpa.destroy(self); | 680 | gpa.destroy(file); |
| 643 | } | 681 | } |
| 644 | 682 | ||
| 645 | pub fn dumpSrc(self: *File, src: usize) void { | 683 | pub fn dumpSrc(file: *File, src: LazySrcLoc) void { |
| 646 | const loc = std.zig.findLineColumn(self.source.bytes, src); | 684 | const loc = std.zig.findLineColumn(file.source.bytes, src); |
| 647 | std.debug.print("{s}:{d}:{d}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); | 685 | std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 }); |
| 648 | } | 686 | } |
| 649 | 687 | ||
| 650 | pub fn getSource(self: *File, module: *Module) ![:0]const u8 { | 688 | pub fn getSource(file: *File, module: *Module) ![:0]const u8 { |
| 651 | switch (self.source) { | 689 | switch (file.source) { |
| 652 | .unloaded => { | 690 | .unloaded => { |
| 653 | const source = try self.pkg.root_src_directory.handle.readFileAllocOptions( | 691 | const source = try file.pkg.root_src_directory.handle.readFileAllocOptions( |
| 654 | module.gpa, | 692 | module.gpa, |
| 655 | self.sub_file_path, | 693 | file.sub_file_path, |
| 656 | std.math.maxInt(u32), | 694 | std.math.maxInt(u32), |
| 657 | null, | 695 | null, |
| 658 | 1, | 696 | 1, |
| 659 | 0, | 697 | 0, |
| 660 | ); | 698 | ); |
| 661 | self.source = .{ .bytes = source }; | 699 | file.source = .{ .bytes = source }; |
| 662 | return source; | 700 | return source; |
| 663 | }, | 701 | }, |
| 664 | .bytes => |bytes| return bytes, | 702 | .bytes => |bytes| return bytes, |
| ... | @@ -666,37 +704,30 @@ pub const Scope = struct { | ... | @@ -666,37 +704,30 @@ pub const Scope = struct { |
| 666 | } | 704 | } |
| 667 | }; | 705 | }; |
| 668 | 706 | ||
| 669 | /// This is a temporary structure, references to it are valid only | 707 | /// This is the context needed to semantically analyze ZIR instructions and |
| 708 | /// produce TZIR instructions. | ||
| 709 | /// This is a temporary structure stored on the stack; references to it are valid only | ||
| 670 | /// during semantic analysis of the block. | 710 | /// during semantic analysis of the block. |
| 671 | pub const Block = struct { | 711 | pub const Block = struct { |
| 672 | pub const base_tag: Tag = .block; | 712 | pub const base_tag: Tag = .block; |
| 673 | 713 | ||
| 674 | base: Scope = Scope{ .tag = base_tag }, | 714 | base: Scope = Scope{ .tag = base_tag }, |
| 675 | parent: ?*Block, | 715 | parent: ?*Block, |
| 676 | /// Maps ZIR to TZIR. Shared to sub-blocks. | 716 | /// Shared among all child blocks. |
| 677 | inst_table: *InstTable, | 717 | sema: *Sema, |
| 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, | ||
| 683 | /// This Decl is the Decl according to the Zig source code corresponding to this Block. | 718 | /// This Decl is the Decl according to the Zig source code corresponding to this Block. |
| 719 | /// This can vary during inline or comptime function calls. See `Sema.owner_decl` | ||
| 720 | /// for the one that will be the same for all Block instances. | ||
| 684 | src_decl: *Decl, | 721 | src_decl: *Decl, |
| 685 | instructions: ArrayListUnmanaged(*Inst), | 722 | instructions: ArrayListUnmanaged(*ir.Inst), |
| 686 | /// Points to the arena allocator of the Decl. | ||
| 687 | arena: *Allocator, | ||
| 688 | label: ?Label = null, | 723 | label: ?Label = null, |
| 689 | inlining: ?*Inlining, | 724 | inlining: ?*Inlining, |
| 690 | is_comptime: bool, | 725 | is_comptime: bool, |
| 691 | /// Shared to sub-blocks. | ||
| 692 | branch_quota: *u32, | ||
| 693 | |||
| 694 | pub const InstTable = std.AutoHashMap(*zir.Inst, *Inst); | ||
| 695 | 726 | ||
| 696 | /// This `Block` maps a block ZIR instruction to the corresponding | 727 | /// This `Block` maps a block ZIR instruction to the corresponding |
| 697 | /// TZIR instruction for break instruction analysis. | 728 | /// TZIR instruction for break instruction analysis. |
| 698 | pub const Label = struct { | 729 | pub const Label = struct { |
| 699 | zir_block: *zir.Inst.Block, | 730 | zir_block: zir.Inst.Index, |
| 700 | merges: Merges, | 731 | merges: Merges, |
| 701 | }; | 732 | }; |
| 702 | 733 | ||
| ... | @@ -706,73 +737,254 @@ pub const Scope = struct { | ... | @@ -706,73 +737,254 @@ pub const Scope = struct { |
| 706 | /// It is shared among all the blocks in an inline or comptime called | 737 | /// It is shared among all the blocks in an inline or comptime called |
| 707 | /// function. | 738 | /// function. |
| 708 | pub const Inlining = struct { | 739 | 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 | merges: Merges, | 740 | merges: Merges, |
| 717 | |||
| 718 | pub const Shared = struct { | ||
| 719 | caller: ?*Fn, | ||
| 720 | branch_count: u32, | ||
| 721 | }; | ||
| 722 | }; | 741 | }; |
| 723 | 742 | ||
| 724 | pub const Merges = struct { | 743 | pub const Merges = struct { |
| 725 | block_inst: *Inst.Block, | 744 | block_inst: *ir.Inst.Block, |
| 726 | /// Separate array list from break_inst_list so that it can be passed directly | 745 | /// Separate array list from break_inst_list so that it can be passed directly |
| 727 | /// to resolvePeerTypes. | 746 | /// to resolvePeerTypes. |
| 728 | results: ArrayListUnmanaged(*Inst), | 747 | results: ArrayListUnmanaged(*ir.Inst), |
| 729 | /// Keeps track of the break instructions so that the operand can be replaced | 748 | /// Keeps track of the break instructions so that the operand can be replaced |
| 730 | /// if we need to add type coercion at the end of block analysis. | 749 | /// if we need to add type coercion at the end of block analysis. |
| 731 | /// Same indexes, capacity, length as `results`. | 750 | /// Same indexes, capacity, length as `results`. |
| 732 | br_list: ArrayListUnmanaged(*Inst.Br), | 751 | br_list: ArrayListUnmanaged(*ir.Inst.Br), |
| 733 | }; | 752 | }; |
| 734 | 753 | ||
| 735 | /// For debugging purposes. | 754 | /// For debugging purposes. |
| 736 | pub fn dump(self: *Block, mod: Module) void { | 755 | pub fn dump(block: *Block, mod: Module) void { |
| 737 | zir.dumpBlock(mod, self); | 756 | zir.dumpBlock(mod, block); |
| 738 | } | 757 | } |
| 739 | 758 | ||
| 740 | pub fn makeSubBlock(parent: *Block) Block { | 759 | pub fn makeSubBlock(parent: *Block) Block { |
| 741 | return .{ | 760 | return .{ |
| 742 | .parent = parent, | 761 | .parent = parent, |
| 743 | .inst_table = parent.inst_table, | 762 | .sema = parent.sema, |
| 744 | .func = parent.func, | ||
| 745 | .owner_decl = parent.owner_decl, | ||
| 746 | .src_decl = parent.src_decl, | 763 | .src_decl = parent.src_decl, |
| 747 | .instructions = .{}, | 764 | .instructions = .{}, |
| 748 | .arena = parent.arena, | ||
| 749 | .label = null, | 765 | .label = null, |
| 750 | .inlining = parent.inlining, | 766 | .inlining = parent.inlining, |
| 751 | .is_comptime = parent.is_comptime, | 767 | .is_comptime = parent.is_comptime, |
| 752 | .branch_quota = parent.branch_quota, | ||
| 753 | }; | 768 | }; |
| 754 | } | 769 | } |
| 770 | |||
| 771 | pub fn wantSafety(block: *const Block) bool { | ||
| 772 | // TODO take into account scope's safety overrides | ||
| 773 | return switch (block.sema.mod.optimizeMode()) { | ||
| 774 | .Debug => true, | ||
| 775 | .ReleaseSafe => true, | ||
| 776 | .ReleaseFast => false, | ||
| 777 | .ReleaseSmall => false, | ||
| 778 | }; | ||
| 779 | } | ||
| 780 | |||
| 781 | pub fn getFileScope(block: *Block) *Scope.File { | ||
| 782 | return block.src_decl.container.file_scope; | ||
| 783 | } | ||
| 784 | |||
| 785 | pub fn addNoOp( | ||
| 786 | block: *Scope.Block, | ||
| 787 | src: LazySrcLoc, | ||
| 788 | ty: Type, | ||
| 789 | comptime tag: ir.Inst.Tag, | ||
| 790 | ) !*ir.Inst { | ||
| 791 | const inst = try block.sema.arena.create(tag.Type()); | ||
| 792 | inst.* = .{ | ||
| 793 | .base = .{ | ||
| 794 | .tag = tag, | ||
| 795 | .ty = ty, | ||
| 796 | .src = src, | ||
| 797 | }, | ||
| 798 | }; | ||
| 799 | try block.instructions.append(block.sema.gpa, &inst.base); | ||
| 800 | return &inst.base; | ||
| 801 | } | ||
| 802 | |||
| 803 | pub fn addUnOp( | ||
| 804 | block: *Scope.Block, | ||
| 805 | src: LazySrcLoc, | ||
| 806 | ty: Type, | ||
| 807 | tag: ir.Inst.Tag, | ||
| 808 | operand: *ir.Inst, | ||
| 809 | ) !*ir.Inst { | ||
| 810 | const inst = try block.sema.arena.create(ir.Inst.UnOp); | ||
| 811 | inst.* = .{ | ||
| 812 | .base = .{ | ||
| 813 | .tag = tag, | ||
| 814 | .ty = ty, | ||
| 815 | .src = src, | ||
| 816 | }, | ||
| 817 | .operand = operand, | ||
| 818 | }; | ||
| 819 | try block.instructions.append(block.sema.gpa, &inst.base); | ||
| 820 | return &inst.base; | ||
| 821 | } | ||
| 822 | |||
| 823 | pub fn addBinOp( | ||
| 824 | block: *Scope.Block, | ||
| 825 | src: LazySrcLoc, | ||
| 826 | ty: Type, | ||
| 827 | tag: ir.Inst.Tag, | ||
| 828 | lhs: *ir.Inst, | ||
| 829 | rhs: *ir.Inst, | ||
| 830 | ) !*ir.Inst { | ||
| 831 | const inst = try block.sema.arena.create(ir.Inst.BinOp); | ||
| 832 | inst.* = .{ | ||
| 833 | .base = .{ | ||
| 834 | .tag = tag, | ||
| 835 | .ty = ty, | ||
| 836 | .src = src, | ||
| 837 | }, | ||
| 838 | .lhs = lhs, | ||
| 839 | .rhs = rhs, | ||
| 840 | }; | ||
| 841 | try block.instructions.append(block.sema.gpa, &inst.base); | ||
| 842 | return &inst.base; | ||
| 843 | } | ||
| 844 | |||
| 845 | pub fn addBr( | ||
| 846 | scope_block: *Scope.Block, | ||
| 847 | src: LazySrcLoc, | ||
| 848 | target_block: *ir.Inst.Block, | ||
| 849 | operand: *ir.Inst, | ||
| 850 | ) !*ir.Inst.Br { | ||
| 851 | const inst = try scope_block.sema.arena.create(ir.Inst.Br); | ||
| 852 | inst.* = .{ | ||
| 853 | .base = .{ | ||
| 854 | .tag = .br, | ||
| 855 | .ty = Type.initTag(.noreturn), | ||
| 856 | .src = src, | ||
| 857 | }, | ||
| 858 | .operand = operand, | ||
| 859 | .block = target_block, | ||
| 860 | }; | ||
| 861 | try scope_block.instructions.append(scope_block.sema.gpa, &inst.base); | ||
| 862 | return inst; | ||
| 863 | } | ||
| 864 | |||
| 865 | pub fn addCondBr( | ||
| 866 | block: *Scope.Block, | ||
| 867 | src: LazySrcLoc, | ||
| 868 | condition: *ir.Inst, | ||
| 869 | then_body: ir.Body, | ||
| 870 | else_body: ir.Body, | ||
| 871 | ) !*ir.Inst { | ||
| 872 | const inst = try block.sema.arena.create(ir.Inst.CondBr); | ||
| 873 | inst.* = .{ | ||
| 874 | .base = .{ | ||
| 875 | .tag = .condbr, | ||
| 876 | .ty = Type.initTag(.noreturn), | ||
| 877 | .src = src, | ||
| 878 | }, | ||
| 879 | .condition = condition, | ||
| 880 | .then_body = then_body, | ||
| 881 | .else_body = else_body, | ||
| 882 | }; | ||
| 883 | try block.instructions.append(block.sema.gpa, &inst.base); | ||
| 884 | return &inst.base; | ||
| 885 | } | ||
| 886 | |||
| 887 | pub fn addCall( | ||
| 888 | block: *Scope.Block, | ||
| 889 | src: LazySrcLoc, | ||
| 890 | ty: Type, | ||
| 891 | func: *ir.Inst, | ||
| 892 | args: []const *ir.Inst, | ||
| 893 | ) !*ir.Inst { | ||
| 894 | const inst = try block.sema.arena.create(ir.Inst.Call); | ||
| 895 | inst.* = .{ | ||
| 896 | .base = .{ | ||
| 897 | .tag = .call, | ||
| 898 | .ty = ty, | ||
| 899 | .src = src, | ||
| 900 | }, | ||
| 901 | .func = func, | ||
| 902 | .args = args, | ||
| 903 | }; | ||
| 904 | try block.instructions.append(block.sema.gpa, &inst.base); | ||
| 905 | return &inst.base; | ||
| 906 | } | ||
| 907 | |||
| 908 | pub fn addSwitchBr( | ||
| 909 | block: *Scope.Block, | ||
| 910 | src: LazySrcLoc, | ||
| 911 | operand: *ir.Inst, | ||
| 912 | cases: []ir.Inst.SwitchBr.Case, | ||
| 913 | else_body: ir.Body, | ||
| 914 | ) !*ir.Inst { | ||
| 915 | const inst = try block.sema.arena.create(ir.Inst.SwitchBr); | ||
| 916 | inst.* = .{ | ||
| 917 | .base = .{ | ||
| 918 | .tag = .switchbr, | ||
| 919 | .ty = Type.initTag(.noreturn), | ||
| 920 | .src = src, | ||
| 921 | }, | ||
| 922 | .target = operand, | ||
| 923 | .cases = cases, | ||
| 924 | .else_body = else_body, | ||
| 925 | }; | ||
| 926 | try block.instructions.append(block.sema.gpa, &inst.base); | ||
| 927 | return &inst.base; | ||
| 928 | } | ||
| 929 | |||
| 930 | pub fn addDbgStmt(block: *Scope.Block, src: LazySrcLoc, abs_byte_off: u32) !*ir.Inst { | ||
| 931 | const inst = try block.sema.arena.create(ir.Inst.DbgStmt); | ||
| 932 | inst.* = .{ | ||
| 933 | .base = .{ | ||
| 934 | .tag = .dbg_stmt, | ||
| 935 | .ty = Type.initTag(.void), | ||
| 936 | .src = src, | ||
| 937 | }, | ||
| 938 | .byte_offset = abs_byte_off, | ||
| 939 | }; | ||
| 940 | try block.instructions.append(block.sema.gpa, &inst.base); | ||
| 941 | return &inst.base; | ||
| 942 | } | ||
| 943 | |||
| 944 | pub fn addStructFieldPtr( | ||
| 945 | block: *Scope.Block, | ||
| 946 | src: LazySrcLoc, | ||
| 947 | ty: Type, | ||
| 948 | struct_ptr: *ir.Inst, | ||
| 949 | field_index: u32, | ||
| 950 | ) !*ir.Inst { | ||
| 951 | const inst = try block.sema.arena.create(ir.Inst.StructFieldPtr); | ||
| 952 | inst.* = .{ | ||
| 953 | .base = .{ | ||
| 954 | .tag = .struct_field_ptr, | ||
| 955 | .ty = ty, | ||
| 956 | .src = src, | ||
| 957 | }, | ||
| 958 | .struct_ptr = struct_ptr, | ||
| 959 | .field_index = field_index, | ||
| 960 | }; | ||
| 961 | try block.instructions.append(block.sema.gpa, &inst.base); | ||
| 962 | return &inst.base; | ||
| 963 | } | ||
| 755 | }; | 964 | }; |
| 756 | 965 | ||
| 757 | /// This is a temporary structure, references to it are valid only | 966 | /// This is a temporary structure; references to it are valid only |
| 758 | /// during semantic analysis of the decl. | 967 | /// while constructing a `zir.Code`. |
| 759 | pub const GenZIR = struct { | 968 | pub const GenZir = struct { |
| 760 | pub const base_tag: Tag = .gen_zir; | 969 | pub const base_tag: Tag = .gen_zir; |
| 761 | base: Scope = Scope{ .tag = base_tag }, | 970 | base: Scope = Scope{ .tag = base_tag }, |
| 762 | /// Parents can be: `GenZIR`, `File` | ||
| 763 | parent: *Scope, | ||
| 764 | decl: *Decl, | ||
| 765 | arena: *Allocator, | ||
| 766 | force_comptime: bool, | 971 | force_comptime: bool, |
| 767 | /// The first N instructions in a function body ZIR are arg instructions. | 972 | /// Parents can be: `GenZir`, `File` |
| 768 | instructions: std.ArrayListUnmanaged(*zir.Inst) = .{}, | 973 | parent: *Scope, |
| 974 | /// All `GenZir` scopes for the same ZIR share this. | ||
| 975 | astgen: *AstGen, | ||
| 976 | /// Keeps track of the list of instructions in this scope only. Indexes | ||
| 977 | /// to instructions in `astgen`. | ||
| 978 | instructions: ArrayListUnmanaged(zir.Inst.Index) = .{}, | ||
| 769 | label: ?Label = null, | 979 | label: ?Label = null, |
| 770 | break_block: ?*zir.Inst.Block = null, | 980 | break_block: zir.Inst.Index = 0, |
| 771 | continue_block: ?*zir.Inst.Block = null, | 981 | continue_block: zir.Inst.Index = 0, |
| 772 | /// Only valid when setBlockResultLoc is called. | 982 | /// Only valid when setBreakResultLoc is called. |
| 773 | break_result_loc: astgen.ResultLoc = undefined, | 983 | break_result_loc: AstGen.ResultLoc = undefined, |
| 774 | /// When a block has a pointer result location, here it is. | 984 | /// When a block has a pointer result location, here it is. |
| 775 | rl_ptr: ?*zir.Inst = null, | 985 | rl_ptr: zir.Inst.Ref = .none, |
| 986 | /// When a block has a type result location, here it is. | ||
| 987 | rl_ty_inst: zir.Inst.Ref = .none, | ||
| 776 | /// Keeps track of how many branches of a block did not actually | 988 | /// Keeps track of how many branches of a block did not actually |
| 777 | /// consume the result location. astgen uses this to figure out | 989 | /// consume the result location. astgen uses this to figure out |
| 778 | /// whether to rely on break instructions or writing to the result | 990 | /// whether to rely on break instructions or writing to the result |
| ... | @@ -784,19 +996,466 @@ pub const Scope = struct { | ... | @@ -784,19 +996,466 @@ pub const Scope = struct { |
| 784 | break_count: usize = 0, | 996 | break_count: usize = 0, |
| 785 | /// Tracks `break :foo bar` instructions so they can possibly be elided later if | 997 | /// Tracks `break :foo bar` instructions so they can possibly be elided later if |
| 786 | /// the labeled block ends up not needing a result location pointer. | 998 | /// the labeled block ends up not needing a result location pointer. |
| 787 | labeled_breaks: std.ArrayListUnmanaged(*zir.Inst.Break) = .{}, | 999 | labeled_breaks: ArrayListUnmanaged(zir.Inst.Index) = .{}, |
| 788 | /// Tracks `store_to_block_ptr` instructions that correspond to break instructions | 1000 | /// Tracks `store_to_block_ptr` instructions that correspond to break instructions |
| 789 | /// so they can possibly be elided later if the labeled block ends up not needing | 1001 | /// so they can possibly be elided later if the labeled block ends up not needing |
| 790 | /// a result location pointer. | 1002 | /// a result location pointer. |
| 791 | labeled_store_to_block_ptr_list: std.ArrayListUnmanaged(*zir.Inst.BinOp) = .{}, | 1003 | labeled_store_to_block_ptr_list: ArrayListUnmanaged(zir.Inst.Index) = .{}, |
| 792 | /// for suspend error notes | ||
| 793 | src: usize = 0, | ||
| 794 | 1004 | ||
| 795 | pub const Label = struct { | 1005 | pub const Label = struct { |
| 796 | token: ast.TokenIndex, | 1006 | token: ast.TokenIndex, |
| 797 | block_inst: *zir.Inst.Block, | 1007 | block_inst: zir.Inst.Index, |
| 798 | used: bool = false, | 1008 | used: bool = false, |
| 799 | }; | 1009 | }; |
| 1010 | |||
| 1011 | /// Only valid to call on the top of the `GenZir` stack. Completes the | ||
| 1012 | /// `AstGen` into a `zir.Code`. Leaves the `AstGen` in an | ||
| 1013 | /// initialized, but empty, state. | ||
| 1014 | pub fn finish(gz: *GenZir) !zir.Code { | ||
| 1015 | const gpa = gz.astgen.mod.gpa; | ||
| 1016 | try gz.setBlockBody(0); | ||
| 1017 | return zir.Code{ | ||
| 1018 | .instructions = gz.astgen.instructions.toOwnedSlice(), | ||
| 1019 | .string_bytes = gz.astgen.string_bytes.toOwnedSlice(gpa), | ||
| 1020 | .extra = gz.astgen.extra.toOwnedSlice(gpa), | ||
| 1021 | .decls = gz.astgen.decls.toOwnedSlice(gpa), | ||
| 1022 | }; | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | pub fn tokSrcLoc(gz: GenZir, token_index: ast.TokenIndex) LazySrcLoc { | ||
| 1026 | return gz.astgen.decl.tokSrcLoc(token_index); | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | pub fn nodeSrcLoc(gz: GenZir, node_index: ast.Node.Index) LazySrcLoc { | ||
| 1030 | return gz.astgen.decl.nodeSrcLoc(node_index); | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | pub fn tree(gz: *const GenZir) *const ast.Tree { | ||
| 1034 | return &gz.astgen.decl.container.file_scope.tree; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | pub fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void { | ||
| 1038 | // Depending on whether the result location is a pointer or value, different | ||
| 1039 | // ZIR needs to be generated. In the former case we rely on storing to the | ||
| 1040 | // pointer to communicate the result, and use breakvoid; in the latter case | ||
| 1041 | // the block break instructions will have the result values. | ||
| 1042 | // One more complication: when the result location is a pointer, we detect | ||
| 1043 | // the scenario where the result location is not consumed. In this case | ||
| 1044 | // we emit ZIR for the block break instructions to have the result values, | ||
| 1045 | // and then rvalue() on that to pass the value to the result location. | ||
| 1046 | switch (parent_rl) { | ||
| 1047 | .ty => |ty_inst| { | ||
| 1048 | gz.rl_ty_inst = ty_inst; | ||
| 1049 | gz.break_result_loc = parent_rl; | ||
| 1050 | }, | ||
| 1051 | .discard, .none, .ptr, .ref => { | ||
| 1052 | gz.break_result_loc = parent_rl; | ||
| 1053 | }, | ||
| 1054 | |||
| 1055 | .inferred_ptr => |ptr| { | ||
| 1056 | gz.rl_ptr = ptr; | ||
| 1057 | gz.break_result_loc = .{ .block_ptr = gz }; | ||
| 1058 | }, | ||
| 1059 | |||
| 1060 | .block_ptr => |parent_block_scope| { | ||
| 1061 | gz.rl_ty_inst = parent_block_scope.rl_ty_inst; | ||
| 1062 | gz.rl_ptr = parent_block_scope.rl_ptr; | ||
| 1063 | gz.break_result_loc = .{ .block_ptr = gz }; | ||
| 1064 | }, | ||
| 1065 | } | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | pub fn setBoolBrBody(gz: GenZir, inst: zir.Inst.Index) !void { | ||
| 1069 | const gpa = gz.astgen.mod.gpa; | ||
| 1070 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | ||
| 1071 | @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); | ||
| 1072 | const zir_datas = gz.astgen.instructions.items(.data); | ||
| 1073 | zir_datas[inst].bool_br.payload_index = gz.astgen.addExtraAssumeCapacity( | ||
| 1074 | zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, | ||
| 1075 | ); | ||
| 1076 | gz.astgen.extra.appendSliceAssumeCapacity(gz.instructions.items); | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | pub fn setBlockBody(gz: GenZir, inst: zir.Inst.Index) !void { | ||
| 1080 | const gpa = gz.astgen.mod.gpa; | ||
| 1081 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | ||
| 1082 | @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); | ||
| 1083 | const zir_datas = gz.astgen.instructions.items(.data); | ||
| 1084 | zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity( | ||
| 1085 | zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, | ||
| 1086 | ); | ||
| 1087 | gz.astgen.extra.appendSliceAssumeCapacity(gz.instructions.items); | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | pub fn identAsString(gz: *GenZir, ident_token: ast.TokenIndex) !u32 { | ||
| 1091 | const astgen = gz.astgen; | ||
| 1092 | const gpa = astgen.mod.gpa; | ||
| 1093 | const string_bytes = &astgen.string_bytes; | ||
| 1094 | const str_index = @intCast(u32, string_bytes.items.len); | ||
| 1095 | try astgen.mod.appendIdentStr(&gz.base, ident_token, string_bytes); | ||
| 1096 | try string_bytes.append(gpa, 0); | ||
| 1097 | return str_index; | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct { | ||
| 1101 | src_node: ast.Node.Index, | ||
| 1102 | param_types: []const zir.Inst.Ref, | ||
| 1103 | ret_ty: zir.Inst.Ref, | ||
| 1104 | cc: zir.Inst.Ref, | ||
| 1105 | }) !zir.Inst.Ref { | ||
| 1106 | assert(args.src_node != 0); | ||
| 1107 | assert(args.ret_ty != .none); | ||
| 1108 | assert(args.cc != .none); | ||
| 1109 | const gpa = gz.astgen.mod.gpa; | ||
| 1110 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1111 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | ||
| 1112 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | ||
| 1113 | @typeInfo(zir.Inst.FnTypeCc).Struct.fields.len + args.param_types.len); | ||
| 1114 | |||
| 1115 | const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnTypeCc{ | ||
| 1116 | .return_type = args.ret_ty, | ||
| 1117 | .cc = args.cc, | ||
| 1118 | .param_types_len = @intCast(u32, args.param_types.len), | ||
| 1119 | }); | ||
| 1120 | gz.astgen.appendRefsAssumeCapacity(args.param_types); | ||
| 1121 | |||
| 1122 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1123 | gz.astgen.instructions.appendAssumeCapacity(.{ | ||
| 1124 | .tag = tag, | ||
| 1125 | .data = .{ .pl_node = .{ | ||
| 1126 | .src_node = gz.astgen.decl.nodeIndexToRelative(args.src_node), | ||
| 1127 | .payload_index = payload_index, | ||
| 1128 | } }, | ||
| 1129 | }); | ||
| 1130 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1131 | return gz.astgen.indexToRef(new_index); | ||
| 1132 | } | ||
| 1133 | |||
| 1134 | pub fn addFnType(gz: *GenZir, tag: zir.Inst.Tag, args: struct { | ||
| 1135 | src_node: ast.Node.Index, | ||
| 1136 | ret_ty: zir.Inst.Ref, | ||
| 1137 | param_types: []const zir.Inst.Ref, | ||
| 1138 | }) !zir.Inst.Ref { | ||
| 1139 | assert(args.src_node != 0); | ||
| 1140 | assert(args.ret_ty != .none); | ||
| 1141 | const gpa = gz.astgen.mod.gpa; | ||
| 1142 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1143 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | ||
| 1144 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | ||
| 1145 | @typeInfo(zir.Inst.FnType).Struct.fields.len + args.param_types.len); | ||
| 1146 | |||
| 1147 | const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnType{ | ||
| 1148 | .return_type = args.ret_ty, | ||
| 1149 | .param_types_len = @intCast(u32, args.param_types.len), | ||
| 1150 | }); | ||
| 1151 | gz.astgen.appendRefsAssumeCapacity(args.param_types); | ||
| 1152 | |||
| 1153 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1154 | gz.astgen.instructions.appendAssumeCapacity(.{ | ||
| 1155 | .tag = tag, | ||
| 1156 | .data = .{ .pl_node = .{ | ||
| 1157 | .src_node = gz.astgen.decl.nodeIndexToRelative(args.src_node), | ||
| 1158 | .payload_index = payload_index, | ||
| 1159 | } }, | ||
| 1160 | }); | ||
| 1161 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1162 | return gz.astgen.indexToRef(new_index); | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | pub fn addCall( | ||
| 1166 | gz: *GenZir, | ||
| 1167 | tag: zir.Inst.Tag, | ||
| 1168 | callee: zir.Inst.Ref, | ||
| 1169 | args: []const zir.Inst.Ref, | ||
| 1170 | /// Absolute node index. This function does the conversion to offset from Decl. | ||
| 1171 | src_node: ast.Node.Index, | ||
| 1172 | ) !zir.Inst.Ref { | ||
| 1173 | assert(callee != .none); | ||
| 1174 | assert(src_node != 0); | ||
| 1175 | const gpa = gz.astgen.mod.gpa; | ||
| 1176 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1177 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | ||
| 1178 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | ||
| 1179 | @typeInfo(zir.Inst.Call).Struct.fields.len + args.len); | ||
| 1180 | |||
| 1181 | const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.Call{ | ||
| 1182 | .callee = callee, | ||
| 1183 | .args_len = @intCast(u32, args.len), | ||
| 1184 | }); | ||
| 1185 | gz.astgen.appendRefsAssumeCapacity(args); | ||
| 1186 | |||
| 1187 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1188 | gz.astgen.instructions.appendAssumeCapacity(.{ | ||
| 1189 | .tag = tag, | ||
| 1190 | .data = .{ .pl_node = .{ | ||
| 1191 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), | ||
| 1192 | .payload_index = payload_index, | ||
| 1193 | } }, | ||
| 1194 | }); | ||
| 1195 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1196 | return gz.astgen.indexToRef(new_index); | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | /// Note that this returns a `zir.Inst.Index` not a ref. | ||
| 1200 | /// Leaves the `payload_index` field undefined. | ||
| 1201 | pub fn addBoolBr( | ||
| 1202 | gz: *GenZir, | ||
| 1203 | tag: zir.Inst.Tag, | ||
| 1204 | lhs: zir.Inst.Ref, | ||
| 1205 | ) !zir.Inst.Index { | ||
| 1206 | assert(lhs != .none); | ||
| 1207 | const gpa = gz.astgen.mod.gpa; | ||
| 1208 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1209 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | ||
| 1210 | |||
| 1211 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1212 | gz.astgen.instructions.appendAssumeCapacity(.{ | ||
| 1213 | .tag = tag, | ||
| 1214 | .data = .{ .bool_br = .{ | ||
| 1215 | .lhs = lhs, | ||
| 1216 | .payload_index = undefined, | ||
| 1217 | } }, | ||
| 1218 | }); | ||
| 1219 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1220 | return new_index; | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | pub fn addInt(gz: *GenZir, integer: u64) !zir.Inst.Ref { | ||
| 1224 | return gz.add(.{ | ||
| 1225 | .tag = .int, | ||
| 1226 | .data = .{ .int = integer }, | ||
| 1227 | }); | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | pub fn addUnNode( | ||
| 1231 | gz: *GenZir, | ||
| 1232 | tag: zir.Inst.Tag, | ||
| 1233 | operand: zir.Inst.Ref, | ||
| 1234 | /// Absolute node index. This function does the conversion to offset from Decl. | ||
| 1235 | src_node: ast.Node.Index, | ||
| 1236 | ) !zir.Inst.Ref { | ||
| 1237 | assert(operand != .none); | ||
| 1238 | return gz.add(.{ | ||
| 1239 | .tag = tag, | ||
| 1240 | .data = .{ .un_node = .{ | ||
| 1241 | .operand = operand, | ||
| 1242 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), | ||
| 1243 | } }, | ||
| 1244 | }); | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | pub fn addPlNode( | ||
| 1248 | gz: *GenZir, | ||
| 1249 | tag: zir.Inst.Tag, | ||
| 1250 | /// Absolute node index. This function does the conversion to offset from Decl. | ||
| 1251 | src_node: ast.Node.Index, | ||
| 1252 | extra: anytype, | ||
| 1253 | ) !zir.Inst.Ref { | ||
| 1254 | const gpa = gz.astgen.mod.gpa; | ||
| 1255 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1256 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | ||
| 1257 | |||
| 1258 | const payload_index = try gz.astgen.addExtra(extra); | ||
| 1259 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1260 | gz.astgen.instructions.appendAssumeCapacity(.{ | ||
| 1261 | .tag = tag, | ||
| 1262 | .data = .{ .pl_node = .{ | ||
| 1263 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), | ||
| 1264 | .payload_index = payload_index, | ||
| 1265 | } }, | ||
| 1266 | }); | ||
| 1267 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1268 | return gz.astgen.indexToRef(new_index); | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | pub fn addArrayTypeSentinel( | ||
| 1272 | gz: *GenZir, | ||
| 1273 | len: zir.Inst.Ref, | ||
| 1274 | sentinel: zir.Inst.Ref, | ||
| 1275 | elem_type: zir.Inst.Ref, | ||
| 1276 | ) !zir.Inst.Ref { | ||
| 1277 | const gpa = gz.astgen.mod.gpa; | ||
| 1278 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1279 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | ||
| 1280 | |||
| 1281 | const payload_index = try gz.astgen.addExtra(zir.Inst.ArrayTypeSentinel{ | ||
| 1282 | .sentinel = sentinel, | ||
| 1283 | .elem_type = elem_type, | ||
| 1284 | }); | ||
| 1285 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1286 | gz.astgen.instructions.appendAssumeCapacity(.{ | ||
| 1287 | .tag = .array_type_sentinel, | ||
| 1288 | .data = .{ .array_type_sentinel = .{ | ||
| 1289 | .len = len, | ||
| 1290 | .payload_index = payload_index, | ||
| 1291 | } }, | ||
| 1292 | }); | ||
| 1293 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1294 | return gz.astgen.indexToRef(new_index); | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | pub fn addUnTok( | ||
| 1298 | gz: *GenZir, | ||
| 1299 | tag: zir.Inst.Tag, | ||
| 1300 | operand: zir.Inst.Ref, | ||
| 1301 | /// Absolute token index. This function does the conversion to Decl offset. | ||
| 1302 | abs_tok_index: ast.TokenIndex, | ||
| 1303 | ) !zir.Inst.Ref { | ||
| 1304 | assert(operand != .none); | ||
| 1305 | return gz.add(.{ | ||
| 1306 | .tag = tag, | ||
| 1307 | .data = .{ .un_tok = .{ | ||
| 1308 | .operand = operand, | ||
| 1309 | .src_tok = abs_tok_index - gz.astgen.decl.srcToken(), | ||
| 1310 | } }, | ||
| 1311 | }); | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | pub fn addStrTok( | ||
| 1315 | gz: *GenZir, | ||
| 1316 | tag: zir.Inst.Tag, | ||
| 1317 | str_index: u32, | ||
| 1318 | /// Absolute token index. This function does the conversion to Decl offset. | ||
| 1319 | abs_tok_index: ast.TokenIndex, | ||
| 1320 | ) !zir.Inst.Ref { | ||
| 1321 | return gz.add(.{ | ||
| 1322 | .tag = tag, | ||
| 1323 | .data = .{ .str_tok = .{ | ||
| 1324 | .start = str_index, | ||
| 1325 | .src_tok = abs_tok_index - gz.astgen.decl.srcToken(), | ||
| 1326 | } }, | ||
| 1327 | }); | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | pub fn addBreak( | ||
| 1331 | gz: *GenZir, | ||
| 1332 | tag: zir.Inst.Tag, | ||
| 1333 | break_block: zir.Inst.Index, | ||
| 1334 | operand: zir.Inst.Ref, | ||
| 1335 | ) !zir.Inst.Index { | ||
| 1336 | return gz.addAsIndex(.{ | ||
| 1337 | .tag = tag, | ||
| 1338 | .data = .{ .@"break" = .{ | ||
| 1339 | .block_inst = break_block, | ||
| 1340 | .operand = operand, | ||
| 1341 | } }, | ||
| 1342 | }); | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | pub fn addBin( | ||
| 1346 | gz: *GenZir, | ||
| 1347 | tag: zir.Inst.Tag, | ||
| 1348 | lhs: zir.Inst.Ref, | ||
| 1349 | rhs: zir.Inst.Ref, | ||
| 1350 | ) !zir.Inst.Ref { | ||
| 1351 | assert(lhs != .none); | ||
| 1352 | assert(rhs != .none); | ||
| 1353 | return gz.add(.{ | ||
| 1354 | .tag = tag, | ||
| 1355 | .data = .{ .bin = .{ | ||
| 1356 | .lhs = lhs, | ||
| 1357 | .rhs = rhs, | ||
| 1358 | } }, | ||
| 1359 | }); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | pub fn addDecl( | ||
| 1363 | gz: *GenZir, | ||
| 1364 | tag: zir.Inst.Tag, | ||
| 1365 | decl_index: u32, | ||
| 1366 | src_node: ast.Node.Index, | ||
| 1367 | ) !zir.Inst.Ref { | ||
| 1368 | return gz.add(.{ | ||
| 1369 | .tag = tag, | ||
| 1370 | .data = .{ .pl_node = .{ | ||
| 1371 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), | ||
| 1372 | .payload_index = decl_index, | ||
| 1373 | } }, | ||
| 1374 | }); | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | pub fn addNode( | ||
| 1378 | gz: *GenZir, | ||
| 1379 | tag: zir.Inst.Tag, | ||
| 1380 | /// Absolute node index. This function does the conversion to offset from Decl. | ||
| 1381 | src_node: ast.Node.Index, | ||
| 1382 | ) !zir.Inst.Ref { | ||
| 1383 | return gz.add(.{ | ||
| 1384 | .tag = tag, | ||
| 1385 | .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(src_node) }, | ||
| 1386 | }); | ||
| 1387 | } | ||
| 1388 | |||
| 1389 | /// Asserts that `str` is 8 or fewer bytes. | ||
| 1390 | pub fn addSmallStr( | ||
| 1391 | gz: *GenZir, | ||
| 1392 | tag: zir.Inst.Tag, | ||
| 1393 | str: []const u8, | ||
| 1394 | ) !zir.Inst.Ref { | ||
| 1395 | var buf: [9]u8 = undefined; | ||
| 1396 | mem.copy(u8, &buf, str); | ||
| 1397 | buf[str.len] = 0; | ||
| 1398 | |||
| 1399 | return gz.add(.{ | ||
| 1400 | .tag = tag, | ||
| 1401 | .data = .{ .small_str = .{ .bytes = buf[0..8].* } }, | ||
| 1402 | }); | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | /// Note that this returns a `zir.Inst.Index` not a ref. | ||
| 1406 | /// Does *not* append the block instruction to the scope. | ||
| 1407 | /// Leaves the `payload_index` field undefined. | ||
| 1408 | pub fn addBlock(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index { | ||
| 1409 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1410 | const gpa = gz.astgen.mod.gpa; | ||
| 1411 | try gz.astgen.instructions.append(gpa, .{ | ||
| 1412 | .tag = tag, | ||
| 1413 | .data = .{ .pl_node = .{ | ||
| 1414 | .src_node = gz.astgen.decl.nodeIndexToRelative(node), | ||
| 1415 | .payload_index = undefined, | ||
| 1416 | } }, | ||
| 1417 | }); | ||
| 1418 | return new_index; | ||
| 1419 | } | ||
| 1420 | |||
| 1421 | /// Note that this returns a `zir.Inst.Index` not a ref. | ||
| 1422 | /// Leaves the `payload_index` field undefined. | ||
| 1423 | pub fn addCondBr(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index { | ||
| 1424 | const gpa = gz.astgen.mod.gpa; | ||
| 1425 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1426 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1427 | try gz.astgen.instructions.append(gpa, .{ | ||
| 1428 | .tag = tag, | ||
| 1429 | .data = .{ .pl_node = .{ | ||
| 1430 | .src_node = gz.astgen.decl.nodeIndexToRelative(node), | ||
| 1431 | .payload_index = undefined, | ||
| 1432 | } }, | ||
| 1433 | }); | ||
| 1434 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1435 | return new_index; | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | pub fn addConst(gz: *GenZir, typed_value: *TypedValue) !zir.Inst.Ref { | ||
| 1439 | return gz.add(.{ | ||
| 1440 | .tag = .@"const", | ||
| 1441 | .data = .{ .@"const" = typed_value }, | ||
| 1442 | }); | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | pub fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref { | ||
| 1446 | return gz.astgen.indexToRef(try gz.addAsIndex(inst)); | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | pub fn addAsIndex(gz: *GenZir, inst: zir.Inst) !zir.Inst.Index { | ||
| 1450 | const gpa = gz.astgen.mod.gpa; | ||
| 1451 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1452 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | ||
| 1453 | |||
| 1454 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1455 | gz.astgen.instructions.appendAssumeCapacity(inst); | ||
| 1456 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1457 | return new_index; | ||
| 1458 | } | ||
| 800 | }; | 1459 | }; |
| 801 | 1460 | ||
| 802 | /// This is always a `const` local and importantly the `inst` is a value type, not a pointer. | 1461 | /// This is always a `const` local and importantly the `inst` is a value type, not a pointer. |
| ... | @@ -805,11 +1464,13 @@ pub const Scope = struct { | ... | @@ -805,11 +1464,13 @@ pub const Scope = struct { |
| 805 | pub const LocalVal = struct { | 1464 | pub const LocalVal = struct { |
| 806 | pub const base_tag: Tag = .local_val; | 1465 | pub const base_tag: Tag = .local_val; |
| 807 | base: Scope = Scope{ .tag = base_tag }, | 1466 | base: Scope = Scope{ .tag = base_tag }, |
| 808 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | 1467 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`. |
| 809 | parent: *Scope, | 1468 | parent: *Scope, |
| 810 | gen_zir: *GenZIR, | 1469 | gen_zir: *GenZir, |
| 811 | name: []const u8, | 1470 | name: []const u8, |
| 812 | inst: *zir.Inst, | 1471 | inst: zir.Inst.Ref, |
| 1472 | /// Source location of the corresponding variable declaration. | ||
| 1473 | src: LazySrcLoc, | ||
| 813 | }; | 1474 | }; |
| 814 | 1475 | ||
| 815 | /// This could be a `const` or `var` local. It has a pointer instead of a value. | 1476 | /// This could be a `const` or `var` local. It has a pointer instead of a value. |
| ... | @@ -818,21 +1479,19 @@ pub const Scope = struct { | ... | @@ -818,21 +1479,19 @@ pub const Scope = struct { |
| 818 | pub const LocalPtr = struct { | 1479 | pub const LocalPtr = struct { |
| 819 | pub const base_tag: Tag = .local_ptr; | 1480 | pub const base_tag: Tag = .local_ptr; |
| 820 | base: Scope = Scope{ .tag = base_tag }, | 1481 | base: Scope = Scope{ .tag = base_tag }, |
| 821 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | 1482 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`. |
| 822 | parent: *Scope, | 1483 | parent: *Scope, |
| 823 | gen_zir: *GenZIR, | 1484 | gen_zir: *GenZir, |
| 824 | name: []const u8, | 1485 | name: []const u8, |
| 825 | ptr: *zir.Inst, | 1486 | ptr: zir.Inst.Ref, |
| 1487 | /// Source location of the corresponding variable declaration. | ||
| 1488 | src: LazySrcLoc, | ||
| 826 | }; | 1489 | }; |
| 827 | 1490 | ||
| 828 | pub const Nosuspend = struct { | 1491 | pub const DeclRef = struct { |
| 829 | pub const base_tag: Tag = .gen_nosuspend; | 1492 | pub const base_tag: Tag = .decl_ref; |
| 830 | |||
| 831 | base: Scope = Scope{ .tag = base_tag }, | 1493 | base: Scope = Scope{ .tag = base_tag }, |
| 832 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | 1494 | decl: *Decl, |
| 833 | parent: *Scope, | ||
| 834 | gen_zir: *GenZIR, | ||
| 835 | src: usize, | ||
| 836 | }; | 1495 | }; |
| 837 | }; | 1496 | }; |
| 838 | 1497 | ||
| ... | @@ -855,17 +1514,17 @@ pub const ErrorMsg = struct { | ... | @@ -855,17 +1514,17 @@ pub const ErrorMsg = struct { |
| 855 | comptime format: []const u8, | 1514 | comptime format: []const u8, |
| 856 | args: anytype, | 1515 | args: anytype, |
| 857 | ) !*ErrorMsg { | 1516 | ) !*ErrorMsg { |
| 858 | const self = try gpa.create(ErrorMsg); | 1517 | const err_msg = try gpa.create(ErrorMsg); |
| 859 | errdefer gpa.destroy(self); | 1518 | errdefer gpa.destroy(err_msg); |
| 860 | self.* = try init(gpa, src_loc, format, args); | 1519 | err_msg.* = try init(gpa, src_loc, format, args); |
| 861 | return self; | 1520 | return err_msg; |
| 862 | } | 1521 | } |
| 863 | 1522 | ||
| 864 | /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`, | 1523 | /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`, |
| 865 | /// as well as all notes. | 1524 | /// as well as all notes. |
| 866 | pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void { | 1525 | pub fn destroy(err_msg: *ErrorMsg, gpa: *Allocator) void { |
| 867 | self.deinit(gpa); | 1526 | err_msg.deinit(gpa); |
| 868 | gpa.destroy(self); | 1527 | gpa.destroy(err_msg); |
| 869 | } | 1528 | } |
| 870 | 1529 | ||
| 871 | pub fn init( | 1530 | pub fn init( |
| ... | @@ -880,84 +1539,715 @@ pub const ErrorMsg = struct { | ... | @@ -880,84 +1539,715 @@ pub const ErrorMsg = struct { |
| 880 | }; | 1539 | }; |
| 881 | } | 1540 | } |
| 882 | 1541 | ||
| 883 | pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void { | 1542 | pub fn deinit(err_msg: *ErrorMsg, gpa: *Allocator) void { |
| 884 | for (self.notes) |*note| { | 1543 | for (err_msg.notes) |*note| { |
| 885 | note.deinit(gpa); | 1544 | note.deinit(gpa); |
| 886 | } | 1545 | } |
| 887 | gpa.free(self.notes); | 1546 | gpa.free(err_msg.notes); |
| 888 | gpa.free(self.msg); | 1547 | gpa.free(err_msg.msg); |
| 889 | self.* = undefined; | 1548 | err_msg.* = undefined; |
| 890 | } | 1549 | } |
| 891 | }; | 1550 | }; |
| 892 | 1551 | ||
| 893 | /// Canonical reference to a position within a source file. | 1552 | /// Canonical reference to a position within a source file. |
| 894 | pub const SrcLoc = struct { | 1553 | pub const SrcLoc = struct { |
| 895 | file_scope: *Scope.File, | 1554 | /// The active field is determined by tag of `lazy`. |
| 896 | byte_offset: usize, | 1555 | container: union { |
| 897 | }; | 1556 | /// The containing `Decl` according to the source code. |
| 898 | 1557 | decl: *Decl, | |
| 899 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; | 1558 | file_scope: *Scope.File, |
| 900 | 1559 | }, | |
| 901 | pub fn deinit(self: *Module) void { | 1560 | /// Relative to `decl`. |
| 902 | const gpa = self.gpa; | 1561 | lazy: LazySrcLoc, |
| 903 | 1562 | ||
| 904 | self.compile_log_text.deinit(gpa); | 1563 | pub fn fileScope(src_loc: SrcLoc) *Scope.File { |
| 905 | 1564 | return switch (src_loc.lazy) { | |
| 906 | self.zig_cache_artifact_directory.handle.close(); | 1565 | .unneeded => unreachable, |
| 907 | 1566 | ||
| 908 | self.deletion_set.deinit(gpa); | 1567 | .byte_abs, |
| 909 | 1568 | .token_abs, | |
| 910 | for (self.decl_table.items()) |entry| { | 1569 | .node_abs, |
| 911 | entry.value.destroy(self); | 1570 | => src_loc.container.file_scope, |
| 912 | } | 1571 | |
| 913 | self.decl_table.deinit(gpa); | 1572 | .byte_offset, |
| 914 | 1573 | .token_offset, | |
| 915 | for (self.failed_decls.items()) |entry| { | 1574 | .node_offset, |
| 916 | entry.value.destroy(gpa); | 1575 | .node_offset_var_decl_ty, |
| 917 | } | 1576 | .node_offset_for_cond, |
| 918 | self.failed_decls.deinit(gpa); | 1577 | .node_offset_builtin_call_arg0, |
| 919 | 1578 | .node_offset_builtin_call_arg1, | |
| 920 | for (self.emit_h_failed_decls.items()) |entry| { | 1579 | .node_offset_array_access_index, |
| 921 | entry.value.destroy(gpa); | 1580 | .node_offset_slice_sentinel, |
| 922 | } | 1581 | .node_offset_call_func, |
| 923 | self.emit_h_failed_decls.deinit(gpa); | 1582 | .node_offset_field_name, |
| 924 | 1583 | .node_offset_deref_ptr, | |
| 925 | for (self.failed_files.items()) |entry| { | 1584 | .node_offset_asm_source, |
| 926 | entry.value.destroy(gpa); | 1585 | .node_offset_asm_ret_ty, |
| 927 | } | 1586 | .node_offset_if_cond, |
| 928 | self.failed_files.deinit(gpa); | 1587 | .node_offset_bin_op, |
| 929 | 1588 | .node_offset_bin_lhs, | |
| 930 | for (self.failed_exports.items()) |entry| { | 1589 | .node_offset_bin_rhs, |
| 931 | entry.value.destroy(gpa); | 1590 | .node_offset_switch_operand, |
| 932 | } | 1591 | .node_offset_switch_special_prong, |
| 933 | self.failed_exports.deinit(gpa); | 1592 | .node_offset_switch_range, |
| 934 | 1593 | .node_offset_fn_type_cc, | |
| 935 | self.compile_log_decls.deinit(gpa); | 1594 | .node_offset_fn_type_ret_ty, |
| 936 | 1595 | => src_loc.container.decl.container.file_scope, | |
| 937 | for (self.decl_exports.items()) |entry| { | 1596 | }; |
| 938 | const export_list = entry.value; | ||
| 939 | gpa.free(export_list); | ||
| 940 | } | ||
| 941 | self.decl_exports.deinit(gpa); | ||
| 942 | |||
| 943 | for (self.export_owners.items()) |entry| { | ||
| 944 | freeExportList(gpa, entry.value); | ||
| 945 | } | 1597 | } |
| 946 | self.export_owners.deinit(gpa); | ||
| 947 | 1598 | ||
| 948 | self.symbol_exports.deinit(gpa); | 1599 | pub fn byteOffset(src_loc: SrcLoc) !u32 { |
| 949 | self.root_scope.destroy(gpa); | 1600 | switch (src_loc.lazy) { |
| 1601 | .unneeded => unreachable, | ||
| 950 | 1602 | ||
| 951 | var it = self.global_error_set.iterator(); | 1603 | .byte_abs => |byte_index| return byte_index, |
| 952 | while (it.next()) |entry| { | ||
| 953 | gpa.free(entry.key); | ||
| 954 | } | ||
| 955 | self.global_error_set.deinit(gpa); | ||
| 956 | 1604 | ||
| 957 | for (self.import_table.items()) |entry| { | 1605 | .token_abs => |tok_index| { |
| 1606 | const tree = src_loc.container.file_scope.base.tree(); | ||
| 1607 | const token_starts = tree.tokens.items(.start); | ||
| 1608 | return token_starts[tok_index]; | ||
| 1609 | }, | ||
| 1610 | .node_abs => |node| { | ||
| 1611 | const tree = src_loc.container.file_scope.base.tree(); | ||
| 1612 | const token_starts = tree.tokens.items(.start); | ||
| 1613 | const tok_index = tree.firstToken(node); | ||
| 1614 | return token_starts[tok_index]; | ||
| 1615 | }, | ||
| 1616 | .byte_offset => |byte_off| { | ||
| 1617 | const decl = src_loc.container.decl; | ||
| 1618 | return decl.srcByteOffset() + byte_off; | ||
| 1619 | }, | ||
| 1620 | .token_offset => |tok_off| { | ||
| 1621 | const decl = src_loc.container.decl; | ||
| 1622 | const tok_index = decl.srcToken() + tok_off; | ||
| 1623 | const tree = decl.container.file_scope.base.tree(); | ||
| 1624 | const token_starts = tree.tokens.items(.start); | ||
| 1625 | return token_starts[tok_index]; | ||
| 1626 | }, | ||
| 1627 | .node_offset, .node_offset_bin_op => |node_off| { | ||
| 1628 | const decl = src_loc.container.decl; | ||
| 1629 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1630 | const tree = decl.container.file_scope.base.tree(); | ||
| 1631 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1632 | const tok_index = main_tokens[node]; | ||
| 1633 | const token_starts = tree.tokens.items(.start); | ||
| 1634 | return token_starts[tok_index]; | ||
| 1635 | }, | ||
| 1636 | .node_offset_var_decl_ty => |node_off| { | ||
| 1637 | const decl = src_loc.container.decl; | ||
| 1638 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1639 | const tree = decl.container.file_scope.base.tree(); | ||
| 1640 | const node_tags = tree.nodes.items(.tag); | ||
| 1641 | const full = switch (node_tags[node]) { | ||
| 1642 | .global_var_decl => tree.globalVarDecl(node), | ||
| 1643 | .local_var_decl => tree.localVarDecl(node), | ||
| 1644 | .simple_var_decl => tree.simpleVarDecl(node), | ||
| 1645 | .aligned_var_decl => tree.alignedVarDecl(node), | ||
| 1646 | else => unreachable, | ||
| 1647 | }; | ||
| 1648 | const tok_index = if (full.ast.type_node != 0) blk: { | ||
| 1649 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1650 | break :blk main_tokens[full.ast.type_node]; | ||
| 1651 | } else blk: { | ||
| 1652 | break :blk full.ast.mut_token + 1; // the name token | ||
| 1653 | }; | ||
| 1654 | const token_starts = tree.tokens.items(.start); | ||
| 1655 | return token_starts[tok_index]; | ||
| 1656 | }, | ||
| 1657 | .node_offset_builtin_call_arg0 => |node_off| { | ||
| 1658 | const decl = src_loc.container.decl; | ||
| 1659 | const tree = decl.container.file_scope.base.tree(); | ||
| 1660 | const node_datas = tree.nodes.items(.data); | ||
| 1661 | const node_tags = tree.nodes.items(.tag); | ||
| 1662 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1663 | const param = switch (node_tags[node]) { | ||
| 1664 | .builtin_call_two, .builtin_call_two_comma => node_datas[node].lhs, | ||
| 1665 | .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs], | ||
| 1666 | else => unreachable, | ||
| 1667 | }; | ||
| 1668 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1669 | const tok_index = main_tokens[param]; | ||
| 1670 | const token_starts = tree.tokens.items(.start); | ||
| 1671 | return token_starts[tok_index]; | ||
| 1672 | }, | ||
| 1673 | .node_offset_builtin_call_arg1 => |node_off| { | ||
| 1674 | const decl = src_loc.container.decl; | ||
| 1675 | const tree = decl.container.file_scope.base.tree(); | ||
| 1676 | const node_datas = tree.nodes.items(.data); | ||
| 1677 | const node_tags = tree.nodes.items(.tag); | ||
| 1678 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1679 | const param = switch (node_tags[node]) { | ||
| 1680 | .builtin_call_two, .builtin_call_two_comma => node_datas[node].rhs, | ||
| 1681 | .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + 1], | ||
| 1682 | else => unreachable, | ||
| 1683 | }; | ||
| 1684 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1685 | const tok_index = main_tokens[param]; | ||
| 1686 | const token_starts = tree.tokens.items(.start); | ||
| 1687 | return token_starts[tok_index]; | ||
| 1688 | }, | ||
| 1689 | .node_offset_array_access_index => |node_off| { | ||
| 1690 | const decl = src_loc.container.decl; | ||
| 1691 | const tree = decl.container.file_scope.base.tree(); | ||
| 1692 | const node_datas = tree.nodes.items(.data); | ||
| 1693 | const node_tags = tree.nodes.items(.tag); | ||
| 1694 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1695 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1696 | const tok_index = main_tokens[node_datas[node].rhs]; | ||
| 1697 | const token_starts = tree.tokens.items(.start); | ||
| 1698 | return token_starts[tok_index]; | ||
| 1699 | }, | ||
| 1700 | .node_offset_slice_sentinel => |node_off| { | ||
| 1701 | const decl = src_loc.container.decl; | ||
| 1702 | const tree = decl.container.file_scope.base.tree(); | ||
| 1703 | const node_datas = tree.nodes.items(.data); | ||
| 1704 | const node_tags = tree.nodes.items(.tag); | ||
| 1705 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1706 | const full = switch (node_tags[node]) { | ||
| 1707 | .slice_open => tree.sliceOpen(node), | ||
| 1708 | .slice => tree.slice(node), | ||
| 1709 | .slice_sentinel => tree.sliceSentinel(node), | ||
| 1710 | else => unreachable, | ||
| 1711 | }; | ||
| 1712 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1713 | const tok_index = main_tokens[full.ast.sentinel]; | ||
| 1714 | const token_starts = tree.tokens.items(.start); | ||
| 1715 | return token_starts[tok_index]; | ||
| 1716 | }, | ||
| 1717 | .node_offset_call_func => |node_off| { | ||
| 1718 | const decl = src_loc.container.decl; | ||
| 1719 | const tree = decl.container.file_scope.base.tree(); | ||
| 1720 | const node_datas = tree.nodes.items(.data); | ||
| 1721 | const node_tags = tree.nodes.items(.tag); | ||
| 1722 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1723 | var params: [1]ast.Node.Index = undefined; | ||
| 1724 | const full = switch (node_tags[node]) { | ||
| 1725 | .call_one, | ||
| 1726 | .call_one_comma, | ||
| 1727 | .async_call_one, | ||
| 1728 | .async_call_one_comma, | ||
| 1729 | => tree.callOne(&params, node), | ||
| 1730 | |||
| 1731 | .call, | ||
| 1732 | .call_comma, | ||
| 1733 | .async_call, | ||
| 1734 | .async_call_comma, | ||
| 1735 | => tree.callFull(node), | ||
| 1736 | |||
| 1737 | else => unreachable, | ||
| 1738 | }; | ||
| 1739 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1740 | const tok_index = main_tokens[full.ast.fn_expr]; | ||
| 1741 | const token_starts = tree.tokens.items(.start); | ||
| 1742 | return token_starts[tok_index]; | ||
| 1743 | }, | ||
| 1744 | .node_offset_field_name => |node_off| { | ||
| 1745 | const decl = src_loc.container.decl; | ||
| 1746 | const tree = decl.container.file_scope.base.tree(); | ||
| 1747 | const node_datas = tree.nodes.items(.data); | ||
| 1748 | const node_tags = tree.nodes.items(.tag); | ||
| 1749 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1750 | const tok_index = node_datas[node].rhs; | ||
| 1751 | const token_starts = tree.tokens.items(.start); | ||
| 1752 | return token_starts[tok_index]; | ||
| 1753 | }, | ||
| 1754 | .node_offset_deref_ptr => |node_off| { | ||
| 1755 | const decl = src_loc.container.decl; | ||
| 1756 | const tree = decl.container.file_scope.base.tree(); | ||
| 1757 | const node_datas = tree.nodes.items(.data); | ||
| 1758 | const node_tags = tree.nodes.items(.tag); | ||
| 1759 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1760 | const tok_index = node_datas[node].lhs; | ||
| 1761 | const token_starts = tree.tokens.items(.start); | ||
| 1762 | return token_starts[tok_index]; | ||
| 1763 | }, | ||
| 1764 | .node_offset_asm_source => |node_off| { | ||
| 1765 | const decl = src_loc.container.decl; | ||
| 1766 | const tree = decl.container.file_scope.base.tree(); | ||
| 1767 | const node_datas = tree.nodes.items(.data); | ||
| 1768 | const node_tags = tree.nodes.items(.tag); | ||
| 1769 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1770 | const full = switch (node_tags[node]) { | ||
| 1771 | .asm_simple => tree.asmSimple(node), | ||
| 1772 | .@"asm" => tree.asmFull(node), | ||
| 1773 | else => unreachable, | ||
| 1774 | }; | ||
| 1775 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1776 | const tok_index = main_tokens[full.ast.template]; | ||
| 1777 | const token_starts = tree.tokens.items(.start); | ||
| 1778 | return token_starts[tok_index]; | ||
| 1779 | }, | ||
| 1780 | .node_offset_asm_ret_ty => |node_off| { | ||
| 1781 | const decl = src_loc.container.decl; | ||
| 1782 | const tree = decl.container.file_scope.base.tree(); | ||
| 1783 | const node_datas = tree.nodes.items(.data); | ||
| 1784 | const node_tags = tree.nodes.items(.tag); | ||
| 1785 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1786 | const full = switch (node_tags[node]) { | ||
| 1787 | .asm_simple => tree.asmSimple(node), | ||
| 1788 | .@"asm" => tree.asmFull(node), | ||
| 1789 | else => unreachable, | ||
| 1790 | }; | ||
| 1791 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1792 | const tok_index = main_tokens[full.outputs[0]]; | ||
| 1793 | const token_starts = tree.tokens.items(.start); | ||
| 1794 | return token_starts[tok_index]; | ||
| 1795 | }, | ||
| 1796 | |||
| 1797 | .node_offset_for_cond, .node_offset_if_cond => |node_off| { | ||
| 1798 | const decl = src_loc.container.decl; | ||
| 1799 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1800 | const tree = decl.container.file_scope.base.tree(); | ||
| 1801 | const node_tags = tree.nodes.items(.tag); | ||
| 1802 | const src_node = switch (node_tags[node]) { | ||
| 1803 | .if_simple => tree.ifSimple(node).ast.cond_expr, | ||
| 1804 | .@"if" => tree.ifFull(node).ast.cond_expr, | ||
| 1805 | .while_simple => tree.whileSimple(node).ast.cond_expr, | ||
| 1806 | .while_cont => tree.whileCont(node).ast.cond_expr, | ||
| 1807 | .@"while" => tree.whileFull(node).ast.cond_expr, | ||
| 1808 | .for_simple => tree.forSimple(node).ast.cond_expr, | ||
| 1809 | .@"for" => tree.forFull(node).ast.cond_expr, | ||
| 1810 | else => unreachable, | ||
| 1811 | }; | ||
| 1812 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1813 | const tok_index = main_tokens[src_node]; | ||
| 1814 | const token_starts = tree.tokens.items(.start); | ||
| 1815 | return token_starts[tok_index]; | ||
| 1816 | }, | ||
| 1817 | .node_offset_bin_lhs => |node_off| { | ||
| 1818 | const decl = src_loc.container.decl; | ||
| 1819 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1820 | const tree = decl.container.file_scope.base.tree(); | ||
| 1821 | const node_datas = tree.nodes.items(.data); | ||
| 1822 | const src_node = node_datas[node].lhs; | ||
| 1823 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1824 | const tok_index = main_tokens[src_node]; | ||
| 1825 | const token_starts = tree.tokens.items(.start); | ||
| 1826 | return token_starts[tok_index]; | ||
| 1827 | }, | ||
| 1828 | .node_offset_bin_rhs => |node_off| { | ||
| 1829 | const decl = src_loc.container.decl; | ||
| 1830 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1831 | const tree = decl.container.file_scope.base.tree(); | ||
| 1832 | const node_datas = tree.nodes.items(.data); | ||
| 1833 | const src_node = node_datas[node].rhs; | ||
| 1834 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1835 | const tok_index = main_tokens[src_node]; | ||
| 1836 | const token_starts = tree.tokens.items(.start); | ||
| 1837 | return token_starts[tok_index]; | ||
| 1838 | }, | ||
| 1839 | |||
| 1840 | .node_offset_switch_operand => |node_off| { | ||
| 1841 | const decl = src_loc.container.decl; | ||
| 1842 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1843 | const tree = decl.container.file_scope.base.tree(); | ||
| 1844 | const node_datas = tree.nodes.items(.data); | ||
| 1845 | const src_node = node_datas[node].lhs; | ||
| 1846 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1847 | const tok_index = main_tokens[src_node]; | ||
| 1848 | const token_starts = tree.tokens.items(.start); | ||
| 1849 | return token_starts[tok_index]; | ||
| 1850 | }, | ||
| 1851 | |||
| 1852 | .node_offset_switch_special_prong => |node_off| { | ||
| 1853 | const decl = src_loc.container.decl; | ||
| 1854 | const switch_node = decl.relativeToNodeIndex(node_off); | ||
| 1855 | const tree = decl.container.file_scope.base.tree(); | ||
| 1856 | const node_datas = tree.nodes.items(.data); | ||
| 1857 | const node_tags = tree.nodes.items(.tag); | ||
| 1858 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1859 | const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); | ||
| 1860 | const case_nodes = tree.extra_data[extra.start..extra.end]; | ||
| 1861 | for (case_nodes) |case_node| { | ||
| 1862 | const case = switch (node_tags[case_node]) { | ||
| 1863 | .switch_case_one => tree.switchCaseOne(case_node), | ||
| 1864 | .switch_case => tree.switchCase(case_node), | ||
| 1865 | else => unreachable, | ||
| 1866 | }; | ||
| 1867 | const is_special = (case.ast.values.len == 0) or | ||
| 1868 | (case.ast.values.len == 1 and | ||
| 1869 | node_tags[case.ast.values[0]] == .identifier and | ||
| 1870 | mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")); | ||
| 1871 | if (!is_special) continue; | ||
| 1872 | |||
| 1873 | const tok_index = main_tokens[case_node]; | ||
| 1874 | const token_starts = tree.tokens.items(.start); | ||
| 1875 | return token_starts[tok_index]; | ||
| 1876 | } else unreachable; | ||
| 1877 | }, | ||
| 1878 | |||
| 1879 | .node_offset_switch_range => |node_off| { | ||
| 1880 | const decl = src_loc.container.decl; | ||
| 1881 | const switch_node = decl.relativeToNodeIndex(node_off); | ||
| 1882 | const tree = decl.container.file_scope.base.tree(); | ||
| 1883 | const node_datas = tree.nodes.items(.data); | ||
| 1884 | const node_tags = tree.nodes.items(.tag); | ||
| 1885 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1886 | const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); | ||
| 1887 | const case_nodes = tree.extra_data[extra.start..extra.end]; | ||
| 1888 | for (case_nodes) |case_node| { | ||
| 1889 | const case = switch (node_tags[case_node]) { | ||
| 1890 | .switch_case_one => tree.switchCaseOne(case_node), | ||
| 1891 | .switch_case => tree.switchCase(case_node), | ||
| 1892 | else => unreachable, | ||
| 1893 | }; | ||
| 1894 | const is_special = (case.ast.values.len == 0) or | ||
| 1895 | (case.ast.values.len == 1 and | ||
| 1896 | node_tags[case.ast.values[0]] == .identifier and | ||
| 1897 | mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")); | ||
| 1898 | if (is_special) continue; | ||
| 1899 | |||
| 1900 | for (case.ast.values) |item_node| { | ||
| 1901 | if (node_tags[item_node] == .switch_range) { | ||
| 1902 | const tok_index = main_tokens[item_node]; | ||
| 1903 | const token_starts = tree.tokens.items(.start); | ||
| 1904 | return token_starts[tok_index]; | ||
| 1905 | } | ||
| 1906 | } | ||
| 1907 | } else unreachable; | ||
| 1908 | }, | ||
| 1909 | |||
| 1910 | .node_offset_fn_type_cc => |node_off| { | ||
| 1911 | const decl = src_loc.container.decl; | ||
| 1912 | const tree = decl.container.file_scope.base.tree(); | ||
| 1913 | const node_datas = tree.nodes.items(.data); | ||
| 1914 | const node_tags = tree.nodes.items(.tag); | ||
| 1915 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1916 | var params: [1]ast.Node.Index = undefined; | ||
| 1917 | const full = switch (node_tags[node]) { | ||
| 1918 | .fn_proto_simple => tree.fnProtoSimple(&params, node), | ||
| 1919 | .fn_proto_multi => tree.fnProtoMulti(node), | ||
| 1920 | .fn_proto_one => tree.fnProtoOne(&params, node), | ||
| 1921 | .fn_proto => tree.fnProto(node), | ||
| 1922 | else => unreachable, | ||
| 1923 | }; | ||
| 1924 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1925 | const tok_index = main_tokens[full.ast.callconv_expr]; | ||
| 1926 | const token_starts = tree.tokens.items(.start); | ||
| 1927 | return token_starts[tok_index]; | ||
| 1928 | }, | ||
| 1929 | |||
| 1930 | .node_offset_fn_type_ret_ty => |node_off| { | ||
| 1931 | const decl = src_loc.container.decl; | ||
| 1932 | const tree = decl.container.file_scope.base.tree(); | ||
| 1933 | const node_datas = tree.nodes.items(.data); | ||
| 1934 | const node_tags = tree.nodes.items(.tag); | ||
| 1935 | const node = decl.relativeToNodeIndex(node_off); | ||
| 1936 | var params: [1]ast.Node.Index = undefined; | ||
| 1937 | const full = switch (node_tags[node]) { | ||
| 1938 | .fn_proto_simple => tree.fnProtoSimple(&params, node), | ||
| 1939 | .fn_proto_multi => tree.fnProtoMulti(node), | ||
| 1940 | .fn_proto_one => tree.fnProtoOne(&params, node), | ||
| 1941 | .fn_proto => tree.fnProto(node), | ||
| 1942 | else => unreachable, | ||
| 1943 | }; | ||
| 1944 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1945 | const tok_index = main_tokens[full.ast.return_type]; | ||
| 1946 | const token_starts = tree.tokens.items(.start); | ||
| 1947 | return token_starts[tok_index]; | ||
| 1948 | }, | ||
| 1949 | } | ||
| 1950 | } | ||
| 1951 | }; | ||
| 1952 | |||
| 1953 | /// Resolving a source location into a byte offset may require doing work | ||
| 1954 | /// that we would rather not do unless the error actually occurs. | ||
| 1955 | /// Therefore we need a data structure that contains the information necessary | ||
| 1956 | /// to lazily produce a `SrcLoc` as required. | ||
| 1957 | /// Most of the offsets in this data structure are relative to the containing Decl. | ||
| 1958 | /// This makes the source location resolve properly even when a Decl gets | ||
| 1959 | /// shifted up or down in the file, as long as the Decl's contents itself | ||
| 1960 | /// do not change. | ||
| 1961 | pub const LazySrcLoc = union(enum) { | ||
| 1962 | /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting | ||
| 1963 | /// that all code paths which would need to resolve the source location are | ||
| 1964 | /// unreachable. If you are debugging this tag incorrectly being this value, | ||
| 1965 | /// look into using reverse-continue with a memory watchpoint to see where the | ||
| 1966 | /// value is being set to this tag. | ||
| 1967 | unneeded, | ||
| 1968 | /// The source location points to a byte offset within a source file, | ||
| 1969 | /// offset from 0. The source file is determined contextually. | ||
| 1970 | /// Inside a `SrcLoc`, the `file_scope` union field will be active. | ||
| 1971 | byte_abs: u32, | ||
| 1972 | /// The source location points to a token within a source file, | ||
| 1973 | /// offset from 0. The source file is determined contextually. | ||
| 1974 | /// Inside a `SrcLoc`, the `file_scope` union field will be active. | ||
| 1975 | token_abs: u32, | ||
| 1976 | /// The source location points to an AST node within a source file, | ||
| 1977 | /// offset from 0. The source file is determined contextually. | ||
| 1978 | /// Inside a `SrcLoc`, the `file_scope` union field will be active. | ||
| 1979 | node_abs: u32, | ||
| 1980 | /// The source location points to a byte offset within a source file, | ||
| 1981 | /// offset from the byte offset of the Decl within the file. | ||
| 1982 | /// The Decl is determined contextually. | ||
| 1983 | byte_offset: u32, | ||
| 1984 | /// This data is the offset into the token list from the Decl token. | ||
| 1985 | /// The Decl is determined contextually. | ||
| 1986 | token_offset: u32, | ||
| 1987 | /// The source location points to an AST node, which is this value offset | ||
| 1988 | /// from its containing Decl node AST index. | ||
| 1989 | /// The Decl is determined contextually. | ||
| 1990 | node_offset: i32, | ||
| 1991 | /// The source location points to a variable declaration type expression, | ||
| 1992 | /// found by taking this AST node index offset from the containing | ||
| 1993 | /// Decl AST node, which points to a variable declaration AST node. Next, navigate | ||
| 1994 | /// to the type expression. | ||
| 1995 | /// The Decl is determined contextually. | ||
| 1996 | node_offset_var_decl_ty: i32, | ||
| 1997 | /// The source location points to a for loop condition expression, | ||
| 1998 | /// found by taking this AST node index offset from the containing | ||
| 1999 | /// Decl AST node, which points to a for loop AST node. Next, navigate | ||
| 2000 | /// to the condition expression. | ||
| 2001 | /// The Decl is determined contextually. | ||
| 2002 | node_offset_for_cond: i32, | ||
| 2003 | /// The source location points to the first parameter of a builtin | ||
| 2004 | /// function call, found by taking this AST node index offset from the containing | ||
| 2005 | /// Decl AST node, which points to a builtin call AST node. Next, navigate | ||
| 2006 | /// to the first parameter. | ||
| 2007 | /// The Decl is determined contextually. | ||
| 2008 | node_offset_builtin_call_arg0: i32, | ||
| 2009 | /// Same as `node_offset_builtin_call_arg0` except arg index 1. | ||
| 2010 | node_offset_builtin_call_arg1: i32, | ||
| 2011 | /// The source location points to the index expression of an array access | ||
| 2012 | /// expression, found by taking this AST node index offset from the containing | ||
| 2013 | /// Decl AST node, which points to an array access AST node. Next, navigate | ||
| 2014 | /// to the index expression. | ||
| 2015 | /// The Decl is determined contextually. | ||
| 2016 | node_offset_array_access_index: i32, | ||
| 2017 | /// The source location points to the sentinel expression of a slice | ||
| 2018 | /// expression, found by taking this AST node index offset from the containing | ||
| 2019 | /// Decl AST node, which points to a slice AST node. Next, navigate | ||
| 2020 | /// to the sentinel expression. | ||
| 2021 | /// The Decl is determined contextually. | ||
| 2022 | node_offset_slice_sentinel: i32, | ||
| 2023 | /// The source location points to the callee expression of a function | ||
| 2024 | /// call expression, found by taking this AST node index offset from the containing | ||
| 2025 | /// Decl AST node, which points to a function call AST node. Next, navigate | ||
| 2026 | /// to the callee expression. | ||
| 2027 | /// The Decl is determined contextually. | ||
| 2028 | node_offset_call_func: i32, | ||
| 2029 | /// The source location points to the field name of a field access expression, | ||
| 2030 | /// found by taking this AST node index offset from the containing | ||
| 2031 | /// Decl AST node, which points to a field access AST node. Next, navigate | ||
| 2032 | /// to the field name token. | ||
| 2033 | /// The Decl is determined contextually. | ||
| 2034 | node_offset_field_name: i32, | ||
| 2035 | /// The source location points to the pointer of a pointer deref expression, | ||
| 2036 | /// found by taking this AST node index offset from the containing | ||
| 2037 | /// Decl AST node, which points to a pointer deref AST node. Next, navigate | ||
| 2038 | /// to the pointer expression. | ||
| 2039 | /// The Decl is determined contextually. | ||
| 2040 | node_offset_deref_ptr: i32, | ||
| 2041 | /// The source location points to the assembly source code of an inline assembly | ||
| 2042 | /// expression, found by taking this AST node index offset from the containing | ||
| 2043 | /// Decl AST node, which points to inline assembly AST node. Next, navigate | ||
| 2044 | /// to the asm template source code. | ||
| 2045 | /// The Decl is determined contextually. | ||
| 2046 | node_offset_asm_source: i32, | ||
| 2047 | /// The source location points to the return type of an inline assembly | ||
| 2048 | /// expression, found by taking this AST node index offset from the containing | ||
| 2049 | /// Decl AST node, which points to inline assembly AST node. Next, navigate | ||
| 2050 | /// to the return type expression. | ||
| 2051 | /// The Decl is determined contextually. | ||
| 2052 | node_offset_asm_ret_ty: i32, | ||
| 2053 | /// The source location points to the condition expression of an if | ||
| 2054 | /// expression, found by taking this AST node index offset from the containing | ||
| 2055 | /// Decl AST node, which points to an if expression AST node. Next, navigate | ||
| 2056 | /// to the condition expression. | ||
| 2057 | /// The Decl is determined contextually. | ||
| 2058 | node_offset_if_cond: i32, | ||
| 2059 | /// The source location points to a binary expression, such as `a + b`, found | ||
| 2060 | /// by taking this AST node index offset from the containing Decl AST node. | ||
| 2061 | /// The Decl is determined contextually. | ||
| 2062 | node_offset_bin_op: i32, | ||
| 2063 | /// The source location points to the LHS of a binary expression, found | ||
| 2064 | /// by taking this AST node index offset from the containing Decl AST node, | ||
| 2065 | /// which points to a binary expression AST node. Next, nagivate to the LHS. | ||
| 2066 | /// The Decl is determined contextually. | ||
| 2067 | node_offset_bin_lhs: i32, | ||
| 2068 | /// The source location points to the RHS of a binary expression, found | ||
| 2069 | /// by taking this AST node index offset from the containing Decl AST node, | ||
| 2070 | /// which points to a binary expression AST node. Next, nagivate to the RHS. | ||
| 2071 | /// The Decl is determined contextually. | ||
| 2072 | node_offset_bin_rhs: i32, | ||
| 2073 | /// The source location points to the operand of a switch expression, found | ||
| 2074 | /// by taking this AST node index offset from the containing Decl AST node, | ||
| 2075 | /// which points to a switch expression AST node. Next, nagivate to the operand. | ||
| 2076 | /// The Decl is determined contextually. | ||
| 2077 | node_offset_switch_operand: i32, | ||
| 2078 | /// The source location points to the else/`_` prong of a switch expression, found | ||
| 2079 | /// by taking this AST node index offset from the containing Decl AST node, | ||
| 2080 | /// which points to a switch expression AST node. Next, nagivate to the else/`_` prong. | ||
| 2081 | /// The Decl is determined contextually. | ||
| 2082 | node_offset_switch_special_prong: i32, | ||
| 2083 | /// The source location points to all the ranges of a switch expression, found | ||
| 2084 | /// by taking this AST node index offset from the containing Decl AST node, | ||
| 2085 | /// which points to a switch expression AST node. Next, nagivate to any of the | ||
| 2086 | /// range nodes. The error applies to all of them. | ||
| 2087 | /// The Decl is determined contextually. | ||
| 2088 | node_offset_switch_range: i32, | ||
| 2089 | /// The source location points to the calling convention of a function type | ||
| 2090 | /// expression, found by taking this AST node index offset from the containing | ||
| 2091 | /// Decl AST node, which points to a function type AST node. Next, nagivate to | ||
| 2092 | /// the calling convention node. | ||
| 2093 | /// The Decl is determined contextually. | ||
| 2094 | node_offset_fn_type_cc: i32, | ||
| 2095 | /// The source location points to the return type of a function type | ||
| 2096 | /// expression, found by taking this AST node index offset from the containing | ||
| 2097 | /// Decl AST node, which points to a function type AST node. Next, nagivate to | ||
| 2098 | /// the return type node. | ||
| 2099 | /// The Decl is determined contextually. | ||
| 2100 | node_offset_fn_type_ret_ty: i32, | ||
| 2101 | |||
| 2102 | /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope. | ||
| 2103 | pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc { | ||
| 2104 | return switch (lazy) { | ||
| 2105 | .unneeded, | ||
| 2106 | .byte_abs, | ||
| 2107 | .token_abs, | ||
| 2108 | .node_abs, | ||
| 2109 | => .{ | ||
| 2110 | .container = .{ .file_scope = scope.getFileScope() }, | ||
| 2111 | .lazy = lazy, | ||
| 2112 | }, | ||
| 2113 | |||
| 2114 | .byte_offset, | ||
| 2115 | .token_offset, | ||
| 2116 | .node_offset, | ||
| 2117 | .node_offset_var_decl_ty, | ||
| 2118 | .node_offset_for_cond, | ||
| 2119 | .node_offset_builtin_call_arg0, | ||
| 2120 | .node_offset_builtin_call_arg1, | ||
| 2121 | .node_offset_array_access_index, | ||
| 2122 | .node_offset_slice_sentinel, | ||
| 2123 | .node_offset_call_func, | ||
| 2124 | .node_offset_field_name, | ||
| 2125 | .node_offset_deref_ptr, | ||
| 2126 | .node_offset_asm_source, | ||
| 2127 | .node_offset_asm_ret_ty, | ||
| 2128 | .node_offset_if_cond, | ||
| 2129 | .node_offset_bin_op, | ||
| 2130 | .node_offset_bin_lhs, | ||
| 2131 | .node_offset_bin_rhs, | ||
| 2132 | .node_offset_switch_operand, | ||
| 2133 | .node_offset_switch_special_prong, | ||
| 2134 | .node_offset_switch_range, | ||
| 2135 | .node_offset_fn_type_cc, | ||
| 2136 | .node_offset_fn_type_ret_ty, | ||
| 2137 | => .{ | ||
| 2138 | .container = .{ .decl = scope.srcDecl().? }, | ||
| 2139 | .lazy = lazy, | ||
| 2140 | }, | ||
| 2141 | }; | ||
| 2142 | } | ||
| 2143 | |||
| 2144 | /// Upgrade to a `SrcLoc` based on the `Decl` provided. | ||
| 2145 | pub fn toSrcLocWithDecl(lazy: LazySrcLoc, decl: *Decl) SrcLoc { | ||
| 2146 | return switch (lazy) { | ||
| 2147 | .unneeded, | ||
| 2148 | .byte_abs, | ||
| 2149 | .token_abs, | ||
| 2150 | .node_abs, | ||
| 2151 | => .{ | ||
| 2152 | .container = .{ .file_scope = decl.getFileScope() }, | ||
| 2153 | .lazy = lazy, | ||
| 2154 | }, | ||
| 2155 | |||
| 2156 | .byte_offset, | ||
| 2157 | .token_offset, | ||
| 2158 | .node_offset, | ||
| 2159 | .node_offset_var_decl_ty, | ||
| 2160 | .node_offset_for_cond, | ||
| 2161 | .node_offset_builtin_call_arg0, | ||
| 2162 | .node_offset_builtin_call_arg1, | ||
| 2163 | .node_offset_array_access_index, | ||
| 2164 | .node_offset_slice_sentinel, | ||
| 2165 | .node_offset_call_func, | ||
| 2166 | .node_offset_field_name, | ||
| 2167 | .node_offset_deref_ptr, | ||
| 2168 | .node_offset_asm_source, | ||
| 2169 | .node_offset_asm_ret_ty, | ||
| 2170 | .node_offset_if_cond, | ||
| 2171 | .node_offset_bin_op, | ||
| 2172 | .node_offset_bin_lhs, | ||
| 2173 | .node_offset_bin_rhs, | ||
| 2174 | .node_offset_switch_operand, | ||
| 2175 | .node_offset_switch_special_prong, | ||
| 2176 | .node_offset_switch_range, | ||
| 2177 | .node_offset_fn_type_cc, | ||
| 2178 | .node_offset_fn_type_ret_ty, | ||
| 2179 | => .{ | ||
| 2180 | .container = .{ .decl = decl }, | ||
| 2181 | .lazy = lazy, | ||
| 2182 | }, | ||
| 2183 | }; | ||
| 2184 | } | ||
| 2185 | }; | ||
| 2186 | |||
| 2187 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; | ||
| 2188 | |||
| 2189 | pub fn deinit(mod: *Module) void { | ||
| 2190 | const gpa = mod.gpa; | ||
| 2191 | |||
| 2192 | mod.compile_log_text.deinit(gpa); | ||
| 2193 | |||
| 2194 | mod.zig_cache_artifact_directory.handle.close(); | ||
| 2195 | |||
| 2196 | mod.deletion_set.deinit(gpa); | ||
| 2197 | |||
| 2198 | for (mod.decl_table.items()) |entry| { | ||
| 2199 | entry.value.destroy(mod); | ||
| 2200 | } | ||
| 2201 | mod.decl_table.deinit(gpa); | ||
| 2202 | |||
| 2203 | for (mod.failed_decls.items()) |entry| { | ||
| 2204 | entry.value.destroy(gpa); | ||
| 2205 | } | ||
| 2206 | mod.failed_decls.deinit(gpa); | ||
| 2207 | |||
| 2208 | for (mod.emit_h_failed_decls.items()) |entry| { | ||
| 2209 | entry.value.destroy(gpa); | ||
| 2210 | } | ||
| 2211 | mod.emit_h_failed_decls.deinit(gpa); | ||
| 2212 | |||
| 2213 | for (mod.failed_files.items()) |entry| { | ||
| 2214 | entry.value.destroy(gpa); | ||
| 2215 | } | ||
| 2216 | mod.failed_files.deinit(gpa); | ||
| 2217 | |||
| 2218 | for (mod.failed_exports.items()) |entry| { | ||
| 2219 | entry.value.destroy(gpa); | ||
| 2220 | } | ||
| 2221 | mod.failed_exports.deinit(gpa); | ||
| 2222 | |||
| 2223 | mod.compile_log_decls.deinit(gpa); | ||
| 2224 | |||
| 2225 | for (mod.decl_exports.items()) |entry| { | ||
| 2226 | const export_list = entry.value; | ||
| 2227 | gpa.free(export_list); | ||
| 2228 | } | ||
| 2229 | mod.decl_exports.deinit(gpa); | ||
| 2230 | |||
| 2231 | for (mod.export_owners.items()) |entry| { | ||
| 2232 | freeExportList(gpa, entry.value); | ||
| 2233 | } | ||
| 2234 | mod.export_owners.deinit(gpa); | ||
| 2235 | |||
| 2236 | mod.symbol_exports.deinit(gpa); | ||
| 2237 | mod.root_scope.destroy(gpa); | ||
| 2238 | |||
| 2239 | var it = mod.global_error_set.iterator(); | ||
| 2240 | while (it.next()) |entry| { | ||
| 2241 | gpa.free(entry.key); | ||
| 2242 | } | ||
| 2243 | mod.global_error_set.deinit(gpa); | ||
| 2244 | |||
| 2245 | mod.error_name_list.deinit(gpa); | ||
| 2246 | |||
| 2247 | for (mod.import_table.items()) |entry| { | ||
| 958 | entry.value.destroy(gpa); | 2248 | entry.value.destroy(gpa); |
| 959 | } | 2249 | } |
| 960 | self.import_table.deinit(gpa); | 2250 | mod.import_table.deinit(gpa); |
| 961 | } | 2251 | } |
| 962 | 2252 | ||
| 963 | fn freeExportList(gpa: *Allocator, export_list: []*Export) void { | 2253 | fn freeExportList(gpa: *Allocator, export_list: []*Export) void { |
| ... | @@ -1102,42 +2392,51 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { | ... | @@ -1102,42 +2392,51 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { |
| 1102 | // A comptime decl does not store any value so we can just deinit this arena after analysis is done. | 2392 | // A comptime decl does not store any value so we can just deinit this arena after analysis is done. |
| 1103 | var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa); | 2393 | var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1104 | defer analysis_arena.deinit(); | 2394 | 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 | 2395 | ||
| 1113 | const block_expr = node_datas[decl_node].lhs; | 2396 | var code: zir.Code = blk: { |
| 1114 | _ = try astgen.comptimeExpr(mod, &gen_scope.base, .none, block_expr); | 2397 | var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator); |
| 1115 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { | 2398 | defer astgen.deinit(); |
| 1116 | zir.dumpZir(mod.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; | 2399 | |
| 1117 | } | 2400 | var gen_scope: Scope.GenZir = .{ |
| 2401 | .force_comptime = true, | ||
| 2402 | .parent = &decl.container.base, | ||
| 2403 | .astgen = &astgen, | ||
| 2404 | }; | ||
| 2405 | defer gen_scope.instructions.deinit(mod.gpa); | ||
| 1118 | 2406 | ||
| 1119 | var inst_table = Scope.Block.InstTable.init(mod.gpa); | 2407 | const block_expr = node_datas[decl_node].lhs; |
| 1120 | defer inst_table.deinit(); | 2408 | _ = try AstGen.comptimeExpr(&gen_scope, &gen_scope.base, .none, block_expr); |
| 1121 | 2409 | ||
| 1122 | var branch_quota: u32 = default_eval_branch_quota; | 2410 | const code = try gen_scope.finish(); |
| 2411 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { | ||
| 2412 | code.dump(mod.gpa, "comptime_block", &gen_scope.base, 0) catch {}; | ||
| 2413 | } | ||
| 2414 | break :blk code; | ||
| 2415 | }; | ||
| 2416 | defer code.deinit(mod.gpa); | ||
| 1123 | 2417 | ||
| 2418 | var sema: Sema = .{ | ||
| 2419 | .mod = mod, | ||
| 2420 | .gpa = mod.gpa, | ||
| 2421 | .arena = &analysis_arena.allocator, | ||
| 2422 | .code = code, | ||
| 2423 | .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, code.instructions.len), | ||
| 2424 | .owner_decl = decl, | ||
| 2425 | .func = null, | ||
| 2426 | .owner_func = null, | ||
| 2427 | .param_inst_list = &.{}, | ||
| 2428 | }; | ||
| 1124 | var block_scope: Scope.Block = .{ | 2429 | var block_scope: Scope.Block = .{ |
| 1125 | .parent = null, | 2430 | .parent = null, |
| 1126 | .inst_table = &inst_table, | 2431 | .sema = &sema, |
| 1127 | .func = null, | ||
| 1128 | .owner_decl = decl, | ||
| 1129 | .src_decl = decl, | 2432 | .src_decl = decl, |
| 1130 | .instructions = .{}, | 2433 | .instructions = .{}, |
| 1131 | .arena = &analysis_arena.allocator, | ||
| 1132 | .inlining = null, | 2434 | .inlining = null, |
| 1133 | .is_comptime = true, | 2435 | .is_comptime = true, |
| 1134 | .branch_quota = &branch_quota, | ||
| 1135 | }; | 2436 | }; |
| 1136 | defer block_scope.instructions.deinit(mod.gpa); | 2437 | defer block_scope.instructions.deinit(mod.gpa); |
| 1137 | 2438 | ||
| 1138 | _ = try zir_sema.analyzeBody(mod, &block_scope, .{ | 2439 | _ = try sema.root(&block_scope); |
| 1139 | .instructions = gen_scope.instructions.items, | ||
| 1140 | }); | ||
| 1141 | 2440 | ||
| 1142 | decl.analysis = .complete; | 2441 | decl.analysis = .complete; |
| 1143 | decl.generation = mod.generation; | 2442 | decl.generation = mod.generation; |
| ... | @@ -1160,7 +2459,6 @@ fn astgenAndSemaFn( | ... | @@ -1160,7 +2459,6 @@ fn astgenAndSemaFn( |
| 1160 | 2459 | ||
| 1161 | decl.analysis = .in_progress; | 2460 | decl.analysis = .in_progress; |
| 1162 | 2461 | ||
| 1163 | const token_starts = tree.tokens.items(.start); | ||
| 1164 | const token_tags = tree.tokens.items(.tag); | 2462 | const token_tags = tree.tokens.items(.tag); |
| 1165 | 2463 | ||
| 1166 | // This arena allocator's memory is discarded at the end of this function. It is used | 2464 | // This arena allocator's memory is discarded at the end of this function. It is used |
| ... | @@ -1168,11 +2466,14 @@ fn astgenAndSemaFn( | ... | @@ -1168,11 +2466,14 @@ fn astgenAndSemaFn( |
| 1168 | // to complete the Decl analysis. | 2466 | // to complete the Decl analysis. |
| 1169 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); | 2467 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1170 | defer fn_type_scope_arena.deinit(); | 2468 | defer fn_type_scope_arena.deinit(); |
| 1171 | var fn_type_scope: Scope.GenZIR = .{ | 2469 | |
| 1172 | .decl = decl, | 2470 | var fn_type_astgen = try AstGen.init(mod, decl, &fn_type_scope_arena.allocator); |
| 1173 | .arena = &fn_type_scope_arena.allocator, | 2471 | defer fn_type_astgen.deinit(); |
| 1174 | .parent = &decl.container.base, | 2472 | |
| 2473 | var fn_type_scope: Scope.GenZir = .{ | ||
| 1175 | .force_comptime = true, | 2474 | .force_comptime = true, |
| 2475 | .parent = &decl.container.base, | ||
| 2476 | .astgen = &fn_type_astgen, | ||
| 1176 | }; | 2477 | }; |
| 1177 | defer fn_type_scope.instructions.deinit(mod.gpa); | 2478 | defer fn_type_scope.instructions.deinit(mod.gpa); |
| 1178 | 2479 | ||
| ... | @@ -1189,13 +2490,7 @@ fn astgenAndSemaFn( | ... | @@ -1189,13 +2490,7 @@ fn astgenAndSemaFn( |
| 1189 | } | 2490 | } |
| 1190 | break :blk count; | 2491 | break :blk count; |
| 1191 | }; | 2492 | }; |
| 1192 | const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_count); | 2493 | const param_types = try fn_type_scope_arena.allocator.alloc(zir.Inst.Ref, 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 }; | ||
| 1199 | 2494 | ||
| 1200 | var is_var_args = false; | 2495 | var is_var_args = false; |
| 1201 | { | 2496 | { |
| ... | @@ -1220,7 +2515,7 @@ fn astgenAndSemaFn( | ... | @@ -1220,7 +2515,7 @@ fn astgenAndSemaFn( |
| 1220 | const param_type_node = param.type_expr; | 2515 | const param_type_node = param.type_expr; |
| 1221 | assert(param_type_node != 0); | 2516 | assert(param_type_node != 0); |
| 1222 | param_types[param_type_i] = | 2517 | param_types[param_type_i] = |
| 1223 | try astgen.expr(mod, &fn_type_scope.base, type_type_rl, param_type_node); | 2518 | try AstGen.expr(&fn_type_scope, &fn_type_scope.base, .{ .ty = .type_type }, param_type_node); |
| 1224 | } | 2519 | } |
| 1225 | assert(param_type_i == param_count); | 2520 | assert(param_type_i == param_count); |
| 1226 | } | 2521 | } |
| ... | @@ -1289,10 +2584,10 @@ fn astgenAndSemaFn( | ... | @@ -1289,10 +2584,10 @@ fn astgenAndSemaFn( |
| 1289 | if (token_tags[maybe_bang] == .bang) { | 2584 | if (token_tags[maybe_bang] == .bang) { |
| 1290 | return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); | 2585 | return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); |
| 1291 | } | 2586 | } |
| 1292 | const return_type_inst = try astgen.expr( | 2587 | const return_type_inst = try AstGen.expr( |
| 1293 | mod, | 2588 | &fn_type_scope, |
| 1294 | &fn_type_scope.base, | 2589 | &fn_type_scope.base, |
| 1295 | type_type_rl, | 2590 | .{ .ty = .type_type }, |
| 1296 | fn_proto.ast.return_type, | 2591 | fn_proto.ast.return_type, |
| 1297 | ); | 2592 | ); |
| 1298 | 2593 | ||
| ... | @@ -1301,73 +2596,72 @@ fn astgenAndSemaFn( | ... | @@ -1301,73 +2596,72 @@ fn astgenAndSemaFn( |
| 1301 | else | 2596 | else |
| 1302 | false; | 2597 | false; |
| 1303 | 2598 | ||
| 1304 | const cc_inst = if (fn_proto.ast.callconv_expr != 0) cc: { | 2599 | const cc: zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0) |
| 1305 | // TODO instead of enum literal type, this needs to be the | 2600 | // TODO instead of enum literal type, this needs to be the |
| 1306 | // std.builtin.CallingConvention enum. We need to implement importing other files | 2601 | // std.builtin.CallingConvention enum. We need to implement importing other files |
| 1307 | // and enums in order to fix this. | 2602 | // and enums in order to fix this. |
| 1308 | const src = token_starts[tree.firstToken(fn_proto.ast.callconv_expr)]; | 2603 | try AstGen.comptimeExpr( |
| 1309 | const enum_lit_ty = try astgen.addZIRInstConst(mod, &fn_type_scope.base, src, .{ | 2604 | &fn_type_scope, |
| 1310 | .ty = Type.initTag(.type), | 2605 | &fn_type_scope.base, |
| 1311 | .val = Value.initTag(.enum_literal_type), | 2606 | .{ .ty = .enum_literal_type }, |
| 1312 | }); | 2607 | fn_proto.ast.callconv_expr, |
| 1313 | break :cc try astgen.comptimeExpr(mod, &fn_type_scope.base, .{ | 2608 | ) |
| 1314 | .ty = enum_lit_ty, | 2609 | else if (is_extern) // note: https://github.com/ziglang/zig/issues/5269 |
| 1315 | }, fn_proto.ast.callconv_expr); | 2610 | try fn_type_scope.addSmallStr(.enum_literal_small, "C") |
| 1316 | } else if (is_extern) cc: { | 2611 | else |
| 1317 | // note: https://github.com/ziglang/zig/issues/5269 | 2612 | .none; |
| 1318 | const src = token_starts[fn_proto.extern_export_token.?]; | 2613 | |
| 1319 | break :cc try astgen.addZIRInst(mod, &fn_type_scope.base, src, zir.Inst.EnumLiteral, .{ .name = "C" }, .{}); | 2614 | const fn_type_inst: zir.Inst.Ref = if (cc != .none) fn_type: { |
| 1320 | } else null; | 2615 | const tag: zir.Inst.Tag = if (is_var_args) .fn_type_cc_var_args else .fn_type_cc; |
| 1321 | 2616 | break :fn_type try fn_type_scope.addFnTypeCc(tag, .{ | |
| 1322 | const fn_type_inst = if (cc_inst) |cc| fn_type: { | 2617 | .src_node = fn_proto.ast.proto_node, |
| 1323 | var fn_type = try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{ | 2618 | .ret_ty = return_type_inst, |
| 1324 | .return_type = return_type_inst, | ||
| 1325 | .param_types = param_types, | 2619 | .param_types = param_types, |
| 1326 | .cc = cc, | 2620 | .cc = cc, |
| 1327 | }); | 2621 | }); |
| 1328 | if (is_var_args) fn_type.tag = .fn_type_cc_var_args; | ||
| 1329 | break :fn_type fn_type; | ||
| 1330 | } else fn_type: { | 2622 | } else fn_type: { |
| 1331 | var fn_type = try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type, .{ | 2623 | const tag: zir.Inst.Tag = if (is_var_args) .fn_type_var_args else .fn_type; |
| 1332 | .return_type = return_type_inst, | 2624 | break :fn_type try fn_type_scope.addFnType(tag, .{ |
| 2625 | .src_node = fn_proto.ast.proto_node, | ||
| 2626 | .ret_ty = return_type_inst, | ||
| 1333 | .param_types = param_types, | 2627 | .param_types = param_types, |
| 1334 | }); | 2628 | }); |
| 1335 | if (is_var_args) fn_type.tag = .fn_type_var_args; | ||
| 1336 | break :fn_type fn_type; | ||
| 1337 | }; | 2629 | }; |
| 1338 | 2630 | _ = try fn_type_scope.addBreak(.break_inline, 0, fn_type_inst); | |
| 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 | } | ||
| 1342 | 2631 | ||
| 1343 | // We need the memory for the Type to go into the arena for the Decl | 2632 | // We need the memory for the Type to go into the arena for the Decl |
| 1344 | var decl_arena = std.heap.ArenaAllocator.init(mod.gpa); | 2633 | var decl_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1345 | errdefer decl_arena.deinit(); | 2634 | errdefer decl_arena.deinit(); |
| 1346 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | 2635 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1347 | 2636 | ||
| 1348 | var inst_table = Scope.Block.InstTable.init(mod.gpa); | 2637 | var fn_type_code = try fn_type_scope.finish(); |
| 1349 | defer inst_table.deinit(); | 2638 | defer fn_type_code.deinit(mod.gpa); |
| 1350 | 2639 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { | |
| 1351 | var branch_quota: u32 = default_eval_branch_quota; | 2640 | fn_type_code.dump(mod.gpa, "fn_type", &fn_type_scope.base, 0) catch {}; |
| 2641 | } | ||
| 1352 | 2642 | ||
| 2643 | var fn_type_sema: Sema = .{ | ||
| 2644 | .mod = mod, | ||
| 2645 | .gpa = mod.gpa, | ||
| 2646 | .arena = &decl_arena.allocator, | ||
| 2647 | .code = fn_type_code, | ||
| 2648 | .inst_map = try fn_type_scope_arena.allocator.alloc(*ir.Inst, fn_type_code.instructions.len), | ||
| 2649 | .owner_decl = decl, | ||
| 2650 | .func = null, | ||
| 2651 | .owner_func = null, | ||
| 2652 | .param_inst_list = &.{}, | ||
| 2653 | }; | ||
| 1353 | var block_scope: Scope.Block = .{ | 2654 | var block_scope: Scope.Block = .{ |
| 1354 | .parent = null, | 2655 | .parent = null, |
| 1355 | .inst_table = &inst_table, | 2656 | .sema = &fn_type_sema, |
| 1356 | .func = null, | ||
| 1357 | .owner_decl = decl, | ||
| 1358 | .src_decl = decl, | 2657 | .src_decl = decl, |
| 1359 | .instructions = .{}, | 2658 | .instructions = .{}, |
| 1360 | .arena = &decl_arena.allocator, | ||
| 1361 | .inlining = null, | 2659 | .inlining = null, |
| 1362 | .is_comptime = false, | 2660 | .is_comptime = true, |
| 1363 | .branch_quota = &branch_quota, | ||
| 1364 | }; | 2661 | }; |
| 1365 | defer block_scope.instructions.deinit(mod.gpa); | 2662 | defer block_scope.instructions.deinit(mod.gpa); |
| 1366 | 2663 | ||
| 1367 | const fn_type = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, fn_type_inst, .{ | 2664 | const fn_type = try fn_type_sema.rootAsType(&block_scope); |
| 1368 | .instructions = fn_type_scope.instructions.items, | ||
| 1369 | }); | ||
| 1370 | |||
| 1371 | if (body_node == 0) { | 2665 | if (body_node == 0) { |
| 1372 | if (!is_extern) { | 2666 | if (!is_extern) { |
| 1373 | return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function has no body", .{}); | 2667 | return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function has no body", .{}); |
| ... | @@ -1409,63 +2703,69 @@ fn astgenAndSemaFn( | ... | @@ -1409,63 +2703,69 @@ fn astgenAndSemaFn( |
| 1409 | const new_func = try decl_arena.allocator.create(Fn); | 2703 | const new_func = try decl_arena.allocator.create(Fn); |
| 1410 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); | 2704 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); |
| 1411 | 2705 | ||
| 1412 | const fn_zir: zir.Body = blk: { | 2706 | const fn_zir: zir.Code = blk: { |
| 1413 | // We put the ZIR inside the Decl arena. | 2707 | // We put the ZIR inside the Decl arena. |
| 1414 | var gen_scope: Scope.GenZIR = .{ | 2708 | var astgen = try AstGen.init(mod, decl, &decl_arena.allocator); |
| 1415 | .decl = decl, | 2709 | astgen.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count); |
| 1416 | .arena = &decl_arena.allocator, | 2710 | defer astgen.deinit(); |
| 1417 | .parent = &decl.container.base, | 2711 | |
| 2712 | var gen_scope: Scope.GenZir = .{ | ||
| 1418 | .force_comptime = false, | 2713 | .force_comptime = false, |
| 2714 | .parent = &decl.container.base, | ||
| 2715 | .astgen = &astgen, | ||
| 1419 | }; | 2716 | }; |
| 1420 | defer gen_scope.instructions.deinit(mod.gpa); | 2717 | defer gen_scope.instructions.deinit(mod.gpa); |
| 1421 | 2718 | ||
| 1422 | // We need an instruction for each parameter, and they must be first in the body. | 2719 | // Iterate over the parameters. We put the param names as the first N |
| 1423 | try gen_scope.instructions.resize(mod.gpa, param_count); | 2720 | // items inside `extra` so that debug info later can refer to the parameter names |
| 2721 | // even while the respective source code is unloaded. | ||
| 2722 | try astgen.extra.ensureCapacity(mod.gpa, param_count); | ||
| 2723 | |||
| 1424 | var params_scope = &gen_scope.base; | 2724 | var params_scope = &gen_scope.base; |
| 1425 | var i: usize = 0; | 2725 | var i: usize = 0; |
| 1426 | var it = fn_proto.iterate(tree); | 2726 | var it = fn_proto.iterate(tree); |
| 1427 | while (it.next()) |param| : (i += 1) { | 2727 | while (it.next()) |param| : (i += 1) { |
| 1428 | const name_token = param.name_token.?; | 2728 | const name_token = param.name_token.?; |
| 1429 | const src = token_starts[name_token]; | ||
| 1430 | const param_name = try mod.identifierTokenString(&gen_scope.base, name_token); | 2729 | 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 | const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); | 2730 | const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); |
| 1444 | sub_scope.* = .{ | 2731 | sub_scope.* = .{ |
| 1445 | .parent = params_scope, | 2732 | .parent = params_scope, |
| 1446 | .gen_zir = &gen_scope, | 2733 | .gen_zir = &gen_scope, |
| 1447 | .name = param_name, | 2734 | .name = param_name, |
| 1448 | .inst = &arg.base, | 2735 | // Implicit const list first, then implicit arg list. |
| 2736 | .inst = @intToEnum(zir.Inst.Ref, @intCast(u32, zir.Inst.Ref.typed_value_map.len + i)), | ||
| 2737 | .src = decl.tokSrcLoc(name_token), | ||
| 1449 | }; | 2738 | }; |
| 1450 | params_scope = &sub_scope.base; | 2739 | params_scope = &sub_scope.base; |
| 2740 | |||
| 2741 | // Additionally put the param name into `string_bytes` and reference it with | ||
| 2742 | // `extra` so that we have access to the data in codegen, for debug info. | ||
| 2743 | const str_index = @intCast(u32, astgen.string_bytes.items.len); | ||
| 2744 | astgen.extra.appendAssumeCapacity(str_index); | ||
| 2745 | const used_bytes = astgen.string_bytes.items.len; | ||
| 2746 | try astgen.string_bytes.ensureCapacity(mod.gpa, used_bytes + param_name.len + 1); | ||
| 2747 | astgen.string_bytes.appendSliceAssumeCapacity(param_name); | ||
| 2748 | astgen.string_bytes.appendAssumeCapacity(0); | ||
| 1451 | } | 2749 | } |
| 1452 | 2750 | ||
| 1453 | _ = try astgen.expr(mod, params_scope, .none, body_node); | 2751 | _ = try AstGen.expr(&gen_scope, params_scope, .none, body_node); |
| 1454 | 2752 | ||
| 1455 | if (gen_scope.instructions.items.len == 0 or | 2753 | if (gen_scope.instructions.items.len == 0 or |
| 1456 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) | 2754 | !astgen.instructions.items(.tag)[gen_scope.instructions.items.len - 1] |
| 2755 | .isNoReturn()) | ||
| 1457 | { | 2756 | { |
| 1458 | const src = token_starts[tree.lastToken(body_node)]; | 2757 | // astgen uses result location semantics to coerce return operands. |
| 1459 | _ = try astgen.addZIRNoOp(mod, &gen_scope.base, src, .return_void); | 2758 | // Since we are adding the return instruction here, we must handle the coercion. |
| 2759 | // We do this by using the `ret_coerce` instruction. | ||
| 2760 | _ = try gen_scope.addUnTok(.ret_coerce, .void_value, tree.lastToken(body_node)); | ||
| 1460 | } | 2761 | } |
| 1461 | 2762 | ||
| 2763 | const code = try gen_scope.finish(); | ||
| 1462 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { | 2764 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { |
| 1463 | zir.dumpZir(mod.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; | 2765 | code.dump(mod.gpa, "fn_body", &gen_scope.base, param_count) catch {}; |
| 1464 | } | 2766 | } |
| 1465 | 2767 | ||
| 1466 | break :blk .{ | 2768 | break :blk code; |
| 1467 | .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), | ||
| 1468 | }; | ||
| 1469 | }; | 2769 | }; |
| 1470 | 2770 | ||
| 1471 | const is_inline = fn_type.fnCallingConvention() == .Inline; | 2771 | const is_inline = fn_type.fnCallingConvention() == .Inline; |
| ... | @@ -1492,6 +2792,7 @@ fn astgenAndSemaFn( | ... | @@ -1492,6 +2792,7 @@ fn astgenAndSemaFn( |
| 1492 | if (tvm.typed_value.val.castTag(.function)) |payload| { | 2792 | if (tvm.typed_value.val.castTag(.function)) |payload| { |
| 1493 | const prev_func = payload.data; | 2793 | const prev_func = payload.data; |
| 1494 | prev_is_inline = prev_func.state == .inline_only; | 2794 | prev_is_inline = prev_func.state == .inline_only; |
| 2795 | prev_func.deinit(mod.gpa); | ||
| 1495 | } | 2796 | } |
| 1496 | 2797 | ||
| 1497 | tvm.deinit(mod.gpa); | 2798 | tvm.deinit(mod.gpa); |
| ... | @@ -1533,7 +2834,7 @@ fn astgenAndSemaFn( | ... | @@ -1533,7 +2834,7 @@ fn astgenAndSemaFn( |
| 1533 | .{}, | 2834 | .{}, |
| 1534 | ); | 2835 | ); |
| 1535 | } | 2836 | } |
| 1536 | const export_src = token_starts[maybe_export_token]; | 2837 | const export_src = decl.tokSrcLoc(maybe_export_token); |
| 1537 | const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString | 2838 | const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString |
| 1538 | // The scope needs to have the decl in it. | 2839 | // The scope needs to have the decl in it. |
| 1539 | try mod.analyzeExport(&block_scope.base, export_src, name, decl); | 2840 | try mod.analyzeExport(&block_scope.base, export_src, name, decl); |
| ... | @@ -1552,8 +2853,8 @@ fn astgenAndSemaVarDecl( | ... | @@ -1552,8 +2853,8 @@ fn astgenAndSemaVarDecl( |
| 1552 | defer tracy.end(); | 2853 | defer tracy.end(); |
| 1553 | 2854 | ||
| 1554 | decl.analysis = .in_progress; | 2855 | decl.analysis = .in_progress; |
| 2856 | decl.is_pub = var_decl.visib_token != null; | ||
| 1555 | 2857 | ||
| 1556 | const token_starts = tree.tokens.items(.start); | ||
| 1557 | const token_tags = tree.tokens.items(.tag); | 2858 | const token_tags = tree.tokens.items(.tag); |
| 1558 | 2859 | ||
| 1559 | // We need the memory for the Type to go into the arena for the Decl | 2860 | // We need the memory for the Type to go into the arena for the Decl |
| ... | @@ -1561,54 +2862,29 @@ fn astgenAndSemaVarDecl( | ... | @@ -1561,54 +2862,29 @@ fn astgenAndSemaVarDecl( |
| 1561 | errdefer decl_arena.deinit(); | 2862 | errdefer decl_arena.deinit(); |
| 1562 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | 2863 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1563 | 2864 | ||
| 1564 | var decl_inst_table = Scope.Block.InstTable.init(mod.gpa); | 2865 | // Used for simple error reporting. |
| 1565 | defer decl_inst_table.deinit(); | 2866 | var decl_scope: Scope.DeclRef = .{ .decl = decl }; |
| 1566 | 2867 | ||
| 1567 | var branch_quota: u32 = default_eval_branch_quota; | ||
| 1568 | |||
| 1569 | var block_scope: Scope.Block = .{ | ||
| 1570 | .parent = null, | ||
| 1571 | .inst_table = &decl_inst_table, | ||
| 1572 | .func = null, | ||
| 1573 | .owner_decl = decl, | ||
| 1574 | .src_decl = decl, | ||
| 1575 | .instructions = .{}, | ||
| 1576 | .arena = &decl_arena.allocator, | ||
| 1577 | .inlining = null, | ||
| 1578 | .is_comptime = true, | ||
| 1579 | .branch_quota = &branch_quota, | ||
| 1580 | }; | ||
| 1581 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 1582 | |||
| 1583 | decl.is_pub = var_decl.visib_token != null; | ||
| 1584 | const is_extern = blk: { | 2868 | const is_extern = blk: { |
| 1585 | const maybe_extern_token = var_decl.extern_export_token orelse break :blk false; | 2869 | 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; | 2870 | break :blk token_tags[maybe_extern_token] == .keyword_extern; |
| 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; | ||
| 1596 | }; | 2871 | }; |
| 2872 | |||
| 1597 | if (var_decl.lib_name) |lib_name| { | 2873 | if (var_decl.lib_name) |lib_name| { |
| 1598 | assert(is_extern); | 2874 | assert(is_extern); |
| 1599 | return mod.failTok(&block_scope.base, lib_name, "TODO implement function library name", .{}); | 2875 | return mod.failTok(&decl_scope.base, lib_name, "TODO implement function library name", .{}); |
| 1600 | } | 2876 | } |
| 1601 | const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var; | 2877 | const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var; |
| 1602 | const is_threadlocal = if (var_decl.threadlocal_token) |some| blk: { | 2878 | const is_threadlocal = if (var_decl.threadlocal_token) |some| blk: { |
| 1603 | if (!is_mutable) { | 2879 | if (!is_mutable) { |
| 1604 | return mod.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); | 2880 | return mod.failTok(&decl_scope.base, some, "threadlocal variable cannot be constant", .{}); |
| 1605 | } | 2881 | } |
| 1606 | break :blk true; | 2882 | break :blk true; |
| 1607 | } else false; | 2883 | } else false; |
| 1608 | assert(var_decl.comptime_token == null); | 2884 | assert(var_decl.comptime_token == null); |
| 1609 | if (var_decl.ast.align_node != 0) { | 2885 | if (var_decl.ast.align_node != 0) { |
| 1610 | return mod.failNode( | 2886 | return mod.failNode( |
| 1611 | &block_scope.base, | 2887 | &decl_scope.base, |
| 1612 | var_decl.ast.align_node, | 2888 | var_decl.ast.align_node, |
| 1613 | "TODO implement function align expression", | 2889 | "TODO implement function align expression", |
| 1614 | .{}, | 2890 | .{}, |
| ... | @@ -1616,7 +2892,7 @@ fn astgenAndSemaVarDecl( | ... | @@ -1616,7 +2892,7 @@ fn astgenAndSemaVarDecl( |
| 1616 | } | 2892 | } |
| 1617 | if (var_decl.ast.section_node != 0) { | 2893 | if (var_decl.ast.section_node != 0) { |
| 1618 | return mod.failNode( | 2894 | return mod.failNode( |
| 1619 | &block_scope.base, | 2895 | &decl_scope.base, |
| 1620 | var_decl.ast.section_node, | 2896 | var_decl.ast.section_node, |
| 1621 | "TODO implement function section expression", | 2897 | "TODO implement function section expression", |
| 1622 | .{}, | 2898 | .{}, |
| ... | @@ -1624,103 +2900,136 @@ fn astgenAndSemaVarDecl( | ... | @@ -1624,103 +2900,136 @@ fn astgenAndSemaVarDecl( |
| 1624 | } | 2900 | } |
| 1625 | 2901 | ||
| 1626 | const var_info: struct { ty: Type, val: ?Value } = if (var_decl.ast.init_node != 0) vi: { | 2902 | const var_info: struct { ty: Type, val: ?Value } = if (var_decl.ast.init_node != 0) vi: { |
| 2903 | if (is_extern) { | ||
| 2904 | return mod.failNode( | ||
| 2905 | &decl_scope.base, | ||
| 2906 | var_decl.ast.init_node, | ||
| 2907 | "extern variables have no initializers", | ||
| 2908 | .{}, | ||
| 2909 | ); | ||
| 2910 | } | ||
| 2911 | |||
| 1627 | var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); | 2912 | var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1628 | defer gen_scope_arena.deinit(); | 2913 | defer gen_scope_arena.deinit(); |
| 1629 | var gen_scope: Scope.GenZIR = .{ | 2914 | |
| 1630 | .decl = decl, | 2915 | var astgen = try AstGen.init(mod, decl, &gen_scope_arena.allocator); |
| 1631 | .arena = &gen_scope_arena.allocator, | 2916 | defer astgen.deinit(); |
| 1632 | .parent = &decl.container.base, | 2917 | |
| 2918 | var gen_scope: Scope.GenZir = .{ | ||
| 1633 | .force_comptime = true, | 2919 | .force_comptime = true, |
| 2920 | .parent = &decl.container.base, | ||
| 2921 | .astgen = &astgen, | ||
| 1634 | }; | 2922 | }; |
| 1635 | defer gen_scope.instructions.deinit(mod.gpa); | 2923 | defer gen_scope.instructions.deinit(mod.gpa); |
| 1636 | 2924 | ||
| 1637 | const init_result_loc: astgen.ResultLoc = if (var_decl.ast.type_node != 0) rl: { | 2925 | const init_result_loc: AstGen.ResultLoc = if (var_decl.ast.type_node != 0) .{ |
| 1638 | const type_node = var_decl.ast.type_node; | 2926 | .ty = try AstGen.expr(&gen_scope, &gen_scope.base, .{ .ty = .type_type }, 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 }; | ||
| 1646 | } else .none; | 2927 | } else .none; |
| 1647 | 2928 | ||
| 1648 | const init_inst = try astgen.comptimeExpr( | 2929 | const init_inst = try AstGen.comptimeExpr( |
| 1649 | mod, | 2930 | &gen_scope, |
| 1650 | &gen_scope.base, | 2931 | &gen_scope.base, |
| 1651 | init_result_loc, | 2932 | init_result_loc, |
| 1652 | var_decl.ast.init_node, | 2933 | var_decl.ast.init_node, |
| 1653 | ); | 2934 | ); |
| 2935 | _ = try gen_scope.addBreak(.break_inline, 0, init_inst); | ||
| 2936 | var code = try gen_scope.finish(); | ||
| 2937 | defer code.deinit(mod.gpa); | ||
| 1654 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { | 2938 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { |
| 1655 | zir.dumpZir(mod.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; | 2939 | code.dump(mod.gpa, "var_init", &gen_scope.base, 0) catch {}; |
| 1656 | } | 2940 | } |
| 1657 | 2941 | ||
| 1658 | var var_inst_table = Scope.Block.InstTable.init(mod.gpa); | 2942 | var sema: Sema = .{ |
| 1659 | defer var_inst_table.deinit(); | 2943 | .mod = mod, |
| 1660 | 2944 | .gpa = mod.gpa, | |
| 1661 | var branch_quota_vi: u32 = default_eval_branch_quota; | 2945 | .arena = &gen_scope_arena.allocator, |
| 1662 | var inner_block: Scope.Block = .{ | 2946 | .code = code, |
| 1663 | .parent = null, | 2947 | .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), |
| 1664 | .inst_table = &var_inst_table, | ||
| 1665 | .func = null, | ||
| 1666 | .owner_decl = decl, | 2948 | .owner_decl = decl, |
| 2949 | .func = null, | ||
| 2950 | .owner_func = null, | ||
| 2951 | .param_inst_list = &.{}, | ||
| 2952 | }; | ||
| 2953 | var block_scope: Scope.Block = .{ | ||
| 2954 | .parent = null, | ||
| 2955 | .sema = &sema, | ||
| 1667 | .src_decl = decl, | 2956 | .src_decl = decl, |
| 1668 | .instructions = .{}, | 2957 | .instructions = .{}, |
| 1669 | .arena = &gen_scope_arena.allocator, | ||
| 1670 | .inlining = null, | 2958 | .inlining = null, |
| 1671 | .is_comptime = true, | 2959 | .is_comptime = true, |
| 1672 | .branch_quota = &branch_quota_vi, | ||
| 1673 | }; | 2960 | }; |
| 1674 | defer inner_block.instructions.deinit(mod.gpa); | 2961 | defer block_scope.instructions.deinit(mod.gpa); |
| 1675 | try zir_sema.analyzeBody(mod, &inner_block, .{ | ||
| 1676 | .instructions = gen_scope.instructions.items, | ||
| 1677 | }); | ||
| 1678 | 2962 | ||
| 2963 | const init_inst_zir_ref = try sema.rootAsRef(&block_scope); | ||
| 1679 | // The result location guarantees the type coercion. | 2964 | // The result location guarantees the type coercion. |
| 1680 | const analyzed_init_inst = var_inst_table.get(init_inst).?; | 2965 | const analyzed_init_inst = try sema.resolveInst(init_inst_zir_ref); |
| 1681 | // The is_comptime in the Scope.Block guarantees the result is comptime-known. | 2966 | // The is_comptime in the Scope.Block guarantees the result is comptime-known. |
| 1682 | const val = analyzed_init_inst.value().?; | 2967 | const val = analyzed_init_inst.value().?; |
| 1683 | 2968 | ||
| 1684 | const ty = try analyzed_init_inst.ty.copy(block_scope.arena); | ||
| 1685 | break :vi .{ | 2969 | break :vi .{ |
| 1686 | .ty = ty, | 2970 | .ty = try analyzed_init_inst.ty.copy(&decl_arena.allocator), |
| 1687 | .val = try val.copy(block_scope.arena), | 2971 | .val = try val.copy(&decl_arena.allocator), |
| 1688 | }; | 2972 | }; |
| 1689 | } else if (!is_extern) { | 2973 | } else if (!is_extern) { |
| 1690 | return mod.failTok( | 2974 | return mod.failTok( |
| 1691 | &block_scope.base, | 2975 | &decl_scope.base, |
| 1692 | var_decl.ast.mut_token, | 2976 | var_decl.ast.mut_token, |
| 1693 | "variables must be initialized", | 2977 | "variables must be initialized", |
| 1694 | .{}, | 2978 | .{}, |
| 1695 | ); | 2979 | ); |
| 1696 | } else if (var_decl.ast.type_node != 0) vi: { | 2980 | } 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 | var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); | 2981 | var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1700 | defer type_scope_arena.deinit(); | 2982 | defer type_scope_arena.deinit(); |
| 1701 | var type_scope: Scope.GenZIR = .{ | 2983 | |
| 1702 | .decl = decl, | 2984 | var astgen = try AstGen.init(mod, decl, &type_scope_arena.allocator); |
| 1703 | .arena = &type_scope_arena.allocator, | 2985 | defer astgen.deinit(); |
| 1704 | .parent = &decl.container.base, | 2986 | |
| 2987 | var type_scope: Scope.GenZir = .{ | ||
| 1705 | .force_comptime = true, | 2988 | .force_comptime = true, |
| 2989 | .parent = &decl.container.base, | ||
| 2990 | .astgen = &astgen, | ||
| 1706 | }; | 2991 | }; |
| 1707 | defer type_scope.instructions.deinit(mod.gpa); | 2992 | defer type_scope.instructions.deinit(mod.gpa); |
| 1708 | 2993 | ||
| 1709 | const var_type = try astgen.typeExpr(mod, &type_scope.base, type_node); | 2994 | const var_type = try AstGen.typeExpr(&type_scope, &type_scope.base, var_decl.ast.type_node); |
| 2995 | _ = try type_scope.addBreak(.break_inline, 0, var_type); | ||
| 2996 | |||
| 2997 | var code = try type_scope.finish(); | ||
| 2998 | defer code.deinit(mod.gpa); | ||
| 1710 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { | 2999 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { |
| 1711 | zir.dumpZir(mod.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; | 3000 | code.dump(mod.gpa, "var_type", &type_scope.base, 0) catch {}; |
| 1712 | } | 3001 | } |
| 1713 | 3002 | ||
| 1714 | const ty = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, var_type, .{ | 3003 | var sema: Sema = .{ |
| 1715 | .instructions = type_scope.instructions.items, | 3004 | .mod = mod, |
| 1716 | }); | 3005 | .gpa = mod.gpa, |
| 3006 | .arena = &type_scope_arena.allocator, | ||
| 3007 | .code = code, | ||
| 3008 | .inst_map = try type_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), | ||
| 3009 | .owner_decl = decl, | ||
| 3010 | .func = null, | ||
| 3011 | .owner_func = null, | ||
| 3012 | .param_inst_list = &.{}, | ||
| 3013 | }; | ||
| 3014 | var block_scope: Scope.Block = .{ | ||
| 3015 | .parent = null, | ||
| 3016 | .sema = &sema, | ||
| 3017 | .src_decl = decl, | ||
| 3018 | .instructions = .{}, | ||
| 3019 | .inlining = null, | ||
| 3020 | .is_comptime = true, | ||
| 3021 | }; | ||
| 3022 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 3023 | |||
| 3024 | const ty = try sema.rootAsType(&block_scope); | ||
| 3025 | |||
| 1717 | break :vi .{ | 3026 | break :vi .{ |
| 1718 | .ty = ty, | 3027 | .ty = try ty.copy(&decl_arena.allocator), |
| 1719 | .val = null, | 3028 | .val = null, |
| 1720 | }; | 3029 | }; |
| 1721 | } else { | 3030 | } else { |
| 1722 | return mod.failTok( | 3031 | return mod.failTok( |
| 1723 | &block_scope.base, | 3032 | &decl_scope.base, |
| 1724 | var_decl.ast.mut_token, | 3033 | var_decl.ast.mut_token, |
| 1725 | "unable to infer variable type", | 3034 | "unable to infer variable type", |
| 1726 | .{}, | 3035 | .{}, |
| ... | @@ -1729,7 +3038,7 @@ fn astgenAndSemaVarDecl( | ... | @@ -1729,7 +3038,7 @@ fn astgenAndSemaVarDecl( |
| 1729 | 3038 | ||
| 1730 | if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { | 3039 | if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { |
| 1731 | return mod.failTok( | 3040 | return mod.failTok( |
| 1732 | &block_scope.base, | 3041 | &decl_scope.base, |
| 1733 | var_decl.ast.mut_token, | 3042 | var_decl.ast.mut_token, |
| 1734 | "variable of type '{}' must be const", | 3043 | "variable of type '{}' must be const", |
| 1735 | .{var_info.ty}, | 3044 | .{var_info.ty}, |
| ... | @@ -1768,57 +3077,57 @@ fn astgenAndSemaVarDecl( | ... | @@ -1768,57 +3077,57 @@ fn astgenAndSemaVarDecl( |
| 1768 | 3077 | ||
| 1769 | if (var_decl.extern_export_token) |maybe_export_token| { | 3078 | if (var_decl.extern_export_token) |maybe_export_token| { |
| 1770 | if (token_tags[maybe_export_token] == .keyword_export) { | 3079 | if (token_tags[maybe_export_token] == .keyword_export) { |
| 1771 | const export_src = token_starts[maybe_export_token]; | 3080 | const export_src = decl.tokSrcLoc(maybe_export_token); |
| 1772 | const name_token = var_decl.ast.mut_token + 1; | 3081 | const name_token = var_decl.ast.mut_token + 1; |
| 1773 | const name = tree.tokenSlice(name_token); // TODO identifierTokenString | 3082 | const name = tree.tokenSlice(name_token); // TODO identifierTokenString |
| 1774 | // The scope needs to have the decl in it. | 3083 | // The scope needs to have the decl in it. |
| 1775 | try mod.analyzeExport(&block_scope.base, export_src, name, decl); | 3084 | try mod.analyzeExport(&decl_scope.base, export_src, name, decl); |
| 1776 | } | 3085 | } |
| 1777 | } | 3086 | } |
| 1778 | return type_changed; | 3087 | return type_changed; |
| 1779 | } | 3088 | } |
| 1780 | 3089 | ||
| 1781 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { | 3090 | pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !void { |
| 1782 | try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1); | 3091 | try depender.dependencies.ensureCapacity(mod.gpa, depender.dependencies.items().len + 1); |
| 1783 | try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1); | 3092 | try dependee.dependants.ensureCapacity(mod.gpa, dependee.dependants.items().len + 1); |
| 1784 | 3093 | ||
| 1785 | depender.dependencies.putAssumeCapacity(dependee, {}); | 3094 | depender.dependencies.putAssumeCapacity(dependee, {}); |
| 1786 | dependee.dependants.putAssumeCapacity(depender, {}); | 3095 | dependee.dependants.putAssumeCapacity(depender, {}); |
| 1787 | } | 3096 | } |
| 1788 | 3097 | ||
| 1789 | pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree { | 3098 | pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree { |
| 1790 | const tracy = trace(@src()); | 3099 | const tracy = trace(@src()); |
| 1791 | defer tracy.end(); | 3100 | defer tracy.end(); |
| 1792 | 3101 | ||
| 1793 | switch (root_scope.status) { | 3102 | switch (root_scope.status) { |
| 1794 | .never_loaded, .unloaded_success => { | 3103 | .never_loaded, .unloaded_success => { |
| 1795 | try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); | 3104 | try mod.failed_files.ensureCapacity(mod.gpa, mod.failed_files.items().len + 1); |
| 1796 | 3105 | ||
| 1797 | const source = try root_scope.getSource(self); | 3106 | const source = try root_scope.getSource(mod); |
| 1798 | 3107 | ||
| 1799 | var keep_tree = false; | 3108 | var keep_tree = false; |
| 1800 | root_scope.tree = try std.zig.parse(self.gpa, source); | 3109 | root_scope.tree = try std.zig.parse(mod.gpa, source); |
| 1801 | defer if (!keep_tree) root_scope.tree.deinit(self.gpa); | 3110 | defer if (!keep_tree) root_scope.tree.deinit(mod.gpa); |
| 1802 | 3111 | ||
| 1803 | const tree = &root_scope.tree; | 3112 | const tree = &root_scope.tree; |
| 1804 | 3113 | ||
| 1805 | if (tree.errors.len != 0) { | 3114 | if (tree.errors.len != 0) { |
| 1806 | const parse_err = tree.errors[0]; | 3115 | const parse_err = tree.errors[0]; |
| 1807 | 3116 | ||
| 1808 | var msg = std.ArrayList(u8).init(self.gpa); | 3117 | var msg = std.ArrayList(u8).init(mod.gpa); |
| 1809 | defer msg.deinit(); | 3118 | defer msg.deinit(); |
| 1810 | 3119 | ||
| 1811 | try tree.renderError(parse_err, msg.writer()); | 3120 | try tree.renderError(parse_err, msg.writer()); |
| 1812 | const err_msg = try self.gpa.create(ErrorMsg); | 3121 | const err_msg = try mod.gpa.create(ErrorMsg); |
| 1813 | err_msg.* = .{ | 3122 | err_msg.* = .{ |
| 1814 | .src_loc = .{ | 3123 | .src_loc = .{ |
| 1815 | .file_scope = root_scope, | 3124 | .container = .{ .file_scope = root_scope }, |
| 1816 | .byte_offset = tree.tokens.items(.start)[parse_err.token], | 3125 | .lazy = .{ .token_abs = parse_err.token }, |
| 1817 | }, | 3126 | }, |
| 1818 | .msg = msg.toOwnedSlice(), | 3127 | .msg = msg.toOwnedSlice(), |
| 1819 | }; | 3128 | }; |
| 1820 | 3129 | ||
| 1821 | self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg); | 3130 | mod.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg); |
| 1822 | root_scope.status = .unloaded_parse_failure; | 3131 | root_scope.status = .unloaded_parse_failure; |
| 1823 | return error.AnalysisFail; | 3132 | return error.AnalysisFail; |
| 1824 | } | 3133 | } |
| ... | @@ -2051,11 +3360,9 @@ fn semaContainerFn( | ... | @@ -2051,11 +3360,9 @@ fn semaContainerFn( |
| 2051 | const tracy = trace(@src()); | 3360 | const tracy = trace(@src()); |
| 2052 | defer tracy.end(); | 3361 | defer tracy.end(); |
| 2053 | 3362 | ||
| 2054 | const token_starts = tree.tokens.items(.start); | ||
| 2055 | const token_tags = tree.tokens.items(.tag); | ||
| 2056 | |||
| 2057 | // We will create a Decl for it regardless of analysis status. | 3363 | // We will create a Decl for it regardless of analysis status. |
| 2058 | const name_tok = fn_proto.name_token orelse { | 3364 | const name_tok = fn_proto.name_token orelse { |
| 3365 | // This problem will go away with #1717. | ||
| 2059 | @panic("TODO missing function name"); | 3366 | @panic("TODO missing function name"); |
| 2060 | }; | 3367 | }; |
| 2061 | const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString | 3368 | const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString |
| ... | @@ -2068,8 +3375,8 @@ fn semaContainerFn( | ... | @@ -2068,8 +3375,8 @@ fn semaContainerFn( |
| 2068 | if (deleted_decls.swapRemove(decl) == null) { | 3375 | if (deleted_decls.swapRemove(decl) == null) { |
| 2069 | decl.analysis = .sema_failure; | 3376 | decl.analysis = .sema_failure; |
| 2070 | const msg = try ErrorMsg.create(mod.gpa, .{ | 3377 | const msg = try ErrorMsg.create(mod.gpa, .{ |
| 2071 | .file_scope = container_scope.file_scope, | 3378 | .container = .{ .file_scope = container_scope.file_scope }, |
| 2072 | .byte_offset = token_starts[name_tok], | 3379 | .lazy = .{ .token_abs = name_tok }, |
| 2073 | }, "redefinition of '{s}'", .{decl.name}); | 3380 | }, "redefinition of '{s}'", .{decl.name}); |
| 2074 | errdefer msg.destroy(mod.gpa); | 3381 | errdefer msg.destroy(mod.gpa); |
| 2075 | try mod.failed_decls.putNoClobber(mod.gpa, decl, msg); | 3382 | try mod.failed_decls.putNoClobber(mod.gpa, decl, msg); |
| ... | @@ -2098,6 +3405,7 @@ fn semaContainerFn( | ... | @@ -2098,6 +3405,7 @@ fn semaContainerFn( |
| 2098 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | 3405 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| 2099 | container_scope.decls.putAssumeCapacity(new_decl, {}); | 3406 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| 2100 | if (fn_proto.extern_export_token) |maybe_export_token| { | 3407 | if (fn_proto.extern_export_token) |maybe_export_token| { |
| 3408 | const token_tags = tree.tokens.items(.tag); | ||
| 2101 | if (token_tags[maybe_export_token] == .keyword_export) { | 3409 | if (token_tags[maybe_export_token] == .keyword_export) { |
| 2102 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | 3410 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 2103 | } | 3411 | } |
| ... | @@ -2117,11 +3425,7 @@ fn semaContainerVar( | ... | @@ -2117,11 +3425,7 @@ fn semaContainerVar( |
| 2117 | const tracy = trace(@src()); | 3425 | const tracy = trace(@src()); |
| 2118 | defer tracy.end(); | 3426 | defer tracy.end(); |
| 2119 | 3427 | ||
| 2120 | const token_starts = tree.tokens.items(.start); | ||
| 2121 | const token_tags = tree.tokens.items(.tag); | ||
| 2122 | |||
| 2123 | const name_token = var_decl.ast.mut_token + 1; | 3428 | const name_token = var_decl.ast.mut_token + 1; |
| 2124 | const name_src = token_starts[name_token]; | ||
| 2125 | const name = tree.tokenSlice(name_token); // TODO identifierTokenString | 3429 | const name = tree.tokenSlice(name_token); // TODO identifierTokenString |
| 2126 | const name_hash = container_scope.fullyQualifiedNameHash(name); | 3430 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| 2127 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); | 3431 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); |
| ... | @@ -2132,8 +3436,8 @@ fn semaContainerVar( | ... | @@ -2132,8 +3436,8 @@ fn semaContainerVar( |
| 2132 | if (deleted_decls.swapRemove(decl) == null) { | 3436 | if (deleted_decls.swapRemove(decl) == null) { |
| 2133 | decl.analysis = .sema_failure; | 3437 | decl.analysis = .sema_failure; |
| 2134 | const err_msg = try ErrorMsg.create(mod.gpa, .{ | 3438 | const err_msg = try ErrorMsg.create(mod.gpa, .{ |
| 2135 | .file_scope = container_scope.file_scope, | 3439 | .container = .{ .file_scope = container_scope.file_scope }, |
| 2136 | .byte_offset = name_src, | 3440 | .lazy = .{ .token_abs = name_token }, |
| 2137 | }, "redefinition of '{s}'", .{decl.name}); | 3441 | }, "redefinition of '{s}'", .{decl.name}); |
| 2138 | errdefer err_msg.destroy(mod.gpa); | 3442 | errdefer err_msg.destroy(mod.gpa); |
| 2139 | try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg); | 3443 | try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg); |
| ... | @@ -2145,6 +3449,7 @@ fn semaContainerVar( | ... | @@ -2145,6 +3449,7 @@ fn semaContainerVar( |
| 2145 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | 3449 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| 2146 | container_scope.decls.putAssumeCapacity(new_decl, {}); | 3450 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| 2147 | if (var_decl.extern_export_token) |maybe_export_token| { | 3451 | if (var_decl.extern_export_token) |maybe_export_token| { |
| 3452 | const token_tags = tree.tokens.items(.tag); | ||
| 2148 | if (token_tags[maybe_export_token] == .keyword_export) { | 3453 | if (token_tags[maybe_export_token] == .keyword_export) { |
| 2149 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | 3454 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 2150 | } | 3455 | } |
| ... | @@ -2167,11 +3472,11 @@ fn semaContainerField( | ... | @@ -2167,11 +3472,11 @@ fn semaContainerField( |
| 2167 | log.err("TODO: analyze container field", .{}); | 3472 | log.err("TODO: analyze container field", .{}); |
| 2168 | } | 3473 | } |
| 2169 | 3474 | ||
| 2170 | pub fn deleteDecl(self: *Module, decl: *Decl) !void { | 3475 | pub fn deleteDecl(mod: *Module, decl: *Decl) !void { |
| 2171 | const tracy = trace(@src()); | 3476 | const tracy = trace(@src()); |
| 2172 | defer tracy.end(); | 3477 | defer tracy.end(); |
| 2173 | 3478 | ||
| 2174 | try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len); | 3479 | try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.items.len + decl.dependencies.items().len); |
| 2175 | 3480 | ||
| 2176 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will | 3481 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will |
| 2177 | // not be present in the set, and this does nothing. | 3482 | // not be present in the set, and this does nothing. |
| ... | @@ -2179,7 +3484,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { | ... | @@ -2179,7 +3484,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 2179 | 3484 | ||
| 2180 | log.debug("deleting decl '{s}'", .{decl.name}); | 3485 | log.debug("deleting decl '{s}'", .{decl.name}); |
| 2181 | const name_hash = decl.fullyQualifiedNameHash(); | 3486 | const name_hash = decl.fullyQualifiedNameHash(); |
| 2182 | self.decl_table.removeAssertDiscard(name_hash); | 3487 | mod.decl_table.removeAssertDiscard(name_hash); |
| 2183 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. | 3488 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. |
| 2184 | for (decl.dependencies.items()) |entry| { | 3489 | for (decl.dependencies.items()) |entry| { |
| 2185 | const dep = entry.key; | 3490 | const dep = entry.key; |
| ... | @@ -2188,7 +3493,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { | ... | @@ -2188,7 +3493,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 2188 | // We don't recursively perform a deletion here, because during the update, | 3493 | // We don't recursively perform a deletion here, because during the update, |
| 2189 | // another reference to it may turn up. | 3494 | // another reference to it may turn up. |
| 2190 | dep.deletion_flag = true; | 3495 | dep.deletion_flag = true; |
| 2191 | self.deletion_set.appendAssumeCapacity(dep); | 3496 | mod.deletion_set.appendAssumeCapacity(dep); |
| 2192 | } | 3497 | } |
| 2193 | } | 3498 | } |
| 2194 | // Anything that depends on this deleted decl certainly needs to be re-analyzed. | 3499 | // Anything that depends on this deleted decl certainly needs to be re-analyzed. |
| ... | @@ -2197,29 +3502,29 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { | ... | @@ -2197,29 +3502,29 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 2197 | dep.removeDependency(decl); | 3502 | dep.removeDependency(decl); |
| 2198 | if (dep.analysis != .outdated) { | 3503 | if (dep.analysis != .outdated) { |
| 2199 | // TODO Move this failure possibility to the top of the function. | 3504 | // TODO Move this failure possibility to the top of the function. |
| 2200 | try self.markOutdatedDecl(dep); | 3505 | try mod.markOutdatedDecl(dep); |
| 2201 | } | 3506 | } |
| 2202 | } | 3507 | } |
| 2203 | if (self.failed_decls.swapRemove(decl)) |entry| { | 3508 | if (mod.failed_decls.swapRemove(decl)) |entry| { |
| 2204 | entry.value.destroy(self.gpa); | 3509 | entry.value.destroy(mod.gpa); |
| 2205 | } | 3510 | } |
| 2206 | if (self.emit_h_failed_decls.swapRemove(decl)) |entry| { | 3511 | if (mod.emit_h_failed_decls.swapRemove(decl)) |entry| { |
| 2207 | entry.value.destroy(self.gpa); | 3512 | entry.value.destroy(mod.gpa); |
| 2208 | } | 3513 | } |
| 2209 | _ = self.compile_log_decls.swapRemove(decl); | 3514 | _ = mod.compile_log_decls.swapRemove(decl); |
| 2210 | self.deleteDeclExports(decl); | 3515 | mod.deleteDeclExports(decl); |
| 2211 | self.comp.bin_file.freeDecl(decl); | 3516 | mod.comp.bin_file.freeDecl(decl); |
| 2212 | 3517 | ||
| 2213 | decl.destroy(self); | 3518 | decl.destroy(mod); |
| 2214 | } | 3519 | } |
| 2215 | 3520 | ||
| 2216 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of | 3521 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of |
| 2217 | /// this Decl will cause them to be re-created (or not). | 3522 | /// this Decl will cause them to be re-created (or not). |
| 2218 | fn deleteDeclExports(self: *Module, decl: *Decl) void { | 3523 | fn deleteDeclExports(mod: *Module, decl: *Decl) void { |
| 2219 | const kv = self.export_owners.swapRemove(decl) orelse return; | 3524 | const kv = mod.export_owners.swapRemove(decl) orelse return; |
| 2220 | 3525 | ||
| 2221 | for (kv.value) |exp| { | 3526 | for (kv.value) |exp| { |
| 2222 | if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| { | 3527 | if (mod.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| { |
| 2223 | // Remove exports with owner_decl matching the regenerating decl. | 3528 | // Remove exports with owner_decl matching the regenerating decl. |
| 2224 | const list = decl_exports_kv.value; | 3529 | const list = decl_exports_kv.value; |
| 2225 | var i: usize = 0; | 3530 | var i: usize = 0; |
| ... | @@ -2232,73 +3537,101 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { | ... | @@ -2232,73 +3537,101 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { |
| 2232 | i += 1; | 3537 | i += 1; |
| 2233 | } | 3538 | } |
| 2234 | } | 3539 | } |
| 2235 | decl_exports_kv.value = self.gpa.shrink(list, new_len); | 3540 | decl_exports_kv.value = mod.gpa.shrink(list, new_len); |
| 2236 | if (new_len == 0) { | 3541 | if (new_len == 0) { |
| 2237 | self.decl_exports.removeAssertDiscard(exp.exported_decl); | 3542 | mod.decl_exports.removeAssertDiscard(exp.exported_decl); |
| 2238 | } | 3543 | } |
| 2239 | } | 3544 | } |
| 2240 | if (self.comp.bin_file.cast(link.File.Elf)) |elf| { | 3545 | if (mod.comp.bin_file.cast(link.File.Elf)) |elf| { |
| 2241 | elf.deleteExport(exp.link.elf); | 3546 | elf.deleteExport(exp.link.elf); |
| 2242 | } | 3547 | } |
| 2243 | if (self.comp.bin_file.cast(link.File.MachO)) |macho| { | 3548 | if (mod.comp.bin_file.cast(link.File.MachO)) |macho| { |
| 2244 | macho.deleteExport(exp.link.macho); | 3549 | macho.deleteExport(exp.link.macho); |
| 2245 | } | 3550 | } |
| 2246 | if (self.failed_exports.swapRemove(exp)) |entry| { | 3551 | if (mod.failed_exports.swapRemove(exp)) |entry| { |
| 2247 | entry.value.destroy(self.gpa); | 3552 | entry.value.destroy(mod.gpa); |
| 2248 | } | 3553 | } |
| 2249 | _ = self.symbol_exports.swapRemove(exp.options.name); | 3554 | _ = mod.symbol_exports.swapRemove(exp.options.name); |
| 2250 | self.gpa.free(exp.options.name); | 3555 | mod.gpa.free(exp.options.name); |
| 2251 | self.gpa.destroy(exp); | 3556 | mod.gpa.destroy(exp); |
| 2252 | } | 3557 | } |
| 2253 | self.gpa.free(kv.value); | 3558 | mod.gpa.free(kv.value); |
| 2254 | } | 3559 | } |
| 2255 | 3560 | ||
| 2256 | pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { | 3561 | pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void { |
| 2257 | const tracy = trace(@src()); | 3562 | const tracy = trace(@src()); |
| 2258 | defer tracy.end(); | 3563 | defer tracy.end(); |
| 2259 | 3564 | ||
| 2260 | // Use the Decl's arena for function memory. | 3565 | // Use the Decl's arena for function memory. |
| 2261 | var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa); | 3566 | var arena = decl.typed_value.most_recent.arena.?.promote(mod.gpa); |
| 2262 | defer decl.typed_value.most_recent.arena.?.* = arena.state; | 3567 | defer decl.typed_value.most_recent.arena.?.* = arena.state; |
| 2263 | var inst_table = Scope.Block.InstTable.init(self.gpa); | 3568 | |
| 2264 | defer inst_table.deinit(); | 3569 | const fn_ty = decl.typed_value.most_recent.typed_value.ty; |
| 2265 | var branch_quota: u32 = default_eval_branch_quota; | 3570 | const param_inst_list = try mod.gpa.alloc(*ir.Inst, fn_ty.fnParamLen()); |
| 3571 | defer mod.gpa.free(param_inst_list); | ||
| 3572 | |||
| 3573 | for (param_inst_list) |*param_inst, param_index| { | ||
| 3574 | const param_type = fn_ty.fnParamType(param_index); | ||
| 3575 | const name = func.zir.nullTerminatedString(func.zir.extra[param_index]); | ||
| 3576 | const arg_inst = try arena.allocator.create(ir.Inst.Arg); | ||
| 3577 | arg_inst.* = .{ | ||
| 3578 | .base = .{ | ||
| 3579 | .tag = .arg, | ||
| 3580 | .ty = param_type, | ||
| 3581 | .src = .unneeded, | ||
| 3582 | }, | ||
| 3583 | .name = name, | ||
| 3584 | }; | ||
| 3585 | param_inst.* = &arg_inst.base; | ||
| 3586 | } | ||
| 3587 | |||
| 3588 | var sema: Sema = .{ | ||
| 3589 | .mod = mod, | ||
| 3590 | .gpa = mod.gpa, | ||
| 3591 | .arena = &arena.allocator, | ||
| 3592 | .code = func.zir, | ||
| 3593 | .inst_map = try mod.gpa.alloc(*ir.Inst, func.zir.instructions.len), | ||
| 3594 | .owner_decl = decl, | ||
| 3595 | .func = func, | ||
| 3596 | .owner_func = func, | ||
| 3597 | .param_inst_list = param_inst_list, | ||
| 3598 | }; | ||
| 3599 | defer mod.gpa.free(sema.inst_map); | ||
| 2266 | 3600 | ||
| 2267 | var inner_block: Scope.Block = .{ | 3601 | var inner_block: Scope.Block = .{ |
| 2268 | .parent = null, | 3602 | .parent = null, |
| 2269 | .inst_table = &inst_table, | 3603 | .sema = &sema, |
| 2270 | .func = func, | ||
| 2271 | .owner_decl = decl, | ||
| 2272 | .src_decl = decl, | 3604 | .src_decl = decl, |
| 2273 | .instructions = .{}, | 3605 | .instructions = .{}, |
| 2274 | .arena = &arena.allocator, | ||
| 2275 | .inlining = null, | 3606 | .inlining = null, |
| 2276 | .is_comptime = false, | 3607 | .is_comptime = false, |
| 2277 | .branch_quota = &branch_quota, | ||
| 2278 | }; | 3608 | }; |
| 2279 | defer inner_block.instructions.deinit(self.gpa); | 3609 | defer inner_block.instructions.deinit(mod.gpa); |
| 3610 | |||
| 3611 | // TZIR currently requires the arg parameters to be the first N instructions | ||
| 3612 | try inner_block.instructions.appendSlice(mod.gpa, param_inst_list); | ||
| 2280 | 3613 | ||
| 2281 | func.state = .in_progress; | 3614 | func.state = .in_progress; |
| 2282 | log.debug("set {s} to in_progress", .{decl.name}); | 3615 | log.debug("set {s} to in_progress", .{decl.name}); |
| 2283 | 3616 | ||
| 2284 | try zir_sema.analyzeBody(self, &inner_block, func.zir); | 3617 | _ = try sema.root(&inner_block); |
| 2285 | 3618 | ||
| 2286 | const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); | 3619 | const instructions = try arena.allocator.dupe(*ir.Inst, inner_block.instructions.items); |
| 2287 | func.state = .success; | 3620 | func.state = .success; |
| 2288 | func.body = .{ .instructions = instructions }; | 3621 | func.body = .{ .instructions = instructions }; |
| 2289 | log.debug("set {s} to success", .{decl.name}); | 3622 | log.debug("set {s} to success", .{decl.name}); |
| 2290 | } | 3623 | } |
| 2291 | 3624 | ||
| 2292 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | 3625 | fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { |
| 2293 | log.debug("mark {s} outdated", .{decl.name}); | 3626 | log.debug("mark {s} outdated", .{decl.name}); |
| 2294 | try self.comp.work_queue.writeItem(.{ .analyze_decl = decl }); | 3627 | try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl }); |
| 2295 | if (self.failed_decls.swapRemove(decl)) |entry| { | 3628 | if (mod.failed_decls.swapRemove(decl)) |entry| { |
| 2296 | entry.value.destroy(self.gpa); | 3629 | entry.value.destroy(mod.gpa); |
| 2297 | } | 3630 | } |
| 2298 | if (self.emit_h_failed_decls.swapRemove(decl)) |entry| { | 3631 | if (mod.emit_h_failed_decls.swapRemove(decl)) |entry| { |
| 2299 | entry.value.destroy(self.gpa); | 3632 | entry.value.destroy(mod.gpa); |
| 2300 | } | 3633 | } |
| 2301 | _ = self.compile_log_decls.swapRemove(decl); | 3634 | _ = mod.compile_log_decls.swapRemove(decl); |
| 2302 | decl.analysis = .outdated; | 3635 | decl.analysis = .outdated; |
| 2303 | } | 3636 | } |
| 2304 | 3637 | ||
| ... | @@ -2349,65 +3682,39 @@ fn allocateNewDecl( | ... | @@ -2349,65 +3682,39 @@ fn allocateNewDecl( |
| 2349 | } | 3682 | } |
| 2350 | 3683 | ||
| 2351 | fn createNewDecl( | 3684 | fn createNewDecl( |
| 2352 | self: *Module, | 3685 | mod: *Module, |
| 2353 | scope: *Scope, | 3686 | scope: *Scope, |
| 2354 | decl_name: []const u8, | 3687 | decl_name: []const u8, |
| 2355 | src_index: usize, | 3688 | src_index: usize, |
| 2356 | name_hash: Scope.NameHash, | 3689 | name_hash: Scope.NameHash, |
| 2357 | contents_hash: std.zig.SrcHash, | 3690 | contents_hash: std.zig.SrcHash, |
| 2358 | ) !*Decl { | 3691 | ) !*Decl { |
| 2359 | try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1); | 3692 | try mod.decl_table.ensureCapacity(mod.gpa, mod.decl_table.items().len + 1); |
| 2360 | const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash); | 3693 | const new_decl = try mod.allocateNewDecl(scope, src_index, contents_hash); |
| 2361 | errdefer self.gpa.destroy(new_decl); | 3694 | errdefer mod.gpa.destroy(new_decl); |
| 2362 | new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name); | 3695 | new_decl.name = try mem.dupeZ(mod.gpa, u8, decl_name); |
| 2363 | self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl); | 3696 | mod.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl); |
| 2364 | return new_decl; | 3697 | return new_decl; |
| 2365 | } | 3698 | } |
| 2366 | 3699 | ||
| 2367 | /// Get error value for error tag `name`. | 3700 | /// Get error value for error tag `name`. |
| 2368 | pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry { | 3701 | pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged(ErrorInt).Entry { |
| 2369 | const gop = try self.global_error_set.getOrPut(self.gpa, name); | 3702 | const gop = try mod.global_error_set.getOrPut(mod.gpa, name); |
| 2370 | if (gop.found_existing) | 3703 | if (gop.found_existing) |
| 2371 | return gop.entry.*; | 3704 | return gop.entry.*; |
| 2372 | errdefer self.global_error_set.removeAssertDiscard(name); | ||
| 2373 | 3705 | ||
| 2374 | gop.entry.key = try self.gpa.dupe(u8, name); | 3706 | errdefer mod.global_error_set.removeAssertDiscard(name); |
| 2375 | gop.entry.value = @intCast(u16, self.global_error_set.count() - 1); | 3707 | try mod.error_name_list.ensureCapacity(mod.gpa, mod.error_name_list.items.len + 1); |
| 3708 | gop.entry.key = try mod.gpa.dupe(u8, name); | ||
| 3709 | gop.entry.value = @intCast(ErrorInt, mod.error_name_list.items.len); | ||
| 3710 | mod.error_name_list.appendAssumeCapacity(gop.entry.key); | ||
| 2376 | return gop.entry.*; | 3711 | return gop.entry.*; |
| 2377 | } | 3712 | } |
| 2378 | 3713 | ||
| 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 | pub fn analyzeExport( | 3714 | pub fn analyzeExport( |
| 2408 | mod: *Module, | 3715 | mod: *Module, |
| 2409 | scope: *Scope, | 3716 | scope: *Scope, |
| 2410 | src: usize, | 3717 | src: LazySrcLoc, |
| 2411 | borrowed_symbol_name: []const u8, | 3718 | borrowed_symbol_name: []const u8, |
| 2412 | exported_decl: *Decl, | 3719 | exported_decl: *Decl, |
| 2413 | ) !void { | 3720 | ) !void { |
| ... | @@ -2496,178 +3803,11 @@ pub fn analyzeExport( | ... | @@ -2496,178 +3803,11 @@ pub fn analyzeExport( |
| 2496 | }, | 3803 | }, |
| 2497 | }; | 3804 | }; |
| 2498 | } | 3805 | } |
| 2499 | 3806 | pub fn constInst(mod: *Module, arena: *Allocator, src: LazySrcLoc, typed_value: TypedValue) !*ir.Inst { | |
| 2500 | pub fn addNoOp( | 3807 | const const_inst = try arena.create(ir.Inst.Constant); |
| 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); | ||
| 2668 | const_inst.* = .{ | 3808 | const_inst.* = .{ |
| 2669 | .base = .{ | 3809 | .base = .{ |
| 2670 | .tag = Inst.Constant.base_tag, | 3810 | .tag = ir.Inst.Constant.base_tag, |
| 2671 | .ty = typed_value.ty, | 3811 | .ty = typed_value.ty, |
| 2672 | .src = src, | 3812 | .src = src, |
| 2673 | }, | 3813 | }, |
| ... | @@ -2676,94 +3816,94 @@ pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedVal | ... | @@ -2676,94 +3816,94 @@ pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedVal |
| 2676 | return &const_inst.base; | 3816 | return &const_inst.base; |
| 2677 | } | 3817 | } |
| 2678 | 3818 | ||
| 2679 | pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | 3819 | pub fn constType(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type) !*ir.Inst { |
| 2680 | return self.constInst(scope, src, .{ | 3820 | return mod.constInst(arena, src, .{ |
| 2681 | .ty = Type.initTag(.type), | 3821 | .ty = Type.initTag(.type), |
| 2682 | .val = try ty.toValue(scope.arena()), | 3822 | .val = try ty.toValue(arena), |
| 2683 | }); | 3823 | }); |
| 2684 | } | 3824 | } |
| 2685 | 3825 | ||
| 2686 | pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst { | 3826 | pub fn constVoid(mod: *Module, arena: *Allocator, src: LazySrcLoc) !*ir.Inst { |
| 2687 | return self.constInst(scope, src, .{ | 3827 | return mod.constInst(arena, src, .{ |
| 2688 | .ty = Type.initTag(.void), | 3828 | .ty = Type.initTag(.void), |
| 2689 | .val = Value.initTag(.void_value), | 3829 | .val = Value.initTag(.void_value), |
| 2690 | }); | 3830 | }); |
| 2691 | } | 3831 | } |
| 2692 | 3832 | ||
| 2693 | pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst { | 3833 | pub fn constNoReturn(mod: *Module, arena: *Allocator, src: LazySrcLoc) !*ir.Inst { |
| 2694 | return self.constInst(scope, src, .{ | 3834 | return mod.constInst(arena, src, .{ |
| 2695 | .ty = Type.initTag(.noreturn), | 3835 | .ty = Type.initTag(.noreturn), |
| 2696 | .val = Value.initTag(.unreachable_value), | 3836 | .val = Value.initTag(.unreachable_value), |
| 2697 | }); | 3837 | }); |
| 2698 | } | 3838 | } |
| 2699 | 3839 | ||
| 2700 | pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | 3840 | pub fn constUndef(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type) !*ir.Inst { |
| 2701 | return self.constInst(scope, src, .{ | 3841 | return mod.constInst(arena, src, .{ |
| 2702 | .ty = ty, | 3842 | .ty = ty, |
| 2703 | .val = Value.initTag(.undef), | 3843 | .val = Value.initTag(.undef), |
| 2704 | }); | 3844 | }); |
| 2705 | } | 3845 | } |
| 2706 | 3846 | ||
| 2707 | pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst { | 3847 | pub fn constBool(mod: *Module, arena: *Allocator, src: LazySrcLoc, v: bool) !*ir.Inst { |
| 2708 | return self.constInst(scope, src, .{ | 3848 | return mod.constInst(arena, src, .{ |
| 2709 | .ty = Type.initTag(.bool), | 3849 | .ty = Type.initTag(.bool), |
| 2710 | .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)], | 3850 | .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)], |
| 2711 | }); | 3851 | }); |
| 2712 | } | 3852 | } |
| 2713 | 3853 | ||
| 2714 | pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst { | 3854 | pub fn constIntUnsigned(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, int: u64) !*ir.Inst { |
| 2715 | return self.constInst(scope, src, .{ | 3855 | return mod.constInst(arena, src, .{ |
| 2716 | .ty = ty, | 3856 | .ty = ty, |
| 2717 | .val = try Value.Tag.int_u64.create(scope.arena(), int), | 3857 | .val = try Value.Tag.int_u64.create(arena, int), |
| 2718 | }); | 3858 | }); |
| 2719 | } | 3859 | } |
| 2720 | 3860 | ||
| 2721 | pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst { | 3861 | pub fn constIntSigned(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, int: i64) !*ir.Inst { |
| 2722 | return self.constInst(scope, src, .{ | 3862 | return mod.constInst(arena, src, .{ |
| 2723 | .ty = ty, | 3863 | .ty = ty, |
| 2724 | .val = try Value.Tag.int_i64.create(scope.arena(), int), | 3864 | .val = try Value.Tag.int_i64.create(arena, int), |
| 2725 | }); | 3865 | }); |
| 2726 | } | 3866 | } |
| 2727 | 3867 | ||
| 2728 | pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst { | 3868 | pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, big_int: BigIntConst) !*ir.Inst { |
| 2729 | if (big_int.positive) { | 3869 | if (big_int.positive) { |
| 2730 | if (big_int.to(u64)) |x| { | 3870 | if (big_int.to(u64)) |x| { |
| 2731 | return self.constIntUnsigned(scope, src, ty, x); | 3871 | return mod.constIntUnsigned(arena, src, ty, x); |
| 2732 | } else |err| switch (err) { | 3872 | } else |err| switch (err) { |
| 2733 | error.NegativeIntoUnsigned => unreachable, | 3873 | error.NegativeIntoUnsigned => unreachable, |
| 2734 | error.TargetTooSmall => {}, // handled below | 3874 | error.TargetTooSmall => {}, // handled below |
| 2735 | } | 3875 | } |
| 2736 | return self.constInst(scope, src, .{ | 3876 | return mod.constInst(arena, src, .{ |
| 2737 | .ty = ty, | 3877 | .ty = ty, |
| 2738 | .val = try Value.Tag.int_big_positive.create(scope.arena(), big_int.limbs), | 3878 | .val = try Value.Tag.int_big_positive.create(arena, big_int.limbs), |
| 2739 | }); | 3879 | }); |
| 2740 | } else { | 3880 | } else { |
| 2741 | if (big_int.to(i64)) |x| { | 3881 | if (big_int.to(i64)) |x| { |
| 2742 | return self.constIntSigned(scope, src, ty, x); | 3882 | return mod.constIntSigned(arena, src, ty, x); |
| 2743 | } else |err| switch (err) { | 3883 | } else |err| switch (err) { |
| 2744 | error.NegativeIntoUnsigned => unreachable, | 3884 | error.NegativeIntoUnsigned => unreachable, |
| 2745 | error.TargetTooSmall => {}, // handled below | 3885 | error.TargetTooSmall => {}, // handled below |
| 2746 | } | 3886 | } |
| 2747 | return self.constInst(scope, src, .{ | 3887 | return mod.constInst(arena, src, .{ |
| 2748 | .ty = ty, | 3888 | .ty = ty, |
| 2749 | .val = try Value.Tag.int_big_negative.create(scope.arena(), big_int.limbs), | 3889 | .val = try Value.Tag.int_big_negative.create(arena, big_int.limbs), |
| 2750 | }); | 3890 | }); |
| 2751 | } | 3891 | } |
| 2752 | } | 3892 | } |
| 2753 | 3893 | ||
| 2754 | pub fn createAnonymousDecl( | 3894 | pub fn createAnonymousDecl( |
| 2755 | self: *Module, | 3895 | mod: *Module, |
| 2756 | scope: *Scope, | 3896 | scope: *Scope, |
| 2757 | decl_arena: *std.heap.ArenaAllocator, | 3897 | decl_arena: *std.heap.ArenaAllocator, |
| 2758 | typed_value: TypedValue, | 3898 | typed_value: TypedValue, |
| 2759 | ) !*Decl { | 3899 | ) !*Decl { |
| 2760 | const name_index = self.getNextAnonNameIndex(); | 3900 | const name_index = mod.getNextAnonNameIndex(); |
| 2761 | const scope_decl = scope.ownerDecl().?; | 3901 | const scope_decl = scope.ownerDecl().?; |
| 2762 | const name = try std.fmt.allocPrint(self.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index }); | 3902 | const name = try std.fmt.allocPrint(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index }); |
| 2763 | defer self.gpa.free(name); | 3903 | defer mod.gpa.free(name); |
| 2764 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); | 3904 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); |
| 2765 | const src_hash: std.zig.SrcHash = undefined; | 3905 | const src_hash: std.zig.SrcHash = undefined; |
| 2766 | const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); | 3906 | const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); |
| 2767 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | 3907 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 2768 | 3908 | ||
| 2769 | decl_arena_state.* = decl_arena.state; | 3909 | decl_arena_state.* = decl_arena.state; |
| ... | @@ -2774,32 +3914,32 @@ pub fn createAnonymousDecl( | ... | @@ -2774,32 +3914,32 @@ pub fn createAnonymousDecl( |
| 2774 | }, | 3914 | }, |
| 2775 | }; | 3915 | }; |
| 2776 | new_decl.analysis = .complete; | 3916 | new_decl.analysis = .complete; |
| 2777 | new_decl.generation = self.generation; | 3917 | new_decl.generation = mod.generation; |
| 2778 | 3918 | ||
| 2779 | // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size. | 3919 | // TODO: This generates the Decl into the machine code file if it is of a |
| 2780 | // We should be able to further improve the compiler to not omit Decls which are only referenced at | 3920 | // type that is non-zero size. We should be able to further improve the |
| 2781 | // compile-time and not runtime. | 3921 | // compiler to omit Decls which are only referenced at compile-time and not runtime. |
| 2782 | if (typed_value.ty.hasCodeGenBits()) { | 3922 | if (typed_value.ty.hasCodeGenBits()) { |
| 2783 | try self.comp.bin_file.allocateDeclIndexes(new_decl); | 3923 | try mod.comp.bin_file.allocateDeclIndexes(new_decl); |
| 2784 | try self.comp.work_queue.writeItem(.{ .codegen_decl = new_decl }); | 3924 | try mod.comp.work_queue.writeItem(.{ .codegen_decl = new_decl }); |
| 2785 | } | 3925 | } |
| 2786 | 3926 | ||
| 2787 | return new_decl; | 3927 | return new_decl; |
| 2788 | } | 3928 | } |
| 2789 | 3929 | ||
| 2790 | pub fn createContainerDecl( | 3930 | pub fn createContainerDecl( |
| 2791 | self: *Module, | 3931 | mod: *Module, |
| 2792 | scope: *Scope, | 3932 | scope: *Scope, |
| 2793 | base_token: std.zig.ast.TokenIndex, | 3933 | base_token: std.zig.ast.TokenIndex, |
| 2794 | decl_arena: *std.heap.ArenaAllocator, | 3934 | decl_arena: *std.heap.ArenaAllocator, |
| 2795 | typed_value: TypedValue, | 3935 | typed_value: TypedValue, |
| 2796 | ) !*Decl { | 3936 | ) !*Decl { |
| 2797 | const scope_decl = scope.ownerDecl().?; | 3937 | const scope_decl = scope.ownerDecl().?; |
| 2798 | const name = try self.getAnonTypeName(scope, base_token); | 3938 | const name = try mod.getAnonTypeName(scope, base_token); |
| 2799 | defer self.gpa.free(name); | 3939 | defer mod.gpa.free(name); |
| 2800 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); | 3940 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); |
| 2801 | const src_hash: std.zig.SrcHash = undefined; | 3941 | const src_hash: std.zig.SrcHash = undefined; |
| 2802 | const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); | 3942 | const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); |
| 2803 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | 3943 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 2804 | 3944 | ||
| 2805 | decl_arena_state.* = decl_arena.state; | 3945 | decl_arena_state.* = decl_arena.state; |
| ... | @@ -2810,12 +3950,12 @@ pub fn createContainerDecl( | ... | @@ -2810,12 +3950,12 @@ pub fn createContainerDecl( |
| 2810 | }, | 3950 | }, |
| 2811 | }; | 3951 | }; |
| 2812 | new_decl.analysis = .complete; | 3952 | new_decl.analysis = .complete; |
| 2813 | new_decl.generation = self.generation; | 3953 | new_decl.generation = mod.generation; |
| 2814 | 3954 | ||
| 2815 | return new_decl; | 3955 | return new_decl; |
| 2816 | } | 3956 | } |
| 2817 | 3957 | ||
| 2818 | fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 { | 3958 | fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 { |
| 2819 | // TODO add namespaces, generic function signatrues | 3959 | // TODO add namespaces, generic function signatrues |
| 2820 | const tree = scope.tree(); | 3960 | const tree = scope.tree(); |
| 2821 | const token_tags = tree.tokens.items(.tag); | 3961 | const token_tags = tree.tokens.items(.tag); |
| ... | @@ -2827,775 +3967,39 @@ fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIn | ... | @@ -2827,775 +3967,39 @@ fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIn |
| 2827 | else => unreachable, | 3967 | else => unreachable, |
| 2828 | }; | 3968 | }; |
| 2829 | const loc = tree.tokenLocation(0, base_token); | 3969 | const loc = tree.tokenLocation(0, base_token); |
| 2830 | return std.fmt.allocPrint(self.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column }); | 3970 | return std.fmt.allocPrint(mod.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column }); |
| 2831 | } | 3971 | } |
| 2832 | 3972 | ||
| 2833 | fn getNextAnonNameIndex(self: *Module) usize { | 3973 | fn getNextAnonNameIndex(mod: *Module) usize { |
| 2834 | return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic); | 3974 | return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); |
| 2835 | } | 3975 | } |
| 2836 | 3976 | ||
| 2837 | pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { | 3977 | pub fn lookupDeclName(mod: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { |
| 2838 | const namespace = scope.namespace(); | 3978 | const namespace = scope.namespace(); |
| 2839 | const name_hash = namespace.fullyQualifiedNameHash(ident_name); | 3979 | const name_hash = namespace.fullyQualifiedNameHash(ident_name); |
| 2840 | return self.decl_table.get(name_hash); | 3980 | return mod.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); | ||
| 2846 | } | 3981 | } |
| 2847 | 3982 | ||
| 2848 | pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { | 3983 | pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type { |
| 2849 | const scope_decl = scope.ownerDecl().?; | 3984 | const int_payload = try arena.create(Type.Payload.Bits); |
| 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); | ||
| 3322 | int_payload.* = .{ | 3985 | int_payload.* = .{ |
| 3323 | .base = .{ | 3986 | .base = .{ |
| 3324 | .tag = if (signed) .int_signed else .int_unsigned, | 3987 | .tag = switch (signedness) { |
| 3988 | .signed => .int_signed, | ||
| 3989 | .unsigned => .int_unsigned, | ||
| 3990 | }, | ||
| 3325 | }, | 3991 | }, |
| 3326 | .data = bits, | 3992 | .data = bits, |
| 3327 | }; | 3993 | }; |
| 3328 | return Type.initPayload(&int_payload.base); | 3994 | return Type.initPayload(&int_payload.base); |
| 3329 | } | 3995 | } |
| 3330 | 3996 | ||
| 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 | /// We don't return a pointer to the new error note because the pointer | 3997 | /// We don't return a pointer to the new error note because the pointer |
| 3594 | /// becomes invalid when you add another one. | 3998 | /// becomes invalid when you add another one. |
| 3595 | pub fn errNote( | 3999 | pub fn errNote( |
| 3596 | mod: *Module, | 4000 | mod: *Module, |
| 3597 | scope: *Scope, | 4001 | scope: *Scope, |
| 3598 | src: usize, | 4002 | src: LazySrcLoc, |
| 3599 | parent: *ErrorMsg, | 4003 | parent: *ErrorMsg, |
| 3600 | comptime format: []const u8, | 4004 | comptime format: []const u8, |
| 3601 | args: anytype, | 4005 | args: anytype, |
| ... | @@ -3605,10 +4009,7 @@ pub fn errNote( | ... | @@ -3605,10 +4009,7 @@ pub fn errNote( |
| 3605 | 4009 | ||
| 3606 | parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1); | 4010 | parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1); |
| 3607 | parent.notes[parent.notes.len - 1] = .{ | 4011 | parent.notes[parent.notes.len - 1] = .{ |
| 3608 | .src_loc = .{ | 4012 | .src_loc = src.toSrcLoc(scope), |
| 3609 | .file_scope = scope.getFileScope(), | ||
| 3610 | .byte_offset = src, | ||
| 3611 | }, | ||
| 3612 | .msg = msg, | 4013 | .msg = msg, |
| 3613 | }; | 4014 | }; |
| 3614 | } | 4015 | } |
| ... | @@ -3616,121 +4017,112 @@ pub fn errNote( | ... | @@ -3616,121 +4017,112 @@ pub fn errNote( |
| 3616 | pub fn errMsg( | 4017 | pub fn errMsg( |
| 3617 | mod: *Module, | 4018 | mod: *Module, |
| 3618 | scope: *Scope, | 4019 | scope: *Scope, |
| 3619 | src_byte_offset: usize, | 4020 | src: LazySrcLoc, |
| 3620 | comptime format: []const u8, | 4021 | comptime format: []const u8, |
| 3621 | args: anytype, | 4022 | args: anytype, |
| 3622 | ) error{OutOfMemory}!*ErrorMsg { | 4023 | ) error{OutOfMemory}!*ErrorMsg { |
| 3623 | return ErrorMsg.create(mod.gpa, .{ | 4024 | return ErrorMsg.create(mod.gpa, src.toSrcLoc(scope), format, args); |
| 3624 | .file_scope = scope.getFileScope(), | ||
| 3625 | .byte_offset = src_byte_offset, | ||
| 3626 | }, format, args); | ||
| 3627 | } | 4025 | } |
| 3628 | 4026 | ||
| 3629 | pub fn fail( | 4027 | pub fn fail( |
| 3630 | mod: *Module, | 4028 | mod: *Module, |
| 3631 | scope: *Scope, | 4029 | scope: *Scope, |
| 3632 | src_byte_offset: usize, | 4030 | src: LazySrcLoc, |
| 3633 | comptime format: []const u8, | 4031 | comptime format: []const u8, |
| 3634 | args: anytype, | 4032 | args: anytype, |
| 3635 | ) InnerError { | 4033 | ) InnerError { |
| 3636 | const err_msg = try mod.errMsg(scope, src_byte_offset, format, args); | 4034 | const err_msg = try mod.errMsg(scope, src, format, args); |
| 3637 | return mod.failWithOwnedErrorMsg(scope, err_msg); | 4035 | return mod.failWithOwnedErrorMsg(scope, err_msg); |
| 3638 | } | 4036 | } |
| 3639 | 4037 | ||
| 4038 | /// Same as `fail`, except given an absolute byte offset, and the function sets up the `LazySrcLoc` | ||
| 4039 | /// for pointing at it relatively by subtracting from the containing `Decl`. | ||
| 4040 | pub fn failOff( | ||
| 4041 | mod: *Module, | ||
| 4042 | scope: *Scope, | ||
| 4043 | byte_offset: u32, | ||
| 4044 | comptime format: []const u8, | ||
| 4045 | args: anytype, | ||
| 4046 | ) InnerError { | ||
| 4047 | const decl_byte_offset = scope.srcDecl().?.srcByteOffset(); | ||
| 4048 | const src: LazySrcLoc = .{ .byte_offset = byte_offset - decl_byte_offset }; | ||
| 4049 | return mod.fail(scope, src, format, args); | ||
| 4050 | } | ||
| 4051 | |||
| 4052 | /// Same as `fail`, except given a token index, and the function sets up the `LazySrcLoc` | ||
| 4053 | /// for pointing at it relatively by subtracting from the containing `Decl`. | ||
| 3640 | pub fn failTok( | 4054 | pub fn failTok( |
| 3641 | self: *Module, | 4055 | mod: *Module, |
| 3642 | scope: *Scope, | 4056 | scope: *Scope, |
| 3643 | token_index: ast.TokenIndex, | 4057 | token_index: ast.TokenIndex, |
| 3644 | comptime format: []const u8, | 4058 | comptime format: []const u8, |
| 3645 | args: anytype, | 4059 | args: anytype, |
| 3646 | ) InnerError { | 4060 | ) InnerError { |
| 3647 | const src = scope.tree().tokens.items(.start)[token_index]; | 4061 | const src = scope.srcDecl().?.tokSrcLoc(token_index); |
| 3648 | return self.fail(scope, src, format, args); | 4062 | return mod.fail(scope, src, format, args); |
| 3649 | } | 4063 | } |
| 3650 | 4064 | ||
| 4065 | /// Same as `fail`, except given an AST node index, and the function sets up the `LazySrcLoc` | ||
| 4066 | /// for pointing at it relatively by subtracting from the containing `Decl`. | ||
| 3651 | pub fn failNode( | 4067 | pub fn failNode( |
| 3652 | self: *Module, | 4068 | mod: *Module, |
| 3653 | scope: *Scope, | 4069 | scope: *Scope, |
| 3654 | ast_node: ast.Node.Index, | 4070 | node_index: ast.Node.Index, |
| 3655 | comptime format: []const u8, | 4071 | comptime format: []const u8, |
| 3656 | args: anytype, | 4072 | args: anytype, |
| 3657 | ) InnerError { | 4073 | ) InnerError { |
| 3658 | const tree = scope.tree(); | 4074 | const src = scope.srcDecl().?.nodeSrcLoc(node_index); |
| 3659 | const src = tree.tokens.items(.start)[tree.firstToken(ast_node)]; | 4075 | return mod.fail(scope, src, format, args); |
| 3660 | return self.fail(scope, src, format, args); | ||
| 3661 | } | 4076 | } |
| 3662 | 4077 | ||
| 3663 | pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError { | 4078 | pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError { |
| 3664 | @setCold(true); | 4079 | @setCold(true); |
| 3665 | { | 4080 | { |
| 3666 | errdefer err_msg.destroy(self.gpa); | 4081 | errdefer err_msg.destroy(mod.gpa); |
| 3667 | try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); | 4082 | try mod.failed_decls.ensureCapacity(mod.gpa, mod.failed_decls.items().len + 1); |
| 3668 | try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); | 4083 | try mod.failed_files.ensureCapacity(mod.gpa, mod.failed_files.items().len + 1); |
| 3669 | } | 4084 | } |
| 3670 | switch (scope.tag) { | 4085 | switch (scope.tag) { |
| 3671 | .block => { | 4086 | .block => { |
| 3672 | const block = scope.cast(Scope.Block).?; | 4087 | const block = scope.cast(Scope.Block).?; |
| 3673 | if (block.inlining) |inlining| { | 4088 | if (block.sema.owner_func) |func| { |
| 3674 | if (inlining.shared.caller) |func| { | 4089 | func.state = .sema_failure; |
| 3675 | func.state = .sema_failure; | ||
| 3676 | } else { | ||
| 3677 | block.owner_decl.analysis = .sema_failure; | ||
| 3678 | block.owner_decl.generation = self.generation; | ||
| 3679 | } | ||
| 3680 | } else { | 4090 | } else { |
| 3681 | if (block.func) |func| { | 4091 | block.sema.owner_decl.analysis = .sema_failure; |
| 3682 | func.state = .sema_failure; | 4092 | block.sema.owner_decl.generation = mod.generation; |
| 3683 | } else { | ||
| 3684 | block.owner_decl.analysis = .sema_failure; | ||
| 3685 | block.owner_decl.generation = self.generation; | ||
| 3686 | } | ||
| 3687 | } | 4093 | } |
| 3688 | self.failed_decls.putAssumeCapacityNoClobber(block.owner_decl, err_msg); | 4094 | mod.failed_decls.putAssumeCapacityNoClobber(block.sema.owner_decl, err_msg); |
| 3689 | }, | 4095 | }, |
| 3690 | .gen_zir, .gen_suspend => { | 4096 | .gen_zir => { |
| 3691 | const gen_zir = scope.cast(Scope.GenZIR).?; | 4097 | const gen_zir = scope.cast(Scope.GenZir).?; |
| 3692 | gen_zir.decl.analysis = .sema_failure; | 4098 | gen_zir.astgen.decl.analysis = .sema_failure; |
| 3693 | gen_zir.decl.generation = self.generation; | 4099 | gen_zir.astgen.decl.generation = mod.generation; |
| 3694 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | 4100 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg); |
| 3695 | }, | 4101 | }, |
| 3696 | .local_val => { | 4102 | .local_val => { |
| 3697 | const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir; | 4103 | const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir; |
| 3698 | gen_zir.decl.analysis = .sema_failure; | 4104 | gen_zir.astgen.decl.analysis = .sema_failure; |
| 3699 | gen_zir.decl.generation = self.generation; | 4105 | gen_zir.astgen.decl.generation = mod.generation; |
| 3700 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | 4106 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg); |
| 3701 | }, | 4107 | }, |
| 3702 | .local_ptr => { | 4108 | .local_ptr => { |
| 3703 | const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir; | 4109 | const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir; |
| 3704 | gen_zir.decl.analysis = .sema_failure; | 4110 | gen_zir.astgen.decl.analysis = .sema_failure; |
| 3705 | gen_zir.decl.generation = self.generation; | 4111 | gen_zir.astgen.decl.generation = mod.generation; |
| 3706 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | 4112 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.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); | ||
| 3713 | }, | 4113 | }, |
| 3714 | .file => unreachable, | 4114 | .file => unreachable, |
| 3715 | .container => unreachable, | 4115 | .container => unreachable, |
| 4116 | .decl_ref => { | ||
| 4117 | const decl_ref = scope.cast(Scope.DeclRef).?; | ||
| 4118 | decl_ref.decl.analysis = .sema_failure; | ||
| 4119 | decl_ref.decl.generation = mod.generation; | ||
| 4120 | mod.failed_decls.putAssumeCapacityNoClobber(decl_ref.decl, err_msg); | ||
| 4121 | }, | ||
| 3716 | } | 4122 | } |
| 3717 | return error.AnalysisFail; | 4123 | return error.AnalysisFail; |
| 3718 | } | 4124 | } |
| 3719 | 4125 | ||
| 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 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { | 4126 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { |
| 3735 | return @bitCast(u128, a) == @bitCast(u128, b); | 4127 | return @bitCast(u128, a) == @bitCast(u128, b); |
| 3736 | } | 4128 | } |
| ... | @@ -3780,14 +4172,12 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | ... | @@ -3780,14 +4172,12 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { |
| 3780 | } | 4172 | } |
| 3781 | 4173 | ||
| 3782 | pub fn floatAdd( | 4174 | pub fn floatAdd( |
| 3783 | self: *Module, | 4175 | arena: *Allocator, |
| 3784 | scope: *Scope, | ||
| 3785 | float_type: Type, | 4176 | float_type: Type, |
| 3786 | src: usize, | 4177 | src: LazySrcLoc, |
| 3787 | lhs: Value, | 4178 | lhs: Value, |
| 3788 | rhs: Value, | 4179 | rhs: Value, |
| 3789 | ) !Value { | 4180 | ) !Value { |
| 3790 | const arena = scope.arena(); | ||
| 3791 | switch (float_type.tag()) { | 4181 | switch (float_type.tag()) { |
| 3792 | .f16 => { | 4182 | .f16 => { |
| 3793 | @panic("TODO add __trunctfhf2 to compiler-rt"); | 4183 | @panic("TODO add __trunctfhf2 to compiler-rt"); |
| ... | @@ -3815,14 +4205,12 @@ pub fn floatAdd( | ... | @@ -3815,14 +4205,12 @@ pub fn floatAdd( |
| 3815 | } | 4205 | } |
| 3816 | 4206 | ||
| 3817 | pub fn floatSub( | 4207 | pub fn floatSub( |
| 3818 | self: *Module, | 4208 | arena: *Allocator, |
| 3819 | scope: *Scope, | ||
| 3820 | float_type: Type, | 4209 | float_type: Type, |
| 3821 | src: usize, | 4210 | src: LazySrcLoc, |
| 3822 | lhs: Value, | 4211 | lhs: Value, |
| 3823 | rhs: Value, | 4212 | rhs: Value, |
| 3824 | ) !Value { | 4213 | ) !Value { |
| 3825 | const arena = scope.arena(); | ||
| 3826 | switch (float_type.tag()) { | 4214 | switch (float_type.tag()) { |
| 3827 | .f16 => { | 4215 | .f16 => { |
| 3828 | @panic("TODO add __trunctfhf2 to compiler-rt"); | 4216 | @panic("TODO add __trunctfhf2 to compiler-rt"); |
| ... | @@ -3850,9 +4238,8 @@ pub fn floatSub( | ... | @@ -3850,9 +4238,8 @@ pub fn floatSub( |
| 3850 | } | 4238 | } |
| 3851 | 4239 | ||
| 3852 | pub fn simplePtrType( | 4240 | pub fn simplePtrType( |
| 3853 | self: *Module, | 4241 | mod: *Module, |
| 3854 | scope: *Scope, | 4242 | arena: *Allocator, |
| 3855 | src: usize, | ||
| 3856 | elem_ty: Type, | 4243 | elem_ty: Type, |
| 3857 | mutable: bool, | 4244 | mutable: bool, |
| 3858 | size: std.builtin.TypeInfo.Pointer.Size, | 4245 | size: std.builtin.TypeInfo.Pointer.Size, |
| ... | @@ -3863,7 +4250,7 @@ pub fn simplePtrType( | ... | @@ -3863,7 +4250,7 @@ pub fn simplePtrType( |
| 3863 | // TODO stage1 type inference bug | 4250 | // TODO stage1 type inference bug |
| 3864 | const T = Type.Tag; | 4251 | const T = Type.Tag; |
| 3865 | 4252 | ||
| 3866 | const type_payload = try scope.arena().create(Type.Payload.ElemType); | 4253 | const type_payload = try arena.create(Type.Payload.ElemType); |
| 3867 | type_payload.* = .{ | 4254 | type_payload.* = .{ |
| 3868 | .base = .{ | 4255 | .base = .{ |
| 3869 | .tag = switch (size) { | 4256 | .tag = switch (size) { |
| ... | @@ -3879,9 +4266,8 @@ pub fn simplePtrType( | ... | @@ -3879,9 +4266,8 @@ pub fn simplePtrType( |
| 3879 | } | 4266 | } |
| 3880 | 4267 | ||
| 3881 | pub fn ptrType( | 4268 | pub fn ptrType( |
| 3882 | self: *Module, | 4269 | mod: *Module, |
| 3883 | scope: *Scope, | 4270 | arena: *Allocator, |
| 3884 | src: usize, | ||
| 3885 | elem_ty: Type, | 4271 | elem_ty: Type, |
| 3886 | sentinel: ?Value, | 4272 | sentinel: ?Value, |
| 3887 | @"align": u32, | 4273 | @"align": u32, |
| ... | @@ -3895,7 +4281,7 @@ pub fn ptrType( | ... | @@ -3895,7 +4281,7 @@ pub fn ptrType( |
| 3895 | assert(host_size == 0 or bit_offset < host_size * 8); | 4281 | assert(host_size == 0 or bit_offset < host_size * 8); |
| 3896 | 4282 | ||
| 3897 | // TODO check if type can be represented by simplePtrType | 4283 | // TODO check if type can be represented by simplePtrType |
| 3898 | return Type.Tag.pointer.create(scope.arena(), .{ | 4284 | return Type.Tag.pointer.create(arena, .{ |
| 3899 | .pointee_type = elem_ty, | 4285 | .pointee_type = elem_ty, |
| 3900 | .sentinel = sentinel, | 4286 | .sentinel = sentinel, |
| 3901 | .@"align" = @"align", | 4287 | .@"align" = @"align", |
| ... | @@ -3908,23 +4294,23 @@ pub fn ptrType( | ... | @@ -3908,23 +4294,23 @@ pub fn ptrType( |
| 3908 | }); | 4294 | }); |
| 3909 | } | 4295 | } |
| 3910 | 4296 | ||
| 3911 | pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type { | 4297 | pub fn optionalType(mod: *Module, arena: *Allocator, child_type: Type) Allocator.Error!Type { |
| 3912 | switch (child_type.tag()) { | 4298 | switch (child_type.tag()) { |
| 3913 | .single_const_pointer => return Type.Tag.optional_single_const_pointer.create( | 4299 | .single_const_pointer => return Type.Tag.optional_single_const_pointer.create( |
| 3914 | scope.arena(), | 4300 | arena, |
| 3915 | child_type.elemType(), | 4301 | child_type.elemType(), |
| 3916 | ), | 4302 | ), |
| 3917 | .single_mut_pointer => return Type.Tag.optional_single_mut_pointer.create( | 4303 | .single_mut_pointer => return Type.Tag.optional_single_mut_pointer.create( |
| 3918 | scope.arena(), | 4304 | arena, |
| 3919 | child_type.elemType(), | 4305 | child_type.elemType(), |
| 3920 | ), | 4306 | ), |
| 3921 | else => return Type.Tag.optional.create(scope.arena(), child_type), | 4307 | else => return Type.Tag.optional.create(arena, child_type), |
| 3922 | } | 4308 | } |
| 3923 | } | 4309 | } |
| 3924 | 4310 | ||
| 3925 | pub fn arrayType( | 4311 | pub fn arrayType( |
| 3926 | self: *Module, | 4312 | mod: *Module, |
| 3927 | scope: *Scope, | 4313 | arena: *Allocator, |
| 3928 | len: u64, | 4314 | len: u64, |
| 3929 | sentinel: ?Value, | 4315 | sentinel: ?Value, |
| 3930 | elem_type: Type, | 4316 | elem_type: Type, |
| ... | @@ -3932,30 +4318,30 @@ pub fn arrayType( | ... | @@ -3932,30 +4318,30 @@ pub fn arrayType( |
| 3932 | if (elem_type.eql(Type.initTag(.u8))) { | 4318 | if (elem_type.eql(Type.initTag(.u8))) { |
| 3933 | if (sentinel) |some| { | 4319 | if (sentinel) |some| { |
| 3934 | if (some.eql(Value.initTag(.zero))) { | 4320 | if (some.eql(Value.initTag(.zero))) { |
| 3935 | return Type.Tag.array_u8_sentinel_0.create(scope.arena(), len); | 4321 | return Type.Tag.array_u8_sentinel_0.create(arena, len); |
| 3936 | } | 4322 | } |
| 3937 | } else { | 4323 | } else { |
| 3938 | return Type.Tag.array_u8.create(scope.arena(), len); | 4324 | return Type.Tag.array_u8.create(arena, len); |
| 3939 | } | 4325 | } |
| 3940 | } | 4326 | } |
| 3941 | 4327 | ||
| 3942 | if (sentinel) |some| { | 4328 | if (sentinel) |some| { |
| 3943 | return Type.Tag.array_sentinel.create(scope.arena(), .{ | 4329 | return Type.Tag.array_sentinel.create(arena, .{ |
| 3944 | .len = len, | 4330 | .len = len, |
| 3945 | .sentinel = some, | 4331 | .sentinel = some, |
| 3946 | .elem_type = elem_type, | 4332 | .elem_type = elem_type, |
| 3947 | }); | 4333 | }); |
| 3948 | } | 4334 | } |
| 3949 | 4335 | ||
| 3950 | return Type.Tag.array.create(scope.arena(), .{ | 4336 | return Type.Tag.array.create(arena, .{ |
| 3951 | .len = len, | 4337 | .len = len, |
| 3952 | .elem_type = elem_type, | 4338 | .elem_type = elem_type, |
| 3953 | }); | 4339 | }); |
| 3954 | } | 4340 | } |
| 3955 | 4341 | ||
| 3956 | pub fn errorUnionType( | 4342 | pub fn errorUnionType( |
| 3957 | self: *Module, | 4343 | mod: *Module, |
| 3958 | scope: *Scope, | 4344 | arena: *Allocator, |
| 3959 | error_set: Type, | 4345 | error_set: Type, |
| 3960 | payload: Type, | 4346 | payload: Type, |
| 3961 | ) Allocator.Error!Type { | 4347 | ) Allocator.Error!Type { |
| ... | @@ -3964,19 +4350,15 @@ pub fn errorUnionType( | ... | @@ -3964,19 +4350,15 @@ pub fn errorUnionType( |
| 3964 | return Type.initTag(.anyerror_void_error_union); | 4350 | return Type.initTag(.anyerror_void_error_union); |
| 3965 | } | 4351 | } |
| 3966 | 4352 | ||
| 3967 | return Type.Tag.error_union.create(scope.arena(), .{ | 4353 | return Type.Tag.error_union.create(arena, .{ |
| 3968 | .error_set = error_set, | 4354 | .error_set = error_set, |
| 3969 | .payload = payload, | 4355 | .payload = payload, |
| 3970 | }); | 4356 | }); |
| 3971 | } | 4357 | } |
| 3972 | 4358 | ||
| 3973 | pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type { | 4359 | pub fn dumpInst(mod: *Module, scope: *Scope, inst: *ir.Inst) void { |
| 3974 | return Type.Tag.anyframe_T.create(scope.arena(), return_type); | ||
| 3975 | } | ||
| 3976 | |||
| 3977 | pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { | ||
| 3978 | const zir_module = scope.namespace(); | 4360 | const zir_module = scope.namespace(); |
| 3979 | const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source"); | 4361 | const source = zir_module.getSource(mod) catch @panic("dumpInst failed to get source"); |
| 3980 | const loc = std.zig.findLineColumn(source, inst.src); | 4362 | const loc = std.zig.findLineColumn(source, inst.src); |
| 3981 | if (inst.tag == .constant) { | 4363 | if (inst.tag == .constant) { |
| 3982 | std.debug.print("constant ty={} val={} src={s}:{d}:{d}\n", .{ | 4364 | std.debug.print("constant ty={} val={} src={s}:{d}:{d}\n", .{ |
| ... | @@ -4006,267 +4388,117 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { | ... | @@ -4006,267 +4388,117 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { |
| 4006 | } | 4388 | } |
| 4007 | } | 4389 | } |
| 4008 | 4390 | ||
| 4009 | pub const PanicId = enum { | 4391 | pub fn getTarget(mod: Module) Target { |
| 4010 | unreach, | 4392 | return mod.comp.bin_file.options.target; |
| 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); | ||
| 4081 | } | ||
| 4082 | |||
| 4083 | pub fn getTarget(self: Module) Target { | ||
| 4084 | return self.comp.bin_file.options.target; | ||
| 4085 | } | 4393 | } |
| 4086 | 4394 | ||
| 4087 | pub fn optimizeMode(self: Module) std.builtin.Mode { | 4395 | pub fn optimizeMode(mod: Module) std.builtin.Mode { |
| 4088 | return self.comp.bin_file.options.optimize_mode; | 4396 | return mod.comp.bin_file.options.optimize_mode; |
| 4089 | } | 4397 | } |
| 4090 | 4398 | ||
| 4091 | pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void { | 4399 | /// Given an identifier token, obtain the string for it. |
| 4092 | if (!ty.isValidVarType(false)) { | 4400 | /// If the token uses @"" syntax, parses as a string, reports errors if applicable, |
| 4093 | return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty}); | 4401 | /// and allocates the result within `scope.arena()`. |
| 4094 | } | 4402 | /// Otherwise, returns a reference to the source code bytes directly. |
| 4095 | } | 4403 | /// See also `appendIdentStr` and `parseStrLit`. |
| 4096 | |||
| 4097 | /// Identifier token -> String (allocated in scope.arena()) | ||
| 4098 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { | 4404 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { |
| 4099 | const tree = scope.tree(); | 4405 | const tree = scope.tree(); |
| 4100 | const token_tags = tree.tokens.items(.tag); | 4406 | const token_tags = tree.tokens.items(.tag); |
| 4101 | const token_starts = tree.tokens.items(.start); | ||
| 4102 | assert(token_tags[token] == .identifier); | 4407 | assert(token_tags[token] == .identifier); |
| 4103 | |||
| 4104 | const ident_name = tree.tokenSlice(token); | 4408 | const ident_name = tree.tokenSlice(token); |
| 4105 | if (mem.startsWith(u8, ident_name, "@")) { | 4409 | if (!mem.startsWith(u8, ident_name, "@")) { |
| 4106 | const raw_string = ident_name[1..]; | 4410 | return ident_name; |
| 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 | }; | ||
| 4116 | } | 4411 | } |
| 4117 | return ident_name; | 4412 | var buf: ArrayListUnmanaged(u8) = .{}; |
| 4413 | defer buf.deinit(mod.gpa); | ||
| 4414 | try parseStrLit(mod, scope, token, &buf, ident_name, 1); | ||
| 4415 | return buf.toOwnedSlice(mod.gpa); | ||
| 4118 | } | 4416 | } |
| 4119 | 4417 | ||
| 4120 | pub fn emitBackwardBranch(mod: *Module, block: *Scope.Block, src: usize) !void { | 4418 | /// Given an identifier token, obtain the string for it (possibly parsing as a string |
| 4121 | const shared = block.inlining.?.shared; | 4419 | /// literal if it is @"" syntax), and append the string to `buf`. |
| 4122 | shared.branch_count += 1; | 4420 | /// See also `identifierTokenString` and `parseStrLit`. |
| 4123 | if (shared.branch_count > block.branch_quota.*) { | 4421 | pub fn appendIdentStr( |
| 4124 | // TODO show the "called from here" stack | 4422 | mod: *Module, |
| 4125 | return mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{ | 4423 | scope: *Scope, |
| 4126 | block.branch_quota.*, | 4424 | token: ast.TokenIndex, |
| 4127 | }); | 4425 | buf: *ArrayListUnmanaged(u8), |
| 4426 | ) InnerError!void { | ||
| 4427 | const tree = scope.tree(); | ||
| 4428 | const token_tags = tree.tokens.items(.tag); | ||
| 4429 | assert(token_tags[token] == .identifier); | ||
| 4430 | const ident_name = tree.tokenSlice(token); | ||
| 4431 | if (!mem.startsWith(u8, ident_name, "@")) { | ||
| 4432 | return buf.appendSlice(mod.gpa, ident_name); | ||
| 4433 | } else { | ||
| 4434 | return mod.parseStrLit(scope, token, buf, ident_name, 1); | ||
| 4128 | } | 4435 | } |
| 4129 | } | 4436 | } |
| 4130 | 4437 | ||
| 4131 | pub fn namedFieldPtr( | 4438 | /// Appends the result to `buf`. |
| 4439 | pub fn parseStrLit( | ||
| 4132 | mod: *Module, | 4440 | mod: *Module, |
| 4133 | scope: *Scope, | 4441 | scope: *Scope, |
| 4134 | src: usize, | 4442 | token: ast.TokenIndex, |
| 4135 | object_ptr: *Inst, | 4443 | buf: *ArrayListUnmanaged(u8), |
| 4136 | field_name: []const u8, | 4444 | bytes: []const u8, |
| 4137 | field_name_src: usize, | 4445 | offset: u32, |
| 4138 | ) InnerError!*Inst { | 4446 | ) InnerError!void { |
| 4139 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { | 4447 | const tree = scope.tree(); |
| 4140 | .Pointer => object_ptr.ty.elemType(), | 4448 | const token_starts = tree.tokens.items(.start); |
| 4141 | else => return mod.fail(scope, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}), | 4449 | const raw_string = bytes[offset..]; |
| 4142 | }; | 4450 | var buf_managed = buf.toManaged(mod.gpa); |
| 4143 | switch (elem_ty.zigTypeTag()) { | 4451 | const result = std.zig.string_literal.parseAppend(&buf_managed, raw_string); |
| 4144 | .Array => { | 4452 | buf.* = buf_managed.toUnmanaged(); |
| 4145 | if (mem.eql(u8, field_name, "len")) { | 4453 | switch (try result) { |
| 4146 | return mod.constInst(scope, src, .{ | 4454 | .success => return, |
| 4147 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | 4455 | .invalid_character => |bad_index| { |
| 4148 | .val = try Value.Tag.ref_val.create( | 4456 | return mod.failOff( |
| 4149 | scope.arena(), | 4457 | scope, |
| 4150 | try Value.Tag.int_u64.create(scope.arena(), elem_ty.arrayLen()), | 4458 | token_starts[token] + offset + @intCast(u32, bad_index), |
| 4151 | ), | 4459 | "invalid string literal character: '{c}'", |
| 4152 | }); | 4460 | .{raw_string[bad_index]}, |
| 4153 | } else { | 4461 | ); |
| 4154 | return mod.fail( | ||
| 4155 | scope, | ||
| 4156 | field_name_src, | ||
| 4157 | "no member named '{s}' in '{}'", | ||
| 4158 | .{ field_name, elem_ty }, | ||
| 4159 | ); | ||
| 4160 | } | ||
| 4161 | }, | 4462 | }, |
| 4162 | .Pointer => { | 4463 | .expected_hex_digits => |bad_index| { |
| 4163 | const ptr_child = elem_ty.elemType(); | 4464 | return mod.failOff( |
| 4164 | switch (ptr_child.zigTypeTag()) { | 4465 | scope, |
| 4165 | .Array => { | 4466 | token_starts[token] + offset + @intCast(u32, bad_index), |
| 4166 | if (mem.eql(u8, field_name, "len")) { | 4467 | "expected hex digits after '\\x'", |
| 4167 | return mod.constInst(scope, src, .{ | 4468 | .{}, |
| 4168 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | 4469 | ); |
| 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 | } | ||
| 4185 | }, | 4470 | }, |
| 4186 | .Type => { | 4471 | .invalid_hex_escape => |bad_index| { |
| 4187 | _ = try mod.resolveConstValue(scope, object_ptr); | 4472 | return mod.failOff( |
| 4188 | const result = try mod.analyzeDeref(scope, src, object_ptr, object_ptr.src); | 4473 | scope, |
| 4189 | const val = result.value().?; | 4474 | token_starts[token] + offset + @intCast(u32, bad_index), |
| 4190 | const child_type = try val.toType(scope.arena()); | 4475 | "invalid hex digit: '{c}'", |
| 4191 | switch (child_type.zigTypeTag()) { | 4476 | .{raw_string[bad_index]}, |
| 4192 | .ErrorSet => { | 4477 | ); |
| 4193 | var name: []const u8 = undefined; | 4478 | }, |
| 4194 | // TODO resolve inferred error sets | 4479 | .invalid_unicode_escape => |bad_index| { |
| 4195 | if (val.castTag(.error_set)) |payload| | 4480 | return mod.failOff( |
| 4196 | name = (payload.data.fields.getEntry(field_name) orelse return mod.fail(scope, src, "no error named '{s}' in '{}'", .{ field_name, child_type })).key | 4481 | scope, |
| 4197 | else | 4482 | token_starts[token] + offset + @intCast(u32, bad_index), |
| 4198 | name = (try mod.getErrorValue(field_name)).key; | 4483 | "invalid unicode digit: '{c}'", |
| 4199 | 4484 | .{raw_string[bad_index]}, | |
| 4200 | const result_type = if (child_type.tag() == .anyerror) | 4485 | ); |
| 4201 | try Type.Tag.error_set_single.create(scope.arena(), name) | 4486 | }, |
| 4202 | else | 4487 | .missing_matching_rbrace => |bad_index| { |
| 4203 | child_type; | 4488 | return mod.failOff( |
| 4204 | 4489 | scope, | |
| 4205 | return mod.constInst(scope, src, .{ | 4490 | token_starts[token] + offset + @intCast(u32, bad_index), |
| 4206 | .ty = try mod.simplePtrType(scope, src, result_type, false, .One), | 4491 | "missing matching '}}' character", |
| 4207 | .val = try Value.Tag.ref_val.create( | 4492 | .{}, |
| 4208 | scope.arena(), | 4493 | ); |
| 4209 | try Value.Tag.@"error".create(scope.arena(), .{ | 4494 | }, |
| 4210 | .name = name, | 4495 | .expected_unicode_digits => |bad_index| { |
| 4211 | }), | 4496 | return mod.failOff( |
| 4212 | ), | 4497 | scope, |
| 4213 | }); | 4498 | token_starts[token] + offset + @intCast(u32, bad_index), |
| 4214 | }, | 4499 | "expected unicode digits after '\\u'", |
| 4215 | .Struct => { | 4500 | .{}, |
| 4216 | const container_scope = child_type.getContainerScope(); | 4501 | ); |
| 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 | } | ||
| 4230 | }, | 4502 | }, |
| 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 | } | 4503 | } |
| 4270 | |||
| 4271 | return mod.fail(scope, src, "TODO implement more analyze elemptr", .{}); | ||
| 4272 | } | 4504 | } |
src/RangeSet.zig+19-18| ... | @@ -2,13 +2,14 @@ const std = @import("std"); | ... | @@ -2,13 +2,14 @@ const std = @import("std"); |
| 2 | const Order = std.math.Order; | 2 | const Order = std.math.Order; |
| 3 | const Value = @import("value.zig").Value; | 3 | const Value = @import("value.zig").Value; |
| 4 | const RangeSet = @This(); | 4 | const RangeSet = @This(); |
| 5 | const SwitchProngSrc = @import("AstGen.zig").SwitchProngSrc; | ||
| 5 | 6 | ||
| 6 | ranges: std.ArrayList(Range), | 7 | ranges: std.ArrayList(Range), |
| 7 | 8 | ||
| 8 | pub const Range = struct { | 9 | pub const Range = struct { |
| 9 | start: Value, | 10 | first: Value, |
| 10 | end: Value, | 11 | last: Value, |
| 11 | src: usize, | 12 | src: SwitchProngSrc, |
| 12 | }; | 13 | }; |
| 13 | 14 | ||
| 14 | pub fn init(allocator: *std.mem.Allocator) RangeSet { | 15 | pub fn init(allocator: *std.mem.Allocator) RangeSet { |
| ... | @@ -21,18 +22,15 @@ pub fn deinit(self: *RangeSet) void { | ... | @@ -21,18 +22,15 @@ pub fn deinit(self: *RangeSet) void { |
| 21 | self.ranges.deinit(); | 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 | for (self.ranges.items) |range| { | 26 | for (self.ranges.items) |range| { |
| 26 | if ((start.compare(.gte, range.start) and start.compare(.lte, range.end)) or | 27 | if (last.compare(.gte, range.first) and first.compare(.lte, range.last)) { |
| 27 | (end.compare(.gte, range.start) and end.compare(.lte, range.end))) | 28 | return range.src; // They overlap. |
| 28 | { | ||
| 29 | // ranges overlap | ||
| 30 | return range.src; | ||
| 31 | } | 29 | } |
| 32 | } | 30 | } |
| 33 | try self.ranges.append(.{ | 31 | try self.ranges.append(.{ |
| 34 | .start = start, | 32 | .first = first, |
| 35 | .end = end, | 33 | .last = last, |
| 36 | .src = src, | 34 | .src = src, |
| 37 | }); | 35 | }); |
| 38 | return null; | 36 | return null; |
| ... | @@ -40,14 +38,17 @@ pub fn add(self: *RangeSet, start: Value, end: Value, src: usize) !?usize { | ... | @@ -40,14 +38,17 @@ pub fn add(self: *RangeSet, start: Value, end: Value, src: usize) !?usize { |
| 40 | 38 | ||
| 41 | /// Assumes a and b do not overlap | 39 | /// Assumes a and b do not overlap |
| 42 | fn lessThan(_: void, a: Range, b: Range) bool { | 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 | std.sort.sort(Range, self.ranges.items, {}, lessThan); | 48 | std.sort.sort(Range, self.ranges.items, {}, lessThan); |
| 48 | 49 | ||
| 49 | if (!self.ranges.items[0].start.eql(start) or | 50 | if (!self.ranges.items[0].first.eql(first) or |
| 50 | !self.ranges.items[self.ranges.items.len - 1].end.eql(end)) | 51 | !self.ranges.items[self.ranges.items.len - 1].last.eql(last)) |
| 51 | { | 52 | { |
| 52 | return false; | 53 | return false; |
| 53 | } | 54 | } |
| ... | @@ -62,11 +63,11 @@ pub fn spans(self: *RangeSet, start: Value, end: Value) !bool { | ... | @@ -62,11 +63,11 @@ pub fn spans(self: *RangeSet, start: Value, end: Value) !bool { |
| 62 | // i starts counting from the second item. | 63 | // i starts counting from the second item. |
| 63 | const prev = self.ranges.items[i]; | 64 | const prev = self.ranges.items[i]; |
| 64 | 65 | ||
| 65 | // prev.end + 1 == cur.start | 66 | // prev.last + 1 == cur.first |
| 66 | try counter.copy(prev.end.toBigInt(&space)); | 67 | try counter.copy(prev.last.toBigInt(&space)); |
| 67 | try counter.addScalar(counter.toConst(), 1); | 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 | if (!cur_start_int.eq(counter.toConst())) { | 71 | if (!cur_start_int.eq(counter.toConst())) { |
| 71 | return false; | 72 | return false; |
| 72 | } | 73 | } |
src/Sema.zig created+5091| ... | @@ -0,0 +1,5091 @@ | ||
| 1 | //! Semantic analysis of ZIR instructions. | ||
| 2 | //! Shared to every Block. Stored on the stack. | ||
| 3 | //! State used for compiling a `zir.Code` into TZIR. | ||
| 4 | //! Transforms untyped ZIR instructions into semantically-analyzed TZIR instructions. | ||
| 5 | //! Does type checking, comptime control flow, and safety-check generation. | ||
| 6 | //! This is the the heart of the Zig compiler. | ||
| 7 | |||
| 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 | .struct_init_empty => try sema.zirStructInitEmpty(block, inst), | ||
| 263 | |||
| 264 | .struct_decl => try sema.zirStructDecl(block, inst, .Auto), | ||
| 265 | .struct_decl_packed => try sema.zirStructDecl(block, inst, .Packed), | ||
| 266 | .struct_decl_extern => try sema.zirStructDecl(block, inst, .Extern), | ||
| 267 | .enum_decl => try sema.zirEnumDecl(block, inst), | ||
| 268 | .union_decl => try sema.zirUnionDecl(block, inst), | ||
| 269 | .opaque_decl => try sema.zirOpaqueDecl(block, inst), | ||
| 270 | |||
| 271 | // Instructions that we know to *always* be noreturn based solely on their tag. | ||
| 272 | // These functions match the return type of analyzeBody so that we can | ||
| 273 | // tail call them here. | ||
| 274 | .condbr => return sema.zirCondbr(block, inst), | ||
| 275 | .@"break" => return sema.zirBreak(block, inst), | ||
| 276 | .break_inline => return inst, | ||
| 277 | .compile_error => return sema.zirCompileError(block, inst), | ||
| 278 | .ret_coerce => return sema.zirRetTok(block, inst, true), | ||
| 279 | .ret_node => return sema.zirRetNode(block, inst), | ||
| 280 | .ret_tok => return sema.zirRetTok(block, inst, false), | ||
| 281 | .@"unreachable" => return sema.zirUnreachable(block, inst), | ||
| 282 | .repeat => return sema.zirRepeat(block, inst), | ||
| 283 | |||
| 284 | // Instructions that we know can *never* be noreturn based solely on | ||
| 285 | // their tag. We avoid needlessly checking if they are noreturn and | ||
| 286 | // continue the loop. | ||
| 287 | // We also know that they cannot be referenced later, so we avoid | ||
| 288 | // putting them into the map. | ||
| 289 | .breakpoint => { | ||
| 290 | try sema.zirBreakpoint(block, inst); | ||
| 291 | continue; | ||
| 292 | }, | ||
| 293 | .dbg_stmt_node => { | ||
| 294 | try sema.zirDbgStmtNode(block, inst); | ||
| 295 | continue; | ||
| 296 | }, | ||
| 297 | .ensure_err_payload_void => { | ||
| 298 | try sema.zirEnsureErrPayloadVoid(block, inst); | ||
| 299 | continue; | ||
| 300 | }, | ||
| 301 | .ensure_result_non_error => { | ||
| 302 | try sema.zirEnsureResultNonError(block, inst); | ||
| 303 | continue; | ||
| 304 | }, | ||
| 305 | .ensure_result_used => { | ||
| 306 | try sema.zirEnsureResultUsed(block, inst); | ||
| 307 | continue; | ||
| 308 | }, | ||
| 309 | .compile_log => { | ||
| 310 | try sema.zirCompileLog(block, inst); | ||
| 311 | continue; | ||
| 312 | }, | ||
| 313 | .set_eval_branch_quota => { | ||
| 314 | try sema.zirSetEvalBranchQuota(block, inst); | ||
| 315 | continue; | ||
| 316 | }, | ||
| 317 | .store => { | ||
| 318 | try sema.zirStore(block, inst); | ||
| 319 | continue; | ||
| 320 | }, | ||
| 321 | .store_node => { | ||
| 322 | try sema.zirStoreNode(block, inst); | ||
| 323 | continue; | ||
| 324 | }, | ||
| 325 | .store_to_block_ptr => { | ||
| 326 | try sema.zirStoreToBlockPtr(block, inst); | ||
| 327 | continue; | ||
| 328 | }, | ||
| 329 | .store_to_inferred_ptr => { | ||
| 330 | try sema.zirStoreToInferredPtr(block, inst); | ||
| 331 | continue; | ||
| 332 | }, | ||
| 333 | .resolve_inferred_alloc => { | ||
| 334 | try sema.zirResolveInferredAlloc(block, inst); | ||
| 335 | continue; | ||
| 336 | }, | ||
| 337 | .validate_struct_init_ptr => { | ||
| 338 | try sema.zirValidateStructInitPtr(block, inst); | ||
| 339 | continue; | ||
| 340 | }, | ||
| 341 | |||
| 342 | // Special case instructions to handle comptime control flow. | ||
| 343 | .repeat_inline => { | ||
| 344 | // Send comptime control flow back to the beginning of this block. | ||
| 345 | const src: LazySrcLoc = .{ .node_offset = datas[inst].node }; | ||
| 346 | try sema.emitBackwardBranch(block, src); | ||
| 347 | i = 0; | ||
| 348 | continue; | ||
| 349 | }, | ||
| 350 | .block_inline => blk: { | ||
| 351 | // Directly analyze the block body without introducing a new block. | ||
| 352 | const inst_data = datas[inst].pl_node; | ||
| 353 | const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); | ||
| 354 | const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; | ||
| 355 | const break_inst = try sema.analyzeBody(block, inline_body); | ||
| 356 | const break_data = datas[break_inst].@"break"; | ||
| 357 | if (inst == break_data.block_inst) { | ||
| 358 | break :blk try sema.resolveInst(break_data.operand); | ||
| 359 | } else { | ||
| 360 | return break_inst; | ||
| 361 | } | ||
| 362 | }, | ||
| 363 | .condbr_inline => blk: { | ||
| 364 | const inst_data = datas[inst].pl_node; | ||
| 365 | const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; | ||
| 366 | const extra = sema.code.extraData(zir.Inst.CondBr, inst_data.payload_index); | ||
| 367 | const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; | ||
| 368 | const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | ||
| 369 | const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition); | ||
| 370 | const inline_body = if (cond.val.toBool()) then_body else else_body; | ||
| 371 | const break_inst = try sema.analyzeBody(block, inline_body); | ||
| 372 | const break_data = datas[break_inst].@"break"; | ||
| 373 | if (inst == break_data.block_inst) { | ||
| 374 | break :blk try sema.resolveInst(break_data.operand); | ||
| 375 | } else { | ||
| 376 | return break_inst; | ||
| 377 | } | ||
| 378 | }, | ||
| 379 | }; | ||
| 380 | if (map[inst].ty.isNoReturn()) | ||
| 381 | return always_noreturn; | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | /// TODO when we rework TZIR memory layout, this function will no longer have a possible error. | ||
| 386 | pub fn resolveInst(sema: *Sema, zir_ref: zir.Inst.Ref) error{OutOfMemory}!*ir.Inst { | ||
| 387 | var i: usize = @enumToInt(zir_ref); | ||
| 388 | |||
| 389 | // First section of indexes correspond to a set number of constant values. | ||
| 390 | if (i < zir.Inst.Ref.typed_value_map.len) { | ||
| 391 | // TODO when we rework TZIR memory layout, this function can be as simple as: | ||
| 392 | // if (zir_ref < zir.const_inst_list.len + sema.param_count) | ||
| 393 | // return zir_ref; | ||
| 394 | // Until then we allocate memory for a new, mutable `ir.Inst` to match what | ||
| 395 | // TZIR expects. | ||
| 396 | return sema.mod.constInst(sema.arena, .unneeded, zir.Inst.Ref.typed_value_map[i]); | ||
| 397 | } | ||
| 398 | i -= zir.Inst.Ref.typed_value_map.len; | ||
| 399 | |||
| 400 | // Next section of indexes correspond to function parameters, if any. | ||
| 401 | if (i < sema.param_inst_list.len) { | ||
| 402 | return sema.param_inst_list[i]; | ||
| 403 | } | ||
| 404 | i -= sema.param_inst_list.len; | ||
| 405 | |||
| 406 | // Finally, the last section of indexes refers to the map of ZIR=>TZIR. | ||
| 407 | return sema.inst_map[i]; | ||
| 408 | } | ||
| 409 | |||
| 410 | fn resolveConstString( | ||
| 411 | sema: *Sema, | ||
| 412 | block: *Scope.Block, | ||
| 413 | src: LazySrcLoc, | ||
| 414 | zir_ref: zir.Inst.Ref, | ||
| 415 | ) ![]u8 { | ||
| 416 | const tzir_inst = try sema.resolveInst(zir_ref); | ||
| 417 | const wanted_type = Type.initTag(.const_slice_u8); | ||
| 418 | const coerced_inst = try sema.coerce(block, wanted_type, tzir_inst, src); | ||
| 419 | const val = try sema.resolveConstValue(block, src, coerced_inst); | ||
| 420 | return val.toAllocatedBytes(sema.arena); | ||
| 421 | } | ||
| 422 | |||
| 423 | fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: zir.Inst.Ref) !Type { | ||
| 424 | const tzir_inst = try sema.resolveInst(zir_ref); | ||
| 425 | const wanted_type = Type.initTag(.@"type"); | ||
| 426 | const coerced_inst = try sema.coerce(block, wanted_type, tzir_inst, src); | ||
| 427 | const val = try sema.resolveConstValue(block, src, coerced_inst); | ||
| 428 | return val.toType(sema.arena); | ||
| 429 | } | ||
| 430 | |||
| 431 | fn resolveConstValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !Value { | ||
| 432 | return (try sema.resolveDefinedValue(block, src, base)) orelse | ||
| 433 | return sema.failWithNeededComptime(block, src); | ||
| 434 | } | ||
| 435 | |||
| 436 | fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !?Value { | ||
| 437 | if (base.value()) |val| { | ||
| 438 | if (val.isUndef()) { | ||
| 439 | return sema.failWithUseOfUndef(block, src); | ||
| 440 | } | ||
| 441 | return val; | ||
| 442 | } | ||
| 443 | return null; | ||
| 444 | } | ||
| 445 | |||
| 446 | fn failWithNeededComptime(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError { | ||
| 447 | return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{}); | ||
| 448 | } | ||
| 449 | |||
| 450 | fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError { | ||
| 451 | return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{}); | ||
| 452 | } | ||
| 453 | |||
| 454 | /// Appropriate to call when the coercion has already been done by result | ||
| 455 | /// location semantics. Asserts the value fits in the provided `Int` type. | ||
| 456 | /// Only supports `Int` types 64 bits or less. | ||
| 457 | fn resolveAlreadyCoercedInt( | ||
| 458 | sema: *Sema, | ||
| 459 | block: *Scope.Block, | ||
| 460 | src: LazySrcLoc, | ||
| 461 | zir_ref: zir.Inst.Ref, | ||
| 462 | comptime Int: type, | ||
| 463 | ) !Int { | ||
| 464 | comptime assert(@typeInfo(Int).Int.bits <= 64); | ||
| 465 | const tzir_inst = try sema.resolveInst(zir_ref); | ||
| 466 | const val = try sema.resolveConstValue(block, src, tzir_inst); | ||
| 467 | switch (@typeInfo(Int).Int.signedness) { | ||
| 468 | .signed => return @intCast(Int, val.toSignedInt()), | ||
| 469 | .unsigned => return @intCast(Int, val.toUnsignedInt()), | ||
| 470 | } | ||
| 471 | } | ||
| 472 | |||
| 473 | fn resolveInt( | ||
| 474 | sema: *Sema, | ||
| 475 | block: *Scope.Block, | ||
| 476 | src: LazySrcLoc, | ||
| 477 | zir_ref: zir.Inst.Ref, | ||
| 478 | dest_type: Type, | ||
| 479 | ) !u64 { | ||
| 480 | const tzir_inst = try sema.resolveInst(zir_ref); | ||
| 481 | const coerced = try sema.coerce(block, dest_type, tzir_inst, src); | ||
| 482 | const val = try sema.resolveConstValue(block, src, coerced); | ||
| 483 | |||
| 484 | return val.toUnsignedInt(); | ||
| 485 | } | ||
| 486 | |||
| 487 | fn resolveInstConst( | ||
| 488 | sema: *Sema, | ||
| 489 | block: *Scope.Block, | ||
| 490 | src: LazySrcLoc, | ||
| 491 | zir_ref: zir.Inst.Ref, | ||
| 492 | ) InnerError!TypedValue { | ||
| 493 | const tzir_inst = try sema.resolveInst(zir_ref); | ||
| 494 | const val = try sema.resolveConstValue(block, src, tzir_inst); | ||
| 495 | return TypedValue{ | ||
| 496 | .ty = tzir_inst.ty, | ||
| 497 | .val = val, | ||
| 498 | }; | ||
| 499 | } | ||
| 500 | |||
| 501 | fn zirConst(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 502 | const tracy = trace(@src()); | ||
| 503 | defer tracy.end(); | ||
| 504 | |||
| 505 | const tv_ptr = sema.code.instructions.items(.data)[inst].@"const"; | ||
| 506 | // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions | ||
| 507 | // after analysis. This happens, for example, with variable declaration initialization | ||
| 508 | // expressions. | ||
| 509 | const typed_value_copy = try tv_ptr.copy(sema.arena); | ||
| 510 | return sema.mod.constInst(sema.arena, .unneeded, typed_value_copy); | ||
| 511 | } | ||
| 512 | |||
| 513 | fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 514 | const tracy = trace(@src()); | ||
| 515 | defer tracy.end(); | ||
| 516 | return sema.mod.fail(&block.base, sema.src, "TODO implement zir_sema.zirBitcastResultPtr", .{}); | ||
| 517 | } | ||
| 518 | |||
| 519 | fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 520 | const tracy = trace(@src()); | ||
| 521 | defer tracy.end(); | ||
| 522 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirCoerceResultPtr", .{}); | ||
| 523 | } | ||
| 524 | |||
| 525 | fn zirStructDecl( | ||
| 526 | sema: *Sema, | ||
| 527 | block: *Scope.Block, | ||
| 528 | inst: zir.Inst.Index, | ||
| 529 | layout: std.builtin.TypeInfo.ContainerLayout, | ||
| 530 | ) InnerError!*Inst { | ||
| 531 | const tracy = trace(@src()); | ||
| 532 | defer tracy.end(); | ||
| 533 | |||
| 534 | const gpa = sema.gpa; | ||
| 535 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 536 | const src = inst_data.src(); | ||
| 537 | const extra = sema.code.extraData(zir.Inst.StructDecl, inst_data.payload_index); | ||
| 538 | const fields_len = extra.data.fields_len; | ||
| 539 | const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable; | ||
| 540 | |||
| 541 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | ||
| 542 | errdefer new_decl_arena.deinit(); | ||
| 543 | |||
| 544 | var fields_map: std.StringArrayHashMapUnmanaged(Module.Struct.Field) = .{}; | ||
| 545 | try fields_map.ensureCapacity(&new_decl_arena.allocator, fields_len); | ||
| 546 | |||
| 547 | { | ||
| 548 | var field_index: usize = extra.end + bit_bags_count; | ||
| 549 | var bit_bag_index: usize = extra.end; | ||
| 550 | var cur_bit_bag: u32 = undefined; | ||
| 551 | var field_i: u32 = 0; | ||
| 552 | while (field_i < fields_len) : (field_i += 1) { | ||
| 553 | if (field_i % 16 == 0) { | ||
| 554 | cur_bit_bag = sema.code.extra[bit_bag_index]; | ||
| 555 | bit_bag_index += 1; | ||
| 556 | } | ||
| 557 | const has_align = @truncate(u1, cur_bit_bag) != 0; | ||
| 558 | cur_bit_bag >>= 1; | ||
| 559 | const has_default = @truncate(u1, cur_bit_bag) != 0; | ||
| 560 | cur_bit_bag >>= 1; | ||
| 561 | |||
| 562 | const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[field_index]); | ||
| 563 | field_index += 1; | ||
| 564 | const field_type_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]); | ||
| 565 | field_index += 1; | ||
| 566 | |||
| 567 | // This string needs to outlive the ZIR code. | ||
| 568 | const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir); | ||
| 569 | // TODO: if we need to report an error here, use a source location | ||
| 570 | // that points to this type expression rather than the struct. | ||
| 571 | // But only resolve the source location if we need to emit a compile error. | ||
| 572 | const field_ty = try sema.resolveType(block, src, field_type_ref); | ||
| 573 | |||
| 574 | const gop = fields_map.getOrPutAssumeCapacity(field_name); | ||
| 575 | assert(!gop.found_existing); | ||
| 576 | gop.entry.value = .{ | ||
| 577 | .ty = field_ty, | ||
| 578 | .abi_align = Value.initTag(.abi_align_default), | ||
| 579 | .default_val = Value.initTag(.unreachable_value), | ||
| 580 | }; | ||
| 581 | |||
| 582 | if (has_align) { | ||
| 583 | const align_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]); | ||
| 584 | field_index += 1; | ||
| 585 | // TODO: if we need to report an error here, use a source location | ||
| 586 | // that points to this alignment expression rather than the struct. | ||
| 587 | // But only resolve the source location if we need to emit a compile error. | ||
| 588 | gop.entry.value.abi_align = (try sema.resolveInstConst(block, src, align_ref)).val; | ||
| 589 | } | ||
| 590 | if (has_default) { | ||
| 591 | const default_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]); | ||
| 592 | field_index += 1; | ||
| 593 | // TODO: if we need to report an error here, use a source location | ||
| 594 | // that points to this default value expression rather than the struct. | ||
| 595 | // But only resolve the source location if we need to emit a compile error. | ||
| 596 | gop.entry.value.default_val = (try sema.resolveInstConst(block, src, default_ref)).val; | ||
| 597 | } | ||
| 598 | } | ||
| 599 | } | ||
| 600 | |||
| 601 | const struct_obj = try new_decl_arena.allocator.create(Module.Struct); | ||
| 602 | const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj); | ||
| 603 | struct_obj.* = .{ | ||
| 604 | .owner_decl = sema.owner_decl, | ||
| 605 | .fields = fields_map, | ||
| 606 | .node_offset = inst_data.src_node, | ||
| 607 | .container = .{ | ||
| 608 | .ty = struct_ty, | ||
| 609 | .file_scope = block.getFileScope(), | ||
| 610 | }, | ||
| 611 | }; | ||
| 612 | const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{ | ||
| 613 | .ty = Type.initTag(.type), | ||
| 614 | .val = try Value.Tag.ty.create(gpa, struct_ty), | ||
| 615 | }); | ||
| 616 | return sema.analyzeDeclVal(block, src, new_decl); | ||
| 617 | } | ||
| 618 | |||
| 619 | fn zirEnumDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 620 | const tracy = trace(@src()); | ||
| 621 | defer tracy.end(); | ||
| 622 | |||
| 623 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 624 | const src = inst_data.src(); | ||
| 625 | const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); | ||
| 626 | |||
| 627 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirEnumDecl", .{}); | ||
| 628 | } | ||
| 629 | |||
| 630 | fn zirUnionDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 631 | const tracy = trace(@src()); | ||
| 632 | defer tracy.end(); | ||
| 633 | |||
| 634 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 635 | const src = inst_data.src(); | ||
| 636 | const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); | ||
| 637 | |||
| 638 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirUnionDecl", .{}); | ||
| 639 | } | ||
| 640 | |||
| 641 | fn zirOpaqueDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 642 | const tracy = trace(@src()); | ||
| 643 | defer tracy.end(); | ||
| 644 | |||
| 645 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 646 | const src = inst_data.src(); | ||
| 647 | const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); | ||
| 648 | |||
| 649 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirOpaqueDecl", .{}); | ||
| 650 | } | ||
| 651 | |||
| 652 | fn zirRetPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 653 | const tracy = trace(@src()); | ||
| 654 | defer tracy.end(); | ||
| 655 | |||
| 656 | const src: LazySrcLoc = .unneeded; | ||
| 657 | try sema.requireFunctionBlock(block, src); | ||
| 658 | const fn_ty = sema.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | ||
| 659 | const ret_type = fn_ty.fnReturnType(); | ||
| 660 | const ptr_type = try sema.mod.simplePtrType(sema.arena, ret_type, true, .One); | ||
| 661 | return block.addNoOp(src, ptr_type, .alloc); | ||
| 662 | } | ||
| 663 | |||
| 664 | fn zirRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 665 | const tracy = trace(@src()); | ||
| 666 | defer tracy.end(); | ||
| 667 | |||
| 668 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 669 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 670 | return sema.analyzeRef(block, inst_data.src(), operand); | ||
| 671 | } | ||
| 672 | |||
| 673 | fn zirRetType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 674 | const tracy = trace(@src()); | ||
| 675 | defer tracy.end(); | ||
| 676 | |||
| 677 | const src: LazySrcLoc = .unneeded; | ||
| 678 | try sema.requireFunctionBlock(block, src); | ||
| 679 | const fn_ty = sema.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | ||
| 680 | const ret_type = fn_ty.fnReturnType(); | ||
| 681 | return sema.mod.constType(sema.arena, src, ret_type); | ||
| 682 | } | ||
| 683 | |||
| 684 | fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 685 | const tracy = trace(@src()); | ||
| 686 | defer tracy.end(); | ||
| 687 | |||
| 688 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 689 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 690 | const src = inst_data.src(); | ||
| 691 | |||
| 692 | return sema.ensureResultUsed(block, operand, src); | ||
| 693 | } | ||
| 694 | |||
| 695 | fn ensureResultUsed( | ||
| 696 | sema: *Sema, | ||
| 697 | block: *Scope.Block, | ||
| 698 | operand: *Inst, | ||
| 699 | src: LazySrcLoc, | ||
| 700 | ) InnerError!void { | ||
| 701 | switch (operand.ty.zigTypeTag()) { | ||
| 702 | .Void, .NoReturn => return, | ||
| 703 | else => return sema.mod.fail(&block.base, src, "expression value is ignored", .{}), | ||
| 704 | } | ||
| 705 | } | ||
| 706 | |||
| 707 | fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 708 | const tracy = trace(@src()); | ||
| 709 | defer tracy.end(); | ||
| 710 | |||
| 711 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 712 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 713 | const src = inst_data.src(); | ||
| 714 | switch (operand.ty.zigTypeTag()) { | ||
| 715 | .ErrorSet, .ErrorUnion => return sema.mod.fail(&block.base, src, "error is discarded", .{}), | ||
| 716 | else => return, | ||
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 720 | fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 721 | const tracy = trace(@src()); | ||
| 722 | defer tracy.end(); | ||
| 723 | |||
| 724 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 725 | const src = inst_data.src(); | ||
| 726 | const array_ptr = try sema.resolveInst(inst_data.operand); | ||
| 727 | |||
| 728 | const elem_ty = array_ptr.ty.elemType(); | ||
| 729 | if (!elem_ty.isIndexable()) { | ||
| 730 | const cond_src: LazySrcLoc = .{ .node_offset_for_cond = inst_data.src_node }; | ||
| 731 | const msg = msg: { | ||
| 732 | const msg = try sema.mod.errMsg( | ||
| 733 | &block.base, | ||
| 734 | cond_src, | ||
| 735 | "type '{}' does not support indexing", | ||
| 736 | .{elem_ty}, | ||
| 737 | ); | ||
| 738 | errdefer msg.destroy(sema.gpa); | ||
| 739 | try sema.mod.errNote( | ||
| 740 | &block.base, | ||
| 741 | cond_src, | ||
| 742 | msg, | ||
| 743 | "for loop operand must be an array, slice, tuple, or vector", | ||
| 744 | .{}, | ||
| 745 | ); | ||
| 746 | break :msg msg; | ||
| 747 | }; | ||
| 748 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | ||
| 749 | } | ||
| 750 | const result_ptr = try sema.namedFieldPtr(block, src, array_ptr, "len", src); | ||
| 751 | return sema.analyzeLoad(block, src, result_ptr, result_ptr.src); | ||
| 752 | } | ||
| 753 | |||
| 754 | fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 755 | const tracy = trace(@src()); | ||
| 756 | defer tracy.end(); | ||
| 757 | |||
| 758 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 759 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | ||
| 760 | const var_decl_src = inst_data.src(); | ||
| 761 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); | ||
| 762 | const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One); | ||
| 763 | try sema.requireRuntimeBlock(block, var_decl_src); | ||
| 764 | return block.addNoOp(var_decl_src, ptr_type, .alloc); | ||
| 765 | } | ||
| 766 | |||
| 767 | fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 768 | const tracy = trace(@src()); | ||
| 769 | defer tracy.end(); | ||
| 770 | |||
| 771 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 772 | const var_decl_src = inst_data.src(); | ||
| 773 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | ||
| 774 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); | ||
| 775 | try sema.validateVarType(block, ty_src, var_type); | ||
| 776 | const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One); | ||
| 777 | try sema.requireRuntimeBlock(block, var_decl_src); | ||
| 778 | return block.addNoOp(var_decl_src, ptr_type, .alloc); | ||
| 779 | } | ||
| 780 | |||
| 781 | fn zirAllocInferred( | ||
| 782 | sema: *Sema, | ||
| 783 | block: *Scope.Block, | ||
| 784 | inst: zir.Inst.Index, | ||
| 785 | inferred_alloc_ty: Type, | ||
| 786 | ) InnerError!*Inst { | ||
| 787 | const tracy = trace(@src()); | ||
| 788 | defer tracy.end(); | ||
| 789 | |||
| 790 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 791 | const src = inst_data.src(); | ||
| 792 | |||
| 793 | const val_payload = try sema.arena.create(Value.Payload.InferredAlloc); | ||
| 794 | val_payload.* = .{ | ||
| 795 | .data = .{}, | ||
| 796 | }; | ||
| 797 | // `Module.constInst` does not add the instruction to the block because it is | ||
| 798 | // not needed in the case of constant values. However here, we plan to "downgrade" | ||
| 799 | // to a normal instruction when we hit `resolve_inferred_alloc`. So we append | ||
| 800 | // to the block even though it is currently a `.constant`. | ||
| 801 | const result = try sema.mod.constInst(sema.arena, src, .{ | ||
| 802 | .ty = inferred_alloc_ty, | ||
| 803 | .val = Value.initPayload(&val_payload.base), | ||
| 804 | }); | ||
| 805 | try sema.requireFunctionBlock(block, src); | ||
| 806 | try block.instructions.append(sema.gpa, result); | ||
| 807 | return result; | ||
| 808 | } | ||
| 809 | |||
| 810 | fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 811 | const tracy = trace(@src()); | ||
| 812 | defer tracy.end(); | ||
| 813 | |||
| 814 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 815 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | ||
| 816 | const ptr = try sema.resolveInst(inst_data.operand); | ||
| 817 | const ptr_val = ptr.castTag(.constant).?.val; | ||
| 818 | const inferred_alloc = ptr_val.castTag(.inferred_alloc).?; | ||
| 819 | const peer_inst_list = inferred_alloc.data.stored_inst_list.items; | ||
| 820 | const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list); | ||
| 821 | const var_is_mut = switch (ptr.ty.tag()) { | ||
| 822 | .inferred_alloc_const => false, | ||
| 823 | .inferred_alloc_mut => true, | ||
| 824 | else => unreachable, | ||
| 825 | }; | ||
| 826 | if (var_is_mut) { | ||
| 827 | try sema.validateVarType(block, ty_src, final_elem_ty); | ||
| 828 | } | ||
| 829 | const final_ptr_ty = try sema.mod.simplePtrType(sema.arena, final_elem_ty, true, .One); | ||
| 830 | |||
| 831 | // Change it to a normal alloc. | ||
| 832 | ptr.ty = final_ptr_ty; | ||
| 833 | ptr.tag = .alloc; | ||
| 834 | } | ||
| 835 | |||
| 836 | fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 837 | const tracy = trace(@src()); | ||
| 838 | defer tracy.end(); | ||
| 839 | |||
| 840 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 841 | const src = inst_data.src(); | ||
| 842 | const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); | ||
| 843 | const instrs = sema.code.extra[extra.end..][0..extra.data.body_len]; | ||
| 844 | |||
| 845 | log.warn("TODO implement zirValidateStructInitPtr (compile errors for missing/dupe fields)", .{}); | ||
| 846 | } | ||
| 847 | |||
| 848 | fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 849 | const tracy = trace(@src()); | ||
| 850 | defer tracy.end(); | ||
| 851 | |||
| 852 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 853 | const ptr = try sema.resolveInst(bin_inst.lhs); | ||
| 854 | const value = try sema.resolveInst(bin_inst.rhs); | ||
| 855 | const ptr_ty = try sema.mod.simplePtrType(sema.arena, value.ty, true, .One); | ||
| 856 | // TODO detect when this store should be done at compile-time. For example, | ||
| 857 | // if expressions should force it when the condition is compile-time known. | ||
| 858 | const src: LazySrcLoc = .unneeded; | ||
| 859 | try sema.requireRuntimeBlock(block, src); | ||
| 860 | const bitcasted_ptr = try block.addUnOp(src, ptr_ty, .bitcast, ptr); | ||
| 861 | return sema.storePtr(block, src, bitcasted_ptr, value); | ||
| 862 | } | ||
| 863 | |||
| 864 | fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 865 | const tracy = trace(@src()); | ||
| 866 | defer tracy.end(); | ||
| 867 | |||
| 868 | const src: LazySrcLoc = .unneeded; | ||
| 869 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 870 | const ptr = try sema.resolveInst(bin_inst.lhs); | ||
| 871 | const value = try sema.resolveInst(bin_inst.rhs); | ||
| 872 | const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?; | ||
| 873 | // Add the stored instruction to the set we will use to resolve peer types | ||
| 874 | // for the inferred allocation. | ||
| 875 | try inferred_alloc.data.stored_inst_list.append(sema.arena, value); | ||
| 876 | // Create a runtime bitcast instruction with exactly the type the pointer wants. | ||
| 877 | const ptr_ty = try sema.mod.simplePtrType(sema.arena, value.ty, true, .One); | ||
| 878 | try sema.requireRuntimeBlock(block, src); | ||
| 879 | const bitcasted_ptr = try block.addUnOp(src, ptr_ty, .bitcast, ptr); | ||
| 880 | return sema.storePtr(block, src, bitcasted_ptr, value); | ||
| 881 | } | ||
| 882 | |||
| 883 | fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 884 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 885 | const src = inst_data.src(); | ||
| 886 | try sema.requireFunctionBlock(block, src); | ||
| 887 | const quota = try sema.resolveAlreadyCoercedInt(block, src, inst_data.operand, u32); | ||
| 888 | if (sema.branch_quota < quota) | ||
| 889 | sema.branch_quota = quota; | ||
| 890 | } | ||
| 891 | |||
| 892 | fn zirStore(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 893 | const tracy = trace(@src()); | ||
| 894 | defer tracy.end(); | ||
| 895 | |||
| 896 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 897 | const ptr = try sema.resolveInst(bin_inst.lhs); | ||
| 898 | const value = try sema.resolveInst(bin_inst.rhs); | ||
| 899 | return sema.storePtr(block, sema.src, ptr, value); | ||
| 900 | } | ||
| 901 | |||
| 902 | fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 903 | const tracy = trace(@src()); | ||
| 904 | defer tracy.end(); | ||
| 905 | |||
| 906 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 907 | const src = inst_data.src(); | ||
| 908 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 909 | const ptr = try sema.resolveInst(extra.lhs); | ||
| 910 | const value = try sema.resolveInst(extra.rhs); | ||
| 911 | return sema.storePtr(block, src, ptr, value); | ||
| 912 | } | ||
| 913 | |||
| 914 | fn zirParamType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 915 | const tracy = trace(@src()); | ||
| 916 | defer tracy.end(); | ||
| 917 | |||
| 918 | const src: LazySrcLoc = .unneeded; | ||
| 919 | const inst_data = sema.code.instructions.items(.data)[inst].param_type; | ||
| 920 | const fn_inst = try sema.resolveInst(inst_data.callee); | ||
| 921 | const param_index = inst_data.param_index; | ||
| 922 | |||
| 923 | const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) { | ||
| 924 | .Fn => fn_inst.ty, | ||
| 925 | .BoundFn => { | ||
| 926 | return sema.mod.fail(&block.base, fn_inst.src, "TODO implement zirParamType for method call syntax", .{}); | ||
| 927 | }, | ||
| 928 | else => { | ||
| 929 | return sema.mod.fail(&block.base, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty}); | ||
| 930 | }, | ||
| 931 | }; | ||
| 932 | |||
| 933 | const param_count = fn_ty.fnParamLen(); | ||
| 934 | if (param_index >= param_count) { | ||
| 935 | if (fn_ty.fnIsVarArgs()) { | ||
| 936 | return sema.mod.constType(sema.arena, src, Type.initTag(.var_args_param)); | ||
| 937 | } | ||
| 938 | return sema.mod.fail(&block.base, src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{ | ||
| 939 | param_index, | ||
| 940 | fn_ty, | ||
| 941 | param_count, | ||
| 942 | }); | ||
| 943 | } | ||
| 944 | |||
| 945 | // TODO support generic functions | ||
| 946 | const param_type = fn_ty.fnParamType(param_index); | ||
| 947 | return sema.mod.constType(sema.arena, src, param_type); | ||
| 948 | } | ||
| 949 | |||
| 950 | fn zirStr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 951 | const tracy = trace(@src()); | ||
| 952 | defer tracy.end(); | ||
| 953 | |||
| 954 | const zir_bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code); | ||
| 955 | |||
| 956 | // `zir_bytes` references memory inside the ZIR module, which can get deallocated | ||
| 957 | // after semantic analysis is complete, for example in the case of the initialization | ||
| 958 | // expression of a variable declaration. We need the memory to be in the new | ||
| 959 | // anonymous Decl's arena. | ||
| 960 | |||
| 961 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | ||
| 962 | errdefer new_decl_arena.deinit(); | ||
| 963 | |||
| 964 | const bytes = try new_decl_arena.allocator.dupe(u8, zir_bytes); | ||
| 965 | |||
| 966 | const decl_ty = try Type.Tag.array_u8_sentinel_0.create(&new_decl_arena.allocator, bytes.len); | ||
| 967 | const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, bytes); | ||
| 968 | |||
| 969 | const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{ | ||
| 970 | .ty = decl_ty, | ||
| 971 | .val = decl_val, | ||
| 972 | }); | ||
| 973 | return sema.analyzeDeclRef(block, .unneeded, new_decl); | ||
| 974 | } | ||
| 975 | |||
| 976 | fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 977 | const tracy = trace(@src()); | ||
| 978 | defer tracy.end(); | ||
| 979 | |||
| 980 | const int = sema.code.instructions.items(.data)[inst].int; | ||
| 981 | return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int); | ||
| 982 | } | ||
| 983 | |||
| 984 | fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { | ||
| 985 | const tracy = trace(@src()); | ||
| 986 | defer tracy.end(); | ||
| 987 | |||
| 988 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 989 | const src = inst_data.src(); | ||
| 990 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 991 | const msg = try sema.resolveConstString(block, operand_src, inst_data.operand); | ||
| 992 | return sema.mod.fail(&block.base, src, "{s}", .{msg}); | ||
| 993 | } | ||
| 994 | |||
| 995 | fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 996 | var managed = sema.mod.compile_log_text.toManaged(sema.gpa); | ||
| 997 | defer sema.mod.compile_log_text = managed.moveToUnmanaged(); | ||
| 998 | const writer = managed.writer(); | ||
| 999 | |||
| 1000 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1001 | const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); | ||
| 1002 | const args = sema.code.refSlice(extra.end, extra.data.operands_len); | ||
| 1003 | |||
| 1004 | for (args) |arg_ref, i| { | ||
| 1005 | if (i != 0) try writer.print(", ", .{}); | ||
| 1006 | |||
| 1007 | const arg = try sema.resolveInst(arg_ref); | ||
| 1008 | if (arg.value()) |val| { | ||
| 1009 | try writer.print("@as({}, {})", .{ arg.ty, val }); | ||
| 1010 | } else { | ||
| 1011 | try writer.print("@as({}, [runtime value])", .{arg.ty}); | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | try writer.print("\n", .{}); | ||
| 1015 | |||
| 1016 | const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, sema.owner_decl); | ||
| 1017 | if (!gop.found_existing) { | ||
| 1018 | gop.entry.value = inst_data.src().toSrcLoc(&block.base); | ||
| 1019 | } | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { | ||
| 1023 | const tracy = trace(@src()); | ||
| 1024 | defer tracy.end(); | ||
| 1025 | |||
| 1026 | const src_node = sema.code.instructions.items(.data)[inst].node; | ||
| 1027 | const src: LazySrcLoc = .{ .node_offset = src_node }; | ||
| 1028 | try sema.requireRuntimeBlock(block, src); | ||
| 1029 | return always_noreturn; | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1033 | const tracy = trace(@src()); | ||
| 1034 | defer tracy.end(); | ||
| 1035 | |||
| 1036 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1037 | const src = inst_data.src(); | ||
| 1038 | const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); | ||
| 1039 | const body = sema.code.extra[extra.end..][0..extra.data.body_len]; | ||
| 1040 | |||
| 1041 | // TZIR expects a block outside the loop block too. | ||
| 1042 | const block_inst = try sema.arena.create(Inst.Block); | ||
| 1043 | block_inst.* = .{ | ||
| 1044 | .base = .{ | ||
| 1045 | .tag = Inst.Block.base_tag, | ||
| 1046 | .ty = undefined, | ||
| 1047 | .src = src, | ||
| 1048 | }, | ||
| 1049 | .body = undefined, | ||
| 1050 | }; | ||
| 1051 | |||
| 1052 | var child_block = parent_block.makeSubBlock(); | ||
| 1053 | child_block.label = Scope.Block.Label{ | ||
| 1054 | .zir_block = inst, | ||
| 1055 | .merges = .{ | ||
| 1056 | .results = .{}, | ||
| 1057 | .br_list = .{}, | ||
| 1058 | .block_inst = block_inst, | ||
| 1059 | }, | ||
| 1060 | }; | ||
| 1061 | const merges = &child_block.label.?.merges; | ||
| 1062 | |||
| 1063 | defer child_block.instructions.deinit(sema.gpa); | ||
| 1064 | defer merges.results.deinit(sema.gpa); | ||
| 1065 | defer merges.br_list.deinit(sema.gpa); | ||
| 1066 | |||
| 1067 | // Reserve space for a Loop instruction so that generated Break instructions can | ||
| 1068 | // point to it, even if it doesn't end up getting used because the code ends up being | ||
| 1069 | // comptime evaluated. | ||
| 1070 | const loop_inst = try sema.arena.create(Inst.Loop); | ||
| 1071 | loop_inst.* = .{ | ||
| 1072 | .base = .{ | ||
| 1073 | .tag = Inst.Loop.base_tag, | ||
| 1074 | .ty = Type.initTag(.noreturn), | ||
| 1075 | .src = src, | ||
| 1076 | }, | ||
| 1077 | .body = undefined, | ||
| 1078 | }; | ||
| 1079 | |||
| 1080 | var loop_block = child_block.makeSubBlock(); | ||
| 1081 | defer loop_block.instructions.deinit(sema.gpa); | ||
| 1082 | |||
| 1083 | _ = try sema.analyzeBody(&loop_block, body); | ||
| 1084 | |||
| 1085 | // Loop repetition is implied so the last instruction may or may not be a noreturn instruction. | ||
| 1086 | |||
| 1087 | try child_block.instructions.append(sema.gpa, &loop_inst.base); | ||
| 1088 | loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, loop_block.instructions.items) }; | ||
| 1089 | |||
| 1090 | return sema.analyzeBlockBody(parent_block, src, &child_block, merges); | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1094 | const tracy = trace(@src()); | ||
| 1095 | defer tracy.end(); | ||
| 1096 | |||
| 1097 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1098 | const src = inst_data.src(); | ||
| 1099 | const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); | ||
| 1100 | const body = sema.code.extra[extra.end..][0..extra.data.body_len]; | ||
| 1101 | |||
| 1102 | // Reserve space for a Block instruction so that generated Break instructions can | ||
| 1103 | // point to it, even if it doesn't end up getting used because the code ends up being | ||
| 1104 | // comptime evaluated. | ||
| 1105 | const block_inst = try sema.arena.create(Inst.Block); | ||
| 1106 | block_inst.* = .{ | ||
| 1107 | .base = .{ | ||
| 1108 | .tag = Inst.Block.base_tag, | ||
| 1109 | .ty = undefined, // Set after analysis. | ||
| 1110 | .src = src, | ||
| 1111 | }, | ||
| 1112 | .body = undefined, | ||
| 1113 | }; | ||
| 1114 | |||
| 1115 | var child_block: Scope.Block = .{ | ||
| 1116 | .parent = parent_block, | ||
| 1117 | .sema = sema, | ||
| 1118 | .src_decl = parent_block.src_decl, | ||
| 1119 | .instructions = .{}, | ||
| 1120 | // TODO @as here is working around a stage1 miscompilation bug :( | ||
| 1121 | .label = @as(?Scope.Block.Label, Scope.Block.Label{ | ||
| 1122 | .zir_block = inst, | ||
| 1123 | .merges = .{ | ||
| 1124 | .results = .{}, | ||
| 1125 | .br_list = .{}, | ||
| 1126 | .block_inst = block_inst, | ||
| 1127 | }, | ||
| 1128 | }), | ||
| 1129 | .inlining = parent_block.inlining, | ||
| 1130 | .is_comptime = parent_block.is_comptime, | ||
| 1131 | }; | ||
| 1132 | const merges = &child_block.label.?.merges; | ||
| 1133 | |||
| 1134 | defer child_block.instructions.deinit(sema.gpa); | ||
| 1135 | defer merges.results.deinit(sema.gpa); | ||
| 1136 | defer merges.br_list.deinit(sema.gpa); | ||
| 1137 | |||
| 1138 | _ = try sema.analyzeBody(&child_block, body); | ||
| 1139 | |||
| 1140 | return sema.analyzeBlockBody(parent_block, src, &child_block, merges); | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | fn analyzeBlockBody( | ||
| 1144 | sema: *Sema, | ||
| 1145 | parent_block: *Scope.Block, | ||
| 1146 | src: LazySrcLoc, | ||
| 1147 | child_block: *Scope.Block, | ||
| 1148 | merges: *Scope.Block.Merges, | ||
| 1149 | ) InnerError!*Inst { | ||
| 1150 | const tracy = trace(@src()); | ||
| 1151 | defer tracy.end(); | ||
| 1152 | |||
| 1153 | // Blocks must terminate with noreturn instruction. | ||
| 1154 | assert(child_block.instructions.items.len != 0); | ||
| 1155 | assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn()); | ||
| 1156 | |||
| 1157 | if (merges.results.items.len == 0) { | ||
| 1158 | // No need for a block instruction. We can put the new instructions | ||
| 1159 | // directly into the parent block. | ||
| 1160 | const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items); | ||
| 1161 | try parent_block.instructions.appendSlice(sema.gpa, copied_instructions); | ||
| 1162 | return copied_instructions[copied_instructions.len - 1]; | ||
| 1163 | } | ||
| 1164 | if (merges.results.items.len == 1) { | ||
| 1165 | const last_inst_index = child_block.instructions.items.len - 1; | ||
| 1166 | const last_inst = child_block.instructions.items[last_inst_index]; | ||
| 1167 | if (last_inst.breakBlock()) |br_block| { | ||
| 1168 | if (br_block == merges.block_inst) { | ||
| 1169 | // No need for a block instruction. We can put the new instructions directly | ||
| 1170 | // into the parent block. Here we omit the break instruction. | ||
| 1171 | const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]); | ||
| 1172 | try parent_block.instructions.appendSlice(sema.gpa, copied_instructions); | ||
| 1173 | return merges.results.items[0]; | ||
| 1174 | } | ||
| 1175 | } | ||
| 1176 | } | ||
| 1177 | // It is impossible to have the number of results be > 1 in a comptime scope. | ||
| 1178 | assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition. | ||
| 1179 | |||
| 1180 | // Need to set the type and emit the Block instruction. This allows machine code generation | ||
| 1181 | // to emit a jump instruction to after the block when it encounters the break. | ||
| 1182 | try parent_block.instructions.append(sema.gpa, &merges.block_inst.base); | ||
| 1183 | const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items); | ||
| 1184 | merges.block_inst.base.ty = resolved_ty; | ||
| 1185 | merges.block_inst.body = .{ | ||
| 1186 | .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items), | ||
| 1187 | }; | ||
| 1188 | // Now that the block has its type resolved, we need to go back into all the break | ||
| 1189 | // instructions, and insert type coercion on the operands. | ||
| 1190 | for (merges.br_list.items) |br| { | ||
| 1191 | if (br.operand.ty.eql(resolved_ty)) { | ||
| 1192 | // No type coercion needed. | ||
| 1193 | continue; | ||
| 1194 | } | ||
| 1195 | var coerce_block = parent_block.makeSubBlock(); | ||
| 1196 | defer coerce_block.instructions.deinit(sema.gpa); | ||
| 1197 | const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br.operand, br.operand.src); | ||
| 1198 | // If no instructions were produced, such as in the case of a coercion of a | ||
| 1199 | // constant value to a new type, we can simply point the br operand to it. | ||
| 1200 | if (coerce_block.instructions.items.len == 0) { | ||
| 1201 | br.operand = coerced_operand; | ||
| 1202 | continue; | ||
| 1203 | } | ||
| 1204 | assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand); | ||
| 1205 | // Here we depend on the br instruction having been over-allocated (if necessary) | ||
| 1206 | // inside zirBreak so that it can be converted into a br_block_flat instruction. | ||
| 1207 | const br_src = br.base.src; | ||
| 1208 | const br_ty = br.base.ty; | ||
| 1209 | const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br); | ||
| 1210 | br_block_flat.* = .{ | ||
| 1211 | .base = .{ | ||
| 1212 | .src = br_src, | ||
| 1213 | .ty = br_ty, | ||
| 1214 | .tag = .br_block_flat, | ||
| 1215 | }, | ||
| 1216 | .block = merges.block_inst, | ||
| 1217 | .body = .{ | ||
| 1218 | .instructions = try sema.arena.dupe(*Inst, coerce_block.instructions.items), | ||
| 1219 | }, | ||
| 1220 | }; | ||
| 1221 | } | ||
| 1222 | return &merges.block_inst.base; | ||
| 1223 | } | ||
| 1224 | |||
| 1225 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 1226 | const tracy = trace(@src()); | ||
| 1227 | defer tracy.end(); | ||
| 1228 | |||
| 1229 | const src_node = sema.code.instructions.items(.data)[inst].node; | ||
| 1230 | const src: LazySrcLoc = .{ .node_offset = src_node }; | ||
| 1231 | try sema.requireRuntimeBlock(block, src); | ||
| 1232 | _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint); | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { | ||
| 1236 | const tracy = trace(@src()); | ||
| 1237 | defer tracy.end(); | ||
| 1238 | |||
| 1239 | const inst_data = sema.code.instructions.items(.data)[inst].@"break"; | ||
| 1240 | const src = sema.src; | ||
| 1241 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 1242 | const zir_block = inst_data.block_inst; | ||
| 1243 | |||
| 1244 | var block = start_block; | ||
| 1245 | while (true) { | ||
| 1246 | if (block.label) |*label| { | ||
| 1247 | if (label.zir_block == zir_block) { | ||
| 1248 | // Here we add a br instruction, but we over-allocate a little bit | ||
| 1249 | // (if necessary) to make it possible to convert the instruction into | ||
| 1250 | // a br_block_flat instruction later. | ||
| 1251 | const br = @ptrCast(*Inst.Br, try sema.arena.alignedAlloc( | ||
| 1252 | u8, | ||
| 1253 | Inst.convertable_br_align, | ||
| 1254 | Inst.convertable_br_size, | ||
| 1255 | )); | ||
| 1256 | br.* = .{ | ||
| 1257 | .base = .{ | ||
| 1258 | .tag = .br, | ||
| 1259 | .ty = Type.initTag(.noreturn), | ||
| 1260 | .src = src, | ||
| 1261 | }, | ||
| 1262 | .operand = operand, | ||
| 1263 | .block = label.merges.block_inst, | ||
| 1264 | }; | ||
| 1265 | try start_block.instructions.append(sema.gpa, &br.base); | ||
| 1266 | try label.merges.results.append(sema.gpa, operand); | ||
| 1267 | try label.merges.br_list.append(sema.gpa, br); | ||
| 1268 | return inst; | ||
| 1269 | } | ||
| 1270 | } | ||
| 1271 | block = block.parent.?; | ||
| 1272 | } | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 1276 | const tracy = trace(@src()); | ||
| 1277 | defer tracy.end(); | ||
| 1278 | |||
| 1279 | // We do not set sema.src here because dbg_stmt instructions are only emitted for | ||
| 1280 | // ZIR code that possibly will need to generate runtime code. So error messages | ||
| 1281 | // and other source locations must not rely on sema.src being set from dbg_stmt | ||
| 1282 | // instructions. | ||
| 1283 | if (block.is_comptime) return; | ||
| 1284 | |||
| 1285 | const src_node = sema.code.instructions.items(.data)[inst].node; | ||
| 1286 | const src: LazySrcLoc = .{ .node_offset = src_node }; | ||
| 1287 | |||
| 1288 | const src_loc = src.toSrcLoc(&block.base); | ||
| 1289 | const abs_byte_off = try src_loc.byteOffset(); | ||
| 1290 | _ = try block.addDbgStmt(src, abs_byte_off); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1294 | const tracy = trace(@src()); | ||
| 1295 | defer tracy.end(); | ||
| 1296 | |||
| 1297 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1298 | const src = inst_data.src(); | ||
| 1299 | const decl = sema.code.decls[inst_data.payload_index]; | ||
| 1300 | return sema.analyzeDeclRef(block, src, decl); | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1304 | const tracy = trace(@src()); | ||
| 1305 | defer tracy.end(); | ||
| 1306 | |||
| 1307 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1308 | const src = inst_data.src(); | ||
| 1309 | const decl = sema.code.decls[inst_data.payload_index]; | ||
| 1310 | return sema.analyzeDeclVal(block, src, decl); | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | fn zirCallNone( | ||
| 1314 | sema: *Sema, | ||
| 1315 | block: *Scope.Block, | ||
| 1316 | inst: zir.Inst.Index, | ||
| 1317 | ensure_result_used: bool, | ||
| 1318 | ) InnerError!*Inst { | ||
| 1319 | const tracy = trace(@src()); | ||
| 1320 | defer tracy.end(); | ||
| 1321 | |||
| 1322 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1323 | const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; | ||
| 1324 | |||
| 1325 | return sema.analyzeCall(block, inst_data.operand, func_src, inst_data.src(), .auto, ensure_result_used, &.{}); | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | fn zirCall( | ||
| 1329 | sema: *Sema, | ||
| 1330 | block: *Scope.Block, | ||
| 1331 | inst: zir.Inst.Index, | ||
| 1332 | modifier: std.builtin.CallOptions.Modifier, | ||
| 1333 | ensure_result_used: bool, | ||
| 1334 | ) InnerError!*Inst { | ||
| 1335 | const tracy = trace(@src()); | ||
| 1336 | defer tracy.end(); | ||
| 1337 | |||
| 1338 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1339 | const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; | ||
| 1340 | const call_src = inst_data.src(); | ||
| 1341 | const extra = sema.code.extraData(zir.Inst.Call, inst_data.payload_index); | ||
| 1342 | const args = sema.code.refSlice(extra.end, extra.data.args_len); | ||
| 1343 | |||
| 1344 | return sema.analyzeCall(block, extra.data.callee, func_src, call_src, modifier, ensure_result_used, args); | ||
| 1345 | } | ||
| 1346 | |||
| 1347 | fn analyzeCall( | ||
| 1348 | sema: *Sema, | ||
| 1349 | block: *Scope.Block, | ||
| 1350 | zir_func: zir.Inst.Ref, | ||
| 1351 | func_src: LazySrcLoc, | ||
| 1352 | call_src: LazySrcLoc, | ||
| 1353 | modifier: std.builtin.CallOptions.Modifier, | ||
| 1354 | ensure_result_used: bool, | ||
| 1355 | zir_args: []const zir.Inst.Ref, | ||
| 1356 | ) InnerError!*ir.Inst { | ||
| 1357 | const func = try sema.resolveInst(zir_func); | ||
| 1358 | |||
| 1359 | if (func.ty.zigTypeTag() != .Fn) | ||
| 1360 | return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty}); | ||
| 1361 | |||
| 1362 | const cc = func.ty.fnCallingConvention(); | ||
| 1363 | if (cc == .Naked) { | ||
| 1364 | // TODO add error note: declared here | ||
| 1365 | return sema.mod.fail( | ||
| 1366 | &block.base, | ||
| 1367 | func_src, | ||
| 1368 | "unable to call function with naked calling convention", | ||
| 1369 | .{}, | ||
| 1370 | ); | ||
| 1371 | } | ||
| 1372 | const fn_params_len = func.ty.fnParamLen(); | ||
| 1373 | if (func.ty.fnIsVarArgs()) { | ||
| 1374 | assert(cc == .C); | ||
| 1375 | if (zir_args.len < fn_params_len) { | ||
| 1376 | // TODO add error note: declared here | ||
| 1377 | return sema.mod.fail( | ||
| 1378 | &block.base, | ||
| 1379 | func_src, | ||
| 1380 | "expected at least {d} argument(s), found {d}", | ||
| 1381 | .{ fn_params_len, zir_args.len }, | ||
| 1382 | ); | ||
| 1383 | } | ||
| 1384 | } else if (fn_params_len != zir_args.len) { | ||
| 1385 | // TODO add error note: declared here | ||
| 1386 | return sema.mod.fail( | ||
| 1387 | &block.base, | ||
| 1388 | func_src, | ||
| 1389 | "expected {d} argument(s), found {d}", | ||
| 1390 | .{ fn_params_len, zir_args.len }, | ||
| 1391 | ); | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | if (modifier == .compile_time) { | ||
| 1395 | return sema.mod.fail(&block.base, call_src, "TODO implement comptime function calls", .{}); | ||
| 1396 | } | ||
| 1397 | if (modifier != .auto) { | ||
| 1398 | return sema.mod.fail(&block.base, call_src, "TODO implement call with modifier {}", .{modifier}); | ||
| 1399 | } | ||
| 1400 | |||
| 1401 | // TODO handle function calls of generic functions | ||
| 1402 | const casted_args = try sema.arena.alloc(*Inst, zir_args.len); | ||
| 1403 | for (zir_args) |zir_arg, i| { | ||
| 1404 | // the args are already casted to the result of a param type instruction. | ||
| 1405 | casted_args[i] = try sema.resolveInst(zir_arg); | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | const ret_type = func.ty.fnReturnType(); | ||
| 1409 | |||
| 1410 | const is_comptime_call = block.is_comptime or modifier == .compile_time; | ||
| 1411 | const is_inline_call = is_comptime_call or modifier == .always_inline or | ||
| 1412 | func.ty.fnCallingConvention() == .Inline; | ||
| 1413 | const result: *Inst = if (is_inline_call) res: { | ||
| 1414 | const func_val = try sema.resolveConstValue(block, func_src, func); | ||
| 1415 | const module_fn = switch (func_val.tag()) { | ||
| 1416 | .function => func_val.castTag(.function).?.data, | ||
| 1417 | .extern_fn => return sema.mod.fail(&block.base, call_src, "{s} call of extern function", .{ | ||
| 1418 | @as([]const u8, if (is_comptime_call) "comptime" else "inline"), | ||
| 1419 | }), | ||
| 1420 | else => unreachable, | ||
| 1421 | }; | ||
| 1422 | |||
| 1423 | // Analyze the ZIR. The same ZIR gets analyzed into a runtime function | ||
| 1424 | // or an inlined call depending on what union tag the `label` field is | ||
| 1425 | // set to in the `Scope.Block`. | ||
| 1426 | // This block instruction will be used to capture the return value from the | ||
| 1427 | // inlined function. | ||
| 1428 | const block_inst = try sema.arena.create(Inst.Block); | ||
| 1429 | block_inst.* = .{ | ||
| 1430 | .base = .{ | ||
| 1431 | .tag = Inst.Block.base_tag, | ||
| 1432 | .ty = ret_type, | ||
| 1433 | .src = call_src, | ||
| 1434 | }, | ||
| 1435 | .body = undefined, | ||
| 1436 | }; | ||
| 1437 | // This one is shared among sub-blocks within the same callee, but not | ||
| 1438 | // shared among the entire inline/comptime call stack. | ||
| 1439 | var inlining: Scope.Block.Inlining = .{ | ||
| 1440 | .merges = .{ | ||
| 1441 | .results = .{}, | ||
| 1442 | .br_list = .{}, | ||
| 1443 | .block_inst = block_inst, | ||
| 1444 | }, | ||
| 1445 | }; | ||
| 1446 | var inline_sema: Sema = .{ | ||
| 1447 | .mod = sema.mod, | ||
| 1448 | .gpa = sema.mod.gpa, | ||
| 1449 | .arena = sema.arena, | ||
| 1450 | .code = module_fn.zir, | ||
| 1451 | .inst_map = try sema.gpa.alloc(*ir.Inst, module_fn.zir.instructions.len), | ||
| 1452 | .owner_decl = sema.owner_decl, | ||
| 1453 | .owner_func = sema.owner_func, | ||
| 1454 | .func = module_fn, | ||
| 1455 | .param_inst_list = casted_args, | ||
| 1456 | .branch_quota = sema.branch_quota, | ||
| 1457 | .branch_count = sema.branch_count, | ||
| 1458 | }; | ||
| 1459 | defer sema.gpa.free(inline_sema.inst_map); | ||
| 1460 | |||
| 1461 | var child_block: Scope.Block = .{ | ||
| 1462 | .parent = null, | ||
| 1463 | .sema = &inline_sema, | ||
| 1464 | .src_decl = module_fn.owner_decl, | ||
| 1465 | .instructions = .{}, | ||
| 1466 | .label = null, | ||
| 1467 | .inlining = &inlining, | ||
| 1468 | .is_comptime = is_comptime_call, | ||
| 1469 | }; | ||
| 1470 | |||
| 1471 | const merges = &child_block.inlining.?.merges; | ||
| 1472 | |||
| 1473 | defer child_block.instructions.deinit(sema.gpa); | ||
| 1474 | defer merges.results.deinit(sema.gpa); | ||
| 1475 | defer merges.br_list.deinit(sema.gpa); | ||
| 1476 | |||
| 1477 | try inline_sema.emitBackwardBranch(&child_block, call_src); | ||
| 1478 | |||
| 1479 | // This will have return instructions analyzed as break instructions to | ||
| 1480 | // the block_inst above. | ||
| 1481 | _ = try inline_sema.root(&child_block); | ||
| 1482 | |||
| 1483 | const result = try inline_sema.analyzeBlockBody(block, call_src, &child_block, merges); | ||
| 1484 | |||
| 1485 | sema.branch_quota = inline_sema.branch_quota; | ||
| 1486 | sema.branch_count = inline_sema.branch_count; | ||
| 1487 | |||
| 1488 | break :res result; | ||
| 1489 | } else res: { | ||
| 1490 | try sema.requireRuntimeBlock(block, call_src); | ||
| 1491 | break :res try block.addCall(call_src, ret_type, func, casted_args); | ||
| 1492 | }; | ||
| 1493 | |||
| 1494 | if (ensure_result_used) { | ||
| 1495 | try sema.ensureResultUsed(block, result, call_src); | ||
| 1496 | } | ||
| 1497 | return result; | ||
| 1498 | } | ||
| 1499 | |||
| 1500 | fn zirIntType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1501 | const tracy = trace(@src()); | ||
| 1502 | defer tracy.end(); | ||
| 1503 | |||
| 1504 | const int_type = sema.code.instructions.items(.data)[inst].int_type; | ||
| 1505 | const src = int_type.src(); | ||
| 1506 | const ty = try Module.makeIntType(sema.arena, int_type.signedness, int_type.bit_count); | ||
| 1507 | |||
| 1508 | return sema.mod.constType(sema.arena, src, ty); | ||
| 1509 | } | ||
| 1510 | |||
| 1511 | fn zirOptionalType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1512 | const tracy = trace(@src()); | ||
| 1513 | defer tracy.end(); | ||
| 1514 | |||
| 1515 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1516 | const src = inst_data.src(); | ||
| 1517 | const child_type = try sema.resolveType(block, src, inst_data.operand); | ||
| 1518 | const opt_type = try sema.mod.optionalType(sema.arena, child_type); | ||
| 1519 | |||
| 1520 | return sema.mod.constType(sema.arena, src, opt_type); | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | fn zirOptionalTypeFromPtrElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1524 | const tracy = trace(@src()); | ||
| 1525 | defer tracy.end(); | ||
| 1526 | |||
| 1527 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1528 | const ptr = try sema.resolveInst(inst_data.operand); | ||
| 1529 | const elem_ty = ptr.ty.elemType(); | ||
| 1530 | const opt_ty = try sema.mod.optionalType(sema.arena, elem_ty); | ||
| 1531 | |||
| 1532 | return sema.mod.constType(sema.arena, inst_data.src(), opt_ty); | ||
| 1533 | } | ||
| 1534 | |||
| 1535 | fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1536 | const tracy = trace(@src()); | ||
| 1537 | defer tracy.end(); | ||
| 1538 | |||
| 1539 | // TODO these should be lazily evaluated | ||
| 1540 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1541 | const len = try sema.resolveInstConst(block, .unneeded, bin_inst.lhs); | ||
| 1542 | const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs); | ||
| 1543 | const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), null, elem_type); | ||
| 1544 | |||
| 1545 | return sema.mod.constType(sema.arena, .unneeded, array_ty); | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1549 | const tracy = trace(@src()); | ||
| 1550 | defer tracy.end(); | ||
| 1551 | |||
| 1552 | // TODO these should be lazily evaluated | ||
| 1553 | const inst_data = sema.code.instructions.items(.data)[inst].array_type_sentinel; | ||
| 1554 | const len = try sema.resolveInstConst(block, .unneeded, inst_data.len); | ||
| 1555 | const extra = sema.code.extraData(zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; | ||
| 1556 | const sentinel = try sema.resolveInstConst(block, .unneeded, extra.sentinel); | ||
| 1557 | const elem_type = try sema.resolveType(block, .unneeded, extra.elem_type); | ||
| 1558 | const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), sentinel.val, elem_type); | ||
| 1559 | |||
| 1560 | return sema.mod.constType(sema.arena, .unneeded, array_ty); | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1564 | const tracy = trace(@src()); | ||
| 1565 | defer tracy.end(); | ||
| 1566 | |||
| 1567 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1568 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 1569 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; | ||
| 1570 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; | ||
| 1571 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | ||
| 1572 | const error_union = try sema.resolveType(block, lhs_src, extra.lhs); | ||
| 1573 | const payload = try sema.resolveType(block, rhs_src, extra.rhs); | ||
| 1574 | |||
| 1575 | if (error_union.zigTypeTag() != .ErrorSet) { | ||
| 1576 | return sema.mod.fail(&block.base, lhs_src, "expected error set type, found {}", .{ | ||
| 1577 | error_union.elemType(), | ||
| 1578 | }); | ||
| 1579 | } | ||
| 1580 | const err_union_ty = try sema.mod.errorUnionType(sema.arena, error_union, payload); | ||
| 1581 | return sema.mod.constType(sema.arena, src, err_union_ty); | ||
| 1582 | } | ||
| 1583 | |||
| 1584 | fn zirErrorValue(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1585 | const tracy = trace(@src()); | ||
| 1586 | defer tracy.end(); | ||
| 1587 | |||
| 1588 | const inst_data = sema.code.instructions.items(.data)[inst].str_tok; | ||
| 1589 | const src = inst_data.src(); | ||
| 1590 | |||
| 1591 | // Create an anonymous error set type with only this error value, and return the value. | ||
| 1592 | const entry = try sema.mod.getErrorValue(inst_data.get(sema.code)); | ||
| 1593 | const result_type = try Type.Tag.error_set_single.create(sema.arena, entry.key); | ||
| 1594 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1595 | .ty = result_type, | ||
| 1596 | .val = try Value.Tag.@"error".create(sema.arena, .{ | ||
| 1597 | .name = entry.key, | ||
| 1598 | }), | ||
| 1599 | }); | ||
| 1600 | } | ||
| 1601 | |||
| 1602 | fn zirErrorToInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1603 | const tracy = trace(@src()); | ||
| 1604 | defer tracy.end(); | ||
| 1605 | |||
| 1606 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1607 | const src = inst_data.src(); | ||
| 1608 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 1609 | const op = try sema.resolveInst(inst_data.operand); | ||
| 1610 | const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src); | ||
| 1611 | |||
| 1612 | if (op_coerced.value()) |val| { | ||
| 1613 | const payload = try sema.arena.create(Value.Payload.U64); | ||
| 1614 | payload.* = .{ | ||
| 1615 | .base = .{ .tag = .int_u64 }, | ||
| 1616 | .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value, | ||
| 1617 | }; | ||
| 1618 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1619 | .ty = Type.initTag(.u16), | ||
| 1620 | .val = Value.initPayload(&payload.base), | ||
| 1621 | }); | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | try sema.requireRuntimeBlock(block, src); | ||
| 1625 | return block.addUnOp(src, Type.initTag(.u16), .error_to_int, op_coerced); | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1629 | const tracy = trace(@src()); | ||
| 1630 | defer tracy.end(); | ||
| 1631 | |||
| 1632 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1633 | const src = inst_data.src(); | ||
| 1634 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 1635 | |||
| 1636 | const op = try sema.resolveInst(inst_data.operand); | ||
| 1637 | |||
| 1638 | if (try sema.resolveDefinedValue(block, operand_src, op)) |value| { | ||
| 1639 | const int = value.toUnsignedInt(); | ||
| 1640 | if (int > sema.mod.global_error_set.count() or int == 0) | ||
| 1641 | return sema.mod.fail(&block.base, operand_src, "integer value {d} represents no error", .{int}); | ||
| 1642 | const payload = try sema.arena.create(Value.Payload.Error); | ||
| 1643 | payload.* = .{ | ||
| 1644 | .base = .{ .tag = .@"error" }, | ||
| 1645 | .data = .{ .name = sema.mod.error_name_list.items[int] }, | ||
| 1646 | }; | ||
| 1647 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1648 | .ty = Type.initTag(.anyerror), | ||
| 1649 | .val = Value.initPayload(&payload.base), | ||
| 1650 | }); | ||
| 1651 | } | ||
| 1652 | try sema.requireRuntimeBlock(block, src); | ||
| 1653 | if (block.wantSafety()) { | ||
| 1654 | return sema.mod.fail(&block.base, src, "TODO: get max errors in compilation", .{}); | ||
| 1655 | // const is_gt_max = @panic("TODO get max errors in compilation"); | ||
| 1656 | // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code); | ||
| 1657 | } | ||
| 1658 | return block.addUnOp(src, Type.initTag(.anyerror), .int_to_error, op); | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1662 | const tracy = trace(@src()); | ||
| 1663 | defer tracy.end(); | ||
| 1664 | |||
| 1665 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1666 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 1667 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; | ||
| 1668 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; | ||
| 1669 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | ||
| 1670 | const lhs_ty = try sema.resolveType(block, lhs_src, extra.lhs); | ||
| 1671 | const rhs_ty = try sema.resolveType(block, rhs_src, extra.rhs); | ||
| 1672 | if (rhs_ty.zigTypeTag() != .ErrorSet) | ||
| 1673 | return sema.mod.fail(&block.base, rhs_src, "expected error set type, found {}", .{rhs_ty}); | ||
| 1674 | if (lhs_ty.zigTypeTag() != .ErrorSet) | ||
| 1675 | return sema.mod.fail(&block.base, lhs_src, "expected error set type, found {}", .{lhs_ty}); | ||
| 1676 | |||
| 1677 | // Anything merged with anyerror is anyerror. | ||
| 1678 | if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) { | ||
| 1679 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1680 | .ty = Type.initTag(.type), | ||
| 1681 | .val = Value.initTag(.anyerror_type), | ||
| 1682 | }); | ||
| 1683 | } | ||
| 1684 | // When we support inferred error sets, we'll want to use a data structure that can | ||
| 1685 | // represent a merged set of errors without forcing them to be resolved here. Until then | ||
| 1686 | // we re-use the same data structure that is used for explicit error set declarations. | ||
| 1687 | var set: std.StringHashMapUnmanaged(void) = .{}; | ||
| 1688 | defer set.deinit(sema.gpa); | ||
| 1689 | |||
| 1690 | switch (lhs_ty.tag()) { | ||
| 1691 | .error_set_single => { | ||
| 1692 | const name = lhs_ty.castTag(.error_set_single).?.data; | ||
| 1693 | try set.put(sema.gpa, name, {}); | ||
| 1694 | }, | ||
| 1695 | .error_set => { | ||
| 1696 | const lhs_set = lhs_ty.castTag(.error_set).?.data; | ||
| 1697 | try set.ensureCapacity(sema.gpa, set.count() + lhs_set.names_len); | ||
| 1698 | for (lhs_set.names_ptr[0..lhs_set.names_len]) |name| { | ||
| 1699 | set.putAssumeCapacityNoClobber(name, {}); | ||
| 1700 | } | ||
| 1701 | }, | ||
| 1702 | else => unreachable, | ||
| 1703 | } | ||
| 1704 | switch (rhs_ty.tag()) { | ||
| 1705 | .error_set_single => { | ||
| 1706 | const name = rhs_ty.castTag(.error_set_single).?.data; | ||
| 1707 | try set.put(sema.gpa, name, {}); | ||
| 1708 | }, | ||
| 1709 | .error_set => { | ||
| 1710 | const rhs_set = rhs_ty.castTag(.error_set).?.data; | ||
| 1711 | try set.ensureCapacity(sema.gpa, set.count() + rhs_set.names_len); | ||
| 1712 | for (rhs_set.names_ptr[0..rhs_set.names_len]) |name| { | ||
| 1713 | set.putAssumeCapacity(name, {}); | ||
| 1714 | } | ||
| 1715 | }, | ||
| 1716 | else => unreachable, | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | const new_names = try sema.arena.alloc([]const u8, set.count()); | ||
| 1720 | var it = set.iterator(); | ||
| 1721 | var i: usize = 0; | ||
| 1722 | while (it.next()) |entry| : (i += 1) { | ||
| 1723 | new_names[i] = entry.key; | ||
| 1724 | } | ||
| 1725 | |||
| 1726 | const new_error_set = try sema.arena.create(Module.ErrorSet); | ||
| 1727 | new_error_set.* = .{ | ||
| 1728 | .owner_decl = sema.owner_decl, | ||
| 1729 | .node_offset = inst_data.src_node, | ||
| 1730 | .names_ptr = new_names.ptr, | ||
| 1731 | .names_len = @intCast(u32, new_names.len), | ||
| 1732 | }; | ||
| 1733 | const error_set_ty = try Type.Tag.error_set.create(sema.arena, new_error_set); | ||
| 1734 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1735 | .ty = Type.initTag(.type), | ||
| 1736 | .val = try Value.Tag.ty.create(sema.arena, error_set_ty), | ||
| 1737 | }); | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | fn zirEnumLiteral(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1741 | const tracy = trace(@src()); | ||
| 1742 | defer tracy.end(); | ||
| 1743 | |||
| 1744 | const inst_data = sema.code.instructions.items(.data)[inst].str_tok; | ||
| 1745 | const src = inst_data.src(); | ||
| 1746 | const duped_name = try sema.arena.dupe(u8, inst_data.get(sema.code)); | ||
| 1747 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1748 | .ty = Type.initTag(.enum_literal), | ||
| 1749 | .val = try Value.Tag.enum_literal.create(sema.arena, duped_name), | ||
| 1750 | }); | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | fn zirEnumLiteralSmall(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1754 | const tracy = trace(@src()); | ||
| 1755 | defer tracy.end(); | ||
| 1756 | |||
| 1757 | const name = sema.code.instructions.items(.data)[inst].small_str.get(); | ||
| 1758 | const src: LazySrcLoc = .unneeded; | ||
| 1759 | const duped_name = try sema.arena.dupe(u8, name); | ||
| 1760 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1761 | .ty = Type.initTag(.enum_literal), | ||
| 1762 | .val = try Value.Tag.enum_literal.create(sema.arena, duped_name), | ||
| 1763 | }); | ||
| 1764 | } | ||
| 1765 | |||
| 1766 | /// Pointer in, pointer out. | ||
| 1767 | fn zirOptionalPayloadPtr( | ||
| 1768 | sema: *Sema, | ||
| 1769 | block: *Scope.Block, | ||
| 1770 | inst: zir.Inst.Index, | ||
| 1771 | safety_check: bool, | ||
| 1772 | ) InnerError!*Inst { | ||
| 1773 | const tracy = trace(@src()); | ||
| 1774 | defer tracy.end(); | ||
| 1775 | |||
| 1776 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1777 | const optional_ptr = try sema.resolveInst(inst_data.operand); | ||
| 1778 | assert(optional_ptr.ty.zigTypeTag() == .Pointer); | ||
| 1779 | const src = inst_data.src(); | ||
| 1780 | |||
| 1781 | const opt_type = optional_ptr.ty.elemType(); | ||
| 1782 | if (opt_type.zigTypeTag() != .Optional) { | ||
| 1783 | return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type}); | ||
| 1784 | } | ||
| 1785 | |||
| 1786 | const child_type = try opt_type.optionalChildAlloc(sema.arena); | ||
| 1787 | const child_pointer = try sema.mod.simplePtrType(sema.arena, child_type, !optional_ptr.ty.isConstPtr(), .One); | ||
| 1788 | |||
| 1789 | if (optional_ptr.value()) |pointer_val| { | ||
| 1790 | const val = try pointer_val.pointerDeref(sema.arena); | ||
| 1791 | if (val.isNull()) { | ||
| 1792 | return sema.mod.fail(&block.base, src, "unable to unwrap null", .{}); | ||
| 1793 | } | ||
| 1794 | // The same Value represents the pointer to the optional and the payload. | ||
| 1795 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1796 | .ty = child_pointer, | ||
| 1797 | .val = pointer_val, | ||
| 1798 | }); | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | try sema.requireRuntimeBlock(block, src); | ||
| 1802 | if (safety_check and block.wantSafety()) { | ||
| 1803 | const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null_ptr, optional_ptr); | ||
| 1804 | try sema.addSafetyCheck(block, is_non_null, .unwrap_null); | ||
| 1805 | } | ||
| 1806 | return block.addUnOp(src, child_pointer, .optional_payload_ptr, optional_ptr); | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | /// Value in, value out. | ||
| 1810 | fn zirOptionalPayload( | ||
| 1811 | sema: *Sema, | ||
| 1812 | block: *Scope.Block, | ||
| 1813 | inst: zir.Inst.Index, | ||
| 1814 | safety_check: bool, | ||
| 1815 | ) InnerError!*Inst { | ||
| 1816 | const tracy = trace(@src()); | ||
| 1817 | defer tracy.end(); | ||
| 1818 | |||
| 1819 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1820 | const src = inst_data.src(); | ||
| 1821 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 1822 | const opt_type = operand.ty; | ||
| 1823 | if (opt_type.zigTypeTag() != .Optional) { | ||
| 1824 | return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type}); | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | const child_type = try opt_type.optionalChildAlloc(sema.arena); | ||
| 1828 | |||
| 1829 | if (operand.value()) |val| { | ||
| 1830 | if (val.isNull()) { | ||
| 1831 | return sema.mod.fail(&block.base, src, "unable to unwrap null", .{}); | ||
| 1832 | } | ||
| 1833 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1834 | .ty = child_type, | ||
| 1835 | .val = val, | ||
| 1836 | }); | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | try sema.requireRuntimeBlock(block, src); | ||
| 1840 | if (safety_check and block.wantSafety()) { | ||
| 1841 | const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null, operand); | ||
| 1842 | try sema.addSafetyCheck(block, is_non_null, .unwrap_null); | ||
| 1843 | } | ||
| 1844 | return block.addUnOp(src, child_type, .optional_payload, operand); | ||
| 1845 | } | ||
| 1846 | |||
| 1847 | /// Value in, value out | ||
| 1848 | fn zirErrUnionPayload( | ||
| 1849 | sema: *Sema, | ||
| 1850 | block: *Scope.Block, | ||
| 1851 | inst: zir.Inst.Index, | ||
| 1852 | safety_check: bool, | ||
| 1853 | ) InnerError!*Inst { | ||
| 1854 | const tracy = trace(@src()); | ||
| 1855 | defer tracy.end(); | ||
| 1856 | |||
| 1857 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1858 | const src = inst_data.src(); | ||
| 1859 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 1860 | if (operand.ty.zigTypeTag() != .ErrorUnion) | ||
| 1861 | return sema.mod.fail(&block.base, operand.src, "expected error union type, found '{}'", .{operand.ty}); | ||
| 1862 | |||
| 1863 | if (operand.value()) |val| { | ||
| 1864 | if (val.getError()) |name| { | ||
| 1865 | return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name}); | ||
| 1866 | } | ||
| 1867 | const data = val.castTag(.error_union).?.data; | ||
| 1868 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1869 | .ty = operand.ty.castTag(.error_union).?.data.payload, | ||
| 1870 | .val = data, | ||
| 1871 | }); | ||
| 1872 | } | ||
| 1873 | try sema.requireRuntimeBlock(block, src); | ||
| 1874 | if (safety_check and block.wantSafety()) { | ||
| 1875 | const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand); | ||
| 1876 | try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion); | ||
| 1877 | } | ||
| 1878 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_payload, operand); | ||
| 1879 | } | ||
| 1880 | |||
| 1881 | /// Pointer in, pointer out. | ||
| 1882 | fn zirErrUnionPayloadPtr( | ||
| 1883 | sema: *Sema, | ||
| 1884 | block: *Scope.Block, | ||
| 1885 | inst: zir.Inst.Index, | ||
| 1886 | safety_check: bool, | ||
| 1887 | ) InnerError!*Inst { | ||
| 1888 | const tracy = trace(@src()); | ||
| 1889 | defer tracy.end(); | ||
| 1890 | |||
| 1891 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1892 | const src = inst_data.src(); | ||
| 1893 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 1894 | assert(operand.ty.zigTypeTag() == .Pointer); | ||
| 1895 | |||
| 1896 | if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) | ||
| 1897 | return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()}); | ||
| 1898 | |||
| 1899 | const operand_pointer_ty = try sema.mod.simplePtrType(sema.arena, operand.ty.elemType().castTag(.error_union).?.data.payload, !operand.ty.isConstPtr(), .One); | ||
| 1900 | |||
| 1901 | if (operand.value()) |pointer_val| { | ||
| 1902 | const val = try pointer_val.pointerDeref(sema.arena); | ||
| 1903 | if (val.getError()) |name| { | ||
| 1904 | return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name}); | ||
| 1905 | } | ||
| 1906 | const data = val.castTag(.error_union).?.data; | ||
| 1907 | // The same Value represents the pointer to the error union and the payload. | ||
| 1908 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1909 | .ty = operand_pointer_ty, | ||
| 1910 | .val = try Value.Tag.ref_val.create( | ||
| 1911 | sema.arena, | ||
| 1912 | data, | ||
| 1913 | ), | ||
| 1914 | }); | ||
| 1915 | } | ||
| 1916 | |||
| 1917 | try sema.requireRuntimeBlock(block, src); | ||
| 1918 | if (safety_check and block.wantSafety()) { | ||
| 1919 | const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand); | ||
| 1920 | try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion); | ||
| 1921 | } | ||
| 1922 | return block.addUnOp(src, operand_pointer_ty, .unwrap_errunion_payload_ptr, operand); | ||
| 1923 | } | ||
| 1924 | |||
| 1925 | /// Value in, value out | ||
| 1926 | fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1927 | const tracy = trace(@src()); | ||
| 1928 | defer tracy.end(); | ||
| 1929 | |||
| 1930 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1931 | const src = inst_data.src(); | ||
| 1932 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 1933 | if (operand.ty.zigTypeTag() != .ErrorUnion) | ||
| 1934 | return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty}); | ||
| 1935 | |||
| 1936 | if (operand.value()) |val| { | ||
| 1937 | assert(val.getError() != null); | ||
| 1938 | const data = val.castTag(.error_union).?.data; | ||
| 1939 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1940 | .ty = operand.ty.castTag(.error_union).?.data.error_set, | ||
| 1941 | .val = data, | ||
| 1942 | }); | ||
| 1943 | } | ||
| 1944 | |||
| 1945 | try sema.requireRuntimeBlock(block, src); | ||
| 1946 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand); | ||
| 1947 | } | ||
| 1948 | |||
| 1949 | /// Pointer in, value out | ||
| 1950 | fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1951 | const tracy = trace(@src()); | ||
| 1952 | defer tracy.end(); | ||
| 1953 | |||
| 1954 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1955 | const src = inst_data.src(); | ||
| 1956 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 1957 | assert(operand.ty.zigTypeTag() == .Pointer); | ||
| 1958 | |||
| 1959 | if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) | ||
| 1960 | return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()}); | ||
| 1961 | |||
| 1962 | if (operand.value()) |pointer_val| { | ||
| 1963 | const val = try pointer_val.pointerDeref(sema.arena); | ||
| 1964 | assert(val.getError() != null); | ||
| 1965 | const data = val.castTag(.error_union).?.data; | ||
| 1966 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 1967 | .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set, | ||
| 1968 | .val = data, | ||
| 1969 | }); | ||
| 1970 | } | ||
| 1971 | |||
| 1972 | try sema.requireRuntimeBlock(block, src); | ||
| 1973 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand); | ||
| 1974 | } | ||
| 1975 | |||
| 1976 | fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | ||
| 1977 | const tracy = trace(@src()); | ||
| 1978 | defer tracy.end(); | ||
| 1979 | |||
| 1980 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1981 | const src = inst_data.src(); | ||
| 1982 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 1983 | if (operand.ty.zigTypeTag() != .ErrorUnion) | ||
| 1984 | return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty}); | ||
| 1985 | if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) { | ||
| 1986 | return sema.mod.fail(&block.base, src, "expression value is ignored", .{}); | ||
| 1987 | } | ||
| 1988 | } | ||
| 1989 | |||
| 1990 | fn zirFnType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: bool) InnerError!*Inst { | ||
| 1991 | const tracy = trace(@src()); | ||
| 1992 | defer tracy.end(); | ||
| 1993 | |||
| 1994 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1995 | const src = inst_data.src(); | ||
| 1996 | const extra = sema.code.extraData(zir.Inst.FnType, inst_data.payload_index); | ||
| 1997 | const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len); | ||
| 1998 | |||
| 1999 | return sema.fnTypeCommon( | ||
| 2000 | block, | ||
| 2001 | inst_data.src_node, | ||
| 2002 | param_types, | ||
| 2003 | extra.data.return_type, | ||
| 2004 | .Unspecified, | ||
| 2005 | var_args, | ||
| 2006 | ); | ||
| 2007 | } | ||
| 2008 | |||
| 2009 | fn zirFnTypeCc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: bool) InnerError!*Inst { | ||
| 2010 | const tracy = trace(@src()); | ||
| 2011 | defer tracy.end(); | ||
| 2012 | |||
| 2013 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2014 | const src = inst_data.src(); | ||
| 2015 | const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; | ||
| 2016 | const extra = sema.code.extraData(zir.Inst.FnTypeCc, inst_data.payload_index); | ||
| 2017 | const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len); | ||
| 2018 | |||
| 2019 | const cc_tv = try sema.resolveInstConst(block, cc_src, extra.data.cc); | ||
| 2020 | // TODO once we're capable of importing and analyzing decls from | ||
| 2021 | // std.builtin, this needs to change | ||
| 2022 | const cc_str = cc_tv.val.castTag(.enum_literal).?.data; | ||
| 2023 | const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse | ||
| 2024 | return sema.mod.fail(&block.base, cc_src, "Unknown calling convention {s}", .{cc_str}); | ||
| 2025 | return sema.fnTypeCommon( | ||
| 2026 | block, | ||
| 2027 | inst_data.src_node, | ||
| 2028 | param_types, | ||
| 2029 | extra.data.return_type, | ||
| 2030 | cc, | ||
| 2031 | var_args, | ||
| 2032 | ); | ||
| 2033 | } | ||
| 2034 | |||
| 2035 | fn fnTypeCommon( | ||
| 2036 | sema: *Sema, | ||
| 2037 | block: *Scope.Block, | ||
| 2038 | src_node_offset: i32, | ||
| 2039 | zir_param_types: []const zir.Inst.Ref, | ||
| 2040 | zir_return_type: zir.Inst.Ref, | ||
| 2041 | cc: std.builtin.CallingConvention, | ||
| 2042 | var_args: bool, | ||
| 2043 | ) InnerError!*Inst { | ||
| 2044 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; | ||
| 2045 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; | ||
| 2046 | const return_type = try sema.resolveType(block, ret_ty_src, zir_return_type); | ||
| 2047 | |||
| 2048 | // Hot path for some common function types. | ||
| 2049 | if (zir_param_types.len == 0 and !var_args) { | ||
| 2050 | if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { | ||
| 2051 | return sema.mod.constType(sema.arena, src, Type.initTag(.fn_noreturn_no_args)); | ||
| 2052 | } | ||
| 2053 | |||
| 2054 | if (return_type.zigTypeTag() == .Void and cc == .Unspecified) { | ||
| 2055 | return sema.mod.constType(sema.arena, src, Type.initTag(.fn_void_no_args)); | ||
| 2056 | } | ||
| 2057 | |||
| 2058 | if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) { | ||
| 2059 | return sema.mod.constType(sema.arena, src, Type.initTag(.fn_naked_noreturn_no_args)); | ||
| 2060 | } | ||
| 2061 | |||
| 2062 | if (return_type.zigTypeTag() == .Void and cc == .C) { | ||
| 2063 | return sema.mod.constType(sema.arena, src, Type.initTag(.fn_ccc_void_no_args)); | ||
| 2064 | } | ||
| 2065 | } | ||
| 2066 | |||
| 2067 | const param_types = try sema.arena.alloc(Type, zir_param_types.len); | ||
| 2068 | for (zir_param_types) |param_type, i| { | ||
| 2069 | // TODO make a compile error from `resolveType` report the source location | ||
| 2070 | // of the specific parameter. Will need to take a similar strategy as | ||
| 2071 | // `resolveSwitchItemVal` to avoid resolving the source location unless | ||
| 2072 | // we actually need to report an error. | ||
| 2073 | param_types[i] = try sema.resolveType(block, src, param_type); | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | const fn_ty = try Type.Tag.function.create(sema.arena, .{ | ||
| 2077 | .param_types = param_types, | ||
| 2078 | .return_type = return_type, | ||
| 2079 | .cc = cc, | ||
| 2080 | .is_var_args = var_args, | ||
| 2081 | }); | ||
| 2082 | return sema.mod.constType(sema.arena, src, fn_ty); | ||
| 2083 | } | ||
| 2084 | |||
| 2085 | fn zirAs(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2086 | const tracy = trace(@src()); | ||
| 2087 | defer tracy.end(); | ||
| 2088 | |||
| 2089 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2090 | return sema.analyzeAs(block, .unneeded, bin_inst.lhs, bin_inst.rhs); | ||
| 2091 | } | ||
| 2092 | |||
| 2093 | fn zirAsNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2094 | const tracy = trace(@src()); | ||
| 2095 | defer tracy.end(); | ||
| 2096 | |||
| 2097 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2098 | const src = inst_data.src(); | ||
| 2099 | const extra = sema.code.extraData(zir.Inst.As, inst_data.payload_index).data; | ||
| 2100 | return sema.analyzeAs(block, src, extra.dest_type, extra.operand); | ||
| 2101 | } | ||
| 2102 | |||
| 2103 | fn analyzeAs( | ||
| 2104 | sema: *Sema, | ||
| 2105 | block: *Scope.Block, | ||
| 2106 | src: LazySrcLoc, | ||
| 2107 | zir_dest_type: zir.Inst.Ref, | ||
| 2108 | zir_operand: zir.Inst.Ref, | ||
| 2109 | ) InnerError!*Inst { | ||
| 2110 | const dest_type = try sema.resolveType(block, src, zir_dest_type); | ||
| 2111 | const operand = try sema.resolveInst(zir_operand); | ||
| 2112 | return sema.coerce(block, dest_type, operand, src); | ||
| 2113 | } | ||
| 2114 | |||
| 2115 | fn zirPtrtoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2116 | const tracy = trace(@src()); | ||
| 2117 | defer tracy.end(); | ||
| 2118 | |||
| 2119 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 2120 | const ptr = try sema.resolveInst(inst_data.operand); | ||
| 2121 | if (ptr.ty.zigTypeTag() != .Pointer) { | ||
| 2122 | const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 2123 | return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}); | ||
| 2124 | } | ||
| 2125 | // TODO handle known-pointer-address | ||
| 2126 | const src = inst_data.src(); | ||
| 2127 | try sema.requireRuntimeBlock(block, src); | ||
| 2128 | const ty = Type.initTag(.usize); | ||
| 2129 | return block.addUnOp(src, ty, .ptrtoint, ptr); | ||
| 2130 | } | ||
| 2131 | |||
| 2132 | fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2133 | const tracy = trace(@src()); | ||
| 2134 | defer tracy.end(); | ||
| 2135 | |||
| 2136 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2137 | const src = inst_data.src(); | ||
| 2138 | const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; | ||
| 2139 | const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data; | ||
| 2140 | const field_name = sema.code.nullTerminatedString(extra.field_name_start); | ||
| 2141 | const object = try sema.resolveInst(extra.lhs); | ||
| 2142 | const object_ptr = try sema.analyzeRef(block, src, object); | ||
| 2143 | const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 2144 | return sema.analyzeLoad(block, src, result_ptr, result_ptr.src); | ||
| 2145 | } | ||
| 2146 | |||
| 2147 | fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2148 | const tracy = trace(@src()); | ||
| 2149 | defer tracy.end(); | ||
| 2150 | |||
| 2151 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2152 | const src = inst_data.src(); | ||
| 2153 | const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; | ||
| 2154 | const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data; | ||
| 2155 | const field_name = sema.code.nullTerminatedString(extra.field_name_start); | ||
| 2156 | const object_ptr = try sema.resolveInst(extra.lhs); | ||
| 2157 | return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 2158 | } | ||
| 2159 | |||
| 2160 | fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2161 | const tracy = trace(@src()); | ||
| 2162 | defer tracy.end(); | ||
| 2163 | |||
| 2164 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2165 | const src = inst_data.src(); | ||
| 2166 | const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 2167 | const extra = sema.code.extraData(zir.Inst.FieldNamed, inst_data.payload_index).data; | ||
| 2168 | const object = try sema.resolveInst(extra.lhs); | ||
| 2169 | const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); | ||
| 2170 | const object_ptr = try sema.analyzeRef(block, src, object); | ||
| 2171 | const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 2172 | return sema.analyzeLoad(block, src, result_ptr, src); | ||
| 2173 | } | ||
| 2174 | |||
| 2175 | fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2176 | const tracy = trace(@src()); | ||
| 2177 | defer tracy.end(); | ||
| 2178 | |||
| 2179 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2180 | const src = inst_data.src(); | ||
| 2181 | const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 2182 | const extra = sema.code.extraData(zir.Inst.FieldNamed, inst_data.payload_index).data; | ||
| 2183 | const object_ptr = try sema.resolveInst(extra.lhs); | ||
| 2184 | const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); | ||
| 2185 | return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 2186 | } | ||
| 2187 | |||
| 2188 | fn zirIntcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2189 | const tracy = trace(@src()); | ||
| 2190 | defer tracy.end(); | ||
| 2191 | |||
| 2192 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2193 | const src = inst_data.src(); | ||
| 2194 | const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 2195 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 2196 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 2197 | |||
| 2198 | const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs); | ||
| 2199 | const operand = try sema.resolveInst(extra.rhs); | ||
| 2200 | |||
| 2201 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { | ||
| 2202 | .ComptimeInt => true, | ||
| 2203 | .Int => false, | ||
| 2204 | else => return sema.mod.fail( | ||
| 2205 | &block.base, | ||
| 2206 | dest_ty_src, | ||
| 2207 | "expected integer type, found '{}'", | ||
| 2208 | .{dest_type}, | ||
| 2209 | ), | ||
| 2210 | }; | ||
| 2211 | |||
| 2212 | switch (operand.ty.zigTypeTag()) { | ||
| 2213 | .ComptimeInt, .Int => {}, | ||
| 2214 | else => return sema.mod.fail( | ||
| 2215 | &block.base, | ||
| 2216 | operand_src, | ||
| 2217 | "expected integer type, found '{}'", | ||
| 2218 | .{operand.ty}, | ||
| 2219 | ), | ||
| 2220 | } | ||
| 2221 | |||
| 2222 | if (operand.value() != null) { | ||
| 2223 | return sema.coerce(block, dest_type, operand, operand_src); | ||
| 2224 | } else if (dest_is_comptime_int) { | ||
| 2225 | return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_int'", .{}); | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten int", .{}); | ||
| 2229 | } | ||
| 2230 | |||
| 2231 | fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2232 | const tracy = trace(@src()); | ||
| 2233 | defer tracy.end(); | ||
| 2234 | |||
| 2235 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2236 | const src = inst_data.src(); | ||
| 2237 | const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 2238 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 2239 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 2240 | |||
| 2241 | const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs); | ||
| 2242 | const operand = try sema.resolveInst(extra.rhs); | ||
| 2243 | return sema.bitcast(block, dest_type, operand); | ||
| 2244 | } | ||
| 2245 | |||
| 2246 | fn zirFloatcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2247 | const tracy = trace(@src()); | ||
| 2248 | defer tracy.end(); | ||
| 2249 | |||
| 2250 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2251 | const src = inst_data.src(); | ||
| 2252 | const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 2253 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 2254 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 2255 | |||
| 2256 | const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs); | ||
| 2257 | const operand = try sema.resolveInst(extra.rhs); | ||
| 2258 | |||
| 2259 | const dest_is_comptime_float = switch (dest_type.zigTypeTag()) { | ||
| 2260 | .ComptimeFloat => true, | ||
| 2261 | .Float => false, | ||
| 2262 | else => return sema.mod.fail( | ||
| 2263 | &block.base, | ||
| 2264 | dest_ty_src, | ||
| 2265 | "expected float type, found '{}'", | ||
| 2266 | .{dest_type}, | ||
| 2267 | ), | ||
| 2268 | }; | ||
| 2269 | |||
| 2270 | switch (operand.ty.zigTypeTag()) { | ||
| 2271 | .ComptimeFloat, .Float, .ComptimeInt => {}, | ||
| 2272 | else => return sema.mod.fail( | ||
| 2273 | &block.base, | ||
| 2274 | operand_src, | ||
| 2275 | "expected float type, found '{}'", | ||
| 2276 | .{operand.ty}, | ||
| 2277 | ), | ||
| 2278 | } | ||
| 2279 | |||
| 2280 | if (operand.value() != null) { | ||
| 2281 | return sema.coerce(block, dest_type, operand, operand_src); | ||
| 2282 | } else if (dest_is_comptime_float) { | ||
| 2283 | return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_float'", .{}); | ||
| 2284 | } | ||
| 2285 | |||
| 2286 | return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten float", .{}); | ||
| 2287 | } | ||
| 2288 | |||
| 2289 | fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2290 | const tracy = trace(@src()); | ||
| 2291 | defer tracy.end(); | ||
| 2292 | |||
| 2293 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2294 | const array = try sema.resolveInst(bin_inst.lhs); | ||
| 2295 | const array_ptr = try sema.analyzeRef(block, sema.src, array); | ||
| 2296 | const elem_index = try sema.resolveInst(bin_inst.rhs); | ||
| 2297 | const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src); | ||
| 2298 | return sema.analyzeLoad(block, sema.src, result_ptr, sema.src); | ||
| 2299 | } | ||
| 2300 | |||
| 2301 | fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2302 | const tracy = trace(@src()); | ||
| 2303 | defer tracy.end(); | ||
| 2304 | |||
| 2305 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2306 | const src = inst_data.src(); | ||
| 2307 | const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; | ||
| 2308 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 2309 | const array = try sema.resolveInst(extra.lhs); | ||
| 2310 | const array_ptr = try sema.analyzeRef(block, src, array); | ||
| 2311 | const elem_index = try sema.resolveInst(extra.rhs); | ||
| 2312 | const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); | ||
| 2313 | return sema.analyzeLoad(block, src, result_ptr, src); | ||
| 2314 | } | ||
| 2315 | |||
| 2316 | fn zirElemPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2317 | const tracy = trace(@src()); | ||
| 2318 | defer tracy.end(); | ||
| 2319 | |||
| 2320 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2321 | const array_ptr = try sema.resolveInst(bin_inst.lhs); | ||
| 2322 | const elem_index = try sema.resolveInst(bin_inst.rhs); | ||
| 2323 | return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src); | ||
| 2324 | } | ||
| 2325 | |||
| 2326 | fn zirElemPtrNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2327 | const tracy = trace(@src()); | ||
| 2328 | defer tracy.end(); | ||
| 2329 | |||
| 2330 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2331 | const src = inst_data.src(); | ||
| 2332 | const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; | ||
| 2333 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 2334 | const array_ptr = try sema.resolveInst(extra.lhs); | ||
| 2335 | const elem_index = try sema.resolveInst(extra.rhs); | ||
| 2336 | return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); | ||
| 2337 | } | ||
| 2338 | |||
| 2339 | fn zirSliceStart(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2340 | const tracy = trace(@src()); | ||
| 2341 | defer tracy.end(); | ||
| 2342 | |||
| 2343 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2344 | const src = inst_data.src(); | ||
| 2345 | const extra = sema.code.extraData(zir.Inst.SliceStart, inst_data.payload_index).data; | ||
| 2346 | const array_ptr = try sema.resolveInst(extra.lhs); | ||
| 2347 | const start = try sema.resolveInst(extra.start); | ||
| 2348 | |||
| 2349 | return sema.analyzeSlice(block, src, array_ptr, start, null, null, .unneeded); | ||
| 2350 | } | ||
| 2351 | |||
| 2352 | fn zirSliceEnd(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2353 | const tracy = trace(@src()); | ||
| 2354 | defer tracy.end(); | ||
| 2355 | |||
| 2356 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2357 | const src = inst_data.src(); | ||
| 2358 | const extra = sema.code.extraData(zir.Inst.SliceEnd, inst_data.payload_index).data; | ||
| 2359 | const array_ptr = try sema.resolveInst(extra.lhs); | ||
| 2360 | const start = try sema.resolveInst(extra.start); | ||
| 2361 | const end = try sema.resolveInst(extra.end); | ||
| 2362 | |||
| 2363 | return sema.analyzeSlice(block, src, array_ptr, start, end, null, .unneeded); | ||
| 2364 | } | ||
| 2365 | |||
| 2366 | fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2367 | const tracy = trace(@src()); | ||
| 2368 | defer tracy.end(); | ||
| 2369 | |||
| 2370 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2371 | const src = inst_data.src(); | ||
| 2372 | const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node }; | ||
| 2373 | const extra = sema.code.extraData(zir.Inst.SliceSentinel, inst_data.payload_index).data; | ||
| 2374 | const array_ptr = try sema.resolveInst(extra.lhs); | ||
| 2375 | const start = try sema.resolveInst(extra.start); | ||
| 2376 | const end = try sema.resolveInst(extra.end); | ||
| 2377 | const sentinel = try sema.resolveInst(extra.sentinel); | ||
| 2378 | |||
| 2379 | return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src); | ||
| 2380 | } | ||
| 2381 | |||
| 2382 | fn zirSwitchCapture( | ||
| 2383 | sema: *Sema, | ||
| 2384 | block: *Scope.Block, | ||
| 2385 | inst: zir.Inst.Index, | ||
| 2386 | is_multi: bool, | ||
| 2387 | is_ref: bool, | ||
| 2388 | ) InnerError!*Inst { | ||
| 2389 | const tracy = trace(@src()); | ||
| 2390 | defer tracy.end(); | ||
| 2391 | |||
| 2392 | const zir_datas = sema.code.instructions.items(.data); | ||
| 2393 | const capture_info = zir_datas[inst].switch_capture; | ||
| 2394 | const switch_info = zir_datas[capture_info.switch_inst].pl_node; | ||
| 2395 | const src = switch_info.src(); | ||
| 2396 | |||
| 2397 | return sema.mod.fail(&block.base, src, "TODO implement Sema for zirSwitchCapture", .{}); | ||
| 2398 | } | ||
| 2399 | |||
| 2400 | fn zirSwitchCaptureElse( | ||
| 2401 | sema: *Sema, | ||
| 2402 | block: *Scope.Block, | ||
| 2403 | inst: zir.Inst.Index, | ||
| 2404 | is_ref: bool, | ||
| 2405 | ) InnerError!*Inst { | ||
| 2406 | const tracy = trace(@src()); | ||
| 2407 | defer tracy.end(); | ||
| 2408 | |||
| 2409 | const zir_datas = sema.code.instructions.items(.data); | ||
| 2410 | const capture_info = zir_datas[inst].switch_capture; | ||
| 2411 | const switch_info = zir_datas[capture_info.switch_inst].pl_node; | ||
| 2412 | const src = switch_info.src(); | ||
| 2413 | |||
| 2414 | return sema.mod.fail(&block.base, src, "TODO implement Sema for zirSwitchCaptureElse", .{}); | ||
| 2415 | } | ||
| 2416 | |||
| 2417 | fn zirSwitchBlock( | ||
| 2418 | sema: *Sema, | ||
| 2419 | block: *Scope.Block, | ||
| 2420 | inst: zir.Inst.Index, | ||
| 2421 | is_ref: bool, | ||
| 2422 | special_prong: zir.SpecialProng, | ||
| 2423 | ) InnerError!*Inst { | ||
| 2424 | const tracy = trace(@src()); | ||
| 2425 | defer tracy.end(); | ||
| 2426 | |||
| 2427 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2428 | const src = inst_data.src(); | ||
| 2429 | const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node }; | ||
| 2430 | const extra = sema.code.extraData(zir.Inst.SwitchBlock, inst_data.payload_index); | ||
| 2431 | |||
| 2432 | const operand_ptr = try sema.resolveInst(extra.data.operand); | ||
| 2433 | const operand = if (is_ref) | ||
| 2434 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) | ||
| 2435 | else | ||
| 2436 | operand_ptr; | ||
| 2437 | |||
| 2438 | return sema.analyzeSwitch( | ||
| 2439 | block, | ||
| 2440 | operand, | ||
| 2441 | extra.end, | ||
| 2442 | special_prong, | ||
| 2443 | extra.data.cases_len, | ||
| 2444 | 0, | ||
| 2445 | inst, | ||
| 2446 | inst_data.src_node, | ||
| 2447 | ); | ||
| 2448 | } | ||
| 2449 | |||
| 2450 | fn zirSwitchBlockMulti( | ||
| 2451 | sema: *Sema, | ||
| 2452 | block: *Scope.Block, | ||
| 2453 | inst: zir.Inst.Index, | ||
| 2454 | is_ref: bool, | ||
| 2455 | special_prong: zir.SpecialProng, | ||
| 2456 | ) InnerError!*Inst { | ||
| 2457 | const tracy = trace(@src()); | ||
| 2458 | defer tracy.end(); | ||
| 2459 | |||
| 2460 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2461 | const src = inst_data.src(); | ||
| 2462 | const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node }; | ||
| 2463 | const extra = sema.code.extraData(zir.Inst.SwitchBlockMulti, inst_data.payload_index); | ||
| 2464 | |||
| 2465 | const operand_ptr = try sema.resolveInst(extra.data.operand); | ||
| 2466 | const operand = if (is_ref) | ||
| 2467 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) | ||
| 2468 | else | ||
| 2469 | operand_ptr; | ||
| 2470 | |||
| 2471 | return sema.analyzeSwitch( | ||
| 2472 | block, | ||
| 2473 | operand, | ||
| 2474 | extra.end, | ||
| 2475 | special_prong, | ||
| 2476 | extra.data.scalar_cases_len, | ||
| 2477 | extra.data.multi_cases_len, | ||
| 2478 | inst, | ||
| 2479 | inst_data.src_node, | ||
| 2480 | ); | ||
| 2481 | } | ||
| 2482 | |||
| 2483 | fn analyzeSwitch( | ||
| 2484 | sema: *Sema, | ||
| 2485 | block: *Scope.Block, | ||
| 2486 | operand: *Inst, | ||
| 2487 | extra_end: usize, | ||
| 2488 | special_prong: zir.SpecialProng, | ||
| 2489 | scalar_cases_len: usize, | ||
| 2490 | multi_cases_len: usize, | ||
| 2491 | switch_inst: zir.Inst.Index, | ||
| 2492 | src_node_offset: i32, | ||
| 2493 | ) InnerError!*Inst { | ||
| 2494 | const gpa = sema.gpa; | ||
| 2495 | const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) { | ||
| 2496 | .none => .{ .body = &.{}, .end = extra_end }, | ||
| 2497 | .under, .@"else" => blk: { | ||
| 2498 | const body_len = sema.code.extra[extra_end]; | ||
| 2499 | const extra_body_start = extra_end + 1; | ||
| 2500 | break :blk .{ | ||
| 2501 | .body = sema.code.extra[extra_body_start..][0..body_len], | ||
| 2502 | .end = extra_body_start + body_len, | ||
| 2503 | }; | ||
| 2504 | }, | ||
| 2505 | }; | ||
| 2506 | |||
| 2507 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; | ||
| 2508 | const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset }; | ||
| 2509 | const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset }; | ||
| 2510 | |||
| 2511 | // Validate usage of '_' prongs. | ||
| 2512 | if (special_prong == .under and !operand.ty.isExhaustiveEnum()) { | ||
| 2513 | const msg = msg: { | ||
| 2514 | const msg = try sema.mod.errMsg( | ||
| 2515 | &block.base, | ||
| 2516 | src, | ||
| 2517 | "'_' prong only allowed when switching on non-exhaustive enums", | ||
| 2518 | .{}, | ||
| 2519 | ); | ||
| 2520 | errdefer msg.destroy(gpa); | ||
| 2521 | try sema.mod.errNote( | ||
| 2522 | &block.base, | ||
| 2523 | special_prong_src, | ||
| 2524 | msg, | ||
| 2525 | "'_' prong here", | ||
| 2526 | .{}, | ||
| 2527 | ); | ||
| 2528 | break :msg msg; | ||
| 2529 | }; | ||
| 2530 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | ||
| 2531 | } | ||
| 2532 | |||
| 2533 | // Validate for duplicate items, missing else prong, and invalid range. | ||
| 2534 | switch (operand.ty.zigTypeTag()) { | ||
| 2535 | .Enum => return sema.mod.fail(&block.base, src, "TODO validate switch .Enum", .{}), | ||
| 2536 | .ErrorSet => return sema.mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}), | ||
| 2537 | .Union => return sema.mod.fail(&block.base, src, "TODO validate switch .Union", .{}), | ||
| 2538 | .Int, .ComptimeInt => { | ||
| 2539 | var range_set = RangeSet.init(gpa); | ||
| 2540 | defer range_set.deinit(); | ||
| 2541 | |||
| 2542 | var extra_index: usize = special.end; | ||
| 2543 | { | ||
| 2544 | var scalar_i: u32 = 0; | ||
| 2545 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | ||
| 2546 | const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2547 | extra_index += 1; | ||
| 2548 | const body_len = sema.code.extra[extra_index]; | ||
| 2549 | extra_index += 1; | ||
| 2550 | const body = sema.code.extra[extra_index..][0..body_len]; | ||
| 2551 | extra_index += body_len; | ||
| 2552 | |||
| 2553 | try sema.validateSwitchItem( | ||
| 2554 | block, | ||
| 2555 | &range_set, | ||
| 2556 | item_ref, | ||
| 2557 | src_node_offset, | ||
| 2558 | .{ .scalar = scalar_i }, | ||
| 2559 | ); | ||
| 2560 | } | ||
| 2561 | } | ||
| 2562 | { | ||
| 2563 | var multi_i: u32 = 0; | ||
| 2564 | while (multi_i < multi_cases_len) : (multi_i += 1) { | ||
| 2565 | const items_len = sema.code.extra[extra_index]; | ||
| 2566 | extra_index += 1; | ||
| 2567 | const ranges_len = sema.code.extra[extra_index]; | ||
| 2568 | extra_index += 1; | ||
| 2569 | const body_len = sema.code.extra[extra_index]; | ||
| 2570 | extra_index += 1; | ||
| 2571 | const items = sema.code.refSlice(extra_index, items_len); | ||
| 2572 | extra_index += items_len; | ||
| 2573 | |||
| 2574 | for (items) |item_ref, item_i| { | ||
| 2575 | try sema.validateSwitchItem( | ||
| 2576 | block, | ||
| 2577 | &range_set, | ||
| 2578 | item_ref, | ||
| 2579 | src_node_offset, | ||
| 2580 | .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, | ||
| 2581 | ); | ||
| 2582 | } | ||
| 2583 | |||
| 2584 | var range_i: u32 = 0; | ||
| 2585 | while (range_i < ranges_len) : (range_i += 1) { | ||
| 2586 | const item_first = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2587 | extra_index += 1; | ||
| 2588 | const item_last = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2589 | extra_index += 1; | ||
| 2590 | |||
| 2591 | try sema.validateSwitchRange( | ||
| 2592 | block, | ||
| 2593 | &range_set, | ||
| 2594 | item_first, | ||
| 2595 | item_last, | ||
| 2596 | src_node_offset, | ||
| 2597 | .{ .range = .{ .prong = multi_i, .item = range_i } }, | ||
| 2598 | ); | ||
| 2599 | } | ||
| 2600 | |||
| 2601 | extra_index += body_len; | ||
| 2602 | } | ||
| 2603 | } | ||
| 2604 | |||
| 2605 | check_range: { | ||
| 2606 | if (operand.ty.zigTypeTag() == .Int) { | ||
| 2607 | var arena = std.heap.ArenaAllocator.init(gpa); | ||
| 2608 | defer arena.deinit(); | ||
| 2609 | |||
| 2610 | const min_int = try operand.ty.minInt(&arena, sema.mod.getTarget()); | ||
| 2611 | const max_int = try operand.ty.maxInt(&arena, sema.mod.getTarget()); | ||
| 2612 | if (try range_set.spans(min_int, max_int)) { | ||
| 2613 | if (special_prong == .@"else") { | ||
| 2614 | return sema.mod.fail( | ||
| 2615 | &block.base, | ||
| 2616 | special_prong_src, | ||
| 2617 | "unreachable else prong; all cases already handled", | ||
| 2618 | .{}, | ||
| 2619 | ); | ||
| 2620 | } | ||
| 2621 | break :check_range; | ||
| 2622 | } | ||
| 2623 | } | ||
| 2624 | if (special_prong != .@"else") { | ||
| 2625 | return sema.mod.fail( | ||
| 2626 | &block.base, | ||
| 2627 | src, | ||
| 2628 | "switch must handle all possibilities", | ||
| 2629 | .{}, | ||
| 2630 | ); | ||
| 2631 | } | ||
| 2632 | } | ||
| 2633 | }, | ||
| 2634 | .Bool => { | ||
| 2635 | var true_count: u8 = 0; | ||
| 2636 | var false_count: u8 = 0; | ||
| 2637 | |||
| 2638 | var extra_index: usize = special.end; | ||
| 2639 | { | ||
| 2640 | var scalar_i: u32 = 0; | ||
| 2641 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | ||
| 2642 | const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2643 | extra_index += 1; | ||
| 2644 | const body_len = sema.code.extra[extra_index]; | ||
| 2645 | extra_index += 1; | ||
| 2646 | const body = sema.code.extra[extra_index..][0..body_len]; | ||
| 2647 | extra_index += body_len; | ||
| 2648 | |||
| 2649 | try sema.validateSwitchItemBool( | ||
| 2650 | block, | ||
| 2651 | &true_count, | ||
| 2652 | &false_count, | ||
| 2653 | item_ref, | ||
| 2654 | src_node_offset, | ||
| 2655 | .{ .scalar = scalar_i }, | ||
| 2656 | ); | ||
| 2657 | } | ||
| 2658 | } | ||
| 2659 | { | ||
| 2660 | var multi_i: u32 = 0; | ||
| 2661 | while (multi_i < multi_cases_len) : (multi_i += 1) { | ||
| 2662 | const items_len = sema.code.extra[extra_index]; | ||
| 2663 | extra_index += 1; | ||
| 2664 | const ranges_len = sema.code.extra[extra_index]; | ||
| 2665 | extra_index += 1; | ||
| 2666 | const body_len = sema.code.extra[extra_index]; | ||
| 2667 | extra_index += 1; | ||
| 2668 | const items = sema.code.refSlice(extra_index, items_len); | ||
| 2669 | extra_index += items_len + body_len; | ||
| 2670 | |||
| 2671 | for (items) |item_ref, item_i| { | ||
| 2672 | try sema.validateSwitchItemBool( | ||
| 2673 | block, | ||
| 2674 | &true_count, | ||
| 2675 | &false_count, | ||
| 2676 | item_ref, | ||
| 2677 | src_node_offset, | ||
| 2678 | .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, | ||
| 2679 | ); | ||
| 2680 | } | ||
| 2681 | |||
| 2682 | try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset); | ||
| 2683 | } | ||
| 2684 | } | ||
| 2685 | switch (special_prong) { | ||
| 2686 | .@"else" => { | ||
| 2687 | if (true_count + false_count == 2) { | ||
| 2688 | return sema.mod.fail( | ||
| 2689 | &block.base, | ||
| 2690 | src, | ||
| 2691 | "unreachable else prong; all cases already handled", | ||
| 2692 | .{}, | ||
| 2693 | ); | ||
| 2694 | } | ||
| 2695 | }, | ||
| 2696 | .under, .none => { | ||
| 2697 | if (true_count + false_count < 2) { | ||
| 2698 | return sema.mod.fail( | ||
| 2699 | &block.base, | ||
| 2700 | src, | ||
| 2701 | "switch must handle all possibilities", | ||
| 2702 | .{}, | ||
| 2703 | ); | ||
| 2704 | } | ||
| 2705 | }, | ||
| 2706 | } | ||
| 2707 | }, | ||
| 2708 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { | ||
| 2709 | if (special_prong != .@"else") { | ||
| 2710 | return sema.mod.fail( | ||
| 2711 | &block.base, | ||
| 2712 | src, | ||
| 2713 | "else prong required when switching on type '{}'", | ||
| 2714 | .{operand.ty}, | ||
| 2715 | ); | ||
| 2716 | } | ||
| 2717 | |||
| 2718 | var seen_values = ValueSrcMap.init(gpa); | ||
| 2719 | defer seen_values.deinit(); | ||
| 2720 | |||
| 2721 | var extra_index: usize = special.end; | ||
| 2722 | { | ||
| 2723 | var scalar_i: u32 = 0; | ||
| 2724 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | ||
| 2725 | const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2726 | extra_index += 1; | ||
| 2727 | const body_len = sema.code.extra[extra_index]; | ||
| 2728 | extra_index += 1; | ||
| 2729 | const body = sema.code.extra[extra_index..][0..body_len]; | ||
| 2730 | extra_index += body_len; | ||
| 2731 | |||
| 2732 | try sema.validateSwitchItemSparse( | ||
| 2733 | block, | ||
| 2734 | &seen_values, | ||
| 2735 | item_ref, | ||
| 2736 | src_node_offset, | ||
| 2737 | .{ .scalar = scalar_i }, | ||
| 2738 | ); | ||
| 2739 | } | ||
| 2740 | } | ||
| 2741 | { | ||
| 2742 | var multi_i: u32 = 0; | ||
| 2743 | while (multi_i < multi_cases_len) : (multi_i += 1) { | ||
| 2744 | const items_len = sema.code.extra[extra_index]; | ||
| 2745 | extra_index += 1; | ||
| 2746 | const ranges_len = sema.code.extra[extra_index]; | ||
| 2747 | extra_index += 1; | ||
| 2748 | const body_len = sema.code.extra[extra_index]; | ||
| 2749 | extra_index += 1; | ||
| 2750 | const items = sema.code.refSlice(extra_index, items_len); | ||
| 2751 | extra_index += items_len + body_len; | ||
| 2752 | |||
| 2753 | for (items) |item_ref, item_i| { | ||
| 2754 | try sema.validateSwitchItemSparse( | ||
| 2755 | block, | ||
| 2756 | &seen_values, | ||
| 2757 | item_ref, | ||
| 2758 | src_node_offset, | ||
| 2759 | .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, | ||
| 2760 | ); | ||
| 2761 | } | ||
| 2762 | |||
| 2763 | try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset); | ||
| 2764 | } | ||
| 2765 | } | ||
| 2766 | }, | ||
| 2767 | |||
| 2768 | .ErrorUnion, | ||
| 2769 | .NoReturn, | ||
| 2770 | .Array, | ||
| 2771 | .Struct, | ||
| 2772 | .Undefined, | ||
| 2773 | .Null, | ||
| 2774 | .Optional, | ||
| 2775 | .BoundFn, | ||
| 2776 | .Opaque, | ||
| 2777 | .Vector, | ||
| 2778 | .Frame, | ||
| 2779 | .AnyFrame, | ||
| 2780 | .ComptimeFloat, | ||
| 2781 | .Float, | ||
| 2782 | => return sema.mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{ | ||
| 2783 | operand.ty, | ||
| 2784 | }), | ||
| 2785 | } | ||
| 2786 | |||
| 2787 | if (try sema.resolveDefinedValue(block, src, operand)) |operand_val| { | ||
| 2788 | var extra_index: usize = special.end; | ||
| 2789 | { | ||
| 2790 | var scalar_i: usize = 0; | ||
| 2791 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | ||
| 2792 | const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2793 | extra_index += 1; | ||
| 2794 | const body_len = sema.code.extra[extra_index]; | ||
| 2795 | extra_index += 1; | ||
| 2796 | const body = sema.code.extra[extra_index..][0..body_len]; | ||
| 2797 | extra_index += body_len; | ||
| 2798 | |||
| 2799 | // Validation above ensured these will succeed. | ||
| 2800 | const item = sema.resolveInst(item_ref) catch unreachable; | ||
| 2801 | const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable; | ||
| 2802 | if (operand_val.eql(item_val)) { | ||
| 2803 | return sema.resolveBody(block, body); | ||
| 2804 | } | ||
| 2805 | } | ||
| 2806 | } | ||
| 2807 | { | ||
| 2808 | var multi_i: usize = 0; | ||
| 2809 | while (multi_i < multi_cases_len) : (multi_i += 1) { | ||
| 2810 | const items_len = sema.code.extra[extra_index]; | ||
| 2811 | extra_index += 1; | ||
| 2812 | const ranges_len = sema.code.extra[extra_index]; | ||
| 2813 | extra_index += 1; | ||
| 2814 | const body_len = sema.code.extra[extra_index]; | ||
| 2815 | extra_index += 1; | ||
| 2816 | const items = sema.code.refSlice(extra_index, items_len); | ||
| 2817 | extra_index += items_len; | ||
| 2818 | const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len]; | ||
| 2819 | |||
| 2820 | for (items) |item_ref| { | ||
| 2821 | // Validation above ensured these will succeed. | ||
| 2822 | const item = sema.resolveInst(item_ref) catch unreachable; | ||
| 2823 | const item_val = sema.resolveConstValue(block, item.src, item) catch unreachable; | ||
| 2824 | if (operand_val.eql(item_val)) { | ||
| 2825 | return sema.resolveBody(block, body); | ||
| 2826 | } | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | var range_i: usize = 0; | ||
| 2830 | while (range_i < ranges_len) : (range_i += 1) { | ||
| 2831 | const item_first = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2832 | extra_index += 1; | ||
| 2833 | const item_last = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2834 | extra_index += 1; | ||
| 2835 | |||
| 2836 | // Validation above ensured these will succeed. | ||
| 2837 | const first_tv = sema.resolveInstConst(block, .unneeded, item_first) catch unreachable; | ||
| 2838 | const last_tv = sema.resolveInstConst(block, .unneeded, item_last) catch unreachable; | ||
| 2839 | if (Value.compare(operand_val, .gte, first_tv.val) and | ||
| 2840 | Value.compare(operand_val, .lte, last_tv.val)) | ||
| 2841 | { | ||
| 2842 | return sema.resolveBody(block, body); | ||
| 2843 | } | ||
| 2844 | } | ||
| 2845 | |||
| 2846 | extra_index += body_len; | ||
| 2847 | } | ||
| 2848 | } | ||
| 2849 | return sema.resolveBody(block, special.body); | ||
| 2850 | } | ||
| 2851 | |||
| 2852 | if (scalar_cases_len + multi_cases_len == 0) { | ||
| 2853 | return sema.resolveBody(block, special.body); | ||
| 2854 | } | ||
| 2855 | |||
| 2856 | try sema.requireRuntimeBlock(block, src); | ||
| 2857 | |||
| 2858 | const block_inst = try sema.arena.create(Inst.Block); | ||
| 2859 | block_inst.* = .{ | ||
| 2860 | .base = .{ | ||
| 2861 | .tag = Inst.Block.base_tag, | ||
| 2862 | .ty = undefined, // Set after analysis. | ||
| 2863 | .src = src, | ||
| 2864 | }, | ||
| 2865 | .body = undefined, | ||
| 2866 | }; | ||
| 2867 | |||
| 2868 | var child_block: Scope.Block = .{ | ||
| 2869 | .parent = block, | ||
| 2870 | .sema = sema, | ||
| 2871 | .src_decl = block.src_decl, | ||
| 2872 | .instructions = .{}, | ||
| 2873 | // TODO @as here is working around a stage1 miscompilation bug :( | ||
| 2874 | .label = @as(?Scope.Block.Label, Scope.Block.Label{ | ||
| 2875 | .zir_block = switch_inst, | ||
| 2876 | .merges = .{ | ||
| 2877 | .results = .{}, | ||
| 2878 | .br_list = .{}, | ||
| 2879 | .block_inst = block_inst, | ||
| 2880 | }, | ||
| 2881 | }), | ||
| 2882 | .inlining = block.inlining, | ||
| 2883 | .is_comptime = block.is_comptime, | ||
| 2884 | }; | ||
| 2885 | const merges = &child_block.label.?.merges; | ||
| 2886 | defer child_block.instructions.deinit(gpa); | ||
| 2887 | defer merges.results.deinit(gpa); | ||
| 2888 | defer merges.br_list.deinit(gpa); | ||
| 2889 | |||
| 2890 | // TODO when reworking TZIR memory layout make multi cases get generated as cases, | ||
| 2891 | // not as part of the "else" block. | ||
| 2892 | const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len); | ||
| 2893 | |||
| 2894 | var case_block = child_block.makeSubBlock(); | ||
| 2895 | defer case_block.instructions.deinit(gpa); | ||
| 2896 | |||
| 2897 | var extra_index: usize = special.end; | ||
| 2898 | |||
| 2899 | var scalar_i: usize = 0; | ||
| 2900 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | ||
| 2901 | const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2902 | extra_index += 1; | ||
| 2903 | const body_len = sema.code.extra[extra_index]; | ||
| 2904 | extra_index += 1; | ||
| 2905 | const body = sema.code.extra[extra_index..][0..body_len]; | ||
| 2906 | extra_index += body_len; | ||
| 2907 | |||
| 2908 | case_block.instructions.shrinkRetainingCapacity(0); | ||
| 2909 | // We validate these above; these two calls are guaranteed to succeed. | ||
| 2910 | const item = sema.resolveInst(item_ref) catch unreachable; | ||
| 2911 | const item_val = sema.resolveConstValue(&case_block, .unneeded, item) catch unreachable; | ||
| 2912 | |||
| 2913 | _ = try sema.analyzeBody(&case_block, body); | ||
| 2914 | |||
| 2915 | cases[scalar_i] = .{ | ||
| 2916 | .item = item_val, | ||
| 2917 | .body = .{ .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items) }, | ||
| 2918 | }; | ||
| 2919 | } | ||
| 2920 | |||
| 2921 | var first_else_body: Body = undefined; | ||
| 2922 | var prev_condbr: ?*Inst.CondBr = null; | ||
| 2923 | |||
| 2924 | var multi_i: usize = 0; | ||
| 2925 | while (multi_i < multi_cases_len) : (multi_i += 1) { | ||
| 2926 | const items_len = sema.code.extra[extra_index]; | ||
| 2927 | extra_index += 1; | ||
| 2928 | const ranges_len = sema.code.extra[extra_index]; | ||
| 2929 | extra_index += 1; | ||
| 2930 | const body_len = sema.code.extra[extra_index]; | ||
| 2931 | extra_index += 1; | ||
| 2932 | const items = sema.code.refSlice(extra_index, items_len); | ||
| 2933 | extra_index += items_len; | ||
| 2934 | |||
| 2935 | case_block.instructions.shrinkRetainingCapacity(0); | ||
| 2936 | |||
| 2937 | var any_ok: ?*Inst = null; | ||
| 2938 | const bool_ty = comptime Type.initTag(.bool); | ||
| 2939 | |||
| 2940 | for (items) |item_ref| { | ||
| 2941 | const item = try sema.resolveInst(item_ref); | ||
| 2942 | _ = try sema.resolveConstValue(&child_block, item.src, item); | ||
| 2943 | |||
| 2944 | const cmp_ok = try case_block.addBinOp(item.src, bool_ty, .cmp_eq, operand, item); | ||
| 2945 | if (any_ok) |some| { | ||
| 2946 | any_ok = try case_block.addBinOp(item.src, bool_ty, .bool_or, some, cmp_ok); | ||
| 2947 | } else { | ||
| 2948 | any_ok = cmp_ok; | ||
| 2949 | } | ||
| 2950 | } | ||
| 2951 | |||
| 2952 | var range_i: usize = 0; | ||
| 2953 | while (range_i < ranges_len) : (range_i += 1) { | ||
| 2954 | const first_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2955 | extra_index += 1; | ||
| 2956 | const last_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | ||
| 2957 | extra_index += 1; | ||
| 2958 | |||
| 2959 | const item_first = try sema.resolveInst(first_ref); | ||
| 2960 | const item_last = try sema.resolveInst(last_ref); | ||
| 2961 | |||
| 2962 | _ = try sema.resolveConstValue(&child_block, item_first.src, item_first); | ||
| 2963 | _ = try sema.resolveConstValue(&child_block, item_last.src, item_last); | ||
| 2964 | |||
| 2965 | const range_src = item_first.src; | ||
| 2966 | |||
| 2967 | // operand >= first and operand <= last | ||
| 2968 | const range_first_ok = try case_block.addBinOp( | ||
| 2969 | item_first.src, | ||
| 2970 | bool_ty, | ||
| 2971 | .cmp_gte, | ||
| 2972 | operand, | ||
| 2973 | item_first, | ||
| 2974 | ); | ||
| 2975 | const range_last_ok = try case_block.addBinOp( | ||
| 2976 | item_last.src, | ||
| 2977 | bool_ty, | ||
| 2978 | .cmp_lte, | ||
| 2979 | operand, | ||
| 2980 | item_last, | ||
| 2981 | ); | ||
| 2982 | const range_ok = try case_block.addBinOp( | ||
| 2983 | range_src, | ||
| 2984 | bool_ty, | ||
| 2985 | .bool_and, | ||
| 2986 | range_first_ok, | ||
| 2987 | range_last_ok, | ||
| 2988 | ); | ||
| 2989 | if (any_ok) |some| { | ||
| 2990 | any_ok = try case_block.addBinOp(range_src, bool_ty, .bool_or, some, range_ok); | ||
| 2991 | } else { | ||
| 2992 | any_ok = range_ok; | ||
| 2993 | } | ||
| 2994 | } | ||
| 2995 | |||
| 2996 | const new_condbr = try sema.arena.create(Inst.CondBr); | ||
| 2997 | new_condbr.* = .{ | ||
| 2998 | .base = .{ | ||
| 2999 | .tag = .condbr, | ||
| 3000 | .ty = Type.initTag(.noreturn), | ||
| 3001 | .src = src, | ||
| 3002 | }, | ||
| 3003 | .condition = any_ok.?, | ||
| 3004 | .then_body = undefined, | ||
| 3005 | .else_body = undefined, | ||
| 3006 | }; | ||
| 3007 | try case_block.instructions.append(gpa, &new_condbr.base); | ||
| 3008 | |||
| 3009 | const cond_body: Body = .{ | ||
| 3010 | .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items), | ||
| 3011 | }; | ||
| 3012 | |||
| 3013 | case_block.instructions.shrinkRetainingCapacity(0); | ||
| 3014 | const body = sema.code.extra[extra_index..][0..body_len]; | ||
| 3015 | extra_index += body_len; | ||
| 3016 | _ = try sema.analyzeBody(&case_block, body); | ||
| 3017 | new_condbr.then_body = .{ | ||
| 3018 | .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items), | ||
| 3019 | }; | ||
| 3020 | if (prev_condbr) |condbr| { | ||
| 3021 | condbr.else_body = cond_body; | ||
| 3022 | } else { | ||
| 3023 | first_else_body = cond_body; | ||
| 3024 | } | ||
| 3025 | prev_condbr = new_condbr; | ||
| 3026 | } | ||
| 3027 | |||
| 3028 | const final_else_body: Body = blk: { | ||
| 3029 | if (special.body.len != 0) { | ||
| 3030 | case_block.instructions.shrinkRetainingCapacity(0); | ||
| 3031 | _ = try sema.analyzeBody(&case_block, special.body); | ||
| 3032 | const else_body: Body = .{ | ||
| 3033 | .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items), | ||
| 3034 | }; | ||
| 3035 | if (prev_condbr) |condbr| { | ||
| 3036 | condbr.else_body = else_body; | ||
| 3037 | break :blk first_else_body; | ||
| 3038 | } else { | ||
| 3039 | break :blk else_body; | ||
| 3040 | } | ||
| 3041 | } else { | ||
| 3042 | break :blk .{ .instructions = &.{} }; | ||
| 3043 | } | ||
| 3044 | }; | ||
| 3045 | |||
| 3046 | _ = try child_block.addSwitchBr(src, operand, cases, final_else_body); | ||
| 3047 | return sema.analyzeBlockBody(block, src, &child_block, merges); | ||
| 3048 | } | ||
| 3049 | |||
| 3050 | fn resolveSwitchItemVal( | ||
| 3051 | sema: *Sema, | ||
| 3052 | block: *Scope.Block, | ||
| 3053 | item_ref: zir.Inst.Ref, | ||
| 3054 | switch_node_offset: i32, | ||
| 3055 | switch_prong_src: AstGen.SwitchProngSrc, | ||
| 3056 | range_expand: AstGen.SwitchProngSrc.RangeExpand, | ||
| 3057 | ) InnerError!Value { | ||
| 3058 | const item = try sema.resolveInst(item_ref); | ||
| 3059 | // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc | ||
| 3060 | // because we only have the switch AST node. Only if we know for sure we need to report | ||
| 3061 | // a compile error do we resolve the full source locations. | ||
| 3062 | if (item.value()) |val| { | ||
| 3063 | if (val.isUndef()) { | ||
| 3064 | const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand); | ||
| 3065 | return sema.failWithUseOfUndef(block, src); | ||
| 3066 | } | ||
| 3067 | return val; | ||
| 3068 | } | ||
| 3069 | const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand); | ||
| 3070 | return sema.failWithNeededComptime(block, src); | ||
| 3071 | } | ||
| 3072 | |||
| 3073 | fn validateSwitchRange( | ||
| 3074 | sema: *Sema, | ||
| 3075 | block: *Scope.Block, | ||
| 3076 | range_set: *RangeSet, | ||
| 3077 | first_ref: zir.Inst.Ref, | ||
| 3078 | last_ref: zir.Inst.Ref, | ||
| 3079 | src_node_offset: i32, | ||
| 3080 | switch_prong_src: AstGen.SwitchProngSrc, | ||
| 3081 | ) InnerError!void { | ||
| 3082 | const first_val = try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first); | ||
| 3083 | const last_val = try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last); | ||
| 3084 | const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src); | ||
| 3085 | return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); | ||
| 3086 | } | ||
| 3087 | |||
| 3088 | fn validateSwitchItem( | ||
| 3089 | sema: *Sema, | ||
| 3090 | block: *Scope.Block, | ||
| 3091 | range_set: *RangeSet, | ||
| 3092 | item_ref: zir.Inst.Ref, | ||
| 3093 | src_node_offset: i32, | ||
| 3094 | switch_prong_src: AstGen.SwitchProngSrc, | ||
| 3095 | ) InnerError!void { | ||
| 3096 | const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); | ||
| 3097 | const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src); | ||
| 3098 | return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); | ||
| 3099 | } | ||
| 3100 | |||
| 3101 | fn validateSwitchDupe( | ||
| 3102 | sema: *Sema, | ||
| 3103 | block: *Scope.Block, | ||
| 3104 | maybe_prev_src: ?AstGen.SwitchProngSrc, | ||
| 3105 | switch_prong_src: AstGen.SwitchProngSrc, | ||
| 3106 | src_node_offset: i32, | ||
| 3107 | ) InnerError!void { | ||
| 3108 | const prev_prong_src = maybe_prev_src orelse return; | ||
| 3109 | const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none); | ||
| 3110 | const prev_src = prev_prong_src.resolve(block.src_decl, src_node_offset, .none); | ||
| 3111 | const msg = msg: { | ||
| 3112 | const msg = try sema.mod.errMsg( | ||
| 3113 | &block.base, | ||
| 3114 | src, | ||
| 3115 | "duplicate switch value", | ||
| 3116 | .{}, | ||
| 3117 | ); | ||
| 3118 | errdefer msg.destroy(sema.gpa); | ||
| 3119 | try sema.mod.errNote( | ||
| 3120 | &block.base, | ||
| 3121 | prev_src, | ||
| 3122 | msg, | ||
| 3123 | "previous value here", | ||
| 3124 | .{}, | ||
| 3125 | ); | ||
| 3126 | break :msg msg; | ||
| 3127 | }; | ||
| 3128 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | ||
| 3129 | } | ||
| 3130 | |||
| 3131 | fn validateSwitchItemBool( | ||
| 3132 | sema: *Sema, | ||
| 3133 | block: *Scope.Block, | ||
| 3134 | true_count: *u8, | ||
| 3135 | false_count: *u8, | ||
| 3136 | item_ref: zir.Inst.Ref, | ||
| 3137 | src_node_offset: i32, | ||
| 3138 | switch_prong_src: AstGen.SwitchProngSrc, | ||
| 3139 | ) InnerError!void { | ||
| 3140 | const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); | ||
| 3141 | if (item_val.toBool()) { | ||
| 3142 | true_count.* += 1; | ||
| 3143 | } else { | ||
| 3144 | false_count.* += 1; | ||
| 3145 | } | ||
| 3146 | if (true_count.* + false_count.* > 2) { | ||
| 3147 | const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none); | ||
| 3148 | return sema.mod.fail(&block.base, src, "duplicate switch value", .{}); | ||
| 3149 | } | ||
| 3150 | } | ||
| 3151 | |||
| 3152 | const ValueSrcMap = std.HashMap(Value, AstGen.SwitchProngSrc, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage); | ||
| 3153 | |||
| 3154 | fn validateSwitchItemSparse( | ||
| 3155 | sema: *Sema, | ||
| 3156 | block: *Scope.Block, | ||
| 3157 | seen_values: *ValueSrcMap, | ||
| 3158 | item_ref: zir.Inst.Ref, | ||
| 3159 | src_node_offset: i32, | ||
| 3160 | switch_prong_src: AstGen.SwitchProngSrc, | ||
| 3161 | ) InnerError!void { | ||
| 3162 | const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); | ||
| 3163 | const entry = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return; | ||
| 3164 | return sema.validateSwitchDupe(block, entry.value, switch_prong_src, src_node_offset); | ||
| 3165 | } | ||
| 3166 | |||
| 3167 | fn validateSwitchNoRange( | ||
| 3168 | sema: *Sema, | ||
| 3169 | block: *Scope.Block, | ||
| 3170 | ranges_len: u32, | ||
| 3171 | operand_ty: Type, | ||
| 3172 | src_node_offset: i32, | ||
| 3173 | ) InnerError!void { | ||
| 3174 | if (ranges_len == 0) | ||
| 3175 | return; | ||
| 3176 | |||
| 3177 | const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset }; | ||
| 3178 | const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset }; | ||
| 3179 | |||
| 3180 | const msg = msg: { | ||
| 3181 | const msg = try sema.mod.errMsg( | ||
| 3182 | &block.base, | ||
| 3183 | operand_src, | ||
| 3184 | "ranges not allowed when switching on type '{}'", | ||
| 3185 | .{operand_ty}, | ||
| 3186 | ); | ||
| 3187 | errdefer msg.destroy(sema.gpa); | ||
| 3188 | try sema.mod.errNote( | ||
| 3189 | &block.base, | ||
| 3190 | range_src, | ||
| 3191 | msg, | ||
| 3192 | "range here", | ||
| 3193 | .{}, | ||
| 3194 | ); | ||
| 3195 | break :msg msg; | ||
| 3196 | }; | ||
| 3197 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | ||
| 3198 | } | ||
| 3199 | |||
| 3200 | fn zirImport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3201 | const tracy = trace(@src()); | ||
| 3202 | defer tracy.end(); | ||
| 3203 | |||
| 3204 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3205 | const src = inst_data.src(); | ||
| 3206 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 3207 | const operand = try sema.resolveConstString(block, operand_src, inst_data.operand); | ||
| 3208 | |||
| 3209 | const file_scope = sema.analyzeImport(block, src, operand) catch |err| switch (err) { | ||
| 3210 | error.ImportOutsidePkgPath => { | ||
| 3211 | return sema.mod.fail(&block.base, src, "import of file outside package path: '{s}'", .{operand}); | ||
| 3212 | }, | ||
| 3213 | error.FileNotFound => { | ||
| 3214 | return sema.mod.fail(&block.base, src, "unable to find '{s}'", .{operand}); | ||
| 3215 | }, | ||
| 3216 | else => { | ||
| 3217 | // TODO: make sure this gets retried and not cached | ||
| 3218 | return sema.mod.fail(&block.base, src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); | ||
| 3219 | }, | ||
| 3220 | }; | ||
| 3221 | return sema.mod.constType(sema.arena, src, file_scope.root_container.ty); | ||
| 3222 | } | ||
| 3223 | |||
| 3224 | fn zirShl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3225 | const tracy = trace(@src()); | ||
| 3226 | defer tracy.end(); | ||
| 3227 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirShl", .{}); | ||
| 3228 | } | ||
| 3229 | |||
| 3230 | fn zirShr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3231 | const tracy = trace(@src()); | ||
| 3232 | defer tracy.end(); | ||
| 3233 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirShr", .{}); | ||
| 3234 | } | ||
| 3235 | |||
| 3236 | fn zirBitwise( | ||
| 3237 | sema: *Sema, | ||
| 3238 | block: *Scope.Block, | ||
| 3239 | inst: zir.Inst.Index, | ||
| 3240 | ir_tag: ir.Inst.Tag, | ||
| 3241 | ) InnerError!*Inst { | ||
| 3242 | const tracy = trace(@src()); | ||
| 3243 | defer tracy.end(); | ||
| 3244 | |||
| 3245 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 3246 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; | ||
| 3247 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; | ||
| 3248 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | ||
| 3249 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 3250 | const lhs = try sema.resolveInst(extra.lhs); | ||
| 3251 | const rhs = try sema.resolveInst(extra.rhs); | ||
| 3252 | |||
| 3253 | const instructions = &[_]*Inst{ lhs, rhs }; | ||
| 3254 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions); | ||
| 3255 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); | ||
| 3256 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); | ||
| 3257 | |||
| 3258 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) | ||
| 3259 | resolved_type.elemType() | ||
| 3260 | else | ||
| 3261 | resolved_type; | ||
| 3262 | |||
| 3263 | const scalar_tag = scalar_type.zigTypeTag(); | ||
| 3264 | |||
| 3265 | if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { | ||
| 3266 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 3267 | return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{ | ||
| 3268 | lhs.ty.arrayLen(), | ||
| 3269 | rhs.ty.arrayLen(), | ||
| 3270 | }); | ||
| 3271 | } | ||
| 3272 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBitwise", .{}); | ||
| 3273 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { | ||
| 3274 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ | ||
| 3275 | lhs.ty, | ||
| 3276 | rhs.ty, | ||
| 3277 | }); | ||
| 3278 | } | ||
| 3279 | |||
| 3280 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; | ||
| 3281 | |||
| 3282 | if (!is_int) { | ||
| 3283 | return sema.mod.fail(&block.base, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); | ||
| 3284 | } | ||
| 3285 | |||
| 3286 | if (casted_lhs.value()) |lhs_val| { | ||
| 3287 | if (casted_rhs.value()) |rhs_val| { | ||
| 3288 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | ||
| 3289 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 3290 | .ty = resolved_type, | ||
| 3291 | .val = Value.initTag(.undef), | ||
| 3292 | }); | ||
| 3293 | } | ||
| 3294 | return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{}); | ||
| 3295 | } | ||
| 3296 | } | ||
| 3297 | |||
| 3298 | try sema.requireRuntimeBlock(block, src); | ||
| 3299 | return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs); | ||
| 3300 | } | ||
| 3301 | |||
| 3302 | fn zirBitNot(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3303 | const tracy = trace(@src()); | ||
| 3304 | defer tracy.end(); | ||
| 3305 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirBitNot", .{}); | ||
| 3306 | } | ||
| 3307 | |||
| 3308 | fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3309 | const tracy = trace(@src()); | ||
| 3310 | defer tracy.end(); | ||
| 3311 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayCat", .{}); | ||
| 3312 | } | ||
| 3313 | |||
| 3314 | fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3315 | const tracy = trace(@src()); | ||
| 3316 | defer tracy.end(); | ||
| 3317 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayMul", .{}); | ||
| 3318 | } | ||
| 3319 | |||
| 3320 | fn zirNegate( | ||
| 3321 | sema: *Sema, | ||
| 3322 | block: *Scope.Block, | ||
| 3323 | inst: zir.Inst.Index, | ||
| 3324 | tag_override: zir.Inst.Tag, | ||
| 3325 | ) InnerError!*Inst { | ||
| 3326 | const tracy = trace(@src()); | ||
| 3327 | defer tracy.end(); | ||
| 3328 | |||
| 3329 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3330 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; | ||
| 3331 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; | ||
| 3332 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | ||
| 3333 | const lhs = try sema.resolveInst(.zero); | ||
| 3334 | const rhs = try sema.resolveInst(inst_data.operand); | ||
| 3335 | |||
| 3336 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); | ||
| 3337 | } | ||
| 3338 | |||
| 3339 | fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3340 | const tracy = trace(@src()); | ||
| 3341 | defer tracy.end(); | ||
| 3342 | |||
| 3343 | const tag_override = block.sema.code.instructions.items(.tag)[inst]; | ||
| 3344 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 3345 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; | ||
| 3346 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; | ||
| 3347 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | ||
| 3348 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 3349 | const lhs = try sema.resolveInst(extra.lhs); | ||
| 3350 | const rhs = try sema.resolveInst(extra.rhs); | ||
| 3351 | |||
| 3352 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); | ||
| 3353 | } | ||
| 3354 | |||
| 3355 | fn analyzeArithmetic( | ||
| 3356 | sema: *Sema, | ||
| 3357 | block: *Scope.Block, | ||
| 3358 | zir_tag: zir.Inst.Tag, | ||
| 3359 | lhs: *Inst, | ||
| 3360 | rhs: *Inst, | ||
| 3361 | src: LazySrcLoc, | ||
| 3362 | lhs_src: LazySrcLoc, | ||
| 3363 | rhs_src: LazySrcLoc, | ||
| 3364 | ) InnerError!*Inst { | ||
| 3365 | const instructions = &[_]*Inst{ lhs, rhs }; | ||
| 3366 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions); | ||
| 3367 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); | ||
| 3368 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); | ||
| 3369 | |||
| 3370 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) | ||
| 3371 | resolved_type.elemType() | ||
| 3372 | else | ||
| 3373 | resolved_type; | ||
| 3374 | |||
| 3375 | const scalar_tag = scalar_type.zigTypeTag(); | ||
| 3376 | |||
| 3377 | if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { | ||
| 3378 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 3379 | return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{ | ||
| 3380 | lhs.ty.arrayLen(), | ||
| 3381 | rhs.ty.arrayLen(), | ||
| 3382 | }); | ||
| 3383 | } | ||
| 3384 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBinOp", .{}); | ||
| 3385 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { | ||
| 3386 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ | ||
| 3387 | lhs.ty, | ||
| 3388 | rhs.ty, | ||
| 3389 | }); | ||
| 3390 | } | ||
| 3391 | |||
| 3392 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; | ||
| 3393 | const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; | ||
| 3394 | |||
| 3395 | if (!is_int and !(is_float and floatOpAllowed(zir_tag))) { | ||
| 3396 | return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); | ||
| 3397 | } | ||
| 3398 | |||
| 3399 | if (casted_lhs.value()) |lhs_val| { | ||
| 3400 | if (casted_rhs.value()) |rhs_val| { | ||
| 3401 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | ||
| 3402 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 3403 | .ty = resolved_type, | ||
| 3404 | .val = Value.initTag(.undef), | ||
| 3405 | }); | ||
| 3406 | } | ||
| 3407 | // incase rhs is 0, simply return lhs without doing any calculations | ||
| 3408 | // TODO Once division is implemented we should throw an error when dividing by 0. | ||
| 3409 | if (rhs_val.compareWithZero(.eq)) { | ||
| 3410 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 3411 | .ty = scalar_type, | ||
| 3412 | .val = lhs_val, | ||
| 3413 | }); | ||
| 3414 | } | ||
| 3415 | |||
| 3416 | const value = switch (zir_tag) { | ||
| 3417 | .add => blk: { | ||
| 3418 | const val = if (is_int) | ||
| 3419 | try Module.intAdd(sema.arena, lhs_val, rhs_val) | ||
| 3420 | else | ||
| 3421 | try Module.floatAdd(sema.arena, scalar_type, src, lhs_val, rhs_val); | ||
| 3422 | break :blk val; | ||
| 3423 | }, | ||
| 3424 | .sub => blk: { | ||
| 3425 | const val = if (is_int) | ||
| 3426 | try Module.intSub(sema.arena, lhs_val, rhs_val) | ||
| 3427 | else | ||
| 3428 | try Module.floatSub(sema.arena, scalar_type, src, lhs_val, rhs_val); | ||
| 3429 | break :blk val; | ||
| 3430 | }, | ||
| 3431 | else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tag)}), | ||
| 3432 | }; | ||
| 3433 | |||
| 3434 | log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value }); | ||
| 3435 | |||
| 3436 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 3437 | .ty = scalar_type, | ||
| 3438 | .val = value, | ||
| 3439 | }); | ||
| 3440 | } | ||
| 3441 | } | ||
| 3442 | |||
| 3443 | try sema.requireRuntimeBlock(block, src); | ||
| 3444 | const ir_tag: Inst.Tag = switch (zir_tag) { | ||
| 3445 | .add => .add, | ||
| 3446 | .addwrap => .addwrap, | ||
| 3447 | .sub => .sub, | ||
| 3448 | .subwrap => .subwrap, | ||
| 3449 | .mul => .mul, | ||
| 3450 | .mulwrap => .mulwrap, | ||
| 3451 | else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tag)}), | ||
| 3452 | }; | ||
| 3453 | |||
| 3454 | return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs); | ||
| 3455 | } | ||
| 3456 | |||
| 3457 | fn zirLoad(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3458 | const tracy = trace(@src()); | ||
| 3459 | defer tracy.end(); | ||
| 3460 | |||
| 3461 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3462 | const src = inst_data.src(); | ||
| 3463 | const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node }; | ||
| 3464 | const ptr = try sema.resolveInst(inst_data.operand); | ||
| 3465 | return sema.analyzeLoad(block, src, ptr, ptr_src); | ||
| 3466 | } | ||
| 3467 | |||
| 3468 | fn zirAsm( | ||
| 3469 | sema: *Sema, | ||
| 3470 | block: *Scope.Block, | ||
| 3471 | inst: zir.Inst.Index, | ||
| 3472 | is_volatile: bool, | ||
| 3473 | ) InnerError!*Inst { | ||
| 3474 | const tracy = trace(@src()); | ||
| 3475 | defer tracy.end(); | ||
| 3476 | |||
| 3477 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 3478 | const src = inst_data.src(); | ||
| 3479 | const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = inst_data.src_node }; | ||
| 3480 | const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = inst_data.src_node }; | ||
| 3481 | const extra = sema.code.extraData(zir.Inst.Asm, inst_data.payload_index); | ||
| 3482 | const return_type = try sema.resolveType(block, ret_ty_src, extra.data.return_type); | ||
| 3483 | const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source); | ||
| 3484 | |||
| 3485 | var extra_i = extra.end; | ||
| 3486 | const Output = struct { name: []const u8, inst: *Inst }; | ||
| 3487 | const output: ?Output = if (extra.data.output != .none) blk: { | ||
| 3488 | const name = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | ||
| 3489 | extra_i += 1; | ||
| 3490 | break :blk Output{ | ||
| 3491 | .name = name, | ||
| 3492 | .inst = try sema.resolveInst(extra.data.output), | ||
| 3493 | }; | ||
| 3494 | } else null; | ||
| 3495 | |||
| 3496 | const args = try sema.arena.alloc(*Inst, extra.data.args_len); | ||
| 3497 | const inputs = try sema.arena.alloc([]const u8, extra.data.args_len); | ||
| 3498 | const clobbers = try sema.arena.alloc([]const u8, extra.data.clobbers_len); | ||
| 3499 | |||
| 3500 | for (args) |*arg| { | ||
| 3501 | arg.* = try sema.resolveInst(@intToEnum(zir.Inst.Ref, sema.code.extra[extra_i])); | ||
| 3502 | extra_i += 1; | ||
| 3503 | } | ||
| 3504 | for (inputs) |*name| { | ||
| 3505 | name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | ||
| 3506 | extra_i += 1; | ||
| 3507 | } | ||
| 3508 | for (clobbers) |*name| { | ||
| 3509 | name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | ||
| 3510 | extra_i += 1; | ||
| 3511 | } | ||
| 3512 | |||
| 3513 | try sema.requireRuntimeBlock(block, src); | ||
| 3514 | const asm_tzir = try sema.arena.create(Inst.Assembly); | ||
| 3515 | asm_tzir.* = .{ | ||
| 3516 | .base = .{ | ||
| 3517 | .tag = .assembly, | ||
| 3518 | .ty = return_type, | ||
| 3519 | .src = src, | ||
| 3520 | }, | ||
| 3521 | .asm_source = asm_source, | ||
| 3522 | .is_volatile = is_volatile, | ||
| 3523 | .output = if (output) |o| o.inst else null, | ||
| 3524 | .output_name = if (output) |o| o.name else null, | ||
| 3525 | .inputs = inputs, | ||
| 3526 | .clobbers = clobbers, | ||
| 3527 | .args = args, | ||
| 3528 | }; | ||
| 3529 | try block.instructions.append(sema.gpa, &asm_tzir.base); | ||
| 3530 | return &asm_tzir.base; | ||
| 3531 | } | ||
| 3532 | |||
| 3533 | fn zirCmp( | ||
| 3534 | sema: *Sema, | ||
| 3535 | block: *Scope.Block, | ||
| 3536 | inst: zir.Inst.Index, | ||
| 3537 | op: std.math.CompareOperator, | ||
| 3538 | ) InnerError!*Inst { | ||
| 3539 | const tracy = trace(@src()); | ||
| 3540 | defer tracy.end(); | ||
| 3541 | |||
| 3542 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 3543 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 3544 | const src: LazySrcLoc = inst_data.src(); | ||
| 3545 | const lhs = try sema.resolveInst(extra.lhs); | ||
| 3546 | const rhs = try sema.resolveInst(extra.rhs); | ||
| 3547 | |||
| 3548 | const is_equality_cmp = switch (op) { | ||
| 3549 | .eq, .neq => true, | ||
| 3550 | else => false, | ||
| 3551 | }; | ||
| 3552 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | ||
| 3553 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | ||
| 3554 | if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) { | ||
| 3555 | // null == null, null != null | ||
| 3556 | return sema.mod.constBool(sema.arena, src, op == .eq); | ||
| 3557 | } else if (is_equality_cmp and | ||
| 3558 | ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or | ||
| 3559 | rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) | ||
| 3560 | { | ||
| 3561 | // comparing null with optionals | ||
| 3562 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; | ||
| 3563 | return sema.analyzeIsNull(block, src, opt_operand, op == .neq); | ||
| 3564 | } else if (is_equality_cmp and | ||
| 3565 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) | ||
| 3566 | { | ||
| 3567 | return sema.mod.fail(&block.base, src, "TODO implement C pointer cmp", .{}); | ||
| 3568 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { | ||
| 3569 | const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty; | ||
| 3570 | return sema.mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type}); | ||
| 3571 | } else if (is_equality_cmp and | ||
| 3572 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | ||
| 3573 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | ||
| 3574 | { | ||
| 3575 | return sema.mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | ||
| 3576 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { | ||
| 3577 | if (!is_equality_cmp) { | ||
| 3578 | return sema.mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)}); | ||
| 3579 | } | ||
| 3580 | if (rhs.value()) |rval| { | ||
| 3581 | if (lhs.value()) |lval| { | ||
| 3582 | // TODO optimisation oppurtunity: evaluate if std.mem.eql is faster with the names, or calling to Module.getErrorValue to get the values and then compare them is faster | ||
| 3583 | return sema.mod.constBool(sema.arena, src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq)); | ||
| 3584 | } | ||
| 3585 | } | ||
| 3586 | try sema.requireRuntimeBlock(block, src); | ||
| 3587 | return block.addBinOp(src, Type.initTag(.bool), if (op == .eq) .cmp_eq else .cmp_neq, lhs, rhs); | ||
| 3588 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { | ||
| 3589 | // This operation allows any combination of integer and float types, regardless of the | ||
| 3590 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for | ||
| 3591 | // numeric types. | ||
| 3592 | return sema.cmpNumeric(block, src, lhs, rhs, op); | ||
| 3593 | } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { | ||
| 3594 | if (!is_equality_cmp) { | ||
| 3595 | return sema.mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)}); | ||
| 3596 | } | ||
| 3597 | return sema.mod.constBool(sema.arena, src, lhs.value().?.eql(rhs.value().?) == (op == .eq)); | ||
| 3598 | } | ||
| 3599 | return sema.mod.fail(&block.base, src, "TODO implement more cmp analysis", .{}); | ||
| 3600 | } | ||
| 3601 | |||
| 3602 | fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3603 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3604 | const src = inst_data.src(); | ||
| 3605 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 3606 | return sema.mod.constType(sema.arena, src, operand.ty); | ||
| 3607 | } | ||
| 3608 | |||
| 3609 | fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3610 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3611 | const src = inst_data.src(); | ||
| 3612 | const operand_ptr = try sema.resolveInst(inst_data.operand); | ||
| 3613 | const elem_ty = operand_ptr.ty.elemType(); | ||
| 3614 | return sema.mod.constType(sema.arena, src, elem_ty); | ||
| 3615 | } | ||
| 3616 | |||
| 3617 | fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3618 | const tracy = trace(@src()); | ||
| 3619 | defer tracy.end(); | ||
| 3620 | |||
| 3621 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 3622 | const src = inst_data.src(); | ||
| 3623 | const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); | ||
| 3624 | const args = sema.code.refSlice(extra.end, extra.data.operands_len); | ||
| 3625 | |||
| 3626 | const inst_list = try sema.gpa.alloc(*ir.Inst, extra.data.operands_len); | ||
| 3627 | defer sema.gpa.free(inst_list); | ||
| 3628 | |||
| 3629 | for (args) |arg_ref, i| { | ||
| 3630 | inst_list[i] = try sema.resolveInst(arg_ref); | ||
| 3631 | } | ||
| 3632 | |||
| 3633 | const result_type = try sema.resolvePeerTypes(block, src, inst_list); | ||
| 3634 | return sema.mod.constType(sema.arena, src, result_type); | ||
| 3635 | } | ||
| 3636 | |||
| 3637 | fn zirBoolNot(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3638 | const tracy = trace(@src()); | ||
| 3639 | defer tracy.end(); | ||
| 3640 | |||
| 3641 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3642 | const src = inst_data.src(); | ||
| 3643 | const uncasted_operand = try sema.resolveInst(inst_data.operand); | ||
| 3644 | |||
| 3645 | const bool_type = Type.initTag(.bool); | ||
| 3646 | const operand = try sema.coerce(block, bool_type, uncasted_operand, uncasted_operand.src); | ||
| 3647 | if (try sema.resolveDefinedValue(block, src, operand)) |val| { | ||
| 3648 | return sema.mod.constBool(sema.arena, src, !val.toBool()); | ||
| 3649 | } | ||
| 3650 | try sema.requireRuntimeBlock(block, src); | ||
| 3651 | return block.addUnOp(src, bool_type, .not, operand); | ||
| 3652 | } | ||
| 3653 | |||
| 3654 | fn zirBoolOp( | ||
| 3655 | sema: *Sema, | ||
| 3656 | block: *Scope.Block, | ||
| 3657 | inst: zir.Inst.Index, | ||
| 3658 | comptime is_bool_or: bool, | ||
| 3659 | ) InnerError!*Inst { | ||
| 3660 | const tracy = trace(@src()); | ||
| 3661 | defer tracy.end(); | ||
| 3662 | |||
| 3663 | const src: LazySrcLoc = .unneeded; | ||
| 3664 | const bool_type = Type.initTag(.bool); | ||
| 3665 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 3666 | const uncasted_lhs = try sema.resolveInst(bin_inst.lhs); | ||
| 3667 | const lhs = try sema.coerce(block, bool_type, uncasted_lhs, uncasted_lhs.src); | ||
| 3668 | const uncasted_rhs = try sema.resolveInst(bin_inst.rhs); | ||
| 3669 | const rhs = try sema.coerce(block, bool_type, uncasted_rhs, uncasted_rhs.src); | ||
| 3670 | |||
| 3671 | if (lhs.value()) |lhs_val| { | ||
| 3672 | if (rhs.value()) |rhs_val| { | ||
| 3673 | if (is_bool_or) { | ||
| 3674 | return sema.mod.constBool(sema.arena, src, lhs_val.toBool() or rhs_val.toBool()); | ||
| 3675 | } else { | ||
| 3676 | return sema.mod.constBool(sema.arena, src, lhs_val.toBool() and rhs_val.toBool()); | ||
| 3677 | } | ||
| 3678 | } | ||
| 3679 | } | ||
| 3680 | try sema.requireRuntimeBlock(block, src); | ||
| 3681 | const tag: ir.Inst.Tag = if (is_bool_or) .bool_or else .bool_and; | ||
| 3682 | return block.addBinOp(src, bool_type, tag, lhs, rhs); | ||
| 3683 | } | ||
| 3684 | |||
| 3685 | fn zirBoolBr( | ||
| 3686 | sema: *Sema, | ||
| 3687 | parent_block: *Scope.Block, | ||
| 3688 | inst: zir.Inst.Index, | ||
| 3689 | is_bool_or: bool, | ||
| 3690 | ) InnerError!*Inst { | ||
| 3691 | const tracy = trace(@src()); | ||
| 3692 | defer tracy.end(); | ||
| 3693 | |||
| 3694 | const datas = sema.code.instructions.items(.data); | ||
| 3695 | const inst_data = datas[inst].bool_br; | ||
| 3696 | const src: LazySrcLoc = .unneeded; | ||
| 3697 | const lhs = try sema.resolveInst(inst_data.lhs); | ||
| 3698 | const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); | ||
| 3699 | const body = sema.code.extra[extra.end..][0..extra.data.body_len]; | ||
| 3700 | |||
| 3701 | if (try sema.resolveDefinedValue(parent_block, src, lhs)) |lhs_val| { | ||
| 3702 | if (lhs_val.toBool() == is_bool_or) { | ||
| 3703 | return sema.mod.constBool(sema.arena, src, is_bool_or); | ||
| 3704 | } | ||
| 3705 | // comptime-known left-hand side. No need for a block here; the result | ||
| 3706 | // is simply the rhs expression. Here we rely on there only being 1 | ||
| 3707 | // break instruction (`break_inline`). | ||
| 3708 | return sema.resolveBody(parent_block, body); | ||
| 3709 | } | ||
| 3710 | |||
| 3711 | const block_inst = try sema.arena.create(Inst.Block); | ||
| 3712 | block_inst.* = .{ | ||
| 3713 | .base = .{ | ||
| 3714 | .tag = Inst.Block.base_tag, | ||
| 3715 | .ty = Type.initTag(.bool), | ||
| 3716 | .src = src, | ||
| 3717 | }, | ||
| 3718 | .body = undefined, | ||
| 3719 | }; | ||
| 3720 | |||
| 3721 | var child_block = parent_block.makeSubBlock(); | ||
| 3722 | defer child_block.instructions.deinit(sema.gpa); | ||
| 3723 | |||
| 3724 | var then_block = child_block.makeSubBlock(); | ||
| 3725 | defer then_block.instructions.deinit(sema.gpa); | ||
| 3726 | |||
| 3727 | var else_block = child_block.makeSubBlock(); | ||
| 3728 | defer else_block.instructions.deinit(sema.gpa); | ||
| 3729 | |||
| 3730 | const lhs_block = if (is_bool_or) &then_block else &else_block; | ||
| 3731 | const rhs_block = if (is_bool_or) &else_block else &then_block; | ||
| 3732 | |||
| 3733 | const lhs_result = try sema.mod.constInst(sema.arena, src, .{ | ||
| 3734 | .ty = Type.initTag(.bool), | ||
| 3735 | .val = if (is_bool_or) Value.initTag(.bool_true) else Value.initTag(.bool_false), | ||
| 3736 | }); | ||
| 3737 | _ = try lhs_block.addBr(src, block_inst, lhs_result); | ||
| 3738 | |||
| 3739 | const rhs_result = try sema.resolveBody(rhs_block, body); | ||
| 3740 | _ = try rhs_block.addBr(src, block_inst, rhs_result); | ||
| 3741 | |||
| 3742 | const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) }; | ||
| 3743 | const tzir_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, rhs_block.instructions.items) }; | ||
| 3744 | _ = try child_block.addCondBr(src, lhs, tzir_then_body, tzir_else_body); | ||
| 3745 | |||
| 3746 | block_inst.body = .{ | ||
| 3747 | .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items), | ||
| 3748 | }; | ||
| 3749 | try parent_block.instructions.append(sema.gpa, &block_inst.base); | ||
| 3750 | return &block_inst.base; | ||
| 3751 | } | ||
| 3752 | |||
| 3753 | fn zirIsNull( | ||
| 3754 | sema: *Sema, | ||
| 3755 | block: *Scope.Block, | ||
| 3756 | inst: zir.Inst.Index, | ||
| 3757 | invert_logic: bool, | ||
| 3758 | ) InnerError!*Inst { | ||
| 3759 | const tracy = trace(@src()); | ||
| 3760 | defer tracy.end(); | ||
| 3761 | |||
| 3762 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3763 | const src = inst_data.src(); | ||
| 3764 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 3765 | return sema.analyzeIsNull(block, src, operand, invert_logic); | ||
| 3766 | } | ||
| 3767 | |||
| 3768 | fn zirIsNullPtr( | ||
| 3769 | sema: *Sema, | ||
| 3770 | block: *Scope.Block, | ||
| 3771 | inst: zir.Inst.Index, | ||
| 3772 | invert_logic: bool, | ||
| 3773 | ) InnerError!*Inst { | ||
| 3774 | const tracy = trace(@src()); | ||
| 3775 | defer tracy.end(); | ||
| 3776 | |||
| 3777 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3778 | const src = inst_data.src(); | ||
| 3779 | const ptr = try sema.resolveInst(inst_data.operand); | ||
| 3780 | const loaded = try sema.analyzeLoad(block, src, ptr, src); | ||
| 3781 | return sema.analyzeIsNull(block, src, loaded, invert_logic); | ||
| 3782 | } | ||
| 3783 | |||
| 3784 | fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3785 | const tracy = trace(@src()); | ||
| 3786 | defer tracy.end(); | ||
| 3787 | |||
| 3788 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3789 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 3790 | return sema.analyzeIsErr(block, inst_data.src(), operand); | ||
| 3791 | } | ||
| 3792 | |||
| 3793 | fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3794 | const tracy = trace(@src()); | ||
| 3795 | defer tracy.end(); | ||
| 3796 | |||
| 3797 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3798 | const src = inst_data.src(); | ||
| 3799 | const ptr = try sema.resolveInst(inst_data.operand); | ||
| 3800 | const loaded = try sema.analyzeLoad(block, src, ptr, src); | ||
| 3801 | return sema.analyzeIsErr(block, src, loaded); | ||
| 3802 | } | ||
| 3803 | |||
| 3804 | fn zirCondbr( | ||
| 3805 | sema: *Sema, | ||
| 3806 | parent_block: *Scope.Block, | ||
| 3807 | inst: zir.Inst.Index, | ||
| 3808 | ) InnerError!zir.Inst.Index { | ||
| 3809 | const tracy = trace(@src()); | ||
| 3810 | defer tracy.end(); | ||
| 3811 | |||
| 3812 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 3813 | const src = inst_data.src(); | ||
| 3814 | const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; | ||
| 3815 | const extra = sema.code.extraData(zir.Inst.CondBr, inst_data.payload_index); | ||
| 3816 | |||
| 3817 | const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; | ||
| 3818 | const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | ||
| 3819 | |||
| 3820 | const uncasted_cond = try sema.resolveInst(extra.data.condition); | ||
| 3821 | const cond = try sema.coerce(parent_block, Type.initTag(.bool), uncasted_cond, cond_src); | ||
| 3822 | |||
| 3823 | if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| { | ||
| 3824 | const body = if (cond_val.toBool()) then_body else else_body; | ||
| 3825 | _ = try sema.analyzeBody(parent_block, body); | ||
| 3826 | return always_noreturn; | ||
| 3827 | } | ||
| 3828 | |||
| 3829 | var sub_block = parent_block.makeSubBlock(); | ||
| 3830 | defer sub_block.instructions.deinit(sema.gpa); | ||
| 3831 | |||
| 3832 | _ = try sema.analyzeBody(&sub_block, then_body); | ||
| 3833 | const tzir_then_body: ir.Body = .{ | ||
| 3834 | .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items), | ||
| 3835 | }; | ||
| 3836 | |||
| 3837 | sub_block.instructions.shrinkRetainingCapacity(0); | ||
| 3838 | |||
| 3839 | _ = try sema.analyzeBody(&sub_block, else_body); | ||
| 3840 | const tzir_else_body: ir.Body = .{ | ||
| 3841 | .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items), | ||
| 3842 | }; | ||
| 3843 | |||
| 3844 | _ = try parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body); | ||
| 3845 | return always_noreturn; | ||
| 3846 | } | ||
| 3847 | |||
| 3848 | fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { | ||
| 3849 | const tracy = trace(@src()); | ||
| 3850 | defer tracy.end(); | ||
| 3851 | |||
| 3852 | const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable"; | ||
| 3853 | const src = inst_data.src(); | ||
| 3854 | const safety_check = inst_data.safety; | ||
| 3855 | try sema.requireRuntimeBlock(block, src); | ||
| 3856 | // TODO Add compile error for @optimizeFor occurring too late in a scope. | ||
| 3857 | if (safety_check and block.wantSafety()) { | ||
| 3858 | return sema.safetyPanic(block, src, .unreach); | ||
| 3859 | } else { | ||
| 3860 | _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach); | ||
| 3861 | return always_noreturn; | ||
| 3862 | } | ||
| 3863 | } | ||
| 3864 | |||
| 3865 | fn zirRetTok( | ||
| 3866 | sema: *Sema, | ||
| 3867 | block: *Scope.Block, | ||
| 3868 | inst: zir.Inst.Index, | ||
| 3869 | need_coercion: bool, | ||
| 3870 | ) InnerError!zir.Inst.Index { | ||
| 3871 | const tracy = trace(@src()); | ||
| 3872 | defer tracy.end(); | ||
| 3873 | |||
| 3874 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 3875 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 3876 | const src = inst_data.src(); | ||
| 3877 | |||
| 3878 | return sema.analyzeRet(block, operand, src, need_coercion); | ||
| 3879 | } | ||
| 3880 | |||
| 3881 | fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { | ||
| 3882 | const tracy = trace(@src()); | ||
| 3883 | defer tracy.end(); | ||
| 3884 | |||
| 3885 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 3886 | const operand = try sema.resolveInst(inst_data.operand); | ||
| 3887 | const src = inst_data.src(); | ||
| 3888 | |||
| 3889 | return sema.analyzeRet(block, operand, src, false); | ||
| 3890 | } | ||
| 3891 | |||
| 3892 | fn analyzeRet( | ||
| 3893 | sema: *Sema, | ||
| 3894 | block: *Scope.Block, | ||
| 3895 | operand: *Inst, | ||
| 3896 | src: LazySrcLoc, | ||
| 3897 | need_coercion: bool, | ||
| 3898 | ) InnerError!zir.Inst.Index { | ||
| 3899 | if (block.inlining) |inlining| { | ||
| 3900 | // We are inlining a function call; rewrite the `ret` as a `break`. | ||
| 3901 | try inlining.merges.results.append(sema.gpa, operand); | ||
| 3902 | _ = try block.addBr(src, inlining.merges.block_inst, operand); | ||
| 3903 | return always_noreturn; | ||
| 3904 | } | ||
| 3905 | |||
| 3906 | if (need_coercion) { | ||
| 3907 | if (sema.func) |func| { | ||
| 3908 | const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty; | ||
| 3909 | const fn_ret_ty = fn_ty.fnReturnType(); | ||
| 3910 | const casted_operand = try sema.coerce(block, fn_ret_ty, operand, src); | ||
| 3911 | if (fn_ret_ty.zigTypeTag() == .Void) | ||
| 3912 | _ = try block.addNoOp(src, Type.initTag(.noreturn), .retvoid) | ||
| 3913 | else | ||
| 3914 | _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, casted_operand); | ||
| 3915 | return always_noreturn; | ||
| 3916 | } | ||
| 3917 | } | ||
| 3918 | _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, operand); | ||
| 3919 | return always_noreturn; | ||
| 3920 | } | ||
| 3921 | |||
| 3922 | fn floatOpAllowed(tag: zir.Inst.Tag) bool { | ||
| 3923 | // extend this swich as additional operators are implemented | ||
| 3924 | return switch (tag) { | ||
| 3925 | .add, .sub => true, | ||
| 3926 | else => false, | ||
| 3927 | }; | ||
| 3928 | } | ||
| 3929 | |||
| 3930 | fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3931 | const tracy = trace(@src()); | ||
| 3932 | defer tracy.end(); | ||
| 3933 | |||
| 3934 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple; | ||
| 3935 | const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type); | ||
| 3936 | const ty = try sema.mod.ptrType( | ||
| 3937 | sema.arena, | ||
| 3938 | elem_type, | ||
| 3939 | null, | ||
| 3940 | 0, | ||
| 3941 | 0, | ||
| 3942 | 0, | ||
| 3943 | inst_data.is_mutable, | ||
| 3944 | inst_data.is_allowzero, | ||
| 3945 | inst_data.is_volatile, | ||
| 3946 | inst_data.size, | ||
| 3947 | ); | ||
| 3948 | return sema.mod.constType(sema.arena, .unneeded, ty); | ||
| 3949 | } | ||
| 3950 | |||
| 3951 | fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 3952 | const tracy = trace(@src()); | ||
| 3953 | defer tracy.end(); | ||
| 3954 | |||
| 3955 | const src: LazySrcLoc = .unneeded; | ||
| 3956 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; | ||
| 3957 | const extra = sema.code.extraData(zir.Inst.PtrType, inst_data.payload_index); | ||
| 3958 | |||
| 3959 | var extra_i = extra.end; | ||
| 3960 | |||
| 3961 | const sentinel = if (inst_data.flags.has_sentinel) blk: { | ||
| 3962 | const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]); | ||
| 3963 | extra_i += 1; | ||
| 3964 | break :blk (try sema.resolveInstConst(block, .unneeded, ref)).val; | ||
| 3965 | } else null; | ||
| 3966 | |||
| 3967 | const abi_align = if (inst_data.flags.has_align) blk: { | ||
| 3968 | const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]); | ||
| 3969 | extra_i += 1; | ||
| 3970 | break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32); | ||
| 3971 | } else 0; | ||
| 3972 | |||
| 3973 | const bit_start = if (inst_data.flags.has_bit_range) blk: { | ||
| 3974 | const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]); | ||
| 3975 | extra_i += 1; | ||
| 3976 | break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16); | ||
| 3977 | } else 0; | ||
| 3978 | |||
| 3979 | const bit_end = if (inst_data.flags.has_bit_range) blk: { | ||
| 3980 | const ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_i]); | ||
| 3981 | extra_i += 1; | ||
| 3982 | break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16); | ||
| 3983 | } else 0; | ||
| 3984 | |||
| 3985 | if (bit_end != 0 and bit_start >= bit_end * 8) | ||
| 3986 | return sema.mod.fail(&block.base, src, "bit offset starts after end of host integer", .{}); | ||
| 3987 | |||
| 3988 | const elem_type = try sema.resolveType(block, .unneeded, extra.data.elem_type); | ||
| 3989 | |||
| 3990 | const ty = try sema.mod.ptrType( | ||
| 3991 | sema.arena, | ||
| 3992 | elem_type, | ||
| 3993 | sentinel, | ||
| 3994 | abi_align, | ||
| 3995 | bit_start, | ||
| 3996 | bit_end, | ||
| 3997 | inst_data.flags.is_mutable, | ||
| 3998 | inst_data.flags.is_allowzero, | ||
| 3999 | inst_data.flags.is_volatile, | ||
| 4000 | inst_data.size, | ||
| 4001 | ); | ||
| 4002 | return sema.mod.constType(sema.arena, src, ty); | ||
| 4003 | } | ||
| 4004 | |||
| 4005 | fn zirStructInitEmpty(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 4006 | const tracy = trace(@src()); | ||
| 4007 | defer tracy.end(); | ||
| 4008 | |||
| 4009 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 4010 | const src = inst_data.src(); | ||
| 4011 | const struct_type = try sema.resolveType(block, src, inst_data.operand); | ||
| 4012 | |||
| 4013 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 4014 | .ty = struct_type, | ||
| 4015 | .val = Value.initTag(.empty_struct_value), | ||
| 4016 | }); | ||
| 4017 | } | ||
| 4018 | |||
| 4019 | fn requireFunctionBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | ||
| 4020 | if (sema.func == null) { | ||
| 4021 | return sema.mod.fail(&block.base, src, "instruction illegal outside function body", .{}); | ||
| 4022 | } | ||
| 4023 | } | ||
| 4024 | |||
| 4025 | fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | ||
| 4026 | if (block.is_comptime) { | ||
| 4027 | return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{}); | ||
| 4028 | } | ||
| 4029 | try sema.requireFunctionBlock(block, src); | ||
| 4030 | } | ||
| 4031 | |||
| 4032 | fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void { | ||
| 4033 | if (!ty.isValidVarType(false)) { | ||
| 4034 | return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty}); | ||
| 4035 | } | ||
| 4036 | } | ||
| 4037 | |||
| 4038 | pub const PanicId = enum { | ||
| 4039 | unreach, | ||
| 4040 | unwrap_null, | ||
| 4041 | unwrap_errunion, | ||
| 4042 | invalid_error_code, | ||
| 4043 | }; | ||
| 4044 | |||
| 4045 | fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void { | ||
| 4046 | const block_inst = try sema.arena.create(Inst.Block); | ||
| 4047 | block_inst.* = .{ | ||
| 4048 | .base = .{ | ||
| 4049 | .tag = Inst.Block.base_tag, | ||
| 4050 | .ty = Type.initTag(.void), | ||
| 4051 | .src = ok.src, | ||
| 4052 | }, | ||
| 4053 | .body = .{ | ||
| 4054 | .instructions = try sema.arena.alloc(*Inst, 1), // Only need space for the condbr. | ||
| 4055 | }, | ||
| 4056 | }; | ||
| 4057 | |||
| 4058 | const ok_body: ir.Body = .{ | ||
| 4059 | .instructions = try sema.arena.alloc(*Inst, 1), // Only need space for the br_void. | ||
| 4060 | }; | ||
| 4061 | const br_void = try sema.arena.create(Inst.BrVoid); | ||
| 4062 | br_void.* = .{ | ||
| 4063 | .base = .{ | ||
| 4064 | .tag = .br_void, | ||
| 4065 | .ty = Type.initTag(.noreturn), | ||
| 4066 | .src = ok.src, | ||
| 4067 | }, | ||
| 4068 | .block = block_inst, | ||
| 4069 | }; | ||
| 4070 | ok_body.instructions[0] = &br_void.base; | ||
| 4071 | |||
| 4072 | var fail_block: Scope.Block = .{ | ||
| 4073 | .parent = parent_block, | ||
| 4074 | .sema = sema, | ||
| 4075 | .src_decl = parent_block.src_decl, | ||
| 4076 | .instructions = .{}, | ||
| 4077 | .inlining = parent_block.inlining, | ||
| 4078 | .is_comptime = parent_block.is_comptime, | ||
| 4079 | }; | ||
| 4080 | |||
| 4081 | defer fail_block.instructions.deinit(sema.gpa); | ||
| 4082 | |||
| 4083 | _ = try sema.safetyPanic(&fail_block, ok.src, panic_id); | ||
| 4084 | |||
| 4085 | const fail_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, fail_block.instructions.items) }; | ||
| 4086 | |||
| 4087 | const condbr = try sema.arena.create(Inst.CondBr); | ||
| 4088 | condbr.* = .{ | ||
| 4089 | .base = .{ | ||
| 4090 | .tag = .condbr, | ||
| 4091 | .ty = Type.initTag(.noreturn), | ||
| 4092 | .src = ok.src, | ||
| 4093 | }, | ||
| 4094 | .condition = ok, | ||
| 4095 | .then_body = ok_body, | ||
| 4096 | .else_body = fail_body, | ||
| 4097 | }; | ||
| 4098 | block_inst.body.instructions[0] = &condbr.base; | ||
| 4099 | |||
| 4100 | try parent_block.instructions.append(sema.gpa, &block_inst.base); | ||
| 4101 | } | ||
| 4102 | |||
| 4103 | fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Index { | ||
| 4104 | // TODO Once we have a panic function to call, call it here instead of breakpoint. | ||
| 4105 | _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint); | ||
| 4106 | _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach); | ||
| 4107 | return always_noreturn; | ||
| 4108 | } | ||
| 4109 | |||
| 4110 | fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | ||
| 4111 | sema.branch_count += 1; | ||
| 4112 | if (sema.branch_count > sema.branch_quota) { | ||
| 4113 | // TODO show the "called from here" stack | ||
| 4114 | return sema.mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{sema.branch_quota}); | ||
| 4115 | } | ||
| 4116 | } | ||
| 4117 | |||
| 4118 | fn namedFieldPtr( | ||
| 4119 | sema: *Sema, | ||
| 4120 | block: *Scope.Block, | ||
| 4121 | src: LazySrcLoc, | ||
| 4122 | object_ptr: *Inst, | ||
| 4123 | field_name: []const u8, | ||
| 4124 | field_name_src: LazySrcLoc, | ||
| 4125 | ) InnerError!*Inst { | ||
| 4126 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { | ||
| 4127 | .Pointer => object_ptr.ty.elemType(), | ||
| 4128 | else => return sema.mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}), | ||
| 4129 | }; | ||
| 4130 | switch (elem_ty.zigTypeTag()) { | ||
| 4131 | .Array => { | ||
| 4132 | if (mem.eql(u8, field_name, "len")) { | ||
| 4133 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 4134 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | ||
| 4135 | .val = try Value.Tag.ref_val.create( | ||
| 4136 | sema.arena, | ||
| 4137 | try Value.Tag.int_u64.create(sema.arena, elem_ty.arrayLen()), | ||
| 4138 | ), | ||
| 4139 | }); | ||
| 4140 | } else { | ||
| 4141 | return sema.mod.fail( | ||
| 4142 | &block.base, | ||
| 4143 | field_name_src, | ||
| 4144 | "no member named '{s}' in '{}'", | ||
| 4145 | .{ field_name, elem_ty }, | ||
| 4146 | ); | ||
| 4147 | } | ||
| 4148 | }, | ||
| 4149 | .Pointer => { | ||
| 4150 | const ptr_child = elem_ty.elemType(); | ||
| 4151 | switch (ptr_child.zigTypeTag()) { | ||
| 4152 | .Array => { | ||
| 4153 | if (mem.eql(u8, field_name, "len")) { | ||
| 4154 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 4155 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | ||
| 4156 | .val = try Value.Tag.ref_val.create( | ||
| 4157 | sema.arena, | ||
| 4158 | try Value.Tag.int_u64.create(sema.arena, ptr_child.arrayLen()), | ||
| 4159 | ), | ||
| 4160 | }); | ||
| 4161 | } else { | ||
| 4162 | return sema.mod.fail( | ||
| 4163 | &block.base, | ||
| 4164 | field_name_src, | ||
| 4165 | "no member named '{s}' in '{}'", | ||
| 4166 | .{ field_name, elem_ty }, | ||
| 4167 | ); | ||
| 4168 | } | ||
| 4169 | }, | ||
| 4170 | else => {}, | ||
| 4171 | } | ||
| 4172 | }, | ||
| 4173 | .Type => { | ||
| 4174 | _ = try sema.resolveConstValue(block, object_ptr.src, object_ptr); | ||
| 4175 | const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src); | ||
| 4176 | const val = result.value().?; | ||
| 4177 | const child_type = try val.toType(sema.arena); | ||
| 4178 | switch (child_type.zigTypeTag()) { | ||
| 4179 | .ErrorSet => { | ||
| 4180 | // TODO resolve inferred error sets | ||
| 4181 | const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: { | ||
| 4182 | const error_set = payload.data; | ||
| 4183 | // TODO this is O(N). I'm putting off solving this until we solve inferred | ||
| 4184 | // error sets at the same time. | ||
| 4185 | const names = error_set.names_ptr[0..error_set.names_len]; | ||
| 4186 | for (names) |name| { | ||
| 4187 | if (mem.eql(u8, field_name, name)) { | ||
| 4188 | break :blk name; | ||
| 4189 | } | ||
| 4190 | } | ||
| 4191 | return sema.mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{ | ||
| 4192 | field_name, | ||
| 4193 | child_type, | ||
| 4194 | }); | ||
| 4195 | } else (try sema.mod.getErrorValue(field_name)).key; | ||
| 4196 | |||
| 4197 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 4198 | .ty = try sema.mod.simplePtrType(sema.arena, child_type, false, .One), | ||
| 4199 | .val = try Value.Tag.ref_val.create( | ||
| 4200 | sema.arena, | ||
| 4201 | try Value.Tag.@"error".create(sema.arena, .{ | ||
| 4202 | .name = name, | ||
| 4203 | }), | ||
| 4204 | ), | ||
| 4205 | }); | ||
| 4206 | }, | ||
| 4207 | .Struct => { | ||
| 4208 | const container_scope = child_type.getContainerScope(); | ||
| 4209 | if (sema.mod.lookupDeclName(&container_scope.base, field_name)) |decl| { | ||
| 4210 | // TODO if !decl.is_pub and inDifferentFiles() "{} is private" | ||
| 4211 | return sema.analyzeDeclRef(block, src, decl); | ||
| 4212 | } | ||
| 4213 | |||
| 4214 | if (container_scope.file_scope == sema.mod.root_scope) { | ||
| 4215 | return sema.mod.fail(&block.base, src, "root source file has no member called '{s}'", .{field_name}); | ||
| 4216 | } else { | ||
| 4217 | return sema.mod.fail(&block.base, src, "container '{}' has no member called '{s}'", .{ child_type, field_name }); | ||
| 4218 | } | ||
| 4219 | }, | ||
| 4220 | else => return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{child_type}), | ||
| 4221 | } | ||
| 4222 | }, | ||
| 4223 | .Struct => return sema.analyzeStructFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty), | ||
| 4224 | else => {}, | ||
| 4225 | } | ||
| 4226 | return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty}); | ||
| 4227 | } | ||
| 4228 | |||
| 4229 | fn analyzeStructFieldPtr( | ||
| 4230 | sema: *Sema, | ||
| 4231 | block: *Scope.Block, | ||
| 4232 | src: LazySrcLoc, | ||
| 4233 | struct_ptr: *Inst, | ||
| 4234 | field_name: []const u8, | ||
| 4235 | field_name_src: LazySrcLoc, | ||
| 4236 | elem_ty: Type, | ||
| 4237 | ) InnerError!*Inst { | ||
| 4238 | const mod = sema.mod; | ||
| 4239 | const arena = sema.arena; | ||
| 4240 | assert(elem_ty.zigTypeTag() == .Struct); | ||
| 4241 | |||
| 4242 | const struct_obj = elem_ty.castTag(.@"struct").?.data; | ||
| 4243 | |||
| 4244 | const field_index = struct_obj.fields.getIndex(field_name) orelse { | ||
| 4245 | // TODO note: struct S declared here | ||
| 4246 | return mod.fail(&block.base, field_name_src, "no field named '{s}' in struct '{}'", .{ | ||
| 4247 | field_name, elem_ty, | ||
| 4248 | }); | ||
| 4249 | }; | ||
| 4250 | const field = struct_obj.fields.entries.items[field_index].value; | ||
| 4251 | const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One); | ||
| 4252 | // TODO comptime field access | ||
| 4253 | try sema.requireRuntimeBlock(block, src); | ||
| 4254 | return block.addStructFieldPtr(src, ptr_field_ty, struct_ptr, @intCast(u32, field_index)); | ||
| 4255 | } | ||
| 4256 | |||
| 4257 | fn elemPtr( | ||
| 4258 | sema: *Sema, | ||
| 4259 | block: *Scope.Block, | ||
| 4260 | src: LazySrcLoc, | ||
| 4261 | array_ptr: *Inst, | ||
| 4262 | elem_index: *Inst, | ||
| 4263 | elem_index_src: LazySrcLoc, | ||
| 4264 | ) InnerError!*Inst { | ||
| 4265 | const elem_ty = switch (array_ptr.ty.zigTypeTag()) { | ||
| 4266 | .Pointer => array_ptr.ty.elemType(), | ||
| 4267 | else => return sema.mod.fail(&block.base, array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}), | ||
| 4268 | }; | ||
| 4269 | if (!elem_ty.isIndexable()) { | ||
| 4270 | return sema.mod.fail(&block.base, src, "array access of non-array type '{}'", .{elem_ty}); | ||
| 4271 | } | ||
| 4272 | |||
| 4273 | if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) { | ||
| 4274 | // we have to deref the ptr operand to get the actual array pointer | ||
| 4275 | const array_ptr_deref = try sema.analyzeLoad(block, src, array_ptr, array_ptr.src); | ||
| 4276 | if (array_ptr_deref.value()) |array_ptr_val| { | ||
| 4277 | if (elem_index.value()) |index_val| { | ||
| 4278 | // Both array pointer and index are compile-time known. | ||
| 4279 | const index_u64 = index_val.toUnsignedInt(); | ||
| 4280 | // @intCast here because it would have been impossible to construct a value that | ||
| 4281 | // required a larger index. | ||
| 4282 | const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64)); | ||
| 4283 | const pointee_type = elem_ty.elemType().elemType(); | ||
| 4284 | |||
| 4285 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 4286 | .ty = try Type.Tag.single_const_pointer.create(sema.arena, pointee_type), | ||
| 4287 | .val = elem_ptr, | ||
| 4288 | }); | ||
| 4289 | } | ||
| 4290 | } | ||
| 4291 | } | ||
| 4292 | |||
| 4293 | return sema.mod.fail(&block.base, src, "TODO implement more analyze elemptr", .{}); | ||
| 4294 | } | ||
| 4295 | |||
| 4296 | fn coerce( | ||
| 4297 | sema: *Sema, | ||
| 4298 | block: *Scope.Block, | ||
| 4299 | dest_type: Type, | ||
| 4300 | inst: *Inst, | ||
| 4301 | inst_src: LazySrcLoc, | ||
| 4302 | ) InnerError!*Inst { | ||
| 4303 | if (dest_type.tag() == .var_args_param) { | ||
| 4304 | return sema.coerceVarArgParam(block, inst); | ||
| 4305 | } | ||
| 4306 | // If the types are the same, we can return the operand. | ||
| 4307 | if (dest_type.eql(inst.ty)) | ||
| 4308 | return inst; | ||
| 4309 | |||
| 4310 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); | ||
| 4311 | if (in_memory_result == .ok) { | ||
| 4312 | return sema.bitcast(block, dest_type, inst); | ||
| 4313 | } | ||
| 4314 | |||
| 4315 | // undefined to anything | ||
| 4316 | if (inst.value()) |val| { | ||
| 4317 | if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) { | ||
| 4318 | return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = val }); | ||
| 4319 | } | ||
| 4320 | } | ||
| 4321 | assert(inst.ty.zigTypeTag() != .Undefined); | ||
| 4322 | |||
| 4323 | // T to E!T or E to E!T | ||
| 4324 | if (dest_type.tag() == .error_union) { | ||
| 4325 | return try sema.wrapErrorUnion(block, dest_type, inst); | ||
| 4326 | } | ||
| 4327 | |||
| 4328 | // comptime known number to other number | ||
| 4329 | if (try sema.coerceNum(block, dest_type, inst)) |some| | ||
| 4330 | return some; | ||
| 4331 | |||
| 4332 | const target = sema.mod.getTarget(); | ||
| 4333 | |||
| 4334 | switch (dest_type.zigTypeTag()) { | ||
| 4335 | .Optional => { | ||
| 4336 | // null to ?T | ||
| 4337 | if (inst.ty.zigTypeTag() == .Null) { | ||
| 4338 | return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) }); | ||
| 4339 | } | ||
| 4340 | |||
| 4341 | // T to ?T | ||
| 4342 | var buf: Type.Payload.ElemType = undefined; | ||
| 4343 | const child_type = dest_type.optionalChild(&buf); | ||
| 4344 | if (child_type.eql(inst.ty)) { | ||
| 4345 | return sema.wrapOptional(block, dest_type, inst); | ||
| 4346 | } else if (try sema.coerceNum(block, child_type, inst)) |some| { | ||
| 4347 | return sema.wrapOptional(block, dest_type, some); | ||
| 4348 | } | ||
| 4349 | }, | ||
| 4350 | .Pointer => { | ||
| 4351 | // Coercions where the source is a single pointer to an array. | ||
| 4352 | src_array_ptr: { | ||
| 4353 | if (!inst.ty.isSinglePointer()) break :src_array_ptr; | ||
| 4354 | const array_type = inst.ty.elemType(); | ||
| 4355 | if (array_type.zigTypeTag() != .Array) break :src_array_ptr; | ||
| 4356 | const array_elem_type = array_type.elemType(); | ||
| 4357 | if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr; | ||
| 4358 | if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr; | ||
| 4359 | |||
| 4360 | const dst_elem_type = dest_type.elemType(); | ||
| 4361 | switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) { | ||
| 4362 | .ok => {}, | ||
| 4363 | .no_match => break :src_array_ptr, | ||
| 4364 | } | ||
| 4365 | |||
| 4366 | switch (dest_type.ptrSize()) { | ||
| 4367 | .Slice => { | ||
| 4368 | // *[N]T to []T | ||
| 4369 | return sema.coerceArrayPtrToSlice(block, dest_type, inst); | ||
| 4370 | }, | ||
| 4371 | .C => { | ||
| 4372 | // *[N]T to [*c]T | ||
| 4373 | return sema.coerceArrayPtrToMany(block, dest_type, inst); | ||
| 4374 | }, | ||
| 4375 | .Many => { | ||
| 4376 | // *[N]T to [*]T | ||
| 4377 | // *[N:s]T to [*:s]T | ||
| 4378 | const src_sentinel = array_type.sentinel(); | ||
| 4379 | const dst_sentinel = dest_type.sentinel(); | ||
| 4380 | if (src_sentinel == null and dst_sentinel == null) | ||
| 4381 | return sema.coerceArrayPtrToMany(block, dest_type, inst); | ||
| 4382 | |||
| 4383 | if (src_sentinel) |src_s| { | ||
| 4384 | if (dst_sentinel) |dst_s| { | ||
| 4385 | if (src_s.eql(dst_s)) { | ||
| 4386 | return sema.coerceArrayPtrToMany(block, dest_type, inst); | ||
| 4387 | } | ||
| 4388 | } | ||
| 4389 | } | ||
| 4390 | }, | ||
| 4391 | .One => {}, | ||
| 4392 | } | ||
| 4393 | } | ||
| 4394 | }, | ||
| 4395 | .Int => { | ||
| 4396 | // integer widening | ||
| 4397 | if (inst.ty.zigTypeTag() == .Int) { | ||
| 4398 | assert(inst.value() == null); // handled above | ||
| 4399 | |||
| 4400 | const dst_info = dest_type.intInfo(target); | ||
| 4401 | const src_info = inst.ty.intInfo(target); | ||
| 4402 | if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or | ||
| 4403 | // small enough unsigned ints can get casted to large enough signed ints | ||
| 4404 | (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits)) | ||
| 4405 | { | ||
| 4406 | try sema.requireRuntimeBlock(block, inst_src); | ||
| 4407 | return block.addUnOp(inst_src, dest_type, .intcast, inst); | ||
| 4408 | } | ||
| 4409 | } | ||
| 4410 | }, | ||
| 4411 | .Float => { | ||
| 4412 | // float widening | ||
| 4413 | if (inst.ty.zigTypeTag() == .Float) { | ||
| 4414 | assert(inst.value() == null); // handled above | ||
| 4415 | |||
| 4416 | const src_bits = inst.ty.floatBits(target); | ||
| 4417 | const dst_bits = dest_type.floatBits(target); | ||
| 4418 | if (dst_bits >= src_bits) { | ||
| 4419 | try sema.requireRuntimeBlock(block, inst_src); | ||
| 4420 | return block.addUnOp(inst_src, dest_type, .floatcast, inst); | ||
| 4421 | } | ||
| 4422 | } | ||
| 4423 | }, | ||
| 4424 | else => {}, | ||
| 4425 | } | ||
| 4426 | |||
| 4427 | return sema.mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty }); | ||
| 4428 | } | ||
| 4429 | |||
| 4430 | const InMemoryCoercionResult = enum { | ||
| 4431 | ok, | ||
| 4432 | no_match, | ||
| 4433 | }; | ||
| 4434 | |||
| 4435 | fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult { | ||
| 4436 | if (dest_type.eql(src_type)) | ||
| 4437 | return .ok; | ||
| 4438 | |||
| 4439 | // TODO: implement more of this function | ||
| 4440 | |||
| 4441 | return .no_match; | ||
| 4442 | } | ||
| 4443 | |||
| 4444 | fn coerceNum(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) InnerError!?*Inst { | ||
| 4445 | const val = inst.value() orelse return null; | ||
| 4446 | const src_zig_tag = inst.ty.zigTypeTag(); | ||
| 4447 | const dst_zig_tag = dest_type.zigTypeTag(); | ||
| 4448 | |||
| 4449 | const target = sema.mod.getTarget(); | ||
| 4450 | |||
| 4451 | if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { | ||
| 4452 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 4453 | if (val.floatHasFraction()) { | ||
| 4454 | return sema.mod.fail(&block.base, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty }); | ||
| 4455 | } | ||
| 4456 | return sema.mod.fail(&block.base, inst.src, "TODO float to int", .{}); | ||
| 4457 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 4458 | if (!val.intFitsInType(dest_type, target)) { | ||
| 4459 | return sema.mod.fail(&block.base, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); | ||
| 4460 | } | ||
| 4461 | return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 4462 | } | ||
| 4463 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { | ||
| 4464 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 4465 | const res = val.floatCast(sema.arena, dest_type, target) catch |err| switch (err) { | ||
| 4466 | error.Overflow => return sema.mod.fail( | ||
| 4467 | &block.base, | ||
| 4468 | inst.src, | ||
| 4469 | "cast of value {} to type '{}' loses information", | ||
| 4470 | .{ val, dest_type }, | ||
| 4471 | ), | ||
| 4472 | error.OutOfMemory => return error.OutOfMemory, | ||
| 4473 | }; | ||
| 4474 | return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = res }); | ||
| 4475 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 4476 | return sema.mod.fail(&block.base, inst.src, "TODO int to float", .{}); | ||
| 4477 | } | ||
| 4478 | } | ||
| 4479 | return null; | ||
| 4480 | } | ||
| 4481 | |||
| 4482 | fn coerceVarArgParam(sema: *Sema, block: *Scope.Block, inst: *Inst) !*Inst { | ||
| 4483 | switch (inst.ty.zigTypeTag()) { | ||
| 4484 | .ComptimeInt, .ComptimeFloat => return sema.mod.fail(&block.base, inst.src, "integer and float literals in var args function must be casted", .{}), | ||
| 4485 | else => {}, | ||
| 4486 | } | ||
| 4487 | // TODO implement more of this function. | ||
| 4488 | return inst; | ||
| 4489 | } | ||
| 4490 | |||
| 4491 | fn storePtr( | ||
| 4492 | sema: *Sema, | ||
| 4493 | block: *Scope.Block, | ||
| 4494 | src: LazySrcLoc, | ||
| 4495 | ptr: *Inst, | ||
| 4496 | uncasted_value: *Inst, | ||
| 4497 | ) !void { | ||
| 4498 | if (ptr.ty.isConstPtr()) | ||
| 4499 | return sema.mod.fail(&block.base, src, "cannot assign to constant", .{}); | ||
| 4500 | |||
| 4501 | const elem_ty = ptr.ty.elemType(); | ||
| 4502 | const value = try sema.coerce(block, elem_ty, uncasted_value, src); | ||
| 4503 | if (elem_ty.onePossibleValue() != null) | ||
| 4504 | return; | ||
| 4505 | |||
| 4506 | // TODO handle comptime pointer writes | ||
| 4507 | // TODO handle if the element type requires comptime | ||
| 4508 | |||
| 4509 | try sema.requireRuntimeBlock(block, src); | ||
| 4510 | _ = try block.addBinOp(src, Type.initTag(.void), .store, ptr, value); | ||
| 4511 | } | ||
| 4512 | |||
| 4513 | fn bitcast(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 4514 | if (inst.value()) |val| { | ||
| 4515 | // Keep the comptime Value representation; take the new type. | ||
| 4516 | return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 4517 | } | ||
| 4518 | // TODO validate the type size and other compile errors | ||
| 4519 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 4520 | return block.addUnOp(inst.src, dest_type, .bitcast, inst); | ||
| 4521 | } | ||
| 4522 | |||
| 4523 | fn coerceArrayPtrToSlice(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 4524 | if (inst.value()) |val| { | ||
| 4525 | // The comptime Value representation is compatible with both types. | ||
| 4526 | return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 4527 | } | ||
| 4528 | return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); | ||
| 4529 | } | ||
| 4530 | |||
| 4531 | fn coerceArrayPtrToMany(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 4532 | if (inst.value()) |val| { | ||
| 4533 | // The comptime Value representation is compatible with both types. | ||
| 4534 | return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 4535 | } | ||
| 4536 | return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{}); | ||
| 4537 | } | ||
| 4538 | |||
| 4539 | fn analyzeDeclVal(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst { | ||
| 4540 | const decl_ref = try sema.analyzeDeclRef(block, src, decl); | ||
| 4541 | return sema.analyzeLoad(block, src, decl_ref, src); | ||
| 4542 | } | ||
| 4543 | |||
| 4544 | fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst { | ||
| 4545 | try sema.mod.declareDeclDependency(sema.owner_decl, decl); | ||
| 4546 | sema.mod.ensureDeclAnalyzed(decl) catch |err| { | ||
| 4547 | if (sema.func) |func| { | ||
| 4548 | func.state = .dependency_failure; | ||
| 4549 | } else { | ||
| 4550 | sema.owner_decl.analysis = .dependency_failure; | ||
| 4551 | } | ||
| 4552 | return err; | ||
| 4553 | }; | ||
| 4554 | |||
| 4555 | const decl_tv = try decl.typedValue(); | ||
| 4556 | if (decl_tv.val.tag() == .variable) { | ||
| 4557 | return sema.analyzeVarRef(block, src, decl_tv); | ||
| 4558 | } | ||
| 4559 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 4560 | .ty = try sema.mod.simplePtrType(sema.arena, decl_tv.ty, false, .One), | ||
| 4561 | .val = try Value.Tag.decl_ref.create(sema.arena, decl), | ||
| 4562 | }); | ||
| 4563 | } | ||
| 4564 | |||
| 4565 | fn analyzeVarRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, tv: TypedValue) InnerError!*Inst { | ||
| 4566 | const variable = tv.val.castTag(.variable).?.data; | ||
| 4567 | |||
| 4568 | const ty = try sema.mod.simplePtrType(sema.arena, tv.ty, variable.is_mutable, .One); | ||
| 4569 | if (!variable.is_mutable and !variable.is_extern) { | ||
| 4570 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 4571 | .ty = ty, | ||
| 4572 | .val = try Value.Tag.ref_val.create(sema.arena, variable.init), | ||
| 4573 | }); | ||
| 4574 | } | ||
| 4575 | |||
| 4576 | try sema.requireRuntimeBlock(block, src); | ||
| 4577 | const inst = try sema.arena.create(Inst.VarPtr); | ||
| 4578 | inst.* = .{ | ||
| 4579 | .base = .{ | ||
| 4580 | .tag = .varptr, | ||
| 4581 | .ty = ty, | ||
| 4582 | .src = src, | ||
| 4583 | }, | ||
| 4584 | .variable = variable, | ||
| 4585 | }; | ||
| 4586 | try block.instructions.append(sema.gpa, &inst.base); | ||
| 4587 | return &inst.base; | ||
| 4588 | } | ||
| 4589 | |||
| 4590 | fn analyzeRef( | ||
| 4591 | sema: *Sema, | ||
| 4592 | block: *Scope.Block, | ||
| 4593 | src: LazySrcLoc, | ||
| 4594 | operand: *Inst, | ||
| 4595 | ) InnerError!*Inst { | ||
| 4596 | const ptr_type = try sema.mod.simplePtrType(sema.arena, operand.ty, false, .One); | ||
| 4597 | |||
| 4598 | if (operand.value()) |val| { | ||
| 4599 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 4600 | .ty = ptr_type, | ||
| 4601 | .val = try Value.Tag.ref_val.create(sema.arena, val), | ||
| 4602 | }); | ||
| 4603 | } | ||
| 4604 | |||
| 4605 | try sema.requireRuntimeBlock(block, src); | ||
| 4606 | return block.addUnOp(src, ptr_type, .ref, operand); | ||
| 4607 | } | ||
| 4608 | |||
| 4609 | fn analyzeLoad( | ||
| 4610 | sema: *Sema, | ||
| 4611 | block: *Scope.Block, | ||
| 4612 | src: LazySrcLoc, | ||
| 4613 | ptr: *Inst, | ||
| 4614 | ptr_src: LazySrcLoc, | ||
| 4615 | ) InnerError!*Inst { | ||
| 4616 | const elem_ty = switch (ptr.ty.zigTypeTag()) { | ||
| 4617 | .Pointer => ptr.ty.elemType(), | ||
| 4618 | else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}), | ||
| 4619 | }; | ||
| 4620 | if (ptr.value()) |val| { | ||
| 4621 | return sema.mod.constInst(sema.arena, src, .{ | ||
| 4622 | .ty = elem_ty, | ||
| 4623 | .val = try val.pointerDeref(sema.arena), | ||
| 4624 | }); | ||
| 4625 | } | ||
| 4626 | |||
| 4627 | try sema.requireRuntimeBlock(block, src); | ||
| 4628 | return block.addUnOp(src, elem_ty, .load, ptr); | ||
| 4629 | } | ||
| 4630 | |||
| 4631 | fn analyzeIsNull( | ||
| 4632 | sema: *Sema, | ||
| 4633 | block: *Scope.Block, | ||
| 4634 | src: LazySrcLoc, | ||
| 4635 | operand: *Inst, | ||
| 4636 | invert_logic: bool, | ||
| 4637 | ) InnerError!*Inst { | ||
| 4638 | if (operand.value()) |opt_val| { | ||
| 4639 | const is_null = opt_val.isNull(); | ||
| 4640 | const bool_value = if (invert_logic) !is_null else is_null; | ||
| 4641 | return sema.mod.constBool(sema.arena, src, bool_value); | ||
| 4642 | } | ||
| 4643 | try sema.requireRuntimeBlock(block, src); | ||
| 4644 | const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null; | ||
| 4645 | return block.addUnOp(src, Type.initTag(.bool), inst_tag, operand); | ||
| 4646 | } | ||
| 4647 | |||
| 4648 | fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst { | ||
| 4649 | const ot = operand.ty.zigTypeTag(); | ||
| 4650 | if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false); | ||
| 4651 | if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true); | ||
| 4652 | assert(ot == .ErrorUnion); | ||
| 4653 | if (operand.value()) |err_union| { | ||
| 4654 | return sema.mod.constBool(sema.arena, src, err_union.getError() != null); | ||
| 4655 | } | ||
| 4656 | try sema.requireRuntimeBlock(block, src); | ||
| 4657 | return block.addUnOp(src, Type.initTag(.bool), .is_err, operand); | ||
| 4658 | } | ||
| 4659 | |||
| 4660 | fn analyzeSlice( | ||
| 4661 | sema: *Sema, | ||
| 4662 | block: *Scope.Block, | ||
| 4663 | src: LazySrcLoc, | ||
| 4664 | array_ptr: *Inst, | ||
| 4665 | start: *Inst, | ||
| 4666 | end_opt: ?*Inst, | ||
| 4667 | sentinel_opt: ?*Inst, | ||
| 4668 | sentinel_src: LazySrcLoc, | ||
| 4669 | ) InnerError!*Inst { | ||
| 4670 | const ptr_child = switch (array_ptr.ty.zigTypeTag()) { | ||
| 4671 | .Pointer => array_ptr.ty.elemType(), | ||
| 4672 | else => return sema.mod.fail(&block.base, src, "expected pointer, found '{}'", .{array_ptr.ty}), | ||
| 4673 | }; | ||
| 4674 | |||
| 4675 | var array_type = ptr_child; | ||
| 4676 | const elem_type = switch (ptr_child.zigTypeTag()) { | ||
| 4677 | .Array => ptr_child.elemType(), | ||
| 4678 | .Pointer => blk: { | ||
| 4679 | if (ptr_child.isSinglePointer()) { | ||
| 4680 | if (ptr_child.elemType().zigTypeTag() == .Array) { | ||
| 4681 | array_type = ptr_child.elemType(); | ||
| 4682 | break :blk ptr_child.elemType().elemType(); | ||
| 4683 | } | ||
| 4684 | |||
| 4685 | return sema.mod.fail(&block.base, src, "slice of single-item pointer", .{}); | ||
| 4686 | } | ||
| 4687 | break :blk ptr_child.elemType(); | ||
| 4688 | }, | ||
| 4689 | else => return sema.mod.fail(&block.base, src, "slice of non-array type '{}'", .{ptr_child}), | ||
| 4690 | }; | ||
| 4691 | |||
| 4692 | const slice_sentinel = if (sentinel_opt) |sentinel| blk: { | ||
| 4693 | const casted = try sema.coerce(block, elem_type, sentinel, sentinel.src); | ||
| 4694 | break :blk try sema.resolveConstValue(block, sentinel_src, casted); | ||
| 4695 | } else null; | ||
| 4696 | |||
| 4697 | var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice; | ||
| 4698 | var return_elem_type = elem_type; | ||
| 4699 | if (end_opt) |end| { | ||
| 4700 | if (end.value()) |end_val| { | ||
| 4701 | if (start.value()) |start_val| { | ||
| 4702 | const start_u64 = start_val.toUnsignedInt(); | ||
| 4703 | const end_u64 = end_val.toUnsignedInt(); | ||
| 4704 | if (start_u64 > end_u64) { | ||
| 4705 | return sema.mod.fail(&block.base, src, "out of bounds slice", .{}); | ||
| 4706 | } | ||
| 4707 | |||
| 4708 | const len = end_u64 - start_u64; | ||
| 4709 | const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen()) | ||
| 4710 | array_type.sentinel() | ||
| 4711 | else | ||
| 4712 | slice_sentinel; | ||
| 4713 | return_elem_type = try sema.mod.arrayType(sema.arena, len, array_sentinel, elem_type); | ||
| 4714 | return_ptr_size = .One; | ||
| 4715 | } | ||
| 4716 | } | ||
| 4717 | } | ||
| 4718 | const return_type = try sema.mod.ptrType( | ||
| 4719 | sema.arena, | ||
| 4720 | return_elem_type, | ||
| 4721 | if (end_opt == null) slice_sentinel else null, | ||
| 4722 | 0, // TODO alignment | ||
| 4723 | 0, | ||
| 4724 | 0, | ||
| 4725 | !ptr_child.isConstPtr(), | ||
| 4726 | ptr_child.isAllowzeroPtr(), | ||
| 4727 | ptr_child.isVolatilePtr(), | ||
| 4728 | return_ptr_size, | ||
| 4729 | ); | ||
| 4730 | |||
| 4731 | return sema.mod.fail(&block.base, src, "TODO implement analysis of slice", .{}); | ||
| 4732 | } | ||
| 4733 | |||
| 4734 | fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_string: []const u8) !*Scope.File { | ||
| 4735 | const cur_pkg = block.getFileScope().pkg; | ||
| 4736 | const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse "."; | ||
| 4737 | const found_pkg = cur_pkg.table.get(target_string); | ||
| 4738 | |||
| 4739 | const resolved_path = if (found_pkg) |pkg| | ||
| 4740 | try std.fs.path.resolve(sema.gpa, &[_][]const u8{ pkg.root_src_directory.path orelse ".", pkg.root_src_path }) | ||
| 4741 | else | ||
| 4742 | try std.fs.path.resolve(sema.gpa, &[_][]const u8{ cur_pkg_dir_path, target_string }); | ||
| 4743 | errdefer sema.gpa.free(resolved_path); | ||
| 4744 | |||
| 4745 | if (sema.mod.import_table.get(resolved_path)) |some| { | ||
| 4746 | sema.gpa.free(resolved_path); | ||
| 4747 | return some; | ||
| 4748 | } | ||
| 4749 | |||
| 4750 | if (found_pkg == null) { | ||
| 4751 | const resolved_root_path = try std.fs.path.resolve(sema.gpa, &[_][]const u8{cur_pkg_dir_path}); | ||
| 4752 | defer sema.gpa.free(resolved_root_path); | ||
| 4753 | |||
| 4754 | if (!mem.startsWith(u8, resolved_path, resolved_root_path)) { | ||
| 4755 | return error.ImportOutsidePkgPath; | ||
| 4756 | } | ||
| 4757 | } | ||
| 4758 | |||
| 4759 | // TODO Scope.Container arena for ty and sub_file_path | ||
| 4760 | const file_scope = try sema.gpa.create(Scope.File); | ||
| 4761 | errdefer sema.gpa.destroy(file_scope); | ||
| 4762 | const struct_ty = try Type.Tag.empty_struct.create(sema.gpa, &file_scope.root_container); | ||
| 4763 | errdefer sema.gpa.destroy(struct_ty.castTag(.empty_struct).?); | ||
| 4764 | |||
| 4765 | file_scope.* = .{ | ||
| 4766 | .sub_file_path = resolved_path, | ||
| 4767 | .source = .{ .unloaded = {} }, | ||
| 4768 | .tree = undefined, | ||
| 4769 | .status = .never_loaded, | ||
| 4770 | .pkg = found_pkg orelse cur_pkg, | ||
| 4771 | .root_container = .{ | ||
| 4772 | .file_scope = file_scope, | ||
| 4773 | .decls = .{}, | ||
| 4774 | .ty = struct_ty, | ||
| 4775 | }, | ||
| 4776 | }; | ||
| 4777 | sema.mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) { | ||
| 4778 | error.AnalysisFail => { | ||
| 4779 | assert(sema.mod.comp.totalErrorCount() != 0); | ||
| 4780 | }, | ||
| 4781 | else => |e| return e, | ||
| 4782 | }; | ||
| 4783 | try sema.mod.import_table.put(sema.gpa, file_scope.sub_file_path, file_scope); | ||
| 4784 | return file_scope; | ||
| 4785 | } | ||
| 4786 | |||
| 4787 | /// Asserts that lhs and rhs types are both numeric. | ||
| 4788 | fn cmpNumeric( | ||
| 4789 | sema: *Sema, | ||
| 4790 | block: *Scope.Block, | ||
| 4791 | src: LazySrcLoc, | ||
| 4792 | lhs: *Inst, | ||
| 4793 | rhs: *Inst, | ||
| 4794 | op: std.math.CompareOperator, | ||
| 4795 | ) InnerError!*Inst { | ||
| 4796 | assert(lhs.ty.isNumeric()); | ||
| 4797 | assert(rhs.ty.isNumeric()); | ||
| 4798 | |||
| 4799 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | ||
| 4800 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | ||
| 4801 | |||
| 4802 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { | ||
| 4803 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 4804 | return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{ | ||
| 4805 | lhs.ty.arrayLen(), | ||
| 4806 | rhs.ty.arrayLen(), | ||
| 4807 | }); | ||
| 4808 | } | ||
| 4809 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{}); | ||
| 4810 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { | ||
| 4811 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | ||
| 4812 | lhs.ty, | ||
| 4813 | rhs.ty, | ||
| 4814 | }); | ||
| 4815 | } | ||
| 4816 | |||
| 4817 | if (lhs.value()) |lhs_val| { | ||
| 4818 | if (rhs.value()) |rhs_val| { | ||
| 4819 | return sema.mod.constBool(sema.arena, src, Value.compare(lhs_val, op, rhs_val)); | ||
| 4820 | } | ||
| 4821 | } | ||
| 4822 | |||
| 4823 | // TODO handle comparisons against lazy zero values | ||
| 4824 | // Some values can be compared against zero without being runtime known or without forcing | ||
| 4825 | // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to | ||
| 4826 | // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout | ||
| 4827 | // of this function if we don't need to. | ||
| 4828 | |||
| 4829 | // It must be a runtime comparison. | ||
| 4830 | try sema.requireRuntimeBlock(block, src); | ||
| 4831 | // For floats, emit a float comparison instruction. | ||
| 4832 | const lhs_is_float = switch (lhs_ty_tag) { | ||
| 4833 | .Float, .ComptimeFloat => true, | ||
| 4834 | else => false, | ||
| 4835 | }; | ||
| 4836 | const rhs_is_float = switch (rhs_ty_tag) { | ||
| 4837 | .Float, .ComptimeFloat => true, | ||
| 4838 | else => false, | ||
| 4839 | }; | ||
| 4840 | const target = sema.mod.getTarget(); | ||
| 4841 | if (lhs_is_float and rhs_is_float) { | ||
| 4842 | // Implicit cast the smaller one to the larger one. | ||
| 4843 | const dest_type = x: { | ||
| 4844 | if (lhs_ty_tag == .ComptimeFloat) { | ||
| 4845 | break :x rhs.ty; | ||
| 4846 | } else if (rhs_ty_tag == .ComptimeFloat) { | ||
| 4847 | break :x lhs.ty; | ||
| 4848 | } | ||
| 4849 | if (lhs.ty.floatBits(target) >= rhs.ty.floatBits(target)) { | ||
| 4850 | break :x lhs.ty; | ||
| 4851 | } else { | ||
| 4852 | break :x rhs.ty; | ||
| 4853 | } | ||
| 4854 | }; | ||
| 4855 | const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs.src); | ||
| 4856 | const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs.src); | ||
| 4857 | return block.addBinOp(src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | ||
| 4858 | } | ||
| 4859 | // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. | ||
| 4860 | // For mixed signed and unsigned integers, implicit cast both operands to a signed | ||
| 4861 | // integer with + 1 bit. | ||
| 4862 | // For mixed floats and integers, extract the integer part from the float, cast that to | ||
| 4863 | // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, | ||
| 4864 | // add/subtract 1. | ||
| 4865 | const lhs_is_signed = if (lhs.value()) |lhs_val| | ||
| 4866 | lhs_val.compareWithZero(.lt) | ||
| 4867 | else | ||
| 4868 | (lhs.ty.isFloat() or lhs.ty.isSignedInt()); | ||
| 4869 | const rhs_is_signed = if (rhs.value()) |rhs_val| | ||
| 4870 | rhs_val.compareWithZero(.lt) | ||
| 4871 | else | ||
| 4872 | (rhs.ty.isFloat() or rhs.ty.isSignedInt()); | ||
| 4873 | const dest_int_is_signed = lhs_is_signed or rhs_is_signed; | ||
| 4874 | |||
| 4875 | var dest_float_type: ?Type = null; | ||
| 4876 | |||
| 4877 | var lhs_bits: usize = undefined; | ||
| 4878 | if (lhs.value()) |lhs_val| { | ||
| 4879 | if (lhs_val.isUndef()) | ||
| 4880 | return sema.mod.constUndef(sema.arena, src, Type.initTag(.bool)); | ||
| 4881 | const is_unsigned = if (lhs_is_float) x: { | ||
| 4882 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 4883 | var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(sema.gpa); | ||
| 4884 | defer bigint.deinit(); | ||
| 4885 | const zcmp = lhs_val.orderAgainstZero(); | ||
| 4886 | if (lhs_val.floatHasFraction()) { | ||
| 4887 | switch (op) { | ||
| 4888 | .eq => return sema.mod.constBool(sema.arena, src, false), | ||
| 4889 | .neq => return sema.mod.constBool(sema.arena, src, true), | ||
| 4890 | else => {}, | ||
| 4891 | } | ||
| 4892 | if (zcmp == .lt) { | ||
| 4893 | try bigint.addScalar(bigint.toConst(), -1); | ||
| 4894 | } else { | ||
| 4895 | try bigint.addScalar(bigint.toConst(), 1); | ||
| 4896 | } | ||
| 4897 | } | ||
| 4898 | lhs_bits = bigint.toConst().bitCountTwosComp(); | ||
| 4899 | break :x (zcmp != .lt); | ||
| 4900 | } else x: { | ||
| 4901 | lhs_bits = lhs_val.intBitCountTwosComp(); | ||
| 4902 | break :x (lhs_val.orderAgainstZero() != .lt); | ||
| 4903 | }; | ||
| 4904 | lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 4905 | } else if (lhs_is_float) { | ||
| 4906 | dest_float_type = lhs.ty; | ||
| 4907 | } else { | ||
| 4908 | const int_info = lhs.ty.intInfo(target); | ||
| 4909 | lhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed); | ||
| 4910 | } | ||
| 4911 | |||
| 4912 | var rhs_bits: usize = undefined; | ||
| 4913 | if (rhs.value()) |rhs_val| { | ||
| 4914 | if (rhs_val.isUndef()) | ||
| 4915 | return sema.mod.constUndef(sema.arena, src, Type.initTag(.bool)); | ||
| 4916 | const is_unsigned = if (rhs_is_float) x: { | ||
| 4917 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 4918 | var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(sema.gpa); | ||
| 4919 | defer bigint.deinit(); | ||
| 4920 | const zcmp = rhs_val.orderAgainstZero(); | ||
| 4921 | if (rhs_val.floatHasFraction()) { | ||
| 4922 | switch (op) { | ||
| 4923 | .eq => return sema.mod.constBool(sema.arena, src, false), | ||
| 4924 | .neq => return sema.mod.constBool(sema.arena, src, true), | ||
| 4925 | else => {}, | ||
| 4926 | } | ||
| 4927 | if (zcmp == .lt) { | ||
| 4928 | try bigint.addScalar(bigint.toConst(), -1); | ||
| 4929 | } else { | ||
| 4930 | try bigint.addScalar(bigint.toConst(), 1); | ||
| 4931 | } | ||
| 4932 | } | ||
| 4933 | rhs_bits = bigint.toConst().bitCountTwosComp(); | ||
| 4934 | break :x (zcmp != .lt); | ||
| 4935 | } else x: { | ||
| 4936 | rhs_bits = rhs_val.intBitCountTwosComp(); | ||
| 4937 | break :x (rhs_val.orderAgainstZero() != .lt); | ||
| 4938 | }; | ||
| 4939 | rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 4940 | } else if (rhs_is_float) { | ||
| 4941 | dest_float_type = rhs.ty; | ||
| 4942 | } else { | ||
| 4943 | const int_info = rhs.ty.intInfo(target); | ||
| 4944 | rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed); | ||
| 4945 | } | ||
| 4946 | |||
| 4947 | const dest_type = if (dest_float_type) |ft| ft else blk: { | ||
| 4948 | const max_bits = std.math.max(lhs_bits, rhs_bits); | ||
| 4949 | const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { | ||
| 4950 | error.Overflow => return sema.mod.fail(&block.base, src, "{d} exceeds maximum integer bit count", .{max_bits}), | ||
| 4951 | }; | ||
| 4952 | const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned; | ||
| 4953 | break :blk try Module.makeIntType(sema.arena, signedness, casted_bits); | ||
| 4954 | }; | ||
| 4955 | const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs.src); | ||
| 4956 | const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs.src); | ||
| 4957 | |||
| 4958 | return block.addBinOp(src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | ||
| 4959 | } | ||
| 4960 | |||
| 4961 | fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 4962 | if (inst.value()) |val| { | ||
| 4963 | return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 4964 | } | ||
| 4965 | |||
| 4966 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 4967 | return block.addUnOp(inst.src, dest_type, .wrap_optional, inst); | ||
| 4968 | } | ||
| 4969 | |||
| 4970 | fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 4971 | // TODO deal with inferred error sets | ||
| 4972 | const err_union = dest_type.castTag(.error_union).?; | ||
| 4973 | if (inst.value()) |val| { | ||
| 4974 | const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: { | ||
| 4975 | _ = try sema.coerce(block, err_union.data.payload, inst, inst.src); | ||
| 4976 | break :blk val; | ||
| 4977 | } else switch (err_union.data.error_set.tag()) { | ||
| 4978 | .anyerror => val, | ||
| 4979 | .error_set_single => blk: { | ||
| 4980 | const expected_name = val.castTag(.@"error").?.data.name; | ||
| 4981 | const n = err_union.data.error_set.castTag(.error_set_single).?.data; | ||
| 4982 | if (!mem.eql(u8, expected_name, n)) { | ||
| 4983 | return sema.mod.fail( | ||
| 4984 | &block.base, | ||
| 4985 | inst.src, | ||
| 4986 | "expected type '{}', found type '{}'", | ||
| 4987 | .{ err_union.data.error_set, inst.ty }, | ||
| 4988 | ); | ||
| 4989 | } | ||
| 4990 | break :blk val; | ||
| 4991 | }, | ||
| 4992 | .error_set => blk: { | ||
| 4993 | const expected_name = val.castTag(.@"error").?.data.name; | ||
| 4994 | const error_set = err_union.data.error_set.castTag(.error_set).?.data; | ||
| 4995 | const names = error_set.names_ptr[0..error_set.names_len]; | ||
| 4996 | // TODO this is O(N). I'm putting off solving this until we solve inferred | ||
| 4997 | // error sets at the same time. | ||
| 4998 | const found = for (names) |name| { | ||
| 4999 | if (mem.eql(u8, expected_name, name)) break true; | ||
| 5000 | } else false; | ||
| 5001 | if (!found) { | ||
| 5002 | return sema.mod.fail( | ||
| 5003 | &block.base, | ||
| 5004 | inst.src, | ||
| 5005 | "expected type '{}', found type '{}'", | ||
| 5006 | .{ err_union.data.error_set, inst.ty }, | ||
| 5007 | ); | ||
| 5008 | } | ||
| 5009 | break :blk val; | ||
| 5010 | }, | ||
| 5011 | else => unreachable, | ||
| 5012 | }; | ||
| 5013 | |||
| 5014 | return sema.mod.constInst(sema.arena, inst.src, .{ | ||
| 5015 | .ty = dest_type, | ||
| 5016 | // creating a SubValue for the error_union payload | ||
| 5017 | .val = try Value.Tag.error_union.create( | ||
| 5018 | sema.arena, | ||
| 5019 | to_wrap, | ||
| 5020 | ), | ||
| 5021 | }); | ||
| 5022 | } | ||
| 5023 | |||
| 5024 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 5025 | |||
| 5026 | // we are coercing from E to E!T | ||
| 5027 | if (inst.ty.zigTypeTag() == .ErrorSet) { | ||
| 5028 | var coerced = try sema.coerce(block, err_union.data.error_set, inst, inst.src); | ||
| 5029 | return block.addUnOp(inst.src, dest_type, .wrap_errunion_err, coerced); | ||
| 5030 | } else { | ||
| 5031 | var coerced = try sema.coerce(block, err_union.data.payload, inst, inst.src); | ||
| 5032 | return block.addUnOp(inst.src, dest_type, .wrap_errunion_payload, coerced); | ||
| 5033 | } | ||
| 5034 | } | ||
| 5035 | |||
| 5036 | fn resolvePeerTypes(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, instructions: []*Inst) !Type { | ||
| 5037 | if (instructions.len == 0) | ||
| 5038 | return Type.initTag(.noreturn); | ||
| 5039 | |||
| 5040 | if (instructions.len == 1) | ||
| 5041 | return instructions[0].ty; | ||
| 5042 | |||
| 5043 | const target = sema.mod.getTarget(); | ||
| 5044 | |||
| 5045 | var chosen = instructions[0]; | ||
| 5046 | for (instructions[1..]) |candidate| { | ||
| 5047 | if (candidate.ty.eql(chosen.ty)) | ||
| 5048 | continue; | ||
| 5049 | if (candidate.ty.zigTypeTag() == .NoReturn) | ||
| 5050 | continue; | ||
| 5051 | if (chosen.ty.zigTypeTag() == .NoReturn) { | ||
| 5052 | chosen = candidate; | ||
| 5053 | continue; | ||
| 5054 | } | ||
| 5055 | if (candidate.ty.zigTypeTag() == .Undefined) | ||
| 5056 | continue; | ||
| 5057 | if (chosen.ty.zigTypeTag() == .Undefined) { | ||
| 5058 | chosen = candidate; | ||
| 5059 | continue; | ||
| 5060 | } | ||
| 5061 | if (chosen.ty.isInt() and | ||
| 5062 | candidate.ty.isInt() and | ||
| 5063 | chosen.ty.isSignedInt() == candidate.ty.isSignedInt()) | ||
| 5064 | { | ||
| 5065 | if (chosen.ty.intInfo(target).bits < candidate.ty.intInfo(target).bits) { | ||
| 5066 | chosen = candidate; | ||
| 5067 | } | ||
| 5068 | continue; | ||
| 5069 | } | ||
| 5070 | if (chosen.ty.isFloat() and candidate.ty.isFloat()) { | ||
| 5071 | if (chosen.ty.floatBits(target) < candidate.ty.floatBits(target)) { | ||
| 5072 | chosen = candidate; | ||
| 5073 | } | ||
| 5074 | continue; | ||
| 5075 | } | ||
| 5076 | |||
| 5077 | if (chosen.ty.zigTypeTag() == .ComptimeInt and candidate.ty.isInt()) { | ||
| 5078 | chosen = candidate; | ||
| 5079 | continue; | ||
| 5080 | } | ||
| 5081 | |||
| 5082 | if (chosen.ty.isInt() and candidate.ty.zigTypeTag() == .ComptimeInt) { | ||
| 5083 | continue; | ||
| 5084 | } | ||
| 5085 | |||
| 5086 | // TODO error notes pointing out each type | ||
| 5087 | return sema.mod.fail(&block.base, src, "incompatible types: '{}' and '{}'", .{ chosen.ty, candidate.ty }); | ||
| 5088 | } | ||
| 5089 | |||
| 5090 | return chosen.ty; | ||
| 5091 | } | ||
src/astgen.zig deleted-4318| ... | @@ -1,4318 +0,0 @@ | ||
| 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+97-136| ... | @@ -17,6 +17,8 @@ const DW = std.dwarf; | ... | @@ -17,6 +17,8 @@ const DW = std.dwarf; |
| 17 | const leb128 = std.leb; | 17 | const leb128 = std.leb; |
| 18 | const log = std.log.scoped(.codegen); | 18 | const log = std.log.scoped(.codegen); |
| 19 | const build_options = @import("build_options"); | 19 | const build_options = @import("build_options"); |
| 20 | const LazySrcLoc = Module.LazySrcLoc; | ||
| 21 | const RegisterManager = @import("register_manager.zig").RegisterManager; | ||
| 20 | 22 | ||
| 21 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. | 23 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. |
| 22 | pub const BlockData = struct { | 24 | pub const BlockData = struct { |
| ... | @@ -285,11 +287,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -285,11 +287,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 285 | /// across each runtime branch upon joining. | 287 | /// across each runtime branch upon joining. |
| 286 | branch_stack: *std.ArrayList(Branch), | 288 | branch_stack: *std.ArrayList(Branch), |
| 287 | 289 | ||
| 288 | /// The key must be canonical register. | 290 | register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{}, |
| 289 | registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{}, | ||
| 290 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), | ||
| 291 | /// Tracks all registers allocated in the course of this function | ||
| 292 | allocated_registers: FreeRegInt = 0, | ||
| 293 | /// Maps offset to what is stored there. | 291 | /// Maps offset to what is stored there. |
| 294 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, | 292 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 295 | 293 | ||
| ... | @@ -381,49 +379,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -381,49 +379,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 381 | } | 379 | } |
| 382 | }; | 380 | }; |
| 383 | 381 | ||
| 384 | fn markRegUsed(self: *Self, reg: Register) void { | ||
| 385 | if (FreeRegInt == u0) return; | ||
| 386 | const index = reg.allocIndex() orelse return; | ||
| 387 | const ShiftInt = math.Log2Int(FreeRegInt); | ||
| 388 | const shift = @intCast(ShiftInt, index); | ||
| 389 | const mask = @as(FreeRegInt, 1) << shift; | ||
| 390 | self.free_registers &= ~mask; | ||
| 391 | self.allocated_registers |= mask; | ||
| 392 | } | ||
| 393 | |||
| 394 | fn markRegFree(self: *Self, reg: Register) void { | ||
| 395 | if (FreeRegInt == u0) return; | ||
| 396 | const index = reg.allocIndex() orelse return; | ||
| 397 | const ShiftInt = math.Log2Int(FreeRegInt); | ||
| 398 | const shift = @intCast(ShiftInt, index); | ||
| 399 | self.free_registers |= @as(FreeRegInt, 1) << shift; | ||
| 400 | } | ||
| 401 | |||
| 402 | /// Before calling, must ensureCapacity + 1 on self.registers. | ||
| 403 | /// Returns `null` if all registers are allocated. | ||
| 404 | fn allocReg(self: *Self, inst: *ir.Inst) ?Register { | ||
| 405 | const free_index = @ctz(FreeRegInt, self.free_registers); | ||
| 406 | if (free_index >= callee_preserved_regs.len) { | ||
| 407 | return null; | ||
| 408 | } | ||
| 409 | const mask = @as(FreeRegInt, 1) << free_index; | ||
| 410 | self.free_registers &= ~mask; | ||
| 411 | self.allocated_registers |= mask; | ||
| 412 | const reg = callee_preserved_regs[free_index]; | ||
| 413 | self.registers.putAssumeCapacityNoClobber(reg, inst); | ||
| 414 | log.debug("alloc {} => {*}", .{ reg, inst }); | ||
| 415 | return reg; | ||
| 416 | } | ||
| 417 | |||
| 418 | /// Does not track the register. | ||
| 419 | fn findUnusedReg(self: *Self) ?Register { | ||
| 420 | const free_index = @ctz(FreeRegInt, self.free_registers); | ||
| 421 | if (free_index >= callee_preserved_regs.len) { | ||
| 422 | return null; | ||
| 423 | } | ||
| 424 | return callee_preserved_regs[free_index]; | ||
| 425 | } | ||
| 426 | |||
| 427 | const StackAllocation = struct { | 382 | const StackAllocation = struct { |
| 428 | inst: *ir.Inst, | 383 | inst: *ir.Inst, |
| 429 | /// TODO do we need size? should be determined by inst.ty.abiSize() | 384 | /// TODO do we need size? should be determined by inst.ty.abiSize() |
| ... | @@ -494,11 +449,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -494,11 +449,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 494 | .rbrace_src = src_data.rbrace_src, | 449 | .rbrace_src = src_data.rbrace_src, |
| 495 | .source = src_data.source, | 450 | .source = src_data.source, |
| 496 | }; | 451 | }; |
| 497 | defer function.registers.deinit(bin_file.allocator); | 452 | defer function.register_manager.deinit(bin_file.allocator); |
| 498 | defer function.stack.deinit(bin_file.allocator); | 453 | defer function.stack.deinit(bin_file.allocator); |
| 499 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); | 454 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 500 | 455 | ||
| 501 | var call_info = function.resolveCallingConventionValues(src_loc.byte_offset, fn_type) catch |err| switch (err) { | 456 | var call_info = function.resolveCallingConventionValues(src_loc.lazy, fn_type) catch |err| switch (err) { |
| 502 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 457 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 503 | else => |e| return e, | 458 | else => |e| return e, |
| 504 | }; | 459 | }; |
| ... | @@ -606,10 +561,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -606,10 +561,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 606 | .r14 = true, // lr | 561 | .r14 = true, // lr |
| 607 | }; | 562 | }; |
| 608 | inline for (callee_preserved_regs) |reg, i| { | 563 | inline for (callee_preserved_regs) |reg, i| { |
| 609 | const ShiftInt = math.Log2Int(FreeRegInt); | 564 | if (self.register_manager.isRegAllocated(reg)) { |
| 610 | const shift = @intCast(ShiftInt, i); | ||
| 611 | const mask = @as(FreeRegInt, 1) << shift; | ||
| 612 | if (self.allocated_registers & mask != 0) { | ||
| 613 | @field(saved_regs, @tagName(reg)) = true; | 565 | @field(saved_regs, @tagName(reg)) = true; |
| 614 | } | 566 | } |
| 615 | } | 567 | } |
| ... | @@ -791,8 +743,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -791,8 +743,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 791 | } | 743 | } |
| 792 | } | 744 | } |
| 793 | 745 | ||
| 794 | fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void { | 746 | fn dbgAdvancePCAndLine(self: *Self, abs_byte_off: usize) InnerError!void { |
| 795 | self.prev_di_src = src; | 747 | self.prev_di_src = abs_byte_off; |
| 796 | self.prev_di_pc = self.code.items.len; | 748 | self.prev_di_pc = self.code.items.len; |
| 797 | switch (self.debug_output) { | 749 | switch (self.debug_output) { |
| 798 | .dwarf => |dbg_out| { | 750 | .dwarf => |dbg_out| { |
| ... | @@ -800,7 +752,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -800,7 +752,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 800 | // lookup table, and changing ir.Inst from storing byte offset to token. Currently | 752 | // lookup table, and changing ir.Inst from storing byte offset to token. Currently |
| 801 | // this involves scanning over the source code for newlines | 753 | // this involves scanning over the source code for newlines |
| 802 | // (but only from the previous byte offset to the new one). | 754 | // (but only from the previous byte offset to the new one). |
| 803 | const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src); | 755 | const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, abs_byte_off); |
| 804 | const delta_pc = self.code.items.len - self.prev_di_pc; | 756 | const delta_pc = self.code.items.len - self.prev_di_pc; |
| 805 | // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit | 757 | // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit |
| 806 | // single-byte opcodes that add different numbers to both the PC and the line number | 758 | // single-byte opcodes that add different numbers to both the PC and the line number |
| ... | @@ -828,8 +780,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -828,8 +780,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 828 | switch (prev_value) { | 780 | switch (prev_value) { |
| 829 | .register => |reg| { | 781 | .register => |reg| { |
| 830 | const canon_reg = toCanonicalReg(reg); | 782 | const canon_reg = toCanonicalReg(reg); |
| 831 | _ = self.registers.remove(canon_reg); | 783 | self.register_manager.freeReg(canon_reg); |
| 832 | self.markRegFree(canon_reg); | ||
| 833 | }, | 784 | }, |
| 834 | else => {}, // TODO process stack allocation death | 785 | else => {}, // TODO process stack allocation death |
| 835 | } | 786 | } |
| ... | @@ -897,6 +848,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -897,6 +848,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 897 | .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?), | 848 | .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?), |
| 898 | .is_err => return self.genIsErr(inst.castTag(.is_err).?), | 849 | .is_err => return self.genIsErr(inst.castTag(.is_err).?), |
| 899 | .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?), | 850 | .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?), |
| 851 | .error_to_int => return self.genErrorToInt(inst.castTag(.error_to_int).?), | ||
| 852 | .int_to_error => return self.genIntToError(inst.castTag(.int_to_error).?), | ||
| 900 | .load => return self.genLoad(inst.castTag(.load).?), | 853 | .load => return self.genLoad(inst.castTag(.load).?), |
| 901 | .loop => return self.genLoop(inst.castTag(.loop).?), | 854 | .loop => return self.genLoop(inst.castTag(.loop).?), |
| 902 | .not => return self.genNot(inst.castTag(.not).?), | 855 | .not => return self.genNot(inst.castTag(.not).?), |
| ... | @@ -907,6 +860,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -907,6 +860,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 907 | .ret => return self.genRet(inst.castTag(.ret).?), | 860 | .ret => return self.genRet(inst.castTag(.ret).?), |
| 908 | .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?), | 861 | .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?), |
| 909 | .store => return self.genStore(inst.castTag(.store).?), | 862 | .store => return self.genStore(inst.castTag(.store).?), |
| 863 | .struct_field_ptr => return self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?), | ||
| 910 | .sub => return self.genSub(inst.castTag(.sub).?), | 864 | .sub => return self.genSub(inst.castTag(.sub).?), |
| 911 | .subwrap => return self.genSubWrap(inst.castTag(.subwrap).?), | 865 | .subwrap => return self.genSubWrap(inst.castTag(.subwrap).?), |
| 912 | .switchbr => return self.genSwitch(inst.castTag(.switchbr).?), | 866 | .switchbr => return self.genSwitch(inst.castTag(.switchbr).?), |
| ... | @@ -965,8 +919,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -965,8 +919,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 965 | const ptr_bits = arch.ptrBitWidth(); | 919 | const ptr_bits = arch.ptrBitWidth(); |
| 966 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 920 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 967 | if (abi_size <= ptr_bytes) { | 921 | if (abi_size <= ptr_bytes) { |
| 968 | try self.registers.ensureCapacity(self.gpa, self.registers.count() + 1); | 922 | try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1); |
| 969 | if (self.allocReg(inst)) |reg| { | 923 | if (self.register_manager.tryAllocReg(inst)) |reg| { |
| 970 | return MCValue{ .register = registerAlias(reg, abi_size) }; | 924 | return MCValue{ .register = registerAlias(reg, abi_size) }; |
| 971 | } | 925 | } |
| 972 | } | 926 | } |
| ... | @@ -975,26 +929,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -975,26 +929,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 975 | return MCValue{ .stack_offset = stack_offset }; | 929 | return MCValue{ .stack_offset = stack_offset }; |
| 976 | } | 930 | } |
| 977 | 931 | ||
| 932 | pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: Register, inst: *ir.Inst) !void { | ||
| 933 | const stack_mcv = try self.allocRegOrMem(inst, false); | ||
| 934 | const reg_mcv = self.getResolvedInstValue(inst); | ||
| 935 | assert(reg == toCanonicalReg(reg_mcv.register)); | ||
| 936 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | ||
| 937 | try branch.inst_table.put(self.gpa, inst, stack_mcv); | ||
| 938 | try self.genSetStack(src, inst.ty, stack_mcv.stack_offset, reg_mcv); | ||
| 939 | } | ||
| 940 | |||
| 978 | /// Copies a value to a register without tracking the register. The register is not considered | 941 | /// Copies a value to a register without tracking the register. The register is not considered |
| 979 | /// allocated. A second call to `copyToTmpRegister` may return the same register. | 942 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 980 | /// This can have a side effect of spilling instructions to the stack to free up a register. | 943 | /// 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 { | 944 | fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register { |
| 982 | const reg = self.findUnusedReg() orelse b: { | 945 | const reg = try self.register_manager.allocRegWithoutTracking(); |
| 983 | // We'll take over the first register. Move the instruction that was previously | ||
| 984 | // there to a stack allocation. | ||
| 985 | const reg = callee_preserved_regs[0]; | ||
| 986 | const regs_entry = self.registers.remove(reg).?; | ||
| 987 | const spilled_inst = regs_entry.value; | ||
| 988 | |||
| 989 | const stack_mcv = try self.allocRegOrMem(spilled_inst, false); | ||
| 990 | const reg_mcv = self.getResolvedInstValue(spilled_inst); | ||
| 991 | assert(reg == toCanonicalReg(reg_mcv.register)); | ||
| 992 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | ||
| 993 | try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv); | ||
| 994 | try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv); | ||
| 995 | |||
| 996 | break :b reg; | ||
| 997 | }; | ||
| 998 | try self.genSetReg(src, ty, reg, mcv); | 946 | try self.genSetReg(src, ty, reg, mcv); |
| 999 | return reg; | 947 | return reg; |
| 1000 | } | 948 | } |
| ... | @@ -1003,25 +951,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1003,25 +951,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1003 | /// `reg_owner` is the instruction that gets associated with the register in the register table. | 951 | /// `reg_owner` is the instruction that gets associated with the register in the register table. |
| 1004 | /// This can have a side effect of spilling instructions to the stack to free up a register. | 952 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 1005 | fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue { | 953 | fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue { |
| 1006 | try self.registers.ensureCapacity(self.gpa, @intCast(u32, self.registers.count() + 1)); | 954 | try self.register_manager.registers.ensureCapacity(self.gpa, @intCast(u32, self.register_manager.registers.count() + 1)); |
| 1007 | 955 | ||
| 1008 | const reg = self.allocReg(reg_owner) orelse b: { | 956 | const reg = try self.register_manager.allocReg(reg_owner); |
| 1009 | // We'll take over the first register. Move the instruction that was previously | ||
| 1010 | // there to a stack allocation. | ||
| 1011 | const reg = callee_preserved_regs[0]; | ||
| 1012 | const regs_entry = self.registers.getEntry(reg).?; | ||
| 1013 | const spilled_inst = regs_entry.value; | ||
| 1014 | regs_entry.value = reg_owner; | ||
| 1015 | |||
| 1016 | const stack_mcv = try self.allocRegOrMem(spilled_inst, false); | ||
| 1017 | const reg_mcv = self.getResolvedInstValue(spilled_inst); | ||
| 1018 | assert(reg == toCanonicalReg(reg_mcv.register)); | ||
| 1019 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | ||
| 1020 | try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv); | ||
| 1021 | try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv); | ||
| 1022 | |||
| 1023 | break :b reg; | ||
| 1024 | }; | ||
| 1025 | try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv); | 957 | try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv); |
| 1026 | return MCValue{ .register = reg }; | 958 | return MCValue{ .register = reg }; |
| 1027 | } | 959 | } |
| ... | @@ -1298,7 +1230,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1298,7 +1230,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1298 | .register => |reg| { | 1230 | .register => |reg| { |
| 1299 | // If it's in the registers table, need to associate the register with the | 1231 | // If it's in the registers table, need to associate the register with the |
| 1300 | // new instruction. | 1232 | // new instruction. |
| 1301 | if (self.registers.getEntry(toCanonicalReg(reg))) |entry| { | 1233 | if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| { |
| 1302 | entry.value = inst; | 1234 | entry.value = inst; |
| 1303 | } | 1235 | } |
| 1304 | log.debug("reusing {} => {*}", .{ reg, inst }); | 1236 | log.debug("reusing {} => {*}", .{ reg, inst }); |
| ... | @@ -1400,6 +1332,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1400,6 +1332,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1400 | return .none; | 1332 | return .none; |
| 1401 | } | 1333 | } |
| 1402 | 1334 | ||
| 1335 | fn genStructFieldPtr(self: *Self, inst: *ir.Inst.StructFieldPtr) !MCValue { | ||
| 1336 | return self.fail(inst.base.src, "TODO implement codegen struct_field_ptr", .{}); | ||
| 1337 | } | ||
| 1338 | |||
| 1403 | fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue { | 1339 | fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue { |
| 1404 | // No side effects, so if it's unreferenced, do nothing. | 1340 | // No side effects, so if it's unreferenced, do nothing. |
| 1405 | if (inst.base.isUnused()) | 1341 | if (inst.base.isUnused()) |
| ... | @@ -1457,7 +1393,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1457,7 +1393,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1457 | 1393 | ||
| 1458 | fn genArmBinOpCode( | 1394 | fn genArmBinOpCode( |
| 1459 | self: *Self, | 1395 | self: *Self, |
| 1460 | src: usize, | 1396 | src: LazySrcLoc, |
| 1461 | dst_reg: Register, | 1397 | dst_reg: Register, |
| 1462 | lhs_mcv: MCValue, | 1398 | lhs_mcv: MCValue, |
| 1463 | rhs_mcv: MCValue, | 1399 | rhs_mcv: MCValue, |
| ... | @@ -1620,7 +1556,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1620,7 +1556,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1620 | 1556 | ||
| 1621 | fn genX8664BinMathCode( | 1557 | fn genX8664BinMathCode( |
| 1622 | self: *Self, | 1558 | self: *Self, |
| 1623 | src: usize, | 1559 | src: LazySrcLoc, |
| 1624 | dst_ty: Type, | 1560 | dst_ty: Type, |
| 1625 | dst_mcv: MCValue, | 1561 | dst_mcv: MCValue, |
| 1626 | src_mcv: MCValue, | 1562 | src_mcv: MCValue, |
| ... | @@ -1706,7 +1642,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1706,7 +1642,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1706 | } | 1642 | } |
| 1707 | } | 1643 | } |
| 1708 | 1644 | ||
| 1709 | fn genX8664ModRMRegToStack(self: *Self, src: usize, ty: Type, off: u32, reg: Register, opcode: u8) !void { | 1645 | fn genX8664ModRMRegToStack(self: *Self, src: LazySrcLoc, ty: Type, off: u32, reg: Register, opcode: u8) !void { |
| 1710 | const abi_size = ty.abiSize(self.target.*); | 1646 | const abi_size = ty.abiSize(self.target.*); |
| 1711 | const adj_off = off + abi_size; | 1647 | const adj_off = off + abi_size; |
| 1712 | try self.code.ensureCapacity(self.code.items.len + 7); | 1648 | try self.code.ensureCapacity(self.code.items.len + 7); |
| ... | @@ -1787,7 +1723,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1787,7 +1723,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1787 | const arg_index = self.arg_index; | 1723 | const arg_index = self.arg_index; |
| 1788 | self.arg_index += 1; | 1724 | self.arg_index += 1; |
| 1789 | 1725 | ||
| 1790 | if (FreeRegInt == u0) { | 1726 | if (callee_preserved_regs.len == 0) { |
| 1791 | return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch}); | 1727 | return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch}); |
| 1792 | } | 1728 | } |
| 1793 | 1729 | ||
| ... | @@ -1799,15 +1735,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1799,15 +1735,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1799 | 1735 | ||
| 1800 | switch (result) { | 1736 | switch (result) { |
| 1801 | .register => |reg| { | 1737 | .register => |reg| { |
| 1802 | try self.registers.putNoClobber(self.gpa, toCanonicalReg(reg), &inst.base); | 1738 | try self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base); |
| 1803 | self.markRegUsed(reg); | ||
| 1804 | }, | 1739 | }, |
| 1805 | else => {}, | 1740 | else => {}, |
| 1806 | } | 1741 | } |
| 1807 | return result; | 1742 | return result; |
| 1808 | } | 1743 | } |
| 1809 | 1744 | ||
| 1810 | fn genBreakpoint(self: *Self, src: usize) !MCValue { | 1745 | fn genBreakpoint(self: *Self, src: LazySrcLoc) !MCValue { |
| 1811 | switch (arch) { | 1746 | switch (arch) { |
| 1812 | .i386, .x86_64 => { | 1747 | .i386, .x86_64 => { |
| 1813 | try self.code.append(0xcc); // int3 | 1748 | try self.code.append(0xcc); // int3 |
| ... | @@ -2194,6 +2129,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2194,6 +2129,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2194 | unreachable; | 2129 | unreachable; |
| 2195 | } | 2130 | } |
| 2196 | 2131 | ||
| 2132 | switch (info.return_value) { | ||
| 2133 | .register => |reg| { | ||
| 2134 | if (Register.allocIndex(reg) == null) { | ||
| 2135 | // Save function return value in a callee saved register | ||
| 2136 | return try self.copyToNewRegister(&inst.base, info.return_value); | ||
| 2137 | } | ||
| 2138 | }, | ||
| 2139 | else => {}, | ||
| 2140 | } | ||
| 2141 | |||
| 2197 | return info.return_value; | 2142 | return info.return_value; |
| 2198 | } | 2143 | } |
| 2199 | 2144 | ||
| ... | @@ -2224,7 +2169,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2224,7 +2169,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2224 | } | 2169 | } |
| 2225 | } | 2170 | } |
| 2226 | 2171 | ||
| 2227 | fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue { | 2172 | fn ret(self: *Self, src: LazySrcLoc, mcv: MCValue) !MCValue { |
| 2228 | const ret_ty = self.fn_type.fnReturnType(); | 2173 | const ret_ty = self.fn_type.fnReturnType(); |
| 2229 | try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv); | 2174 | try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv); |
| 2230 | switch (arch) { | 2175 | switch (arch) { |
| ... | @@ -2314,8 +2259,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2314,8 +2259,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2314 | } | 2259 | } |
| 2315 | } | 2260 | } |
| 2316 | 2261 | ||
| 2317 | fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue { | 2262 | fn genDbgStmt(self: *Self, inst: *ir.Inst.DbgStmt) !MCValue { |
| 2318 | try self.dbgAdvancePCAndLine(inst.base.src); | 2263 | // TODO when reworking tzir memory layout, rework source locations here as |
| 2264 | // well to be more efficient, as well as support inlined function calls correctly. | ||
| 2265 | // For now we convert LazySrcLoc to absolute byte offset, to match what the | ||
| 2266 | // existing codegen code expects. | ||
| 2267 | try self.dbgAdvancePCAndLine(inst.byte_offset); | ||
| 2319 | assert(inst.base.isUnused()); | 2268 | assert(inst.base.isUnused()); |
| 2320 | return MCValue.dead; | 2269 | return MCValue.dead; |
| 2321 | } | 2270 | } |
| ... | @@ -2409,10 +2358,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2409,10 +2358,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2409 | 2358 | ||
| 2410 | // Capture the state of register and stack allocation state so that we can revert to it. | 2359 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 2411 | const parent_next_stack_offset = self.next_stack_offset; | 2360 | const parent_next_stack_offset = self.next_stack_offset; |
| 2412 | const parent_free_registers = self.free_registers; | 2361 | const parent_free_registers = self.register_manager.free_registers; |
| 2413 | var parent_stack = try self.stack.clone(self.gpa); | 2362 | var parent_stack = try self.stack.clone(self.gpa); |
| 2414 | defer parent_stack.deinit(self.gpa); | 2363 | defer parent_stack.deinit(self.gpa); |
| 2415 | var parent_registers = try self.registers.clone(self.gpa); | 2364 | var parent_registers = try self.register_manager.registers.clone(self.gpa); |
| 2416 | defer parent_registers.deinit(self.gpa); | 2365 | defer parent_registers.deinit(self.gpa); |
| 2417 | 2366 | ||
| 2418 | try self.branch_stack.append(.{}); | 2367 | try self.branch_stack.append(.{}); |
| ... | @@ -2429,8 +2378,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2429,8 +2378,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2429 | var saved_then_branch = self.branch_stack.pop(); | 2378 | var saved_then_branch = self.branch_stack.pop(); |
| 2430 | defer saved_then_branch.deinit(self.gpa); | 2379 | defer saved_then_branch.deinit(self.gpa); |
| 2431 | 2380 | ||
| 2432 | self.registers.deinit(self.gpa); | 2381 | self.register_manager.registers.deinit(self.gpa); |
| 2433 | self.registers = parent_registers; | 2382 | self.register_manager.registers = parent_registers; |
| 2434 | parent_registers = .{}; | 2383 | parent_registers = .{}; |
| 2435 | 2384 | ||
| 2436 | self.stack.deinit(self.gpa); | 2385 | self.stack.deinit(self.gpa); |
| ... | @@ -2438,7 +2387,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2438,7 +2387,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2438 | parent_stack = .{}; | 2387 | parent_stack = .{}; |
| 2439 | 2388 | ||
| 2440 | self.next_stack_offset = parent_next_stack_offset; | 2389 | self.next_stack_offset = parent_next_stack_offset; |
| 2441 | self.free_registers = parent_free_registers; | 2390 | self.register_manager.free_registers = parent_free_registers; |
| 2442 | 2391 | ||
| 2443 | try self.performReloc(inst.base.src, reloc); | 2392 | try self.performReloc(inst.base.src, reloc); |
| 2444 | const else_branch = self.branch_stack.addOneAssumeCapacity(); | 2393 | const else_branch = self.branch_stack.addOneAssumeCapacity(); |
| ... | @@ -2552,6 +2501,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2552,6 +2501,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2552 | return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{}); | 2501 | return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{}); |
| 2553 | } | 2502 | } |
| 2554 | 2503 | ||
| 2504 | fn genErrorToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | ||
| 2505 | return self.resolveInst(inst.operand); | ||
| 2506 | } | ||
| 2507 | |||
| 2508 | fn genIntToError(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | ||
| 2509 | return self.resolveInst(inst.operand); | ||
| 2510 | } | ||
| 2511 | |||
| 2555 | fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue { | 2512 | fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue { |
| 2556 | // A loop is a setup to be able to jump back to the beginning. | 2513 | // A loop is a setup to be able to jump back to the beginning. |
| 2557 | const start_index = self.code.items.len; | 2514 | const start_index = self.code.items.len; |
| ... | @@ -2561,7 +2518,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2561,7 +2518,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2561 | } | 2518 | } |
| 2562 | 2519 | ||
| 2563 | /// Send control flow to the `index` of `self.code`. | 2520 | /// Send control flow to the `index` of `self.code`. |
| 2564 | fn jump(self: *Self, src: usize, index: usize) !void { | 2521 | fn jump(self: *Self, src: LazySrcLoc, index: usize) !void { |
| 2565 | switch (arch) { | 2522 | switch (arch) { |
| 2566 | .i386, .x86_64 => { | 2523 | .i386, .x86_64 => { |
| 2567 | try self.code.ensureCapacity(self.code.items.len + 5); | 2524 | try self.code.ensureCapacity(self.code.items.len + 5); |
| ... | @@ -2618,7 +2575,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2618,7 +2575,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2618 | } | 2575 | } |
| 2619 | } | 2576 | } |
| 2620 | 2577 | ||
| 2621 | fn performReloc(self: *Self, src: usize, reloc: Reloc) !void { | 2578 | fn performReloc(self: *Self, src: LazySrcLoc, reloc: Reloc) !void { |
| 2622 | switch (reloc) { | 2579 | switch (reloc) { |
| 2623 | .rel32 => |pos| { | 2580 | .rel32 => |pos| { |
| 2624 | const amt = self.code.items.len - (pos + 4); | 2581 | const amt = self.code.items.len - (pos + 4); |
| ... | @@ -2682,7 +2639,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2682,7 +2639,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2682 | } | 2639 | } |
| 2683 | } | 2640 | } |
| 2684 | 2641 | ||
| 2685 | fn br(self: *Self, src: usize, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue { | 2642 | fn br(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue { |
| 2686 | if (operand.ty.hasCodeGenBits()) { | 2643 | if (operand.ty.hasCodeGenBits()) { |
| 2687 | const operand_mcv = try self.resolveInst(operand); | 2644 | const operand_mcv = try self.resolveInst(operand); |
| 2688 | const block_mcv = @bitCast(MCValue, block.codegen.mcv); | 2645 | const block_mcv = @bitCast(MCValue, block.codegen.mcv); |
| ... | @@ -2695,7 +2652,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2695,7 +2652,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2695 | return self.brVoid(src, block); | 2652 | return self.brVoid(src, block); |
| 2696 | } | 2653 | } |
| 2697 | 2654 | ||
| 2698 | fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue { | 2655 | fn brVoid(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block) !MCValue { |
| 2699 | // Emit a jump with a relocation. It will be patched up after the block ends. | 2656 | // Emit a jump with a relocation. It will be patched up after the block ends. |
| 2700 | try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1); | 2657 | try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1); |
| 2701 | 2658 | ||
| ... | @@ -2757,7 +2714,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2757,7 +2714,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2757 | return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{}); | 2714 | return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{}); |
| 2758 | } | 2715 | } |
| 2759 | 2716 | ||
| 2760 | if (inst.output) |output| { | 2717 | if (inst.output_name) |output| { |
| 2761 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { | 2718 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2762 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); | 2719 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2763 | } | 2720 | } |
| ... | @@ -2789,7 +2746,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2789,7 +2746,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2789 | return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{}); | 2746 | return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{}); |
| 2790 | } | 2747 | } |
| 2791 | 2748 | ||
| 2792 | if (inst.output) |output| { | 2749 | if (inst.output_name) |output| { |
| 2793 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { | 2750 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2794 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); | 2751 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2795 | } | 2752 | } |
| ... | @@ -2819,7 +2776,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2819,7 +2776,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2819 | return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{}); | 2776 | return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{}); |
| 2820 | } | 2777 | } |
| 2821 | 2778 | ||
| 2822 | if (inst.output) |output| { | 2779 | if (inst.output_name) |output| { |
| 2823 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { | 2780 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2824 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); | 2781 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2825 | } | 2782 | } |
| ... | @@ -2849,7 +2806,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2849,7 +2806,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2849 | return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); | 2806 | return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); |
| 2850 | } | 2807 | } |
| 2851 | 2808 | ||
| 2852 | if (inst.output) |output| { | 2809 | if (inst.output_name) |output| { |
| 2853 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { | 2810 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2854 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); | 2811 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2855 | } | 2812 | } |
| ... | @@ -2899,7 +2856,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2899,7 +2856,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2899 | } | 2856 | } |
| 2900 | 2857 | ||
| 2901 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. | 2858 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. |
| 2902 | fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void { | 2859 | fn setRegOrMem(self: *Self, src: LazySrcLoc, ty: Type, loc: MCValue, val: MCValue) !void { |
| 2903 | switch (loc) { | 2860 | switch (loc) { |
| 2904 | .none => return, | 2861 | .none => return, |
| 2905 | .register => |reg| return self.genSetReg(src, ty, reg, val), | 2862 | .register => |reg| return self.genSetReg(src, ty, reg, val), |
| ... | @@ -2911,7 +2868,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2911,7 +2868,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2911 | } | 2868 | } |
| 2912 | } | 2869 | } |
| 2913 | 2870 | ||
| 2914 | fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { | 2871 | fn genSetStack(self: *Self, src: LazySrcLoc, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { |
| 2915 | switch (arch) { | 2872 | switch (arch) { |
| 2916 | .arm, .armeb => switch (mcv) { | 2873 | .arm, .armeb => switch (mcv) { |
| 2917 | .dead => unreachable, | 2874 | .dead => unreachable, |
| ... | @@ -3111,7 +3068,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3111,7 +3068,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3111 | const adj_off = stack_offset + abi_size; | 3068 | const adj_off = stack_offset + abi_size; |
| 3112 | 3069 | ||
| 3113 | switch (abi_size) { | 3070 | switch (abi_size) { |
| 3114 | 4, 8 => { | 3071 | 1, 2, 4, 8 => { |
| 3115 | const offset = if (math.cast(i9, adj_off)) |imm| | 3072 | const offset = if (math.cast(i9, adj_off)) |imm| |
| 3116 | Instruction.LoadStoreOffset.imm_post_index(-imm) | 3073 | Instruction.LoadStoreOffset.imm_post_index(-imm) |
| 3117 | else |_| | 3074 | else |_| |
| ... | @@ -3121,8 +3078,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3121,8 +3078,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3121 | .aarch64_32 => .w29, | 3078 | .aarch64_32 => .w29, |
| 3122 | else => unreachable, | 3079 | else => unreachable, |
| 3123 | }; | 3080 | }; |
| 3081 | const str = switch (abi_size) { | ||
| 3082 | 1 => Instruction.strb, | ||
| 3083 | 2 => Instruction.strh, | ||
| 3084 | 4, 8 => Instruction.str, | ||
| 3085 | else => unreachable, // unexpected abi size | ||
| 3086 | }; | ||
| 3124 | 3087 | ||
| 3125 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.str(reg, rn, .{ | 3088 | writeInt(u32, try self.code.addManyAsArray(4), str(reg, rn, .{ |
| 3126 | .offset = offset, | 3089 | .offset = offset, |
| 3127 | }).toU32()); | 3090 | }).toU32()); |
| 3128 | }, | 3091 | }, |
| ... | @@ -3144,7 +3107,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3144,7 +3107,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3144 | } | 3107 | } |
| 3145 | } | 3108 | } |
| 3146 | 3109 | ||
| 3147 | fn genSetReg(self: *Self, src: usize, ty: Type, reg: Register, mcv: MCValue) InnerError!void { | 3110 | fn genSetReg(self: *Self, src: LazySrcLoc, ty: Type, reg: Register, mcv: MCValue) InnerError!void { |
| 3148 | switch (arch) { | 3111 | switch (arch) { |
| 3149 | .arm, .armeb => switch (mcv) { | 3112 | .arm, .armeb => switch (mcv) { |
| 3150 | .dead => unreachable, | 3113 | .dead => unreachable, |
| ... | @@ -3687,7 +3650,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3687,7 +3650,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3687 | return mcv; | 3650 | return mcv; |
| 3688 | } | 3651 | } |
| 3689 | 3652 | ||
| 3690 | fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) InnerError!MCValue { | 3653 | fn genTypedValue(self: *Self, src: LazySrcLoc, typed_value: TypedValue) InnerError!MCValue { |
| 3691 | if (typed_value.val.isUndef()) | 3654 | if (typed_value.val.isUndef()) |
| 3692 | return MCValue{ .undef = {} }; | 3655 | return MCValue{ .undef = {} }; |
| 3693 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 3656 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| ... | @@ -3762,7 +3725,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3762,7 +3725,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3762 | }; | 3725 | }; |
| 3763 | 3726 | ||
| 3764 | /// Caller must call `CallMCValues.deinit`. | 3727 | /// Caller must call `CallMCValues.deinit`. |
| 3765 | fn resolveCallingConventionValues(self: *Self, src: usize, fn_ty: Type) !CallMCValues { | 3728 | fn resolveCallingConventionValues(self: *Self, src: LazySrcLoc, fn_ty: Type) !CallMCValues { |
| 3766 | const cc = fn_ty.fnCallingConvention(); | 3729 | const cc = fn_ty.fnCallingConvention(); |
| 3767 | const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); | 3730 | const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); |
| 3768 | defer self.gpa.free(param_types); | 3731 | defer self.gpa.free(param_types); |
| ... | @@ -3976,13 +3939,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3976,13 +3939,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3976 | }; | 3939 | }; |
| 3977 | } | 3940 | } |
| 3978 | 3941 | ||
| 3979 | fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) InnerError { | 3942 | fn fail(self: *Self, src: LazySrcLoc, comptime format: []const u8, args: anytype) InnerError { |
| 3980 | @setCold(true); | 3943 | @setCold(true); |
| 3981 | assert(self.err_msg == null); | 3944 | assert(self.err_msg == null); |
| 3982 | self.err_msg = try ErrorMsg.create(self.bin_file.allocator, .{ | 3945 | const src_loc = if (src != .unneeded) |
| 3983 | .file_scope = self.src_loc.file_scope, | 3946 | src.toSrcLocWithDecl(self.mod_fn.owner_decl) |
| 3984 | .byte_offset = src, | 3947 | else |
| 3985 | }, format, args); | 3948 | self.src_loc; |
| 3949 | self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src_loc, format, args); | ||
| 3986 | return error.CodegenFail; | 3950 | return error.CodegenFail; |
| 3987 | } | 3951 | } |
| 3988 | 3952 | ||
| ... | @@ -4012,9 +3976,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -4012,9 +3976,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4012 | }, | 3976 | }, |
| 4013 | }; | 3977 | }; |
| 4014 | 3978 | ||
| 4015 | /// An integer whose bits represent all the registers and whether they are free. | ||
| 4016 | const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len); | ||
| 4017 | |||
| 4018 | fn parseRegName(name: []const u8) ?Register { | 3979 | fn parseRegName(name: []const u8) ?Register { |
| 4019 | if (@hasDecl(Register, "parseRegName")) { | 3980 | if (@hasDecl(Register, "parseRegName")) { |
| 4020 | return Register.parseRegName(name); | 3981 | return Register.parseRegName(name); |
src/codegen/aarch64.zig+55-31| ... | @@ -484,7 +484,23 @@ pub const Instruction = union(enum) { | ... | @@ -484,7 +484,23 @@ pub const Instruction = union(enum) { |
| 484 | } | 484 | } |
| 485 | }; | 485 | }; |
| 486 | 486 | ||
| 487 | fn loadStoreRegister(rt: Register, rn: Register, offset: LoadStoreOffset, load: bool) Instruction { | 487 | /// Which kind of load/store to perform |
| 488 | const LoadStoreVariant = enum { | ||
| 489 | /// 32-bit or 64-bit | ||
| 490 | normal, | ||
| 491 | /// 16-bit | ||
| 492 | half, | ||
| 493 | /// 8-bit | ||
| 494 | byte, | ||
| 495 | }; | ||
| 496 | |||
| 497 | fn loadStoreRegister( | ||
| 498 | rt: Register, | ||
| 499 | rn: Register, | ||
| 500 | offset: LoadStoreOffset, | ||
| 501 | variant: LoadStoreVariant, | ||
| 502 | load: bool, | ||
| 503 | ) Instruction { | ||
| 488 | const off = offset.toU12(); | 504 | const off = offset.toU12(); |
| 489 | const op1: u2 = blk: { | 505 | const op1: u2 = blk: { |
| 490 | switch (offset) { | 506 | switch (offset) { |
| ... | @@ -497,35 +513,27 @@ pub const Instruction = union(enum) { | ... | @@ -497,35 +513,27 @@ pub const Instruction = union(enum) { |
| 497 | break :blk 0b00; | 513 | break :blk 0b00; |
| 498 | }; | 514 | }; |
| 499 | const opc: u2 = if (load) 0b01 else 0b00; | 515 | const opc: u2 = if (load) 0b01 else 0b00; |
| 500 | switch (rt.size()) { | 516 | return Instruction{ |
| 501 | 32 => { | 517 | .LoadStoreRegister = .{ |
| 502 | return Instruction{ | 518 | .rt = rt.id(), |
| 503 | .LoadStoreRegister = .{ | 519 | .rn = rn.id(), |
| 504 | .rt = rt.id(), | 520 | .offset = off, |
| 505 | .rn = rn.id(), | 521 | .opc = opc, |
| 506 | .offset = offset.toU12(), | 522 | .op1 = op1, |
| 507 | .opc = opc, | 523 | .v = 0, |
| 508 | .op1 = op1, | 524 | .size = blk: { |
| 509 | .v = 0, | 525 | switch (variant) { |
| 510 | .size = 0b10, | 526 | .normal => switch (rt.size()) { |
| 511 | }, | 527 | 32 => break :blk 0b10, |
| 512 | }; | 528 | 64 => break :blk 0b11, |
| 513 | }, | 529 | else => unreachable, // unexpected register size |
| 514 | 64 => { | 530 | }, |
| 515 | return Instruction{ | 531 | .half => break :blk 0b01, |
| 516 | .LoadStoreRegister = .{ | 532 | .byte => break :blk 0b00, |
| 517 | .rt = rt.id(), | 533 | } |
| 518 | .rn = rn.id(), | 534 | }, |
| 519 | .offset = offset.toU12(), | ||
| 520 | .opc = opc, | ||
| 521 | .op1 = op1, | ||
| 522 | .v = 0, | ||
| 523 | .size = 0b11, | ||
| 524 | }, | ||
| 525 | }; | ||
| 526 | }, | 535 | }, |
| 527 | else => unreachable, // unexpected register size | 536 | }; |
| 528 | } | ||
| 529 | } | 537 | } |
| 530 | 538 | ||
| 531 | fn loadStorePairOfRegisters( | 539 | fn loadStorePairOfRegisters( |
| ... | @@ -748,7 +756,7 @@ pub const Instruction = union(enum) { | ... | @@ -748,7 +756,7 @@ pub const Instruction = union(enum) { |
| 748 | 756 | ||
| 749 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { | 757 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { |
| 750 | switch (args) { | 758 | switch (args) { |
| 751 | .register => |info| return loadStoreRegister(rt, info.rn, info.offset, true), | 759 | .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .normal, true), |
| 752 | .literal => |literal| return loadLiteral(rt, literal), | 760 | .literal => |literal| return loadLiteral(rt, literal), |
| 753 | } | 761 | } |
| 754 | } | 762 | } |
| ... | @@ -758,7 +766,15 @@ pub const Instruction = union(enum) { | ... | @@ -758,7 +766,15 @@ pub const Instruction = union(enum) { |
| 758 | }; | 766 | }; |
| 759 | 767 | ||
| 760 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { | 768 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { |
| 761 | return loadStoreRegister(rt, rn, args.offset, false); | 769 | return loadStoreRegister(rt, rn, args.offset, .normal, false); |
| 770 | } | ||
| 771 | |||
| 772 | pub fn strh(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 773 | return loadStoreRegister(rt, rn, args.offset, .half, false); | ||
| 774 | } | ||
| 775 | |||
| 776 | pub fn strb(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 777 | return loadStoreRegister(rt, rn, args.offset, .byte, false); | ||
| 762 | } | 778 | } |
| 763 | 779 | ||
| 764 | // Load or store pair of registers | 780 | // Load or store pair of registers |
| ... | @@ -996,6 +1012,14 @@ test "serialize instructions" { | ... | @@ -996,6 +1012,14 @@ test "serialize instructions" { |
| 996 | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), | 1012 | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), |
| 997 | .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, | 1013 | .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, |
| 998 | }, | 1014 | }, |
| 1015 | .{ // strh w0, [x1] | ||
| 1016 | .inst = Instruction.strh(.w0, .x1, .{}), | ||
| 1017 | .expected = 0b01_111_0_01_00_000000000000_00001_00000, | ||
| 1018 | }, | ||
| 1019 | .{ // strb w8, [x9] | ||
| 1020 | .inst = Instruction.strb(.w8, .x9, .{}), | ||
| 1021 | .expected = 0b00_111_0_01_00_000000000000_01001_01000, | ||
| 1022 | }, | ||
| 999 | .{ // adr x2, #0x8 | 1023 | .{ // adr x2, #0x8 |
| 1000 | .inst = Instruction.adr(.x2, 0x8), | 1024 | .inst = Instruction.adr(.x2, 0x8), |
| 1001 | .expected = 0b0_00_10000_0000000000000000010_00010, | 1025 | .expected = 0b0_00_10000_0000000000000000010_00010, |
src/codegen/c.zig+27-17| ... | @@ -14,6 +14,7 @@ const TypedValue = @import("../TypedValue.zig"); | ... | @@ -14,6 +14,7 @@ const TypedValue = @import("../TypedValue.zig"); |
| 14 | const C = link.File.C; | 14 | const C = link.File.C; |
| 15 | const Decl = Module.Decl; | 15 | const Decl = Module.Decl; |
| 16 | const trace = @import("../tracy.zig").trace; | 16 | const trace = @import("../tracy.zig").trace; |
| 17 | const LazySrcLoc = Module.LazySrcLoc; | ||
| 17 | 18 | ||
| 18 | const Mutability = enum { Const, Mut }; | 19 | const Mutability = enum { Const, Mut }; |
| 19 | 20 | ||
| ... | @@ -145,11 +146,10 @@ pub const DeclGen = struct { | ... | @@ -145,11 +146,10 @@ pub const DeclGen = struct { |
| 145 | error_msg: ?*Module.ErrorMsg, | 146 | error_msg: ?*Module.ErrorMsg, |
| 146 | typedefs: TypedefMap, | 147 | typedefs: TypedefMap, |
| 147 | 148 | ||
| 148 | fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { | 149 | fn fail(dg: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { |
| 149 | dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, .{ | 150 | @setCold(true); |
| 150 | .file_scope = dg.decl.getFileScope(), | 151 | const src_loc = src.toSrcLocWithDecl(dg.decl); |
| 151 | .byte_offset = src, | 152 | dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, src_loc, format, args); |
| 152 | }, format, args); | ||
| 153 | return error.AnalysisFail; | 153 | return error.AnalysisFail; |
| 154 | } | 154 | } |
| 155 | 155 | ||
| ... | @@ -160,7 +160,7 @@ pub const DeclGen = struct { | ... | @@ -160,7 +160,7 @@ pub const DeclGen = struct { |
| 160 | val: Value, | 160 | val: Value, |
| 161 | ) error{ OutOfMemory, AnalysisFail }!void { | 161 | ) error{ OutOfMemory, AnalysisFail }!void { |
| 162 | if (val.isUndef()) { | 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 | switch (t.zigTypeTag()) { | 165 | switch (t.zigTypeTag()) { |
| 166 | .Int => { | 166 | .Int => { |
| ... | @@ -193,7 +193,7 @@ pub const DeclGen = struct { | ... | @@ -193,7 +193,7 @@ pub const DeclGen = struct { |
| 193 | try writer.print("{s}", .{decl.name}); | 193 | try writer.print("{s}", .{decl.name}); |
| 194 | }, | 194 | }, |
| 195 | else => |e| return dg.fail( | 195 | else => |e| return dg.fail( |
| 196 | dg.decl.src(), | 196 | .{ .node_offset = 0 }, |
| 197 | "TODO: C backend: implement Pointer value {s}", | 197 | "TODO: C backend: implement Pointer value {s}", |
| 198 | .{@tagName(e)}, | 198 | .{@tagName(e)}, |
| 199 | ), | 199 | ), |
| ... | @@ -276,7 +276,7 @@ pub const DeclGen = struct { | ... | @@ -276,7 +276,7 @@ pub const DeclGen = struct { |
| 276 | try writer.writeAll(", .error = 0 }"); | 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 | @tagName(e), | 280 | @tagName(e), |
| 281 | }), | 281 | }), |
| 282 | } | 282 | } |
| ... | @@ -350,7 +350,7 @@ pub const DeclGen = struct { | ... | @@ -350,7 +350,7 @@ pub const DeclGen = struct { |
| 350 | break; | 350 | break; |
| 351 | } | 351 | } |
| 352 | } else { | 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 | else => unreachable, | 356 | else => unreachable, |
| ... | @@ -358,7 +358,7 @@ pub const DeclGen = struct { | ... | @@ -358,7 +358,7 @@ pub const DeclGen = struct { |
| 358 | }, | 358 | }, |
| 359 | .Pointer => { | 359 | .Pointer => { |
| 360 | if (t.isSlice()) { | 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 | } else { | 362 | } else { |
| 363 | try dg.renderType(w, t.elemType()); | 363 | try dg.renderType(w, t.elemType()); |
| 364 | try w.writeAll(" *"); | 364 | try w.writeAll(" *"); |
| ... | @@ -431,7 +431,7 @@ pub const DeclGen = struct { | ... | @@ -431,7 +431,7 @@ pub const DeclGen = struct { |
| 431 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | 431 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 432 | }, | 432 | }, |
| 433 | .Null, .Undefined => unreachable, // must be const or comptime | 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 | @tagName(e), | 435 | @tagName(e), |
| 436 | }), | 436 | }), |
| 437 | } | 437 | } |
| ... | @@ -569,13 +569,15 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi | ... | @@ -569,13 +569,15 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 569 | .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?), | 569 | .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?), |
| 570 | .is_err => try genIsErr(o, inst.castTag(.is_err).?), | 570 | .is_err => try genIsErr(o, inst.castTag(.is_err).?), |
| 571 | .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), | 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 | .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), | 574 | .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), |
| 573 | .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), | 575 | .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), |
| 574 | .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?), | 576 | .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?), |
| 575 | .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?), | 577 | .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?), |
| 576 | .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?), | 578 | .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?), |
| 577 | .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?), | 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 | switch (result_value) { | 582 | switch (result_value) { |
| 581 | .none => {}, | 583 | .none => {}, |
| ... | @@ -756,11 +758,11 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue { | ... | @@ -756,11 +758,11 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue { |
| 756 | try writer.writeAll(");\n"); | 758 | try writer.writeAll(");\n"); |
| 757 | return result_local; | 759 | return result_local; |
| 758 | } else { | 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 | // TODO emit #line directive here with line number and filename | 766 | // TODO emit #line directive here with line number and filename |
| 765 | return CValue.none; | 767 | return CValue.none; |
| 766 | } | 768 | } |
| ... | @@ -913,13 +915,13 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { | ... | @@ -913,13 +915,13 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { |
| 913 | try o.writeCValue(writer, arg_c_value); | 915 | try o.writeCValue(writer, arg_c_value); |
| 914 | try writer.writeAll(";\n"); | 916 | try writer.writeAll(";\n"); |
| 915 | } else { | 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 | const volatile_string: []const u8 = if (as.is_volatile) "volatile " else ""; | 921 | const volatile_string: []const u8 = if (as.is_volatile) "volatile " else ""; |
| 920 | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source }); | 922 | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source }); |
| 921 | if (as.output) |_| { | 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 | if (as.inputs.len > 0) { | 926 | if (as.inputs.len > 0) { |
| 925 | if (as.output == null) { | 927 | if (as.output == null) { |
| ... | @@ -945,7 +947,7 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { | ... | @@ -945,7 +947,7 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { |
| 945 | if (as.base.isUnused()) | 947 | if (as.base.isUnused()) |
| 946 | return CValue.none; | 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 | fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue { | 953 | fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue { |
| ... | @@ -1072,6 +1074,14 @@ fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue { | ... | @@ -1072,6 +1074,14 @@ fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1072 | return local; | 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 | fn IndentWriter(comptime UnderlyingWriter: type) type { | 1085 | fn IndentWriter(comptime UnderlyingWriter: type) type { |
| 1076 | return struct { | 1086 | return struct { |
| 1077 | const Self = @This(); | 1087 | const Self = @This(); |
src/codegen/llvm.zig+400-361| ... | @@ -15,6 +15,8 @@ const Inst = ir.Inst; | ... | @@ -15,6 +15,8 @@ const Inst = ir.Inst; |
| 15 | const Value = @import("../value.zig").Value; | 15 | const Value = @import("../value.zig").Value; |
| 16 | const Type = @import("../type.zig").Type; | 16 | const Type = @import("../type.zig").Type; |
| 17 | 17 | ||
| 18 | const LazySrcLoc = Module.LazySrcLoc; | ||
| 19 | |||
| 18 | pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { | 20 | pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 19 | const llvm_arch = switch (target.cpu.arch) { | 21 | const llvm_arch = switch (target.cpu.arch) { |
| 20 | .arm => "arm", | 22 | .arm => "arm", |
| ... | @@ -146,79 +148,42 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { | ... | @@ -146,79 +148,42 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 146 | return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi }); | 148 | return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi }); |
| 147 | } | 149 | } |
| 148 | 150 | ||
| 149 | pub const LLVMIRModule = struct { | 151 | pub const Object = struct { |
| 150 | module: *Module, | ||
| 151 | llvm_module: *const llvm.Module, | 152 | llvm_module: *const llvm.Module, |
| 152 | context: *const llvm.Context, | 153 | context: *const llvm.Context, |
| 153 | target_machine: *const llvm.TargetMachine, | 154 | target_machine: *const llvm.TargetMachine, |
| 154 | builder: *const llvm.Builder, | 155 | object_pathZ: [:0]const u8, |
| 155 | |||
| 156 | object_path: []const u8, | ||
| 157 | |||
| 158 | gpa: *Allocator, | ||
| 159 | err_msg: ?*Module.ErrorMsg = null, | ||
| 160 | |||
| 161 | // TODO: The fields below should really move into a different struct, | ||
| 162 | // because they are only valid when generating a function | ||
| 163 | |||
| 164 | /// This stores the LLVM values used in a function, such that they can be | ||
| 165 | /// referred to in other instructions. This table is cleared before every function is generated. | ||
| 166 | /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks | ||
| 167 | /// in here, however if a block ends, the instructions can be thrown away. | ||
| 168 | func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{}, | ||
| 169 | |||
| 170 | /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. | ||
| 171 | args: []*const llvm.Value = &[_]*const llvm.Value{}, | ||
| 172 | arg_index: usize = 0, | ||
| 173 | |||
| 174 | entry_block: *const llvm.BasicBlock = undefined, | ||
| 175 | /// This fields stores the last alloca instruction, such that we can append more alloca instructions | ||
| 176 | /// to the top of the function. | ||
| 177 | latest_alloca_inst: ?*const llvm.Value = null, | ||
| 178 | 156 | ||
| 179 | llvm_func: *const llvm.Value = undefined, | 157 | pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Object { |
| 180 | 158 | const self = try allocator.create(Object); | |
| 181 | /// This data structure is used to implement breaking to blocks. | ||
| 182 | blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct { | ||
| 183 | parent_bb: *const llvm.BasicBlock, | ||
| 184 | break_bbs: *BreakBasicBlocks, | ||
| 185 | break_vals: *BreakValues, | ||
| 186 | }) = .{}, | ||
| 187 | |||
| 188 | src_loc: Module.SrcLoc, | ||
| 189 | |||
| 190 | const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock); | ||
| 191 | const BreakValues = std.ArrayListUnmanaged(*const llvm.Value); | ||
| 192 | |||
| 193 | pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule { | ||
| 194 | const self = try allocator.create(LLVMIRModule); | ||
| 195 | errdefer allocator.destroy(self); | 159 | errdefer allocator.destroy(self); |
| 196 | 160 | ||
| 197 | const gpa = options.module.?.gpa; | 161 | const obj_basename = try std.zig.binNameAlloc(allocator, .{ |
| 198 | |||
| 199 | const obj_basename = try std.zig.binNameAlloc(gpa, .{ | ||
| 200 | .root_name = options.root_name, | 162 | .root_name = options.root_name, |
| 201 | .target = options.target, | 163 | .target = options.target, |
| 202 | .output_mode = .Obj, | 164 | .output_mode = .Obj, |
| 203 | }); | 165 | }); |
| 204 | defer gpa.free(obj_basename); | 166 | defer allocator.free(obj_basename); |
| 205 | 167 | ||
| 206 | const o_directory = options.module.?.zig_cache_artifact_directory; | 168 | const o_directory = options.module.?.zig_cache_artifact_directory; |
| 207 | const object_path = try o_directory.join(gpa, &[_][]const u8{obj_basename}); | 169 | const object_path = try o_directory.join(allocator, &[_][]const u8{obj_basename}); |
| 208 | errdefer gpa.free(object_path); | 170 | defer allocator.free(object_path); |
| 171 | |||
| 172 | const object_pathZ = try allocator.dupeZ(u8, object_path); | ||
| 173 | errdefer allocator.free(object_pathZ); | ||
| 209 | 174 | ||
| 210 | const context = llvm.Context.create(); | 175 | const context = llvm.Context.create(); |
| 211 | errdefer context.dispose(); | 176 | errdefer context.dispose(); |
| 212 | 177 | ||
| 213 | initializeLLVMTargets(); | 178 | initializeLLVMTargets(); |
| 214 | 179 | ||
| 215 | const root_nameZ = try gpa.dupeZ(u8, options.root_name); | 180 | const root_nameZ = try allocator.dupeZ(u8, options.root_name); |
| 216 | defer gpa.free(root_nameZ); | 181 | defer allocator.free(root_nameZ); |
| 217 | const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context); | 182 | const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context); |
| 218 | errdefer llvm_module.dispose(); | 183 | errdefer llvm_module.dispose(); |
| 219 | 184 | ||
| 220 | const llvm_target_triple = try targetTriple(gpa, options.target); | 185 | const llvm_target_triple = try targetTriple(allocator, options.target); |
| 221 | defer gpa.free(llvm_target_triple); | 186 | defer allocator.free(llvm_target_triple); |
| 222 | 187 | ||
| 223 | var error_message: [*:0]const u8 = undefined; | 188 | var error_message: [*:0]const u8 = undefined; |
| 224 | var target: *const llvm.Target = undefined; | 189 | var target: *const llvm.Target = undefined; |
| ... | @@ -253,34 +218,21 @@ pub const LLVMIRModule = struct { | ... | @@ -253,34 +218,21 @@ pub const LLVMIRModule = struct { |
| 253 | ); | 218 | ); |
| 254 | errdefer target_machine.dispose(); | 219 | errdefer target_machine.dispose(); |
| 255 | 220 | ||
| 256 | const builder = context.createBuilder(); | ||
| 257 | errdefer builder.dispose(); | ||
| 258 | |||
| 259 | self.* = .{ | 221 | self.* = .{ |
| 260 | .module = options.module.?, | ||
| 261 | .llvm_module = llvm_module, | 222 | .llvm_module = llvm_module, |
| 262 | .context = context, | 223 | .context = context, |
| 263 | .target_machine = target_machine, | 224 | .target_machine = target_machine, |
| 264 | .builder = builder, | 225 | .object_pathZ = object_pathZ, |
| 265 | .object_path = object_path, | ||
| 266 | .gpa = gpa, | ||
| 267 | // TODO move this field into a struct that is only instantiated per gen() call | ||
| 268 | .src_loc = undefined, | ||
| 269 | }; | 226 | }; |
| 270 | return self; | 227 | return self; |
| 271 | } | 228 | } |
| 272 | 229 | ||
| 273 | pub fn deinit(self: *LLVMIRModule, allocator: *Allocator) void { | 230 | pub fn deinit(self: *Object, allocator: *Allocator) void { |
| 274 | self.builder.dispose(); | ||
| 275 | self.target_machine.dispose(); | 231 | self.target_machine.dispose(); |
| 276 | self.llvm_module.dispose(); | 232 | self.llvm_module.dispose(); |
| 277 | self.context.dispose(); | 233 | self.context.dispose(); |
| 278 | 234 | ||
| 279 | self.func_inst_table.deinit(self.gpa); | 235 | allocator.free(self.object_pathZ); |
| 280 | self.gpa.free(self.object_path); | ||
| 281 | |||
| 282 | self.blocks.deinit(self.gpa); | ||
| 283 | |||
| 284 | allocator.destroy(self); | 236 | allocator.destroy(self); |
| 285 | } | 237 | } |
| 286 | 238 | ||
| ... | @@ -292,7 +244,7 @@ pub const LLVMIRModule = struct { | ... | @@ -292,7 +244,7 @@ pub const LLVMIRModule = struct { |
| 292 | llvm.initializeAllAsmParsers(); | 244 | llvm.initializeAllAsmParsers(); |
| 293 | } | 245 | } |
| 294 | 246 | ||
| 295 | pub fn flushModule(self: *LLVMIRModule, comp: *Compilation) !void { | 247 | pub fn flushModule(self: *Object, comp: *Compilation) !void { |
| 296 | if (comp.verbose_llvm_ir) { | 248 | if (comp.verbose_llvm_ir) { |
| 297 | const dump = self.llvm_module.printToString(); | 249 | const dump = self.llvm_module.printToString(); |
| 298 | defer llvm.disposeMessage(dump); | 250 | defer llvm.disposeMessage(dump); |
| ... | @@ -313,13 +265,10 @@ pub const LLVMIRModule = struct { | ... | @@ -313,13 +265,10 @@ pub const LLVMIRModule = struct { |
| 313 | } | 265 | } |
| 314 | } | 266 | } |
| 315 | 267 | ||
| 316 | const object_pathZ = try self.gpa.dupeZ(u8, self.object_path); | ||
| 317 | defer self.gpa.free(object_pathZ); | ||
| 318 | |||
| 319 | var error_message: [*:0]const u8 = undefined; | 268 | var error_message: [*:0]const u8 = undefined; |
| 320 | if (self.target_machine.emitToFile( | 269 | if (self.target_machine.emitToFile( |
| 321 | self.llvm_module, | 270 | self.llvm_module, |
| 322 | object_pathZ.ptr, | 271 | self.object_pathZ.ptr, |
| 323 | .ObjectFile, | 272 | .ObjectFile, |
| 324 | &error_message, | 273 | &error_message, |
| 325 | ).toBool()) { | 274 | ).toBool()) { |
| ... | @@ -331,44 +280,68 @@ pub const LLVMIRModule = struct { | ... | @@ -331,44 +280,68 @@ pub const LLVMIRModule = struct { |
| 331 | } | 280 | } |
| 332 | } | 281 | } |
| 333 | 282 | ||
| 334 | pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { | 283 | pub fn updateDecl(self: *Object, module: *Module, decl: *Module.Decl) !void { |
| 335 | self.gen(module, decl) catch |err| switch (err) { | 284 | var dg: DeclGen = .{ |
| 285 | .object = self, | ||
| 286 | .module = module, | ||
| 287 | .decl = decl, | ||
| 288 | .err_msg = null, | ||
| 289 | .gpa = module.gpa, | ||
| 290 | }; | ||
| 291 | dg.genDecl() catch |err| switch (err) { | ||
| 336 | error.CodegenFail => { | 292 | error.CodegenFail => { |
| 337 | decl.analysis = .codegen_failure; | 293 | decl.analysis = .codegen_failure; |
| 338 | try module.failed_decls.put(module.gpa, decl, self.err_msg.?); | 294 | try module.failed_decls.put(module.gpa, decl, dg.err_msg.?); |
| 339 | self.err_msg = null; | 295 | dg.err_msg = null; |
| 340 | return; | 296 | return; |
| 341 | }, | 297 | }, |
| 342 | else => |e| return e, | 298 | else => |e| return e, |
| 343 | }; | 299 | }; |
| 344 | } | 300 | } |
| 301 | }; | ||
| 345 | 302 | ||
| 346 | fn gen(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { | 303 | pub const DeclGen = struct { |
| 347 | const typed_value = decl.typed_value.most_recent.typed_value; | 304 | object: *Object, |
| 348 | const src = decl.src(); | 305 | module: *Module, |
| 306 | decl: *Module.Decl, | ||
| 307 | err_msg: ?*Module.ErrorMsg, | ||
| 349 | 308 | ||
| 350 | self.src_loc = decl.srcLoc(); | 309 | gpa: *Allocator, |
| 310 | |||
| 311 | fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { | ||
| 312 | @setCold(true); | ||
| 313 | assert(self.err_msg == null); | ||
| 314 | const src_loc = @as(LazySrcLoc, .{ .node_offset = 0 }).toSrcLocWithDecl(self.decl); | ||
| 315 | self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, "TODO (LLVM): " ++ format, args); | ||
| 316 | return error.CodegenFail; | ||
| 317 | } | ||
| 318 | |||
| 319 | fn llvmModule(self: *DeclGen) *const llvm.Module { | ||
| 320 | return self.object.llvm_module; | ||
| 321 | } | ||
| 322 | |||
| 323 | fn context(self: *DeclGen) *const llvm.Context { | ||
| 324 | return self.object.context; | ||
| 325 | } | ||
| 326 | |||
| 327 | fn genDecl(self: *DeclGen) !void { | ||
| 328 | const decl = self.decl; | ||
| 329 | const typed_value = decl.typed_value.most_recent.typed_value; | ||
| 351 | 330 | ||
| 352 | log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val }); | 331 | log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val }); |
| 353 | 332 | ||
| 354 | if (typed_value.val.castTag(.function)) |func_payload| { | 333 | if (typed_value.val.castTag(.function)) |func_payload| { |
| 355 | const func = func_payload.data; | 334 | const func = func_payload.data; |
| 356 | 335 | ||
| 357 | const llvm_func = try self.resolveLLVMFunction(func.owner_decl, src); | 336 | const llvm_func = try self.resolveLLVMFunction(func.owner_decl); |
| 358 | 337 | ||
| 359 | // This gets the LLVM values from the function and stores them in `self.args`. | 338 | // This gets the LLVM values from the function and stores them in `self.args`. |
| 360 | const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen(); | 339 | const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen(); |
| 361 | var args = try self.gpa.alloc(*const llvm.Value, fn_param_len); | 340 | var args = try self.gpa.alloc(*const llvm.Value, fn_param_len); |
| 362 | defer self.gpa.free(args); | ||
| 363 | 341 | ||
| 364 | for (args) |*arg, i| { | 342 | for (args) |*arg, i| { |
| 365 | arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); | 343 | arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); |
| 366 | } | 344 | } |
| 367 | self.args = args; | ||
| 368 | self.arg_index = 0; | ||
| 369 | |||
| 370 | // Make sure no other LLVM values from other functions can be referenced | ||
| 371 | self.func_inst_table.clearRetainingCapacity(); | ||
| 372 | 345 | ||
| 373 | // We remove all the basic blocks of a function to support incremental | 346 | // We remove all the basic blocks of a function to support incremental |
| 374 | // compilation! | 347 | // compilation! |
| ... | @@ -377,20 +350,293 @@ pub const LLVMIRModule = struct { | ... | @@ -377,20 +350,293 @@ pub const LLVMIRModule = struct { |
| 377 | bb.deleteBasicBlock(); | 350 | bb.deleteBasicBlock(); |
| 378 | } | 351 | } |
| 379 | 352 | ||
| 380 | self.entry_block = self.context.appendBasicBlock(llvm_func, "Entry"); | 353 | const builder = self.context().createBuilder(); |
| 381 | self.builder.positionBuilderAtEnd(self.entry_block); | 354 | |
| 382 | self.latest_alloca_inst = null; | 355 | const entry_block = self.context().appendBasicBlock(llvm_func, "Entry"); |
| 383 | self.llvm_func = llvm_func; | 356 | builder.positionBuilderAtEnd(entry_block); |
| 357 | |||
| 358 | var fg: FuncGen = .{ | ||
| 359 | .dg = self, | ||
| 360 | .builder = builder, | ||
| 361 | .args = args, | ||
| 362 | .arg_index = 0, | ||
| 363 | .func_inst_table = .{}, | ||
| 364 | .entry_block = entry_block, | ||
| 365 | .latest_alloca_inst = null, | ||
| 366 | .llvm_func = llvm_func, | ||
| 367 | .blocks = .{}, | ||
| 368 | }; | ||
| 369 | defer fg.deinit(); | ||
| 384 | 370 | ||
| 385 | try self.genBody(func.body); | 371 | try fg.genBody(func.body); |
| 386 | } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| { | 372 | } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| { |
| 387 | _ = try self.resolveLLVMFunction(extern_fn.data, src); | 373 | _ = try self.resolveLLVMFunction(extern_fn.data); |
| 388 | } else { | 374 | } else { |
| 389 | _ = try self.resolveGlobalDecl(decl, src); | 375 | _ = try self.resolveGlobalDecl(decl); |
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | /// If the llvm function does not exist, create it | ||
| 380 | fn resolveLLVMFunction(self: *DeclGen, func: *Module.Decl) !*const llvm.Value { | ||
| 381 | // TODO: do we want to store this in our own datastructure? | ||
| 382 | if (self.llvmModule().getNamedFunction(func.name)) |llvm_fn| return llvm_fn; | ||
| 383 | |||
| 384 | const zig_fn_type = func.typed_value.most_recent.typed_value.ty; | ||
| 385 | const return_type = zig_fn_type.fnReturnType(); | ||
| 386 | |||
| 387 | const fn_param_len = zig_fn_type.fnParamLen(); | ||
| 388 | |||
| 389 | const fn_param_types = try self.gpa.alloc(Type, fn_param_len); | ||
| 390 | defer self.gpa.free(fn_param_types); | ||
| 391 | zig_fn_type.fnParamTypes(fn_param_types); | ||
| 392 | |||
| 393 | const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len); | ||
| 394 | defer self.gpa.free(llvm_param); | ||
| 395 | |||
| 396 | for (fn_param_types) |fn_param, i| { | ||
| 397 | llvm_param[i] = try self.getLLVMType(fn_param); | ||
| 398 | } | ||
| 399 | |||
| 400 | const fn_type = llvm.Type.functionType( | ||
| 401 | try self.getLLVMType(return_type), | ||
| 402 | if (fn_param_len == 0) null else llvm_param.ptr, | ||
| 403 | @intCast(c_uint, fn_param_len), | ||
| 404 | .False, | ||
| 405 | ); | ||
| 406 | const llvm_fn = self.llvmModule().addFunction(func.name, fn_type); | ||
| 407 | |||
| 408 | if (return_type.tag() == .noreturn) { | ||
| 409 | self.addFnAttr(llvm_fn, "noreturn"); | ||
| 410 | } | ||
| 411 | |||
| 412 | return llvm_fn; | ||
| 413 | } | ||
| 414 | |||
| 415 | fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value { | ||
| 416 | // TODO: do we want to store this in our own datastructure? | ||
| 417 | if (self.llvmModule().getNamedGlobal(decl.name)) |val| return val; | ||
| 418 | |||
| 419 | const typed_value = decl.typed_value.most_recent.typed_value; | ||
| 420 | |||
| 421 | // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`. | ||
| 422 | const llvm_type = try self.getLLVMType(typed_value.ty); | ||
| 423 | const val = try self.genTypedValue(typed_value, null); | ||
| 424 | const global = self.llvmModule().addGlobal(llvm_type, decl.name); | ||
| 425 | llvm.setInitializer(global, val); | ||
| 426 | |||
| 427 | // TODO ask the Decl if it is const | ||
| 428 | // https://github.com/ziglang/zig/issues/7582 | ||
| 429 | |||
| 430 | return global; | ||
| 431 | } | ||
| 432 | |||
| 433 | fn getLLVMType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type { | ||
| 434 | switch (t.zigTypeTag()) { | ||
| 435 | .Void => return self.context().voidType(), | ||
| 436 | .NoReturn => return self.context().voidType(), | ||
| 437 | .Int => { | ||
| 438 | const info = t.intInfo(self.module.getTarget()); | ||
| 439 | return self.context().intType(info.bits); | ||
| 440 | }, | ||
| 441 | .Bool => return self.context().intType(1), | ||
| 442 | .Pointer => { | ||
| 443 | if (t.isSlice()) { | ||
| 444 | return self.todo("implement slices", .{}); | ||
| 445 | } else { | ||
| 446 | const elem_type = try self.getLLVMType(t.elemType()); | ||
| 447 | return elem_type.pointerType(0); | ||
| 448 | } | ||
| 449 | }, | ||
| 450 | .Array => { | ||
| 451 | const elem_type = try self.getLLVMType(t.elemType()); | ||
| 452 | return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); | ||
| 453 | }, | ||
| 454 | .Optional => { | ||
| 455 | if (!t.isPtrLikeOptional()) { | ||
| 456 | var buf: Type.Payload.ElemType = undefined; | ||
| 457 | const child_type = t.optionalChild(&buf); | ||
| 458 | |||
| 459 | var optional_types: [2]*const llvm.Type = .{ | ||
| 460 | try self.getLLVMType(child_type), | ||
| 461 | self.context().intType(1), | ||
| 462 | }; | ||
| 463 | return self.context().structType(&optional_types, 2, .False); | ||
| 464 | } else { | ||
| 465 | return self.todo("implement optional pointers as actual pointers", .{}); | ||
| 466 | } | ||
| 467 | }, | ||
| 468 | else => return self.todo("implement getLLVMType for type '{}'", .{t}), | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | // TODO: figure out a way to remove the FuncGen argument | ||
| 473 | fn genTypedValue(self: *DeclGen, tv: TypedValue, fg: ?*FuncGen) error{ OutOfMemory, CodegenFail }!*const llvm.Value { | ||
| 474 | const llvm_type = try self.getLLVMType(tv.ty); | ||
| 475 | |||
| 476 | if (tv.val.isUndef()) | ||
| 477 | return llvm_type.getUndef(); | ||
| 478 | |||
| 479 | switch (tv.ty.zigTypeTag()) { | ||
| 480 | .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), | ||
| 481 | .Int => { | ||
| 482 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 483 | const bigint = tv.val.toBigInt(&bigint_space); | ||
| 484 | |||
| 485 | if (bigint.eqZero()) return llvm_type.constNull(); | ||
| 486 | |||
| 487 | if (bigint.limbs.len != 1) { | ||
| 488 | return self.todo("implement bigger bigint", .{}); | ||
| 489 | } | ||
| 490 | const llvm_int = llvm_type.constInt(bigint.limbs[0], .False); | ||
| 491 | if (!bigint.positive) { | ||
| 492 | return llvm.constNeg(llvm_int); | ||
| 493 | } | ||
| 494 | return llvm_int; | ||
| 495 | }, | ||
| 496 | .Pointer => switch (tv.val.tag()) { | ||
| 497 | .decl_ref => { | ||
| 498 | const decl = tv.val.castTag(.decl_ref).?.data; | ||
| 499 | const val = try self.resolveGlobalDecl(decl); | ||
| 500 | |||
| 501 | const usize_type = try self.getLLVMType(Type.initTag(.usize)); | ||
| 502 | |||
| 503 | // TODO: second index should be the index into the memory! | ||
| 504 | var indices: [2]*const llvm.Value = .{ | ||
| 505 | usize_type.constNull(), | ||
| 506 | usize_type.constNull(), | ||
| 507 | }; | ||
| 508 | |||
| 509 | // TODO: consider using buildInBoundsGEP2 for opaque pointers | ||
| 510 | return fg.?.builder.buildInBoundsGEP(val, &indices, 2, ""); | ||
| 511 | }, | ||
| 512 | .ref_val => { | ||
| 513 | const elem_value = tv.val.castTag(.ref_val).?.data; | ||
| 514 | const elem_type = tv.ty.castPointer().?.data; | ||
| 515 | const alloca = fg.?.buildAlloca(try self.getLLVMType(elem_type)); | ||
| 516 | _ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca); | ||
| 517 | return alloca; | ||
| 518 | }, | ||
| 519 | else => return self.todo("implement const of pointer type '{}'", .{tv.ty}), | ||
| 520 | }, | ||
| 521 | .Array => { | ||
| 522 | if (tv.val.castTag(.bytes)) |payload| { | ||
| 523 | const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: { | ||
| 524 | if (sentinel.tag() == .zero) break :blk true; | ||
| 525 | return self.todo("handle other sentinel values", .{}); | ||
| 526 | } else false; | ||
| 527 | |||
| 528 | return self.context().constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel)); | ||
| 529 | } else { | ||
| 530 | return self.todo("handle more array values", .{}); | ||
| 531 | } | ||
| 532 | }, | ||
| 533 | .Optional => { | ||
| 534 | if (!tv.ty.isPtrLikeOptional()) { | ||
| 535 | var buf: Type.Payload.ElemType = undefined; | ||
| 536 | const child_type = tv.ty.optionalChild(&buf); | ||
| 537 | const llvm_child_type = try self.getLLVMType(child_type); | ||
| 538 | |||
| 539 | if (tv.val.tag() == .null_value) { | ||
| 540 | var optional_values: [2]*const llvm.Value = .{ | ||
| 541 | llvm_child_type.constNull(), | ||
| 542 | self.context().intType(1).constNull(), | ||
| 543 | }; | ||
| 544 | return self.context().constStruct(&optional_values, 2, .False); | ||
| 545 | } else { | ||
| 546 | var optional_values: [2]*const llvm.Value = .{ | ||
| 547 | try self.genTypedValue(.{ .ty = child_type, .val = tv.val }, fg), | ||
| 548 | self.context().intType(1).constAllOnes(), | ||
| 549 | }; | ||
| 550 | return self.context().constStruct(&optional_values, 2, .False); | ||
| 551 | } | ||
| 552 | } else { | ||
| 553 | return self.todo("implement const of optional pointer", .{}); | ||
| 554 | } | ||
| 555 | }, | ||
| 556 | else => return self.todo("implement const of type '{}'", .{tv.ty}), | ||
| 557 | } | ||
| 558 | } | ||
| 559 | |||
| 560 | // Helper functions | ||
| 561 | fn addAttr(self: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { | ||
| 562 | const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); | ||
| 563 | assert(kind_id != 0); | ||
| 564 | const llvm_attr = self.context().createEnumAttribute(kind_id, 0); | ||
| 565 | val.addAttributeAtIndex(index, llvm_attr); | ||
| 566 | } | ||
| 567 | |||
| 568 | fn addFnAttr(self: *DeclGen, val: *const llvm.Value, attr_name: []const u8) void { | ||
| 569 | // TODO: improve this API, `addAttr(-1, attr_name)` | ||
| 570 | self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name); | ||
| 571 | } | ||
| 572 | }; | ||
| 573 | |||
| 574 | pub const FuncGen = struct { | ||
| 575 | dg: *DeclGen, | ||
| 576 | |||
| 577 | builder: *const llvm.Builder, | ||
| 578 | |||
| 579 | /// This stores the LLVM values used in a function, such that they can be | ||
| 580 | /// referred to in other instructions. This table is cleared before every function is generated. | ||
| 581 | /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks | ||
| 582 | /// in here, however if a block ends, the instructions can be thrown away. | ||
| 583 | func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value), | ||
| 584 | |||
| 585 | /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. | ||
| 586 | args: []*const llvm.Value, | ||
| 587 | arg_index: usize, | ||
| 588 | |||
| 589 | entry_block: *const llvm.BasicBlock, | ||
| 590 | /// This fields stores the last alloca instruction, such that we can append more alloca instructions | ||
| 591 | /// to the top of the function. | ||
| 592 | latest_alloca_inst: ?*const llvm.Value, | ||
| 593 | |||
| 594 | llvm_func: *const llvm.Value, | ||
| 595 | |||
| 596 | /// This data structure is used to implement breaking to blocks. | ||
| 597 | blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct { | ||
| 598 | parent_bb: *const llvm.BasicBlock, | ||
| 599 | break_bbs: *BreakBasicBlocks, | ||
| 600 | break_vals: *BreakValues, | ||
| 601 | }), | ||
| 602 | |||
| 603 | const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock); | ||
| 604 | const BreakValues = std.ArrayListUnmanaged(*const llvm.Value); | ||
| 605 | |||
| 606 | fn deinit(self: *FuncGen) void { | ||
| 607 | self.builder.dispose(); | ||
| 608 | self.func_inst_table.deinit(self.gpa()); | ||
| 609 | self.gpa().free(self.args); | ||
| 610 | self.blocks.deinit(self.gpa()); | ||
| 611 | } | ||
| 612 | |||
| 613 | fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { | ||
| 614 | @setCold(true); | ||
| 615 | return self.dg.todo(format, args); | ||
| 616 | } | ||
| 617 | |||
| 618 | fn llvmModule(self: *FuncGen) *const llvm.Module { | ||
| 619 | return self.dg.object.llvm_module; | ||
| 620 | } | ||
| 621 | |||
| 622 | fn context(self: *FuncGen) *const llvm.Context { | ||
| 623 | return self.dg.object.context; | ||
| 624 | } | ||
| 625 | |||
| 626 | fn gpa(self: *FuncGen) *Allocator { | ||
| 627 | return self.dg.gpa; | ||
| 628 | } | ||
| 629 | |||
| 630 | fn resolveInst(self: *FuncGen, inst: *ir.Inst) !*const llvm.Value { | ||
| 631 | if (inst.value()) |val| { | ||
| 632 | return self.dg.genTypedValue(.{ .ty = inst.ty, .val = val }, self); | ||
| 390 | } | 633 | } |
| 634 | if (self.func_inst_table.get(inst)) |value| return value; | ||
| 635 | |||
| 636 | return self.todo("implement global llvm values (or the value is not in the func_inst_table table)", .{}); | ||
| 391 | } | 637 | } |
| 392 | 638 | ||
| 393 | fn genBody(self: *LLVMIRModule, body: ir.Body) error{ OutOfMemory, CodegenFail }!void { | 639 | fn genBody(self: *FuncGen, body: ir.Body) error{ OutOfMemory, CodegenFail }!void { |
| 394 | for (body.instructions) |inst| { | 640 | for (body.instructions) |inst| { |
| 395 | const opt_value = switch (inst.tag) { | 641 | const opt_value = switch (inst.tag) { |
| 396 | .add => try self.genAdd(inst.castTag(.add).?), | 642 | .add => try self.genAdd(inst.castTag(.add).?), |
| ... | @@ -428,13 +674,13 @@ pub const LLVMIRModule = struct { | ... | @@ -428,13 +674,13 @@ pub const LLVMIRModule = struct { |
| 428 | // TODO: implement debug info | 674 | // TODO: implement debug info |
| 429 | break :blk null; | 675 | break :blk null; |
| 430 | }, | 676 | }, |
| 431 | else => |tag| return self.fail(inst.src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), | 677 | else => |tag| return self.todo("implement TZIR instruction: {}", .{tag}), |
| 432 | }; | 678 | }; |
| 433 | if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val); | 679 | if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa(), inst, val); |
| 434 | } | 680 | } |
| 435 | } | 681 | } |
| 436 | 682 | ||
| 437 | fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value { | 683 | fn genCall(self: *FuncGen, inst: *Inst.Call) !?*const llvm.Value { |
| 438 | if (inst.func.value()) |func_value| { | 684 | if (inst.func.value()) |func_value| { |
| 439 | const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| | 685 | const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| |
| 440 | extern_fn.data | 686 | extern_fn.data |
| ... | @@ -444,12 +690,12 @@ pub const LLVMIRModule = struct { | ... | @@ -444,12 +690,12 @@ pub const LLVMIRModule = struct { |
| 444 | unreachable; | 690 | unreachable; |
| 445 | 691 | ||
| 446 | const zig_fn_type = fn_decl.typed_value.most_recent.typed_value.ty; | 692 | const zig_fn_type = fn_decl.typed_value.most_recent.typed_value.ty; |
| 447 | const llvm_fn = try self.resolveLLVMFunction(fn_decl, inst.base.src); | 693 | const llvm_fn = try self.dg.resolveLLVMFunction(fn_decl); |
| 448 | 694 | ||
| 449 | const num_args = inst.args.len; | 695 | const num_args = inst.args.len; |
| 450 | 696 | ||
| 451 | const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, num_args); | 697 | const llvm_param_vals = try self.gpa().alloc(*const llvm.Value, num_args); |
| 452 | defer self.gpa.free(llvm_param_vals); | 698 | defer self.gpa().free(llvm_param_vals); |
| 453 | 699 | ||
| 454 | for (inst.args) |arg, i| { | 700 | for (inst.args) |arg, i| { |
| 455 | llvm_param_vals[i] = try self.resolveInst(arg); | 701 | llvm_param_vals[i] = try self.resolveInst(arg); |
| ... | @@ -474,27 +720,32 @@ pub const LLVMIRModule = struct { | ... | @@ -474,27 +720,32 @@ pub const LLVMIRModule = struct { |
| 474 | 720 | ||
| 475 | return call; | 721 | return call; |
| 476 | } else { | 722 | } else { |
| 477 | return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{}); | 723 | return self.todo("implement calling runtime known function pointer", .{}); |
| 478 | } | 724 | } |
| 479 | } | 725 | } |
| 480 | 726 | ||
| 481 | fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value { | 727 | fn genRetVoid(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value { |
| 482 | _ = self.builder.buildRetVoid(); | 728 | _ = self.builder.buildRetVoid(); |
| 483 | return null; | 729 | return null; |
| 484 | } | 730 | } |
| 485 | 731 | ||
| 486 | fn genRet(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { | 732 | fn genRet(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { |
| 733 | if (!inst.operand.ty.hasCodeGenBits()) { | ||
| 734 | // TODO: in astgen these instructions should turn into `retvoid` instructions. | ||
| 735 | _ = self.builder.buildRetVoid(); | ||
| 736 | return null; | ||
| 737 | } | ||
| 487 | _ = self.builder.buildRet(try self.resolveInst(inst.operand)); | 738 | _ = self.builder.buildRet(try self.resolveInst(inst.operand)); |
| 488 | return null; | 739 | return null; |
| 489 | } | 740 | } |
| 490 | 741 | ||
| 491 | fn genCmp(self: *LLVMIRModule, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value { | 742 | fn genCmp(self: *FuncGen, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value { |
| 492 | const lhs = try self.resolveInst(inst.lhs); | 743 | const lhs = try self.resolveInst(inst.lhs); |
| 493 | const rhs = try self.resolveInst(inst.rhs); | 744 | const rhs = try self.resolveInst(inst.rhs); |
| 494 | 745 | ||
| 495 | if (!inst.base.ty.isInt()) | 746 | if (!inst.base.ty.isInt()) |
| 496 | if (inst.base.ty.tag() != .bool) | 747 | if (inst.base.ty.tag() != .bool) |
| 497 | return self.fail(inst.base.src, "TODO implement 'genCmp' for type {}", .{inst.base.ty}); | 748 | return self.todo("implement 'genCmp' for type {}", .{inst.base.ty}); |
| 498 | 749 | ||
| 499 | const is_signed = inst.base.ty.isSignedInt(); | 750 | const is_signed = inst.base.ty.isSignedInt(); |
| 500 | const operation = switch (op) { | 751 | const operation = switch (op) { |
| ... | @@ -509,21 +760,21 @@ pub const LLVMIRModule = struct { | ... | @@ -509,21 +760,21 @@ pub const LLVMIRModule = struct { |
| 509 | return self.builder.buildICmp(operation, lhs, rhs, ""); | 760 | return self.builder.buildICmp(operation, lhs, rhs, ""); |
| 510 | } | 761 | } |
| 511 | 762 | ||
| 512 | fn genBlock(self: *LLVMIRModule, inst: *Inst.Block) !?*const llvm.Value { | 763 | fn genBlock(self: *FuncGen, inst: *Inst.Block) !?*const llvm.Value { |
| 513 | const parent_bb = self.context.createBasicBlock("Block"); | 764 | const parent_bb = self.context().createBasicBlock("Block"); |
| 514 | 765 | ||
| 515 | // 5 breaks to a block seems like a reasonable default. | 766 | // 5 breaks to a block seems like a reasonable default. |
| 516 | var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5); | 767 | var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa(), 5); |
| 517 | var break_vals = try BreakValues.initCapacity(self.gpa, 5); | 768 | var break_vals = try BreakValues.initCapacity(self.gpa(), 5); |
| 518 | try self.blocks.putNoClobber(self.gpa, inst, .{ | 769 | try self.blocks.putNoClobber(self.gpa(), inst, .{ |
| 519 | .parent_bb = parent_bb, | 770 | .parent_bb = parent_bb, |
| 520 | .break_bbs = &break_bbs, | 771 | .break_bbs = &break_bbs, |
| 521 | .break_vals = &break_vals, | 772 | .break_vals = &break_vals, |
| 522 | }); | 773 | }); |
| 523 | defer { | 774 | defer { |
| 524 | self.blocks.removeAssertDiscard(inst); | 775 | self.blocks.removeAssertDiscard(inst); |
| 525 | break_bbs.deinit(self.gpa); | 776 | break_bbs.deinit(self.gpa()); |
| 526 | break_vals.deinit(self.gpa); | 777 | break_vals.deinit(self.gpa()); |
| 527 | } | 778 | } |
| 528 | 779 | ||
| 529 | try self.genBody(inst.body); | 780 | try self.genBody(inst.body); |
| ... | @@ -534,7 +785,7 @@ pub const LLVMIRModule = struct { | ... | @@ -534,7 +785,7 @@ pub const LLVMIRModule = struct { |
| 534 | // If the block does not return a value, we dont have to create a phi node. | 785 | // If the block does not return a value, we dont have to create a phi node. |
| 535 | if (!inst.base.ty.hasCodeGenBits()) return null; | 786 | if (!inst.base.ty.hasCodeGenBits()) return null; |
| 536 | 787 | ||
| 537 | const phi_node = self.builder.buildPhi(try self.getLLVMType(inst.base.ty, inst.base.src), ""); | 788 | const phi_node = self.builder.buildPhi(try self.dg.getLLVMType(inst.base.ty), ""); |
| 538 | phi_node.addIncoming( | 789 | phi_node.addIncoming( |
| 539 | break_vals.items.ptr, | 790 | break_vals.items.ptr, |
| 540 | break_bbs.items.ptr, | 791 | break_bbs.items.ptr, |
| ... | @@ -543,7 +794,7 @@ pub const LLVMIRModule = struct { | ... | @@ -543,7 +794,7 @@ pub const LLVMIRModule = struct { |
| 543 | return phi_node; | 794 | return phi_node; |
| 544 | } | 795 | } |
| 545 | 796 | ||
| 546 | fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value { | 797 | fn genBr(self: *FuncGen, inst: *Inst.Br) !?*const llvm.Value { |
| 547 | var block = self.blocks.get(inst.block).?; | 798 | var block = self.blocks.get(inst.block).?; |
| 548 | 799 | ||
| 549 | // If the break doesn't break a value, then we don't have to add | 800 | // If the break doesn't break a value, then we don't have to add |
| ... | @@ -556,25 +807,25 @@ pub const LLVMIRModule = struct { | ... | @@ -556,25 +807,25 @@ pub const LLVMIRModule = struct { |
| 556 | 807 | ||
| 557 | // For the phi node, we need the basic blocks and the values of the | 808 | // For the phi node, we need the basic blocks and the values of the |
| 558 | // break instructions. | 809 | // break instructions. |
| 559 | try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); | 810 | try block.break_bbs.append(self.gpa(), self.builder.getInsertBlock()); |
| 560 | try block.break_vals.append(self.gpa, val); | 811 | try block.break_vals.append(self.gpa(), val); |
| 561 | 812 | ||
| 562 | _ = self.builder.buildBr(block.parent_bb); | 813 | _ = self.builder.buildBr(block.parent_bb); |
| 563 | } | 814 | } |
| 564 | return null; | 815 | return null; |
| 565 | } | 816 | } |
| 566 | 817 | ||
| 567 | fn genBrVoid(self: *LLVMIRModule, inst: *Inst.BrVoid) !?*const llvm.Value { | 818 | fn genBrVoid(self: *FuncGen, inst: *Inst.BrVoid) !?*const llvm.Value { |
| 568 | var block = self.blocks.get(inst.block).?; | 819 | var block = self.blocks.get(inst.block).?; |
| 569 | _ = self.builder.buildBr(block.parent_bb); | 820 | _ = self.builder.buildBr(block.parent_bb); |
| 570 | return null; | 821 | return null; |
| 571 | } | 822 | } |
| 572 | 823 | ||
| 573 | fn genCondBr(self: *LLVMIRModule, inst: *Inst.CondBr) !?*const llvm.Value { | 824 | fn genCondBr(self: *FuncGen, inst: *Inst.CondBr) !?*const llvm.Value { |
| 574 | const condition_value = try self.resolveInst(inst.condition); | 825 | const condition_value = try self.resolveInst(inst.condition); |
| 575 | 826 | ||
| 576 | const then_block = self.context.appendBasicBlock(self.llvm_func, "Then"); | 827 | const then_block = self.context().appendBasicBlock(self.llvm_func, "Then"); |
| 577 | const else_block = self.context.appendBasicBlock(self.llvm_func, "Else"); | 828 | const else_block = self.context().appendBasicBlock(self.llvm_func, "Else"); |
| 578 | { | 829 | { |
| 579 | const prev_block = self.builder.getInsertBlock(); | 830 | const prev_block = self.builder.getInsertBlock(); |
| 580 | defer self.builder.positionBuilderAtEnd(prev_block); | 831 | defer self.builder.positionBuilderAtEnd(prev_block); |
| ... | @@ -589,8 +840,8 @@ pub const LLVMIRModule = struct { | ... | @@ -589,8 +840,8 @@ pub const LLVMIRModule = struct { |
| 589 | return null; | 840 | return null; |
| 590 | } | 841 | } |
| 591 | 842 | ||
| 592 | fn genLoop(self: *LLVMIRModule, inst: *Inst.Loop) !?*const llvm.Value { | 843 | fn genLoop(self: *FuncGen, inst: *Inst.Loop) !?*const llvm.Value { |
| 593 | const loop_block = self.context.appendBasicBlock(self.llvm_func, "Loop"); | 844 | const loop_block = self.context().appendBasicBlock(self.llvm_func, "Loop"); |
| 594 | _ = self.builder.buildBr(loop_block); | 845 | _ = self.builder.buildBr(loop_block); |
| 595 | 846 | ||
| 596 | self.builder.positionBuilderAtEnd(loop_block); | 847 | self.builder.positionBuilderAtEnd(loop_block); |
| ... | @@ -600,20 +851,20 @@ pub const LLVMIRModule = struct { | ... | @@ -600,20 +851,20 @@ pub const LLVMIRModule = struct { |
| 600 | return null; | 851 | return null; |
| 601 | } | 852 | } |
| 602 | 853 | ||
| 603 | fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { | 854 | fn genNot(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { |
| 604 | return self.builder.buildNot(try self.resolveInst(inst.operand), ""); | 855 | return self.builder.buildNot(try self.resolveInst(inst.operand), ""); |
| 605 | } | 856 | } |
| 606 | 857 | ||
| 607 | fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value { | 858 | fn genUnreach(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value { |
| 608 | _ = self.builder.buildUnreachable(); | 859 | _ = self.builder.buildUnreachable(); |
| 609 | return null; | 860 | return null; |
| 610 | } | 861 | } |
| 611 | 862 | ||
| 612 | fn genIsNonNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { | 863 | fn genIsNonNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { |
| 613 | const operand = try self.resolveInst(inst.operand); | 864 | const operand = try self.resolveInst(inst.operand); |
| 614 | 865 | ||
| 615 | if (operand_is_ptr) { | 866 | if (operand_is_ptr) { |
| 616 | const index_type = self.context.intType(32); | 867 | const index_type = self.context().intType(32); |
| 617 | 868 | ||
| 618 | var indices: [2]*const llvm.Value = .{ | 869 | var indices: [2]*const llvm.Value = .{ |
| 619 | index_type.constNull(), | 870 | index_type.constNull(), |
| ... | @@ -626,15 +877,15 @@ pub const LLVMIRModule = struct { | ... | @@ -626,15 +877,15 @@ pub const LLVMIRModule = struct { |
| 626 | } | 877 | } |
| 627 | } | 878 | } |
| 628 | 879 | ||
| 629 | fn genIsNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { | 880 | fn genIsNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { |
| 630 | return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, ""); | 881 | return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, ""); |
| 631 | } | 882 | } |
| 632 | 883 | ||
| 633 | fn genOptionalPayload(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { | 884 | fn genOptionalPayload(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { |
| 634 | const operand = try self.resolveInst(inst.operand); | 885 | const operand = try self.resolveInst(inst.operand); |
| 635 | 886 | ||
| 636 | if (operand_is_ptr) { | 887 | if (operand_is_ptr) { |
| 637 | const index_type = self.context.intType(32); | 888 | const index_type = self.context().intType(32); |
| 638 | 889 | ||
| 639 | var indices: [2]*const llvm.Value = .{ | 890 | var indices: [2]*const llvm.Value = .{ |
| 640 | index_type.constNull(), | 891 | index_type.constNull(), |
| ... | @@ -647,12 +898,12 @@ pub const LLVMIRModule = struct { | ... | @@ -647,12 +898,12 @@ pub const LLVMIRModule = struct { |
| 647 | } | 898 | } |
| 648 | } | 899 | } |
| 649 | 900 | ||
| 650 | fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { | 901 | fn genAdd(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { |
| 651 | const lhs = try self.resolveInst(inst.lhs); | 902 | const lhs = try self.resolveInst(inst.lhs); |
| 652 | const rhs = try self.resolveInst(inst.rhs); | 903 | const rhs = try self.resolveInst(inst.rhs); |
| 653 | 904 | ||
| 654 | if (!inst.base.ty.isInt()) | 905 | if (!inst.base.ty.isInt()) |
| 655 | return self.fail(inst.base.src, "TODO implement 'genAdd' for type {}", .{inst.base.ty}); | 906 | return self.todo("implement 'genAdd' for type {}", .{inst.base.ty}); |
| 656 | 907 | ||
| 657 | return if (inst.base.ty.isSignedInt()) | 908 | return if (inst.base.ty.isSignedInt()) |
| 658 | self.builder.buildNSWAdd(lhs, rhs, "") | 909 | self.builder.buildNSWAdd(lhs, rhs, "") |
| ... | @@ -660,12 +911,12 @@ pub const LLVMIRModule = struct { | ... | @@ -660,12 +911,12 @@ pub const LLVMIRModule = struct { |
| 660 | self.builder.buildNUWAdd(lhs, rhs, ""); | 911 | self.builder.buildNUWAdd(lhs, rhs, ""); |
| 661 | } | 912 | } |
| 662 | 913 | ||
| 663 | fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { | 914 | fn genSub(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { |
| 664 | const lhs = try self.resolveInst(inst.lhs); | 915 | const lhs = try self.resolveInst(inst.lhs); |
| 665 | const rhs = try self.resolveInst(inst.rhs); | 916 | const rhs = try self.resolveInst(inst.rhs); |
| 666 | 917 | ||
| 667 | if (!inst.base.ty.isInt()) | 918 | if (!inst.base.ty.isInt()) |
| 668 | return self.fail(inst.base.src, "TODO implement 'genSub' for type {}", .{inst.base.ty}); | 919 | return self.todo("implement 'genSub' for type {}", .{inst.base.ty}); |
| 669 | 920 | ||
| 670 | return if (inst.base.ty.isSignedInt()) | 921 | return if (inst.base.ty.isSignedInt()) |
| 671 | self.builder.buildNSWSub(lhs, rhs, "") | 922 | self.builder.buildNSWSub(lhs, rhs, "") |
| ... | @@ -673,44 +924,44 @@ pub const LLVMIRModule = struct { | ... | @@ -673,44 +924,44 @@ pub const LLVMIRModule = struct { |
| 673 | self.builder.buildNUWSub(lhs, rhs, ""); | 924 | self.builder.buildNUWSub(lhs, rhs, ""); |
| 674 | } | 925 | } |
| 675 | 926 | ||
| 676 | fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { | 927 | fn genIntCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { |
| 677 | const val = try self.resolveInst(inst.operand); | 928 | const val = try self.resolveInst(inst.operand); |
| 678 | 929 | ||
| 679 | const signed = inst.base.ty.isSignedInt(); | 930 | const signed = inst.base.ty.isSignedInt(); |
| 680 | // TODO: Should we use intcast here or just a simple bitcast? | 931 | // TODO: Should we use intcast here or just a simple bitcast? |
| 681 | // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes | 932 | // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes |
| 682 | return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), llvm.Bool.fromBool(signed), ""); | 933 | return self.builder.buildIntCast2(val, try self.dg.getLLVMType(inst.base.ty), llvm.Bool.fromBool(signed), ""); |
| 683 | } | 934 | } |
| 684 | 935 | ||
| 685 | fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { | 936 | fn genBitCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { |
| 686 | const val = try self.resolveInst(inst.operand); | 937 | const val = try self.resolveInst(inst.operand); |
| 687 | const dest_type = try self.getLLVMType(inst.base.ty, inst.base.src); | 938 | const dest_type = try self.dg.getLLVMType(inst.base.ty); |
| 688 | 939 | ||
| 689 | return self.builder.buildBitCast(val, dest_type, ""); | 940 | return self.builder.buildBitCast(val, dest_type, ""); |
| 690 | } | 941 | } |
| 691 | 942 | ||
| 692 | fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.Value { | 943 | fn genArg(self: *FuncGen, inst: *Inst.Arg) !?*const llvm.Value { |
| 693 | const arg_val = self.args[self.arg_index]; | 944 | const arg_val = self.args[self.arg_index]; |
| 694 | self.arg_index += 1; | 945 | self.arg_index += 1; |
| 695 | 946 | ||
| 696 | const ptr_val = self.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src)); | 947 | const ptr_val = self.buildAlloca(try self.dg.getLLVMType(inst.base.ty)); |
| 697 | _ = self.builder.buildStore(arg_val, ptr_val); | 948 | _ = self.builder.buildStore(arg_val, ptr_val); |
| 698 | return self.builder.buildLoad(ptr_val, ""); | 949 | return self.builder.buildLoad(ptr_val, ""); |
| 699 | } | 950 | } |
| 700 | 951 | ||
| 701 | fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value { | 952 | fn genAlloc(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value { |
| 702 | // buildAlloca expects the pointee type, not the pointer type, so assert that | 953 | // buildAlloca expects the pointee type, not the pointer type, so assert that |
| 703 | // a Payload.PointerSimple is passed to the alloc instruction. | 954 | // a Payload.PointerSimple is passed to the alloc instruction. |
| 704 | const pointee_type = inst.base.ty.castPointer().?.data; | 955 | const pointee_type = inst.base.ty.castPointer().?.data; |
| 705 | 956 | ||
| 706 | // TODO: figure out a way to get the name of the var decl. | 957 | // TODO: figure out a way to get the name of the var decl. |
| 707 | // TODO: set alignment and volatile | 958 | // TODO: set alignment and volatile |
| 708 | return self.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src)); | 959 | return self.buildAlloca(try self.dg.getLLVMType(pointee_type)); |
| 709 | } | 960 | } |
| 710 | 961 | ||
| 711 | /// Use this instead of builder.buildAlloca, because this function makes sure to | 962 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| 712 | /// put the alloca instruction at the top of the function! | 963 | /// put the alloca instruction at the top of the function! |
| 713 | fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value { | 964 | fn buildAlloca(self: *FuncGen, t: *const llvm.Type) *const llvm.Value { |
| 714 | const prev_block = self.builder.getInsertBlock(); | 965 | const prev_block = self.builder.getInsertBlock(); |
| 715 | defer self.builder.positionBuilderAtEnd(prev_block); | 966 | defer self.builder.positionBuilderAtEnd(prev_block); |
| 716 | 967 | ||
| ... | @@ -732,242 +983,30 @@ pub const LLVMIRModule = struct { | ... | @@ -732,242 +983,30 @@ pub const LLVMIRModule = struct { |
| 732 | return val; | 983 | return val; |
| 733 | } | 984 | } |
| 734 | 985 | ||
| 735 | fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { | 986 | fn genStore(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { |
| 736 | const val = try self.resolveInst(inst.rhs); | 987 | const val = try self.resolveInst(inst.rhs); |
| 737 | const ptr = try self.resolveInst(inst.lhs); | 988 | const ptr = try self.resolveInst(inst.lhs); |
| 738 | _ = self.builder.buildStore(val, ptr); | 989 | _ = self.builder.buildStore(val, ptr); |
| 739 | return null; | 990 | return null; |
| 740 | } | 991 | } |
| 741 | 992 | ||
| 742 | fn genLoad(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { | 993 | fn genLoad(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { |
| 743 | const ptr_val = try self.resolveInst(inst.operand); | 994 | const ptr_val = try self.resolveInst(inst.operand); |
| 744 | return self.builder.buildLoad(ptr_val, ""); | 995 | return self.builder.buildLoad(ptr_val, ""); |
| 745 | } | 996 | } |
| 746 | 997 | ||
| 747 | fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value { | 998 | fn genBreakpoint(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value { |
| 748 | const llvn_fn = self.getIntrinsic("llvm.debugtrap"); | 999 | const llvn_fn = self.getIntrinsic("llvm.debugtrap"); |
| 749 | _ = self.builder.buildCall(llvn_fn, null, 0, ""); | 1000 | _ = self.builder.buildCall(llvn_fn, null, 0, ""); |
| 750 | return null; | 1001 | return null; |
| 751 | } | 1002 | } |
| 752 | 1003 | ||
| 753 | fn getIntrinsic(self: *LLVMIRModule, name: []const u8) *const llvm.Value { | 1004 | fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value { |
| 754 | const id = llvm.lookupIntrinsicID(name.ptr, name.len); | 1005 | const id = llvm.lookupIntrinsicID(name.ptr, name.len); |
| 755 | assert(id != 0); | 1006 | assert(id != 0); |
| 756 | // TODO: add support for overload intrinsics by passing the prefix of the intrinsic | 1007 | // TODO: add support for overload intrinsics by passing the prefix of the intrinsic |
| 757 | // to `lookupIntrinsicID` and then passing the correct types to | 1008 | // to `lookupIntrinsicID` and then passing the correct types to |
| 758 | // `getIntrinsicDeclaration` | 1009 | // `getIntrinsicDeclaration` |
| 759 | return self.llvm_module.getIntrinsicDeclaration(id, null, 0); | 1010 | return self.llvmModule().getIntrinsicDeclaration(id, null, 0); |
| 760 | } | ||
| 761 | |||
| 762 | fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.Value { | ||
| 763 | if (inst.value()) |val| { | ||
| 764 | return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val }); | ||
| 765 | } | ||
| 766 | if (self.func_inst_table.get(inst)) |value| return value; | ||
| 767 | |||
| 768 | return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{}); | ||
| 769 | } | ||
| 770 | |||
| 771 | fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value { | ||
| 772 | const llvm_type = try self.getLLVMType(tv.ty, src); | ||
| 773 | |||
| 774 | if (tv.val.isUndef()) | ||
| 775 | return llvm_type.getUndef(); | ||
| 776 | |||
| 777 | switch (tv.ty.zigTypeTag()) { | ||
| 778 | .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), | ||
| 779 | .Int => { | ||
| 780 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 781 | const bigint = tv.val.toBigInt(&bigint_space); | ||
| 782 | |||
| 783 | if (bigint.eqZero()) return llvm_type.constNull(); | ||
| 784 | |||
| 785 | if (bigint.limbs.len != 1) { | ||
| 786 | return self.fail(src, "TODO implement bigger bigint", .{}); | ||
| 787 | } | ||
| 788 | const llvm_int = llvm_type.constInt(bigint.limbs[0], .False); | ||
| 789 | if (!bigint.positive) { | ||
| 790 | return llvm.constNeg(llvm_int); | ||
| 791 | } | ||
| 792 | return llvm_int; | ||
| 793 | }, | ||
| 794 | .Pointer => switch (tv.val.tag()) { | ||
| 795 | .decl_ref => { | ||
| 796 | const decl = tv.val.castTag(.decl_ref).?.data; | ||
| 797 | const val = try self.resolveGlobalDecl(decl, src); | ||
| 798 | |||
| 799 | const usize_type = try self.getLLVMType(Type.initTag(.usize), src); | ||
| 800 | |||
| 801 | // TODO: second index should be the index into the memory! | ||
| 802 | var indices: [2]*const llvm.Value = .{ | ||
| 803 | usize_type.constNull(), | ||
| 804 | usize_type.constNull(), | ||
| 805 | }; | ||
| 806 | |||
| 807 | // TODO: consider using buildInBoundsGEP2 for opaque pointers | ||
| 808 | return self.builder.buildInBoundsGEP(val, &indices, 2, ""); | ||
| 809 | }, | ||
| 810 | .ref_val => { | ||
| 811 | const elem_value = tv.val.castTag(.ref_val).?.data; | ||
| 812 | const elem_type = tv.ty.castPointer().?.data; | ||
| 813 | const alloca = self.buildAlloca(try self.getLLVMType(elem_type, src)); | ||
| 814 | _ = self.builder.buildStore(try self.genTypedValue(src, .{ .ty = elem_type, .val = elem_value }), alloca); | ||
| 815 | return alloca; | ||
| 816 | }, | ||
| 817 | else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), | ||
| 818 | }, | ||
| 819 | .Array => { | ||
| 820 | if (tv.val.castTag(.bytes)) |payload| { | ||
| 821 | const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: { | ||
| 822 | if (sentinel.tag() == .zero) break :blk true; | ||
| 823 | return self.fail(src, "TODO handle other sentinel values", .{}); | ||
| 824 | } else false; | ||
| 825 | |||
| 826 | return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel)); | ||
| 827 | } else { | ||
| 828 | return self.fail(src, "TODO handle more array values", .{}); | ||
| 829 | } | ||
| 830 | }, | ||
| 831 | .Optional => { | ||
| 832 | if (!tv.ty.isPtrLikeOptional()) { | ||
| 833 | var buf: Type.Payload.ElemType = undefined; | ||
| 834 | const child_type = tv.ty.optionalChild(&buf); | ||
| 835 | const llvm_child_type = try self.getLLVMType(child_type, src); | ||
| 836 | |||
| 837 | if (tv.val.tag() == .null_value) { | ||
| 838 | var optional_values: [2]*const llvm.Value = .{ | ||
| 839 | llvm_child_type.constNull(), | ||
| 840 | self.context.intType(1).constNull(), | ||
| 841 | }; | ||
| 842 | return self.context.constStruct(&optional_values, 2, .False); | ||
| 843 | } else { | ||
| 844 | var optional_values: [2]*const llvm.Value = .{ | ||
| 845 | try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }), | ||
| 846 | self.context.intType(1).constAllOnes(), | ||
| 847 | }; | ||
| 848 | return self.context.constStruct(&optional_values, 2, .False); | ||
| 849 | } | ||
| 850 | } else { | ||
| 851 | return self.fail(src, "TODO implement const of optional pointer", .{}); | ||
| 852 | } | ||
| 853 | }, | ||
| 854 | else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), | ||
| 855 | } | ||
| 856 | } | ||
| 857 | |||
| 858 | fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.Type { | ||
| 859 | switch (t.zigTypeTag()) { | ||
| 860 | .Void => return self.context.voidType(), | ||
| 861 | .NoReturn => return self.context.voidType(), | ||
| 862 | .Int => { | ||
| 863 | const info = t.intInfo(self.module.getTarget()); | ||
| 864 | return self.context.intType(info.bits); | ||
| 865 | }, | ||
| 866 | .Bool => return self.context.intType(1), | ||
| 867 | .Pointer => { | ||
| 868 | if (t.isSlice()) { | ||
| 869 | return self.fail(src, "TODO: LLVM backend: implement slices", .{}); | ||
| 870 | } else { | ||
| 871 | const elem_type = try self.getLLVMType(t.elemType(), src); | ||
| 872 | return elem_type.pointerType(0); | ||
| 873 | } | ||
| 874 | }, | ||
| 875 | .Array => { | ||
| 876 | const elem_type = try self.getLLVMType(t.elemType(), src); | ||
| 877 | return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); | ||
| 878 | }, | ||
| 879 | .Optional => { | ||
| 880 | if (!t.isPtrLikeOptional()) { | ||
| 881 | var buf: Type.Payload.ElemType = undefined; | ||
| 882 | const child_type = t.optionalChild(&buf); | ||
| 883 | |||
| 884 | var optional_types: [2]*const llvm.Type = .{ | ||
| 885 | try self.getLLVMType(child_type, src), | ||
| 886 | self.context.intType(1), | ||
| 887 | }; | ||
| 888 | return self.context.structType(&optional_types, 2, .False); | ||
| 889 | } else { | ||
| 890 | return self.fail(src, "TODO implement optional pointers as actual pointers", .{}); | ||
| 891 | } | ||
| 892 | }, | ||
| 893 | else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), | ||
| 894 | } | ||
| 895 | } | ||
| 896 | |||
| 897 | fn resolveGlobalDecl(self: *LLVMIRModule, decl: *Module.Decl, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.Value { | ||
| 898 | // TODO: do we want to store this in our own datastructure? | ||
| 899 | if (self.llvm_module.getNamedGlobal(decl.name)) |val| return val; | ||
| 900 | |||
| 901 | const typed_value = decl.typed_value.most_recent.typed_value; | ||
| 902 | |||
| 903 | // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`. | ||
| 904 | const llvm_type = try self.getLLVMType(typed_value.ty, src); | ||
| 905 | const val = try self.genTypedValue(src, typed_value); | ||
| 906 | const global = self.llvm_module.addGlobal(llvm_type, decl.name); | ||
| 907 | llvm.setInitializer(global, val); | ||
| 908 | |||
| 909 | // TODO ask the Decl if it is const | ||
| 910 | // https://github.com/ziglang/zig/issues/7582 | ||
| 911 | |||
| 912 | return global; | ||
| 913 | } | ||
| 914 | |||
| 915 | /// If the llvm function does not exist, create it | ||
| 916 | fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Decl, src: usize) !*const llvm.Value { | ||
| 917 | // TODO: do we want to store this in our own datastructure? | ||
| 918 | if (self.llvm_module.getNamedFunction(func.name)) |llvm_fn| return llvm_fn; | ||
| 919 | |||
| 920 | const zig_fn_type = func.typed_value.most_recent.typed_value.ty; | ||
| 921 | const return_type = zig_fn_type.fnReturnType(); | ||
| 922 | |||
| 923 | const fn_param_len = zig_fn_type.fnParamLen(); | ||
| 924 | |||
| 925 | const fn_param_types = try self.gpa.alloc(Type, fn_param_len); | ||
| 926 | defer self.gpa.free(fn_param_types); | ||
| 927 | zig_fn_type.fnParamTypes(fn_param_types); | ||
| 928 | |||
| 929 | const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len); | ||
| 930 | defer self.gpa.free(llvm_param); | ||
| 931 | |||
| 932 | for (fn_param_types) |fn_param, i| { | ||
| 933 | llvm_param[i] = try self.getLLVMType(fn_param, src); | ||
| 934 | } | ||
| 935 | |||
| 936 | const fn_type = llvm.Type.functionType( | ||
| 937 | try self.getLLVMType(return_type, src), | ||
| 938 | if (fn_param_len == 0) null else llvm_param.ptr, | ||
| 939 | @intCast(c_uint, fn_param_len), | ||
| 940 | .False, | ||
| 941 | ); | ||
| 942 | const llvm_fn = self.llvm_module.addFunction(func.name, fn_type); | ||
| 943 | |||
| 944 | if (return_type.tag() == .noreturn) { | ||
| 945 | self.addFnAttr(llvm_fn, "noreturn"); | ||
| 946 | } | ||
| 947 | |||
| 948 | return llvm_fn; | ||
| 949 | } | ||
| 950 | |||
| 951 | // Helper functions | ||
| 952 | fn addAttr(self: LLVMIRModule, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { | ||
| 953 | const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); | ||
| 954 | assert(kind_id != 0); | ||
| 955 | const llvm_attr = self.context.createEnumAttribute(kind_id, 0); | ||
| 956 | val.addAttributeAtIndex(index, llvm_attr); | ||
| 957 | } | ||
| 958 | |||
| 959 | fn addFnAttr(self: *LLVMIRModule, val: *const llvm.Value, attr_name: []const u8) void { | ||
| 960 | // TODO: improve this API, `addAttr(-1, attr_name)` | ||
| 961 | self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name); | ||
| 962 | } | ||
| 963 | |||
| 964 | pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { | ||
| 965 | @setCold(true); | ||
| 966 | assert(self.err_msg == null); | ||
| 967 | self.err_msg = try Module.ErrorMsg.create(self.gpa, .{ | ||
| 968 | .file_scope = self.src_loc.file_scope, | ||
| 969 | .byte_offset = src, | ||
| 970 | }, format, args); | ||
| 971 | return error.CodegenFail; | ||
| 972 | } | 1011 | } |
| 973 | }; | 1012 | }; |
src/codegen/wasm.zig+9-10| ... | @@ -14,6 +14,7 @@ const Type = @import("../type.zig").Type; | ... | @@ -14,6 +14,7 @@ const Type = @import("../type.zig").Type; |
| 14 | const Value = @import("../value.zig").Value; | 14 | const Value = @import("../value.zig").Value; |
| 15 | const Compilation = @import("../Compilation.zig"); | 15 | const Compilation = @import("../Compilation.zig"); |
| 16 | const AnyMCValue = @import("../codegen.zig").AnyMCValue; | 16 | const AnyMCValue = @import("../codegen.zig").AnyMCValue; |
| 17 | const LazySrcLoc = Module.LazySrcLoc; | ||
| 17 | 18 | ||
| 18 | /// Wasm Value, created when generating an instruction | 19 | /// Wasm Value, created when generating an instruction |
| 19 | const WValue = union(enum) { | 20 | const WValue = union(enum) { |
| ... | @@ -70,11 +71,9 @@ pub const Context = struct { | ... | @@ -70,11 +71,9 @@ pub const Context = struct { |
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | /// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig | 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 | fn fail(self: *Context, src: LazySrcLoc, comptime fmt: []const u8, args: anytype) InnerError { |
| 74 | self.err_msg = try Module.ErrorMsg.create(self.gpa, .{ | 75 | const src_loc = src.toSrcLocWithDecl(self.decl); |
| 75 | .file_scope = self.decl.getFileScope(), | 76 | self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args); |
| 76 | .byte_offset = src, | ||
| 77 | }, fmt, args); | ||
| 78 | return error.CodegenFail; | 77 | return error.CodegenFail; |
| 79 | } | 78 | } |
| 80 | 79 | ||
| ... | @@ -91,7 +90,7 @@ pub const Context = struct { | ... | @@ -91,7 +90,7 @@ pub const Context = struct { |
| 91 | } | 90 | } |
| 92 | 91 | ||
| 93 | /// Using a given `Type`, returns the corresponding wasm value type | 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 | return switch (ty.tag()) { | 94 | return switch (ty.tag()) { |
| 96 | .f32 => wasm.valtype(.f32), | 95 | .f32 => wasm.valtype(.f32), |
| 97 | .f64 => wasm.valtype(.f64), | 96 | .f64 => wasm.valtype(.f64), |
| ... | @@ -104,7 +103,7 @@ pub const Context = struct { | ... | @@ -104,7 +103,7 @@ pub const Context = struct { |
| 104 | /// Using a given `Type`, returns the corresponding wasm value type | 103 | /// Using a given `Type`, returns the corresponding wasm value type |
| 105 | /// Differently from `genValtype` this also allows `void` to create a block | 104 | /// Differently from `genValtype` this also allows `void` to create a block |
| 106 | /// with no return type | 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 | return switch (ty.tag()) { | 107 | return switch (ty.tag()) { |
| 109 | .void, .noreturn => wasm.block_empty, | 108 | .void, .noreturn => wasm.block_empty, |
| 110 | else => self.genValtype(src, ty), | 109 | else => self.genValtype(src, ty), |
| ... | @@ -139,7 +138,7 @@ pub const Context = struct { | ... | @@ -139,7 +138,7 @@ pub const Context = struct { |
| 139 | ty.fnParamTypes(params); | 138 | ty.fnParamTypes(params); |
| 140 | for (params) |param_type| { | 139 | for (params) |param_type| { |
| 141 | // Can we maybe get the source index of each param? | 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 | try writer.writeByte(val_type); | 142 | try writer.writeByte(val_type); |
| 144 | } | 143 | } |
| 145 | } | 144 | } |
| ... | @@ -151,7 +150,7 @@ pub const Context = struct { | ... | @@ -151,7 +150,7 @@ pub const Context = struct { |
| 151 | else => |ret_type| { | 150 | else => |ret_type| { |
| 152 | try leb.writeULEB128(writer, @as(u32, 1)); | 151 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 153 | // Can we maybe get the source index of the return type? | 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 | try writer.writeByte(val_type); | 154 | try writer.writeByte(val_type); |
| 156 | }, | 155 | }, |
| 157 | } | 156 | } |
| ... | @@ -168,7 +167,7 @@ pub const Context = struct { | ... | @@ -168,7 +167,7 @@ pub const Context = struct { |
| 168 | const mod_fn = blk: { | 167 | const mod_fn = blk: { |
| 169 | if (tv.val.castTag(.function)) |func| break :blk func.data; | 168 | if (tv.val.castTag(.function)) |func| break :blk func.data; |
| 170 | if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions | 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 | // Reserve space to write the size after generating the code as well as space for locals count | 173 | // Reserve space to write the size after generating the code as well as space for locals count |
src/ir.zig+564-12| ... | @@ -25,8 +25,7 @@ pub const Inst = struct { | ... | @@ -25,8 +25,7 @@ pub const Inst = struct { |
| 25 | /// lifetimes of operands are encoded elsewhere. | 25 | /// lifetimes of operands are encoded elsewhere. |
| 26 | deaths: DeathsInt = undefined, | 26 | deaths: DeathsInt = undefined, |
| 27 | ty: Type, | 27 | ty: Type, |
| 28 | /// Byte offset into the source. | 28 | src: Module.LazySrcLoc, |
| 29 | src: usize, | ||
| 30 | 29 | ||
| 31 | pub const DeathsInt = u16; | 30 | pub const DeathsInt = u16; |
| 32 | pub const DeathsBitIndex = std.math.Log2Int(DeathsInt); | 31 | pub const DeathsBitIndex = std.math.Log2Int(DeathsInt); |
| ... | @@ -81,22 +80,28 @@ pub const Inst = struct { | ... | @@ -81,22 +80,28 @@ pub const Inst = struct { |
| 81 | condbr, | 80 | condbr, |
| 82 | constant, | 81 | constant, |
| 83 | dbg_stmt, | 82 | dbg_stmt, |
| 84 | // ?T => bool | 83 | /// ?T => bool |
| 85 | is_null, | 84 | is_null, |
| 86 | // ?T => bool (inverted logic) | 85 | /// ?T => bool (inverted logic) |
| 87 | is_non_null, | 86 | is_non_null, |
| 88 | // *?T => bool | 87 | /// *?T => bool |
| 89 | is_null_ptr, | 88 | is_null_ptr, |
| 90 | // *?T => bool (inverted logic) | 89 | /// *?T => bool (inverted logic) |
| 91 | is_non_null_ptr, | 90 | is_non_null_ptr, |
| 92 | // E!T => bool | 91 | /// E!T => bool |
| 93 | is_err, | 92 | is_err, |
| 94 | // *E!T => bool | 93 | /// *E!T => bool |
| 95 | is_err_ptr, | 94 | is_err_ptr, |
| 95 | /// E => u16 | ||
| 96 | error_to_int, | ||
| 97 | /// u16 => E | ||
| 98 | int_to_error, | ||
| 96 | bool_and, | 99 | bool_and, |
| 97 | bool_or, | 100 | bool_or, |
| 98 | /// Read a value from a pointer. | 101 | /// Read a value from a pointer. |
| 99 | load, | 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 | loop, | 105 | loop, |
| 101 | ptrtoint, | 106 | ptrtoint, |
| 102 | ref, | 107 | ref, |
| ... | @@ -113,9 +118,9 @@ pub const Inst = struct { | ... | @@ -113,9 +118,9 @@ pub const Inst = struct { |
| 113 | not, | 118 | not, |
| 114 | floatcast, | 119 | floatcast, |
| 115 | intcast, | 120 | intcast, |
| 116 | // ?T => T | 121 | /// ?T => T |
| 117 | optional_payload, | 122 | optional_payload, |
| 118 | // *?T => *T | 123 | /// *?T => *T |
| 119 | optional_payload_ptr, | 124 | optional_payload_ptr, |
| 120 | wrap_optional, | 125 | wrap_optional, |
| 121 | /// E!T -> T | 126 | /// E!T -> T |
| ... | @@ -132,6 +137,8 @@ pub const Inst = struct { | ... | @@ -132,6 +137,8 @@ pub const Inst = struct { |
| 132 | wrap_errunion_err, | 137 | wrap_errunion_err, |
| 133 | xor, | 138 | xor, |
| 134 | switchbr, | 139 | switchbr, |
| 140 | /// Given a pointer to a struct and a field index, returns a pointer to the field. | ||
| 141 | struct_field_ptr, | ||
| 135 | 142 | ||
| 136 | pub fn Type(tag: Tag) type { | 143 | pub fn Type(tag: Tag) type { |
| 137 | return switch (tag) { | 144 | return switch (tag) { |
| ... | @@ -139,7 +146,6 @@ pub const Inst = struct { | ... | @@ -139,7 +146,6 @@ pub const Inst = struct { |
| 139 | .retvoid, | 146 | .retvoid, |
| 140 | .unreach, | 147 | .unreach, |
| 141 | .breakpoint, | 148 | .breakpoint, |
| 142 | .dbg_stmt, | ||
| 143 | => NoOp, | 149 | => NoOp, |
| 144 | 150 | ||
| 145 | .ref, | 151 | .ref, |
| ... | @@ -152,6 +158,8 @@ pub const Inst = struct { | ... | @@ -152,6 +158,8 @@ pub const Inst = struct { |
| 152 | .is_null_ptr, | 158 | .is_null_ptr, |
| 153 | .is_err, | 159 | .is_err, |
| 154 | .is_err_ptr, | 160 | .is_err_ptr, |
| 161 | .int_to_error, | ||
| 162 | .error_to_int, | ||
| 155 | .ptrtoint, | 163 | .ptrtoint, |
| 156 | .floatcast, | 164 | .floatcast, |
| 157 | .intcast, | 165 | .intcast, |
| ... | @@ -198,7 +206,9 @@ pub const Inst = struct { | ... | @@ -198,7 +206,9 @@ pub const Inst = struct { |
| 198 | .constant => Constant, | 206 | .constant => Constant, |
| 199 | .loop => Loop, | 207 | .loop => Loop, |
| 200 | .varptr => VarPtr, | 208 | .varptr => VarPtr, |
| 209 | .struct_field_ptr => StructFieldPtr, | ||
| 201 | .switchbr => SwitchBr, | 210 | .switchbr => SwitchBr, |
| 211 | .dbg_stmt => DbgStmt, | ||
| 202 | }; | 212 | }; |
| 203 | } | 213 | } |
| 204 | 214 | ||
| ... | @@ -360,7 +370,8 @@ pub const Inst = struct { | ... | @@ -360,7 +370,8 @@ pub const Inst = struct { |
| 360 | base: Inst, | 370 | base: Inst, |
| 361 | asm_source: []const u8, | 371 | asm_source: []const u8, |
| 362 | is_volatile: bool, | 372 | is_volatile: bool, |
| 363 | output: ?[]const u8, | 373 | output: ?*Inst, |
| 374 | output_name: ?[]const u8, | ||
| 364 | inputs: []const []const u8, | 375 | inputs: []const []const u8, |
| 365 | clobbers: []const []const u8, | 376 | clobbers: []const []const u8, |
| 366 | args: []const *Inst, | 377 | args: []const *Inst, |
| ... | @@ -544,6 +555,27 @@ pub const Inst = struct { | ... | @@ -544,6 +555,27 @@ pub const Inst = struct { |
| 544 | } | 555 | } |
| 545 | }; | 556 | }; |
| 546 | 557 | ||
| 558 | pub const StructFieldPtr = struct { | ||
| 559 | pub const base_tag = Tag.struct_field_ptr; | ||
| 560 | |||
| 561 | base: Inst, | ||
| 562 | struct_ptr: *Inst, | ||
| 563 | field_index: usize, | ||
| 564 | |||
| 565 | pub fn operandCount(self: *const StructFieldPtr) usize { | ||
| 566 | return 1; | ||
| 567 | } | ||
| 568 | pub fn getOperand(self: *const StructFieldPtr, index: usize) ?*Inst { | ||
| 569 | var i = index; | ||
| 570 | |||
| 571 | if (i < 1) | ||
| 572 | return self.struct_ptr; | ||
| 573 | i -= 1; | ||
| 574 | |||
| 575 | return null; | ||
| 576 | } | ||
| 577 | }; | ||
| 578 | |||
| 547 | pub const SwitchBr = struct { | 579 | pub const SwitchBr = struct { |
| 548 | pub const base_tag = Tag.switchbr; | 580 | pub const base_tag = Tag.switchbr; |
| 549 | 581 | ||
| ... | @@ -584,8 +616,528 @@ pub const Inst = struct { | ... | @@ -584,8 +616,528 @@ pub const Inst = struct { |
| 584 | return (self.deaths + self.else_index)[0..self.else_deaths]; | 616 | return (self.deaths + self.else_index)[0..self.else_deaths]; |
| 585 | } | 617 | } |
| 586 | }; | 618 | }; |
| 619 | |||
| 620 | pub const DbgStmt = struct { | ||
| 621 | pub const base_tag = Tag.dbg_stmt; | ||
| 622 | |||
| 623 | base: Inst, | ||
| 624 | byte_offset: u32, | ||
| 625 | |||
| 626 | pub fn operandCount(self: *const DbgStmt) usize { | ||
| 627 | return 0; | ||
| 628 | } | ||
| 629 | pub fn getOperand(self: *const DbgStmt, index: usize) ?*Inst { | ||
| 630 | return null; | ||
| 631 | } | ||
| 632 | }; | ||
| 587 | }; | 633 | }; |
| 588 | 634 | ||
| 589 | pub const Body = struct { | 635 | pub const Body = struct { |
| 590 | instructions: []*Inst, | 636 | instructions: []*Inst, |
| 591 | }; | 637 | }; |
| 638 | |||
| 639 | /// For debugging purposes, prints a function representation to stderr. | ||
| 640 | pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { | ||
| 641 | const allocator = old_module.gpa; | ||
| 642 | var ctx: DumpTzir = .{ | ||
| 643 | .allocator = allocator, | ||
| 644 | .arena = std.heap.ArenaAllocator.init(allocator), | ||
| 645 | .old_module = &old_module, | ||
| 646 | .module_fn = module_fn, | ||
| 647 | .indent = 2, | ||
| 648 | .inst_table = DumpTzir.InstTable.init(allocator), | ||
| 649 | .partial_inst_table = DumpTzir.InstTable.init(allocator), | ||
| 650 | .const_table = DumpTzir.InstTable.init(allocator), | ||
| 651 | }; | ||
| 652 | defer ctx.inst_table.deinit(); | ||
| 653 | defer ctx.partial_inst_table.deinit(); | ||
| 654 | defer ctx.const_table.deinit(); | ||
| 655 | defer ctx.arena.deinit(); | ||
| 656 | |||
| 657 | switch (module_fn.state) { | ||
| 658 | .queued => std.debug.print("(queued)", .{}), | ||
| 659 | .inline_only => std.debug.print("(inline_only)", .{}), | ||
| 660 | .in_progress => std.debug.print("(in_progress)", .{}), | ||
| 661 | .sema_failure => std.debug.print("(sema_failure)", .{}), | ||
| 662 | .dependency_failure => std.debug.print("(dependency_failure)", .{}), | ||
| 663 | .success => { | ||
| 664 | const writer = std.io.getStdErr().writer(); | ||
| 665 | ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR"); | ||
| 666 | }, | ||
| 667 | } | ||
| 668 | } | ||
| 669 | |||
| 670 | const DumpTzir = struct { | ||
| 671 | allocator: *std.mem.Allocator, | ||
| 672 | arena: std.heap.ArenaAllocator, | ||
| 673 | old_module: *const Module, | ||
| 674 | module_fn: *Module.Fn, | ||
| 675 | indent: usize, | ||
| 676 | inst_table: InstTable, | ||
| 677 | partial_inst_table: InstTable, | ||
| 678 | const_table: InstTable, | ||
| 679 | next_index: usize = 0, | ||
| 680 | next_partial_index: usize = 0, | ||
| 681 | next_const_index: usize = 0, | ||
| 682 | |||
| 683 | const InstTable = std.AutoArrayHashMap(*Inst, usize); | ||
| 684 | |||
| 685 | /// TODO: Improve this code to include a stack of Body and store the instructions | ||
| 686 | /// in there. Now we are putting all the instructions in a function local table, | ||
| 687 | /// however instructions that are in a Body can be thown away when the Body ends. | ||
| 688 | fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void { | ||
| 689 | // First pass to pre-populate the table so that we can show even invalid references. | ||
| 690 | // Must iterate the same order we iterate the second time. | ||
| 691 | // We also look for constants and put them in the const_table. | ||
| 692 | try dtz.fetchInstsAndResolveConsts(body); | ||
| 693 | |||
| 694 | std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name}); | ||
| 695 | |||
| 696 | for (dtz.const_table.items()) |entry| { | ||
| 697 | const constant = entry.key.castTag(.constant).?; | ||
| 698 | try writer.print(" @{d}: {} = {};\n", .{ | ||
| 699 | entry.value, constant.base.ty, constant.val, | ||
| 700 | }); | ||
| 701 | } | ||
| 702 | |||
| 703 | return dtz.dumpBody(body, writer); | ||
| 704 | } | ||
| 705 | |||
| 706 | fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void { | ||
| 707 | for (body.instructions) |inst| { | ||
| 708 | try dtz.inst_table.put(inst, dtz.next_index); | ||
| 709 | dtz.next_index += 1; | ||
| 710 | switch (inst.tag) { | ||
| 711 | .alloc, | ||
| 712 | .retvoid, | ||
| 713 | .unreach, | ||
| 714 | .breakpoint, | ||
| 715 | .dbg_stmt, | ||
| 716 | .arg, | ||
| 717 | => {}, | ||
| 718 | |||
| 719 | .ref, | ||
| 720 | .ret, | ||
| 721 | .bitcast, | ||
| 722 | .not, | ||
| 723 | .is_non_null, | ||
| 724 | .is_non_null_ptr, | ||
| 725 | .is_null, | ||
| 726 | .is_null_ptr, | ||
| 727 | .is_err, | ||
| 728 | .is_err_ptr, | ||
| 729 | .error_to_int, | ||
| 730 | .int_to_error, | ||
| 731 | .ptrtoint, | ||
| 732 | .floatcast, | ||
| 733 | .intcast, | ||
| 734 | .load, | ||
| 735 | .optional_payload, | ||
| 736 | .optional_payload_ptr, | ||
| 737 | .wrap_optional, | ||
| 738 | .wrap_errunion_payload, | ||
| 739 | .wrap_errunion_err, | ||
| 740 | .unwrap_errunion_payload, | ||
| 741 | .unwrap_errunion_err, | ||
| 742 | .unwrap_errunion_payload_ptr, | ||
| 743 | .unwrap_errunion_err_ptr, | ||
| 744 | => { | ||
| 745 | const un_op = inst.cast(Inst.UnOp).?; | ||
| 746 | try dtz.findConst(un_op.operand); | ||
| 747 | }, | ||
| 748 | |||
| 749 | .add, | ||
| 750 | .addwrap, | ||
| 751 | .sub, | ||
| 752 | .subwrap, | ||
| 753 | .mul, | ||
| 754 | .mulwrap, | ||
| 755 | .cmp_lt, | ||
| 756 | .cmp_lte, | ||
| 757 | .cmp_eq, | ||
| 758 | .cmp_gte, | ||
| 759 | .cmp_gt, | ||
| 760 | .cmp_neq, | ||
| 761 | .store, | ||
| 762 | .bool_and, | ||
| 763 | .bool_or, | ||
| 764 | .bit_and, | ||
| 765 | .bit_or, | ||
| 766 | .xor, | ||
| 767 | => { | ||
| 768 | const bin_op = inst.cast(Inst.BinOp).?; | ||
| 769 | try dtz.findConst(bin_op.lhs); | ||
| 770 | try dtz.findConst(bin_op.rhs); | ||
| 771 | }, | ||
| 772 | |||
| 773 | .br => { | ||
| 774 | const br = inst.castTag(.br).?; | ||
| 775 | try dtz.findConst(&br.block.base); | ||
| 776 | try dtz.findConst(br.operand); | ||
| 777 | }, | ||
| 778 | |||
| 779 | .br_block_flat => { | ||
| 780 | const br_block_flat = inst.castTag(.br_block_flat).?; | ||
| 781 | try dtz.findConst(&br_block_flat.block.base); | ||
| 782 | try dtz.fetchInstsAndResolveConsts(br_block_flat.body); | ||
| 783 | }, | ||
| 784 | |||
| 785 | .br_void => { | ||
| 786 | const br_void = inst.castTag(.br_void).?; | ||
| 787 | try dtz.findConst(&br_void.block.base); | ||
| 788 | }, | ||
| 789 | |||
| 790 | .block => { | ||
| 791 | const block = inst.castTag(.block).?; | ||
| 792 | try dtz.fetchInstsAndResolveConsts(block.body); | ||
| 793 | }, | ||
| 794 | |||
| 795 | .condbr => { | ||
| 796 | const condbr = inst.castTag(.condbr).?; | ||
| 797 | try dtz.findConst(condbr.condition); | ||
| 798 | try dtz.fetchInstsAndResolveConsts(condbr.then_body); | ||
| 799 | try dtz.fetchInstsAndResolveConsts(condbr.else_body); | ||
| 800 | }, | ||
| 801 | .switchbr => { | ||
| 802 | const switchbr = inst.castTag(.switchbr).?; | ||
| 803 | try dtz.findConst(switchbr.target); | ||
| 804 | try dtz.fetchInstsAndResolveConsts(switchbr.else_body); | ||
| 805 | for (switchbr.cases) |case| { | ||
| 806 | try dtz.fetchInstsAndResolveConsts(case.body); | ||
| 807 | } | ||
| 808 | }, | ||
| 809 | |||
| 810 | .loop => { | ||
| 811 | const loop = inst.castTag(.loop).?; | ||
| 812 | try dtz.fetchInstsAndResolveConsts(loop.body); | ||
| 813 | }, | ||
| 814 | .call => { | ||
| 815 | const call = inst.castTag(.call).?; | ||
| 816 | try dtz.findConst(call.func); | ||
| 817 | for (call.args) |arg| { | ||
| 818 | try dtz.findConst(arg); | ||
| 819 | } | ||
| 820 | }, | ||
| 821 | .struct_field_ptr => { | ||
| 822 | const struct_field_ptr = inst.castTag(.struct_field_ptr).?; | ||
| 823 | try dtz.findConst(struct_field_ptr.struct_ptr); | ||
| 824 | }, | ||
| 825 | |||
| 826 | // TODO fill out this debug printing | ||
| 827 | .assembly, | ||
| 828 | .constant, | ||
| 829 | .varptr, | ||
| 830 | => {}, | ||
| 831 | } | ||
| 832 | } | ||
| 833 | } | ||
| 834 | |||
| 835 | fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void { | ||
| 836 | for (body.instructions) |inst| { | ||
| 837 | const my_index = dtz.next_partial_index; | ||
| 838 | try dtz.partial_inst_table.put(inst, my_index); | ||
| 839 | dtz.next_partial_index += 1; | ||
| 840 | |||
| 841 | try writer.writeByteNTimes(' ', dtz.indent); | ||
| 842 | try writer.print("%{d}: {} = {s}(", .{ | ||
| 843 | my_index, inst.ty, @tagName(inst.tag), | ||
| 844 | }); | ||
| 845 | switch (inst.tag) { | ||
| 846 | .alloc, | ||
| 847 | .retvoid, | ||
| 848 | .unreach, | ||
| 849 | .breakpoint, | ||
| 850 | .dbg_stmt, | ||
| 851 | => try writer.writeAll(")\n"), | ||
| 852 | |||
| 853 | .ref, | ||
| 854 | .ret, | ||
| 855 | .bitcast, | ||
| 856 | .not, | ||
| 857 | .is_non_null, | ||
| 858 | .is_null, | ||
| 859 | .is_non_null_ptr, | ||
| 860 | .is_null_ptr, | ||
| 861 | .is_err, | ||
| 862 | .is_err_ptr, | ||
| 863 | .error_to_int, | ||
| 864 | .int_to_error, | ||
| 865 | .ptrtoint, | ||
| 866 | .floatcast, | ||
| 867 | .intcast, | ||
| 868 | .load, | ||
| 869 | .optional_payload, | ||
| 870 | .optional_payload_ptr, | ||
| 871 | .wrap_optional, | ||
| 872 | .wrap_errunion_err, | ||
| 873 | .wrap_errunion_payload, | ||
| 874 | .unwrap_errunion_err, | ||
| 875 | .unwrap_errunion_payload, | ||
| 876 | .unwrap_errunion_payload_ptr, | ||
| 877 | .unwrap_errunion_err_ptr, | ||
| 878 | => { | ||
| 879 | const un_op = inst.cast(Inst.UnOp).?; | ||
| 880 | const kinky = try dtz.writeInst(writer, un_op.operand); | ||
| 881 | if (kinky != null) { | ||
| 882 | try writer.writeAll(") // Instruction does not dominate all uses!\n"); | ||
| 883 | } else { | ||
| 884 | try writer.writeAll(")\n"); | ||
| 885 | } | ||
| 886 | }, | ||
| 887 | |||
| 888 | .add, | ||
| 889 | .addwrap, | ||
| 890 | .sub, | ||
| 891 | .subwrap, | ||
| 892 | .mul, | ||
| 893 | .mulwrap, | ||
| 894 | .cmp_lt, | ||
| 895 | .cmp_lte, | ||
| 896 | .cmp_eq, | ||
| 897 | .cmp_gte, | ||
| 898 | .cmp_gt, | ||
| 899 | .cmp_neq, | ||
| 900 | .store, | ||
| 901 | .bool_and, | ||
| 902 | .bool_or, | ||
| 903 | .bit_and, | ||
| 904 | .bit_or, | ||
| 905 | .xor, | ||
| 906 | => { | ||
| 907 | const bin_op = inst.cast(Inst.BinOp).?; | ||
| 908 | |||
| 909 | const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs); | ||
| 910 | try writer.writeAll(", "); | ||
| 911 | const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs); | ||
| 912 | |||
| 913 | if (lhs_kinky != null or rhs_kinky != null) { | ||
| 914 | try writer.writeAll(") // Instruction does not dominate all uses!"); | ||
| 915 | if (lhs_kinky) |lhs| { | ||
| 916 | try writer.print(" %{d}", .{lhs}); | ||
| 917 | } | ||
| 918 | if (rhs_kinky) |rhs| { | ||
| 919 | try writer.print(" %{d}", .{rhs}); | ||
| 920 | } | ||
| 921 | try writer.writeAll("\n"); | ||
| 922 | } else { | ||
| 923 | try writer.writeAll(")\n"); | ||
| 924 | } | ||
| 925 | }, | ||
| 926 | |||
| 927 | .arg => { | ||
| 928 | const arg = inst.castTag(.arg).?; | ||
| 929 | try writer.print("{s})\n", .{arg.name}); | ||
| 930 | }, | ||
| 931 | |||
| 932 | .br => { | ||
| 933 | const br = inst.castTag(.br).?; | ||
| 934 | |||
| 935 | const lhs_kinky = try dtz.writeInst(writer, &br.block.base); | ||
| 936 | try writer.writeAll(", "); | ||
| 937 | const rhs_kinky = try dtz.writeInst(writer, br.operand); | ||
| 938 | |||
| 939 | if (lhs_kinky != null or rhs_kinky != null) { | ||
| 940 | try writer.writeAll(") // Instruction does not dominate all uses!"); | ||
| 941 | if (lhs_kinky) |lhs| { | ||
| 942 | try writer.print(" %{d}", .{lhs}); | ||
| 943 | } | ||
| 944 | if (rhs_kinky) |rhs| { | ||
| 945 | try writer.print(" %{d}", .{rhs}); | ||
| 946 | } | ||
| 947 | try writer.writeAll("\n"); | ||
| 948 | } else { | ||
| 949 | try writer.writeAll(")\n"); | ||
| 950 | } | ||
| 951 | }, | ||
| 952 | |||
| 953 | .br_block_flat => { | ||
| 954 | const br_block_flat = inst.castTag(.br_block_flat).?; | ||
| 955 | const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base); | ||
| 956 | if (block_kinky != null) { | ||
| 957 | try writer.writeAll(", { // Instruction does not dominate all uses!\n"); | ||
| 958 | } else { | ||
| 959 | try writer.writeAll(", {\n"); | ||
| 960 | } | ||
| 961 | |||
| 962 | const old_indent = dtz.indent; | ||
| 963 | dtz.indent += 2; | ||
| 964 | try dtz.dumpBody(br_block_flat.body, writer); | ||
| 965 | dtz.indent = old_indent; | ||
| 966 | |||
| 967 | try writer.writeByteNTimes(' ', dtz.indent); | ||
| 968 | try writer.writeAll("})\n"); | ||
| 969 | }, | ||
| 970 | |||
| 971 | .br_void => { | ||
| 972 | const br_void = inst.castTag(.br_void).?; | ||
| 973 | const kinky = try dtz.writeInst(writer, &br_void.block.base); | ||
| 974 | if (kinky) |_| { | ||
| 975 | try writer.writeAll(") // Instruction does not dominate all uses!\n"); | ||
| 976 | } else { | ||
| 977 | try writer.writeAll(")\n"); | ||
| 978 | } | ||
| 979 | }, | ||
| 980 | |||
| 981 | .block => { | ||
| 982 | const block = inst.castTag(.block).?; | ||
| 983 | |||
| 984 | try writer.writeAll("{\n"); | ||
| 985 | |||
| 986 | const old_indent = dtz.indent; | ||
| 987 | dtz.indent += 2; | ||
| 988 | try dtz.dumpBody(block.body, writer); | ||
| 989 | dtz.indent = old_indent; | ||
| 990 | |||
| 991 | try writer.writeByteNTimes(' ', dtz.indent); | ||
| 992 | try writer.writeAll("})\n"); | ||
| 993 | }, | ||
| 994 | |||
| 995 | .condbr => { | ||
| 996 | const condbr = inst.castTag(.condbr).?; | ||
| 997 | |||
| 998 | const condition_kinky = try dtz.writeInst(writer, condbr.condition); | ||
| 999 | if (condition_kinky != null) { | ||
| 1000 | try writer.writeAll(", { // Instruction does not dominate all uses!\n"); | ||
| 1001 | } else { | ||
| 1002 | try writer.writeAll(", {\n"); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | const old_indent = dtz.indent; | ||
| 1006 | dtz.indent += 2; | ||
| 1007 | try dtz.dumpBody(condbr.then_body, writer); | ||
| 1008 | |||
| 1009 | try writer.writeByteNTimes(' ', old_indent); | ||
| 1010 | try writer.writeAll("}, {\n"); | ||
| 1011 | |||
| 1012 | try dtz.dumpBody(condbr.else_body, writer); | ||
| 1013 | dtz.indent = old_indent; | ||
| 1014 | |||
| 1015 | try writer.writeByteNTimes(' ', old_indent); | ||
| 1016 | try writer.writeAll("})\n"); | ||
| 1017 | }, | ||
| 1018 | |||
| 1019 | .switchbr => { | ||
| 1020 | const switchbr = inst.castTag(.switchbr).?; | ||
| 1021 | |||
| 1022 | const condition_kinky = try dtz.writeInst(writer, switchbr.target); | ||
| 1023 | if (condition_kinky != null) { | ||
| 1024 | try writer.writeAll(", { // Instruction does not dominate all uses!\n"); | ||
| 1025 | } else { | ||
| 1026 | try writer.writeAll(", {\n"); | ||
| 1027 | } | ||
| 1028 | const old_indent = dtz.indent; | ||
| 1029 | |||
| 1030 | if (switchbr.else_body.instructions.len != 0) { | ||
| 1031 | dtz.indent += 2; | ||
| 1032 | try dtz.dumpBody(switchbr.else_body, writer); | ||
| 1033 | |||
| 1034 | try writer.writeByteNTimes(' ', old_indent); | ||
| 1035 | try writer.writeAll("}, {\n"); | ||
| 1036 | dtz.indent = old_indent; | ||
| 1037 | } | ||
| 1038 | for (switchbr.cases) |case| { | ||
| 1039 | dtz.indent += 2; | ||
| 1040 | try dtz.dumpBody(case.body, writer); | ||
| 1041 | |||
| 1042 | try writer.writeByteNTimes(' ', old_indent); | ||
| 1043 | try writer.writeAll("}, {\n"); | ||
| 1044 | dtz.indent = old_indent; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | try writer.writeByteNTimes(' ', old_indent); | ||
| 1048 | try writer.writeAll("})\n"); | ||
| 1049 | }, | ||
| 1050 | |||
| 1051 | .loop => { | ||
| 1052 | const loop = inst.castTag(.loop).?; | ||
| 1053 | |||
| 1054 | try writer.writeAll("{\n"); | ||
| 1055 | |||
| 1056 | const old_indent = dtz.indent; | ||
| 1057 | dtz.indent += 2; | ||
| 1058 | try dtz.dumpBody(loop.body, writer); | ||
| 1059 | dtz.indent = old_indent; | ||
| 1060 | |||
| 1061 | try writer.writeByteNTimes(' ', dtz.indent); | ||
| 1062 | try writer.writeAll("})\n"); | ||
| 1063 | }, | ||
| 1064 | |||
| 1065 | .call => { | ||
| 1066 | const call = inst.castTag(.call).?; | ||
| 1067 | |||
| 1068 | const args_kinky = try dtz.allocator.alloc(?usize, call.args.len); | ||
| 1069 | defer dtz.allocator.free(args_kinky); | ||
| 1070 | std.mem.set(?usize, args_kinky, null); | ||
| 1071 | var any_kinky_args = false; | ||
| 1072 | |||
| 1073 | const func_kinky = try dtz.writeInst(writer, call.func); | ||
| 1074 | |||
| 1075 | for (call.args) |arg, i| { | ||
| 1076 | try writer.writeAll(", "); | ||
| 1077 | |||
| 1078 | args_kinky[i] = try dtz.writeInst(writer, arg); | ||
| 1079 | any_kinky_args = any_kinky_args or args_kinky[i] != null; | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | if (func_kinky != null or any_kinky_args) { | ||
| 1083 | try writer.writeAll(") // Instruction does not dominate all uses!"); | ||
| 1084 | if (func_kinky) |func_index| { | ||
| 1085 | try writer.print(" %{d}", .{func_index}); | ||
| 1086 | } | ||
| 1087 | for (args_kinky) |arg_kinky| { | ||
| 1088 | if (arg_kinky) |arg_index| { | ||
| 1089 | try writer.print(" %{d}", .{arg_index}); | ||
| 1090 | } | ||
| 1091 | } | ||
| 1092 | try writer.writeAll("\n"); | ||
| 1093 | } else { | ||
| 1094 | try writer.writeAll(")\n"); | ||
| 1095 | } | ||
| 1096 | }, | ||
| 1097 | |||
| 1098 | .struct_field_ptr => { | ||
| 1099 | const struct_field_ptr = inst.castTag(.struct_field_ptr).?; | ||
| 1100 | const kinky = try dtz.writeInst(writer, struct_field_ptr.struct_ptr); | ||
| 1101 | if (kinky != null) { | ||
| 1102 | try writer.print("{d}) // Instruction does not dominate all uses!\n", .{ | ||
| 1103 | struct_field_ptr.field_index, | ||
| 1104 | }); | ||
| 1105 | } else { | ||
| 1106 | try writer.print("{d})\n", .{struct_field_ptr.field_index}); | ||
| 1107 | } | ||
| 1108 | }, | ||
| 1109 | |||
| 1110 | // TODO fill out this debug printing | ||
| 1111 | .assembly, | ||
| 1112 | .constant, | ||
| 1113 | .varptr, | ||
| 1114 | => { | ||
| 1115 | try writer.writeAll("!TODO!)\n"); | ||
| 1116 | }, | ||
| 1117 | } | ||
| 1118 | } | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize { | ||
| 1122 | if (dtz.partial_inst_table.get(inst)) |operand_index| { | ||
| 1123 | try writer.print("%{d}", .{operand_index}); | ||
| 1124 | return null; | ||
| 1125 | } else if (dtz.const_table.get(inst)) |operand_index| { | ||
| 1126 | try writer.print("@{d}", .{operand_index}); | ||
| 1127 | return null; | ||
| 1128 | } else if (dtz.inst_table.get(inst)) |operand_index| { | ||
| 1129 | try writer.print("%{d}", .{operand_index}); | ||
| 1130 | return operand_index; | ||
| 1131 | } else { | ||
| 1132 | try writer.writeAll("!BADREF!"); | ||
| 1133 | return null; | ||
| 1134 | } | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | fn findConst(dtz: *DumpTzir, operand: *Inst) !void { | ||
| 1138 | if (operand.tag == .constant) { | ||
| 1139 | try dtz.const_table.put(operand, dtz.next_const_index); | ||
| 1140 | dtz.next_const_index += 1; | ||
| 1141 | } | ||
| 1142 | } | ||
| 1143 | }; |
src/link/C.zig+1-2| ... | @@ -185,8 +185,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { | ... | @@ -185,8 +185,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 185 | if (module.global_error_set.size == 0) break :render_errors; | 185 | if (module.global_error_set.size == 0) break :render_errors; |
| 186 | var it = module.global_error_set.iterator(); | 186 | var it = module.global_error_set.iterator(); |
| 187 | while (it.next()) |entry| { | 187 | while (it.next()) |entry| { |
| 188 | // + 1 because 0 represents no error | 188 | try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value }); |
| 189 | try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value + 1 }); | ||
| 190 | } | 189 | } |
| 191 | try err_typedef_writer.writeByte('\n'); | 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,7 +34,7 @@ pub const base_tag: link.File.Tag = .coff; |
| 34 | const msdos_stub = @embedFile("msdos-stub.bin"); | 34 | const msdos_stub = @embedFile("msdos-stub.bin"); |
| 35 | 35 | ||
| 36 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. | 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 | base: link.File, | 39 | base: link.File, |
| 40 | ptr_width: PtrWidth, | 40 | ptr_width: PtrWidth, |
| ... | @@ -129,7 +129,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -129,7 +129,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 129 | const self = try createEmpty(allocator, options); | 129 | const self = try createEmpty(allocator, options); |
| 130 | errdefer self.base.destroy(); | 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 | return self; | 133 | return self; |
| 134 | } | 134 | } |
| 135 | 135 | ||
| ... | @@ -413,7 +413,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff { | ... | @@ -413,7 +413,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff { |
| 413 | } | 413 | } |
| 414 | 414 | ||
| 415 | pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { | 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 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); | 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,7 +660,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { |
| 660 | defer tracy.end(); | 660 | defer tracy.end(); |
| 661 | 661 | ||
| 662 | if (build_options.have_llvm) | 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 | const typed_value = decl.typed_value.most_recent.typed_value; | 665 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 666 | if (typed_value.val.tag() == .extern_fn) { | 666 | if (typed_value.val.tag() == .extern_fn) { |
| ... | @@ -720,15 +720,15 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { | ... | @@ -720,15 +720,15 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { |
| 720 | } | 720 | } |
| 721 | 721 | ||
| 722 | pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { | 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 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | 725 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 726 | self.freeTextBlock(&decl.link.coff); | 726 | self.freeTextBlock(&decl.link.coff); |
| 727 | self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {}; | 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 { | 730 | pub fn updateDeclExports(self: *Coff, module: *Module, decl: *Module.Decl, exports: []const *Module.Export) !void { |
| 731 | if (self.llvm_ir_module) |_| return; | 731 | if (self.llvm_object) |_| return; |
| 732 | 732 | ||
| 733 | for (exports) |exp| { | 733 | for (exports) |exp| { |
| 734 | if (exp.options.section) |section_name| { | 734 | if (exp.options.section) |section_name| { |
| ... | @@ -771,7 +771,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void { | ... | @@ -771,7 +771,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void { |
| 771 | defer tracy.end(); | 771 | defer tracy.end(); |
| 772 | 772 | ||
| 773 | if (build_options.have_llvm) | 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 | if (self.text_section_size_dirty) { | 776 | if (self.text_section_size_dirty) { |
| 777 | // Write the new raw size in the .text header | 777 | // Write the new raw size in the .text header |
| ... | @@ -1308,7 +1308,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -1308,7 +1308,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1308 | } | 1308 | } |
| 1309 | 1309 | ||
| 1310 | pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { | 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 | return self.text_section_virtual_address + decl.link.coff.text_offset; | 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,7 +1318,7 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v |
| 1318 | 1318 | ||
| 1319 | pub fn deinit(self: *Coff) void { | 1319 | pub fn deinit(self: *Coff) void { |
| 1320 | if (build_options.have_llvm) | 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 | self.text_block_free_list.deinit(self.base.allocator); | 1323 | self.text_block_free_list.deinit(self.base.allocator); |
| 1324 | self.offset_table.deinit(self.base.allocator); | 1324 | self.offset_table.deinit(self.base.allocator); |
src/link/Elf.zig+13-13| ... | @@ -35,7 +35,7 @@ base: File, | ... | @@ -35,7 +35,7 @@ base: File, |
| 35 | ptr_width: PtrWidth, | 35 | ptr_width: PtrWidth, |
| 36 | 36 | ||
| 37 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. | 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 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | 40 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 41 | /// Same order as in the file. | 41 | /// Same order as in the file. |
| ... | @@ -232,7 +232,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -232,7 +232,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 232 | const self = try createEmpty(allocator, options); | 232 | const self = try createEmpty(allocator, options); |
| 233 | errdefer self.base.destroy(); | 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 | return self; | 236 | return self; |
| 237 | } | 237 | } |
| 238 | 238 | ||
| ... | @@ -299,7 +299,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { | ... | @@ -299,7 +299,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { |
| 299 | 299 | ||
| 300 | pub fn deinit(self: *Elf) void { | 300 | pub fn deinit(self: *Elf) void { |
| 301 | if (build_options.have_llvm) | 301 | if (build_options.have_llvm) |
| 302 | if (self.llvm_ir_module) |ir_module| | 302 | if (self.llvm_object) |ir_module| |
| 303 | ir_module.deinit(self.base.allocator); | 303 | ir_module.deinit(self.base.allocator); |
| 304 | 304 | ||
| 305 | self.sections.deinit(self.base.allocator); | 305 | self.sections.deinit(self.base.allocator); |
| ... | @@ -318,7 +318,7 @@ pub fn deinit(self: *Elf) void { | ... | @@ -318,7 +318,7 @@ pub fn deinit(self: *Elf) void { |
| 318 | } | 318 | } |
| 319 | 319 | ||
| 320 | pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 { | 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 | assert(decl.link.elf.local_sym_index != 0); | 322 | assert(decl.link.elf.local_sym_index != 0); |
| 323 | return self.local_symbols.items[decl.link.elf.local_sym_index].st_value; | 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,7 +438,7 @@ fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 { |
| 438 | } | 438 | } |
| 439 | 439 | ||
| 440 | pub fn populateMissingMetadata(self: *Elf) !void { | 440 | pub fn populateMissingMetadata(self: *Elf) !void { |
| 441 | assert(self.llvm_ir_module == null); | 441 | assert(self.llvm_object == null); |
| 442 | 442 | ||
| 443 | const small_ptr = switch (self.ptr_width) { | 443 | const small_ptr = switch (self.ptr_width) { |
| 444 | .p32 => true, | 444 | .p32 => true, |
| ... | @@ -745,7 +745,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { | ... | @@ -745,7 +745,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 745 | defer tracy.end(); | 745 | defer tracy.end(); |
| 746 | 746 | ||
| 747 | if (build_options.have_llvm) | 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 | // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the | 750 | // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the |
| 751 | // Zig source code. | 751 | // Zig source code. |
| ... | @@ -2111,7 +2111,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al | ... | @@ -2111,7 +2111,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al |
| 2111 | } | 2111 | } |
| 2112 | 2112 | ||
| 2113 | pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { | 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 | if (decl.link.elf.local_sym_index != 0) return; | 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,7 +2149,7 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { |
| 2149 | } | 2149 | } |
| 2150 | 2150 | ||
| 2151 | pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { | 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 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | 2154 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 2155 | self.freeTextBlock(&decl.link.elf); | 2155 | self.freeTextBlock(&decl.link.elf); |
| ... | @@ -2189,7 +2189,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { | ... | @@ -2189,7 +2189,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { |
| 2189 | defer tracy.end(); | 2189 | defer tracy.end(); |
| 2190 | 2190 | ||
| 2191 | if (build_options.have_llvm) | 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 | const typed_value = decl.typed_value.most_recent.typed_value; | 2194 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 2195 | if (typed_value.val.tag() == .extern_fn) { | 2195 | if (typed_value.val.tag() == .extern_fn) { |
| ... | @@ -2670,10 +2670,10 @@ fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const | ... | @@ -2670,10 +2670,10 @@ fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const |
| 2670 | pub fn updateDeclExports( | 2670 | pub fn updateDeclExports( |
| 2671 | self: *Elf, | 2671 | self: *Elf, |
| 2672 | module: *Module, | 2672 | module: *Module, |
| 2673 | decl: *const Module.Decl, | 2673 | decl: *Module.Decl, |
| 2674 | exports: []const *Module.Export, | 2674 | exports: []const *Module.Export, |
| 2675 | ) !void { | 2675 | ) !void { |
| 2676 | if (self.llvm_ir_module) |_| return; | 2676 | if (self.llvm_object) |_| return; |
| 2677 | 2677 | ||
| 2678 | const tracy = trace(@src()); | 2678 | const tracy = trace(@src()); |
| 2679 | defer tracy.end(); | 2679 | defer tracy.end(); |
| ... | @@ -2748,7 +2748,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec | ... | @@ -2748,7 +2748,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec |
| 2748 | const tracy = trace(@src()); | 2748 | const tracy = trace(@src()); |
| 2749 | defer tracy.end(); | 2749 | defer tracy.end(); |
| 2750 | 2750 | ||
| 2751 | if (self.llvm_ir_module) |_| return; | 2751 | if (self.llvm_object) |_| return; |
| 2752 | 2752 | ||
| 2753 | const tree = decl.container.file_scope.tree; | 2753 | const tree = decl.container.file_scope.tree; |
| 2754 | const node_tags = tree.nodes.items(.tag); | 2754 | const node_tags = tree.nodes.items(.tag); |
| ... | @@ -2773,7 +2773,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec | ... | @@ -2773,7 +2773,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec |
| 2773 | } | 2773 | } |
| 2774 | 2774 | ||
| 2775 | pub fn deleteExport(self: *Elf, exp: Export) void { | 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 | const sym_index = exp.sym_index orelse return; | 2778 | const sym_index = exp.sym_index orelse return; |
| 2779 | self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {}; | 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,7 +1340,7 @@ pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.D |
| 1340 | pub fn updateDeclExports( | 1340 | pub fn updateDeclExports( |
| 1341 | self: *MachO, | 1341 | self: *MachO, |
| 1342 | module: *Module, | 1342 | module: *Module, |
| 1343 | decl: *const Module.Decl, | 1343 | decl: *Module.Decl, |
| 1344 | exports: []const *Module.Export, | 1344 | exports: []const *Module.Export, |
| 1345 | ) !void { | 1345 | ) !void { |
| 1346 | const tracy = trace(@src()); | 1346 | const tracy = trace(@src()); |
src/main.zig+46-28| ... | @@ -1487,7 +1487,7 @@ fn buildOutputType( | ... | @@ -1487,7 +1487,7 @@ fn buildOutputType( |
| 1487 | for (diags.arch.?.allCpuModels()) |cpu| { | 1487 | for (diags.arch.?.allCpuModels()) |cpu| { |
| 1488 | help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help; | 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 | @tagName(diags.arch.?), help_text.items, | 1491 | @tagName(diags.arch.?), help_text.items, |
| 1492 | }); | 1492 | }); |
| 1493 | } | 1493 | } |
| ... | @@ -1499,7 +1499,7 @@ fn buildOutputType( | ... | @@ -1499,7 +1499,7 @@ fn buildOutputType( |
| 1499 | for (diags.arch.?.allFeaturesList()) |feature| { | 1499 | for (diags.arch.?.allFeaturesList()) |feature| { |
| 1500 | help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help; | 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 | @tagName(diags.arch.?), help_text.items, | 1503 | @tagName(diags.arch.?), help_text.items, |
| 1504 | }); | 1504 | }); |
| 1505 | } | 1505 | } |
| ... | @@ -1750,15 +1750,12 @@ fn buildOutputType( | ... | @@ -1750,15 +1750,12 @@ fn buildOutputType( |
| 1750 | } | 1750 | } |
| 1751 | 1751 | ||
| 1752 | const self_exe_path = try fs.selfExePathAlloc(arena); | 1752 | const self_exe_path = try fs.selfExePathAlloc(arena); |
| 1753 | var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| | 1753 | var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{ |
| 1754 | .{ | 1754 | .path = lib_dir, |
| 1755 | .path = lib_dir, | 1755 | .handle = try fs.cwd().openDir(lib_dir, .{}), |
| 1756 | .handle = try fs.cwd().openDir(lib_dir, .{}), | 1756 | } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { |
| 1757 | } | 1757 | fatal("unable to find zig installation directory: {s}", .{@errorName(err)}); |
| 1758 | else | 1758 | }; |
| 1759 | introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { | ||
| 1760 | fatal("unable to find zig installation directory: {s}", .{@errorName(err)}); | ||
| 1761 | }; | ||
| 1762 | defer zig_lib_directory.handle.close(); | 1759 | defer zig_lib_directory.handle.close(); |
| 1763 | 1760 | ||
| 1764 | var thread_pool: ThreadPool = undefined; | 1761 | var thread_pool: ThreadPool = undefined; |
| ... | @@ -2115,12 +2112,37 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi | ... | @@ -2115,12 +2112,37 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi |
| 2115 | } else switch (hook) { | 2112 | } else switch (hook) { |
| 2116 | .none => {}, | 2113 | .none => {}, |
| 2117 | .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}), | 2114 | .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}), |
| 2118 | .update => |full_path| _ = try comp.bin_file.options.emit.?.directory.handle.updateFile( | 2115 | .update => |full_path| { |
| 2119 | comp.bin_file.options.emit.?.sub_path, | 2116 | const bin_sub_path = comp.bin_file.options.emit.?.sub_path; |
| 2120 | fs.cwd(), | 2117 | const cwd = fs.cwd(); |
| 2121 | full_path, | 2118 | const cache_dir = comp.bin_file.options.emit.?.directory.handle; |
| 2122 | .{}, | 2119 | _ = try cache_dir.updateFile(bin_sub_path, cwd, full_path, .{}); |
| 2123 | ), | 2120 | |
| 2121 | // If a .pdb file is part of the expected output, we must also copy | ||
| 2122 | // it into place here. | ||
| 2123 | const coff_or_pe = switch (comp.bin_file.options.object_format) { | ||
| 2124 | .coff, .pe => true, | ||
| 2125 | else => false, | ||
| 2126 | }; | ||
| 2127 | const have_pdb = coff_or_pe and !comp.bin_file.options.strip; | ||
| 2128 | if (have_pdb) { | ||
| 2129 | // Replace `.out` or `.exe` with `.pdb` on both the source and destination | ||
| 2130 | const src_bin_ext = fs.path.extension(bin_sub_path); | ||
| 2131 | const dst_bin_ext = fs.path.extension(full_path); | ||
| 2132 | |||
| 2133 | const src_pdb_path = try std.fmt.allocPrint(gpa, "{s}.pdb", .{ | ||
| 2134 | bin_sub_path[0 .. bin_sub_path.len - src_bin_ext.len], | ||
| 2135 | }); | ||
| 2136 | defer gpa.free(src_pdb_path); | ||
| 2137 | |||
| 2138 | const dst_pdb_path = try std.fmt.allocPrint(gpa, "{s}.pdb", .{ | ||
| 2139 | full_path[0 .. full_path.len - dst_bin_ext.len], | ||
| 2140 | }); | ||
| 2141 | defer gpa.free(dst_pdb_path); | ||
| 2142 | |||
| 2143 | _ = try cache_dir.updateFile(src_pdb_path, cwd, dst_pdb_path, .{}); | ||
| 2144 | } | ||
| 2145 | }, | ||
| 2124 | } | 2146 | } |
| 2125 | } | 2147 | } |
| 2126 | 2148 | ||
| ... | @@ -2461,15 +2483,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v | ... | @@ -2461,15 +2483,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v |
| 2461 | } | 2483 | } |
| 2462 | } | 2484 | } |
| 2463 | 2485 | ||
| 2464 | var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| | 2486 | var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{ |
| 2465 | .{ | 2487 | .path = lib_dir, |
| 2466 | .path = lib_dir, | 2488 | .handle = try fs.cwd().openDir(lib_dir, .{}), |
| 2467 | .handle = try fs.cwd().openDir(lib_dir, .{}), | 2489 | } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { |
| 2468 | } | 2490 | fatal("unable to find zig installation directory: {s}", .{@errorName(err)}); |
| 2469 | else | 2491 | }; |
| 2470 | introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { | ||
| 2471 | fatal("unable to find zig installation directory: {s}", .{@errorName(err)}); | ||
| 2472 | }; | ||
| 2473 | defer zig_lib_directory.handle.close(); | 2492 | defer zig_lib_directory.handle.close(); |
| 2474 | 2493 | ||
| 2475 | const std_special = "std" ++ fs.path.sep_str ++ "special"; | 2494 | const std_special = "std" ++ fs.path.sep_str ++ "special"; |
| ... | @@ -3281,8 +3300,7 @@ pub const ClangArgIterator = struct { | ... | @@ -3281,8 +3300,7 @@ pub const ClangArgIterator = struct { |
| 3281 | self.zig_equivalent = clang_arg.zig_equivalent; | 3300 | self.zig_equivalent = clang_arg.zig_equivalent; |
| 3282 | break :find_clang_arg; | 3301 | break :find_clang_arg; |
| 3283 | }, | 3302 | }, |
| 3284 | } | 3303 | } else { |
| 3285 | else { | ||
| 3286 | fatal("Unknown Clang option: '{s}'", .{arg}); | 3304 | fatal("Unknown Clang option: '{s}'", .{arg}); |
| 3287 | } | 3305 | } |
| 3288 | } | 3306 | } |
src/register_manager.zig created+228| ... | @@ -0,0 +1,228 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const math = std.math; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const Allocator = std.mem.Allocator; | ||
| 5 | const ir = @import("ir.zig"); | ||
| 6 | const Type = @import("type.zig").Type; | ||
| 7 | const Module = @import("Module.zig"); | ||
| 8 | const LazySrcLoc = Module.LazySrcLoc; | ||
| 9 | |||
| 10 | const log = std.log.scoped(.register_manager); | ||
| 11 | |||
| 12 | pub fn RegisterManager( | ||
| 13 | comptime Function: type, | ||
| 14 | comptime Register: type, | ||
| 15 | comptime callee_preserved_regs: []const Register, | ||
| 16 | ) type { | ||
| 17 | return struct { | ||
| 18 | /// The key must be canonical register. | ||
| 19 | registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{}, | ||
| 20 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), | ||
| 21 | /// Tracks all registers allocated in the course of this function | ||
| 22 | allocated_registers: FreeRegInt = 0, | ||
| 23 | |||
| 24 | const Self = @This(); | ||
| 25 | |||
| 26 | /// An integer whose bits represent all the registers and whether they are free. | ||
| 27 | const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len); | ||
| 28 | const ShiftInt = math.Log2Int(FreeRegInt); | ||
| 29 | |||
| 30 | fn getFunction(self: *Self) *Function { | ||
| 31 | return @fieldParentPtr(Function, "register_manager", self); | ||
| 32 | } | ||
| 33 | |||
| 34 | pub fn deinit(self: *Self, allocator: *Allocator) void { | ||
| 35 | self.registers.deinit(allocator); | ||
| 36 | } | ||
| 37 | |||
| 38 | fn markRegUsed(self: *Self, reg: Register) void { | ||
| 39 | if (FreeRegInt == u0) return; | ||
| 40 | const index = reg.allocIndex() orelse return; | ||
| 41 | const shift = @intCast(ShiftInt, index); | ||
| 42 | const mask = @as(FreeRegInt, 1) << shift; | ||
| 43 | self.free_registers &= ~mask; | ||
| 44 | self.allocated_registers |= mask; | ||
| 45 | } | ||
| 46 | |||
| 47 | fn markRegFree(self: *Self, reg: Register) void { | ||
| 48 | if (FreeRegInt == u0) return; | ||
| 49 | const index = reg.allocIndex() orelse return; | ||
| 50 | const shift = @intCast(ShiftInt, index); | ||
| 51 | self.free_registers |= @as(FreeRegInt, 1) << shift; | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Returns whether this register was allocated in the course | ||
| 55 | /// of this function | ||
| 56 | pub fn isRegAllocated(self: Self, reg: Register) bool { | ||
| 57 | if (FreeRegInt == u0) return false; | ||
| 58 | const index = reg.allocIndex() orelse return false; | ||
| 59 | const shift = @intCast(ShiftInt, index); | ||
| 60 | return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Before calling, must ensureCapacity + 1 on self.registers. | ||
| 64 | /// Returns `null` if all registers are allocated. | ||
| 65 | pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register { | ||
| 66 | const free_index = @ctz(FreeRegInt, self.free_registers); | ||
| 67 | if (free_index >= callee_preserved_regs.len) { | ||
| 68 | return null; | ||
| 69 | } | ||
| 70 | |||
| 71 | // This is necessary because the return type of @ctz is 1 | ||
| 72 | // bit longer than ShiftInt if callee_preserved_regs.len | ||
| 73 | // is a power of two. This int cast is always safe because | ||
| 74 | // free_index < callee_preserved_regs.len | ||
| 75 | const shift = @intCast(ShiftInt, free_index); | ||
| 76 | const mask = @as(FreeRegInt, 1) << shift; | ||
| 77 | self.free_registers &= ~mask; | ||
| 78 | self.allocated_registers |= mask; | ||
| 79 | |||
| 80 | const reg = callee_preserved_regs[free_index]; | ||
| 81 | self.registers.putAssumeCapacityNoClobber(reg, inst); | ||
| 82 | log.debug("alloc {} => {*}", .{ reg, inst }); | ||
| 83 | return reg; | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Before calling, must ensureCapacity + 1 on self.registers. | ||
| 87 | pub fn allocReg(self: *Self, inst: *ir.Inst) !Register { | ||
| 88 | return self.tryAllocReg(inst) orelse b: { | ||
| 89 | // We'll take over the first register. Move the instruction that was previously | ||
| 90 | // there to a stack allocation. | ||
| 91 | const reg = callee_preserved_regs[0]; | ||
| 92 | const regs_entry = self.registers.getEntry(reg).?; | ||
| 93 | const spilled_inst = regs_entry.value; | ||
| 94 | regs_entry.value = inst; | ||
| 95 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | ||
| 96 | |||
| 97 | break :b reg; | ||
| 98 | }; | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Does not track the register. | ||
| 102 | /// Returns `null` if all registers are allocated. | ||
| 103 | pub fn findUnusedReg(self: *Self) ?Register { | ||
| 104 | const free_index = @ctz(FreeRegInt, self.free_registers); | ||
| 105 | if (free_index >= callee_preserved_regs.len) { | ||
| 106 | return null; | ||
| 107 | } | ||
| 108 | return callee_preserved_regs[free_index]; | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Does not track the register. | ||
| 112 | pub fn allocRegWithoutTracking(self: *Self) !Register { | ||
| 113 | return self.findUnusedReg() orelse b: { | ||
| 114 | // We'll take over the first register. Move the instruction that was previously | ||
| 115 | // there to a stack allocation. | ||
| 116 | const reg = callee_preserved_regs[0]; | ||
| 117 | const regs_entry = self.registers.remove(reg).?; | ||
| 118 | const spilled_inst = regs_entry.value; | ||
| 119 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | ||
| 120 | |||
| 121 | break :b reg; | ||
| 122 | }; | ||
| 123 | } | ||
| 124 | |||
| 125 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) !void { | ||
| 126 | try self.registers.putNoClobber(self.getFunction().gpa, reg, inst); | ||
| 127 | self.markRegUsed(reg); | ||
| 128 | } | ||
| 129 | |||
| 130 | pub fn freeReg(self: *Self, reg: Register) void { | ||
| 131 | _ = self.registers.remove(reg); | ||
| 132 | self.markRegFree(reg); | ||
| 133 | } | ||
| 134 | }; | ||
| 135 | } | ||
| 136 | |||
| 137 | const MockRegister = enum(u2) { | ||
| 138 | r0, r1, r2, r3, | ||
| 139 | |||
| 140 | pub fn allocIndex(self: MockRegister) ?u2 { | ||
| 141 | inline for (mock_callee_preserved_regs) |cpreg, i| { | ||
| 142 | if (self == cpreg) return i; | ||
| 143 | } | ||
| 144 | return null; | ||
| 145 | } | ||
| 146 | }; | ||
| 147 | |||
| 148 | const mock_callee_preserved_regs = [_]MockRegister{ .r2, .r3 }; | ||
| 149 | |||
| 150 | const MockFunction = struct { | ||
| 151 | allocator: *Allocator, | ||
| 152 | register_manager: RegisterManager(Self, MockRegister, &mock_callee_preserved_regs) = .{}, | ||
| 153 | spilled: std.ArrayListUnmanaged(MockRegister) = .{}, | ||
| 154 | |||
| 155 | const Self = @This(); | ||
| 156 | |||
| 157 | pub fn deinit(self: *Self) void { | ||
| 158 | self.register_manager.deinit(self.allocator); | ||
| 159 | self.spilled.deinit(self.allocator); | ||
| 160 | } | ||
| 161 | |||
| 162 | pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: MockRegister, inst: *ir.Inst) !void { | ||
| 163 | try self.spilled.append(self.allocator, reg); | ||
| 164 | } | ||
| 165 | }; | ||
| 166 | |||
| 167 | test "tryAllocReg: no spilling" { | ||
| 168 | const allocator = std.testing.allocator; | ||
| 169 | |||
| 170 | var function = MockFunction{ | ||
| 171 | .allocator = allocator, | ||
| 172 | }; | ||
| 173 | defer function.deinit(); | ||
| 174 | |||
| 175 | var mock_instruction = ir.Inst{ | ||
| 176 | .tag = .breakpoint, | ||
| 177 | .ty = Type.initTag(.void), | ||
| 178 | .src = .unneeded, | ||
| 179 | }; | ||
| 180 | |||
| 181 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | ||
| 182 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); | ||
| 183 | |||
| 184 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | ||
| 185 | std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction)); | ||
| 186 | std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction)); | ||
| 187 | std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction)); | ||
| 188 | |||
| 189 | std.testing.expect(function.register_manager.isRegAllocated(.r2)); | ||
| 190 | std.testing.expect(function.register_manager.isRegAllocated(.r3)); | ||
| 191 | |||
| 192 | function.register_manager.freeReg(.r2); | ||
| 193 | function.register_manager.freeReg(.r3); | ||
| 194 | |||
| 195 | std.testing.expect(function.register_manager.isRegAllocated(.r2)); | ||
| 196 | std.testing.expect(function.register_manager.isRegAllocated(.r3)); | ||
| 197 | } | ||
| 198 | |||
| 199 | test "allocReg: spilling" { | ||
| 200 | const allocator = std.testing.allocator; | ||
| 201 | |||
| 202 | var function = MockFunction{ | ||
| 203 | .allocator = allocator, | ||
| 204 | }; | ||
| 205 | defer function.deinit(); | ||
| 206 | |||
| 207 | var mock_instruction = ir.Inst{ | ||
| 208 | .tag = .breakpoint, | ||
| 209 | .ty = Type.initTag(.void), | ||
| 210 | .src = .unneeded, | ||
| 211 | }; | ||
| 212 | |||
| 213 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | ||
| 214 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); | ||
| 215 | |||
| 216 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | ||
| 217 | std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction)); | ||
| 218 | std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction)); | ||
| 219 | |||
| 220 | // Spill a register | ||
| 221 | std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction)); | ||
| 222 | std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items); | ||
| 223 | |||
| 224 | // No spilling necessary | ||
| 225 | function.register_manager.freeReg(.r3); | ||
| 226 | std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction)); | ||
| 227 | std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items); | ||
| 228 | } | ||
src/stage1/analyze.cpp+3-1| ... | @@ -8723,7 +8723,9 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS | ... | @@ -8723,7 +8723,9 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS |
| 8723 | assert(async_frame_type->id == ZigTypeIdFnFrame); | 8723 | assert(async_frame_type->id == ZigTypeIdFnFrame); |
| 8724 | assert(field_type->id == ZigTypeIdFn); | 8724 | assert(field_type->id == ZigTypeIdFn); |
| 8725 | resolve_llvm_types_fn(g, async_frame_type->data.frame.fn); | 8725 | resolve_llvm_types_fn(g, async_frame_type->data.frame.fn); |
| 8726 | llvm_type = LLVMPointerType(async_frame_type->data.frame.fn->raw_type_ref, 0); | 8726 | |
| 8727 | const unsigned addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref); | ||
| 8728 | llvm_type = LLVMPointerType(async_frame_type->data.frame.fn->raw_type_ref, addrspace); | ||
| 8727 | } else { | 8729 | } else { |
| 8728 | llvm_type = get_llvm_type(g, field_type); | 8730 | llvm_type = get_llvm_type(g, field_type); |
| 8729 | } | 8731 | } |
src/stage1/ir.cpp+1-10| ... | @@ -7641,12 +7641,7 @@ static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, | ... | @@ -7641,12 +7641,7 @@ static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, |
| 7641 | 7641 | ||
| 7642 | bool is_nosuspend = get_scope_nosuspend(scope) != nullptr; | 7642 | bool is_nosuspend = get_scope_nosuspend(scope) != nullptr; |
| 7643 | CallModifier modifier = node->data.fn_call_expr.modifier; | 7643 | CallModifier modifier = node->data.fn_call_expr.modifier; |
| 7644 | if (is_nosuspend) { | 7644 | if (is_nosuspend && modifier != CallModifierAsync) { |
| 7645 | if (modifier == CallModifierAsync) { | ||
| 7646 | add_node_error(irb->codegen, node, | ||
| 7647 | buf_sprintf("async call in nosuspend scope")); | ||
| 7648 | return irb->codegen->invalid_inst_src; | ||
| 7649 | } | ||
| 7650 | modifier = CallModifierNoSuspend; | 7645 | modifier = CallModifierNoSuspend; |
| 7651 | } | 7646 | } |
| 7652 | 7647 | ||
| ... | @@ -10129,10 +10124,6 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod | ... | @@ -10129,10 +10124,6 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod |
| 10129 | 10124 | ||
| 10130 | static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) { | 10125 | static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) { |
| 10131 | assert(node->type == NodeTypeResume); | 10126 | assert(node->type == NodeTypeResume); |
| 10132 | if (get_scope_nosuspend(scope) != nullptr) { | ||
| 10133 | add_node_error(irb->codegen, node, buf_sprintf("resume in nosuspend scope")); | ||
| 10134 | return irb->codegen->invalid_inst_src; | ||
| 10135 | } | ||
| 10136 | 10127 | ||
| 10137 | IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr); | 10128 | IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr); |
| 10138 | if (target_inst == irb->codegen->invalid_inst_src) | 10129 | if (target_inst == irb->codegen->invalid_inst_src) |
src/translate_c.zig+1-1| ... | @@ -4343,7 +4343,7 @@ fn isZigPrimitiveType(name: []const u8) bool { | ... | @@ -4343,7 +4343,7 @@ fn isZigPrimitiveType(name: []const u8) bool { |
| 4343 | } | 4343 | } |
| 4344 | return true; | 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 | const MacroCtx = struct { | 4349 | const MacroCtx = struct { |
src/type.zig+290-196| ... | @@ -4,6 +4,7 @@ const assert = std.debug.assert; | ... | @@ -4,6 +4,7 @@ const assert = std.debug.assert; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | const Target = std.Target; | 5 | const Target = std.Target; |
| 6 | const Module = @import("Module.zig"); | 6 | const Module = @import("Module.zig"); |
| 7 | const log = std.log.scoped(.Type); | ||
| 7 | 8 | ||
| 8 | /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication. | 9 | /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication. |
| 9 | /// It's important for this type to be small. | 10 | /// It's important for this type to be small. |
| ... | @@ -92,11 +93,9 @@ pub const Type = extern union { | ... | @@ -92,11 +93,9 @@ pub const Type = extern union { |
| 92 | 93 | ||
| 93 | .anyerror_void_error_union, .error_union => return .ErrorUnion, | 94 | .anyerror_void_error_union, .error_union => return .ErrorUnion, |
| 94 | 95 | ||
| 95 | .anyframe_T, .@"anyframe" => return .AnyFrame, | 96 | .empty_struct => return .Struct, |
| 96 | 97 | .empty_struct_literal => return .Struct, | |
| 97 | .@"struct", .empty_struct => return .Struct, | 98 | .@"struct" => return .Struct, |
| 98 | .@"enum" => return .Enum, | ||
| 99 | .@"union" => return .Union, | ||
| 100 | 99 | ||
| 101 | .var_args_param => unreachable, // can be any type | 100 | .var_args_param => unreachable, // can be any type |
| 102 | } | 101 | } |
| ... | @@ -173,6 +172,125 @@ pub const Type = extern union { | ... | @@ -173,6 +172,125 @@ pub const Type = extern union { |
| 173 | }; | 172 | }; |
| 174 | } | 173 | } |
| 175 | 174 | ||
| 175 | pub fn ptrInfo(self: Type) Payload.Pointer { | ||
| 176 | switch (self.tag()) { | ||
| 177 | .single_const_pointer_to_comptime_int => return .{ .data = .{ | ||
| 178 | .pointee_type = Type.initTag(.comptime_int), | ||
| 179 | .sentinel = null, | ||
| 180 | .@"align" = 0, | ||
| 181 | .bit_offset = 0, | ||
| 182 | .host_size = 0, | ||
| 183 | .@"allowzero" = false, | ||
| 184 | .mutable = false, | ||
| 185 | .@"volatile" = false, | ||
| 186 | .size = .One, | ||
| 187 | } }, | ||
| 188 | .const_slice_u8 => return .{ .data = .{ | ||
| 189 | .pointee_type = Type.initTag(.u8), | ||
| 190 | .sentinel = null, | ||
| 191 | .@"align" = 0, | ||
| 192 | .bit_offset = 0, | ||
| 193 | .host_size = 0, | ||
| 194 | .@"allowzero" = false, | ||
| 195 | .mutable = false, | ||
| 196 | .@"volatile" = false, | ||
| 197 | .size = .Slice, | ||
| 198 | } }, | ||
| 199 | .single_const_pointer => return .{ .data = .{ | ||
| 200 | .pointee_type = self.castPointer().?.data, | ||
| 201 | .sentinel = null, | ||
| 202 | .@"align" = 0, | ||
| 203 | .bit_offset = 0, | ||
| 204 | .host_size = 0, | ||
| 205 | .@"allowzero" = false, | ||
| 206 | .mutable = false, | ||
| 207 | .@"volatile" = false, | ||
| 208 | .size = .One, | ||
| 209 | } }, | ||
| 210 | .single_mut_pointer => return .{ .data = .{ | ||
| 211 | .pointee_type = self.castPointer().?.data, | ||
| 212 | .sentinel = null, | ||
| 213 | .@"align" = 0, | ||
| 214 | .bit_offset = 0, | ||
| 215 | .host_size = 0, | ||
| 216 | .@"allowzero" = false, | ||
| 217 | .mutable = true, | ||
| 218 | .@"volatile" = false, | ||
| 219 | .size = .One, | ||
| 220 | } }, | ||
| 221 | .many_const_pointer => return .{ .data = .{ | ||
| 222 | .pointee_type = self.castPointer().?.data, | ||
| 223 | .sentinel = null, | ||
| 224 | .@"align" = 0, | ||
| 225 | .bit_offset = 0, | ||
| 226 | .host_size = 0, | ||
| 227 | .@"allowzero" = false, | ||
| 228 | .mutable = false, | ||
| 229 | .@"volatile" = false, | ||
| 230 | .size = .Many, | ||
| 231 | } }, | ||
| 232 | .many_mut_pointer => return .{ .data = .{ | ||
| 233 | .pointee_type = self.castPointer().?.data, | ||
| 234 | .sentinel = null, | ||
| 235 | .@"align" = 0, | ||
| 236 | .bit_offset = 0, | ||
| 237 | .host_size = 0, | ||
| 238 | .@"allowzero" = false, | ||
| 239 | .mutable = true, | ||
| 240 | .@"volatile" = false, | ||
| 241 | .size = .Many, | ||
| 242 | } }, | ||
| 243 | .c_const_pointer => return .{ .data = .{ | ||
| 244 | .pointee_type = self.castPointer().?.data, | ||
| 245 | .sentinel = null, | ||
| 246 | .@"align" = 0, | ||
| 247 | .bit_offset = 0, | ||
| 248 | .host_size = 0, | ||
| 249 | .@"allowzero" = false, | ||
| 250 | .mutable = false, | ||
| 251 | .@"volatile" = false, | ||
| 252 | .size = .C, | ||
| 253 | } }, | ||
| 254 | .c_mut_pointer => return .{ .data = .{ | ||
| 255 | .pointee_type = self.castPointer().?.data, | ||
| 256 | .sentinel = null, | ||
| 257 | .@"align" = 0, | ||
| 258 | .bit_offset = 0, | ||
| 259 | .host_size = 0, | ||
| 260 | .@"allowzero" = false, | ||
| 261 | .mutable = true, | ||
| 262 | .@"volatile" = false, | ||
| 263 | .size = .C, | ||
| 264 | } }, | ||
| 265 | .const_slice => return .{ .data = .{ | ||
| 266 | .pointee_type = self.castPointer().?.data, | ||
| 267 | .sentinel = null, | ||
| 268 | .@"align" = 0, | ||
| 269 | .bit_offset = 0, | ||
| 270 | .host_size = 0, | ||
| 271 | .@"allowzero" = false, | ||
| 272 | .mutable = false, | ||
| 273 | .@"volatile" = false, | ||
| 274 | .size = .Slice, | ||
| 275 | } }, | ||
| 276 | .mut_slice => return .{ .data = .{ | ||
| 277 | .pointee_type = self.castPointer().?.data, | ||
| 278 | .sentinel = null, | ||
| 279 | .@"align" = 0, | ||
| 280 | .bit_offset = 0, | ||
| 281 | .host_size = 0, | ||
| 282 | .@"allowzero" = false, | ||
| 283 | .mutable = true, | ||
| 284 | .@"volatile" = false, | ||
| 285 | .size = .Slice, | ||
| 286 | } }, | ||
| 287 | |||
| 288 | .pointer => return self.castTag(.pointer).?.*, | ||
| 289 | |||
| 290 | else => unreachable, | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 176 | pub fn eql(a: Type, b: Type) bool { | 294 | pub fn eql(a: Type, b: Type) bool { |
| 177 | // As a shortcut, if the small tags / addresses match, we're done. | 295 | // As a shortcut, if the small tags / addresses match, we're done. |
| 178 | if (a.tag_if_small_enough == b.tag_if_small_enough) | 296 | if (a.tag_if_small_enough == b.tag_if_small_enough) |
| ... | @@ -195,25 +313,38 @@ pub const Type = extern union { | ... | @@ -195,25 +313,38 @@ pub const Type = extern union { |
| 195 | return a.elemType().eql(b.elemType()); | 313 | return a.elemType().eql(b.elemType()); |
| 196 | }, | 314 | }, |
| 197 | .Pointer => { | 315 | .Pointer => { |
| 198 | // Hot path for common case: | 316 | const info_a = a.ptrInfo().data; |
| 199 | if (a.castPointer()) |a_payload| { | 317 | const info_b = b.ptrInfo().data; |
| 200 | if (b.castPointer()) |b_payload| { | 318 | if (!info_a.pointee_type.eql(info_b.pointee_type)) |
| 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) | ||
| 207 | return false; | 319 | return false; |
| 208 | 320 | if (info_a.size != info_b.size) | |
| 209 | const ptr_size_a = ptrSize(a); | 321 | return false; |
| 210 | const ptr_size_b = ptrSize(b); | 322 | if (info_a.mutable != info_b.mutable) |
| 211 | if (ptr_size_a != ptr_size_b) | 323 | return false; |
| 324 | if (info_a.@"volatile" != info_b.@"volatile") | ||
| 325 | return false; | ||
| 326 | if (info_a.@"allowzero" != info_b.@"allowzero") | ||
| 327 | return false; | ||
| 328 | if (info_a.bit_offset != info_b.bit_offset) | ||
| 329 | return false; | ||
| 330 | if (info_a.host_size != info_b.host_size) | ||
| 212 | return false; | 331 | return false; |
| 213 | 332 | ||
| 214 | std.debug.panic("TODO implement more pointer Type equality comparison: {} and {}", .{ | 333 | const sentinel_a = info_a.sentinel; |
| 215 | a, b, | 334 | const sentinel_b = info_b.sentinel; |
| 216 | }); | 335 | if (sentinel_a) |sa| { |
| 336 | if (sentinel_b) |sb| { | ||
| 337 | if (!sa.eql(sb)) | ||
| 338 | return false; | ||
| 339 | } else { | ||
| 340 | return false; | ||
| 341 | } | ||
| 342 | } else { | ||
| 343 | if (sentinel_b != null) | ||
| 344 | return false; | ||
| 345 | } | ||
| 346 | |||
| 347 | return true; | ||
| 217 | }, | 348 | }, |
| 218 | .Int => { | 349 | .Int => { |
| 219 | // Detect that e.g. u64 != usize, even if the bits match on a particular target. | 350 | // Detect that e.g. u64 != usize, even if the bits match on a particular target. |
| ... | @@ -399,10 +530,10 @@ pub const Type = extern union { | ... | @@ -399,10 +530,10 @@ pub const Type = extern union { |
| 399 | .const_slice_u8, | 530 | .const_slice_u8, |
| 400 | .enum_literal, | 531 | .enum_literal, |
| 401 | .anyerror_void_error_union, | 532 | .anyerror_void_error_union, |
| 402 | .@"anyframe", | ||
| 403 | .inferred_alloc_const, | 533 | .inferred_alloc_const, |
| 404 | .inferred_alloc_mut, | 534 | .inferred_alloc_mut, |
| 405 | .var_args_param, | 535 | .var_args_param, |
| 536 | .empty_struct_literal, | ||
| 406 | => unreachable, | 537 | => unreachable, |
| 407 | 538 | ||
| 408 | .array_u8, | 539 | .array_u8, |
| ... | @@ -420,7 +551,6 @@ pub const Type = extern union { | ... | @@ -420,7 +551,6 @@ pub const Type = extern union { |
| 420 | .optional, | 551 | .optional, |
| 421 | .optional_single_mut_pointer, | 552 | .optional_single_mut_pointer, |
| 422 | .optional_single_const_pointer, | 553 | .optional_single_const_pointer, |
| 423 | .anyframe_T, | ||
| 424 | => return self.copyPayloadShallow(allocator, Payload.ElemType), | 554 | => return self.copyPayloadShallow(allocator, Payload.ElemType), |
| 425 | 555 | ||
| 426 | .int_signed, | 556 | .int_signed, |
| ... | @@ -480,13 +610,10 @@ pub const Type = extern union { | ... | @@ -480,13 +610,10 @@ pub const Type = extern union { |
| 480 | .payload = try payload.payload.copy(allocator), | 610 | .payload = try payload.payload.copy(allocator), |
| 481 | }); | 611 | }); |
| 482 | }, | 612 | }, |
| 483 | .error_set => return self.copyPayloadShallow(allocator, Payload.Decl), | 613 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), |
| 484 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), | 614 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), |
| 485 | .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), | 615 | .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), |
| 486 | |||
| 487 | .@"enum" => return self.copyPayloadShallow(allocator, Payload.Enum), | ||
| 488 | .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), | 616 | .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), |
| 489 | .@"union" => return self.copyPayloadShallow(allocator, Payload.Union), | ||
| 490 | .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque), | 617 | .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque), |
| 491 | } | 618 | } |
| 492 | } | 619 | } |
| ... | @@ -549,9 +676,8 @@ pub const Type = extern union { | ... | @@ -549,9 +676,8 @@ pub const Type = extern union { |
| 549 | .@"null" => return out_stream.writeAll("@Type(.Null)"), | 676 | .@"null" => return out_stream.writeAll("@Type(.Null)"), |
| 550 | .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"), | 677 | .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"), |
| 551 | 678 | ||
| 552 | // TODO this should print the structs name | 679 | .empty_struct, .empty_struct_literal => return out_stream.writeAll("struct {}"), |
| 553 | .empty_struct => return out_stream.writeAll("struct {}"), | 680 | .@"struct" => return out_stream.writeAll("(struct)"), |
| 554 | .@"anyframe" => return out_stream.writeAll("anyframe"), | ||
| 555 | .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"), | 681 | .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"), |
| 556 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), | 682 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 557 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), | 683 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), |
| ... | @@ -579,12 +705,6 @@ pub const Type = extern union { | ... | @@ -579,12 +705,6 @@ pub const Type = extern union { |
| 579 | continue; | 705 | continue; |
| 580 | }, | 706 | }, |
| 581 | 707 | ||
| 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 | .array_u8 => { | 708 | .array_u8 => { |
| 589 | const len = ty.castTag(.array_u8).?.data; | 709 | const len = ty.castTag(.array_u8).?.data; |
| 590 | return out_stream.print("[{d}]u8", .{len}); | 710 | return out_stream.print("[{d}]u8", .{len}); |
| ... | @@ -715,8 +835,8 @@ pub const Type = extern union { | ... | @@ -715,8 +835,8 @@ pub const Type = extern union { |
| 715 | continue; | 835 | continue; |
| 716 | }, | 836 | }, |
| 717 | .error_set => { | 837 | .error_set => { |
| 718 | const decl = ty.castTag(.error_set).?.data; | 838 | const error_set = ty.castTag(.error_set).?.data; |
| 719 | return out_stream.writeAll(std.mem.spanZ(decl.name)); | 839 | return out_stream.writeAll(std.mem.spanZ(error_set.owner_decl.name)); |
| 720 | }, | 840 | }, |
| 721 | .error_set_single => { | 841 | .error_set_single => { |
| 722 | const name = ty.castTag(.error_set_single).?.data; | 842 | const name = ty.castTag(.error_set_single).?.data; |
| ... | @@ -725,9 +845,6 @@ pub const Type = extern union { | ... | @@ -725,9 +845,6 @@ pub const Type = extern union { |
| 725 | .inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"), | 845 | .inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"), |
| 726 | .inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"), | 846 | .inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"), |
| 727 | // TODO use declaration name | 847 | // 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 | .@"opaque" => return out_stream.writeAll("opaque {}"), | 848 | .@"opaque" => return out_stream.writeAll("opaque {}"), |
| 732 | } | 849 | } |
| 733 | unreachable; | 850 | unreachable; |
| ... | @@ -822,12 +939,22 @@ pub const Type = extern union { | ... | @@ -822,12 +939,22 @@ pub const Type = extern union { |
| 822 | .optional, | 939 | .optional, |
| 823 | .optional_single_mut_pointer, | 940 | .optional_single_mut_pointer, |
| 824 | .optional_single_const_pointer, | 941 | .optional_single_const_pointer, |
| 825 | .@"anyframe", | ||
| 826 | .anyframe_T, | ||
| 827 | .anyerror_void_error_union, | 942 | .anyerror_void_error_union, |
| 828 | .error_set, | 943 | .error_set, |
| 829 | .error_set_single, | 944 | .error_set_single, |
| 830 | => true, | 945 | => true, |
| 946 | |||
| 947 | .@"struct" => { | ||
| 948 | // TODO introduce lazy value mechanism | ||
| 949 | const struct_obj = self.castTag(.@"struct").?.data; | ||
| 950 | for (struct_obj.fields.entries.items) |entry| { | ||
| 951 | if (entry.value.ty.hasCodeGenBits()) | ||
| 952 | return true; | ||
| 953 | } else { | ||
| 954 | return false; | ||
| 955 | } | ||
| 956 | }, | ||
| 957 | |||
| 831 | // TODO lazy types | 958 | // TODO lazy types |
| 832 | .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, | 959 | .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, |
| 833 | .array_u8 => self.arrayLen() != 0, | 960 | .array_u8 => self.arrayLen() != 0, |
| ... | @@ -839,10 +966,6 @@ pub const Type = extern union { | ... | @@ -839,10 +966,6 @@ pub const Type = extern union { |
| 839 | return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits(); | 966 | return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits(); |
| 840 | }, | 967 | }, |
| 841 | 968 | ||
| 842 | .@"enum" => @panic("TODO"), | ||
| 843 | .@"struct" => @panic("TODO"), | ||
| 844 | .@"union" => @panic("TODO"), | ||
| 845 | |||
| 846 | .c_void, | 969 | .c_void, |
| 847 | .void, | 970 | .void, |
| 848 | .type, | 971 | .type, |
| ... | @@ -853,6 +976,7 @@ pub const Type = extern union { | ... | @@ -853,6 +976,7 @@ pub const Type = extern union { |
| 853 | .@"undefined", | 976 | .@"undefined", |
| 854 | .enum_literal, | 977 | .enum_literal, |
| 855 | .empty_struct, | 978 | .empty_struct, |
| 979 | .empty_struct_literal, | ||
| 856 | .@"opaque", | 980 | .@"opaque", |
| 857 | => false, | 981 | => false, |
| 858 | 982 | ||
| ... | @@ -863,7 +987,39 @@ pub const Type = extern union { | ... | @@ -863,7 +987,39 @@ pub const Type = extern union { |
| 863 | } | 987 | } |
| 864 | 988 | ||
| 865 | pub fn isNoReturn(self: Type) bool { | 989 | pub fn isNoReturn(self: Type) bool { |
| 866 | return self.zigTypeTag() == .NoReturn; | 990 | const definitely_correct_result = self.zigTypeTag() == .NoReturn; |
| 991 | const fast_result = self.tag_if_small_enough == @enumToInt(Tag.noreturn); | ||
| 992 | assert(fast_result == definitely_correct_result); | ||
| 993 | return fast_result; | ||
| 994 | } | ||
| 995 | |||
| 996 | pub fn ptrAlignment(self: Type, target: Target) u32 { | ||
| 997 | switch (self.tag()) { | ||
| 998 | .single_const_pointer, | ||
| 999 | .single_mut_pointer, | ||
| 1000 | .many_const_pointer, | ||
| 1001 | .many_mut_pointer, | ||
| 1002 | .c_const_pointer, | ||
| 1003 | .c_mut_pointer, | ||
| 1004 | .const_slice, | ||
| 1005 | .mut_slice, | ||
| 1006 | .optional_single_const_pointer, | ||
| 1007 | .optional_single_mut_pointer, | ||
| 1008 | => return self.cast(Payload.ElemType).?.data.abiAlignment(target), | ||
| 1009 | |||
| 1010 | .const_slice_u8 => return 1, | ||
| 1011 | |||
| 1012 | .pointer => { | ||
| 1013 | const ptr_info = self.castTag(.pointer).?.data; | ||
| 1014 | if (ptr_info.@"align" != 0) { | ||
| 1015 | return ptr_info.@"align"; | ||
| 1016 | } else { | ||
| 1017 | return ptr_info.pointee_type.abiAlignment(); | ||
| 1018 | } | ||
| 1019 | }, | ||
| 1020 | |||
| 1021 | else => unreachable, | ||
| 1022 | } | ||
| 867 | } | 1023 | } |
| 868 | 1024 | ||
| 869 | /// Asserts that hasCodeGenBits() is true. | 1025 | /// Asserts that hasCodeGenBits() is true. |
| ... | @@ -907,17 +1063,9 @@ pub const Type = extern union { | ... | @@ -907,17 +1063,9 @@ pub const Type = extern union { |
| 907 | .mut_slice, | 1063 | .mut_slice, |
| 908 | .optional_single_const_pointer, | 1064 | .optional_single_const_pointer, |
| 909 | .optional_single_mut_pointer, | 1065 | .optional_single_mut_pointer, |
| 910 | .@"anyframe", | 1066 | .pointer, |
| 911 | .anyframe_T, | ||
| 912 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), | 1067 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 913 | 1068 | ||
| 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 | .c_short => return @divExact(CType.short.sizeInBits(target), 8), | 1069 | .c_short => return @divExact(CType.short.sizeInBits(target), 8), |
| 922 | .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8), | 1070 | .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8), |
| 923 | .c_int => return @divExact(CType.int.sizeInBits(target), 8), | 1071 | .c_int => return @divExact(CType.int.sizeInBits(target), 8), |
| ... | @@ -967,9 +1115,9 @@ pub const Type = extern union { | ... | @@ -967,9 +1115,9 @@ pub const Type = extern union { |
| 967 | @panic("TODO abiAlignment error union"); | 1115 | @panic("TODO abiAlignment error union"); |
| 968 | }, | 1116 | }, |
| 969 | 1117 | ||
| 970 | .@"enum" => self.cast(Payload.Enum).?.abiAlignment(target), | 1118 | .@"struct" => { |
| 971 | .@"struct" => @panic("TODO"), | 1119 | @panic("TODO abiAlignment struct"); |
| 972 | .@"union" => @panic("TODO"), | 1120 | }, |
| 973 | 1121 | ||
| 974 | .c_void, | 1122 | .c_void, |
| 975 | .void, | 1123 | .void, |
| ... | @@ -981,6 +1129,7 @@ pub const Type = extern union { | ... | @@ -981,6 +1129,7 @@ pub const Type = extern union { |
| 981 | .@"undefined", | 1129 | .@"undefined", |
| 982 | .enum_literal, | 1130 | .enum_literal, |
| 983 | .empty_struct, | 1131 | .empty_struct, |
| 1132 | .empty_struct_literal, | ||
| 984 | .inferred_alloc_const, | 1133 | .inferred_alloc_const, |
| 985 | .inferred_alloc_mut, | 1134 | .inferred_alloc_mut, |
| 986 | .@"opaque", | 1135 | .@"opaque", |
| ... | @@ -1008,11 +1157,16 @@ pub const Type = extern union { | ... | @@ -1008,11 +1157,16 @@ pub const Type = extern union { |
| 1008 | .enum_literal => unreachable, | 1157 | .enum_literal => unreachable, |
| 1009 | .single_const_pointer_to_comptime_int => unreachable, | 1158 | .single_const_pointer_to_comptime_int => unreachable, |
| 1010 | .empty_struct => unreachable, | 1159 | .empty_struct => unreachable, |
| 1160 | .empty_struct_literal => unreachable, | ||
| 1011 | .inferred_alloc_const => unreachable, | 1161 | .inferred_alloc_const => unreachable, |
| 1012 | .inferred_alloc_mut => unreachable, | 1162 | .inferred_alloc_mut => unreachable, |
| 1013 | .@"opaque" => unreachable, | 1163 | .@"opaque" => unreachable, |
| 1014 | .var_args_param => unreachable, | 1164 | .var_args_param => unreachable, |
| 1015 | 1165 | ||
| 1166 | .@"struct" => { | ||
| 1167 | @panic("TODO abiSize struct"); | ||
| 1168 | }, | ||
| 1169 | |||
| 1016 | .u8, | 1170 | .u8, |
| 1017 | .i8, | 1171 | .i8, |
| 1018 | .bool, | 1172 | .bool, |
| ... | @@ -1038,7 +1192,7 @@ pub const Type = extern union { | ... | @@ -1038,7 +1192,7 @@ pub const Type = extern union { |
| 1038 | .i64, .u64 => return 8, | 1192 | .i64, .u64 => return 8, |
| 1039 | .u128, .i128 => return 16, | 1193 | .u128, .i128 => return 16, |
| 1040 | 1194 | ||
| 1041 | .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), | 1195 | .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 1042 | 1196 | ||
| 1043 | .const_slice, | 1197 | .const_slice, |
| 1044 | .mut_slice, | 1198 | .mut_slice, |
| ... | @@ -1119,10 +1273,6 @@ pub const Type = extern union { | ... | @@ -1119,10 +1273,6 @@ pub const Type = extern union { |
| 1119 | } | 1273 | } |
| 1120 | @panic("TODO abiSize error union"); | 1274 | @panic("TODO abiSize error union"); |
| 1121 | }, | 1275 | }, |
| 1122 | |||
| 1123 | .@"enum" => @panic("TODO"), | ||
| 1124 | .@"struct" => @panic("TODO"), | ||
| 1125 | .@"union" => @panic("TODO"), | ||
| 1126 | }; | 1276 | }; |
| 1127 | } | 1277 | } |
| 1128 | 1278 | ||
| ... | @@ -1186,15 +1336,12 @@ pub const Type = extern union { | ... | @@ -1186,15 +1336,12 @@ pub const Type = extern union { |
| 1186 | .const_slice, | 1336 | .const_slice, |
| 1187 | .mut_slice, | 1337 | .mut_slice, |
| 1188 | .error_union, | 1338 | .error_union, |
| 1189 | .@"anyframe", | ||
| 1190 | .anyframe_T, | ||
| 1191 | .anyerror_void_error_union, | 1339 | .anyerror_void_error_union, |
| 1192 | .error_set, | 1340 | .error_set, |
| 1193 | .error_set_single, | 1341 | .error_set_single, |
| 1194 | .empty_struct, | ||
| 1195 | .@"enum", | ||
| 1196 | .@"struct", | 1342 | .@"struct", |
| 1197 | .@"union", | 1343 | .empty_struct, |
| 1344 | .empty_struct_literal, | ||
| 1198 | .@"opaque", | 1345 | .@"opaque", |
| 1199 | .var_args_param, | 1346 | .var_args_param, |
| 1200 | => false, | 1347 | => false, |
| ... | @@ -1264,16 +1411,13 @@ pub const Type = extern union { | ... | @@ -1264,16 +1411,13 @@ pub const Type = extern union { |
| 1264 | .optional_single_const_pointer, | 1411 | .optional_single_const_pointer, |
| 1265 | .enum_literal, | 1412 | .enum_literal, |
| 1266 | .error_union, | 1413 | .error_union, |
| 1267 | .@"anyframe", | ||
| 1268 | .anyframe_T, | ||
| 1269 | .anyerror_void_error_union, | 1414 | .anyerror_void_error_union, |
| 1270 | .error_set, | 1415 | .error_set, |
| 1271 | .error_set_single, | 1416 | .error_set_single, |
| 1272 | .empty_struct, | 1417 | .empty_struct, |
| 1273 | .@"enum", | 1418 | .empty_struct_literal, |
| 1274 | .@"struct", | ||
| 1275 | .@"union", | ||
| 1276 | .@"opaque", | 1419 | .@"opaque", |
| 1420 | .@"struct", | ||
| 1277 | .var_args_param, | 1421 | .var_args_param, |
| 1278 | => unreachable, | 1422 | => unreachable, |
| 1279 | 1423 | ||
| ... | @@ -1361,17 +1505,14 @@ pub const Type = extern union { | ... | @@ -1361,17 +1505,14 @@ pub const Type = extern union { |
| 1361 | .optional_single_const_pointer, | 1505 | .optional_single_const_pointer, |
| 1362 | .enum_literal, | 1506 | .enum_literal, |
| 1363 | .error_union, | 1507 | .error_union, |
| 1364 | .@"anyframe", | ||
| 1365 | .anyframe_T, | ||
| 1366 | .anyerror_void_error_union, | 1508 | .anyerror_void_error_union, |
| 1367 | .error_set, | 1509 | .error_set, |
| 1368 | .error_set_single, | 1510 | .error_set_single, |
| 1369 | .empty_struct, | 1511 | .empty_struct, |
| 1512 | .empty_struct_literal, | ||
| 1370 | .inferred_alloc_const, | 1513 | .inferred_alloc_const, |
| 1371 | .inferred_alloc_mut, | 1514 | .inferred_alloc_mut, |
| 1372 | .@"enum", | ||
| 1373 | .@"struct", | 1515 | .@"struct", |
| 1374 | .@"union", | ||
| 1375 | .@"opaque", | 1516 | .@"opaque", |
| 1376 | .var_args_param, | 1517 | .var_args_param, |
| 1377 | => false, | 1518 | => false, |
| ... | @@ -1442,17 +1583,14 @@ pub const Type = extern union { | ... | @@ -1442,17 +1583,14 @@ pub const Type = extern union { |
| 1442 | .enum_literal, | 1583 | .enum_literal, |
| 1443 | .mut_slice, | 1584 | .mut_slice, |
| 1444 | .error_union, | 1585 | .error_union, |
| 1445 | .@"anyframe", | ||
| 1446 | .anyframe_T, | ||
| 1447 | .anyerror_void_error_union, | 1586 | .anyerror_void_error_union, |
| 1448 | .error_set, | 1587 | .error_set, |
| 1449 | .error_set_single, | 1588 | .error_set_single, |
| 1450 | .empty_struct, | 1589 | .empty_struct, |
| 1590 | .empty_struct_literal, | ||
| 1451 | .inferred_alloc_const, | 1591 | .inferred_alloc_const, |
| 1452 | .inferred_alloc_mut, | 1592 | .inferred_alloc_mut, |
| 1453 | .@"enum", | ||
| 1454 | .@"struct", | 1593 | .@"struct", |
| 1455 | .@"union", | ||
| 1456 | .@"opaque", | 1594 | .@"opaque", |
| 1457 | .var_args_param, | 1595 | .var_args_param, |
| 1458 | => false, | 1596 | => false, |
| ... | @@ -1532,17 +1670,14 @@ pub const Type = extern union { | ... | @@ -1532,17 +1670,14 @@ pub const Type = extern union { |
| 1532 | .optional_single_const_pointer, | 1670 | .optional_single_const_pointer, |
| 1533 | .enum_literal, | 1671 | .enum_literal, |
| 1534 | .error_union, | 1672 | .error_union, |
| 1535 | .@"anyframe", | ||
| 1536 | .anyframe_T, | ||
| 1537 | .anyerror_void_error_union, | 1673 | .anyerror_void_error_union, |
| 1538 | .error_set, | 1674 | .error_set, |
| 1539 | .error_set_single, | 1675 | .error_set_single, |
| 1540 | .empty_struct, | 1676 | .empty_struct, |
| 1677 | .empty_struct_literal, | ||
| 1541 | .inferred_alloc_const, | 1678 | .inferred_alloc_const, |
| 1542 | .inferred_alloc_mut, | 1679 | .inferred_alloc_mut, |
| 1543 | .@"enum", | ||
| 1544 | .@"struct", | 1680 | .@"struct", |
| 1545 | .@"union", | ||
| 1546 | .@"opaque", | 1681 | .@"opaque", |
| 1547 | .var_args_param, | 1682 | .var_args_param, |
| 1548 | => false, | 1683 | => false, |
| ... | @@ -1617,17 +1752,14 @@ pub const Type = extern union { | ... | @@ -1617,17 +1752,14 @@ pub const Type = extern union { |
| 1617 | .optional_single_const_pointer, | 1752 | .optional_single_const_pointer, |
| 1618 | .enum_literal, | 1753 | .enum_literal, |
| 1619 | .error_union, | 1754 | .error_union, |
| 1620 | .@"anyframe", | ||
| 1621 | .anyframe_T, | ||
| 1622 | .anyerror_void_error_union, | 1755 | .anyerror_void_error_union, |
| 1623 | .error_set, | 1756 | .error_set, |
| 1624 | .error_set_single, | 1757 | .error_set_single, |
| 1625 | .empty_struct, | 1758 | .empty_struct, |
| 1759 | .empty_struct_literal, | ||
| 1626 | .inferred_alloc_const, | 1760 | .inferred_alloc_const, |
| 1627 | .inferred_alloc_mut, | 1761 | .inferred_alloc_mut, |
| 1628 | .@"enum", | ||
| 1629 | .@"struct", | 1762 | .@"struct", |
| 1630 | .@"union", | ||
| 1631 | .@"opaque", | 1763 | .@"opaque", |
| 1632 | .var_args_param, | 1764 | .var_args_param, |
| 1633 | => false, | 1765 | => false, |
| ... | @@ -1689,7 +1821,11 @@ pub const Type = extern union { | ... | @@ -1689,7 +1821,11 @@ pub const Type = extern union { |
| 1689 | .ErrorUnion => ty = ty.errorUnionChild(), | 1821 | .ErrorUnion => ty = ty.errorUnionChild(), |
| 1690 | 1822 | ||
| 1691 | .Fn => @panic("TODO fn isValidVarType"), | 1823 | .Fn => @panic("TODO fn isValidVarType"), |
| 1692 | .Struct => @panic("TODO struct isValidVarType"), | 1824 | .Struct => { |
| 1825 | // TODO this is not always correct; introduce lazy value mechanism | ||
| 1826 | // and here we need to force a resolve of "type requires comptime". | ||
| 1827 | return true; | ||
| 1828 | }, | ||
| 1693 | .Union => @panic("TODO union isValidVarType"), | 1829 | .Union => @panic("TODO union isValidVarType"), |
| 1694 | }; | 1830 | }; |
| 1695 | } | 1831 | } |
| ... | @@ -1744,17 +1880,14 @@ pub const Type = extern union { | ... | @@ -1744,17 +1880,14 @@ pub const Type = extern union { |
| 1744 | .optional_single_mut_pointer => unreachable, | 1880 | .optional_single_mut_pointer => unreachable, |
| 1745 | .enum_literal => unreachable, | 1881 | .enum_literal => unreachable, |
| 1746 | .error_union => unreachable, | 1882 | .error_union => unreachable, |
| 1747 | .@"anyframe" => unreachable, | ||
| 1748 | .anyframe_T => unreachable, | ||
| 1749 | .anyerror_void_error_union => unreachable, | 1883 | .anyerror_void_error_union => unreachable, |
| 1750 | .error_set => unreachable, | 1884 | .error_set => unreachable, |
| 1751 | .error_set_single => unreachable, | 1885 | .error_set_single => unreachable, |
| 1886 | .@"struct" => unreachable, | ||
| 1752 | .empty_struct => unreachable, | 1887 | .empty_struct => unreachable, |
| 1888 | .empty_struct_literal => unreachable, | ||
| 1753 | .inferred_alloc_const => unreachable, | 1889 | .inferred_alloc_const => unreachable, |
| 1754 | .inferred_alloc_mut => unreachable, | 1890 | .inferred_alloc_mut => unreachable, |
| 1755 | .@"enum" => unreachable, | ||
| 1756 | .@"struct" => unreachable, | ||
| 1757 | .@"union" => unreachable, | ||
| 1758 | .@"opaque" => unreachable, | 1891 | .@"opaque" => unreachable, |
| 1759 | .var_args_param => unreachable, | 1892 | .var_args_param => unreachable, |
| 1760 | 1893 | ||
| ... | @@ -1897,17 +2030,14 @@ pub const Type = extern union { | ... | @@ -1897,17 +2030,14 @@ pub const Type = extern union { |
| 1897 | .optional_single_const_pointer, | 2030 | .optional_single_const_pointer, |
| 1898 | .enum_literal, | 2031 | .enum_literal, |
| 1899 | .error_union, | 2032 | .error_union, |
| 1900 | .@"anyframe", | ||
| 1901 | .anyframe_T, | ||
| 1902 | .anyerror_void_error_union, | 2033 | .anyerror_void_error_union, |
| 1903 | .error_set, | 2034 | .error_set, |
| 1904 | .error_set_single, | 2035 | .error_set_single, |
| 2036 | .@"struct", | ||
| 1905 | .empty_struct, | 2037 | .empty_struct, |
| 2038 | .empty_struct_literal, | ||
| 1906 | .inferred_alloc_const, | 2039 | .inferred_alloc_const, |
| 1907 | .inferred_alloc_mut, | 2040 | .inferred_alloc_mut, |
| 1908 | .@"enum", | ||
| 1909 | .@"struct", | ||
| 1910 | .@"union", | ||
| 1911 | .@"opaque", | 2041 | .@"opaque", |
| 1912 | .var_args_param, | 2042 | .var_args_param, |
| 1913 | => unreachable, | 2043 | => unreachable, |
| ... | @@ -1972,17 +2102,14 @@ pub const Type = extern union { | ... | @@ -1972,17 +2102,14 @@ pub const Type = extern union { |
| 1972 | .optional_single_const_pointer, | 2102 | .optional_single_const_pointer, |
| 1973 | .enum_literal, | 2103 | .enum_literal, |
| 1974 | .error_union, | 2104 | .error_union, |
| 1975 | .@"anyframe", | ||
| 1976 | .anyframe_T, | ||
| 1977 | .anyerror_void_error_union, | 2105 | .anyerror_void_error_union, |
| 1978 | .error_set, | 2106 | .error_set, |
| 1979 | .error_set_single, | 2107 | .error_set_single, |
| 2108 | .@"struct", | ||
| 1980 | .empty_struct, | 2109 | .empty_struct, |
| 2110 | .empty_struct_literal, | ||
| 1981 | .inferred_alloc_const, | 2111 | .inferred_alloc_const, |
| 1982 | .inferred_alloc_mut, | 2112 | .inferred_alloc_mut, |
| 1983 | .@"enum", | ||
| 1984 | .@"struct", | ||
| 1985 | .@"union", | ||
| 1986 | .@"opaque", | 2113 | .@"opaque", |
| 1987 | .var_args_param, | 2114 | .var_args_param, |
| 1988 | => unreachable, | 2115 | => unreachable, |
| ... | @@ -2062,17 +2189,14 @@ pub const Type = extern union { | ... | @@ -2062,17 +2189,14 @@ pub const Type = extern union { |
| 2062 | .optional_single_const_pointer, | 2189 | .optional_single_const_pointer, |
| 2063 | .enum_literal, | 2190 | .enum_literal, |
| 2064 | .error_union, | 2191 | .error_union, |
| 2065 | .@"anyframe", | ||
| 2066 | .anyframe_T, | ||
| 2067 | .anyerror_void_error_union, | 2192 | .anyerror_void_error_union, |
| 2068 | .error_set, | 2193 | .error_set, |
| 2069 | .error_set_single, | 2194 | .error_set_single, |
| 2195 | .@"struct", | ||
| 2070 | .empty_struct, | 2196 | .empty_struct, |
| 2197 | .empty_struct_literal, | ||
| 2071 | .inferred_alloc_const, | 2198 | .inferred_alloc_const, |
| 2072 | .inferred_alloc_mut, | 2199 | .inferred_alloc_mut, |
| 2073 | .@"enum", | ||
| 2074 | .@"struct", | ||
| 2075 | .@"union", | ||
| 2076 | .@"opaque", | 2200 | .@"opaque", |
| 2077 | .var_args_param, | 2201 | .var_args_param, |
| 2078 | => false, | 2202 | => false, |
| ... | @@ -2148,17 +2272,14 @@ pub const Type = extern union { | ... | @@ -2148,17 +2272,14 @@ pub const Type = extern union { |
| 2148 | .optional_single_const_pointer, | 2272 | .optional_single_const_pointer, |
| 2149 | .enum_literal, | 2273 | .enum_literal, |
| 2150 | .error_union, | 2274 | .error_union, |
| 2151 | .@"anyframe", | ||
| 2152 | .anyframe_T, | ||
| 2153 | .anyerror_void_error_union, | 2275 | .anyerror_void_error_union, |
| 2154 | .error_set, | 2276 | .error_set, |
| 2155 | .error_set_single, | 2277 | .error_set_single, |
| 2278 | .@"struct", | ||
| 2156 | .empty_struct, | 2279 | .empty_struct, |
| 2280 | .empty_struct_literal, | ||
| 2157 | .inferred_alloc_const, | 2281 | .inferred_alloc_const, |
| 2158 | .inferred_alloc_mut, | 2282 | .inferred_alloc_mut, |
| 2159 | .@"enum", | ||
| 2160 | .@"struct", | ||
| 2161 | .@"union", | ||
| 2162 | .@"opaque", | 2283 | .@"opaque", |
| 2163 | .var_args_param, | 2284 | .var_args_param, |
| 2164 | => false, | 2285 | => false, |
| ... | @@ -2220,17 +2341,14 @@ pub const Type = extern union { | ... | @@ -2220,17 +2341,14 @@ pub const Type = extern union { |
| 2220 | .optional_single_const_pointer, | 2341 | .optional_single_const_pointer, |
| 2221 | .enum_literal, | 2342 | .enum_literal, |
| 2222 | .error_union, | 2343 | .error_union, |
| 2223 | .@"anyframe", | ||
| 2224 | .anyframe_T, | ||
| 2225 | .anyerror_void_error_union, | 2344 | .anyerror_void_error_union, |
| 2226 | .error_set, | 2345 | .error_set, |
| 2227 | .error_set_single, | 2346 | .error_set_single, |
| 2347 | .@"struct", | ||
| 2228 | .empty_struct, | 2348 | .empty_struct, |
| 2349 | .empty_struct_literal, | ||
| 2229 | .inferred_alloc_const, | 2350 | .inferred_alloc_const, |
| 2230 | .inferred_alloc_mut, | 2351 | .inferred_alloc_mut, |
| 2231 | .@"enum", | ||
| 2232 | .@"struct", | ||
| 2233 | .@"union", | ||
| 2234 | .@"opaque", | 2352 | .@"opaque", |
| 2235 | .var_args_param, | 2353 | .var_args_param, |
| 2236 | => unreachable, | 2354 | => unreachable, |
| ... | @@ -2320,17 +2438,14 @@ pub const Type = extern union { | ... | @@ -2320,17 +2438,14 @@ pub const Type = extern union { |
| 2320 | .optional_single_const_pointer, | 2438 | .optional_single_const_pointer, |
| 2321 | .enum_literal, | 2439 | .enum_literal, |
| 2322 | .error_union, | 2440 | .error_union, |
| 2323 | .@"anyframe", | ||
| 2324 | .anyframe_T, | ||
| 2325 | .anyerror_void_error_union, | 2441 | .anyerror_void_error_union, |
| 2326 | .error_set, | 2442 | .error_set, |
| 2327 | .error_set_single, | 2443 | .error_set_single, |
| 2444 | .@"struct", | ||
| 2328 | .empty_struct, | 2445 | .empty_struct, |
| 2446 | .empty_struct_literal, | ||
| 2329 | .inferred_alloc_const, | 2447 | .inferred_alloc_const, |
| 2330 | .inferred_alloc_mut, | 2448 | .inferred_alloc_mut, |
| 2331 | .@"enum", | ||
| 2332 | .@"struct", | ||
| 2333 | .@"union", | ||
| 2334 | .@"opaque", | 2449 | .@"opaque", |
| 2335 | .var_args_param, | 2450 | .var_args_param, |
| 2336 | => false, | 2451 | => false, |
| ... | @@ -2441,17 +2556,14 @@ pub const Type = extern union { | ... | @@ -2441,17 +2556,14 @@ pub const Type = extern union { |
| 2441 | .optional_single_const_pointer, | 2556 | .optional_single_const_pointer, |
| 2442 | .enum_literal, | 2557 | .enum_literal, |
| 2443 | .error_union, | 2558 | .error_union, |
| 2444 | .@"anyframe", | ||
| 2445 | .anyframe_T, | ||
| 2446 | .anyerror_void_error_union, | 2559 | .anyerror_void_error_union, |
| 2447 | .error_set, | 2560 | .error_set, |
| 2448 | .error_set_single, | 2561 | .error_set_single, |
| 2562 | .@"struct", | ||
| 2449 | .empty_struct, | 2563 | .empty_struct, |
| 2564 | .empty_struct_literal, | ||
| 2450 | .inferred_alloc_const, | 2565 | .inferred_alloc_const, |
| 2451 | .inferred_alloc_mut, | 2566 | .inferred_alloc_mut, |
| 2452 | .@"enum", | ||
| 2453 | .@"struct", | ||
| 2454 | .@"union", | ||
| 2455 | .@"opaque", | 2567 | .@"opaque", |
| 2456 | .var_args_param, | 2568 | .var_args_param, |
| 2457 | => unreachable, | 2569 | => unreachable, |
| ... | @@ -2528,17 +2640,14 @@ pub const Type = extern union { | ... | @@ -2528,17 +2640,14 @@ pub const Type = extern union { |
| 2528 | .optional_single_const_pointer, | 2640 | .optional_single_const_pointer, |
| 2529 | .enum_literal, | 2641 | .enum_literal, |
| 2530 | .error_union, | 2642 | .error_union, |
| 2531 | .@"anyframe", | ||
| 2532 | .anyframe_T, | ||
| 2533 | .anyerror_void_error_union, | 2643 | .anyerror_void_error_union, |
| 2534 | .error_set, | 2644 | .error_set, |
| 2535 | .error_set_single, | 2645 | .error_set_single, |
| 2646 | .@"struct", | ||
| 2536 | .empty_struct, | 2647 | .empty_struct, |
| 2648 | .empty_struct_literal, | ||
| 2537 | .inferred_alloc_const, | 2649 | .inferred_alloc_const, |
| 2538 | .inferred_alloc_mut, | 2650 | .inferred_alloc_mut, |
| 2539 | .@"enum", | ||
| 2540 | .@"struct", | ||
| 2541 | .@"union", | ||
| 2542 | .@"opaque", | 2651 | .@"opaque", |
| 2543 | .var_args_param, | 2652 | .var_args_param, |
| 2544 | => unreachable, | 2653 | => unreachable, |
| ... | @@ -2614,17 +2723,14 @@ pub const Type = extern union { | ... | @@ -2614,17 +2723,14 @@ pub const Type = extern union { |
| 2614 | .optional_single_const_pointer, | 2723 | .optional_single_const_pointer, |
| 2615 | .enum_literal, | 2724 | .enum_literal, |
| 2616 | .error_union, | 2725 | .error_union, |
| 2617 | .@"anyframe", | ||
| 2618 | .anyframe_T, | ||
| 2619 | .anyerror_void_error_union, | 2726 | .anyerror_void_error_union, |
| 2620 | .error_set, | 2727 | .error_set, |
| 2621 | .error_set_single, | 2728 | .error_set_single, |
| 2729 | .@"struct", | ||
| 2622 | .empty_struct, | 2730 | .empty_struct, |
| 2731 | .empty_struct_literal, | ||
| 2623 | .inferred_alloc_const, | 2732 | .inferred_alloc_const, |
| 2624 | .inferred_alloc_mut, | 2733 | .inferred_alloc_mut, |
| 2625 | .@"enum", | ||
| 2626 | .@"struct", | ||
| 2627 | .@"union", | ||
| 2628 | .@"opaque", | 2734 | .@"opaque", |
| 2629 | .var_args_param, | 2735 | .var_args_param, |
| 2630 | => unreachable, | 2736 | => unreachable, |
| ... | @@ -2700,17 +2806,14 @@ pub const Type = extern union { | ... | @@ -2700,17 +2806,14 @@ pub const Type = extern union { |
| 2700 | .optional_single_const_pointer, | 2806 | .optional_single_const_pointer, |
| 2701 | .enum_literal, | 2807 | .enum_literal, |
| 2702 | .error_union, | 2808 | .error_union, |
| 2703 | .@"anyframe", | ||
| 2704 | .anyframe_T, | ||
| 2705 | .anyerror_void_error_union, | 2809 | .anyerror_void_error_union, |
| 2706 | .error_set, | 2810 | .error_set, |
| 2707 | .error_set_single, | 2811 | .error_set_single, |
| 2812 | .@"struct", | ||
| 2708 | .empty_struct, | 2813 | .empty_struct, |
| 2814 | .empty_struct_literal, | ||
| 2709 | .inferred_alloc_const, | 2815 | .inferred_alloc_const, |
| 2710 | .inferred_alloc_mut, | 2816 | .inferred_alloc_mut, |
| 2711 | .@"enum", | ||
| 2712 | .@"struct", | ||
| 2713 | .@"union", | ||
| 2714 | .@"opaque", | 2817 | .@"opaque", |
| 2715 | .var_args_param, | 2818 | .var_args_param, |
| 2716 | => unreachable, | 2819 | => unreachable, |
| ... | @@ -2783,17 +2886,14 @@ pub const Type = extern union { | ... | @@ -2783,17 +2886,14 @@ pub const Type = extern union { |
| 2783 | .optional_single_const_pointer, | 2886 | .optional_single_const_pointer, |
| 2784 | .enum_literal, | 2887 | .enum_literal, |
| 2785 | .error_union, | 2888 | .error_union, |
| 2786 | .@"anyframe", | ||
| 2787 | .anyframe_T, | ||
| 2788 | .anyerror_void_error_union, | 2889 | .anyerror_void_error_union, |
| 2789 | .error_set, | 2890 | .error_set, |
| 2790 | .error_set_single, | 2891 | .error_set_single, |
| 2892 | .@"struct", | ||
| 2791 | .empty_struct, | 2893 | .empty_struct, |
| 2894 | .empty_struct_literal, | ||
| 2792 | .inferred_alloc_const, | 2895 | .inferred_alloc_const, |
| 2793 | .inferred_alloc_mut, | 2896 | .inferred_alloc_mut, |
| 2794 | .@"enum", | ||
| 2795 | .@"struct", | ||
| 2796 | .@"union", | ||
| 2797 | .@"opaque", | 2897 | .@"opaque", |
| 2798 | .var_args_param, | 2898 | .var_args_param, |
| 2799 | => unreachable, | 2899 | => unreachable, |
| ... | @@ -2866,17 +2966,14 @@ pub const Type = extern union { | ... | @@ -2866,17 +2966,14 @@ pub const Type = extern union { |
| 2866 | .optional_single_const_pointer, | 2966 | .optional_single_const_pointer, |
| 2867 | .enum_literal, | 2967 | .enum_literal, |
| 2868 | .error_union, | 2968 | .error_union, |
| 2869 | .@"anyframe", | ||
| 2870 | .anyframe_T, | ||
| 2871 | .anyerror_void_error_union, | 2969 | .anyerror_void_error_union, |
| 2872 | .error_set, | 2970 | .error_set, |
| 2873 | .error_set_single, | 2971 | .error_set_single, |
| 2972 | .@"struct", | ||
| 2874 | .empty_struct, | 2973 | .empty_struct, |
| 2974 | .empty_struct_literal, | ||
| 2875 | .inferred_alloc_const, | 2975 | .inferred_alloc_const, |
| 2876 | .inferred_alloc_mut, | 2976 | .inferred_alloc_mut, |
| 2877 | .@"enum", | ||
| 2878 | .@"struct", | ||
| 2879 | .@"union", | ||
| 2880 | .@"opaque", | 2977 | .@"opaque", |
| 2881 | .var_args_param, | 2978 | .var_args_param, |
| 2882 | => unreachable, | 2979 | => unreachable, |
| ... | @@ -2949,17 +3046,14 @@ pub const Type = extern union { | ... | @@ -2949,17 +3046,14 @@ pub const Type = extern union { |
| 2949 | .optional_single_const_pointer, | 3046 | .optional_single_const_pointer, |
| 2950 | .enum_literal, | 3047 | .enum_literal, |
| 2951 | .error_union, | 3048 | .error_union, |
| 2952 | .@"anyframe", | ||
| 2953 | .anyframe_T, | ||
| 2954 | .anyerror_void_error_union, | 3049 | .anyerror_void_error_union, |
| 2955 | .error_set, | 3050 | .error_set, |
| 2956 | .error_set_single, | 3051 | .error_set_single, |
| 3052 | .@"struct", | ||
| 2957 | .empty_struct, | 3053 | .empty_struct, |
| 3054 | .empty_struct_literal, | ||
| 2958 | .inferred_alloc_const, | 3055 | .inferred_alloc_const, |
| 2959 | .inferred_alloc_mut, | 3056 | .inferred_alloc_mut, |
| 2960 | .@"enum", | ||
| 2961 | .@"struct", | ||
| 2962 | .@"union", | ||
| 2963 | .@"opaque", | 3057 | .@"opaque", |
| 2964 | .var_args_param, | 3058 | .var_args_param, |
| 2965 | => false, | 3059 | => false, |
| ... | @@ -3016,8 +3110,6 @@ pub const Type = extern union { | ... | @@ -3016,8 +3110,6 @@ pub const Type = extern union { |
| 3016 | .optional_single_const_pointer, | 3110 | .optional_single_const_pointer, |
| 3017 | .enum_literal, | 3111 | .enum_literal, |
| 3018 | .anyerror_void_error_union, | 3112 | .anyerror_void_error_union, |
| 3019 | .anyframe_T, | ||
| 3020 | .@"anyframe", | ||
| 3021 | .error_union, | 3113 | .error_union, |
| 3022 | .error_set, | 3114 | .error_set, |
| 3023 | .error_set_single, | 3115 | .error_set_single, |
| ... | @@ -3025,11 +3117,12 @@ pub const Type = extern union { | ... | @@ -3025,11 +3117,12 @@ pub const Type = extern union { |
| 3025 | .var_args_param, | 3117 | .var_args_param, |
| 3026 | => return null, | 3118 | => return null, |
| 3027 | 3119 | ||
| 3028 | .@"enum" => @panic("TODO onePossibleValue enum"), | 3120 | .@"struct" => { |
| 3029 | .@"struct" => @panic("TODO onePossibleValue struct"), | 3121 | log.warn("TODO implement Type.onePossibleValue for structs", .{}); |
| 3030 | .@"union" => @panic("TODO onePossibleValue union"), | 3122 | return null; |
| 3123 | }, | ||
| 3031 | 3124 | ||
| 3032 | .empty_struct => return Value.initTag(.empty_struct_value), | 3125 | .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value), |
| 3033 | .void => return Value.initTag(.void_value), | 3126 | .void => return Value.initTag(.void_value), |
| 3034 | .noreturn => return Value.initTag(.unreachable_value), | 3127 | .noreturn => return Value.initTag(.unreachable_value), |
| 3035 | .@"null" => return Value.initTag(.null_value), | 3128 | .@"null" => return Value.initTag(.null_value), |
| ... | @@ -3128,17 +3221,14 @@ pub const Type = extern union { | ... | @@ -3128,17 +3221,14 @@ pub const Type = extern union { |
| 3128 | .optional_single_const_pointer, | 3221 | .optional_single_const_pointer, |
| 3129 | .enum_literal, | 3222 | .enum_literal, |
| 3130 | .error_union, | 3223 | .error_union, |
| 3131 | .@"anyframe", | ||
| 3132 | .anyframe_T, | ||
| 3133 | .anyerror_void_error_union, | 3224 | .anyerror_void_error_union, |
| 3134 | .error_set, | 3225 | .error_set, |
| 3135 | .error_set_single, | 3226 | .error_set_single, |
| 3227 | .@"struct", | ||
| 3136 | .empty_struct, | 3228 | .empty_struct, |
| 3229 | .empty_struct_literal, | ||
| 3137 | .inferred_alloc_const, | 3230 | .inferred_alloc_const, |
| 3138 | .inferred_alloc_mut, | 3231 | .inferred_alloc_mut, |
| 3139 | .@"enum", | ||
| 3140 | .@"struct", | ||
| 3141 | .@"union", | ||
| 3142 | .@"opaque", | 3232 | .@"opaque", |
| 3143 | .var_args_param, | 3233 | .var_args_param, |
| 3144 | => return false, | 3234 | => return false, |
| ... | @@ -3220,8 +3310,6 @@ pub const Type = extern union { | ... | @@ -3220,8 +3310,6 @@ pub const Type = extern union { |
| 3220 | .optional_single_const_pointer, | 3310 | .optional_single_const_pointer, |
| 3221 | .enum_literal, | 3311 | .enum_literal, |
| 3222 | .error_union, | 3312 | .error_union, |
| 3223 | .@"anyframe", | ||
| 3224 | .anyframe_T, | ||
| 3225 | .anyerror_void_error_union, | 3313 | .anyerror_void_error_union, |
| 3226 | .error_set, | 3314 | .error_set, |
| 3227 | .error_set_single, | 3315 | .error_set_single, |
| ... | @@ -3231,13 +3319,12 @@ pub const Type = extern union { | ... | @@ -3231,13 +3319,12 @@ pub const Type = extern union { |
| 3231 | .inferred_alloc_const, | 3319 | .inferred_alloc_const, |
| 3232 | .inferred_alloc_mut, | 3320 | .inferred_alloc_mut, |
| 3233 | .var_args_param, | 3321 | .var_args_param, |
| 3322 | .empty_struct_literal, | ||
| 3234 | => unreachable, | 3323 | => unreachable, |
| 3235 | 3324 | ||
| 3325 | .@"struct" => &self.castTag(.@"struct").?.data.container, | ||
| 3236 | .empty_struct => self.castTag(.empty_struct).?.data, | 3326 | .empty_struct => self.castTag(.empty_struct).?.data, |
| 3237 | .@"enum" => &self.castTag(.@"enum").?.scope, | 3327 | .@"opaque" => &self.castTag(.@"opaque").?.data, |
| 3238 | .@"struct" => &self.castTag(.@"struct").?.scope, | ||
| 3239 | .@"union" => &self.castTag(.@"union").?.scope, | ||
| 3240 | .@"opaque" => &self.castTag(.@"opaque").?.scope, | ||
| 3241 | }; | 3328 | }; |
| 3242 | } | 3329 | } |
| 3243 | 3330 | ||
| ... | @@ -3296,6 +3383,10 @@ pub const Type = extern union { | ... | @@ -3296,6 +3383,10 @@ pub const Type = extern union { |
| 3296 | } | 3383 | } |
| 3297 | } | 3384 | } |
| 3298 | 3385 | ||
| 3386 | pub fn isExhaustiveEnum(ty: Type) bool { | ||
| 3387 | return false; // TODO | ||
| 3388 | } | ||
| 3389 | |||
| 3299 | /// This enum does not directly correspond to `std.builtin.TypeId` because | 3390 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| 3300 | /// it has extra enum tags in it, as a way of using less memory. For example, | 3391 | /// it has extra enum tags in it, as a way of using less memory. For example, |
| 3301 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types | 3392 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types |
| ... | @@ -3346,11 +3437,12 @@ pub const Type = extern union { | ... | @@ -3346,11 +3437,12 @@ pub const Type = extern union { |
| 3346 | fn_ccc_void_no_args, | 3437 | fn_ccc_void_no_args, |
| 3347 | single_const_pointer_to_comptime_int, | 3438 | single_const_pointer_to_comptime_int, |
| 3348 | anyerror_void_error_union, | 3439 | anyerror_void_error_union, |
| 3349 | @"anyframe", | ||
| 3350 | const_slice_u8, | 3440 | const_slice_u8, |
| 3351 | /// This is a special type for variadic parameters of a function call. | 3441 | /// This is a special type for variadic parameters of a function call. |
| 3352 | /// Casts to it will validate that the type can be passed to a c calling convetion function. | 3442 | /// Casts to it will validate that the type can be passed to a c calling convetion function. |
| 3353 | var_args_param, | 3443 | var_args_param, |
| 3444 | /// Same as `empty_struct` except it has an empty namespace. | ||
| 3445 | empty_struct_literal, | ||
| 3354 | /// This is a special value that tracks a set of types that have been stored | 3446 | /// This is a special value that tracks a set of types that have been stored |
| 3355 | /// to an inferred allocation. It does not support most of the normal type queries. | 3447 | /// to an inferred allocation. It does not support most of the normal type queries. |
| 3356 | /// However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc. | 3448 | /// However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc. |
| ... | @@ -3379,14 +3471,11 @@ pub const Type = extern union { | ... | @@ -3379,14 +3471,11 @@ pub const Type = extern union { |
| 3379 | optional_single_mut_pointer, | 3471 | optional_single_mut_pointer, |
| 3380 | optional_single_const_pointer, | 3472 | optional_single_const_pointer, |
| 3381 | error_union, | 3473 | error_union, |
| 3382 | anyframe_T, | ||
| 3383 | error_set, | 3474 | error_set, |
| 3384 | error_set_single, | 3475 | error_set_single, |
| 3385 | empty_struct, | 3476 | empty_struct, |
| 3386 | @"enum", | ||
| 3387 | @"struct", | ||
| 3388 | @"union", | ||
| 3389 | @"opaque", | 3477 | @"opaque", |
| 3478 | @"struct", | ||
| 3390 | 3479 | ||
| 3391 | pub const last_no_payload_tag = Tag.inferred_alloc_const; | 3480 | pub const last_no_payload_tag = Tag.inferred_alloc_const; |
| 3392 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | 3481 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | @@ -3435,11 +3524,11 @@ pub const Type = extern union { | ... | @@ -3435,11 +3524,11 @@ pub const Type = extern union { |
| 3435 | .fn_ccc_void_no_args, | 3524 | .fn_ccc_void_no_args, |
| 3436 | .single_const_pointer_to_comptime_int, | 3525 | .single_const_pointer_to_comptime_int, |
| 3437 | .anyerror_void_error_union, | 3526 | .anyerror_void_error_union, |
| 3438 | .@"anyframe", | ||
| 3439 | .const_slice_u8, | 3527 | .const_slice_u8, |
| 3440 | .inferred_alloc_const, | 3528 | .inferred_alloc_const, |
| 3441 | .inferred_alloc_mut, | 3529 | .inferred_alloc_mut, |
| 3442 | .var_args_param, | 3530 | .var_args_param, |
| 3531 | .empty_struct_literal, | ||
| 3443 | => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), | 3532 | => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), |
| 3444 | 3533 | ||
| 3445 | .array_u8, | 3534 | .array_u8, |
| ... | @@ -3457,25 +3546,23 @@ pub const Type = extern union { | ... | @@ -3457,25 +3546,23 @@ pub const Type = extern union { |
| 3457 | .optional, | 3546 | .optional, |
| 3458 | .optional_single_mut_pointer, | 3547 | .optional_single_mut_pointer, |
| 3459 | .optional_single_const_pointer, | 3548 | .optional_single_const_pointer, |
| 3460 | .anyframe_T, | ||
| 3461 | => Payload.ElemType, | 3549 | => Payload.ElemType, |
| 3462 | 3550 | ||
| 3463 | .int_signed, | 3551 | .int_signed, |
| 3464 | .int_unsigned, | 3552 | .int_unsigned, |
| 3465 | => Payload.Bits, | 3553 | => Payload.Bits, |
| 3466 | 3554 | ||
| 3555 | .error_set => Payload.ErrorSet, | ||
| 3556 | |||
| 3467 | .array => Payload.Array, | 3557 | .array => Payload.Array, |
| 3468 | .array_sentinel => Payload.ArraySentinel, | 3558 | .array_sentinel => Payload.ArraySentinel, |
| 3469 | .pointer => Payload.Pointer, | 3559 | .pointer => Payload.Pointer, |
| 3470 | .function => Payload.Function, | 3560 | .function => Payload.Function, |
| 3471 | .error_union => Payload.ErrorUnion, | 3561 | .error_union => Payload.ErrorUnion, |
| 3472 | .error_set => Payload.Decl, | ||
| 3473 | .error_set_single => Payload.Name, | 3562 | .error_set_single => Payload.Name, |
| 3474 | .empty_struct => Payload.ContainerScope, | ||
| 3475 | .@"enum" => Payload.Enum, | ||
| 3476 | .@"struct" => Payload.Struct, | ||
| 3477 | .@"union" => Payload.Union, | ||
| 3478 | .@"opaque" => Payload.Opaque, | 3563 | .@"opaque" => Payload.Opaque, |
| 3564 | .@"struct" => Payload.Struct, | ||
| 3565 | .empty_struct => Payload.ContainerScope, | ||
| 3479 | }; | 3566 | }; |
| 3480 | } | 3567 | } |
| 3481 | 3568 | ||
| ... | @@ -3550,6 +3637,13 @@ pub const Type = extern union { | ... | @@ -3550,6 +3637,13 @@ pub const Type = extern union { |
| 3550 | }, | 3637 | }, |
| 3551 | }; | 3638 | }; |
| 3552 | 3639 | ||
| 3640 | pub const ErrorSet = struct { | ||
| 3641 | pub const base_tag = Tag.error_set; | ||
| 3642 | |||
| 3643 | base: Payload = Payload{ .tag = base_tag }, | ||
| 3644 | data: *Module.ErrorSet, | ||
| 3645 | }; | ||
| 3646 | |||
| 3553 | pub const Pointer = struct { | 3647 | pub const Pointer = struct { |
| 3554 | pub const base_tag = Tag.pointer; | 3648 | pub const base_tag = Tag.pointer; |
| 3555 | 3649 | ||
| ... | @@ -3598,13 +3692,13 @@ pub const Type = extern union { | ... | @@ -3598,13 +3692,13 @@ pub const Type = extern union { |
| 3598 | 3692 | ||
| 3599 | pub const Opaque = struct { | 3693 | pub const Opaque = struct { |
| 3600 | base: Payload = .{ .tag = .@"opaque" }, | 3694 | base: Payload = .{ .tag = .@"opaque" }, |
| 3601 | 3695 | data: Module.Scope.Container, | |
| 3602 | scope: Module.Scope.Container, | ||
| 3603 | }; | 3696 | }; |
| 3604 | 3697 | ||
| 3605 | pub const Enum = @import("type/Enum.zig"); | 3698 | pub const Struct = struct { |
| 3606 | pub const Struct = @import("type/Struct.zig"); | 3699 | base: Payload = .{ .tag = .@"struct" }, |
| 3607 | pub const Union = @import("type/Union.zig"); | 3700 | data: *Module.Struct, |
| 3701 | }; | ||
| 3608 | }; | 3702 | }; |
| 3609 | }; | 3703 | }; |
| 3610 | 3704 |
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+60-69| ... | @@ -30,6 +30,8 @@ pub const Value = extern union { | ... | @@ -30,6 +30,8 @@ pub const Value = extern union { |
| 30 | i32_type, | 30 | i32_type, |
| 31 | u64_type, | 31 | u64_type, |
| 32 | i64_type, | 32 | i64_type, |
| 33 | u128_type, | ||
| 34 | i128_type, | ||
| 33 | usize_type, | 35 | usize_type, |
| 34 | isize_type, | 36 | isize_type, |
| 35 | c_short_type, | 37 | c_short_type, |
| ... | @@ -62,18 +64,19 @@ pub const Value = extern union { | ... | @@ -62,18 +64,19 @@ pub const Value = extern union { |
| 62 | single_const_pointer_to_comptime_int_type, | 64 | single_const_pointer_to_comptime_int_type, |
| 63 | const_slice_u8_type, | 65 | const_slice_u8_type, |
| 64 | enum_literal_type, | 66 | enum_literal_type, |
| 65 | anyframe_type, | ||
| 66 | 67 | ||
| 67 | undef, | 68 | undef, |
| 68 | zero, | 69 | zero, |
| 69 | one, | 70 | one, |
| 70 | void_value, | 71 | void_value, |
| 71 | unreachable_value, | 72 | unreachable_value, |
| 72 | empty_struct_value, | ||
| 73 | empty_array, | ||
| 74 | null_value, | 73 | null_value, |
| 75 | bool_true, | 74 | bool_true, |
| 76 | bool_false, // See last_no_payload_tag below. | 75 | bool_false, |
| 76 | |||
| 77 | abi_align_default, | ||
| 78 | empty_struct_value, | ||
| 79 | empty_array, // See last_no_payload_tag below. | ||
| 77 | // After this, the tag requires a payload. | 80 | // After this, the tag requires a payload. |
| 78 | 81 | ||
| 79 | ty, | 82 | ty, |
| ... | @@ -100,14 +103,13 @@ pub const Value = extern union { | ... | @@ -100,14 +103,13 @@ pub const Value = extern union { |
| 100 | float_64, | 103 | float_64, |
| 101 | float_128, | 104 | float_128, |
| 102 | enum_literal, | 105 | enum_literal, |
| 103 | error_set, | ||
| 104 | @"error", | 106 | @"error", |
| 105 | error_union, | 107 | error_union, |
| 106 | /// This is a special value that tracks a set of types that have been stored | 108 | /// This is a special value that tracks a set of types that have been stored |
| 107 | /// to an inferred allocation. It does not support any of the normal value queries. | 109 | /// to an inferred allocation. It does not support any of the normal value queries. |
| 108 | inferred_alloc, | 110 | inferred_alloc, |
| 109 | 111 | ||
| 110 | pub const last_no_payload_tag = Tag.bool_false; | 112 | pub const last_no_payload_tag = Tag.empty_array; |
| 111 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | 113 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| 112 | 114 | ||
| 113 | pub fn Type(comptime t: Tag) type { | 115 | pub fn Type(comptime t: Tag) type { |
| ... | @@ -120,6 +122,8 @@ pub const Value = extern union { | ... | @@ -120,6 +122,8 @@ pub const Value = extern union { |
| 120 | .i32_type, | 122 | .i32_type, |
| 121 | .u64_type, | 123 | .u64_type, |
| 122 | .i64_type, | 124 | .i64_type, |
| 125 | .u128_type, | ||
| 126 | .i128_type, | ||
| 123 | .usize_type, | 127 | .usize_type, |
| 124 | .isize_type, | 128 | .isize_type, |
| 125 | .c_short_type, | 129 | .c_short_type, |
| ... | @@ -152,7 +156,6 @@ pub const Value = extern union { | ... | @@ -152,7 +156,6 @@ pub const Value = extern union { |
| 152 | .single_const_pointer_to_comptime_int_type, | 156 | .single_const_pointer_to_comptime_int_type, |
| 153 | .const_slice_u8_type, | 157 | .const_slice_u8_type, |
| 154 | .enum_literal_type, | 158 | .enum_literal_type, |
| 155 | .anyframe_type, | ||
| 156 | .undef, | 159 | .undef, |
| 157 | .zero, | 160 | .zero, |
| 158 | .one, | 161 | .one, |
| ... | @@ -163,6 +166,7 @@ pub const Value = extern union { | ... | @@ -163,6 +166,7 @@ pub const Value = extern union { |
| 163 | .null_value, | 166 | .null_value, |
| 164 | .bool_true, | 167 | .bool_true, |
| 165 | .bool_false, | 168 | .bool_false, |
| 169 | .abi_align_default, | ||
| 166 | => @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"), | 170 | => @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"), |
| 167 | 171 | ||
| 168 | .int_big_positive, | 172 | .int_big_positive, |
| ... | @@ -193,7 +197,6 @@ pub const Value = extern union { | ... | @@ -193,7 +197,6 @@ pub const Value = extern union { |
| 193 | .float_32 => Payload.Float_32, | 197 | .float_32 => Payload.Float_32, |
| 194 | .float_64 => Payload.Float_64, | 198 | .float_64 => Payload.Float_64, |
| 195 | .float_128 => Payload.Float_128, | 199 | .float_128 => Payload.Float_128, |
| 196 | .error_set => Payload.ErrorSet, | ||
| 197 | .@"error" => Payload.Error, | 200 | .@"error" => Payload.Error, |
| 198 | .inferred_alloc => Payload.InferredAlloc, | 201 | .inferred_alloc => Payload.InferredAlloc, |
| 199 | }; | 202 | }; |
| ... | @@ -275,6 +278,8 @@ pub const Value = extern union { | ... | @@ -275,6 +278,8 @@ pub const Value = extern union { |
| 275 | .i32_type, | 278 | .i32_type, |
| 276 | .u64_type, | 279 | .u64_type, |
| 277 | .i64_type, | 280 | .i64_type, |
| 281 | .u128_type, | ||
| 282 | .i128_type, | ||
| 278 | .usize_type, | 283 | .usize_type, |
| 279 | .isize_type, | 284 | .isize_type, |
| 280 | .c_short_type, | 285 | .c_short_type, |
| ... | @@ -307,7 +312,6 @@ pub const Value = extern union { | ... | @@ -307,7 +312,6 @@ pub const Value = extern union { |
| 307 | .single_const_pointer_to_comptime_int_type, | 312 | .single_const_pointer_to_comptime_int_type, |
| 308 | .const_slice_u8_type, | 313 | .const_slice_u8_type, |
| 309 | .enum_literal_type, | 314 | .enum_literal_type, |
| 310 | .anyframe_type, | ||
| 311 | .undef, | 315 | .undef, |
| 312 | .zero, | 316 | .zero, |
| 313 | .one, | 317 | .one, |
| ... | @@ -318,6 +322,7 @@ pub const Value = extern union { | ... | @@ -318,6 +322,7 @@ pub const Value = extern union { |
| 318 | .bool_true, | 322 | .bool_true, |
| 319 | .bool_false, | 323 | .bool_false, |
| 320 | .empty_struct_value, | 324 | .empty_struct_value, |
| 325 | .abi_align_default, | ||
| 321 | => unreachable, | 326 | => unreachable, |
| 322 | 327 | ||
| 323 | .ty => { | 328 | .ty => { |
| ... | @@ -400,7 +405,6 @@ pub const Value = extern union { | ... | @@ -400,7 +405,6 @@ pub const Value = extern union { |
| 400 | return Value{ .ptr_otherwise = &new_payload.base }; | 405 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 401 | }, | 406 | }, |
| 402 | 407 | ||
| 403 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), | ||
| 404 | .inferred_alloc => unreachable, | 408 | .inferred_alloc => unreachable, |
| 405 | } | 409 | } |
| 406 | } | 410 | } |
| ... | @@ -429,6 +433,8 @@ pub const Value = extern union { | ... | @@ -429,6 +433,8 @@ pub const Value = extern union { |
| 429 | .i32_type => return out_stream.writeAll("i32"), | 433 | .i32_type => return out_stream.writeAll("i32"), |
| 430 | .u64_type => return out_stream.writeAll("u64"), | 434 | .u64_type => return out_stream.writeAll("u64"), |
| 431 | .i64_type => return out_stream.writeAll("i64"), | 435 | .i64_type => return out_stream.writeAll("i64"), |
| 436 | .u128_type => return out_stream.writeAll("u128"), | ||
| 437 | .i128_type => return out_stream.writeAll("i128"), | ||
| 432 | .isize_type => return out_stream.writeAll("isize"), | 438 | .isize_type => return out_stream.writeAll("isize"), |
| 433 | .usize_type => return out_stream.writeAll("usize"), | 439 | .usize_type => return out_stream.writeAll("usize"), |
| 434 | .c_short_type => return out_stream.writeAll("c_short"), | 440 | .c_short_type => return out_stream.writeAll("c_short"), |
| ... | @@ -461,9 +467,8 @@ pub const Value = extern union { | ... | @@ -461,9 +467,8 @@ pub const Value = extern union { |
| 461 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), | 467 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), |
| 462 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), | 468 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), |
| 463 | .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"), | 469 | .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"), |
| 464 | .anyframe_type => return out_stream.writeAll("anyframe"), | 470 | .abi_align_default => return out_stream.writeAll("(default ABI alignment)"), |
| 465 | 471 | ||
| 466 | // TODO this should print `NAME{}` | ||
| 467 | .empty_struct_value => return out_stream.writeAll("struct {}{}"), | 472 | .empty_struct_value => return out_stream.writeAll("struct {}{}"), |
| 468 | .null_value => return out_stream.writeAll("null"), | 473 | .null_value => return out_stream.writeAll("null"), |
| 469 | .undef => return out_stream.writeAll("undefined"), | 474 | .undef => return out_stream.writeAll("undefined"), |
| ... | @@ -510,15 +515,6 @@ pub const Value = extern union { | ... | @@ -510,15 +515,6 @@ pub const Value = extern union { |
| 510 | .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), | 515 | .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), |
| 511 | .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}), | 516 | .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}), |
| 512 | .float_128 => return out_stream.print("{}", .{val.castTag(.float_128).?.data}), | 517 | .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 | .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}), | 518 | .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}), |
| 523 | // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that | 519 | // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that |
| 524 | .error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}), | 520 | .error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}), |
| ... | @@ -557,6 +553,8 @@ pub const Value = extern union { | ... | @@ -557,6 +553,8 @@ pub const Value = extern union { |
| 557 | .i32_type => Type.initTag(.i32), | 553 | .i32_type => Type.initTag(.i32), |
| 558 | .u64_type => Type.initTag(.u64), | 554 | .u64_type => Type.initTag(.u64), |
| 559 | .i64_type => Type.initTag(.i64), | 555 | .i64_type => Type.initTag(.i64), |
| 556 | .u128_type => Type.initTag(.u128), | ||
| 557 | .i128_type => Type.initTag(.i128), | ||
| 560 | .usize_type => Type.initTag(.usize), | 558 | .usize_type => Type.initTag(.usize), |
| 561 | .isize_type => Type.initTag(.isize), | 559 | .isize_type => Type.initTag(.isize), |
| 562 | .c_short_type => Type.initTag(.c_short), | 560 | .c_short_type => Type.initTag(.c_short), |
| ... | @@ -589,7 +587,6 @@ pub const Value = extern union { | ... | @@ -589,7 +587,6 @@ pub const Value = extern union { |
| 589 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), | 587 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), |
| 590 | .const_slice_u8_type => Type.initTag(.const_slice_u8), | 588 | .const_slice_u8_type => Type.initTag(.const_slice_u8), |
| 591 | .enum_literal_type => Type.initTag(.enum_literal), | 589 | .enum_literal_type => Type.initTag(.enum_literal), |
| 592 | .anyframe_type => Type.initTag(.@"anyframe"), | ||
| 593 | 590 | ||
| 594 | .int_type => { | 591 | .int_type => { |
| 595 | const payload = self.castTag(.int_type).?.data; | 592 | const payload = self.castTag(.int_type).?.data; |
| ... | @@ -602,10 +599,6 @@ pub const Value = extern union { | ... | @@ -602,10 +599,6 @@ pub const Value = extern union { |
| 602 | }; | 599 | }; |
| 603 | return Type.initPayload(&new.base); | 600 | return Type.initPayload(&new.base); |
| 604 | }, | 601 | }, |
| 605 | .error_set => { | ||
| 606 | const payload = self.castTag(.error_set).?.data; | ||
| 607 | return Type.Tag.error_set.create(allocator, payload.decl); | ||
| 608 | }, | ||
| 609 | 602 | ||
| 610 | .undef, | 603 | .undef, |
| 611 | .zero, | 604 | .zero, |
| ... | @@ -637,6 +630,7 @@ pub const Value = extern union { | ... | @@ -637,6 +630,7 @@ pub const Value = extern union { |
| 637 | .error_union, | 630 | .error_union, |
| 638 | .empty_struct_value, | 631 | .empty_struct_value, |
| 639 | .inferred_alloc, | 632 | .inferred_alloc, |
| 633 | .abi_align_default, | ||
| 640 | => unreachable, | 634 | => unreachable, |
| 641 | }; | 635 | }; |
| 642 | } | 636 | } |
| ... | @@ -654,6 +648,8 @@ pub const Value = extern union { | ... | @@ -654,6 +648,8 @@ pub const Value = extern union { |
| 654 | .i32_type, | 648 | .i32_type, |
| 655 | .u64_type, | 649 | .u64_type, |
| 656 | .i64_type, | 650 | .i64_type, |
| 651 | .u128_type, | ||
| 652 | .i128_type, | ||
| 657 | .usize_type, | 653 | .usize_type, |
| 658 | .isize_type, | 654 | .isize_type, |
| 659 | .c_short_type, | 655 | .c_short_type, |
| ... | @@ -686,7 +682,6 @@ pub const Value = extern union { | ... | @@ -686,7 +682,6 @@ pub const Value = extern union { |
| 686 | .single_const_pointer_to_comptime_int_type, | 682 | .single_const_pointer_to_comptime_int_type, |
| 687 | .const_slice_u8_type, | 683 | .const_slice_u8_type, |
| 688 | .enum_literal_type, | 684 | .enum_literal_type, |
| 689 | .anyframe_type, | ||
| 690 | .null_value, | 685 | .null_value, |
| 691 | .function, | 686 | .function, |
| 692 | .extern_fn, | 687 | .extern_fn, |
| ... | @@ -704,11 +699,11 @@ pub const Value = extern union { | ... | @@ -704,11 +699,11 @@ pub const Value = extern union { |
| 704 | .unreachable_value, | 699 | .unreachable_value, |
| 705 | .empty_array, | 700 | .empty_array, |
| 706 | .enum_literal, | 701 | .enum_literal, |
| 707 | .error_set, | ||
| 708 | .error_union, | 702 | .error_union, |
| 709 | .@"error", | 703 | .@"error", |
| 710 | .empty_struct_value, | 704 | .empty_struct_value, |
| 711 | .inferred_alloc, | 705 | .inferred_alloc, |
| 706 | .abi_align_default, | ||
| 712 | => unreachable, | 707 | => unreachable, |
| 713 | 708 | ||
| 714 | .undef => unreachable, | 709 | .undef => unreachable, |
| ... | @@ -741,6 +736,8 @@ pub const Value = extern union { | ... | @@ -741,6 +736,8 @@ pub const Value = extern union { |
| 741 | .i32_type, | 736 | .i32_type, |
| 742 | .u64_type, | 737 | .u64_type, |
| 743 | .i64_type, | 738 | .i64_type, |
| 739 | .u128_type, | ||
| 740 | .i128_type, | ||
| 744 | .usize_type, | 741 | .usize_type, |
| 745 | .isize_type, | 742 | .isize_type, |
| 746 | .c_short_type, | 743 | .c_short_type, |
| ... | @@ -773,7 +770,6 @@ pub const Value = extern union { | ... | @@ -773,7 +770,6 @@ pub const Value = extern union { |
| 773 | .single_const_pointer_to_comptime_int_type, | 770 | .single_const_pointer_to_comptime_int_type, |
| 774 | .const_slice_u8_type, | 771 | .const_slice_u8_type, |
| 775 | .enum_literal_type, | 772 | .enum_literal_type, |
| 776 | .anyframe_type, | ||
| 777 | .null_value, | 773 | .null_value, |
| 778 | .function, | 774 | .function, |
| 779 | .extern_fn, | 775 | .extern_fn, |
| ... | @@ -791,11 +787,11 @@ pub const Value = extern union { | ... | @@ -791,11 +787,11 @@ pub const Value = extern union { |
| 791 | .unreachable_value, | 787 | .unreachable_value, |
| 792 | .empty_array, | 788 | .empty_array, |
| 793 | .enum_literal, | 789 | .enum_literal, |
| 794 | .error_set, | ||
| 795 | .@"error", | 790 | .@"error", |
| 796 | .error_union, | 791 | .error_union, |
| 797 | .empty_struct_value, | 792 | .empty_struct_value, |
| 798 | .inferred_alloc, | 793 | .inferred_alloc, |
| 794 | .abi_align_default, | ||
| 799 | => unreachable, | 795 | => unreachable, |
| 800 | 796 | ||
| 801 | .undef => unreachable, | 797 | .undef => unreachable, |
| ... | @@ -828,6 +824,8 @@ pub const Value = extern union { | ... | @@ -828,6 +824,8 @@ pub const Value = extern union { |
| 828 | .i32_type, | 824 | .i32_type, |
| 829 | .u64_type, | 825 | .u64_type, |
| 830 | .i64_type, | 826 | .i64_type, |
| 827 | .u128_type, | ||
| 828 | .i128_type, | ||
| 831 | .usize_type, | 829 | .usize_type, |
| 832 | .isize_type, | 830 | .isize_type, |
| 833 | .c_short_type, | 831 | .c_short_type, |
| ... | @@ -860,7 +858,6 @@ pub const Value = extern union { | ... | @@ -860,7 +858,6 @@ pub const Value = extern union { |
| 860 | .single_const_pointer_to_comptime_int_type, | 858 | .single_const_pointer_to_comptime_int_type, |
| 861 | .const_slice_u8_type, | 859 | .const_slice_u8_type, |
| 862 | .enum_literal_type, | 860 | .enum_literal_type, |
| 863 | .anyframe_type, | ||
| 864 | .null_value, | 861 | .null_value, |
| 865 | .function, | 862 | .function, |
| 866 | .extern_fn, | 863 | .extern_fn, |
| ... | @@ -878,11 +875,11 @@ pub const Value = extern union { | ... | @@ -878,11 +875,11 @@ pub const Value = extern union { |
| 878 | .unreachable_value, | 875 | .unreachable_value, |
| 879 | .empty_array, | 876 | .empty_array, |
| 880 | .enum_literal, | 877 | .enum_literal, |
| 881 | .error_set, | ||
| 882 | .@"error", | 878 | .@"error", |
| 883 | .error_union, | 879 | .error_union, |
| 884 | .empty_struct_value, | 880 | .empty_struct_value, |
| 885 | .inferred_alloc, | 881 | .inferred_alloc, |
| 882 | .abi_align_default, | ||
| 886 | => unreachable, | 883 | => unreachable, |
| 887 | 884 | ||
| 888 | .undef => unreachable, | 885 | .undef => unreachable, |
| ... | @@ -942,6 +939,8 @@ pub const Value = extern union { | ... | @@ -942,6 +939,8 @@ pub const Value = extern union { |
| 942 | .i32_type, | 939 | .i32_type, |
| 943 | .u64_type, | 940 | .u64_type, |
| 944 | .i64_type, | 941 | .i64_type, |
| 942 | .u128_type, | ||
| 943 | .i128_type, | ||
| 945 | .usize_type, | 944 | .usize_type, |
| 946 | .isize_type, | 945 | .isize_type, |
| 947 | .c_short_type, | 946 | .c_short_type, |
| ... | @@ -974,7 +973,6 @@ pub const Value = extern union { | ... | @@ -974,7 +973,6 @@ pub const Value = extern union { |
| 974 | .single_const_pointer_to_comptime_int_type, | 973 | .single_const_pointer_to_comptime_int_type, |
| 975 | .const_slice_u8_type, | 974 | .const_slice_u8_type, |
| 976 | .enum_literal_type, | 975 | .enum_literal_type, |
| 977 | .anyframe_type, | ||
| 978 | .null_value, | 976 | .null_value, |
| 979 | .function, | 977 | .function, |
| 980 | .extern_fn, | 978 | .extern_fn, |
| ... | @@ -993,11 +991,11 @@ pub const Value = extern union { | ... | @@ -993,11 +991,11 @@ pub const Value = extern union { |
| 993 | .unreachable_value, | 991 | .unreachable_value, |
| 994 | .empty_array, | 992 | .empty_array, |
| 995 | .enum_literal, | 993 | .enum_literal, |
| 996 | .error_set, | ||
| 997 | .@"error", | 994 | .@"error", |
| 998 | .error_union, | 995 | .error_union, |
| 999 | .empty_struct_value, | 996 | .empty_struct_value, |
| 1000 | .inferred_alloc, | 997 | .inferred_alloc, |
| 998 | .abi_align_default, | ||
| 1001 | => unreachable, | 999 | => unreachable, |
| 1002 | 1000 | ||
| 1003 | .zero, | 1001 | .zero, |
| ... | @@ -1034,6 +1032,8 @@ pub const Value = extern union { | ... | @@ -1034,6 +1032,8 @@ pub const Value = extern union { |
| 1034 | .i32_type, | 1032 | .i32_type, |
| 1035 | .u64_type, | 1033 | .u64_type, |
| 1036 | .i64_type, | 1034 | .i64_type, |
| 1035 | .u128_type, | ||
| 1036 | .i128_type, | ||
| 1037 | .usize_type, | 1037 | .usize_type, |
| 1038 | .isize_type, | 1038 | .isize_type, |
| 1039 | .c_short_type, | 1039 | .c_short_type, |
| ... | @@ -1066,7 +1066,6 @@ pub const Value = extern union { | ... | @@ -1066,7 +1066,6 @@ pub const Value = extern union { |
| 1066 | .single_const_pointer_to_comptime_int_type, | 1066 | .single_const_pointer_to_comptime_int_type, |
| 1067 | .const_slice_u8_type, | 1067 | .const_slice_u8_type, |
| 1068 | .enum_literal_type, | 1068 | .enum_literal_type, |
| 1069 | .anyframe_type, | ||
| 1070 | .null_value, | 1069 | .null_value, |
| 1071 | .function, | 1070 | .function, |
| 1072 | .extern_fn, | 1071 | .extern_fn, |
| ... | @@ -1084,11 +1083,11 @@ pub const Value = extern union { | ... | @@ -1084,11 +1083,11 @@ pub const Value = extern union { |
| 1084 | .unreachable_value, | 1083 | .unreachable_value, |
| 1085 | .empty_array, | 1084 | .empty_array, |
| 1086 | .enum_literal, | 1085 | .enum_literal, |
| 1087 | .error_set, | ||
| 1088 | .@"error", | 1086 | .@"error", |
| 1089 | .error_union, | 1087 | .error_union, |
| 1090 | .empty_struct_value, | 1088 | .empty_struct_value, |
| 1091 | .inferred_alloc, | 1089 | .inferred_alloc, |
| 1090 | .abi_align_default, | ||
| 1092 | => unreachable, | 1091 | => unreachable, |
| 1093 | 1092 | ||
| 1094 | .zero, | 1093 | .zero, |
| ... | @@ -1191,6 +1190,8 @@ pub const Value = extern union { | ... | @@ -1191,6 +1190,8 @@ pub const Value = extern union { |
| 1191 | .i32_type, | 1190 | .i32_type, |
| 1192 | .u64_type, | 1191 | .u64_type, |
| 1193 | .i64_type, | 1192 | .i64_type, |
| 1193 | .u128_type, | ||
| 1194 | .i128_type, | ||
| 1194 | .usize_type, | 1195 | .usize_type, |
| 1195 | .isize_type, | 1196 | .isize_type, |
| 1196 | .c_short_type, | 1197 | .c_short_type, |
| ... | @@ -1223,7 +1224,6 @@ pub const Value = extern union { | ... | @@ -1223,7 +1224,6 @@ pub const Value = extern union { |
| 1223 | .single_const_pointer_to_comptime_int_type, | 1224 | .single_const_pointer_to_comptime_int_type, |
| 1224 | .const_slice_u8_type, | 1225 | .const_slice_u8_type, |
| 1225 | .enum_literal_type, | 1226 | .enum_literal_type, |
| 1226 | .anyframe_type, | ||
| 1227 | .bool_true, | 1227 | .bool_true, |
| 1228 | .bool_false, | 1228 | .bool_false, |
| 1229 | .null_value, | 1229 | .null_value, |
| ... | @@ -1244,11 +1244,11 @@ pub const Value = extern union { | ... | @@ -1244,11 +1244,11 @@ pub const Value = extern union { |
| 1244 | .void_value, | 1244 | .void_value, |
| 1245 | .unreachable_value, | 1245 | .unreachable_value, |
| 1246 | .enum_literal, | 1246 | .enum_literal, |
| 1247 | .error_set, | ||
| 1248 | .@"error", | 1247 | .@"error", |
| 1249 | .error_union, | 1248 | .error_union, |
| 1250 | .empty_struct_value, | 1249 | .empty_struct_value, |
| 1251 | .inferred_alloc, | 1250 | .inferred_alloc, |
| 1251 | .abi_align_default, | ||
| 1252 | => unreachable, | 1252 | => unreachable, |
| 1253 | 1253 | ||
| 1254 | .zero, | 1254 | .zero, |
| ... | @@ -1275,6 +1275,8 @@ pub const Value = extern union { | ... | @@ -1275,6 +1275,8 @@ pub const Value = extern union { |
| 1275 | .i32_type, | 1275 | .i32_type, |
| 1276 | .u64_type, | 1276 | .u64_type, |
| 1277 | .i64_type, | 1277 | .i64_type, |
| 1278 | .u128_type, | ||
| 1279 | .i128_type, | ||
| 1278 | .usize_type, | 1280 | .usize_type, |
| 1279 | .isize_type, | 1281 | .isize_type, |
| 1280 | .c_short_type, | 1282 | .c_short_type, |
| ... | @@ -1307,7 +1309,6 @@ pub const Value = extern union { | ... | @@ -1307,7 +1309,6 @@ pub const Value = extern union { |
| 1307 | .single_const_pointer_to_comptime_int_type, | 1309 | .single_const_pointer_to_comptime_int_type, |
| 1308 | .const_slice_u8_type, | 1310 | .const_slice_u8_type, |
| 1309 | .enum_literal_type, | 1311 | .enum_literal_type, |
| 1310 | .anyframe_type, | ||
| 1311 | .null_value, | 1312 | .null_value, |
| 1312 | .function, | 1313 | .function, |
| 1313 | .extern_fn, | 1314 | .extern_fn, |
| ... | @@ -1322,11 +1323,11 @@ pub const Value = extern union { | ... | @@ -1322,11 +1323,11 @@ pub const Value = extern union { |
| 1322 | .unreachable_value, | 1323 | .unreachable_value, |
| 1323 | .empty_array, | 1324 | .empty_array, |
| 1324 | .enum_literal, | 1325 | .enum_literal, |
| 1325 | .error_set, | ||
| 1326 | .@"error", | 1326 | .@"error", |
| 1327 | .error_union, | 1327 | .error_union, |
| 1328 | .empty_struct_value, | 1328 | .empty_struct_value, |
| 1329 | .inferred_alloc, | 1329 | .inferred_alloc, |
| 1330 | .abi_align_default, | ||
| 1330 | => unreachable, | 1331 | => unreachable, |
| 1331 | 1332 | ||
| 1332 | .zero, | 1333 | .zero, |
| ... | @@ -1427,6 +1428,8 @@ pub const Value = extern union { | ... | @@ -1427,6 +1428,8 @@ pub const Value = extern union { |
| 1427 | .i32_type, | 1428 | .i32_type, |
| 1428 | .u64_type, | 1429 | .u64_type, |
| 1429 | .i64_type, | 1430 | .i64_type, |
| 1431 | .u128_type, | ||
| 1432 | .i128_type, | ||
| 1430 | .usize_type, | 1433 | .usize_type, |
| 1431 | .isize_type, | 1434 | .isize_type, |
| 1432 | .c_short_type, | 1435 | .c_short_type, |
| ... | @@ -1459,18 +1462,13 @@ pub const Value = extern union { | ... | @@ -1459,18 +1462,13 @@ pub const Value = extern union { |
| 1459 | .single_const_pointer_to_comptime_int_type, | 1462 | .single_const_pointer_to_comptime_int_type, |
| 1460 | .const_slice_u8_type, | 1463 | .const_slice_u8_type, |
| 1461 | .enum_literal_type, | 1464 | .enum_literal_type, |
| 1462 | .anyframe_type, | ||
| 1463 | .ty, | 1465 | .ty, |
| 1466 | .abi_align_default, | ||
| 1464 | => { | 1467 | => { |
| 1465 | // Directly return Type.hash, toType can only fail for .int_type and .error_set. | 1468 | // Directly return Type.hash, toType can only fail for .int_type. |
| 1466 | var allocator = std.heap.FixedBufferAllocator.init(&[_]u8{}); | 1469 | var allocator = std.heap.FixedBufferAllocator.init(&[_]u8{}); |
| 1467 | return (self.toType(&allocator.allocator) catch unreachable).hash(); | 1470 | return (self.toType(&allocator.allocator) catch unreachable).hash(); |
| 1468 | }, | 1471 | }, |
| 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 | .int_type => { | 1472 | .int_type => { |
| 1475 | const payload = self.castTag(.int_type).?.data; | 1473 | const payload = self.castTag(.int_type).?.data; |
| 1476 | var int_payload = Type.Payload.Bits{ | 1474 | var int_payload = Type.Payload.Bits{ |
| ... | @@ -1585,6 +1583,8 @@ pub const Value = extern union { | ... | @@ -1585,6 +1583,8 @@ pub const Value = extern union { |
| 1585 | .i32_type, | 1583 | .i32_type, |
| 1586 | .u64_type, | 1584 | .u64_type, |
| 1587 | .i64_type, | 1585 | .i64_type, |
| 1586 | .u128_type, | ||
| 1587 | .i128_type, | ||
| 1588 | .usize_type, | 1588 | .usize_type, |
| 1589 | .isize_type, | 1589 | .isize_type, |
| 1590 | .c_short_type, | 1590 | .c_short_type, |
| ... | @@ -1617,7 +1617,6 @@ pub const Value = extern union { | ... | @@ -1617,7 +1617,6 @@ pub const Value = extern union { |
| 1617 | .single_const_pointer_to_comptime_int_type, | 1617 | .single_const_pointer_to_comptime_int_type, |
| 1618 | .const_slice_u8_type, | 1618 | .const_slice_u8_type, |
| 1619 | .enum_literal_type, | 1619 | .enum_literal_type, |
| 1620 | .anyframe_type, | ||
| 1621 | .zero, | 1620 | .zero, |
| 1622 | .one, | 1621 | .one, |
| 1623 | .bool_true, | 1622 | .bool_true, |
| ... | @@ -1641,11 +1640,11 @@ pub const Value = extern union { | ... | @@ -1641,11 +1640,11 @@ pub const Value = extern union { |
| 1641 | .unreachable_value, | 1640 | .unreachable_value, |
| 1642 | .empty_array, | 1641 | .empty_array, |
| 1643 | .enum_literal, | 1642 | .enum_literal, |
| 1644 | .error_set, | ||
| 1645 | .@"error", | 1643 | .@"error", |
| 1646 | .error_union, | 1644 | .error_union, |
| 1647 | .empty_struct_value, | 1645 | .empty_struct_value, |
| 1648 | .inferred_alloc, | 1646 | .inferred_alloc, |
| 1647 | .abi_align_default, | ||
| 1649 | => unreachable, | 1648 | => unreachable, |
| 1650 | 1649 | ||
| 1651 | .ref_val => self.castTag(.ref_val).?.data, | 1650 | .ref_val => self.castTag(.ref_val).?.data, |
| ... | @@ -1672,6 +1671,8 @@ pub const Value = extern union { | ... | @@ -1672,6 +1671,8 @@ pub const Value = extern union { |
| 1672 | .i32_type, | 1671 | .i32_type, |
| 1673 | .u64_type, | 1672 | .u64_type, |
| 1674 | .i64_type, | 1673 | .i64_type, |
| 1674 | .u128_type, | ||
| 1675 | .i128_type, | ||
| 1675 | .usize_type, | 1676 | .usize_type, |
| 1676 | .isize_type, | 1677 | .isize_type, |
| 1677 | .c_short_type, | 1678 | .c_short_type, |
| ... | @@ -1704,7 +1705,6 @@ pub const Value = extern union { | ... | @@ -1704,7 +1705,6 @@ pub const Value = extern union { |
| 1704 | .single_const_pointer_to_comptime_int_type, | 1705 | .single_const_pointer_to_comptime_int_type, |
| 1705 | .const_slice_u8_type, | 1706 | .const_slice_u8_type, |
| 1706 | .enum_literal_type, | 1707 | .enum_literal_type, |
| 1707 | .anyframe_type, | ||
| 1708 | .zero, | 1708 | .zero, |
| 1709 | .one, | 1709 | .one, |
| 1710 | .bool_true, | 1710 | .bool_true, |
| ... | @@ -1728,11 +1728,11 @@ pub const Value = extern union { | ... | @@ -1728,11 +1728,11 @@ pub const Value = extern union { |
| 1728 | .void_value, | 1728 | .void_value, |
| 1729 | .unreachable_value, | 1729 | .unreachable_value, |
| 1730 | .enum_literal, | 1730 | .enum_literal, |
| 1731 | .error_set, | ||
| 1732 | .@"error", | 1731 | .@"error", |
| 1733 | .error_union, | 1732 | .error_union, |
| 1734 | .empty_struct_value, | 1733 | .empty_struct_value, |
| 1735 | .inferred_alloc, | 1734 | .inferred_alloc, |
| 1735 | .abi_align_default, | ||
| 1736 | => unreachable, | 1736 | => unreachable, |
| 1737 | 1737 | ||
| 1738 | .empty_array => unreachable, // out of bounds array index | 1738 | .empty_array => unreachable, // out of bounds array index |
| ... | @@ -1776,6 +1776,8 @@ pub const Value = extern union { | ... | @@ -1776,6 +1776,8 @@ pub const Value = extern union { |
| 1776 | .i32_type, | 1776 | .i32_type, |
| 1777 | .u64_type, | 1777 | .u64_type, |
| 1778 | .i64_type, | 1778 | .i64_type, |
| 1779 | .u128_type, | ||
| 1780 | .i128_type, | ||
| 1779 | .usize_type, | 1781 | .usize_type, |
| 1780 | .isize_type, | 1782 | .isize_type, |
| 1781 | .c_short_type, | 1783 | .c_short_type, |
| ... | @@ -1808,7 +1810,6 @@ pub const Value = extern union { | ... | @@ -1808,7 +1810,6 @@ pub const Value = extern union { |
| 1808 | .single_const_pointer_to_comptime_int_type, | 1810 | .single_const_pointer_to_comptime_int_type, |
| 1809 | .const_slice_u8_type, | 1811 | .const_slice_u8_type, |
| 1810 | .enum_literal_type, | 1812 | .enum_literal_type, |
| 1811 | .anyframe_type, | ||
| 1812 | .zero, | 1813 | .zero, |
| 1813 | .one, | 1814 | .one, |
| 1814 | .empty_array, | 1815 | .empty_array, |
| ... | @@ -1832,10 +1833,10 @@ pub const Value = extern union { | ... | @@ -1832,10 +1833,10 @@ pub const Value = extern union { |
| 1832 | .float_128, | 1833 | .float_128, |
| 1833 | .void_value, | 1834 | .void_value, |
| 1834 | .enum_literal, | 1835 | .enum_literal, |
| 1835 | .error_set, | ||
| 1836 | .@"error", | 1836 | .@"error", |
| 1837 | .error_union, | 1837 | .error_union, |
| 1838 | .empty_struct_value, | 1838 | .empty_struct_value, |
| 1839 | .abi_align_default, | ||
| 1839 | => false, | 1840 | => false, |
| 1840 | 1841 | ||
| 1841 | .undef => unreachable, | 1842 | .undef => unreachable, |
| ... | @@ -1858,6 +1859,8 @@ pub const Value = extern union { | ... | @@ -1858,6 +1859,8 @@ pub const Value = extern union { |
| 1858 | .i32_type, | 1859 | .i32_type, |
| 1859 | .u64_type, | 1860 | .u64_type, |
| 1860 | .i64_type, | 1861 | .i64_type, |
| 1862 | .u128_type, | ||
| 1863 | .i128_type, | ||
| 1861 | .usize_type, | 1864 | .usize_type, |
| 1862 | .isize_type, | 1865 | .isize_type, |
| 1863 | .c_short_type, | 1866 | .c_short_type, |
| ... | @@ -1890,7 +1893,6 @@ pub const Value = extern union { | ... | @@ -1890,7 +1893,6 @@ pub const Value = extern union { |
| 1890 | .single_const_pointer_to_comptime_int_type, | 1893 | .single_const_pointer_to_comptime_int_type, |
| 1891 | .const_slice_u8_type, | 1894 | .const_slice_u8_type, |
| 1892 | .enum_literal_type, | 1895 | .enum_literal_type, |
| 1893 | .anyframe_type, | ||
| 1894 | .zero, | 1896 | .zero, |
| 1895 | .one, | 1897 | .one, |
| 1896 | .null_value, | 1898 | .null_value, |
| ... | @@ -1915,8 +1917,8 @@ pub const Value = extern union { | ... | @@ -1915,8 +1917,8 @@ pub const Value = extern union { |
| 1915 | .float_128, | 1917 | .float_128, |
| 1916 | .void_value, | 1918 | .void_value, |
| 1917 | .enum_literal, | 1919 | .enum_literal, |
| 1918 | .error_set, | ||
| 1919 | .empty_struct_value, | 1920 | .empty_struct_value, |
| 1921 | .abi_align_default, | ||
| 1920 | => null, | 1922 | => null, |
| 1921 | 1923 | ||
| 1922 | .error_union => { | 1924 | .error_union => { |
| ... | @@ -1960,6 +1962,8 @@ pub const Value = extern union { | ... | @@ -1960,6 +1962,8 @@ pub const Value = extern union { |
| 1960 | .i32_type, | 1962 | .i32_type, |
| 1961 | .u64_type, | 1963 | .u64_type, |
| 1962 | .i64_type, | 1964 | .i64_type, |
| 1965 | .u128_type, | ||
| 1966 | .i128_type, | ||
| 1963 | .usize_type, | 1967 | .usize_type, |
| 1964 | .isize_type, | 1968 | .isize_type, |
| 1965 | .c_short_type, | 1969 | .c_short_type, |
| ... | @@ -1992,8 +1996,6 @@ pub const Value = extern union { | ... | @@ -1992,8 +1996,6 @@ pub const Value = extern union { |
| 1992 | .single_const_pointer_to_comptime_int_type, | 1996 | .single_const_pointer_to_comptime_int_type, |
| 1993 | .const_slice_u8_type, | 1997 | .const_slice_u8_type, |
| 1994 | .enum_literal_type, | 1998 | .enum_literal_type, |
| 1995 | .anyframe_type, | ||
| 1996 | .error_set, | ||
| 1997 | => true, | 1999 | => true, |
| 1998 | 2000 | ||
| 1999 | .zero, | 2001 | .zero, |
| ... | @@ -2023,6 +2025,7 @@ pub const Value = extern union { | ... | @@ -2023,6 +2025,7 @@ pub const Value = extern union { |
| 2023 | .error_union, | 2025 | .error_union, |
| 2024 | .empty_struct_value, | 2026 | .empty_struct_value, |
| 2025 | .null_value, | 2027 | .null_value, |
| 2028 | .abi_align_default, | ||
| 2026 | => false, | 2029 | => false, |
| 2027 | 2030 | ||
| 2028 | .undef => unreachable, | 2031 | .undef => unreachable, |
| ... | @@ -2137,18 +2140,6 @@ pub const Value = extern union { | ... | @@ -2137,18 +2140,6 @@ pub const Value = extern union { |
| 2137 | data: f128, | 2140 | data: f128, |
| 2138 | }; | 2141 | }; |
| 2139 | 2142 | ||
| 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 | pub const Error = struct { | 2143 | pub const Error = struct { |
| 2153 | base: Payload = .{ .tag = .@"error" }, | 2144 | base: Payload = .{ .tag = .@"error" }, |
| 2154 | data: struct { | 2145 | data: struct { |
src/zir.zig+1859-1584| ... | @@ -1,4 +1,5 @@ | ... | @@ -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 | const std = @import("std"); | 4 | const std = @import("std"); |
| 4 | const mem = std.mem; | 5 | const mem = std.mem; |
| ... | @@ -6,524 +7,663 @@ const Allocator = std.mem.Allocator; | ... | @@ -6,524 +7,663 @@ const Allocator = std.mem.Allocator; |
| 6 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 7 | const BigIntConst = std.math.big.int.Const; | 8 | const BigIntConst = std.math.big.int.Const; |
| 8 | const BigIntMutable = std.math.big.int.Mutable; | 9 | const BigIntMutable = std.math.big.int.Mutable; |
| 10 | const ast = std.zig.ast; | ||
| 11 | |||
| 9 | const Type = @import("type.zig").Type; | 12 | const Type = @import("type.zig").Type; |
| 10 | const Value = @import("value.zig").Value; | 13 | const Value = @import("value.zig").Value; |
| 11 | const TypedValue = @import("TypedValue.zig"); | 14 | const TypedValue = @import("TypedValue.zig"); |
| 12 | const ir = @import("ir.zig"); | 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 | 113 | /// These are untyped instructions generated from an Abstract Syntax Tree. |
| 16 | /// in-memory, analyzed instructions with types and values. | 114 | /// The data here is immutable because it is possible to have multiple |
| 17 | /// We use a table to map these instruction to their respective semantically analyzed | 115 | /// analyses on the same ZIR happening at the same time. |
| 18 | /// instructions because it is possible to have multiple analyses on the same ZIR | ||
| 19 | /// happening at the same time. | ||
| 20 | pub const Inst = struct { | 116 | pub const Inst = struct { |
| 21 | tag: Tag, | 117 | tag: Tag, |
| 22 | /// Byte offset into the source. | 118 | data: Data, |
| 23 | src: usize, | ||
| 24 | 119 | ||
| 25 | /// These names are used directly as the instruction names in the text format. | 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 | /// Arithmetic addition, asserts no integer overflow. | 122 | /// Arithmetic addition, asserts no integer overflow. |
| 123 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 28 | add, | 124 | add, |
| 29 | /// Twos complement wrapping integer addition. | 125 | /// Twos complement wrapping integer addition. |
| 126 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 30 | addwrap, | 127 | addwrap, |
| 31 | /// Allocates stack local memory. Its lifetime ends when the block ends that contains | 128 | /// Allocates stack local memory. |
| 32 | /// this instruction. The operand is the type of the allocated object. | 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 | alloc, | 132 | alloc, |
| 34 | /// Same as `alloc` except mutable. | 133 | /// Same as `alloc` except mutable. |
| 35 | alloc_mut, | 134 | alloc_mut, |
| 36 | /// Same as `alloc` except the type is inferred. | 135 | /// Same as `alloc` except the type is inferred. |
| 136 | /// The operand is unused. | ||
| 37 | alloc_inferred, | 137 | alloc_inferred, |
| 38 | /// Same as `alloc_inferred` except mutable. | 138 | /// Same as `alloc_inferred` except mutable. |
| 39 | alloc_inferred_mut, | 139 | alloc_inferred_mut, |
| 40 | /// Create an `anyframe->T`. | ||
| 41 | anyframe_type, | ||
| 42 | /// Array concatenation. `a ++ b` | 140 | /// Array concatenation. `a ++ b` |
| 141 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 43 | array_cat, | 142 | array_cat, |
| 44 | /// Array multiplication `a ** b` | 143 | /// Array multiplication `a ** b` |
| 144 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 45 | array_mul, | 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 | array_type, | 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 | array_type_sentinel, | 151 | array_type_sentinel, |
| 50 | /// Given a pointer to an indexable object, returns the len property. This is | 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 | 153 | /// used by for loops. This instruction also emits a for-loop specific compile |
| 52 | /// if the indexable object is not indexable. | 154 | /// error if the indexable object is not indexable. |
| 155 | /// Uses the `un_node` field. The AST node is the for loop node. | ||
| 53 | indexable_ptr_len, | 156 | indexable_ptr_len, |
| 54 | /// Function parameter value. These must be first in a function's main block, | 157 | /// Type coercion. No source location attached. |
| 55 | /// in respective order with the parameters. | 158 | /// Uses the `bin` field. |
| 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. | ||
| 61 | as, | 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 | @"asm", | 165 | @"asm", |
| 64 | /// Await an async function. | 166 | /// Inline assembly with the volatile attribute. |
| 65 | @"await", | 167 | /// Uses the `pl_node` union field. Payload is `Asm`. AST node is the assembly node. |
| 168 | asm_volatile, | ||
| 66 | /// Bitwise AND. `&` | 169 | /// Bitwise AND. `&` |
| 67 | bit_and, | 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 | bitcast, | 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 | /// A typed result location pointer is bitcasted to a new result location pointer. | 174 | /// A typed result location pointer is bitcasted to a new result location pointer. |
| 76 | /// The new result location pointer has an inferred type. | 175 | /// The new result location pointer has an inferred type. |
| 176 | /// Uses the un_node field. | ||
| 77 | bitcast_result_ptr, | 177 | bitcast_result_ptr, |
| 78 | /// Bitwise NOT. `~` | 178 | /// Bitwise NOT. `~` |
| 179 | /// Uses `un_node`. | ||
| 79 | bit_not, | 180 | bit_not, |
| 80 | /// Bitwise OR. `|` | 181 | /// Bitwise OR. `|` |
| 81 | bit_or, | 182 | bit_or, |
| 82 | /// A labeled block of code, which can return a value. | 183 | /// A labeled block of code, which can return a value. |
| 184 | /// Uses the `pl_node` union field. Payload is `Block`. | ||
| 83 | block, | 185 | block, |
| 84 | /// A block of code, which can return a value. There are no instructions that break out of | 186 | /// A list of instructions which are analyzed in the parent context, without |
| 85 | /// this block; it is implied that the final instruction is the result. | 187 | /// generating a runtime block. Must terminate with an "inline" variant of |
| 86 | block_flat, | 188 | /// a noreturn instruction. |
| 87 | /// Same as `block` but additionally makes the inner instructions execute at comptime. | 189 | /// Uses the `pl_node` union field. Payload is `Block`. |
| 88 | block_comptime, | 190 | block_inline, |
| 89 | /// Same as `block_flat` but additionally makes the inner instructions execute at comptime. | ||
| 90 | block_comptime_flat, | ||
| 91 | /// Boolean AND. See also `bit_and`. | 191 | /// Boolean AND. See also `bit_and`. |
| 192 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 92 | bool_and, | 193 | bool_and, |
| 93 | /// Boolean NOT. See also `bit_not`. | 194 | /// Boolean NOT. See also `bit_not`. |
| 195 | /// Uses the `un_node` field. | ||
| 94 | bool_not, | 196 | bool_not, |
| 95 | /// Boolean OR. See also `bit_or`. | 197 | /// Boolean OR. See also `bit_or`. |
| 198 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 96 | bool_or, | 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 | @"break", | 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 | breakpoint, | 219 | breakpoint, |
| 100 | /// Same as `break` but without an operand; the operand is assumed to be the void value. | 220 | /// Function call with modifier `.auto`. |
| 101 | break_void, | 221 | /// Uses `pl_node`. AST node is the function call. Payload is `Call`. |
| 102 | /// Function call. | ||
| 103 | call, | 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 | cmp_lt, | 234 | cmp_lt, |
| 106 | /// `<=` | 235 | /// `<=` |
| 236 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 107 | cmp_lte, | 237 | cmp_lte, |
| 108 | /// `==` | 238 | /// `==` |
| 239 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 109 | cmp_eq, | 240 | cmp_eq, |
| 110 | /// `>=` | 241 | /// `>=` |
| 242 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 111 | cmp_gte, | 243 | cmp_gte, |
| 112 | /// `>` | 244 | /// `>` |
| 245 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 113 | cmp_gt, | 246 | cmp_gt, |
| 114 | /// `!=` | 247 | /// `!=` |
| 248 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 115 | cmp_neq, | 249 | cmp_neq, |
| 116 | /// Coerces a result location pointer to a new element type. It is evaluated "backwards"- | 250 | /// Coerces a result location pointer to a new element type. It is evaluated "backwards"- |
| 117 | /// as type coercion from the new element type to the old element type. | 251 | /// as type coercion from the new element type to the old element type. |
| 252 | /// Uses the `bin` union field. | ||
| 118 | /// LHS is destination element type, RHS is result pointer. | 253 | /// LHS is destination element type, RHS is result pointer. |
| 119 | coerce_result_ptr, | 254 | coerce_result_ptr, |
| 120 | /// Emit an error message and fail compilation. | 255 | /// Emit an error message and fail compilation. |
| 256 | /// Uses the `un_node` field. | ||
| 121 | compile_error, | 257 | compile_error, |
| 122 | /// Log compile time variables and emit an error message. | 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 | compile_log, | 261 | compile_log, |
| 124 | /// Conditional branch. Splits control flow based on a boolean condition value. | 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 | condbr, | 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 | @"const", | 272 | @"const", |
| 128 | /// Container field with just the name. | 273 | /// A struct type definition. Contains references to ZIR instructions for |
| 129 | container_field_named, | 274 | /// the field types, defaults, and alignments. |
| 130 | /// Container field with a type and a name, | 275 | /// Uses the `pl_node` union field. Payload is `StructDecl`. |
| 131 | container_field_typed, | 276 | struct_decl, |
| 132 | /// Container field with all the bells and whistles. | 277 | /// Same as `struct_decl`, except has the `packed` layout. |
| 133 | container_field, | 278 | struct_decl_packed, |
| 279 | /// Same as `struct_decl`, except has the `extern` layout. | ||
| 280 | struct_decl_extern, | ||
| 281 | /// A union type definition. Contains references to ZIR instructions for | ||
| 282 | /// the field types and optional type tag expression. | ||
| 283 | /// Uses the `pl_node` union field. Payload is `UnionDecl`. | ||
| 284 | union_decl, | ||
| 285 | /// An enum type definition. Contains references to ZIR instructions for | ||
| 286 | /// the field value expressions and optional type tag expression. | ||
| 287 | /// Uses the `pl_node` union field. Payload is `EnumDecl`. | ||
| 288 | enum_decl, | ||
| 289 | /// An opaque type definition. Provides an AST node only. | ||
| 290 | /// Uses the `node` union field. | ||
| 291 | opaque_decl, | ||
| 134 | /// Declares the beginning of a statement. Used for debug info. | 292 | /// Declares the beginning of a statement. Used for debug info. |
| 135 | dbg_stmt, | 293 | /// Uses the `node` union field. |
| 294 | dbg_stmt_node, | ||
| 136 | /// Represents a pointer to a global decl. | 295 | /// Represents a pointer to a global decl. |
| 296 | /// Uses the `pl_node` union field. `payload_index` is into `decls`. | ||
| 137 | decl_ref, | 297 | decl_ref, |
| 138 | /// Represents a pointer to a global decl by string name. | 298 | /// Equivalent to a decl_ref followed by load. |
| 139 | decl_ref_str, | 299 | /// Uses the `pl_node` union field. `payload_index` is into `decls`. |
| 140 | /// Equivalent to a decl_ref followed by deref. | ||
| 141 | decl_val, | 300 | decl_val, |
| 142 | /// Load the value from a pointer. | 301 | /// Load the value from a pointer. Assumes `x.*` syntax. |
| 143 | deref, | 302 | /// Uses `un_node` field. AST node is the `x.*` syntax. |
| 303 | load, | ||
| 144 | /// Arithmetic division. Asserts no integer overflow. | 304 | /// Arithmetic division. Asserts no integer overflow. |
| 305 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 145 | div, | 306 | div, |
| 146 | /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at | 307 | /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at |
| 147 | /// the provided index. | 308 | /// the provided index. Uses the `bin` union field. Source location is implied |
| 309 | /// to be the same as the previous instruction. | ||
| 148 | elem_ptr, | 310 | elem_ptr, |
| 311 | /// Same as `elem_ptr` except also stores a source location node. | ||
| 312 | /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`. | ||
| 313 | elem_ptr_node, | ||
| 149 | /// Given an array, slice, or pointer, returns the element at the provided index. | 314 | /// Given an array, slice, or pointer, returns the element at the provided index. |
| 315 | /// Uses the `bin` union field. Source location is implied to be the same | ||
| 316 | /// as the previous instruction. | ||
| 150 | elem_val, | 317 | elem_val, |
| 318 | /// Same as `elem_val` except also stores a source location node. | ||
| 319 | /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`. | ||
| 320 | elem_val_node, | ||
| 321 | /// This instruction has been deleted late in the astgen phase. It must | ||
| 322 | /// be ignored, and the corresponding `Data` is undefined. | ||
| 323 | elided, | ||
| 151 | /// Emits a compile error if the operand is not `void`. | 324 | /// Emits a compile error if the operand is not `void`. |
| 325 | /// Uses the `un_node` field. | ||
| 152 | ensure_result_used, | 326 | ensure_result_used, |
| 153 | /// Emits a compile error if an error is ignored. | 327 | /// Emits a compile error if an error is ignored. |
| 328 | /// Uses the `un_node` field. | ||
| 154 | ensure_result_non_error, | 329 | ensure_result_non_error, |
| 155 | /// Create a `E!T` type. | 330 | /// Create a `E!T` type. |
| 331 | /// Uses the `pl_node` field with `Bin` payload. | ||
| 156 | error_union_type, | 332 | error_union_type, |
| 157 | /// Create an error set. | 333 | /// `error.Foo` syntax. Uses the `str_tok` field of the Data union. |
| 158 | error_set, | ||
| 159 | /// `error.Foo` syntax. | ||
| 160 | error_value, | 334 | error_value, |
| 161 | /// Export the provided Decl as the provided name in the compilation's output object file. | ||
| 162 | @"export", | ||
| 163 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer | 335 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer |
| 164 | /// to the named field. The field name is a []const u8. Used by a.b syntax. | 336 | /// to the named field. The field name is stored in string_bytes. Used by a.b syntax. |
| 337 | /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. | ||
| 165 | field_ptr, | 338 | field_ptr, |
| 166 | /// Given a struct or object that contains virtual fields, returns the named field. | 339 | /// Given a struct or object that contains virtual fields, returns the named field. |
| 167 | /// The field name is a []const u8. Used by a.b syntax. | 340 | /// The field name is stored in string_bytes. Used by a.b syntax. |
| 341 | /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. | ||
| 168 | field_val, | 342 | field_val, |
| 169 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer | 343 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer |
| 170 | /// to the named field. The field name is a comptime instruction. Used by @field. | 344 | /// to the named field. The field name is a comptime instruction. Used by @field. |
| 345 | /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. | ||
| 171 | field_ptr_named, | 346 | field_ptr_named, |
| 172 | /// Given a struct or object that contains virtual fields, returns the named field. | 347 | /// Given a struct or object that contains virtual fields, returns the named field. |
| 173 | /// The field name is a comptime instruction. Used by @field. | 348 | /// The field name is a comptime instruction. Used by @field. |
| 349 | /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. | ||
| 174 | field_val_named, | 350 | field_val_named, |
| 175 | /// Convert a larger float type to any other float type, possibly causing a loss of precision. | 351 | /// Convert a larger float type to any other float type, possibly causing |
| 352 | /// a loss of precision. | ||
| 353 | /// Uses the `pl_node` field. AST is the `@floatCast` syntax. | ||
| 354 | /// Payload is `Bin` with lhs as the dest type, rhs the operand. | ||
| 176 | floatcast, | 355 | floatcast, |
| 177 | /// Declare a function body. | ||
| 178 | @"fn", | ||
| 179 | /// Returns a function type, assuming unspecified calling convention. | 356 | /// Returns a function type, assuming unspecified calling convention. |
| 357 | /// Uses the `pl_node` union field. `payload_index` points to a `FnType`. | ||
| 180 | fn_type, | 358 | fn_type, |
| 181 | /// Same as `fn_type` but the function is variadic. | 359 | /// Same as `fn_type` but the function is variadic. |
| 182 | fn_type_var_args, | 360 | fn_type_var_args, |
| 183 | /// Returns a function type, with a calling convention instruction operand. | 361 | /// Returns a function type, with a calling convention instruction operand. |
| 362 | /// Uses the `pl_node` union field. `payload_index` points to a `FnTypeCc`. | ||
| 184 | fn_type_cc, | 363 | fn_type_cc, |
| 185 | /// Same as `fn_type_cc` but the function is variadic. | 364 | /// Same as `fn_type_cc` but the function is variadic. |
| 186 | fn_type_cc_var_args, | 365 | fn_type_cc_var_args, |
| 187 | /// @import(operand) | 366 | /// `@import(operand)`. |
| 367 | /// Uses the `un_node` field. | ||
| 188 | import, | 368 | import, |
| 189 | /// Integer literal. | 369 | /// Integer literal that fits in a u64. Uses the int union value. |
| 190 | int, | 370 | int, |
| 191 | /// Convert an integer value to another integer type, asserting that the destination type | 371 | /// Convert an integer value to another integer type, asserting that the destination type |
| 192 | /// can hold the same mathematical value. | 372 | /// can hold the same mathematical value. |
| 373 | /// Uses the `pl_node` field. AST is the `@intCast` syntax. | ||
| 374 | /// Payload is `Bin` with lhs as the dest type, rhs the operand. | ||
| 193 | intcast, | 375 | intcast, |
| 194 | /// Make an integer type out of signedness and bit count. | 376 | /// Make an integer type out of signedness and bit count. |
| 377 | /// Payload is `int_type` | ||
| 195 | int_type, | 378 | int_type, |
| 379 | /// Convert an error type to `u16` | ||
| 380 | error_to_int, | ||
| 381 | /// Convert a `u16` to `anyerror` | ||
| 382 | int_to_error, | ||
| 196 | /// Return a boolean false if an optional is null. `x != null` | 383 | /// Return a boolean false if an optional is null. `x != null` |
| 384 | /// Uses the `un_node` field. | ||
| 197 | is_non_null, | 385 | is_non_null, |
| 198 | /// Return a boolean true if an optional is null. `x == null` | 386 | /// Return a boolean true if an optional is null. `x == null` |
| 387 | /// Uses the `un_node` field. | ||
| 199 | is_null, | 388 | is_null, |
| 200 | /// Return a boolean false if an optional is null. `x.* != null` | 389 | /// Return a boolean false if an optional is null. `x.* != null` |
| 390 | /// Uses the `un_node` field. | ||
| 201 | is_non_null_ptr, | 391 | is_non_null_ptr, |
| 202 | /// Return a boolean true if an optional is null. `x.* == null` | 392 | /// Return a boolean true if an optional is null. `x.* == null` |
| 393 | /// Uses the `un_node` field. | ||
| 203 | is_null_ptr, | 394 | is_null_ptr, |
| 204 | /// Return a boolean true if value is an error | 395 | /// Return a boolean true if value is an error |
| 396 | /// Uses the `un_node` field. | ||
| 205 | is_err, | 397 | is_err, |
| 206 | /// Return a boolean true if dereferenced pointer is an error | 398 | /// Return a boolean true if dereferenced pointer is an error |
| 399 | /// Uses the `un_node` field. | ||
| 207 | is_err_ptr, | 400 | is_err_ptr, |
| 208 | /// A labeled block of code that loops forever. At the end of the body it is implied | 401 | /// A labeled block of code that loops forever. At the end of the body will have either |
| 209 | /// to repeat; no explicit "repeat" instruction terminates loop bodies. | 402 | /// a `repeat` instruction or a `repeat_inline` instruction. |
| 403 | /// Uses the `pl_node` field. The AST node is either a for loop or while loop. | ||
| 404 | /// This ZIR instruction is needed because TZIR does not (yet?) match ZIR, and Sema | ||
| 405 | /// needs to emit more than 1 TZIR block for this instruction. | ||
| 406 | /// The payload is `Block`. | ||
| 210 | loop, | 407 | loop, |
| 408 | /// Sends runtime control flow back to the beginning of the current block. | ||
| 409 | /// Uses the `node` field. | ||
| 410 | repeat, | ||
| 411 | /// Sends comptime control flow back to the beginning of the current block. | ||
| 412 | /// Uses the `node` field. | ||
| 413 | repeat_inline, | ||
| 211 | /// Merge two error sets into one, `E1 || E2`. | 414 | /// Merge two error sets into one, `E1 || E2`. |
| 415 | /// Uses the `pl_node` field with payload `Bin`. | ||
| 212 | merge_error_sets, | 416 | merge_error_sets, |
| 213 | /// Ambiguously remainder division or modulus. If the computation would possibly have | 417 | /// Ambiguously remainder division or modulus. If the computation would possibly have |
| 214 | /// a different value depending on whether the operation is remainder division or modulus, | 418 | /// a different value depending on whether the operation is remainder division or modulus, |
| 215 | /// a compile error is emitted. Otherwise the computation is performed. | 419 | /// a compile error is emitted. Otherwise the computation is performed. |
| 420 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 216 | mod_rem, | 421 | mod_rem, |
| 217 | /// Arithmetic multiplication. Asserts no integer overflow. | 422 | /// Arithmetic multiplication. Asserts no integer overflow. |
| 423 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 218 | mul, | 424 | mul, |
| 219 | /// Twos complement wrapping integer multiplication. | 425 | /// Twos complement wrapping integer multiplication. |
| 426 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 220 | mulwrap, | 427 | mulwrap, |
| 221 | /// An await inside a nosuspend scope. | ||
| 222 | nosuspend_await, | ||
| 223 | /// Given a reference to a function and a parameter index, returns the | 428 | /// Given a reference to a function and a parameter index, returns the |
| 224 | /// type of the parameter. TODO what happens when the parameter is `anytype`? | 429 | /// type of the parameter. The only usage of this instruction is for the |
| 430 | /// result location of parameters of function calls. In the case of a function's | ||
| 431 | /// parameter type being `anytype`, it is the type coercion's job to detect this | ||
| 432 | /// scenario and skip the coercion, so that semantic analysis of this instruction | ||
| 433 | /// is not in a position where it must create an invalid type. | ||
| 434 | /// Uses the `param_type` union field. | ||
| 225 | param_type, | 435 | 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 | /// Convert a pointer to a `usize` integer. | 436 | /// Convert a pointer to a `usize` integer. |
| 437 | /// Uses the `un_node` field. The AST node is the builtin fn call node. | ||
| 230 | ptrtoint, | 438 | ptrtoint, |
| 231 | /// Turns an R-Value into a const L-Value. In other words, it takes a value, | 439 | /// Turns an R-Value into a const L-Value. In other words, it takes a value, |
| 232 | /// stores it in a memory location, and returns a const pointer to it. If the value | 440 | /// stores it in a memory location, and returns a const pointer to it. If the value |
| 233 | /// is `comptime`, the memory location is global static constant data. Otherwise, | 441 | /// is `comptime`, the memory location is global static constant data. Otherwise, |
| 234 | /// the memory location is in the stack frame, local to the scope containing the | 442 | /// the memory location is in the stack frame, local to the scope containing the |
| 235 | /// instruction. | 443 | /// instruction. |
| 444 | /// Uses the `un_tok` union field. | ||
| 236 | ref, | 445 | ref, |
| 237 | /// Resume an async function. | ||
| 238 | @"resume", | ||
| 239 | /// Obtains a pointer to the return value. | 446 | /// Obtains a pointer to the return value. |
| 447 | /// Uses the `node` union field. | ||
| 240 | ret_ptr, | 448 | ret_ptr, |
| 241 | /// Obtains the return type of the in-scope function. | 449 | /// Obtains the return type of the in-scope function. |
| 450 | /// Uses the `node` union field. | ||
| 242 | ret_type, | 451 | ret_type, |
| 243 | /// Sends control flow back to the function's callee. Takes an operand as the return value. | 452 | /// Sends control flow back to the function's callee. |
| 244 | @"return", | 453 | /// Includes an operand as the return value. |
| 245 | /// Same as `return` but there is no operand; the operand is implicitly the void value. | 454 | /// Includes an AST node source location. |
| 246 | return_void, | 455 | /// Uses the `un_node` union field. |
| 456 | ret_node, | ||
| 457 | /// Sends control flow back to the function's callee. | ||
| 458 | /// Includes an operand as the return value. | ||
| 459 | /// Includes a token source location. | ||
| 460 | /// Uses the `un_tok` union field. | ||
| 461 | ret_tok, | ||
| 462 | /// Same as `ret_tok` except the operand needs to get coerced to the function's | ||
| 463 | /// return type. | ||
| 464 | ret_coerce, | ||
| 247 | /// Changes the maximum number of backwards branches that compile-time | 465 | /// Changes the maximum number of backwards branches that compile-time |
| 248 | /// code execution can use before giving up and making a compile error. | 466 | /// code execution can use before giving up and making a compile error. |
| 467 | /// Uses the `un_node` union field. | ||
| 249 | set_eval_branch_quota, | 468 | set_eval_branch_quota, |
| 250 | /// Integer shift-left. Zeroes are shifted in from the right hand side. | 469 | /// Integer shift-left. Zeroes are shifted in from the right hand side. |
| 470 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 251 | shl, | 471 | shl, |
| 252 | /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type. | 472 | /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type. |
| 473 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 253 | shr, | 474 | shr, |
| 254 | /// Create a const pointer type with element type T. `*const T` | 475 | /// Create a pointer type that does not have a sentinel, alignment, or bit range specified. |
| 255 | single_const_ptr_type, | 476 | /// Uses the `ptr_type_simple` union field. |
| 256 | /// Create a mutable pointer type with element type T. `*T` | 477 | ptr_type_simple, |
| 257 | single_mut_ptr_type, | 478 | /// Create a pointer type which can have a sentinel, alignment, and/or bit range. |
| 258 | /// Create a const pointer type with element type T. `[*]const T` | 479 | /// Uses the `ptr_type` union field. |
| 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 | ||
| 271 | ptr_type, | 480 | ptr_type, |
| 272 | /// Each `store_to_inferred_ptr` puts the type of the stored value into a set, | 481 | /// Each `store_to_inferred_ptr` puts the type of the stored value into a set, |
| 273 | /// and then `resolve_inferred_alloc` triggers peer type resolution on the set. | 482 | /// and then `resolve_inferred_alloc` triggers peer type resolution on the set. |
| 274 | /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which | 483 | /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which |
| 275 | /// is the allocation that needs to have its type inferred. | 484 | /// is the allocation that needs to have its type inferred. |
| 485 | /// Uses the `un_node` field. The AST node is the var decl. | ||
| 276 | resolve_inferred_alloc, | 486 | resolve_inferred_alloc, |
| 277 | /// Slice operation `array_ptr[start..end:sentinel]` | 487 | /// Slice operation `lhs[rhs..]`. No sentinel and no end offset. |
| 278 | slice, | 488 | /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceStart`. |
| 279 | /// Slice operation with just start `lhs[rhs..]` | ||
| 280 | slice_start, | 489 | slice_start, |
| 281 | /// Write a value to a pointer. For loading, see `deref`. | 490 | /// Slice operation `array_ptr[start..end]`. No sentinel. |
| 491 | /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceEnd`. | ||
| 492 | slice_end, | ||
| 493 | /// Slice operation `array_ptr[start..end:sentinel]`. | ||
| 494 | /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`. | ||
| 495 | slice_sentinel, | ||
| 496 | /// Write a value to a pointer. For loading, see `load`. | ||
| 497 | /// Source location is assumed to be same as previous instruction. | ||
| 498 | /// Uses the `bin` union field. | ||
| 282 | store, | 499 | store, |
| 500 | /// Same as `store` except provides a source location. | ||
| 501 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 502 | store_node, | ||
| 283 | /// Same as `store` but the type of the value being stored will be used to infer | 503 | /// Same as `store` but the type of the value being stored will be used to infer |
| 284 | /// the block type. The LHS is the pointer to store to. | 504 | /// the block type. The LHS is the pointer to store to. |
| 505 | /// Uses the `bin` union field. | ||
| 285 | store_to_block_ptr, | 506 | store_to_block_ptr, |
| 286 | /// Same as `store` but the type of the value being stored will be used to infer | 507 | /// Same as `store` but the type of the value being stored will be used to infer |
| 287 | /// the pointer type. | 508 | /// the pointer type. |
| 509 | /// Uses the `bin` union field - Astgen.zig depends on the ability to change | ||
| 510 | /// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr` | ||
| 511 | /// without changing the data. | ||
| 288 | store_to_inferred_ptr, | 512 | store_to_inferred_ptr, |
| 289 | /// String Literal. Makes an anonymous Decl and then takes a pointer to it. | 513 | /// String Literal. Makes an anonymous Decl and then takes a pointer to it. |
| 514 | /// Uses the `str` union field. | ||
| 290 | str, | 515 | str, |
| 291 | /// Create a struct type. | ||
| 292 | struct_type, | ||
| 293 | /// Arithmetic subtraction. Asserts no integer overflow. | 516 | /// Arithmetic subtraction. Asserts no integer overflow. |
| 517 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 294 | sub, | 518 | sub, |
| 295 | /// Twos complement wrapping integer subtraction. | 519 | /// Twos complement wrapping integer subtraction. |
| 520 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 296 | subwrap, | 521 | subwrap, |
| 522 | /// Arithmetic negation. Asserts no integer overflow. | ||
| 523 | /// Same as sub with a lhs of 0, split into a separate instruction to save memory. | ||
| 524 | /// Uses `un_node`. | ||
| 525 | negate, | ||
| 526 | /// Twos complement wrapping integer negation. | ||
| 527 | /// Same as subwrap with a lhs of 0, split into a separate instruction to save memory. | ||
| 528 | /// Uses `un_node`. | ||
| 529 | negate_wrap, | ||
| 297 | /// Returns the type of a value. | 530 | /// Returns the type of a value. |
| 531 | /// Uses the `un_tok` field. | ||
| 298 | typeof, | 532 | typeof, |
| 299 | /// Is the builtin @TypeOf which returns the type after peertype resolution of one or more params | 533 | /// Given a value which is a pointer, returns the element type. |
| 534 | /// Uses the `un_node` field. | ||
| 535 | typeof_elem, | ||
| 536 | /// The builtin `@TypeOf` which returns the type after Peer Type Resolution | ||
| 537 | /// of one or more params. | ||
| 538 | /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`. | ||
| 300 | typeof_peer, | 539 | typeof_peer, |
| 301 | /// Asserts control-flow will not reach this instruction. Not safety checked - the compiler | 540 | /// Asserts control-flow will not reach this instruction (`unreachable`). |
| 302 | /// will assume the correctness of this instruction. | 541 | /// Uses the `unreachable` union field. |
| 303 | unreachable_unsafe, | 542 | @"unreachable", |
| 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, | ||
| 308 | /// Bitwise XOR. `^` | 543 | /// Bitwise XOR. `^` |
| 544 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 309 | xor, | 545 | xor, |
| 310 | /// Create an optional type '?T' | 546 | /// Create an optional type '?T' |
| 547 | /// Uses the `un_node` field. | ||
| 311 | optional_type, | 548 | optional_type, |
| 312 | /// Create an optional type '?T'. The operand is a pointer value. The optional type will | 549 | /// Create an optional type '?T'. The operand is a pointer value. The optional type will |
| 313 | /// be the type of the pointer element, wrapped in an optional. | 550 | /// be the type of the pointer element, wrapped in an optional. |
| 551 | /// Uses the `un_node` field. | ||
| 314 | optional_type_from_ptr_elem, | 552 | optional_type_from_ptr_elem, |
| 315 | /// Create a union type. | ||
| 316 | union_type, | ||
| 317 | /// ?T => T with safety. | 553 | /// ?T => T with safety. |
| 318 | /// Given an optional value, returns the payload value, with a safety check that | 554 | /// Given an optional value, returns the payload value, with a safety check that |
| 319 | /// the value is non-null. Used for `orelse`, `if` and `while`. | 555 | /// the value is non-null. Used for `orelse`, `if` and `while`. |
| 556 | /// Uses the `un_node` field. | ||
| 320 | optional_payload_safe, | 557 | optional_payload_safe, |
| 321 | /// ?T => T without safety. | 558 | /// ?T => T without safety. |
| 322 | /// Given an optional value, returns the payload value. No safety checks. | 559 | /// Given an optional value, returns the payload value. No safety checks. |
| 560 | /// Uses the `un_node` field. | ||
| 323 | optional_payload_unsafe, | 561 | optional_payload_unsafe, |
| 324 | /// *?T => *T with safety. | 562 | /// *?T => *T with safety. |
| 325 | /// Given a pointer to an optional value, returns a pointer to the payload value, | 563 | /// Given a pointer to an optional value, returns a pointer to the payload value, |
| 326 | /// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`. | 564 | /// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`. |
| 565 | /// Uses the `un_node` field. | ||
| 327 | optional_payload_safe_ptr, | 566 | optional_payload_safe_ptr, |
| 328 | /// *?T => *T without safety. | 567 | /// *?T => *T without safety. |
| 329 | /// Given a pointer to an optional value, returns a pointer to the payload value. | 568 | /// Given a pointer to an optional value, returns a pointer to the payload value. |
| 330 | /// No safety checks. | 569 | /// No safety checks. |
| 570 | /// Uses the `un_node` field. | ||
| 331 | optional_payload_unsafe_ptr, | 571 | optional_payload_unsafe_ptr, |
| 332 | /// E!T => T with safety. | 572 | /// E!T => T with safety. |
| 333 | /// Given an error union value, returns the payload value, with a safety check | 573 | /// Given an error union value, returns the payload value, with a safety check |
| 334 | /// that the value is not an error. Used for catch, if, and while. | 574 | /// that the value is not an error. Used for catch, if, and while. |
| 575 | /// Uses the `un_node` field. | ||
| 335 | err_union_payload_safe, | 576 | err_union_payload_safe, |
| 336 | /// E!T => T without safety. | 577 | /// E!T => T without safety. |
| 337 | /// Given an error union value, returns the payload value. No safety checks. | 578 | /// Given an error union value, returns the payload value. No safety checks. |
| 579 | /// Uses the `un_node` field. | ||
| 338 | err_union_payload_unsafe, | 580 | err_union_payload_unsafe, |
| 339 | /// *E!T => *T with safety. | 581 | /// *E!T => *T with safety. |
| 340 | /// Given a pointer to an error union value, returns a pointer to the payload value, | 582 | /// Given a pointer to an error union value, returns a pointer to the payload value, |
| 341 | /// with a safety check that the value is not an error. Used for catch, if, and while. | 583 | /// with a safety check that the value is not an error. Used for catch, if, and while. |
| 584 | /// Uses the `un_node` field. | ||
| 342 | err_union_payload_safe_ptr, | 585 | err_union_payload_safe_ptr, |
| 343 | /// *E!T => *T without safety. | 586 | /// *E!T => *T without safety. |
| 344 | /// Given a pointer to a error union value, returns a pointer to the payload value. | 587 | /// Given a pointer to a error union value, returns a pointer to the payload value. |
| 345 | /// No safety checks. | 588 | /// No safety checks. |
| 589 | /// Uses the `un_node` field. | ||
| 346 | err_union_payload_unsafe_ptr, | 590 | err_union_payload_unsafe_ptr, |
| 347 | /// E!T => E without safety. | 591 | /// E!T => E without safety. |
| 348 | /// Given an error union value, returns the error code. No safety checks. | 592 | /// Given an error union value, returns the error code. No safety checks. |
| 593 | /// Uses the `un_node` field. | ||
| 349 | err_union_code, | 594 | err_union_code, |
| 350 | /// *E!T => E without safety. | 595 | /// *E!T => E without safety. |
| 351 | /// Given a pointer to an error union value, returns the error code. No safety checks. | 596 | /// Given a pointer to an error union value, returns the error code. No safety checks. |
| 597 | /// Uses the `un_node` field. | ||
| 352 | err_union_code_ptr, | 598 | err_union_code_ptr, |
| 353 | /// Takes a *E!T and raises a compiler error if T != void | 599 | /// Takes a *E!T and raises a compiler error if T != void |
| 600 | /// Uses the `un_tok` field. | ||
| 354 | ensure_err_payload_void, | 601 | ensure_err_payload_void, |
| 355 | /// Create a enum literal, | 602 | /// An enum literal. Uses the `str_tok` union field. |
| 356 | enum_literal, | 603 | enum_literal, |
| 357 | /// Create an enum type. | 604 | /// An enum literal 8 or fewer bytes. No source location. |
| 358 | enum_type, | 605 | /// Uses the `small_str` field. |
| 359 | /// Does nothing; returns a void value. | 606 | enum_literal_small, |
| 360 | void_value, | 607 | /// A switch expression. Uses the `pl_node` union field. |
| 361 | /// Suspend an async function. | 608 | /// AST node is the switch, payload is `SwitchBlock`. |
| 362 | @"suspend", | 609 | /// All prongs of target handled. |
| 363 | /// Suspend an async function. | 610 | switch_block, |
| 364 | /// Same as .suspend but with a block. | 611 | /// Same as switch_block, except one or more prongs have multiple items. |
| 365 | suspend_block, | 612 | switch_block_multi, |
| 366 | /// A switch expression. | 613 | /// Same as switch_block, except has an else prong. |
| 367 | switchbr, | 614 | switch_block_else, |
| 368 | /// Same as `switchbr` but the target is a pointer to the value being switched on. | 615 | /// Same as switch_block_else, except one or more prongs have multiple items. |
| 369 | switchbr_ref, | 616 | switch_block_else_multi, |
| 370 | /// A range in a switch case, `lhs...rhs`. | 617 | /// Same as switch_block, except has an underscore prong. |
| 371 | /// Only checks that `lhs >= rhs` if they are ints, everything else is | 618 | switch_block_under, |
| 372 | /// validated by the .switch instruction. | 619 | /// Same as switch_block, except one or more prongs have multiple items. |
| 373 | switch_range, | 620 | switch_block_under_multi, |
| 374 | 621 | /// Same as `switch_block` but the target is a pointer to the value being switched on. | |
| 375 | pub fn Type(tag: Tag) type { | 622 | switch_block_ref, |
| 376 | return switch (tag) { | 623 | /// Same as `switch_block_multi` but the target is a pointer to the value being switched on. |
| 377 | .alloc_inferred, | 624 | switch_block_ref_multi, |
| 378 | .alloc_inferred_mut, | 625 | /// Same as `switch_block_else` but the target is a pointer to the value being switched on. |
| 379 | .breakpoint, | 626 | switch_block_ref_else, |
| 380 | .dbg_stmt, | 627 | /// Same as `switch_block_else_multi` but the target is a pointer to the |
| 381 | .return_void, | 628 | /// value being switched on. |
| 382 | .ret_ptr, | 629 | switch_block_ref_else_multi, |
| 383 | .ret_type, | 630 | /// Same as `switch_block_under` but the target is a pointer to the value |
| 384 | .unreachable_unsafe, | 631 | /// being switched on. |
| 385 | .unreachable_safe, | 632 | switch_block_ref_under, |
| 386 | .void_value, | 633 | /// Same as `switch_block_under_multi` but the target is a pointer to |
| 387 | .@"suspend", | 634 | /// the value being switched on. |
| 388 | => NoOp, | 635 | switch_block_ref_under_multi, |
| 389 | 636 | /// Produces the capture value for a switch prong. | |
| 390 | .alloc, | 637 | /// Uses the `switch_capture` field. |
| 391 | .alloc_mut, | 638 | switch_capture, |
| 392 | .bool_not, | 639 | /// Produces the capture value for a switch prong. |
| 393 | .compile_error, | 640 | /// Result is a pointer to the value. |
| 394 | .deref, | 641 | /// Uses the `switch_capture` field. |
| 395 | .@"return", | 642 | switch_capture_ref, |
| 396 | .is_null, | 643 | /// Produces the capture value for a switch prong. |
| 397 | .is_non_null, | 644 | /// The prong is one of the multi cases. |
| 398 | .is_null_ptr, | 645 | /// Uses the `switch_capture` field. |
| 399 | .is_non_null_ptr, | 646 | switch_capture_multi, |
| 400 | .is_err, | 647 | /// Produces the capture value for a switch prong. |
| 401 | .is_err_ptr, | 648 | /// The prong is one of the multi cases. |
| 402 | .ptrtoint, | 649 | /// Result is a pointer to the value. |
| 403 | .ensure_result_used, | 650 | /// Uses the `switch_capture` field. |
| 404 | .ensure_result_non_error, | 651 | switch_capture_multi_ref, |
| 405 | .bitcast_result_ptr, | 652 | /// Produces the capture value for the else/'_' switch prong. |
| 406 | .ref, | 653 | /// Uses the `switch_capture` field. |
| 407 | .bitcast_ref, | 654 | switch_capture_else, |
| 408 | .typeof, | 655 | /// Produces the capture value for the else/'_' switch prong. |
| 409 | .resolve_inferred_alloc, | 656 | /// Result is a pointer to the value. |
| 410 | .single_const_ptr_type, | 657 | /// Uses the `switch_capture` field. |
| 411 | .single_mut_ptr_type, | 658 | switch_capture_else_ref, |
| 412 | .many_const_ptr_type, | 659 | /// Given a set of `field_ptr` instructions, assumes they are all part of a struct |
| 413 | .many_mut_ptr_type, | 660 | /// initialization expression, and emits compile errors for duplicate fields |
| 414 | .c_const_ptr_type, | 661 | /// as well as missing fields, if applicable. |
| 415 | .c_mut_ptr_type, | 662 | /// Uses the `pl_node` field. Payload is `Block`. |
| 416 | .mut_slice_type, | 663 | validate_struct_init_ptr, |
| 417 | .const_slice_type, | 664 | /// A struct literal with a specified type, with no fields. |
| 418 | .optional_type, | 665 | /// Uses the `un_node` field. |
| 419 | .optional_type_from_ptr_elem, | 666 | struct_init_empty, |
| 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 | } | ||
| 527 | 667 | ||
| 528 | /// Returns whether the instruction is one of the control flow "noreturn" types. | 668 | /// Returns whether the instruction is one of the control flow "noreturn" types. |
| 529 | /// Function calls do not count. | 669 | /// Function calls do not count. |
| ... | @@ -540,23 +680,28 @@ pub const Inst = struct { | ... | @@ -540,23 +680,28 @@ pub const Inst = struct { |
| 540 | .array_type, | 680 | .array_type, |
| 541 | .array_type_sentinel, | 681 | .array_type_sentinel, |
| 542 | .indexable_ptr_len, | 682 | .indexable_ptr_len, |
| 543 | .arg, | ||
| 544 | .as, | 683 | .as, |
| 684 | .as_node, | ||
| 545 | .@"asm", | 685 | .@"asm", |
| 686 | .asm_volatile, | ||
| 546 | .bit_and, | 687 | .bit_and, |
| 547 | .bitcast, | 688 | .bitcast, |
| 548 | .bitcast_ref, | ||
| 549 | .bitcast_result_ptr, | 689 | .bitcast_result_ptr, |
| 550 | .bit_or, | 690 | .bit_or, |
| 551 | .block, | 691 | .block, |
| 552 | .block_flat, | 692 | .block_inline, |
| 553 | .block_comptime, | 693 | .loop, |
| 554 | .block_comptime_flat, | 694 | .bool_br_and, |
| 695 | .bool_br_or, | ||
| 555 | .bool_not, | 696 | .bool_not, |
| 556 | .bool_and, | 697 | .bool_and, |
| 557 | .bool_or, | 698 | .bool_or, |
| 558 | .breakpoint, | 699 | .breakpoint, |
| 559 | .call, | 700 | .call, |
| 701 | .call_chkused, | ||
| 702 | .call_compile_time, | ||
| 703 | .call_none, | ||
| 704 | .call_none_chkused, | ||
| 560 | .cmp_lt, | 705 | .cmp_lt, |
| 561 | .cmp_lte, | 706 | .cmp_lte, |
| 562 | .cmp_eq, | 707 | .cmp_eq, |
| ... | @@ -565,23 +710,28 @@ pub const Inst = struct { | ... | @@ -565,23 +710,28 @@ pub const Inst = struct { |
| 565 | .cmp_neq, | 710 | .cmp_neq, |
| 566 | .coerce_result_ptr, | 711 | .coerce_result_ptr, |
| 567 | .@"const", | 712 | .@"const", |
| 568 | .dbg_stmt, | 713 | .struct_decl, |
| 714 | .struct_decl_packed, | ||
| 715 | .struct_decl_extern, | ||
| 716 | .union_decl, | ||
| 717 | .enum_decl, | ||
| 718 | .opaque_decl, | ||
| 719 | .dbg_stmt_node, | ||
| 569 | .decl_ref, | 720 | .decl_ref, |
| 570 | .decl_ref_str, | ||
| 571 | .decl_val, | 721 | .decl_val, |
| 572 | .deref, | 722 | .load, |
| 573 | .div, | 723 | .div, |
| 574 | .elem_ptr, | 724 | .elem_ptr, |
| 575 | .elem_val, | 725 | .elem_val, |
| 726 | .elem_ptr_node, | ||
| 727 | .elem_val_node, | ||
| 576 | .ensure_result_used, | 728 | .ensure_result_used, |
| 577 | .ensure_result_non_error, | 729 | .ensure_result_non_error, |
| 578 | .@"export", | ||
| 579 | .floatcast, | 730 | .floatcast, |
| 580 | .field_ptr, | 731 | .field_ptr, |
| 581 | .field_val, | 732 | .field_val, |
| 582 | .field_ptr_named, | 733 | .field_ptr_named, |
| 583 | .field_val_named, | 734 | .field_val_named, |
| 584 | .@"fn", | ||
| 585 | .fn_type, | 735 | .fn_type, |
| 586 | .fn_type_var_args, | 736 | .fn_type_var_args, |
| 587 | .fn_type_cc, | 737 | .fn_type_cc, |
| ... | @@ -599,28 +749,23 @@ pub const Inst = struct { | ... | @@ -599,28 +749,23 @@ pub const Inst = struct { |
| 599 | .mul, | 749 | .mul, |
| 600 | .mulwrap, | 750 | .mulwrap, |
| 601 | .param_type, | 751 | .param_type, |
| 602 | .primitive, | ||
| 603 | .ptrtoint, | 752 | .ptrtoint, |
| 604 | .ref, | 753 | .ref, |
| 605 | .ret_ptr, | 754 | .ret_ptr, |
| 606 | .ret_type, | 755 | .ret_type, |
| 607 | .shl, | 756 | .shl, |
| 608 | .shr, | 757 | .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 | .store, | 758 | .store, |
| 759 | .store_node, | ||
| 618 | .store_to_block_ptr, | 760 | .store_to_block_ptr, |
| 619 | .store_to_inferred_ptr, | 761 | .store_to_inferred_ptr, |
| 620 | .str, | 762 | .str, |
| 621 | .sub, | 763 | .sub, |
| 622 | .subwrap, | 764 | .subwrap, |
| 765 | .negate, | ||
| 766 | .negate_wrap, | ||
| 623 | .typeof, | 767 | .typeof, |
| 768 | .typeof_elem, | ||
| 624 | .xor, | 769 | .xor, |
| 625 | .optional_type, | 770 | .optional_type, |
| 626 | .optional_type_from_ptr_elem, | 771 | .optional_type_from_ptr_elem, |
| ... | @@ -634,1436 +779,1566 @@ pub const Inst = struct { | ... | @@ -634,1436 +779,1566 @@ pub const Inst = struct { |
| 634 | .err_union_payload_unsafe_ptr, | 779 | .err_union_payload_unsafe_ptr, |
| 635 | .err_union_code, | 780 | .err_union_code, |
| 636 | .err_union_code_ptr, | 781 | .err_union_code_ptr, |
| 782 | .error_to_int, | ||
| 783 | .int_to_error, | ||
| 637 | .ptr_type, | 784 | .ptr_type, |
| 785 | .ptr_type_simple, | ||
| 638 | .ensure_err_payload_void, | 786 | .ensure_err_payload_void, |
| 639 | .enum_literal, | 787 | .enum_literal, |
| 788 | .enum_literal_small, | ||
| 640 | .merge_error_sets, | 789 | .merge_error_sets, |
| 641 | .anyframe_type, | ||
| 642 | .error_union_type, | 790 | .error_union_type, |
| 643 | .bit_not, | 791 | .bit_not, |
| 644 | .error_set, | ||
| 645 | .error_value, | 792 | .error_value, |
| 646 | .slice, | ||
| 647 | .slice_start, | 793 | .slice_start, |
| 794 | .slice_end, | ||
| 795 | .slice_sentinel, | ||
| 648 | .import, | 796 | .import, |
| 649 | .typeof_peer, | 797 | .typeof_peer, |
| 650 | .resolve_inferred_alloc, | 798 | .resolve_inferred_alloc, |
| 651 | .set_eval_branch_quota, | 799 | .set_eval_branch_quota, |
| 652 | .compile_log, | 800 | .compile_log, |
| 653 | .enum_type, | 801 | .elided, |
| 654 | .union_type, | 802 | .switch_capture, |
| 655 | .struct_type, | 803 | .switch_capture_ref, |
| 656 | .void_value, | 804 | .switch_capture_multi, |
| 657 | .switch_range, | 805 | .switch_capture_multi_ref, |
| 658 | .@"resume", | 806 | .switch_capture_else, |
| 659 | .@"await", | 807 | .switch_capture_else_ref, |
| 660 | .nosuspend_await, | 808 | .switch_block, |
| 809 | .switch_block_multi, | ||
| 810 | .switch_block_else, | ||
| 811 | .switch_block_else_multi, | ||
| 812 | .switch_block_under, | ||
| 813 | .switch_block_under_multi, | ||
| 814 | .switch_block_ref, | ||
| 815 | .switch_block_ref_multi, | ||
| 816 | .switch_block_ref_else, | ||
| 817 | .switch_block_ref_else_multi, | ||
| 818 | .switch_block_ref_under, | ||
| 819 | .switch_block_ref_under_multi, | ||
| 820 | .validate_struct_init_ptr, | ||
| 821 | .struct_init_empty, | ||
| 661 | => false, | 822 | => false, |
| 662 | 823 | ||
| 663 | .@"break", | 824 | .@"break", |
| 664 | .break_void, | 825 | .break_inline, |
| 665 | .condbr, | 826 | .condbr, |
| 827 | .condbr_inline, | ||
| 666 | .compile_error, | 828 | .compile_error, |
| 667 | .@"return", | 829 | .ret_node, |
| 668 | .return_void, | 830 | .ret_tok, |
| 669 | .unreachable_unsafe, | 831 | .ret_coerce, |
| 670 | .unreachable_safe, | 832 | .@"unreachable", |
| 671 | .loop, | 833 | .repeat, |
| 672 | .container_field_named, | 834 | .repeat_inline, |
| 673 | .container_field_typed, | ||
| 674 | .container_field, | ||
| 675 | .switchbr, | ||
| 676 | .switchbr_ref, | ||
| 677 | .@"suspend", | ||
| 678 | .suspend_block, | ||
| 679 | => true, | 835 | => true, |
| 680 | }; | 836 | }; |
| 681 | } | 837 | } |
| 682 | }; | 838 | }; |
| 683 | 839 | ||
| 684 | /// Prefer `castTag` to this. | 840 | /// The position of a ZIR instruction within the `Code` instructions array. |
| 685 | pub fn cast(base: *Inst, comptime T: type) ?*T { | 841 | pub const Index = u32; |
| 686 | if (@hasField(T, "base_tag")) { | 842 | |
| 687 | return base.castTag(T.base_tag); | 843 | /// A reference to a TypedValue, parameter of the current function, |
| 688 | } | 844 | /// or ZIR instruction. |
| 689 | inline for (@typeInfo(Tag).Enum.fields) |field| { | 845 | /// |
| 690 | const tag = @intToEnum(Tag, field.value); | 846 | /// If the Ref has a tag in this enum, it refers to a TypedValue which may be |
| 691 | if (base.tag == tag) { | 847 | /// retrieved with Ref.toTypedValue(). |
| 692 | if (T == tag.Type()) { | 848 | /// |
| 693 | return @fieldParentPtr(T, "base", base); | 849 | /// If the value of a Ref does not have a tag, it referes to either a parameter |
| 694 | } | 850 | /// of the current function or a ZIR instruction. |
| 695 | return null; | 851 | /// |
| 696 | } | 852 | /// The first values after the the last tag refer to parameters which may be |
| 697 | } | 853 | /// derived by subtracting typed_value_map.len. |
| 698 | unreachable; | 854 | /// |
| 699 | } | 855 | /// All further values refer to ZIR instructions which may be derived by |
| 700 | 856 | /// subtracting typed_value_map.len and the number of parameters. | |
| 701 | pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() { | 857 | /// |
| 702 | if (base.tag == tag) { | 858 | /// When adding a tag to this enum, consider adding a corresponding entry to |
| 703 | return @fieldParentPtr(tag.Type(), "base", base); | 859 | /// `simple_types` in astgen. |
| 704 | } | 860 | /// |
| 705 | return null; | 861 | /// The tag type is specified so that it is safe to bitcast between `[]u32` |
| 706 | } | 862 | /// and `[]Ref`. |
| 707 | 863 | pub const Ref = enum(u32) { | |
| 708 | pub const NoOp = struct { | 864 | /// This Ref does not correspond to any ZIR instruction or constant |
| 709 | base: Inst, | 865 | /// value and may instead be used as a sentinel to indicate null. |
| 866 | none, | ||
| 867 | |||
| 868 | u8_type, | ||
| 869 | i8_type, | ||
| 870 | u16_type, | ||
| 871 | i16_type, | ||
| 872 | u32_type, | ||
| 873 | i32_type, | ||
| 874 | u64_type, | ||
| 875 | i64_type, | ||
| 876 | usize_type, | ||
| 877 | isize_type, | ||
| 878 | c_short_type, | ||
| 879 | c_ushort_type, | ||
| 880 | c_int_type, | ||
| 881 | c_uint_type, | ||
| 882 | c_long_type, | ||
| 883 | c_ulong_type, | ||
| 884 | c_longlong_type, | ||
| 885 | c_ulonglong_type, | ||
| 886 | c_longdouble_type, | ||
| 887 | f16_type, | ||
| 888 | f32_type, | ||
| 889 | f64_type, | ||
| 890 | f128_type, | ||
| 891 | c_void_type, | ||
| 892 | bool_type, | ||
| 893 | void_type, | ||
| 894 | type_type, | ||
| 895 | anyerror_type, | ||
| 896 | comptime_int_type, | ||
| 897 | comptime_float_type, | ||
| 898 | noreturn_type, | ||
| 899 | null_type, | ||
| 900 | undefined_type, | ||
| 901 | fn_noreturn_no_args_type, | ||
| 902 | fn_void_no_args_type, | ||
| 903 | fn_naked_noreturn_no_args_type, | ||
| 904 | fn_ccc_void_no_args_type, | ||
| 905 | single_const_pointer_to_comptime_int_type, | ||
| 906 | const_slice_u8_type, | ||
| 907 | enum_literal_type, | ||
| 908 | |||
| 909 | /// `undefined` (untyped) | ||
| 910 | undef, | ||
| 911 | /// `0` (comptime_int) | ||
| 912 | zero, | ||
| 913 | /// `1` (comptime_int) | ||
| 914 | one, | ||
| 915 | /// `{}` | ||
| 916 | void_value, | ||
| 917 | /// `unreachable` (noreturn type) | ||
| 918 | unreachable_value, | ||
| 919 | /// `null` (untyped) | ||
| 920 | null_value, | ||
| 921 | /// `true` | ||
| 922 | bool_true, | ||
| 923 | /// `false` | ||
| 924 | bool_false, | ||
| 925 | /// `.{}` (untyped) | ||
| 926 | empty_struct, | ||
| 927 | /// `0` (usize) | ||
| 928 | zero_usize, | ||
| 929 | /// `1` (usize) | ||
| 930 | one_usize, | ||
| 931 | |||
| 932 | _, | ||
| 933 | |||
| 934 | pub const typed_value_map = std.enums.directEnumArray(Ref, TypedValue, 0, .{ | ||
| 935 | .none = undefined, | ||
| 936 | |||
| 937 | .u8_type = .{ | ||
| 938 | .ty = Type.initTag(.type), | ||
| 939 | .val = Value.initTag(.u8_type), | ||
| 940 | }, | ||
| 941 | .i8_type = .{ | ||
| 942 | .ty = Type.initTag(.type), | ||
| 943 | .val = Value.initTag(.i8_type), | ||
| 944 | }, | ||
| 945 | .u16_type = .{ | ||
| 946 | .ty = Type.initTag(.type), | ||
| 947 | .val = Value.initTag(.u16_type), | ||
| 948 | }, | ||
| 949 | .i16_type = .{ | ||
| 950 | .ty = Type.initTag(.type), | ||
| 951 | .val = Value.initTag(.i16_type), | ||
| 952 | }, | ||
| 953 | .u32_type = .{ | ||
| 954 | .ty = Type.initTag(.type), | ||
| 955 | .val = Value.initTag(.u32_type), | ||
| 956 | }, | ||
| 957 | .i32_type = .{ | ||
| 958 | .ty = Type.initTag(.type), | ||
| 959 | .val = Value.initTag(.i32_type), | ||
| 960 | }, | ||
| 961 | .u64_type = .{ | ||
| 962 | .ty = Type.initTag(.type), | ||
| 963 | .val = Value.initTag(.u64_type), | ||
| 964 | }, | ||
| 965 | .i64_type = .{ | ||
| 966 | .ty = Type.initTag(.type), | ||
| 967 | .val = Value.initTag(.i64_type), | ||
| 968 | }, | ||
| 969 | .usize_type = .{ | ||
| 970 | .ty = Type.initTag(.type), | ||
| 971 | .val = Value.initTag(.usize_type), | ||
| 972 | }, | ||
| 973 | .isize_type = .{ | ||
| 974 | .ty = Type.initTag(.type), | ||
| 975 | .val = Value.initTag(.isize_type), | ||
| 976 | }, | ||
| 977 | .c_short_type = .{ | ||
| 978 | .ty = Type.initTag(.type), | ||
| 979 | .val = Value.initTag(.c_short_type), | ||
| 980 | }, | ||
| 981 | .c_ushort_type = .{ | ||
| 982 | .ty = Type.initTag(.type), | ||
| 983 | .val = Value.initTag(.c_ushort_type), | ||
| 984 | }, | ||
| 985 | .c_int_type = .{ | ||
| 986 | .ty = Type.initTag(.type), | ||
| 987 | .val = Value.initTag(.c_int_type), | ||
| 988 | }, | ||
| 989 | .c_uint_type = .{ | ||
| 990 | .ty = Type.initTag(.type), | ||
| 991 | .val = Value.initTag(.c_uint_type), | ||
| 992 | }, | ||
| 993 | .c_long_type = .{ | ||
| 994 | .ty = Type.initTag(.type), | ||
| 995 | .val = Value.initTag(.c_long_type), | ||
| 996 | }, | ||
| 997 | .c_ulong_type = .{ | ||
| 998 | .ty = Type.initTag(.type), | ||
| 999 | .val = Value.initTag(.c_ulong_type), | ||
| 1000 | }, | ||
| 1001 | .c_longlong_type = .{ | ||
| 1002 | .ty = Type.initTag(.type), | ||
| 1003 | .val = Value.initTag(.c_longlong_type), | ||
| 1004 | }, | ||
| 1005 | .c_ulonglong_type = .{ | ||
| 1006 | .ty = Type.initTag(.type), | ||
| 1007 | .val = Value.initTag(.c_ulonglong_type), | ||
| 1008 | }, | ||
| 1009 | .c_longdouble_type = .{ | ||
| 1010 | .ty = Type.initTag(.type), | ||
| 1011 | .val = Value.initTag(.c_longdouble_type), | ||
| 1012 | }, | ||
| 1013 | .f16_type = .{ | ||
| 1014 | .ty = Type.initTag(.type), | ||
| 1015 | .val = Value.initTag(.f16_type), | ||
| 1016 | }, | ||
| 1017 | .f32_type = .{ | ||
| 1018 | .ty = Type.initTag(.type), | ||
| 1019 | .val = Value.initTag(.f32_type), | ||
| 1020 | }, | ||
| 1021 | .f64_type = .{ | ||
| 1022 | .ty = Type.initTag(.type), | ||
| 1023 | .val = Value.initTag(.f64_type), | ||
| 1024 | }, | ||
| 1025 | .f128_type = .{ | ||
| 1026 | .ty = Type.initTag(.type), | ||
| 1027 | .val = Value.initTag(.f128_type), | ||
| 1028 | }, | ||
| 1029 | .c_void_type = .{ | ||
| 1030 | .ty = Type.initTag(.type), | ||
| 1031 | .val = Value.initTag(.c_void_type), | ||
| 1032 | }, | ||
| 1033 | .bool_type = .{ | ||
| 1034 | .ty = Type.initTag(.type), | ||
| 1035 | .val = Value.initTag(.bool_type), | ||
| 1036 | }, | ||
| 1037 | .void_type = .{ | ||
| 1038 | .ty = Type.initTag(.type), | ||
| 1039 | .val = Value.initTag(.void_type), | ||
| 1040 | }, | ||
| 1041 | .type_type = .{ | ||
| 1042 | .ty = Type.initTag(.type), | ||
| 1043 | .val = Value.initTag(.type_type), | ||
| 1044 | }, | ||
| 1045 | .anyerror_type = .{ | ||
| 1046 | .ty = Type.initTag(.type), | ||
| 1047 | .val = Value.initTag(.anyerror_type), | ||
| 1048 | }, | ||
| 1049 | .comptime_int_type = .{ | ||
| 1050 | .ty = Type.initTag(.type), | ||
| 1051 | .val = Value.initTag(.comptime_int_type), | ||
| 1052 | }, | ||
| 1053 | .comptime_float_type = .{ | ||
| 1054 | .ty = Type.initTag(.type), | ||
| 1055 | .val = Value.initTag(.comptime_float_type), | ||
| 1056 | }, | ||
| 1057 | .noreturn_type = .{ | ||
| 1058 | .ty = Type.initTag(.type), | ||
| 1059 | .val = Value.initTag(.noreturn_type), | ||
| 1060 | }, | ||
| 1061 | .null_type = .{ | ||
| 1062 | .ty = Type.initTag(.type), | ||
| 1063 | .val = Value.initTag(.null_type), | ||
| 1064 | }, | ||
| 1065 | .undefined_type = .{ | ||
| 1066 | .ty = Type.initTag(.type), | ||
| 1067 | .val = Value.initTag(.undefined_type), | ||
| 1068 | }, | ||
| 1069 | .fn_noreturn_no_args_type = .{ | ||
| 1070 | .ty = Type.initTag(.type), | ||
| 1071 | .val = Value.initTag(.fn_noreturn_no_args_type), | ||
| 1072 | }, | ||
| 1073 | .fn_void_no_args_type = .{ | ||
| 1074 | .ty = Type.initTag(.type), | ||
| 1075 | .val = Value.initTag(.fn_void_no_args_type), | ||
| 1076 | }, | ||
| 1077 | .fn_naked_noreturn_no_args_type = .{ | ||
| 1078 | .ty = Type.initTag(.type), | ||
| 1079 | .val = Value.initTag(.fn_naked_noreturn_no_args_type), | ||
| 1080 | }, | ||
| 1081 | .fn_ccc_void_no_args_type = .{ | ||
| 1082 | .ty = Type.initTag(.type), | ||
| 1083 | .val = Value.initTag(.fn_ccc_void_no_args_type), | ||
| 1084 | }, | ||
| 1085 | .single_const_pointer_to_comptime_int_type = .{ | ||
| 1086 | .ty = Type.initTag(.type), | ||
| 1087 | .val = Value.initTag(.single_const_pointer_to_comptime_int_type), | ||
| 1088 | }, | ||
| 1089 | .const_slice_u8_type = .{ | ||
| 1090 | .ty = Type.initTag(.type), | ||
| 1091 | .val = Value.initTag(.const_slice_u8_type), | ||
| 1092 | }, | ||
| 1093 | .enum_literal_type = .{ | ||
| 1094 | .ty = Type.initTag(.type), | ||
| 1095 | .val = Value.initTag(.enum_literal_type), | ||
| 1096 | }, | ||
| 710 | 1097 | ||
| 711 | positionals: struct {}, | 1098 | .undef = .{ |
| 712 | kw_args: struct {}, | 1099 | .ty = Type.initTag(.@"undefined"), |
| 1100 | .val = Value.initTag(.undef), | ||
| 1101 | }, | ||
| 1102 | .zero = .{ | ||
| 1103 | .ty = Type.initTag(.comptime_int), | ||
| 1104 | .val = Value.initTag(.zero), | ||
| 1105 | }, | ||
| 1106 | .zero_usize = .{ | ||
| 1107 | .ty = Type.initTag(.usize), | ||
| 1108 | .val = Value.initTag(.zero), | ||
| 1109 | }, | ||
| 1110 | .one = .{ | ||
| 1111 | .ty = Type.initTag(.comptime_int), | ||
| 1112 | .val = Value.initTag(.one), | ||
| 1113 | }, | ||
| 1114 | .one_usize = .{ | ||
| 1115 | .ty = Type.initTag(.usize), | ||
| 1116 | .val = Value.initTag(.one), | ||
| 1117 | }, | ||
| 1118 | .void_value = .{ | ||
| 1119 | .ty = Type.initTag(.void), | ||
| 1120 | .val = Value.initTag(.void_value), | ||
| 1121 | }, | ||
| 1122 | .unreachable_value = .{ | ||
| 1123 | .ty = Type.initTag(.noreturn), | ||
| 1124 | .val = Value.initTag(.unreachable_value), | ||
| 1125 | }, | ||
| 1126 | .null_value = .{ | ||
| 1127 | .ty = Type.initTag(.@"null"), | ||
| 1128 | .val = Value.initTag(.null_value), | ||
| 1129 | }, | ||
| 1130 | .bool_true = .{ | ||
| 1131 | .ty = Type.initTag(.bool), | ||
| 1132 | .val = Value.initTag(.bool_true), | ||
| 1133 | }, | ||
| 1134 | .bool_false = .{ | ||
| 1135 | .ty = Type.initTag(.bool), | ||
| 1136 | .val = Value.initTag(.bool_false), | ||
| 1137 | }, | ||
| 1138 | .empty_struct = .{ | ||
| 1139 | .ty = Type.initTag(.empty_struct_literal), | ||
| 1140 | .val = Value.initTag(.empty_struct_value), | ||
| 1141 | }, | ||
| 1142 | }); | ||
| 713 | }; | 1143 | }; |
| 714 | 1144 | ||
| 715 | pub const UnOp = struct { | 1145 | /// All instructions have an 8-byte payload, which is contained within |
| 716 | base: Inst, | 1146 | /// this union. `Tag` determines which union field is active, as well as |
| 717 | 1147 | /// how to interpret the data within. | |
| 718 | positionals: struct { | 1148 | pub const Data = union { |
| 719 | operand: *Inst, | 1149 | /// Used for unary operators, with an AST node source location. |
| 1150 | un_node: struct { | ||
| 1151 | /// Offset from Decl AST node index. | ||
| 1152 | src_node: i32, | ||
| 1153 | /// The meaning of this operand depends on the corresponding `Tag`. | ||
| 1154 | operand: Ref, | ||
| 1155 | |||
| 1156 | pub fn src(self: @This()) LazySrcLoc { | ||
| 1157 | return .{ .node_offset = self.src_node }; | ||
| 1158 | } | ||
| 720 | }, | 1159 | }, |
| 721 | kw_args: struct {}, | 1160 | /// Used for unary operators, with a token source location. |
| 722 | }; | 1161 | un_tok: struct { |
| 723 | 1162 | /// Offset from Decl AST token index. | |
| 724 | pub const BinOp = struct { | 1163 | src_tok: ast.TokenIndex, |
| 725 | base: Inst, | 1164 | /// The meaning of this operand depends on the corresponding `Tag`. |
| 726 | 1165 | operand: Ref, | |
| 727 | positionals: struct { | 1166 | |
| 728 | lhs: *Inst, | 1167 | pub fn src(self: @This()) LazySrcLoc { |
| 729 | rhs: *Inst, | 1168 | return .{ .token_offset = self.src_tok }; |
| 1169 | } | ||
| 730 | }, | 1170 | }, |
| 731 | kw_args: struct {}, | 1171 | pl_node: struct { |
| 732 | }; | 1172 | /// Offset from Decl AST node index. |
| 733 | 1173 | /// `Tag` determines which kind of AST node this points to. | |
| 734 | pub const Arg = struct { | 1174 | src_node: i32, |
| 735 | pub const base_tag = Tag.arg; | 1175 | /// index into extra. |
| 736 | base: Inst, | 1176 | /// `Tag` determines what lives there. |
| 1177 | payload_index: u32, | ||
| 1178 | |||
| 1179 | pub fn src(self: @This()) LazySrcLoc { | ||
| 1180 | return .{ .node_offset = self.src_node }; | ||
| 1181 | } | ||
| 1182 | }, | ||
| 1183 | bin: Bin, | ||
| 1184 | @"const": *TypedValue, | ||
| 1185 | /// For strings which may contain null bytes. | ||
| 1186 | str: struct { | ||
| 1187 | /// Offset into `string_bytes`. | ||
| 1188 | start: u32, | ||
| 1189 | /// Number of bytes in the string. | ||
| 1190 | len: u32, | ||
| 1191 | |||
| 1192 | pub fn get(self: @This(), code: Code) []const u8 { | ||
| 1193 | return code.string_bytes[self.start..][0..self.len]; | ||
| 1194 | } | ||
| 1195 | }, | ||
| 1196 | /// Strings 8 or fewer bytes which may not contain null bytes. | ||
| 1197 | small_str: struct { | ||
| 1198 | bytes: [8]u8, | ||
| 1199 | |||
| 1200 | pub fn get(self: @This()) []const u8 { | ||
| 1201 | const end = for (self.bytes) |byte, i| { | ||
| 1202 | if (byte == 0) break i; | ||
| 1203 | } else self.bytes.len; | ||
| 1204 | return self.bytes[0..end]; | ||
| 1205 | } | ||
| 1206 | }, | ||
| 1207 | str_tok: struct { | ||
| 1208 | /// Offset into `string_bytes`. Null-terminated. | ||
| 1209 | start: u32, | ||
| 1210 | /// Offset from Decl AST token index. | ||
| 1211 | src_tok: u32, | ||
| 1212 | |||
| 1213 | pub fn get(self: @This(), code: Code) [:0]const u8 { | ||
| 1214 | return code.nullTerminatedString(self.start); | ||
| 1215 | } | ||
| 737 | 1216 | ||
| 738 | positionals: struct { | 1217 | pub fn src(self: @This()) LazySrcLoc { |
| 739 | /// This exists to be passed to the arg TZIR instruction, which | 1218 | return .{ .token_offset = self.src_tok }; |
| 740 | /// needs it for debug info. | 1219 | } |
| 741 | name: []const u8, | 1220 | }, |
| 1221 | /// Offset from Decl AST token index. | ||
| 1222 | tok: ast.TokenIndex, | ||
| 1223 | /// Offset from Decl AST node index. | ||
| 1224 | node: i32, | ||
| 1225 | int: u64, | ||
| 1226 | array_type_sentinel: struct { | ||
| 1227 | len: Ref, | ||
| 1228 | /// index into extra, points to an `ArrayTypeSentinel` | ||
| 1229 | payload_index: u32, | ||
| 1230 | }, | ||
| 1231 | ptr_type_simple: struct { | ||
| 1232 | is_allowzero: bool, | ||
| 1233 | is_mutable: bool, | ||
| 1234 | is_volatile: bool, | ||
| 1235 | size: std.builtin.TypeInfo.Pointer.Size, | ||
| 1236 | elem_type: Ref, | ||
| 1237 | }, | ||
| 1238 | ptr_type: struct { | ||
| 1239 | flags: packed struct { | ||
| 1240 | is_allowzero: bool, | ||
| 1241 | is_mutable: bool, | ||
| 1242 | is_volatile: bool, | ||
| 1243 | has_sentinel: bool, | ||
| 1244 | has_align: bool, | ||
| 1245 | has_bit_range: bool, | ||
| 1246 | _: u2 = undefined, | ||
| 1247 | }, | ||
| 1248 | size: std.builtin.TypeInfo.Pointer.Size, | ||
| 1249 | /// Index into extra. See `PtrType`. | ||
| 1250 | payload_index: u32, | ||
| 1251 | }, | ||
| 1252 | int_type: struct { | ||
| 1253 | /// Offset from Decl AST node index. | ||
| 1254 | /// `Tag` determines which kind of AST node this points to. | ||
| 1255 | src_node: i32, | ||
| 1256 | signedness: std.builtin.Signedness, | ||
| 1257 | bit_count: u16, | ||
| 1258 | |||
| 1259 | pub fn src(self: @This()) LazySrcLoc { | ||
| 1260 | return .{ .node_offset = self.src_node }; | ||
| 1261 | } | ||
| 1262 | }, | ||
| 1263 | bool_br: struct { | ||
| 1264 | lhs: Ref, | ||
| 1265 | /// Points to a `Block`. | ||
| 1266 | payload_index: u32, | ||
| 1267 | }, | ||
| 1268 | param_type: struct { | ||
| 1269 | callee: Ref, | ||
| 1270 | param_index: u32, | ||
| 1271 | }, | ||
| 1272 | @"unreachable": struct { | ||
| 1273 | /// Offset from Decl AST node index. | ||
| 1274 | /// `Tag` determines which kind of AST node this points to. | ||
| 1275 | src_node: i32, | ||
| 1276 | /// `false`: Not safety checked - the compiler will assume the | ||
| 1277 | /// correctness of this instruction. | ||
| 1278 | /// `true`: In safety-checked modes, this will generate a call | ||
| 1279 | /// to the panic function unless it can be proven unreachable by the compiler. | ||
| 1280 | safety: bool, | ||
| 1281 | |||
| 1282 | pub fn src(self: @This()) LazySrcLoc { | ||
| 1283 | return .{ .node_offset = self.src_node }; | ||
| 1284 | } | ||
| 1285 | }, | ||
| 1286 | @"break": struct { | ||
| 1287 | block_inst: Index, | ||
| 1288 | operand: Ref, | ||
| 1289 | }, | ||
| 1290 | switch_capture: struct { | ||
| 1291 | switch_inst: Index, | ||
| 1292 | prong_index: u32, | ||
| 742 | }, | 1293 | }, |
| 743 | kw_args: struct {}, | ||
| 744 | }; | ||
| 745 | 1294 | ||
| 746 | pub const Block = struct { | 1295 | // Make sure we don't accidentally add a field to make this union |
| 747 | pub const base_tag = Tag.block; | 1296 | // bigger than expected. Note that in Debug builds, Zig is allowed |
| 748 | base: Inst, | 1297 | // to insert a secret field for safety checks. |
| 1298 | comptime { | ||
| 1299 | if (std.builtin.mode != .Debug) { | ||
| 1300 | assert(@sizeOf(Data) == 8); | ||
| 1301 | } | ||
| 1302 | } | ||
| 1303 | }; | ||
| 749 | 1304 | ||
| 750 | positionals: struct { | 1305 | /// Stored in extra. Trailing is: |
| 751 | body: Body, | 1306 | /// * output_name: u32 // index into string_bytes (null terminated) if output is present |
| 752 | }, | 1307 | /// * arg: Ref // for every args_len. |
| 753 | kw_args: struct {}, | 1308 | /// * constraint: u32 // index into string_bytes (null terminated) for every args_len. |
| 1309 | /// * clobber: u32 // index into string_bytes (null terminated) for every clobbers_len. | ||
| 1310 | pub const Asm = struct { | ||
| 1311 | asm_source: Ref, | ||
| 1312 | return_type: Ref, | ||
| 1313 | /// May be omitted. | ||
| 1314 | output: Ref, | ||
| 1315 | args_len: u32, | ||
| 1316 | clobbers_len: u32, | ||
| 754 | }; | 1317 | }; |
| 755 | 1318 | ||
| 756 | pub const Break = struct { | 1319 | /// This data is stored inside extra, with trailing parameter type indexes |
| 757 | pub const base_tag = Tag.@"break"; | 1320 | /// according to `param_types_len`. |
| 758 | base: Inst, | 1321 | /// Each param type is a `Ref`. |
| 1322 | pub const FnTypeCc = struct { | ||
| 1323 | return_type: Ref, | ||
| 1324 | cc: Ref, | ||
| 1325 | param_types_len: u32, | ||
| 1326 | }; | ||
| 759 | 1327 | ||
| 760 | positionals: struct { | 1328 | /// This data is stored inside extra, with trailing parameter type indexes |
| 761 | block: *Block, | 1329 | /// according to `param_types_len`. |
| 762 | operand: *Inst, | 1330 | /// Each param type is a `Ref`. |
| 763 | }, | 1331 | pub const FnType = struct { |
| 764 | kw_args: struct {}, | 1332 | return_type: Ref, |
| 1333 | param_types_len: u32, | ||
| 765 | }; | 1334 | }; |
| 766 | 1335 | ||
| 767 | pub const BreakVoid = struct { | 1336 | /// This data is stored inside extra, with trailing operands according to `operands_len`. |
| 768 | pub const base_tag = Tag.break_void; | 1337 | /// Each operand is a `Ref`. |
| 769 | base: Inst, | 1338 | pub const MultiOp = struct { |
| 1339 | operands_len: u32, | ||
| 1340 | }; | ||
| 770 | 1341 | ||
| 771 | positionals: struct { | 1342 | /// This data is stored inside extra, with trailing operands according to `body_len`. |
| 772 | block: *Block, | 1343 | /// Each operand is an `Index`. |
| 773 | }, | 1344 | pub const Block = struct { |
| 774 | kw_args: struct {}, | 1345 | body_len: u32, |
| 775 | }; | 1346 | }; |
| 776 | 1347 | ||
| 777 | // TODO break this into multiple call instructions to avoid paying the cost | 1348 | /// Stored inside extra, with trailing arguments according to `args_len`. |
| 778 | // of the calling convention field most of the time. | 1349 | /// Each argument is a `Ref`. |
| 779 | pub const Call = struct { | 1350 | pub const Call = struct { |
| 780 | pub const base_tag = Tag.call; | 1351 | callee: Ref, |
| 781 | base: Inst, | 1352 | args_len: u32, |
| 782 | |||
| 783 | positionals: struct { | ||
| 784 | func: *Inst, | ||
| 785 | args: []*Inst, | ||
| 786 | modifier: std.builtin.CallOptions.Modifier = .auto, | ||
| 787 | }, | ||
| 788 | kw_args: struct {}, | ||
| 789 | }; | 1353 | }; |
| 790 | 1354 | ||
| 791 | pub const DeclRef = struct { | 1355 | /// This data is stored inside extra, with two sets of trailing `Ref`: |
| 792 | pub const base_tag = Tag.decl_ref; | 1356 | /// * 0. the then body, according to `then_body_len`. |
| 793 | base: Inst, | 1357 | /// * 1. the else body, according to `else_body_len`. |
| 794 | 1358 | pub const CondBr = struct { | |
| 795 | positionals: struct { | 1359 | condition: Ref, |
| 796 | decl: *IrModule.Decl, | 1360 | then_body_len: u32, |
| 797 | }, | 1361 | else_body_len: u32, |
| 798 | kw_args: struct {}, | ||
| 799 | }; | 1362 | }; |
| 800 | 1363 | ||
| 801 | pub const DeclRefStr = struct { | 1364 | /// Stored in extra. Depending on the flags in Data, there will be up to 4 |
| 802 | pub const base_tag = Tag.decl_ref_str; | 1365 | /// trailing Ref fields: |
| 803 | base: Inst, | 1366 | /// 0. sentinel: Ref // if `has_sentinel` flag is set |
| 804 | 1367 | /// 1. align: Ref // if `has_align` flag is set | |
| 805 | positionals: struct { | 1368 | /// 2. bit_start: Ref // if `has_bit_range` flag is set |
| 806 | name: *Inst, | 1369 | /// 3. bit_end: Ref // if `has_bit_range` flag is set |
| 807 | }, | 1370 | pub const PtrType = struct { |
| 808 | kw_args: struct {}, | 1371 | elem_type: Ref, |
| 809 | }; | 1372 | }; |
| 810 | 1373 | ||
| 811 | pub const DeclVal = struct { | 1374 | pub const ArrayTypeSentinel = struct { |
| 812 | pub const base_tag = Tag.decl_val; | 1375 | sentinel: Ref, |
| 813 | base: Inst, | 1376 | elem_type: Ref, |
| 814 | |||
| 815 | positionals: struct { | ||
| 816 | decl: *IrModule.Decl, | ||
| 817 | }, | ||
| 818 | kw_args: struct {}, | ||
| 819 | }; | 1377 | }; |
| 820 | 1378 | ||
| 821 | pub const CompileLog = struct { | 1379 | pub const SliceStart = struct { |
| 822 | pub const base_tag = Tag.compile_log; | 1380 | lhs: Ref, |
| 823 | base: Inst, | 1381 | start: Ref, |
| 824 | |||
| 825 | positionals: struct { | ||
| 826 | to_log: []*Inst, | ||
| 827 | }, | ||
| 828 | kw_args: struct {}, | ||
| 829 | }; | 1382 | }; |
| 830 | 1383 | ||
| 831 | pub const Const = struct { | 1384 | pub const SliceEnd = struct { |
| 832 | pub const base_tag = Tag.@"const"; | 1385 | lhs: Ref, |
| 833 | base: Inst, | 1386 | start: Ref, |
| 834 | 1387 | end: Ref, | |
| 835 | positionals: struct { | ||
| 836 | typed_value: TypedValue, | ||
| 837 | }, | ||
| 838 | kw_args: struct {}, | ||
| 839 | }; | 1388 | }; |
| 840 | 1389 | ||
| 841 | pub const Str = struct { | 1390 | pub const SliceSentinel = struct { |
| 842 | pub const base_tag = Tag.str; | 1391 | lhs: Ref, |
| 843 | base: Inst, | 1392 | start: Ref, |
| 844 | 1393 | end: Ref, | |
| 845 | positionals: struct { | 1394 | sentinel: Ref, |
| 846 | bytes: []const u8, | ||
| 847 | }, | ||
| 848 | kw_args: struct {}, | ||
| 849 | }; | 1395 | }; |
| 850 | 1396 | ||
| 851 | pub const Int = struct { | 1397 | /// The meaning of these operands depends on the corresponding `Tag`. |
| 852 | pub const base_tag = Tag.int; | 1398 | pub const Bin = struct { |
| 853 | base: Inst, | 1399 | lhs: Ref, |
| 854 | 1400 | rhs: Ref, | |
| 855 | positionals: struct { | ||
| 856 | int: BigIntConst, | ||
| 857 | }, | ||
| 858 | kw_args: struct {}, | ||
| 859 | }; | 1401 | }; |
| 860 | 1402 | ||
| 861 | pub const Loop = struct { | 1403 | /// This form is supported when there are no ranges, and exactly 1 item per block. |
| 862 | pub const base_tag = Tag.loop; | 1404 | /// Depending on zir tag and len fields, extra fields trail |
| 863 | base: Inst, | 1405 | /// this one in the extra array. |
| 1406 | /// 0. else_body { // If the tag has "_else" or "_under" in it. | ||
| 1407 | /// body_len: u32, | ||
| 1408 | /// body member Index for every body_len | ||
| 1409 | /// } | ||
| 1410 | /// 1. cases: { | ||
| 1411 | /// item: Ref, | ||
| 1412 | /// body_len: u32, | ||
| 1413 | /// body member Index for every body_len | ||
| 1414 | /// } for every cases_len | ||
| 1415 | pub const SwitchBlock = struct { | ||
| 1416 | operand: Ref, | ||
| 1417 | cases_len: u32, | ||
| 1418 | }; | ||
| 864 | 1419 | ||
| 865 | positionals: struct { | 1420 | /// This form is required when there exists a block which has more than one item, |
| 866 | body: Body, | 1421 | /// or a range. |
| 867 | }, | 1422 | /// Depending on zir tag and len fields, extra fields trail |
| 868 | kw_args: struct {}, | 1423 | /// this one in the extra array. |
| 1424 | /// 0. else_body { // If the tag has "_else" or "_under" in it. | ||
| 1425 | /// body_len: u32, | ||
| 1426 | /// body member Index for every body_len | ||
| 1427 | /// } | ||
| 1428 | /// 1. scalar_cases: { // for every scalar_cases_len | ||
| 1429 | /// item: Ref, | ||
| 1430 | /// body_len: u32, | ||
| 1431 | /// body member Index for every body_len | ||
| 1432 | /// } | ||
| 1433 | /// 2. multi_cases: { // for every multi_cases_len | ||
| 1434 | /// items_len: u32, | ||
| 1435 | /// ranges_len: u32, | ||
| 1436 | /// body_len: u32, | ||
| 1437 | /// item: Ref // for every items_len | ||
| 1438 | /// ranges: { // for every ranges_len | ||
| 1439 | /// item_first: Ref, | ||
| 1440 | /// item_last: Ref, | ||
| 1441 | /// } | ||
| 1442 | /// body member Index for every body_len | ||
| 1443 | /// } | ||
| 1444 | pub const SwitchBlockMulti = struct { | ||
| 1445 | operand: Ref, | ||
| 1446 | scalar_cases_len: u32, | ||
| 1447 | multi_cases_len: u32, | ||
| 869 | }; | 1448 | }; |
| 870 | 1449 | ||
| 871 | pub const Field = struct { | 1450 | pub const Field = struct { |
| 872 | base: Inst, | 1451 | lhs: Ref, |
| 873 | 1452 | /// Offset into `string_bytes`. | |
| 874 | positionals: struct { | 1453 | field_name_start: u32, |
| 875 | object: *Inst, | ||
| 876 | field_name: []const u8, | ||
| 877 | }, | ||
| 878 | kw_args: struct {}, | ||
| 879 | }; | 1454 | }; |
| 880 | 1455 | ||
| 881 | pub const FieldNamed = struct { | 1456 | pub const FieldNamed = struct { |
| 882 | base: Inst, | 1457 | lhs: Ref, |
| 883 | 1458 | field_name: Ref, | |
| 884 | positionals: struct { | ||
| 885 | object: *Inst, | ||
| 886 | field_name: *Inst, | ||
| 887 | }, | ||
| 888 | kw_args: struct {}, | ||
| 889 | }; | 1459 | }; |
| 890 | 1460 | ||
| 891 | pub const Asm = struct { | 1461 | pub const As = struct { |
| 892 | pub const base_tag = Tag.@"asm"; | 1462 | dest_type: Ref, |
| 893 | base: Inst, | 1463 | operand: Ref, |
| 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 | }, | ||
| 906 | }; | 1464 | }; |
| 907 | 1465 | ||
| 908 | pub const Fn = struct { | 1466 | /// Trailing: |
| 909 | pub const base_tag = Tag.@"fn"; | 1467 | /// 0. has_bits: u32 // for every 16 fields |
| 910 | base: Inst, | 1468 | /// - sets of 2 bits: |
| 911 | 1469 | /// 0b0X: whether corresponding field has an align expression | |
| 912 | positionals: struct { | 1470 | /// 0bX0: whether corresponding field has a default expression |
| 913 | fn_type: *Inst, | 1471 | /// 1. fields: { // for every fields_len |
| 914 | body: Body, | 1472 | /// field_name: u32, |
| 915 | }, | 1473 | /// field_type: Ref, |
| 916 | kw_args: struct {}, | 1474 | /// align: Ref, // if corresponding bit is set |
| 1475 | /// default_value: Ref, // if corresponding bit is set | ||
| 1476 | /// } | ||
| 1477 | pub const StructDecl = struct { | ||
| 1478 | fields_len: u32, | ||
| 917 | }; | 1479 | }; |
| 918 | 1480 | ||
| 919 | pub const FnType = struct { | 1481 | /// Trailing: |
| 920 | pub const base_tag = Tag.fn_type; | 1482 | /// 0. has_bits: u32 // for every 32 fields |
| 921 | base: Inst, | 1483 | /// - the bit is whether corresponding field has an value expression |
| 922 | 1484 | /// 1. field_name: u32 // for every field: null terminated string index | |
| 923 | positionals: struct { | 1485 | /// 2. value: Ref // for every field for which corresponding bit is set |
| 924 | param_types: []*Inst, | 1486 | pub const EnumDecl = struct { |
| 925 | return_type: *Inst, | 1487 | /// Can be `Ref.none`. |
| 926 | }, | 1488 | tag_type: Ref, |
| 927 | kw_args: struct {}, | 1489 | fields_len: u32, |
| 928 | }; | 1490 | }; |
| 929 | 1491 | ||
| 930 | pub const FnTypeCc = struct { | 1492 | /// Trailing: |
| 931 | pub const base_tag = Tag.fn_type_cc; | 1493 | /// 0. has_bits: u32 // for every 10 fields (+1) |
| 932 | base: Inst, | 1494 | /// - first bit is special: set if and only if auto enum tag is enabled. |
| 933 | 1495 | /// - sets of 3 bits: | |
| 934 | positionals: struct { | 1496 | /// 0b00X: whether corresponding field has a type expression |
| 935 | param_types: []*Inst, | 1497 | /// 0b0X0: whether corresponding field has a align expression |
| 936 | return_type: *Inst, | 1498 | /// 0bX00: whether corresponding field has a tag value expression |
| 937 | cc: *Inst, | 1499 | /// 1. field_name: u32 // for every field: null terminated string index |
| 938 | }, | 1500 | /// 2. opt_exprs // Ref for every field for which corresponding bit is set |
| 939 | kw_args: struct {}, | 1501 | /// - interleaved. type if present, align if present, tag value if present. |
| 1502 | pub const UnionDecl = struct { | ||
| 1503 | /// Can be `Ref.none`. | ||
| 1504 | tag_type: Ref, | ||
| 1505 | fields_len: u32, | ||
| 940 | }; | 1506 | }; |
| 1507 | }; | ||
| 941 | 1508 | ||
| 942 | pub const IntType = struct { | 1509 | pub const SpecialProng = enum { none, @"else", under }; |
| 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 | 1510 | ||
| 953 | pub const Export = struct { | 1511 | const Writer = struct { |
| 954 | pub const base_tag = Tag.@"export"; | 1512 | gpa: *Allocator, |
| 955 | base: Inst, | 1513 | arena: *Allocator, |
| 1514 | scope: *Module.Scope, | ||
| 1515 | code: Code, | ||
| 1516 | indent: usize, | ||
| 1517 | param_count: usize, | ||
| 956 | 1518 | ||
| 957 | positionals: struct { | 1519 | fn writeInstToStream( |
| 958 | symbol_name: *Inst, | 1520 | self: *Writer, |
| 959 | decl_name: []const u8, | 1521 | stream: anytype, |
| 960 | }, | 1522 | inst: Inst.Index, |
| 961 | kw_args: struct {}, | 1523 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 962 | }; | 1524 | const tags = self.code.instructions.items(.tag); |
| 1525 | const tag = tags[inst]; | ||
| 1526 | try stream.print("= {s}(", .{@tagName(tags[inst])}); | ||
| 1527 | switch (tag) { | ||
| 1528 | .array_type, | ||
| 1529 | .as, | ||
| 1530 | .coerce_result_ptr, | ||
| 1531 | .elem_ptr, | ||
| 1532 | .elem_val, | ||
| 1533 | .intcast, | ||
| 1534 | .store, | ||
| 1535 | .store_to_block_ptr, | ||
| 1536 | => try self.writeBin(stream, inst), | ||
| 1537 | |||
| 1538 | .alloc, | ||
| 1539 | .alloc_mut, | ||
| 1540 | .alloc_inferred, | ||
| 1541 | .alloc_inferred_mut, | ||
| 1542 | .indexable_ptr_len, | ||
| 1543 | .bit_not, | ||
| 1544 | .bool_not, | ||
| 1545 | .negate, | ||
| 1546 | .negate_wrap, | ||
| 1547 | .call_none, | ||
| 1548 | .call_none_chkused, | ||
| 1549 | .compile_error, | ||
| 1550 | .load, | ||
| 1551 | .ensure_result_used, | ||
| 1552 | .ensure_result_non_error, | ||
| 1553 | .import, | ||
| 1554 | .ptrtoint, | ||
| 1555 | .ret_node, | ||
| 1556 | .set_eval_branch_quota, | ||
| 1557 | .resolve_inferred_alloc, | ||
| 1558 | .optional_type, | ||
| 1559 | .optional_type_from_ptr_elem, | ||
| 1560 | .optional_payload_safe, | ||
| 1561 | .optional_payload_unsafe, | ||
| 1562 | .optional_payload_safe_ptr, | ||
| 1563 | .optional_payload_unsafe_ptr, | ||
| 1564 | .err_union_payload_safe, | ||
| 1565 | .err_union_payload_unsafe, | ||
| 1566 | .err_union_payload_safe_ptr, | ||
| 1567 | .err_union_payload_unsafe_ptr, | ||
| 1568 | .err_union_code, | ||
| 1569 | .err_union_code_ptr, | ||
| 1570 | .int_to_error, | ||
| 1571 | .error_to_int, | ||
| 1572 | .is_non_null, | ||
| 1573 | .is_null, | ||
| 1574 | .is_non_null_ptr, | ||
| 1575 | .is_null_ptr, | ||
| 1576 | .is_err, | ||
| 1577 | .is_err_ptr, | ||
| 1578 | .typeof, | ||
| 1579 | .typeof_elem, | ||
| 1580 | .struct_init_empty, | ||
| 1581 | => try self.writeUnNode(stream, inst), | ||
| 1582 | |||
| 1583 | .ref, | ||
| 1584 | .ret_tok, | ||
| 1585 | .ret_coerce, | ||
| 1586 | .ensure_err_payload_void, | ||
| 1587 | => try self.writeUnTok(stream, inst), | ||
| 1588 | |||
| 1589 | .bool_br_and, | ||
| 1590 | .bool_br_or, | ||
| 1591 | => try self.writeBoolBr(stream, inst), | ||
| 1592 | |||
| 1593 | .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), | ||
| 1594 | .@"const" => try self.writeConst(stream, inst), | ||
| 1595 | .param_type => try self.writeParamType(stream, inst), | ||
| 1596 | .ptr_type_simple => try self.writePtrTypeSimple(stream, inst), | ||
| 1597 | .ptr_type => try self.writePtrType(stream, inst), | ||
| 1598 | .int => try self.writeInt(stream, inst), | ||
| 1599 | .str => try self.writeStr(stream, inst), | ||
| 1600 | .elided => try stream.writeAll(")"), | ||
| 1601 | .int_type => try self.writeIntType(stream, inst), | ||
| 1602 | |||
| 1603 | .@"break", | ||
| 1604 | .break_inline, | ||
| 1605 | => try self.writeBreak(stream, inst), | ||
| 1606 | |||
| 1607 | .@"asm", | ||
| 1608 | .asm_volatile, | ||
| 1609 | .elem_ptr_node, | ||
| 1610 | .elem_val_node, | ||
| 1611 | .field_ptr_named, | ||
| 1612 | .field_val_named, | ||
| 1613 | .floatcast, | ||
| 1614 | .slice_start, | ||
| 1615 | .slice_end, | ||
| 1616 | .slice_sentinel, | ||
| 1617 | .union_decl, | ||
| 1618 | .enum_decl, | ||
| 1619 | => try self.writePlNode(stream, inst), | ||
| 1620 | |||
| 1621 | .add, | ||
| 1622 | .addwrap, | ||
| 1623 | .array_cat, | ||
| 1624 | .array_mul, | ||
| 1625 | .mul, | ||
| 1626 | .mulwrap, | ||
| 1627 | .sub, | ||
| 1628 | .subwrap, | ||
| 1629 | .bool_and, | ||
| 1630 | .bool_or, | ||
| 1631 | .cmp_lt, | ||
| 1632 | .cmp_lte, | ||
| 1633 | .cmp_eq, | ||
| 1634 | .cmp_gte, | ||
| 1635 | .cmp_gt, | ||
| 1636 | .cmp_neq, | ||
| 1637 | .div, | ||
| 1638 | .mod_rem, | ||
| 1639 | .shl, | ||
| 1640 | .shr, | ||
| 1641 | .xor, | ||
| 1642 | .store_node, | ||
| 1643 | .error_union_type, | ||
| 1644 | .merge_error_sets, | ||
| 1645 | .bit_and, | ||
| 1646 | .bit_or, | ||
| 1647 | => try self.writePlNodeBin(stream, inst), | ||
| 1648 | |||
| 1649 | .call, | ||
| 1650 | .call_chkused, | ||
| 1651 | .call_compile_time, | ||
| 1652 | => try self.writePlNodeCall(stream, inst), | ||
| 1653 | |||
| 1654 | .block, | ||
| 1655 | .block_inline, | ||
| 1656 | .loop, | ||
| 1657 | .validate_struct_init_ptr, | ||
| 1658 | => try self.writePlNodeBlock(stream, inst), | ||
| 1659 | |||
| 1660 | .condbr, | ||
| 1661 | .condbr_inline, | ||
| 1662 | => try self.writePlNodeCondBr(stream, inst), | ||
| 1663 | |||
| 1664 | .struct_decl, | ||
| 1665 | .struct_decl_packed, | ||
| 1666 | .struct_decl_extern, | ||
| 1667 | => try self.writeStructDecl(stream, inst), | ||
| 1668 | |||
| 1669 | .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none), | ||
| 1670 | .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), | ||
| 1671 | .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under), | ||
| 1672 | .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none), | ||
| 1673 | .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), | ||
| 1674 | .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under), | ||
| 1675 | |||
| 1676 | .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none), | ||
| 1677 | .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"), | ||
| 1678 | .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under), | ||
| 1679 | .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none), | ||
| 1680 | .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"), | ||
| 1681 | .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under), | ||
| 1682 | |||
| 1683 | .compile_log, | ||
| 1684 | .typeof_peer, | ||
| 1685 | => try self.writePlNodeMultiOp(stream, inst), | ||
| 1686 | |||
| 1687 | .decl_ref, | ||
| 1688 | .decl_val, | ||
| 1689 | => try self.writePlNodeDecl(stream, inst), | ||
| 1690 | |||
| 1691 | .field_ptr, | ||
| 1692 | .field_val, | ||
| 1693 | => try self.writePlNodeField(stream, inst), | ||
| 1694 | |||
| 1695 | .as_node => try self.writeAs(stream, inst), | ||
| 1696 | |||
| 1697 | .breakpoint, | ||
| 1698 | .opaque_decl, | ||
| 1699 | .dbg_stmt_node, | ||
| 1700 | .ret_ptr, | ||
| 1701 | .ret_type, | ||
| 1702 | .repeat, | ||
| 1703 | .repeat_inline, | ||
| 1704 | => try self.writeNode(stream, inst), | ||
| 1705 | |||
| 1706 | .error_value, | ||
| 1707 | .enum_literal, | ||
| 1708 | => try self.writeStrTok(stream, inst), | ||
| 1709 | |||
| 1710 | .fn_type => try self.writeFnType(stream, inst, false), | ||
| 1711 | .fn_type_cc => try self.writeFnTypeCc(stream, inst, false), | ||
| 1712 | .fn_type_var_args => try self.writeFnType(stream, inst, true), | ||
| 1713 | .fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true), | ||
| 1714 | |||
| 1715 | .@"unreachable" => try self.writeUnreachable(stream, inst), | ||
| 1716 | |||
| 1717 | .enum_literal_small => try self.writeSmallStr(stream, inst), | ||
| 1718 | |||
| 1719 | .switch_capture, | ||
| 1720 | .switch_capture_ref, | ||
| 1721 | .switch_capture_multi, | ||
| 1722 | .switch_capture_multi_ref, | ||
| 1723 | .switch_capture_else, | ||
| 1724 | .switch_capture_else_ref, | ||
| 1725 | => try self.writeSwitchCapture(stream, inst), | ||
| 1726 | |||
| 1727 | .bitcast, | ||
| 1728 | .bitcast_result_ptr, | ||
| 1729 | .store_to_inferred_ptr, | ||
| 1730 | => try stream.writeAll("TODO)"), | ||
| 1731 | } | ||
| 1732 | } | ||
| 963 | 1733 | ||
| 964 | pub const ParamType = struct { | 1734 | fn writeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 965 | pub const base_tag = Tag.param_type; | 1735 | const inst_data = self.code.instructions.items(.data)[inst].bin; |
| 966 | base: Inst, | 1736 | try self.writeInstRef(stream, inst_data.lhs); |
| 967 | 1737 | try stream.writeAll(", "); | |
| 968 | positionals: struct { | 1738 | try self.writeInstRef(stream, inst_data.rhs); |
| 969 | func: *Inst, | 1739 | try stream.writeByte(')'); |
| 970 | arg_index: usize, | 1740 | } |
| 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 | 1741 | ||
| 1142 | positionals: struct { | 1742 | fn writeUnNode( |
| 1143 | name: []const u8, | 1743 | self: *Writer, |
| 1144 | }, | 1744 | stream: anytype, |
| 1145 | kw_args: struct {}, | 1745 | inst: Inst.Index, |
| 1146 | }; | 1746 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1747 | const inst_data = self.code.instructions.items(.data)[inst].un_node; | ||
| 1748 | try self.writeInstRef(stream, inst_data.operand); | ||
| 1749 | try stream.writeAll(") "); | ||
| 1750 | try self.writeSrc(stream, inst_data.src()); | ||
| 1751 | } | ||
| 1147 | 1752 | ||
| 1148 | pub const Slice = struct { | 1753 | fn writeUnTok( |
| 1149 | pub const base_tag = Tag.slice; | 1754 | self: *Writer, |
| 1150 | base: Inst, | 1755 | stream: anytype, |
| 1756 | inst: Inst.Index, | ||
| 1757 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | ||
| 1758 | const inst_data = self.code.instructions.items(.data)[inst].un_tok; | ||
| 1759 | try self.writeInstRef(stream, inst_data.operand); | ||
| 1760 | try stream.writeAll(") "); | ||
| 1761 | try self.writeSrc(stream, inst_data.src()); | ||
| 1762 | } | ||
| 1151 | 1763 | ||
| 1152 | positionals: struct { | 1764 | fn writeArrayTypeSentinel( |
| 1153 | array_ptr: *Inst, | 1765 | self: *Writer, |
| 1154 | start: *Inst, | 1766 | stream: anytype, |
| 1155 | }, | 1767 | inst: Inst.Index, |
| 1156 | kw_args: struct { | 1768 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1157 | end: ?*Inst = null, | 1769 | const inst_data = self.code.instructions.items(.data)[inst].array_type_sentinel; |
| 1158 | sentinel: ?*Inst = null, | 1770 | try stream.writeAll("TODO)"); |
| 1159 | }, | 1771 | } |
| 1160 | }; | ||
| 1161 | 1772 | ||
| 1162 | pub const TypeOfPeer = struct { | 1773 | fn writeConst( |
| 1163 | pub const base_tag = .typeof_peer; | 1774 | self: *Writer, |
| 1164 | base: Inst, | 1775 | stream: anytype, |
| 1165 | positionals: struct { | 1776 | inst: Inst.Index, |
| 1166 | items: []*Inst, | 1777 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1167 | }, | 1778 | const inst_data = self.code.instructions.items(.data)[inst].@"const"; |
| 1168 | kw_args: struct {}, | 1779 | try stream.writeAll("TODO)"); |
| 1169 | }; | 1780 | } |
| 1170 | 1781 | ||
| 1171 | pub const ContainerFieldNamed = struct { | 1782 | fn writeParamType( |
| 1172 | pub const base_tag = Tag.container_field_named; | 1783 | self: *Writer, |
| 1173 | base: Inst, | 1784 | stream: anytype, |
| 1785 | inst: Inst.Index, | ||
| 1786 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | ||
| 1787 | const inst_data = self.code.instructions.items(.data)[inst].param_type; | ||
| 1788 | try self.writeInstRef(stream, inst_data.callee); | ||
| 1789 | try stream.print(", {d})", .{inst_data.param_index}); | ||
| 1790 | } | ||
| 1174 | 1791 | ||
| 1175 | positionals: struct { | 1792 | fn writePtrTypeSimple( |
| 1176 | bytes: []const u8, | 1793 | self: *Writer, |
| 1177 | }, | 1794 | stream: anytype, |
| 1178 | kw_args: struct {}, | 1795 | inst: Inst.Index, |
| 1179 | }; | 1796 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1797 | const inst_data = self.code.instructions.items(.data)[inst].ptr_type_simple; | ||
| 1798 | try stream.writeAll("TODO)"); | ||
| 1799 | } | ||
| 1180 | 1800 | ||
| 1181 | pub const ContainerFieldTyped = struct { | 1801 | fn writePtrType( |
| 1182 | pub const base_tag = Tag.container_field_typed; | 1802 | self: *Writer, |
| 1183 | base: Inst, | 1803 | stream: anytype, |
| 1804 | inst: Inst.Index, | ||
| 1805 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | ||
| 1806 | const inst_data = self.code.instructions.items(.data)[inst].ptr_type; | ||
| 1807 | try stream.writeAll("TODO)"); | ||
| 1808 | } | ||
| 1184 | 1809 | ||
| 1185 | positionals: struct { | 1810 | fn writeInt( |
| 1186 | bytes: []const u8, | 1811 | self: *Writer, |
| 1187 | ty: *Inst, | 1812 | stream: anytype, |
| 1188 | }, | 1813 | inst: Inst.Index, |
| 1189 | kw_args: struct {}, | 1814 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1190 | }; | 1815 | const inst_data = self.code.instructions.items(.data)[inst].int; |
| 1816 | try stream.print("{d})", .{inst_data}); | ||
| 1817 | } | ||
| 1191 | 1818 | ||
| 1192 | pub const ContainerField = struct { | 1819 | fn writeStr( |
| 1193 | pub const base_tag = Tag.container_field; | 1820 | self: *Writer, |
| 1194 | base: Inst, | 1821 | stream: anytype, |
| 1822 | inst: Inst.Index, | ||
| 1823 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | ||
| 1824 | const inst_data = self.code.instructions.items(.data)[inst].str; | ||
| 1825 | const str = inst_data.get(self.code); | ||
| 1826 | try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)}); | ||
| 1827 | } | ||
| 1195 | 1828 | ||
| 1196 | positionals: struct { | 1829 | fn writePlNode( |
| 1197 | bytes: []const u8, | 1830 | self: *Writer, |
| 1198 | }, | 1831 | stream: anytype, |
| 1199 | kw_args: struct { | 1832 | inst: Inst.Index, |
| 1200 | ty: ?*Inst = null, | 1833 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1201 | init: ?*Inst = null, | 1834 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1202 | alignment: ?*Inst = null, | 1835 | try stream.writeAll("TODO) "); |
| 1203 | is_comptime: bool = false, | 1836 | try self.writeSrc(stream, inst_data.src()); |
| 1204 | }, | 1837 | } |
| 1205 | }; | ||
| 1206 | 1838 | ||
| 1207 | pub const EnumType = struct { | 1839 | fn writePlNodeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1208 | pub const base_tag = Tag.enum_type; | 1840 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1209 | base: Inst, | 1841 | const extra = self.code.extraData(Inst.Bin, inst_data.payload_index).data; |
| 1842 | try self.writeInstRef(stream, extra.lhs); | ||
| 1843 | try stream.writeAll(", "); | ||
| 1844 | try self.writeInstRef(stream, extra.rhs); | ||
| 1845 | try stream.writeAll(") "); | ||
| 1846 | try self.writeSrc(stream, inst_data.src()); | ||
| 1847 | } | ||
| 1210 | 1848 | ||
| 1211 | positionals: struct { | 1849 | fn writePlNodeCall(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1212 | fields: []*Inst, | 1850 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1213 | }, | 1851 | const extra = self.code.extraData(Inst.Call, inst_data.payload_index); |
| 1214 | kw_args: struct { | 1852 | const args = self.code.refSlice(extra.end, extra.data.args_len); |
| 1215 | tag_type: ?*Inst = null, | ||
| 1216 | layout: std.builtin.TypeInfo.ContainerLayout = .Auto, | ||
| 1217 | }, | ||
| 1218 | }; | ||
| 1219 | 1853 | ||
| 1220 | pub const StructType = struct { | 1854 | try self.writeInstRef(stream, extra.data.callee); |
| 1221 | pub const base_tag = Tag.struct_type; | 1855 | try stream.writeAll(", ["); |
| 1222 | base: Inst, | 1856 | for (args) |arg, i| { |
| 1857 | if (i != 0) try stream.writeAll(", "); | ||
| 1858 | try self.writeInstRef(stream, arg); | ||
| 1859 | } | ||
| 1860 | try stream.writeAll("]) "); | ||
| 1861 | try self.writeSrc(stream, inst_data.src()); | ||
| 1862 | } | ||
| 1223 | 1863 | ||
| 1224 | positionals: struct { | 1864 | fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1225 | fields: []*Inst, | 1865 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1226 | }, | 1866 | const extra = self.code.extraData(Inst.Block, inst_data.payload_index); |
| 1227 | kw_args: struct { | 1867 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; |
| 1228 | layout: std.builtin.TypeInfo.ContainerLayout = .Auto, | 1868 | try stream.writeAll("{\n"); |
| 1229 | }, | 1869 | self.indent += 2; |
| 1230 | }; | 1870 | try self.writeBody(stream, body); |
| 1871 | self.indent -= 2; | ||
| 1872 | try stream.writeByteNTimes(' ', self.indent); | ||
| 1873 | try stream.writeAll("}) "); | ||
| 1874 | try self.writeSrc(stream, inst_data.src()); | ||
| 1875 | } | ||
| 1231 | 1876 | ||
| 1232 | pub const UnionType = struct { | 1877 | fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1233 | pub const base_tag = Tag.union_type; | 1878 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1234 | base: Inst, | 1879 | const extra = self.code.extraData(Inst.CondBr, inst_data.payload_index); |
| 1880 | const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; | ||
| 1881 | const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | ||
| 1882 | try self.writeInstRef(stream, extra.data.condition); | ||
| 1883 | try stream.writeAll(", {\n"); | ||
| 1884 | self.indent += 2; | ||
| 1885 | try self.writeBody(stream, then_body); | ||
| 1886 | self.indent -= 2; | ||
| 1887 | try stream.writeByteNTimes(' ', self.indent); | ||
| 1888 | try stream.writeAll("}, {\n"); | ||
| 1889 | self.indent += 2; | ||
| 1890 | try self.writeBody(stream, else_body); | ||
| 1891 | self.indent -= 2; | ||
| 1892 | try stream.writeByteNTimes(' ', self.indent); | ||
| 1893 | try stream.writeAll("}) "); | ||
| 1894 | try self.writeSrc(stream, inst_data.src()); | ||
| 1895 | } | ||
| 1235 | 1896 | ||
| 1236 | positionals: struct { | 1897 | fn writeStructDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1237 | fields: []*Inst, | 1898 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1238 | }, | 1899 | const extra = self.code.extraData(Inst.StructDecl, inst_data.payload_index); |
| 1239 | kw_args: struct { | 1900 | const fields_len = extra.data.fields_len; |
| 1240 | init_inst: ?*Inst = null, | 1901 | const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable; |
| 1241 | has_enum_token: bool, | 1902 | |
| 1242 | layout: std.builtin.TypeInfo.ContainerLayout = .Auto, | 1903 | try stream.writeAll("{\n"); |
| 1243 | }, | 1904 | self.indent += 2; |
| 1244 | }; | 1905 | |
| 1906 | var field_index: usize = extra.end + bit_bags_count; | ||
| 1907 | var bit_bag_index: usize = extra.end; | ||
| 1908 | var cur_bit_bag: u32 = undefined; | ||
| 1909 | var field_i: u32 = 0; | ||
| 1910 | while (field_i < fields_len) : (field_i += 1) { | ||
| 1911 | if (field_i % 16 == 0) { | ||
| 1912 | cur_bit_bag = self.code.extra[bit_bag_index]; | ||
| 1913 | bit_bag_index += 1; | ||
| 1914 | } | ||
| 1915 | const has_align = @truncate(u1, cur_bit_bag) != 0; | ||
| 1916 | cur_bit_bag >>= 1; | ||
| 1917 | const has_default = @truncate(u1, cur_bit_bag) != 0; | ||
| 1918 | cur_bit_bag >>= 1; | ||
| 1919 | |||
| 1920 | const field_name = self.code.nullTerminatedString(self.code.extra[field_index]); | ||
| 1921 | field_index += 1; | ||
| 1922 | const field_type = @intToEnum(Inst.Ref, self.code.extra[field_index]); | ||
| 1923 | field_index += 1; | ||
| 1924 | |||
| 1925 | try stream.writeByteNTimes(' ', self.indent); | ||
| 1926 | try stream.print("{}: ", .{std.zig.fmtId(field_name)}); | ||
| 1927 | try self.writeInstRef(stream, field_type); | ||
| 1928 | |||
| 1929 | if (has_align) { | ||
| 1930 | const align_ref = @intToEnum(Inst.Ref, self.code.extra[field_index]); | ||
| 1931 | field_index += 1; | ||
| 1932 | |||
| 1933 | try stream.writeAll(" align("); | ||
| 1934 | try self.writeInstRef(stream, align_ref); | ||
| 1935 | try stream.writeAll(")"); | ||
| 1936 | } | ||
| 1937 | if (has_default) { | ||
| 1938 | const default_ref = @intToEnum(Inst.Ref, self.code.extra[field_index]); | ||
| 1939 | field_index += 1; | ||
| 1245 | 1940 | ||
| 1246 | pub const SwitchBr = struct { | 1941 | try stream.writeAll(" = "); |
| 1247 | base: Inst, | 1942 | try self.writeInstRef(stream, default_ref); |
| 1248 | 1943 | } | |
| 1249 | positionals: struct { | 1944 | try stream.writeAll(",\n"); |
| 1250 | target: *Inst, | 1945 | } |
| 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 {}, | ||
| 1260 | 1946 | ||
| 1261 | pub const SpecialProng = enum { | 1947 | self.indent -= 2; |
| 1262 | none, | 1948 | try stream.writeByteNTimes(' ', self.indent); |
| 1263 | @"else", | 1949 | try stream.writeAll("}) "); |
| 1264 | underscore, | 1950 | try self.writeSrc(stream, inst_data.src()); |
| 1265 | }; | 1951 | } |
| 1266 | 1952 | ||
| 1267 | pub const Case = struct { | 1953 | fn writePlNodeSwitchBr( |
| 1268 | item: *Inst, | 1954 | self: *Writer, |
| 1269 | body: Body, | 1955 | stream: anytype, |
| 1956 | inst: Inst.Index, | ||
| 1957 | special_prong: SpecialProng, | ||
| 1958 | ) !void { | ||
| 1959 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | ||
| 1960 | const extra = self.code.extraData(Inst.SwitchBlock, inst_data.payload_index); | ||
| 1961 | const special: struct { | ||
| 1962 | body: []const Inst.Index, | ||
| 1963 | end: usize, | ||
| 1964 | } = switch (special_prong) { | ||
| 1965 | .none => .{ .body = &.{}, .end = extra.end }, | ||
| 1966 | .under, .@"else" => blk: { | ||
| 1967 | const body_len = self.code.extra[extra.end]; | ||
| 1968 | const extra_body_start = extra.end + 1; | ||
| 1969 | break :blk .{ | ||
| 1970 | .body = self.code.extra[extra_body_start..][0..body_len], | ||
| 1971 | .end = extra_body_start + body_len, | ||
| 1972 | }; | ||
| 1973 | }, | ||
| 1270 | }; | 1974 | }; |
| 1271 | }; | ||
| 1272 | }; | ||
| 1273 | 1975 | ||
| 1274 | pub const ErrorMsg = struct { | 1976 | try self.writeInstRef(stream, extra.data.operand); |
| 1275 | byte_offset: usize, | ||
| 1276 | msg: []const u8, | ||
| 1277 | }; | ||
| 1278 | |||
| 1279 | pub const Body = struct { | ||
| 1280 | instructions: []*Inst, | ||
| 1281 | }; | ||
| 1282 | 1977 | ||
| 1283 | pub const Module = struct { | 1978 | if (special.body.len != 0) { |
| 1284 | decls: []*Decl, | 1979 | const prong_name = switch (special_prong) { |
| 1285 | arena: std.heap.ArenaAllocator, | 1980 | .@"else" => "else", |
| 1286 | error_msg: ?ErrorMsg = null, | 1981 | .under => "_", |
| 1287 | metadata: std.AutoHashMap(*Inst, MetaData), | 1982 | else => unreachable, |
| 1288 | body_metadata: std.AutoHashMap(*Body, BodyMetaData), | 1983 | }; |
| 1289 | 1984 | try stream.print(", {s} => {{\n", .{prong_name}); | |
| 1290 | pub const Decl = struct { | 1985 | self.indent += 2; |
| 1291 | name: []const u8, | 1986 | try self.writeBody(stream, special.body); |
| 1292 | 1987 | self.indent -= 2; | |
| 1293 | /// Hash of slice into the source of the part after the = and before the next instruction. | 1988 | try stream.writeByteNTimes(' ', self.indent); |
| 1294 | contents_hash: std.zig.SrcHash, | 1989 | try stream.writeAll("}"); |
| 1990 | } | ||
| 1295 | 1991 | ||
| 1296 | inst: *Inst, | 1992 | var extra_index: usize = special.end; |
| 1297 | }; | 1993 | { |
| 1994 | var scalar_i: usize = 0; | ||
| 1995 | while (scalar_i < extra.data.cases_len) : (scalar_i += 1) { | ||
| 1996 | const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | ||
| 1997 | extra_index += 1; | ||
| 1998 | const body_len = self.code.extra[extra_index]; | ||
| 1999 | extra_index += 1; | ||
| 2000 | const body = self.code.extra[extra_index..][0..body_len]; | ||
| 2001 | extra_index += body_len; | ||
| 1298 | 2002 | ||
| 1299 | pub const MetaData = struct { | 2003 | try stream.writeAll(", "); |
| 1300 | deaths: ir.Inst.DeathsInt, | 2004 | try self.writeInstRef(stream, item_ref); |
| 1301 | addr: usize, | 2005 | try stream.writeAll(" => {\n"); |
| 1302 | }; | 2006 | self.indent += 2; |
| 2007 | try self.writeBody(stream, body); | ||
| 2008 | self.indent -= 2; | ||
| 2009 | try stream.writeByteNTimes(' ', self.indent); | ||
| 2010 | try stream.writeAll("}"); | ||
| 2011 | } | ||
| 2012 | } | ||
| 2013 | try stream.writeAll(") "); | ||
| 2014 | try self.writeSrc(stream, inst_data.src()); | ||
| 2015 | } | ||
| 1303 | 2016 | ||
| 1304 | pub const BodyMetaData = struct { | 2017 | fn writePlNodeSwitchBlockMulti( |
| 1305 | deaths: []*Inst, | 2018 | self: *Writer, |
| 1306 | }; | 2019 | stream: anytype, |
| 2020 | inst: Inst.Index, | ||
| 2021 | special_prong: SpecialProng, | ||
| 2022 | ) !void { | ||
| 2023 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | ||
| 2024 | const extra = self.code.extraData(Inst.SwitchBlockMulti, inst_data.payload_index); | ||
| 2025 | const special: struct { | ||
| 2026 | body: []const Inst.Index, | ||
| 2027 | end: usize, | ||
| 2028 | } = switch (special_prong) { | ||
| 2029 | .none => .{ .body = &.{}, .end = extra.end }, | ||
| 2030 | .under, .@"else" => blk: { | ||
| 2031 | const body_len = self.code.extra[extra.end]; | ||
| 2032 | const extra_body_start = extra.end + 1; | ||
| 2033 | break :blk .{ | ||
| 2034 | .body = self.code.extra[extra_body_start..][0..body_len], | ||
| 2035 | .end = extra_body_start + body_len, | ||
| 2036 | }; | ||
| 2037 | }, | ||
| 2038 | }; | ||
| 1307 | 2039 | ||
| 1308 | pub fn deinit(self: *Module, allocator: *Allocator) void { | 2040 | try self.writeInstRef(stream, extra.data.operand); |
| 1309 | self.metadata.deinit(); | ||
| 1310 | self.body_metadata.deinit(); | ||
| 1311 | allocator.free(self.decls); | ||
| 1312 | self.arena.deinit(); | ||
| 1313 | self.* = undefined; | ||
| 1314 | } | ||
| 1315 | 2041 | ||
| 1316 | /// This is a debugging utility for rendering the tree to stderr. | 2042 | if (special.body.len != 0) { |
| 1317 | pub fn dump(self: Module) void { | 2043 | const prong_name = switch (special_prong) { |
| 1318 | self.writeToStream(std.heap.page_allocator, std.io.getStdErr().writer()) catch {}; | 2044 | .@"else" => "else", |
| 1319 | } | 2045 | .under => "_", |
| 2046 | else => unreachable, | ||
| 2047 | }; | ||
| 2048 | try stream.print(", {s} => {{\n", .{prong_name}); | ||
| 2049 | self.indent += 2; | ||
| 2050 | try self.writeBody(stream, special.body); | ||
| 2051 | self.indent -= 2; | ||
| 2052 | try stream.writeByteNTimes(' ', self.indent); | ||
| 2053 | try stream.writeAll("}"); | ||
| 2054 | } | ||
| 1320 | 2055 | ||
| 1321 | const DeclAndIndex = struct { | 2056 | var extra_index: usize = special.end; |
| 1322 | decl: *Decl, | 2057 | { |
| 1323 | index: usize, | 2058 | var scalar_i: usize = 0; |
| 1324 | }; | 2059 | while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) { |
| 2060 | const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | ||
| 2061 | extra_index += 1; | ||
| 2062 | const body_len = self.code.extra[extra_index]; | ||
| 2063 | extra_index += 1; | ||
| 2064 | const body = self.code.extra[extra_index..][0..body_len]; | ||
| 2065 | extra_index += body_len; | ||
| 1325 | 2066 | ||
| 1326 | /// TODO Look into making a table to speed this up. | 2067 | try stream.writeAll(", "); |
| 1327 | pub fn findDecl(self: Module, name: []const u8) ?DeclAndIndex { | 2068 | try self.writeInstRef(stream, item_ref); |
| 1328 | for (self.decls) |decl, i| { | 2069 | try stream.writeAll(" => {\n"); |
| 1329 | if (mem.eql(u8, decl.name, name)) { | 2070 | self.indent += 2; |
| 1330 | return DeclAndIndex{ | 2071 | try self.writeBody(stream, body); |
| 1331 | .decl = decl, | 2072 | self.indent -= 2; |
| 1332 | .index = i, | 2073 | try stream.writeByteNTimes(' ', self.indent); |
| 1333 | }; | 2074 | try stream.writeAll("}"); |
| 1334 | } | 2075 | } |
| 1335 | } | 2076 | } |
| 1336 | return null; | 2077 | { |
| 1337 | } | 2078 | var multi_i: usize = 0; |
| 2079 | while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) { | ||
| 2080 | const items_len = self.code.extra[extra_index]; | ||
| 2081 | extra_index += 1; | ||
| 2082 | const ranges_len = self.code.extra[extra_index]; | ||
| 2083 | extra_index += 1; | ||
| 2084 | const body_len = self.code.extra[extra_index]; | ||
| 2085 | extra_index += 1; | ||
| 2086 | const items = self.code.refSlice(extra_index, items_len); | ||
| 2087 | extra_index += items_len; | ||
| 2088 | |||
| 2089 | for (items) |item_ref| { | ||
| 2090 | try stream.writeAll(", "); | ||
| 2091 | try self.writeInstRef(stream, item_ref); | ||
| 2092 | } | ||
| 1338 | 2093 | ||
| 1339 | pub fn findInstDecl(self: Module, inst: *Inst) ?DeclAndIndex { | 2094 | var range_i: usize = 0; |
| 1340 | for (self.decls) |decl, i| { | 2095 | while (range_i < ranges_len) : (range_i += 1) { |
| 1341 | if (decl.inst == inst) { | 2096 | const item_first = @intToEnum(Inst.Ref, self.code.extra[extra_index]); |
| 1342 | return DeclAndIndex{ | 2097 | extra_index += 1; |
| 1343 | .decl = decl, | 2098 | const item_last = @intToEnum(Inst.Ref, self.code.extra[extra_index]); |
| 1344 | .index = i, | 2099 | extra_index += 1; |
| 1345 | }; | 2100 | |
| 2101 | try stream.writeAll(", "); | ||
| 2102 | try self.writeInstRef(stream, item_first); | ||
| 2103 | try stream.writeAll("..."); | ||
| 2104 | try self.writeInstRef(stream, item_last); | ||
| 2105 | } | ||
| 2106 | |||
| 2107 | const body = self.code.extra[extra_index..][0..body_len]; | ||
| 2108 | extra_index += body_len; | ||
| 2109 | try stream.writeAll(" => {\n"); | ||
| 2110 | self.indent += 2; | ||
| 2111 | try self.writeBody(stream, body); | ||
| 2112 | self.indent -= 2; | ||
| 2113 | try stream.writeByteNTimes(' ', self.indent); | ||
| 2114 | try stream.writeAll("}"); | ||
| 1346 | } | 2115 | } |
| 1347 | } | 2116 | } |
| 1348 | return null; | 2117 | try stream.writeAll(") "); |
| 2118 | try self.writeSrc(stream, inst_data.src()); | ||
| 1349 | } | 2119 | } |
| 1350 | 2120 | ||
| 1351 | /// The allocator is used for temporary storage, but this function always returns | 2121 | fn writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1352 | /// with no resources allocated. | 2122 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1353 | pub fn writeToStream(self: Module, allocator: *Allocator, stream: anytype) !void { | 2123 | const extra = self.code.extraData(Inst.MultiOp, inst_data.payload_index); |
| 1354 | var write = Writer{ | 2124 | const operands = self.code.refSlice(extra.end, extra.data.operands_len); |
| 1355 | .module = &self, | ||
| 1356 | .inst_table = InstPtrTable.init(allocator), | ||
| 1357 | .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator), | ||
| 1358 | .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator), | ||
| 1359 | .arena = std.heap.ArenaAllocator.init(allocator), | ||
| 1360 | .indent = 2, | ||
| 1361 | .next_instr_index = undefined, | ||
| 1362 | }; | ||
| 1363 | defer write.arena.deinit(); | ||
| 1364 | defer write.inst_table.deinit(); | ||
| 1365 | defer write.block_table.deinit(); | ||
| 1366 | defer write.loop_table.deinit(); | ||
| 1367 | |||
| 1368 | // First, build a map of *Inst to @ or % indexes | ||
| 1369 | try write.inst_table.ensureCapacity(@intCast(u32, self.decls.len)); | ||
| 1370 | 2125 | ||
| 1371 | for (self.decls) |decl, decl_i| { | 2126 | for (operands) |operand, i| { |
| 1372 | try write.inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name }); | 2127 | if (i != 0) try stream.writeAll(", "); |
| 2128 | try self.writeInstRef(stream, operand); | ||
| 1373 | } | 2129 | } |
| 2130 | try stream.writeAll(") "); | ||
| 2131 | try self.writeSrc(stream, inst_data.src()); | ||
| 2132 | } | ||
| 1374 | 2133 | ||
| 1375 | for (self.decls) |decl, i| { | 2134 | fn writePlNodeDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1376 | write.next_instr_index = 0; | 2135 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1377 | try stream.print("@{s} ", .{decl.name}); | 2136 | const decl = self.code.decls[inst_data.payload_index]; |
| 1378 | try write.writeInstToStream(stream, decl.inst); | 2137 | try stream.print("{s}) ", .{decl.name}); |
| 1379 | try stream.writeByte('\n'); | 2138 | try self.writeSrc(stream, inst_data.src()); |
| 1380 | } | ||
| 1381 | } | 2139 | } |
| 1382 | }; | ||
| 1383 | 2140 | ||
| 1384 | const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 }); | 2141 | fn writePlNodeField(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 2142 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | ||
| 2143 | const extra = self.code.extraData(Inst.Field, inst_data.payload_index).data; | ||
| 2144 | const name = self.code.nullTerminatedString(extra.field_name_start); | ||
| 2145 | try self.writeInstRef(stream, extra.lhs); | ||
| 2146 | try stream.print(", \"{}\") ", .{std.zig.fmtEscapes(name)}); | ||
| 2147 | try self.writeSrc(stream, inst_data.src()); | ||
| 2148 | } | ||
| 1385 | 2149 | ||
| 1386 | const Writer = struct { | 2150 | fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1387 | module: *const Module, | 2151 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1388 | inst_table: InstPtrTable, | 2152 | const extra = self.code.extraData(Inst.As, inst_data.payload_index).data; |
| 1389 | block_table: std.AutoHashMap(*Inst.Block, []const u8), | 2153 | try self.writeInstRef(stream, extra.dest_type); |
| 1390 | loop_table: std.AutoHashMap(*Inst.Loop, []const u8), | 2154 | try stream.writeAll(", "); |
| 1391 | arena: std.heap.ArenaAllocator, | 2155 | try self.writeInstRef(stream, extra.operand); |
| 1392 | indent: usize, | 2156 | try stream.writeAll(") "); |
| 1393 | next_instr_index: usize, | 2157 | try self.writeSrc(stream, inst_data.src()); |
| 2158 | } | ||
| 1394 | 2159 | ||
| 1395 | fn writeInstToStream( | 2160 | fn writeNode( |
| 1396 | self: *Writer, | 2161 | self: *Writer, |
| 1397 | stream: anytype, | 2162 | stream: anytype, |
| 1398 | inst: *Inst, | 2163 | inst: Inst.Index, |
| 1399 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | 2164 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1400 | inline for (@typeInfo(Inst.Tag).Enum.fields) |enum_field| { | 2165 | const src_node = self.code.instructions.items(.data)[inst].node; |
| 1401 | const expected_tag = @field(Inst.Tag, enum_field.name); | 2166 | const src: LazySrcLoc = .{ .node_offset = src_node }; |
| 1402 | if (inst.tag == expected_tag) { | 2167 | try stream.writeAll(") "); |
| 1403 | return self.writeInstToStreamGeneric(stream, expected_tag, inst); | 2168 | try self.writeSrc(stream, src); |
| 1404 | } | ||
| 1405 | } | ||
| 1406 | unreachable; // all tags handled | ||
| 1407 | } | 2169 | } |
| 1408 | 2170 | ||
| 1409 | fn writeInstToStreamGeneric( | 2171 | fn writeStrTok( |
| 1410 | self: *Writer, | 2172 | self: *Writer, |
| 1411 | stream: anytype, | 2173 | stream: anytype, |
| 1412 | comptime inst_tag: Inst.Tag, | 2174 | inst: Inst.Index, |
| 1413 | base: *Inst, | ||
| 1414 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | 2175 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1415 | const SpecificInst = inst_tag.Type(); | 2176 | const inst_data = self.code.instructions.items(.data)[inst].str_tok; |
| 1416 | const inst = @fieldParentPtr(SpecificInst, "base", base); | 2177 | const str = inst_data.get(self.code); |
| 1417 | const Positionals = @TypeOf(inst.positionals); | 2178 | try stream.print("\"{}\") ", .{std.zig.fmtEscapes(str)}); |
| 1418 | try stream.writeAll("= " ++ @tagName(inst_tag) ++ "("); | 2179 | try self.writeSrc(stream, inst_data.src()); |
| 1419 | const pos_fields = @typeInfo(Positionals).Struct.fields; | 2180 | } |
| 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 | } | ||
| 1426 | 2181 | ||
| 1427 | comptime var need_comma = pos_fields.len != 0; | 2182 | fn writeFnType( |
| 1428 | const KW_Args = @TypeOf(inst.kw_args); | 2183 | self: *Writer, |
| 1429 | inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| { | 2184 | stream: anytype, |
| 1430 | if (@typeInfo(arg_field.field_type) == .Optional) { | 2185 | inst: Inst.Index, |
| 1431 | if (@field(inst.kw_args, arg_field.name)) |non_optional| { | 2186 | var_args: bool, |
| 1432 | if (need_comma) try stream.writeAll(", "); | 2187 | ) !void { |
| 1433 | try stream.print("{s}=", .{arg_field.name}); | 2188 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1434 | try self.writeParamToStream(stream, &non_optional); | 2189 | const src = inst_data.src(); |
| 1435 | need_comma = true; | 2190 | const extra = self.code.extraData(Inst.FnType, inst_data.payload_index); |
| 1436 | } | 2191 | const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); |
| 1437 | } else { | 2192 | return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, .none, src); |
| 1438 | if (need_comma) try stream.writeAll(", "); | 2193 | } |
| 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 | } | ||
| 1444 | 2194 | ||
| 1445 | try stream.writeByte(')'); | 2195 | fn writeFnTypeCc( |
| 2196 | self: *Writer, | ||
| 2197 | stream: anytype, | ||
| 2198 | inst: Inst.Index, | ||
| 2199 | var_args: bool, | ||
| 2200 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | ||
| 2201 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | ||
| 2202 | const src = inst_data.src(); | ||
| 2203 | const extra = self.code.extraData(Inst.FnTypeCc, inst_data.payload_index); | ||
| 2204 | const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); | ||
| 2205 | const cc = extra.data.cc; | ||
| 2206 | return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, cc, src); | ||
| 1446 | } | 2207 | } |
| 1447 | 2208 | ||
| 1448 | fn writeParamToStream(self: *Writer, stream: anytype, param_ptr: anytype) !void { | 2209 | fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1449 | const param = param_ptr.*; | 2210 | const inst_data = self.code.instructions.items(.data)[inst].bool_br; |
| 1450 | if (@typeInfo(@TypeOf(param)) == .Enum) { | 2211 | const extra = self.code.extraData(Inst.Block, inst_data.payload_index); |
| 1451 | return stream.writeAll(@tagName(param)); | 2212 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; |
| 1452 | } | 2213 | try self.writeInstRef(stream, inst_data.lhs); |
| 1453 | switch (@TypeOf(param)) { | 2214 | try stream.writeAll(", {\n"); |
| 1454 | *Inst => return self.writeInstParamToStream(stream, param), | 2215 | self.indent += 2; |
| 1455 | ?*Inst => return self.writeInstParamToStream(stream, param.?), | 2216 | try self.writeBody(stream, body); |
| 1456 | []*Inst => { | 2217 | self.indent -= 2; |
| 1457 | try stream.writeByte('['); | 2218 | try stream.writeByteNTimes(' ', self.indent); |
| 1458 | for (param) |inst, i| { | 2219 | try stream.writeAll("})"); |
| 1459 | if (i != 0) { | 2220 | } |
| 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 | } | ||
| 1479 | 2221 | ||
| 1480 | for (param.instructions) |inst| { | 2222 | fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1481 | const my_i = self.next_instr_index; | 2223 | const int_type = self.code.instructions.items(.data)[inst].int_type; |
| 1482 | self.next_instr_index += 1; | 2224 | const prefix: u8 = switch (int_type.signedness) { |
| 1483 | try self.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = undefined }); | 2225 | .signed => 'i', |
| 1484 | try stream.writeByteNTimes(' ', self.indent); | 2226 | .unsigned => 'u', |
| 1485 | try stream.print("%{d} ", .{my_i}); | 2227 | }; |
| 1486 | if (inst.cast(Inst.Block)) |block| { | 2228 | try stream.print("{c}{d}) ", .{ prefix, int_type.bit_count }); |
| 1487 | const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{d}", .{my_i}); | 2229 | try self.writeSrc(stream, int_type.src()); |
| 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 | } | ||
| 1555 | } | 2230 | } |
| 1556 | 2231 | ||
| 1557 | fn writeInstParamToStream(self: *Writer, stream: anytype, inst: *Inst) !void { | 2232 | fn writeBreak(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1558 | if (self.inst_table.get(inst)) |info| { | 2233 | const inst_data = self.code.instructions.items(.data)[inst].@"break"; |
| 1559 | if (info.index) |i| { | 2234 | |
| 1560 | try stream.print("%{d}", .{info.index}); | 2235 | try self.writeInstIndex(stream, inst_data.block_inst); |
| 1561 | } else { | 2236 | try stream.writeAll(", "); |
| 1562 | try stream.print("@{s}", .{info.name}); | 2237 | try self.writeInstRef(stream, inst_data.operand); |
| 1563 | } | 2238 | try stream.writeAll(")"); |
| 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 | } | ||
| 1571 | } | 2239 | } |
| 1572 | }; | ||
| 1573 | 2240 | ||
| 1574 | /// For debugging purposes, prints a function representation to stderr. | 2241 | fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1575 | pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void { | 2242 | const inst_data = self.code.instructions.items(.data)[inst].@"unreachable"; |
| 1576 | const allocator = old_module.gpa; | 2243 | const safety_str = if (inst_data.safety) "safe" else "unsafe"; |
| 1577 | var ctx: DumpTzir = .{ | 2244 | try stream.print("{s}) ", .{safety_str}); |
| 1578 | .allocator = allocator, | 2245 | try self.writeSrc(stream, inst_data.src()); |
| 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 | }, | ||
| 1602 | } | 2246 | } |
| 1603 | } | ||
| 1604 | 2247 | ||
| 1605 | const DumpTzir = struct { | 2248 | fn writeFnTypeCommon( |
| 1606 | allocator: *Allocator, | 2249 | self: *Writer, |
| 1607 | arena: std.heap.ArenaAllocator, | 2250 | stream: anytype, |
| 1608 | old_module: *const IrModule, | 2251 | param_types: []const Inst.Ref, |
| 1609 | module_fn: *IrModule.Fn, | 2252 | ret_ty: Inst.Ref, |
| 1610 | indent: usize, | 2253 | var_args: bool, |
| 1611 | inst_table: InstTable, | 2254 | cc: Inst.Ref, |
| 1612 | partial_inst_table: InstTable, | 2255 | src: LazySrcLoc, |
| 1613 | const_table: InstTable, | 2256 | ) !void { |
| 1614 | next_index: usize = 0, | 2257 | try stream.writeAll("["); |
| 1615 | next_partial_index: usize = 0, | 2258 | for (param_types) |param_type, i| { |
| 1616 | next_const_index: usize = 0, | 2259 | if (i != 0) try stream.writeAll(", "); |
| 1617 | 2260 | try self.writeInstRef(stream, param_type); | |
| 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 | }); | ||
| 1636 | } | 2261 | } |
| 1637 | 2262 | try stream.writeAll("], "); | |
| 1638 | return dtz.dumpBody(body, writer); | 2263 | try self.writeInstRef(stream, ret_ty); |
| 2264 | try self.writeOptionalInstRef(stream, ", cc=", cc); | ||
| 2265 | try self.writeFlag(stream, ", var_args", var_args); | ||
| 2266 | try stream.writeAll(") "); | ||
| 2267 | try self.writeSrc(stream, src); | ||
| 1639 | } | 2268 | } |
| 1640 | 2269 | ||
| 1641 | fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: ir.Body) error{OutOfMemory}!void { | 2270 | fn writeSmallStr( |
| 1642 | for (body.instructions) |inst| { | 2271 | self: *Writer, |
| 1643 | try dtz.inst_table.put(inst, dtz.next_index); | 2272 | stream: anytype, |
| 1644 | dtz.next_index += 1; | 2273 | inst: Inst.Index, |
| 1645 | switch (inst.tag) { | 2274 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1646 | .alloc, | 2275 | const str = self.code.instructions.items(.data)[inst].small_str.get(); |
| 1647 | .retvoid, | 2276 | try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)}); |
| 1648 | .unreach, | 2277 | } |
| 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 | }, | ||
| 1681 | 2278 | ||
| 1682 | .add, | 2279 | fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1683 | .addwrap, | 2280 | const inst_data = self.code.instructions.items(.data)[inst].switch_capture; |
| 1684 | .sub, | 2281 | try self.writeInstIndex(stream, inst_data.switch_inst); |
| 1685 | .subwrap, | 2282 | try stream.print(", {d})", .{inst_data.prong_index}); |
| 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 | } | ||
| 1755 | } | 2283 | } |
| 1756 | 2284 | ||
| 1757 | fn dumpBody(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void { | 2285 | fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void { |
| 1758 | for (body.instructions) |inst| { | 2286 | var i: usize = @enumToInt(ref); |
| 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"), | ||
| 1774 | 2287 | ||
| 1775 | .ref, | 2288 | if (i < Inst.Ref.typed_value_map.len) { |
| 1776 | .ret, | 2289 | return stream.print("@{}", .{ref}); |
| 1777 | .bitcast, | 2290 | } |
| 1778 | .not, | 2291 | i -= Inst.Ref.typed_value_map.len; |
| 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 | }, | ||
| 1807 | 2292 | ||
| 1808 | .add, | 2293 | if (i < self.param_count) { |
| 1809 | .addwrap, | 2294 | return stream.print("${d}", .{i}); |
| 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 | } | ||
| 1995 | } | 2295 | } |
| 2296 | i -= self.param_count; | ||
| 2297 | |||
| 2298 | return self.writeInstIndex(stream, @intCast(Inst.Index, i)); | ||
| 1996 | } | 2299 | } |
| 1997 | 2300 | ||
| 1998 | fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *ir.Inst) !?usize { | 2301 | fn writeInstIndex(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1999 | if (dtz.partial_inst_table.get(inst)) |operand_index| { | 2302 | return stream.print("%{d}", .{inst}); |
| 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 | } | ||
| 2012 | } | 2303 | } |
| 2013 | 2304 | ||
| 2014 | fn findConst(dtz: *DumpTzir, operand: *ir.Inst) !void { | 2305 | fn writeOptionalInstRef( |
| 2015 | if (operand.tag == .constant) { | 2306 | self: *Writer, |
| 2016 | try dtz.const_table.put(operand, dtz.next_const_index); | 2307 | stream: anytype, |
| 2017 | dtz.next_const_index += 1; | 2308 | prefix: []const u8, |
| 2018 | } | 2309 | inst: Inst.Ref, |
| 2310 | ) !void { | ||
| 2311 | if (inst == .none) return; | ||
| 2312 | try stream.writeAll(prefix); | ||
| 2313 | try self.writeInstRef(stream, inst); | ||
| 2019 | } | 2314 | } |
| 2020 | }; | ||
| 2021 | 2315 | ||
| 2022 | /// For debugging purposes, like dumpFn but for unanalyzed zir blocks | 2316 | fn writeFlag( |
| 2023 | pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8, instructions: []*Inst) !void { | 2317 | self: *Writer, |
| 2024 | var fib = std.heap.FixedBufferAllocator.init(&[_]u8{}); | 2318 | stream: anytype, |
| 2025 | var module = Module{ | 2319 | name: []const u8, |
| 2026 | .decls = &[_]*Module.Decl{}, | 2320 | flag: bool, |
| 2027 | .arena = std.heap.ArenaAllocator.init(&fib.allocator), | 2321 | ) !void { |
| 2028 | .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(&fib.allocator), | 2322 | if (!flag) return; |
| 2029 | .body_metadata = std.AutoHashMap(*Body, Module.BodyMetaData).init(&fib.allocator), | 2323 | try stream.writeAll(name); |
| 2030 | }; | 2324 | } |
| 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 | } | ||
| 2061 | 2325 | ||
| 2062 | try write.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = "inst" }); | 2326 | fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void { |
| 2063 | try stderr.print(" %{d} ", .{my_i}); | 2327 | const tree = self.scope.tree(); |
| 2064 | try write.writeInstToStream(stderr, inst); | 2328 | const src_loc = src.toSrcLoc(self.scope); |
| 2065 | try stderr.writeByte('\n'); | 2329 | const abs_byte_off = try src_loc.byteOffset(); |
| 2330 | const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off); | ||
| 2331 | try stream.print("{s}:{d}:{d}", .{ | ||
| 2332 | @tagName(src), delta_line.line + 1, delta_line.column + 1, | ||
| 2333 | }); | ||
| 2066 | } | 2334 | } |
| 2067 | 2335 | ||
| 2068 | try stderr.print("}} // {s} {s}\n\n", .{ kind, decl_name }); | 2336 | fn writeBody(self: *Writer, stream: anytype, body: []const Inst.Index) !void { |
| 2069 | } | 2337 | for (body) |inst| { |
| 2338 | try stream.writeByteNTimes(' ', self.indent); | ||
| 2339 | try stream.print("%{d} ", .{inst}); | ||
| 2340 | try self.writeInstToStream(stream, inst); | ||
| 2341 | try stream.writeByte('\n'); | ||
| 2342 | } | ||
| 2343 | } | ||
| 2344 | }; |
src/zir_sema.zig deleted-2597| ... | @@ -1,2597 +0,0 @@ | ||
| 1 | //! Semantic analysis of ZIR instructions. | ||
| 2 | //! This file operates on a `Module` instance, transforming untyped ZIR | ||
| 3 | //! instructions into semantically-analyzed IR instructions. It does type | ||
| 4 | //! checking, comptime control flow, and safety-check generation. This is the | ||
| 5 | //! the heart of the Zig compiler. | ||
| 6 | //! When deciding if something goes into this file or into Module, here is a | ||
| 7 | //! guiding principle: if it has to do with (untyped) ZIR instructions, it goes | ||
| 8 | //! here. If the analysis operates on typed IR instructions, it goes in Module. | ||
| 9 | |||
| 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/compile_errors.zig-2| ... | @@ -1027,9 +1027,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -1027,9 +1027,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1027 | \\} | 1027 | \\} |
| 1028 | \\fn foo() void {} | 1028 | \\fn foo() void {} |
| 1029 | , &[_][]const u8{ | 1029 | , &[_][]const u8{ |
| 1030 | "tmp.zig:3:21: error: async call in nosuspend scope", | ||
| 1031 | "tmp.zig:4:9: error: suspend in nosuspend scope", | 1030 | "tmp.zig:4:9: error: suspend in nosuspend scope", |
| 1032 | "tmp.zig:5:9: error: resume in nosuspend scope", | ||
| 1033 | }); | 1031 | }); |
| 1034 | 1032 | ||
| 1035 | cases.add("atomicrmw with bool op not .Xchg", | 1033 | cases.add("atomicrmw with bool op not .Xchg", |
test/stage1/behavior/async_fn.zig+63-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = std.builtin; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const expectEqual = std.testing.expectEqual; | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | const expectEqualStrings = std.testing.expectEqualStrings; | 5 | const expectEqualStrings = std.testing.expectEqualStrings; |
| ... | @@ -1545,6 +1545,68 @@ test "nosuspend on function calls" { | ... | @@ -1545,6 +1545,68 @@ test "nosuspend on function calls" { |
| 1545 | expectEqual(@as(i32, 42), (try nosuspend S1.d()).b); | 1545 | expectEqual(@as(i32, 42), (try nosuspend S1.d()).b); |
| 1546 | } | 1546 | } |
| 1547 | 1547 | ||
| 1548 | test "nosuspend on async function calls" { | ||
| 1549 | const S0 = struct { | ||
| 1550 | b: i32 = 42, | ||
| 1551 | }; | ||
| 1552 | const S1 = struct { | ||
| 1553 | fn c() S0 { | ||
| 1554 | return S0{}; | ||
| 1555 | } | ||
| 1556 | fn d() !S0 { | ||
| 1557 | return S0{}; | ||
| 1558 | } | ||
| 1559 | }; | ||
| 1560 | var frame_c = nosuspend async S1.c(); | ||
| 1561 | expectEqual(@as(i32, 42), (await frame_c).b); | ||
| 1562 | var frame_d = nosuspend async S1.d(); | ||
| 1563 | expectEqual(@as(i32, 42), (try await frame_d).b); | ||
| 1564 | } | ||
| 1565 | |||
| 1566 | // test "resume nosuspend async function calls" { | ||
| 1567 | // const S0 = struct { | ||
| 1568 | // b: i32 = 42, | ||
| 1569 | // }; | ||
| 1570 | // const S1 = struct { | ||
| 1571 | // fn c() S0 { | ||
| 1572 | // suspend; | ||
| 1573 | // return S0{}; | ||
| 1574 | // } | ||
| 1575 | // fn d() !S0 { | ||
| 1576 | // suspend; | ||
| 1577 | // return S0{}; | ||
| 1578 | // } | ||
| 1579 | // }; | ||
| 1580 | // var frame_c = nosuspend async S1.c(); | ||
| 1581 | // resume frame_c; | ||
| 1582 | // expectEqual(@as(i32, 42), (await frame_c).b); | ||
| 1583 | // var frame_d = nosuspend async S1.d(); | ||
| 1584 | // resume frame_d; | ||
| 1585 | // expectEqual(@as(i32, 42), (try await frame_d).b); | ||
| 1586 | // } | ||
| 1587 | |||
| 1588 | test "nosuspend resume async function calls" { | ||
| 1589 | const S0 = struct { | ||
| 1590 | b: i32 = 42, | ||
| 1591 | }; | ||
| 1592 | const S1 = struct { | ||
| 1593 | fn c() S0 { | ||
| 1594 | suspend; | ||
| 1595 | return S0{}; | ||
| 1596 | } | ||
| 1597 | fn d() !S0 { | ||
| 1598 | suspend; | ||
| 1599 | return S0{}; | ||
| 1600 | } | ||
| 1601 | }; | ||
| 1602 | var frame_c = async S1.c(); | ||
| 1603 | nosuspend resume frame_c; | ||
| 1604 | expectEqual(@as(i32, 42), (await frame_c).b); | ||
| 1605 | var frame_d = async S1.d(); | ||
| 1606 | nosuspend resume frame_d; | ||
| 1607 | expectEqual(@as(i32, 42), (try await frame_d).b); | ||
| 1608 | } | ||
| 1609 | |||
| 1548 | test "avoid forcing frame alignment resolution implicit cast to *c_void" { | 1610 | test "avoid forcing frame alignment resolution implicit cast to *c_void" { |
| 1549 | const S = struct { | 1611 | const S = struct { |
| 1550 | var x: ?*c_void = null; | 1612 | var x: ?*c_void = null; |
test/stage2/arm.zig+41| ... | @@ -378,4 +378,45 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -378,4 +378,45 @@ pub fn addCases(ctx: *TestContext) !void { |
| 378 | "", | 378 | "", |
| 379 | ); | 379 | ); |
| 380 | } | 380 | } |
| 381 | |||
| 382 | { | ||
| 383 | var case = ctx.exe("save function return values in callee preserved register", linux_arm); | ||
| 384 | // Here, it is necessary to save the result of bar() into a | ||
| 385 | // callee preserved register, otherwise it will be overwritten | ||
| 386 | // by the first parameter to baz. | ||
| 387 | case.addCompareOutput( | ||
| 388 | \\export fn _start() noreturn { | ||
| 389 | \\ assert(foo() == 43); | ||
| 390 | \\ exit(); | ||
| 391 | \\} | ||
| 392 | \\ | ||
| 393 | \\fn foo() u32 { | ||
| 394 | \\ return bar() + baz(42); | ||
| 395 | \\} | ||
| 396 | \\ | ||
| 397 | \\fn bar() u32 { | ||
| 398 | \\ return 1; | ||
| 399 | \\} | ||
| 400 | \\ | ||
| 401 | \\fn baz(x: u32) u32 { | ||
| 402 | \\ return x; | ||
| 403 | \\} | ||
| 404 | \\ | ||
| 405 | \\fn assert(ok: bool) void { | ||
| 406 | \\ if (!ok) unreachable; | ||
| 407 | \\} | ||
| 408 | \\ | ||
| 409 | \\fn exit() noreturn { | ||
| 410 | \\ asm volatile ("svc #0" | ||
| 411 | \\ : | ||
| 412 | \\ : [number] "{r7}" (1), | ||
| 413 | \\ [arg1] "{r0}" (0) | ||
| 414 | \\ : "memory" | ||
| 415 | \\ ); | ||
| 416 | \\ unreachable; | ||
| 417 | \\} | ||
| 418 | , | ||
| 419 | "", | ||
| 420 | ); | ||
| 421 | } | ||
| 381 | } | 422 | } |
test/stage2/cbe.zig+180| ... | @@ -39,6 +39,21 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -39,6 +39,21 @@ pub fn addCases(ctx: *TestContext) !void { |
| 39 | \\} | 39 | \\} |
| 40 | \\fn unused() void {} | 40 | \\fn unused() void {} |
| 41 | , "yo!" ++ std.cstr.line_sep); | 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,6 +69,42 @@ pub fn addCases(ctx: *TestContext) !void { |
| 54 | , "Hello, world!" ++ std.cstr.line_sep); | 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 | var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64); | 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,6 +294,134 @@ pub fn addCases(ctx: *TestContext) !void { |
| 243 | \\ return a - 4; | 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 | // var case = ctx.exeFromCompiledC("optionals", .{}); | 427 | // var case = ctx.exeFromCompiledC("optionals", .{}); |
| ... | @@ -271,6 +450,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -271,6 +450,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 271 | // \\} | 450 | // \\} |
| 272 | // , ""); | 451 | // , ""); |
| 273 | //} | 452 | //} |
| 453 | |||
| 274 | { | 454 | { |
| 275 | var case = ctx.exeFromCompiledC("errors", .{}); | 455 | var case = ctx.exeFromCompiledC("errors", .{}); |
| 276 | case.addCompareOutput( | 456 | case.addCompareOutput( |
test/stage2/test.zig+50-10| ... | @@ -355,7 +355,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -355,7 +355,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 355 | \\ const z = @TypeOf(true, 1); | 355 | \\ const z = @TypeOf(true, 1); |
| 356 | \\ unreachable; | 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,6 +621,43 @@ pub fn addCases(ctx: *TestContext) !void { |
| 621 | "hello\nhello\nhello\nhello\n", | 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 | // Labeled blocks (no conditional branch) | 661 | // Labeled blocks (no conditional branch) |
| 625 | case.addCompareOutput( | 662 | case.addCompareOutput( |
| 626 | \\export fn _start() noreturn { | 663 | \\export fn _start() noreturn { |
| ... | @@ -1070,7 +1107,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1070,7 +1107,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1070 | \\} | 1107 | \\} |
| 1071 | \\fn x() void {} | 1108 | \\fn x() void {} |
| 1072 | , &[_][]const u8{ | 1109 | , &[_][]const u8{ |
| 1073 | ":11:8: error: found compile log statement", | 1110 | ":9:5: error: found compile log statement", |
| 1074 | ":4:5: note: also here", | 1111 | ":4:5: note: also here", |
| 1075 | }); | 1112 | }); |
| 1076 | } | 1113 | } |
| ... | @@ -1294,10 +1331,9 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -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 | 1334 | // This additionally tests that the compile error reports the correct source location. |
| 1298 | // to be relative to the containing Decl so that they can survive when the byte | 1335 | // Without storing source locations relative to the owner decl, the compile error |
| 1299 | // offset of a previous Decl changes. Here the change from 7 to 999 introduces | 1336 | // here would be off by 2 bytes (from the "7" -> "999"). |
| 1300 | // +2 to the byte offset and makes the error location wrong by 2 bytes. | ||
| 1301 | case.addError( | 1337 | case.addError( |
| 1302 | \\export fn _start() noreturn { | 1338 | \\export fn _start() noreturn { |
| 1303 | \\ const y = fibonacci(999); | 1339 | \\ const y = fibonacci(999); |
| ... | @@ -1318,7 +1354,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1318,7 +1354,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1318 | \\ ); | 1354 | \\ ); |
| 1319 | \\ unreachable; | 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 | var case = ctx.exe("orelse at comptime", linux_x64); | 1360 | var case = ctx.exe("orelse at comptime", linux_x64); |
| ... | @@ -1442,6 +1478,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1442,6 +1478,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1442 | , | 1478 | , |
| 1443 | "", | 1479 | "", |
| 1444 | ); | 1480 | ); |
| 1481 | |||
| 1445 | case.addCompareOutput( | 1482 | case.addCompareOutput( |
| 1446 | \\export fn _start() noreturn { | 1483 | \\export fn _start() noreturn { |
| 1447 | \\ const i: anyerror!u64 = error.B; | 1484 | \\ const i: anyerror!u64 = error.B; |
| ... | @@ -1464,6 +1501,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1464,6 +1501,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1464 | , | 1501 | , |
| 1465 | "", | 1502 | "", |
| 1466 | ); | 1503 | ); |
| 1504 | |||
| 1467 | case.addCompareOutput( | 1505 | case.addCompareOutput( |
| 1468 | \\export fn _start() noreturn { | 1506 | \\export fn _start() noreturn { |
| 1469 | \\ const a: anyerror!comptime_int = 42; | 1507 | \\ const a: anyerror!comptime_int = 42; |
| ... | @@ -1485,11 +1523,12 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1485,11 +1523,12 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1485 | \\ unreachable; | 1523 | \\ unreachable; |
| 1486 | \\} | 1524 | \\} |
| 1487 | , ""); | 1525 | , ""); |
| 1526 | |||
| 1488 | case.addCompareOutput( | 1527 | case.addCompareOutput( |
| 1489 | \\export fn _start() noreturn { | 1528 | \\export fn _start() noreturn { |
| 1490 | \\const a: anyerror!u32 = error.B; | 1529 | \\ const a: anyerror!u32 = error.B; |
| 1491 | \\_ = &(a catch |err| assert(err == error.B)); | 1530 | \\ _ = &(a catch |err| assert(err == error.B)); |
| 1492 | \\exit(); | 1531 | \\ exit(); |
| 1493 | \\} | 1532 | \\} |
| 1494 | \\fn assert(b: bool) void { | 1533 | \\fn assert(b: bool) void { |
| 1495 | \\ if (!b) unreachable; | 1534 | \\ if (!b) unreachable; |
| ... | @@ -1504,6 +1543,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1504,6 +1543,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1504 | \\ unreachable; | 1543 | \\ unreachable; |
| 1505 | \\} | 1544 | \\} |
| 1506 | , ""); | 1545 | , ""); |
| 1546 | |||
| 1507 | case.addCompareOutput( | 1547 | case.addCompareOutput( |
| 1508 | \\export fn _start() noreturn { | 1548 | \\export fn _start() noreturn { |
| 1509 | \\ const a: anyerror!u32 = error.Bar; | 1549 | \\ const a: anyerror!u32 = error.Bar; |