Commit 20d4f7213d

Veikka Tuominen <git@vexu.eu>
2022-07-11 14:39:21
Sema: add notes about function return type
1 parent c9e1360
src/Sema.zig
@@ -4135,23 +4135,24 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
     const ptr = try sema.resolveInst(extra.lhs);
     const operand = try sema.resolveInst(extra.rhs);
 
+    const is_ret = if (Zir.refToIndex(extra.lhs)) |ptr_index|
+        zir_tags[ptr_index] == .ret_ptr
+    else
+        false;
+
     // Check for the possibility of this pattern:
     //   %a = ret_ptr
     //   %b = store(%a, %c)
     // Where %c is an error union or error set. In such case we need to add
     // to the current function's inferred error set, if any.
-    if ((sema.typeOf(operand).zigTypeTag() == .ErrorUnion or
+    if (is_ret and (sema.typeOf(operand).zigTypeTag() == .ErrorUnion or
         sema.typeOf(operand).zigTypeTag() == .ErrorSet) and
         sema.fn_ret_ty.zigTypeTag() == .ErrorUnion)
     {
-        if (Zir.refToIndex(extra.lhs)) |ptr_index| {
-            if (zir_tags[ptr_index] == .ret_ptr) {
-                try sema.addToInferredErrorSet(operand);
-            }
-        }
+        try sema.addToInferredErrorSet(operand);
     }
 
-    return sema.storePtr(block, src, ptr, operand);
+    return sema.storePtr2(block, src, ptr, src, operand, src, if (is_ret) .ret_ptr else .store);
 }
 
 fn zirParamType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -5543,7 +5544,7 @@ fn analyzeCall(
             try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst)
         else
             try sema.resolveInst(fn_info.ret_ty_ref);
-        const ret_ty_src = func_src; // TODO better source location
+        const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
         const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
         // Create a fresh inferred error set type for inline/comptime calls.
         const fn_ret_ty = blk: {
@@ -6885,7 +6886,7 @@ fn zirFunc(
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
     const target = sema.mod.getTarget();
-    const ret_ty_src = inst_data.src(); // TODO better source location
+    const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node };
 
     var extra_index = extra.end;
 
@@ -7467,13 +7468,20 @@ fn analyzeAs(
     zir_dest_type: Zir.Inst.Ref,
     zir_operand: Zir.Inst.Ref,
 ) CompileError!Air.Inst.Ref {
+    const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index|
+        sema.code.instructions.items(.tag)[ptr_index] == .ret_type
+    else
+        false;
     const dest_ty = try sema.resolveType(block, src, zir_dest_type);
     const operand = try sema.resolveInst(zir_operand);
     if (dest_ty.tag() == .var_args_param) return operand;
     if (dest_ty.zigTypeTag() == .NoReturn) {
         return sema.fail(block, src, "cannot cast to noreturn", .{});
     }
-    return sema.coerce(block, dest_ty, operand, src);
+    return sema.coerceExtra(block, dest_ty, operand, src, true, is_ret) catch |err| switch (err) {
+        error.NotCoercible => unreachable,
+        else => |e| return e,
+    };
 }
 
 fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -13656,7 +13664,10 @@ fn analyzeRet(
     if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
         try sema.addToInferredErrorSet(uncasted_operand);
     }
-    const operand = try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src);
+    const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, true, true) catch |err| switch (err) {
+        error.NotCoercible => unreachable,
+        else => |e| return e,
+    };
 
     if (block.inlining) |inlining| {
         if (block.is_comptime) {
@@ -19869,7 +19880,7 @@ fn coerce(
     inst: Air.Inst.Ref,
     inst_src: LazySrcLoc,
 ) CompileError!Air.Inst.Ref {
-    return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, true) catch |err| switch (err) {
+    return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, true, false) catch |err| switch (err) {
         error.NotCoercible => unreachable,
         else => |e| return e,
     };
@@ -19888,6 +19899,7 @@ fn coerceExtra(
     inst: Air.Inst.Ref,
     inst_src: LazySrcLoc,
     report_err: bool,
+    is_ret: bool,
 ) CoersionError!Air.Inst.Ref {
     switch (dest_ty_unresolved.tag()) {
         .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src),
@@ -19939,7 +19951,7 @@ fn coerceExtra(
 
             // T to ?T
             const child_type = try dest_ty.optionalChildAlloc(sema.arena);
-            const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, false) catch |err| switch (err) {
+            const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, false, is_ret) catch |err| switch (err) {
                 error.NotCoercible => {
                     if (in_memory_result == .no_match) {
                         // Try to give more useful notes
@@ -20056,7 +20068,7 @@ fn coerceExtra(
                         return sema.addConstant(dest_ty, Value.@"null");
                     },
                     .ComptimeInt => {
-                        const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, false) catch |err| switch (err) {
+                        const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, false, is_ret) catch |err| switch (err) {
                             error.NotCoercible => break :pointer,
                             else => |e| return e,
                         };
@@ -20067,7 +20079,7 @@ fn coerceExtra(
                             .signed => Type.isize,
                             .unsigned => Type.usize,
                         };
-                        const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, false) catch |err| switch (err) {
+                        const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, false, is_ret) catch |err| switch (err) {
                             error.NotCoercible => {
                                 // Try to give more useful notes
                                 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src);
@@ -20414,6 +20426,19 @@ fn coerceExtra(
 
     if (!report_err) return error.NotCoercible;
 
+    if (is_ret and dest_ty.zigTypeTag() == .NoReturn) {
+        const msg = msg: {
+            const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{});
+            errdefer msg.destroy(sema.gpa);
+
+            const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
+            const src_decl = sema.mod.declPtr(sema.func.?.owner_decl);
+            try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "'noreturn' declared here", .{});
+            break :msg msg;
+        };
+        return sema.failWithOwnedErrorMsg(block, msg);
+    }
+
     const msg = msg: {
         const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod) });
         errdefer msg.destroy(sema.gpa);
@@ -20436,6 +20461,20 @@ fn coerceExtra(
         }
 
         try in_memory_result.report(sema, block, inst_src, msg);
+
+        // Add notes about function return type
+        if (is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) {
+            const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
+            const src_decl = sema.mod.declPtr(sema.func.?.owner_decl);
+            if (inst_ty.isError() and !dest_ty.isError()) {
+                try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "function cannot return an error", .{});
+            } else {
+                try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "function return type declared here", .{});
+            }
+        }
+
+        // TODO maybe add "cannot store an error in type '{}'" note
+
         break :msg msg;
     };
     return sema.failWithOwnedErrorMsg(block, msg);
@@ -21372,6 +21411,8 @@ fn storePtr2(
     // TODO do the same thing for anon structs as for tuples above.
     // However, beware of the need to handle missing/extra fields.
 
+    const is_ret = air_tag == .ret_ptr;
+
     // Detect if we are storing an array operand to a bitcasted vector pointer.
     // If so, we instead reach through the bitcasted pointer to the vector pointer,
     // bitcast the array operand to a vector, and then lower this as a store of
@@ -21380,12 +21421,18 @@ fn storePtr2(
     // https://github.com/ziglang/zig/issues/11154
     if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
         const vector_ty = sema.typeOf(vector_ptr).childType();
-        const vector = try sema.coerce(block, vector_ty, uncasted_operand, operand_src);
+        const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) {
+            error.NotCoercible => unreachable,
+            else => |e| return e,
+        };
         try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store);
         return;
     }
 
-    const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
+    const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) {
+        error.NotCoercible => unreachable,
+        else => |e| return e,
+    };
     const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand);
 
     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
@@ -21415,7 +21462,11 @@ fn storePtr2(
 
     try sema.requireRuntimeBlock(block, runtime_src);
     try sema.queueFullTypeResolution(elem_ty);
-    _ = try block.addBinOp(air_tag, ptr, operand);
+    if (is_ret) {
+        _ = try block.addBinOp(.store, ptr, operand);
+    } else {
+        _ = try block.addBinOp(air_tag, ptr, operand);
+    }
 }
 
 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector
test/cases/aarch64-macos/hello_world_with_updates.1.zig
@@ -2,4 +2,5 @@ pub export fn main() noreturn {}
 
 // error
 //
-// :1:32: error: expected type 'noreturn', found 'void'
+// :1:32: error: function declared 'noreturn' returns
+// :1:22: note: 'noreturn' declared here
test/cases/compile_errors/stage1/test/helpful_return_type_error_message.zig
@@ -16,15 +16,17 @@ export fn quux() u32 {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
-// is_test=1
 //
-// tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'
-// tmp.zig:1:17: note: function cannot return an error
-// tmp.zig:8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set'
-// tmp.zig:7:17: note: function cannot return an error
-// tmp.zig:11:15: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
-// tmp.zig:10:17: note: function cannot return an error
-// tmp.zig:15:14: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
-// tmp.zig:14:5: note: cannot store an error in type 'u32'
+// :2:18: error: expected type 'u32', found 'error{Ohno}'
+// :1:17: note: function cannot return an error
+// :8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set'
+// :7:17: note: function cannot return an error
+// :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
+// :10:17: note: function cannot return an error
+// :11:15: note: cannot convert error union to payload type
+// :11:15: note: consider using `try`, `catch`, or `if`
+// :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
+// :15:14: note: cannot convert error union to payload type
+// :15:14: note: consider using `try`, `catch`, or `if`
test/cases/compile_errors/address_of_number_literal.zig
@@ -9,3 +9,4 @@ export fn entry() usize { return @sizeOf(@TypeOf(&foo)); }
 //
 // :3:30: error: expected type '*const i32', found '*const comptime_int'
 // :3:30: note: pointer type child 'comptime_int' cannot cast into pointer type child 'i32'
+// :3:10: note: function return type declared here
test/cases/compile_errors/incompatible_sentinels.zig
@@ -21,8 +21,10 @@ export fn entry4() void {
 //
 // :4:12: error: expected type '[*:0]u8', found '[*:255]u8'
 // :4:12: note: pointer sentinel '255' cannot cast into pointer sentinel '0'
+// :3:35: note: function return type declared here
 // :7:12: error: expected type '[*:0]u8', found '[*]u8'
 // :7:12: note: destination pointer requires '0' sentinel
+// :6:31: note: function return type declared here
 // :10:35: error: expected type '[2:0]u8', found '[2:255]u8'
 // :10:35: note: array sentinel '255' cannot cast into array sentinel '0'
 // :14:31: error: expected type '[2:0]u8', found '[2]u8'
test/cases/compile_errors/incorrect_return_type.zig
@@ -21,3 +21,4 @@
 // :8:16: error: expected type 'tmp.A', found 'tmp.B'
 // :10:12: note: struct declared here
 // :4:12: note: struct declared here
+// :7:11: note: function return type declared here
test/cases/compile_errors/invalid_address_space_coercion.zig
@@ -12,3 +12,4 @@ pub fn main() void {
 //
 // :2:12: error: expected type '*i32', found '*addrspace(.gs) i32'
 // :2:12: note: address space 'gs' cannot cast into address space 'generic'
+// :1:34: note: function return type declared here
test/cases/compile_errors/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig
@@ -12,3 +12,4 @@ pub fn main() void {
 //
 // :2:12: error: expected type '*i32', found '*addrspace(.gs) i32'
 // :2:12: note: address space 'gs' cannot cast into address space 'generic'
+// :1:34: note: function return type declared here
test/cases/compile_errors/pointer_with_different_address_spaces.zig
@@ -12,3 +12,4 @@ export fn entry2() void {
 //
 // :2:12: error: expected type '*addrspace(.fs) i32', found '*addrspace(.gs) i32'
 // :2:12: note: address space 'gs' cannot cast into address space 'fs'
+// :1:34: note: function return type declared here
test/cases/compile_errors/pointers_with_different_address_spaces.zig
@@ -12,3 +12,4 @@ pub fn main() void {
 //
 // :2:13: error: expected type '*i32', found '*addrspace(.gs) i32'
 // :2:13: note: address space 'gs' cannot cast into address space 'generic'
+// :1:35: note: function return type declared here
test/cases/compile_errors/slice_sentinel_mismatch-2.zig
@@ -10,3 +10,4 @@ comptime { _ = foo; }
 //
 // :3:12: error: expected type '[:0]u8', found '[]u8'
 // :3:12: note: destination pointer requires '0' sentinel
+// :1:10: note: function return type declared here
test/cases/compile_errors/try_in_function_with_non_error_return_type.zig
@@ -8,3 +8,4 @@ fn something() anyerror!void { }
 // target=native
 //
 // :2:5: error: expected type 'void', found 'anyerror'
+// :1:15: note: function cannot return an error
test/cases/compile_errors/unreachable_with_return.zig
@@ -5,4 +5,5 @@ export fn entry() void { a(); }
 // backend=stage2
 // target=native
 //
-// :1:18: error: expected type 'noreturn', found 'void'
+// :1:18: error: function declared 'noreturn' returns
+// :1:8: note: 'noreturn' declared here
test/cases/compile_errors/variable_has_wrong_type.zig
@@ -8,3 +8,4 @@ export fn f() i32 {
 // target=native
 //
 // :3:12: error: expected type 'i32', found '*const [1:0]u8'
+// :1:15: note: function return type declared here
test/cases/x86_64-linux/hello_world_with_updates.1.zig
@@ -2,4 +2,5 @@ pub export fn _start() noreturn {}
 
 // error
 //
-// :1:34: error: expected type 'noreturn', found 'void'
+// :1:34: error: function declared 'noreturn' returns
+// :1:24: note: 'noreturn' declared here
test/cases/x86_64-macos/hello_world_with_updates.1.zig
@@ -2,4 +2,5 @@ pub export fn main() noreturn {}
 
 // error
 //
-// :1:32: error: expected type 'noreturn', found 'void'
+// :1:32: error: function declared 'noreturn' returns
+// :1:22: note: 'noreturn' declared here