| author | |
| committer | |
| log | 402f87a213b9f3d5e19d5a1d412d8963957d8849 |
| tree | fc82b22ed9387a937457594fd5c99a4589e71eb6 |
| parent | 68f4eb0f67b6e5f7c20332e79c8e8decb07748ee |
| signature | Commit is signed but in an unrecognized format. |
6 files changed, 4123 insertions(+), 4115 deletions(-)
CMakeLists.txt+1-1| ... | @@ -538,7 +538,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -538,7 +538,7 @@ set(ZIG_STAGE2_SOURCES |
| 538 | "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" | 538 | "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" |
| 539 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" | 539 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" |
| 540 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" | 540 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" |
| 541 | "${CMAKE_SOURCE_DIR}/src/astgen.zig" | 541 | "${CMAKE_SOURCE_DIR}/src/AstGen.zig" |
| 542 | "${CMAKE_SOURCE_DIR}/src/clang.zig" | 542 | "${CMAKE_SOURCE_DIR}/src/clang.zig" |
| 543 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" | 543 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" |
| 544 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" | 544 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" |
src/AstGen.zig created+3979| ... | @@ -0,0 +1,3979 @@ | ||
| 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 InnerError = Module.InnerError; | ||
| 23 | const Decl = Module.Decl; | ||
| 24 | const BuiltinFn = @import("BuiltinFn.zig"); | ||
| 25 | |||
| 26 | instructions: std.MultiArrayList(zir.Inst) = .{}, | ||
| 27 | string_bytes: ArrayListUnmanaged(u8) = .{}, | ||
| 28 | extra: ArrayListUnmanaged(u32) = .{}, | ||
| 29 | decl_map: std.StringArrayHashMapUnmanaged(void) = .{}, | ||
| 30 | decls: ArrayListUnmanaged(*Decl) = .{}, | ||
| 31 | /// The end of special indexes. `zir.Inst.Ref` subtracts against this number to convert | ||
| 32 | /// to `zir.Inst.Index`. The default here is correct if there are 0 parameters. | ||
| 33 | ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len, | ||
| 34 | mod: *Module, | ||
| 35 | decl: *Decl, | ||
| 36 | arena: *Allocator, | ||
| 37 | |||
| 38 | /// Call `deinit` on the result. | ||
| 39 | pub fn init(mod: *Module, decl: *Decl, arena: *Allocator) !AstGen { | ||
| 40 | var astgen: AstGen = .{ | ||
| 41 | .mod = mod, | ||
| 42 | .decl = decl, | ||
| 43 | .arena = arena, | ||
| 44 | }; | ||
| 45 | // Must be a block instruction at index 0 with the root body. | ||
| 46 | try astgen.instructions.append(mod.gpa, .{ | ||
| 47 | .tag = .block, | ||
| 48 | .data = .{ .pl_node = .{ | ||
| 49 | .src_node = 0, | ||
| 50 | .payload_index = undefined, | ||
| 51 | } }, | ||
| 52 | }); | ||
| 53 | return astgen; | ||
| 54 | } | ||
| 55 | |||
| 56 | pub fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 { | ||
| 57 | const fields = std.meta.fields(@TypeOf(extra)); | ||
| 58 | try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len + fields.len); | ||
| 59 | return addExtraAssumeCapacity(astgen, extra); | ||
| 60 | } | ||
| 61 | |||
| 62 | pub fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 { | ||
| 63 | const fields = std.meta.fields(@TypeOf(extra)); | ||
| 64 | const result = @intCast(u32, astgen.extra.items.len); | ||
| 65 | inline for (fields) |field| { | ||
| 66 | astgen.extra.appendAssumeCapacity(switch (field.field_type) { | ||
| 67 | u32 => @field(extra, field.name), | ||
| 68 | zir.Inst.Ref => @enumToInt(@field(extra, field.name)), | ||
| 69 | else => @compileError("bad field type"), | ||
| 70 | }); | ||
| 71 | } | ||
| 72 | return result; | ||
| 73 | } | ||
| 74 | |||
| 75 | pub fn appendRefs(astgen: *AstGen, refs: []const zir.Inst.Ref) !void { | ||
| 76 | const coerced = @bitCast([]const u32, refs); | ||
| 77 | return astgen.extra.appendSlice(astgen.mod.gpa, coerced); | ||
| 78 | } | ||
| 79 | |||
| 80 | pub fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const zir.Inst.Ref) void { | ||
| 81 | const coerced = @bitCast([]const u32, refs); | ||
| 82 | astgen.extra.appendSliceAssumeCapacity(coerced); | ||
| 83 | } | ||
| 84 | |||
| 85 | pub fn refIsNoReturn(astgen: AstGen, inst_ref: zir.Inst.Ref) bool { | ||
| 86 | if (inst_ref == .unreachable_value) return true; | ||
| 87 | if (astgen.refToIndex(inst_ref)) |inst_index| { | ||
| 88 | return astgen.instructions.items(.tag)[inst_index].isNoReturn(); | ||
| 89 | } | ||
| 90 | return false; | ||
| 91 | } | ||
| 92 | |||
| 93 | pub fn indexToRef(astgen: AstGen, inst: zir.Inst.Index) zir.Inst.Ref { | ||
| 94 | return @intToEnum(zir.Inst.Ref, astgen.ref_start_index + inst); | ||
| 95 | } | ||
| 96 | |||
| 97 | pub fn refToIndex(astgen: AstGen, inst: zir.Inst.Ref) ?zir.Inst.Index { | ||
| 98 | const ref_int = @enumToInt(inst); | ||
| 99 | if (ref_int >= astgen.ref_start_index) { | ||
| 100 | return ref_int - astgen.ref_start_index; | ||
| 101 | } else { | ||
| 102 | return null; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | pub fn deinit(astgen: *AstGen) void { | ||
| 107 | const gpa = astgen.mod.gpa; | ||
| 108 | astgen.instructions.deinit(gpa); | ||
| 109 | astgen.extra.deinit(gpa); | ||
| 110 | astgen.string_bytes.deinit(gpa); | ||
| 111 | astgen.decl_map.deinit(gpa); | ||
| 112 | astgen.decls.deinit(gpa); | ||
| 113 | } | ||
| 114 | |||
| 115 | pub const ResultLoc = union(enum) { | ||
| 116 | /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the | ||
| 117 | /// expression should be generated. The result instruction from the expression must | ||
| 118 | /// be ignored. | ||
| 119 | discard, | ||
| 120 | /// The expression has an inferred type, and it will be evaluated as an rvalue. | ||
| 121 | none, | ||
| 122 | /// The expression must generate a pointer rather than a value. For example, the left hand side | ||
| 123 | /// of an assignment uses this kind of result location. | ||
| 124 | ref, | ||
| 125 | /// The expression will be coerced into this type, but it will be evaluated as an rvalue. | ||
| 126 | ty: zir.Inst.Ref, | ||
| 127 | /// The expression must store its result into this typed pointer. The result instruction | ||
| 128 | /// from the expression must be ignored. | ||
| 129 | ptr: zir.Inst.Ref, | ||
| 130 | /// The expression must store its result into this allocation, which has an inferred type. | ||
| 131 | /// The result instruction from the expression must be ignored. | ||
| 132 | /// Always an instruction with tag `alloc_inferred`. | ||
| 133 | inferred_ptr: zir.Inst.Ref, | ||
| 134 | /// The expression must store its result into this pointer, which is a typed pointer that | ||
| 135 | /// has been bitcasted to whatever the expression's type is. | ||
| 136 | /// The result instruction from the expression must be ignored. | ||
| 137 | bitcasted_ptr: zir.Inst.Ref, | ||
| 138 | /// There is a pointer for the expression to store its result into, however, its type | ||
| 139 | /// is inferred based on peer type resolution for a `zir.Inst.Block`. | ||
| 140 | /// The result instruction from the expression must be ignored. | ||
| 141 | block_ptr: *Module.Scope.GenZir, | ||
| 142 | |||
| 143 | pub const Strategy = struct { | ||
| 144 | elide_store_to_block_ptr_instructions: bool, | ||
| 145 | tag: Tag, | ||
| 146 | |||
| 147 | pub const Tag = enum { | ||
| 148 | /// Both branches will use break_void; result location is used to communicate the | ||
| 149 | /// result instruction. | ||
| 150 | break_void, | ||
| 151 | /// Use break statements to pass the block result value, and call rvalue() at | ||
| 152 | /// the end depending on rl. Also elide the store_to_block_ptr instructions | ||
| 153 | /// depending on rl. | ||
| 154 | break_operand, | ||
| 155 | }; | ||
| 156 | }; | ||
| 157 | }; | ||
| 158 | |||
| 159 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 160 | return expr(mod, scope, .{ .ty = .type_type }, type_node); | ||
| 161 | } | ||
| 162 | |||
| 163 | fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 164 | const tree = scope.tree(); | ||
| 165 | const node_tags = tree.nodes.items(.tag); | ||
| 166 | const main_tokens = tree.nodes.items(.main_token); | ||
| 167 | switch (node_tags[node]) { | ||
| 168 | .root => unreachable, | ||
| 169 | .@"usingnamespace" => unreachable, | ||
| 170 | .test_decl => unreachable, | ||
| 171 | .global_var_decl => unreachable, | ||
| 172 | .local_var_decl => unreachable, | ||
| 173 | .simple_var_decl => unreachable, | ||
| 174 | .aligned_var_decl => unreachable, | ||
| 175 | .switch_case => unreachable, | ||
| 176 | .switch_case_one => unreachable, | ||
| 177 | .container_field_init => unreachable, | ||
| 178 | .container_field_align => unreachable, | ||
| 179 | .container_field => unreachable, | ||
| 180 | .asm_output => unreachable, | ||
| 181 | .asm_input => unreachable, | ||
| 182 | |||
| 183 | .assign, | ||
| 184 | .assign_bit_and, | ||
| 185 | .assign_bit_or, | ||
| 186 | .assign_bit_shift_left, | ||
| 187 | .assign_bit_shift_right, | ||
| 188 | .assign_bit_xor, | ||
| 189 | .assign_div, | ||
| 190 | .assign_sub, | ||
| 191 | .assign_sub_wrap, | ||
| 192 | .assign_mod, | ||
| 193 | .assign_add, | ||
| 194 | .assign_add_wrap, | ||
| 195 | .assign_mul, | ||
| 196 | .assign_mul_wrap, | ||
| 197 | .add, | ||
| 198 | .add_wrap, | ||
| 199 | .sub, | ||
| 200 | .sub_wrap, | ||
| 201 | .mul, | ||
| 202 | .mul_wrap, | ||
| 203 | .div, | ||
| 204 | .mod, | ||
| 205 | .bit_and, | ||
| 206 | .bit_or, | ||
| 207 | .bit_shift_left, | ||
| 208 | .bit_shift_right, | ||
| 209 | .bit_xor, | ||
| 210 | .bang_equal, | ||
| 211 | .equal_equal, | ||
| 212 | .greater_than, | ||
| 213 | .greater_or_equal, | ||
| 214 | .less_than, | ||
| 215 | .less_or_equal, | ||
| 216 | .array_cat, | ||
| 217 | .array_mult, | ||
| 218 | .bool_and, | ||
| 219 | .bool_or, | ||
| 220 | .@"asm", | ||
| 221 | .asm_simple, | ||
| 222 | .string_literal, | ||
| 223 | .integer_literal, | ||
| 224 | .call, | ||
| 225 | .call_comma, | ||
| 226 | .async_call, | ||
| 227 | .async_call_comma, | ||
| 228 | .call_one, | ||
| 229 | .call_one_comma, | ||
| 230 | .async_call_one, | ||
| 231 | .async_call_one_comma, | ||
| 232 | .unreachable_literal, | ||
| 233 | .@"return", | ||
| 234 | .@"if", | ||
| 235 | .if_simple, | ||
| 236 | .@"while", | ||
| 237 | .while_simple, | ||
| 238 | .while_cont, | ||
| 239 | .bool_not, | ||
| 240 | .address_of, | ||
| 241 | .float_literal, | ||
| 242 | .undefined_literal, | ||
| 243 | .true_literal, | ||
| 244 | .false_literal, | ||
| 245 | .null_literal, | ||
| 246 | .optional_type, | ||
| 247 | .block, | ||
| 248 | .block_semicolon, | ||
| 249 | .block_two, | ||
| 250 | .block_two_semicolon, | ||
| 251 | .@"break", | ||
| 252 | .ptr_type_aligned, | ||
| 253 | .ptr_type_sentinel, | ||
| 254 | .ptr_type, | ||
| 255 | .ptr_type_bit_range, | ||
| 256 | .array_type, | ||
| 257 | .array_type_sentinel, | ||
| 258 | .enum_literal, | ||
| 259 | .multiline_string_literal, | ||
| 260 | .char_literal, | ||
| 261 | .@"defer", | ||
| 262 | .@"errdefer", | ||
| 263 | .@"catch", | ||
| 264 | .error_union, | ||
| 265 | .merge_error_sets, | ||
| 266 | .switch_range, | ||
| 267 | .@"await", | ||
| 268 | .bit_not, | ||
| 269 | .negation, | ||
| 270 | .negation_wrap, | ||
| 271 | .@"resume", | ||
| 272 | .@"try", | ||
| 273 | .slice, | ||
| 274 | .slice_open, | ||
| 275 | .slice_sentinel, | ||
| 276 | .array_init_one, | ||
| 277 | .array_init_one_comma, | ||
| 278 | .array_init_dot_two, | ||
| 279 | .array_init_dot_two_comma, | ||
| 280 | .array_init_dot, | ||
| 281 | .array_init_dot_comma, | ||
| 282 | .array_init, | ||
| 283 | .array_init_comma, | ||
| 284 | .struct_init_one, | ||
| 285 | .struct_init_one_comma, | ||
| 286 | .struct_init_dot_two, | ||
| 287 | .struct_init_dot_two_comma, | ||
| 288 | .struct_init_dot, | ||
| 289 | .struct_init_dot_comma, | ||
| 290 | .struct_init, | ||
| 291 | .struct_init_comma, | ||
| 292 | .@"switch", | ||
| 293 | .switch_comma, | ||
| 294 | .@"for", | ||
| 295 | .for_simple, | ||
| 296 | .@"suspend", | ||
| 297 | .@"continue", | ||
| 298 | .@"anytype", | ||
| 299 | .fn_proto_simple, | ||
| 300 | .fn_proto_multi, | ||
| 301 | .fn_proto_one, | ||
| 302 | .fn_proto, | ||
| 303 | .fn_decl, | ||
| 304 | .anyframe_type, | ||
| 305 | .anyframe_literal, | ||
| 306 | .error_set_decl, | ||
| 307 | .container_decl, | ||
| 308 | .container_decl_trailing, | ||
| 309 | .container_decl_two, | ||
| 310 | .container_decl_two_trailing, | ||
| 311 | .container_decl_arg, | ||
| 312 | .container_decl_arg_trailing, | ||
| 313 | .tagged_union, | ||
| 314 | .tagged_union_trailing, | ||
| 315 | .tagged_union_two, | ||
| 316 | .tagged_union_two_trailing, | ||
| 317 | .tagged_union_enum_tag, | ||
| 318 | .tagged_union_enum_tag_trailing, | ||
| 319 | .@"comptime", | ||
| 320 | .@"nosuspend", | ||
| 321 | .error_value, | ||
| 322 | => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), | ||
| 323 | |||
| 324 | .builtin_call, | ||
| 325 | .builtin_call_comma, | ||
| 326 | .builtin_call_two, | ||
| 327 | .builtin_call_two_comma, | ||
| 328 | => { | ||
| 329 | const builtin_token = main_tokens[node]; | ||
| 330 | const builtin_name = tree.tokenSlice(builtin_token); | ||
| 331 | // If the builtin is an invalid name, we don't cause an error here; instead | ||
| 332 | // let it pass, and the error will be "invalid builtin function" later. | ||
| 333 | if (BuiltinFn.list.get(builtin_name)) |info| { | ||
| 334 | if (!info.allows_lvalue) { | ||
| 335 | return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}); | ||
| 336 | } | ||
| 337 | } | ||
| 338 | }, | ||
| 339 | |||
| 340 | // These can be assigned to. | ||
| 341 | .unwrap_optional, | ||
| 342 | .deref, | ||
| 343 | .field_access, | ||
| 344 | .array_access, | ||
| 345 | .identifier, | ||
| 346 | .grouped_expression, | ||
| 347 | .@"orelse", | ||
| 348 | => {}, | ||
| 349 | } | ||
| 350 | return expr(mod, scope, .ref, node); | ||
| 351 | } | ||
| 352 | |||
| 353 | /// Turn Zig AST into untyped ZIR istructions. | ||
| 354 | /// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the | ||
| 355 | /// result instruction can be used to inspect whether it is isNoReturn() but that is it, | ||
| 356 | /// it must otherwise not be used. | ||
| 357 | pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 358 | const tree = scope.tree(); | ||
| 359 | const main_tokens = tree.nodes.items(.main_token); | ||
| 360 | const token_tags = tree.tokens.items(.tag); | ||
| 361 | const node_datas = tree.nodes.items(.data); | ||
| 362 | const node_tags = tree.nodes.items(.tag); | ||
| 363 | |||
| 364 | const gz = scope.getGenZir(); | ||
| 365 | |||
| 366 | switch (node_tags[node]) { | ||
| 367 | .root => unreachable, // Top-level declaration. | ||
| 368 | .@"usingnamespace" => unreachable, // Top-level declaration. | ||
| 369 | .test_decl => unreachable, // Top-level declaration. | ||
| 370 | .container_field_init => unreachable, // Top-level declaration. | ||
| 371 | .container_field_align => unreachable, // Top-level declaration. | ||
| 372 | .container_field => unreachable, // Top-level declaration. | ||
| 373 | .fn_decl => unreachable, // Top-level declaration. | ||
| 374 | |||
| 375 | .global_var_decl => unreachable, // Handled in `blockExpr`. | ||
| 376 | .local_var_decl => unreachable, // Handled in `blockExpr`. | ||
| 377 | .simple_var_decl => unreachable, // Handled in `blockExpr`. | ||
| 378 | .aligned_var_decl => unreachable, // Handled in `blockExpr`. | ||
| 379 | |||
| 380 | .switch_case => unreachable, // Handled in `switchExpr`. | ||
| 381 | .switch_case_one => unreachable, // Handled in `switchExpr`. | ||
| 382 | .switch_range => unreachable, // Handled in `switchExpr`. | ||
| 383 | |||
| 384 | .asm_output => unreachable, // Handled in `asmExpr`. | ||
| 385 | .asm_input => unreachable, // Handled in `asmExpr`. | ||
| 386 | |||
| 387 | .assign => { | ||
| 388 | try assign(mod, scope, node); | ||
| 389 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 390 | }, | ||
| 391 | .assign_bit_and => { | ||
| 392 | try assignOp(mod, scope, node, .bit_and); | ||
| 393 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 394 | }, | ||
| 395 | .assign_bit_or => { | ||
| 396 | try assignOp(mod, scope, node, .bit_or); | ||
| 397 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 398 | }, | ||
| 399 | .assign_bit_shift_left => { | ||
| 400 | try assignOp(mod, scope, node, .shl); | ||
| 401 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 402 | }, | ||
| 403 | .assign_bit_shift_right => { | ||
| 404 | try assignOp(mod, scope, node, .shr); | ||
| 405 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 406 | }, | ||
| 407 | .assign_bit_xor => { | ||
| 408 | try assignOp(mod, scope, node, .xor); | ||
| 409 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 410 | }, | ||
| 411 | .assign_div => { | ||
| 412 | try assignOp(mod, scope, node, .div); | ||
| 413 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 414 | }, | ||
| 415 | .assign_sub => { | ||
| 416 | try assignOp(mod, scope, node, .sub); | ||
| 417 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 418 | }, | ||
| 419 | .assign_sub_wrap => { | ||
| 420 | try assignOp(mod, scope, node, .subwrap); | ||
| 421 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 422 | }, | ||
| 423 | .assign_mod => { | ||
| 424 | try assignOp(mod, scope, node, .mod_rem); | ||
| 425 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 426 | }, | ||
| 427 | .assign_add => { | ||
| 428 | try assignOp(mod, scope, node, .add); | ||
| 429 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 430 | }, | ||
| 431 | .assign_add_wrap => { | ||
| 432 | try assignOp(mod, scope, node, .addwrap); | ||
| 433 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 434 | }, | ||
| 435 | .assign_mul => { | ||
| 436 | try assignOp(mod, scope, node, .mul); | ||
| 437 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 438 | }, | ||
| 439 | .assign_mul_wrap => { | ||
| 440 | try assignOp(mod, scope, node, .mulwrap); | ||
| 441 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 442 | }, | ||
| 443 | |||
| 444 | .add => return simpleBinOp(mod, scope, rl, node, .add), | ||
| 445 | .add_wrap => return simpleBinOp(mod, scope, rl, node, .addwrap), | ||
| 446 | .sub => return simpleBinOp(mod, scope, rl, node, .sub), | ||
| 447 | .sub_wrap => return simpleBinOp(mod, scope, rl, node, .subwrap), | ||
| 448 | .mul => return simpleBinOp(mod, scope, rl, node, .mul), | ||
| 449 | .mul_wrap => return simpleBinOp(mod, scope, rl, node, .mulwrap), | ||
| 450 | .div => return simpleBinOp(mod, scope, rl, node, .div), | ||
| 451 | .mod => return simpleBinOp(mod, scope, rl, node, .mod_rem), | ||
| 452 | .bit_and => return simpleBinOp(mod, scope, rl, node, .bit_and), | ||
| 453 | .bit_or => return simpleBinOp(mod, scope, rl, node, .bit_or), | ||
| 454 | .bit_shift_left => return simpleBinOp(mod, scope, rl, node, .shl), | ||
| 455 | .bit_shift_right => return simpleBinOp(mod, scope, rl, node, .shr), | ||
| 456 | .bit_xor => return simpleBinOp(mod, scope, rl, node, .xor), | ||
| 457 | |||
| 458 | .bang_equal => return simpleBinOp(mod, scope, rl, node, .cmp_neq), | ||
| 459 | .equal_equal => return simpleBinOp(mod, scope, rl, node, .cmp_eq), | ||
| 460 | .greater_than => return simpleBinOp(mod, scope, rl, node, .cmp_gt), | ||
| 461 | .greater_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_gte), | ||
| 462 | .less_than => return simpleBinOp(mod, scope, rl, node, .cmp_lt), | ||
| 463 | .less_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_lte), | ||
| 464 | |||
| 465 | .array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat), | ||
| 466 | .array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul), | ||
| 467 | |||
| 468 | .error_union => return simpleBinOp(mod, scope, rl, node, .error_union_type), | ||
| 469 | .merge_error_sets => return simpleBinOp(mod, scope, rl, node, .merge_error_sets), | ||
| 470 | |||
| 471 | .bool_and => return boolBinOp(mod, scope, rl, node, .bool_br_and), | ||
| 472 | .bool_or => return boolBinOp(mod, scope, rl, node, .bool_br_or), | ||
| 473 | |||
| 474 | .bool_not => return boolNot(mod, scope, rl, node), | ||
| 475 | .bit_not => return bitNot(mod, scope, rl, node), | ||
| 476 | |||
| 477 | .negation => return negation(mod, scope, rl, node, .negate), | ||
| 478 | .negation_wrap => return negation(mod, scope, rl, node, .negate_wrap), | ||
| 479 | |||
| 480 | .identifier => return identifier(mod, scope, rl, node), | ||
| 481 | |||
| 482 | .asm_simple => return asmExpr(mod, scope, rl, node, tree.asmSimple(node)), | ||
| 483 | .@"asm" => return asmExpr(mod, scope, rl, node, tree.asmFull(node)), | ||
| 484 | |||
| 485 | .string_literal => return stringLiteral(mod, scope, rl, node), | ||
| 486 | .multiline_string_literal => return multilineStringLiteral(mod, scope, rl, node), | ||
| 487 | |||
| 488 | .integer_literal => return integerLiteral(mod, scope, rl, node), | ||
| 489 | |||
| 490 | .builtin_call_two, .builtin_call_two_comma => { | ||
| 491 | if (node_datas[node].lhs == 0) { | ||
| 492 | const params = [_]ast.Node.Index{}; | ||
| 493 | return builtinCall(mod, scope, rl, node, &params); | ||
| 494 | } else if (node_datas[node].rhs == 0) { | ||
| 495 | const params = [_]ast.Node.Index{node_datas[node].lhs}; | ||
| 496 | return builtinCall(mod, scope, rl, node, &params); | ||
| 497 | } else { | ||
| 498 | const params = [_]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; | ||
| 499 | return builtinCall(mod, scope, rl, node, &params); | ||
| 500 | } | ||
| 501 | }, | ||
| 502 | .builtin_call, .builtin_call_comma => { | ||
| 503 | const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; | ||
| 504 | return builtinCall(mod, scope, rl, node, params); | ||
| 505 | }, | ||
| 506 | |||
| 507 | .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => { | ||
| 508 | var params: [1]ast.Node.Index = undefined; | ||
| 509 | return callExpr(mod, scope, rl, node, tree.callOne(&params, node)); | ||
| 510 | }, | ||
| 511 | .call, .call_comma, .async_call, .async_call_comma => { | ||
| 512 | return callExpr(mod, scope, rl, node, tree.callFull(node)); | ||
| 513 | }, | ||
| 514 | |||
| 515 | .unreachable_literal => { | ||
| 516 | _ = try gz.addAsIndex(.{ | ||
| 517 | .tag = .@"unreachable", | ||
| 518 | .data = .{ .@"unreachable" = .{ | ||
| 519 | .safety = true, | ||
| 520 | .src_node = gz.astgen.decl.nodeIndexToRelative(node), | ||
| 521 | } }, | ||
| 522 | }); | ||
| 523 | return zir.Inst.Ref.unreachable_value; | ||
| 524 | }, | ||
| 525 | .@"return" => return ret(mod, scope, node), | ||
| 526 | .field_access => return fieldAccess(mod, scope, rl, node), | ||
| 527 | .float_literal => return floatLiteral(mod, scope, rl, node), | ||
| 528 | |||
| 529 | .if_simple => return ifExpr(mod, scope, rl, node, tree.ifSimple(node)), | ||
| 530 | .@"if" => return ifExpr(mod, scope, rl, node, tree.ifFull(node)), | ||
| 531 | |||
| 532 | .while_simple => return whileExpr(mod, scope, rl, node, tree.whileSimple(node)), | ||
| 533 | .while_cont => return whileExpr(mod, scope, rl, node, tree.whileCont(node)), | ||
| 534 | .@"while" => return whileExpr(mod, scope, rl, node, tree.whileFull(node)), | ||
| 535 | |||
| 536 | .for_simple => return forExpr(mod, scope, rl, node, tree.forSimple(node)), | ||
| 537 | .@"for" => return forExpr(mod, scope, rl, node, tree.forFull(node)), | ||
| 538 | |||
| 539 | .slice_open => { | ||
| 540 | const lhs = try expr(mod, scope, .ref, node_datas[node].lhs); | ||
| 541 | const start = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs); | ||
| 542 | const result = try gz.addPlNode(.slice_start, node, zir.Inst.SliceStart{ | ||
| 543 | .lhs = lhs, | ||
| 544 | .start = start, | ||
| 545 | }); | ||
| 546 | return rvalue(mod, scope, rl, result, node); | ||
| 547 | }, | ||
| 548 | .slice => { | ||
| 549 | const lhs = try expr(mod, scope, .ref, node_datas[node].lhs); | ||
| 550 | const extra = tree.extraData(node_datas[node].rhs, ast.Node.Slice); | ||
| 551 | const start = try expr(mod, scope, .{ .ty = .usize_type }, extra.start); | ||
| 552 | const end = try expr(mod, scope, .{ .ty = .usize_type }, extra.end); | ||
| 553 | const result = try gz.addPlNode(.slice_end, node, zir.Inst.SliceEnd{ | ||
| 554 | .lhs = lhs, | ||
| 555 | .start = start, | ||
| 556 | .end = end, | ||
| 557 | }); | ||
| 558 | return rvalue(mod, scope, rl, result, node); | ||
| 559 | }, | ||
| 560 | .slice_sentinel => { | ||
| 561 | const lhs = try expr(mod, scope, .ref, node_datas[node].lhs); | ||
| 562 | const extra = tree.extraData(node_datas[node].rhs, ast.Node.SliceSentinel); | ||
| 563 | const start = try expr(mod, scope, .{ .ty = .usize_type }, extra.start); | ||
| 564 | const end = try expr(mod, scope, .{ .ty = .usize_type }, extra.end); | ||
| 565 | const sentinel = try expr(mod, scope, .{ .ty = .usize_type }, extra.sentinel); | ||
| 566 | const result = try gz.addPlNode(.slice_sentinel, node, zir.Inst.SliceSentinel{ | ||
| 567 | .lhs = lhs, | ||
| 568 | .start = start, | ||
| 569 | .end = end, | ||
| 570 | .sentinel = sentinel, | ||
| 571 | }); | ||
| 572 | return rvalue(mod, scope, rl, result, node); | ||
| 573 | }, | ||
| 574 | |||
| 575 | .deref => { | ||
| 576 | const lhs = try expr(mod, scope, .none, node_datas[node].lhs); | ||
| 577 | const result = try gz.addUnNode(.load, lhs, node); | ||
| 578 | return rvalue(mod, scope, rl, result, node); | ||
| 579 | }, | ||
| 580 | .address_of => { | ||
| 581 | const result = try expr(mod, scope, .ref, node_datas[node].lhs); | ||
| 582 | return rvalue(mod, scope, rl, result, node); | ||
| 583 | }, | ||
| 584 | .undefined_literal => return rvalue(mod, scope, rl, .undef, node), | ||
| 585 | .true_literal => return rvalue(mod, scope, rl, .bool_true, node), | ||
| 586 | .false_literal => return rvalue(mod, scope, rl, .bool_false, node), | ||
| 587 | .null_literal => return rvalue(mod, scope, rl, .null_value, node), | ||
| 588 | .optional_type => { | ||
| 589 | const operand = try typeExpr(mod, scope, node_datas[node].lhs); | ||
| 590 | const result = try gz.addUnNode(.optional_type, operand, node); | ||
| 591 | return rvalue(mod, scope, rl, result, node); | ||
| 592 | }, | ||
| 593 | .unwrap_optional => switch (rl) { | ||
| 594 | .ref => return gz.addUnNode( | ||
| 595 | .optional_payload_safe_ptr, | ||
| 596 | try expr(mod, scope, .ref, node_datas[node].lhs), | ||
| 597 | node, | ||
| 598 | ), | ||
| 599 | else => return rvalue(mod, scope, rl, try gz.addUnNode( | ||
| 600 | .optional_payload_safe, | ||
| 601 | try expr(mod, scope, .none, node_datas[node].lhs), | ||
| 602 | node, | ||
| 603 | ), node), | ||
| 604 | }, | ||
| 605 | .block_two, .block_two_semicolon => { | ||
| 606 | const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; | ||
| 607 | if (node_datas[node].lhs == 0) { | ||
| 608 | return blockExpr(mod, scope, rl, node, statements[0..0]); | ||
| 609 | } else if (node_datas[node].rhs == 0) { | ||
| 610 | return blockExpr(mod, scope, rl, node, statements[0..1]); | ||
| 611 | } else { | ||
| 612 | return blockExpr(mod, scope, rl, node, statements[0..2]); | ||
| 613 | } | ||
| 614 | }, | ||
| 615 | .block, .block_semicolon => { | ||
| 616 | const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; | ||
| 617 | return blockExpr(mod, scope, rl, node, statements); | ||
| 618 | }, | ||
| 619 | .enum_literal => return simpleStrTok(mod, scope, rl, main_tokens[node], node, .enum_literal), | ||
| 620 | .error_value => return simpleStrTok(mod, scope, rl, node_datas[node].rhs, node, .error_value), | ||
| 621 | .anyframe_literal => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 622 | .anyframe_type => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 623 | .@"catch" => { | ||
| 624 | const catch_token = main_tokens[node]; | ||
| 625 | const payload_token: ?ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) | ||
| 626 | catch_token + 2 | ||
| 627 | else | ||
| 628 | null; | ||
| 629 | switch (rl) { | ||
| 630 | .ref => return orelseCatchExpr( | ||
| 631 | mod, | ||
| 632 | scope, | ||
| 633 | rl, | ||
| 634 | node, | ||
| 635 | node_datas[node].lhs, | ||
| 636 | .is_err_ptr, | ||
| 637 | .err_union_payload_unsafe_ptr, | ||
| 638 | .err_union_code_ptr, | ||
| 639 | node_datas[node].rhs, | ||
| 640 | payload_token, | ||
| 641 | ), | ||
| 642 | else => return orelseCatchExpr( | ||
| 643 | mod, | ||
| 644 | scope, | ||
| 645 | rl, | ||
| 646 | node, | ||
| 647 | node_datas[node].lhs, | ||
| 648 | .is_err, | ||
| 649 | .err_union_payload_unsafe, | ||
| 650 | .err_union_code, | ||
| 651 | node_datas[node].rhs, | ||
| 652 | payload_token, | ||
| 653 | ), | ||
| 654 | } | ||
| 655 | }, | ||
| 656 | .@"orelse" => switch (rl) { | ||
| 657 | .ref => return orelseCatchExpr( | ||
| 658 | mod, | ||
| 659 | scope, | ||
| 660 | rl, | ||
| 661 | node, | ||
| 662 | node_datas[node].lhs, | ||
| 663 | .is_null_ptr, | ||
| 664 | .optional_payload_unsafe_ptr, | ||
| 665 | undefined, | ||
| 666 | node_datas[node].rhs, | ||
| 667 | null, | ||
| 668 | ), | ||
| 669 | else => return orelseCatchExpr( | ||
| 670 | mod, | ||
| 671 | scope, | ||
| 672 | rl, | ||
| 673 | node, | ||
| 674 | node_datas[node].lhs, | ||
| 675 | .is_null, | ||
| 676 | .optional_payload_unsafe, | ||
| 677 | undefined, | ||
| 678 | node_datas[node].rhs, | ||
| 679 | null, | ||
| 680 | ), | ||
| 681 | }, | ||
| 682 | |||
| 683 | .ptr_type_aligned => return ptrType(mod, scope, rl, node, tree.ptrTypeAligned(node)), | ||
| 684 | .ptr_type_sentinel => return ptrType(mod, scope, rl, node, tree.ptrTypeSentinel(node)), | ||
| 685 | .ptr_type => return ptrType(mod, scope, rl, node, tree.ptrType(node)), | ||
| 686 | .ptr_type_bit_range => return ptrType(mod, scope, rl, node, tree.ptrTypeBitRange(node)), | ||
| 687 | |||
| 688 | .container_decl, | ||
| 689 | .container_decl_trailing, | ||
| 690 | => return containerDecl(mod, scope, rl, tree.containerDecl(node)), | ||
| 691 | .container_decl_two, .container_decl_two_trailing => { | ||
| 692 | var buffer: [2]ast.Node.Index = undefined; | ||
| 693 | return containerDecl(mod, scope, rl, tree.containerDeclTwo(&buffer, node)); | ||
| 694 | }, | ||
| 695 | .container_decl_arg, | ||
| 696 | .container_decl_arg_trailing, | ||
| 697 | => return containerDecl(mod, scope, rl, tree.containerDeclArg(node)), | ||
| 698 | |||
| 699 | .tagged_union, | ||
| 700 | .tagged_union_trailing, | ||
| 701 | => return containerDecl(mod, scope, rl, tree.taggedUnion(node)), | ||
| 702 | .tagged_union_two, .tagged_union_two_trailing => { | ||
| 703 | var buffer: [2]ast.Node.Index = undefined; | ||
| 704 | return containerDecl(mod, scope, rl, tree.taggedUnionTwo(&buffer, node)); | ||
| 705 | }, | ||
| 706 | .tagged_union_enum_tag, | ||
| 707 | .tagged_union_enum_tag_trailing, | ||
| 708 | => return containerDecl(mod, scope, rl, tree.taggedUnionEnumTag(node)), | ||
| 709 | |||
| 710 | .@"break" => return breakExpr(mod, scope, node), | ||
| 711 | .@"continue" => return continueExpr(mod, scope, node), | ||
| 712 | .grouped_expression => return expr(mod, scope, rl, node_datas[node].lhs), | ||
| 713 | .array_type => return arrayType(mod, scope, rl, node), | ||
| 714 | .array_type_sentinel => return arrayTypeSentinel(mod, scope, rl, node), | ||
| 715 | .char_literal => return charLiteral(mod, scope, rl, node), | ||
| 716 | .error_set_decl => return errorSetDecl(mod, scope, rl, node), | ||
| 717 | .array_access => return arrayAccess(mod, scope, rl, node), | ||
| 718 | .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs), | ||
| 719 | .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node), | ||
| 720 | |||
| 721 | .@"nosuspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 722 | .@"suspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 723 | .@"await" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 724 | .@"resume" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 725 | |||
| 726 | .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}), | ||
| 727 | .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}), | ||
| 728 | .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), | ||
| 729 | |||
| 730 | .array_init_one, | ||
| 731 | .array_init_one_comma, | ||
| 732 | .array_init_dot_two, | ||
| 733 | .array_init_dot_two_comma, | ||
| 734 | .array_init_dot, | ||
| 735 | .array_init_dot_comma, | ||
| 736 | .array_init, | ||
| 737 | .array_init_comma, | ||
| 738 | => return mod.failNode(scope, node, "TODO implement astgen.expr for array literals", .{}), | ||
| 739 | |||
| 740 | .struct_init_one, | ||
| 741 | .struct_init_one_comma, | ||
| 742 | .struct_init_dot_two, | ||
| 743 | .struct_init_dot_two_comma, | ||
| 744 | .struct_init_dot, | ||
| 745 | .struct_init_dot_comma, | ||
| 746 | .struct_init, | ||
| 747 | .struct_init_comma, | ||
| 748 | => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}), | ||
| 749 | |||
| 750 | .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}), | ||
| 751 | .fn_proto_simple, | ||
| 752 | .fn_proto_multi, | ||
| 753 | .fn_proto_one, | ||
| 754 | .fn_proto, | ||
| 755 | => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}), | ||
| 756 | } | ||
| 757 | } | ||
| 758 | |||
| 759 | pub fn comptimeExpr( | ||
| 760 | mod: *Module, | ||
| 761 | parent_scope: *Scope, | ||
| 762 | rl: ResultLoc, | ||
| 763 | node: ast.Node.Index, | ||
| 764 | ) InnerError!zir.Inst.Ref { | ||
| 765 | const gz = parent_scope.getGenZir(); | ||
| 766 | |||
| 767 | const prev_force_comptime = gz.force_comptime; | ||
| 768 | gz.force_comptime = true; | ||
| 769 | const result = try expr(mod, parent_scope, rl, node); | ||
| 770 | gz.force_comptime = prev_force_comptime; | ||
| 771 | return result; | ||
| 772 | } | ||
| 773 | |||
| 774 | fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 775 | const parent_gz = parent_scope.getGenZir(); | ||
| 776 | const tree = parent_gz.tree(); | ||
| 777 | const node_datas = tree.nodes.items(.data); | ||
| 778 | const break_label = node_datas[node].lhs; | ||
| 779 | const rhs = node_datas[node].rhs; | ||
| 780 | |||
| 781 | // Look for the label in the scope. | ||
| 782 | var scope = parent_scope; | ||
| 783 | while (true) { | ||
| 784 | switch (scope.tag) { | ||
| 785 | .gen_zir => { | ||
| 786 | const block_gz = scope.cast(Scope.GenZir).?; | ||
| 787 | |||
| 788 | const block_inst = blk: { | ||
| 789 | if (break_label != 0) { | ||
| 790 | if (block_gz.label) |*label| { | ||
| 791 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { | ||
| 792 | label.used = true; | ||
| 793 | break :blk label.block_inst; | ||
| 794 | } | ||
| 795 | } | ||
| 796 | } else if (block_gz.break_block != 0) { | ||
| 797 | break :blk block_gz.break_block; | ||
| 798 | } | ||
| 799 | scope = block_gz.parent; | ||
| 800 | continue; | ||
| 801 | }; | ||
| 802 | |||
| 803 | if (rhs == 0) { | ||
| 804 | _ = try parent_gz.addBreak(.@"break", block_inst, .void_value); | ||
| 805 | return zir.Inst.Ref.unreachable_value; | ||
| 806 | } | ||
| 807 | block_gz.break_count += 1; | ||
| 808 | const prev_rvalue_rl_count = block_gz.rvalue_rl_count; | ||
| 809 | const operand = try expr(mod, parent_scope, block_gz.break_result_loc, rhs); | ||
| 810 | const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count; | ||
| 811 | |||
| 812 | const br = try parent_gz.addBreak(.@"break", block_inst, operand); | ||
| 813 | |||
| 814 | if (block_gz.break_result_loc == .block_ptr) { | ||
| 815 | try block_gz.labeled_breaks.append(mod.gpa, br); | ||
| 816 | |||
| 817 | if (have_store_to_block) { | ||
| 818 | const zir_tags = parent_gz.astgen.instructions.items(.tag); | ||
| 819 | const zir_datas = parent_gz.astgen.instructions.items(.data); | ||
| 820 | const store_inst = @intCast(u32, zir_tags.len - 2); | ||
| 821 | assert(zir_tags[store_inst] == .store_to_block_ptr); | ||
| 822 | assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr); | ||
| 823 | try block_gz.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst); | ||
| 824 | } | ||
| 825 | } | ||
| 826 | return zir.Inst.Ref.unreachable_value; | ||
| 827 | }, | ||
| 828 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 829 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 830 | else => if (break_label != 0) { | ||
| 831 | const label_name = try mod.identifierTokenString(parent_scope, break_label); | ||
| 832 | return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); | ||
| 833 | } else { | ||
| 834 | return mod.failNode(parent_scope, node, "break expression outside loop", .{}); | ||
| 835 | }, | ||
| 836 | } | ||
| 837 | } | ||
| 838 | } | ||
| 839 | |||
| 840 | fn continueExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 841 | const parent_gz = parent_scope.getGenZir(); | ||
| 842 | const tree = parent_gz.tree(); | ||
| 843 | const node_datas = tree.nodes.items(.data); | ||
| 844 | const break_label = node_datas[node].lhs; | ||
| 845 | |||
| 846 | // Look for the label in the scope. | ||
| 847 | var scope = parent_scope; | ||
| 848 | while (true) { | ||
| 849 | switch (scope.tag) { | ||
| 850 | .gen_zir => { | ||
| 851 | const gen_zir = scope.cast(Scope.GenZir).?; | ||
| 852 | const continue_block = gen_zir.continue_block; | ||
| 853 | if (continue_block == 0) { | ||
| 854 | scope = gen_zir.parent; | ||
| 855 | continue; | ||
| 856 | } | ||
| 857 | if (break_label != 0) blk: { | ||
| 858 | if (gen_zir.label) |*label| { | ||
| 859 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { | ||
| 860 | label.used = true; | ||
| 861 | break :blk; | ||
| 862 | } | ||
| 863 | } | ||
| 864 | // found continue but either it has a different label, or no label | ||
| 865 | scope = gen_zir.parent; | ||
| 866 | continue; | ||
| 867 | } | ||
| 868 | |||
| 869 | // TODO emit a break_inline if the loop being continued is inline | ||
| 870 | _ = try parent_gz.addBreak(.@"break", continue_block, .void_value); | ||
| 871 | return zir.Inst.Ref.unreachable_value; | ||
| 872 | }, | ||
| 873 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 874 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 875 | else => if (break_label != 0) { | ||
| 876 | const label_name = try mod.identifierTokenString(parent_scope, break_label); | ||
| 877 | return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); | ||
| 878 | } else { | ||
| 879 | return mod.failNode(parent_scope, node, "continue expression outside loop", .{}); | ||
| 880 | }, | ||
| 881 | } | ||
| 882 | } | ||
| 883 | } | ||
| 884 | |||
| 885 | pub fn blockExpr( | ||
| 886 | mod: *Module, | ||
| 887 | scope: *Scope, | ||
| 888 | rl: ResultLoc, | ||
| 889 | block_node: ast.Node.Index, | ||
| 890 | statements: []const ast.Node.Index, | ||
| 891 | ) InnerError!zir.Inst.Ref { | ||
| 892 | const tracy = trace(@src()); | ||
| 893 | defer tracy.end(); | ||
| 894 | |||
| 895 | const tree = scope.tree(); | ||
| 896 | const main_tokens = tree.nodes.items(.main_token); | ||
| 897 | const token_tags = tree.tokens.items(.tag); | ||
| 898 | |||
| 899 | const lbrace = main_tokens[block_node]; | ||
| 900 | if (token_tags[lbrace - 1] == .colon and | ||
| 901 | token_tags[lbrace - 2] == .identifier) | ||
| 902 | { | ||
| 903 | return labeledBlockExpr(mod, scope, rl, block_node, statements, .block); | ||
| 904 | } | ||
| 905 | |||
| 906 | try blockExprStmts(mod, scope, block_node, statements); | ||
| 907 | return rvalue(mod, scope, rl, .void_value, block_node); | ||
| 908 | } | ||
| 909 | |||
| 910 | fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void { | ||
| 911 | // Look for the label in the scope. | ||
| 912 | var scope = parent_scope; | ||
| 913 | while (true) { | ||
| 914 | switch (scope.tag) { | ||
| 915 | .gen_zir => { | ||
| 916 | const gen_zir = scope.cast(Scope.GenZir).?; | ||
| 917 | if (gen_zir.label) |prev_label| { | ||
| 918 | if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) { | ||
| 919 | const tree = parent_scope.tree(); | ||
| 920 | const main_tokens = tree.nodes.items(.main_token); | ||
| 921 | |||
| 922 | const label_name = try mod.identifierTokenString(parent_scope, label); | ||
| 923 | const msg = msg: { | ||
| 924 | const msg = try mod.errMsg( | ||
| 925 | parent_scope, | ||
| 926 | gen_zir.tokSrcLoc(label), | ||
| 927 | "redefinition of label '{s}'", | ||
| 928 | .{label_name}, | ||
| 929 | ); | ||
| 930 | errdefer msg.destroy(mod.gpa); | ||
| 931 | try mod.errNote( | ||
| 932 | parent_scope, | ||
| 933 | gen_zir.tokSrcLoc(prev_label.token), | ||
| 934 | msg, | ||
| 935 | "previous definition is here", | ||
| 936 | .{}, | ||
| 937 | ); | ||
| 938 | break :msg msg; | ||
| 939 | }; | ||
| 940 | return mod.failWithOwnedErrorMsg(parent_scope, msg); | ||
| 941 | } | ||
| 942 | } | ||
| 943 | scope = gen_zir.parent; | ||
| 944 | }, | ||
| 945 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 946 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 947 | else => return, | ||
| 948 | } | ||
| 949 | } | ||
| 950 | } | ||
| 951 | |||
| 952 | fn labeledBlockExpr( | ||
| 953 | mod: *Module, | ||
| 954 | parent_scope: *Scope, | ||
| 955 | rl: ResultLoc, | ||
| 956 | block_node: ast.Node.Index, | ||
| 957 | statements: []const ast.Node.Index, | ||
| 958 | zir_tag: zir.Inst.Tag, | ||
| 959 | ) InnerError!zir.Inst.Ref { | ||
| 960 | const tracy = trace(@src()); | ||
| 961 | defer tracy.end(); | ||
| 962 | |||
| 963 | assert(zir_tag == .block); | ||
| 964 | |||
| 965 | const tree = parent_scope.tree(); | ||
| 966 | const main_tokens = tree.nodes.items(.main_token); | ||
| 967 | const token_tags = tree.tokens.items(.tag); | ||
| 968 | |||
| 969 | const lbrace = main_tokens[block_node]; | ||
| 970 | const label_token = lbrace - 2; | ||
| 971 | assert(token_tags[label_token] == .identifier); | ||
| 972 | |||
| 973 | try checkLabelRedefinition(mod, parent_scope, label_token); | ||
| 974 | |||
| 975 | // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct | ||
| 976 | // so that break statements can reference it. | ||
| 977 | const gz = parent_scope.getGenZir(); | ||
| 978 | const block_inst = try gz.addBlock(zir_tag, block_node); | ||
| 979 | try gz.instructions.append(mod.gpa, block_inst); | ||
| 980 | |||
| 981 | var block_scope: Scope.GenZir = .{ | ||
| 982 | .parent = parent_scope, | ||
| 983 | .astgen = gz.astgen, | ||
| 984 | .force_comptime = gz.force_comptime, | ||
| 985 | .instructions = .{}, | ||
| 986 | // TODO @as here is working around a stage1 miscompilation bug :( | ||
| 987 | .label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{ | ||
| 988 | .token = label_token, | ||
| 989 | .block_inst = block_inst, | ||
| 990 | }), | ||
| 991 | }; | ||
| 992 | setBlockResultLoc(&block_scope, rl); | ||
| 993 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 994 | defer block_scope.labeled_breaks.deinit(mod.gpa); | ||
| 995 | defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); | ||
| 996 | |||
| 997 | try blockExprStmts(mod, &block_scope.base, block_node, statements); | ||
| 998 | |||
| 999 | if (!block_scope.label.?.used) { | ||
| 1000 | return mod.failTok(parent_scope, label_token, "unused block label", .{}); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | const zir_tags = gz.astgen.instructions.items(.tag); | ||
| 1004 | const zir_datas = gz.astgen.instructions.items(.data); | ||
| 1005 | |||
| 1006 | const strat = rlStrategy(rl, &block_scope); | ||
| 1007 | switch (strat.tag) { | ||
| 1008 | .break_void => { | ||
| 1009 | // The code took advantage of the result location as a pointer. | ||
| 1010 | // Turn the break instruction operands into void. | ||
| 1011 | for (block_scope.labeled_breaks.items) |br| { | ||
| 1012 | zir_datas[br].@"break".operand = .void_value; | ||
| 1013 | } | ||
| 1014 | try block_scope.setBlockBody(block_inst); | ||
| 1015 | |||
| 1016 | return gz.astgen.indexToRef(block_inst); | ||
| 1017 | }, | ||
| 1018 | .break_operand => { | ||
| 1019 | // All break operands are values that did not use the result location pointer. | ||
| 1020 | if (strat.elide_store_to_block_ptr_instructions) { | ||
| 1021 | for (block_scope.labeled_store_to_block_ptr_list.items) |inst| { | ||
| 1022 | zir_tags[inst] = .elided; | ||
| 1023 | zir_datas[inst] = undefined; | ||
| 1024 | } | ||
| 1025 | // TODO technically not needed since we changed the tag to elided but | ||
| 1026 | // would be better still to elide the ones that are in this list. | ||
| 1027 | } | ||
| 1028 | try block_scope.setBlockBody(block_inst); | ||
| 1029 | const block_ref = gz.astgen.indexToRef(block_inst); | ||
| 1030 | switch (rl) { | ||
| 1031 | .ref => return block_ref, | ||
| 1032 | else => return rvalue(mod, parent_scope, rl, block_ref, block_node), | ||
| 1033 | } | ||
| 1034 | }, | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | fn blockExprStmts( | ||
| 1039 | mod: *Module, | ||
| 1040 | parent_scope: *Scope, | ||
| 1041 | node: ast.Node.Index, | ||
| 1042 | statements: []const ast.Node.Index, | ||
| 1043 | ) !void { | ||
| 1044 | const tree = parent_scope.tree(); | ||
| 1045 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1046 | const node_tags = tree.nodes.items(.tag); | ||
| 1047 | |||
| 1048 | var block_arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 1049 | defer block_arena.deinit(); | ||
| 1050 | |||
| 1051 | const gz = parent_scope.getGenZir(); | ||
| 1052 | |||
| 1053 | var scope = parent_scope; | ||
| 1054 | for (statements) |statement| { | ||
| 1055 | if (!gz.force_comptime) { | ||
| 1056 | _ = try gz.addNode(.dbg_stmt_node, statement); | ||
| 1057 | } | ||
| 1058 | switch (node_tags[statement]) { | ||
| 1059 | .global_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.globalVarDecl(statement)), | ||
| 1060 | .local_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.localVarDecl(statement)), | ||
| 1061 | .simple_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.simpleVarDecl(statement)), | ||
| 1062 | .aligned_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.alignedVarDecl(statement)), | ||
| 1063 | |||
| 1064 | .assign => try assign(mod, scope, statement), | ||
| 1065 | .assign_bit_and => try assignOp(mod, scope, statement, .bit_and), | ||
| 1066 | .assign_bit_or => try assignOp(mod, scope, statement, .bit_or), | ||
| 1067 | .assign_bit_shift_left => try assignOp(mod, scope, statement, .shl), | ||
| 1068 | .assign_bit_shift_right => try assignOp(mod, scope, statement, .shr), | ||
| 1069 | .assign_bit_xor => try assignOp(mod, scope, statement, .xor), | ||
| 1070 | .assign_div => try assignOp(mod, scope, statement, .div), | ||
| 1071 | .assign_sub => try assignOp(mod, scope, statement, .sub), | ||
| 1072 | .assign_sub_wrap => try assignOp(mod, scope, statement, .subwrap), | ||
| 1073 | .assign_mod => try assignOp(mod, scope, statement, .mod_rem), | ||
| 1074 | .assign_add => try assignOp(mod, scope, statement, .add), | ||
| 1075 | .assign_add_wrap => try assignOp(mod, scope, statement, .addwrap), | ||
| 1076 | .assign_mul => try assignOp(mod, scope, statement, .mul), | ||
| 1077 | .assign_mul_wrap => try assignOp(mod, scope, statement, .mulwrap), | ||
| 1078 | |||
| 1079 | else => { | ||
| 1080 | // We need to emit an error if the result is not `noreturn` or `void`, but | ||
| 1081 | // we want to avoid adding the ZIR instruction if possible for performance. | ||
| 1082 | const maybe_unused_result = try expr(mod, scope, .none, statement); | ||
| 1083 | const elide_check = if (gz.astgen.refToIndex(maybe_unused_result)) |inst| b: { | ||
| 1084 | // Note that this array becomes invalid after appending more items to it | ||
| 1085 | // in the above while loop. | ||
| 1086 | const zir_tags = gz.astgen.instructions.items(.tag); | ||
| 1087 | switch (zir_tags[inst]) { | ||
| 1088 | .@"const" => { | ||
| 1089 | const tv = gz.astgen.instructions.items(.data)[inst].@"const"; | ||
| 1090 | break :b switch (tv.ty.zigTypeTag()) { | ||
| 1091 | .NoReturn, .Void => true, | ||
| 1092 | else => false, | ||
| 1093 | }; | ||
| 1094 | }, | ||
| 1095 | // For some instructions, swap in a slightly different ZIR tag | ||
| 1096 | // so we can avoid a separate ensure_result_used instruction. | ||
| 1097 | .call_none_chkused => unreachable, | ||
| 1098 | .call_none => { | ||
| 1099 | zir_tags[inst] = .call_none_chkused; | ||
| 1100 | break :b true; | ||
| 1101 | }, | ||
| 1102 | .call_chkused => unreachable, | ||
| 1103 | .call => { | ||
| 1104 | zir_tags[inst] = .call_chkused; | ||
| 1105 | break :b true; | ||
| 1106 | }, | ||
| 1107 | |||
| 1108 | // ZIR instructions that might be a type other than `noreturn` or `void`. | ||
| 1109 | .add, | ||
| 1110 | .addwrap, | ||
| 1111 | .alloc, | ||
| 1112 | .alloc_mut, | ||
| 1113 | .alloc_inferred, | ||
| 1114 | .alloc_inferred_mut, | ||
| 1115 | .array_cat, | ||
| 1116 | .array_mul, | ||
| 1117 | .array_type, | ||
| 1118 | .array_type_sentinel, | ||
| 1119 | .indexable_ptr_len, | ||
| 1120 | .as, | ||
| 1121 | .as_node, | ||
| 1122 | .@"asm", | ||
| 1123 | .asm_volatile, | ||
| 1124 | .bit_and, | ||
| 1125 | .bitcast, | ||
| 1126 | .bitcast_ref, | ||
| 1127 | .bitcast_result_ptr, | ||
| 1128 | .bit_or, | ||
| 1129 | .block, | ||
| 1130 | .block_inline, | ||
| 1131 | .loop, | ||
| 1132 | .bool_br_and, | ||
| 1133 | .bool_br_or, | ||
| 1134 | .bool_not, | ||
| 1135 | .bool_and, | ||
| 1136 | .bool_or, | ||
| 1137 | .call_compile_time, | ||
| 1138 | .cmp_lt, | ||
| 1139 | .cmp_lte, | ||
| 1140 | .cmp_eq, | ||
| 1141 | .cmp_gte, | ||
| 1142 | .cmp_gt, | ||
| 1143 | .cmp_neq, | ||
| 1144 | .coerce_result_ptr, | ||
| 1145 | .decl_ref, | ||
| 1146 | .decl_val, | ||
| 1147 | .load, | ||
| 1148 | .div, | ||
| 1149 | .elem_ptr, | ||
| 1150 | .elem_val, | ||
| 1151 | .elem_ptr_node, | ||
| 1152 | .elem_val_node, | ||
| 1153 | .floatcast, | ||
| 1154 | .field_ptr, | ||
| 1155 | .field_val, | ||
| 1156 | .field_ptr_named, | ||
| 1157 | .field_val_named, | ||
| 1158 | .fn_type, | ||
| 1159 | .fn_type_var_args, | ||
| 1160 | .fn_type_cc, | ||
| 1161 | .fn_type_cc_var_args, | ||
| 1162 | .int, | ||
| 1163 | .intcast, | ||
| 1164 | .int_type, | ||
| 1165 | .is_non_null, | ||
| 1166 | .is_null, | ||
| 1167 | .is_non_null_ptr, | ||
| 1168 | .is_null_ptr, | ||
| 1169 | .is_err, | ||
| 1170 | .is_err_ptr, | ||
| 1171 | .mod_rem, | ||
| 1172 | .mul, | ||
| 1173 | .mulwrap, | ||
| 1174 | .param_type, | ||
| 1175 | .ptrtoint, | ||
| 1176 | .ref, | ||
| 1177 | .ret_ptr, | ||
| 1178 | .ret_type, | ||
| 1179 | .shl, | ||
| 1180 | .shr, | ||
| 1181 | .str, | ||
| 1182 | .sub, | ||
| 1183 | .subwrap, | ||
| 1184 | .negate, | ||
| 1185 | .negate_wrap, | ||
| 1186 | .typeof, | ||
| 1187 | .xor, | ||
| 1188 | .optional_type, | ||
| 1189 | .optional_type_from_ptr_elem, | ||
| 1190 | .optional_payload_safe, | ||
| 1191 | .optional_payload_unsafe, | ||
| 1192 | .optional_payload_safe_ptr, | ||
| 1193 | .optional_payload_unsafe_ptr, | ||
| 1194 | .err_union_payload_safe, | ||
| 1195 | .err_union_payload_unsafe, | ||
| 1196 | .err_union_payload_safe_ptr, | ||
| 1197 | .err_union_payload_unsafe_ptr, | ||
| 1198 | .err_union_code, | ||
| 1199 | .err_union_code_ptr, | ||
| 1200 | .ptr_type, | ||
| 1201 | .ptr_type_simple, | ||
| 1202 | .enum_literal, | ||
| 1203 | .enum_literal_small, | ||
| 1204 | .merge_error_sets, | ||
| 1205 | .error_union_type, | ||
| 1206 | .bit_not, | ||
| 1207 | .error_set, | ||
| 1208 | .error_value, | ||
| 1209 | .slice_start, | ||
| 1210 | .slice_end, | ||
| 1211 | .slice_sentinel, | ||
| 1212 | .import, | ||
| 1213 | .typeof_peer, | ||
| 1214 | => break :b false, | ||
| 1215 | |||
| 1216 | // ZIR instructions that are always either `noreturn` or `void`. | ||
| 1217 | .breakpoint, | ||
| 1218 | .dbg_stmt_node, | ||
| 1219 | .ensure_result_used, | ||
| 1220 | .ensure_result_non_error, | ||
| 1221 | .set_eval_branch_quota, | ||
| 1222 | .compile_log, | ||
| 1223 | .ensure_err_payload_void, | ||
| 1224 | .@"break", | ||
| 1225 | .break_inline, | ||
| 1226 | .condbr, | ||
| 1227 | .condbr_inline, | ||
| 1228 | .compile_error, | ||
| 1229 | .ret_node, | ||
| 1230 | .ret_tok, | ||
| 1231 | .ret_coerce, | ||
| 1232 | .@"unreachable", | ||
| 1233 | .elided, | ||
| 1234 | .store, | ||
| 1235 | .store_node, | ||
| 1236 | .store_to_block_ptr, | ||
| 1237 | .store_to_inferred_ptr, | ||
| 1238 | .resolve_inferred_alloc, | ||
| 1239 | .repeat, | ||
| 1240 | .repeat_inline, | ||
| 1241 | => break :b true, | ||
| 1242 | } | ||
| 1243 | } else switch (maybe_unused_result) { | ||
| 1244 | .none => unreachable, | ||
| 1245 | |||
| 1246 | .void_value, | ||
| 1247 | .unreachable_value, | ||
| 1248 | => true, | ||
| 1249 | |||
| 1250 | else => false, | ||
| 1251 | }; | ||
| 1252 | if (!elide_check) { | ||
| 1253 | _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement); | ||
| 1254 | } | ||
| 1255 | }, | ||
| 1256 | } | ||
| 1257 | } | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | fn varDecl( | ||
| 1261 | mod: *Module, | ||
| 1262 | scope: *Scope, | ||
| 1263 | node: ast.Node.Index, | ||
| 1264 | block_arena: *Allocator, | ||
| 1265 | var_decl: ast.full.VarDecl, | ||
| 1266 | ) InnerError!*Scope { | ||
| 1267 | if (var_decl.comptime_token) |comptime_token| { | ||
| 1268 | return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{}); | ||
| 1269 | } | ||
| 1270 | if (var_decl.ast.align_node != 0) { | ||
| 1271 | return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{}); | ||
| 1272 | } | ||
| 1273 | const gz = scope.getGenZir(); | ||
| 1274 | const astgen = gz.astgen; | ||
| 1275 | const tree = scope.tree(); | ||
| 1276 | const token_tags = tree.tokens.items(.tag); | ||
| 1277 | |||
| 1278 | const name_token = var_decl.ast.mut_token + 1; | ||
| 1279 | const name_src = gz.tokSrcLoc(name_token); | ||
| 1280 | const ident_name = try mod.identifierTokenString(scope, name_token); | ||
| 1281 | |||
| 1282 | // Local variables shadowing detection, including function parameters. | ||
| 1283 | { | ||
| 1284 | var s = scope; | ||
| 1285 | while (true) switch (s.tag) { | ||
| 1286 | .local_val => { | ||
| 1287 | const local_val = s.cast(Scope.LocalVal).?; | ||
| 1288 | if (mem.eql(u8, local_val.name, ident_name)) { | ||
| 1289 | const msg = msg: { | ||
| 1290 | const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{ | ||
| 1291 | ident_name, | ||
| 1292 | }); | ||
| 1293 | errdefer msg.destroy(mod.gpa); | ||
| 1294 | try mod.errNote(scope, local_val.src, msg, "previous definition is here", .{}); | ||
| 1295 | break :msg msg; | ||
| 1296 | }; | ||
| 1297 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 1298 | } | ||
| 1299 | s = local_val.parent; | ||
| 1300 | }, | ||
| 1301 | .local_ptr => { | ||
| 1302 | const local_ptr = s.cast(Scope.LocalPtr).?; | ||
| 1303 | if (mem.eql(u8, local_ptr.name, ident_name)) { | ||
| 1304 | const msg = msg: { | ||
| 1305 | const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{ | ||
| 1306 | ident_name, | ||
| 1307 | }); | ||
| 1308 | errdefer msg.destroy(mod.gpa); | ||
| 1309 | try mod.errNote(scope, local_ptr.src, msg, "previous definition is here", .{}); | ||
| 1310 | break :msg msg; | ||
| 1311 | }; | ||
| 1312 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 1313 | } | ||
| 1314 | s = local_ptr.parent; | ||
| 1315 | }, | ||
| 1316 | .gen_zir => s = s.cast(Scope.GenZir).?.parent, | ||
| 1317 | else => break, | ||
| 1318 | }; | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | // Namespace vars shadowing detection | ||
| 1322 | if (mod.lookupDeclName(scope, ident_name)) |_| { | ||
| 1323 | // TODO add note for other definition | ||
| 1324 | return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); | ||
| 1325 | } | ||
| 1326 | if (var_decl.ast.init_node == 0) { | ||
| 1327 | return mod.fail(scope, name_src, "variables must be initialized", .{}); | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | switch (token_tags[var_decl.ast.mut_token]) { | ||
| 1331 | .keyword_const => { | ||
| 1332 | // Depending on the type of AST the initialization expression is, we may need an lvalue | ||
| 1333 | // or an rvalue as a result location. If it is an rvalue, we can use the instruction as | ||
| 1334 | // the variable, no memory location needed. | ||
| 1335 | if (!nodeMayNeedMemoryLocation(scope, var_decl.ast.init_node)) { | ||
| 1336 | const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{ | ||
| 1337 | .ty = try typeExpr(mod, scope, var_decl.ast.type_node), | ||
| 1338 | } else .none; | ||
| 1339 | const init_inst = try expr(mod, scope, result_loc, var_decl.ast.init_node); | ||
| 1340 | const sub_scope = try block_arena.create(Scope.LocalVal); | ||
| 1341 | sub_scope.* = .{ | ||
| 1342 | .parent = scope, | ||
| 1343 | .gen_zir = gz, | ||
| 1344 | .name = ident_name, | ||
| 1345 | .inst = init_inst, | ||
| 1346 | .src = name_src, | ||
| 1347 | }; | ||
| 1348 | return &sub_scope.base; | ||
| 1349 | } | ||
| 1350 | |||
| 1351 | // Detect whether the initialization expression actually uses the | ||
| 1352 | // result location pointer. | ||
| 1353 | var init_scope: Scope.GenZir = .{ | ||
| 1354 | .parent = scope, | ||
| 1355 | .force_comptime = gz.force_comptime, | ||
| 1356 | .astgen = astgen, | ||
| 1357 | }; | ||
| 1358 | defer init_scope.instructions.deinit(mod.gpa); | ||
| 1359 | |||
| 1360 | var resolve_inferred_alloc: zir.Inst.Ref = .none; | ||
| 1361 | var opt_type_inst: zir.Inst.Ref = .none; | ||
| 1362 | if (var_decl.ast.type_node != 0) { | ||
| 1363 | const type_inst = try typeExpr(mod, &init_scope.base, var_decl.ast.type_node); | ||
| 1364 | opt_type_inst = type_inst; | ||
| 1365 | init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node); | ||
| 1366 | } else { | ||
| 1367 | const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node); | ||
| 1368 | resolve_inferred_alloc = alloc; | ||
| 1369 | init_scope.rl_ptr = alloc; | ||
| 1370 | } | ||
| 1371 | const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope }; | ||
| 1372 | const init_inst = try expr(mod, &init_scope.base, init_result_loc, var_decl.ast.init_node); | ||
| 1373 | const zir_tags = astgen.instructions.items(.tag); | ||
| 1374 | const zir_datas = astgen.instructions.items(.data); | ||
| 1375 | |||
| 1376 | const parent_zir = &gz.instructions; | ||
| 1377 | if (init_scope.rvalue_rl_count == 1) { | ||
| 1378 | // Result location pointer not used. We don't need an alloc for this | ||
| 1379 | // const local, and type inference becomes trivial. | ||
| 1380 | // Move the init_scope instructions into the parent scope, eliding | ||
| 1381 | // the alloc instruction and the store_to_block_ptr instruction. | ||
| 1382 | const expected_len = parent_zir.items.len + init_scope.instructions.items.len - 2; | ||
| 1383 | try parent_zir.ensureCapacity(mod.gpa, expected_len); | ||
| 1384 | for (init_scope.instructions.items) |src_inst| { | ||
| 1385 | if (astgen.indexToRef(src_inst) == init_scope.rl_ptr) continue; | ||
| 1386 | if (zir_tags[src_inst] == .store_to_block_ptr) { | ||
| 1387 | if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue; | ||
| 1388 | } | ||
| 1389 | parent_zir.appendAssumeCapacity(src_inst); | ||
| 1390 | } | ||
| 1391 | assert(parent_zir.items.len == expected_len); | ||
| 1392 | const casted_init = if (opt_type_inst != .none) | ||
| 1393 | try gz.addPlNode(.as_node, var_decl.ast.type_node, zir.Inst.As{ | ||
| 1394 | .dest_type = opt_type_inst, | ||
| 1395 | .operand = init_inst, | ||
| 1396 | }) | ||
| 1397 | else | ||
| 1398 | init_inst; | ||
| 1399 | |||
| 1400 | const sub_scope = try block_arena.create(Scope.LocalVal); | ||
| 1401 | sub_scope.* = .{ | ||
| 1402 | .parent = scope, | ||
| 1403 | .gen_zir = gz, | ||
| 1404 | .name = ident_name, | ||
| 1405 | .inst = casted_init, | ||
| 1406 | .src = name_src, | ||
| 1407 | }; | ||
| 1408 | return &sub_scope.base; | ||
| 1409 | } | ||
| 1410 | // The initialization expression took advantage of the result location | ||
| 1411 | // of the const local. In this case we will create an alloc and a LocalPtr for it. | ||
| 1412 | // Move the init_scope instructions into the parent scope, swapping | ||
| 1413 | // store_to_block_ptr for store_to_inferred_ptr. | ||
| 1414 | const expected_len = parent_zir.items.len + init_scope.instructions.items.len; | ||
| 1415 | try parent_zir.ensureCapacity(mod.gpa, expected_len); | ||
| 1416 | for (init_scope.instructions.items) |src_inst| { | ||
| 1417 | if (zir_tags[src_inst] == .store_to_block_ptr) { | ||
| 1418 | if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) { | ||
| 1419 | zir_tags[src_inst] = .store_to_inferred_ptr; | ||
| 1420 | } | ||
| 1421 | } | ||
| 1422 | parent_zir.appendAssumeCapacity(src_inst); | ||
| 1423 | } | ||
| 1424 | assert(parent_zir.items.len == expected_len); | ||
| 1425 | if (resolve_inferred_alloc != .none) { | ||
| 1426 | _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); | ||
| 1427 | } | ||
| 1428 | const sub_scope = try block_arena.create(Scope.LocalPtr); | ||
| 1429 | sub_scope.* = .{ | ||
| 1430 | .parent = scope, | ||
| 1431 | .gen_zir = gz, | ||
| 1432 | .name = ident_name, | ||
| 1433 | .ptr = init_scope.rl_ptr, | ||
| 1434 | .src = name_src, | ||
| 1435 | }; | ||
| 1436 | return &sub_scope.base; | ||
| 1437 | }, | ||
| 1438 | .keyword_var => { | ||
| 1439 | var resolve_inferred_alloc: zir.Inst.Ref = .none; | ||
| 1440 | const var_data: struct { | ||
| 1441 | result_loc: ResultLoc, | ||
| 1442 | alloc: zir.Inst.Ref, | ||
| 1443 | } = if (var_decl.ast.type_node != 0) a: { | ||
| 1444 | const type_inst = try typeExpr(mod, scope, var_decl.ast.type_node); | ||
| 1445 | |||
| 1446 | const alloc = try gz.addUnNode(.alloc_mut, type_inst, node); | ||
| 1447 | break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } }; | ||
| 1448 | } else a: { | ||
| 1449 | const alloc = try gz.addUnNode(.alloc_inferred_mut, undefined, node); | ||
| 1450 | resolve_inferred_alloc = alloc; | ||
| 1451 | break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } }; | ||
| 1452 | }; | ||
| 1453 | const init_inst = try expr(mod, scope, var_data.result_loc, var_decl.ast.init_node); | ||
| 1454 | if (resolve_inferred_alloc != .none) { | ||
| 1455 | _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); | ||
| 1456 | } | ||
| 1457 | const sub_scope = try block_arena.create(Scope.LocalPtr); | ||
| 1458 | sub_scope.* = .{ | ||
| 1459 | .parent = scope, | ||
| 1460 | .gen_zir = gz, | ||
| 1461 | .name = ident_name, | ||
| 1462 | .ptr = var_data.alloc, | ||
| 1463 | .src = name_src, | ||
| 1464 | }; | ||
| 1465 | return &sub_scope.base; | ||
| 1466 | }, | ||
| 1467 | else => unreachable, | ||
| 1468 | } | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | fn assign(mod: *Module, scope: *Scope, infix_node: ast.Node.Index) InnerError!void { | ||
| 1472 | const tree = scope.tree(); | ||
| 1473 | const node_datas = tree.nodes.items(.data); | ||
| 1474 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1475 | const node_tags = tree.nodes.items(.tag); | ||
| 1476 | |||
| 1477 | const lhs = node_datas[infix_node].lhs; | ||
| 1478 | const rhs = node_datas[infix_node].rhs; | ||
| 1479 | if (node_tags[lhs] == .identifier) { | ||
| 1480 | // This intentionally does not support `@"_"` syntax. | ||
| 1481 | const ident_name = tree.tokenSlice(main_tokens[lhs]); | ||
| 1482 | if (mem.eql(u8, ident_name, "_")) { | ||
| 1483 | _ = try expr(mod, scope, .discard, rhs); | ||
| 1484 | return; | ||
| 1485 | } | ||
| 1486 | } | ||
| 1487 | const lvalue = try lvalExpr(mod, scope, lhs); | ||
| 1488 | _ = try expr(mod, scope, .{ .ptr = lvalue }, rhs); | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | fn assignOp( | ||
| 1492 | mod: *Module, | ||
| 1493 | scope: *Scope, | ||
| 1494 | infix_node: ast.Node.Index, | ||
| 1495 | op_inst_tag: zir.Inst.Tag, | ||
| 1496 | ) InnerError!void { | ||
| 1497 | const tree = scope.tree(); | ||
| 1498 | const node_datas = tree.nodes.items(.data); | ||
| 1499 | const gz = scope.getGenZir(); | ||
| 1500 | |||
| 1501 | const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs); | ||
| 1502 | const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); | ||
| 1503 | const lhs_type = try gz.addUnTok(.typeof, lhs, infix_node); | ||
| 1504 | const rhs = try expr(mod, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs); | ||
| 1505 | |||
| 1506 | const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{ | ||
| 1507 | .lhs = lhs, | ||
| 1508 | .rhs = rhs, | ||
| 1509 | }); | ||
| 1510 | _ = try gz.addBin(.store, lhs_ptr, result); | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | fn boolNot(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 1514 | const tree = scope.tree(); | ||
| 1515 | const node_datas = tree.nodes.items(.data); | ||
| 1516 | |||
| 1517 | const operand = try expr(mod, scope, .{ .ty = .bool_type }, node_datas[node].lhs); | ||
| 1518 | const gz = scope.getGenZir(); | ||
| 1519 | const result = try gz.addUnNode(.bool_not, operand, node); | ||
| 1520 | return rvalue(mod, scope, rl, result, node); | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | fn bitNot(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 1524 | const tree = scope.tree(); | ||
| 1525 | const node_datas = tree.nodes.items(.data); | ||
| 1526 | |||
| 1527 | const gz = scope.getGenZir(); | ||
| 1528 | const operand = try expr(mod, scope, .none, node_datas[node].lhs); | ||
| 1529 | const result = try gz.addUnNode(.bit_not, operand, node); | ||
| 1530 | return rvalue(mod, scope, rl, result, node); | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | fn negation( | ||
| 1534 | mod: *Module, | ||
| 1535 | scope: *Scope, | ||
| 1536 | rl: ResultLoc, | ||
| 1537 | node: ast.Node.Index, | ||
| 1538 | tag: zir.Inst.Tag, | ||
| 1539 | ) InnerError!zir.Inst.Ref { | ||
| 1540 | const tree = scope.tree(); | ||
| 1541 | const node_datas = tree.nodes.items(.data); | ||
| 1542 | |||
| 1543 | const gz = scope.getGenZir(); | ||
| 1544 | const operand = try expr(mod, scope, .none, node_datas[node].lhs); | ||
| 1545 | const result = try gz.addUnNode(tag, operand, node); | ||
| 1546 | return rvalue(mod, scope, rl, result, node); | ||
| 1547 | } | ||
| 1548 | |||
| 1549 | fn ptrType( | ||
| 1550 | mod: *Module, | ||
| 1551 | scope: *Scope, | ||
| 1552 | rl: ResultLoc, | ||
| 1553 | node: ast.Node.Index, | ||
| 1554 | ptr_info: ast.full.PtrType, | ||
| 1555 | ) InnerError!zir.Inst.Ref { | ||
| 1556 | const tree = scope.tree(); | ||
| 1557 | const gz = scope.getGenZir(); | ||
| 1558 | |||
| 1559 | const elem_type = try typeExpr(mod, scope, ptr_info.ast.child_type); | ||
| 1560 | |||
| 1561 | const simple = ptr_info.ast.align_node == 0 and | ||
| 1562 | ptr_info.ast.sentinel == 0 and | ||
| 1563 | ptr_info.ast.bit_range_start == 0; | ||
| 1564 | |||
| 1565 | if (simple) { | ||
| 1566 | const result = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ | ||
| 1567 | .ptr_type_simple = .{ | ||
| 1568 | .is_allowzero = ptr_info.allowzero_token != null, | ||
| 1569 | .is_mutable = ptr_info.const_token == null, | ||
| 1570 | .is_volatile = ptr_info.volatile_token != null, | ||
| 1571 | .size = ptr_info.size, | ||
| 1572 | .elem_type = elem_type, | ||
| 1573 | }, | ||
| 1574 | } }); | ||
| 1575 | return rvalue(mod, scope, rl, result, node); | ||
| 1576 | } | ||
| 1577 | |||
| 1578 | var sentinel_ref: zir.Inst.Ref = .none; | ||
| 1579 | var align_ref: zir.Inst.Ref = .none; | ||
| 1580 | var bit_start_ref: zir.Inst.Ref = .none; | ||
| 1581 | var bit_end_ref: zir.Inst.Ref = .none; | ||
| 1582 | var trailing_count: u32 = 0; | ||
| 1583 | |||
| 1584 | if (ptr_info.ast.sentinel != 0) { | ||
| 1585 | sentinel_ref = try expr(mod, scope, .{ .ty = elem_type }, ptr_info.ast.sentinel); | ||
| 1586 | trailing_count += 1; | ||
| 1587 | } | ||
| 1588 | if (ptr_info.ast.align_node != 0) { | ||
| 1589 | align_ref = try expr(mod, scope, .none, ptr_info.ast.align_node); | ||
| 1590 | trailing_count += 1; | ||
| 1591 | } | ||
| 1592 | if (ptr_info.ast.bit_range_start != 0) { | ||
| 1593 | assert(ptr_info.ast.bit_range_end != 0); | ||
| 1594 | bit_start_ref = try expr(mod, scope, .none, ptr_info.ast.bit_range_start); | ||
| 1595 | bit_end_ref = try expr(mod, scope, .none, ptr_info.ast.bit_range_end); | ||
| 1596 | trailing_count += 2; | ||
| 1597 | } | ||
| 1598 | |||
| 1599 | const gpa = gz.astgen.mod.gpa; | ||
| 1600 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1601 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | ||
| 1602 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | ||
| 1603 | @typeInfo(zir.Inst.PtrType).Struct.fields.len + trailing_count); | ||
| 1604 | |||
| 1605 | const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.PtrType{ .elem_type = elem_type }); | ||
| 1606 | if (sentinel_ref != .none) { | ||
| 1607 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(sentinel_ref)); | ||
| 1608 | } | ||
| 1609 | if (align_ref != .none) { | ||
| 1610 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref)); | ||
| 1611 | } | ||
| 1612 | if (bit_start_ref != .none) { | ||
| 1613 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref)); | ||
| 1614 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref)); | ||
| 1615 | } | ||
| 1616 | |||
| 1617 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | ||
| 1618 | const result = gz.astgen.indexToRef(new_index); | ||
| 1619 | gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{ | ||
| 1620 | .ptr_type = .{ | ||
| 1621 | .flags = .{ | ||
| 1622 | .is_allowzero = ptr_info.allowzero_token != null, | ||
| 1623 | .is_mutable = ptr_info.const_token == null, | ||
| 1624 | .is_volatile = ptr_info.volatile_token != null, | ||
| 1625 | .has_sentinel = sentinel_ref != .none, | ||
| 1626 | .has_align = align_ref != .none, | ||
| 1627 | .has_bit_range = bit_start_ref != .none, | ||
| 1628 | }, | ||
| 1629 | .size = ptr_info.size, | ||
| 1630 | .payload_index = payload_index, | ||
| 1631 | }, | ||
| 1632 | } }); | ||
| 1633 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1634 | |||
| 1635 | return rvalue(mod, scope, rl, result, node); | ||
| 1636 | } | ||
| 1637 | |||
| 1638 | fn arrayType(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { | ||
| 1639 | const tree = scope.tree(); | ||
| 1640 | const node_datas = tree.nodes.items(.data); | ||
| 1641 | const gz = scope.getGenZir(); | ||
| 1642 | |||
| 1643 | // TODO check for [_]T | ||
| 1644 | const len = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].lhs); | ||
| 1645 | const elem_type = try typeExpr(mod, scope, node_datas[node].rhs); | ||
| 1646 | |||
| 1647 | const result = try gz.addBin(.array_type, len, elem_type); | ||
| 1648 | return rvalue(mod, scope, rl, result, node); | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | fn arrayTypeSentinel(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { | ||
| 1652 | const tree = scope.tree(); | ||
| 1653 | const node_datas = tree.nodes.items(.data); | ||
| 1654 | const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel); | ||
| 1655 | const gz = scope.getGenZir(); | ||
| 1656 | |||
| 1657 | // TODO check for [_]T | ||
| 1658 | const len = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].lhs); | ||
| 1659 | const elem_type = try typeExpr(mod, scope, extra.elem_type); | ||
| 1660 | const sentinel = try expr(mod, scope, .{ .ty = elem_type }, extra.sentinel); | ||
| 1661 | |||
| 1662 | const result = try gz.addArrayTypeSentinel(len, elem_type, sentinel); | ||
| 1663 | return rvalue(mod, scope, rl, result, node); | ||
| 1664 | } | ||
| 1665 | |||
| 1666 | fn containerDecl( | ||
| 1667 | mod: *Module, | ||
| 1668 | scope: *Scope, | ||
| 1669 | rl: ResultLoc, | ||
| 1670 | container_decl: ast.full.ContainerDecl, | ||
| 1671 | ) InnerError!zir.Inst.Ref { | ||
| 1672 | return mod.failTok(scope, container_decl.ast.main_token, "TODO implement container decls", .{}); | ||
| 1673 | } | ||
| 1674 | |||
| 1675 | fn errorSetDecl( | ||
| 1676 | mod: *Module, | ||
| 1677 | scope: *Scope, | ||
| 1678 | rl: ResultLoc, | ||
| 1679 | node: ast.Node.Index, | ||
| 1680 | ) InnerError!zir.Inst.Ref { | ||
| 1681 | if (true) @panic("TODO update for zir-memory-layout branch"); | ||
| 1682 | const gz = scope.getGenZir(); | ||
| 1683 | const tree = gz.tree(); | ||
| 1684 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1685 | const token_tags = tree.tokens.items(.tag); | ||
| 1686 | |||
| 1687 | // Count how many fields there are. | ||
| 1688 | const error_token = main_tokens[node]; | ||
| 1689 | const count: usize = count: { | ||
| 1690 | var tok_i = error_token + 2; | ||
| 1691 | var count: usize = 0; | ||
| 1692 | while (true) : (tok_i += 1) { | ||
| 1693 | switch (token_tags[tok_i]) { | ||
| 1694 | .doc_comment, .comma => {}, | ||
| 1695 | .identifier => count += 1, | ||
| 1696 | .r_brace => break :count count, | ||
| 1697 | else => unreachable, | ||
| 1698 | } | ||
| 1699 | } else unreachable; // TODO should not need else unreachable here | ||
| 1700 | }; | ||
| 1701 | |||
| 1702 | const fields = try scope.arena().alloc([]const u8, count); | ||
| 1703 | { | ||
| 1704 | var tok_i = error_token + 2; | ||
| 1705 | var field_i: usize = 0; | ||
| 1706 | while (true) : (tok_i += 1) { | ||
| 1707 | switch (token_tags[tok_i]) { | ||
| 1708 | .doc_comment, .comma => {}, | ||
| 1709 | .identifier => { | ||
| 1710 | fields[field_i] = try mod.identifierTokenString(scope, tok_i); | ||
| 1711 | field_i += 1; | ||
| 1712 | }, | ||
| 1713 | .r_brace => break, | ||
| 1714 | else => unreachable, | ||
| 1715 | } | ||
| 1716 | } | ||
| 1717 | } | ||
| 1718 | const result = try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}); | ||
| 1719 | return rvalue(mod, scope, rl, result); | ||
| 1720 | } | ||
| 1721 | |||
| 1722 | fn orelseCatchExpr( | ||
| 1723 | mod: *Module, | ||
| 1724 | scope: *Scope, | ||
| 1725 | rl: ResultLoc, | ||
| 1726 | node: ast.Node.Index, | ||
| 1727 | lhs: ast.Node.Index, | ||
| 1728 | cond_op: zir.Inst.Tag, | ||
| 1729 | unwrap_op: zir.Inst.Tag, | ||
| 1730 | unwrap_code_op: zir.Inst.Tag, | ||
| 1731 | rhs: ast.Node.Index, | ||
| 1732 | payload_token: ?ast.TokenIndex, | ||
| 1733 | ) InnerError!zir.Inst.Ref { | ||
| 1734 | const parent_gz = scope.getGenZir(); | ||
| 1735 | const tree = parent_gz.tree(); | ||
| 1736 | |||
| 1737 | var block_scope: Scope.GenZir = .{ | ||
| 1738 | .parent = scope, | ||
| 1739 | .astgen = parent_gz.astgen, | ||
| 1740 | .force_comptime = parent_gz.force_comptime, | ||
| 1741 | .instructions = .{}, | ||
| 1742 | }; | ||
| 1743 | setBlockResultLoc(&block_scope, rl); | ||
| 1744 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 1745 | |||
| 1746 | // This could be a pointer or value depending on the `operand_rl` parameter. | ||
| 1747 | // We cannot use `block_scope.break_result_loc` because that has the bare | ||
| 1748 | // type, whereas this expression has the optional type. Later we make | ||
| 1749 | // up for this fact by calling rvalue on the else branch. | ||
| 1750 | block_scope.break_count += 1; | ||
| 1751 | |||
| 1752 | // TODO handle catch | ||
| 1753 | const operand_rl: ResultLoc = switch (block_scope.break_result_loc) { | ||
| 1754 | .ref => .ref, | ||
| 1755 | .discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => .none, | ||
| 1756 | .ty => |elem_ty| blk: { | ||
| 1757 | const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node); | ||
| 1758 | break :blk .{ .ty = wrapped_ty }; | ||
| 1759 | }, | ||
| 1760 | .ptr => |ptr_ty| blk: { | ||
| 1761 | const wrapped_ty = try block_scope.addUnNode(.optional_type_from_ptr_elem, ptr_ty, node); | ||
| 1762 | break :blk .{ .ty = wrapped_ty }; | ||
| 1763 | }, | ||
| 1764 | }; | ||
| 1765 | const operand = try expr(mod, &block_scope.base, operand_rl, lhs); | ||
| 1766 | const cond = try block_scope.addUnNode(cond_op, operand, node); | ||
| 1767 | const condbr = try block_scope.addCondBr(.condbr, node); | ||
| 1768 | |||
| 1769 | const block = try parent_gz.addBlock(.block, node); | ||
| 1770 | try parent_gz.instructions.append(mod.gpa, block); | ||
| 1771 | try block_scope.setBlockBody(block); | ||
| 1772 | |||
| 1773 | var then_scope: Scope.GenZir = .{ | ||
| 1774 | .parent = scope, | ||
| 1775 | .astgen = parent_gz.astgen, | ||
| 1776 | .force_comptime = block_scope.force_comptime, | ||
| 1777 | .instructions = .{}, | ||
| 1778 | }; | ||
| 1779 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 1780 | |||
| 1781 | var err_val_scope: Scope.LocalVal = undefined; | ||
| 1782 | const then_sub_scope = blk: { | ||
| 1783 | const payload = payload_token orelse break :blk &then_scope.base; | ||
| 1784 | if (mem.eql(u8, tree.tokenSlice(payload), "_")) { | ||
| 1785 | return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{}); | ||
| 1786 | } | ||
| 1787 | const err_name = try mod.identifierTokenString(scope, payload); | ||
| 1788 | err_val_scope = .{ | ||
| 1789 | .parent = &then_scope.base, | ||
| 1790 | .gen_zir = &then_scope, | ||
| 1791 | .name = err_name, | ||
| 1792 | .inst = try then_scope.addUnNode(unwrap_code_op, operand, node), | ||
| 1793 | .src = parent_gz.tokSrcLoc(payload), | ||
| 1794 | }; | ||
| 1795 | break :blk &err_val_scope.base; | ||
| 1796 | }; | ||
| 1797 | |||
| 1798 | block_scope.break_count += 1; | ||
| 1799 | const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, rhs); | ||
| 1800 | // We hold off on the break instructions as well as copying the then/else | ||
| 1801 | // instructions into place until we know whether to keep store_to_block_ptr | ||
| 1802 | // instructions or not. | ||
| 1803 | |||
| 1804 | var else_scope: Scope.GenZir = .{ | ||
| 1805 | .parent = scope, | ||
| 1806 | .astgen = parent_gz.astgen, | ||
| 1807 | .force_comptime = block_scope.force_comptime, | ||
| 1808 | .instructions = .{}, | ||
| 1809 | }; | ||
| 1810 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 1811 | |||
| 1812 | // This could be a pointer or value depending on `unwrap_op`. | ||
| 1813 | const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node); | ||
| 1814 | const else_result = switch (rl) { | ||
| 1815 | .ref => unwrapped_payload, | ||
| 1816 | else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload, node), | ||
| 1817 | }; | ||
| 1818 | |||
| 1819 | return finishThenElseBlock( | ||
| 1820 | mod, | ||
| 1821 | scope, | ||
| 1822 | rl, | ||
| 1823 | node, | ||
| 1824 | &block_scope, | ||
| 1825 | &then_scope, | ||
| 1826 | &else_scope, | ||
| 1827 | condbr, | ||
| 1828 | cond, | ||
| 1829 | node, | ||
| 1830 | node, | ||
| 1831 | then_result, | ||
| 1832 | else_result, | ||
| 1833 | block, | ||
| 1834 | block, | ||
| 1835 | .@"break", | ||
| 1836 | ); | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | fn finishThenElseBlock( | ||
| 1840 | mod: *Module, | ||
| 1841 | parent_scope: *Scope, | ||
| 1842 | rl: ResultLoc, | ||
| 1843 | node: ast.Node.Index, | ||
| 1844 | block_scope: *Scope.GenZir, | ||
| 1845 | then_scope: *Scope.GenZir, | ||
| 1846 | else_scope: *Scope.GenZir, | ||
| 1847 | condbr: zir.Inst.Index, | ||
| 1848 | cond: zir.Inst.Ref, | ||
| 1849 | then_src: ast.Node.Index, | ||
| 1850 | else_src: ast.Node.Index, | ||
| 1851 | then_result: zir.Inst.Ref, | ||
| 1852 | else_result: zir.Inst.Ref, | ||
| 1853 | main_block: zir.Inst.Index, | ||
| 1854 | then_break_block: zir.Inst.Index, | ||
| 1855 | break_tag: zir.Inst.Tag, | ||
| 1856 | ) InnerError!zir.Inst.Ref { | ||
| 1857 | // We now have enough information to decide whether the result instruction should | ||
| 1858 | // be communicated via result location pointer or break instructions. | ||
| 1859 | const strat = rlStrategy(rl, block_scope); | ||
| 1860 | const astgen = block_scope.astgen; | ||
| 1861 | switch (strat.tag) { | ||
| 1862 | .break_void => { | ||
| 1863 | if (!astgen.refIsNoReturn(then_result)) { | ||
| 1864 | _ = try then_scope.addBreak(break_tag, then_break_block, .void_value); | ||
| 1865 | } | ||
| 1866 | const elide_else = if (else_result != .none) astgen.refIsNoReturn(else_result) else false; | ||
| 1867 | if (!elide_else) { | ||
| 1868 | _ = try else_scope.addBreak(break_tag, main_block, .void_value); | ||
| 1869 | } | ||
| 1870 | assert(!strat.elide_store_to_block_ptr_instructions); | ||
| 1871 | try setCondBrPayload(condbr, cond, then_scope, else_scope); | ||
| 1872 | return astgen.indexToRef(main_block); | ||
| 1873 | }, | ||
| 1874 | .break_operand => { | ||
| 1875 | if (!astgen.refIsNoReturn(then_result)) { | ||
| 1876 | _ = try then_scope.addBreak(break_tag, then_break_block, then_result); | ||
| 1877 | } | ||
| 1878 | if (else_result != .none) { | ||
| 1879 | if (!astgen.refIsNoReturn(else_result)) { | ||
| 1880 | _ = try else_scope.addBreak(break_tag, main_block, else_result); | ||
| 1881 | } | ||
| 1882 | } else { | ||
| 1883 | _ = try else_scope.addBreak(break_tag, main_block, .void_value); | ||
| 1884 | } | ||
| 1885 | if (strat.elide_store_to_block_ptr_instructions) { | ||
| 1886 | try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope); | ||
| 1887 | } else { | ||
| 1888 | try setCondBrPayload(condbr, cond, then_scope, else_scope); | ||
| 1889 | } | ||
| 1890 | const block_ref = astgen.indexToRef(main_block); | ||
| 1891 | switch (rl) { | ||
| 1892 | .ref => return block_ref, | ||
| 1893 | else => return rvalue(mod, parent_scope, rl, block_ref, node), | ||
| 1894 | } | ||
| 1895 | }, | ||
| 1896 | } | ||
| 1897 | } | ||
| 1898 | |||
| 1899 | /// Return whether the identifier names of two tokens are equal. Resolves @"" | ||
| 1900 | /// tokens without allocating. | ||
| 1901 | /// OK in theory it could do it without allocating. This implementation | ||
| 1902 | /// allocates when the @"" form is used. | ||
| 1903 | fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { | ||
| 1904 | const ident_name_1 = try mod.identifierTokenString(scope, token1); | ||
| 1905 | const ident_name_2 = try mod.identifierTokenString(scope, token2); | ||
| 1906 | return mem.eql(u8, ident_name_1, ident_name_2); | ||
| 1907 | } | ||
| 1908 | |||
| 1909 | pub fn fieldAccess( | ||
| 1910 | mod: *Module, | ||
| 1911 | scope: *Scope, | ||
| 1912 | rl: ResultLoc, | ||
| 1913 | node: ast.Node.Index, | ||
| 1914 | ) InnerError!zir.Inst.Ref { | ||
| 1915 | const gz = scope.getGenZir(); | ||
| 1916 | const tree = gz.tree(); | ||
| 1917 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1918 | const node_datas = tree.nodes.items(.data); | ||
| 1919 | const object_node = node_datas[node].lhs; | ||
| 1920 | const dot_token = main_tokens[node]; | ||
| 1921 | const field_ident = dot_token + 1; | ||
| 1922 | const string_bytes = &gz.astgen.string_bytes; | ||
| 1923 | const str_index = @intCast(u32, string_bytes.items.len); | ||
| 1924 | try mod.appendIdentStr(scope, field_ident, string_bytes); | ||
| 1925 | try string_bytes.append(mod.gpa, 0); | ||
| 1926 | switch (rl) { | ||
| 1927 | .ref => return gz.addPlNode(.field_ptr, node, zir.Inst.Field{ | ||
| 1928 | .lhs = try expr(mod, scope, .ref, object_node), | ||
| 1929 | .field_name_start = str_index, | ||
| 1930 | }), | ||
| 1931 | else => return rvalue(mod, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{ | ||
| 1932 | .lhs = try expr(mod, scope, .none, object_node), | ||
| 1933 | .field_name_start = str_index, | ||
| 1934 | }), node), | ||
| 1935 | } | ||
| 1936 | } | ||
| 1937 | |||
| 1938 | fn arrayAccess( | ||
| 1939 | mod: *Module, | ||
| 1940 | scope: *Scope, | ||
| 1941 | rl: ResultLoc, | ||
| 1942 | node: ast.Node.Index, | ||
| 1943 | ) InnerError!zir.Inst.Ref { | ||
| 1944 | const gz = scope.getGenZir(); | ||
| 1945 | const tree = gz.tree(); | ||
| 1946 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1947 | const node_datas = tree.nodes.items(.data); | ||
| 1948 | switch (rl) { | ||
| 1949 | .ref => return gz.addBin( | ||
| 1950 | .elem_ptr, | ||
| 1951 | try expr(mod, scope, .ref, node_datas[node].lhs), | ||
| 1952 | try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs), | ||
| 1953 | ), | ||
| 1954 | else => return rvalue(mod, scope, rl, try gz.addBin( | ||
| 1955 | .elem_val, | ||
| 1956 | try expr(mod, scope, .none, node_datas[node].lhs), | ||
| 1957 | try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs), | ||
| 1958 | ), node), | ||
| 1959 | } | ||
| 1960 | } | ||
| 1961 | |||
| 1962 | fn simpleBinOp( | ||
| 1963 | mod: *Module, | ||
| 1964 | scope: *Scope, | ||
| 1965 | rl: ResultLoc, | ||
| 1966 | node: ast.Node.Index, | ||
| 1967 | op_inst_tag: zir.Inst.Tag, | ||
| 1968 | ) InnerError!zir.Inst.Ref { | ||
| 1969 | const gz = scope.getGenZir(); | ||
| 1970 | const tree = gz.tree(); | ||
| 1971 | const node_datas = tree.nodes.items(.data); | ||
| 1972 | |||
| 1973 | const result = try gz.addPlNode(op_inst_tag, node, zir.Inst.Bin{ | ||
| 1974 | .lhs = try expr(mod, scope, .none, node_datas[node].lhs), | ||
| 1975 | .rhs = try expr(mod, scope, .none, node_datas[node].rhs), | ||
| 1976 | }); | ||
| 1977 | return rvalue(mod, scope, rl, result, node); | ||
| 1978 | } | ||
| 1979 | |||
| 1980 | fn simpleStrTok( | ||
| 1981 | mod: *Module, | ||
| 1982 | scope: *Scope, | ||
| 1983 | rl: ResultLoc, | ||
| 1984 | ident_token: ast.TokenIndex, | ||
| 1985 | node: ast.Node.Index, | ||
| 1986 | op_inst_tag: zir.Inst.Tag, | ||
| 1987 | ) InnerError!zir.Inst.Ref { | ||
| 1988 | const gz = scope.getGenZir(); | ||
| 1989 | const string_bytes = &gz.astgen.string_bytes; | ||
| 1990 | const str_index = @intCast(u32, string_bytes.items.len); | ||
| 1991 | try mod.appendIdentStr(scope, ident_token, string_bytes); | ||
| 1992 | try string_bytes.append(mod.gpa, 0); | ||
| 1993 | const result = try gz.addStrTok(op_inst_tag, str_index, ident_token); | ||
| 1994 | return rvalue(mod, scope, rl, result, node); | ||
| 1995 | } | ||
| 1996 | |||
| 1997 | fn boolBinOp( | ||
| 1998 | mod: *Module, | ||
| 1999 | scope: *Scope, | ||
| 2000 | rl: ResultLoc, | ||
| 2001 | node: ast.Node.Index, | ||
| 2002 | zir_tag: zir.Inst.Tag, | ||
| 2003 | ) InnerError!zir.Inst.Ref { | ||
| 2004 | const gz = scope.getGenZir(); | ||
| 2005 | const node_datas = gz.tree().nodes.items(.data); | ||
| 2006 | |||
| 2007 | const lhs = try expr(mod, scope, .{ .ty = .bool_type }, node_datas[node].lhs); | ||
| 2008 | const bool_br = try gz.addBoolBr(zir_tag, lhs); | ||
| 2009 | |||
| 2010 | var rhs_scope: Scope.GenZir = .{ | ||
| 2011 | .parent = scope, | ||
| 2012 | .astgen = gz.astgen, | ||
| 2013 | .force_comptime = gz.force_comptime, | ||
| 2014 | }; | ||
| 2015 | defer rhs_scope.instructions.deinit(mod.gpa); | ||
| 2016 | const rhs = try expr(mod, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs); | ||
| 2017 | _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs); | ||
| 2018 | try rhs_scope.setBoolBrBody(bool_br); | ||
| 2019 | |||
| 2020 | const block_ref = gz.astgen.indexToRef(bool_br); | ||
| 2021 | return rvalue(mod, scope, rl, block_ref, node); | ||
| 2022 | } | ||
| 2023 | |||
| 2024 | fn ifExpr( | ||
| 2025 | mod: *Module, | ||
| 2026 | scope: *Scope, | ||
| 2027 | rl: ResultLoc, | ||
| 2028 | node: ast.Node.Index, | ||
| 2029 | if_full: ast.full.If, | ||
| 2030 | ) InnerError!zir.Inst.Ref { | ||
| 2031 | const parent_gz = scope.getGenZir(); | ||
| 2032 | var block_scope: Scope.GenZir = .{ | ||
| 2033 | .parent = scope, | ||
| 2034 | .astgen = parent_gz.astgen, | ||
| 2035 | .force_comptime = parent_gz.force_comptime, | ||
| 2036 | .instructions = .{}, | ||
| 2037 | }; | ||
| 2038 | setBlockResultLoc(&block_scope, rl); | ||
| 2039 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 2040 | |||
| 2041 | const cond = c: { | ||
| 2042 | // TODO https://github.com/ziglang/zig/issues/7929 | ||
| 2043 | if (if_full.error_token) |error_token| { | ||
| 2044 | return mod.failTok(scope, error_token, "TODO implement if error union", .{}); | ||
| 2045 | } else if (if_full.payload_token) |payload_token| { | ||
| 2046 | return mod.failTok(scope, payload_token, "TODO implement if optional", .{}); | ||
| 2047 | } else { | ||
| 2048 | break :c try expr(mod, &block_scope.base, .{ .ty = .bool_type }, if_full.ast.cond_expr); | ||
| 2049 | } | ||
| 2050 | }; | ||
| 2051 | |||
| 2052 | const condbr = try block_scope.addCondBr(.condbr, node); | ||
| 2053 | |||
| 2054 | const block = try parent_gz.addBlock(.block, node); | ||
| 2055 | try parent_gz.instructions.append(mod.gpa, block); | ||
| 2056 | try block_scope.setBlockBody(block); | ||
| 2057 | |||
| 2058 | var then_scope: Scope.GenZir = .{ | ||
| 2059 | .parent = scope, | ||
| 2060 | .astgen = parent_gz.astgen, | ||
| 2061 | .force_comptime = block_scope.force_comptime, | ||
| 2062 | .instructions = .{}, | ||
| 2063 | }; | ||
| 2064 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 2065 | |||
| 2066 | // declare payload to the then_scope | ||
| 2067 | const then_sub_scope = &then_scope.base; | ||
| 2068 | |||
| 2069 | block_scope.break_count += 1; | ||
| 2070 | const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr); | ||
| 2071 | // We hold off on the break instructions as well as copying the then/else | ||
| 2072 | // instructions into place until we know whether to keep store_to_block_ptr | ||
| 2073 | // instructions or not. | ||
| 2074 | |||
| 2075 | var else_scope: Scope.GenZir = .{ | ||
| 2076 | .parent = scope, | ||
| 2077 | .astgen = parent_gz.astgen, | ||
| 2078 | .force_comptime = block_scope.force_comptime, | ||
| 2079 | .instructions = .{}, | ||
| 2080 | }; | ||
| 2081 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2082 | |||
| 2083 | const else_node = if_full.ast.else_expr; | ||
| 2084 | const else_info: struct { | ||
| 2085 | src: ast.Node.Index, | ||
| 2086 | result: zir.Inst.Ref, | ||
| 2087 | } = if (else_node != 0) blk: { | ||
| 2088 | block_scope.break_count += 1; | ||
| 2089 | const sub_scope = &else_scope.base; | ||
| 2090 | break :blk .{ | ||
| 2091 | .src = else_node, | ||
| 2092 | .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node), | ||
| 2093 | }; | ||
| 2094 | } else .{ | ||
| 2095 | .src = if_full.ast.then_expr, | ||
| 2096 | .result = .none, | ||
| 2097 | }; | ||
| 2098 | |||
| 2099 | return finishThenElseBlock( | ||
| 2100 | mod, | ||
| 2101 | scope, | ||
| 2102 | rl, | ||
| 2103 | node, | ||
| 2104 | &block_scope, | ||
| 2105 | &then_scope, | ||
| 2106 | &else_scope, | ||
| 2107 | condbr, | ||
| 2108 | cond, | ||
| 2109 | if_full.ast.then_expr, | ||
| 2110 | else_info.src, | ||
| 2111 | then_result, | ||
| 2112 | else_info.result, | ||
| 2113 | block, | ||
| 2114 | block, | ||
| 2115 | .@"break", | ||
| 2116 | ); | ||
| 2117 | } | ||
| 2118 | |||
| 2119 | fn setCondBrPayload( | ||
| 2120 | condbr: zir.Inst.Index, | ||
| 2121 | cond: zir.Inst.Ref, | ||
| 2122 | then_scope: *Scope.GenZir, | ||
| 2123 | else_scope: *Scope.GenZir, | ||
| 2124 | ) !void { | ||
| 2125 | const astgen = then_scope.astgen; | ||
| 2126 | |||
| 2127 | try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len + | ||
| 2128 | @typeInfo(zir.Inst.CondBr).Struct.fields.len + | ||
| 2129 | then_scope.instructions.items.len + else_scope.instructions.items.len); | ||
| 2130 | |||
| 2131 | const zir_datas = astgen.instructions.items(.data); | ||
| 2132 | zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{ | ||
| 2133 | .condition = cond, | ||
| 2134 | .then_body_len = @intCast(u32, then_scope.instructions.items.len), | ||
| 2135 | .else_body_len = @intCast(u32, else_scope.instructions.items.len), | ||
| 2136 | }); | ||
| 2137 | astgen.extra.appendSliceAssumeCapacity(then_scope.instructions.items); | ||
| 2138 | astgen.extra.appendSliceAssumeCapacity(else_scope.instructions.items); | ||
| 2139 | } | ||
| 2140 | |||
| 2141 | /// If `elide_block_store_ptr` is set, expects to find exactly 1 .store_to_block_ptr instruction. | ||
| 2142 | fn setCondBrPayloadElideBlockStorePtr( | ||
| 2143 | condbr: zir.Inst.Index, | ||
| 2144 | cond: zir.Inst.Ref, | ||
| 2145 | then_scope: *Scope.GenZir, | ||
| 2146 | else_scope: *Scope.GenZir, | ||
| 2147 | ) !void { | ||
| 2148 | const astgen = then_scope.astgen; | ||
| 2149 | |||
| 2150 | try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len + | ||
| 2151 | @typeInfo(zir.Inst.CondBr).Struct.fields.len + | ||
| 2152 | then_scope.instructions.items.len + else_scope.instructions.items.len - 2); | ||
| 2153 | |||
| 2154 | const zir_datas = astgen.instructions.items(.data); | ||
| 2155 | zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{ | ||
| 2156 | .condition = cond, | ||
| 2157 | .then_body_len = @intCast(u32, then_scope.instructions.items.len - 1), | ||
| 2158 | .else_body_len = @intCast(u32, else_scope.instructions.items.len - 1), | ||
| 2159 | }); | ||
| 2160 | |||
| 2161 | const zir_tags = astgen.instructions.items(.tag); | ||
| 2162 | for ([_]*Scope.GenZir{ then_scope, else_scope }) |scope| { | ||
| 2163 | for (scope.instructions.items) |src_inst| { | ||
| 2164 | if (zir_tags[src_inst] != .store_to_block_ptr) { | ||
| 2165 | astgen.extra.appendAssumeCapacity(src_inst); | ||
| 2166 | } | ||
| 2167 | } | ||
| 2168 | } | ||
| 2169 | } | ||
| 2170 | |||
| 2171 | fn whileExpr( | ||
| 2172 | mod: *Module, | ||
| 2173 | scope: *Scope, | ||
| 2174 | rl: ResultLoc, | ||
| 2175 | node: ast.Node.Index, | ||
| 2176 | while_full: ast.full.While, | ||
| 2177 | ) InnerError!zir.Inst.Ref { | ||
| 2178 | if (while_full.label_token) |label_token| { | ||
| 2179 | try checkLabelRedefinition(mod, scope, label_token); | ||
| 2180 | } | ||
| 2181 | const parent_gz = scope.getGenZir(); | ||
| 2182 | const is_inline = parent_gz.force_comptime or while_full.inline_token != null; | ||
| 2183 | const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop; | ||
| 2184 | const loop_block = try parent_gz.addBlock(loop_tag, node); | ||
| 2185 | try parent_gz.instructions.append(mod.gpa, loop_block); | ||
| 2186 | |||
| 2187 | var loop_scope: Scope.GenZir = .{ | ||
| 2188 | .parent = scope, | ||
| 2189 | .astgen = parent_gz.astgen, | ||
| 2190 | .force_comptime = parent_gz.force_comptime, | ||
| 2191 | .instructions = .{}, | ||
| 2192 | }; | ||
| 2193 | setBlockResultLoc(&loop_scope, rl); | ||
| 2194 | defer loop_scope.instructions.deinit(mod.gpa); | ||
| 2195 | |||
| 2196 | var continue_scope: Scope.GenZir = .{ | ||
| 2197 | .parent = &loop_scope.base, | ||
| 2198 | .astgen = parent_gz.astgen, | ||
| 2199 | .force_comptime = loop_scope.force_comptime, | ||
| 2200 | .instructions = .{}, | ||
| 2201 | }; | ||
| 2202 | defer continue_scope.instructions.deinit(mod.gpa); | ||
| 2203 | |||
| 2204 | const cond = c: { | ||
| 2205 | // TODO https://github.com/ziglang/zig/issues/7929 | ||
| 2206 | if (while_full.error_token) |error_token| { | ||
| 2207 | return mod.failTok(scope, error_token, "TODO implement while error union", .{}); | ||
| 2208 | } else if (while_full.payload_token) |payload_token| { | ||
| 2209 | return mod.failTok(scope, payload_token, "TODO implement while optional", .{}); | ||
| 2210 | } else { | ||
| 2211 | const bool_type_rl: ResultLoc = .{ .ty = .bool_type }; | ||
| 2212 | break :c try expr(mod, &continue_scope.base, bool_type_rl, while_full.ast.cond_expr); | ||
| 2213 | } | ||
| 2214 | }; | ||
| 2215 | |||
| 2216 | const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; | ||
| 2217 | const condbr = try continue_scope.addCondBr(condbr_tag, node); | ||
| 2218 | const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block; | ||
| 2219 | const cond_block = try loop_scope.addBlock(block_tag, node); | ||
| 2220 | try loop_scope.instructions.append(mod.gpa, cond_block); | ||
| 2221 | try continue_scope.setBlockBody(cond_block); | ||
| 2222 | |||
| 2223 | // TODO avoid emitting the continue expr when there | ||
| 2224 | // are no jumps to it. This happens when the last statement of a while body is noreturn | ||
| 2225 | // and there are no `continue` statements. | ||
| 2226 | if (while_full.ast.cont_expr != 0) { | ||
| 2227 | _ = try expr(mod, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr); | ||
| 2228 | } | ||
| 2229 | const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; | ||
| 2230 | _ = try loop_scope.addNode(repeat_tag, node); | ||
| 2231 | |||
| 2232 | try loop_scope.setBlockBody(loop_block); | ||
| 2233 | loop_scope.break_block = loop_block; | ||
| 2234 | loop_scope.continue_block = cond_block; | ||
| 2235 | if (while_full.label_token) |label_token| { | ||
| 2236 | loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{ | ||
| 2237 | .token = label_token, | ||
| 2238 | .block_inst = loop_block, | ||
| 2239 | }); | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | var then_scope: Scope.GenZir = .{ | ||
| 2243 | .parent = &continue_scope.base, | ||
| 2244 | .astgen = parent_gz.astgen, | ||
| 2245 | .force_comptime = continue_scope.force_comptime, | ||
| 2246 | .instructions = .{}, | ||
| 2247 | }; | ||
| 2248 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 2249 | |||
| 2250 | const then_sub_scope = &then_scope.base; | ||
| 2251 | |||
| 2252 | loop_scope.break_count += 1; | ||
| 2253 | const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr); | ||
| 2254 | |||
| 2255 | var else_scope: Scope.GenZir = .{ | ||
| 2256 | .parent = &continue_scope.base, | ||
| 2257 | .astgen = parent_gz.astgen, | ||
| 2258 | .force_comptime = continue_scope.force_comptime, | ||
| 2259 | .instructions = .{}, | ||
| 2260 | }; | ||
| 2261 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2262 | |||
| 2263 | const else_node = while_full.ast.else_expr; | ||
| 2264 | const else_info: struct { | ||
| 2265 | src: ast.Node.Index, | ||
| 2266 | result: zir.Inst.Ref, | ||
| 2267 | } = if (else_node != 0) blk: { | ||
| 2268 | loop_scope.break_count += 1; | ||
| 2269 | const sub_scope = &else_scope.base; | ||
| 2270 | break :blk .{ | ||
| 2271 | .src = else_node, | ||
| 2272 | .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node), | ||
| 2273 | }; | ||
| 2274 | } else .{ | ||
| 2275 | .src = while_full.ast.then_expr, | ||
| 2276 | .result = .none, | ||
| 2277 | }; | ||
| 2278 | |||
| 2279 | if (loop_scope.label) |some| { | ||
| 2280 | if (!some.used) { | ||
| 2281 | return mod.failTok(scope, some.token, "unused while loop label", .{}); | ||
| 2282 | } | ||
| 2283 | } | ||
| 2284 | const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; | ||
| 2285 | return finishThenElseBlock( | ||
| 2286 | mod, | ||
| 2287 | scope, | ||
| 2288 | rl, | ||
| 2289 | node, | ||
| 2290 | &loop_scope, | ||
| 2291 | &then_scope, | ||
| 2292 | &else_scope, | ||
| 2293 | condbr, | ||
| 2294 | cond, | ||
| 2295 | while_full.ast.then_expr, | ||
| 2296 | else_info.src, | ||
| 2297 | then_result, | ||
| 2298 | else_info.result, | ||
| 2299 | loop_block, | ||
| 2300 | cond_block, | ||
| 2301 | break_tag, | ||
| 2302 | ); | ||
| 2303 | } | ||
| 2304 | |||
| 2305 | fn forExpr( | ||
| 2306 | mod: *Module, | ||
| 2307 | scope: *Scope, | ||
| 2308 | rl: ResultLoc, | ||
| 2309 | node: ast.Node.Index, | ||
| 2310 | for_full: ast.full.While, | ||
| 2311 | ) InnerError!zir.Inst.Ref { | ||
| 2312 | if (for_full.label_token) |label_token| { | ||
| 2313 | try checkLabelRedefinition(mod, scope, label_token); | ||
| 2314 | } | ||
| 2315 | // Set up variables and constants. | ||
| 2316 | const parent_gz = scope.getGenZir(); | ||
| 2317 | const is_inline = parent_gz.force_comptime or for_full.inline_token != null; | ||
| 2318 | const tree = parent_gz.tree(); | ||
| 2319 | const token_tags = tree.tokens.items(.tag); | ||
| 2320 | |||
| 2321 | const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr); | ||
| 2322 | const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr); | ||
| 2323 | |||
| 2324 | const index_ptr = blk: { | ||
| 2325 | const index_ptr = try parent_gz.addUnNode(.alloc, .usize_type, node); | ||
| 2326 | // initialize to zero | ||
| 2327 | _ = try parent_gz.addBin(.store, index_ptr, .zero_usize); | ||
| 2328 | break :blk index_ptr; | ||
| 2329 | }; | ||
| 2330 | |||
| 2331 | const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop; | ||
| 2332 | const loop_block = try parent_gz.addBlock(loop_tag, node); | ||
| 2333 | try parent_gz.instructions.append(mod.gpa, loop_block); | ||
| 2334 | |||
| 2335 | var loop_scope: Scope.GenZir = .{ | ||
| 2336 | .parent = scope, | ||
| 2337 | .astgen = parent_gz.astgen, | ||
| 2338 | .force_comptime = parent_gz.force_comptime, | ||
| 2339 | .instructions = .{}, | ||
| 2340 | }; | ||
| 2341 | setBlockResultLoc(&loop_scope, rl); | ||
| 2342 | defer loop_scope.instructions.deinit(mod.gpa); | ||
| 2343 | |||
| 2344 | var cond_scope: Scope.GenZir = .{ | ||
| 2345 | .parent = &loop_scope.base, | ||
| 2346 | .astgen = parent_gz.astgen, | ||
| 2347 | .force_comptime = loop_scope.force_comptime, | ||
| 2348 | .instructions = .{}, | ||
| 2349 | }; | ||
| 2350 | defer cond_scope.instructions.deinit(mod.gpa); | ||
| 2351 | |||
| 2352 | // check condition i < array_expr.len | ||
| 2353 | const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr); | ||
| 2354 | const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, zir.Inst.Bin{ | ||
| 2355 | .lhs = index, | ||
| 2356 | .rhs = len, | ||
| 2357 | }); | ||
| 2358 | |||
| 2359 | const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; | ||
| 2360 | const condbr = try cond_scope.addCondBr(condbr_tag, node); | ||
| 2361 | const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block; | ||
| 2362 | const cond_block = try loop_scope.addBlock(block_tag, node); | ||
| 2363 | try loop_scope.instructions.append(mod.gpa, cond_block); | ||
| 2364 | try cond_scope.setBlockBody(cond_block); | ||
| 2365 | |||
| 2366 | // Increment the index variable. | ||
| 2367 | const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr); | ||
| 2368 | const index_plus_one = try loop_scope.addPlNode(.add, node, zir.Inst.Bin{ | ||
| 2369 | .lhs = index_2, | ||
| 2370 | .rhs = .one_usize, | ||
| 2371 | }); | ||
| 2372 | _ = try loop_scope.addBin(.store, index_ptr, index_plus_one); | ||
| 2373 | const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; | ||
| 2374 | _ = try loop_scope.addNode(repeat_tag, node); | ||
| 2375 | |||
| 2376 | try loop_scope.setBlockBody(loop_block); | ||
| 2377 | loop_scope.break_block = loop_block; | ||
| 2378 | loop_scope.continue_block = cond_block; | ||
| 2379 | if (for_full.label_token) |label_token| { | ||
| 2380 | loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{ | ||
| 2381 | .token = label_token, | ||
| 2382 | .block_inst = loop_block, | ||
| 2383 | }); | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | var then_scope: Scope.GenZir = .{ | ||
| 2387 | .parent = &cond_scope.base, | ||
| 2388 | .astgen = parent_gz.astgen, | ||
| 2389 | .force_comptime = cond_scope.force_comptime, | ||
| 2390 | .instructions = .{}, | ||
| 2391 | }; | ||
| 2392 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 2393 | |||
| 2394 | var index_scope: Scope.LocalPtr = undefined; | ||
| 2395 | const then_sub_scope = blk: { | ||
| 2396 | const payload_token = for_full.payload_token.?; | ||
| 2397 | const ident = if (token_tags[payload_token] == .asterisk) | ||
| 2398 | payload_token + 1 | ||
| 2399 | else | ||
| 2400 | payload_token; | ||
| 2401 | const is_ptr = ident != payload_token; | ||
| 2402 | const value_name = tree.tokenSlice(ident); | ||
| 2403 | if (!mem.eql(u8, value_name, "_")) { | ||
| 2404 | return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{}); | ||
| 2405 | } else if (is_ptr) { | ||
| 2406 | return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{}); | ||
| 2407 | } | ||
| 2408 | |||
| 2409 | const index_token = if (token_tags[ident + 1] == .comma) | ||
| 2410 | ident + 2 | ||
| 2411 | else | ||
| 2412 | break :blk &then_scope.base; | ||
| 2413 | if (mem.eql(u8, tree.tokenSlice(index_token), "_")) { | ||
| 2414 | return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{}); | ||
| 2415 | } | ||
| 2416 | const index_name = try mod.identifierTokenString(&then_scope.base, index_token); | ||
| 2417 | index_scope = .{ | ||
| 2418 | .parent = &then_scope.base, | ||
| 2419 | .gen_zir = &then_scope, | ||
| 2420 | .name = index_name, | ||
| 2421 | .ptr = index_ptr, | ||
| 2422 | .src = parent_gz.tokSrcLoc(index_token), | ||
| 2423 | }; | ||
| 2424 | break :blk &index_scope.base; | ||
| 2425 | }; | ||
| 2426 | |||
| 2427 | loop_scope.break_count += 1; | ||
| 2428 | const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr); | ||
| 2429 | |||
| 2430 | var else_scope: Scope.GenZir = .{ | ||
| 2431 | .parent = &cond_scope.base, | ||
| 2432 | .astgen = parent_gz.astgen, | ||
| 2433 | .force_comptime = cond_scope.force_comptime, | ||
| 2434 | .instructions = .{}, | ||
| 2435 | }; | ||
| 2436 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2437 | |||
| 2438 | const else_node = for_full.ast.else_expr; | ||
| 2439 | const else_info: struct { | ||
| 2440 | src: ast.Node.Index, | ||
| 2441 | result: zir.Inst.Ref, | ||
| 2442 | } = if (else_node != 0) blk: { | ||
| 2443 | loop_scope.break_count += 1; | ||
| 2444 | const sub_scope = &else_scope.base; | ||
| 2445 | break :blk .{ | ||
| 2446 | .src = else_node, | ||
| 2447 | .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node), | ||
| 2448 | }; | ||
| 2449 | } else .{ | ||
| 2450 | .src = for_full.ast.then_expr, | ||
| 2451 | .result = .none, | ||
| 2452 | }; | ||
| 2453 | |||
| 2454 | if (loop_scope.label) |some| { | ||
| 2455 | if (!some.used) { | ||
| 2456 | return mod.failTok(scope, some.token, "unused for loop label", .{}); | ||
| 2457 | } | ||
| 2458 | } | ||
| 2459 | const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; | ||
| 2460 | return finishThenElseBlock( | ||
| 2461 | mod, | ||
| 2462 | scope, | ||
| 2463 | rl, | ||
| 2464 | node, | ||
| 2465 | &loop_scope, | ||
| 2466 | &then_scope, | ||
| 2467 | &else_scope, | ||
| 2468 | condbr, | ||
| 2469 | cond, | ||
| 2470 | for_full.ast.then_expr, | ||
| 2471 | else_info.src, | ||
| 2472 | then_result, | ||
| 2473 | else_info.result, | ||
| 2474 | loop_block, | ||
| 2475 | cond_block, | ||
| 2476 | break_tag, | ||
| 2477 | ); | ||
| 2478 | } | ||
| 2479 | |||
| 2480 | fn getRangeNode( | ||
| 2481 | node_tags: []const ast.Node.Tag, | ||
| 2482 | node_datas: []const ast.Node.Data, | ||
| 2483 | start_node: ast.Node.Index, | ||
| 2484 | ) ?ast.Node.Index { | ||
| 2485 | var node = start_node; | ||
| 2486 | while (true) { | ||
| 2487 | switch (node_tags[node]) { | ||
| 2488 | .switch_range => return node, | ||
| 2489 | .grouped_expression => node = node_datas[node].lhs, | ||
| 2490 | else => return null, | ||
| 2491 | } | ||
| 2492 | } | ||
| 2493 | } | ||
| 2494 | |||
| 2495 | fn switchExpr( | ||
| 2496 | mod: *Module, | ||
| 2497 | scope: *Scope, | ||
| 2498 | rl: ResultLoc, | ||
| 2499 | switch_node: ast.Node.Index, | ||
| 2500 | ) InnerError!zir.Inst.Ref { | ||
| 2501 | if (true) @panic("TODO update for zir-memory-layout"); | ||
| 2502 | const parent_gz = scope.getGenZir(); | ||
| 2503 | const tree = parent_gz.tree(); | ||
| 2504 | const node_datas = tree.nodes.items(.data); | ||
| 2505 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2506 | const token_tags = tree.tokens.items(.tag); | ||
| 2507 | const node_tags = tree.nodes.items(.tag); | ||
| 2508 | |||
| 2509 | const switch_token = main_tokens[switch_node]; | ||
| 2510 | const target_node = node_datas[switch_node].lhs; | ||
| 2511 | const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); | ||
| 2512 | const case_nodes = tree.extra_data[extra.start..extra.end]; | ||
| 2513 | |||
| 2514 | const switch_src = token_starts[switch_token]; | ||
| 2515 | |||
| 2516 | var block_scope: Scope.GenZir = .{ | ||
| 2517 | .parent = scope, | ||
| 2518 | .decl = scope.ownerDecl().?, | ||
| 2519 | .arena = scope.arena(), | ||
| 2520 | .force_comptime = parent_gz.force_comptime, | ||
| 2521 | .instructions = .{}, | ||
| 2522 | }; | ||
| 2523 | setBlockResultLoc(&block_scope, rl); | ||
| 2524 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 2525 | |||
| 2526 | var items = std.ArrayList(zir.Inst.Ref).init(mod.gpa); | ||
| 2527 | defer items.deinit(); | ||
| 2528 | |||
| 2529 | // First we gather all the switch items and check else/'_' prongs. | ||
| 2530 | var else_src: ?usize = null; | ||
| 2531 | var underscore_src: ?usize = null; | ||
| 2532 | var first_range: ?*zir.Inst = null; | ||
| 2533 | var simple_case_count: usize = 0; | ||
| 2534 | var any_payload_is_ref = false; | ||
| 2535 | for (case_nodes) |case_node| { | ||
| 2536 | const case = switch (node_tags[case_node]) { | ||
| 2537 | .switch_case_one => tree.switchCaseOne(case_node), | ||
| 2538 | .switch_case => tree.switchCase(case_node), | ||
| 2539 | else => unreachable, | ||
| 2540 | }; | ||
| 2541 | if (case.payload_token) |payload_token| { | ||
| 2542 | if (token_tags[payload_token] == .asterisk) { | ||
| 2543 | any_payload_is_ref = true; | ||
| 2544 | } | ||
| 2545 | } | ||
| 2546 | // Check for else/_ prong, those are handled last. | ||
| 2547 | if (case.ast.values.len == 0) { | ||
| 2548 | const case_src = token_starts[case.ast.arrow_token - 1]; | ||
| 2549 | if (else_src) |src| { | ||
| 2550 | const msg = msg: { | ||
| 2551 | const msg = try mod.errMsg( | ||
| 2552 | scope, | ||
| 2553 | case_src, | ||
| 2554 | "multiple else prongs in switch expression", | ||
| 2555 | .{}, | ||
| 2556 | ); | ||
| 2557 | errdefer msg.destroy(mod.gpa); | ||
| 2558 | try mod.errNote(scope, src, msg, "previous else prong is here", .{}); | ||
| 2559 | break :msg msg; | ||
| 2560 | }; | ||
| 2561 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 2562 | } | ||
| 2563 | else_src = case_src; | ||
| 2564 | continue; | ||
| 2565 | } else if (case.ast.values.len == 1 and | ||
| 2566 | node_tags[case.ast.values[0]] == .identifier and | ||
| 2567 | mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) | ||
| 2568 | { | ||
| 2569 | const case_src = token_starts[case.ast.arrow_token - 1]; | ||
| 2570 | if (underscore_src) |src| { | ||
| 2571 | const msg = msg: { | ||
| 2572 | const msg = try mod.errMsg( | ||
| 2573 | scope, | ||
| 2574 | case_src, | ||
| 2575 | "multiple '_' prongs in switch expression", | ||
| 2576 | .{}, | ||
| 2577 | ); | ||
| 2578 | errdefer msg.destroy(mod.gpa); | ||
| 2579 | try mod.errNote(scope, src, msg, "previous '_' prong is here", .{}); | ||
| 2580 | break :msg msg; | ||
| 2581 | }; | ||
| 2582 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 2583 | } | ||
| 2584 | underscore_src = case_src; | ||
| 2585 | continue; | ||
| 2586 | } | ||
| 2587 | |||
| 2588 | if (else_src) |some_else| { | ||
| 2589 | if (underscore_src) |some_underscore| { | ||
| 2590 | const msg = msg: { | ||
| 2591 | const msg = try mod.errMsg( | ||
| 2592 | scope, | ||
| 2593 | switch_src, | ||
| 2594 | "else and '_' prong in switch expression", | ||
| 2595 | .{}, | ||
| 2596 | ); | ||
| 2597 | errdefer msg.destroy(mod.gpa); | ||
| 2598 | try mod.errNote(scope, some_else, msg, "else prong is here", .{}); | ||
| 2599 | try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{}); | ||
| 2600 | break :msg msg; | ||
| 2601 | }; | ||
| 2602 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 2603 | } | ||
| 2604 | } | ||
| 2605 | |||
| 2606 | if (case.ast.values.len == 1 and | ||
| 2607 | getRangeNode(node_tags, node_datas, case.ast.values[0]) == null) | ||
| 2608 | { | ||
| 2609 | simple_case_count += 1; | ||
| 2610 | } | ||
| 2611 | |||
| 2612 | // Generate all the switch items as comptime expressions. | ||
| 2613 | for (case.ast.values) |item| { | ||
| 2614 | if (getRangeNode(node_tags, node_datas, item)) |range| { | ||
| 2615 | const start = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].lhs); | ||
| 2616 | const end = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].rhs); | ||
| 2617 | const range_src = token_starts[main_tokens[range]]; | ||
| 2618 | const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end); | ||
| 2619 | try items.append(range_inst); | ||
| 2620 | } else { | ||
| 2621 | const item_inst = try comptimeExpr(mod, &block_scope.base, .none, item); | ||
| 2622 | try items.append(item_inst); | ||
| 2623 | } | ||
| 2624 | } | ||
| 2625 | } | ||
| 2626 | |||
| 2627 | var special_prong: zir.Inst.SwitchBr.SpecialProng = .none; | ||
| 2628 | if (else_src != null) special_prong = .@"else"; | ||
| 2629 | if (underscore_src != null) special_prong = .underscore; | ||
| 2630 | var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count); | ||
| 2631 | |||
| 2632 | const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{ | ||
| 2633 | .rl = .ref, | ||
| 2634 | .tag = .switchbr_ref, | ||
| 2635 | } else .{ | ||
| 2636 | .rl = .none, | ||
| 2637 | .tag = .switchbr, | ||
| 2638 | }; | ||
| 2639 | const target = try expr(mod, &block_scope.base, rl_and_tag.rl, target_node); | ||
| 2640 | const switch_inst = try addZirInstT(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, rl_and_tag.tag, .{ | ||
| 2641 | .target = target, | ||
| 2642 | .cases = cases, | ||
| 2643 | .items = try block_scope.arena.dupe(zir.Inst.Ref, items.items), | ||
| 2644 | .else_body = undefined, // populated below | ||
| 2645 | .range = first_range, | ||
| 2646 | .special_prong = special_prong, | ||
| 2647 | }); | ||
| 2648 | const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ | ||
| 2649 | .instructions = try block_scope.arena.dupe(zir.Inst.Ref, block_scope.instructions.items), | ||
| 2650 | }); | ||
| 2651 | |||
| 2652 | var case_scope: Scope.GenZir = .{ | ||
| 2653 | .parent = scope, | ||
| 2654 | .decl = block_scope.decl, | ||
| 2655 | .arena = block_scope.arena, | ||
| 2656 | .force_comptime = block_scope.force_comptime, | ||
| 2657 | .instructions = .{}, | ||
| 2658 | }; | ||
| 2659 | defer case_scope.instructions.deinit(mod.gpa); | ||
| 2660 | |||
| 2661 | var else_scope: Scope.GenZir = .{ | ||
| 2662 | .parent = scope, | ||
| 2663 | .decl = case_scope.decl, | ||
| 2664 | .arena = case_scope.arena, | ||
| 2665 | .force_comptime = case_scope.force_comptime, | ||
| 2666 | .instructions = .{}, | ||
| 2667 | }; | ||
| 2668 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2669 | |||
| 2670 | // Now generate all but the special cases. | ||
| 2671 | var special_case: ?ast.full.SwitchCase = null; | ||
| 2672 | var items_index: usize = 0; | ||
| 2673 | var case_index: usize = 0; | ||
| 2674 | for (case_nodes) |case_node| { | ||
| 2675 | const case = switch (node_tags[case_node]) { | ||
| 2676 | .switch_case_one => tree.switchCaseOne(case_node), | ||
| 2677 | .switch_case => tree.switchCase(case_node), | ||
| 2678 | else => unreachable, | ||
| 2679 | }; | ||
| 2680 | const case_src = token_starts[main_tokens[case_node]]; | ||
| 2681 | case_scope.instructions.shrinkRetainingCapacity(0); | ||
| 2682 | |||
| 2683 | // Check for else/_ prong, those are handled last. | ||
| 2684 | if (case.ast.values.len == 0) { | ||
| 2685 | special_case = case; | ||
| 2686 | continue; | ||
| 2687 | } else if (case.ast.values.len == 1 and | ||
| 2688 | node_tags[case.ast.values[0]] == .identifier and | ||
| 2689 | mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) | ||
| 2690 | { | ||
| 2691 | special_case = case; | ||
| 2692 | continue; | ||
| 2693 | } | ||
| 2694 | |||
| 2695 | // If this is a simple one item prong then it is handled by the switchbr. | ||
| 2696 | if (case.ast.values.len == 1 and | ||
| 2697 | getRangeNode(node_tags, node_datas, case.ast.values[0]) == null) | ||
| 2698 | { | ||
| 2699 | const item = items.items[items_index]; | ||
| 2700 | items_index += 1; | ||
| 2701 | try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target); | ||
| 2702 | |||
| 2703 | cases[case_index] = .{ | ||
| 2704 | .item = item, | ||
| 2705 | .body = .{ .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items) }, | ||
| 2706 | }; | ||
| 2707 | case_index += 1; | ||
| 2708 | continue; | ||
| 2709 | } | ||
| 2710 | |||
| 2711 | // Check if the target matches any of the items. | ||
| 2712 | // 1, 2, 3..6 will result in | ||
| 2713 | // target == 1 or target == 2 or (target >= 3 and target <= 6) | ||
| 2714 | // TODO handle multiple items as switch prongs rather than along with ranges. | ||
| 2715 | var any_ok: ?*zir.Inst = null; | ||
| 2716 | for (case.ast.values) |item| { | ||
| 2717 | if (getRangeNode(node_tags, node_datas, item)) |range| { | ||
| 2718 | const range_src = token_starts[main_tokens[range]]; | ||
| 2719 | const range_inst = items.items[items_index].castTag(.switch_range).?; | ||
| 2720 | items_index += 1; | ||
| 2721 | |||
| 2722 | // target >= start and target <= end | ||
| 2723 | const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, range_inst.positionals.lhs); | ||
| 2724 | const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, range_inst.positionals.rhs); | ||
| 2725 | const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok); | ||
| 2726 | |||
| 2727 | if (any_ok) |some| { | ||
| 2728 | any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok); | ||
| 2729 | } else { | ||
| 2730 | any_ok = range_ok; | ||
| 2731 | } | ||
| 2732 | continue; | ||
| 2733 | } | ||
| 2734 | |||
| 2735 | const item_inst = items.items[items_index]; | ||
| 2736 | items_index += 1; | ||
| 2737 | const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst); | ||
| 2738 | |||
| 2739 | if (any_ok) |some| { | ||
| 2740 | any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok); | ||
| 2741 | } else { | ||
| 2742 | any_ok = cpm_ok; | ||
| 2743 | } | ||
| 2744 | } | ||
| 2745 | |||
| 2746 | const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{ | ||
| 2747 | .condition = any_ok.?, | ||
| 2748 | .then_body = undefined, // populated below | ||
| 2749 | .else_body = undefined, // populated below | ||
| 2750 | }, .{}); | ||
| 2751 | const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{ | ||
| 2752 | .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items), | ||
| 2753 | }); | ||
| 2754 | |||
| 2755 | // reset cond_scope for then_body | ||
| 2756 | case_scope.instructions.items.len = 0; | ||
| 2757 | try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target); | ||
| 2758 | condbr.positionals.then_body = .{ | ||
| 2759 | .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items), | ||
| 2760 | }; | ||
| 2761 | |||
| 2762 | // reset cond_scope for else_body | ||
| 2763 | case_scope.instructions.items.len = 0; | ||
| 2764 | _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{ | ||
| 2765 | .block = cond_block, | ||
| 2766 | }, .{}); | ||
| 2767 | condbr.positionals.else_body = .{ | ||
| 2768 | .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items), | ||
| 2769 | }; | ||
| 2770 | } | ||
| 2771 | |||
| 2772 | // Finally generate else block or a break. | ||
| 2773 | if (special_case) |case| { | ||
| 2774 | try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target); | ||
| 2775 | } else { | ||
| 2776 | // Not handling all possible cases is a compile error. | ||
| 2777 | _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe); | ||
| 2778 | } | ||
| 2779 | switch_inst.positionals.else_body = .{ | ||
| 2780 | .instructions = try block_scope.arena.dupe(zir.Inst.Ref, else_scope.instructions.items), | ||
| 2781 | }; | ||
| 2782 | |||
| 2783 | return &block.base; | ||
| 2784 | } | ||
| 2785 | |||
| 2786 | fn switchCaseExpr( | ||
| 2787 | mod: *Module, | ||
| 2788 | scope: *Scope, | ||
| 2789 | rl: ResultLoc, | ||
| 2790 | block: *zir.Inst.Block, | ||
| 2791 | case: ast.full.SwitchCase, | ||
| 2792 | target: zir.Inst.Ref, | ||
| 2793 | ) !void { | ||
| 2794 | const tree = scope.tree(); | ||
| 2795 | const node_datas = tree.nodes.items(.data); | ||
| 2796 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2797 | const token_tags = tree.tokens.items(.tag); | ||
| 2798 | |||
| 2799 | const case_src = token_starts[case.ast.arrow_token]; | ||
| 2800 | const sub_scope = blk: { | ||
| 2801 | const payload_token = case.payload_token orelse break :blk scope; | ||
| 2802 | const ident = if (token_tags[payload_token] == .asterisk) | ||
| 2803 | payload_token + 1 | ||
| 2804 | else | ||
| 2805 | payload_token; | ||
| 2806 | const is_ptr = ident != payload_token; | ||
| 2807 | const value_name = tree.tokenSlice(ident); | ||
| 2808 | if (mem.eql(u8, value_name, "_")) { | ||
| 2809 | if (is_ptr) { | ||
| 2810 | return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{}); | ||
| 2811 | } | ||
| 2812 | break :blk scope; | ||
| 2813 | } | ||
| 2814 | return mod.failTok(scope, ident, "TODO implement switch value payload", .{}); | ||
| 2815 | }; | ||
| 2816 | |||
| 2817 | const case_body = try expr(mod, sub_scope, rl, case.ast.target_expr); | ||
| 2818 | if (!case_body.tag.isNoReturn()) { | ||
| 2819 | _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{ | ||
| 2820 | .block = block, | ||
| 2821 | .operand = case_body, | ||
| 2822 | }, .{}); | ||
| 2823 | } | ||
| 2824 | } | ||
| 2825 | |||
| 2826 | fn ret(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 2827 | const tree = scope.tree(); | ||
| 2828 | const node_datas = tree.nodes.items(.data); | ||
| 2829 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2830 | |||
| 2831 | const operand_node = node_datas[node].lhs; | ||
| 2832 | const gz = scope.getGenZir(); | ||
| 2833 | const operand: zir.Inst.Ref = if (operand_node != 0) operand: { | ||
| 2834 | const rl: ResultLoc = if (nodeMayNeedMemoryLocation(scope, operand_node)) .{ | ||
| 2835 | .ptr = try gz.addNode(.ret_ptr, node), | ||
| 2836 | } else .{ | ||
| 2837 | .ty = try gz.addNode(.ret_type, node), | ||
| 2838 | }; | ||
| 2839 | break :operand try expr(mod, scope, rl, operand_node); | ||
| 2840 | } else .void_value; | ||
| 2841 | _ = try gz.addUnNode(.ret_node, operand, node); | ||
| 2842 | return zir.Inst.Ref.unreachable_value; | ||
| 2843 | } | ||
| 2844 | |||
| 2845 | fn identifier( | ||
| 2846 | mod: *Module, | ||
| 2847 | scope: *Scope, | ||
| 2848 | rl: ResultLoc, | ||
| 2849 | ident: ast.Node.Index, | ||
| 2850 | ) InnerError!zir.Inst.Ref { | ||
| 2851 | const tracy = trace(@src()); | ||
| 2852 | defer tracy.end(); | ||
| 2853 | |||
| 2854 | const tree = scope.tree(); | ||
| 2855 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2856 | |||
| 2857 | const gz = scope.getGenZir(); | ||
| 2858 | |||
| 2859 | const ident_token = main_tokens[ident]; | ||
| 2860 | const ident_name = try mod.identifierTokenString(scope, ident_token); | ||
| 2861 | if (mem.eql(u8, ident_name, "_")) { | ||
| 2862 | return mod.failNode(scope, ident, "TODO implement '_' identifier", .{}); | ||
| 2863 | } | ||
| 2864 | |||
| 2865 | if (simple_types.get(ident_name)) |zir_const_ref| { | ||
| 2866 | return rvalue(mod, scope, rl, zir_const_ref, ident); | ||
| 2867 | } | ||
| 2868 | |||
| 2869 | if (ident_name.len >= 2) integer: { | ||
| 2870 | const first_c = ident_name[0]; | ||
| 2871 | if (first_c == 'i' or first_c == 'u') { | ||
| 2872 | const signedness: std.builtin.Signedness = switch (first_c == 'i') { | ||
| 2873 | true => .signed, | ||
| 2874 | false => .unsigned, | ||
| 2875 | }; | ||
| 2876 | const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) { | ||
| 2877 | error.Overflow => return mod.failNode( | ||
| 2878 | scope, | ||
| 2879 | ident, | ||
| 2880 | "primitive integer type '{s}' exceeds maximum bit width of 65535", | ||
| 2881 | .{ident_name}, | ||
| 2882 | ), | ||
| 2883 | error.InvalidCharacter => break :integer, | ||
| 2884 | }; | ||
| 2885 | const result = try gz.add(.{ | ||
| 2886 | .tag = .int_type, | ||
| 2887 | .data = .{ .int_type = .{ | ||
| 2888 | .src_node = gz.astgen.decl.nodeIndexToRelative(ident), | ||
| 2889 | .signedness = signedness, | ||
| 2890 | .bit_count = bit_count, | ||
| 2891 | } }, | ||
| 2892 | }); | ||
| 2893 | return rvalue(mod, scope, rl, result, ident); | ||
| 2894 | } | ||
| 2895 | } | ||
| 2896 | |||
| 2897 | // Local variables, including function parameters. | ||
| 2898 | { | ||
| 2899 | var s = scope; | ||
| 2900 | while (true) switch (s.tag) { | ||
| 2901 | .local_val => { | ||
| 2902 | const local_val = s.cast(Scope.LocalVal).?; | ||
| 2903 | if (mem.eql(u8, local_val.name, ident_name)) { | ||
| 2904 | return rvalue(mod, scope, rl, local_val.inst, ident); | ||
| 2905 | } | ||
| 2906 | s = local_val.parent; | ||
| 2907 | }, | ||
| 2908 | .local_ptr => { | ||
| 2909 | const local_ptr = s.cast(Scope.LocalPtr).?; | ||
| 2910 | if (mem.eql(u8, local_ptr.name, ident_name)) { | ||
| 2911 | if (rl == .ref) return local_ptr.ptr; | ||
| 2912 | const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident); | ||
| 2913 | return rvalue(mod, scope, rl, loaded, ident); | ||
| 2914 | } | ||
| 2915 | s = local_ptr.parent; | ||
| 2916 | }, | ||
| 2917 | .gen_zir => s = s.cast(Scope.GenZir).?.parent, | ||
| 2918 | else => break, | ||
| 2919 | }; | ||
| 2920 | } | ||
| 2921 | |||
| 2922 | const gop = try gz.astgen.decl_map.getOrPut(mod.gpa, ident_name); | ||
| 2923 | if (!gop.found_existing) { | ||
| 2924 | const decl = mod.lookupDeclName(scope, ident_name) orelse | ||
| 2925 | return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name}); | ||
| 2926 | try gz.astgen.decls.append(mod.gpa, decl); | ||
| 2927 | } | ||
| 2928 | const decl_index = @intCast(u32, gop.index); | ||
| 2929 | switch (rl) { | ||
| 2930 | .ref => return gz.addDecl(.decl_ref, decl_index, ident), | ||
| 2931 | else => return rvalue(mod, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident), | ||
| 2932 | } | ||
| 2933 | } | ||
| 2934 | |||
| 2935 | fn stringLiteral( | ||
| 2936 | mod: *Module, | ||
| 2937 | scope: *Scope, | ||
| 2938 | rl: ResultLoc, | ||
| 2939 | node: ast.Node.Index, | ||
| 2940 | ) InnerError!zir.Inst.Ref { | ||
| 2941 | const tree = scope.tree(); | ||
| 2942 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2943 | const gz = scope.getGenZir(); | ||
| 2944 | const string_bytes = &gz.astgen.string_bytes; | ||
| 2945 | const str_index = string_bytes.items.len; | ||
| 2946 | const str_lit_token = main_tokens[node]; | ||
| 2947 | const token_bytes = tree.tokenSlice(str_lit_token); | ||
| 2948 | try mod.parseStrLit(scope, str_lit_token, string_bytes, token_bytes, 0); | ||
| 2949 | const str_len = string_bytes.items.len - str_index; | ||
| 2950 | const result = try gz.add(.{ | ||
| 2951 | .tag = .str, | ||
| 2952 | .data = .{ .str = .{ | ||
| 2953 | .start = @intCast(u32, str_index), | ||
| 2954 | .len = @intCast(u32, str_len), | ||
| 2955 | } }, | ||
| 2956 | }); | ||
| 2957 | return rvalue(mod, scope, rl, result, node); | ||
| 2958 | } | ||
| 2959 | |||
| 2960 | fn multilineStringLiteral( | ||
| 2961 | mod: *Module, | ||
| 2962 | scope: *Scope, | ||
| 2963 | rl: ResultLoc, | ||
| 2964 | node: ast.Node.Index, | ||
| 2965 | ) InnerError!zir.Inst.Ref { | ||
| 2966 | const gz = scope.getGenZir(); | ||
| 2967 | const tree = gz.tree(); | ||
| 2968 | const node_datas = tree.nodes.items(.data); | ||
| 2969 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2970 | |||
| 2971 | const start = node_datas[node].lhs; | ||
| 2972 | const end = node_datas[node].rhs; | ||
| 2973 | const string_bytes = &gz.astgen.string_bytes; | ||
| 2974 | const str_index = string_bytes.items.len; | ||
| 2975 | |||
| 2976 | // First line: do not append a newline. | ||
| 2977 | var tok_i = start; | ||
| 2978 | { | ||
| 2979 | const slice = tree.tokenSlice(tok_i); | ||
| 2980 | const line_bytes = slice[2 .. slice.len - 1]; | ||
| 2981 | try string_bytes.appendSlice(mod.gpa, line_bytes); | ||
| 2982 | tok_i += 1; | ||
| 2983 | } | ||
| 2984 | // Following lines: each line prepends a newline. | ||
| 2985 | while (tok_i <= end) : (tok_i += 1) { | ||
| 2986 | const slice = tree.tokenSlice(tok_i); | ||
| 2987 | const line_bytes = slice[2 .. slice.len - 1]; | ||
| 2988 | try string_bytes.ensureCapacity(mod.gpa, string_bytes.items.len + line_bytes.len + 1); | ||
| 2989 | string_bytes.appendAssumeCapacity('\n'); | ||
| 2990 | string_bytes.appendSliceAssumeCapacity(line_bytes); | ||
| 2991 | } | ||
| 2992 | const result = try gz.add(.{ | ||
| 2993 | .tag = .str, | ||
| 2994 | .data = .{ .str = .{ | ||
| 2995 | .start = @intCast(u32, str_index), | ||
| 2996 | .len = @intCast(u32, string_bytes.items.len - str_index), | ||
| 2997 | } }, | ||
| 2998 | }); | ||
| 2999 | return rvalue(mod, scope, rl, result, node); | ||
| 3000 | } | ||
| 3001 | |||
| 3002 | fn charLiteral(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { | ||
| 3003 | const gz = scope.getGenZir(); | ||
| 3004 | const tree = gz.tree(); | ||
| 3005 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3006 | const main_token = main_tokens[node]; | ||
| 3007 | const slice = tree.tokenSlice(main_token); | ||
| 3008 | |||
| 3009 | var bad_index: usize = undefined; | ||
| 3010 | const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) { | ||
| 3011 | error.InvalidCharacter => { | ||
| 3012 | const bad_byte = slice[bad_index]; | ||
| 3013 | const token_starts = tree.tokens.items(.start); | ||
| 3014 | const src_off = @intCast(u32, token_starts[main_token] + bad_index); | ||
| 3015 | return mod.failOff(scope, src_off, "invalid character: '{c}'\n", .{bad_byte}); | ||
| 3016 | }, | ||
| 3017 | }; | ||
| 3018 | const result = try gz.addInt(value); | ||
| 3019 | return rvalue(mod, scope, rl, result, node); | ||
| 3020 | } | ||
| 3021 | |||
| 3022 | fn integerLiteral( | ||
| 3023 | mod: *Module, | ||
| 3024 | scope: *Scope, | ||
| 3025 | rl: ResultLoc, | ||
| 3026 | node: ast.Node.Index, | ||
| 3027 | ) InnerError!zir.Inst.Ref { | ||
| 3028 | const tree = scope.tree(); | ||
| 3029 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3030 | const int_token = main_tokens[node]; | ||
| 3031 | const prefixed_bytes = tree.tokenSlice(int_token); | ||
| 3032 | const gz = scope.getGenZir(); | ||
| 3033 | if (std.fmt.parseInt(u64, prefixed_bytes, 0)) |small_int| { | ||
| 3034 | const result: zir.Inst.Ref = switch (small_int) { | ||
| 3035 | 0 => .zero, | ||
| 3036 | 1 => .one, | ||
| 3037 | else => try gz.addInt(small_int), | ||
| 3038 | }; | ||
| 3039 | return rvalue(mod, scope, rl, result, node); | ||
| 3040 | } else |err| { | ||
| 3041 | return mod.failNode(scope, node, "TODO implement int literals that don't fit in a u64", .{}); | ||
| 3042 | } | ||
| 3043 | } | ||
| 3044 | |||
| 3045 | fn floatLiteral( | ||
| 3046 | mod: *Module, | ||
| 3047 | scope: *Scope, | ||
| 3048 | rl: ResultLoc, | ||
| 3049 | node: ast.Node.Index, | ||
| 3050 | ) InnerError!zir.Inst.Ref { | ||
| 3051 | const arena = scope.arena(); | ||
| 3052 | const tree = scope.tree(); | ||
| 3053 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3054 | const gz = scope.getGenZir(); | ||
| 3055 | |||
| 3056 | const main_token = main_tokens[node]; | ||
| 3057 | const bytes = tree.tokenSlice(main_token); | ||
| 3058 | if (bytes.len > 2 and bytes[1] == 'x') { | ||
| 3059 | assert(bytes[0] == '0'); // validated by tokenizer | ||
| 3060 | return mod.failTok(scope, main_token, "TODO implement hex floats", .{}); | ||
| 3061 | } | ||
| 3062 | const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) { | ||
| 3063 | error.InvalidCharacter => unreachable, // validated by tokenizer | ||
| 3064 | }; | ||
| 3065 | const typed_value = try arena.create(TypedValue); | ||
| 3066 | typed_value.* = .{ | ||
| 3067 | .ty = Type.initTag(.comptime_float), | ||
| 3068 | .val = try Value.Tag.float_128.create(arena, float_number), | ||
| 3069 | }; | ||
| 3070 | const result = try gz.add(.{ | ||
| 3071 | .tag = .@"const", | ||
| 3072 | .data = .{ .@"const" = typed_value }, | ||
| 3073 | }); | ||
| 3074 | return rvalue(mod, scope, rl, result, node); | ||
| 3075 | } | ||
| 3076 | |||
| 3077 | fn asmExpr( | ||
| 3078 | mod: *Module, | ||
| 3079 | scope: *Scope, | ||
| 3080 | rl: ResultLoc, | ||
| 3081 | node: ast.Node.Index, | ||
| 3082 | full: ast.full.Asm, | ||
| 3083 | ) InnerError!zir.Inst.Ref { | ||
| 3084 | const arena = scope.arena(); | ||
| 3085 | const tree = scope.tree(); | ||
| 3086 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3087 | const node_datas = tree.nodes.items(.data); | ||
| 3088 | const gz = scope.getGenZir(); | ||
| 3089 | |||
| 3090 | const asm_source = try expr(mod, scope, .{ .ty = .const_slice_u8_type }, full.ast.template); | ||
| 3091 | |||
| 3092 | if (full.outputs.len != 0) { | ||
| 3093 | return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{}); | ||
| 3094 | } | ||
| 3095 | |||
| 3096 | const constraints = try arena.alloc(u32, full.inputs.len); | ||
| 3097 | const args = try arena.alloc(zir.Inst.Ref, full.inputs.len); | ||
| 3098 | |||
| 3099 | for (full.inputs) |input, i| { | ||
| 3100 | const constraint_token = main_tokens[input] + 2; | ||
| 3101 | const string_bytes = &gz.astgen.string_bytes; | ||
| 3102 | constraints[i] = @intCast(u32, string_bytes.items.len); | ||
| 3103 | const token_bytes = tree.tokenSlice(constraint_token); | ||
| 3104 | try mod.parseStrLit(scope, constraint_token, string_bytes, token_bytes, 0); | ||
| 3105 | try string_bytes.append(mod.gpa, 0); | ||
| 3106 | |||
| 3107 | args[i] = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[input].lhs); | ||
| 3108 | } | ||
| 3109 | |||
| 3110 | const tag: zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm"; | ||
| 3111 | const result = try gz.addPlNode(tag, node, zir.Inst.Asm{ | ||
| 3112 | .asm_source = asm_source, | ||
| 3113 | .return_type = .void_type, | ||
| 3114 | .output = .none, | ||
| 3115 | .args_len = @intCast(u32, full.inputs.len), | ||
| 3116 | .clobbers_len = 0, // TODO implement asm clobbers | ||
| 3117 | }); | ||
| 3118 | |||
| 3119 | try gz.astgen.extra.ensureCapacity(mod.gpa, gz.astgen.extra.items.len + | ||
| 3120 | args.len + constraints.len); | ||
| 3121 | gz.astgen.appendRefsAssumeCapacity(args); | ||
| 3122 | gz.astgen.extra.appendSliceAssumeCapacity(constraints); | ||
| 3123 | |||
| 3124 | return rvalue(mod, scope, rl, result, node); | ||
| 3125 | } | ||
| 3126 | |||
| 3127 | fn as( | ||
| 3128 | mod: *Module, | ||
| 3129 | scope: *Scope, | ||
| 3130 | rl: ResultLoc, | ||
| 3131 | builtin_token: ast.TokenIndex, | ||
| 3132 | node: ast.Node.Index, | ||
| 3133 | lhs: ast.Node.Index, | ||
| 3134 | rhs: ast.Node.Index, | ||
| 3135 | ) InnerError!zir.Inst.Ref { | ||
| 3136 | const dest_type = try typeExpr(mod, scope, lhs); | ||
| 3137 | switch (rl) { | ||
| 3138 | .none, .discard, .ref, .ty => { | ||
| 3139 | const result = try expr(mod, scope, .{ .ty = dest_type }, rhs); | ||
| 3140 | return rvalue(mod, scope, rl, result, node); | ||
| 3141 | }, | ||
| 3142 | |||
| 3143 | .ptr => |result_ptr| { | ||
| 3144 | return asRlPtr(mod, scope, rl, result_ptr, rhs, dest_type); | ||
| 3145 | }, | ||
| 3146 | .block_ptr => |block_scope| { | ||
| 3147 | return asRlPtr(mod, scope, rl, block_scope.rl_ptr, rhs, dest_type); | ||
| 3148 | }, | ||
| 3149 | |||
| 3150 | .bitcasted_ptr => |bitcasted_ptr| { | ||
| 3151 | // TODO here we should be able to resolve the inference; we now have a type for the result. | ||
| 3152 | return mod.failTok(scope, builtin_token, "TODO implement @as with result location @bitCast", .{}); | ||
| 3153 | }, | ||
| 3154 | .inferred_ptr => |result_alloc| { | ||
| 3155 | // TODO here we should be able to resolve the inference; we now have a type for the result. | ||
| 3156 | return mod.failTok(scope, builtin_token, "TODO implement @as with inferred-type result location pointer", .{}); | ||
| 3157 | }, | ||
| 3158 | } | ||
| 3159 | } | ||
| 3160 | |||
| 3161 | fn asRlPtr( | ||
| 3162 | mod: *Module, | ||
| 3163 | scope: *Scope, | ||
| 3164 | rl: ResultLoc, | ||
| 3165 | result_ptr: zir.Inst.Ref, | ||
| 3166 | operand_node: ast.Node.Index, | ||
| 3167 | dest_type: zir.Inst.Ref, | ||
| 3168 | ) InnerError!zir.Inst.Ref { | ||
| 3169 | // Detect whether this expr() call goes into rvalue() to store the result into the | ||
| 3170 | // result location. If it does, elide the coerce_result_ptr instruction | ||
| 3171 | // as well as the store instruction, instead passing the result as an rvalue. | ||
| 3172 | const parent_gz = scope.getGenZir(); | ||
| 3173 | const astgen = parent_gz.astgen; | ||
| 3174 | |||
| 3175 | var as_scope: Scope.GenZir = .{ | ||
| 3176 | .parent = scope, | ||
| 3177 | .astgen = astgen, | ||
| 3178 | .force_comptime = parent_gz.force_comptime, | ||
| 3179 | .instructions = .{}, | ||
| 3180 | }; | ||
| 3181 | defer as_scope.instructions.deinit(mod.gpa); | ||
| 3182 | |||
| 3183 | as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); | ||
| 3184 | const result = try expr(mod, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node); | ||
| 3185 | const parent_zir = &parent_gz.instructions; | ||
| 3186 | if (as_scope.rvalue_rl_count == 1) { | ||
| 3187 | // Busted! This expression didn't actually need a pointer. | ||
| 3188 | const zir_tags = astgen.instructions.items(.tag); | ||
| 3189 | const zir_datas = astgen.instructions.items(.data); | ||
| 3190 | const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2; | ||
| 3191 | try parent_zir.ensureCapacity(mod.gpa, expected_len); | ||
| 3192 | for (as_scope.instructions.items) |src_inst| { | ||
| 3193 | if (astgen.indexToRef(src_inst) == as_scope.rl_ptr) continue; | ||
| 3194 | if (zir_tags[src_inst] == .store_to_block_ptr) { | ||
| 3195 | if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; | ||
| 3196 | } | ||
| 3197 | parent_zir.appendAssumeCapacity(src_inst); | ||
| 3198 | } | ||
| 3199 | assert(parent_zir.items.len == expected_len); | ||
| 3200 | const casted_result = try parent_gz.addBin(.as, dest_type, result); | ||
| 3201 | return rvalue(mod, scope, rl, casted_result, operand_node); | ||
| 3202 | } else { | ||
| 3203 | try parent_zir.appendSlice(mod.gpa, as_scope.instructions.items); | ||
| 3204 | return result; | ||
| 3205 | } | ||
| 3206 | } | ||
| 3207 | |||
| 3208 | fn bitCast( | ||
| 3209 | mod: *Module, | ||
| 3210 | scope: *Scope, | ||
| 3211 | rl: ResultLoc, | ||
| 3212 | builtin_token: ast.TokenIndex, | ||
| 3213 | node: ast.Node.Index, | ||
| 3214 | lhs: ast.Node.Index, | ||
| 3215 | rhs: ast.Node.Index, | ||
| 3216 | ) InnerError!zir.Inst.Ref { | ||
| 3217 | if (true) @panic("TODO update for zir-memory-layout"); | ||
| 3218 | const dest_type = try typeExpr(mod, scope, lhs); | ||
| 3219 | switch (rl) { | ||
| 3220 | .none => { | ||
| 3221 | const operand = try expr(mod, scope, .none, rhs); | ||
| 3222 | return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand); | ||
| 3223 | }, | ||
| 3224 | .discard => { | ||
| 3225 | const operand = try expr(mod, scope, .none, rhs); | ||
| 3226 | const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand); | ||
| 3227 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); | ||
| 3228 | return result; | ||
| 3229 | }, | ||
| 3230 | .ref => { | ||
| 3231 | const operand = try expr(mod, scope, .ref, rhs); | ||
| 3232 | const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand); | ||
| 3233 | return result; | ||
| 3234 | }, | ||
| 3235 | .ty => |result_ty| { | ||
| 3236 | const result = try expr(mod, scope, .none, rhs); | ||
| 3237 | const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result); | ||
| 3238 | return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted); | ||
| 3239 | }, | ||
| 3240 | .ptr => |result_ptr| { | ||
| 3241 | const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr); | ||
| 3242 | return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, rhs); | ||
| 3243 | }, | ||
| 3244 | .bitcasted_ptr => |bitcasted_ptr| { | ||
| 3245 | return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location another @bitCast", .{}); | ||
| 3246 | }, | ||
| 3247 | .block_ptr => |block_ptr| { | ||
| 3248 | return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location inferred peer types", .{}); | ||
| 3249 | }, | ||
| 3250 | .inferred_ptr => |result_alloc| { | ||
| 3251 | // TODO here we should be able to resolve the inference; we now have a type for the result. | ||
| 3252 | return mod.failTok(scope, builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{}); | ||
| 3253 | }, | ||
| 3254 | } | ||
| 3255 | } | ||
| 3256 | |||
| 3257 | fn typeOf( | ||
| 3258 | mod: *Module, | ||
| 3259 | scope: *Scope, | ||
| 3260 | rl: ResultLoc, | ||
| 3261 | builtin_token: ast.TokenIndex, | ||
| 3262 | node: ast.Node.Index, | ||
| 3263 | params: []const ast.Node.Index, | ||
| 3264 | ) InnerError!zir.Inst.Ref { | ||
| 3265 | if (params.len < 1) { | ||
| 3266 | return mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{}); | ||
| 3267 | } | ||
| 3268 | const gz = scope.getGenZir(); | ||
| 3269 | if (params.len == 1) { | ||
| 3270 | return rvalue( | ||
| 3271 | mod, | ||
| 3272 | scope, | ||
| 3273 | rl, | ||
| 3274 | try gz.addUnTok(.typeof, try expr(mod, scope, .none, params[0]), node), | ||
| 3275 | node, | ||
| 3276 | ); | ||
| 3277 | } | ||
| 3278 | const arena = scope.arena(); | ||
| 3279 | var items = try arena.alloc(zir.Inst.Ref, params.len); | ||
| 3280 | for (params) |param, param_i| { | ||
| 3281 | items[param_i] = try expr(mod, scope, .none, param); | ||
| 3282 | } | ||
| 3283 | |||
| 3284 | const result = try gz.addPlNode(.typeof_peer, node, zir.Inst.MultiOp{ | ||
| 3285 | .operands_len = @intCast(u32, params.len), | ||
| 3286 | }); | ||
| 3287 | try gz.astgen.appendRefs(items); | ||
| 3288 | |||
| 3289 | return rvalue(mod, scope, rl, result, node); | ||
| 3290 | } | ||
| 3291 | |||
| 3292 | fn builtinCall( | ||
| 3293 | mod: *Module, | ||
| 3294 | scope: *Scope, | ||
| 3295 | rl: ResultLoc, | ||
| 3296 | node: ast.Node.Index, | ||
| 3297 | params: []const ast.Node.Index, | ||
| 3298 | ) InnerError!zir.Inst.Ref { | ||
| 3299 | const tree = scope.tree(); | ||
| 3300 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3301 | |||
| 3302 | const builtin_token = main_tokens[node]; | ||
| 3303 | const builtin_name = tree.tokenSlice(builtin_token); | ||
| 3304 | |||
| 3305 | // We handle the different builtins manually because they have different semantics depending | ||
| 3306 | // on the function. For example, `@as` and others participate in result location semantics, | ||
| 3307 | // and `@cImport` creates a special scope that collects a .c source code text buffer. | ||
| 3308 | // Also, some builtins have a variable number of parameters. | ||
| 3309 | |||
| 3310 | const info = BuiltinFn.list.get(builtin_name) orelse { | ||
| 3311 | return mod.failTok(scope, builtin_token, "invalid builtin function: '{s}'", .{ | ||
| 3312 | builtin_name, | ||
| 3313 | }); | ||
| 3314 | }; | ||
| 3315 | if (info.param_count) |expected| { | ||
| 3316 | if (expected != params.len) { | ||
| 3317 | const s = if (expected == 1) "" else "s"; | ||
| 3318 | return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{ | ||
| 3319 | expected, s, params.len, | ||
| 3320 | }); | ||
| 3321 | } | ||
| 3322 | } | ||
| 3323 | |||
| 3324 | const gz = scope.getGenZir(); | ||
| 3325 | |||
| 3326 | switch (info.tag) { | ||
| 3327 | .ptr_to_int => { | ||
| 3328 | const operand = try expr(mod, scope, .none, params[0]); | ||
| 3329 | const result = try gz.addUnNode(.ptrtoint, operand, node); | ||
| 3330 | return rvalue(mod, scope, rl, result, node); | ||
| 3331 | }, | ||
| 3332 | .float_cast => { | ||
| 3333 | const dest_type = try typeExpr(mod, scope, params[0]); | ||
| 3334 | const rhs = try expr(mod, scope, .none, params[1]); | ||
| 3335 | const result = try gz.addPlNode(.floatcast, node, zir.Inst.Bin{ | ||
| 3336 | .lhs = dest_type, | ||
| 3337 | .rhs = rhs, | ||
| 3338 | }); | ||
| 3339 | return rvalue(mod, scope, rl, result, node); | ||
| 3340 | }, | ||
| 3341 | .int_cast => { | ||
| 3342 | const dest_type = try typeExpr(mod, scope, params[0]); | ||
| 3343 | const rhs = try expr(mod, scope, .none, params[1]); | ||
| 3344 | const result = try gz.addPlNode(.intcast, node, zir.Inst.Bin{ | ||
| 3345 | .lhs = dest_type, | ||
| 3346 | .rhs = rhs, | ||
| 3347 | }); | ||
| 3348 | return rvalue(mod, scope, rl, result, node); | ||
| 3349 | }, | ||
| 3350 | .breakpoint => { | ||
| 3351 | const result = try gz.add(.{ | ||
| 3352 | .tag = .breakpoint, | ||
| 3353 | .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(node) }, | ||
| 3354 | }); | ||
| 3355 | return rvalue(mod, scope, rl, result, node); | ||
| 3356 | }, | ||
| 3357 | .import => { | ||
| 3358 | const target = try expr(mod, scope, .none, params[0]); | ||
| 3359 | const result = try gz.addUnNode(.import, target, node); | ||
| 3360 | return rvalue(mod, scope, rl, result, node); | ||
| 3361 | }, | ||
| 3362 | .compile_error => { | ||
| 3363 | const target = try expr(mod, scope, .none, params[0]); | ||
| 3364 | const result = try gz.addUnNode(.compile_error, target, node); | ||
| 3365 | return rvalue(mod, scope, rl, result, node); | ||
| 3366 | }, | ||
| 3367 | .set_eval_branch_quota => { | ||
| 3368 | const quota = try expr(mod, scope, .{ .ty = .u32_type }, params[0]); | ||
| 3369 | const result = try gz.addUnNode(.set_eval_branch_quota, quota, node); | ||
| 3370 | return rvalue(mod, scope, rl, result, node); | ||
| 3371 | }, | ||
| 3372 | .compile_log => { | ||
| 3373 | const arg_refs = try mod.gpa.alloc(zir.Inst.Ref, params.len); | ||
| 3374 | defer mod.gpa.free(arg_refs); | ||
| 3375 | |||
| 3376 | for (params) |param, i| arg_refs[i] = try expr(mod, scope, .none, param); | ||
| 3377 | |||
| 3378 | const result = try gz.addPlNode(.compile_log, node, zir.Inst.MultiOp{ | ||
| 3379 | .operands_len = @intCast(u32, params.len), | ||
| 3380 | }); | ||
| 3381 | try gz.astgen.appendRefs(arg_refs); | ||
| 3382 | return rvalue(mod, scope, rl, result, node); | ||
| 3383 | }, | ||
| 3384 | .field => { | ||
| 3385 | const field_name = try comptimeExpr(mod, scope, .{ .ty = .const_slice_u8_type }, params[1]); | ||
| 3386 | if (rl == .ref) { | ||
| 3387 | return try gz.addPlNode(.field_ptr_named, node, zir.Inst.FieldNamed{ | ||
| 3388 | .lhs = try expr(mod, scope, .ref, params[0]), | ||
| 3389 | .field_name = field_name, | ||
| 3390 | }); | ||
| 3391 | } | ||
| 3392 | const result = try gz.addPlNode(.field_val_named, node, zir.Inst.FieldNamed{ | ||
| 3393 | .lhs = try expr(mod, scope, .none, params[0]), | ||
| 3394 | .field_name = field_name, | ||
| 3395 | }); | ||
| 3396 | return rvalue(mod, scope, rl, result, node); | ||
| 3397 | }, | ||
| 3398 | .as => return as(mod, scope, rl, builtin_token, node, params[0], params[1]), | ||
| 3399 | .bit_cast => return bitCast(mod, scope, rl, builtin_token, node, params[0], params[1]), | ||
| 3400 | .TypeOf => return typeOf(mod, scope, rl, builtin_token, node, params), | ||
| 3401 | |||
| 3402 | .add_with_overflow, | ||
| 3403 | .align_cast, | ||
| 3404 | .align_of, | ||
| 3405 | .atomic_load, | ||
| 3406 | .atomic_rmw, | ||
| 3407 | .atomic_store, | ||
| 3408 | .bit_offset_of, | ||
| 3409 | .bool_to_int, | ||
| 3410 | .bit_size_of, | ||
| 3411 | .mul_add, | ||
| 3412 | .byte_swap, | ||
| 3413 | .bit_reverse, | ||
| 3414 | .byte_offset_of, | ||
| 3415 | .call, | ||
| 3416 | .c_define, | ||
| 3417 | .c_import, | ||
| 3418 | .c_include, | ||
| 3419 | .clz, | ||
| 3420 | .cmpxchg_strong, | ||
| 3421 | .cmpxchg_weak, | ||
| 3422 | .ctz, | ||
| 3423 | .c_undef, | ||
| 3424 | .div_exact, | ||
| 3425 | .div_floor, | ||
| 3426 | .div_trunc, | ||
| 3427 | .embed_file, | ||
| 3428 | .enum_to_int, | ||
| 3429 | .error_name, | ||
| 3430 | .error_return_trace, | ||
| 3431 | .error_to_int, | ||
| 3432 | .err_set_cast, | ||
| 3433 | .@"export", | ||
| 3434 | .fence, | ||
| 3435 | .field_parent_ptr, | ||
| 3436 | .float_to_int, | ||
| 3437 | .has_decl, | ||
| 3438 | .has_field, | ||
| 3439 | .int_to_enum, | ||
| 3440 | .int_to_error, | ||
| 3441 | .int_to_float, | ||
| 3442 | .int_to_ptr, | ||
| 3443 | .memcpy, | ||
| 3444 | .memset, | ||
| 3445 | .wasm_memory_size, | ||
| 3446 | .wasm_memory_grow, | ||
| 3447 | .mod, | ||
| 3448 | .mul_with_overflow, | ||
| 3449 | .panic, | ||
| 3450 | .pop_count, | ||
| 3451 | .ptr_cast, | ||
| 3452 | .rem, | ||
| 3453 | .return_address, | ||
| 3454 | .set_align_stack, | ||
| 3455 | .set_cold, | ||
| 3456 | .set_float_mode, | ||
| 3457 | .set_runtime_safety, | ||
| 3458 | .shl_exact, | ||
| 3459 | .shl_with_overflow, | ||
| 3460 | .shr_exact, | ||
| 3461 | .shuffle, | ||
| 3462 | .size_of, | ||
| 3463 | .splat, | ||
| 3464 | .reduce, | ||
| 3465 | .src, | ||
| 3466 | .sqrt, | ||
| 3467 | .sin, | ||
| 3468 | .cos, | ||
| 3469 | .exp, | ||
| 3470 | .exp2, | ||
| 3471 | .log, | ||
| 3472 | .log2, | ||
| 3473 | .log10, | ||
| 3474 | .fabs, | ||
| 3475 | .floor, | ||
| 3476 | .ceil, | ||
| 3477 | .trunc, | ||
| 3478 | .round, | ||
| 3479 | .sub_with_overflow, | ||
| 3480 | .tag_name, | ||
| 3481 | .This, | ||
| 3482 | .truncate, | ||
| 3483 | .Type, | ||
| 3484 | .type_info, | ||
| 3485 | .type_name, | ||
| 3486 | .union_init, | ||
| 3487 | => return mod.failTok(scope, builtin_token, "TODO: implement builtin function {s}", .{ | ||
| 3488 | builtin_name, | ||
| 3489 | }), | ||
| 3490 | |||
| 3491 | .async_call, | ||
| 3492 | .frame, | ||
| 3493 | .Frame, | ||
| 3494 | .frame_address, | ||
| 3495 | .frame_size, | ||
| 3496 | => return mod.failTok(scope, builtin_token, "async and related features are not yet supported", .{}), | ||
| 3497 | } | ||
| 3498 | } | ||
| 3499 | |||
| 3500 | fn callExpr( | ||
| 3501 | mod: *Module, | ||
| 3502 | scope: *Scope, | ||
| 3503 | rl: ResultLoc, | ||
| 3504 | node: ast.Node.Index, | ||
| 3505 | call: ast.full.Call, | ||
| 3506 | ) InnerError!zir.Inst.Ref { | ||
| 3507 | if (call.async_token) |async_token| { | ||
| 3508 | return mod.failTok(scope, async_token, "async and related features are not yet supported", .{}); | ||
| 3509 | } | ||
| 3510 | const lhs = try expr(mod, scope, .none, call.ast.fn_expr); | ||
| 3511 | |||
| 3512 | const args = try mod.gpa.alloc(zir.Inst.Ref, call.ast.params.len); | ||
| 3513 | defer mod.gpa.free(args); | ||
| 3514 | |||
| 3515 | const gz = scope.getGenZir(); | ||
| 3516 | for (call.ast.params) |param_node, i| { | ||
| 3517 | const param_type = try gz.add(.{ | ||
| 3518 | .tag = .param_type, | ||
| 3519 | .data = .{ .param_type = .{ | ||
| 3520 | .callee = lhs, | ||
| 3521 | .param_index = @intCast(u32, i), | ||
| 3522 | } }, | ||
| 3523 | }); | ||
| 3524 | args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node); | ||
| 3525 | } | ||
| 3526 | |||
| 3527 | const modifier: std.builtin.CallOptions.Modifier = switch (call.async_token != null) { | ||
| 3528 | true => .async_kw, | ||
| 3529 | false => .auto, | ||
| 3530 | }; | ||
| 3531 | const result: zir.Inst.Ref = res: { | ||
| 3532 | const tag: zir.Inst.Tag = switch (modifier) { | ||
| 3533 | .auto => switch (args.len == 0) { | ||
| 3534 | true => break :res try gz.addUnNode(.call_none, lhs, node), | ||
| 3535 | false => .call, | ||
| 3536 | }, | ||
| 3537 | .async_kw => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 3538 | .never_tail => unreachable, | ||
| 3539 | .never_inline => unreachable, | ||
| 3540 | .no_async => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 3541 | .always_tail => unreachable, | ||
| 3542 | .always_inline => unreachable, | ||
| 3543 | .compile_time => .call_compile_time, | ||
| 3544 | }; | ||
| 3545 | break :res try gz.addCall(tag, lhs, args, node); | ||
| 3546 | }; | ||
| 3547 | return rvalue(mod, scope, rl, result, node); // TODO function call with result location | ||
| 3548 | } | ||
| 3549 | |||
| 3550 | pub const simple_types = std.ComptimeStringMap(zir.Inst.Ref, .{ | ||
| 3551 | .{ "u8", .u8_type }, | ||
| 3552 | .{ "i8", .i8_type }, | ||
| 3553 | .{ "u16", .u16_type }, | ||
| 3554 | .{ "i16", .i16_type }, | ||
| 3555 | .{ "u32", .u32_type }, | ||
| 3556 | .{ "i32", .i32_type }, | ||
| 3557 | .{ "u64", .u64_type }, | ||
| 3558 | .{ "i64", .i64_type }, | ||
| 3559 | .{ "usize", .usize_type }, | ||
| 3560 | .{ "isize", .isize_type }, | ||
| 3561 | .{ "c_short", .c_short_type }, | ||
| 3562 | .{ "c_ushort", .c_ushort_type }, | ||
| 3563 | .{ "c_int", .c_int_type }, | ||
| 3564 | .{ "c_uint", .c_uint_type }, | ||
| 3565 | .{ "c_long", .c_long_type }, | ||
| 3566 | .{ "c_ulong", .c_ulong_type }, | ||
| 3567 | .{ "c_longlong", .c_longlong_type }, | ||
| 3568 | .{ "c_ulonglong", .c_ulonglong_type }, | ||
| 3569 | .{ "c_longdouble", .c_longdouble_type }, | ||
| 3570 | .{ "f16", .f16_type }, | ||
| 3571 | .{ "f32", .f32_type }, | ||
| 3572 | .{ "f64", .f64_type }, | ||
| 3573 | .{ "f128", .f128_type }, | ||
| 3574 | .{ "c_void", .c_void_type }, | ||
| 3575 | .{ "bool", .bool_type }, | ||
| 3576 | .{ "void", .void_type }, | ||
| 3577 | .{ "type", .type_type }, | ||
| 3578 | .{ "anyerror", .anyerror_type }, | ||
| 3579 | .{ "comptime_int", .comptime_int_type }, | ||
| 3580 | .{ "comptime_float", .comptime_float_type }, | ||
| 3581 | .{ "noreturn", .noreturn_type }, | ||
| 3582 | .{ "null", .null_type }, | ||
| 3583 | .{ "undefined", .undefined_type }, | ||
| 3584 | .{ "undefined", .undef }, | ||
| 3585 | .{ "null", .null_value }, | ||
| 3586 | .{ "true", .bool_true }, | ||
| 3587 | .{ "false", .bool_false }, | ||
| 3588 | }); | ||
| 3589 | |||
| 3590 | fn nodeMayNeedMemoryLocation(scope: *Scope, start_node: ast.Node.Index) bool { | ||
| 3591 | const tree = scope.tree(); | ||
| 3592 | const node_tags = tree.nodes.items(.tag); | ||
| 3593 | const node_datas = tree.nodes.items(.data); | ||
| 3594 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3595 | const token_tags = tree.tokens.items(.tag); | ||
| 3596 | |||
| 3597 | var node = start_node; | ||
| 3598 | while (true) { | ||
| 3599 | switch (node_tags[node]) { | ||
| 3600 | .root, | ||
| 3601 | .@"usingnamespace", | ||
| 3602 | .test_decl, | ||
| 3603 | .switch_case, | ||
| 3604 | .switch_case_one, | ||
| 3605 | .container_field_init, | ||
| 3606 | .container_field_align, | ||
| 3607 | .container_field, | ||
| 3608 | .asm_output, | ||
| 3609 | .asm_input, | ||
| 3610 | => unreachable, | ||
| 3611 | |||
| 3612 | .@"return", | ||
| 3613 | .@"break", | ||
| 3614 | .@"continue", | ||
| 3615 | .bit_not, | ||
| 3616 | .bool_not, | ||
| 3617 | .global_var_decl, | ||
| 3618 | .local_var_decl, | ||
| 3619 | .simple_var_decl, | ||
| 3620 | .aligned_var_decl, | ||
| 3621 | .@"defer", | ||
| 3622 | .@"errdefer", | ||
| 3623 | .address_of, | ||
| 3624 | .optional_type, | ||
| 3625 | .negation, | ||
| 3626 | .negation_wrap, | ||
| 3627 | .@"resume", | ||
| 3628 | .array_type, | ||
| 3629 | .array_type_sentinel, | ||
| 3630 | .ptr_type_aligned, | ||
| 3631 | .ptr_type_sentinel, | ||
| 3632 | .ptr_type, | ||
| 3633 | .ptr_type_bit_range, | ||
| 3634 | .@"suspend", | ||
| 3635 | .@"anytype", | ||
| 3636 | .fn_proto_simple, | ||
| 3637 | .fn_proto_multi, | ||
| 3638 | .fn_proto_one, | ||
| 3639 | .fn_proto, | ||
| 3640 | .fn_decl, | ||
| 3641 | .anyframe_type, | ||
| 3642 | .anyframe_literal, | ||
| 3643 | .integer_literal, | ||
| 3644 | .float_literal, | ||
| 3645 | .enum_literal, | ||
| 3646 | .string_literal, | ||
| 3647 | .multiline_string_literal, | ||
| 3648 | .char_literal, | ||
| 3649 | .true_literal, | ||
| 3650 | .false_literal, | ||
| 3651 | .null_literal, | ||
| 3652 | .undefined_literal, | ||
| 3653 | .unreachable_literal, | ||
| 3654 | .identifier, | ||
| 3655 | .error_set_decl, | ||
| 3656 | .container_decl, | ||
| 3657 | .container_decl_trailing, | ||
| 3658 | .container_decl_two, | ||
| 3659 | .container_decl_two_trailing, | ||
| 3660 | .container_decl_arg, | ||
| 3661 | .container_decl_arg_trailing, | ||
| 3662 | .tagged_union, | ||
| 3663 | .tagged_union_trailing, | ||
| 3664 | .tagged_union_two, | ||
| 3665 | .tagged_union_two_trailing, | ||
| 3666 | .tagged_union_enum_tag, | ||
| 3667 | .tagged_union_enum_tag_trailing, | ||
| 3668 | .@"asm", | ||
| 3669 | .asm_simple, | ||
| 3670 | .add, | ||
| 3671 | .add_wrap, | ||
| 3672 | .array_cat, | ||
| 3673 | .array_mult, | ||
| 3674 | .assign, | ||
| 3675 | .assign_bit_and, | ||
| 3676 | .assign_bit_or, | ||
| 3677 | .assign_bit_shift_left, | ||
| 3678 | .assign_bit_shift_right, | ||
| 3679 | .assign_bit_xor, | ||
| 3680 | .assign_div, | ||
| 3681 | .assign_sub, | ||
| 3682 | .assign_sub_wrap, | ||
| 3683 | .assign_mod, | ||
| 3684 | .assign_add, | ||
| 3685 | .assign_add_wrap, | ||
| 3686 | .assign_mul, | ||
| 3687 | .assign_mul_wrap, | ||
| 3688 | .bang_equal, | ||
| 3689 | .bit_and, | ||
| 3690 | .bit_or, | ||
| 3691 | .bit_shift_left, | ||
| 3692 | .bit_shift_right, | ||
| 3693 | .bit_xor, | ||
| 3694 | .bool_and, | ||
| 3695 | .bool_or, | ||
| 3696 | .div, | ||
| 3697 | .equal_equal, | ||
| 3698 | .error_union, | ||
| 3699 | .greater_or_equal, | ||
| 3700 | .greater_than, | ||
| 3701 | .less_or_equal, | ||
| 3702 | .less_than, | ||
| 3703 | .merge_error_sets, | ||
| 3704 | .mod, | ||
| 3705 | .mul, | ||
| 3706 | .mul_wrap, | ||
| 3707 | .switch_range, | ||
| 3708 | .field_access, | ||
| 3709 | .sub, | ||
| 3710 | .sub_wrap, | ||
| 3711 | .slice, | ||
| 3712 | .slice_open, | ||
| 3713 | .slice_sentinel, | ||
| 3714 | .deref, | ||
| 3715 | .array_access, | ||
| 3716 | .error_value, | ||
| 3717 | .while_simple, // This variant cannot have an else expression. | ||
| 3718 | .while_cont, // This variant cannot have an else expression. | ||
| 3719 | .for_simple, // This variant cannot have an else expression. | ||
| 3720 | .if_simple, // This variant cannot have an else expression. | ||
| 3721 | => return false, | ||
| 3722 | |||
| 3723 | // Forward the question to the LHS sub-expression. | ||
| 3724 | .grouped_expression, | ||
| 3725 | .@"try", | ||
| 3726 | .@"await", | ||
| 3727 | .@"comptime", | ||
| 3728 | .@"nosuspend", | ||
| 3729 | .unwrap_optional, | ||
| 3730 | => node = node_datas[node].lhs, | ||
| 3731 | |||
| 3732 | // Forward the question to the RHS sub-expression. | ||
| 3733 | .@"catch", | ||
| 3734 | .@"orelse", | ||
| 3735 | => node = node_datas[node].rhs, | ||
| 3736 | |||
| 3737 | // True because these are exactly the expressions we need memory locations for. | ||
| 3738 | .array_init_one, | ||
| 3739 | .array_init_one_comma, | ||
| 3740 | .array_init_dot_two, | ||
| 3741 | .array_init_dot_two_comma, | ||
| 3742 | .array_init_dot, | ||
| 3743 | .array_init_dot_comma, | ||
| 3744 | .array_init, | ||
| 3745 | .array_init_comma, | ||
| 3746 | .struct_init_one, | ||
| 3747 | .struct_init_one_comma, | ||
| 3748 | .struct_init_dot_two, | ||
| 3749 | .struct_init_dot_two_comma, | ||
| 3750 | .struct_init_dot, | ||
| 3751 | .struct_init_dot_comma, | ||
| 3752 | .struct_init, | ||
| 3753 | .struct_init_comma, | ||
| 3754 | => return true, | ||
| 3755 | |||
| 3756 | // True because depending on comptime conditions, sub-expressions | ||
| 3757 | // may be the kind that need memory locations. | ||
| 3758 | .@"while", // This variant always has an else expression. | ||
| 3759 | .@"if", // This variant always has an else expression. | ||
| 3760 | .@"for", // This variant always has an else expression. | ||
| 3761 | .@"switch", | ||
| 3762 | .switch_comma, | ||
| 3763 | .call_one, | ||
| 3764 | .call_one_comma, | ||
| 3765 | .async_call_one, | ||
| 3766 | .async_call_one_comma, | ||
| 3767 | .call, | ||
| 3768 | .call_comma, | ||
| 3769 | .async_call, | ||
| 3770 | .async_call_comma, | ||
| 3771 | => return true, | ||
| 3772 | |||
| 3773 | .block_two, | ||
| 3774 | .block_two_semicolon, | ||
| 3775 | .block, | ||
| 3776 | .block_semicolon, | ||
| 3777 | => { | ||
| 3778 | const lbrace = main_tokens[node]; | ||
| 3779 | if (token_tags[lbrace - 1] == .colon) { | ||
| 3780 | // Labeled blocks may need a memory location to forward | ||
| 3781 | // to their break statements. | ||
| 3782 | return true; | ||
| 3783 | } else { | ||
| 3784 | return false; | ||
| 3785 | } | ||
| 3786 | }, | ||
| 3787 | |||
| 3788 | .builtin_call, | ||
| 3789 | .builtin_call_comma, | ||
| 3790 | .builtin_call_two, | ||
| 3791 | .builtin_call_two_comma, | ||
| 3792 | => { | ||
| 3793 | const builtin_token = main_tokens[node]; | ||
| 3794 | const builtin_name = tree.tokenSlice(builtin_token); | ||
| 3795 | // If the builtin is an invalid name, we don't cause an error here; instead | ||
| 3796 | // let it pass, and the error will be "invalid builtin function" later. | ||
| 3797 | const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false; | ||
| 3798 | return builtin_info.needs_mem_loc; | ||
| 3799 | }, | ||
| 3800 | } | ||
| 3801 | } | ||
| 3802 | } | ||
| 3803 | |||
| 3804 | /// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of | ||
| 3805 | /// result locations must call this function on their result. | ||
| 3806 | /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer. | ||
| 3807 | /// If the `ResultLoc` is `ty`, it will coerce the result to the type. | ||
| 3808 | fn rvalue( | ||
| 3809 | mod: *Module, | ||
| 3810 | scope: *Scope, | ||
| 3811 | rl: ResultLoc, | ||
| 3812 | result: zir.Inst.Ref, | ||
| 3813 | src_node: ast.Node.Index, | ||
| 3814 | ) InnerError!zir.Inst.Ref { | ||
| 3815 | const gz = scope.getGenZir(); | ||
| 3816 | switch (rl) { | ||
| 3817 | .none => return result, | ||
| 3818 | .discard => { | ||
| 3819 | // Emit a compile error for discarding error values. | ||
| 3820 | _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); | ||
| 3821 | return result; | ||
| 3822 | }, | ||
| 3823 | .ref => { | ||
| 3824 | // We need a pointer but we have a value. | ||
| 3825 | const tree = scope.tree(); | ||
| 3826 | const src_token = tree.firstToken(src_node); | ||
| 3827 | return gz.addUnTok(.ref, result, src_token); | ||
| 3828 | }, | ||
| 3829 | .ty => |ty_inst| { | ||
| 3830 | // Quickly eliminate some common, unnecessary type coercion. | ||
| 3831 | const as_ty = @as(u64, @enumToInt(zir.Inst.Ref.type_type)) << 32; | ||
| 3832 | const as_comptime_int = @as(u64, @enumToInt(zir.Inst.Ref.comptime_int_type)) << 32; | ||
| 3833 | const as_bool = @as(u64, @enumToInt(zir.Inst.Ref.bool_type)) << 32; | ||
| 3834 | const as_usize = @as(u64, @enumToInt(zir.Inst.Ref.usize_type)) << 32; | ||
| 3835 | const as_void = @as(u64, @enumToInt(zir.Inst.Ref.void_type)) << 32; | ||
| 3836 | switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) { | ||
| 3837 | as_ty | @enumToInt(zir.Inst.Ref.u8_type), | ||
| 3838 | as_ty | @enumToInt(zir.Inst.Ref.i8_type), | ||
| 3839 | as_ty | @enumToInt(zir.Inst.Ref.u16_type), | ||
| 3840 | as_ty | @enumToInt(zir.Inst.Ref.i16_type), | ||
| 3841 | as_ty | @enumToInt(zir.Inst.Ref.u32_type), | ||
| 3842 | as_ty | @enumToInt(zir.Inst.Ref.i32_type), | ||
| 3843 | as_ty | @enumToInt(zir.Inst.Ref.u64_type), | ||
| 3844 | as_ty | @enumToInt(zir.Inst.Ref.i64_type), | ||
| 3845 | as_ty | @enumToInt(zir.Inst.Ref.usize_type), | ||
| 3846 | as_ty | @enumToInt(zir.Inst.Ref.isize_type), | ||
| 3847 | as_ty | @enumToInt(zir.Inst.Ref.c_short_type), | ||
| 3848 | as_ty | @enumToInt(zir.Inst.Ref.c_ushort_type), | ||
| 3849 | as_ty | @enumToInt(zir.Inst.Ref.c_int_type), | ||
| 3850 | as_ty | @enumToInt(zir.Inst.Ref.c_uint_type), | ||
| 3851 | as_ty | @enumToInt(zir.Inst.Ref.c_long_type), | ||
| 3852 | as_ty | @enumToInt(zir.Inst.Ref.c_ulong_type), | ||
| 3853 | as_ty | @enumToInt(zir.Inst.Ref.c_longlong_type), | ||
| 3854 | as_ty | @enumToInt(zir.Inst.Ref.c_ulonglong_type), | ||
| 3855 | as_ty | @enumToInt(zir.Inst.Ref.c_longdouble_type), | ||
| 3856 | as_ty | @enumToInt(zir.Inst.Ref.f16_type), | ||
| 3857 | as_ty | @enumToInt(zir.Inst.Ref.f32_type), | ||
| 3858 | as_ty | @enumToInt(zir.Inst.Ref.f64_type), | ||
| 3859 | as_ty | @enumToInt(zir.Inst.Ref.f128_type), | ||
| 3860 | as_ty | @enumToInt(zir.Inst.Ref.c_void_type), | ||
| 3861 | as_ty | @enumToInt(zir.Inst.Ref.bool_type), | ||
| 3862 | as_ty | @enumToInt(zir.Inst.Ref.void_type), | ||
| 3863 | as_ty | @enumToInt(zir.Inst.Ref.type_type), | ||
| 3864 | as_ty | @enumToInt(zir.Inst.Ref.anyerror_type), | ||
| 3865 | as_ty | @enumToInt(zir.Inst.Ref.comptime_int_type), | ||
| 3866 | as_ty | @enumToInt(zir.Inst.Ref.comptime_float_type), | ||
| 3867 | as_ty | @enumToInt(zir.Inst.Ref.noreturn_type), | ||
| 3868 | as_ty | @enumToInt(zir.Inst.Ref.null_type), | ||
| 3869 | as_ty | @enumToInt(zir.Inst.Ref.undefined_type), | ||
| 3870 | as_ty | @enumToInt(zir.Inst.Ref.fn_noreturn_no_args_type), | ||
| 3871 | as_ty | @enumToInt(zir.Inst.Ref.fn_void_no_args_type), | ||
| 3872 | as_ty | @enumToInt(zir.Inst.Ref.fn_naked_noreturn_no_args_type), | ||
| 3873 | as_ty | @enumToInt(zir.Inst.Ref.fn_ccc_void_no_args_type), | ||
| 3874 | as_ty | @enumToInt(zir.Inst.Ref.single_const_pointer_to_comptime_int_type), | ||
| 3875 | as_ty | @enumToInt(zir.Inst.Ref.const_slice_u8_type), | ||
| 3876 | as_ty | @enumToInt(zir.Inst.Ref.enum_literal_type), | ||
| 3877 | as_comptime_int | @enumToInt(zir.Inst.Ref.zero), | ||
| 3878 | as_comptime_int | @enumToInt(zir.Inst.Ref.one), | ||
| 3879 | as_bool | @enumToInt(zir.Inst.Ref.bool_true), | ||
| 3880 | as_bool | @enumToInt(zir.Inst.Ref.bool_false), | ||
| 3881 | as_usize | @enumToInt(zir.Inst.Ref.zero_usize), | ||
| 3882 | as_usize | @enumToInt(zir.Inst.Ref.one_usize), | ||
| 3883 | as_void | @enumToInt(zir.Inst.Ref.void_value), | ||
| 3884 | => return result, // type of result is already correct | ||
| 3885 | |||
| 3886 | // Need an explicit type coercion instruction. | ||
| 3887 | else => return gz.addPlNode(.as_node, src_node, zir.Inst.As{ | ||
| 3888 | .dest_type = ty_inst, | ||
| 3889 | .operand = result, | ||
| 3890 | }), | ||
| 3891 | } | ||
| 3892 | }, | ||
| 3893 | .ptr => |ptr_inst| { | ||
| 3894 | _ = try gz.addPlNode(.store_node, src_node, zir.Inst.Bin{ | ||
| 3895 | .lhs = ptr_inst, | ||
| 3896 | .rhs = result, | ||
| 3897 | }); | ||
| 3898 | return result; | ||
| 3899 | }, | ||
| 3900 | .bitcasted_ptr => |bitcasted_ptr| { | ||
| 3901 | return mod.failNode(scope, src_node, "TODO implement rvalue .bitcasted_ptr", .{}); | ||
| 3902 | }, | ||
| 3903 | .inferred_ptr => |alloc| { | ||
| 3904 | _ = try gz.addBin(.store_to_inferred_ptr, alloc, result); | ||
| 3905 | return result; | ||
| 3906 | }, | ||
| 3907 | .block_ptr => |block_scope| { | ||
| 3908 | block_scope.rvalue_rl_count += 1; | ||
| 3909 | _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result); | ||
| 3910 | return result; | ||
| 3911 | }, | ||
| 3912 | } | ||
| 3913 | } | ||
| 3914 | |||
| 3915 | fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZir) ResultLoc.Strategy { | ||
| 3916 | var elide_store_to_block_ptr_instructions = false; | ||
| 3917 | switch (rl) { | ||
| 3918 | // In this branch there will not be any store_to_block_ptr instructions. | ||
| 3919 | .discard, .none, .ty, .ref => return .{ | ||
| 3920 | .tag = .break_operand, | ||
| 3921 | .elide_store_to_block_ptr_instructions = false, | ||
| 3922 | }, | ||
| 3923 | // The pointer got passed through to the sub-expressions, so we will use | ||
| 3924 | // break_void here. | ||
| 3925 | // In this branch there will not be any store_to_block_ptr instructions. | ||
| 3926 | .ptr => return .{ | ||
| 3927 | .tag = .break_void, | ||
| 3928 | .elide_store_to_block_ptr_instructions = false, | ||
| 3929 | }, | ||
| 3930 | .inferred_ptr, .bitcasted_ptr, .block_ptr => { | ||
| 3931 | if (block_scope.rvalue_rl_count == block_scope.break_count) { | ||
| 3932 | // Neither prong of the if consumed the result location, so we can | ||
| 3933 | // use break instructions to create an rvalue. | ||
| 3934 | return .{ | ||
| 3935 | .tag = .break_operand, | ||
| 3936 | .elide_store_to_block_ptr_instructions = true, | ||
| 3937 | }; | ||
| 3938 | } else { | ||
| 3939 | // Allow the store_to_block_ptr instructions to remain so that | ||
| 3940 | // semantic analysis can turn them into bitcasts. | ||
| 3941 | return .{ | ||
| 3942 | .tag = .break_void, | ||
| 3943 | .elide_store_to_block_ptr_instructions = false, | ||
| 3944 | }; | ||
| 3945 | } | ||
| 3946 | }, | ||
| 3947 | } | ||
| 3948 | } | ||
| 3949 | |||
| 3950 | fn setBlockResultLoc(block_scope: *Scope.GenZir, parent_rl: ResultLoc) void { | ||
| 3951 | // Depending on whether the result location is a pointer or value, different | ||
| 3952 | // ZIR needs to be generated. In the former case we rely on storing to the | ||
| 3953 | // pointer to communicate the result, and use breakvoid; in the latter case | ||
| 3954 | // the block break instructions will have the result values. | ||
| 3955 | // One more complication: when the result location is a pointer, we detect | ||
| 3956 | // the scenario where the result location is not consumed. In this case | ||
| 3957 | // we emit ZIR for the block break instructions to have the result values, | ||
| 3958 | // and then rvalue() on that to pass the value to the result location. | ||
| 3959 | switch (parent_rl) { | ||
| 3960 | .discard, .none, .ty, .ptr, .ref => { | ||
| 3961 | block_scope.break_result_loc = parent_rl; | ||
| 3962 | }, | ||
| 3963 | |||
| 3964 | .inferred_ptr => |ptr| { | ||
| 3965 | block_scope.rl_ptr = ptr; | ||
| 3966 | block_scope.break_result_loc = .{ .block_ptr = block_scope }; | ||
| 3967 | }, | ||
| 3968 | |||
| 3969 | .bitcasted_ptr => |ptr| { | ||
| 3970 | block_scope.rl_ptr = ptr; | ||
| 3971 | block_scope.break_result_loc = .{ .block_ptr = block_scope }; | ||
| 3972 | }, | ||
| 3973 | |||
| 3974 | .block_ptr => |parent_block_scope| { | ||
| 3975 | block_scope.rl_ptr = parent_block_scope.rl_ptr; | ||
| 3976 | block_scope.break_result_loc = .{ .block_ptr = block_scope }; | ||
| 3977 | }, | ||
| 3978 | } | ||
| 3979 | } | ||
src/Module.zig+140-230| ... | @@ -23,7 +23,7 @@ const link = @import("link.zig"); | ... | @@ -23,7 +23,7 @@ const link = @import("link.zig"); |
| 23 | const ir = @import("ir.zig"); | 23 | const ir = @import("ir.zig"); |
| 24 | const zir = @import("zir.zig"); | 24 | const zir = @import("zir.zig"); |
| 25 | const trace = @import("tracy.zig").trace; | 25 | const trace = @import("tracy.zig").trace; |
| 26 | const astgen = @import("astgen.zig"); | 26 | const AstGen = @import("AstGen.zig"); |
| 27 | const Sema = @import("Sema.zig"); | 27 | const Sema = @import("Sema.zig"); |
| 28 | const target_util = @import("target.zig"); | 28 | const target_util = @import("target.zig"); |
| 29 | 29 | ||
| ... | @@ -407,9 +407,9 @@ pub const Scope = struct { | ... | @@ -407,9 +407,9 @@ pub const Scope = struct { |
| 407 | pub fn arena(scope: *Scope) *Allocator { | 407 | pub fn arena(scope: *Scope) *Allocator { |
| 408 | switch (scope.tag) { | 408 | switch (scope.tag) { |
| 409 | .block => return scope.cast(Block).?.sema.arena, | 409 | .block => return scope.cast(Block).?.sema.arena, |
| 410 | .gen_zir => return scope.cast(GenZir).?.zir_code.arena, | 410 | .gen_zir => return scope.cast(GenZir).?.astgen.arena, |
| 411 | .local_val => return scope.cast(LocalVal).?.gen_zir.zir_code.arena, | 411 | .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.arena, |
| 412 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir.zir_code.arena, | 412 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.arena, |
| 413 | .file => unreachable, | 413 | .file => unreachable, |
| 414 | .container => unreachable, | 414 | .container => unreachable, |
| 415 | .decl_ref => unreachable, | 415 | .decl_ref => unreachable, |
| ... | @@ -419,9 +419,9 @@ pub const Scope = struct { | ... | @@ -419,9 +419,9 @@ pub const Scope = struct { |
| 419 | pub fn ownerDecl(scope: *Scope) ?*Decl { | 419 | pub fn ownerDecl(scope: *Scope) ?*Decl { |
| 420 | return switch (scope.tag) { | 420 | return switch (scope.tag) { |
| 421 | .block => scope.cast(Block).?.sema.owner_decl, | 421 | .block => scope.cast(Block).?.sema.owner_decl, |
| 422 | .gen_zir => scope.cast(GenZir).?.zir_code.decl, | 422 | .gen_zir => scope.cast(GenZir).?.astgen.decl, |
| 423 | .local_val => scope.cast(LocalVal).?.gen_zir.zir_code.decl, | 423 | .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl, |
| 424 | .local_ptr => scope.cast(LocalPtr).?.gen_zir.zir_code.decl, | 424 | .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl, |
| 425 | .file => null, | 425 | .file => null, |
| 426 | .container => null, | 426 | .container => null, |
| 427 | .decl_ref => scope.cast(DeclRef).?.decl, | 427 | .decl_ref => scope.cast(DeclRef).?.decl, |
| ... | @@ -431,9 +431,9 @@ pub const Scope = struct { | ... | @@ -431,9 +431,9 @@ pub const Scope = struct { |
| 431 | pub fn srcDecl(scope: *Scope) ?*Decl { | 431 | pub fn srcDecl(scope: *Scope) ?*Decl { |
| 432 | return switch (scope.tag) { | 432 | return switch (scope.tag) { |
| 433 | .block => scope.cast(Block).?.src_decl, | 433 | .block => scope.cast(Block).?.src_decl, |
| 434 | .gen_zir => scope.cast(GenZir).?.zir_code.decl, | 434 | .gen_zir => scope.cast(GenZir).?.astgen.decl, |
| 435 | .local_val => scope.cast(LocalVal).?.gen_zir.zir_code.decl, | 435 | .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl, |
| 436 | .local_ptr => scope.cast(LocalPtr).?.gen_zir.zir_code.decl, | 436 | .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl, |
| 437 | .file => null, | 437 | .file => null, |
| 438 | .container => null, | 438 | .container => null, |
| 439 | .decl_ref => scope.cast(DeclRef).?.decl, | 439 | .decl_ref => scope.cast(DeclRef).?.decl, |
| ... | @@ -444,9 +444,9 @@ pub const Scope = struct { | ... | @@ -444,9 +444,9 @@ pub const Scope = struct { |
| 444 | pub fn namespace(scope: *Scope) *Container { | 444 | pub fn namespace(scope: *Scope) *Container { |
| 445 | switch (scope.tag) { | 445 | switch (scope.tag) { |
| 446 | .block => return scope.cast(Block).?.sema.owner_decl.container, | 446 | .block => return scope.cast(Block).?.sema.owner_decl.container, |
| 447 | .gen_zir => return scope.cast(GenZir).?.zir_code.decl.container, | 447 | .gen_zir => return scope.cast(GenZir).?.astgen.decl.container, |
| 448 | .local_val => return scope.cast(LocalVal).?.gen_zir.zir_code.decl.container, | 448 | .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.decl.container, |
| 449 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir.zir_code.decl.container, | 449 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.decl.container, |
| 450 | .file => return &scope.cast(File).?.root_container, | 450 | .file => return &scope.cast(File).?.root_container, |
| 451 | .container => return scope.cast(Container).?, | 451 | .container => return scope.cast(Container).?, |
| 452 | .decl_ref => return scope.cast(DeclRef).?.decl.container, | 452 | .decl_ref => return scope.cast(DeclRef).?.decl.container, |
| ... | @@ -474,8 +474,8 @@ pub const Scope = struct { | ... | @@ -474,8 +474,8 @@ pub const Scope = struct { |
| 474 | .file => return &scope.cast(File).?.tree, | 474 | .file => return &scope.cast(File).?.tree, |
| 475 | .block => return &scope.cast(Block).?.src_decl.container.file_scope.tree, | 475 | .block => return &scope.cast(Block).?.src_decl.container.file_scope.tree, |
| 476 | .gen_zir => return scope.cast(GenZir).?.tree(), | 476 | .gen_zir => return scope.cast(GenZir).?.tree(), |
| 477 | .local_val => return &scope.cast(LocalVal).?.gen_zir.zir_code.decl.container.file_scope.tree, | 477 | .local_val => return &scope.cast(LocalVal).?.gen_zir.astgen.decl.container.file_scope.tree, |
| 478 | .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.zir_code.decl.container.file_scope.tree, | 478 | .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.astgen.decl.container.file_scope.tree, |
| 479 | .container => return &scope.cast(Container).?.file_scope.tree, | 479 | .container => return &scope.cast(Container).?.file_scope.tree, |
| 480 | .decl_ref => return &scope.cast(DeclRef).?.decl.container.file_scope.tree, | 480 | .decl_ref => return &scope.cast(DeclRef).?.decl.container.file_scope.tree, |
| 481 | } | 481 | } |
| ... | @@ -913,15 +913,15 @@ pub const Scope = struct { | ... | @@ -913,15 +913,15 @@ pub const Scope = struct { |
| 913 | /// Parents can be: `GenZir`, `File` | 913 | /// Parents can be: `GenZir`, `File` |
| 914 | parent: *Scope, | 914 | parent: *Scope, |
| 915 | /// All `GenZir` scopes for the same ZIR share this. | 915 | /// All `GenZir` scopes for the same ZIR share this. |
| 916 | zir_code: *WipZirCode, | 916 | astgen: *AstGen, |
| 917 | /// Keeps track of the list of instructions in this scope only. Indexes | 917 | /// Keeps track of the list of instructions in this scope only. Indexes |
| 918 | /// to instructions in `zir_code`. | 918 | /// to instructions in `astgen`. |
| 919 | instructions: ArrayListUnmanaged(zir.Inst.Index) = .{}, | 919 | instructions: ArrayListUnmanaged(zir.Inst.Index) = .{}, |
| 920 | label: ?Label = null, | 920 | label: ?Label = null, |
| 921 | break_block: zir.Inst.Index = 0, | 921 | break_block: zir.Inst.Index = 0, |
| 922 | continue_block: zir.Inst.Index = 0, | 922 | continue_block: zir.Inst.Index = 0, |
| 923 | /// Only valid when setBlockResultLoc is called. | 923 | /// Only valid when setBlockResultLoc is called. |
| 924 | break_result_loc: astgen.ResultLoc = undefined, | 924 | break_result_loc: AstGen.ResultLoc = undefined, |
| 925 | /// When a block has a pointer result location, here it is. | 925 | /// When a block has a pointer result location, here it is. |
| 926 | rl_ptr: zir.Inst.Ref = .none, | 926 | rl_ptr: zir.Inst.Ref = .none, |
| 927 | /// Keeps track of how many branches of a block did not actually | 927 | /// Keeps track of how many branches of a block did not actually |
| ... | @@ -948,49 +948,51 @@ pub const Scope = struct { | ... | @@ -948,49 +948,51 @@ pub const Scope = struct { |
| 948 | }; | 948 | }; |
| 949 | 949 | ||
| 950 | /// Only valid to call on the top of the `GenZir` stack. Completes the | 950 | /// Only valid to call on the top of the `GenZir` stack. Completes the |
| 951 | /// `WipZirCode` into a `zir.Code`. Leaves the `WipZirCode` in an | 951 | /// `AstGen` into a `zir.Code`. Leaves the `AstGen` in an |
| 952 | /// initialized, but empty, state. | 952 | /// initialized, but empty, state. |
| 953 | pub fn finish(gz: *GenZir) !zir.Code { | 953 | pub fn finish(gz: *GenZir) !zir.Code { |
| 954 | const gpa = gz.zir_code.gpa; | 954 | const gpa = gz.astgen.mod.gpa; |
| 955 | try gz.setBlockBody(0); | 955 | try gz.setBlockBody(0); |
| 956 | return zir.Code{ | 956 | return zir.Code{ |
| 957 | .instructions = gz.zir_code.instructions.toOwnedSlice(), | 957 | .instructions = gz.astgen.instructions.toOwnedSlice(), |
| 958 | .string_bytes = gz.zir_code.string_bytes.toOwnedSlice(gpa), | 958 | .string_bytes = gz.astgen.string_bytes.toOwnedSlice(gpa), |
| 959 | .extra = gz.zir_code.extra.toOwnedSlice(gpa), | 959 | .extra = gz.astgen.extra.toOwnedSlice(gpa), |
| 960 | .decls = gz.zir_code.decls.toOwnedSlice(gpa), | 960 | .decls = gz.astgen.decls.toOwnedSlice(gpa), |
| 961 | }; | 961 | }; |
| 962 | } | 962 | } |
| 963 | 963 | ||
| 964 | pub fn tokSrcLoc(gz: GenZir, token_index: ast.TokenIndex) LazySrcLoc { | 964 | pub fn tokSrcLoc(gz: GenZir, token_index: ast.TokenIndex) LazySrcLoc { |
| 965 | return gz.zir_code.decl.tokSrcLoc(token_index); | 965 | return gz.astgen.decl.tokSrcLoc(token_index); |
| 966 | } | 966 | } |
| 967 | 967 | ||
| 968 | pub fn nodeSrcLoc(gz: GenZir, node_index: ast.Node.Index) LazySrcLoc { | 968 | pub fn nodeSrcLoc(gz: GenZir, node_index: ast.Node.Index) LazySrcLoc { |
| 969 | return gz.zir_code.decl.nodeSrcLoc(node_index); | 969 | return gz.astgen.decl.nodeSrcLoc(node_index); |
| 970 | } | 970 | } |
| 971 | 971 | ||
| 972 | pub fn tree(gz: *const GenZir) *const ast.Tree { | 972 | pub fn tree(gz: *const GenZir) *const ast.Tree { |
| 973 | return &gz.zir_code.decl.container.file_scope.tree; | 973 | return &gz.astgen.decl.container.file_scope.tree; |
| 974 | } | 974 | } |
| 975 | 975 | ||
| 976 | pub fn setBoolBrBody(gz: GenZir, inst: zir.Inst.Index) !void { | 976 | pub fn setBoolBrBody(gz: GenZir, inst: zir.Inst.Index) !void { |
| 977 | try gz.zir_code.extra.ensureCapacity(gz.zir_code.gpa, gz.zir_code.extra.items.len + | 977 | const gpa = gz.astgen.mod.gpa; |
| 978 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | ||
| 978 | @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); | 979 | @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); |
| 979 | const zir_datas = gz.zir_code.instructions.items(.data); | 980 | const zir_datas = gz.astgen.instructions.items(.data); |
| 980 | zir_datas[inst].bool_br.payload_index = gz.zir_code.addExtraAssumeCapacity( | 981 | zir_datas[inst].bool_br.payload_index = gz.astgen.addExtraAssumeCapacity( |
| 981 | zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, | 982 | zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, |
| 982 | ); | 983 | ); |
| 983 | gz.zir_code.extra.appendSliceAssumeCapacity(gz.instructions.items); | 984 | gz.astgen.extra.appendSliceAssumeCapacity(gz.instructions.items); |
| 984 | } | 985 | } |
| 985 | 986 | ||
| 986 | pub fn setBlockBody(gz: GenZir, inst: zir.Inst.Index) !void { | 987 | pub fn setBlockBody(gz: GenZir, inst: zir.Inst.Index) !void { |
| 987 | try gz.zir_code.extra.ensureCapacity(gz.zir_code.gpa, gz.zir_code.extra.items.len + | 988 | const gpa = gz.astgen.mod.gpa; |
| 989 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | ||
| 988 | @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); | 990 | @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); |
| 989 | const zir_datas = gz.zir_code.instructions.items(.data); | 991 | const zir_datas = gz.astgen.instructions.items(.data); |
| 990 | zir_datas[inst].pl_node.payload_index = gz.zir_code.addExtraAssumeCapacity( | 992 | zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity( |
| 991 | zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, | 993 | zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, |
| 992 | ); | 994 | ); |
| 993 | gz.zir_code.extra.appendSliceAssumeCapacity(gz.instructions.items); | 995 | gz.astgen.extra.appendSliceAssumeCapacity(gz.instructions.items); |
| 994 | } | 996 | } |
| 995 | 997 | ||
| 996 | pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct { | 998 | pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct { |
| ... | @@ -1000,20 +1002,20 @@ pub const Scope = struct { | ... | @@ -1000,20 +1002,20 @@ pub const Scope = struct { |
| 1000 | }) !zir.Inst.Ref { | 1002 | }) !zir.Inst.Ref { |
| 1001 | assert(args.ret_ty != .none); | 1003 | assert(args.ret_ty != .none); |
| 1002 | assert(args.cc != .none); | 1004 | assert(args.cc != .none); |
| 1003 | const gpa = gz.zir_code.gpa; | 1005 | const gpa = gz.astgen.mod.gpa; |
| 1004 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | 1006 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); |
| 1005 | try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1); | 1007 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); |
| 1006 | try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len + | 1008 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + |
| 1007 | @typeInfo(zir.Inst.FnTypeCc).Struct.fields.len + args.param_types.len); | 1009 | @typeInfo(zir.Inst.FnTypeCc).Struct.fields.len + args.param_types.len); |
| 1008 | 1010 | ||
| 1009 | const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.FnTypeCc{ | 1011 | const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnTypeCc{ |
| 1010 | .cc = args.cc, | 1012 | .cc = args.cc, |
| 1011 | .param_types_len = @intCast(u32, args.param_types.len), | 1013 | .param_types_len = @intCast(u32, args.param_types.len), |
| 1012 | }); | 1014 | }); |
| 1013 | gz.zir_code.appendRefsAssumeCapacity(args.param_types); | 1015 | gz.astgen.appendRefsAssumeCapacity(args.param_types); |
| 1014 | 1016 | ||
| 1015 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | 1017 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); |
| 1016 | gz.zir_code.instructions.appendAssumeCapacity(.{ | 1018 | gz.astgen.instructions.appendAssumeCapacity(.{ |
| 1017 | .tag = tag, | 1019 | .tag = tag, |
| 1018 | .data = .{ .fn_type = .{ | 1020 | .data = .{ .fn_type = .{ |
| 1019 | .return_type = args.ret_ty, | 1021 | .return_type = args.ret_ty, |
| ... | @@ -1021,7 +1023,7 @@ pub const Scope = struct { | ... | @@ -1021,7 +1023,7 @@ pub const Scope = struct { |
| 1021 | } }, | 1023 | } }, |
| 1022 | }); | 1024 | }); |
| 1023 | gz.instructions.appendAssumeCapacity(new_index); | 1025 | gz.instructions.appendAssumeCapacity(new_index); |
| 1024 | return gz.zir_code.indexToRef(new_index); | 1026 | return gz.astgen.indexToRef(new_index); |
| 1025 | } | 1027 | } |
| 1026 | 1028 | ||
| 1027 | pub fn addFnType( | 1029 | pub fn addFnType( |
| ... | @@ -1031,19 +1033,19 @@ pub const Scope = struct { | ... | @@ -1031,19 +1033,19 @@ pub const Scope = struct { |
| 1031 | param_types: []const zir.Inst.Ref, | 1033 | param_types: []const zir.Inst.Ref, |
| 1032 | ) !zir.Inst.Ref { | 1034 | ) !zir.Inst.Ref { |
| 1033 | assert(ret_ty != .none); | 1035 | assert(ret_ty != .none); |
| 1034 | const gpa = gz.zir_code.gpa; | 1036 | const gpa = gz.astgen.mod.gpa; |
| 1035 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | 1037 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); |
| 1036 | try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1); | 1038 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); |
| 1037 | try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len + | 1039 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + |
| 1038 | @typeInfo(zir.Inst.FnType).Struct.fields.len + param_types.len); | 1040 | @typeInfo(zir.Inst.FnType).Struct.fields.len + param_types.len); |
| 1039 | 1041 | ||
| 1040 | const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.FnType{ | 1042 | const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnType{ |
| 1041 | .param_types_len = @intCast(u32, param_types.len), | 1043 | .param_types_len = @intCast(u32, param_types.len), |
| 1042 | }); | 1044 | }); |
| 1043 | gz.zir_code.appendRefsAssumeCapacity(param_types); | 1045 | gz.astgen.appendRefsAssumeCapacity(param_types); |
| 1044 | 1046 | ||
| 1045 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | 1047 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); |
| 1046 | gz.zir_code.instructions.appendAssumeCapacity(.{ | 1048 | gz.astgen.instructions.appendAssumeCapacity(.{ |
| 1047 | .tag = tag, | 1049 | .tag = tag, |
| 1048 | .data = .{ .fn_type = .{ | 1050 | .data = .{ .fn_type = .{ |
| 1049 | .return_type = ret_ty, | 1051 | .return_type = ret_ty, |
| ... | @@ -1051,7 +1053,7 @@ pub const Scope = struct { | ... | @@ -1051,7 +1053,7 @@ pub const Scope = struct { |
| 1051 | } }, | 1053 | } }, |
| 1052 | }); | 1054 | }); |
| 1053 | gz.instructions.appendAssumeCapacity(new_index); | 1055 | gz.instructions.appendAssumeCapacity(new_index); |
| 1054 | return gz.zir_code.indexToRef(new_index); | 1056 | return gz.astgen.indexToRef(new_index); |
| 1055 | } | 1057 | } |
| 1056 | 1058 | ||
| 1057 | pub fn addCall( | 1059 | pub fn addCall( |
| ... | @@ -1064,28 +1066,28 @@ pub const Scope = struct { | ... | @@ -1064,28 +1066,28 @@ pub const Scope = struct { |
| 1064 | ) !zir.Inst.Ref { | 1066 | ) !zir.Inst.Ref { |
| 1065 | assert(callee != .none); | 1067 | assert(callee != .none); |
| 1066 | assert(src_node != 0); | 1068 | assert(src_node != 0); |
| 1067 | const gpa = gz.zir_code.gpa; | 1069 | const gpa = gz.astgen.mod.gpa; |
| 1068 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | 1070 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); |
| 1069 | try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1); | 1071 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); |
| 1070 | try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len + | 1072 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + |
| 1071 | @typeInfo(zir.Inst.Call).Struct.fields.len + args.len); | 1073 | @typeInfo(zir.Inst.Call).Struct.fields.len + args.len); |
| 1072 | 1074 | ||
| 1073 | const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.Call{ | 1075 | const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.Call{ |
| 1074 | .callee = callee, | 1076 | .callee = callee, |
| 1075 | .args_len = @intCast(u32, args.len), | 1077 | .args_len = @intCast(u32, args.len), |
| 1076 | }); | 1078 | }); |
| 1077 | gz.zir_code.appendRefsAssumeCapacity(args); | 1079 | gz.astgen.appendRefsAssumeCapacity(args); |
| 1078 | 1080 | ||
| 1079 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | 1081 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); |
| 1080 | gz.zir_code.instructions.appendAssumeCapacity(.{ | 1082 | gz.astgen.instructions.appendAssumeCapacity(.{ |
| 1081 | .tag = tag, | 1083 | .tag = tag, |
| 1082 | .data = .{ .pl_node = .{ | 1084 | .data = .{ .pl_node = .{ |
| 1083 | .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node), | 1085 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), |
| 1084 | .payload_index = payload_index, | 1086 | .payload_index = payload_index, |
| 1085 | } }, | 1087 | } }, |
| 1086 | }); | 1088 | }); |
| 1087 | gz.instructions.appendAssumeCapacity(new_index); | 1089 | gz.instructions.appendAssumeCapacity(new_index); |
| 1088 | return gz.zir_code.indexToRef(new_index); | 1090 | return gz.astgen.indexToRef(new_index); |
| 1089 | } | 1091 | } |
| 1090 | 1092 | ||
| 1091 | /// Note that this returns a `zir.Inst.Index` not a ref. | 1093 | /// Note that this returns a `zir.Inst.Index` not a ref. |
| ... | @@ -1096,12 +1098,12 @@ pub const Scope = struct { | ... | @@ -1096,12 +1098,12 @@ pub const Scope = struct { |
| 1096 | lhs: zir.Inst.Ref, | 1098 | lhs: zir.Inst.Ref, |
| 1097 | ) !zir.Inst.Index { | 1099 | ) !zir.Inst.Index { |
| 1098 | assert(lhs != .none); | 1100 | assert(lhs != .none); |
| 1099 | const gpa = gz.zir_code.gpa; | 1101 | const gpa = gz.astgen.mod.gpa; |
| 1100 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | 1102 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); |
| 1101 | try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1); | 1103 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); |
| 1102 | 1104 | ||
| 1103 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | 1105 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); |
| 1104 | gz.zir_code.instructions.appendAssumeCapacity(.{ | 1106 | gz.astgen.instructions.appendAssumeCapacity(.{ |
| 1105 | .tag = tag, | 1107 | .tag = tag, |
| 1106 | .data = .{ .bool_br = .{ | 1108 | .data = .{ .bool_br = .{ |
| 1107 | .lhs = lhs, | 1109 | .lhs = lhs, |
| ... | @@ -1131,7 +1133,7 @@ pub const Scope = struct { | ... | @@ -1131,7 +1133,7 @@ pub const Scope = struct { |
| 1131 | .tag = tag, | 1133 | .tag = tag, |
| 1132 | .data = .{ .un_node = .{ | 1134 | .data = .{ .un_node = .{ |
| 1133 | .operand = operand, | 1135 | .operand = operand, |
| 1134 | .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node), | 1136 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), |
| 1135 | } }, | 1137 | } }, |
| 1136 | }); | 1138 | }); |
| 1137 | } | 1139 | } |
| ... | @@ -1143,21 +1145,21 @@ pub const Scope = struct { | ... | @@ -1143,21 +1145,21 @@ pub const Scope = struct { |
| 1143 | src_node: ast.Node.Index, | 1145 | src_node: ast.Node.Index, |
| 1144 | extra: anytype, | 1146 | extra: anytype, |
| 1145 | ) !zir.Inst.Ref { | 1147 | ) !zir.Inst.Ref { |
| 1146 | const gpa = gz.zir_code.gpa; | 1148 | const gpa = gz.astgen.mod.gpa; |
| 1147 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | 1149 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); |
| 1148 | try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1); | 1150 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); |
| 1149 | 1151 | ||
| 1150 | const payload_index = try gz.zir_code.addExtra(extra); | 1152 | const payload_index = try gz.astgen.addExtra(extra); |
| 1151 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | 1153 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); |
| 1152 | gz.zir_code.instructions.appendAssumeCapacity(.{ | 1154 | gz.astgen.instructions.appendAssumeCapacity(.{ |
| 1153 | .tag = tag, | 1155 | .tag = tag, |
| 1154 | .data = .{ .pl_node = .{ | 1156 | .data = .{ .pl_node = .{ |
| 1155 | .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node), | 1157 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), |
| 1156 | .payload_index = payload_index, | 1158 | .payload_index = payload_index, |
| 1157 | } }, | 1159 | } }, |
| 1158 | }); | 1160 | }); |
| 1159 | gz.instructions.appendAssumeCapacity(new_index); | 1161 | gz.instructions.appendAssumeCapacity(new_index); |
| 1160 | return gz.zir_code.indexToRef(new_index); | 1162 | return gz.astgen.indexToRef(new_index); |
| 1161 | } | 1163 | } |
| 1162 | 1164 | ||
| 1163 | pub fn addArrayTypeSentinel( | 1165 | pub fn addArrayTypeSentinel( |
| ... | @@ -1166,16 +1168,16 @@ pub const Scope = struct { | ... | @@ -1166,16 +1168,16 @@ pub const Scope = struct { |
| 1166 | sentinel: zir.Inst.Ref, | 1168 | sentinel: zir.Inst.Ref, |
| 1167 | elem_type: zir.Inst.Ref, | 1169 | elem_type: zir.Inst.Ref, |
| 1168 | ) !zir.Inst.Ref { | 1170 | ) !zir.Inst.Ref { |
| 1169 | const gpa = gz.zir_code.gpa; | 1171 | const gpa = gz.astgen.mod.gpa; |
| 1170 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | 1172 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); |
| 1171 | try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1); | 1173 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); |
| 1172 | 1174 | ||
| 1173 | const payload_index = try gz.zir_code.addExtra(zir.Inst.ArrayTypeSentinel{ | 1175 | const payload_index = try gz.astgen.addExtra(zir.Inst.ArrayTypeSentinel{ |
| 1174 | .sentinel = sentinel, | 1176 | .sentinel = sentinel, |
| 1175 | .elem_type = elem_type, | 1177 | .elem_type = elem_type, |
| 1176 | }); | 1178 | }); |
| 1177 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | 1179 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); |
| 1178 | gz.zir_code.instructions.appendAssumeCapacity(.{ | 1180 | gz.astgen.instructions.appendAssumeCapacity(.{ |
| 1179 | .tag = .array_type_sentinel, | 1181 | .tag = .array_type_sentinel, |
| 1180 | .data = .{ .array_type_sentinel = .{ | 1182 | .data = .{ .array_type_sentinel = .{ |
| 1181 | .len = len, | 1183 | .len = len, |
| ... | @@ -1183,7 +1185,7 @@ pub const Scope = struct { | ... | @@ -1183,7 +1185,7 @@ pub const Scope = struct { |
| 1183 | } }, | 1185 | } }, |
| 1184 | }); | 1186 | }); |
| 1185 | gz.instructions.appendAssumeCapacity(new_index); | 1187 | gz.instructions.appendAssumeCapacity(new_index); |
| 1186 | return gz.zir_code.indexToRef(new_index); | 1188 | return gz.astgen.indexToRef(new_index); |
| 1187 | } | 1189 | } |
| 1188 | 1190 | ||
| 1189 | pub fn addUnTok( | 1191 | pub fn addUnTok( |
| ... | @@ -1198,7 +1200,7 @@ pub const Scope = struct { | ... | @@ -1198,7 +1200,7 @@ pub const Scope = struct { |
| 1198 | .tag = tag, | 1200 | .tag = tag, |
| 1199 | .data = .{ .un_tok = .{ | 1201 | .data = .{ .un_tok = .{ |
| 1200 | .operand = operand, | 1202 | .operand = operand, |
| 1201 | .src_tok = abs_tok_index - gz.zir_code.decl.srcToken(), | 1203 | .src_tok = abs_tok_index - gz.astgen.decl.srcToken(), |
| 1202 | } }, | 1204 | } }, |
| 1203 | }); | 1205 | }); |
| 1204 | } | 1206 | } |
| ... | @@ -1214,7 +1216,7 @@ pub const Scope = struct { | ... | @@ -1214,7 +1216,7 @@ pub const Scope = struct { |
| 1214 | .tag = tag, | 1216 | .tag = tag, |
| 1215 | .data = .{ .str_tok = .{ | 1217 | .data = .{ .str_tok = .{ |
| 1216 | .start = str_index, | 1218 | .start = str_index, |
| 1217 | .src_tok = abs_tok_index - gz.zir_code.decl.srcToken(), | 1219 | .src_tok = abs_tok_index - gz.astgen.decl.srcToken(), |
| 1218 | } }, | 1220 | } }, |
| 1219 | }); | 1221 | }); |
| 1220 | } | 1222 | } |
| ... | @@ -1260,7 +1262,7 @@ pub const Scope = struct { | ... | @@ -1260,7 +1262,7 @@ pub const Scope = struct { |
| 1260 | return gz.add(.{ | 1262 | return gz.add(.{ |
| 1261 | .tag = tag, | 1263 | .tag = tag, |
| 1262 | .data = .{ .pl_node = .{ | 1264 | .data = .{ .pl_node = .{ |
| 1263 | .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node), | 1265 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), |
| 1264 | .payload_index = decl_index, | 1266 | .payload_index = decl_index, |
| 1265 | } }, | 1267 | } }, |
| 1266 | }); | 1268 | }); |
| ... | @@ -1274,7 +1276,7 @@ pub const Scope = struct { | ... | @@ -1274,7 +1276,7 @@ pub const Scope = struct { |
| 1274 | ) !zir.Inst.Ref { | 1276 | ) !zir.Inst.Ref { |
| 1275 | return gz.add(.{ | 1277 | return gz.add(.{ |
| 1276 | .tag = tag, | 1278 | .tag = tag, |
| 1277 | .data = .{ .node = gz.zir_code.decl.nodeIndexToRelative(src_node) }, | 1279 | .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(src_node) }, |
| 1278 | }); | 1280 | }); |
| 1279 | } | 1281 | } |
| 1280 | 1282 | ||
| ... | @@ -1298,11 +1300,12 @@ pub const Scope = struct { | ... | @@ -1298,11 +1300,12 @@ pub const Scope = struct { |
| 1298 | /// Does *not* append the block instruction to the scope. | 1300 | /// Does *not* append the block instruction to the scope. |
| 1299 | /// Leaves the `payload_index` field undefined. | 1301 | /// Leaves the `payload_index` field undefined. |
| 1300 | pub fn addBlock(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index { | 1302 | pub fn addBlock(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index { |
| 1301 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | 1303 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); |
| 1302 | try gz.zir_code.instructions.append(gz.zir_code.gpa, .{ | 1304 | const gpa = gz.astgen.mod.gpa; |
| 1305 | try gz.astgen.instructions.append(gpa, .{ | ||
| 1303 | .tag = tag, | 1306 | .tag = tag, |
| 1304 | .data = .{ .pl_node = .{ | 1307 | .data = .{ .pl_node = .{ |
| 1305 | .src_node = gz.zir_code.decl.nodeIndexToRelative(node), | 1308 | .src_node = gz.astgen.decl.nodeIndexToRelative(node), |
| 1306 | .payload_index = undefined, | 1309 | .payload_index = undefined, |
| 1307 | } }, | 1310 | } }, |
| 1308 | }); | 1311 | }); |
| ... | @@ -1312,12 +1315,13 @@ pub const Scope = struct { | ... | @@ -1312,12 +1315,13 @@ pub const Scope = struct { |
| 1312 | /// Note that this returns a `zir.Inst.Index` not a ref. | 1315 | /// Note that this returns a `zir.Inst.Index` not a ref. |
| 1313 | /// Leaves the `payload_index` field undefined. | 1316 | /// Leaves the `payload_index` field undefined. |
| 1314 | pub fn addCondBr(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index { | 1317 | pub fn addCondBr(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index { |
| 1315 | try gz.instructions.ensureCapacity(gz.zir_code.gpa, gz.instructions.items.len + 1); | 1318 | const gpa = gz.astgen.mod.gpa; |
| 1316 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | 1319 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); |
| 1317 | try gz.zir_code.instructions.append(gz.zir_code.gpa, .{ | 1320 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); |
| 1321 | try gz.astgen.instructions.append(gpa, .{ | ||
| 1318 | .tag = tag, | 1322 | .tag = tag, |
| 1319 | .data = .{ .pl_node = .{ | 1323 | .data = .{ .pl_node = .{ |
| 1320 | .src_node = gz.zir_code.decl.nodeIndexToRelative(node), | 1324 | .src_node = gz.astgen.decl.nodeIndexToRelative(node), |
| 1321 | .payload_index = undefined, | 1325 | .payload_index = undefined, |
| 1322 | } }, | 1326 | } }, |
| 1323 | }); | 1327 | }); |
| ... | @@ -1326,16 +1330,16 @@ pub const Scope = struct { | ... | @@ -1326,16 +1330,16 @@ pub const Scope = struct { |
| 1326 | } | 1330 | } |
| 1327 | 1331 | ||
| 1328 | pub fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref { | 1332 | pub fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref { |
| 1329 | return gz.zir_code.indexToRef(try gz.addAsIndex(inst)); | 1333 | return gz.astgen.indexToRef(try gz.addAsIndex(inst)); |
| 1330 | } | 1334 | } |
| 1331 | 1335 | ||
| 1332 | pub fn addAsIndex(gz: *GenZir, inst: zir.Inst) !zir.Inst.Index { | 1336 | pub fn addAsIndex(gz: *GenZir, inst: zir.Inst) !zir.Inst.Index { |
| 1333 | const gpa = gz.zir_code.gpa; | 1337 | const gpa = gz.astgen.mod.gpa; |
| 1334 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | 1338 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); |
| 1335 | try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1); | 1339 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); |
| 1336 | 1340 | ||
| 1337 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | 1341 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); |
| 1338 | gz.zir_code.instructions.appendAssumeCapacity(inst); | 1342 | gz.astgen.instructions.appendAssumeCapacity(inst); |
| 1339 | gz.instructions.appendAssumeCapacity(new_index); | 1343 | gz.instructions.appendAssumeCapacity(new_index); |
| 1340 | return new_index; | 1344 | return new_index; |
| 1341 | } | 1345 | } |
| ... | @@ -1378,100 +1382,6 @@ pub const Scope = struct { | ... | @@ -1378,100 +1382,6 @@ pub const Scope = struct { |
| 1378 | }; | 1382 | }; |
| 1379 | }; | 1383 | }; |
| 1380 | 1384 | ||
| 1381 | /// A Work-In-Progress `zir.Code`. This is a shared parent of all | ||
| 1382 | /// `GenZir` scopes. Once the `zir.Code` is produced, this struct | ||
| 1383 | /// is deinitialized. | ||
| 1384 | /// The `GenZir.finish` function converts this to a `zir.Code`. | ||
| 1385 | pub const WipZirCode = struct { | ||
| 1386 | instructions: std.MultiArrayList(zir.Inst) = .{}, | ||
| 1387 | string_bytes: ArrayListUnmanaged(u8) = .{}, | ||
| 1388 | extra: ArrayListUnmanaged(u32) = .{}, | ||
| 1389 | decl_map: std.StringArrayHashMapUnmanaged(void) = .{}, | ||
| 1390 | decls: ArrayListUnmanaged(*Decl) = .{}, | ||
| 1391 | /// The end of special indexes. `zir.Inst.Ref` subtracts against this number to convert | ||
| 1392 | /// to `zir.Inst.Index`. The default here is correct if there are 0 parameters. | ||
| 1393 | ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len, | ||
| 1394 | decl: *Decl, | ||
| 1395 | gpa: *Allocator, | ||
| 1396 | arena: *Allocator, | ||
| 1397 | |||
| 1398 | pub fn addExtra(wzc: *WipZirCode, extra: anytype) Allocator.Error!u32 { | ||
| 1399 | const fields = std.meta.fields(@TypeOf(extra)); | ||
| 1400 | try wzc.extra.ensureCapacity(wzc.gpa, wzc.extra.items.len + fields.len); | ||
| 1401 | return addExtraAssumeCapacity(wzc, extra); | ||
| 1402 | } | ||
| 1403 | |||
| 1404 | pub fn addExtraAssumeCapacity(wzc: *WipZirCode, extra: anytype) u32 { | ||
| 1405 | const fields = std.meta.fields(@TypeOf(extra)); | ||
| 1406 | const result = @intCast(u32, wzc.extra.items.len); | ||
| 1407 | inline for (fields) |field| { | ||
| 1408 | wzc.extra.appendAssumeCapacity(switch (field.field_type) { | ||
| 1409 | u32 => @field(extra, field.name), | ||
| 1410 | zir.Inst.Ref => @enumToInt(@field(extra, field.name)), | ||
| 1411 | else => @compileError("bad field type"), | ||
| 1412 | }); | ||
| 1413 | } | ||
| 1414 | return result; | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | pub fn appendRefs(wzc: *WipZirCode, refs: []const zir.Inst.Ref) !void { | ||
| 1418 | const coerced = @bitCast([]const u32, refs); | ||
| 1419 | return wzc.extra.appendSlice(wzc.gpa, coerced); | ||
| 1420 | } | ||
| 1421 | |||
| 1422 | pub fn appendRefsAssumeCapacity(wzc: *WipZirCode, refs: []const zir.Inst.Ref) void { | ||
| 1423 | const coerced = @bitCast([]const u32, refs); | ||
| 1424 | wzc.extra.appendSliceAssumeCapacity(coerced); | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | pub fn refIsNoReturn(wzc: WipZirCode, inst_ref: zir.Inst.Ref) bool { | ||
| 1428 | if (inst_ref == .unreachable_value) return true; | ||
| 1429 | if (wzc.refToIndex(inst_ref)) |inst_index| { | ||
| 1430 | return wzc.instructions.items(.tag)[inst_index].isNoReturn(); | ||
| 1431 | } | ||
| 1432 | return false; | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | pub fn indexToRef(wzc: WipZirCode, inst: zir.Inst.Index) zir.Inst.Ref { | ||
| 1436 | return @intToEnum(zir.Inst.Ref, wzc.ref_start_index + inst); | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | pub fn refToIndex(wzc: WipZirCode, inst: zir.Inst.Ref) ?zir.Inst.Index { | ||
| 1440 | const ref_int = @enumToInt(inst); | ||
| 1441 | if (ref_int >= wzc.ref_start_index) { | ||
| 1442 | return ref_int - wzc.ref_start_index; | ||
| 1443 | } else { | ||
| 1444 | return null; | ||
| 1445 | } | ||
| 1446 | } | ||
| 1447 | |||
| 1448 | pub fn deinit(wzc: *WipZirCode) void { | ||
| 1449 | wzc.instructions.deinit(wzc.gpa); | ||
| 1450 | wzc.extra.deinit(wzc.gpa); | ||
| 1451 | wzc.string_bytes.deinit(wzc.gpa); | ||
| 1452 | wzc.decl_map.deinit(wzc.gpa); | ||
| 1453 | wzc.decls.deinit(wzc.gpa); | ||
| 1454 | } | ||
| 1455 | }; | ||
| 1456 | |||
| 1457 | /// Call `deinit` on the result. | ||
| 1458 | fn initAstGen(mod: *Module, decl: *Decl, arena: *Allocator) !WipZirCode { | ||
| 1459 | var wzc: WipZirCode = .{ | ||
| 1460 | .decl = decl, | ||
| 1461 | .arena = arena, | ||
| 1462 | .gpa = mod.gpa, | ||
| 1463 | }; | ||
| 1464 | // Must be a block instruction at index 0 with the root body. | ||
| 1465 | try wzc.instructions.append(mod.gpa, .{ | ||
| 1466 | .tag = .block, | ||
| 1467 | .data = .{ .pl_node = .{ | ||
| 1468 | .src_node = 0, | ||
| 1469 | .payload_index = undefined, | ||
| 1470 | } }, | ||
| 1471 | }); | ||
| 1472 | return wzc; | ||
| 1473 | } | ||
| 1474 | |||
| 1475 | /// This struct holds data necessary to construct API-facing `AllErrors.Message`. | 1385 | /// This struct holds data necessary to construct API-facing `AllErrors.Message`. |
| 1476 | /// Its memory is managed with the general purpose allocator so that they | 1386 | /// Its memory is managed with the general purpose allocator so that they |
| 1477 | /// can be created and destroyed in response to incremental updates. | 1387 | /// can be created and destroyed in response to incremental updates. |
| ... | @@ -2102,18 +2012,18 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { | ... | @@ -2102,18 +2012,18 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { |
| 2102 | defer analysis_arena.deinit(); | 2012 | defer analysis_arena.deinit(); |
| 2103 | 2013 | ||
| 2104 | var code: zir.Code = blk: { | 2014 | var code: zir.Code = blk: { |
| 2105 | var wip_zir_code = try mod.initAstGen(decl, &analysis_arena.allocator); | 2015 | var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator); |
| 2106 | defer wip_zir_code.deinit(); | 2016 | defer astgen.deinit(); |
| 2107 | 2017 | ||
| 2108 | var gen_scope: Scope.GenZir = .{ | 2018 | var gen_scope: Scope.GenZir = .{ |
| 2109 | .force_comptime = true, | 2019 | .force_comptime = true, |
| 2110 | .parent = &decl.container.base, | 2020 | .parent = &decl.container.base, |
| 2111 | .zir_code = &wip_zir_code, | 2021 | .astgen = &astgen, |
| 2112 | }; | 2022 | }; |
| 2113 | defer gen_scope.instructions.deinit(mod.gpa); | 2023 | defer gen_scope.instructions.deinit(mod.gpa); |
| 2114 | 2024 | ||
| 2115 | const block_expr = node_datas[decl_node].lhs; | 2025 | const block_expr = node_datas[decl_node].lhs; |
| 2116 | _ = try astgen.comptimeExpr(mod, &gen_scope.base, .none, block_expr); | 2026 | _ = try AstGen.comptimeExpr(mod, &gen_scope.base, .none, block_expr); |
| 2117 | 2027 | ||
| 2118 | const code = try gen_scope.finish(); | 2028 | const code = try gen_scope.finish(); |
| 2119 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { | 2029 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { |
| ... | @@ -2175,13 +2085,13 @@ fn astgenAndSemaFn( | ... | @@ -2175,13 +2085,13 @@ fn astgenAndSemaFn( |
| 2175 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); | 2085 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 2176 | defer fn_type_scope_arena.deinit(); | 2086 | defer fn_type_scope_arena.deinit(); |
| 2177 | 2087 | ||
| 2178 | var fn_type_wip_zir_code = try mod.initAstGen(decl, &fn_type_scope_arena.allocator); | 2088 | var fn_type_astgen = try AstGen.init(mod, decl, &fn_type_scope_arena.allocator); |
| 2179 | defer fn_type_wip_zir_code.deinit(); | 2089 | defer fn_type_astgen.deinit(); |
| 2180 | 2090 | ||
| 2181 | var fn_type_scope: Scope.GenZir = .{ | 2091 | var fn_type_scope: Scope.GenZir = .{ |
| 2182 | .force_comptime = true, | 2092 | .force_comptime = true, |
| 2183 | .parent = &decl.container.base, | 2093 | .parent = &decl.container.base, |
| 2184 | .zir_code = &fn_type_wip_zir_code, | 2094 | .astgen = &fn_type_astgen, |
| 2185 | }; | 2095 | }; |
| 2186 | defer fn_type_scope.instructions.deinit(mod.gpa); | 2096 | defer fn_type_scope.instructions.deinit(mod.gpa); |
| 2187 | 2097 | ||
| ... | @@ -2223,7 +2133,7 @@ fn astgenAndSemaFn( | ... | @@ -2223,7 +2133,7 @@ fn astgenAndSemaFn( |
| 2223 | const param_type_node = param.type_expr; | 2133 | const param_type_node = param.type_expr; |
| 2224 | assert(param_type_node != 0); | 2134 | assert(param_type_node != 0); |
| 2225 | param_types[param_type_i] = | 2135 | param_types[param_type_i] = |
| 2226 | try astgen.expr(mod, &fn_type_scope.base, .{ .ty = .type_type }, param_type_node); | 2136 | try AstGen.expr(mod, &fn_type_scope.base, .{ .ty = .type_type }, param_type_node); |
| 2227 | } | 2137 | } |
| 2228 | assert(param_type_i == param_count); | 2138 | assert(param_type_i == param_count); |
| 2229 | } | 2139 | } |
| ... | @@ -2292,7 +2202,7 @@ fn astgenAndSemaFn( | ... | @@ -2292,7 +2202,7 @@ fn astgenAndSemaFn( |
| 2292 | if (token_tags[maybe_bang] == .bang) { | 2202 | if (token_tags[maybe_bang] == .bang) { |
| 2293 | return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); | 2203 | return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); |
| 2294 | } | 2204 | } |
| 2295 | const return_type_inst = try astgen.expr( | 2205 | const return_type_inst = try AstGen.expr( |
| 2296 | mod, | 2206 | mod, |
| 2297 | &fn_type_scope.base, | 2207 | &fn_type_scope.base, |
| 2298 | .{ .ty = .type_type }, | 2208 | .{ .ty = .type_type }, |
| ... | @@ -2308,7 +2218,7 @@ fn astgenAndSemaFn( | ... | @@ -2308,7 +2218,7 @@ fn astgenAndSemaFn( |
| 2308 | // TODO instead of enum literal type, this needs to be the | 2218 | // TODO instead of enum literal type, this needs to be the |
| 2309 | // std.builtin.CallingConvention enum. We need to implement importing other files | 2219 | // std.builtin.CallingConvention enum. We need to implement importing other files |
| 2310 | // and enums in order to fix this. | 2220 | // and enums in order to fix this. |
| 2311 | try astgen.comptimeExpr( | 2221 | try AstGen.comptimeExpr( |
| 2312 | mod, | 2222 | mod, |
| 2313 | &fn_type_scope.base, | 2223 | &fn_type_scope.base, |
| 2314 | .{ .ty = .enum_literal_type }, | 2224 | .{ .ty = .enum_literal_type }, |
| ... | @@ -2408,21 +2318,21 @@ fn astgenAndSemaFn( | ... | @@ -2408,21 +2318,21 @@ fn astgenAndSemaFn( |
| 2408 | 2318 | ||
| 2409 | const fn_zir: zir.Code = blk: { | 2319 | const fn_zir: zir.Code = blk: { |
| 2410 | // We put the ZIR inside the Decl arena. | 2320 | // We put the ZIR inside the Decl arena. |
| 2411 | var wip_zir_code = try mod.initAstGen(decl, &decl_arena.allocator); | 2321 | var astgen = try AstGen.init(mod, decl, &decl_arena.allocator); |
| 2412 | wip_zir_code.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count); | 2322 | astgen.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count); |
| 2413 | defer wip_zir_code.deinit(); | 2323 | defer astgen.deinit(); |
| 2414 | 2324 | ||
| 2415 | var gen_scope: Scope.GenZir = .{ | 2325 | var gen_scope: Scope.GenZir = .{ |
| 2416 | .force_comptime = false, | 2326 | .force_comptime = false, |
| 2417 | .parent = &decl.container.base, | 2327 | .parent = &decl.container.base, |
| 2418 | .zir_code = &wip_zir_code, | 2328 | .astgen = &astgen, |
| 2419 | }; | 2329 | }; |
| 2420 | defer gen_scope.instructions.deinit(mod.gpa); | 2330 | defer gen_scope.instructions.deinit(mod.gpa); |
| 2421 | 2331 | ||
| 2422 | // Iterate over the parameters. We put the param names as the first N | 2332 | // Iterate over the parameters. We put the param names as the first N |
| 2423 | // items inside `extra` so that debug info later can refer to the parameter names | 2333 | // items inside `extra` so that debug info later can refer to the parameter names |
| 2424 | // even while the respective source code is unloaded. | 2334 | // even while the respective source code is unloaded. |
| 2425 | try wip_zir_code.extra.ensureCapacity(mod.gpa, param_count); | 2335 | try astgen.extra.ensureCapacity(mod.gpa, param_count); |
| 2426 | 2336 | ||
| 2427 | var params_scope = &gen_scope.base; | 2337 | var params_scope = &gen_scope.base; |
| 2428 | var i: usize = 0; | 2338 | var i: usize = 0; |
| ... | @@ -2443,18 +2353,18 @@ fn astgenAndSemaFn( | ... | @@ -2443,18 +2353,18 @@ fn astgenAndSemaFn( |
| 2443 | 2353 | ||
| 2444 | // Additionally put the param name into `string_bytes` and reference it with | 2354 | // Additionally put the param name into `string_bytes` and reference it with |
| 2445 | // `extra` so that we have access to the data in codegen, for debug info. | 2355 | // `extra` so that we have access to the data in codegen, for debug info. |
| 2446 | const str_index = @intCast(u32, wip_zir_code.string_bytes.items.len); | 2356 | const str_index = @intCast(u32, astgen.string_bytes.items.len); |
| 2447 | wip_zir_code.extra.appendAssumeCapacity(str_index); | 2357 | astgen.extra.appendAssumeCapacity(str_index); |
| 2448 | const used_bytes = wip_zir_code.string_bytes.items.len; | 2358 | const used_bytes = astgen.string_bytes.items.len; |
| 2449 | try wip_zir_code.string_bytes.ensureCapacity(mod.gpa, used_bytes + param_name.len + 1); | 2359 | try astgen.string_bytes.ensureCapacity(mod.gpa, used_bytes + param_name.len + 1); |
| 2450 | wip_zir_code.string_bytes.appendSliceAssumeCapacity(param_name); | 2360 | astgen.string_bytes.appendSliceAssumeCapacity(param_name); |
| 2451 | wip_zir_code.string_bytes.appendAssumeCapacity(0); | 2361 | astgen.string_bytes.appendAssumeCapacity(0); |
| 2452 | } | 2362 | } |
| 2453 | 2363 | ||
| 2454 | _ = try astgen.expr(mod, params_scope, .none, body_node); | 2364 | _ = try AstGen.expr(mod, params_scope, .none, body_node); |
| 2455 | 2365 | ||
| 2456 | if (gen_scope.instructions.items.len == 0 or | 2366 | if (gen_scope.instructions.items.len == 0 or |
| 2457 | !wip_zir_code.instructions.items(.tag)[gen_scope.instructions.items.len - 1] | 2367 | !astgen.instructions.items(.tag)[gen_scope.instructions.items.len - 1] |
| 2458 | .isNoReturn()) | 2368 | .isNoReturn()) |
| 2459 | { | 2369 | { |
| 2460 | // astgen uses result location semantics to coerce return operands. | 2370 | // astgen uses result location semantics to coerce return operands. |
| ... | @@ -2615,21 +2525,21 @@ fn astgenAndSemaVarDecl( | ... | @@ -2615,21 +2525,21 @@ fn astgenAndSemaVarDecl( |
| 2615 | var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); | 2525 | var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 2616 | defer gen_scope_arena.deinit(); | 2526 | defer gen_scope_arena.deinit(); |
| 2617 | 2527 | ||
| 2618 | var wip_zir_code = try mod.initAstGen(decl, &gen_scope_arena.allocator); | 2528 | var astgen = try AstGen.init(mod, decl, &gen_scope_arena.allocator); |
| 2619 | defer wip_zir_code.deinit(); | 2529 | defer astgen.deinit(); |
| 2620 | 2530 | ||
| 2621 | var gen_scope: Scope.GenZir = .{ | 2531 | var gen_scope: Scope.GenZir = .{ |
| 2622 | .force_comptime = true, | 2532 | .force_comptime = true, |
| 2623 | .parent = &decl.container.base, | 2533 | .parent = &decl.container.base, |
| 2624 | .zir_code = &wip_zir_code, | 2534 | .astgen = &astgen, |
| 2625 | }; | 2535 | }; |
| 2626 | defer gen_scope.instructions.deinit(mod.gpa); | 2536 | defer gen_scope.instructions.deinit(mod.gpa); |
| 2627 | 2537 | ||
| 2628 | const init_result_loc: astgen.ResultLoc = if (var_decl.ast.type_node != 0) .{ | 2538 | const init_result_loc: AstGen.ResultLoc = if (var_decl.ast.type_node != 0) .{ |
| 2629 | .ty = try astgen.expr(mod, &gen_scope.base, .{ .ty = .type_type }, var_decl.ast.type_node), | 2539 | .ty = try AstGen.expr(mod, &gen_scope.base, .{ .ty = .type_type }, var_decl.ast.type_node), |
| 2630 | } else .none; | 2540 | } else .none; |
| 2631 | 2541 | ||
| 2632 | const init_inst = try astgen.comptimeExpr( | 2542 | const init_inst = try AstGen.comptimeExpr( |
| 2633 | mod, | 2543 | mod, |
| 2634 | &gen_scope.base, | 2544 | &gen_scope.base, |
| 2635 | init_result_loc, | 2545 | init_result_loc, |
| ... | @@ -2684,17 +2594,17 @@ fn astgenAndSemaVarDecl( | ... | @@ -2684,17 +2594,17 @@ fn astgenAndSemaVarDecl( |
| 2684 | var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); | 2594 | var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 2685 | defer type_scope_arena.deinit(); | 2595 | defer type_scope_arena.deinit(); |
| 2686 | 2596 | ||
| 2687 | var wip_zir_code = try mod.initAstGen(decl, &type_scope_arena.allocator); | 2597 | var astgen = try AstGen.init(mod, decl, &type_scope_arena.allocator); |
| 2688 | defer wip_zir_code.deinit(); | 2598 | defer astgen.deinit(); |
| 2689 | 2599 | ||
| 2690 | var type_scope: Scope.GenZir = .{ | 2600 | var type_scope: Scope.GenZir = .{ |
| 2691 | .force_comptime = true, | 2601 | .force_comptime = true, |
| 2692 | .parent = &decl.container.base, | 2602 | .parent = &decl.container.base, |
| 2693 | .zir_code = &wip_zir_code, | 2603 | .astgen = &astgen, |
| 2694 | }; | 2604 | }; |
| 2695 | defer type_scope.instructions.deinit(mod.gpa); | 2605 | defer type_scope.instructions.deinit(mod.gpa); |
| 2696 | 2606 | ||
| 2697 | const var_type = try astgen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node); | 2607 | const var_type = try AstGen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node); |
| 2698 | _ = try type_scope.addBreak(.break_inline, 0, var_type); | 2608 | _ = try type_scope.addBreak(.break_inline, 0, var_type); |
| 2699 | 2609 | ||
| 2700 | var code = try type_scope.finish(); | 2610 | var code = try type_scope.finish(); |
| ... | @@ -3796,21 +3706,21 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In | ... | @@ -3796,21 +3706,21 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In |
| 3796 | }, | 3706 | }, |
| 3797 | .gen_zir => { | 3707 | .gen_zir => { |
| 3798 | const gen_zir = scope.cast(Scope.GenZir).?; | 3708 | const gen_zir = scope.cast(Scope.GenZir).?; |
| 3799 | gen_zir.zir_code.decl.analysis = .sema_failure; | 3709 | gen_zir.astgen.decl.analysis = .sema_failure; |
| 3800 | gen_zir.zir_code.decl.generation = mod.generation; | 3710 | gen_zir.astgen.decl.generation = mod.generation; |
| 3801 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.zir_code.decl, err_msg); | 3711 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg); |
| 3802 | }, | 3712 | }, |
| 3803 | .local_val => { | 3713 | .local_val => { |
| 3804 | const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir; | 3714 | const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir; |
| 3805 | gen_zir.zir_code.decl.analysis = .sema_failure; | 3715 | gen_zir.astgen.decl.analysis = .sema_failure; |
| 3806 | gen_zir.zir_code.decl.generation = mod.generation; | 3716 | gen_zir.astgen.decl.generation = mod.generation; |
| 3807 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.zir_code.decl, err_msg); | 3717 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg); |
| 3808 | }, | 3718 | }, |
| 3809 | .local_ptr => { | 3719 | .local_ptr => { |
| 3810 | const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir; | 3720 | const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir; |
| 3811 | gen_zir.zir_code.decl.analysis = .sema_failure; | 3721 | gen_zir.astgen.decl.analysis = .sema_failure; |
| 3812 | gen_zir.zir_code.decl.generation = mod.generation; | 3722 | gen_zir.astgen.decl.generation = mod.generation; |
| 3813 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.zir_code.decl, err_msg); | 3723 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg); |
| 3814 | }, | 3724 | }, |
| 3815 | .file => unreachable, | 3725 | .file => unreachable, |
| 3816 | .container => unreachable, | 3726 | .container => unreachable, |
src/astgen.zig deleted-3881| ... | @@ -1,3881 +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.Ref, | ||
| 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.Ref, | ||
| 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 | /// Always an instruction with tag `alloc_inferred`. | ||
| 35 | inferred_ptr: zir.Inst.Ref, | ||
| 36 | /// The expression must store its result into this pointer, which is a typed pointer that | ||
| 37 | /// has been bitcasted to whatever the expression's type is. | ||
| 38 | /// The result instruction from the expression must be ignored. | ||
| 39 | bitcasted_ptr: zir.Inst.Ref, | ||
| 40 | /// There is a pointer for the expression to store its result into, however, its type | ||
| 41 | /// is inferred based on peer type resolution for a `zir.Inst.Block`. | ||
| 42 | /// The result instruction from the expression must be ignored. | ||
| 43 | block_ptr: *Module.Scope.GenZir, | ||
| 44 | |||
| 45 | pub const Strategy = struct { | ||
| 46 | elide_store_to_block_ptr_instructions: bool, | ||
| 47 | tag: Tag, | ||
| 48 | |||
| 49 | pub const Tag = enum { | ||
| 50 | /// Both branches will use break_void; result location is used to communicate the | ||
| 51 | /// result instruction. | ||
| 52 | break_void, | ||
| 53 | /// Use break statements to pass the block result value, and call rvalue() at | ||
| 54 | /// the end depending on rl. Also elide the store_to_block_ptr instructions | ||
| 55 | /// depending on rl. | ||
| 56 | break_operand, | ||
| 57 | }; | ||
| 58 | }; | ||
| 59 | }; | ||
| 60 | |||
| 61 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 62 | return expr(mod, scope, .{ .ty = .type_type }, type_node); | ||
| 63 | } | ||
| 64 | |||
| 65 | fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 66 | const tree = scope.tree(); | ||
| 67 | const node_tags = tree.nodes.items(.tag); | ||
| 68 | const main_tokens = tree.nodes.items(.main_token); | ||
| 69 | switch (node_tags[node]) { | ||
| 70 | .root => unreachable, | ||
| 71 | .@"usingnamespace" => unreachable, | ||
| 72 | .test_decl => unreachable, | ||
| 73 | .global_var_decl => unreachable, | ||
| 74 | .local_var_decl => unreachable, | ||
| 75 | .simple_var_decl => unreachable, | ||
| 76 | .aligned_var_decl => unreachable, | ||
| 77 | .switch_case => unreachable, | ||
| 78 | .switch_case_one => unreachable, | ||
| 79 | .container_field_init => unreachable, | ||
| 80 | .container_field_align => unreachable, | ||
| 81 | .container_field => unreachable, | ||
| 82 | .asm_output => unreachable, | ||
| 83 | .asm_input => unreachable, | ||
| 84 | |||
| 85 | .assign, | ||
| 86 | .assign_bit_and, | ||
| 87 | .assign_bit_or, | ||
| 88 | .assign_bit_shift_left, | ||
| 89 | .assign_bit_shift_right, | ||
| 90 | .assign_bit_xor, | ||
| 91 | .assign_div, | ||
| 92 | .assign_sub, | ||
| 93 | .assign_sub_wrap, | ||
| 94 | .assign_mod, | ||
| 95 | .assign_add, | ||
| 96 | .assign_add_wrap, | ||
| 97 | .assign_mul, | ||
| 98 | .assign_mul_wrap, | ||
| 99 | .add, | ||
| 100 | .add_wrap, | ||
| 101 | .sub, | ||
| 102 | .sub_wrap, | ||
| 103 | .mul, | ||
| 104 | .mul_wrap, | ||
| 105 | .div, | ||
| 106 | .mod, | ||
| 107 | .bit_and, | ||
| 108 | .bit_or, | ||
| 109 | .bit_shift_left, | ||
| 110 | .bit_shift_right, | ||
| 111 | .bit_xor, | ||
| 112 | .bang_equal, | ||
| 113 | .equal_equal, | ||
| 114 | .greater_than, | ||
| 115 | .greater_or_equal, | ||
| 116 | .less_than, | ||
| 117 | .less_or_equal, | ||
| 118 | .array_cat, | ||
| 119 | .array_mult, | ||
| 120 | .bool_and, | ||
| 121 | .bool_or, | ||
| 122 | .@"asm", | ||
| 123 | .asm_simple, | ||
| 124 | .string_literal, | ||
| 125 | .integer_literal, | ||
| 126 | .call, | ||
| 127 | .call_comma, | ||
| 128 | .async_call, | ||
| 129 | .async_call_comma, | ||
| 130 | .call_one, | ||
| 131 | .call_one_comma, | ||
| 132 | .async_call_one, | ||
| 133 | .async_call_one_comma, | ||
| 134 | .unreachable_literal, | ||
| 135 | .@"return", | ||
| 136 | .@"if", | ||
| 137 | .if_simple, | ||
| 138 | .@"while", | ||
| 139 | .while_simple, | ||
| 140 | .while_cont, | ||
| 141 | .bool_not, | ||
| 142 | .address_of, | ||
| 143 | .float_literal, | ||
| 144 | .undefined_literal, | ||
| 145 | .true_literal, | ||
| 146 | .false_literal, | ||
| 147 | .null_literal, | ||
| 148 | .optional_type, | ||
| 149 | .block, | ||
| 150 | .block_semicolon, | ||
| 151 | .block_two, | ||
| 152 | .block_two_semicolon, | ||
| 153 | .@"break", | ||
| 154 | .ptr_type_aligned, | ||
| 155 | .ptr_type_sentinel, | ||
| 156 | .ptr_type, | ||
| 157 | .ptr_type_bit_range, | ||
| 158 | .array_type, | ||
| 159 | .array_type_sentinel, | ||
| 160 | .enum_literal, | ||
| 161 | .multiline_string_literal, | ||
| 162 | .char_literal, | ||
| 163 | .@"defer", | ||
| 164 | .@"errdefer", | ||
| 165 | .@"catch", | ||
| 166 | .error_union, | ||
| 167 | .merge_error_sets, | ||
| 168 | .switch_range, | ||
| 169 | .@"await", | ||
| 170 | .bit_not, | ||
| 171 | .negation, | ||
| 172 | .negation_wrap, | ||
| 173 | .@"resume", | ||
| 174 | .@"try", | ||
| 175 | .slice, | ||
| 176 | .slice_open, | ||
| 177 | .slice_sentinel, | ||
| 178 | .array_init_one, | ||
| 179 | .array_init_one_comma, | ||
| 180 | .array_init_dot_two, | ||
| 181 | .array_init_dot_two_comma, | ||
| 182 | .array_init_dot, | ||
| 183 | .array_init_dot_comma, | ||
| 184 | .array_init, | ||
| 185 | .array_init_comma, | ||
| 186 | .struct_init_one, | ||
| 187 | .struct_init_one_comma, | ||
| 188 | .struct_init_dot_two, | ||
| 189 | .struct_init_dot_two_comma, | ||
| 190 | .struct_init_dot, | ||
| 191 | .struct_init_dot_comma, | ||
| 192 | .struct_init, | ||
| 193 | .struct_init_comma, | ||
| 194 | .@"switch", | ||
| 195 | .switch_comma, | ||
| 196 | .@"for", | ||
| 197 | .for_simple, | ||
| 198 | .@"suspend", | ||
| 199 | .@"continue", | ||
| 200 | .@"anytype", | ||
| 201 | .fn_proto_simple, | ||
| 202 | .fn_proto_multi, | ||
| 203 | .fn_proto_one, | ||
| 204 | .fn_proto, | ||
| 205 | .fn_decl, | ||
| 206 | .anyframe_type, | ||
| 207 | .anyframe_literal, | ||
| 208 | .error_set_decl, | ||
| 209 | .container_decl, | ||
| 210 | .container_decl_trailing, | ||
| 211 | .container_decl_two, | ||
| 212 | .container_decl_two_trailing, | ||
| 213 | .container_decl_arg, | ||
| 214 | .container_decl_arg_trailing, | ||
| 215 | .tagged_union, | ||
| 216 | .tagged_union_trailing, | ||
| 217 | .tagged_union_two, | ||
| 218 | .tagged_union_two_trailing, | ||
| 219 | .tagged_union_enum_tag, | ||
| 220 | .tagged_union_enum_tag_trailing, | ||
| 221 | .@"comptime", | ||
| 222 | .@"nosuspend", | ||
| 223 | .error_value, | ||
| 224 | => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), | ||
| 225 | |||
| 226 | .builtin_call, | ||
| 227 | .builtin_call_comma, | ||
| 228 | .builtin_call_two, | ||
| 229 | .builtin_call_two_comma, | ||
| 230 | => { | ||
| 231 | const builtin_token = main_tokens[node]; | ||
| 232 | const builtin_name = tree.tokenSlice(builtin_token); | ||
| 233 | // If the builtin is an invalid name, we don't cause an error here; instead | ||
| 234 | // let it pass, and the error will be "invalid builtin function" later. | ||
| 235 | if (BuiltinFn.list.get(builtin_name)) |info| { | ||
| 236 | if (!info.allows_lvalue) { | ||
| 237 | return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | }, | ||
| 241 | |||
| 242 | // These can be assigned to. | ||
| 243 | .unwrap_optional, | ||
| 244 | .deref, | ||
| 245 | .field_access, | ||
| 246 | .array_access, | ||
| 247 | .identifier, | ||
| 248 | .grouped_expression, | ||
| 249 | .@"orelse", | ||
| 250 | => {}, | ||
| 251 | } | ||
| 252 | return expr(mod, scope, .ref, node); | ||
| 253 | } | ||
| 254 | |||
| 255 | /// Turn Zig AST into untyped ZIR istructions. | ||
| 256 | /// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the | ||
| 257 | /// result instruction can be used to inspect whether it is isNoReturn() but that is it, | ||
| 258 | /// it must otherwise not be used. | ||
| 259 | pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 260 | const tree = scope.tree(); | ||
| 261 | const main_tokens = tree.nodes.items(.main_token); | ||
| 262 | const token_tags = tree.tokens.items(.tag); | ||
| 263 | const node_datas = tree.nodes.items(.data); | ||
| 264 | const node_tags = tree.nodes.items(.tag); | ||
| 265 | |||
| 266 | const gz = scope.getGenZir(); | ||
| 267 | |||
| 268 | switch (node_tags[node]) { | ||
| 269 | .root => unreachable, // Top-level declaration. | ||
| 270 | .@"usingnamespace" => unreachable, // Top-level declaration. | ||
| 271 | .test_decl => unreachable, // Top-level declaration. | ||
| 272 | .container_field_init => unreachable, // Top-level declaration. | ||
| 273 | .container_field_align => unreachable, // Top-level declaration. | ||
| 274 | .container_field => unreachable, // Top-level declaration. | ||
| 275 | .fn_decl => unreachable, // Top-level declaration. | ||
| 276 | |||
| 277 | .global_var_decl => unreachable, // Handled in `blockExpr`. | ||
| 278 | .local_var_decl => unreachable, // Handled in `blockExpr`. | ||
| 279 | .simple_var_decl => unreachable, // Handled in `blockExpr`. | ||
| 280 | .aligned_var_decl => unreachable, // Handled in `blockExpr`. | ||
| 281 | |||
| 282 | .switch_case => unreachable, // Handled in `switchExpr`. | ||
| 283 | .switch_case_one => unreachable, // Handled in `switchExpr`. | ||
| 284 | .switch_range => unreachable, // Handled in `switchExpr`. | ||
| 285 | |||
| 286 | .asm_output => unreachable, // Handled in `asmExpr`. | ||
| 287 | .asm_input => unreachable, // Handled in `asmExpr`. | ||
| 288 | |||
| 289 | .assign => { | ||
| 290 | try assign(mod, scope, node); | ||
| 291 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 292 | }, | ||
| 293 | .assign_bit_and => { | ||
| 294 | try assignOp(mod, scope, node, .bit_and); | ||
| 295 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 296 | }, | ||
| 297 | .assign_bit_or => { | ||
| 298 | try assignOp(mod, scope, node, .bit_or); | ||
| 299 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 300 | }, | ||
| 301 | .assign_bit_shift_left => { | ||
| 302 | try assignOp(mod, scope, node, .shl); | ||
| 303 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 304 | }, | ||
| 305 | .assign_bit_shift_right => { | ||
| 306 | try assignOp(mod, scope, node, .shr); | ||
| 307 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 308 | }, | ||
| 309 | .assign_bit_xor => { | ||
| 310 | try assignOp(mod, scope, node, .xor); | ||
| 311 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 312 | }, | ||
| 313 | .assign_div => { | ||
| 314 | try assignOp(mod, scope, node, .div); | ||
| 315 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 316 | }, | ||
| 317 | .assign_sub => { | ||
| 318 | try assignOp(mod, scope, node, .sub); | ||
| 319 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 320 | }, | ||
| 321 | .assign_sub_wrap => { | ||
| 322 | try assignOp(mod, scope, node, .subwrap); | ||
| 323 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 324 | }, | ||
| 325 | .assign_mod => { | ||
| 326 | try assignOp(mod, scope, node, .mod_rem); | ||
| 327 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 328 | }, | ||
| 329 | .assign_add => { | ||
| 330 | try assignOp(mod, scope, node, .add); | ||
| 331 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 332 | }, | ||
| 333 | .assign_add_wrap => { | ||
| 334 | try assignOp(mod, scope, node, .addwrap); | ||
| 335 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 336 | }, | ||
| 337 | .assign_mul => { | ||
| 338 | try assignOp(mod, scope, node, .mul); | ||
| 339 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 340 | }, | ||
| 341 | .assign_mul_wrap => { | ||
| 342 | try assignOp(mod, scope, node, .mulwrap); | ||
| 343 | return rvalue(mod, scope, rl, .void_value, node); | ||
| 344 | }, | ||
| 345 | |||
| 346 | .add => return simpleBinOp(mod, scope, rl, node, .add), | ||
| 347 | .add_wrap => return simpleBinOp(mod, scope, rl, node, .addwrap), | ||
| 348 | .sub => return simpleBinOp(mod, scope, rl, node, .sub), | ||
| 349 | .sub_wrap => return simpleBinOp(mod, scope, rl, node, .subwrap), | ||
| 350 | .mul => return simpleBinOp(mod, scope, rl, node, .mul), | ||
| 351 | .mul_wrap => return simpleBinOp(mod, scope, rl, node, .mulwrap), | ||
| 352 | .div => return simpleBinOp(mod, scope, rl, node, .div), | ||
| 353 | .mod => return simpleBinOp(mod, scope, rl, node, .mod_rem), | ||
| 354 | .bit_and => return simpleBinOp(mod, scope, rl, node, .bit_and), | ||
| 355 | .bit_or => return simpleBinOp(mod, scope, rl, node, .bit_or), | ||
| 356 | .bit_shift_left => return simpleBinOp(mod, scope, rl, node, .shl), | ||
| 357 | .bit_shift_right => return simpleBinOp(mod, scope, rl, node, .shr), | ||
| 358 | .bit_xor => return simpleBinOp(mod, scope, rl, node, .xor), | ||
| 359 | |||
| 360 | .bang_equal => return simpleBinOp(mod, scope, rl, node, .cmp_neq), | ||
| 361 | .equal_equal => return simpleBinOp(mod, scope, rl, node, .cmp_eq), | ||
| 362 | .greater_than => return simpleBinOp(mod, scope, rl, node, .cmp_gt), | ||
| 363 | .greater_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_gte), | ||
| 364 | .less_than => return simpleBinOp(mod, scope, rl, node, .cmp_lt), | ||
| 365 | .less_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_lte), | ||
| 366 | |||
| 367 | .array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat), | ||
| 368 | .array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul), | ||
| 369 | |||
| 370 | .error_union => return simpleBinOp(mod, scope, rl, node, .error_union_type), | ||
| 371 | .merge_error_sets => return simpleBinOp(mod, scope, rl, node, .merge_error_sets), | ||
| 372 | |||
| 373 | .bool_and => return boolBinOp(mod, scope, rl, node, .bool_br_and), | ||
| 374 | .bool_or => return boolBinOp(mod, scope, rl, node, .bool_br_or), | ||
| 375 | |||
| 376 | .bool_not => return boolNot(mod, scope, rl, node), | ||
| 377 | .bit_not => return bitNot(mod, scope, rl, node), | ||
| 378 | |||
| 379 | .negation => return negation(mod, scope, rl, node, .negate), | ||
| 380 | .negation_wrap => return negation(mod, scope, rl, node, .negate_wrap), | ||
| 381 | |||
| 382 | .identifier => return identifier(mod, scope, rl, node), | ||
| 383 | |||
| 384 | .asm_simple => return asmExpr(mod, scope, rl, node, tree.asmSimple(node)), | ||
| 385 | .@"asm" => return asmExpr(mod, scope, rl, node, tree.asmFull(node)), | ||
| 386 | |||
| 387 | .string_literal => return stringLiteral(mod, scope, rl, node), | ||
| 388 | .multiline_string_literal => return multilineStringLiteral(mod, scope, rl, node), | ||
| 389 | |||
| 390 | .integer_literal => return integerLiteral(mod, scope, rl, node), | ||
| 391 | |||
| 392 | .builtin_call_two, .builtin_call_two_comma => { | ||
| 393 | if (node_datas[node].lhs == 0) { | ||
| 394 | const params = [_]ast.Node.Index{}; | ||
| 395 | return builtinCall(mod, scope, rl, node, &params); | ||
| 396 | } else if (node_datas[node].rhs == 0) { | ||
| 397 | const params = [_]ast.Node.Index{node_datas[node].lhs}; | ||
| 398 | return builtinCall(mod, scope, rl, node, &params); | ||
| 399 | } else { | ||
| 400 | const params = [_]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; | ||
| 401 | return builtinCall(mod, scope, rl, node, &params); | ||
| 402 | } | ||
| 403 | }, | ||
| 404 | .builtin_call, .builtin_call_comma => { | ||
| 405 | const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; | ||
| 406 | return builtinCall(mod, scope, rl, node, params); | ||
| 407 | }, | ||
| 408 | |||
| 409 | .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => { | ||
| 410 | var params: [1]ast.Node.Index = undefined; | ||
| 411 | return callExpr(mod, scope, rl, node, tree.callOne(&params, node)); | ||
| 412 | }, | ||
| 413 | .call, .call_comma, .async_call, .async_call_comma => { | ||
| 414 | return callExpr(mod, scope, rl, node, tree.callFull(node)); | ||
| 415 | }, | ||
| 416 | |||
| 417 | .unreachable_literal => { | ||
| 418 | _ = try gz.addAsIndex(.{ | ||
| 419 | .tag = .@"unreachable", | ||
| 420 | .data = .{ .@"unreachable" = .{ | ||
| 421 | .safety = true, | ||
| 422 | .src_node = gz.zir_code.decl.nodeIndexToRelative(node), | ||
| 423 | } }, | ||
| 424 | }); | ||
| 425 | return zir.Inst.Ref.unreachable_value; | ||
| 426 | }, | ||
| 427 | .@"return" => return ret(mod, scope, node), | ||
| 428 | .field_access => return fieldAccess(mod, scope, rl, node), | ||
| 429 | .float_literal => return floatLiteral(mod, scope, rl, node), | ||
| 430 | |||
| 431 | .if_simple => return ifExpr(mod, scope, rl, node, tree.ifSimple(node)), | ||
| 432 | .@"if" => return ifExpr(mod, scope, rl, node, tree.ifFull(node)), | ||
| 433 | |||
| 434 | .while_simple => return whileExpr(mod, scope, rl, node, tree.whileSimple(node)), | ||
| 435 | .while_cont => return whileExpr(mod, scope, rl, node, tree.whileCont(node)), | ||
| 436 | .@"while" => return whileExpr(mod, scope, rl, node, tree.whileFull(node)), | ||
| 437 | |||
| 438 | .for_simple => return forExpr(mod, scope, rl, node, tree.forSimple(node)), | ||
| 439 | .@"for" => return forExpr(mod, scope, rl, node, tree.forFull(node)), | ||
| 440 | |||
| 441 | .slice_open => { | ||
| 442 | const lhs = try expr(mod, scope, .ref, node_datas[node].lhs); | ||
| 443 | const start = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs); | ||
| 444 | const result = try gz.addPlNode(.slice_start, node, zir.Inst.SliceStart{ | ||
| 445 | .lhs = lhs, | ||
| 446 | .start = start, | ||
| 447 | }); | ||
| 448 | return rvalue(mod, scope, rl, result, node); | ||
| 449 | }, | ||
| 450 | .slice => { | ||
| 451 | const lhs = try expr(mod, scope, .ref, node_datas[node].lhs); | ||
| 452 | const extra = tree.extraData(node_datas[node].rhs, ast.Node.Slice); | ||
| 453 | const start = try expr(mod, scope, .{ .ty = .usize_type }, extra.start); | ||
| 454 | const end = try expr(mod, scope, .{ .ty = .usize_type }, extra.end); | ||
| 455 | const result = try gz.addPlNode(.slice_end, node, zir.Inst.SliceEnd{ | ||
| 456 | .lhs = lhs, | ||
| 457 | .start = start, | ||
| 458 | .end = end, | ||
| 459 | }); | ||
| 460 | return rvalue(mod, scope, rl, result, node); | ||
| 461 | }, | ||
| 462 | .slice_sentinel => { | ||
| 463 | const lhs = try expr(mod, scope, .ref, node_datas[node].lhs); | ||
| 464 | const extra = tree.extraData(node_datas[node].rhs, ast.Node.SliceSentinel); | ||
| 465 | const start = try expr(mod, scope, .{ .ty = .usize_type }, extra.start); | ||
| 466 | const end = try expr(mod, scope, .{ .ty = .usize_type }, extra.end); | ||
| 467 | const sentinel = try expr(mod, scope, .{ .ty = .usize_type }, extra.sentinel); | ||
| 468 | const result = try gz.addPlNode(.slice_sentinel, node, zir.Inst.SliceSentinel{ | ||
| 469 | .lhs = lhs, | ||
| 470 | .start = start, | ||
| 471 | .end = end, | ||
| 472 | .sentinel = sentinel, | ||
| 473 | }); | ||
| 474 | return rvalue(mod, scope, rl, result, node); | ||
| 475 | }, | ||
| 476 | |||
| 477 | .deref => { | ||
| 478 | const lhs = try expr(mod, scope, .none, node_datas[node].lhs); | ||
| 479 | const result = try gz.addUnNode(.load, lhs, node); | ||
| 480 | return rvalue(mod, scope, rl, result, node); | ||
| 481 | }, | ||
| 482 | .address_of => { | ||
| 483 | const result = try expr(mod, scope, .ref, node_datas[node].lhs); | ||
| 484 | return rvalue(mod, scope, rl, result, node); | ||
| 485 | }, | ||
| 486 | .undefined_literal => return rvalue(mod, scope, rl, .undef, node), | ||
| 487 | .true_literal => return rvalue(mod, scope, rl, .bool_true, node), | ||
| 488 | .false_literal => return rvalue(mod, scope, rl, .bool_false, node), | ||
| 489 | .null_literal => return rvalue(mod, scope, rl, .null_value, node), | ||
| 490 | .optional_type => { | ||
| 491 | const operand = try typeExpr(mod, scope, node_datas[node].lhs); | ||
| 492 | const result = try gz.addUnNode(.optional_type, operand, node); | ||
| 493 | return rvalue(mod, scope, rl, result, node); | ||
| 494 | }, | ||
| 495 | .unwrap_optional => switch (rl) { | ||
| 496 | .ref => return gz.addUnNode( | ||
| 497 | .optional_payload_safe_ptr, | ||
| 498 | try expr(mod, scope, .ref, node_datas[node].lhs), | ||
| 499 | node, | ||
| 500 | ), | ||
| 501 | else => return rvalue(mod, scope, rl, try gz.addUnNode( | ||
| 502 | .optional_payload_safe, | ||
| 503 | try expr(mod, scope, .none, node_datas[node].lhs), | ||
| 504 | node, | ||
| 505 | ), node), | ||
| 506 | }, | ||
| 507 | .block_two, .block_two_semicolon => { | ||
| 508 | const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; | ||
| 509 | if (node_datas[node].lhs == 0) { | ||
| 510 | return blockExpr(mod, scope, rl, node, statements[0..0]); | ||
| 511 | } else if (node_datas[node].rhs == 0) { | ||
| 512 | return blockExpr(mod, scope, rl, node, statements[0..1]); | ||
| 513 | } else { | ||
| 514 | return blockExpr(mod, scope, rl, node, statements[0..2]); | ||
| 515 | } | ||
| 516 | }, | ||
| 517 | .block, .block_semicolon => { | ||
| 518 | const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; | ||
| 519 | return blockExpr(mod, scope, rl, node, statements); | ||
| 520 | }, | ||
| 521 | .enum_literal => return simpleStrTok(mod, scope, rl, main_tokens[node], node, .enum_literal), | ||
| 522 | .error_value => return simpleStrTok(mod, scope, rl, node_datas[node].rhs, node, .error_value), | ||
| 523 | .anyframe_literal => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 524 | .anyframe_type => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 525 | .@"catch" => { | ||
| 526 | const catch_token = main_tokens[node]; | ||
| 527 | const payload_token: ?ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) | ||
| 528 | catch_token + 2 | ||
| 529 | else | ||
| 530 | null; | ||
| 531 | switch (rl) { | ||
| 532 | .ref => return orelseCatchExpr( | ||
| 533 | mod, | ||
| 534 | scope, | ||
| 535 | rl, | ||
| 536 | node, | ||
| 537 | node_datas[node].lhs, | ||
| 538 | .is_err_ptr, | ||
| 539 | .err_union_payload_unsafe_ptr, | ||
| 540 | .err_union_code_ptr, | ||
| 541 | node_datas[node].rhs, | ||
| 542 | payload_token, | ||
| 543 | ), | ||
| 544 | else => return orelseCatchExpr( | ||
| 545 | mod, | ||
| 546 | scope, | ||
| 547 | rl, | ||
| 548 | node, | ||
| 549 | node_datas[node].lhs, | ||
| 550 | .is_err, | ||
| 551 | .err_union_payload_unsafe, | ||
| 552 | .err_union_code, | ||
| 553 | node_datas[node].rhs, | ||
| 554 | payload_token, | ||
| 555 | ), | ||
| 556 | } | ||
| 557 | }, | ||
| 558 | .@"orelse" => switch (rl) { | ||
| 559 | .ref => return orelseCatchExpr( | ||
| 560 | mod, | ||
| 561 | scope, | ||
| 562 | rl, | ||
| 563 | node, | ||
| 564 | node_datas[node].lhs, | ||
| 565 | .is_null_ptr, | ||
| 566 | .optional_payload_unsafe_ptr, | ||
| 567 | undefined, | ||
| 568 | node_datas[node].rhs, | ||
| 569 | null, | ||
| 570 | ), | ||
| 571 | else => return orelseCatchExpr( | ||
| 572 | mod, | ||
| 573 | scope, | ||
| 574 | rl, | ||
| 575 | node, | ||
| 576 | node_datas[node].lhs, | ||
| 577 | .is_null, | ||
| 578 | .optional_payload_unsafe, | ||
| 579 | undefined, | ||
| 580 | node_datas[node].rhs, | ||
| 581 | null, | ||
| 582 | ), | ||
| 583 | }, | ||
| 584 | |||
| 585 | .ptr_type_aligned => return ptrType(mod, scope, rl, node, tree.ptrTypeAligned(node)), | ||
| 586 | .ptr_type_sentinel => return ptrType(mod, scope, rl, node, tree.ptrTypeSentinel(node)), | ||
| 587 | .ptr_type => return ptrType(mod, scope, rl, node, tree.ptrType(node)), | ||
| 588 | .ptr_type_bit_range => return ptrType(mod, scope, rl, node, tree.ptrTypeBitRange(node)), | ||
| 589 | |||
| 590 | .container_decl, | ||
| 591 | .container_decl_trailing, | ||
| 592 | => return containerDecl(mod, scope, rl, tree.containerDecl(node)), | ||
| 593 | .container_decl_two, .container_decl_two_trailing => { | ||
| 594 | var buffer: [2]ast.Node.Index = undefined; | ||
| 595 | return containerDecl(mod, scope, rl, tree.containerDeclTwo(&buffer, node)); | ||
| 596 | }, | ||
| 597 | .container_decl_arg, | ||
| 598 | .container_decl_arg_trailing, | ||
| 599 | => return containerDecl(mod, scope, rl, tree.containerDeclArg(node)), | ||
| 600 | |||
| 601 | .tagged_union, | ||
| 602 | .tagged_union_trailing, | ||
| 603 | => return containerDecl(mod, scope, rl, tree.taggedUnion(node)), | ||
| 604 | .tagged_union_two, .tagged_union_two_trailing => { | ||
| 605 | var buffer: [2]ast.Node.Index = undefined; | ||
| 606 | return containerDecl(mod, scope, rl, tree.taggedUnionTwo(&buffer, node)); | ||
| 607 | }, | ||
| 608 | .tagged_union_enum_tag, | ||
| 609 | .tagged_union_enum_tag_trailing, | ||
| 610 | => return containerDecl(mod, scope, rl, tree.taggedUnionEnumTag(node)), | ||
| 611 | |||
| 612 | .@"break" => return breakExpr(mod, scope, node), | ||
| 613 | .@"continue" => return continueExpr(mod, scope, node), | ||
| 614 | .grouped_expression => return expr(mod, scope, rl, node_datas[node].lhs), | ||
| 615 | .array_type => return arrayType(mod, scope, rl, node), | ||
| 616 | .array_type_sentinel => return arrayTypeSentinel(mod, scope, rl, node), | ||
| 617 | .char_literal => return charLiteral(mod, scope, rl, node), | ||
| 618 | .error_set_decl => return errorSetDecl(mod, scope, rl, node), | ||
| 619 | .array_access => return arrayAccess(mod, scope, rl, node), | ||
| 620 | .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs), | ||
| 621 | .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node), | ||
| 622 | |||
| 623 | .@"nosuspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 624 | .@"suspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 625 | .@"await" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 626 | .@"resume" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 627 | |||
| 628 | .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}), | ||
| 629 | .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}), | ||
| 630 | .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), | ||
| 631 | |||
| 632 | .array_init_one, | ||
| 633 | .array_init_one_comma, | ||
| 634 | .array_init_dot_two, | ||
| 635 | .array_init_dot_two_comma, | ||
| 636 | .array_init_dot, | ||
| 637 | .array_init_dot_comma, | ||
| 638 | .array_init, | ||
| 639 | .array_init_comma, | ||
| 640 | => return mod.failNode(scope, node, "TODO implement astgen.expr for array literals", .{}), | ||
| 641 | |||
| 642 | .struct_init_one, | ||
| 643 | .struct_init_one_comma, | ||
| 644 | .struct_init_dot_two, | ||
| 645 | .struct_init_dot_two_comma, | ||
| 646 | .struct_init_dot, | ||
| 647 | .struct_init_dot_comma, | ||
| 648 | .struct_init, | ||
| 649 | .struct_init_comma, | ||
| 650 | => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}), | ||
| 651 | |||
| 652 | .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}), | ||
| 653 | .fn_proto_simple, | ||
| 654 | .fn_proto_multi, | ||
| 655 | .fn_proto_one, | ||
| 656 | .fn_proto, | ||
| 657 | => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}), | ||
| 658 | } | ||
| 659 | } | ||
| 660 | |||
| 661 | pub fn comptimeExpr( | ||
| 662 | mod: *Module, | ||
| 663 | parent_scope: *Scope, | ||
| 664 | rl: ResultLoc, | ||
| 665 | node: ast.Node.Index, | ||
| 666 | ) InnerError!zir.Inst.Ref { | ||
| 667 | const gz = parent_scope.getGenZir(); | ||
| 668 | |||
| 669 | const prev_force_comptime = gz.force_comptime; | ||
| 670 | gz.force_comptime = true; | ||
| 671 | const result = try expr(mod, parent_scope, rl, node); | ||
| 672 | gz.force_comptime = prev_force_comptime; | ||
| 673 | return result; | ||
| 674 | } | ||
| 675 | |||
| 676 | fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 677 | const parent_gz = parent_scope.getGenZir(); | ||
| 678 | const tree = parent_gz.tree(); | ||
| 679 | const node_datas = tree.nodes.items(.data); | ||
| 680 | const break_label = node_datas[node].lhs; | ||
| 681 | const rhs = node_datas[node].rhs; | ||
| 682 | |||
| 683 | // Look for the label in the scope. | ||
| 684 | var scope = parent_scope; | ||
| 685 | while (true) { | ||
| 686 | switch (scope.tag) { | ||
| 687 | .gen_zir => { | ||
| 688 | const block_gz = scope.cast(Scope.GenZir).?; | ||
| 689 | |||
| 690 | const block_inst = blk: { | ||
| 691 | if (break_label != 0) { | ||
| 692 | if (block_gz.label) |*label| { | ||
| 693 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { | ||
| 694 | label.used = true; | ||
| 695 | break :blk label.block_inst; | ||
| 696 | } | ||
| 697 | } | ||
| 698 | } else if (block_gz.break_block != 0) { | ||
| 699 | break :blk block_gz.break_block; | ||
| 700 | } | ||
| 701 | scope = block_gz.parent; | ||
| 702 | continue; | ||
| 703 | }; | ||
| 704 | |||
| 705 | if (rhs == 0) { | ||
| 706 | _ = try parent_gz.addBreak(.@"break", block_inst, .void_value); | ||
| 707 | return zir.Inst.Ref.unreachable_value; | ||
| 708 | } | ||
| 709 | block_gz.break_count += 1; | ||
| 710 | const prev_rvalue_rl_count = block_gz.rvalue_rl_count; | ||
| 711 | const operand = try expr(mod, parent_scope, block_gz.break_result_loc, rhs); | ||
| 712 | const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count; | ||
| 713 | |||
| 714 | const br = try parent_gz.addBreak(.@"break", block_inst, operand); | ||
| 715 | |||
| 716 | if (block_gz.break_result_loc == .block_ptr) { | ||
| 717 | try block_gz.labeled_breaks.append(mod.gpa, br); | ||
| 718 | |||
| 719 | if (have_store_to_block) { | ||
| 720 | const zir_tags = parent_gz.zir_code.instructions.items(.tag); | ||
| 721 | const zir_datas = parent_gz.zir_code.instructions.items(.data); | ||
| 722 | const store_inst = @intCast(u32, zir_tags.len - 2); | ||
| 723 | assert(zir_tags[store_inst] == .store_to_block_ptr); | ||
| 724 | assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr); | ||
| 725 | try block_gz.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst); | ||
| 726 | } | ||
| 727 | } | ||
| 728 | return zir.Inst.Ref.unreachable_value; | ||
| 729 | }, | ||
| 730 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 731 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 732 | else => if (break_label != 0) { | ||
| 733 | const label_name = try mod.identifierTokenString(parent_scope, break_label); | ||
| 734 | return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); | ||
| 735 | } else { | ||
| 736 | return mod.failNode(parent_scope, node, "break expression outside loop", .{}); | ||
| 737 | }, | ||
| 738 | } | ||
| 739 | } | ||
| 740 | } | ||
| 741 | |||
| 742 | fn continueExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 743 | const parent_gz = parent_scope.getGenZir(); | ||
| 744 | const tree = parent_gz.tree(); | ||
| 745 | const node_datas = tree.nodes.items(.data); | ||
| 746 | const break_label = node_datas[node].lhs; | ||
| 747 | |||
| 748 | // Look for the label in the scope. | ||
| 749 | var scope = parent_scope; | ||
| 750 | while (true) { | ||
| 751 | switch (scope.tag) { | ||
| 752 | .gen_zir => { | ||
| 753 | const gen_zir = scope.cast(Scope.GenZir).?; | ||
| 754 | const continue_block = gen_zir.continue_block; | ||
| 755 | if (continue_block == 0) { | ||
| 756 | scope = gen_zir.parent; | ||
| 757 | continue; | ||
| 758 | } | ||
| 759 | if (break_label != 0) blk: { | ||
| 760 | if (gen_zir.label) |*label| { | ||
| 761 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { | ||
| 762 | label.used = true; | ||
| 763 | break :blk; | ||
| 764 | } | ||
| 765 | } | ||
| 766 | // found continue but either it has a different label, or no label | ||
| 767 | scope = gen_zir.parent; | ||
| 768 | continue; | ||
| 769 | } | ||
| 770 | |||
| 771 | // TODO emit a break_inline if the loop being continued is inline | ||
| 772 | _ = try parent_gz.addBreak(.@"break", continue_block, .void_value); | ||
| 773 | return zir.Inst.Ref.unreachable_value; | ||
| 774 | }, | ||
| 775 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 776 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 777 | else => if (break_label != 0) { | ||
| 778 | const label_name = try mod.identifierTokenString(parent_scope, break_label); | ||
| 779 | return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); | ||
| 780 | } else { | ||
| 781 | return mod.failNode(parent_scope, node, "continue expression outside loop", .{}); | ||
| 782 | }, | ||
| 783 | } | ||
| 784 | } | ||
| 785 | } | ||
| 786 | |||
| 787 | pub fn blockExpr( | ||
| 788 | mod: *Module, | ||
| 789 | scope: *Scope, | ||
| 790 | rl: ResultLoc, | ||
| 791 | block_node: ast.Node.Index, | ||
| 792 | statements: []const ast.Node.Index, | ||
| 793 | ) InnerError!zir.Inst.Ref { | ||
| 794 | const tracy = trace(@src()); | ||
| 795 | defer tracy.end(); | ||
| 796 | |||
| 797 | const tree = scope.tree(); | ||
| 798 | const main_tokens = tree.nodes.items(.main_token); | ||
| 799 | const token_tags = tree.tokens.items(.tag); | ||
| 800 | |||
| 801 | const lbrace = main_tokens[block_node]; | ||
| 802 | if (token_tags[lbrace - 1] == .colon and | ||
| 803 | token_tags[lbrace - 2] == .identifier) | ||
| 804 | { | ||
| 805 | return labeledBlockExpr(mod, scope, rl, block_node, statements, .block); | ||
| 806 | } | ||
| 807 | |||
| 808 | try blockExprStmts(mod, scope, block_node, statements); | ||
| 809 | return rvalue(mod, scope, rl, .void_value, block_node); | ||
| 810 | } | ||
| 811 | |||
| 812 | fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void { | ||
| 813 | // Look for the label in the scope. | ||
| 814 | var scope = parent_scope; | ||
| 815 | while (true) { | ||
| 816 | switch (scope.tag) { | ||
| 817 | .gen_zir => { | ||
| 818 | const gen_zir = scope.cast(Scope.GenZir).?; | ||
| 819 | if (gen_zir.label) |prev_label| { | ||
| 820 | if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) { | ||
| 821 | const tree = parent_scope.tree(); | ||
| 822 | const main_tokens = tree.nodes.items(.main_token); | ||
| 823 | |||
| 824 | const label_name = try mod.identifierTokenString(parent_scope, label); | ||
| 825 | const msg = msg: { | ||
| 826 | const msg = try mod.errMsg( | ||
| 827 | parent_scope, | ||
| 828 | gen_zir.tokSrcLoc(label), | ||
| 829 | "redefinition of label '{s}'", | ||
| 830 | .{label_name}, | ||
| 831 | ); | ||
| 832 | errdefer msg.destroy(mod.gpa); | ||
| 833 | try mod.errNote( | ||
| 834 | parent_scope, | ||
| 835 | gen_zir.tokSrcLoc(prev_label.token), | ||
| 836 | msg, | ||
| 837 | "previous definition is here", | ||
| 838 | .{}, | ||
| 839 | ); | ||
| 840 | break :msg msg; | ||
| 841 | }; | ||
| 842 | return mod.failWithOwnedErrorMsg(parent_scope, msg); | ||
| 843 | } | ||
| 844 | } | ||
| 845 | scope = gen_zir.parent; | ||
| 846 | }, | ||
| 847 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 848 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 849 | else => return, | ||
| 850 | } | ||
| 851 | } | ||
| 852 | } | ||
| 853 | |||
| 854 | fn labeledBlockExpr( | ||
| 855 | mod: *Module, | ||
| 856 | parent_scope: *Scope, | ||
| 857 | rl: ResultLoc, | ||
| 858 | block_node: ast.Node.Index, | ||
| 859 | statements: []const ast.Node.Index, | ||
| 860 | zir_tag: zir.Inst.Tag, | ||
| 861 | ) InnerError!zir.Inst.Ref { | ||
| 862 | const tracy = trace(@src()); | ||
| 863 | defer tracy.end(); | ||
| 864 | |||
| 865 | assert(zir_tag == .block); | ||
| 866 | |||
| 867 | const tree = parent_scope.tree(); | ||
| 868 | const main_tokens = tree.nodes.items(.main_token); | ||
| 869 | const token_tags = tree.tokens.items(.tag); | ||
| 870 | |||
| 871 | const lbrace = main_tokens[block_node]; | ||
| 872 | const label_token = lbrace - 2; | ||
| 873 | assert(token_tags[label_token] == .identifier); | ||
| 874 | |||
| 875 | try checkLabelRedefinition(mod, parent_scope, label_token); | ||
| 876 | |||
| 877 | // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct | ||
| 878 | // so that break statements can reference it. | ||
| 879 | const gz = parent_scope.getGenZir(); | ||
| 880 | const block_inst = try gz.addBlock(zir_tag, block_node); | ||
| 881 | try gz.instructions.append(mod.gpa, block_inst); | ||
| 882 | |||
| 883 | var block_scope: Scope.GenZir = .{ | ||
| 884 | .parent = parent_scope, | ||
| 885 | .zir_code = gz.zir_code, | ||
| 886 | .force_comptime = gz.force_comptime, | ||
| 887 | .instructions = .{}, | ||
| 888 | // TODO @as here is working around a stage1 miscompilation bug :( | ||
| 889 | .label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{ | ||
| 890 | .token = label_token, | ||
| 891 | .block_inst = block_inst, | ||
| 892 | }), | ||
| 893 | }; | ||
| 894 | setBlockResultLoc(&block_scope, rl); | ||
| 895 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 896 | defer block_scope.labeled_breaks.deinit(mod.gpa); | ||
| 897 | defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); | ||
| 898 | |||
| 899 | try blockExprStmts(mod, &block_scope.base, block_node, statements); | ||
| 900 | |||
| 901 | if (!block_scope.label.?.used) { | ||
| 902 | return mod.failTok(parent_scope, label_token, "unused block label", .{}); | ||
| 903 | } | ||
| 904 | |||
| 905 | const zir_tags = gz.zir_code.instructions.items(.tag); | ||
| 906 | const zir_datas = gz.zir_code.instructions.items(.data); | ||
| 907 | |||
| 908 | const strat = rlStrategy(rl, &block_scope); | ||
| 909 | switch (strat.tag) { | ||
| 910 | .break_void => { | ||
| 911 | // The code took advantage of the result location as a pointer. | ||
| 912 | // Turn the break instruction operands into void. | ||
| 913 | for (block_scope.labeled_breaks.items) |br| { | ||
| 914 | zir_datas[br].@"break".operand = .void_value; | ||
| 915 | } | ||
| 916 | try block_scope.setBlockBody(block_inst); | ||
| 917 | |||
| 918 | return gz.zir_code.indexToRef(block_inst); | ||
| 919 | }, | ||
| 920 | .break_operand => { | ||
| 921 | // All break operands are values that did not use the result location pointer. | ||
| 922 | if (strat.elide_store_to_block_ptr_instructions) { | ||
| 923 | for (block_scope.labeled_store_to_block_ptr_list.items) |inst| { | ||
| 924 | zir_tags[inst] = .elided; | ||
| 925 | zir_datas[inst] = undefined; | ||
| 926 | } | ||
| 927 | // TODO technically not needed since we changed the tag to elided but | ||
| 928 | // would be better still to elide the ones that are in this list. | ||
| 929 | } | ||
| 930 | try block_scope.setBlockBody(block_inst); | ||
| 931 | const block_ref = gz.zir_code.indexToRef(block_inst); | ||
| 932 | switch (rl) { | ||
| 933 | .ref => return block_ref, | ||
| 934 | else => return rvalue(mod, parent_scope, rl, block_ref, block_node), | ||
| 935 | } | ||
| 936 | }, | ||
| 937 | } | ||
| 938 | } | ||
| 939 | |||
| 940 | fn blockExprStmts( | ||
| 941 | mod: *Module, | ||
| 942 | parent_scope: *Scope, | ||
| 943 | node: ast.Node.Index, | ||
| 944 | statements: []const ast.Node.Index, | ||
| 945 | ) !void { | ||
| 946 | const tree = parent_scope.tree(); | ||
| 947 | const main_tokens = tree.nodes.items(.main_token); | ||
| 948 | const node_tags = tree.nodes.items(.tag); | ||
| 949 | |||
| 950 | var block_arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 951 | defer block_arena.deinit(); | ||
| 952 | |||
| 953 | const gz = parent_scope.getGenZir(); | ||
| 954 | |||
| 955 | var scope = parent_scope; | ||
| 956 | for (statements) |statement| { | ||
| 957 | if (!gz.force_comptime) { | ||
| 958 | _ = try gz.addNode(.dbg_stmt_node, statement); | ||
| 959 | } | ||
| 960 | switch (node_tags[statement]) { | ||
| 961 | .global_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.globalVarDecl(statement)), | ||
| 962 | .local_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.localVarDecl(statement)), | ||
| 963 | .simple_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.simpleVarDecl(statement)), | ||
| 964 | .aligned_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.alignedVarDecl(statement)), | ||
| 965 | |||
| 966 | .assign => try assign(mod, scope, statement), | ||
| 967 | .assign_bit_and => try assignOp(mod, scope, statement, .bit_and), | ||
| 968 | .assign_bit_or => try assignOp(mod, scope, statement, .bit_or), | ||
| 969 | .assign_bit_shift_left => try assignOp(mod, scope, statement, .shl), | ||
| 970 | .assign_bit_shift_right => try assignOp(mod, scope, statement, .shr), | ||
| 971 | .assign_bit_xor => try assignOp(mod, scope, statement, .xor), | ||
| 972 | .assign_div => try assignOp(mod, scope, statement, .div), | ||
| 973 | .assign_sub => try assignOp(mod, scope, statement, .sub), | ||
| 974 | .assign_sub_wrap => try assignOp(mod, scope, statement, .subwrap), | ||
| 975 | .assign_mod => try assignOp(mod, scope, statement, .mod_rem), | ||
| 976 | .assign_add => try assignOp(mod, scope, statement, .add), | ||
| 977 | .assign_add_wrap => try assignOp(mod, scope, statement, .addwrap), | ||
| 978 | .assign_mul => try assignOp(mod, scope, statement, .mul), | ||
| 979 | .assign_mul_wrap => try assignOp(mod, scope, statement, .mulwrap), | ||
| 980 | |||
| 981 | else => { | ||
| 982 | // We need to emit an error if the result is not `noreturn` or `void`, but | ||
| 983 | // we want to avoid adding the ZIR instruction if possible for performance. | ||
| 984 | const maybe_unused_result = try expr(mod, scope, .none, statement); | ||
| 985 | const elide_check = if (gz.zir_code.refToIndex(maybe_unused_result)) |inst| b: { | ||
| 986 | // Note that this array becomes invalid after appending more items to it | ||
| 987 | // in the above while loop. | ||
| 988 | const zir_tags = gz.zir_code.instructions.items(.tag); | ||
| 989 | switch (zir_tags[inst]) { | ||
| 990 | .@"const" => { | ||
| 991 | const tv = gz.zir_code.instructions.items(.data)[inst].@"const"; | ||
| 992 | break :b switch (tv.ty.zigTypeTag()) { | ||
| 993 | .NoReturn, .Void => true, | ||
| 994 | else => false, | ||
| 995 | }; | ||
| 996 | }, | ||
| 997 | // For some instructions, swap in a slightly different ZIR tag | ||
| 998 | // so we can avoid a separate ensure_result_used instruction. | ||
| 999 | .call_none_chkused => unreachable, | ||
| 1000 | .call_none => { | ||
| 1001 | zir_tags[inst] = .call_none_chkused; | ||
| 1002 | break :b true; | ||
| 1003 | }, | ||
| 1004 | .call_chkused => unreachable, | ||
| 1005 | .call => { | ||
| 1006 | zir_tags[inst] = .call_chkused; | ||
| 1007 | break :b true; | ||
| 1008 | }, | ||
| 1009 | |||
| 1010 | // ZIR instructions that might be a type other than `noreturn` or `void`. | ||
| 1011 | .add, | ||
| 1012 | .addwrap, | ||
| 1013 | .alloc, | ||
| 1014 | .alloc_mut, | ||
| 1015 | .alloc_inferred, | ||
| 1016 | .alloc_inferred_mut, | ||
| 1017 | .array_cat, | ||
| 1018 | .array_mul, | ||
| 1019 | .array_type, | ||
| 1020 | .array_type_sentinel, | ||
| 1021 | .indexable_ptr_len, | ||
| 1022 | .as, | ||
| 1023 | .as_node, | ||
| 1024 | .@"asm", | ||
| 1025 | .asm_volatile, | ||
| 1026 | .bit_and, | ||
| 1027 | .bitcast, | ||
| 1028 | .bitcast_ref, | ||
| 1029 | .bitcast_result_ptr, | ||
| 1030 | .bit_or, | ||
| 1031 | .block, | ||
| 1032 | .block_inline, | ||
| 1033 | .loop, | ||
| 1034 | .bool_br_and, | ||
| 1035 | .bool_br_or, | ||
| 1036 | .bool_not, | ||
| 1037 | .bool_and, | ||
| 1038 | .bool_or, | ||
| 1039 | .call_compile_time, | ||
| 1040 | .cmp_lt, | ||
| 1041 | .cmp_lte, | ||
| 1042 | .cmp_eq, | ||
| 1043 | .cmp_gte, | ||
| 1044 | .cmp_gt, | ||
| 1045 | .cmp_neq, | ||
| 1046 | .coerce_result_ptr, | ||
| 1047 | .decl_ref, | ||
| 1048 | .decl_val, | ||
| 1049 | .load, | ||
| 1050 | .div, | ||
| 1051 | .elem_ptr, | ||
| 1052 | .elem_val, | ||
| 1053 | .elem_ptr_node, | ||
| 1054 | .elem_val_node, | ||
| 1055 | .floatcast, | ||
| 1056 | .field_ptr, | ||
| 1057 | .field_val, | ||
| 1058 | .field_ptr_named, | ||
| 1059 | .field_val_named, | ||
| 1060 | .fn_type, | ||
| 1061 | .fn_type_var_args, | ||
| 1062 | .fn_type_cc, | ||
| 1063 | .fn_type_cc_var_args, | ||
| 1064 | .int, | ||
| 1065 | .intcast, | ||
| 1066 | .int_type, | ||
| 1067 | .is_non_null, | ||
| 1068 | .is_null, | ||
| 1069 | .is_non_null_ptr, | ||
| 1070 | .is_null_ptr, | ||
| 1071 | .is_err, | ||
| 1072 | .is_err_ptr, | ||
| 1073 | .mod_rem, | ||
| 1074 | .mul, | ||
| 1075 | .mulwrap, | ||
| 1076 | .param_type, | ||
| 1077 | .ptrtoint, | ||
| 1078 | .ref, | ||
| 1079 | .ret_ptr, | ||
| 1080 | .ret_type, | ||
| 1081 | .shl, | ||
| 1082 | .shr, | ||
| 1083 | .str, | ||
| 1084 | .sub, | ||
| 1085 | .subwrap, | ||
| 1086 | .negate, | ||
| 1087 | .negate_wrap, | ||
| 1088 | .typeof, | ||
| 1089 | .xor, | ||
| 1090 | .optional_type, | ||
| 1091 | .optional_type_from_ptr_elem, | ||
| 1092 | .optional_payload_safe, | ||
| 1093 | .optional_payload_unsafe, | ||
| 1094 | .optional_payload_safe_ptr, | ||
| 1095 | .optional_payload_unsafe_ptr, | ||
| 1096 | .err_union_payload_safe, | ||
| 1097 | .err_union_payload_unsafe, | ||
| 1098 | .err_union_payload_safe_ptr, | ||
| 1099 | .err_union_payload_unsafe_ptr, | ||
| 1100 | .err_union_code, | ||
| 1101 | .err_union_code_ptr, | ||
| 1102 | .ptr_type, | ||
| 1103 | .ptr_type_simple, | ||
| 1104 | .enum_literal, | ||
| 1105 | .enum_literal_small, | ||
| 1106 | .merge_error_sets, | ||
| 1107 | .error_union_type, | ||
| 1108 | .bit_not, | ||
| 1109 | .error_set, | ||
| 1110 | .error_value, | ||
| 1111 | .slice_start, | ||
| 1112 | .slice_end, | ||
| 1113 | .slice_sentinel, | ||
| 1114 | .import, | ||
| 1115 | .typeof_peer, | ||
| 1116 | => break :b false, | ||
| 1117 | |||
| 1118 | // ZIR instructions that are always either `noreturn` or `void`. | ||
| 1119 | .breakpoint, | ||
| 1120 | .dbg_stmt_node, | ||
| 1121 | .ensure_result_used, | ||
| 1122 | .ensure_result_non_error, | ||
| 1123 | .set_eval_branch_quota, | ||
| 1124 | .compile_log, | ||
| 1125 | .ensure_err_payload_void, | ||
| 1126 | .@"break", | ||
| 1127 | .break_inline, | ||
| 1128 | .condbr, | ||
| 1129 | .condbr_inline, | ||
| 1130 | .compile_error, | ||
| 1131 | .ret_node, | ||
| 1132 | .ret_tok, | ||
| 1133 | .ret_coerce, | ||
| 1134 | .@"unreachable", | ||
| 1135 | .elided, | ||
| 1136 | .store, | ||
| 1137 | .store_node, | ||
| 1138 | .store_to_block_ptr, | ||
| 1139 | .store_to_inferred_ptr, | ||
| 1140 | .resolve_inferred_alloc, | ||
| 1141 | .repeat, | ||
| 1142 | .repeat_inline, | ||
| 1143 | => break :b true, | ||
| 1144 | } | ||
| 1145 | } else switch (maybe_unused_result) { | ||
| 1146 | .none => unreachable, | ||
| 1147 | |||
| 1148 | .void_value, | ||
| 1149 | .unreachable_value, | ||
| 1150 | => true, | ||
| 1151 | |||
| 1152 | else => false, | ||
| 1153 | }; | ||
| 1154 | if (!elide_check) { | ||
| 1155 | _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement); | ||
| 1156 | } | ||
| 1157 | }, | ||
| 1158 | } | ||
| 1159 | } | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | fn varDecl( | ||
| 1163 | mod: *Module, | ||
| 1164 | scope: *Scope, | ||
| 1165 | node: ast.Node.Index, | ||
| 1166 | block_arena: *Allocator, | ||
| 1167 | var_decl: ast.full.VarDecl, | ||
| 1168 | ) InnerError!*Scope { | ||
| 1169 | if (var_decl.comptime_token) |comptime_token| { | ||
| 1170 | return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{}); | ||
| 1171 | } | ||
| 1172 | if (var_decl.ast.align_node != 0) { | ||
| 1173 | return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{}); | ||
| 1174 | } | ||
| 1175 | const gz = scope.getGenZir(); | ||
| 1176 | const wzc = gz.zir_code; | ||
| 1177 | const tree = scope.tree(); | ||
| 1178 | const token_tags = tree.tokens.items(.tag); | ||
| 1179 | |||
| 1180 | const name_token = var_decl.ast.mut_token + 1; | ||
| 1181 | const name_src = gz.tokSrcLoc(name_token); | ||
| 1182 | const ident_name = try mod.identifierTokenString(scope, name_token); | ||
| 1183 | |||
| 1184 | // Local variables shadowing detection, including function parameters. | ||
| 1185 | { | ||
| 1186 | var s = scope; | ||
| 1187 | while (true) switch (s.tag) { | ||
| 1188 | .local_val => { | ||
| 1189 | const local_val = s.cast(Scope.LocalVal).?; | ||
| 1190 | if (mem.eql(u8, local_val.name, ident_name)) { | ||
| 1191 | const msg = msg: { | ||
| 1192 | const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{ | ||
| 1193 | ident_name, | ||
| 1194 | }); | ||
| 1195 | errdefer msg.destroy(mod.gpa); | ||
| 1196 | try mod.errNote(scope, local_val.src, msg, "previous definition is here", .{}); | ||
| 1197 | break :msg msg; | ||
| 1198 | }; | ||
| 1199 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 1200 | } | ||
| 1201 | s = local_val.parent; | ||
| 1202 | }, | ||
| 1203 | .local_ptr => { | ||
| 1204 | const local_ptr = s.cast(Scope.LocalPtr).?; | ||
| 1205 | if (mem.eql(u8, local_ptr.name, ident_name)) { | ||
| 1206 | const msg = msg: { | ||
| 1207 | const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{ | ||
| 1208 | ident_name, | ||
| 1209 | }); | ||
| 1210 | errdefer msg.destroy(mod.gpa); | ||
| 1211 | try mod.errNote(scope, local_ptr.src, msg, "previous definition is here", .{}); | ||
| 1212 | break :msg msg; | ||
| 1213 | }; | ||
| 1214 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 1215 | } | ||
| 1216 | s = local_ptr.parent; | ||
| 1217 | }, | ||
| 1218 | .gen_zir => s = s.cast(Scope.GenZir).?.parent, | ||
| 1219 | else => break, | ||
| 1220 | }; | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | // Namespace vars shadowing detection | ||
| 1224 | if (mod.lookupDeclName(scope, ident_name)) |_| { | ||
| 1225 | // TODO add note for other definition | ||
| 1226 | return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); | ||
| 1227 | } | ||
| 1228 | if (var_decl.ast.init_node == 0) { | ||
| 1229 | return mod.fail(scope, name_src, "variables must be initialized", .{}); | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | switch (token_tags[var_decl.ast.mut_token]) { | ||
| 1233 | .keyword_const => { | ||
| 1234 | // Depending on the type of AST the initialization expression is, we may need an lvalue | ||
| 1235 | // or an rvalue as a result location. If it is an rvalue, we can use the instruction as | ||
| 1236 | // the variable, no memory location needed. | ||
| 1237 | if (!nodeMayNeedMemoryLocation(scope, var_decl.ast.init_node)) { | ||
| 1238 | const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{ | ||
| 1239 | .ty = try typeExpr(mod, scope, var_decl.ast.type_node), | ||
| 1240 | } else .none; | ||
| 1241 | const init_inst = try expr(mod, scope, result_loc, var_decl.ast.init_node); | ||
| 1242 | const sub_scope = try block_arena.create(Scope.LocalVal); | ||
| 1243 | sub_scope.* = .{ | ||
| 1244 | .parent = scope, | ||
| 1245 | .gen_zir = gz, | ||
| 1246 | .name = ident_name, | ||
| 1247 | .inst = init_inst, | ||
| 1248 | .src = name_src, | ||
| 1249 | }; | ||
| 1250 | return &sub_scope.base; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | // Detect whether the initialization expression actually uses the | ||
| 1254 | // result location pointer. | ||
| 1255 | var init_scope: Scope.GenZir = .{ | ||
| 1256 | .parent = scope, | ||
| 1257 | .force_comptime = gz.force_comptime, | ||
| 1258 | .zir_code = wzc, | ||
| 1259 | }; | ||
| 1260 | defer init_scope.instructions.deinit(mod.gpa); | ||
| 1261 | |||
| 1262 | var resolve_inferred_alloc: zir.Inst.Ref = .none; | ||
| 1263 | var opt_type_inst: zir.Inst.Ref = .none; | ||
| 1264 | if (var_decl.ast.type_node != 0) { | ||
| 1265 | const type_inst = try typeExpr(mod, &init_scope.base, var_decl.ast.type_node); | ||
| 1266 | opt_type_inst = type_inst; | ||
| 1267 | init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node); | ||
| 1268 | } else { | ||
| 1269 | const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node); | ||
| 1270 | resolve_inferred_alloc = alloc; | ||
| 1271 | init_scope.rl_ptr = alloc; | ||
| 1272 | } | ||
| 1273 | const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope }; | ||
| 1274 | const init_inst = try expr(mod, &init_scope.base, init_result_loc, var_decl.ast.init_node); | ||
| 1275 | const zir_tags = wzc.instructions.items(.tag); | ||
| 1276 | const zir_datas = wzc.instructions.items(.data); | ||
| 1277 | |||
| 1278 | const parent_zir = &gz.instructions; | ||
| 1279 | if (init_scope.rvalue_rl_count == 1) { | ||
| 1280 | // Result location pointer not used. We don't need an alloc for this | ||
| 1281 | // const local, and type inference becomes trivial. | ||
| 1282 | // Move the init_scope instructions into the parent scope, eliding | ||
| 1283 | // the alloc instruction and the store_to_block_ptr instruction. | ||
| 1284 | const expected_len = parent_zir.items.len + init_scope.instructions.items.len - 2; | ||
| 1285 | try parent_zir.ensureCapacity(mod.gpa, expected_len); | ||
| 1286 | for (init_scope.instructions.items) |src_inst| { | ||
| 1287 | if (wzc.indexToRef(src_inst) == init_scope.rl_ptr) continue; | ||
| 1288 | if (zir_tags[src_inst] == .store_to_block_ptr) { | ||
| 1289 | if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue; | ||
| 1290 | } | ||
| 1291 | parent_zir.appendAssumeCapacity(src_inst); | ||
| 1292 | } | ||
| 1293 | assert(parent_zir.items.len == expected_len); | ||
| 1294 | const casted_init = if (opt_type_inst != .none) | ||
| 1295 | try gz.addPlNode(.as_node, var_decl.ast.type_node, zir.Inst.As{ | ||
| 1296 | .dest_type = opt_type_inst, | ||
| 1297 | .operand = init_inst, | ||
| 1298 | }) | ||
| 1299 | else | ||
| 1300 | init_inst; | ||
| 1301 | |||
| 1302 | const sub_scope = try block_arena.create(Scope.LocalVal); | ||
| 1303 | sub_scope.* = .{ | ||
| 1304 | .parent = scope, | ||
| 1305 | .gen_zir = gz, | ||
| 1306 | .name = ident_name, | ||
| 1307 | .inst = casted_init, | ||
| 1308 | .src = name_src, | ||
| 1309 | }; | ||
| 1310 | return &sub_scope.base; | ||
| 1311 | } | ||
| 1312 | // The initialization expression took advantage of the result location | ||
| 1313 | // of the const local. In this case we will create an alloc and a LocalPtr for it. | ||
| 1314 | // Move the init_scope instructions into the parent scope, swapping | ||
| 1315 | // store_to_block_ptr for store_to_inferred_ptr. | ||
| 1316 | const expected_len = parent_zir.items.len + init_scope.instructions.items.len; | ||
| 1317 | try parent_zir.ensureCapacity(mod.gpa, expected_len); | ||
| 1318 | for (init_scope.instructions.items) |src_inst| { | ||
| 1319 | if (zir_tags[src_inst] == .store_to_block_ptr) { | ||
| 1320 | if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) { | ||
| 1321 | zir_tags[src_inst] = .store_to_inferred_ptr; | ||
| 1322 | } | ||
| 1323 | } | ||
| 1324 | parent_zir.appendAssumeCapacity(src_inst); | ||
| 1325 | } | ||
| 1326 | assert(parent_zir.items.len == expected_len); | ||
| 1327 | if (resolve_inferred_alloc != .none) { | ||
| 1328 | _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); | ||
| 1329 | } | ||
| 1330 | const sub_scope = try block_arena.create(Scope.LocalPtr); | ||
| 1331 | sub_scope.* = .{ | ||
| 1332 | .parent = scope, | ||
| 1333 | .gen_zir = gz, | ||
| 1334 | .name = ident_name, | ||
| 1335 | .ptr = init_scope.rl_ptr, | ||
| 1336 | .src = name_src, | ||
| 1337 | }; | ||
| 1338 | return &sub_scope.base; | ||
| 1339 | }, | ||
| 1340 | .keyword_var => { | ||
| 1341 | var resolve_inferred_alloc: zir.Inst.Ref = .none; | ||
| 1342 | const var_data: struct { | ||
| 1343 | result_loc: ResultLoc, | ||
| 1344 | alloc: zir.Inst.Ref, | ||
| 1345 | } = if (var_decl.ast.type_node != 0) a: { | ||
| 1346 | const type_inst = try typeExpr(mod, scope, var_decl.ast.type_node); | ||
| 1347 | |||
| 1348 | const alloc = try gz.addUnNode(.alloc_mut, type_inst, node); | ||
| 1349 | break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } }; | ||
| 1350 | } else a: { | ||
| 1351 | const alloc = try gz.addUnNode(.alloc_inferred_mut, undefined, node); | ||
| 1352 | resolve_inferred_alloc = alloc; | ||
| 1353 | break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } }; | ||
| 1354 | }; | ||
| 1355 | const init_inst = try expr(mod, scope, var_data.result_loc, var_decl.ast.init_node); | ||
| 1356 | if (resolve_inferred_alloc != .none) { | ||
| 1357 | _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); | ||
| 1358 | } | ||
| 1359 | const sub_scope = try block_arena.create(Scope.LocalPtr); | ||
| 1360 | sub_scope.* = .{ | ||
| 1361 | .parent = scope, | ||
| 1362 | .gen_zir = gz, | ||
| 1363 | .name = ident_name, | ||
| 1364 | .ptr = var_data.alloc, | ||
| 1365 | .src = name_src, | ||
| 1366 | }; | ||
| 1367 | return &sub_scope.base; | ||
| 1368 | }, | ||
| 1369 | else => unreachable, | ||
| 1370 | } | ||
| 1371 | } | ||
| 1372 | |||
| 1373 | fn assign(mod: *Module, scope: *Scope, infix_node: ast.Node.Index) InnerError!void { | ||
| 1374 | const tree = scope.tree(); | ||
| 1375 | const node_datas = tree.nodes.items(.data); | ||
| 1376 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1377 | const node_tags = tree.nodes.items(.tag); | ||
| 1378 | |||
| 1379 | const lhs = node_datas[infix_node].lhs; | ||
| 1380 | const rhs = node_datas[infix_node].rhs; | ||
| 1381 | if (node_tags[lhs] == .identifier) { | ||
| 1382 | // This intentionally does not support `@"_"` syntax. | ||
| 1383 | const ident_name = tree.tokenSlice(main_tokens[lhs]); | ||
| 1384 | if (mem.eql(u8, ident_name, "_")) { | ||
| 1385 | _ = try expr(mod, scope, .discard, rhs); | ||
| 1386 | return; | ||
| 1387 | } | ||
| 1388 | } | ||
| 1389 | const lvalue = try lvalExpr(mod, scope, lhs); | ||
| 1390 | _ = try expr(mod, scope, .{ .ptr = lvalue }, rhs); | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | fn assignOp( | ||
| 1394 | mod: *Module, | ||
| 1395 | scope: *Scope, | ||
| 1396 | infix_node: ast.Node.Index, | ||
| 1397 | op_inst_tag: zir.Inst.Tag, | ||
| 1398 | ) InnerError!void { | ||
| 1399 | const tree = scope.tree(); | ||
| 1400 | const node_datas = tree.nodes.items(.data); | ||
| 1401 | const gz = scope.getGenZir(); | ||
| 1402 | |||
| 1403 | const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs); | ||
| 1404 | const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); | ||
| 1405 | const lhs_type = try gz.addUnTok(.typeof, lhs, infix_node); | ||
| 1406 | const rhs = try expr(mod, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs); | ||
| 1407 | |||
| 1408 | const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{ | ||
| 1409 | .lhs = lhs, | ||
| 1410 | .rhs = rhs, | ||
| 1411 | }); | ||
| 1412 | _ = try gz.addBin(.store, lhs_ptr, result); | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | fn boolNot(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 1416 | const tree = scope.tree(); | ||
| 1417 | const node_datas = tree.nodes.items(.data); | ||
| 1418 | |||
| 1419 | const operand = try expr(mod, scope, .{ .ty = .bool_type }, node_datas[node].lhs); | ||
| 1420 | const gz = scope.getGenZir(); | ||
| 1421 | const result = try gz.addUnNode(.bool_not, operand, node); | ||
| 1422 | return rvalue(mod, scope, rl, result, node); | ||
| 1423 | } | ||
| 1424 | |||
| 1425 | fn bitNot(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 1426 | const tree = scope.tree(); | ||
| 1427 | const node_datas = tree.nodes.items(.data); | ||
| 1428 | |||
| 1429 | const gz = scope.getGenZir(); | ||
| 1430 | const operand = try expr(mod, scope, .none, node_datas[node].lhs); | ||
| 1431 | const result = try gz.addUnNode(.bit_not, operand, node); | ||
| 1432 | return rvalue(mod, scope, rl, result, node); | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | fn negation( | ||
| 1436 | mod: *Module, | ||
| 1437 | scope: *Scope, | ||
| 1438 | rl: ResultLoc, | ||
| 1439 | node: ast.Node.Index, | ||
| 1440 | tag: zir.Inst.Tag, | ||
| 1441 | ) InnerError!zir.Inst.Ref { | ||
| 1442 | const tree = scope.tree(); | ||
| 1443 | const node_datas = tree.nodes.items(.data); | ||
| 1444 | |||
| 1445 | const gz = scope.getGenZir(); | ||
| 1446 | const operand = try expr(mod, scope, .none, node_datas[node].lhs); | ||
| 1447 | const result = try gz.addUnNode(tag, operand, node); | ||
| 1448 | return rvalue(mod, scope, rl, result, node); | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | fn ptrType( | ||
| 1452 | mod: *Module, | ||
| 1453 | scope: *Scope, | ||
| 1454 | rl: ResultLoc, | ||
| 1455 | node: ast.Node.Index, | ||
| 1456 | ptr_info: ast.full.PtrType, | ||
| 1457 | ) InnerError!zir.Inst.Ref { | ||
| 1458 | const tree = scope.tree(); | ||
| 1459 | const gz = scope.getGenZir(); | ||
| 1460 | |||
| 1461 | const elem_type = try typeExpr(mod, scope, ptr_info.ast.child_type); | ||
| 1462 | |||
| 1463 | const simple = ptr_info.ast.align_node == 0 and | ||
| 1464 | ptr_info.ast.sentinel == 0 and | ||
| 1465 | ptr_info.ast.bit_range_start == 0; | ||
| 1466 | |||
| 1467 | if (simple) { | ||
| 1468 | const result = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ | ||
| 1469 | .ptr_type_simple = .{ | ||
| 1470 | .is_allowzero = ptr_info.allowzero_token != null, | ||
| 1471 | .is_mutable = ptr_info.const_token == null, | ||
| 1472 | .is_volatile = ptr_info.volatile_token != null, | ||
| 1473 | .size = ptr_info.size, | ||
| 1474 | .elem_type = elem_type, | ||
| 1475 | }, | ||
| 1476 | } }); | ||
| 1477 | return rvalue(mod, scope, rl, result, node); | ||
| 1478 | } | ||
| 1479 | |||
| 1480 | var sentinel_ref: zir.Inst.Ref = .none; | ||
| 1481 | var align_ref: zir.Inst.Ref = .none; | ||
| 1482 | var bit_start_ref: zir.Inst.Ref = .none; | ||
| 1483 | var bit_end_ref: zir.Inst.Ref = .none; | ||
| 1484 | var trailing_count: u32 = 0; | ||
| 1485 | |||
| 1486 | if (ptr_info.ast.sentinel != 0) { | ||
| 1487 | sentinel_ref = try expr(mod, scope, .{ .ty = elem_type }, ptr_info.ast.sentinel); | ||
| 1488 | trailing_count += 1; | ||
| 1489 | } | ||
| 1490 | if (ptr_info.ast.align_node != 0) { | ||
| 1491 | align_ref = try expr(mod, scope, .none, ptr_info.ast.align_node); | ||
| 1492 | trailing_count += 1; | ||
| 1493 | } | ||
| 1494 | if (ptr_info.ast.bit_range_start != 0) { | ||
| 1495 | assert(ptr_info.ast.bit_range_end != 0); | ||
| 1496 | bit_start_ref = try expr(mod, scope, .none, ptr_info.ast.bit_range_start); | ||
| 1497 | bit_end_ref = try expr(mod, scope, .none, ptr_info.ast.bit_range_end); | ||
| 1498 | trailing_count += 2; | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | const gpa = gz.zir_code.gpa; | ||
| 1502 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | ||
| 1503 | try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1); | ||
| 1504 | try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len + | ||
| 1505 | @typeInfo(zir.Inst.PtrType).Struct.fields.len + trailing_count); | ||
| 1506 | |||
| 1507 | const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.PtrType{ .elem_type = elem_type }); | ||
| 1508 | if (sentinel_ref != .none) { | ||
| 1509 | gz.zir_code.extra.appendAssumeCapacity(@enumToInt(sentinel_ref)); | ||
| 1510 | } | ||
| 1511 | if (align_ref != .none) { | ||
| 1512 | gz.zir_code.extra.appendAssumeCapacity(@enumToInt(align_ref)); | ||
| 1513 | } | ||
| 1514 | if (bit_start_ref != .none) { | ||
| 1515 | gz.zir_code.extra.appendAssumeCapacity(@enumToInt(bit_start_ref)); | ||
| 1516 | gz.zir_code.extra.appendAssumeCapacity(@enumToInt(bit_end_ref)); | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | ||
| 1520 | const result = gz.zir_code.indexToRef(new_index); | ||
| 1521 | gz.zir_code.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{ | ||
| 1522 | .ptr_type = .{ | ||
| 1523 | .flags = .{ | ||
| 1524 | .is_allowzero = ptr_info.allowzero_token != null, | ||
| 1525 | .is_mutable = ptr_info.const_token == null, | ||
| 1526 | .is_volatile = ptr_info.volatile_token != null, | ||
| 1527 | .has_sentinel = sentinel_ref != .none, | ||
| 1528 | .has_align = align_ref != .none, | ||
| 1529 | .has_bit_range = bit_start_ref != .none, | ||
| 1530 | }, | ||
| 1531 | .size = ptr_info.size, | ||
| 1532 | .payload_index = payload_index, | ||
| 1533 | }, | ||
| 1534 | } }); | ||
| 1535 | gz.instructions.appendAssumeCapacity(new_index); | ||
| 1536 | |||
| 1537 | return rvalue(mod, scope, rl, result, node); | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | fn arrayType(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { | ||
| 1541 | const tree = scope.tree(); | ||
| 1542 | const node_datas = tree.nodes.items(.data); | ||
| 1543 | const gz = scope.getGenZir(); | ||
| 1544 | |||
| 1545 | // TODO check for [_]T | ||
| 1546 | const len = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].lhs); | ||
| 1547 | const elem_type = try typeExpr(mod, scope, node_datas[node].rhs); | ||
| 1548 | |||
| 1549 | const result = try gz.addBin(.array_type, len, elem_type); | ||
| 1550 | return rvalue(mod, scope, rl, result, node); | ||
| 1551 | } | ||
| 1552 | |||
| 1553 | fn arrayTypeSentinel(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { | ||
| 1554 | const tree = scope.tree(); | ||
| 1555 | const node_datas = tree.nodes.items(.data); | ||
| 1556 | const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel); | ||
| 1557 | const gz = scope.getGenZir(); | ||
| 1558 | |||
| 1559 | // TODO check for [_]T | ||
| 1560 | const len = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].lhs); | ||
| 1561 | const elem_type = try typeExpr(mod, scope, extra.elem_type); | ||
| 1562 | const sentinel = try expr(mod, scope, .{ .ty = elem_type }, extra.sentinel); | ||
| 1563 | |||
| 1564 | const result = try gz.addArrayTypeSentinel(len, elem_type, sentinel); | ||
| 1565 | return rvalue(mod, scope, rl, result, node); | ||
| 1566 | } | ||
| 1567 | |||
| 1568 | fn containerDecl( | ||
| 1569 | mod: *Module, | ||
| 1570 | scope: *Scope, | ||
| 1571 | rl: ResultLoc, | ||
| 1572 | container_decl: ast.full.ContainerDecl, | ||
| 1573 | ) InnerError!zir.Inst.Ref { | ||
| 1574 | return mod.failTok(scope, container_decl.ast.main_token, "TODO implement container decls", .{}); | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | fn errorSetDecl( | ||
| 1578 | mod: *Module, | ||
| 1579 | scope: *Scope, | ||
| 1580 | rl: ResultLoc, | ||
| 1581 | node: ast.Node.Index, | ||
| 1582 | ) InnerError!zir.Inst.Ref { | ||
| 1583 | if (true) @panic("TODO update for zir-memory-layout branch"); | ||
| 1584 | const gz = scope.getGenZir(); | ||
| 1585 | const tree = gz.tree(); | ||
| 1586 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1587 | const token_tags = tree.tokens.items(.tag); | ||
| 1588 | |||
| 1589 | // Count how many fields there are. | ||
| 1590 | const error_token = main_tokens[node]; | ||
| 1591 | const count: usize = count: { | ||
| 1592 | var tok_i = error_token + 2; | ||
| 1593 | var count: usize = 0; | ||
| 1594 | while (true) : (tok_i += 1) { | ||
| 1595 | switch (token_tags[tok_i]) { | ||
| 1596 | .doc_comment, .comma => {}, | ||
| 1597 | .identifier => count += 1, | ||
| 1598 | .r_brace => break :count count, | ||
| 1599 | else => unreachable, | ||
| 1600 | } | ||
| 1601 | } else unreachable; // TODO should not need else unreachable here | ||
| 1602 | }; | ||
| 1603 | |||
| 1604 | const fields = try scope.arena().alloc([]const u8, count); | ||
| 1605 | { | ||
| 1606 | var tok_i = error_token + 2; | ||
| 1607 | var field_i: usize = 0; | ||
| 1608 | while (true) : (tok_i += 1) { | ||
| 1609 | switch (token_tags[tok_i]) { | ||
| 1610 | .doc_comment, .comma => {}, | ||
| 1611 | .identifier => { | ||
| 1612 | fields[field_i] = try mod.identifierTokenString(scope, tok_i); | ||
| 1613 | field_i += 1; | ||
| 1614 | }, | ||
| 1615 | .r_brace => break, | ||
| 1616 | else => unreachable, | ||
| 1617 | } | ||
| 1618 | } | ||
| 1619 | } | ||
| 1620 | const result = try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}); | ||
| 1621 | return rvalue(mod, scope, rl, result); | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | fn orelseCatchExpr( | ||
| 1625 | mod: *Module, | ||
| 1626 | scope: *Scope, | ||
| 1627 | rl: ResultLoc, | ||
| 1628 | node: ast.Node.Index, | ||
| 1629 | lhs: ast.Node.Index, | ||
| 1630 | cond_op: zir.Inst.Tag, | ||
| 1631 | unwrap_op: zir.Inst.Tag, | ||
| 1632 | unwrap_code_op: zir.Inst.Tag, | ||
| 1633 | rhs: ast.Node.Index, | ||
| 1634 | payload_token: ?ast.TokenIndex, | ||
| 1635 | ) InnerError!zir.Inst.Ref { | ||
| 1636 | const parent_gz = scope.getGenZir(); | ||
| 1637 | const tree = parent_gz.tree(); | ||
| 1638 | |||
| 1639 | var block_scope: Scope.GenZir = .{ | ||
| 1640 | .parent = scope, | ||
| 1641 | .zir_code = parent_gz.zir_code, | ||
| 1642 | .force_comptime = parent_gz.force_comptime, | ||
| 1643 | .instructions = .{}, | ||
| 1644 | }; | ||
| 1645 | setBlockResultLoc(&block_scope, rl); | ||
| 1646 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 1647 | |||
| 1648 | // This could be a pointer or value depending on the `operand_rl` parameter. | ||
| 1649 | // We cannot use `block_scope.break_result_loc` because that has the bare | ||
| 1650 | // type, whereas this expression has the optional type. Later we make | ||
| 1651 | // up for this fact by calling rvalue on the else branch. | ||
| 1652 | block_scope.break_count += 1; | ||
| 1653 | |||
| 1654 | // TODO handle catch | ||
| 1655 | const operand_rl: ResultLoc = switch (block_scope.break_result_loc) { | ||
| 1656 | .ref => .ref, | ||
| 1657 | .discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => .none, | ||
| 1658 | .ty => |elem_ty| blk: { | ||
| 1659 | const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node); | ||
| 1660 | break :blk .{ .ty = wrapped_ty }; | ||
| 1661 | }, | ||
| 1662 | .ptr => |ptr_ty| blk: { | ||
| 1663 | const wrapped_ty = try block_scope.addUnNode(.optional_type_from_ptr_elem, ptr_ty, node); | ||
| 1664 | break :blk .{ .ty = wrapped_ty }; | ||
| 1665 | }, | ||
| 1666 | }; | ||
| 1667 | const operand = try expr(mod, &block_scope.base, operand_rl, lhs); | ||
| 1668 | const cond = try block_scope.addUnNode(cond_op, operand, node); | ||
| 1669 | const condbr = try block_scope.addCondBr(.condbr, node); | ||
| 1670 | |||
| 1671 | const block = try parent_gz.addBlock(.block, node); | ||
| 1672 | try parent_gz.instructions.append(mod.gpa, block); | ||
| 1673 | try block_scope.setBlockBody(block); | ||
| 1674 | |||
| 1675 | var then_scope: Scope.GenZir = .{ | ||
| 1676 | .parent = scope, | ||
| 1677 | .zir_code = parent_gz.zir_code, | ||
| 1678 | .force_comptime = block_scope.force_comptime, | ||
| 1679 | .instructions = .{}, | ||
| 1680 | }; | ||
| 1681 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 1682 | |||
| 1683 | var err_val_scope: Scope.LocalVal = undefined; | ||
| 1684 | const then_sub_scope = blk: { | ||
| 1685 | const payload = payload_token orelse break :blk &then_scope.base; | ||
| 1686 | if (mem.eql(u8, tree.tokenSlice(payload), "_")) { | ||
| 1687 | return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{}); | ||
| 1688 | } | ||
| 1689 | const err_name = try mod.identifierTokenString(scope, payload); | ||
| 1690 | err_val_scope = .{ | ||
| 1691 | .parent = &then_scope.base, | ||
| 1692 | .gen_zir = &then_scope, | ||
| 1693 | .name = err_name, | ||
| 1694 | .inst = try then_scope.addUnNode(unwrap_code_op, operand, node), | ||
| 1695 | .src = parent_gz.tokSrcLoc(payload), | ||
| 1696 | }; | ||
| 1697 | break :blk &err_val_scope.base; | ||
| 1698 | }; | ||
| 1699 | |||
| 1700 | block_scope.break_count += 1; | ||
| 1701 | const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, rhs); | ||
| 1702 | // We hold off on the break instructions as well as copying the then/else | ||
| 1703 | // instructions into place until we know whether to keep store_to_block_ptr | ||
| 1704 | // instructions or not. | ||
| 1705 | |||
| 1706 | var else_scope: Scope.GenZir = .{ | ||
| 1707 | .parent = scope, | ||
| 1708 | .zir_code = parent_gz.zir_code, | ||
| 1709 | .force_comptime = block_scope.force_comptime, | ||
| 1710 | .instructions = .{}, | ||
| 1711 | }; | ||
| 1712 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 1713 | |||
| 1714 | // This could be a pointer or value depending on `unwrap_op`. | ||
| 1715 | const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node); | ||
| 1716 | const else_result = switch (rl) { | ||
| 1717 | .ref => unwrapped_payload, | ||
| 1718 | else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload, node), | ||
| 1719 | }; | ||
| 1720 | |||
| 1721 | return finishThenElseBlock( | ||
| 1722 | mod, | ||
| 1723 | scope, | ||
| 1724 | rl, | ||
| 1725 | node, | ||
| 1726 | &block_scope, | ||
| 1727 | &then_scope, | ||
| 1728 | &else_scope, | ||
| 1729 | condbr, | ||
| 1730 | cond, | ||
| 1731 | node, | ||
| 1732 | node, | ||
| 1733 | then_result, | ||
| 1734 | else_result, | ||
| 1735 | block, | ||
| 1736 | block, | ||
| 1737 | .@"break", | ||
| 1738 | ); | ||
| 1739 | } | ||
| 1740 | |||
| 1741 | fn finishThenElseBlock( | ||
| 1742 | mod: *Module, | ||
| 1743 | parent_scope: *Scope, | ||
| 1744 | rl: ResultLoc, | ||
| 1745 | node: ast.Node.Index, | ||
| 1746 | block_scope: *Scope.GenZir, | ||
| 1747 | then_scope: *Scope.GenZir, | ||
| 1748 | else_scope: *Scope.GenZir, | ||
| 1749 | condbr: zir.Inst.Index, | ||
| 1750 | cond: zir.Inst.Ref, | ||
| 1751 | then_src: ast.Node.Index, | ||
| 1752 | else_src: ast.Node.Index, | ||
| 1753 | then_result: zir.Inst.Ref, | ||
| 1754 | else_result: zir.Inst.Ref, | ||
| 1755 | main_block: zir.Inst.Index, | ||
| 1756 | then_break_block: zir.Inst.Index, | ||
| 1757 | break_tag: zir.Inst.Tag, | ||
| 1758 | ) InnerError!zir.Inst.Ref { | ||
| 1759 | // We now have enough information to decide whether the result instruction should | ||
| 1760 | // be communicated via result location pointer or break instructions. | ||
| 1761 | const strat = rlStrategy(rl, block_scope); | ||
| 1762 | const wzc = block_scope.zir_code; | ||
| 1763 | switch (strat.tag) { | ||
| 1764 | .break_void => { | ||
| 1765 | if (!wzc.refIsNoReturn(then_result)) { | ||
| 1766 | _ = try then_scope.addBreak(break_tag, then_break_block, .void_value); | ||
| 1767 | } | ||
| 1768 | const elide_else = if (else_result != .none) wzc.refIsNoReturn(else_result) else false; | ||
| 1769 | if (!elide_else) { | ||
| 1770 | _ = try else_scope.addBreak(break_tag, main_block, .void_value); | ||
| 1771 | } | ||
| 1772 | assert(!strat.elide_store_to_block_ptr_instructions); | ||
| 1773 | try setCondBrPayload(condbr, cond, then_scope, else_scope); | ||
| 1774 | return wzc.indexToRef(main_block); | ||
| 1775 | }, | ||
| 1776 | .break_operand => { | ||
| 1777 | if (!wzc.refIsNoReturn(then_result)) { | ||
| 1778 | _ = try then_scope.addBreak(break_tag, then_break_block, then_result); | ||
| 1779 | } | ||
| 1780 | if (else_result != .none) { | ||
| 1781 | if (!wzc.refIsNoReturn(else_result)) { | ||
| 1782 | _ = try else_scope.addBreak(break_tag, main_block, else_result); | ||
| 1783 | } | ||
| 1784 | } else { | ||
| 1785 | _ = try else_scope.addBreak(break_tag, main_block, .void_value); | ||
| 1786 | } | ||
| 1787 | if (strat.elide_store_to_block_ptr_instructions) { | ||
| 1788 | try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope); | ||
| 1789 | } else { | ||
| 1790 | try setCondBrPayload(condbr, cond, then_scope, else_scope); | ||
| 1791 | } | ||
| 1792 | const block_ref = wzc.indexToRef(main_block); | ||
| 1793 | switch (rl) { | ||
| 1794 | .ref => return block_ref, | ||
| 1795 | else => return rvalue(mod, parent_scope, rl, block_ref, node), | ||
| 1796 | } | ||
| 1797 | }, | ||
| 1798 | } | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | /// Return whether the identifier names of two tokens are equal. Resolves @"" | ||
| 1802 | /// tokens without allocating. | ||
| 1803 | /// OK in theory it could do it without allocating. This implementation | ||
| 1804 | /// allocates when the @"" form is used. | ||
| 1805 | fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { | ||
| 1806 | const ident_name_1 = try mod.identifierTokenString(scope, token1); | ||
| 1807 | const ident_name_2 = try mod.identifierTokenString(scope, token2); | ||
| 1808 | return mem.eql(u8, ident_name_1, ident_name_2); | ||
| 1809 | } | ||
| 1810 | |||
| 1811 | pub fn fieldAccess( | ||
| 1812 | mod: *Module, | ||
| 1813 | scope: *Scope, | ||
| 1814 | rl: ResultLoc, | ||
| 1815 | node: ast.Node.Index, | ||
| 1816 | ) InnerError!zir.Inst.Ref { | ||
| 1817 | const gz = scope.getGenZir(); | ||
| 1818 | const tree = gz.tree(); | ||
| 1819 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1820 | const node_datas = tree.nodes.items(.data); | ||
| 1821 | const object_node = node_datas[node].lhs; | ||
| 1822 | const dot_token = main_tokens[node]; | ||
| 1823 | const field_ident = dot_token + 1; | ||
| 1824 | const string_bytes = &gz.zir_code.string_bytes; | ||
| 1825 | const str_index = @intCast(u32, string_bytes.items.len); | ||
| 1826 | try mod.appendIdentStr(scope, field_ident, string_bytes); | ||
| 1827 | try string_bytes.append(mod.gpa, 0); | ||
| 1828 | switch (rl) { | ||
| 1829 | .ref => return gz.addPlNode(.field_ptr, node, zir.Inst.Field{ | ||
| 1830 | .lhs = try expr(mod, scope, .ref, object_node), | ||
| 1831 | .field_name_start = str_index, | ||
| 1832 | }), | ||
| 1833 | else => return rvalue(mod, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{ | ||
| 1834 | .lhs = try expr(mod, scope, .none, object_node), | ||
| 1835 | .field_name_start = str_index, | ||
| 1836 | }), node), | ||
| 1837 | } | ||
| 1838 | } | ||
| 1839 | |||
| 1840 | fn arrayAccess( | ||
| 1841 | mod: *Module, | ||
| 1842 | scope: *Scope, | ||
| 1843 | rl: ResultLoc, | ||
| 1844 | node: ast.Node.Index, | ||
| 1845 | ) InnerError!zir.Inst.Ref { | ||
| 1846 | const gz = scope.getGenZir(); | ||
| 1847 | const tree = gz.tree(); | ||
| 1848 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1849 | const node_datas = tree.nodes.items(.data); | ||
| 1850 | switch (rl) { | ||
| 1851 | .ref => return gz.addBin( | ||
| 1852 | .elem_ptr, | ||
| 1853 | try expr(mod, scope, .ref, node_datas[node].lhs), | ||
| 1854 | try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs), | ||
| 1855 | ), | ||
| 1856 | else => return rvalue(mod, scope, rl, try gz.addBin( | ||
| 1857 | .elem_val, | ||
| 1858 | try expr(mod, scope, .none, node_datas[node].lhs), | ||
| 1859 | try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs), | ||
| 1860 | ), node), | ||
| 1861 | } | ||
| 1862 | } | ||
| 1863 | |||
| 1864 | fn simpleBinOp( | ||
| 1865 | mod: *Module, | ||
| 1866 | scope: *Scope, | ||
| 1867 | rl: ResultLoc, | ||
| 1868 | node: ast.Node.Index, | ||
| 1869 | op_inst_tag: zir.Inst.Tag, | ||
| 1870 | ) InnerError!zir.Inst.Ref { | ||
| 1871 | const gz = scope.getGenZir(); | ||
| 1872 | const tree = gz.tree(); | ||
| 1873 | const node_datas = tree.nodes.items(.data); | ||
| 1874 | |||
| 1875 | const result = try gz.addPlNode(op_inst_tag, node, zir.Inst.Bin{ | ||
| 1876 | .lhs = try expr(mod, scope, .none, node_datas[node].lhs), | ||
| 1877 | .rhs = try expr(mod, scope, .none, node_datas[node].rhs), | ||
| 1878 | }); | ||
| 1879 | return rvalue(mod, scope, rl, result, node); | ||
| 1880 | } | ||
| 1881 | |||
| 1882 | fn simpleStrTok( | ||
| 1883 | mod: *Module, | ||
| 1884 | scope: *Scope, | ||
| 1885 | rl: ResultLoc, | ||
| 1886 | ident_token: ast.TokenIndex, | ||
| 1887 | node: ast.Node.Index, | ||
| 1888 | op_inst_tag: zir.Inst.Tag, | ||
| 1889 | ) InnerError!zir.Inst.Ref { | ||
| 1890 | const gz = scope.getGenZir(); | ||
| 1891 | const string_bytes = &gz.zir_code.string_bytes; | ||
| 1892 | const str_index = @intCast(u32, string_bytes.items.len); | ||
| 1893 | try mod.appendIdentStr(scope, ident_token, string_bytes); | ||
| 1894 | try string_bytes.append(mod.gpa, 0); | ||
| 1895 | const result = try gz.addStrTok(op_inst_tag, str_index, ident_token); | ||
| 1896 | return rvalue(mod, scope, rl, result, node); | ||
| 1897 | } | ||
| 1898 | |||
| 1899 | fn boolBinOp( | ||
| 1900 | mod: *Module, | ||
| 1901 | scope: *Scope, | ||
| 1902 | rl: ResultLoc, | ||
| 1903 | node: ast.Node.Index, | ||
| 1904 | zir_tag: zir.Inst.Tag, | ||
| 1905 | ) InnerError!zir.Inst.Ref { | ||
| 1906 | const gz = scope.getGenZir(); | ||
| 1907 | const node_datas = gz.tree().nodes.items(.data); | ||
| 1908 | |||
| 1909 | const lhs = try expr(mod, scope, .{ .ty = .bool_type }, node_datas[node].lhs); | ||
| 1910 | const bool_br = try gz.addBoolBr(zir_tag, lhs); | ||
| 1911 | |||
| 1912 | var rhs_scope: Scope.GenZir = .{ | ||
| 1913 | .parent = scope, | ||
| 1914 | .zir_code = gz.zir_code, | ||
| 1915 | .force_comptime = gz.force_comptime, | ||
| 1916 | }; | ||
| 1917 | defer rhs_scope.instructions.deinit(mod.gpa); | ||
| 1918 | const rhs = try expr(mod, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs); | ||
| 1919 | _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs); | ||
| 1920 | try rhs_scope.setBoolBrBody(bool_br); | ||
| 1921 | |||
| 1922 | const block_ref = gz.zir_code.indexToRef(bool_br); | ||
| 1923 | return rvalue(mod, scope, rl, block_ref, node); | ||
| 1924 | } | ||
| 1925 | |||
| 1926 | fn ifExpr( | ||
| 1927 | mod: *Module, | ||
| 1928 | scope: *Scope, | ||
| 1929 | rl: ResultLoc, | ||
| 1930 | node: ast.Node.Index, | ||
| 1931 | if_full: ast.full.If, | ||
| 1932 | ) InnerError!zir.Inst.Ref { | ||
| 1933 | const parent_gz = scope.getGenZir(); | ||
| 1934 | var block_scope: Scope.GenZir = .{ | ||
| 1935 | .parent = scope, | ||
| 1936 | .zir_code = parent_gz.zir_code, | ||
| 1937 | .force_comptime = parent_gz.force_comptime, | ||
| 1938 | .instructions = .{}, | ||
| 1939 | }; | ||
| 1940 | setBlockResultLoc(&block_scope, rl); | ||
| 1941 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 1942 | |||
| 1943 | const cond = c: { | ||
| 1944 | // TODO https://github.com/ziglang/zig/issues/7929 | ||
| 1945 | if (if_full.error_token) |error_token| { | ||
| 1946 | return mod.failTok(scope, error_token, "TODO implement if error union", .{}); | ||
| 1947 | } else if (if_full.payload_token) |payload_token| { | ||
| 1948 | return mod.failTok(scope, payload_token, "TODO implement if optional", .{}); | ||
| 1949 | } else { | ||
| 1950 | break :c try expr(mod, &block_scope.base, .{ .ty = .bool_type }, if_full.ast.cond_expr); | ||
| 1951 | } | ||
| 1952 | }; | ||
| 1953 | |||
| 1954 | const condbr = try block_scope.addCondBr(.condbr, node); | ||
| 1955 | |||
| 1956 | const block = try parent_gz.addBlock(.block, node); | ||
| 1957 | try parent_gz.instructions.append(mod.gpa, block); | ||
| 1958 | try block_scope.setBlockBody(block); | ||
| 1959 | |||
| 1960 | var then_scope: Scope.GenZir = .{ | ||
| 1961 | .parent = scope, | ||
| 1962 | .zir_code = parent_gz.zir_code, | ||
| 1963 | .force_comptime = block_scope.force_comptime, | ||
| 1964 | .instructions = .{}, | ||
| 1965 | }; | ||
| 1966 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 1967 | |||
| 1968 | // declare payload to the then_scope | ||
| 1969 | const then_sub_scope = &then_scope.base; | ||
| 1970 | |||
| 1971 | block_scope.break_count += 1; | ||
| 1972 | const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr); | ||
| 1973 | // We hold off on the break instructions as well as copying the then/else | ||
| 1974 | // instructions into place until we know whether to keep store_to_block_ptr | ||
| 1975 | // instructions or not. | ||
| 1976 | |||
| 1977 | var else_scope: Scope.GenZir = .{ | ||
| 1978 | .parent = scope, | ||
| 1979 | .zir_code = parent_gz.zir_code, | ||
| 1980 | .force_comptime = block_scope.force_comptime, | ||
| 1981 | .instructions = .{}, | ||
| 1982 | }; | ||
| 1983 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 1984 | |||
| 1985 | const else_node = if_full.ast.else_expr; | ||
| 1986 | const else_info: struct { | ||
| 1987 | src: ast.Node.Index, | ||
| 1988 | result: zir.Inst.Ref, | ||
| 1989 | } = if (else_node != 0) blk: { | ||
| 1990 | block_scope.break_count += 1; | ||
| 1991 | const sub_scope = &else_scope.base; | ||
| 1992 | break :blk .{ | ||
| 1993 | .src = else_node, | ||
| 1994 | .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node), | ||
| 1995 | }; | ||
| 1996 | } else .{ | ||
| 1997 | .src = if_full.ast.then_expr, | ||
| 1998 | .result = .none, | ||
| 1999 | }; | ||
| 2000 | |||
| 2001 | return finishThenElseBlock( | ||
| 2002 | mod, | ||
| 2003 | scope, | ||
| 2004 | rl, | ||
| 2005 | node, | ||
| 2006 | &block_scope, | ||
| 2007 | &then_scope, | ||
| 2008 | &else_scope, | ||
| 2009 | condbr, | ||
| 2010 | cond, | ||
| 2011 | if_full.ast.then_expr, | ||
| 2012 | else_info.src, | ||
| 2013 | then_result, | ||
| 2014 | else_info.result, | ||
| 2015 | block, | ||
| 2016 | block, | ||
| 2017 | .@"break", | ||
| 2018 | ); | ||
| 2019 | } | ||
| 2020 | |||
| 2021 | fn setCondBrPayload( | ||
| 2022 | condbr: zir.Inst.Index, | ||
| 2023 | cond: zir.Inst.Ref, | ||
| 2024 | then_scope: *Scope.GenZir, | ||
| 2025 | else_scope: *Scope.GenZir, | ||
| 2026 | ) !void { | ||
| 2027 | const wzc = then_scope.zir_code; | ||
| 2028 | |||
| 2029 | try wzc.extra.ensureCapacity(wzc.gpa, wzc.extra.items.len + | ||
| 2030 | @typeInfo(zir.Inst.CondBr).Struct.fields.len + | ||
| 2031 | then_scope.instructions.items.len + else_scope.instructions.items.len); | ||
| 2032 | |||
| 2033 | const zir_datas = wzc.instructions.items(.data); | ||
| 2034 | zir_datas[condbr].pl_node.payload_index = wzc.addExtraAssumeCapacity(zir.Inst.CondBr{ | ||
| 2035 | .condition = cond, | ||
| 2036 | .then_body_len = @intCast(u32, then_scope.instructions.items.len), | ||
| 2037 | .else_body_len = @intCast(u32, else_scope.instructions.items.len), | ||
| 2038 | }); | ||
| 2039 | wzc.extra.appendSliceAssumeCapacity(then_scope.instructions.items); | ||
| 2040 | wzc.extra.appendSliceAssumeCapacity(else_scope.instructions.items); | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | /// If `elide_block_store_ptr` is set, expects to find exactly 1 .store_to_block_ptr instruction. | ||
| 2044 | fn setCondBrPayloadElideBlockStorePtr( | ||
| 2045 | condbr: zir.Inst.Index, | ||
| 2046 | cond: zir.Inst.Ref, | ||
| 2047 | then_scope: *Scope.GenZir, | ||
| 2048 | else_scope: *Scope.GenZir, | ||
| 2049 | ) !void { | ||
| 2050 | const wzc = then_scope.zir_code; | ||
| 2051 | |||
| 2052 | try wzc.extra.ensureCapacity(wzc.gpa, wzc.extra.items.len + | ||
| 2053 | @typeInfo(zir.Inst.CondBr).Struct.fields.len + | ||
| 2054 | then_scope.instructions.items.len + else_scope.instructions.items.len - 2); | ||
| 2055 | |||
| 2056 | const zir_datas = wzc.instructions.items(.data); | ||
| 2057 | zir_datas[condbr].pl_node.payload_index = wzc.addExtraAssumeCapacity(zir.Inst.CondBr{ | ||
| 2058 | .condition = cond, | ||
| 2059 | .then_body_len = @intCast(u32, then_scope.instructions.items.len - 1), | ||
| 2060 | .else_body_len = @intCast(u32, else_scope.instructions.items.len - 1), | ||
| 2061 | }); | ||
| 2062 | |||
| 2063 | const zir_tags = wzc.instructions.items(.tag); | ||
| 2064 | for ([_]*Scope.GenZir{ then_scope, else_scope }) |scope| { | ||
| 2065 | for (scope.instructions.items) |src_inst| { | ||
| 2066 | if (zir_tags[src_inst] != .store_to_block_ptr) { | ||
| 2067 | wzc.extra.appendAssumeCapacity(src_inst); | ||
| 2068 | } | ||
| 2069 | } | ||
| 2070 | } | ||
| 2071 | } | ||
| 2072 | |||
| 2073 | fn whileExpr( | ||
| 2074 | mod: *Module, | ||
| 2075 | scope: *Scope, | ||
| 2076 | rl: ResultLoc, | ||
| 2077 | node: ast.Node.Index, | ||
| 2078 | while_full: ast.full.While, | ||
| 2079 | ) InnerError!zir.Inst.Ref { | ||
| 2080 | if (while_full.label_token) |label_token| { | ||
| 2081 | try checkLabelRedefinition(mod, scope, label_token); | ||
| 2082 | } | ||
| 2083 | const parent_gz = scope.getGenZir(); | ||
| 2084 | const is_inline = parent_gz.force_comptime or while_full.inline_token != null; | ||
| 2085 | const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop; | ||
| 2086 | const loop_block = try parent_gz.addBlock(loop_tag, node); | ||
| 2087 | try parent_gz.instructions.append(mod.gpa, loop_block); | ||
| 2088 | |||
| 2089 | var loop_scope: Scope.GenZir = .{ | ||
| 2090 | .parent = scope, | ||
| 2091 | .zir_code = parent_gz.zir_code, | ||
| 2092 | .force_comptime = parent_gz.force_comptime, | ||
| 2093 | .instructions = .{}, | ||
| 2094 | }; | ||
| 2095 | setBlockResultLoc(&loop_scope, rl); | ||
| 2096 | defer loop_scope.instructions.deinit(mod.gpa); | ||
| 2097 | |||
| 2098 | var continue_scope: Scope.GenZir = .{ | ||
| 2099 | .parent = &loop_scope.base, | ||
| 2100 | .zir_code = parent_gz.zir_code, | ||
| 2101 | .force_comptime = loop_scope.force_comptime, | ||
| 2102 | .instructions = .{}, | ||
| 2103 | }; | ||
| 2104 | defer continue_scope.instructions.deinit(mod.gpa); | ||
| 2105 | |||
| 2106 | const cond = c: { | ||
| 2107 | // TODO https://github.com/ziglang/zig/issues/7929 | ||
| 2108 | if (while_full.error_token) |error_token| { | ||
| 2109 | return mod.failTok(scope, error_token, "TODO implement while error union", .{}); | ||
| 2110 | } else if (while_full.payload_token) |payload_token| { | ||
| 2111 | return mod.failTok(scope, payload_token, "TODO implement while optional", .{}); | ||
| 2112 | } else { | ||
| 2113 | const bool_type_rl: ResultLoc = .{ .ty = .bool_type }; | ||
| 2114 | break :c try expr(mod, &continue_scope.base, bool_type_rl, while_full.ast.cond_expr); | ||
| 2115 | } | ||
| 2116 | }; | ||
| 2117 | |||
| 2118 | const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; | ||
| 2119 | const condbr = try continue_scope.addCondBr(condbr_tag, node); | ||
| 2120 | const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block; | ||
| 2121 | const cond_block = try loop_scope.addBlock(block_tag, node); | ||
| 2122 | try loop_scope.instructions.append(mod.gpa, cond_block); | ||
| 2123 | try continue_scope.setBlockBody(cond_block); | ||
| 2124 | |||
| 2125 | // TODO avoid emitting the continue expr when there | ||
| 2126 | // are no jumps to it. This happens when the last statement of a while body is noreturn | ||
| 2127 | // and there are no `continue` statements. | ||
| 2128 | if (while_full.ast.cont_expr != 0) { | ||
| 2129 | _ = try expr(mod, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr); | ||
| 2130 | } | ||
| 2131 | const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; | ||
| 2132 | _ = try loop_scope.addNode(repeat_tag, node); | ||
| 2133 | |||
| 2134 | try loop_scope.setBlockBody(loop_block); | ||
| 2135 | loop_scope.break_block = loop_block; | ||
| 2136 | loop_scope.continue_block = cond_block; | ||
| 2137 | if (while_full.label_token) |label_token| { | ||
| 2138 | loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{ | ||
| 2139 | .token = label_token, | ||
| 2140 | .block_inst = loop_block, | ||
| 2141 | }); | ||
| 2142 | } | ||
| 2143 | |||
| 2144 | var then_scope: Scope.GenZir = .{ | ||
| 2145 | .parent = &continue_scope.base, | ||
| 2146 | .zir_code = parent_gz.zir_code, | ||
| 2147 | .force_comptime = continue_scope.force_comptime, | ||
| 2148 | .instructions = .{}, | ||
| 2149 | }; | ||
| 2150 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 2151 | |||
| 2152 | const then_sub_scope = &then_scope.base; | ||
| 2153 | |||
| 2154 | loop_scope.break_count += 1; | ||
| 2155 | const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr); | ||
| 2156 | |||
| 2157 | var else_scope: Scope.GenZir = .{ | ||
| 2158 | .parent = &continue_scope.base, | ||
| 2159 | .zir_code = parent_gz.zir_code, | ||
| 2160 | .force_comptime = continue_scope.force_comptime, | ||
| 2161 | .instructions = .{}, | ||
| 2162 | }; | ||
| 2163 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2164 | |||
| 2165 | const else_node = while_full.ast.else_expr; | ||
| 2166 | const else_info: struct { | ||
| 2167 | src: ast.Node.Index, | ||
| 2168 | result: zir.Inst.Ref, | ||
| 2169 | } = if (else_node != 0) blk: { | ||
| 2170 | loop_scope.break_count += 1; | ||
| 2171 | const sub_scope = &else_scope.base; | ||
| 2172 | break :blk .{ | ||
| 2173 | .src = else_node, | ||
| 2174 | .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node), | ||
| 2175 | }; | ||
| 2176 | } else .{ | ||
| 2177 | .src = while_full.ast.then_expr, | ||
| 2178 | .result = .none, | ||
| 2179 | }; | ||
| 2180 | |||
| 2181 | if (loop_scope.label) |some| { | ||
| 2182 | if (!some.used) { | ||
| 2183 | return mod.failTok(scope, some.token, "unused while loop label", .{}); | ||
| 2184 | } | ||
| 2185 | } | ||
| 2186 | const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; | ||
| 2187 | return finishThenElseBlock( | ||
| 2188 | mod, | ||
| 2189 | scope, | ||
| 2190 | rl, | ||
| 2191 | node, | ||
| 2192 | &loop_scope, | ||
| 2193 | &then_scope, | ||
| 2194 | &else_scope, | ||
| 2195 | condbr, | ||
| 2196 | cond, | ||
| 2197 | while_full.ast.then_expr, | ||
| 2198 | else_info.src, | ||
| 2199 | then_result, | ||
| 2200 | else_info.result, | ||
| 2201 | loop_block, | ||
| 2202 | cond_block, | ||
| 2203 | break_tag, | ||
| 2204 | ); | ||
| 2205 | } | ||
| 2206 | |||
| 2207 | fn forExpr( | ||
| 2208 | mod: *Module, | ||
| 2209 | scope: *Scope, | ||
| 2210 | rl: ResultLoc, | ||
| 2211 | node: ast.Node.Index, | ||
| 2212 | for_full: ast.full.While, | ||
| 2213 | ) InnerError!zir.Inst.Ref { | ||
| 2214 | if (for_full.label_token) |label_token| { | ||
| 2215 | try checkLabelRedefinition(mod, scope, label_token); | ||
| 2216 | } | ||
| 2217 | // Set up variables and constants. | ||
| 2218 | const parent_gz = scope.getGenZir(); | ||
| 2219 | const is_inline = parent_gz.force_comptime or for_full.inline_token != null; | ||
| 2220 | const tree = parent_gz.tree(); | ||
| 2221 | const token_tags = tree.tokens.items(.tag); | ||
| 2222 | |||
| 2223 | const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr); | ||
| 2224 | const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr); | ||
| 2225 | |||
| 2226 | const index_ptr = blk: { | ||
| 2227 | const index_ptr = try parent_gz.addUnNode(.alloc, .usize_type, node); | ||
| 2228 | // initialize to zero | ||
| 2229 | _ = try parent_gz.addBin(.store, index_ptr, .zero_usize); | ||
| 2230 | break :blk index_ptr; | ||
| 2231 | }; | ||
| 2232 | |||
| 2233 | const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop; | ||
| 2234 | const loop_block = try parent_gz.addBlock(loop_tag, node); | ||
| 2235 | try parent_gz.instructions.append(mod.gpa, loop_block); | ||
| 2236 | |||
| 2237 | var loop_scope: Scope.GenZir = .{ | ||
| 2238 | .parent = scope, | ||
| 2239 | .zir_code = parent_gz.zir_code, | ||
| 2240 | .force_comptime = parent_gz.force_comptime, | ||
| 2241 | .instructions = .{}, | ||
| 2242 | }; | ||
| 2243 | setBlockResultLoc(&loop_scope, rl); | ||
| 2244 | defer loop_scope.instructions.deinit(mod.gpa); | ||
| 2245 | |||
| 2246 | var cond_scope: Scope.GenZir = .{ | ||
| 2247 | .parent = &loop_scope.base, | ||
| 2248 | .zir_code = parent_gz.zir_code, | ||
| 2249 | .force_comptime = loop_scope.force_comptime, | ||
| 2250 | .instructions = .{}, | ||
| 2251 | }; | ||
| 2252 | defer cond_scope.instructions.deinit(mod.gpa); | ||
| 2253 | |||
| 2254 | // check condition i < array_expr.len | ||
| 2255 | const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr); | ||
| 2256 | const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, zir.Inst.Bin{ | ||
| 2257 | .lhs = index, | ||
| 2258 | .rhs = len, | ||
| 2259 | }); | ||
| 2260 | |||
| 2261 | const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; | ||
| 2262 | const condbr = try cond_scope.addCondBr(condbr_tag, node); | ||
| 2263 | const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block; | ||
| 2264 | const cond_block = try loop_scope.addBlock(block_tag, node); | ||
| 2265 | try loop_scope.instructions.append(mod.gpa, cond_block); | ||
| 2266 | try cond_scope.setBlockBody(cond_block); | ||
| 2267 | |||
| 2268 | // Increment the index variable. | ||
| 2269 | const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr); | ||
| 2270 | const index_plus_one = try loop_scope.addPlNode(.add, node, zir.Inst.Bin{ | ||
| 2271 | .lhs = index_2, | ||
| 2272 | .rhs = .one_usize, | ||
| 2273 | }); | ||
| 2274 | _ = try loop_scope.addBin(.store, index_ptr, index_plus_one); | ||
| 2275 | const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; | ||
| 2276 | _ = try loop_scope.addNode(repeat_tag, node); | ||
| 2277 | |||
| 2278 | try loop_scope.setBlockBody(loop_block); | ||
| 2279 | loop_scope.break_block = loop_block; | ||
| 2280 | loop_scope.continue_block = cond_block; | ||
| 2281 | if (for_full.label_token) |label_token| { | ||
| 2282 | loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{ | ||
| 2283 | .token = label_token, | ||
| 2284 | .block_inst = loop_block, | ||
| 2285 | }); | ||
| 2286 | } | ||
| 2287 | |||
| 2288 | var then_scope: Scope.GenZir = .{ | ||
| 2289 | .parent = &cond_scope.base, | ||
| 2290 | .zir_code = parent_gz.zir_code, | ||
| 2291 | .force_comptime = cond_scope.force_comptime, | ||
| 2292 | .instructions = .{}, | ||
| 2293 | }; | ||
| 2294 | defer then_scope.instructions.deinit(mod.gpa); | ||
| 2295 | |||
| 2296 | var index_scope: Scope.LocalPtr = undefined; | ||
| 2297 | const then_sub_scope = blk: { | ||
| 2298 | const payload_token = for_full.payload_token.?; | ||
| 2299 | const ident = if (token_tags[payload_token] == .asterisk) | ||
| 2300 | payload_token + 1 | ||
| 2301 | else | ||
| 2302 | payload_token; | ||
| 2303 | const is_ptr = ident != payload_token; | ||
| 2304 | const value_name = tree.tokenSlice(ident); | ||
| 2305 | if (!mem.eql(u8, value_name, "_")) { | ||
| 2306 | return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{}); | ||
| 2307 | } else if (is_ptr) { | ||
| 2308 | return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{}); | ||
| 2309 | } | ||
| 2310 | |||
| 2311 | const index_token = if (token_tags[ident + 1] == .comma) | ||
| 2312 | ident + 2 | ||
| 2313 | else | ||
| 2314 | break :blk &then_scope.base; | ||
| 2315 | if (mem.eql(u8, tree.tokenSlice(index_token), "_")) { | ||
| 2316 | return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{}); | ||
| 2317 | } | ||
| 2318 | const index_name = try mod.identifierTokenString(&then_scope.base, index_token); | ||
| 2319 | index_scope = .{ | ||
| 2320 | .parent = &then_scope.base, | ||
| 2321 | .gen_zir = &then_scope, | ||
| 2322 | .name = index_name, | ||
| 2323 | .ptr = index_ptr, | ||
| 2324 | .src = parent_gz.tokSrcLoc(index_token), | ||
| 2325 | }; | ||
| 2326 | break :blk &index_scope.base; | ||
| 2327 | }; | ||
| 2328 | |||
| 2329 | loop_scope.break_count += 1; | ||
| 2330 | const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr); | ||
| 2331 | |||
| 2332 | var else_scope: Scope.GenZir = .{ | ||
| 2333 | .parent = &cond_scope.base, | ||
| 2334 | .zir_code = parent_gz.zir_code, | ||
| 2335 | .force_comptime = cond_scope.force_comptime, | ||
| 2336 | .instructions = .{}, | ||
| 2337 | }; | ||
| 2338 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2339 | |||
| 2340 | const else_node = for_full.ast.else_expr; | ||
| 2341 | const else_info: struct { | ||
| 2342 | src: ast.Node.Index, | ||
| 2343 | result: zir.Inst.Ref, | ||
| 2344 | } = if (else_node != 0) blk: { | ||
| 2345 | loop_scope.break_count += 1; | ||
| 2346 | const sub_scope = &else_scope.base; | ||
| 2347 | break :blk .{ | ||
| 2348 | .src = else_node, | ||
| 2349 | .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node), | ||
| 2350 | }; | ||
| 2351 | } else .{ | ||
| 2352 | .src = for_full.ast.then_expr, | ||
| 2353 | .result = .none, | ||
| 2354 | }; | ||
| 2355 | |||
| 2356 | if (loop_scope.label) |some| { | ||
| 2357 | if (!some.used) { | ||
| 2358 | return mod.failTok(scope, some.token, "unused for loop label", .{}); | ||
| 2359 | } | ||
| 2360 | } | ||
| 2361 | const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; | ||
| 2362 | return finishThenElseBlock( | ||
| 2363 | mod, | ||
| 2364 | scope, | ||
| 2365 | rl, | ||
| 2366 | node, | ||
| 2367 | &loop_scope, | ||
| 2368 | &then_scope, | ||
| 2369 | &else_scope, | ||
| 2370 | condbr, | ||
| 2371 | cond, | ||
| 2372 | for_full.ast.then_expr, | ||
| 2373 | else_info.src, | ||
| 2374 | then_result, | ||
| 2375 | else_info.result, | ||
| 2376 | loop_block, | ||
| 2377 | cond_block, | ||
| 2378 | break_tag, | ||
| 2379 | ); | ||
| 2380 | } | ||
| 2381 | |||
| 2382 | fn getRangeNode( | ||
| 2383 | node_tags: []const ast.Node.Tag, | ||
| 2384 | node_datas: []const ast.Node.Data, | ||
| 2385 | start_node: ast.Node.Index, | ||
| 2386 | ) ?ast.Node.Index { | ||
| 2387 | var node = start_node; | ||
| 2388 | while (true) { | ||
| 2389 | switch (node_tags[node]) { | ||
| 2390 | .switch_range => return node, | ||
| 2391 | .grouped_expression => node = node_datas[node].lhs, | ||
| 2392 | else => return null, | ||
| 2393 | } | ||
| 2394 | } | ||
| 2395 | } | ||
| 2396 | |||
| 2397 | fn switchExpr( | ||
| 2398 | mod: *Module, | ||
| 2399 | scope: *Scope, | ||
| 2400 | rl: ResultLoc, | ||
| 2401 | switch_node: ast.Node.Index, | ||
| 2402 | ) InnerError!zir.Inst.Ref { | ||
| 2403 | if (true) @panic("TODO update for zir-memory-layout"); | ||
| 2404 | const parent_gz = scope.getGenZir(); | ||
| 2405 | const tree = parent_gz.tree(); | ||
| 2406 | const node_datas = tree.nodes.items(.data); | ||
| 2407 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2408 | const token_tags = tree.tokens.items(.tag); | ||
| 2409 | const node_tags = tree.nodes.items(.tag); | ||
| 2410 | |||
| 2411 | const switch_token = main_tokens[switch_node]; | ||
| 2412 | const target_node = node_datas[switch_node].lhs; | ||
| 2413 | const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); | ||
| 2414 | const case_nodes = tree.extra_data[extra.start..extra.end]; | ||
| 2415 | |||
| 2416 | const switch_src = token_starts[switch_token]; | ||
| 2417 | |||
| 2418 | var block_scope: Scope.GenZir = .{ | ||
| 2419 | .parent = scope, | ||
| 2420 | .decl = scope.ownerDecl().?, | ||
| 2421 | .arena = scope.arena(), | ||
| 2422 | .force_comptime = parent_gz.force_comptime, | ||
| 2423 | .instructions = .{}, | ||
| 2424 | }; | ||
| 2425 | setBlockResultLoc(&block_scope, rl); | ||
| 2426 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 2427 | |||
| 2428 | var items = std.ArrayList(zir.Inst.Ref).init(mod.gpa); | ||
| 2429 | defer items.deinit(); | ||
| 2430 | |||
| 2431 | // First we gather all the switch items and check else/'_' prongs. | ||
| 2432 | var else_src: ?usize = null; | ||
| 2433 | var underscore_src: ?usize = null; | ||
| 2434 | var first_range: ?*zir.Inst = null; | ||
| 2435 | var simple_case_count: usize = 0; | ||
| 2436 | var any_payload_is_ref = false; | ||
| 2437 | for (case_nodes) |case_node| { | ||
| 2438 | const case = switch (node_tags[case_node]) { | ||
| 2439 | .switch_case_one => tree.switchCaseOne(case_node), | ||
| 2440 | .switch_case => tree.switchCase(case_node), | ||
| 2441 | else => unreachable, | ||
| 2442 | }; | ||
| 2443 | if (case.payload_token) |payload_token| { | ||
| 2444 | if (token_tags[payload_token] == .asterisk) { | ||
| 2445 | any_payload_is_ref = true; | ||
| 2446 | } | ||
| 2447 | } | ||
| 2448 | // Check for else/_ prong, those are handled last. | ||
| 2449 | if (case.ast.values.len == 0) { | ||
| 2450 | const case_src = token_starts[case.ast.arrow_token - 1]; | ||
| 2451 | if (else_src) |src| { | ||
| 2452 | const msg = msg: { | ||
| 2453 | const msg = try mod.errMsg( | ||
| 2454 | scope, | ||
| 2455 | case_src, | ||
| 2456 | "multiple else prongs in switch expression", | ||
| 2457 | .{}, | ||
| 2458 | ); | ||
| 2459 | errdefer msg.destroy(mod.gpa); | ||
| 2460 | try mod.errNote(scope, src, msg, "previous else prong is here", .{}); | ||
| 2461 | break :msg msg; | ||
| 2462 | }; | ||
| 2463 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 2464 | } | ||
| 2465 | else_src = case_src; | ||
| 2466 | continue; | ||
| 2467 | } else if (case.ast.values.len == 1 and | ||
| 2468 | node_tags[case.ast.values[0]] == .identifier and | ||
| 2469 | mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) | ||
| 2470 | { | ||
| 2471 | const case_src = token_starts[case.ast.arrow_token - 1]; | ||
| 2472 | if (underscore_src) |src| { | ||
| 2473 | const msg = msg: { | ||
| 2474 | const msg = try mod.errMsg( | ||
| 2475 | scope, | ||
| 2476 | case_src, | ||
| 2477 | "multiple '_' prongs in switch expression", | ||
| 2478 | .{}, | ||
| 2479 | ); | ||
| 2480 | errdefer msg.destroy(mod.gpa); | ||
| 2481 | try mod.errNote(scope, src, msg, "previous '_' prong is here", .{}); | ||
| 2482 | break :msg msg; | ||
| 2483 | }; | ||
| 2484 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 2485 | } | ||
| 2486 | underscore_src = case_src; | ||
| 2487 | continue; | ||
| 2488 | } | ||
| 2489 | |||
| 2490 | if (else_src) |some_else| { | ||
| 2491 | if (underscore_src) |some_underscore| { | ||
| 2492 | const msg = msg: { | ||
| 2493 | const msg = try mod.errMsg( | ||
| 2494 | scope, | ||
| 2495 | switch_src, | ||
| 2496 | "else and '_' prong in switch expression", | ||
| 2497 | .{}, | ||
| 2498 | ); | ||
| 2499 | errdefer msg.destroy(mod.gpa); | ||
| 2500 | try mod.errNote(scope, some_else, msg, "else prong is here", .{}); | ||
| 2501 | try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{}); | ||
| 2502 | break :msg msg; | ||
| 2503 | }; | ||
| 2504 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 2505 | } | ||
| 2506 | } | ||
| 2507 | |||
| 2508 | if (case.ast.values.len == 1 and | ||
| 2509 | getRangeNode(node_tags, node_datas, case.ast.values[0]) == null) | ||
| 2510 | { | ||
| 2511 | simple_case_count += 1; | ||
| 2512 | } | ||
| 2513 | |||
| 2514 | // Generate all the switch items as comptime expressions. | ||
| 2515 | for (case.ast.values) |item| { | ||
| 2516 | if (getRangeNode(node_tags, node_datas, item)) |range| { | ||
| 2517 | const start = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].lhs); | ||
| 2518 | const end = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].rhs); | ||
| 2519 | const range_src = token_starts[main_tokens[range]]; | ||
| 2520 | const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end); | ||
| 2521 | try items.append(range_inst); | ||
| 2522 | } else { | ||
| 2523 | const item_inst = try comptimeExpr(mod, &block_scope.base, .none, item); | ||
| 2524 | try items.append(item_inst); | ||
| 2525 | } | ||
| 2526 | } | ||
| 2527 | } | ||
| 2528 | |||
| 2529 | var special_prong: zir.Inst.SwitchBr.SpecialProng = .none; | ||
| 2530 | if (else_src != null) special_prong = .@"else"; | ||
| 2531 | if (underscore_src != null) special_prong = .underscore; | ||
| 2532 | var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count); | ||
| 2533 | |||
| 2534 | const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{ | ||
| 2535 | .rl = .ref, | ||
| 2536 | .tag = .switchbr_ref, | ||
| 2537 | } else .{ | ||
| 2538 | .rl = .none, | ||
| 2539 | .tag = .switchbr, | ||
| 2540 | }; | ||
| 2541 | const target = try expr(mod, &block_scope.base, rl_and_tag.rl, target_node); | ||
| 2542 | const switch_inst = try addZirInstT(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, rl_and_tag.tag, .{ | ||
| 2543 | .target = target, | ||
| 2544 | .cases = cases, | ||
| 2545 | .items = try block_scope.arena.dupe(zir.Inst.Ref, items.items), | ||
| 2546 | .else_body = undefined, // populated below | ||
| 2547 | .range = first_range, | ||
| 2548 | .special_prong = special_prong, | ||
| 2549 | }); | ||
| 2550 | const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ | ||
| 2551 | .instructions = try block_scope.arena.dupe(zir.Inst.Ref, block_scope.instructions.items), | ||
| 2552 | }); | ||
| 2553 | |||
| 2554 | var case_scope: Scope.GenZir = .{ | ||
| 2555 | .parent = scope, | ||
| 2556 | .decl = block_scope.decl, | ||
| 2557 | .arena = block_scope.arena, | ||
| 2558 | .force_comptime = block_scope.force_comptime, | ||
| 2559 | .instructions = .{}, | ||
| 2560 | }; | ||
| 2561 | defer case_scope.instructions.deinit(mod.gpa); | ||
| 2562 | |||
| 2563 | var else_scope: Scope.GenZir = .{ | ||
| 2564 | .parent = scope, | ||
| 2565 | .decl = case_scope.decl, | ||
| 2566 | .arena = case_scope.arena, | ||
| 2567 | .force_comptime = case_scope.force_comptime, | ||
| 2568 | .instructions = .{}, | ||
| 2569 | }; | ||
| 2570 | defer else_scope.instructions.deinit(mod.gpa); | ||
| 2571 | |||
| 2572 | // Now generate all but the special cases. | ||
| 2573 | var special_case: ?ast.full.SwitchCase = null; | ||
| 2574 | var items_index: usize = 0; | ||
| 2575 | var case_index: usize = 0; | ||
| 2576 | for (case_nodes) |case_node| { | ||
| 2577 | const case = switch (node_tags[case_node]) { | ||
| 2578 | .switch_case_one => tree.switchCaseOne(case_node), | ||
| 2579 | .switch_case => tree.switchCase(case_node), | ||
| 2580 | else => unreachable, | ||
| 2581 | }; | ||
| 2582 | const case_src = token_starts[main_tokens[case_node]]; | ||
| 2583 | case_scope.instructions.shrinkRetainingCapacity(0); | ||
| 2584 | |||
| 2585 | // Check for else/_ prong, those are handled last. | ||
| 2586 | if (case.ast.values.len == 0) { | ||
| 2587 | special_case = case; | ||
| 2588 | continue; | ||
| 2589 | } else if (case.ast.values.len == 1 and | ||
| 2590 | node_tags[case.ast.values[0]] == .identifier and | ||
| 2591 | mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) | ||
| 2592 | { | ||
| 2593 | special_case = case; | ||
| 2594 | continue; | ||
| 2595 | } | ||
| 2596 | |||
| 2597 | // If this is a simple one item prong then it is handled by the switchbr. | ||
| 2598 | if (case.ast.values.len == 1 and | ||
| 2599 | getRangeNode(node_tags, node_datas, case.ast.values[0]) == null) | ||
| 2600 | { | ||
| 2601 | const item = items.items[items_index]; | ||
| 2602 | items_index += 1; | ||
| 2603 | try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target); | ||
| 2604 | |||
| 2605 | cases[case_index] = .{ | ||
| 2606 | .item = item, | ||
| 2607 | .body = .{ .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items) }, | ||
| 2608 | }; | ||
| 2609 | case_index += 1; | ||
| 2610 | continue; | ||
| 2611 | } | ||
| 2612 | |||
| 2613 | // Check if the target matches any of the items. | ||
| 2614 | // 1, 2, 3..6 will result in | ||
| 2615 | // target == 1 or target == 2 or (target >= 3 and target <= 6) | ||
| 2616 | // TODO handle multiple items as switch prongs rather than along with ranges. | ||
| 2617 | var any_ok: ?*zir.Inst = null; | ||
| 2618 | for (case.ast.values) |item| { | ||
| 2619 | if (getRangeNode(node_tags, node_datas, item)) |range| { | ||
| 2620 | const range_src = token_starts[main_tokens[range]]; | ||
| 2621 | const range_inst = items.items[items_index].castTag(.switch_range).?; | ||
| 2622 | items_index += 1; | ||
| 2623 | |||
| 2624 | // target >= start and target <= end | ||
| 2625 | const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, range_inst.positionals.lhs); | ||
| 2626 | const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, range_inst.positionals.rhs); | ||
| 2627 | const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok); | ||
| 2628 | |||
| 2629 | if (any_ok) |some| { | ||
| 2630 | any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok); | ||
| 2631 | } else { | ||
| 2632 | any_ok = range_ok; | ||
| 2633 | } | ||
| 2634 | continue; | ||
| 2635 | } | ||
| 2636 | |||
| 2637 | const item_inst = items.items[items_index]; | ||
| 2638 | items_index += 1; | ||
| 2639 | const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst); | ||
| 2640 | |||
| 2641 | if (any_ok) |some| { | ||
| 2642 | any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok); | ||
| 2643 | } else { | ||
| 2644 | any_ok = cpm_ok; | ||
| 2645 | } | ||
| 2646 | } | ||
| 2647 | |||
| 2648 | const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{ | ||
| 2649 | .condition = any_ok.?, | ||
| 2650 | .then_body = undefined, // populated below | ||
| 2651 | .else_body = undefined, // populated below | ||
| 2652 | }, .{}); | ||
| 2653 | const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{ | ||
| 2654 | .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items), | ||
| 2655 | }); | ||
| 2656 | |||
| 2657 | // reset cond_scope for then_body | ||
| 2658 | case_scope.instructions.items.len = 0; | ||
| 2659 | try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target); | ||
| 2660 | condbr.positionals.then_body = .{ | ||
| 2661 | .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items), | ||
| 2662 | }; | ||
| 2663 | |||
| 2664 | // reset cond_scope for else_body | ||
| 2665 | case_scope.instructions.items.len = 0; | ||
| 2666 | _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{ | ||
| 2667 | .block = cond_block, | ||
| 2668 | }, .{}); | ||
| 2669 | condbr.positionals.else_body = .{ | ||
| 2670 | .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items), | ||
| 2671 | }; | ||
| 2672 | } | ||
| 2673 | |||
| 2674 | // Finally generate else block or a break. | ||
| 2675 | if (special_case) |case| { | ||
| 2676 | try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target); | ||
| 2677 | } else { | ||
| 2678 | // Not handling all possible cases is a compile error. | ||
| 2679 | _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe); | ||
| 2680 | } | ||
| 2681 | switch_inst.positionals.else_body = .{ | ||
| 2682 | .instructions = try block_scope.arena.dupe(zir.Inst.Ref, else_scope.instructions.items), | ||
| 2683 | }; | ||
| 2684 | |||
| 2685 | return &block.base; | ||
| 2686 | } | ||
| 2687 | |||
| 2688 | fn switchCaseExpr( | ||
| 2689 | mod: *Module, | ||
| 2690 | scope: *Scope, | ||
| 2691 | rl: ResultLoc, | ||
| 2692 | block: *zir.Inst.Block, | ||
| 2693 | case: ast.full.SwitchCase, | ||
| 2694 | target: zir.Inst.Ref, | ||
| 2695 | ) !void { | ||
| 2696 | const tree = scope.tree(); | ||
| 2697 | const node_datas = tree.nodes.items(.data); | ||
| 2698 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2699 | const token_tags = tree.tokens.items(.tag); | ||
| 2700 | |||
| 2701 | const case_src = token_starts[case.ast.arrow_token]; | ||
| 2702 | const sub_scope = blk: { | ||
| 2703 | const payload_token = case.payload_token orelse break :blk scope; | ||
| 2704 | const ident = if (token_tags[payload_token] == .asterisk) | ||
| 2705 | payload_token + 1 | ||
| 2706 | else | ||
| 2707 | payload_token; | ||
| 2708 | const is_ptr = ident != payload_token; | ||
| 2709 | const value_name = tree.tokenSlice(ident); | ||
| 2710 | if (mem.eql(u8, value_name, "_")) { | ||
| 2711 | if (is_ptr) { | ||
| 2712 | return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{}); | ||
| 2713 | } | ||
| 2714 | break :blk scope; | ||
| 2715 | } | ||
| 2716 | return mod.failTok(scope, ident, "TODO implement switch value payload", .{}); | ||
| 2717 | }; | ||
| 2718 | |||
| 2719 | const case_body = try expr(mod, sub_scope, rl, case.ast.target_expr); | ||
| 2720 | if (!case_body.tag.isNoReturn()) { | ||
| 2721 | _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{ | ||
| 2722 | .block = block, | ||
| 2723 | .operand = case_body, | ||
| 2724 | }, .{}); | ||
| 2725 | } | ||
| 2726 | } | ||
| 2727 | |||
| 2728 | fn ret(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { | ||
| 2729 | const tree = scope.tree(); | ||
| 2730 | const node_datas = tree.nodes.items(.data); | ||
| 2731 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2732 | |||
| 2733 | const operand_node = node_datas[node].lhs; | ||
| 2734 | const gz = scope.getGenZir(); | ||
| 2735 | const operand: zir.Inst.Ref = if (operand_node != 0) operand: { | ||
| 2736 | const rl: ResultLoc = if (nodeMayNeedMemoryLocation(scope, operand_node)) .{ | ||
| 2737 | .ptr = try gz.addNode(.ret_ptr, node), | ||
| 2738 | } else .{ | ||
| 2739 | .ty = try gz.addNode(.ret_type, node), | ||
| 2740 | }; | ||
| 2741 | break :operand try expr(mod, scope, rl, operand_node); | ||
| 2742 | } else .void_value; | ||
| 2743 | _ = try gz.addUnNode(.ret_node, operand, node); | ||
| 2744 | return zir.Inst.Ref.unreachable_value; | ||
| 2745 | } | ||
| 2746 | |||
| 2747 | fn identifier( | ||
| 2748 | mod: *Module, | ||
| 2749 | scope: *Scope, | ||
| 2750 | rl: ResultLoc, | ||
| 2751 | ident: ast.Node.Index, | ||
| 2752 | ) InnerError!zir.Inst.Ref { | ||
| 2753 | const tracy = trace(@src()); | ||
| 2754 | defer tracy.end(); | ||
| 2755 | |||
| 2756 | const tree = scope.tree(); | ||
| 2757 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2758 | |||
| 2759 | const gz = scope.getGenZir(); | ||
| 2760 | |||
| 2761 | const ident_token = main_tokens[ident]; | ||
| 2762 | const ident_name = try mod.identifierTokenString(scope, ident_token); | ||
| 2763 | if (mem.eql(u8, ident_name, "_")) { | ||
| 2764 | return mod.failNode(scope, ident, "TODO implement '_' identifier", .{}); | ||
| 2765 | } | ||
| 2766 | |||
| 2767 | if (simple_types.get(ident_name)) |zir_const_ref| { | ||
| 2768 | return rvalue(mod, scope, rl, zir_const_ref, ident); | ||
| 2769 | } | ||
| 2770 | |||
| 2771 | if (ident_name.len >= 2) integer: { | ||
| 2772 | const first_c = ident_name[0]; | ||
| 2773 | if (first_c == 'i' or first_c == 'u') { | ||
| 2774 | const signedness: std.builtin.Signedness = switch (first_c == 'i') { | ||
| 2775 | true => .signed, | ||
| 2776 | false => .unsigned, | ||
| 2777 | }; | ||
| 2778 | const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) { | ||
| 2779 | error.Overflow => return mod.failNode( | ||
| 2780 | scope, | ||
| 2781 | ident, | ||
| 2782 | "primitive integer type '{s}' exceeds maximum bit width of 65535", | ||
| 2783 | .{ident_name}, | ||
| 2784 | ), | ||
| 2785 | error.InvalidCharacter => break :integer, | ||
| 2786 | }; | ||
| 2787 | const result = try gz.add(.{ | ||
| 2788 | .tag = .int_type, | ||
| 2789 | .data = .{ .int_type = .{ | ||
| 2790 | .src_node = gz.zir_code.decl.nodeIndexToRelative(ident), | ||
| 2791 | .signedness = signedness, | ||
| 2792 | .bit_count = bit_count, | ||
| 2793 | } }, | ||
| 2794 | }); | ||
| 2795 | return rvalue(mod, scope, rl, result, ident); | ||
| 2796 | } | ||
| 2797 | } | ||
| 2798 | |||
| 2799 | // Local variables, including function parameters. | ||
| 2800 | { | ||
| 2801 | var s = scope; | ||
| 2802 | while (true) switch (s.tag) { | ||
| 2803 | .local_val => { | ||
| 2804 | const local_val = s.cast(Scope.LocalVal).?; | ||
| 2805 | if (mem.eql(u8, local_val.name, ident_name)) { | ||
| 2806 | return rvalue(mod, scope, rl, local_val.inst, ident); | ||
| 2807 | } | ||
| 2808 | s = local_val.parent; | ||
| 2809 | }, | ||
| 2810 | .local_ptr => { | ||
| 2811 | const local_ptr = s.cast(Scope.LocalPtr).?; | ||
| 2812 | if (mem.eql(u8, local_ptr.name, ident_name)) { | ||
| 2813 | if (rl == .ref) return local_ptr.ptr; | ||
| 2814 | const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident); | ||
| 2815 | return rvalue(mod, scope, rl, loaded, ident); | ||
| 2816 | } | ||
| 2817 | s = local_ptr.parent; | ||
| 2818 | }, | ||
| 2819 | .gen_zir => s = s.cast(Scope.GenZir).?.parent, | ||
| 2820 | else => break, | ||
| 2821 | }; | ||
| 2822 | } | ||
| 2823 | |||
| 2824 | const gop = try gz.zir_code.decl_map.getOrPut(mod.gpa, ident_name); | ||
| 2825 | if (!gop.found_existing) { | ||
| 2826 | const decl = mod.lookupDeclName(scope, ident_name) orelse | ||
| 2827 | return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name}); | ||
| 2828 | try gz.zir_code.decls.append(mod.gpa, decl); | ||
| 2829 | } | ||
| 2830 | const decl_index = @intCast(u32, gop.index); | ||
| 2831 | switch (rl) { | ||
| 2832 | .ref => return gz.addDecl(.decl_ref, decl_index, ident), | ||
| 2833 | else => return rvalue(mod, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident), | ||
| 2834 | } | ||
| 2835 | } | ||
| 2836 | |||
| 2837 | fn stringLiteral( | ||
| 2838 | mod: *Module, | ||
| 2839 | scope: *Scope, | ||
| 2840 | rl: ResultLoc, | ||
| 2841 | node: ast.Node.Index, | ||
| 2842 | ) InnerError!zir.Inst.Ref { | ||
| 2843 | const tree = scope.tree(); | ||
| 2844 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2845 | const gz = scope.getGenZir(); | ||
| 2846 | const string_bytes = &gz.zir_code.string_bytes; | ||
| 2847 | const str_index = string_bytes.items.len; | ||
| 2848 | const str_lit_token = main_tokens[node]; | ||
| 2849 | const token_bytes = tree.tokenSlice(str_lit_token); | ||
| 2850 | try mod.parseStrLit(scope, str_lit_token, string_bytes, token_bytes, 0); | ||
| 2851 | const str_len = string_bytes.items.len - str_index; | ||
| 2852 | const result = try gz.add(.{ | ||
| 2853 | .tag = .str, | ||
| 2854 | .data = .{ .str = .{ | ||
| 2855 | .start = @intCast(u32, str_index), | ||
| 2856 | .len = @intCast(u32, str_len), | ||
| 2857 | } }, | ||
| 2858 | }); | ||
| 2859 | return rvalue(mod, scope, rl, result, node); | ||
| 2860 | } | ||
| 2861 | |||
| 2862 | fn multilineStringLiteral( | ||
| 2863 | mod: *Module, | ||
| 2864 | scope: *Scope, | ||
| 2865 | rl: ResultLoc, | ||
| 2866 | node: ast.Node.Index, | ||
| 2867 | ) InnerError!zir.Inst.Ref { | ||
| 2868 | const gz = scope.getGenZir(); | ||
| 2869 | const tree = gz.tree(); | ||
| 2870 | const node_datas = tree.nodes.items(.data); | ||
| 2871 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2872 | |||
| 2873 | const start = node_datas[node].lhs; | ||
| 2874 | const end = node_datas[node].rhs; | ||
| 2875 | const string_bytes = &gz.zir_code.string_bytes; | ||
| 2876 | const str_index = string_bytes.items.len; | ||
| 2877 | |||
| 2878 | // First line: do not append a newline. | ||
| 2879 | var tok_i = start; | ||
| 2880 | { | ||
| 2881 | const slice = tree.tokenSlice(tok_i); | ||
| 2882 | const line_bytes = slice[2 .. slice.len - 1]; | ||
| 2883 | try string_bytes.appendSlice(mod.gpa, line_bytes); | ||
| 2884 | tok_i += 1; | ||
| 2885 | } | ||
| 2886 | // Following lines: each line prepends a newline. | ||
| 2887 | while (tok_i <= end) : (tok_i += 1) { | ||
| 2888 | const slice = tree.tokenSlice(tok_i); | ||
| 2889 | const line_bytes = slice[2 .. slice.len - 1]; | ||
| 2890 | try string_bytes.ensureCapacity(mod.gpa, string_bytes.items.len + line_bytes.len + 1); | ||
| 2891 | string_bytes.appendAssumeCapacity('\n'); | ||
| 2892 | string_bytes.appendSliceAssumeCapacity(line_bytes); | ||
| 2893 | } | ||
| 2894 | const result = try gz.add(.{ | ||
| 2895 | .tag = .str, | ||
| 2896 | .data = .{ .str = .{ | ||
| 2897 | .start = @intCast(u32, str_index), | ||
| 2898 | .len = @intCast(u32, string_bytes.items.len - str_index), | ||
| 2899 | } }, | ||
| 2900 | }); | ||
| 2901 | return rvalue(mod, scope, rl, result, node); | ||
| 2902 | } | ||
| 2903 | |||
| 2904 | fn charLiteral(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { | ||
| 2905 | const gz = scope.getGenZir(); | ||
| 2906 | const tree = gz.tree(); | ||
| 2907 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2908 | const main_token = main_tokens[node]; | ||
| 2909 | const slice = tree.tokenSlice(main_token); | ||
| 2910 | |||
| 2911 | var bad_index: usize = undefined; | ||
| 2912 | const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) { | ||
| 2913 | error.InvalidCharacter => { | ||
| 2914 | const bad_byte = slice[bad_index]; | ||
| 2915 | const token_starts = tree.tokens.items(.start); | ||
| 2916 | const src_off = @intCast(u32, token_starts[main_token] + bad_index); | ||
| 2917 | return mod.failOff(scope, src_off, "invalid character: '{c}'\n", .{bad_byte}); | ||
| 2918 | }, | ||
| 2919 | }; | ||
| 2920 | const result = try gz.addInt(value); | ||
| 2921 | return rvalue(mod, scope, rl, result, node); | ||
| 2922 | } | ||
| 2923 | |||
| 2924 | fn integerLiteral( | ||
| 2925 | mod: *Module, | ||
| 2926 | scope: *Scope, | ||
| 2927 | rl: ResultLoc, | ||
| 2928 | node: ast.Node.Index, | ||
| 2929 | ) InnerError!zir.Inst.Ref { | ||
| 2930 | const tree = scope.tree(); | ||
| 2931 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2932 | const int_token = main_tokens[node]; | ||
| 2933 | const prefixed_bytes = tree.tokenSlice(int_token); | ||
| 2934 | const gz = scope.getGenZir(); | ||
| 2935 | if (std.fmt.parseInt(u64, prefixed_bytes, 0)) |small_int| { | ||
| 2936 | const result: zir.Inst.Ref = switch (small_int) { | ||
| 2937 | 0 => .zero, | ||
| 2938 | 1 => .one, | ||
| 2939 | else => try gz.addInt(small_int), | ||
| 2940 | }; | ||
| 2941 | return rvalue(mod, scope, rl, result, node); | ||
| 2942 | } else |err| { | ||
| 2943 | return mod.failNode(scope, node, "TODO implement int literals that don't fit in a u64", .{}); | ||
| 2944 | } | ||
| 2945 | } | ||
| 2946 | |||
| 2947 | fn floatLiteral( | ||
| 2948 | mod: *Module, | ||
| 2949 | scope: *Scope, | ||
| 2950 | rl: ResultLoc, | ||
| 2951 | node: ast.Node.Index, | ||
| 2952 | ) InnerError!zir.Inst.Ref { | ||
| 2953 | const arena = scope.arena(); | ||
| 2954 | const tree = scope.tree(); | ||
| 2955 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2956 | const gz = scope.getGenZir(); | ||
| 2957 | |||
| 2958 | const main_token = main_tokens[node]; | ||
| 2959 | const bytes = tree.tokenSlice(main_token); | ||
| 2960 | if (bytes.len > 2 and bytes[1] == 'x') { | ||
| 2961 | assert(bytes[0] == '0'); // validated by tokenizer | ||
| 2962 | return mod.failTok(scope, main_token, "TODO implement hex floats", .{}); | ||
| 2963 | } | ||
| 2964 | const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) { | ||
| 2965 | error.InvalidCharacter => unreachable, // validated by tokenizer | ||
| 2966 | }; | ||
| 2967 | const typed_value = try arena.create(TypedValue); | ||
| 2968 | typed_value.* = .{ | ||
| 2969 | .ty = Type.initTag(.comptime_float), | ||
| 2970 | .val = try Value.Tag.float_128.create(arena, float_number), | ||
| 2971 | }; | ||
| 2972 | const result = try gz.add(.{ | ||
| 2973 | .tag = .@"const", | ||
| 2974 | .data = .{ .@"const" = typed_value }, | ||
| 2975 | }); | ||
| 2976 | return rvalue(mod, scope, rl, result, node); | ||
| 2977 | } | ||
| 2978 | |||
| 2979 | fn asmExpr( | ||
| 2980 | mod: *Module, | ||
| 2981 | scope: *Scope, | ||
| 2982 | rl: ResultLoc, | ||
| 2983 | node: ast.Node.Index, | ||
| 2984 | full: ast.full.Asm, | ||
| 2985 | ) InnerError!zir.Inst.Ref { | ||
| 2986 | const arena = scope.arena(); | ||
| 2987 | const tree = scope.tree(); | ||
| 2988 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2989 | const node_datas = tree.nodes.items(.data); | ||
| 2990 | const gz = scope.getGenZir(); | ||
| 2991 | |||
| 2992 | const asm_source = try expr(mod, scope, .{ .ty = .const_slice_u8_type }, full.ast.template); | ||
| 2993 | |||
| 2994 | if (full.outputs.len != 0) { | ||
| 2995 | return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{}); | ||
| 2996 | } | ||
| 2997 | |||
| 2998 | const constraints = try arena.alloc(u32, full.inputs.len); | ||
| 2999 | const args = try arena.alloc(zir.Inst.Ref, full.inputs.len); | ||
| 3000 | |||
| 3001 | for (full.inputs) |input, i| { | ||
| 3002 | const constraint_token = main_tokens[input] + 2; | ||
| 3003 | const string_bytes = &gz.zir_code.string_bytes; | ||
| 3004 | constraints[i] = @intCast(u32, string_bytes.items.len); | ||
| 3005 | const token_bytes = tree.tokenSlice(constraint_token); | ||
| 3006 | try mod.parseStrLit(scope, constraint_token, string_bytes, token_bytes, 0); | ||
| 3007 | try string_bytes.append(mod.gpa, 0); | ||
| 3008 | |||
| 3009 | args[i] = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[input].lhs); | ||
| 3010 | } | ||
| 3011 | |||
| 3012 | const tag: zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm"; | ||
| 3013 | const result = try gz.addPlNode(tag, node, zir.Inst.Asm{ | ||
| 3014 | .asm_source = asm_source, | ||
| 3015 | .return_type = .void_type, | ||
| 3016 | .output = .none, | ||
| 3017 | .args_len = @intCast(u32, full.inputs.len), | ||
| 3018 | .clobbers_len = 0, // TODO implement asm clobbers | ||
| 3019 | }); | ||
| 3020 | |||
| 3021 | try gz.zir_code.extra.ensureCapacity(mod.gpa, gz.zir_code.extra.items.len + | ||
| 3022 | args.len + constraints.len); | ||
| 3023 | gz.zir_code.appendRefsAssumeCapacity(args); | ||
| 3024 | gz.zir_code.extra.appendSliceAssumeCapacity(constraints); | ||
| 3025 | |||
| 3026 | return rvalue(mod, scope, rl, result, node); | ||
| 3027 | } | ||
| 3028 | |||
| 3029 | fn as( | ||
| 3030 | mod: *Module, | ||
| 3031 | scope: *Scope, | ||
| 3032 | rl: ResultLoc, | ||
| 3033 | builtin_token: ast.TokenIndex, | ||
| 3034 | node: ast.Node.Index, | ||
| 3035 | lhs: ast.Node.Index, | ||
| 3036 | rhs: ast.Node.Index, | ||
| 3037 | ) InnerError!zir.Inst.Ref { | ||
| 3038 | const dest_type = try typeExpr(mod, scope, lhs); | ||
| 3039 | switch (rl) { | ||
| 3040 | .none, .discard, .ref, .ty => { | ||
| 3041 | const result = try expr(mod, scope, .{ .ty = dest_type }, rhs); | ||
| 3042 | return rvalue(mod, scope, rl, result, node); | ||
| 3043 | }, | ||
| 3044 | |||
| 3045 | .ptr => |result_ptr| { | ||
| 3046 | return asRlPtr(mod, scope, rl, result_ptr, rhs, dest_type); | ||
| 3047 | }, | ||
| 3048 | .block_ptr => |block_scope| { | ||
| 3049 | return asRlPtr(mod, scope, rl, block_scope.rl_ptr, rhs, dest_type); | ||
| 3050 | }, | ||
| 3051 | |||
| 3052 | .bitcasted_ptr => |bitcasted_ptr| { | ||
| 3053 | // TODO here we should be able to resolve the inference; we now have a type for the result. | ||
| 3054 | return mod.failTok(scope, builtin_token, "TODO implement @as with result location @bitCast", .{}); | ||
| 3055 | }, | ||
| 3056 | .inferred_ptr => |result_alloc| { | ||
| 3057 | // TODO here we should be able to resolve the inference; we now have a type for the result. | ||
| 3058 | return mod.failTok(scope, builtin_token, "TODO implement @as with inferred-type result location pointer", .{}); | ||
| 3059 | }, | ||
| 3060 | } | ||
| 3061 | } | ||
| 3062 | |||
| 3063 | fn asRlPtr( | ||
| 3064 | mod: *Module, | ||
| 3065 | scope: *Scope, | ||
| 3066 | rl: ResultLoc, | ||
| 3067 | result_ptr: zir.Inst.Ref, | ||
| 3068 | operand_node: ast.Node.Index, | ||
| 3069 | dest_type: zir.Inst.Ref, | ||
| 3070 | ) InnerError!zir.Inst.Ref { | ||
| 3071 | // Detect whether this expr() call goes into rvalue() to store the result into the | ||
| 3072 | // result location. If it does, elide the coerce_result_ptr instruction | ||
| 3073 | // as well as the store instruction, instead passing the result as an rvalue. | ||
| 3074 | const parent_gz = scope.getGenZir(); | ||
| 3075 | const wzc = parent_gz.zir_code; | ||
| 3076 | |||
| 3077 | var as_scope: Scope.GenZir = .{ | ||
| 3078 | .parent = scope, | ||
| 3079 | .zir_code = wzc, | ||
| 3080 | .force_comptime = parent_gz.force_comptime, | ||
| 3081 | .instructions = .{}, | ||
| 3082 | }; | ||
| 3083 | defer as_scope.instructions.deinit(mod.gpa); | ||
| 3084 | |||
| 3085 | as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); | ||
| 3086 | const result = try expr(mod, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node); | ||
| 3087 | const parent_zir = &parent_gz.instructions; | ||
| 3088 | if (as_scope.rvalue_rl_count == 1) { | ||
| 3089 | // Busted! This expression didn't actually need a pointer. | ||
| 3090 | const zir_tags = wzc.instructions.items(.tag); | ||
| 3091 | const zir_datas = wzc.instructions.items(.data); | ||
| 3092 | const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2; | ||
| 3093 | try parent_zir.ensureCapacity(mod.gpa, expected_len); | ||
| 3094 | for (as_scope.instructions.items) |src_inst| { | ||
| 3095 | if (wzc.indexToRef(src_inst) == as_scope.rl_ptr) continue; | ||
| 3096 | if (zir_tags[src_inst] == .store_to_block_ptr) { | ||
| 3097 | if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; | ||
| 3098 | } | ||
| 3099 | parent_zir.appendAssumeCapacity(src_inst); | ||
| 3100 | } | ||
| 3101 | assert(parent_zir.items.len == expected_len); | ||
| 3102 | const casted_result = try parent_gz.addBin(.as, dest_type, result); | ||
| 3103 | return rvalue(mod, scope, rl, casted_result, operand_node); | ||
| 3104 | } else { | ||
| 3105 | try parent_zir.appendSlice(mod.gpa, as_scope.instructions.items); | ||
| 3106 | return result; | ||
| 3107 | } | ||
| 3108 | } | ||
| 3109 | |||
| 3110 | fn bitCast( | ||
| 3111 | mod: *Module, | ||
| 3112 | scope: *Scope, | ||
| 3113 | rl: ResultLoc, | ||
| 3114 | builtin_token: ast.TokenIndex, | ||
| 3115 | node: ast.Node.Index, | ||
| 3116 | lhs: ast.Node.Index, | ||
| 3117 | rhs: ast.Node.Index, | ||
| 3118 | ) InnerError!zir.Inst.Ref { | ||
| 3119 | if (true) @panic("TODO update for zir-memory-layout"); | ||
| 3120 | const dest_type = try typeExpr(mod, scope, lhs); | ||
| 3121 | switch (rl) { | ||
| 3122 | .none => { | ||
| 3123 | const operand = try expr(mod, scope, .none, rhs); | ||
| 3124 | return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand); | ||
| 3125 | }, | ||
| 3126 | .discard => { | ||
| 3127 | const operand = try expr(mod, scope, .none, rhs); | ||
| 3128 | const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand); | ||
| 3129 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); | ||
| 3130 | return result; | ||
| 3131 | }, | ||
| 3132 | .ref => { | ||
| 3133 | const operand = try expr(mod, scope, .ref, rhs); | ||
| 3134 | const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand); | ||
| 3135 | return result; | ||
| 3136 | }, | ||
| 3137 | .ty => |result_ty| { | ||
| 3138 | const result = try expr(mod, scope, .none, rhs); | ||
| 3139 | const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result); | ||
| 3140 | return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted); | ||
| 3141 | }, | ||
| 3142 | .ptr => |result_ptr| { | ||
| 3143 | const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr); | ||
| 3144 | return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, rhs); | ||
| 3145 | }, | ||
| 3146 | .bitcasted_ptr => |bitcasted_ptr| { | ||
| 3147 | return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location another @bitCast", .{}); | ||
| 3148 | }, | ||
| 3149 | .block_ptr => |block_ptr| { | ||
| 3150 | return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location inferred peer types", .{}); | ||
| 3151 | }, | ||
| 3152 | .inferred_ptr => |result_alloc| { | ||
| 3153 | // TODO here we should be able to resolve the inference; we now have a type for the result. | ||
| 3154 | return mod.failTok(scope, builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{}); | ||
| 3155 | }, | ||
| 3156 | } | ||
| 3157 | } | ||
| 3158 | |||
| 3159 | fn typeOf( | ||
| 3160 | mod: *Module, | ||
| 3161 | scope: *Scope, | ||
| 3162 | rl: ResultLoc, | ||
| 3163 | builtin_token: ast.TokenIndex, | ||
| 3164 | node: ast.Node.Index, | ||
| 3165 | params: []const ast.Node.Index, | ||
| 3166 | ) InnerError!zir.Inst.Ref { | ||
| 3167 | if (params.len < 1) { | ||
| 3168 | return mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{}); | ||
| 3169 | } | ||
| 3170 | const gz = scope.getGenZir(); | ||
| 3171 | if (params.len == 1) { | ||
| 3172 | return rvalue( | ||
| 3173 | mod, | ||
| 3174 | scope, | ||
| 3175 | rl, | ||
| 3176 | try gz.addUnTok(.typeof, try expr(mod, scope, .none, params[0]), node), | ||
| 3177 | node, | ||
| 3178 | ); | ||
| 3179 | } | ||
| 3180 | const arena = scope.arena(); | ||
| 3181 | var items = try arena.alloc(zir.Inst.Ref, params.len); | ||
| 3182 | for (params) |param, param_i| { | ||
| 3183 | items[param_i] = try expr(mod, scope, .none, param); | ||
| 3184 | } | ||
| 3185 | |||
| 3186 | const result = try gz.addPlNode(.typeof_peer, node, zir.Inst.MultiOp{ | ||
| 3187 | .operands_len = @intCast(u32, params.len), | ||
| 3188 | }); | ||
| 3189 | try gz.zir_code.appendRefs(items); | ||
| 3190 | |||
| 3191 | return rvalue(mod, scope, rl, result, node); | ||
| 3192 | } | ||
| 3193 | |||
| 3194 | fn builtinCall( | ||
| 3195 | mod: *Module, | ||
| 3196 | scope: *Scope, | ||
| 3197 | rl: ResultLoc, | ||
| 3198 | node: ast.Node.Index, | ||
| 3199 | params: []const ast.Node.Index, | ||
| 3200 | ) InnerError!zir.Inst.Ref { | ||
| 3201 | const tree = scope.tree(); | ||
| 3202 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3203 | |||
| 3204 | const builtin_token = main_tokens[node]; | ||
| 3205 | const builtin_name = tree.tokenSlice(builtin_token); | ||
| 3206 | |||
| 3207 | // We handle the different builtins manually because they have different semantics depending | ||
| 3208 | // on the function. For example, `@as` and others participate in result location semantics, | ||
| 3209 | // and `@cImport` creates a special scope that collects a .c source code text buffer. | ||
| 3210 | // Also, some builtins have a variable number of parameters. | ||
| 3211 | |||
| 3212 | const info = BuiltinFn.list.get(builtin_name) orelse { | ||
| 3213 | return mod.failTok(scope, builtin_token, "invalid builtin function: '{s}'", .{ | ||
| 3214 | builtin_name, | ||
| 3215 | }); | ||
| 3216 | }; | ||
| 3217 | if (info.param_count) |expected| { | ||
| 3218 | if (expected != params.len) { | ||
| 3219 | const s = if (expected == 1) "" else "s"; | ||
| 3220 | return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{ | ||
| 3221 | expected, s, params.len, | ||
| 3222 | }); | ||
| 3223 | } | ||
| 3224 | } | ||
| 3225 | |||
| 3226 | const gz = scope.getGenZir(); | ||
| 3227 | |||
| 3228 | switch (info.tag) { | ||
| 3229 | .ptr_to_int => { | ||
| 3230 | const operand = try expr(mod, scope, .none, params[0]); | ||
| 3231 | const result = try gz.addUnNode(.ptrtoint, operand, node); | ||
| 3232 | return rvalue(mod, scope, rl, result, node); | ||
| 3233 | }, | ||
| 3234 | .float_cast => { | ||
| 3235 | const dest_type = try typeExpr(mod, scope, params[0]); | ||
| 3236 | const rhs = try expr(mod, scope, .none, params[1]); | ||
| 3237 | const result = try gz.addPlNode(.floatcast, node, zir.Inst.Bin{ | ||
| 3238 | .lhs = dest_type, | ||
| 3239 | .rhs = rhs, | ||
| 3240 | }); | ||
| 3241 | return rvalue(mod, scope, rl, result, node); | ||
| 3242 | }, | ||
| 3243 | .int_cast => { | ||
| 3244 | const dest_type = try typeExpr(mod, scope, params[0]); | ||
| 3245 | const rhs = try expr(mod, scope, .none, params[1]); | ||
| 3246 | const result = try gz.addPlNode(.intcast, node, zir.Inst.Bin{ | ||
| 3247 | .lhs = dest_type, | ||
| 3248 | .rhs = rhs, | ||
| 3249 | }); | ||
| 3250 | return rvalue(mod, scope, rl, result, node); | ||
| 3251 | }, | ||
| 3252 | .breakpoint => { | ||
| 3253 | const result = try gz.add(.{ | ||
| 3254 | .tag = .breakpoint, | ||
| 3255 | .data = .{ .node = gz.zir_code.decl.nodeIndexToRelative(node) }, | ||
| 3256 | }); | ||
| 3257 | return rvalue(mod, scope, rl, result, node); | ||
| 3258 | }, | ||
| 3259 | .import => { | ||
| 3260 | const target = try expr(mod, scope, .none, params[0]); | ||
| 3261 | const result = try gz.addUnNode(.import, target, node); | ||
| 3262 | return rvalue(mod, scope, rl, result, node); | ||
| 3263 | }, | ||
| 3264 | .compile_error => { | ||
| 3265 | const target = try expr(mod, scope, .none, params[0]); | ||
| 3266 | const result = try gz.addUnNode(.compile_error, target, node); | ||
| 3267 | return rvalue(mod, scope, rl, result, node); | ||
| 3268 | }, | ||
| 3269 | .set_eval_branch_quota => { | ||
| 3270 | const quota = try expr(mod, scope, .{ .ty = .u32_type }, params[0]); | ||
| 3271 | const result = try gz.addUnNode(.set_eval_branch_quota, quota, node); | ||
| 3272 | return rvalue(mod, scope, rl, result, node); | ||
| 3273 | }, | ||
| 3274 | .compile_log => { | ||
| 3275 | const arg_refs = try mod.gpa.alloc(zir.Inst.Ref, params.len); | ||
| 3276 | defer mod.gpa.free(arg_refs); | ||
| 3277 | |||
| 3278 | for (params) |param, i| arg_refs[i] = try expr(mod, scope, .none, param); | ||
| 3279 | |||
| 3280 | const result = try gz.addPlNode(.compile_log, node, zir.Inst.MultiOp{ | ||
| 3281 | .operands_len = @intCast(u32, params.len), | ||
| 3282 | }); | ||
| 3283 | try gz.zir_code.appendRefs(arg_refs); | ||
| 3284 | return rvalue(mod, scope, rl, result, node); | ||
| 3285 | }, | ||
| 3286 | .field => { | ||
| 3287 | const field_name = try comptimeExpr(mod, scope, .{ .ty = .const_slice_u8_type }, params[1]); | ||
| 3288 | if (rl == .ref) { | ||
| 3289 | return try gz.addPlNode(.field_ptr_named, node, zir.Inst.FieldNamed{ | ||
| 3290 | .lhs = try expr(mod, scope, .ref, params[0]), | ||
| 3291 | .field_name = field_name, | ||
| 3292 | }); | ||
| 3293 | } | ||
| 3294 | const result = try gz.addPlNode(.field_val_named, node, zir.Inst.FieldNamed{ | ||
| 3295 | .lhs = try expr(mod, scope, .none, params[0]), | ||
| 3296 | .field_name = field_name, | ||
| 3297 | }); | ||
| 3298 | return rvalue(mod, scope, rl, result, node); | ||
| 3299 | }, | ||
| 3300 | .as => return as(mod, scope, rl, builtin_token, node, params[0], params[1]), | ||
| 3301 | .bit_cast => return bitCast(mod, scope, rl, builtin_token, node, params[0], params[1]), | ||
| 3302 | .TypeOf => return typeOf(mod, scope, rl, builtin_token, node, params), | ||
| 3303 | |||
| 3304 | .add_with_overflow, | ||
| 3305 | .align_cast, | ||
| 3306 | .align_of, | ||
| 3307 | .atomic_load, | ||
| 3308 | .atomic_rmw, | ||
| 3309 | .atomic_store, | ||
| 3310 | .bit_offset_of, | ||
| 3311 | .bool_to_int, | ||
| 3312 | .bit_size_of, | ||
| 3313 | .mul_add, | ||
| 3314 | .byte_swap, | ||
| 3315 | .bit_reverse, | ||
| 3316 | .byte_offset_of, | ||
| 3317 | .call, | ||
| 3318 | .c_define, | ||
| 3319 | .c_import, | ||
| 3320 | .c_include, | ||
| 3321 | .clz, | ||
| 3322 | .cmpxchg_strong, | ||
| 3323 | .cmpxchg_weak, | ||
| 3324 | .ctz, | ||
| 3325 | .c_undef, | ||
| 3326 | .div_exact, | ||
| 3327 | .div_floor, | ||
| 3328 | .div_trunc, | ||
| 3329 | .embed_file, | ||
| 3330 | .enum_to_int, | ||
| 3331 | .error_name, | ||
| 3332 | .error_return_trace, | ||
| 3333 | .error_to_int, | ||
| 3334 | .err_set_cast, | ||
| 3335 | .@"export", | ||
| 3336 | .fence, | ||
| 3337 | .field_parent_ptr, | ||
| 3338 | .float_to_int, | ||
| 3339 | .has_decl, | ||
| 3340 | .has_field, | ||
| 3341 | .int_to_enum, | ||
| 3342 | .int_to_error, | ||
| 3343 | .int_to_float, | ||
| 3344 | .int_to_ptr, | ||
| 3345 | .memcpy, | ||
| 3346 | .memset, | ||
| 3347 | .wasm_memory_size, | ||
| 3348 | .wasm_memory_grow, | ||
| 3349 | .mod, | ||
| 3350 | .mul_with_overflow, | ||
| 3351 | .panic, | ||
| 3352 | .pop_count, | ||
| 3353 | .ptr_cast, | ||
| 3354 | .rem, | ||
| 3355 | .return_address, | ||
| 3356 | .set_align_stack, | ||
| 3357 | .set_cold, | ||
| 3358 | .set_float_mode, | ||
| 3359 | .set_runtime_safety, | ||
| 3360 | .shl_exact, | ||
| 3361 | .shl_with_overflow, | ||
| 3362 | .shr_exact, | ||
| 3363 | .shuffle, | ||
| 3364 | .size_of, | ||
| 3365 | .splat, | ||
| 3366 | .reduce, | ||
| 3367 | .src, | ||
| 3368 | .sqrt, | ||
| 3369 | .sin, | ||
| 3370 | .cos, | ||
| 3371 | .exp, | ||
| 3372 | .exp2, | ||
| 3373 | .log, | ||
| 3374 | .log2, | ||
| 3375 | .log10, | ||
| 3376 | .fabs, | ||
| 3377 | .floor, | ||
| 3378 | .ceil, | ||
| 3379 | .trunc, | ||
| 3380 | .round, | ||
| 3381 | .sub_with_overflow, | ||
| 3382 | .tag_name, | ||
| 3383 | .This, | ||
| 3384 | .truncate, | ||
| 3385 | .Type, | ||
| 3386 | .type_info, | ||
| 3387 | .type_name, | ||
| 3388 | .union_init, | ||
| 3389 | => return mod.failTok(scope, builtin_token, "TODO: implement builtin function {s}", .{ | ||
| 3390 | builtin_name, | ||
| 3391 | }), | ||
| 3392 | |||
| 3393 | .async_call, | ||
| 3394 | .frame, | ||
| 3395 | .Frame, | ||
| 3396 | .frame_address, | ||
| 3397 | .frame_size, | ||
| 3398 | => return mod.failTok(scope, builtin_token, "async and related features are not yet supported", .{}), | ||
| 3399 | } | ||
| 3400 | } | ||
| 3401 | |||
| 3402 | fn callExpr( | ||
| 3403 | mod: *Module, | ||
| 3404 | scope: *Scope, | ||
| 3405 | rl: ResultLoc, | ||
| 3406 | node: ast.Node.Index, | ||
| 3407 | call: ast.full.Call, | ||
| 3408 | ) InnerError!zir.Inst.Ref { | ||
| 3409 | if (call.async_token) |async_token| { | ||
| 3410 | return mod.failTok(scope, async_token, "async and related features are not yet supported", .{}); | ||
| 3411 | } | ||
| 3412 | const lhs = try expr(mod, scope, .none, call.ast.fn_expr); | ||
| 3413 | |||
| 3414 | const args = try mod.gpa.alloc(zir.Inst.Ref, call.ast.params.len); | ||
| 3415 | defer mod.gpa.free(args); | ||
| 3416 | |||
| 3417 | const gz = scope.getGenZir(); | ||
| 3418 | for (call.ast.params) |param_node, i| { | ||
| 3419 | const param_type = try gz.add(.{ | ||
| 3420 | .tag = .param_type, | ||
| 3421 | .data = .{ .param_type = .{ | ||
| 3422 | .callee = lhs, | ||
| 3423 | .param_index = @intCast(u32, i), | ||
| 3424 | } }, | ||
| 3425 | }); | ||
| 3426 | args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node); | ||
| 3427 | } | ||
| 3428 | |||
| 3429 | const modifier: std.builtin.CallOptions.Modifier = switch (call.async_token != null) { | ||
| 3430 | true => .async_kw, | ||
| 3431 | false => .auto, | ||
| 3432 | }; | ||
| 3433 | const result: zir.Inst.Ref = res: { | ||
| 3434 | const tag: zir.Inst.Tag = switch (modifier) { | ||
| 3435 | .auto => switch (args.len == 0) { | ||
| 3436 | true => break :res try gz.addUnNode(.call_none, lhs, node), | ||
| 3437 | false => .call, | ||
| 3438 | }, | ||
| 3439 | .async_kw => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 3440 | .never_tail => unreachable, | ||
| 3441 | .never_inline => unreachable, | ||
| 3442 | .no_async => return mod.failNode(scope, node, "async and related features are not yet supported", .{}), | ||
| 3443 | .always_tail => unreachable, | ||
| 3444 | .always_inline => unreachable, | ||
| 3445 | .compile_time => .call_compile_time, | ||
| 3446 | }; | ||
| 3447 | break :res try gz.addCall(tag, lhs, args, node); | ||
| 3448 | }; | ||
| 3449 | return rvalue(mod, scope, rl, result, node); // TODO function call with result location | ||
| 3450 | } | ||
| 3451 | |||
| 3452 | pub const simple_types = std.ComptimeStringMap(zir.Inst.Ref, .{ | ||
| 3453 | .{ "u8", .u8_type }, | ||
| 3454 | .{ "i8", .i8_type }, | ||
| 3455 | .{ "u16", .u16_type }, | ||
| 3456 | .{ "i16", .i16_type }, | ||
| 3457 | .{ "u32", .u32_type }, | ||
| 3458 | .{ "i32", .i32_type }, | ||
| 3459 | .{ "u64", .u64_type }, | ||
| 3460 | .{ "i64", .i64_type }, | ||
| 3461 | .{ "usize", .usize_type }, | ||
| 3462 | .{ "isize", .isize_type }, | ||
| 3463 | .{ "c_short", .c_short_type }, | ||
| 3464 | .{ "c_ushort", .c_ushort_type }, | ||
| 3465 | .{ "c_int", .c_int_type }, | ||
| 3466 | .{ "c_uint", .c_uint_type }, | ||
| 3467 | .{ "c_long", .c_long_type }, | ||
| 3468 | .{ "c_ulong", .c_ulong_type }, | ||
| 3469 | .{ "c_longlong", .c_longlong_type }, | ||
| 3470 | .{ "c_ulonglong", .c_ulonglong_type }, | ||
| 3471 | .{ "c_longdouble", .c_longdouble_type }, | ||
| 3472 | .{ "f16", .f16_type }, | ||
| 3473 | .{ "f32", .f32_type }, | ||
| 3474 | .{ "f64", .f64_type }, | ||
| 3475 | .{ "f128", .f128_type }, | ||
| 3476 | .{ "c_void", .c_void_type }, | ||
| 3477 | .{ "bool", .bool_type }, | ||
| 3478 | .{ "void", .void_type }, | ||
| 3479 | .{ "type", .type_type }, | ||
| 3480 | .{ "anyerror", .anyerror_type }, | ||
| 3481 | .{ "comptime_int", .comptime_int_type }, | ||
| 3482 | .{ "comptime_float", .comptime_float_type }, | ||
| 3483 | .{ "noreturn", .noreturn_type }, | ||
| 3484 | .{ "null", .null_type }, | ||
| 3485 | .{ "undefined", .undefined_type }, | ||
| 3486 | .{ "undefined", .undef }, | ||
| 3487 | .{ "null", .null_value }, | ||
| 3488 | .{ "true", .bool_true }, | ||
| 3489 | .{ "false", .bool_false }, | ||
| 3490 | }); | ||
| 3491 | |||
| 3492 | fn nodeMayNeedMemoryLocation(scope: *Scope, start_node: ast.Node.Index) bool { | ||
| 3493 | const tree = scope.tree(); | ||
| 3494 | const node_tags = tree.nodes.items(.tag); | ||
| 3495 | const node_datas = tree.nodes.items(.data); | ||
| 3496 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3497 | const token_tags = tree.tokens.items(.tag); | ||
| 3498 | |||
| 3499 | var node = start_node; | ||
| 3500 | while (true) { | ||
| 3501 | switch (node_tags[node]) { | ||
| 3502 | .root, | ||
| 3503 | .@"usingnamespace", | ||
| 3504 | .test_decl, | ||
| 3505 | .switch_case, | ||
| 3506 | .switch_case_one, | ||
| 3507 | .container_field_init, | ||
| 3508 | .container_field_align, | ||
| 3509 | .container_field, | ||
| 3510 | .asm_output, | ||
| 3511 | .asm_input, | ||
| 3512 | => unreachable, | ||
| 3513 | |||
| 3514 | .@"return", | ||
| 3515 | .@"break", | ||
| 3516 | .@"continue", | ||
| 3517 | .bit_not, | ||
| 3518 | .bool_not, | ||
| 3519 | .global_var_decl, | ||
| 3520 | .local_var_decl, | ||
| 3521 | .simple_var_decl, | ||
| 3522 | .aligned_var_decl, | ||
| 3523 | .@"defer", | ||
| 3524 | .@"errdefer", | ||
| 3525 | .address_of, | ||
| 3526 | .optional_type, | ||
| 3527 | .negation, | ||
| 3528 | .negation_wrap, | ||
| 3529 | .@"resume", | ||
| 3530 | .array_type, | ||
| 3531 | .array_type_sentinel, | ||
| 3532 | .ptr_type_aligned, | ||
| 3533 | .ptr_type_sentinel, | ||
| 3534 | .ptr_type, | ||
| 3535 | .ptr_type_bit_range, | ||
| 3536 | .@"suspend", | ||
| 3537 | .@"anytype", | ||
| 3538 | .fn_proto_simple, | ||
| 3539 | .fn_proto_multi, | ||
| 3540 | .fn_proto_one, | ||
| 3541 | .fn_proto, | ||
| 3542 | .fn_decl, | ||
| 3543 | .anyframe_type, | ||
| 3544 | .anyframe_literal, | ||
| 3545 | .integer_literal, | ||
| 3546 | .float_literal, | ||
| 3547 | .enum_literal, | ||
| 3548 | .string_literal, | ||
| 3549 | .multiline_string_literal, | ||
| 3550 | .char_literal, | ||
| 3551 | .true_literal, | ||
| 3552 | .false_literal, | ||
| 3553 | .null_literal, | ||
| 3554 | .undefined_literal, | ||
| 3555 | .unreachable_literal, | ||
| 3556 | .identifier, | ||
| 3557 | .error_set_decl, | ||
| 3558 | .container_decl, | ||
| 3559 | .container_decl_trailing, | ||
| 3560 | .container_decl_two, | ||
| 3561 | .container_decl_two_trailing, | ||
| 3562 | .container_decl_arg, | ||
| 3563 | .container_decl_arg_trailing, | ||
| 3564 | .tagged_union, | ||
| 3565 | .tagged_union_trailing, | ||
| 3566 | .tagged_union_two, | ||
| 3567 | .tagged_union_two_trailing, | ||
| 3568 | .tagged_union_enum_tag, | ||
| 3569 | .tagged_union_enum_tag_trailing, | ||
| 3570 | .@"asm", | ||
| 3571 | .asm_simple, | ||
| 3572 | .add, | ||
| 3573 | .add_wrap, | ||
| 3574 | .array_cat, | ||
| 3575 | .array_mult, | ||
| 3576 | .assign, | ||
| 3577 | .assign_bit_and, | ||
| 3578 | .assign_bit_or, | ||
| 3579 | .assign_bit_shift_left, | ||
| 3580 | .assign_bit_shift_right, | ||
| 3581 | .assign_bit_xor, | ||
| 3582 | .assign_div, | ||
| 3583 | .assign_sub, | ||
| 3584 | .assign_sub_wrap, | ||
| 3585 | .assign_mod, | ||
| 3586 | .assign_add, | ||
| 3587 | .assign_add_wrap, | ||
| 3588 | .assign_mul, | ||
| 3589 | .assign_mul_wrap, | ||
| 3590 | .bang_equal, | ||
| 3591 | .bit_and, | ||
| 3592 | .bit_or, | ||
| 3593 | .bit_shift_left, | ||
| 3594 | .bit_shift_right, | ||
| 3595 | .bit_xor, | ||
| 3596 | .bool_and, | ||
| 3597 | .bool_or, | ||
| 3598 | .div, | ||
| 3599 | .equal_equal, | ||
| 3600 | .error_union, | ||
| 3601 | .greater_or_equal, | ||
| 3602 | .greater_than, | ||
| 3603 | .less_or_equal, | ||
| 3604 | .less_than, | ||
| 3605 | .merge_error_sets, | ||
| 3606 | .mod, | ||
| 3607 | .mul, | ||
| 3608 | .mul_wrap, | ||
| 3609 | .switch_range, | ||
| 3610 | .field_access, | ||
| 3611 | .sub, | ||
| 3612 | .sub_wrap, | ||
| 3613 | .slice, | ||
| 3614 | .slice_open, | ||
| 3615 | .slice_sentinel, | ||
| 3616 | .deref, | ||
| 3617 | .array_access, | ||
| 3618 | .error_value, | ||
| 3619 | .while_simple, // This variant cannot have an else expression. | ||
| 3620 | .while_cont, // This variant cannot have an else expression. | ||
| 3621 | .for_simple, // This variant cannot have an else expression. | ||
| 3622 | .if_simple, // This variant cannot have an else expression. | ||
| 3623 | => return false, | ||
| 3624 | |||
| 3625 | // Forward the question to the LHS sub-expression. | ||
| 3626 | .grouped_expression, | ||
| 3627 | .@"try", | ||
| 3628 | .@"await", | ||
| 3629 | .@"comptime", | ||
| 3630 | .@"nosuspend", | ||
| 3631 | .unwrap_optional, | ||
| 3632 | => node = node_datas[node].lhs, | ||
| 3633 | |||
| 3634 | // Forward the question to the RHS sub-expression. | ||
| 3635 | .@"catch", | ||
| 3636 | .@"orelse", | ||
| 3637 | => node = node_datas[node].rhs, | ||
| 3638 | |||
| 3639 | // True because these are exactly the expressions we need memory locations for. | ||
| 3640 | .array_init_one, | ||
| 3641 | .array_init_one_comma, | ||
| 3642 | .array_init_dot_two, | ||
| 3643 | .array_init_dot_two_comma, | ||
| 3644 | .array_init_dot, | ||
| 3645 | .array_init_dot_comma, | ||
| 3646 | .array_init, | ||
| 3647 | .array_init_comma, | ||
| 3648 | .struct_init_one, | ||
| 3649 | .struct_init_one_comma, | ||
| 3650 | .struct_init_dot_two, | ||
| 3651 | .struct_init_dot_two_comma, | ||
| 3652 | .struct_init_dot, | ||
| 3653 | .struct_init_dot_comma, | ||
| 3654 | .struct_init, | ||
| 3655 | .struct_init_comma, | ||
| 3656 | => return true, | ||
| 3657 | |||
| 3658 | // True because depending on comptime conditions, sub-expressions | ||
| 3659 | // may be the kind that need memory locations. | ||
| 3660 | .@"while", // This variant always has an else expression. | ||
| 3661 | .@"if", // This variant always has an else expression. | ||
| 3662 | .@"for", // This variant always has an else expression. | ||
| 3663 | .@"switch", | ||
| 3664 | .switch_comma, | ||
| 3665 | .call_one, | ||
| 3666 | .call_one_comma, | ||
| 3667 | .async_call_one, | ||
| 3668 | .async_call_one_comma, | ||
| 3669 | .call, | ||
| 3670 | .call_comma, | ||
| 3671 | .async_call, | ||
| 3672 | .async_call_comma, | ||
| 3673 | => return true, | ||
| 3674 | |||
| 3675 | .block_two, | ||
| 3676 | .block_two_semicolon, | ||
| 3677 | .block, | ||
| 3678 | .block_semicolon, | ||
| 3679 | => { | ||
| 3680 | const lbrace = main_tokens[node]; | ||
| 3681 | if (token_tags[lbrace - 1] == .colon) { | ||
| 3682 | // Labeled blocks may need a memory location to forward | ||
| 3683 | // to their break statements. | ||
| 3684 | return true; | ||
| 3685 | } else { | ||
| 3686 | return false; | ||
| 3687 | } | ||
| 3688 | }, | ||
| 3689 | |||
| 3690 | .builtin_call, | ||
| 3691 | .builtin_call_comma, | ||
| 3692 | .builtin_call_two, | ||
| 3693 | .builtin_call_two_comma, | ||
| 3694 | => { | ||
| 3695 | const builtin_token = main_tokens[node]; | ||
| 3696 | const builtin_name = tree.tokenSlice(builtin_token); | ||
| 3697 | // If the builtin is an invalid name, we don't cause an error here; instead | ||
| 3698 | // let it pass, and the error will be "invalid builtin function" later. | ||
| 3699 | const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false; | ||
| 3700 | return builtin_info.needs_mem_loc; | ||
| 3701 | }, | ||
| 3702 | } | ||
| 3703 | } | ||
| 3704 | } | ||
| 3705 | |||
| 3706 | /// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of | ||
| 3707 | /// result locations must call this function on their result. | ||
| 3708 | /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer. | ||
| 3709 | /// If the `ResultLoc` is `ty`, it will coerce the result to the type. | ||
| 3710 | fn rvalue( | ||
| 3711 | mod: *Module, | ||
| 3712 | scope: *Scope, | ||
| 3713 | rl: ResultLoc, | ||
| 3714 | result: zir.Inst.Ref, | ||
| 3715 | src_node: ast.Node.Index, | ||
| 3716 | ) InnerError!zir.Inst.Ref { | ||
| 3717 | const gz = scope.getGenZir(); | ||
| 3718 | switch (rl) { | ||
| 3719 | .none => return result, | ||
| 3720 | .discard => { | ||
| 3721 | // Emit a compile error for discarding error values. | ||
| 3722 | _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); | ||
| 3723 | return result; | ||
| 3724 | }, | ||
| 3725 | .ref => { | ||
| 3726 | // We need a pointer but we have a value. | ||
| 3727 | const tree = scope.tree(); | ||
| 3728 | const src_token = tree.firstToken(src_node); | ||
| 3729 | return gz.addUnTok(.ref, result, src_token); | ||
| 3730 | }, | ||
| 3731 | .ty => |ty_inst| { | ||
| 3732 | // Quickly eliminate some common, unnecessary type coercion. | ||
| 3733 | const as_ty = @as(u64, @enumToInt(zir.Inst.Ref.type_type)) << 32; | ||
| 3734 | const as_comptime_int = @as(u64, @enumToInt(zir.Inst.Ref.comptime_int_type)) << 32; | ||
| 3735 | const as_bool = @as(u64, @enumToInt(zir.Inst.Ref.bool_type)) << 32; | ||
| 3736 | const as_usize = @as(u64, @enumToInt(zir.Inst.Ref.usize_type)) << 32; | ||
| 3737 | const as_void = @as(u64, @enumToInt(zir.Inst.Ref.void_type)) << 32; | ||
| 3738 | switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) { | ||
| 3739 | as_ty | @enumToInt(zir.Inst.Ref.u8_type), | ||
| 3740 | as_ty | @enumToInt(zir.Inst.Ref.i8_type), | ||
| 3741 | as_ty | @enumToInt(zir.Inst.Ref.u16_type), | ||
| 3742 | as_ty | @enumToInt(zir.Inst.Ref.i16_type), | ||
| 3743 | as_ty | @enumToInt(zir.Inst.Ref.u32_type), | ||
| 3744 | as_ty | @enumToInt(zir.Inst.Ref.i32_type), | ||
| 3745 | as_ty | @enumToInt(zir.Inst.Ref.u64_type), | ||
| 3746 | as_ty | @enumToInt(zir.Inst.Ref.i64_type), | ||
| 3747 | as_ty | @enumToInt(zir.Inst.Ref.usize_type), | ||
| 3748 | as_ty | @enumToInt(zir.Inst.Ref.isize_type), | ||
| 3749 | as_ty | @enumToInt(zir.Inst.Ref.c_short_type), | ||
| 3750 | as_ty | @enumToInt(zir.Inst.Ref.c_ushort_type), | ||
| 3751 | as_ty | @enumToInt(zir.Inst.Ref.c_int_type), | ||
| 3752 | as_ty | @enumToInt(zir.Inst.Ref.c_uint_type), | ||
| 3753 | as_ty | @enumToInt(zir.Inst.Ref.c_long_type), | ||
| 3754 | as_ty | @enumToInt(zir.Inst.Ref.c_ulong_type), | ||
| 3755 | as_ty | @enumToInt(zir.Inst.Ref.c_longlong_type), | ||
| 3756 | as_ty | @enumToInt(zir.Inst.Ref.c_ulonglong_type), | ||
| 3757 | as_ty | @enumToInt(zir.Inst.Ref.c_longdouble_type), | ||
| 3758 | as_ty | @enumToInt(zir.Inst.Ref.f16_type), | ||
| 3759 | as_ty | @enumToInt(zir.Inst.Ref.f32_type), | ||
| 3760 | as_ty | @enumToInt(zir.Inst.Ref.f64_type), | ||
| 3761 | as_ty | @enumToInt(zir.Inst.Ref.f128_type), | ||
| 3762 | as_ty | @enumToInt(zir.Inst.Ref.c_void_type), | ||
| 3763 | as_ty | @enumToInt(zir.Inst.Ref.bool_type), | ||
| 3764 | as_ty | @enumToInt(zir.Inst.Ref.void_type), | ||
| 3765 | as_ty | @enumToInt(zir.Inst.Ref.type_type), | ||
| 3766 | as_ty | @enumToInt(zir.Inst.Ref.anyerror_type), | ||
| 3767 | as_ty | @enumToInt(zir.Inst.Ref.comptime_int_type), | ||
| 3768 | as_ty | @enumToInt(zir.Inst.Ref.comptime_float_type), | ||
| 3769 | as_ty | @enumToInt(zir.Inst.Ref.noreturn_type), | ||
| 3770 | as_ty | @enumToInt(zir.Inst.Ref.null_type), | ||
| 3771 | as_ty | @enumToInt(zir.Inst.Ref.undefined_type), | ||
| 3772 | as_ty | @enumToInt(zir.Inst.Ref.fn_noreturn_no_args_type), | ||
| 3773 | as_ty | @enumToInt(zir.Inst.Ref.fn_void_no_args_type), | ||
| 3774 | as_ty | @enumToInt(zir.Inst.Ref.fn_naked_noreturn_no_args_type), | ||
| 3775 | as_ty | @enumToInt(zir.Inst.Ref.fn_ccc_void_no_args_type), | ||
| 3776 | as_ty | @enumToInt(zir.Inst.Ref.single_const_pointer_to_comptime_int_type), | ||
| 3777 | as_ty | @enumToInt(zir.Inst.Ref.const_slice_u8_type), | ||
| 3778 | as_ty | @enumToInt(zir.Inst.Ref.enum_literal_type), | ||
| 3779 | as_comptime_int | @enumToInt(zir.Inst.Ref.zero), | ||
| 3780 | as_comptime_int | @enumToInt(zir.Inst.Ref.one), | ||
| 3781 | as_bool | @enumToInt(zir.Inst.Ref.bool_true), | ||
| 3782 | as_bool | @enumToInt(zir.Inst.Ref.bool_false), | ||
| 3783 | as_usize | @enumToInt(zir.Inst.Ref.zero_usize), | ||
| 3784 | as_usize | @enumToInt(zir.Inst.Ref.one_usize), | ||
| 3785 | as_void | @enumToInt(zir.Inst.Ref.void_value), | ||
| 3786 | => return result, // type of result is already correct | ||
| 3787 | |||
| 3788 | // Need an explicit type coercion instruction. | ||
| 3789 | else => return gz.addPlNode(.as_node, src_node, zir.Inst.As{ | ||
| 3790 | .dest_type = ty_inst, | ||
| 3791 | .operand = result, | ||
| 3792 | }), | ||
| 3793 | } | ||
| 3794 | }, | ||
| 3795 | .ptr => |ptr_inst| { | ||
| 3796 | _ = try gz.addPlNode(.store_node, src_node, zir.Inst.Bin{ | ||
| 3797 | .lhs = ptr_inst, | ||
| 3798 | .rhs = result, | ||
| 3799 | }); | ||
| 3800 | return result; | ||
| 3801 | }, | ||
| 3802 | .bitcasted_ptr => |bitcasted_ptr| { | ||
| 3803 | return mod.failNode(scope, src_node, "TODO implement rvalue .bitcasted_ptr", .{}); | ||
| 3804 | }, | ||
| 3805 | .inferred_ptr => |alloc| { | ||
| 3806 | _ = try gz.addBin(.store_to_inferred_ptr, alloc, result); | ||
| 3807 | return result; | ||
| 3808 | }, | ||
| 3809 | .block_ptr => |block_scope| { | ||
| 3810 | block_scope.rvalue_rl_count += 1; | ||
| 3811 | _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result); | ||
| 3812 | return result; | ||
| 3813 | }, | ||
| 3814 | } | ||
| 3815 | } | ||
| 3816 | |||
| 3817 | fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZir) ResultLoc.Strategy { | ||
| 3818 | var elide_store_to_block_ptr_instructions = false; | ||
| 3819 | switch (rl) { | ||
| 3820 | // In this branch there will not be any store_to_block_ptr instructions. | ||
| 3821 | .discard, .none, .ty, .ref => return .{ | ||
| 3822 | .tag = .break_operand, | ||
| 3823 | .elide_store_to_block_ptr_instructions = false, | ||
| 3824 | }, | ||
| 3825 | // The pointer got passed through to the sub-expressions, so we will use | ||
| 3826 | // break_void here. | ||
| 3827 | // In this branch there will not be any store_to_block_ptr instructions. | ||
| 3828 | .ptr => return .{ | ||
| 3829 | .tag = .break_void, | ||
| 3830 | .elide_store_to_block_ptr_instructions = false, | ||
| 3831 | }, | ||
| 3832 | .inferred_ptr, .bitcasted_ptr, .block_ptr => { | ||
| 3833 | if (block_scope.rvalue_rl_count == block_scope.break_count) { | ||
| 3834 | // Neither prong of the if consumed the result location, so we can | ||
| 3835 | // use break instructions to create an rvalue. | ||
| 3836 | return .{ | ||
| 3837 | .tag = .break_operand, | ||
| 3838 | .elide_store_to_block_ptr_instructions = true, | ||
| 3839 | }; | ||
| 3840 | } else { | ||
| 3841 | // Allow the store_to_block_ptr instructions to remain so that | ||
| 3842 | // semantic analysis can turn them into bitcasts. | ||
| 3843 | return .{ | ||
| 3844 | .tag = .break_void, | ||
| 3845 | .elide_store_to_block_ptr_instructions = false, | ||
| 3846 | }; | ||
| 3847 | } | ||
| 3848 | }, | ||
| 3849 | } | ||
| 3850 | } | ||
| 3851 | |||
| 3852 | fn setBlockResultLoc(block_scope: *Scope.GenZir, parent_rl: ResultLoc) void { | ||
| 3853 | // Depending on whether the result location is a pointer or value, different | ||
| 3854 | // ZIR needs to be generated. In the former case we rely on storing to the | ||
| 3855 | // pointer to communicate the result, and use breakvoid; in the latter case | ||
| 3856 | // the block break instructions will have the result values. | ||
| 3857 | // One more complication: when the result location is a pointer, we detect | ||
| 3858 | // the scenario where the result location is not consumed. In this case | ||
| 3859 | // we emit ZIR for the block break instructions to have the result values, | ||
| 3860 | // and then rvalue() on that to pass the value to the result location. | ||
| 3861 | switch (parent_rl) { | ||
| 3862 | .discard, .none, .ty, .ptr, .ref => { | ||
| 3863 | block_scope.break_result_loc = parent_rl; | ||
| 3864 | }, | ||
| 3865 | |||
| 3866 | .inferred_ptr => |ptr| { | ||
| 3867 | block_scope.rl_ptr = ptr; | ||
| 3868 | block_scope.break_result_loc = .{ .block_ptr = block_scope }; | ||
| 3869 | }, | ||
| 3870 | |||
| 3871 | .bitcasted_ptr => |ptr| { | ||
| 3872 | block_scope.rl_ptr = ptr; | ||
| 3873 | block_scope.break_result_loc = .{ .block_ptr = block_scope }; | ||
| 3874 | }, | ||
| 3875 | |||
| 3876 | .block_ptr => |parent_block_scope| { | ||
| 3877 | block_scope.rl_ptr = parent_block_scope.rl_ptr; | ||
| 3878 | block_scope.break_result_loc = .{ .block_ptr = block_scope }; | ||
| 3879 | }, | ||
| 3880 | } | ||
| 3881 | } | ||
src/translate_c.zig+1-1| ... | @@ -4266,7 +4266,7 @@ fn isZigPrimitiveType(name: []const u8) bool { | ... | @@ -4266,7 +4266,7 @@ fn isZigPrimitiveType(name: []const u8) bool { |
| 4266 | } | 4266 | } |
| 4267 | return true; | 4267 | return true; |
| 4268 | } | 4268 | } |
| 4269 | return @import("astgen.zig").simple_types.has(name); | 4269 | return @import("Astgen.zig").simple_types.has(name); |
| 4270 | } | 4270 | } |
| 4271 | 4271 | ||
| 4272 | const MacroCtx = struct { | 4272 | const MacroCtx = struct { |
src/zir.zig+2-2| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | //! Zig Intermediate Representation. astgen.zig converts AST nodes to these | 1 | //! Zig Intermediate Representation. Astgen.zig converts AST nodes to these |
| 2 | //! untyped IR instructions. Next, Sema.zig processes these into TZIR. | 2 | //! untyped IR instructions. Next, Sema.zig processes these into TZIR. |
| 3 | 3 | ||
| 4 | const std = @import("std"); | 4 | const std = @import("std"); |
| ... | @@ -491,7 +491,7 @@ pub const Inst = struct { | ... | @@ -491,7 +491,7 @@ pub const Inst = struct { |
| 491 | store_to_block_ptr, | 491 | store_to_block_ptr, |
| 492 | /// Same as `store` but the type of the value being stored will be used to infer | 492 | /// Same as `store` but the type of the value being stored will be used to infer |
| 493 | /// the pointer type. | 493 | /// the pointer type. |
| 494 | /// Uses the `bin` union field - astgen.zig depends on the ability to change | 494 | /// Uses the `bin` union field - Astgen.zig depends on the ability to change |
| 495 | /// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr` | 495 | /// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr` |
| 496 | /// without changing the data. | 496 | /// without changing the data. |
| 497 | store_to_inferred_ptr, | 497 | store_to_inferred_ptr, |