authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-07 11:26:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-07 11:26:07-07:00
log4e8fb9e6a5f9a65bbf6469e386e83ba469e7543b
tree804cac2f05b76ab8c76925b3104dd19ec0e4be44
parent01a39fa1d4d0d2e3305af8e6d6af1079f020db7f

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


5 files changed, 113 insertions(+), 71 deletions(-)

src/Module.zig+21
...@@ -366,6 +366,13 @@ pub const ErrorSet = struct {...@@ -366,6 +366,13 @@ pub const ErrorSet = struct {
366 /// The string bytes are stored in the owner Decl arena.366 /// The string bytes are stored in the owner Decl arena.
367 /// They are in the same order they appear in the AST.367 /// They are in the same order they appear in the AST.
368 names_ptr: [*]const []const u8,368 names_ptr: [*]const []const u8,
369
370 pub fn srcLoc(self: ErrorSet) SrcLoc {
371 return .{
372 .container = .{ .decl = self.owner_decl },
373 .lazy = .{ .node_offset = self.node_offset },
374 };
375 }
369};376};
370377
371/// Represents the data that a struct declaration provides.378/// Represents the data that a struct declaration provides.
...@@ -408,6 +415,13 @@ pub const EnumSimple = struct {...@@ -408,6 +415,13 @@ pub const EnumSimple = struct {
408 fields: std.StringArrayHashMapUnmanaged(void),415 fields: std.StringArrayHashMapUnmanaged(void),
409 /// Offset from `owner_decl`, points to the enum decl AST node.416 /// Offset from `owner_decl`, points to the enum decl AST node.
410 node_offset: i32,417 node_offset: i32,
418
419 pub fn srcLoc(self: EnumSimple) SrcLoc {
420 return .{
421 .container = .{ .decl = self.owner_decl },
422 .lazy = .{ .node_offset = self.node_offset },
423 };
424 }
411};425};
412426
413/// Represents the data that an enum declaration provides, when there is427/// Represents the data that an enum declaration provides, when there is
...@@ -429,6 +443,13 @@ pub const EnumFull = struct {...@@ -429,6 +443,13 @@ pub const EnumFull = struct {
429 node_offset: i32,443 node_offset: i32,
430444
431 pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.hash_u32, Value.eql, false);445 pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.hash_u32, Value.eql, false);
446
447 pub fn srcLoc(self: EnumFull) SrcLoc {
448 return .{
449 .container = .{ .decl = self.owner_decl },
450 .lazy = .{ .node_offset = self.node_offset },
451 };
452 }
432};453};
433454
434/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.455/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
src/Sema.zig+48-66
...@@ -897,7 +897,7 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Ind...@@ -897,7 +897,7 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Ind
897 try mod.errNoteNonLazy(897 try mod.errNoteNonLazy(
898 struct_obj.srcLoc(),898 struct_obj.srcLoc(),
899 msg,899 msg,
900 "'{s}' declared here",900 "struct '{s}' declared here",
901 .{fqn},901 .{fqn},
902 );902 );
903 return mod.failWithOwnedErrorMsg(&block.base, msg);903 return mod.failWithOwnedErrorMsg(&block.base, msg);
...@@ -925,7 +925,7 @@ fn failWithBadFieldAccess(...@@ -925,7 +925,7 @@ fn failWithBadFieldAccess(
925 .{ field_name, fqn },925 .{ field_name, fqn },
926 );926 );
927 errdefer msg.destroy(gpa);927 errdefer msg.destroy(gpa);
928 try mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "'{s}' declared here", .{fqn});928 try mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "struct declared here", .{});
929 break :msg msg;929 break :msg msg;
930 };930 };
931 return mod.failWithOwnedErrorMsg(&block.base, msg);931 return mod.failWithOwnedErrorMsg(&block.base, msg);
...@@ -4479,21 +4479,24 @@ fn namedFieldPtr(...@@ -4479,21 +4479,24 @@ fn namedFieldPtr(
4479 return sema.analyzeDeclRef(block, src, decl);4479 return sema.analyzeDeclRef(block, src, decl);
4480 }4480 }
4481 }4481 }
4482 const maybe_field_index: ?usize = switch (child_type.tag()) {4482 const field_index = child_type.enumFieldIndex(field_name) orelse {
4483 .enum_full, .enum_nonexhaustive => blk: {4483 const msg = msg: {
4484 const enum_full = child_type.castTag(.enum_full).?.data;4484 const msg = try mod.errMsg(
4485 break :blk enum_full.fields.getIndex(field_name);4485 &block.base,
4486 },4486 src,
4487 .enum_simple => blk: {4487 "enum '{}' has no member named '{s}'",
4488 const enum_simple = child_type.castTag(.enum_simple).?.data;4488 .{ child_type, field_name },
4489 break :blk enum_simple.fields.getIndex(field_name);4489 );
4490 },4490 errdefer msg.destroy(sema.gpa);
4491 else => unreachable,4491 try mod.errNoteNonLazy(
4492 };4492 child_type.declSrcLoc(),
4493 const field_index = maybe_field_index orelse {4493 msg,
4494 return mod.fail(&block.base, src, "enum '{}' has no member named '{s}'", .{4494 "enum declared here",
4495 child_type, field_name,4495 .{},
4496 });4496 );
4497 break :msg msg;
4498 };
4499 return mod.failWithOwnedErrorMsg(&block.base, msg);
4497 };4500 };
4498 const field_index_u32 = @intCast(u32, field_index);4501 const field_index_u32 = @intCast(u32, field_index);
4499 const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);4502 const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);
...@@ -4593,10 +4596,13 @@ fn coerce(...@@ -4593,10 +4596,13 @@ fn coerce(
4593 return sema.bitcast(block, dest_type, inst);4596 return sema.bitcast(block, dest_type, inst);
4594 }4597 }
45954598
4599 const mod = sema.mod;
4600 const arena = sema.arena;
4601
4596 // undefined to anything4602 // undefined to anything
4597 if (inst.value()) |val| {4603 if (inst.value()) |val| {
4598 if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {4604 if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
4599 return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = val });4605 return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = val });
4600 }4606 }
4601 }4607 }
4602 assert(inst.ty.zigTypeTag() != .Undefined);4608 assert(inst.ty.zigTypeTag() != .Undefined);
...@@ -4610,13 +4616,13 @@ fn coerce(...@@ -4610,13 +4616,13 @@ fn coerce(
4610 if (try sema.coerceNum(block, dest_type, inst)) |some|4616 if (try sema.coerceNum(block, dest_type, inst)) |some|
4611 return some;4617 return some;
46124618
4613 const target = sema.mod.getTarget();4619 const target = mod.getTarget();
46144620
4615 switch (dest_type.zigTypeTag()) {4621 switch (dest_type.zigTypeTag()) {
4616 .Optional => {4622 .Optional => {
4617 // null to ?T4623 // null to ?T
4618 if (inst.ty.zigTypeTag() == .Null) {4624 if (inst.ty.zigTypeTag() == .Null) {
4619 return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });4625 return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
4620 }4626 }
46214627
4622 // T to ?T4628 // T to ?T
...@@ -4703,63 +4709,39 @@ fn coerce(...@@ -4703,63 +4709,39 @@ fn coerce(
4703 }4709 }
4704 },4710 },
4705 .Enum => {4711 .Enum => {
4712 // enum literal to enum
4706 if (inst.ty.zigTypeTag() == .EnumLiteral) {4713 if (inst.ty.zigTypeTag() == .EnumLiteral) {
4707 const val = (try sema.resolveDefinedValue(block, inst_src, inst)).?;4714 const val = try sema.resolveConstValue(block, inst_src, inst);
4708 const bytes = val.castTag(.enum_literal).?.data;4715 const bytes = val.castTag(.enum_literal).?.data;
4709 switch (dest_type.tag()) {4716 const field_index = dest_type.enumFieldIndex(bytes) orelse {
4710 .enum_full => {4717 const msg = msg: {
4711 const enumeration = dest_type.castTag(.enum_full).?.data;4718 const msg = try mod.errMsg(
4712 const enum_fields = enumeration.fields;
4713 const i = enum_fields.getIndex(bytes) orelse return sema.mod.fail(
4714 &block.base,4719 &block.base,
4715 inst_src,4720 inst_src,
4716 "enum '{s}' has no field named '{s}'",4721 "enum '{}' has no field named '{s}'",
4717 .{ enumeration.owner_decl.name, bytes },4722 .{ dest_type, bytes },
4718 );4723 );
4719 const val_pl = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i));4724 errdefer msg.destroy(sema.gpa);
4720 return sema.mod.constInst(sema.arena, inst_src, .{4725 try mod.errNoteNonLazy(
4721 .ty = dest_type,4726 dest_type.declSrcLoc(),
4722 .val = val_pl,4727 msg,
4723 });4728 "enum declared here",
4724 },4729 .{},
4725 .enum_simple => {
4726 const enumeration = dest_type.castTag(.enum_simple).?.data;
4727 const enum_fields = enumeration.fields;
4728 const i = enum_fields.getIndex(bytes) orelse return sema.mod.fail(
4729 &block.base,
4730 inst_src,
4731 "enum '{s}' has no field named '{s}'",
4732 .{ enumeration.owner_decl.name, bytes },
4733 );
4734 const val_pl = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i));
4735 return sema.mod.constInst(sema.arena, inst_src, .{
4736 .ty = dest_type,
4737 .val = val_pl,
4738 });
4739 },
4740 .enum_nonexhaustive => {
4741 const enumeration = dest_type.castTag(.enum_nonexhaustive).?.data;
4742 const enum_fields = enumeration.fields;
4743 const i = enum_fields.getIndex(bytes) orelse return sema.mod.fail(
4744 &block.base,
4745 inst_src,
4746 "enum '{s}' has no field named '{s}'",
4747 .{ enumeration.owner_decl.name, bytes },
4748 );4730 );
4749 const val_pl = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i));4731 break :msg msg;
4750 return sema.mod.constInst(sema.arena, inst_src, .{4732 };
4751 .ty = dest_type,4733 return mod.failWithOwnedErrorMsg(&block.base, msg);
4752 .val = val_pl,4734 };
4753 });4735 return mod.constInst(arena, inst_src, .{
4754 },4736 .ty = dest_type,
4755 else => unreachable,4737 .val = try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)),
4756 }4738 });
4757 }4739 }
4758 },4740 },
4759 else => {},4741 else => {},
4760 }4742 }
47614743
4762 return sema.mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });4744 return mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });
4763}4745}
47644746
4765const InMemoryCoercionResult = enum {4747const InMemoryCoercionResult = enum {
src/type.zig+36
...@@ -2090,6 +2090,42 @@ pub const Type = extern union {...@@ -2090,6 +2090,42 @@ pub const Type = extern union {
2090 };2090 };
2091 }2091 }
20922092
2093 pub fn enumFieldIndex(ty: Type, field_name: []const u8) ?usize {
2094 switch (ty.tag()) {
2095 .enum_full, .enum_nonexhaustive => {
2096 const enum_full = ty.cast(Payload.EnumFull).?.data;
2097 return enum_full.fields.getIndex(field_name);
2098 },
2099 .enum_simple => {
2100 const enum_simple = ty.castTag(.enum_simple).?.data;
2101 return enum_simple.fields.getIndex(field_name);
2102 },
2103 else => unreachable,
2104 }
2105 }
2106
2107 pub fn declSrcLoc(ty: Type) Module.SrcLoc {
2108 switch (ty.tag()) {
2109 .enum_full, .enum_nonexhaustive => {
2110 const enum_full = ty.cast(Payload.EnumFull).?.data;
2111 return enum_full.srcLoc();
2112 },
2113 .enum_simple => {
2114 const enum_simple = ty.castTag(.enum_simple).?.data;
2115 return enum_simple.srcLoc();
2116 },
2117 .@"struct" => {
2118 const struct_obj = ty.castTag(.@"struct").?.data;
2119 return struct_obj.srcLoc();
2120 },
2121 .error_set => {
2122 const error_set = ty.castTag(.error_set).?.data;
2123 return error_set.srcLoc();
2124 },
2125 else => unreachable,
2126 }
2127 }
2128
2093 /// Asserts the type is an enum.2129 /// Asserts the type is an enum.
2094 pub fn enumHasInt(ty: Type, int: Value, target: Target) bool {2130 pub fn enumHasInt(ty: Type, int: Value, target: Target) bool {
2095 const S = struct {2131 const S = struct {
test/stage2/cbe.zig+2-2
...@@ -508,7 +508,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -508,7 +508,7 @@ pub fn addCases(ctx: *TestContext) !void {
508 \\}508 \\}
509 , &.{509 , &.{
510 ":3:21: error: mising struct field: x",510 ":3:21: error: mising struct field: x",
511 ":1:15: note: 'Point' declared here",511 ":1:15: note: struct 'Point' declared here",
512 });512 });
513 case.addError(513 case.addError(
514 \\const Point = struct { x: i32, y: i32 };514 \\const Point = struct { x: i32, y: i32 };
...@@ -522,7 +522,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -522,7 +522,7 @@ pub fn addCases(ctx: *TestContext) !void {
522 \\}522 \\}
523 , &.{523 , &.{
524 ":6:10: error: no field named 'z' in struct 'Point'",524 ":6:10: error: no field named 'z' in struct 'Point'",
525 ":1:15: note: 'Point' declared here",525 ":1:15: note: struct declared here",
526 });526 });
527 case.addCompareOutput(527 case.addCompareOutput(
528 \\const Point = struct { x: i32, y: i32 };528 \\const Point = struct { x: i32, y: i32 };
test/stage2/test.zig+6-3
...@@ -1022,7 +1022,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1022,7 +1022,7 @@ pub fn addCases(ctx: *TestContext) !void {
1022 "Hello, World!\n",1022 "Hello, World!\n",
1023 );1023 );
1024 try case.files.append(.{1024 try case.files.append(.{
1025 .src =1025 .src =
1026 \\pub fn print() void {1026 \\pub fn print() void {
1027 \\ asm volatile ("syscall"1027 \\ asm volatile ("syscall"
1028 \\ :1028 \\ :
...@@ -1621,11 +1621,11 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1621,11 +1621,11 @@ pub fn addCases(ctx: *TestContext) !void {
1621 "",1621 "",
1622 );1622 );
1623 case.addError(1623 case.addError(
1624 \\const E = enum { a, b };
1625 \\export fn _start() noreturn {1624 \\export fn _start() noreturn {
1626 \\ const a: E = .c;1625 \\ const a: E = .c;
1627 \\ exit();1626 \\ exit();
1628 \\}1627 \\}
1628 \\const E = enum { a, b };
1629 \\fn exit() noreturn {1629 \\fn exit() noreturn {
1630 \\ asm volatile ("syscall"1630 \\ asm volatile ("syscall"
1631 \\ :1631 \\ :
...@@ -1635,6 +1635,9 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1635,6 +1635,9 @@ pub fn addCases(ctx: *TestContext) !void {
1635 \\ );1635 \\ );
1636 \\ unreachable;1636 \\ unreachable;
1637 \\}1637 \\}
1638 , &.{":3:19: error: enum 'E' has no field named 'c'"});1638 , &.{
1639 ":2:19: error: enum 'E' has no field named 'c'",
1640 ":5:11: note: enum declared here",
1641 });
1639 }1642 }
1640}1643}