diff options
| author | Loris Cro <kappaloris@gmail.com> | 2023-06-18 09:06:40 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-06-18 09:06:40 +0200 |
| commit | 216ef10dc471e4db60a30208be178d6c59efeaaf (patch) | |
| tree | 8c239dab283ae9cb3b7fe099bae240bcc53f894e /src/AstGen.zig | |
| parent | 0fc1d396495c1ab482197021dedac8bea3f9401c (diff) | |
| parent | 729a051e9e38674233190aea23c0ac8c134f2d67 (diff) | |
| download | zig-216ef10dc471e4db60a30208be178d6c59efeaaf.tar.gz zig-216ef10dc471e4db60a30208be178d6c59efeaaf.zip | |
Merge branch 'master' into autodoc-searchkey
Diffstat (limited to 'src/AstGen.zig')
| -rw-r--r-- | src/AstGen.zig | 528 |
1 files changed, 335 insertions, 193 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig index aece3eafec..86bd0fd4f4 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -849,10 +849,35 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE return rvalue(gz, ri, result, node); }, .slice => { + const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); + const lhs_node = node_datas[node].lhs; + const lhs_tag = node_tags[lhs_node]; + const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel; + const lhs_is_open_slice = lhs_tag == .slice_open or + (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0); + if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) { + const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs); + + const start = if (lhs_is_slice_sentinel) start: { + const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel); + break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start); + } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs); + + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; + try emitDbgStmt(gz, cursor); + const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{ + .lhs = lhs, + .start = start, + .len = len, + .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), + .sentinel = .none, + }); + return rvalue(gz, ri, result, node); + } const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); - const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end); try emitDbgStmt(gz, cursor); @@ -864,10 +889,36 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE return rvalue(gz, ri, result, node); }, .slice_sentinel => { + const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); + const lhs_node = node_datas[node].lhs; + const lhs_tag = node_tags[lhs_node]; + const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel; + const lhs_is_open_slice = lhs_tag == .slice_open or + (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0); + if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) { + const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs); + + const start = if (lhs_is_slice_sentinel) start: { + const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel); + break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start); + } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs); + + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; + const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel); + try emitDbgStmt(gz, cursor); + const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{ + .lhs = lhs, + .start = start, + .len = len, + .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), + .sentinel = sentinel, + }); + return rvalue(gz, ri, result, node); + } const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); - const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel); @@ -2431,7 +2482,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As switch (zir_tags[inst]) { // For some instructions, modify the zir data // so we can avoid a separate ensure_result_used instruction. - .call => { + .call, .field_call => { const extra_index = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index; const slot = &gz.astgen.extra.items[extra_index]; var flags = @bitCast(Zir.Inst.Call.Flags, slot.*); @@ -2506,7 +2557,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .field_ptr, .field_ptr_init, .field_val, - .field_call_bind, .field_ptr_named, .field_val_named, .func, @@ -2557,15 +2607,10 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .slice_start, .slice_end, .slice_sentinel, + .slice_length, .import, .switch_block, - .switch_cond, - .switch_cond_ref, - .switch_capture, - .switch_capture_ref, - .switch_capture_multi, - .switch_capture_multi_ref, - .switch_capture_tag, + .switch_block_ref, .struct_init_empty, .struct_init, .struct_init_ref, @@ -2905,7 +2950,15 @@ fn deferStmt( const sub_scope = if (!have_err_code) &defer_gen.base else blk: { try gz.addDbgBlockBegin(); const ident_name = try gz.astgen.identAsString(payload_token); - remapped_err_code = @intCast(u32, try gz.astgen.instructions.addOne(gz.astgen.gpa)); + remapped_err_code = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); + try gz.astgen.instructions.append(gz.astgen.gpa, .{ + .tag = .extended, + .data = .{ .extended = .{ + .opcode = .value_placeholder, + .small = undefined, + .operand = undefined, + } }, + }); const remapped_err_code_ref = Zir.indexToRef(remapped_err_code); local_val_scope = .{ .parent = &defer_gen.base, @@ -2923,11 +2976,27 @@ fn deferStmt( if (have_err_code) try gz.addDbgBlockEnd(); _ = try defer_gen.addBreak(.break_inline, 0, .void_value); + // We must handle ref_table for remapped_err_code manually. const body = defer_gen.instructionsSlice(); - const body_len = gz.astgen.countBodyLenAfterFixups(body); + const body_len = blk: { + var refs: u32 = 0; + if (have_err_code) { + var cur_inst = remapped_err_code; + while (gz.astgen.ref_table.get(cur_inst)) |ref_inst| { + refs += 1; + cur_inst = ref_inst; + } + } + break :blk gz.astgen.countBodyLenAfterFixups(body) + refs; + }; const index = @intCast(u32, gz.astgen.extra.items.len); try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len); + if (have_err_code) { + if (gz.astgen.ref_table.fetchRemove(remapped_err_code)) |kv| { + gz.astgen.appendPossiblyRefdBodyInst(&gz.astgen.extra, kv.value); + } + } gz.astgen.appendBodyWithFixups(body); const defer_scope = try block_arena.create(Scope.Defer); @@ -3859,7 +3928,7 @@ fn fnDecl( var section_gz = decl_gz.makeSubBlock(params_scope); defer section_gz.unstack(); const section_ref: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: { - const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = .const_slice_u8_type } }, fn_proto.ast.section_expr); + const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, fn_proto.ast.section_expr); if (section_gz.instructionsSlice().len == 0) { // In this case we will send a len=0 body which can be encoded more efficiently. break :inst inst; @@ -4062,7 +4131,7 @@ fn globalVarDecl( break :inst try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .address_space_type } }, var_decl.ast.addrspace_node); }; const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: { - break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .const_slice_u8_type } }, var_decl.ast.section_node); + break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .slice_const_u8_type } }, var_decl.ast.section_node); }; const has_section_or_addrspace = section_inst != .none or addrspace_inst != .none; wip_members.nextDecl(is_pub, is_export, align_inst != .none, has_section_or_addrspace); @@ -4422,7 +4491,7 @@ fn testDecl( .cc_gz = null, .align_ref = .none, .align_gz = null, - .ret_ref = .void_type, + .ret_ref = .anyerror_void_error_union_type, .ret_gz = null, .section_ref = .none, .section_gz = null, @@ -4435,7 +4504,7 @@ fn testDecl( .body_gz = &fn_block, .lib_name = 0, .is_var_args = false, - .is_inferred_error = true, + .is_inferred_error = false, .is_test = true, .is_extern = false, .is_noinline = false, @@ -6640,6 +6709,7 @@ fn switchExpr( // for the following variables, make note of the special prong AST node index, // and bail out with a compile error if there are multiple special prongs present. var any_payload_is_ref = false; + var any_has_tag_capture = false; var scalar_cases_len: u32 = 0; var multi_cases_len: u32 = 0; var inline_cases_len: u32 = 0; @@ -6650,8 +6720,12 @@ fn switchExpr( for (case_nodes) |case_node| { const case = tree.fullSwitchCase(case_node).?; if (case.payload_token) |payload_token| { - if (token_tags[payload_token] == .asterisk) { + const ident = if (token_tags[payload_token] == .asterisk) blk: { any_payload_is_ref = true; + break :blk payload_token + 1; + } else payload_token; + if (token_tags[ident + 1] == .comma) { + any_has_tag_capture = true; } } // Check for else/`_` prong. @@ -6760,13 +6834,7 @@ fn switchExpr( const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node); - const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond; - const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node); - // Sema expects a dbg_stmt immediately after switch_cond(_ref) - try emitDbgStmt(parent_gz, operand_lc); - // We need the type of the operand to use as the result location for all the prong items. - const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node); - const item_ri: ResultInfo = .{ .rl = .{ .ty = cond_ty_inst } }; + const item_ri: ResultInfo = .{ .rl = .none }; // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti, // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with @@ -6785,13 +6853,30 @@ fn switchExpr( block_scope.instructions_top = GenZir.unstacked_top; block_scope.setBreakResultInfo(ri); + // Sema expects a dbg_stmt immediately before switch_block(_ref) + try emitDbgStmt(parent_gz, operand_lc); // This gets added to the parent block later, after the item expressions. - const switch_block = try parent_gz.makeBlockInst(.switch_block, switch_node); + const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block; + const switch_block = try parent_gz.makeBlockInst(switch_tag, switch_node); // We re-use this same scope for all cases, including the special prong, if any. var case_scope = parent_gz.makeSubBlock(&block_scope.base); case_scope.instructions_top = GenZir.unstacked_top; + // If any prong has an inline tag capture, allocate a shared dummy instruction for it + const tag_inst = if (any_has_tag_capture) tag_inst: { + const inst = @intCast(Zir.Inst.Index, astgen.instructions.len); + try astgen.instructions.append(astgen.gpa, .{ + .tag = .extended, + .data = .{ .extended = .{ + .opcode = .value_placeholder, + .small = undefined, + .operand = undefined, + } }, // TODO rename opcode + }); + break :tag_inst inst; + } else undefined; + // In this pass we generate all the item and prong expressions. var multi_case_index: u32 = 0; var scalar_case_index: u32 = 0; @@ -6805,17 +6890,22 @@ fn switchExpr( var dbg_var_inst: Zir.Inst.Ref = undefined; var dbg_var_tag_name: ?u32 = null; var dbg_var_tag_inst: Zir.Inst.Ref = undefined; - var capture_inst: Zir.Inst.Index = 0; - var tag_inst: Zir.Inst.Index = 0; + var has_tag_capture = false; var capture_val_scope: Scope.LocalVal = undefined; var tag_scope: Scope.LocalVal = undefined; + + var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none; + const sub_scope = blk: { const payload_token = case.payload_token orelse break :blk &case_scope.base; const ident = if (token_tags[payload_token] == .asterisk) payload_token + 1 else payload_token; + const is_ptr = ident != payload_token; + capture = if (is_ptr) .by_ref else .by_val; + const ident_slice = tree.tokenSlice(ident); var payload_sub_scope: *Scope = undefined; if (mem.eql(u8, ident_slice, "_")) { @@ -6824,53 +6914,18 @@ fn switchExpr( } payload_sub_scope = &case_scope.base; } else { - if (case_node == special_node) { - const capture_tag: Zir.Inst.Tag = if (is_ptr) - .switch_capture_ref - else - .switch_capture; - capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len); - try astgen.instructions.append(gpa, .{ - .tag = capture_tag, - .data = .{ - .switch_capture = .{ - .switch_inst = switch_block, - // Max int communicates that this is the else/underscore prong. - .prong_index = std.math.maxInt(u32), - }, - }, - }); - } else { - const is_multi_case_bits: u2 = @boolToInt(is_multi_case); - const is_ptr_bits: u2 = @boolToInt(is_ptr); - const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) { - 0b00 => .switch_capture, - 0b01 => .switch_capture_ref, - 0b10 => .switch_capture_multi, - 0b11 => .switch_capture_multi_ref, - }; - const capture_index = if (is_multi_case) multi_case_index else scalar_case_index; - capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len); - try astgen.instructions.append(gpa, .{ - .tag = capture_tag, - .data = .{ .switch_capture = .{ - .switch_inst = switch_block, - .prong_index = capture_index, - } }, - }); - } const capture_name = try astgen.identAsString(ident); try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture); capture_val_scope = .{ .parent = &case_scope.base, .gen_zir = &case_scope, .name = capture_name, - .inst = indexToRef(capture_inst), + .inst = indexToRef(switch_block), .token_src = payload_token, .id_cat = .capture, }; dbg_var_name = capture_name; - dbg_var_inst = indexToRef(capture_inst); + dbg_var_inst = indexToRef(switch_block); payload_sub_scope = &capture_val_scope.base; } @@ -6886,14 +6941,9 @@ fn switchExpr( } const tag_name = try astgen.identAsString(tag_token); try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture"); - tag_inst = @intCast(Zir.Inst.Index, astgen.instructions.len); - try astgen.instructions.append(gpa, .{ - .tag = .switch_capture_tag, - .data = .{ .un_tok = .{ - .operand = cond, - .src_tok = case_scope.tokenIndexToRelative(tag_token), - } }, - }); + + assert(any_has_tag_capture); + has_tag_capture = true; tag_scope = .{ .parent = payload_sub_scope, @@ -6959,8 +7009,6 @@ fn switchExpr( case_scope.instructions_top = parent_gz.instructions.items.len; defer case_scope.unstack(); - if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst); - if (tag_inst != 0) try case_scope.instructions.append(gpa, tag_inst); try case_scope.addDbgBlockBegin(); if (dbg_var_name) |some| { try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst); @@ -6978,10 +7026,42 @@ fn switchExpr( } const case_slice = case_scope.instructionsSlice(); - const body_len = astgen.countBodyLenAfterFixups(case_slice); + // Since we use the switch_block instruction itself to refer to the + // capture, which will not be added to the child block, we need to + // handle ref_table manually, and the same for the inline tag + // capture instruction. + const refs_len = refs: { + var n: usize = 0; + var check_inst = switch_block; + while (astgen.ref_table.get(check_inst)) |ref_inst| { + n += 1; + check_inst = ref_inst; + } + if (has_tag_capture) { + check_inst = tag_inst; + while (astgen.ref_table.get(check_inst)) |ref_inst| { + n += 1; + check_inst = ref_inst; + } + } + break :refs n; + }; + const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice); try payloads.ensureUnusedCapacity(gpa, body_len); - const inline_bit = @as(u32, @boolToInt(case.inline_token != null)) << 31; - payloads.items[body_len_index] = body_len | inline_bit; + payloads.items[body_len_index] = @bitCast(u32, Zir.Inst.SwitchBlock.ProngInfo{ + .body_len = @intCast(u28, body_len), + .capture = capture, + .is_inline = case.inline_token != null, + .has_tag_capture = has_tag_capture, + }); + if (astgen.ref_table.fetchRemove(switch_block)) |kv| { + appendPossiblyRefdBodyInst(astgen, payloads, kv.value); + } + if (has_tag_capture) { + if (astgen.ref_table.fetchRemove(tag_inst)) |kv| { + appendPossiblyRefdBodyInst(astgen, payloads, kv.value); + } + } appendBodyWithFixupsArrayList(astgen, payloads, case_slice); } } @@ -6990,14 +7070,16 @@ fn switchExpr( try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len + @boolToInt(multi_cases_len != 0) + + @boolToInt(any_has_tag_capture) + payloads.items.len - case_table_end); const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{ - .operand = cond, + .operand = raw_operand, .bits = Zir.Inst.SwitchBlock.Bits{ .has_multi_cases = multi_cases_len != 0, .has_else = special_prong == .@"else", .has_under = special_prong == .under, + .any_has_tag_capture = any_has_tag_capture, .scalar_cases_len = @intCast(Zir.Inst.SwitchBlock.Bits.ScalarCasesLen, scalar_cases_len), }, }); @@ -7006,6 +7088,10 @@ fn switchExpr( astgen.extra.appendAssumeCapacity(multi_cases_len); } + if (any_has_tag_capture) { + astgen.extra.appendAssumeCapacity(tag_inst); + } + const zir_datas = astgen.instructions.items(.data); const zir_tags = astgen.instructions.items(.tag); @@ -7028,7 +7114,7 @@ fn switchExpr( end_index += 3 + items_len + 2 * ranges_len; } - const body_len = @truncate(u31, payloads.items[body_len_index]); + const body_len = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, payloads.items[body_len_index]).body_len; end_index += body_len; switch (strat.tag) { @@ -7614,14 +7700,16 @@ fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token .invalid_digit => |info| return astgen.failOff(token, @intCast(u32, info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }), .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(u32, i), "invalid digit '{c}' in exponent", .{bytes[i]}), .duplicate_exponent => |i| return astgen.failOff(token, @intCast(u32, i), "duplicate exponent", .{}), - .invalid_hex_exponent => |i| return astgen.failOff(token, @intCast(u32, i), "hex exponent in decimal float", .{}), .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(u32, i), "expected digit before exponent", .{}), .special_after_underscore => |i| return astgen.failOff(token, @intCast(u32, i), "expected digit before '{c}'", .{bytes[i]}), .trailing_special => |i| return astgen.failOff(token, @intCast(u32, i), "expected digit after '{c}'", .{bytes[i - 1]}), .trailing_underscore => |i| return astgen.failOff(token, @intCast(u32, i), "trailing digit separator", .{}), .duplicate_period => unreachable, // Validated by tokenizer .invalid_character => unreachable, // Validated by tokenizer - .invalid_exponent_sign => unreachable, // Validated by tokenizer + .invalid_exponent_sign => |i| { + assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer + return astgen.failOff(token, @intCast(u32, i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] }); + }, } } @@ -7801,7 +7889,7 @@ fn unionInit( params: []const Ast.Node.Index, ) InnerError!Zir.Inst.Ref { const union_type = try typeExpr(gz, scope, params[0]); - const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[1]); + const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[1]); const field_type = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{ .container_type = union_type, .field_name = field_name, @@ -7907,6 +7995,48 @@ fn typeOf( return rvalue(gz, ri, typeof_inst, node); } +fn minMax( + gz: *GenZir, + scope: *Scope, + ri: ResultInfo, + node: Ast.Node.Index, + args: []const Ast.Node.Index, + comptime op: enum { min, max }, +) InnerError!Zir.Inst.Ref { + const astgen = gz.astgen; + if (args.len < 2) { + return astgen.failNode(node, "expected at least 2 arguments, found 0", .{}); + } + if (args.len == 2) { + const tag: Zir.Inst.Tag = switch (op) { + .min => .min, + .max => .max, + }; + const a = try expr(gz, scope, .{ .rl = .none }, args[0]); + const b = try expr(gz, scope, .{ .rl = .none }, args[1]); + const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ + .lhs = a, + .rhs = b, + }); + return rvalue(gz, ri, result, node); + } + const payload_index = try addExtra(astgen, Zir.Inst.NodeMultiOp{ + .src_node = gz.nodeIndexToRelative(node), + }); + var extra_index = try reserveExtra(gz.astgen, args.len); + for (args) |arg| { + const arg_ref = try expr(gz, scope, .{ .rl = .none }, arg); + astgen.extra.items[extra_index] = @enumToInt(arg_ref); + extra_index += 1; + } + const tag: Zir.Inst.Extended = switch (op) { + .min => .min_multi, + .max => .max_multi, + }; + const result = try gz.addExtendedMultiOpPayloadIndex(tag, payload_index, args.len); + return rvalue(gz, ri, result, node); +} + fn builtinCall( gz: *GenZir, scope: *Scope, @@ -7981,12 +8111,12 @@ fn builtinCall( if (ri.rl == .ref) { return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]), - .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[1]), + .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[1]), }); } const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{ .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), - .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[1]), + .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[1]), }); return rvalue(gz, ri, result, node); }, @@ -7997,6 +8127,8 @@ fn builtinCall( .TypeOf => return typeOf( gz, scope, ri, node, params), .union_init => return unionInit(gz, scope, ri, node, params), .c_import => return cImport( gz, scope, node, params[0]), + .min => return minMax( gz, scope, ri, node, params, .min), + .max => return minMax( gz, scope, ri, node, params, .max), // zig fmt: on .@"export" => { @@ -8150,11 +8282,11 @@ fn builtinCall( .align_of => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of), .ptr_to_int => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .ptr_to_int), - .compile_error => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .compile_error), + .compile_error => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[0], .compile_error), .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota), .enum_to_int => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .enum_to_int), .bool_to_int => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .bool_to_int), - .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .embed_file), + .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[0], .embed_file), .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .anyerror_type } }, params[0], .error_name), .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_runtime_safety), .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt), @@ -8213,12 +8345,12 @@ fn builtinCall( }, .panic => { try emitDbgNode(gz, node); - return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .panic); + return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[0], .panic); }, .trap => { try emitDbgNode(gz, node); _ = try gz.addNode(.trap, node); - return rvalue(gz, ri, .void_value, node); + return rvalue(gz, ri, .unreachable_value, node); }, .error_to_int => { const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); @@ -8329,7 +8461,7 @@ fn builtinCall( }, .c_define => { if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{}); - const name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0]); + const name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[0]); const value = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]); const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{ .node = gz.nodeIndexToRelative(node), @@ -8358,25 +8490,6 @@ fn builtinCall( return rvalue(gz, ri, result, node); }, - .max => { - const a = try expr(gz, scope, .{ .rl = .none }, params[0]); - const b = try expr(gz, scope, .{ .rl = .none }, params[1]); - const result = try gz.addPlNode(.max, node, Zir.Inst.Bin{ - .lhs = a, - .rhs = b, - }); - return rvalue(gz, ri, result, node); - }, - .min => { - const a = try expr(gz, scope, .{ .rl = .none }, params[0]); - const b = try expr(gz, scope, .{ .rl = .none }, params[1]); - const result = try gz.addPlNode(.min, node, Zir.Inst.Bin{ - .lhs = a, - .rhs = b, - }); - return rvalue(gz, ri, result, node); - }, - .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow), .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow), .mul_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .mul_with_overflow), @@ -8428,8 +8541,8 @@ fn builtinCall( return rvalue(gz, ri, result, node); }, .call => { - const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .modifier_type } }, params[0]); - const callee = try calleeExpr(gz, scope, params[1]); + const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .call_modifier_type } }, params[0]); + const callee = try expr(gz, scope, .{ .rl = .none }, params[1]); const args = try expr(gz, scope, .{ .rl = .none }, params[2]); const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{ .modifier = modifier, @@ -8444,7 +8557,7 @@ fn builtinCall( }, .field_parent_ptr => { const parent_type = try typeExpr(gz, scope, params[0]); - const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[1]); + const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[1]); const result = try gz.addPlNode(.field_parent_ptr, node, Zir.Inst.FieldParentPtr{ .parent_type = parent_type, .field_name = field_name, @@ -8599,7 +8712,7 @@ fn hasDeclOrField( tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { const container_type = try typeExpr(gz, scope, lhs_node); - const name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, rhs_node); + const name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .slice_const_u8_type } }, rhs_node); const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = container_type, .rhs = name, @@ -8749,7 +8862,7 @@ fn simpleCBuiltin( ) InnerError!Zir.Inst.Ref { const name: []const u8 = if (tag == .c_undef) "C undef" else "C include"; if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name}); - const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, operand_node); + const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .slice_const_u8_type } }, operand_node); _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(node), .operand = operand, @@ -8767,7 +8880,7 @@ fn offsetOf( tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { const type_inst = try typeExpr(gz, scope, lhs_node); - const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, rhs_node); + const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .slice_const_u8_type } }, rhs_node); const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = type_inst, .rhs = field_name, @@ -8889,7 +9002,10 @@ fn callExpr( } }); } - assert(callee != .none); + switch (callee) { + .direct => |obj| assert(obj != .none), + .field => |field| assert(field.obj_ptr != .none), + } assert(node != 0); const call_index = @intCast(Zir.Inst.Index, astgen.instructions.len); @@ -8928,89 +9044,98 @@ fn callExpr( else => false, }; - const payload_index = try addExtra(astgen, Zir.Inst.Call{ - .callee = callee, - .flags = .{ - .pop_error_return_trace = !propagate_error_trace, - .packed_modifier = @intCast(Zir.Inst.Call.Flags.PackedModifier, @enumToInt(modifier)), - .args_len = @intCast(Zir.Inst.Call.Flags.PackedArgsLen, call.ast.params.len), + switch (callee) { + .direct => |callee_obj| { + const payload_index = try addExtra(astgen, Zir.Inst.Call{ + .callee = callee_obj, + .flags = .{ + .pop_error_return_trace = !propagate_error_trace, + .packed_modifier = @intCast(Zir.Inst.Call.Flags.PackedModifier, @enumToInt(modifier)), + .args_len = @intCast(Zir.Inst.Call.Flags.PackedArgsLen, call.ast.params.len), + }, + }); + if (call.ast.params.len != 0) { + try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]); + } + gz.astgen.instructions.set(call_index, .{ + .tag = .call, + .data = .{ .pl_node = .{ + .src_node = gz.nodeIndexToRelative(node), + .payload_index = payload_index, + } }, + }); + }, + .field => |callee_field| { + const payload_index = try addExtra(astgen, Zir.Inst.FieldCall{ + .obj_ptr = callee_field.obj_ptr, + .field_name_start = callee_field.field_name_start, + .flags = .{ + .pop_error_return_trace = !propagate_error_trace, + .packed_modifier = @intCast(Zir.Inst.Call.Flags.PackedModifier, @enumToInt(modifier)), + .args_len = @intCast(Zir.Inst.Call.Flags.PackedArgsLen, call.ast.params.len), + }, + }); + if (call.ast.params.len != 0) { + try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]); + } + gz.astgen.instructions.set(call_index, .{ + .tag = .field_call, + .data = .{ .pl_node = .{ + .src_node = gz.nodeIndexToRelative(node), + .payload_index = payload_index, + } }, + }); }, - }); - if (call.ast.params.len != 0) { - try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]); } - gz.astgen.instructions.set(call_index, .{ - .tag = .call, - .data = .{ .pl_node = .{ - .src_node = gz.nodeIndexToRelative(node), - .payload_index = payload_index, - } }, - }); return rvalue(gz, ri, call_inst, node); // TODO function call with result location } -/// calleeExpr generates the function part of a call expression (f in f(x)), or the -/// callee argument to the @call() builtin. If the lhs is a field access or the -/// @field() builtin, we need to generate a special field_call_bind instruction -/// instead of the normal field_val or field_ptr. If this is a inst.func() call, -/// this instruction will capture the value of the first argument before evaluating -/// the other arguments. We need to use .ref here to guarantee we will be able to -/// promote an lvalue to an address if the first parameter requires it. This -/// unfortunately also means we need to take a reference to any types on the lhs. +const Callee = union(enum) { + field: struct { + /// A *pointer* to the object the field is fetched on, so that we can + /// promote the lvalue to an address if the first parameter requires it. + obj_ptr: Zir.Inst.Ref, + /// Offset into `string_bytes`. + field_name_start: u32, + }, + direct: Zir.Inst.Ref, +}; + +/// calleeExpr generates the function part of a call expression (f in f(x)), but +/// *not* the callee argument to the @call() builtin. Its purpose is to +/// distinguish between standard calls and method call syntax `a.b()`. Thus, if +/// the lhs is a field access, we return using the `field` union field; +/// otherwise, we use the `direct` union field. fn calleeExpr( gz: *GenZir, scope: *Scope, node: Ast.Node.Index, -) InnerError!Zir.Inst.Ref { +) InnerError!Callee { const astgen = gz.astgen; const tree = astgen.tree; const tag = tree.nodes.items(.tag)[node]; switch (tag) { - .field_access => return addFieldAccess(.field_call_bind, gz, scope, .{ .rl = .ref }, node), - - .builtin_call_two, - .builtin_call_two_comma, - .builtin_call, - .builtin_call_comma, - => { - const node_datas = tree.nodes.items(.data); + .field_access => { const main_tokens = tree.nodes.items(.main_token); - const builtin_token = main_tokens[node]; - const builtin_name = tree.tokenSlice(builtin_token); - - var inline_params: [2]Ast.Node.Index = undefined; - var params: []Ast.Node.Index = switch (tag) { - .builtin_call, - .builtin_call_comma, - => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs], - - .builtin_call_two, - .builtin_call_two_comma, - => blk: { - inline_params = .{ node_datas[node].lhs, node_datas[node].rhs }; - const len: usize = if (inline_params[0] == 0) @as(usize, 0) else if (inline_params[1] == 0) @as(usize, 1) else @as(usize, 2); - break :blk inline_params[0..len]; - }, - - else => unreachable, - }; + const node_datas = tree.nodes.items(.data); + const object_node = node_datas[node].lhs; + const dot_token = main_tokens[node]; + const field_ident = dot_token + 1; + const str_index = try astgen.identAsString(field_ident); + // Capture the object by reference so we can promote it to an + // address in Sema if needed. + const lhs = try expr(gz, scope, .{ .rl = .ref }, object_node); - // If anything is wrong, fall back to builtinCall. - // It will emit any necessary compile errors and notes. - if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) { - const lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]); - const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[1]); - return gz.addExtendedPayload(.field_call_bind_named, Zir.Inst.FieldNamedNode{ - .node = gz.nodeIndexToRelative(node), - .lhs = lhs, - .field_name = field_name, - }); - } + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + try emitDbgStmt(gz, cursor); - return builtinCall(gz, scope, .{ .rl = .none }, node, params); + return .{ .field = .{ + .obj_ptr = lhs, + .field_name_start = str_index, + } }; }, - else => return expr(gz, scope, .{ .rl = .none }, node), + else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) }, } } @@ -10157,6 +10282,8 @@ fn rvalue( as_ty | @enumToInt(Zir.Inst.Ref.i32_type), as_ty | @enumToInt(Zir.Inst.Ref.u64_type), as_ty | @enumToInt(Zir.Inst.Ref.i64_type), + as_ty | @enumToInt(Zir.Inst.Ref.u128_type), + as_ty | @enumToInt(Zir.Inst.Ref.i128_type), as_ty | @enumToInt(Zir.Inst.Ref.usize_type), as_ty | @enumToInt(Zir.Inst.Ref.isize_type), as_ty | @enumToInt(Zir.Inst.Ref.c_char_type), @@ -10182,15 +10309,30 @@ fn rvalue( as_ty | @enumToInt(Zir.Inst.Ref.comptime_int_type), as_ty | @enumToInt(Zir.Inst.Ref.comptime_float_type), as_ty | @enumToInt(Zir.Inst.Ref.noreturn_type), + as_ty | @enumToInt(Zir.Inst.Ref.anyframe_type), as_ty | @enumToInt(Zir.Inst.Ref.null_type), as_ty | @enumToInt(Zir.Inst.Ref.undefined_type), - as_ty | @enumToInt(Zir.Inst.Ref.fn_noreturn_no_args_type), - as_ty | @enumToInt(Zir.Inst.Ref.fn_void_no_args_type), - as_ty | @enumToInt(Zir.Inst.Ref.fn_naked_noreturn_no_args_type), - as_ty | @enumToInt(Zir.Inst.Ref.fn_ccc_void_no_args_type), - as_ty | @enumToInt(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type), - as_ty | @enumToInt(Zir.Inst.Ref.const_slice_u8_type), as_ty | @enumToInt(Zir.Inst.Ref.enum_literal_type), + as_ty | @enumToInt(Zir.Inst.Ref.atomic_order_type), + as_ty | @enumToInt(Zir.Inst.Ref.atomic_rmw_op_type), + as_ty | @enumToInt(Zir.Inst.Ref.calling_convention_type), + as_ty | @enumToInt(Zir.Inst.Ref.address_space_type), + as_ty | @enumToInt(Zir.Inst.Ref.float_mode_type), + as_ty | @enumToInt(Zir.Inst.Ref.reduce_op_type), + as_ty | @enumToInt(Zir.Inst.Ref.call_modifier_type), + as_ty | @enumToInt(Zir.Inst.Ref.prefetch_options_type), + as_ty | @enumToInt(Zir.Inst.Ref.export_options_type), + as_ty | @enumToInt(Zir.Inst.Ref.extern_options_type), + as_ty | @enumToInt(Zir.Inst.Ref.type_info_type), + as_ty | @enumToInt(Zir.Inst.Ref.manyptr_u8_type), + as_ty | @enumToInt(Zir.Inst.Ref.manyptr_const_u8_type), + as_ty | @enumToInt(Zir.Inst.Ref.manyptr_const_u8_sentinel_0_type), + as_ty | @enumToInt(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type), + as_ty | @enumToInt(Zir.Inst.Ref.slice_const_u8_type), + as_ty | @enumToInt(Zir.Inst.Ref.slice_const_u8_sentinel_0_type), + as_ty | @enumToInt(Zir.Inst.Ref.anyerror_void_error_union_type), + as_ty | @enumToInt(Zir.Inst.Ref.generic_poison_type), + as_ty | @enumToInt(Zir.Inst.Ref.empty_struct_type), as_comptime_int | @enumToInt(Zir.Inst.Ref.zero), as_comptime_int | @enumToInt(Zir.Inst.Ref.one), as_bool | @enumToInt(Zir.Inst.Ref.bool_true), @@ -10563,8 +10705,8 @@ fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !u32 { const string_bytes = &astgen.string_bytes; const str_index = @intCast(u32, string_bytes.items.len); try astgen.appendIdentStr(ident_token, string_bytes); - const key = string_bytes.items[str_index..]; - const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{ + const key: []const u8 = string_bytes.items[str_index..]; + const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{ .bytes = string_bytes, }, StringIndexContext{ .bytes = string_bytes, @@ -11647,9 +11789,9 @@ const GenZir = struct { ) !Zir.Inst.Index { const gpa = gz.astgen.gpa; const param_body = param_gz.instructionsSlice(); + const body_len = gz.astgen.countBodyLenAfterFixups(param_body); try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); - try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).Struct.fields.len + - param_body.len); + try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).Struct.fields.len + body_len); const doc_comment_index = if (first_doc_comment) |first| try gz.astgen.docCommentAsStringFromFirst(abs_tok_index, first) @@ -11659,9 +11801,9 @@ const GenZir = struct { const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{ .name = name, .doc_comment = doc_comment_index, - .body_len = @intCast(u32, param_body.len), + .body_len = @intCast(u32, body_len), }); - gz.astgen.extra.appendSliceAssumeCapacity(param_body); + gz.astgen.appendBodyWithFixups(param_body); param_gz.unstack(); const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); |
