From 0a7f8c2e013f24aa6c94093400fe6377ab74b4e1 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 9 May 2023 15:30:14 +0300 Subject: [PATCH 1/7] Sema: return const pointers from ref inits Closes #12189 --- src/Sema.zig | 25 ++++++++++++++++--------- test/behavior/basic.zig | 7 +++++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 225a6c5bff..d3749efce3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -252,7 +252,6 @@ pub const Block = struct { // TODO is_comptime and comptime_reason should probably be merged together. is_comptime: bool, is_typeof: bool = false, - is_coerce_result_ptr: bool = false, /// Keep track of the active error return trace index around blocks so that we can correctly /// pop the error trace upon block exit. @@ -2469,7 +2468,6 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE // kind of transformations to make on the result pointer. var trash_block = block.makeSubBlock(); trash_block.is_comptime = false; - trash_block.is_coerce_result_ptr = true; defer trash_block.instructions.deinit(sema.gpa); const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr)); @@ -2579,6 +2577,9 @@ fn coerceResultPtr( .array_to_slice => { return sema.fail(block, src, "TODO coerce_result_ptr array_to_slice", .{}); }, + .get_union_tag => { + return sema.fail(block, src, "TODO coerce_result_ptr get_union_tag", .{}); + }, else => { if (std.debug.runtime_safety) { std.debug.panic("unexpected AIR tag for coerce_result_ptr: {}", .{ @@ -3563,6 +3564,13 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro )); } + return sema.makePtrConst(block, alloc); +} + +fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { + const alloc_ty = sema.typeOf(alloc); + + var ptr_info = alloc_ty.ptrInfo().data; ptr_info.mutable = false; const const_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); @@ -3831,7 +3839,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com var trash_block = block.makeSubBlock(); trash_block.is_comptime = false; - trash_block.is_coerce_result_ptr = true; defer trash_block.instructions.deinit(gpa); const mut_final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ @@ -17773,7 +17780,7 @@ fn zirStructInit( try sema.storePtr(block, src, field_ptr, init_inst); const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(), tag_val); _ = try block.addBinOp(.set_union_tag, alloc, new_tag); - return alloc; + return sema.makePtrConst(block, alloc); } try sema.requireRuntimeBlock(block, src, null); @@ -17900,7 +17907,7 @@ fn finishStructInit( try sema.storePtr(block, dest_src, field_ptr, field_init); } - return alloc; + return sema.makePtrConst(block, alloc); } try sema.requireRuntimeBlock(block, dest_src, null); @@ -18017,7 +18024,7 @@ fn zirStructInitAnon( } } - return alloc; + return sema.makePtrConst(block, alloc); } const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len); @@ -18120,7 +18127,7 @@ fn zirArrayInit( const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); _ = try block.addBinOp(.store, elem_ptr, arg); } - return alloc; + return sema.makePtrConst(block, alloc); } const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ @@ -18135,7 +18142,7 @@ fn zirArrayInit( const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); _ = try block.addBinOp(.store, elem_ptr, arg); } - return alloc; + return sema.makePtrConst(block, alloc); } return block.addAggregateInit(array_ty, resolved_args); @@ -18213,7 +18220,7 @@ fn zirArrayInitAnon( } } - return alloc; + return sema.makePtrConst(block, alloc); } const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 073be26288..5b8ca80198 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1145,3 +1145,10 @@ test "arrays and vectors with big integers" { try expect(b[0] == comptime std.math.maxInt(Int)); } } + +test "pointer to struct literal with runtime field is constant" { + const S = struct { data: usize }; + var runtime_zero: usize = 0; + const ptr = &S{ .data = runtime_zero }; + try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_const); +} From 73f283e3e2326c5c8a7f5b87de5c7eed7b7ea099 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 10 May 2023 11:38:04 +0300 Subject: [PATCH 2/7] Sema: fix nested call debug info Closes #15631 --- src/Sema.zig | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d3749efce3..436b065b5d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6387,14 +6387,18 @@ fn zirCall( } resolved_args[arg_index] = resolved; } - if (sema.owner_func == null or !sema.owner_func.?.calls_or_awaits_errorable_fn) + if (sema.owner_func == null or !sema.owner_func.?.calls_or_awaits_errorable_fn) { input_is_error = false; // input was an error type, but no errorable fn's were actually called + } + + // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction. + const call_dbg_node = inst - 1; if (sema.mod.backendSupportsFeature(.error_return_trace) and sema.mod.comp.bin_file.options.error_return_tracing and !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_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src); + break :b try sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); }; const return_ty = sema.typeOf(call_inst); @@ -6423,11 +6427,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_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src); + return sema.analyzeCall(block, func, func_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_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src); + return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); } } @@ -6511,6 +6515,7 @@ fn analyzeCall( ensure_result_used: bool, uncasted_args: []const Air.Inst.Ref, bound_arg_src: ?LazySrcLoc, + call_dbg_node: ?Zir.Inst.Index, ) CompileError!Air.Inst.Ref { const mod = sema.mod; @@ -6628,6 +6633,7 @@ fn analyzeCall( uncasted_args, call_tag, bound_arg_src, + call_dbg_node, )) |some| { return some; } else |err| switch (err) { @@ -7016,6 +7022,8 @@ fn analyzeCall( } } + if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); + try sema.queueFullTypeResolution(func_ty_info.return_type); if (sema.owner_func != null and func_ty_info.return_type.isError()) { sema.owner_func.?.calls_or_awaits_errorable_fn = true; @@ -7252,6 +7260,7 @@ fn instantiateGenericCall( uncasted_args: []const Air.Inst.Ref, call_tag: Air.Inst.Tag, bound_arg_src: ?LazySrcLoc, + call_dbg_node: ?Zir.Inst.Index, ) CompileError!Air.Inst.Ref { const mod = sema.mod; const gpa = sema.gpa; @@ -7508,6 +7517,8 @@ fn instantiateGenericCall( try sema.queueFullTypeResolution(new_fn_info.return_type); } + if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); + if (sema.owner_func != null and new_fn_info.return_type.isError()) { sema.owner_func.?.calls_or_awaits_errorable_fn = true; } @@ -11822,7 +11833,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op const panic_fn = try sema.getBuiltin("panicUnwrapError"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [2]Air.Inst.Ref = .{ err_return_trace, operand }; - _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null); + _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null, null); return true; }, .panic => { @@ -11833,7 +11844,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op const panic_fn = try sema.getBuiltin("panic"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value }; - _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null); + _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null, null); return true; }, else => unreachable, @@ -17279,7 +17290,7 @@ fn retWithErrTracing( const args: [1]Air.Inst.Ref = .{err_return_trace}; if (!need_check) { - _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); + _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null, null); _ = try block.addUnOp(ret_tag, operand); return always_noreturn; } @@ -17290,7 +17301,7 @@ fn retWithErrTracing( var else_block = block.makeSubBlock(); defer else_block.instructions.deinit(gpa); - _ = try sema.analyzeCall(&else_block, return_err_fn, src, src, .never_inline, false, &args, null); + _ = try sema.analyzeCall(&else_block, return_err_fn, src, src, .never_inline, false, &args, null, null); _ = try else_block.addUnOp(ret_tag, operand); try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + @@ -21647,7 +21658,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } } const ensure_result_used = extra.flags.ensure_result_used; - return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src); + return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, null); } fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -23481,7 +23492,7 @@ fn panicWithMsg( Value.null, ); const args: [3]Air.Inst.Ref = .{ msg_inst, null_stack_trace, .null_value }; - _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null); + _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null, null); } fn panicUnwrapError( @@ -23519,7 +23530,7 @@ fn panicUnwrapError( const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand); const err_return_trace = try sema.getErrorReturnTrace(&fail_block); const args: [2]Air.Inst.Ref = .{ err_return_trace, err }; - _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, &args, null); + _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, &args, null, null); } } try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); @@ -23604,7 +23615,7 @@ fn panicSentinelMismatch( else { const panic_fn = try sema.getBuiltin("checkNonScalarSentinel"); const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel }; - _ = try sema.analyzeCall(parent_block, panic_fn, sema.src, sema.src, .auto, false, &args, null); + _ = try sema.analyzeCall(parent_block, panic_fn, sema.src, sema.src, .auto, false, &args, null, null); return; }; @@ -23641,7 +23652,7 @@ fn safetyCheckFormatted( _ = try fail_block.addNoOp(.trap); } else { const panic_fn = try sema.getBuiltin(func); - _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null); + _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null, null); } try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); } From 67afd2a470153681d3a2323ec388e3ecd545cef1 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 10 May 2023 12:27:59 +0300 Subject: [PATCH 3/7] Sema: make `@call` compile errors match regular calls Closes #15642 --- src/Sema.zig | 203 ++++++++++-------- .../member_function_arg_mismatch.zig | 6 + 2 files changed, 118 insertions(+), 91 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 436b065b5d..f8c77c7c05 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5217,7 +5217,7 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.I if (block.is_comptime) { return sema.fail(block, src, "encountered @panic at comptime", .{}); } - try sema.panicWithMsg(block, src, msg_inst); + try sema.panicWithMsg(block, msg_inst); return always_noreturn; } @@ -6295,7 +6295,6 @@ fn zirCall( } else { resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len); } - const total_args = args_len + @boolToInt(bound_arg_src != null); const callee_ty = sema.typeOf(func); const func_ty = func_ty: { @@ -6311,45 +6310,16 @@ fn zirCall( } return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)}); }; - const func_ty_info = func_ty.fnInfo(); - - const fn_params_len = func_ty_info.param_types.len; - check_args: { - if (func_ty_info.is_var_args) { - assert(func_ty_info.cc == .C); - if (total_args >= fn_params_len) break :check_args; - } else if (fn_params_len == total_args) { - break :check_args; - } - - const maybe_decl = try sema.funcDeclSrc(func); - const member_str = if (bound_arg_src != null) "member function " else ""; - const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; - const msg = msg: { - const msg = try sema.errMsg( - block, - func_src, - "{s}expected {s}{d} argument(s), found {d}", - .{ - member_str, - variadic_str, - fn_params_len - @boolToInt(bound_arg_src != null), - args_len, - }, - ); - errdefer msg.destroy(sema.gpa); - - if (maybe_decl) |fn_decl| try sema.mod.errNoteNonLazy(fn_decl.srcLoc(), msg, "function declared here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(msg); - } + 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); const args_body = sema.code.extra[extra.end..]; var input_is_error = false; const block_index = @intCast(Air.Inst.Index, block.instructions.items.len); + const func_ty_info = func_ty.fnInfo(); + const fn_params_len = func_ty_info.param_types.len; const parent_comptime = block.is_comptime; // `extra_index` and `arg_index` are separate since the bound function is passed as the first argument. var extra_index: usize = 0; @@ -6398,7 +6368,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_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); + 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); }; const return_ty = sema.typeOf(call_inst); @@ -6427,14 +6397,86 @@ 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_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); + return sema.analyzeCall(block, func, func_ty, func_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_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); + return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); } } +fn checkCallArgumentCount( + sema: *Sema, + block: *Block, + func: Air.Inst.Ref, + func_src: LazySrcLoc, + func_ty: Type, + total_args: usize, + member_fn: bool, +) !void { + const func_ty_info = func_ty.fnInfo(); + const fn_params_len = func_ty_info.param_types.len; + const args_len = total_args - @boolToInt(member_fn); + if (func_ty_info.is_var_args) { + assert(func_ty_info.cc == .C); + if (total_args >= fn_params_len) return; + } else if (fn_params_len == total_args) { + return; + } + + const maybe_decl = try sema.funcDeclSrc(func); + const member_str = if (member_fn) "member function " else ""; + const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; + const msg = msg: { + const msg = try sema.errMsg( + block, + func_src, + "{s}expected {s}{d} argument(s), found {d}", + .{ + member_str, + variadic_str, + fn_params_len - @boolToInt(member_fn), + args_len, + }, + ); + errdefer msg.destroy(sema.gpa); + + if (maybe_decl) |fn_decl| try sema.mod.errNoteNonLazy(fn_decl.srcLoc(), msg, "function declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); +} + +fn callBuiltin( + sema: *Sema, + block: *Block, + builtin_fn: Air.Inst.Ref, + modifier: std.builtin.CallModifier, + args: []const Air.Inst.Ref, +) !void { + const callee_ty = sema.typeOf(builtin_fn); + const func_ty = func_ty: { + switch (callee_ty.zigTypeTag()) { + .Fn => break :func_ty callee_ty, + .Pointer => { + const ptr_info = callee_ty.ptrInfo().data; + if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Fn) { + break :func_ty ptr_info.pointee_type; + } + }, + else => {}, + } + std.debug.panic("type '{}' is not a function calling builtin fn", .{callee_ty.fmt(sema.mod)}); + }; + + const func_ty_info = func_ty.fnInfo(); + const fn_params_len = func_ty_info.param_types.len; + if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) { + std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len }); + } + _ = try sema.analyzeCall(block, builtin_fn, func_ty, sema.src, sema.src, modifier, false, args, null, null); +} + const GenericCallAdapter = struct { generic_fn: *Module.Fn, precomputed_hash: u64, @@ -6509,6 +6551,7 @@ fn analyzeCall( sema: *Sema, block: *Block, func: Air.Inst.Ref, + func_ty: Type, func_src: LazySrcLoc, call_src: LazySrcLoc, modifier: std.builtin.CallModifier, @@ -6519,22 +6562,10 @@ fn analyzeCall( ) CompileError!Air.Inst.Ref { const mod = sema.mod; - const callee_ty = sema.typeOf(func); - const func_ty = func_ty: { - switch (callee_ty.zigTypeTag()) { - .Fn => break :func_ty callee_ty, - .Pointer => { - const ptr_info = callee_ty.ptrInfo().data; - if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Fn) { - break :func_ty ptr_info.pointee_type; - } - }, - else => {}, - } - return sema.fail(block, func_src, "type '{}' is not a function", .{callee_ty.fmt(sema.mod)}); - }; + const callee_ty = sema.typeOf(func); const func_ty_info = func_ty.fnInfo(); + const fn_params_len = func_ty_info.param_types.len; const cc = func_ty_info.cc; if (cc == .Naked) { const maybe_decl = try sema.funcDeclSrc(func); @@ -6552,27 +6583,6 @@ fn analyzeCall( }; return sema.failWithOwnedErrorMsg(msg); } - const fn_params_len = func_ty_info.param_types.len; - if (func_ty_info.is_var_args) { - assert(cc == .C); - if (uncasted_args.len < fn_params_len) { - // TODO add error note: declared here - return sema.fail( - block, - func_src, - "expected at least {d} argument(s), found {d}", - .{ fn_params_len, uncasted_args.len }, - ); - } - } else if (fn_params_len != uncasted_args.len) { - // TODO add error note: declared here - return sema.fail( - block, - call_src, - "expected {d} argument(s), found {d}", - .{ fn_params_len, uncasted_args.len }, - ); - } const call_tag: Air.Inst.Tag = switch (modifier) { .auto, @@ -11822,9 +11832,6 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op .as_node => try sema.zirAsNode(block, inst), .field_val => try sema.zirFieldVal(block, inst), .@"unreachable" => { - const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable"; - const src = inst_data.src(); - if (!sema.mod.comp.formatted_panics) { try sema.safetyPanic(block, .unwrap_error); return true; @@ -11833,18 +11840,17 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op const panic_fn = try sema.getBuiltin("panicUnwrapError"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [2]Air.Inst.Ref = .{ err_return_trace, operand }; - _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null, null); + try sema.callBuiltin(block, panic_fn, .auto, &args); return true; }, .panic => { const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); const msg_inst = try sema.resolveInst(inst_data.operand); const panic_fn = try sema.getBuiltin("panic"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value }; - _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null, null); + try sema.callBuiltin(block, panic_fn, .auto, &args); return true; }, else => unreachable, @@ -17258,7 +17264,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); - return sema.retWithErrTracing(block, src, is_non_err, .ret_load, ret_ptr); + return sema.retWithErrTracing(block, is_non_err, .ret_load, ret_ptr); } _ = try block.addUnOp(.ret_load, ret_ptr); @@ -17268,7 +17274,6 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir fn retWithErrTracing( sema: *Sema, block: *Block, - src: LazySrcLoc, is_non_err: Air.Inst.Ref, ret_tag: Air.Inst.Tag, operand: Air.Inst.Ref, @@ -17290,7 +17295,7 @@ fn retWithErrTracing( const args: [1]Air.Inst.Ref = .{err_return_trace}; if (!need_check) { - _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null, null); + try sema.callBuiltin(block, return_err_fn, .never_inline, &args); _ = try block.addUnOp(ret_tag, operand); return always_noreturn; } @@ -17301,7 +17306,7 @@ fn retWithErrTracing( var else_block = block.makeSubBlock(); defer else_block.instructions.deinit(gpa); - _ = try sema.analyzeCall(&else_block, return_err_fn, src, src, .never_inline, false, &args, null, null); + try sema.callBuiltin(&else_block, return_err_fn, .never_inline, &args); _ = try else_block.addUnOp(ret_tag, operand); try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + @@ -17447,7 +17452,7 @@ fn analyzeRet( // Avoid adding a frame to the error return trace in case the value is comptime-known // to be not an error. const is_non_err = try sema.analyzeIsNonErr(block, src, operand); - return sema.retWithErrTracing(block, src, is_non_err, .ret, operand); + return sema.retWithErrTracing(block, is_non_err, .ret, operand); } _ = try block.addUnOp(.ret, operand); @@ -21657,8 +21662,25 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty); } } + + const callee_ty = sema.typeOf(func); + const func_ty = func_ty: { + switch (callee_ty.zigTypeTag()) { + .Fn => break :func_ty callee_ty, + .Pointer => { + const ptr_info = callee_ty.ptrInfo().data; + if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Fn) { + break :func_ty ptr_info.pointee_type; + } + }, + else => {}, + } + 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); + const ensure_result_used = extra.flags.ensure_result_used; - return sema.analyzeCall(block, func, 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, bound_arg_src, null); } fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -23469,7 +23491,6 @@ fn addSafetyCheckExtra( fn panicWithMsg( sema: *Sema, block: *Block, - src: LazySrcLoc, msg_inst: Air.Inst.Ref, ) !void { const mod = sema.mod; @@ -23492,7 +23513,7 @@ fn panicWithMsg( Value.null, ); const args: [3]Air.Inst.Ref = .{ msg_inst, null_stack_trace, .null_value }; - _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null, null); + try sema.callBuiltin(block, panic_fn, .auto, &args); } fn panicUnwrapError( @@ -23530,7 +23551,7 @@ fn panicUnwrapError( const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand); const err_return_trace = try sema.getErrorReturnTrace(&fail_block); const args: [2]Air.Inst.Ref = .{ err_return_trace, err }; - _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, &args, null, null); + try sema.callBuiltin(&fail_block, panic_fn, .auto, &args); } } try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); @@ -23615,7 +23636,7 @@ fn panicSentinelMismatch( else { const panic_fn = try sema.getBuiltin("checkNonScalarSentinel"); const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel }; - _ = try sema.analyzeCall(parent_block, panic_fn, sema.src, sema.src, .auto, false, &args, null, null); + try sema.callBuiltin(parent_block, panic_fn, .auto, &args); return; }; @@ -23652,7 +23673,7 @@ fn safetyCheckFormatted( _ = try fail_block.addNoOp(.trap); } else { const panic_fn = try sema.getBuiltin(func); - _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null, null); + try sema.callBuiltin(&fail_block, panic_fn, .auto, args); } try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); } @@ -23671,7 +23692,7 @@ fn safetyPanic( )).?; const msg_inst = try sema.analyzeDeclVal(block, sema.src, msg_decl_index); - try sema.panicWithMsg(block, sema.src, msg_inst); + try sema.panicWithMsg(block, msg_inst); } fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { diff --git a/test/cases/compile_errors/member_function_arg_mismatch.zig b/test/cases/compile_errors/member_function_arg_mismatch.zig index b739be9544..88f8b36a2d 100644 --- a/test/cases/compile_errors/member_function_arg_mismatch.zig +++ b/test/cases/compile_errors/member_function_arg_mismatch.zig @@ -6,6 +6,10 @@ 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 @@ -13,3 +17,5 @@ pub export fn entry() 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 From f0fdaf32d3b802e9db16a0753d9ff49a8667089b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 10 May 2023 16:26:54 +0300 Subject: [PATCH 4/7] fix incorrect use of mutable pointers to temporary values --- lib/std/os/linux/x86.zig | 2 +- src/Module.zig | 2 +- src/link/MachO/Atom.zig | 2 +- test/behavior/array.zig | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/os/linux/x86.zig b/lib/std/os/linux/x86.zig index 2e67fa6b5b..c9274e11ee 100644 --- a/lib/std/os/linux/x86.zig +++ b/lib/std/os/linux/x86.zig @@ -108,7 +108,7 @@ pub fn syscall6( ); } -pub fn socketcall(call: usize, args: [*]usize) usize { +pub fn socketcall(call: usize, args: [*]const usize) usize { return asm volatile ("int $0x80" : [ret] "={eax}" (-> usize), : [number] "{eax}" (@enumToInt(SYS.socketcall)), diff --git a/src/Module.zig b/src/Module.zig index b0c18def78..6d1a5acb09 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -6098,7 +6098,7 @@ pub const PeerTypeCandidateSrc = union(enum) { none: void, /// When we want to know the the src of candidate i, look up at /// index i in this slice - override: []?LazySrcLoc, + override: []const ?LazySrcLoc, /// resolvePeerTypes originates from a @TypeOf(...) call typeof_builtin_call_node_offset: i32, diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index fb05595b7d..970371e455 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -116,7 +116,7 @@ pub fn addRelocation(macho_file: *MachO, atom_index: Index, reloc: Relocation) ! return addRelocations(macho_file, atom_index, &[_]Relocation{reloc}); } -pub fn addRelocations(macho_file: *MachO, atom_index: Index, relocs: []Relocation) !void { +pub fn addRelocations(macho_file: *MachO, atom_index: Index, relocs: []const Relocation) !void { const gpa = macho_file.base.allocator; const gop = try macho_file.relocs.getOrPut(gpa, atom_index); if (!gop.found_existing) { diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 6cf0ab9e1d..a54aaa898e 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -667,7 +667,7 @@ test "array init of container level array variable" { test "runtime initialized sentinel-terminated array literal" { var c: u16 = 300; const f = &[_:0x9999]u16{c}; - const g = @ptrCast(*[4]u8, f); + const g = @ptrCast(*const [4]u8, f); try std.testing.expect(g[2] == 0x99); try std.testing.expect(g[3] == 0x99); } From c0102ac1c89db1727d7c58d566376a6a79af4e98 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 10 May 2023 17:04:48 +0300 Subject: [PATCH 5/7] Sema: add error for resolving inferred error set of generic function Closes #14991 --- src/Sema.zig | 10 ++++++++++ ...esolve_inferred_error_set_of_generic_fn.zig | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 test/cases/compile_errors/resolve_inferred_error_set_of_generic_fn.zig diff --git a/src/Sema.zig b/src/Sema.zig index f8c77c7c05..2521bddce0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -31523,6 +31523,16 @@ fn resolveInferredErrorSet( if (ies_func_info.return_type.tag() == .generic_poison) { assert(ies_func_info.cc == .Inline); } else if (ies_func_info.return_type.errorUnionSet().castTag(.error_set_inferred).?.data == ies) { + if (ies_func_info.is_generic) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "unable to resolve inferred error set of generic function", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.mod.errNoteNonLazy(ies_func_owner_decl.srcLoc(), msg, "generic function declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } // In this case we are dealing with the actual InferredErrorSet object that // corresponds to the function, not one created to track an inline/comptime call. try sema.ensureFuncBodyAnalyzed(ies.func); diff --git a/test/cases/compile_errors/resolve_inferred_error_set_of_generic_fn.zig b/test/cases/compile_errors/resolve_inferred_error_set_of_generic_fn.zig new file mode 100644 index 0000000000..587ff7a878 --- /dev/null +++ b/test/cases/compile_errors/resolve_inferred_error_set_of_generic_fn.zig @@ -0,0 +1,18 @@ +fn foo(a: anytype) !void { + if (a == 0) return error.A; + return error.B; +} +const Error = error{ A, B }; +export fn entry() void { + const info = @typeInfo(@TypeOf(foo)); + const ret_type = info.Fn.return_type.?; + const error_set = @typeInfo(ret_type).ErrorUnion.error_set; + _ = Error || error_set; +} + +// error +// backend=stage2 +// target=native +// +// :10:15: error: unable to resolve inferred error set of generic function +// :1:1: note: generic function declared here From 0958d5d7db24222bdcc0fb8e8cdc9838953b9857 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 11 May 2023 12:23:39 +0300 Subject: [PATCH 6/7] Sema: fix crash when generating anon name on invalid code Closes #15615 --- src/Sema.zig | 11 +++++++---- src/print_air.zig | 1 - ...struct_type_returned_from_non-generic_function.zig | 10 ++++++++++ 3 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 test/cases/compile_errors/struct_type_returned_from_non-generic_function.zig diff --git a/src/Sema.zig b/src/Sema.zig index 2521bddce0..a3502726bb 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2730,9 +2730,13 @@ fn createAnonymousDeclTypeNamed( for (fn_info.param_body) |zir_inst| switch (zir_tags[zir_inst]) { .param, .param_comptime, .param_anytype, .param_anytype_comptime => { const arg = sema.inst_map.get(zir_inst).?; - // The comptime call code in analyzeCall already did this, so we're - // just repeating it here and it's guaranteed to work. - const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg, "") catch unreachable; + // If this is being called in a generic function then analyzeCall will + // have already resolved the args and this will work. + // If not then this is a struct type being returned from a non-generic + // function and the name doesn't matter since it will later + // result in a compile error. + const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg, "") catch + return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null); if (arg_i != 0) try buf.appendSlice(","); try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)}); @@ -6562,7 +6566,6 @@ fn analyzeCall( ) CompileError!Air.Inst.Ref { const mod = sema.mod; - const callee_ty = sema.typeOf(func); const func_ty_info = func_ty.fnInfo(); const fn_params_len = func_ty_info.param_types.len; diff --git a/src/print_air.zig b/src/print_air.zig index d90d31ec67..6ed2e48bbc 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -917,7 +917,6 @@ const Writer = struct { try s.writeAll("\n"); try s.writeByteNTimes(' ', old_indent); - try s.writeAll("}"); } fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { diff --git a/test/cases/compile_errors/struct_type_returned_from_non-generic_function.zig b/test/cases/compile_errors/struct_type_returned_from_non-generic_function.zig new file mode 100644 index 0000000000..27e2b7b1db --- /dev/null +++ b/test/cases/compile_errors/struct_type_returned_from_non-generic_function.zig @@ -0,0 +1,10 @@ +pub export fn entry(param: usize) usize { + return struct{ param }; +} + +// error +// backend=stage2 +// target=native +// +// :2:12: error: expected type 'usize', found 'type' +// :1:35: note: function return type declared here From 5aa9628de3c6637f45b9d8cf8cbd19c422a74f6f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 12 May 2023 14:59:26 +0300 Subject: [PATCH 7/7] Sema: handle recursive inferred errors better in analyzeIsNonErrComptimeOnly Closes #15669 --- src/Sema.zig | 11 ++++------- test/behavior/error.zig | 41 +++++++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index a3502726bb..a93a7a6079 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -29153,8 +29153,6 @@ fn analyzeIsNonErrComptimeOnly( if (ies.errors.count() != 0) break :blk; if (maybe_operand_val == null) { // Try to avoid resolving inferred error set if possible. - if (ies.errors.count() != 0) break :blk; - if (ies.is_anyerror) break :blk; for (ies.inferred_error_sets.keys()) |other_ies| { if (ies == other_ies) continue; try sema.resolveInferredErrorSet(block, src, other_ies); @@ -29166,11 +29164,10 @@ fn analyzeIsNonErrComptimeOnly( if (other_ies.errors.count() != 0) break :blk; } - if (ies.func == sema.owner_func) { - // We're checking the inferred errorset of the current function and none of - // its child inferred error sets contained any errors meaning that any value - // so far with this type can't contain errors either. - return Air.Inst.Ref.bool_true; + if (!ies.is_resolved and ies.func.state == .in_progress) { + // Calling resolveInferredErrorSet would immediately fail + // so we'll have to rely on runtime checks. + return Air.Inst.Ref.none; } try sema.resolveInferredErrorSet(block, src, ies); if (ies.is_anyerror) break :blk; diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 91b5561d62..acb4b8fb61 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -678,22 +678,6 @@ test "error union payload is properly aligned" { if (blk.a != 1) unreachable; } -test "ret_ptr doesn't cause own inferred error set to be resolved" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - - const S = struct { - fn foo() !void {} - - fn doTheTest() !void { - errdefer @compileError("bad"); - - return try @This().foo(); - } - }; - try S.doTheTest(); -} - test "simple else prong allowed even when all errors handled" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -889,3 +873,28 @@ test "optional error set return type" { try expect(null == S.foo(true)); try expect(E.A == S.foo(false).?); } + +test "try used in recursive function with inferred error set" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const Value = union(enum) { + values: []const @This(), + b, + + fn x(value: @This()) !void { + switch (value.values[0]) { + .values => return try x(value.values[0]), + .b => return error.a, + } + } + }; + const a = Value{ + .values = &[1]Value{ + .{ + .values = &[1]Value{.{ .b = {} }}, + }, + }, + }; + try expectError(error.a, Value.x(a)); +}