| 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 | 538 | "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" |
| 539 | 539 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" |
| 540 | 540 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" |
| 541 | "${CMAKE_SOURCE_DIR}/src/astgen.zig" | |
| 541 | "${CMAKE_SOURCE_DIR}/src/AstGen.zig" | |
| 542 | 542 | "${CMAKE_SOURCE_DIR}/src/clang.zig" |
| 543 | 543 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" |
| 544 | 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 | 23 | const ir = @import("ir.zig"); |
| 24 | 24 | const zir = @import("zir.zig"); |
| 25 | 25 | const trace = @import("tracy.zig").trace; |
| 26 | const astgen = @import("astgen.zig"); | |
| 26 | const AstGen = @import("AstGen.zig"); | |
| 27 | 27 | const Sema = @import("Sema.zig"); |
| 28 | 28 | const target_util = @import("target.zig"); |
| 29 | 29 | |
| ... | ... | @@ -407,9 +407,9 @@ pub const Scope = struct { |
| 407 | 407 | pub fn arena(scope: *Scope) *Allocator { |
| 408 | 408 | switch (scope.tag) { |
| 409 | 409 | .block => return scope.cast(Block).?.sema.arena, |
| 410 | .gen_zir => return scope.cast(GenZir).?.zir_code.arena, | |
| 411 | .local_val => return scope.cast(LocalVal).?.gen_zir.zir_code.arena, | |
| 412 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir.zir_code.arena, | |
| 410 | .gen_zir => return scope.cast(GenZir).?.astgen.arena, | |
| 411 | .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.arena, | |
| 412 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.arena, | |
| 413 | 413 | .file => unreachable, |
| 414 | 414 | .container => unreachable, |
| 415 | 415 | .decl_ref => unreachable, |
| ... | ... | @@ -419,9 +419,9 @@ pub const Scope = struct { |
| 419 | 419 | pub fn ownerDecl(scope: *Scope) ?*Decl { |
| 420 | 420 | return switch (scope.tag) { |
| 421 | 421 | .block => scope.cast(Block).?.sema.owner_decl, |
| 422 | .gen_zir => scope.cast(GenZir).?.zir_code.decl, | |
| 423 | .local_val => scope.cast(LocalVal).?.gen_zir.zir_code.decl, | |
| 424 | .local_ptr => scope.cast(LocalPtr).?.gen_zir.zir_code.decl, | |
| 422 | .gen_zir => scope.cast(GenZir).?.astgen.decl, | |
| 423 | .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl, | |
| 424 | .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl, | |
| 425 | 425 | .file => null, |
| 426 | 426 | .container => null, |
| 427 | 427 | .decl_ref => scope.cast(DeclRef).?.decl, |
| ... | ... | @@ -431,9 +431,9 @@ pub const Scope = struct { |
| 431 | 431 | pub fn srcDecl(scope: *Scope) ?*Decl { |
| 432 | 432 | return switch (scope.tag) { |
| 433 | 433 | .block => scope.cast(Block).?.src_decl, |
| 434 | .gen_zir => scope.cast(GenZir).?.zir_code.decl, | |
| 435 | .local_val => scope.cast(LocalVal).?.gen_zir.zir_code.decl, | |
| 436 | .local_ptr => scope.cast(LocalPtr).?.gen_zir.zir_code.decl, | |
| 434 | .gen_zir => scope.cast(GenZir).?.astgen.decl, | |
| 435 | .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl, | |
| 436 | .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl, | |
| 437 | 437 | .file => null, |
| 438 | 438 | .container => null, |
| 439 | 439 | .decl_ref => scope.cast(DeclRef).?.decl, |
| ... | ... | @@ -444,9 +444,9 @@ pub const Scope = struct { |
| 444 | 444 | pub fn namespace(scope: *Scope) *Container { |
| 445 | 445 | switch (scope.tag) { |
| 446 | 446 | .block => return scope.cast(Block).?.sema.owner_decl.container, |
| 447 | .gen_zir => return scope.cast(GenZir).?.zir_code.decl.container, | |
| 448 | .local_val => return scope.cast(LocalVal).?.gen_zir.zir_code.decl.container, | |
| 449 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir.zir_code.decl.container, | |
| 447 | .gen_zir => return scope.cast(GenZir).?.astgen.decl.container, | |
| 448 | .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.decl.container, | |
| 449 | .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.decl.container, | |
| 450 | 450 | .file => return &scope.cast(File).?.root_container, |
| 451 | 451 | .container => return scope.cast(Container).?, |
| 452 | 452 | .decl_ref => return scope.cast(DeclRef).?.decl.container, |
| ... | ... | @@ -474,8 +474,8 @@ pub const Scope = struct { |
| 474 | 474 | .file => return &scope.cast(File).?.tree, |
| 475 | 475 | .block => return &scope.cast(Block).?.src_decl.container.file_scope.tree, |
| 476 | 476 | .gen_zir => return scope.cast(GenZir).?.tree(), |
| 477 | .local_val => return &scope.cast(LocalVal).?.gen_zir.zir_code.decl.container.file_scope.tree, | |
| 478 | .local_ptr => return &scope.cast(LocalPtr).?.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.astgen.decl.container.file_scope.tree, | |
| 479 | 479 | .container => return &scope.cast(Container).?.file_scope.tree, |
| 480 | 480 | .decl_ref => return &scope.cast(DeclRef).?.decl.container.file_scope.tree, |
| 481 | 481 | } |
| ... | ... | @@ -913,15 +913,15 @@ pub const Scope = struct { |
| 913 | 913 | /// Parents can be: `GenZir`, `File` |
| 914 | 914 | parent: *Scope, |
| 915 | 915 | /// All `GenZir` scopes for the same ZIR share this. |
| 916 | zir_code: *WipZirCode, | |
| 916 | astgen: *AstGen, | |
| 917 | 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 | 919 | instructions: ArrayListUnmanaged(zir.Inst.Index) = .{}, |
| 920 | 920 | label: ?Label = null, |
| 921 | 921 | break_block: zir.Inst.Index = 0, |
| 922 | 922 | continue_block: zir.Inst.Index = 0, |
| 923 | 923 | /// Only valid when setBlockResultLoc is called. |
| 924 | break_result_loc: astgen.ResultLoc = undefined, | |
| 924 | break_result_loc: AstGen.ResultLoc = undefined, | |
| 925 | 925 | /// When a block has a pointer result location, here it is. |
| 926 | 926 | rl_ptr: zir.Inst.Ref = .none, |
| 927 | 927 | /// Keeps track of how many branches of a block did not actually |
| ... | ... | @@ -948,49 +948,51 @@ pub const Scope = struct { |
| 948 | 948 | }; |
| 949 | 949 | |
| 950 | 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 | 952 | /// initialized, but empty, state. |
| 953 | 953 | pub fn finish(gz: *GenZir) !zir.Code { |
| 954 | const gpa = gz.zir_code.gpa; | |
| 954 | const gpa = gz.astgen.mod.gpa; | |
| 955 | 955 | try gz.setBlockBody(0); |
| 956 | 956 | return zir.Code{ |
| 957 | .instructions = gz.zir_code.instructions.toOwnedSlice(), | |
| 958 | .string_bytes = gz.zir_code.string_bytes.toOwnedSlice(gpa), | |
| 959 | .extra = gz.zir_code.extra.toOwnedSlice(gpa), | |
| 960 | .decls = gz.zir_code.decls.toOwnedSlice(gpa), | |
| 957 | .instructions = gz.astgen.instructions.toOwnedSlice(), | |
| 958 | .string_bytes = gz.astgen.string_bytes.toOwnedSlice(gpa), | |
| 959 | .extra = gz.astgen.extra.toOwnedSlice(gpa), | |
| 960 | .decls = gz.astgen.decls.toOwnedSlice(gpa), | |
| 961 | 961 | }; |
| 962 | 962 | } |
| 963 | 963 | |
| 964 | 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 | 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 | 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 | 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 | 979 | @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); |
| 979 | const zir_datas = gz.zir_code.instructions.items(.data); | |
| 980 | zir_datas[inst].bool_br.payload_index = gz.zir_code.addExtraAssumeCapacity( | |
| 980 | const zir_datas = gz.astgen.instructions.items(.data); | |
| 981 | zir_datas[inst].bool_br.payload_index = gz.astgen.addExtraAssumeCapacity( | |
| 981 | 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 | 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 | 990 | @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); |
| 989 | const zir_datas = gz.zir_code.instructions.items(.data); | |
| 990 | zir_datas[inst].pl_node.payload_index = gz.zir_code.addExtraAssumeCapacity( | |
| 991 | const zir_datas = gz.astgen.instructions.items(.data); | |
| 992 | zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity( | |
| 991 | 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 | 998 | pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct { |
| ... | ... | @@ -1000,20 +1002,20 @@ pub const Scope = struct { |
| 1000 | 1002 | }) !zir.Inst.Ref { |
| 1001 | 1003 | assert(args.ret_ty != .none); |
| 1002 | 1004 | assert(args.cc != .none); |
| 1003 | const gpa = gz.zir_code.gpa; | |
| 1005 | const gpa = gz.astgen.mod.gpa; | |
| 1004 | 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); | |
| 1006 | try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len + | |
| 1007 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | |
| 1008 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | |
| 1007 | 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 | 1012 | .cc = args.cc, |
| 1011 | 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); | |
| 1016 | gz.zir_code.instructions.appendAssumeCapacity(.{ | |
| 1017 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | |
| 1018 | gz.astgen.instructions.appendAssumeCapacity(.{ | |
| 1017 | 1019 | .tag = tag, |
| 1018 | 1020 | .data = .{ .fn_type = .{ |
| 1019 | 1021 | .return_type = args.ret_ty, |
| ... | ... | @@ -1021,7 +1023,7 @@ pub const Scope = struct { |
| 1021 | 1023 | } }, |
| 1022 | 1024 | }); |
| 1023 | 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 | 1029 | pub fn addFnType( |
| ... | ... | @@ -1031,19 +1033,19 @@ pub const Scope = struct { |
| 1031 | 1033 | param_types: []const zir.Inst.Ref, |
| 1032 | 1034 | ) !zir.Inst.Ref { |
| 1033 | 1035 | assert(ret_ty != .none); |
| 1034 | const gpa = gz.zir_code.gpa; | |
| 1036 | const gpa = gz.astgen.mod.gpa; | |
| 1035 | 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); | |
| 1037 | try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len + | |
| 1038 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | |
| 1039 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | |
| 1038 | 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 | 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); | |
| 1046 | gz.zir_code.instructions.appendAssumeCapacity(.{ | |
| 1047 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | |
| 1048 | gz.astgen.instructions.appendAssumeCapacity(.{ | |
| 1047 | 1049 | .tag = tag, |
| 1048 | 1050 | .data = .{ .fn_type = .{ |
| 1049 | 1051 | .return_type = ret_ty, |
| ... | ... | @@ -1051,7 +1053,7 @@ pub const Scope = struct { |
| 1051 | 1053 | } }, |
| 1052 | 1054 | }); |
| 1053 | 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 | 1059 | pub fn addCall( |
| ... | ... | @@ -1064,28 +1066,28 @@ pub const Scope = struct { |
| 1064 | 1066 | ) !zir.Inst.Ref { |
| 1065 | 1067 | assert(callee != .none); |
| 1066 | 1068 | assert(src_node != 0); |
| 1067 | const gpa = gz.zir_code.gpa; | |
| 1069 | const gpa = gz.astgen.mod.gpa; | |
| 1068 | 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); | |
| 1070 | try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len + | |
| 1071 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | |
| 1072 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | |
| 1071 | 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 | 1076 | .callee = callee, |
| 1075 | 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); | |
| 1080 | gz.zir_code.instructions.appendAssumeCapacity(.{ | |
| 1081 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | |
| 1082 | gz.astgen.instructions.appendAssumeCapacity(.{ | |
| 1081 | 1083 | .tag = tag, |
| 1082 | 1084 | .data = .{ .pl_node = .{ |
| 1083 | .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node), | |
| 1085 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), | |
| 1084 | 1086 | .payload_index = payload_index, |
| 1085 | 1087 | } }, |
| 1086 | 1088 | }); |
| 1087 | 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 | 1093 | /// Note that this returns a `zir.Inst.Index` not a ref. |
| ... | ... | @@ -1096,12 +1098,12 @@ pub const Scope = struct { |
| 1096 | 1098 | lhs: zir.Inst.Ref, |
| 1097 | 1099 | ) !zir.Inst.Index { |
| 1098 | 1100 | assert(lhs != .none); |
| 1099 | const gpa = gz.zir_code.gpa; | |
| 1101 | const gpa = gz.astgen.mod.gpa; | |
| 1100 | 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); | |
| 1104 | gz.zir_code.instructions.appendAssumeCapacity(.{ | |
| 1105 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | |
| 1106 | gz.astgen.instructions.appendAssumeCapacity(.{ | |
| 1105 | 1107 | .tag = tag, |
| 1106 | 1108 | .data = .{ .bool_br = .{ |
| 1107 | 1109 | .lhs = lhs, |
| ... | ... | @@ -1131,7 +1133,7 @@ pub const Scope = struct { |
| 1131 | 1133 | .tag = tag, |
| 1132 | 1134 | .data = .{ .un_node = .{ |
| 1133 | 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 | 1145 | src_node: ast.Node.Index, |
| 1144 | 1146 | extra: anytype, |
| 1145 | 1147 | ) !zir.Inst.Ref { |
| 1146 | const gpa = gz.zir_code.gpa; | |
| 1148 | const gpa = gz.astgen.mod.gpa; | |
| 1147 | 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); | |
| 1151 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | |
| 1152 | gz.zir_code.instructions.appendAssumeCapacity(.{ | |
| 1152 | const payload_index = try gz.astgen.addExtra(extra); | |
| 1153 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | |
| 1154 | gz.astgen.instructions.appendAssumeCapacity(.{ | |
| 1153 | 1155 | .tag = tag, |
| 1154 | 1156 | .data = .{ .pl_node = .{ |
| 1155 | .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node), | |
| 1157 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), | |
| 1156 | 1158 | .payload_index = payload_index, |
| 1157 | 1159 | } }, |
| 1158 | 1160 | }); |
| 1159 | 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 | 1165 | pub fn addArrayTypeSentinel( |
| ... | ... | @@ -1166,16 +1168,16 @@ pub const Scope = struct { |
| 1166 | 1168 | sentinel: zir.Inst.Ref, |
| 1167 | 1169 | elem_type: zir.Inst.Ref, |
| 1168 | 1170 | ) !zir.Inst.Ref { |
| 1169 | const gpa = gz.zir_code.gpa; | |
| 1171 | const gpa = gz.astgen.mod.gpa; | |
| 1170 | 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 | 1176 | .sentinel = sentinel, |
| 1175 | 1177 | .elem_type = elem_type, |
| 1176 | 1178 | }); |
| 1177 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | |
| 1178 | gz.zir_code.instructions.appendAssumeCapacity(.{ | |
| 1179 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | |
| 1180 | gz.astgen.instructions.appendAssumeCapacity(.{ | |
| 1179 | 1181 | .tag = .array_type_sentinel, |
| 1180 | 1182 | .data = .{ .array_type_sentinel = .{ |
| 1181 | 1183 | .len = len, |
| ... | ... | @@ -1183,7 +1185,7 @@ pub const Scope = struct { |
| 1183 | 1185 | } }, |
| 1184 | 1186 | }); |
| 1185 | 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 | 1191 | pub fn addUnTok( |
| ... | ... | @@ -1198,7 +1200,7 @@ pub const Scope = struct { |
| 1198 | 1200 | .tag = tag, |
| 1199 | 1201 | .data = .{ .un_tok = .{ |
| 1200 | 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 | 1216 | .tag = tag, |
| 1215 | 1217 | .data = .{ .str_tok = .{ |
| 1216 | 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 | 1262 | return gz.add(.{ |
| 1261 | 1263 | .tag = tag, |
| 1262 | 1264 | .data = .{ .pl_node = .{ |
| 1263 | .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node), | |
| 1265 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), | |
| 1264 | 1266 | .payload_index = decl_index, |
| 1265 | 1267 | } }, |
| 1266 | 1268 | }); |
| ... | ... | @@ -1274,7 +1276,7 @@ pub const Scope = struct { |
| 1274 | 1276 | ) !zir.Inst.Ref { |
| 1275 | 1277 | return gz.add(.{ |
| 1276 | 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 | 1300 | /// Does *not* append the block instruction to the scope. |
| 1299 | 1301 | /// Leaves the `payload_index` field undefined. |
| 1300 | 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); | |
| 1302 | try gz.zir_code.instructions.append(gz.zir_code.gpa, .{ | |
| 1303 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | |
| 1304 | const gpa = gz.astgen.mod.gpa; | |
| 1305 | try gz.astgen.instructions.append(gpa, .{ | |
| 1303 | 1306 | .tag = tag, |
| 1304 | 1307 | .data = .{ .pl_node = .{ |
| 1305 | .src_node = gz.zir_code.decl.nodeIndexToRelative(node), | |
| 1308 | .src_node = gz.astgen.decl.nodeIndexToRelative(node), | |
| 1306 | 1309 | .payload_index = undefined, |
| 1307 | 1310 | } }, |
| 1308 | 1311 | }); |
| ... | ... | @@ -1312,12 +1315,13 @@ pub const Scope = struct { |
| 1312 | 1315 | /// Note that this returns a `zir.Inst.Index` not a ref. |
| 1313 | 1316 | /// Leaves the `payload_index` field undefined. |
| 1314 | 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); | |
| 1316 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); | |
| 1317 | try gz.zir_code.instructions.append(gz.zir_code.gpa, .{ | |
| 1318 | const gpa = gz.astgen.mod.gpa; | |
| 1319 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | |
| 1320 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | |
| 1321 | try gz.astgen.instructions.append(gpa, .{ | |
| 1318 | 1322 | .tag = tag, |
| 1319 | 1323 | .data = .{ .pl_node = .{ |
| 1320 | .src_node = gz.zir_code.decl.nodeIndexToRelative(node), | |
| 1324 | .src_node = gz.astgen.decl.nodeIndexToRelative(node), | |
| 1321 | 1325 | .payload_index = undefined, |
| 1322 | 1326 | } }, |
| 1323 | 1327 | }); |
| ... | ... | @@ -1326,16 +1330,16 @@ pub const Scope = struct { |
| 1326 | 1330 | } |
| 1327 | 1331 | |
| 1328 | 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 | 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 | 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); | |
| 1338 | gz.zir_code.instructions.appendAssumeCapacity(inst); | |
| 1341 | const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); | |
| 1342 | gz.astgen.instructions.appendAssumeCapacity(inst); | |
| 1339 | 1343 | gz.instructions.appendAssumeCapacity(new_index); |
| 1340 | 1344 | return new_index; |
| 1341 | 1345 | } |
| ... | ... | @@ -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 | 1385 | /// This struct holds data necessary to construct API-facing `AllErrors.Message`. |
| 1476 | 1386 | /// Its memory is managed with the general purpose allocator so that they |
| 1477 | 1387 | /// can be created and destroyed in response to incremental updates. |
| ... | ... | @@ -2102,18 +2012,18 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { |
| 2102 | 2012 | defer analysis_arena.deinit(); |
| 2103 | 2013 | |
| 2104 | 2014 | var code: zir.Code = blk: { |
| 2105 | var wip_zir_code = try mod.initAstGen(decl, &analysis_arena.allocator); | |
| 2106 | defer wip_zir_code.deinit(); | |
| 2015 | var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator); | |
| 2016 | defer astgen.deinit(); | |
| 2107 | 2017 | |
| 2108 | 2018 | var gen_scope: Scope.GenZir = .{ |
| 2109 | 2019 | .force_comptime = true, |
| 2110 | 2020 | .parent = &decl.container.base, |
| 2111 | .zir_code = &wip_zir_code, | |
| 2021 | .astgen = &astgen, | |
| 2112 | 2022 | }; |
| 2113 | 2023 | defer gen_scope.instructions.deinit(mod.gpa); |
| 2114 | 2024 | |
| 2115 | 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 | 2028 | const code = try gen_scope.finish(); |
| 2119 | 2029 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { |
| ... | ... | @@ -2175,13 +2085,13 @@ fn astgenAndSemaFn( |
| 2175 | 2085 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 2176 | 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); | |
| 2179 | defer fn_type_wip_zir_code.deinit(); | |
| 2088 | var fn_type_astgen = try AstGen.init(mod, decl, &fn_type_scope_arena.allocator); | |
| 2089 | defer fn_type_astgen.deinit(); | |
| 2180 | 2090 | |
| 2181 | 2091 | var fn_type_scope: Scope.GenZir = .{ |
| 2182 | 2092 | .force_comptime = true, |
| 2183 | 2093 | .parent = &decl.container.base, |
| 2184 | .zir_code = &fn_type_wip_zir_code, | |
| 2094 | .astgen = &fn_type_astgen, | |
| 2185 | 2095 | }; |
| 2186 | 2096 | defer fn_type_scope.instructions.deinit(mod.gpa); |
| 2187 | 2097 | |
| ... | ... | @@ -2223,7 +2133,7 @@ fn astgenAndSemaFn( |
| 2223 | 2133 | const param_type_node = param.type_expr; |
| 2224 | 2134 | assert(param_type_node != 0); |
| 2225 | 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 | 2138 | assert(param_type_i == param_count); |
| 2229 | 2139 | } |
| ... | ... | @@ -2292,7 +2202,7 @@ fn astgenAndSemaFn( |
| 2292 | 2202 | if (token_tags[maybe_bang] == .bang) { |
| 2293 | 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 | 2206 | mod, |
| 2297 | 2207 | &fn_type_scope.base, |
| 2298 | 2208 | .{ .ty = .type_type }, |
| ... | ... | @@ -2308,7 +2218,7 @@ fn astgenAndSemaFn( |
| 2308 | 2218 | // TODO instead of enum literal type, this needs to be the |
| 2309 | 2219 | // std.builtin.CallingConvention enum. We need to implement importing other files |
| 2310 | 2220 | // and enums in order to fix this. |
| 2311 | try astgen.comptimeExpr( | |
| 2221 | try AstGen.comptimeExpr( | |
| 2312 | 2222 | mod, |
| 2313 | 2223 | &fn_type_scope.base, |
| 2314 | 2224 | .{ .ty = .enum_literal_type }, |
| ... | ... | @@ -2408,21 +2318,21 @@ fn astgenAndSemaFn( |
| 2408 | 2318 | |
| 2409 | 2319 | const fn_zir: zir.Code = blk: { |
| 2410 | 2320 | // We put the ZIR inside the Decl arena. |
| 2411 | var wip_zir_code = try mod.initAstGen(decl, &decl_arena.allocator); | |
| 2412 | wip_zir_code.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count); | |
| 2413 | defer wip_zir_code.deinit(); | |
| 2321 | var astgen = try AstGen.init(mod, decl, &decl_arena.allocator); | |
| 2322 | astgen.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count); | |
| 2323 | defer astgen.deinit(); | |
| 2414 | 2324 | |
| 2415 | 2325 | var gen_scope: Scope.GenZir = .{ |
| 2416 | 2326 | .force_comptime = false, |
| 2417 | 2327 | .parent = &decl.container.base, |
| 2418 | .zir_code = &wip_zir_code, | |
| 2328 | .astgen = &astgen, | |
| 2419 | 2329 | }; |
| 2420 | 2330 | defer gen_scope.instructions.deinit(mod.gpa); |
| 2421 | 2331 | |
| 2422 | 2332 | // Iterate over the parameters. We put the param names as the first N |
| 2423 | 2333 | // items inside `extra` so that debug info later can refer to the parameter names |
| 2424 | 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 | 2337 | var params_scope = &gen_scope.base; |
| 2428 | 2338 | var i: usize = 0; |
| ... | ... | @@ -2443,18 +2353,18 @@ fn astgenAndSemaFn( |
| 2443 | 2353 | |
| 2444 | 2354 | // Additionally put the param name into `string_bytes` and reference it with |
| 2445 | 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); | |
| 2447 | wip_zir_code.extra.appendAssumeCapacity(str_index); | |
| 2448 | const used_bytes = wip_zir_code.string_bytes.items.len; | |
| 2449 | try wip_zir_code.string_bytes.ensureCapacity(mod.gpa, used_bytes + param_name.len + 1); | |
| 2450 | wip_zir_code.string_bytes.appendSliceAssumeCapacity(param_name); | |
| 2451 | wip_zir_code.string_bytes.appendAssumeCapacity(0); | |
| 2356 | const str_index = @intCast(u32, astgen.string_bytes.items.len); | |
| 2357 | astgen.extra.appendAssumeCapacity(str_index); | |
| 2358 | const used_bytes = astgen.string_bytes.items.len; | |
| 2359 | try astgen.string_bytes.ensureCapacity(mod.gpa, used_bytes + param_name.len + 1); | |
| 2360 | astgen.string_bytes.appendSliceAssumeCapacity(param_name); | |
| 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 | 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 | 2368 | .isNoReturn()) |
| 2459 | 2369 | { |
| 2460 | 2370 | // astgen uses result location semantics to coerce return operands. |
| ... | ... | @@ -2615,21 +2525,21 @@ fn astgenAndSemaVarDecl( |
| 2615 | 2525 | var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 2616 | 2526 | defer gen_scope_arena.deinit(); |
| 2617 | 2527 | |
| 2618 | var wip_zir_code = try mod.initAstGen(decl, &gen_scope_arena.allocator); | |
| 2619 | defer wip_zir_code.deinit(); | |
| 2528 | var astgen = try AstGen.init(mod, decl, &gen_scope_arena.allocator); | |
| 2529 | defer astgen.deinit(); | |
| 2620 | 2530 | |
| 2621 | 2531 | var gen_scope: Scope.GenZir = .{ |
| 2622 | 2532 | .force_comptime = true, |
| 2623 | 2533 | .parent = &decl.container.base, |
| 2624 | .zir_code = &wip_zir_code, | |
| 2534 | .astgen = &astgen, | |
| 2625 | 2535 | }; |
| 2626 | 2536 | defer gen_scope.instructions.deinit(mod.gpa); |
| 2627 | 2537 | |
| 2628 | 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), | |
| 2538 | const init_result_loc: AstGen.ResultLoc = if (var_decl.ast.type_node != 0) .{ | |
| 2539 | .ty = try AstGen.expr(mod, &gen_scope.base, .{ .ty = .type_type }, var_decl.ast.type_node), | |
| 2630 | 2540 | } else .none; |
| 2631 | 2541 | |
| 2632 | const init_inst = try astgen.comptimeExpr( | |
| 2542 | const init_inst = try AstGen.comptimeExpr( | |
| 2633 | 2543 | mod, |
| 2634 | 2544 | &gen_scope.base, |
| 2635 | 2545 | init_result_loc, |
| ... | ... | @@ -2684,17 +2594,17 @@ fn astgenAndSemaVarDecl( |
| 2684 | 2594 | var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 2685 | 2595 | defer type_scope_arena.deinit(); |
| 2686 | 2596 | |
| 2687 | var wip_zir_code = try mod.initAstGen(decl, &type_scope_arena.allocator); | |
| 2688 | defer wip_zir_code.deinit(); | |
| 2597 | var astgen = try AstGen.init(mod, decl, &type_scope_arena.allocator); | |
| 2598 | defer astgen.deinit(); | |
| 2689 | 2599 | |
| 2690 | 2600 | var type_scope: Scope.GenZir = .{ |
| 2691 | 2601 | .force_comptime = true, |
| 2692 | 2602 | .parent = &decl.container.base, |
| 2693 | .zir_code = &wip_zir_code, | |
| 2603 | .astgen = &astgen, | |
| 2694 | 2604 | }; |
| 2695 | 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 | 2608 | _ = try type_scope.addBreak(.break_inline, 0, var_type); |
| 2699 | 2609 | |
| 2700 | 2610 | var code = try type_scope.finish(); |
| ... | ... | @@ -3796,21 +3706,21 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In |
| 3796 | 3706 | }, |
| 3797 | 3707 | .gen_zir => { |
| 3798 | 3708 | const gen_zir = scope.cast(Scope.GenZir).?; |
| 3799 | gen_zir.zir_code.decl.analysis = .sema_failure; | |
| 3800 | gen_zir.zir_code.decl.generation = mod.generation; | |
| 3801 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.zir_code.decl, err_msg); | |
| 3709 | gen_zir.astgen.decl.analysis = .sema_failure; | |
| 3710 | gen_zir.astgen.decl.generation = mod.generation; | |
| 3711 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg); | |
| 3802 | 3712 | }, |
| 3803 | 3713 | .local_val => { |
| 3804 | 3714 | const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir; |
| 3805 | gen_zir.zir_code.decl.analysis = .sema_failure; | |
| 3806 | gen_zir.zir_code.decl.generation = mod.generation; | |
| 3807 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.zir_code.decl, err_msg); | |
| 3715 | gen_zir.astgen.decl.analysis = .sema_failure; | |
| 3716 | gen_zir.astgen.decl.generation = mod.generation; | |
| 3717 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg); | |
| 3808 | 3718 | }, |
| 3809 | 3719 | .local_ptr => { |
| 3810 | 3720 | const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir; |
| 3811 | gen_zir.zir_code.decl.analysis = .sema_failure; | |
| 3812 | gen_zir.zir_code.decl.generation = mod.generation; | |
| 3813 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.zir_code.decl, err_msg); | |
| 3721 | gen_zir.astgen.decl.analysis = .sema_failure; | |
| 3722 | gen_zir.astgen.decl.generation = mod.generation; | |
| 3723 | mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg); | |
| 3814 | 3724 | }, |
| 3815 | 3725 | .file => unreachable, |
| 3816 | 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 | 4266 | } |
| 4267 | 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 | 4272 | const MacroCtx = struct { |
src/zir.zig+2-2| ... | ... | @@ -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 | 2 | //! untyped IR instructions. Next, Sema.zig processes these into TZIR. |
| 3 | 3 | |
| 4 | 4 | const std = @import("std"); |
| ... | ... | @@ -491,7 +491,7 @@ pub const Inst = struct { |
| 491 | 491 | store_to_block_ptr, |
| 492 | 492 | /// Same as `store` but the type of the value being stored will be used to infer |
| 493 | 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 | 495 | /// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr` |
| 496 | 496 | /// without changing the data. |
| 497 | 497 | store_to_inferred_ptr, |