authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-07 22:02:45-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-07 22:02:45-07:00
loge730172e47749db8a9d3be5949231bde95343b39
treed77ee85e8f3c99a6a13979a22e8650063677b75a
parentb67378fb08fb3129b66385479a278a93940f09f5

stage2: fix switch validation of handling all enum values

There were several problems, all fixed: * AstGen was storing field names as references to the original source code bytes. However, that data would be destroyed when the source file is updated. Now, it correctly stores the field names in the Decl arena for the enum. The same fix applies to error set field names. * Sema was missing a memset inside `analyzeSwitch`, leaving the "seen enum fields" array with undefined memory. Now that they are all properly set to null, the validation works. * Moved the "enum declared here" note to the end. It looked weird interrupting the notes for which enum values were missing.

3 files changed, 40 insertions(+), 12 deletions(-)

src/Module.zig+16-5
...@@ -4582,28 +4582,39 @@ pub fn optimizeMode(mod: Module) std.builtin.Mode {...@@ -4582,28 +4582,39 @@ pub fn optimizeMode(mod: Module) std.builtin.Mode {
4582/// Otherwise, returns a reference to the source code bytes directly.4582/// Otherwise, returns a reference to the source code bytes directly.
4583/// See also `appendIdentStr` and `parseStrLit`.4583/// See also `appendIdentStr` and `parseStrLit`.
4584pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {4584pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
4585 return mod.identifierTokenStringTreeArena(scope, token, scope.tree(), scope.arena());4585 const tree = scope.tree();
4586 const token_tags = tree.tokens.items(.tag);
4587 assert(token_tags[token] == .identifier);
4588 const ident_name = tree.tokenSlice(token);
4589 if (!mem.startsWith(u8, ident_name, "@")) {
4590 return ident_name;
4591 }
4592 var buf: ArrayListUnmanaged(u8) = .{};
4593 defer buf.deinit(mod.gpa);
4594 try parseStrLit(mod, scope, token, &buf, ident_name, 1);
4595 const duped = try scope.arena().dupe(u8, buf.items);
4596 return duped;
4586}4597}
45874598
4588/// `scope` is only used for error reporting.4599/// `scope` is only used for error reporting.
4600/// The string is stored in `arena` regardless of whether it uses @"" syntax.
4589pub fn identifierTokenStringTreeArena(4601pub fn identifierTokenStringTreeArena(
4590 mod: *Module,4602 mod: *Module,
4591 scope: *Scope,4603 scope: *Scope,
4592 token: ast.TokenIndex,4604 token: ast.TokenIndex,
4593 tree: *const ast.Tree,4605 tree: *const ast.Tree,
4594 arena: *Allocator,4606 arena: *Allocator,
4595) InnerError![]const u8 {4607) InnerError![]u8 {
4596 const token_tags = tree.tokens.items(.tag);4608 const token_tags = tree.tokens.items(.tag);
4597 assert(token_tags[token] == .identifier);4609 assert(token_tags[token] == .identifier);
4598 const ident_name = tree.tokenSlice(token);4610 const ident_name = tree.tokenSlice(token);
4599 if (!mem.startsWith(u8, ident_name, "@")) {4611 if (!mem.startsWith(u8, ident_name, "@")) {
4600 return ident_name;4612 return arena.dupe(u8, ident_name);
4601 }4613 }
4602 var buf: ArrayListUnmanaged(u8) = .{};4614 var buf: ArrayListUnmanaged(u8) = .{};
4603 defer buf.deinit(mod.gpa);4615 defer buf.deinit(mod.gpa);
4604 try parseStrLit(mod, scope, token, &buf, ident_name, 1);4616 try parseStrLit(mod, scope, token, &buf, ident_name, 1);
4605 const duped = try arena.dupe(u8, buf.items);4617 return arena.dupe(u8, buf.items);
4606 return duped;
4607}4618}
46084619
4609/// Given an identifier token, obtain the string for it (possibly parsing as a string4620/// Given an identifier token, obtain the string for it (possibly parsing as a string
src/Sema.zig+9-7
...@@ -2789,6 +2789,8 @@ fn analyzeSwitch(...@@ -2789,6 +2789,8 @@ fn analyzeSwitch(
2789 var seen_fields = try gpa.alloc(?AstGen.SwitchProngSrc, operand.ty.enumFieldCount());2789 var seen_fields = try gpa.alloc(?AstGen.SwitchProngSrc, operand.ty.enumFieldCount());
2790 defer gpa.free(seen_fields);2790 defer gpa.free(seen_fields);
27912791
2792 mem.set(?AstGen.SwitchProngSrc, seen_fields, null);
2793
2792 var extra_index: usize = special.end;2794 var extra_index: usize = special.end;
2793 {2795 {
2794 var scalar_i: u32 = 0;2796 var scalar_i: u32 = 0;
...@@ -2849,12 +2851,6 @@ fn analyzeSwitch(...@@ -2849,12 +2851,6 @@ fn analyzeSwitch(
2849 .{},2851 .{},
2850 );2852 );
2851 errdefer msg.destroy(sema.gpa);2853 errdefer msg.destroy(sema.gpa);
2852 try mod.errNoteNonLazy(
2853 operand.ty.declSrcLoc(),
2854 msg,
2855 "enum '{}' declared here",
2856 .{operand.ty},
2857 );
2858 for (seen_fields) |seen_src, i| {2854 for (seen_fields) |seen_src, i| {
2859 if (seen_src != null) continue;2855 if (seen_src != null) continue;
28602856
...@@ -2865,10 +2861,16 @@ fn analyzeSwitch(...@@ -2865,10 +2861,16 @@ fn analyzeSwitch(
2865 &block.base,2861 &block.base,
2866 src,2862 src,
2867 msg,2863 msg,
2868 "unhandled enumeration value: '{s}",2864 "unhandled enumeration value: '{s}'",
2869 .{field_name},2865 .{field_name},
2870 );2866 );
2871 }2867 }
2868 try mod.errNoteNonLazy(
2869 operand.ty.declSrcLoc(),
2870 msg,
2871 "enum '{}' declared here",
2872 .{operand.ty},
2873 );
2872 break :msg msg;2874 break :msg msg;
2873 };2875 };
2874 return mod.failWithOwnedErrorMsg(&block.base, msg);2876 return mod.failWithOwnedErrorMsg(&block.base, msg);
test/stage2/cbe.zig+15
...@@ -702,6 +702,21 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -702,6 +702,21 @@ pub fn addCases(ctx: *TestContext) !void {
702 ":3:15: error: enum 'E' has no tag with value 3",702 ":3:15: error: enum 'E' has no tag with value 3",
703 ":1:11: note: enum declared here",703 ":1:11: note: enum declared here",
704 });704 });
705
706 case.addError(
707 \\const E = enum { a, b, c };
708 \\export fn foo() void {
709 \\ var x: E = .a;
710 \\ switch (x) {
711 \\ .a => {},
712 \\ .c => {},
713 \\ }
714 \\}
715 , &.{
716 ":4:5: error: switch must handle all possibilities",
717 ":4:5: note: unhandled enumeration value: 'b'",
718 ":1:11: note: enum 'E' declared here",
719 });
705 }720 }
706721
707 ctx.c("empty start function", linux_x64,722 ctx.c("empty start function", linux_x64,