| author | |
| committer | |
| log | e730172e47749db8a9d3be5949231bde95343b39 |
| tree | d77ee85e8f3c99a6a13979a22e8650063677b75a |
| parent | b67378fb08fb3129b66385479a278a93940f09f5 |
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`. |
| 4584 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { | 4584 | pub 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 | } |
| 4587 | 4598 | ||
| 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. | ||
| 4589 | pub fn identifierTokenStringTreeArena( | 4601 | pub 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 | } |
| 4608 | 4619 | ||
| 4609 | /// Given an identifier token, obtain the string for it (possibly parsing as a string | 4620 | /// 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); |
| 2791 | 2791 | ||
| 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; |
| 2860 | 2856 | ||
| ... | @@ -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 | } |
| 706 | 721 | ||
| 707 | ctx.c("empty start function", linux_x64, | 722 | ctx.c("empty start function", linux_x64, |