diff --git a/lib/std/Thread/Mutex.zig b/lib/std/Thread/Mutex.zig index 89dedaf2b7fe..9114caaa1209 100644 --- a/lib/std/Thread/Mutex.zig +++ b/lib/std/Thread/Mutex.zig @@ -169,7 +169,7 @@ const FutexImpl = struct { } } - inline fn lockFast(self: *@This(), comptime casFn: []const u8) bool { + inline fn lockFast(self: *@This(), comptime cas_fn_name: []const u8) bool { // On x86, use `lock bts` instead of `lock cmpxchg` as: // - they both seem to mark the cache-line as modified regardless: https://stackoverflow.com/a/63350048 // - `lock bts` is smaller instruction-wise which makes it better for inlining @@ -180,7 +180,8 @@ const FutexImpl = struct { // Acquire barrier ensures grabbing the lock happens before the critical section // and that the previous lock holder's critical section happens before we grab the lock. - return @field(self.state, casFn)(unlocked, locked, .Acquire, .Monotonic) == null; + const casFn = @field(@TypeOf(self.state), cas_fn_name); + return casFn(&self.state, unlocked, locked, .Acquire, .Monotonic) == null; } fn lockSlow(self: *@This()) void { diff --git a/lib/std/crypto/siphash.zig b/lib/std/crypto/siphash.zig index d91485cdfac9..1303970b222e 100644 --- a/lib/std/crypto/siphash.zig +++ b/lib/std/crypto/siphash.zig @@ -167,8 +167,8 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round pub fn hash(msg: []const u8, key: *const [key_length]u8) T { const aligned_len = msg.len - (msg.len % 8); var c = Self.init(key); - @call(.always_inline, c.update, .{msg[0..aligned_len]}); - return @call(.always_inline, c.final, .{msg[aligned_len..]}); + @call(.always_inline, update, .{ &c, msg[0..aligned_len] }); + return @call(.always_inline, final, .{ &c, msg[aligned_len..] }); } }; } diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig index d4640262f25b..b78159ca0b73 100644 --- a/lib/std/hash/auto_hash.zig +++ b/lib/std/hash/auto_hash.zig @@ -64,9 +64,13 @@ pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) vo /// Strategy is provided to determine if pointers should be followed or not. pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { const Key = @TypeOf(key); + const Hasher = switch (@typeInfo(@TypeOf(hasher))) { + .Pointer => |ptr| ptr.child, + else => @TypeOf(hasher), + }; if (strat == .Shallow and comptime meta.trait.hasUniqueRepresentation(Key)) { - @call(.always_inline, hasher.update, .{mem.asBytes(&key)}); + @call(.always_inline, Hasher.update, .{ hasher, mem.asBytes(&key) }); return; } @@ -89,12 +93,12 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { // TODO Check if the situation is better after #561 is resolved. .Int => { if (comptime meta.trait.hasUniqueRepresentation(Key)) { - @call(.always_inline, hasher.update, .{std.mem.asBytes(&key)}); + @call(.always_inline, Hasher.update, .{ hasher, std.mem.asBytes(&key) }); } else { // Take only the part containing the key value, the remaining // bytes are undefined and must not be hashed! const byte_size = comptime std.math.divCeil(comptime_int, @bitSizeOf(Key), 8) catch unreachable; - @call(.always_inline, hasher.update, .{std.mem.asBytes(&key)[0..byte_size]}); + @call(.always_inline, Hasher.update, .{ hasher, std.mem.asBytes(&key)[0..byte_size] }); } }, diff --git a/lib/std/hash/wyhash.zig b/lib/std/hash/wyhash.zig index 934c4f96adfa..682619e1a4bd 100644 --- a/lib/std/hash/wyhash.zig +++ b/lib/std/hash/wyhash.zig @@ -65,7 +65,7 @@ const WyhashStateless = struct { var off: usize = 0; while (off < b.len) : (off += 32) { - @call(.always_inline, self.round, .{b[off..][0..32]}); + @call(.always_inline, round, .{ self, b[off..][0..32] }); } self.msg_len += b.len; @@ -121,8 +121,8 @@ const WyhashStateless = struct { const aligned_len = input.len - (input.len % 32); var c = WyhashStateless.init(seed); - @call(.always_inline, c.update, .{input[0..aligned_len]}); - return @call(.always_inline, c.final, .{input[aligned_len..]}); + @call(.always_inline, update, .{ &c, input[0..aligned_len] }); + return @call(.always_inline, final, .{ &c, input[aligned_len..] }); } }; diff --git a/src/AstGen.zig b/src/AstGen.zig index eacd2d53ea66..9ed6be87ba63 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2482,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.*); @@ -2557,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, @@ -8516,7 +8515,7 @@ fn builtinCall( }, .call => { const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .modifier_type } }, params[0]); - const callee = try calleeExpr(gz, scope, params[1]); + 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, @@ -8976,7 +8975,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); @@ -9015,89 +9017,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) }, } } diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 5e1c4c7822ae..afb5bc6704be 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -2141,7 +2141,7 @@ fn walkInstruction( .expr = .{ .declRef = decl_status }, }; }, - .field_val, .field_call_bind, .field_ptr, .field_type => { + .field_val, .field_ptr, .field_type => { // TODO: field type uses Zir.Inst.FieldType, it just happens to have the // same layout as Zir.Inst.Field :^) const pl_node = data[inst_index].pl_node; @@ -2163,7 +2163,6 @@ fn walkInstruction( const lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; if (tags[lhs] != .field_val and - tags[lhs] != .field_call_bind and tags[lhs] != .field_ptr and tags[lhs] != .field_type) break :blk lhs_extra.data.lhs; @@ -2191,7 +2190,7 @@ fn walkInstruction( const wr = blk: { if (@enumToInt(lhs_ref) >= Ref.typed_value_map.len) { const lhs_inst = @enumToInt(lhs_ref) - Ref.typed_value_map.len; - if (tags[lhs_inst] == .call) { + if (tags[lhs_inst] == .call or tags[lhs_inst] == .field_call) { break :blk DocData.WalkResult{ .expr = .{ .comptimeExpr = 0, diff --git a/src/Module.zig b/src/Module.zig index d87c86b86496..c191fd6c7bb2 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2489,8 +2489,21 @@ pub const SrcLoc = struct { const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = src_loc.declRelativeToNodeIndex(node_off); + var buf: [1]Ast.Node.Index = undefined; const tok_index = switch (node_tags[node]) { .field_access => node_datas[node].rhs, + .call_one, + .call_one_comma, + .async_call_one, + .async_call_one_comma, + .call, + .call_comma, + .async_call, + .async_call_comma, + => blk: { + const full = tree.fullCall(&buf, node).?; + break :blk tree.lastToken(full.ast.fn_expr); + }, else => tree.firstToken(node) - 2, }; const start = tree.tokens.items(.start)[tok_index]; @@ -3083,7 +3096,8 @@ pub const LazySrcLoc = union(enum) { /// The payload is offset from the containing Decl AST node. /// The source location points to the field name of: /// * a field access expression (`a.b`), or - /// * the operand ("b" node) of a field initialization expression (`.a = b`) + /// * the callee of a method call (`a.b()`), or + /// * the operand ("b" node) of a field initialization expression (`.a = b`), or /// The Decl is determined contextually. node_offset_field_name: i32, /// The source location points to the pointer of a pointer deref expression, diff --git a/src/Sema.zig b/src/Sema.zig index 18b8d40af63c..6a1c889a6eee 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -920,7 +920,8 @@ fn analyzeBodyInner( .bool_br_and => try sema.zirBoolBr(block, inst, false), .bool_br_or => try sema.zirBoolBr(block, inst, true), .c_import => try sema.zirCImport(block, inst), - .call => try sema.zirCall(block, inst), + .call => try sema.zirCall(block, inst, .direct), + .field_call => try sema.zirCall(block, inst, .field), .closure_get => try sema.zirClosureGet(block, inst), .cmp_lt => try sema.zirCmp(block, inst, .lt), .cmp_lte => try sema.zirCmp(block, inst, .lte), @@ -952,7 +953,6 @@ fn analyzeBodyInner( .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), .field_val => try sema.zirFieldVal(block, inst), .field_val_named => try sema.zirFieldValNamed(block, inst), - .field_call_bind => try sema.zirFieldCallBind(block, inst), .func => try sema.zirFunc(block, inst, false), .func_inferred => try sema.zirFunc(block, inst, true), .func_fancy => try sema.zirFuncFancy(block, inst), @@ -1149,7 +1149,6 @@ fn analyzeBodyInner( .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), .prefetch => try sema.zirPrefetch( block, extended), - .field_call_bind_named => try sema.zirFieldCallBindNamed(block, extended), .err_set_cast => try sema.zirErrSetCast( block, extended), .await_nosuspend => try sema.zirAwaitNosuspend( block, extended), .select => try sema.zirSelect( block, extended), @@ -6262,38 +6261,50 @@ fn zirCall( sema: *Sema, block: *Block, inst: Zir.Inst.Index, + comptime kind: enum { direct, field }, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; + const callee_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; const call_src = inst_data.src(); - const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index); + const ExtraType = switch (kind) { + .direct => Zir.Inst.Call, + .field => Zir.Inst.FieldCall, + }; + const extra = sema.code.extraData(ExtraType, inst_data.payload_index); const args_len = extra.data.flags.args_len; const modifier = @intToEnum(std.builtin.CallModifier, extra.data.flags.packed_modifier); const ensure_result_used = extra.data.flags.ensure_result_used; const pop_error_return_trace = extra.data.flags.pop_error_return_trace; - var func = try sema.resolveInst(extra.data.callee); + const callee: ResolvedFieldCallee = switch (kind) { + .direct => .{ .direct = try sema.resolveInst(extra.data.callee) }, + .field => blk: { + const object_ptr = try sema.resolveInst(extra.data.obj_ptr); + const field_name = sema.code.nullTerminatedString(extra.data.field_name_start); + const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; + break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src); + }, + }; var resolved_args: []Air.Inst.Ref = undefined; - var arg_index: u32 = 0; - - const func_type = sema.typeOf(func); - - // Desugar bound functions here var bound_arg_src: ?LazySrcLoc = null; - if (func_type.tag() == .bound_fn) { - bound_arg_src = func_src; - const bound_func = try sema.resolveValue(block, .unneeded, func, ""); - const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data; - func = bound_data.func_inst; - resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len + 1); - resolved_args[arg_index] = bound_data.arg0_inst; - arg_index += 1; - } else { - resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len); + var func: Air.Inst.Ref = undefined; + var arg_index: u32 = 0; + switch (callee) { + .direct => |func_inst| { + resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len); + func = func_inst; + }, + .method => |method| { + resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len + 1); + func = method.func_inst; + resolved_args[0] = method.arg0_inst; + arg_index += 1; + bound_arg_src = callee_src; + }, } const callee_ty = sema.typeOf(func); @@ -6308,10 +6319,11 @@ fn zirCall( }, else => {}, } - return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)}); + return sema.fail(block, callee_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)}); }; + const total_args = args_len + @boolToInt(bound_arg_src != null); - try sema.checkCallArgumentCount(block, func, func_src, func_ty, total_args, bound_arg_src != null); + try sema.checkCallArgumentCount(block, func, callee_src, func_ty, total_args, bound_arg_src != null); const args_body = sema.code.extra[extra.end..]; @@ -6369,7 +6381,7 @@ fn zirCall( !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace)) { const call_inst: Air.Inst.Ref = if (modifier == .always_tail) undefined else b: { - break :b try sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); + break :b try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); }; const return_ty = sema.typeOf(call_inst); @@ -6398,11 +6410,11 @@ fn zirCall( } if (modifier == .always_tail) // Perform the call *after* the restore, so that a tail call is possible. - return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); + return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); return call_inst; } else { - return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); + return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); } } @@ -9467,19 +9479,6 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index, initializing: b return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, initializing); } -fn zirFieldCallBind(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; - const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; - const field_name = sema.code.nullTerminatedString(extra.field_name_start); - const object_ptr = try sema.resolveInst(extra.lhs); - return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); -} - fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -9506,18 +9505,6 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); } -fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const extra = sema.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data; - const src = LazySrcLoc.nodeOffset(extra.node); - const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; - const object_ptr = try sema.resolveInst(extra.lhs); - const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime-known"); - return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); -} - fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -21673,25 +21660,9 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)}); } - var resolved_args: []Air.Inst.Ref = undefined; - - // Desugar bound functions here - var bound_arg_src: ?LazySrcLoc = null; - if (sema.typeOf(func).tag() == .bound_fn) { - bound_arg_src = func_src; - const bound_func = try sema.resolveValue(block, .unneeded, func, ""); - const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data; - func = bound_data.func_inst; - resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount() + 1); - resolved_args[0] = bound_data.arg0_inst; - for (resolved_args[1..], 0..) |*resolved, i| { - resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty); - } - } else { - resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount()); - for (resolved_args, 0..) |*resolved, i| { - resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty); - } + var resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount()); + for (resolved_args, 0..) |*resolved, i| { + resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty); } const callee_ty = sema.typeOf(func); @@ -21708,10 +21679,10 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)}); }; - try sema.checkCallArgumentCount(block, func, func_src, func_ty, resolved_args.len, bound_arg_src != null); + try sema.checkCallArgumentCount(block, func, func_src, func_ty, resolved_args.len, false); const ensure_result_used = extra.flags.ensure_result_used; - return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, null); + return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, null, null); } fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -24175,6 +24146,16 @@ fn fieldPtr( return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); } +const ResolvedFieldCallee = union(enum) { + /// The LHS of the call was an actual field with this value. + direct: Air.Inst.Ref, + /// This is a method call, with the function and first argument given. + method: struct { + func_inst: Air.Inst.Ref, + arg0_inst: Air.Inst.Ref, + }, +}; + fn fieldCallBind( sema: *Sema, block: *Block, @@ -24182,7 +24163,7 @@ fn fieldCallBind( raw_ptr: Air.Inst.Ref, field_name: []const u8, field_name_src: LazySrcLoc, -) CompileError!Air.Inst.Ref { +) CompileError!ResolvedFieldCallee { // When editing this function, note that there is corresponding logic to be edited // in `fieldVal`. This function takes a pointer and returns a pointer. @@ -24202,7 +24183,6 @@ fn fieldCallBind( else raw_ptr; - const arena = sema.arena; find_field: { switch (concrete_ty.zigTypeTag()) { .Struct => { @@ -24216,7 +24196,7 @@ fn fieldCallBind( return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr); } else if (struct_ty.isTuple()) { if (mem.eql(u8, field_name, "len")) { - return sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount()); + return .{ .direct = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount()) }; } if (std.fmt.parseUnsigned(u32, field_name, 10)) |field_index| { if (field_index >= struct_ty.structFieldCount()) break :find_field; @@ -24243,7 +24223,7 @@ fn fieldCallBind( }, .Type => { const namespace = try sema.analyzeLoad(block, src, object_ptr, src); - return sema.fieldVal(block, src, namespace, field_name, field_name_src); + return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) }; }, else => {}, } @@ -24272,54 +24252,47 @@ fn fieldCallBind( first_param_type.childType().eql(concrete_ty, sema.mod))) { // zig fmt: on + // Note that if the param type is generic poison, we know that it must + // specifically be `anytype` since it's the first parameter, meaning we + // can safely assume it can be a pointer. // TODO: bound fn calls on rvalues should probably // generate a by-value argument somehow. - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ + return .{ .method = .{ .func_inst = decl_val, .arg0_inst = object_ptr, - }); - return sema.addConstant(ty, value); + } }; } else if (first_param_type.eql(concrete_ty, sema.mod)) { const deref = try sema.analyzeLoad(block, src, object_ptr, src); - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ + return .{ .method = .{ .func_inst = decl_val, .arg0_inst = deref, - }); - return sema.addConstant(ty, value); + } }; } else if (first_param_type.zigTypeTag() == .Optional) { var opt_buf: Type.Payload.ElemType = undefined; const child = first_param_type.optionalChild(&opt_buf); if (child.eql(concrete_ty, sema.mod)) { const deref = try sema.analyzeLoad(block, src, object_ptr, src); - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ + return .{ .method = .{ .func_inst = decl_val, .arg0_inst = deref, - }); - return sema.addConstant(ty, value); + } }; } else if (child.zigTypeTag() == .Pointer and child.ptrSize() == .One and child.childType().eql(concrete_ty, sema.mod)) { - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ + return .{ .method = .{ .func_inst = decl_val, .arg0_inst = object_ptr, - }); - return sema.addConstant(ty, value); + } }; } } else if (first_param_type.zigTypeTag() == .ErrorUnion and first_param_type.errorUnionPayload().eql(concrete_ty, sema.mod)) { const deref = try sema.analyzeLoad(block, src, object_ptr, src); - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ + return .{ .method = .{ .func_inst = decl_val, .arg0_inst = deref, - }); - return sema.addConstant(ty, value); + } }; } } break :found_decl decl_idx; @@ -24351,7 +24324,7 @@ fn finishFieldCallBind( field_ty: Type, field_index: u32, object_ptr: Air.Inst.Ref, -) CompileError!Air.Inst.Ref { +) CompileError!ResolvedFieldCallee { const arena = sema.arena; const ptr_field_ty = try Type.ptr(arena, sema.mod, .{ .pointee_type = field_ty, @@ -24362,7 +24335,7 @@ fn finishFieldCallBind( const container_ty = ptr_ty.childType(); if (container_ty.zigTypeTag() == .Struct) { if (container_ty.structFieldValueComptime(field_index)) |default_val| { - return sema.addConstant(field_ty, default_val); + return .{ .direct = try sema.addConstant(field_ty, default_val) }; } } @@ -24375,12 +24348,12 @@ fn finishFieldCallBind( .field_index = field_index, }), ); - return sema.analyzeLoad(block, src, pointer, src); + return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) }; } try sema.requireRuntimeBlock(block, src, null); const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); - return sema.analyzeLoad(block, src, ptr_inst, src); + return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) }; } fn namespaceLookup( @@ -31281,7 +31254,6 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .inferred_alloc_mut => unreachable, .inferred_alloc_const => unreachable, - .bound_fn => unreachable, .array, .array_sentinel, @@ -32666,7 +32638,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .single_const_pointer, .single_mut_pointer, .pointer, - .bound_fn, => return null, .optional => { @@ -33308,7 +33279,6 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .inferred_alloc_mut => unreachable, .inferred_alloc_const => unreachable, - .bound_fn => unreachable, .array, .array_sentinel, diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 415d21b2e633..d74fbda93e8d 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -499,10 +499,6 @@ pub fn print( // TODO these should not appear in this function .inferred_alloc => return writer.writeAll("(inferred allocation value)"), .inferred_alloc_comptime => return writer.writeAll("(inferred comptime allocation value)"), - .bound_fn => { - const bound_func = val.castTag(.bound_fn).?.data; - return writer.print("(bound_fn %{}(%{})", .{ bound_func.func_inst, bound_func.arg0_inst }); - }, .generic_poison_type => return writer.writeAll("(generic poison type)"), .generic_poison => return writer.writeAll("(generic poison)"), .runtime_value => return writer.writeAll("[runtime value]"), diff --git a/src/Zir.zig b/src/Zir.zig index 156aa201d6ee..2bd5b21f7913 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -297,6 +297,14 @@ pub const Inst = struct { /// Uses the `pl_node` union field with payload `Call`. /// AST node is the function call. call, + /// Function call using `a.b()` syntax. + /// Uses the named field as the callee. If there is no such field, searches in the type for + /// a decl matching the field name. The decl is resolved and we ensure that it's a function + /// which can accept the object as the first parameter, with one pointer fixup. This + /// function is then used as the callee, with the object as an implicit first parameter. + /// Uses the `pl_node` union field with payload `FieldCall`. + /// AST node is the function call. + field_call, /// Implements the `@call` builtin. /// Uses the `pl_node` union field with payload `BuiltinCall`. /// AST node is the builtin call. @@ -432,15 +440,6 @@ pub const Inst = struct { /// This instruction also accepts a pointer. /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. field_val, - /// Given a pointer to a struct or object that contains virtual fields, returns the - /// named field. If there is no named field, searches in the type for a decl that - /// matches the field name. The decl is resolved and we ensure that it's a function - /// which can accept the object as the first parameter, with one pointer fixup. If - /// all of that works, this instruction produces a special "bound function" value - /// which contains both the function and the saved first parameter value. - /// Bound functions may only be used as the function parameter to a `call` or - /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler. - field_call_bind, /// Given a pointer to a struct or object that contains virtual fields, returns a pointer /// to the named field. The field name is a comptime instruction. Used by @field. /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. @@ -1051,6 +1050,7 @@ pub const Inst = struct { .bool_br_or, .bool_not, .call, + .field_call, .cmp_lt, .cmp_lte, .cmp_eq, @@ -1083,7 +1083,6 @@ pub const Inst = struct { .field_ptr, .field_ptr_init, .field_val, - .field_call_bind, .field_ptr_named, .field_val_named, .func, @@ -1361,6 +1360,7 @@ pub const Inst = struct { .bool_br_or, .bool_not, .call, + .field_call, .cmp_lt, .cmp_lte, .cmp_eq, @@ -1383,7 +1383,6 @@ pub const Inst = struct { .field_ptr, .field_ptr_init, .field_val, - .field_call_bind, .field_ptr_named, .field_val_named, .func, @@ -1601,6 +1600,7 @@ pub const Inst = struct { .check_comptime_control_flow = .un_node, .for_len = .pl_node, .call = .pl_node, + .field_call = .pl_node, .cmp_lt = .pl_node, .cmp_lte = .pl_node, .cmp_eq = .pl_node, @@ -1641,7 +1641,6 @@ pub const Inst = struct { .field_val = .pl_node, .field_ptr_named = .pl_node, .field_val_named = .pl_node, - .field_call_bind = .pl_node, .func = .pl_node, .func_inferred = .pl_node, .func_fancy = .pl_node, @@ -1955,16 +1954,6 @@ pub const Inst = struct { /// The `@prefetch` builtin. /// `operand` is payload index to `BinNode`. prefetch, - /// Given a pointer to a struct or object that contains virtual fields, returns the - /// named field. If there is no named field, searches in the type for a decl that - /// matches the field name. The decl is resolved and we ensure that it's a function - /// which can accept the object as the first parameter, with one pointer fixup. If - /// all of that works, this instruction produces a special "bound function" value - /// which contains both the function and the saved first parameter value. - /// Bound functions may only be used as the function parameter to a `call` or - /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler. - /// Uses `pl_node` field. The AST node is the `@field` builtin. Payload is FieldNamedNode. - field_call_bind_named, /// Implements the `@fence` builtin. /// `operand` is payload index to `UnNode`. fence, @@ -2913,6 +2902,19 @@ pub const Inst = struct { }; }; + /// Stored inside extra, with trailing arguments according to `args_len`. + /// Implicit 0. arg_0_start: u32, // always same as `args_len` + /// 1. arg_end: u32, // for each `args_len` + /// arg_N_start is the same as arg_N-1_end + pub const FieldCall = struct { + // Note: Flags *must* come first so that unusedResultExpr + // can find it when it goes to modify them. + flags: Call.Flags, + obj_ptr: Ref, + /// Offset into `string_bytes`. + field_name_start: u32, + }; + pub const TypeOfPeer = struct { src_node: i32, body_len: u32, @@ -3187,12 +3189,6 @@ pub const Inst = struct { field_name: Ref, }; - pub const FieldNamedNode = struct { - node: i32, - lhs: Ref, - field_name: Ref, - }; - pub const As = struct { dest_type: Ref, operand: Ref, diff --git a/src/print_air.zig b/src/print_air.zig index 2190177fb000..2e8ab1a6422d 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -369,7 +369,6 @@ const Writer = struct { .inferred_alloc_const => try s.writeAll("(inferred_alloc_const)"), .inferred_alloc_mut => try s.writeAll("(inferred_alloc_mut)"), .generic_poison => try s.writeAll("(generic_poison)"), - .bound_fn => try s.writeAll("(bound_fn)"), else => try ty.print(s, w.module), } } diff --git a/src/print_zir.zig b/src/print_zir.zig index f5e84fcf5bc6..f67cb55ba981 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -362,7 +362,8 @@ const Writer = struct { .@"export" => try self.writePlNodeExport(stream, inst), .export_value => try self.writePlNodeExportValue(stream, inst), - .call => try self.writeCall(stream, inst), + .call => try self.writeCall(stream, inst, .direct), + .field_call => try self.writeCall(stream, inst, .field), .block, .block_comptime, @@ -392,7 +393,6 @@ const Writer = struct { .field_ptr, .field_ptr_init, .field_val, - .field_call_bind, => try self.writePlNodeField(stream, inst), .field_ptr_named, @@ -543,15 +543,6 @@ const Writer = struct { try self.writeSrc(stream, src); }, - .field_call_bind_named => { - const extra = self.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data; - const src = LazySrcLoc.nodeOffset(extra.node); - try self.writeInstRef(stream, extra.lhs); - try stream.writeAll(", "); - try self.writeInstRef(stream, extra.field_name); - try stream.writeAll(") "); - try self.writeSrc(stream, src); - }, .builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended), .cmpxchg => try self.writeCmpxchg(stream, extended), } @@ -1176,9 +1167,18 @@ const Writer = struct { try self.writeSrc(stream, src); } - fn writeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeCall( + self: *Writer, + stream: anytype, + inst: Zir.Inst.Index, + comptime kind: enum { direct, field }, + ) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index); + const ExtraType = switch (kind) { + .direct => Zir.Inst.Call, + .field => Zir.Inst.FieldCall, + }; + const extra = self.code.extraData(ExtraType, inst_data.payload_index); const args_len = extra.data.flags.args_len; const body = self.code.extra[extra.end..]; @@ -1186,7 +1186,14 @@ const Writer = struct { try stream.writeAll("nodiscard "); } try stream.print(".{s}, ", .{@tagName(@intToEnum(std.builtin.CallModifier, extra.data.flags.packed_modifier))}); - try self.writeInstRef(stream, extra.data.callee); + switch (kind) { + .direct => try self.writeInstRef(stream, extra.data.callee), + .field => { + const field_name = self.code.nullTerminatedString(extra.data.field_name_start); + try self.writeInstRef(stream, extra.data.obj_ptr); + try stream.print(", {}", .{std.zig.fmtId(field_name)}); + }, + } try stream.writeAll(", ["); self.indent += 2; diff --git a/src/type.zig b/src/type.zig index 741b173db618..aea2d885711f 100644 --- a/src/type.zig +++ b/src/type.zig @@ -156,8 +156,6 @@ pub const Type = extern union { .union_tagged, .type_info, => return .Union, - - .bound_fn => unreachable, } } @@ -933,7 +931,6 @@ pub const Type = extern union { // for example, a was resolved into .union_tagged but b was one of these tags. .type_info => unreachable, // needed to resolve the type before now - .bound_fn => unreachable, } } @@ -1242,7 +1239,6 @@ pub const Type = extern union { // we can't hash these based on tags because they wouldn't match the expanded version. .type_info => unreachable, // needed to resolve the type before now - .bound_fn => unreachable, } } @@ -1349,7 +1345,6 @@ pub const Type = extern union { .type_info, .@"anyframe", .generic_poison, - .bound_fn, => unreachable, .array_u8, @@ -1613,7 +1608,6 @@ pub const Type = extern union { .comptime_int, .comptime_float, .noreturn, - .bound_fn, => return writer.writeAll(@tagName(t)), .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"), @@ -1949,7 +1943,6 @@ pub const Type = extern union { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, .generic_poison => unreachable, - .bound_fn => unreachable, // TODO get rid of these Type.Tag values. .atomic_order => unreachable, @@ -2468,7 +2461,6 @@ pub const Type = extern union { .enum_literal, .empty_struct, .empty_struct_literal, - .bound_fn, // These are function *bodies*, not pointers. // Special exceptions have to be made when emitting functions due to // this returning false. @@ -2703,7 +2695,6 @@ pub const Type = extern union { .inferred_alloc_mut => unreachable, .inferred_alloc_const => unreachable, - .bound_fn => unreachable, .array, .array_sentinel, @@ -3182,7 +3173,6 @@ pub const Type = extern union { .noreturn, .inferred_alloc_const, .inferred_alloc_mut, - .bound_fn, => unreachable, .generic_poison => unreachable, @@ -3282,7 +3272,6 @@ pub const Type = extern union { .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer .function => unreachable, // represents machine code; not a pointer .@"opaque" => unreachable, // no size available - .bound_fn => unreachable, .noreturn => unreachable, .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, @@ -3630,7 +3619,6 @@ pub const Type = extern union { .inferred_alloc_mut => unreachable, .@"opaque" => unreachable, .generic_poison => unreachable, - .bound_fn => unreachable, .void => return 0, .bool, .u1 => return 1, @@ -5042,7 +5030,6 @@ pub const Type = extern union { .single_const_pointer, .single_mut_pointer, .pointer, - .bound_fn, => return null, .optional => { @@ -5245,7 +5232,6 @@ pub const Type = extern union { .inferred_alloc_mut => unreachable, .inferred_alloc_const => unreachable, - .bound_fn => unreachable, .array, .array_sentinel, @@ -6081,7 +6067,6 @@ pub const Type = extern union { inferred_alloc_mut, /// Same as `inferred_alloc_mut` but the local is `var` not `const`. inferred_alloc_const, // See last_no_payload_tag below. - bound_fn, // After this, the tag requires a payload. array_u8, @@ -6126,7 +6111,7 @@ pub const Type = extern union { enum_full, enum_nonexhaustive, - pub const last_no_payload_tag = Tag.bound_fn; + pub const last_no_payload_tag = Tag.inferred_alloc_const; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; pub fn Type(comptime t: Tag) type { @@ -6199,7 +6184,6 @@ pub const Type = extern union { .extern_options, .type_info, .@"anyframe", - .bound_fn, => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), .array_u8, diff --git a/src/value.zig b/src/value.zig index 503797abba46..93edf60eb2f5 100644 --- a/src/value.zig +++ b/src/value.zig @@ -183,10 +183,6 @@ pub const Value = extern union { /// Used to coordinate alloc_inferred, store_to_inferred_ptr, and resolve_inferred_alloc /// instructions for comptime code. inferred_alloc_comptime, - /// Used sometimes as the result of field_call_bind. This value is always temporary, - /// and refers directly to the air. It will never be referenced by the air itself. - /// TODO: This is probably a bad encoding, maybe put temp data in the sema instead. - bound_fn, /// The ABI alignment of the payload type. lazy_align, /// The ABI size of the payload type. @@ -326,7 +322,6 @@ pub const Value = extern union { .inferred_alloc_comptime => Payload.InferredAllocComptime, .aggregate => Payload.Aggregate, .@"union" => Payload.Union, - .bound_fn => Payload.BoundFn, .comptime_field_ptr => Payload.ComptimeFieldPtr, }; } @@ -477,7 +472,6 @@ pub const Value = extern union { .extern_options_type, .type_info_type, .generic_poison, - .bound_fn, => unreachable, .ty, .lazy_align, .lazy_size => { @@ -837,10 +831,6 @@ pub const Value = extern union { try out_stream.writeAll("(opt_payload_ptr)"); val = val.castTag(.opt_payload_ptr).?.data.container_ptr; }, - .bound_fn => { - const bound_func = val.castTag(.bound_fn).?.data; - return out_stream.print("(bound_fn %{}(%{})", .{ bound_func.func_inst, bound_func.arg0_inst }); - }, }; } @@ -5657,16 +5647,6 @@ pub const Value = extern union { val: Value, }, }; - - pub const BoundFn = struct { - pub const base_tag = Tag.bound_fn; - - base: Payload = Payload{ .tag = base_tag }, - data: struct { - func_inst: Air.Inst.Ref, - arg0_inst: Air.Inst.Ref, - }, - }; }; /// Big enough to fit any non-BigInt value diff --git a/test/behavior/member_func.zig b/test/behavior/member_func.zig index 3e3635af9811..bb1e1e1769b8 100644 --- a/test/behavior/member_func.zig +++ b/test/behavior/member_func.zig @@ -86,18 +86,6 @@ test "@field field calls" { const pv = &v; const pcv: *const HasFuncs = pv; - try expect(@field(v, "get")() == 0); - @field(v, "inc")(); - try expect(v.state == 1); - try expect(@field(v, "get")() == 1); - - @field(pv, "inc")(); - try expect(v.state == 2); - try expect(@field(pv, "get")() == 2); - try expect(@field(v, "getPtr")().* == 2); - try expect(@field(pcv, "get")() == 2); - try expect(@field(pcv, "getPtr")().* == 2); - v.func_field = HasFuncs.one; try expect(@field(v, "func_field")(0) == 1); try expect(@field(pv, "func_field")(0) == 1); diff --git a/test/cases/compile_errors/member_function_arg_mismatch.zig b/test/cases/compile_errors/member_function_arg_mismatch.zig index 88f8b36a2d95..b739be95440b 100644 --- a/test/cases/compile_errors/member_function_arg_mismatch.zig +++ b/test/cases/compile_errors/member_function_arg_mismatch.zig @@ -6,10 +6,6 @@ pub export fn entry() void { var s: S = undefined; s.foo(true); } -pub export fn entry2() void { - var s: S = undefined; - @call(.auto, s.foo, .{true}); -} // error // backend=stage2 @@ -17,5 +13,3 @@ pub export fn entry2() void { // // :7:6: error: member function expected 2 argument(s), found 1 // :3:5: note: function declared here -// :11:19: error: member function expected 2 argument(s), found 1 -// :3:5: note: function declared here