Sema: DRY up enum field analysis and add "declared here" notes

This commit is contained in:
Andrew Kelley 2021-04-07 11:26:07 -07:00
parent 01a39fa1d4
commit 4e8fb9e6a5
5 changed files with 113 additions and 71 deletions

View File

@ -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.

View File

@ -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 },
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,
});
},
.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 },
);
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 {

View File

@ -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 {

View File

@ -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 };

View File

@ -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",
});
}
}