From 29ae6515f3adc23df3d889acaf7ec62a2d01b707 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 6 Oct 2022 17:05:34 +0300 Subject: [PATCH 1/3] AstGen: use 'shadows' instead of 'redeclaration' when names are in different scopes Closes #8532 --- doc/langref.html.in | 2 +- src/AstGen.zig | 77 ++++++++++++++----- .../compile_errors/decl_shadows_local.zig | 4 +- ...local_shadows_global_that_occurs_later.zig | 2 +- .../local_variable_redeclares_parameter.zig | 2 +- .../local_variable_shadowing_global.zig | 2 +- .../parameter_shadowing_global.zig | 2 +- test/cases/function_redeclaration.zig | 2 +- test/cases/variable_shadowing.1.zig | 2 +- test/cases/variable_shadowing.3.zig | 2 +- test/cases/variable_shadowing.4.zig | 2 +- test/cases/variable_shadowing.5.zig | 2 +- test/cases/variable_shadowing.6.zig | 2 +- test/cases/variable_shadowing.7.zig | 2 +- test/cases/variable_shadowing.8.zig | 2 +- test/cases/variable_shadowing.9.zig | 2 +- 16 files changed, 72 insertions(+), 37 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 7cc26d662ab77e10e2634956d76deed2291eea65..0315dc10e105b7e659c9b12ff9df6a28fdef0318 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4064,7 +4064,7 @@ test "labeled break from labeled block expression" { {#header_open|Shadowing#}

{#link|Identifiers#} are never allowed to "hide" other identifiers by using the same name:

- {#code_begin|test_err|local shadows declaration#} + {#code_begin|test_err|local variable shadows declaration#} const pi = 3.14; test "inside test block" { diff --git a/src/AstGen.zig b/src/AstGen.zig index c12778929c19ec272c55af73328b93f947b54826..1f4bd7dd6633ccc963785f86a429e64544bd2402 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2004,7 +2004,8 @@ fn blockExpr( return labeledBlockExpr(gz, scope, rl, block_node, statements); } - try blockExprStmts(gz, scope, statements); + var sub_gz = gz.makeSubBlock(scope); + try blockExprStmts(&sub_gz, &sub_gz.base, statements); return rvalue(gz, rl, .void_value, block_node); } @@ -2772,7 +2773,13 @@ fn varDecl( } const ident_name = try astgen.identAsString(name_token); - try astgen.detectLocalShadowing(scope, ident_name, name_token, ident_name_raw); + try astgen.detectLocalShadowing( + scope, + ident_name, + name_token, + ident_name_raw, + if (token_tags[var_decl.ast.mut_token] == .keyword_const) .@"local constant" else .@"local variable", + ); if (var_decl.ast.init_node == 0) { return astgen.failNode(node, "variables must be initialized", .{}); @@ -3502,7 +3509,7 @@ fn fnDecl( const param_name = try astgen.identAsString(name_token); if (!is_extern) { - try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes); + try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter"); } break :blk param_name; } else if (!is_extern) { @@ -5181,7 +5188,7 @@ fn orelseCatchExpr( } const err_name = try astgen.identAsString(payload); - try astgen.detectLocalShadowing(scope, err_name, payload, err_str); + try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .@"capture"); err_val_scope = .{ .parent = &else_scope.base, @@ -5480,7 +5487,7 @@ fn ifExpr( const token_name_str = tree.tokenSlice(token_name_index); if (mem.eql(u8, "_", token_name_str)) break :s &then_scope.base; - try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str); + try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .@"capture"); payload_val_scope = .{ .parent = &then_scope.base, .gen_zir = &then_scope, @@ -5505,7 +5512,7 @@ fn ifExpr( break :s &then_scope.base; const payload_inst = try then_scope.addUnNode(tag, cond.inst, if_full.ast.then_expr); const ident_name = try astgen.identAsString(ident_token); - try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes); + try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .@"capture"); payload_val_scope = .{ .parent = &then_scope.base, .gen_zir = &then_scope, @@ -5551,7 +5558,7 @@ fn ifExpr( const error_token_str = tree.tokenSlice(error_token); if (mem.eql(u8, "_", error_token_str)) break :s &else_scope.base; - try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str); + try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .@"capture"); payload_val_scope = .{ .parent = &else_scope.base, .gen_zir = &else_scope, @@ -5816,7 +5823,7 @@ fn whileExpr( break :s &then_scope.base; const payload_name_loc = payload_token + @boolToInt(payload_is_ref); const ident_name = try astgen.identAsString(payload_name_loc); - try astgen.detectLocalShadowing(&then_scope.base, ident_name, payload_name_loc, ident_bytes); + try astgen.detectLocalShadowing(&then_scope.base, ident_name, payload_name_loc, ident_bytes, .@"capture"); payload_val_scope = .{ .parent = &then_scope.base, .gen_zir = &then_scope, @@ -5843,7 +5850,7 @@ fn whileExpr( const ident_bytes = tree.tokenSlice(ident_token); if (mem.eql(u8, "_", ident_bytes)) break :s &then_scope.base; - try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes); + try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .@"capture"); payload_val_scope = .{ .parent = &then_scope.base, .gen_zir = &then_scope, @@ -5919,7 +5926,7 @@ fn whileExpr( const ident_bytes = tree.tokenSlice(error_token); if (mem.eql(u8, ident_bytes, "_")) break :s &else_scope.base; - try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes); + try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes, .@"capture"); payload_val_scope = .{ .parent = &else_scope.base, .gen_zir = &else_scope, @@ -6092,7 +6099,7 @@ fn forExpr( .lhs = array_ptr, .rhs = index, }); - try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident, value_name); + try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident, value_name, .@"capture"); payload_val_scope = .{ .parent = &then_scope.base, .gen_zir = &then_scope, @@ -6118,7 +6125,7 @@ fn forExpr( return astgen.failTok(index_token, "discard of index capture; omit it instead", .{}); } const index_name = try astgen.identAsString(index_token); - try astgen.detectLocalShadowing(payload_sub_scope, index_name, index_token, token_bytes); + try astgen.detectLocalShadowing(payload_sub_scope, index_name, index_token, token_bytes, .@"loop index capture"); index_scope = .{ .parent = payload_sub_scope, .gen_zir = &then_scope, @@ -6433,7 +6440,7 @@ fn switchExpr( }); } const capture_name = try astgen.identAsString(ident); - try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice); + try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .@"capture"); capture_val_scope = .{ .parent = &case_scope.base, .gen_zir = &case_scope, @@ -6458,7 +6465,7 @@ fn switchExpr( return astgen.failTok(tag_token, "tag capture on non-inline prong", .{}); } const tag_name = try astgen.identAsString(tag_token); - try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice); + try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture"); tag_inst = @intCast(Zir.Inst.Index, astgen.instructions.len); try astgen.instructions.append(gpa, .{ .tag = .switch_capture_tag, @@ -11669,6 +11676,7 @@ fn detectLocalShadowing( ident_name: u32, name_token: Ast.TokenIndex, token_bytes: []const u8, + id_cat: Scope.IdCat, ) !void { const gpa = astgen.gpa; if (token_bytes[0] != '@' and isPrimitive(token_bytes)) { @@ -11682,6 +11690,7 @@ fn detectLocalShadowing( } var s = scope; + var outer_scope = false; while (true) switch (s.tag) { .local_val => { const local_val = s.cast(Scope.LocalVal).?; @@ -11689,6 +11698,17 @@ fn detectLocalShadowing( const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); const name = try gpa.dupe(u8, name_slice); defer gpa.free(name); + if (outer_scope) { + return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{ + @tagName(id_cat), name, @tagName(local_val.id_cat), + }, &[_]u32{ + try astgen.errNoteTok( + local_val.token_src, + "previous declaration here", + .{}, + ), + }); + } return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ @tagName(local_val.id_cat), name, }, &[_]u32{ @@ -11707,6 +11727,17 @@ fn detectLocalShadowing( const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); const name = try gpa.dupe(u8, name_slice); defer gpa.free(name); + if (outer_scope) { + return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{ + @tagName(id_cat), name, @tagName(local_ptr.id_cat), + }, &[_]u32{ + try astgen.errNoteTok( + local_ptr.token_src, + "previous declaration here", + .{}, + ), + }); + } return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ @tagName(local_ptr.id_cat), name, }, &[_]u32{ @@ -11720,6 +11751,7 @@ fn detectLocalShadowing( s = local_ptr.parent; }, .namespace => { + outer_scope = true; const ns = s.cast(Scope.Namespace).?; const decl_node = ns.decls.get(ident_name) orelse { s = ns.parent; @@ -11728,13 +11760,16 @@ fn detectLocalShadowing( const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); const name = try gpa.dupe(u8, name_slice); defer gpa.free(name); - return astgen.failTokNotes(name_token, "local shadows declaration of '{s}'", .{ - name, + return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{ + @tagName(id_cat), name, }, &[_]u32{ try astgen.errNoteNode(decl_node, "declared here", .{}), }); }, - .gen_zir => s = s.cast(GenZir).?.parent, + .gen_zir => { + s = s.cast(GenZir).?.parent; + outer_scope = true; + }, .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, .top => break, }; @@ -11844,8 +11879,8 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast. .local_val => { const local_val = s.cast(Scope.LocalVal).?; if (local_val.name == name_str_index) { - return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ - @tagName(local_val.id_cat), token_bytes, + return astgen.failTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{ + token_bytes, @tagName(local_val.id_cat), }, &[_]u32{ try astgen.errNoteTok( local_val.token_src, @@ -11859,8 +11894,8 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast. .local_ptr => { const local_ptr = s.cast(Scope.LocalPtr).?; if (local_ptr.name == name_str_index) { - return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ - @tagName(local_ptr.id_cat), token_bytes, + return astgen.failTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{ + token_bytes, @tagName(local_ptr.id_cat), }, &[_]u32{ try astgen.errNoteTok( local_ptr.token_src, diff --git a/test/cases/compile_errors/decl_shadows_local.zig b/test/cases/compile_errors/decl_shadows_local.zig index cb48cafa4574e28f0c6f84ff4c3fa44cc49ec409..44066ab659be83f1d34e78f3976609c39b6ae345 100644 --- a/test/cases/compile_errors/decl_shadows_local.zig +++ b/test/cases/compile_errors/decl_shadows_local.zig @@ -16,7 +16,7 @@ fn bar(a: usize) void { // backend=stage2 // target=native // -// :3:15: error: redeclaration of function parameter 'a' +// :3:15: error: declaration 'a' shadows function parameter from outer scope // :1:8: note: previous declaration here -// :9:19: error: redeclaration of function parameter 'a' +// :9:19: error: declaration 'a' shadows function parameter from outer scope // :6:8: note: previous declaration here diff --git a/test/cases/compile_errors/local_shadows_global_that_occurs_later.zig b/test/cases/compile_errors/local_shadows_global_that_occurs_later.zig index 1d88ac2c88a96f8d22529e44b022c5c9f446c8f7..4c55060c5296864021e5c27ff1dd12023473b142 100644 --- a/test/cases/compile_errors/local_shadows_global_that_occurs_later.zig +++ b/test/cases/compile_errors/local_shadows_global_that_occurs_later.zig @@ -8,5 +8,5 @@ fn foo() void {} // backend=stage2 // target=native // -// :2:9: error: local shadows declaration of 'foo' +// :2:9: error: local variable shadows declaration of 'foo' // :5:1: note: declared here diff --git a/test/cases/compile_errors/local_variable_redeclares_parameter.zig b/test/cases/compile_errors/local_variable_redeclares_parameter.zig index f5693ca9b6306545c8cddfde5250187bed267a2e..6e523c2c8f6d6faa433041b562c435646d04ab4d 100644 --- a/test/cases/compile_errors/local_variable_redeclares_parameter.zig +++ b/test/cases/compile_errors/local_variable_redeclares_parameter.zig @@ -7,5 +7,5 @@ export fn entry() void { f(1); } // backend=stage2 // target=native // -// :2:11: error: redeclaration of function parameter 'a' +// :2:11: error: local constant 'a' shadows function parameter from outer scope // :1:6: note: previous declaration here diff --git a/test/cases/compile_errors/local_variable_shadowing_global.zig b/test/cases/compile_errors/local_variable_shadowing_global.zig index 1320bfcb41fd56c1d0cf895b339a6f58ad9cedcd..91df6a7c3d81efffcc352c7d6819d2774f8c86b0 100644 --- a/test/cases/compile_errors/local_variable_shadowing_global.zig +++ b/test/cases/compile_errors/local_variable_shadowing_global.zig @@ -10,5 +10,5 @@ export fn entry() void { // backend=stage2 // target=native // -// :5:9: error: local shadows declaration of 'Bar' +// :5:9: error: local variable shadows declaration of 'Bar' // :2:1: note: declared here diff --git a/test/cases/compile_errors/parameter_shadowing_global.zig b/test/cases/compile_errors/parameter_shadowing_global.zig index 47d112807bbff9bf3049185f3dd0df0b6c1d24c9..5cc837f05e7f0faaa52359db1c1db1b605712991 100644 --- a/test/cases/compile_errors/parameter_shadowing_global.zig +++ b/test/cases/compile_errors/parameter_shadowing_global.zig @@ -8,5 +8,5 @@ export fn entry() void { // backend=stage2 // target=native // -// :2:6: error: local shadows declaration of 'Foo' +// :2:6: error: function parameter shadows declaration of 'Foo' // :1:1: note: declared here diff --git a/test/cases/function_redeclaration.zig b/test/cases/function_redeclaration.zig index a9664646c98a4dcb5b889de88ddfc60ce8d5d8d5..b6cbf664a9fdc37a1a86caf6e1b3e4f5e10e33cf 100644 --- a/test/cases/function_redeclaration.zig +++ b/test/cases/function_redeclaration.zig @@ -10,5 +10,5 @@ fn foo() void { // // :3:1: error: redeclaration of 'entry' // :2:1: note: other declaration here -// :6:9: error: local shadows declaration of 'foo' +// :6:9: error: local variable shadows declaration of 'foo' // :5:1: note: declared here diff --git a/test/cases/variable_shadowing.1.zig b/test/cases/variable_shadowing.1.zig index af24af038dfc0da511879b35a0edd97415320621..2a2945a65f7e874755d799e69ed8f605b30cc8b9 100644 --- a/test/cases/variable_shadowing.1.zig +++ b/test/cases/variable_shadowing.1.zig @@ -5,5 +5,5 @@ pub fn main() void { // error // -// :3:9: error: local shadows declaration of 'testing' +// :3:9: error: local variable shadows declaration of 'testing' // :1:1: note: declared here diff --git a/test/cases/variable_shadowing.3.zig b/test/cases/variable_shadowing.3.zig index 89288705e1b1d70326edefcc81da2ae02cca7ecf..1e22ccf123dae65c0eec4e1c0b36a3199a35e43b 100644 --- a/test/cases/variable_shadowing.3.zig +++ b/test/cases/variable_shadowing.3.zig @@ -6,5 +6,5 @@ pub fn main() void { // error // -// :3:19: error: redeclaration of local variable 'i' +// :3:19: error: loop index capture 'i' shadows local variable from outer scope // :2:9: note: previous declaration here diff --git a/test/cases/variable_shadowing.4.zig b/test/cases/variable_shadowing.4.zig index b798cc13c1dc018a136fcca8ef4548839b8130fd..ab9f93d17d4a6417c74f87a38f850a5bd84bd304 100644 --- a/test/cases/variable_shadowing.4.zig +++ b/test/cases/variable_shadowing.4.zig @@ -6,5 +6,5 @@ pub fn main() void { // error // -// :3:16: error: redeclaration of local variable 'i' +// :3:16: error: capture 'i' shadows local variable from outer scope // :2:9: note: previous declaration here diff --git a/test/cases/variable_shadowing.5.zig b/test/cases/variable_shadowing.5.zig index 484d218ea20e1e46451a1947d8bbedd5aef61d95..1a5676190ada5ff1982a036b309d7fcd939c25de 100644 --- a/test/cases/variable_shadowing.5.zig +++ b/test/cases/variable_shadowing.5.zig @@ -6,5 +6,5 @@ pub fn main() void { // error // -// :3:18: error: redeclaration of local variable 'i' +// :3:18: error: capture 'i' shadows local variable from outer scope // :2:9: note: previous declaration here diff --git a/test/cases/variable_shadowing.6.zig b/test/cases/variable_shadowing.6.zig index 3e53c4da2fcbb95783caf037ff8849ddd8d07999..894a398b61de8c79fc10b1f487850d6dadc9875b 100644 --- a/test/cases/variable_shadowing.6.zig +++ b/test/cases/variable_shadowing.6.zig @@ -9,5 +9,5 @@ pub fn main() void { // error // -// :5:13: error: redeclaration of local variable 'i' +// :5:13: error: capture 'i' shadows local variable from outer scope // :2:9: note: previous declaration here diff --git a/test/cases/variable_shadowing.7.zig b/test/cases/variable_shadowing.7.zig index 95888839a7107a446c9e850946b4f4484d599f00..e8368699b4dd4018a38e54a318bada43277ee88c 100644 --- a/test/cases/variable_shadowing.7.zig +++ b/test/cases/variable_shadowing.7.zig @@ -5,5 +5,5 @@ pub fn main() void { // error // -// :3:16: error: redeclaration of local variable 'i' +// :3:16: error: capture 'i' shadows local variable from outer scope // :2:9: note: previous declaration here diff --git a/test/cases/variable_shadowing.8.zig b/test/cases/variable_shadowing.8.zig index 57e616e1d699d2123eb62fc92acda0a0ec02e4fc..1a3f560589ae716294fed6ddc3f111437975a101 100644 --- a/test/cases/variable_shadowing.8.zig +++ b/test/cases/variable_shadowing.8.zig @@ -5,5 +5,5 @@ pub fn main() void { // error // -// :3:16: error: redeclaration of local variable 'i' +// :3:16: error: capture 'i' shadows local variable from outer scope // :2:9: note: previous declaration here diff --git a/test/cases/variable_shadowing.9.zig b/test/cases/variable_shadowing.9.zig index b8a1198aed4608b4c8fde8f40e3f2f04fdcdef5a..28e2b16834bf43a708c64616e6919b0c602b6a60 100644 --- a/test/cases/variable_shadowing.9.zig +++ b/test/cases/variable_shadowing.9.zig @@ -5,5 +5,5 @@ pub fn main() void { // error // -// :3:28: error: redeclaration of local variable 'i' +// :3:28: error: capture 'i' shadows local variable from outer scope // :2:9: note: previous declaration here -- 2.54.0 From 2315e1b41073a8d6a7f7fe3deb98ec98e45d72f6 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 6 Oct 2022 17:25:00 +0300 Subject: [PATCH 2/3] safety: add safety check for hitting else branch on a corrupt enum value Closes #7053 --- src/Sema.zig | 15 ++++++++++ ...else on corrupt enum value - one prong.zig | 24 +++++++++++++++ ...tch else on corrupt enum value - union.zig | 29 +++++++++++++++++++ .../switch else on corrupt enum value.zig | 23 +++++++++++++++ 4 files changed, 91 insertions(+) create mode 100644 test/cases/safety/switch else on corrupt enum value - one prong.zig create mode 100644 test/cases/safety/switch else on corrupt enum value - union.zig create mode 100644 test/cases/safety/switch else on corrupt enum value.zig diff --git a/src/Sema.zig b/src/Sema.zig index ef45bf7174ff677f052ab902ff8278ec773156ff..ca8e91dd68d7484315efef6751472a25dcf2b17f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9998,6 +9998,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges); } + const backend_supports_is_named_enum = sema.mod.comp.bin_file.options.use_llvm; + if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) { if (empty_enum) { return Air.Inst.Ref.void_value; @@ -10008,6 +10010,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) { return Air.Inst.Ref.unreachable_value; } + if (backend_supports_is_named_enum and block.wantSafety() and operand_ty.zigTypeTag() == .Enum and + (!operand_ty.isNonexhaustiveEnum() or union_originally)) + { + const ok = try block.addUnOp(.is_named_enum_value, operand); + try sema.addSafetyCheck(block, ok, .corrupt_switch); + } return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges); } @@ -10465,6 +10473,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError case_block.wip_capture_scope = wip_captures.scope; case_block.inline_case_capture = .none; + if (backend_supports_is_named_enum and special.body.len != 0 and block.wantSafety() and + operand_ty.zigTypeTag() == .Enum and (!operand_ty.isNonexhaustiveEnum() or union_originally)) + { + const ok = try case_block.addUnOp(.is_named_enum_value, operand); + try sema.addSafetyCheck(&case_block, ok, .corrupt_switch); + } + const analyze_body = if (union_originally and !special.is_inline) for (seen_enum_fields) |seen_field, index| { if (seen_field != null) continue; diff --git a/test/cases/safety/switch else on corrupt enum value - one prong.zig b/test/cases/safety/switch else on corrupt enum value - one prong.zig new file mode 100644 index 0000000000000000000000000000000000000000..2c0b58fcd4eeaa530826634b460e7d9ade02da84 --- /dev/null +++ b/test/cases/safety/switch else on corrupt enum value - one prong.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "switch on corrupt value")) { + std.process.exit(0); + } + std.process.exit(1); +} +const E = enum(u32) { + one = 1, + two = 2, +}; +pub fn main() !void { + var a: E = undefined; + @ptrCast(*u32, &a).* = 255; + switch (a) { + .one => @panic("one"), + else => @panic("else"), + } +} +// run +// backend=llvm +// target=native diff --git a/test/cases/safety/switch else on corrupt enum value - union.zig b/test/cases/safety/switch else on corrupt enum value - union.zig new file mode 100644 index 0000000000000000000000000000000000000000..358ecc89aca55dd36f2464a0d23605fab44ef202 --- /dev/null +++ b/test/cases/safety/switch else on corrupt enum value - union.zig @@ -0,0 +1,29 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "switch on corrupt value")) { + std.process.exit(0); + } + std.process.exit(1); +} +const E = enum(u16) { + one = 1, + two = 2, + _, +}; +const U = union(E) { + one: u16, + two: u16, +}; +pub fn main() !void { + var a: U = undefined; + @ptrCast(*align(@alignOf(U)) u32, &a).* = 0xFFFF_FFFF; + switch (a) { + .one => @panic("one"), + else => @panic("else"), + } +} +// run +// backend=llvm +// target=native diff --git a/test/cases/safety/switch else on corrupt enum value.zig b/test/cases/safety/switch else on corrupt enum value.zig new file mode 100644 index 0000000000000000000000000000000000000000..af04b7f4c3b1eeab8eeda30693eeb782c3434889 --- /dev/null +++ b/test/cases/safety/switch else on corrupt enum value.zig @@ -0,0 +1,23 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "switch on corrupt value")) { + std.process.exit(0); + } + std.process.exit(1); +} +const E = enum(u32) { + one = 1, + two = 2, +}; +pub fn main() !void { + var a: E = undefined; + @ptrCast(*u32, &a).* = 255; + switch (a) { + else => @panic("else"), + } +} +// run +// backend=llvm +// target=native -- 2.54.0 From 4a6cc1c602a08ddff6c498d8c37919c5c0c842f0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 6 Oct 2022 17:46:02 +0300 Subject: [PATCH 3/3] Sema: allow equality comparisons between error unions and error sets Closes #1302 --- src/Sema.zig | 12 +++++++++++ test/behavior/error.zig | 21 +++++++++++++++++++ ...rison_with_error_union_and_error_value.zig | 10 --------- 3 files changed, 33 insertions(+), 10 deletions(-) delete mode 100644 test/cases/compile_errors/comparison_with_error_union_and_error_value.zig diff --git a/src/Sema.zig b/src/Sema.zig index ca8e91dd68d7484315efef6751472a25dcf2b17f..02f6712d6c23c1c33d5b25d88dc2f47464126180 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7622,6 +7622,10 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); + return sema.analyzeErrUnionCode(block, src, operand); +} + +fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { const operand_ty = sema.typeOf(operand); if (operand_ty.zigTypeTag() != .ErrorUnion) { return sema.fail(block, src, "expected error union type, found '{}'", .{ @@ -14129,6 +14133,14 @@ fn analyzeCmp( // numeric types. return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); } + if (is_equality_cmp and lhs_ty.zigTypeTag() == .ErrorUnion and rhs_ty.zigTypeTag() == .ErrorSet) { + const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs); + return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src); + } + if (is_equality_cmp and lhs_ty.zigTypeTag() == .ErrorSet and rhs_ty.zigTypeTag() == .ErrorUnion) { + const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs); + return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src); + } const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); if (!resolved_type.isSelfComparable(is_equality_cmp)) { diff --git a/test/behavior/error.zig b/test/behavior/error.zig index dc29c9bc5bee549b78a14ff7f002c698557d56ff..ba5bdfdc2007c2cbd9d03f3c2aa2d22da6e22065 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -809,3 +809,24 @@ test "alignment of wrapping an error union payload" { }; try expect((S.foo() catch unreachable).x == 1234); } + +test "compare error union and error set" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var a: anyerror = error.Foo; + var b: anyerror!u32 = error.Bar; + + try expect(a != b); + try expect(b != a); + + b = error.Foo; + + try expect(a == b); + try expect(b == a); + + b = 2; + + try expect(a != b); + try expect(b != a); +} diff --git a/test/cases/compile_errors/comparison_with_error_union_and_error_value.zig b/test/cases/compile_errors/comparison_with_error_union_and_error_value.zig deleted file mode 100644 index 3a9eabcb95153a9fca5112563da5090581bd81bd..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/comparison_with_error_union_and_error_value.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry() void { - var number_or_error: anyerror!i32 = error.SomethingAwful; - _ = number_or_error == error.SomethingAwful; -} - -// error -// backend=stage2 -// target=native -// -// :3:25: error: operator == not allowed for type 'anyerror!i32' -- 2.54.0