diff --git a/src/AstGen.zig b/src/AstGen.zig index b32ee67b0a..6ef69e2c6b 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -841,13 +841,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .@"if", => { const if_full = tree.fullIf(node).?; - if (if_full.error_token) |error_token| { - const tag = node_tags[if_full.ast.else_expr]; - if ((tag == .@"switch" or tag == .switch_comma) and - std.mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(error_token + 4))) - { - return switchExprErrUnion(gz, scope, ri.br(), node, .@"if"); + no_switch_on_err: { + const error_token = if_full.error_token orelse break :no_switch_on_err; + switch (node_tags[if_full.ast.else_expr]) { + .@"switch", .switch_comma => {}, + else => break :no_switch_on_err, } + const switch_operand = node_datas[if_full.ast.else_expr].lhs; + if (node_tags[switch_operand] != .identifier) break :no_switch_on_err; + if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(main_tokens[switch_operand]))) break :no_switch_on_err; + return switchExprErrUnion(gz, scope, ri.br(), node, .@"if"); } return ifExpr(gz, scope, ri.br(), node, if_full); }, @@ -1026,16 +1029,21 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE }, .@"catch" => { const catch_token = main_tokens[node]; - const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) blk: { - if (token_tags.len > catch_token + 6 and - token_tags[catch_token + 4] == .keyword_switch) - { - if (std.mem.eql(u8, tree.tokenSlice(catch_token + 2), tree.tokenSlice(catch_token + 6))) { - return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch"); - } + const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) + catch_token + 2 + else + null; + no_switch_on_err: { + const capture_token = payload_token orelse break :no_switch_on_err; + switch (node_tags[node_datas[node].rhs]) { + .@"switch", .switch_comma => {}, + else => break :no_switch_on_err, } - break :blk catch_token + 2; - } else null; + const switch_operand = node_datas[node_datas[node].rhs].lhs; + if (node_tags[switch_operand] != .identifier) break :no_switch_on_err; + if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(main_tokens[switch_operand]))) break :no_switch_on_err; + return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch"); + } switch (ri.rl) { .ref, .ref_coerced_ty => return orelseCatchExpr( gz, @@ -7219,7 +7227,9 @@ fn switchExprErrUnion( }; const capture_token = case.payload_token orelse break :blk &err_scope.base; - assert(token_tags[capture_token] == .identifier); + if (token_tags[capture_token] != .identifier) { + return astgen.failTok(capture_token + 1, "error set cannot be captured by reference", .{}); + } const capture_slice = tree.tokenSlice(capture_token); if (mem.eql(u8, capture_slice, "_")) { diff --git a/test/cases/compile_errors/error_set_cannot_capture_by_reference.zig b/test/cases/compile_errors/error_set_cannot_capture_by_reference.zig new file mode 100644 index 0000000000..c7b9da5c74 --- /dev/null +++ b/test/cases/compile_errors/error_set_cannot_capture_by_reference.zig @@ -0,0 +1,15 @@ +export fn entry() void { + const err: error{Foo} = error.Foo; + + switch (err) { + error.Foo => |*foo| { + foo catch {}; + }, + } +} + +// error +// backend=stage2 +// target=native +// +// :5:23: error: error set cannot be captured by reference diff --git a/test/cases/compile_errors/switch_on_error_union_discard.zig b/test/cases/compile_errors/switch_on_error_union_discard.zig index 34a607cfef..c8607f8518 100644 --- a/test/cases/compile_errors/switch_on_error_union_discard.zig +++ b/test/cases/compile_errors/switch_on_error_union_discard.zig @@ -1,10 +1,8 @@ export fn entry() void { const x: error{}!u32 = 0; - if (x) |v| v else |_| switch (_) { - } + if (x) |v| v else |_| switch (_) {} } - // error // backend=stage2 // target=native diff --git a/test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig b/test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig new file mode 100644 index 0000000000..5621410ea8 --- /dev/null +++ b/test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig @@ -0,0 +1,24 @@ +comptime { + const e: error{Foo}!u32 = error.Foo; + e catch |err| switch (err) { + error.Foo => |*foo| { + foo catch {}; + }, + }; +} + +comptime { + const e: error{Foo}!u32 = error.Foo; + if (e) {} else |err| switch (err) { + error.Foo => |*foo| { + foo catch {}; + }, + } +} + +// error +// backend=stage2 +// target=native +// +// :4:24: error: error set cannot be captured by reference +// :13:24: error: error set cannot be captured by reference diff --git a/test/cases/compile_errors/switch_on_error_with_non_trivial_switch_operand.zig b/test/cases/compile_errors/switch_on_error_with_non_trivial_switch_operand.zig new file mode 100644 index 0000000000..8ca35b5aaf --- /dev/null +++ b/test/cases/compile_errors/switch_on_error_with_non_trivial_switch_operand.zig @@ -0,0 +1,22 @@ +export fn entry1() void { + var x: error{Foo}!u32 = 0; + _ = &x; + if (x) |_| {} else |err| switch (err + 1) { + else => {}, + } +} + +export fn entry2() void { + var x: error{Foo}!u32 = 0; + _ = &x; + _ = x catch |err| switch (err + 1) { + else => {}, + }; +} + +// error +// backend=stage2 +// target=native +// +// :4:42: error: invalid operands to binary expression: 'ErrorSet' and 'ComptimeInt' +// :12:35: error: invalid operands to binary expression: 'ErrorSet' and 'ComptimeInt'