From 60830e36e38f49bc797ae731c9dd58d7231039bf Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 21 Jul 2023 02:19:32 +0200 Subject: [PATCH 1/6] Sema error: talk about discarding instead of suppressing Maybe I'm just being pedantic here (most likely) but I don't like how we're just telling the user here how to "suppress this error" by "assigning the value to '_'". I think it's better if we use the word "discard" here which I think is the official terminology and also tells the user what it actually means to "assign the value to '_'". Also, using the value would also be a way to "suppress the error". It is just one of the two options: discard or use. --- src/Sema.zig | 2 +- .../for_loop_body_expression_ignored.zig | 8 ++++---- .../compile_errors/generic_instantiation_failure.zig | 2 +- .../ignored_assert-err-ok_return_value.zig | 2 +- .../ignored_comptime_statement_value.zig | 2 +- test/cases/compile_errors/ignored_comptime_value.zig | 4 ++-- .../ignored_deferred_statement_value.zig | 2 +- test/cases/compile_errors/ignored_return_value.zig | 2 +- test/cases/compile_errors/ignored_statement_value.zig | 2 +- .../while_loop_body_expression_ignored.zig | 10 +++++----- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 37fdf0adb5..3ff817cbd1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3571,7 +3571,7 @@ fn ensureResultUsed( const msg = try sema.errMsg(block, src, "value of type '{}' ignored", .{ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); try sema.errNote(block, src, msg, "all non-void values must be used", .{}); - try sema.errNote(block, src, msg, "this error can be suppressed by assigning the value to '_'", .{}); + try sema.errNote(block, src, msg, "to discard the value, assign it to '_'", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); diff --git a/test/cases/compile_errors/for_loop_body_expression_ignored.zig b/test/cases/compile_errors/for_loop_body_expression_ignored.zig index a0247139ea..9c5e381e63 100644 --- a/test/cases/compile_errors/for_loop_body_expression_ignored.zig +++ b/test/cases/compile_errors/for_loop_body_expression_ignored.zig @@ -23,13 +23,13 @@ export fn f4() void { // // :5:30: error: value of type 'usize' ignored // :5:30: note: all non-void values must be used -// :5:30: note: this error can be suppressed by assigning the value to '_' +// :5:30: note: to discard the value, assign it to '_' // :9:30: error: value of type 'usize' ignored // :9:30: note: all non-void values must be used -// :9:30: note: this error can be suppressed by assigning the value to '_' +// :9:30: note: to discard the value, assign it to '_' // :13:31: error: value of type 'bool' ignored // :13:31: note: all non-void values must be used -// :13:31: note: this error can be suppressed by assigning the value to '_' +// :13:31: note: to discard the value, assign it to '_' // :16:42: error: value of type 'usize' ignored // :16:42: note: all non-void values must be used -// :16:42: note: this error can be suppressed by assigning the value to '_' +// :16:42: note: to discard the value, assign it to '_' diff --git a/test/cases/compile_errors/generic_instantiation_failure.zig b/test/cases/compile_errors/generic_instantiation_failure.zig index 42e4c4e8c8..d820f8f410 100644 --- a/test/cases/compile_errors/generic_instantiation_failure.zig +++ b/test/cases/compile_errors/generic_instantiation_failure.zig @@ -24,4 +24,4 @@ pub export fn entry() void { // // :18:43: error: value of type 'type' ignored // :18:43: note: all non-void values must be used -// :18:43: note: this error can be suppressed by assigning the value to '_' +// :18:43: note: to discard the value, assign it to '_' diff --git a/test/cases/compile_errors/ignored_assert-err-ok_return_value.zig b/test/cases/compile_errors/ignored_assert-err-ok_return_value.zig index 1257636622..abe2b006b7 100644 --- a/test/cases/compile_errors/ignored_assert-err-ok_return_value.zig +++ b/test/cases/compile_errors/ignored_assert-err-ok_return_value.zig @@ -11,4 +11,4 @@ fn bar() anyerror!i32 { // // :2:11: error: value of type 'i32' ignored // :2:11: note: all non-void values must be used -// :2:11: note: this error can be suppressed by assigning the value to '_' +// :2:11: note: to discard the value, assign it to '_' diff --git a/test/cases/compile_errors/ignored_comptime_statement_value.zig b/test/cases/compile_errors/ignored_comptime_statement_value.zig index 2067f3b716..4dd0e0fc39 100644 --- a/test/cases/compile_errors/ignored_comptime_statement_value.zig +++ b/test/cases/compile_errors/ignored_comptime_statement_value.zig @@ -10,4 +10,4 @@ export fn foo() void { // // :3:9: error: value of type 'comptime_int' ignored // :3:9: note: all non-void values must be used -// :3:9: note: this error can be suppressed by assigning the value to '_' +// :3:9: note: to discard the value, assign it to '_' diff --git a/test/cases/compile_errors/ignored_comptime_value.zig b/test/cases/compile_errors/ignored_comptime_value.zig index 3414d0861f..ed7eb80b50 100644 --- a/test/cases/compile_errors/ignored_comptime_value.zig +++ b/test/cases/compile_errors/ignored_comptime_value.zig @@ -14,7 +14,7 @@ fn bar() u8 { // // :2:5: error: value of type 'comptime_int' ignored // :2:5: note: all non-void values must be used -// :2:5: note: this error can be suppressed by assigning the value to '_' +// :2:5: note: to discard the value, assign it to '_' // :5:5: error: value of type 'u8' ignored // :5:5: note: all non-void values must be used -// :5:5: note: this error can be suppressed by assigning the value to '_' +// :5:5: note: to discard the value, assign it to '_' diff --git a/test/cases/compile_errors/ignored_deferred_statement_value.zig b/test/cases/compile_errors/ignored_deferred_statement_value.zig index 1f42efc3f5..70b88a4deb 100644 --- a/test/cases/compile_errors/ignored_deferred_statement_value.zig +++ b/test/cases/compile_errors/ignored_deferred_statement_value.zig @@ -10,4 +10,4 @@ export fn foo() void { // // :3:9: error: value of type 'comptime_int' ignored // :3:9: note: all non-void values must be used -// :3:9: note: this error can be suppressed by assigning the value to '_' +// :3:9: note: to discard the value, assign it to '_' diff --git a/test/cases/compile_errors/ignored_return_value.zig b/test/cases/compile_errors/ignored_return_value.zig index 08424c4fe9..f721bc8655 100644 --- a/test/cases/compile_errors/ignored_return_value.zig +++ b/test/cases/compile_errors/ignored_return_value.zig @@ -11,4 +11,4 @@ fn bar() i32 { // // :2:8: error: value of type 'i32' ignored // :2:8: note: all non-void values must be used -// :2:8: note: this error can be suppressed by assigning the value to '_' +// :2:8: note: to discard the value, assign it to '_' diff --git a/test/cases/compile_errors/ignored_statement_value.zig b/test/cases/compile_errors/ignored_statement_value.zig index c73da84420..6c4ff442c5 100644 --- a/test/cases/compile_errors/ignored_statement_value.zig +++ b/test/cases/compile_errors/ignored_statement_value.zig @@ -8,4 +8,4 @@ export fn foo() void { // // :2:5: error: value of type 'comptime_int' ignored // :2:5: note: all non-void values must be used -// :2:5: note: this error can be suppressed by assigning the value to '_' +// :2:5: note: to discard the value, assign it to '_' diff --git a/test/cases/compile_errors/while_loop_body_expression_ignored.zig b/test/cases/compile_errors/while_loop_body_expression_ignored.zig index b3e83f202a..dae546df02 100644 --- a/test/cases/compile_errors/while_loop_body_expression_ignored.zig +++ b/test/cases/compile_errors/while_loop_body_expression_ignored.zig @@ -32,16 +32,16 @@ export fn f5() void { // // :5:25: error: value of type 'usize' ignored // :5:25: note: all non-void values must be used -// :5:25: note: this error can be suppressed by assigning the value to '_' +// :5:25: note: to discard the value, assign it to '_' // :10:26: error: value of type 'usize' ignored // :10:26: note: all non-void values must be used -// :10:26: note: this error can be suppressed by assigning the value to '_' +// :10:26: note: to discard the value, assign it to '_' // :15:26: error: value of type 'usize' ignored // :15:26: note: all non-void values must be used -// :15:26: note: this error can be suppressed by assigning the value to '_' +// :15:26: note: to discard the value, assign it to '_' // :20:23: error: value of type 'bool' ignored // :20:23: note: all non-void values must be used -// :20:23: note: this error can be suppressed by assigning the value to '_' +// :20:23: note: to discard the value, assign it to '_' // :25:34: error: value of type 'usize' ignored // :25:34: note: all non-void values must be used -// :25:34: note: this error can be suppressed by assigning the value to '_' +// :25:34: note: to discard the value, assign it to '_' From 31689d0dd92a683e5ed04facea7bc677ef47785b Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 21 Jul 2023 02:30:47 +0200 Subject: [PATCH 2/6] Sema: remove periods from the few error messages that have them For consistency. --- src/Sema.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 3ff817cbd1..123ba93cbe 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9531,7 +9531,7 @@ fn handleExternLibName( return sema.fail( block, src_loc, - "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by '-l{s}' or '-fPIC'.", + "dependency on dynamic library '{s}' requires enabling Position Independent Code; fixed by '-l{s}' or '-fPIC'", .{ lib_name, lib_name }, ); } From 8579904ddd489b73cb61721c31b9e9c14ed9e264 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 21 Jul 2023 02:58:41 +0200 Subject: [PATCH 3/6] Sema: add error note for !?Type types when optional type is expected --- src/Sema.zig | 17 ++++++++++++++--- .../while_expected_optional_got_error_union.zig | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 123ba93cbe..f286b6385f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2228,8 +2228,19 @@ fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: T }); } -fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError { - return sema.fail(block, src, "expected optional type, found '{}'", .{optional_ty.fmt(sema.mod)}); +fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError { + const mod = sema.mod; + const msg = msg: { + const msg = try sema.errMsg(block, src, "expected optional type, found '{}'", .{ + non_optional_ty.fmt(mod), + }); + errdefer msg.destroy(sema.gpa); + if (non_optional_ty.zigTypeTag(mod) == .ErrorUnion) { + try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); + } + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { @@ -9038,7 +9049,7 @@ fn analyzeOptionalPayloadPtr( const opt_type = optional_ptr_ty.childType(zcu); if (opt_type.zigTypeTag(zcu) != .Optional) { - return sema.fail(block, src, "expected optional type, found '{}'", .{opt_type.fmt(zcu)}); + return sema.failWithExpectedOptionalType(block, src, opt_type); } const child_type = opt_type.optionalChild(zcu); diff --git a/test/cases/compile_errors/while_expected_optional_got_error_union.zig b/test/cases/compile_errors/while_expected_optional_got_error_union.zig index 7bde2c866d..1b4a64770b 100644 --- a/test/cases/compile_errors/while_expected_optional_got_error_union.zig +++ b/test/cases/compile_errors/while_expected_optional_got_error_union.zig @@ -12,3 +12,4 @@ fn bar() anyerror!i32 { // target=native // // :2:15: error: expected optional type, found 'anyerror!i32' +// :2:15: note: consider using 'try', 'catch', or 'if' From 9ae43567a3ef14a815482ccaa2a5b412a716743e Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 21 Jul 2023 03:22:59 +0200 Subject: [PATCH 4/6] Sema: improve error set/union discard/ignore errors Previously the error had a note suggesting to use `try`, `catch`, or `if`, even for error sets where none of those work. Instead, in case of an error set the way you can handle the error depends very much on the specific case. For example you might be in a `catch` where you are discarding or ignoring the error set capture value, in which case one way to handle the error might be to `return` the error. So, in that case, we do not attach that error note. Additionally, this makes the error tell you what kind of an error it is: is it an error set or an error union? This distinction is very relevant in how to handle the error. --- src/Sema.zig | 10 ++++++---- .../compile_errors/discarding_error_value.zig | 9 +++++++-- .../ignored_deferred_function_call.zig | 10 +++++++++- .../ignored_expression_in_while_continuation.zig | 14 +++++++++++--- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index f286b6385f..c55531d924 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3568,9 +3568,10 @@ fn ensureResultUsed( const mod = sema.mod; switch (ty.zigTypeTag(mod)) { .Void, .NoReturn => return, - .ErrorSet, .ErrorUnion => { + .ErrorSet => return sema.fail(block, src, "error set is ignored", .{}), + .ErrorUnion => { const msg = msg: { - const msg = try sema.errMsg(block, src, "error is ignored", .{}); + const msg = try sema.errMsg(block, src, "error union is ignored", .{}); errdefer msg.destroy(sema.gpa); try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); break :msg msg; @@ -3600,9 +3601,10 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const src = inst_data.src(); const operand_ty = sema.typeOf(operand); switch (operand_ty.zigTypeTag(mod)) { - .ErrorSet, .ErrorUnion => { + .ErrorSet => return sema.fail(block, src, "error set is discarded", .{}), + .ErrorUnion => { const msg = msg: { - const msg = try sema.errMsg(block, src, "error is discarded", .{}); + const msg = try sema.errMsg(block, src, "error union is discarded", .{}); errdefer msg.destroy(sema.gpa); try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); break :msg msg; diff --git a/test/cases/compile_errors/discarding_error_value.zig b/test/cases/compile_errors/discarding_error_value.zig index c24d517d3e..eab3ecaf98 100644 --- a/test/cases/compile_errors/discarding_error_value.zig +++ b/test/cases/compile_errors/discarding_error_value.zig @@ -1,13 +1,18 @@ -export fn entry() void { +export fn entry1() void { _ = foo(); } fn foo() !void { return error.OutOfMemory; } +export fn entry2() void { + const x: error{a} = undefined; + _ = x; +} // error // backend=stage2 // target=native // -// :2:12: error: error is discarded +// :2:12: error: error union is discarded // :2:12: note: consider using 'try', 'catch', or 'if' +// :9:9: error: error set is discarded diff --git a/test/cases/compile_errors/ignored_deferred_function_call.zig b/test/cases/compile_errors/ignored_deferred_function_call.zig index 9537255d33..90c978b40f 100644 --- a/test/cases/compile_errors/ignored_deferred_function_call.zig +++ b/test/cases/compile_errors/ignored_deferred_function_call.zig @@ -5,9 +5,17 @@ fn bar() anyerror!i32 { return 0; } +export fn foo2() void { + defer bar2(); +} +fn bar2() anyerror { + return error.a; +} + // error // backend=stage2 // target=native // -// :2:14: error: error is ignored +// :2:14: error: error union is ignored // :2:14: note: consider using 'try', 'catch', or 'if' +// :9:15: error: error set is ignored diff --git a/test/cases/compile_errors/ignored_expression_in_while_continuation.zig b/test/cases/compile_errors/ignored_expression_in_while_continuation.zig index 4fa396190a..ca1b60da5e 100644 --- a/test/cases/compile_errors/ignored_expression_in_while_continuation.zig +++ b/test/cases/compile_errors/ignored_expression_in_while_continuation.zig @@ -15,13 +15,21 @@ fn bad() anyerror!void { return error.Bad; } +export fn d() void { + while (true) : (bad2()) {} +} +fn bad2() anyerror { + return error.Bad; +} + // error // backend=stage2 // target=native // -// :2:24: error: error is ignored +// :2:24: error: error union is ignored // :2:24: note: consider using 'try', 'catch', or 'if' -// :7:25: error: error is ignored +// :7:25: error: error union is ignored // :7:25: note: consider using 'try', 'catch', or 'if' -// :12:25: error: error is ignored +// :12:25: error: error union is ignored // :12:25: note: consider using 'try', 'catch', or 'if' +// :19:25: error: error set is ignored From f14cf13ff8553030c47748a0cbd455514cd1f4a3 Mon Sep 17 00:00:00 2001 From: Wooster Date: Sun, 30 Jul 2023 11:10:41 +0200 Subject: [PATCH 5/6] Sema: suggest using try/catch/if on method call on error union --- src/Sema.zig | 3 +++ .../method_call_on_error_union.zig | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 test/cases/compile_errors/method_call_on_error_union.zig diff --git a/src/Sema.zig b/src/Sema.zig index c55531d924..a5c5f37e4c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -27888,6 +27888,9 @@ fn fieldCallBind( const decl = mod.declPtr(decl_idx); try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "'{}' is not a member function", .{field_name.fmt(ip)}); } + if (concrete_ty.zigTypeTag(mod) == .ErrorUnion) { + try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); + } break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); diff --git a/test/cases/compile_errors/method_call_on_error_union.zig b/test/cases/compile_errors/method_call_on_error_union.zig new file mode 100644 index 0000000000..4ce6b3baa8 --- /dev/null +++ b/test/cases/compile_errors/method_call_on_error_union.zig @@ -0,0 +1,21 @@ +const X = struct { + fn init() !X { + return error.a; + } + + fn a(x: X) void { + _ = x; + } +}; + +export fn entry() void { + const x = X.init(); + x.a(); +} + +// error +// backend=stage2 +// target=native +// +// :13:6: error: no field or member function named 'a' in '@typeInfo(@typeInfo(@TypeOf(tmp.X.init)).Fn.return_type.?).ErrorUnion.error_set!tmp.X' +// :13:6: note: consider using 'try', 'catch', or 'if' From ac55685a94b3db97e9d2eadef0432948b3c16a03 Mon Sep 17 00:00:00 2001 From: wooster0 Date: Wed, 22 May 2024 02:16:56 +0900 Subject: [PATCH 6/6] Sema: add missing declared here note --- src/Sema.zig | 1 + .../expected_optional_type_got_container.zig | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 test/cases/compile_errors/expected_optional_type_got_container.zig diff --git a/src/Sema.zig b/src/Sema.zig index a5c5f37e4c..7a1885d652 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2238,6 +2238,7 @@ fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non if (non_optional_ty.zigTypeTag(mod) == .ErrorUnion) { try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); } + try addDeclaredHereNote(sema, msg, non_optional_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); diff --git a/test/cases/compile_errors/expected_optional_type_got_container.zig b/test/cases/compile_errors/expected_optional_type_got_container.zig new file mode 100644 index 0000000000..12bb629629 --- /dev/null +++ b/test/cases/compile_errors/expected_optional_type_got_container.zig @@ -0,0 +1,16 @@ +export fn foo() void { + while (bar()) |x| { + _ = x; + } +} +const X = enum { a }; +fn bar() X { + return .a; +} + +// error +// backend=stage2 +// target=native +// +// :2:15: error: expected optional type, found 'tmp.X' +// :6:11: note: enum declared here