stage2: implement field access for Enum.tag syntax

This commit is contained in:
Andrew Kelley 2021-04-06 22:36:28 -07:00
parent 2adeace905
commit acf9151008
3 changed files with 90 additions and 42 deletions

View File

@ -4351,22 +4351,25 @@ fn namedFieldPtr(
field_name: []const u8,
field_name_src: LazySrcLoc,
) InnerError!*Inst {
const mod = sema.mod;
const arena = sema.arena;
const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
.Pointer => object_ptr.ty.elemType(),
else => return sema.mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
else => return mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
};
switch (elem_ty.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
return sema.mod.constInst(sema.arena, src, .{
return mod.constInst(arena, src, .{
.ty = Type.initTag(.single_const_pointer_to_comptime_int),
.val = try Value.Tag.ref_val.create(
sema.arena,
try Value.Tag.int_u64.create(sema.arena, elem_ty.arrayLen()),
arena,
try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()),
),
});
} else {
return sema.mod.fail(
return mod.fail(
&block.base,
field_name_src,
"no member named '{s}' in '{}'",
@ -4379,15 +4382,15 @@ fn namedFieldPtr(
switch (ptr_child.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
return sema.mod.constInst(sema.arena, src, .{
return mod.constInst(arena, src, .{
.ty = Type.initTag(.single_const_pointer_to_comptime_int),
.val = try Value.Tag.ref_val.create(
sema.arena,
try Value.Tag.int_u64.create(sema.arena, ptr_child.arrayLen()),
arena,
try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
),
});
} else {
return sema.mod.fail(
return mod.fail(
&block.base,
field_name_src,
"no member named '{s}' in '{}'",
@ -4402,7 +4405,7 @@ fn namedFieldPtr(
_ = try sema.resolveConstValue(block, object_ptr.src, object_ptr);
const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src);
const val = result.value().?;
const child_type = try val.toType(sema.arena);
const child_type = try val.toType(arena);
switch (child_type.zigTypeTag()) {
.ErrorSet => {
// TODO resolve inferred error sets
@ -4416,42 +4419,87 @@ fn namedFieldPtr(
break :blk name;
}
}
return sema.mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{
return mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{
field_name,
child_type,
});
} else (try sema.mod.getErrorValue(field_name)).key;
} else (try mod.getErrorValue(field_name)).key;
return sema.mod.constInst(sema.arena, src, .{
.ty = try sema.mod.simplePtrType(sema.arena, child_type, false, .One),
return mod.constInst(arena, src, .{
.ty = try mod.simplePtrType(arena, child_type, false, .One),
.val = try Value.Tag.ref_val.create(
sema.arena,
try Value.Tag.@"error".create(sema.arena, .{
arena,
try Value.Tag.@"error".create(arena, .{
.name = name,
}),
),
});
},
.Struct => {
const container_scope = child_type.getContainerScope();
if (sema.mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
// TODO if !decl.is_pub and inDifferentFiles() "{} is private"
return sema.analyzeDeclRef(block, src, decl);
}
.Struct, .Opaque, .Union => {
if (child_type.getContainerScope()) |container_scope| {
if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
// TODO if !decl.is_pub and inDifferentFiles() "{} is private"
return sema.analyzeDeclRef(block, src, decl);
}
if (container_scope.file_scope == sema.mod.root_scope) {
return sema.mod.fail(&block.base, src, "root source file has no member called '{s}'", .{field_name});
} else {
return sema.mod.fail(&block.base, src, "container '{}' has no member called '{s}'", .{ child_type, field_name });
// TODO this will give false positives for structs inside the root file
if (container_scope.file_scope == mod.root_scope) {
return mod.fail(
&block.base,
src,
"root source file has no member named '{s}'",
.{field_name},
);
}
}
// TODO add note: declared here
const kw_name = switch (child_type.zigTypeTag()) {
.Struct => "struct",
.Opaque => "opaque",
.Union => "union",
else => unreachable,
};
return mod.fail(&block.base, src, "{s} '{}' has no member named '{s}'", .{
kw_name, child_type, field_name,
});
},
else => return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{child_type}),
.Enum => {
if (child_type.getContainerScope()) |container_scope| {
if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
// TODO if !decl.is_pub and inDifferentFiles() "{} is private"
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_u32 = @intCast(u32, field_index);
const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);
return mod.constInst(arena, src, .{
.ty = try mod.simplePtrType(arena, child_type, false, .One),
.val = try Value.Tag.ref_val.create(arena, enum_val),
});
},
else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),
}
},
.Struct => return sema.analyzeStructFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty),
else => {},
}
return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty});
return mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty});
}
fn analyzeStructFieldPtr(

View File

@ -634,13 +634,13 @@ pub const Type = extern union {
}
pub fn format(
self: Type,
start_type: Type,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
comptime assert(fmt.len == 0);
var ty = self;
var ty = start_type;
while (true) {
const t = ty.tag();
switch (t) {
@ -687,15 +687,15 @@ pub const Type = extern union {
.empty_struct, .empty_struct_literal => return writer.writeAll("struct {}"),
.@"struct" => {
const struct_obj = self.castTag(.@"struct").?.data;
const struct_obj = ty.castTag(.@"struct").?.data;
return struct_obj.owner_decl.renderFullyQualifiedName(writer);
},
.enum_full, .enum_nonexhaustive => {
const enum_full = self.castTag(.enum_full).?.data;
const enum_full = ty.castTag(.enum_full).?.data;
return enum_full.owner_decl.renderFullyQualifiedName(writer);
},
.enum_simple => {
const enum_simple = self.castTag(.enum_simple).?.data;
const enum_simple = ty.castTag(.enum_simple).?.data;
return enum_simple.owner_decl.renderFullyQualifiedName(writer);
},
.@"opaque" => {
@ -1886,8 +1886,8 @@ pub const Type = extern union {
};
}
pub fn onePossibleValue(self: Type) ?Value {
var ty = self;
pub fn onePossibleValue(starting_type: Type) ?Value {
var ty = starting_type;
while (true) switch (ty.tag()) {
.f16,
.f32,
@ -1954,7 +1954,7 @@ pub const Type = extern union {
return Value.initTag(.empty_struct_value);
},
.enum_full => {
const enum_full = self.castTag(.enum_full).?.data;
const enum_full = ty.castTag(.enum_full).?.data;
if (enum_full.fields.count() == 1) {
return enum_full.values.entries.items[0].key;
} else {
@ -1962,14 +1962,14 @@ pub const Type = extern union {
}
},
.enum_simple => {
const enum_simple = self.castTag(.enum_simple).?.data;
const enum_simple = ty.castTag(.enum_simple).?.data;
if (enum_simple.fields.count() == 1) {
return Value.initTag(.zero);
} else {
return null;
}
},
.enum_nonexhaustive => return self.castTag(.enum_full).?.data.tag_ty.onePossibleValue(),
.enum_nonexhaustive => ty = ty.castTag(.enum_full).?.data.tag_ty,
.empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
.void => return Value.initTag(.void_value),
@ -2016,15 +2016,15 @@ pub const Type = extern union {
(self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);
}
/// Asserts that the type is a container. (note: ErrorSet is not a container).
pub fn getContainerScope(self: Type) *Module.Scope.Container {
/// Returns null if the type has no container.
pub fn getContainerScope(self: Type) ?*Module.Scope.Container {
return switch (self.tag()) {
.@"struct" => &self.castTag(.@"struct").?.data.container,
.enum_full => &self.castTag(.enum_full).?.data.container,
.empty_struct => self.castTag(.empty_struct).?.data,
.@"opaque" => &self.castTag(.@"opaque").?.data,
else => unreachable,
else => null,
};
}

View File

@ -1572,6 +1572,7 @@ const Writer = struct {
.intcast,
.store,
.store_to_block_ptr,
.store_to_inferred_ptr,
=> try self.writeBin(stream, inst),
.alloc,
@ -1769,7 +1770,6 @@ const Writer = struct {
.bitcast,
.bitcast_result_ptr,
.store_to_inferred_ptr,
=> try stream.writeAll("TODO)"),
}
}