mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
Sema: DRY up enum field analysis and add "declared here" notes
This commit is contained in:
parent
01a39fa1d4
commit
4e8fb9e6a5
@ -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.
|
||||
|
||||
114
src/Sema.zig
114
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 },
|
||||
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 {
|
||||
|
||||
36
src/type.zig
36
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 {
|
||||
|
||||
@ -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 };
|
||||
|
||||
@ -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",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user