Commit 4e8fb9e6a5

Andrew Kelley <andrew@ziglang.org>
2021-04-07 20:26:07
Sema: DRY up enum field analysis and add "declared here" notes
1 parent 01a39fa
Changed files (5)
src/Module.zig
@@ -366,6 +366,13 @@ pub const ErrorSet = struct {
     /// The string bytes are stored in the owner Decl arena.
     /// They are in the same order they appear in the AST.
     names_ptr: [*]const []const u8,
+
+    pub fn srcLoc(self: ErrorSet) SrcLoc {
+        return .{
+            .container = .{ .decl = self.owner_decl },
+            .lazy = .{ .node_offset = self.node_offset },
+        };
+    }
 };
 
 /// Represents the data that a struct declaration provides.
@@ -408,6 +415,13 @@ pub const EnumSimple = struct {
     fields: std.StringArrayHashMapUnmanaged(void),
     /// Offset from `owner_decl`, points to the enum decl AST node.
     node_offset: i32,
+
+    pub fn srcLoc(self: EnumSimple) SrcLoc {
+        return .{
+            .container = .{ .decl = self.owner_decl },
+            .lazy = .{ .node_offset = self.node_offset },
+        };
+    }
 };
 
 /// Represents the data that an enum declaration provides, when there is
@@ -429,6 +443,13 @@ pub const EnumFull = struct {
     node_offset: i32,
 
     pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.hash_u32, Value.eql, false);
+
+    pub fn srcLoc(self: EnumFull) SrcLoc {
+        return .{
+            .container = .{ .decl = self.owner_decl },
+            .lazy = .{ .node_offset = self.node_offset },
+        };
+    }
 };
 
 /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
src/Sema.zig
@@ -897,7 +897,7 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Ind
         try mod.errNoteNonLazy(
             struct_obj.srcLoc(),
             msg,
-            "'{s}' declared here",
+            "struct '{s}' declared here",
             .{fqn},
         );
         return mod.failWithOwnedErrorMsg(&block.base, msg);
@@ -925,7 +925,7 @@ fn failWithBadFieldAccess(
             .{ field_name, fqn },
         );
         errdefer msg.destroy(gpa);
-        try mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "'{s}' declared here", .{fqn});
+        try mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "struct declared here", .{});
         break :msg msg;
     };
     return mod.failWithOwnedErrorMsg(&block.base, msg);
@@ -4479,21 +4479,24 @@ fn namedFieldPtr(
                             return sema.analyzeDeclRef(block, src, decl);
                         }
                     }
-                    const maybe_field_index: ?usize = switch (child_type.tag()) {
-                        .enum_full, .enum_nonexhaustive => blk: {
-                            const enum_full = child_type.castTag(.enum_full).?.data;
-                            break :blk enum_full.fields.getIndex(field_name);
-                        },
-                        .enum_simple => blk: {
-                            const enum_simple = child_type.castTag(.enum_simple).?.data;
-                            break :blk enum_simple.fields.getIndex(field_name);
-                        },
-                        else => unreachable,
-                    };
-                    const field_index = maybe_field_index orelse {
-                        return mod.fail(&block.base, src, "enum '{}' has no member named '{s}'", .{
-                            child_type, field_name,
-                        });
+                    const field_index = child_type.enumFieldIndex(field_name) orelse {
+                        const msg = msg: {
+                            const msg = try mod.errMsg(
+                                &block.base,
+                                src,
+                                "enum '{}' has no member named '{s}'",
+                                .{ child_type, field_name },
+                            );
+                            errdefer msg.destroy(sema.gpa);
+                            try mod.errNoteNonLazy(
+                                child_type.declSrcLoc(),
+                                msg,
+                                "enum declared here",
+                                .{},
+                            );
+                            break :msg msg;
+                        };
+                        return mod.failWithOwnedErrorMsg(&block.base, msg);
                     };
                     const field_index_u32 = @intCast(u32, field_index);
                     const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);
@@ -4593,10 +4596,13 @@ fn coerce(
         return sema.bitcast(block, dest_type, inst);
     }
 
+    const mod = sema.mod;
+    const arena = sema.arena;
+
     // undefined to anything
     if (inst.value()) |val| {
         if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
-            return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = val });
+            return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = val });
         }
     }
     assert(inst.ty.zigTypeTag() != .Undefined);
@@ -4610,13 +4616,13 @@ fn coerce(
     if (try sema.coerceNum(block, dest_type, inst)) |some|
         return some;
 
-    const target = sema.mod.getTarget();
+    const target = mod.getTarget();
 
     switch (dest_type.zigTypeTag()) {
         .Optional => {
             // null to ?T
             if (inst.ty.zigTypeTag() == .Null) {
-                return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
+                return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
             }
 
             // T to ?T
@@ -4703,63 +4709,39 @@ fn coerce(
             }
         },
         .Enum => {
+            // enum literal to enum
             if (inst.ty.zigTypeTag() == .EnumLiteral) {
-                const val = (try sema.resolveDefinedValue(block, inst_src, inst)).?;
+                const val = try sema.resolveConstValue(block, inst_src, inst);
                 const bytes = val.castTag(.enum_literal).?.data;
-                switch (dest_type.tag()) {
-                    .enum_full => {
-                        const enumeration = dest_type.castTag(.enum_full).?.data;
-                        const enum_fields = enumeration.fields;
-                        const i = enum_fields.getIndex(bytes) orelse return sema.mod.fail(
+                const field_index = dest_type.enumFieldIndex(bytes) orelse {
+                    const msg = msg: {
+                        const msg = try mod.errMsg(
                             &block.base,
                             inst_src,
-                            "enum '{s}' has no field named '{s}'",
-                            .{ enumeration.owner_decl.name, bytes },
+                            "enum '{}' has no field named '{s}'",
+                            .{ dest_type, bytes },
                         );
-                        const val_pl = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i));
-                        return sema.mod.constInst(sema.arena, inst_src, .{
-                            .ty = dest_type,
-                            .val = val_pl,
-                        });
-                    },
-                    .enum_simple => {
-                        const enumeration = dest_type.castTag(.enum_simple).?.data;
-                        const enum_fields = enumeration.fields;
-                        const i = enum_fields.getIndex(bytes) orelse return sema.mod.fail(
-                            &block.base,
-                            inst_src,
-                            "enum '{s}' has no field named '{s}'",
-                            .{ enumeration.owner_decl.name, bytes },
-                        );
-                        const val_pl = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i));
-                        return sema.mod.constInst(sema.arena, inst_src, .{
-                            .ty = dest_type,
-                            .val = val_pl,
-                        });
-                    },
-                    .enum_nonexhaustive => {
-                        const enumeration = dest_type.castTag(.enum_nonexhaustive).?.data;
-                        const enum_fields = enumeration.fields;
-                        const i = enum_fields.getIndex(bytes) orelse return sema.mod.fail(
-                            &block.base,
-                            inst_src,
-                            "enum '{s}' has no field named '{s}'",
-                            .{ enumeration.owner_decl.name, bytes },
+                        errdefer msg.destroy(sema.gpa);
+                        try mod.errNoteNonLazy(
+                            dest_type.declSrcLoc(),
+                            msg,
+                            "enum declared here",
+                            .{},
                         );
-                        const val_pl = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i));
-                        return sema.mod.constInst(sema.arena, inst_src, .{
-                            .ty = dest_type,
-                            .val = val_pl,
-                        });
-                    },
-                    else => unreachable,
-                }
+                        break :msg msg;
+                    };
+                    return mod.failWithOwnedErrorMsg(&block.base, msg);
+                };
+                return mod.constInst(arena, inst_src, .{
+                    .ty = dest_type,
+                    .val = try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)),
+                });
             }
         },
         else => {},
     }
 
-    return sema.mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });
+    return mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });
 }
 
 const InMemoryCoercionResult = enum {
src/type.zig
@@ -2090,6 +2090,42 @@ pub const Type = extern union {
         };
     }
 
+    pub fn enumFieldIndex(ty: Type, field_name: []const u8) ?usize {
+        switch (ty.tag()) {
+            .enum_full, .enum_nonexhaustive => {
+                const enum_full = ty.cast(Payload.EnumFull).?.data;
+                return enum_full.fields.getIndex(field_name);
+            },
+            .enum_simple => {
+                const enum_simple = ty.castTag(.enum_simple).?.data;
+                return enum_simple.fields.getIndex(field_name);
+            },
+            else => unreachable,
+        }
+    }
+
+    pub fn declSrcLoc(ty: Type) Module.SrcLoc {
+        switch (ty.tag()) {
+            .enum_full, .enum_nonexhaustive => {
+                const enum_full = ty.cast(Payload.EnumFull).?.data;
+                return enum_full.srcLoc();
+            },
+            .enum_simple => {
+                const enum_simple = ty.castTag(.enum_simple).?.data;
+                return enum_simple.srcLoc();
+            },
+            .@"struct" => {
+                const struct_obj = ty.castTag(.@"struct").?.data;
+                return struct_obj.srcLoc();
+            },
+            .error_set => {
+                const error_set = ty.castTag(.error_set).?.data;
+                return error_set.srcLoc();
+            },
+            else => unreachable,
+        }
+    }
+
     /// Asserts the type is an enum.
     pub fn enumHasInt(ty: Type, int: Value, target: Target) bool {
         const S = struct {
test/stage2/cbe.zig
@@ -508,7 +508,7 @@ pub fn addCases(ctx: *TestContext) !void {
             \\}
         , &.{
             ":3:21: error: mising struct field: x",
-            ":1:15: note: 'Point' declared here",
+            ":1:15: note: struct 'Point' declared here",
         });
         case.addError(
             \\const Point = struct { x: i32, y: i32 };
@@ -522,7 +522,7 @@ pub fn addCases(ctx: *TestContext) !void {
             \\}
         , &.{
             ":6:10: error: no field named 'z' in struct 'Point'",
-            ":1:15: note: 'Point' declared here",
+            ":1:15: note: struct declared here",
         });
         case.addCompareOutput(
             \\const Point = struct { x: i32, y: i32 };
test/stage2/test.zig
@@ -1022,7 +1022,7 @@ pub fn addCases(ctx: *TestContext) !void {
             "Hello, World!\n",
         );
         try case.files.append(.{
-            .src =
+            .src = 
             \\pub fn print() void {
             \\    asm volatile ("syscall"
             \\        :
@@ -1621,11 +1621,11 @@ pub fn addCases(ctx: *TestContext) !void {
             "",
         );
         case.addError(
-            \\const E = enum { a, b };
             \\export fn _start() noreturn {
             \\    const a: E = .c;
             \\    exit();
             \\}
+            \\const E = enum { a, b };
             \\fn exit() noreturn {
             \\    asm volatile ("syscall"
             \\        :
@@ -1635,6 +1635,9 @@ pub fn addCases(ctx: *TestContext) !void {
             \\    );
             \\    unreachable;
             \\}
-        , &.{":3:19: error: enum 'E' has no field named 'c'"});
+        , &.{
+            ":2:19: error: enum 'E' has no field named 'c'",
+            ":5:11: note: enum declared here",
+        });
     }
 }