authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-06 22:36:28-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-06 22:36:28-07:00
logacf9151008d5a97e5d91b34cc29f73af79062b48
treec6c9d3e4b1560dfe45b73880c0036965785357b1
parent2adeace905d6c36e3a333647c21287493ab98485

stage2: implement field access for `Enum.tag` syntax


3 files changed, 90 insertions(+), 42 deletions(-)

src/Sema.zig+76-28
...@@ -4351,22 +4351,25 @@ fn namedFieldPtr(...@@ -4351,22 +4351,25 @@ fn namedFieldPtr(
4351 field_name: []const u8,4351 field_name: []const u8,
4352 field_name_src: LazySrcLoc,4352 field_name_src: LazySrcLoc,
4353) InnerError!*Inst {4353) InnerError!*Inst {
4354 const mod = sema.mod;
4355 const arena = sema.arena;
4356
4354 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {4357 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
4355 .Pointer => object_ptr.ty.elemType(),4358 .Pointer => object_ptr.ty.elemType(),
4356 else => return sema.mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),4359 else => return mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
4357 };4360 };
4358 switch (elem_ty.zigTypeTag()) {4361 switch (elem_ty.zigTypeTag()) {
4359 .Array => {4362 .Array => {
4360 if (mem.eql(u8, field_name, "len")) {4363 if (mem.eql(u8, field_name, "len")) {
4361 return sema.mod.constInst(sema.arena, src, .{4364 return mod.constInst(arena, src, .{
4362 .ty = Type.initTag(.single_const_pointer_to_comptime_int),4365 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
4363 .val = try Value.Tag.ref_val.create(4366 .val = try Value.Tag.ref_val.create(
4364 sema.arena,4367 arena,
4365 try Value.Tag.int_u64.create(sema.arena, elem_ty.arrayLen()),4368 try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()),
4366 ),4369 ),
4367 });4370 });
4368 } else {4371 } else {
4369 return sema.mod.fail(4372 return mod.fail(
4370 &block.base,4373 &block.base,
4371 field_name_src,4374 field_name_src,
4372 "no member named '{s}' in '{}'",4375 "no member named '{s}' in '{}'",
...@@ -4379,15 +4382,15 @@ fn namedFieldPtr(...@@ -4379,15 +4382,15 @@ fn namedFieldPtr(
4379 switch (ptr_child.zigTypeTag()) {4382 switch (ptr_child.zigTypeTag()) {
4380 .Array => {4383 .Array => {
4381 if (mem.eql(u8, field_name, "len")) {4384 if (mem.eql(u8, field_name, "len")) {
4382 return sema.mod.constInst(sema.arena, src, .{4385 return mod.constInst(arena, src, .{
4383 .ty = Type.initTag(.single_const_pointer_to_comptime_int),4386 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
4384 .val = try Value.Tag.ref_val.create(4387 .val = try Value.Tag.ref_val.create(
4385 sema.arena,4388 arena,
4386 try Value.Tag.int_u64.create(sema.arena, ptr_child.arrayLen()),4389 try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
4387 ),4390 ),
4388 });4391 });
4389 } else {4392 } else {
4390 return sema.mod.fail(4393 return mod.fail(
4391 &block.base,4394 &block.base,
4392 field_name_src,4395 field_name_src,
4393 "no member named '{s}' in '{}'",4396 "no member named '{s}' in '{}'",
...@@ -4402,7 +4405,7 @@ fn namedFieldPtr(...@@ -4402,7 +4405,7 @@ fn namedFieldPtr(
4402 _ = try sema.resolveConstValue(block, object_ptr.src, object_ptr);4405 _ = try sema.resolveConstValue(block, object_ptr.src, object_ptr);
4403 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src);4406 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src);
4404 const val = result.value().?;4407 const val = result.value().?;
4405 const child_type = try val.toType(sema.arena);4408 const child_type = try val.toType(arena);
4406 switch (child_type.zigTypeTag()) {4409 switch (child_type.zigTypeTag()) {
4407 .ErrorSet => {4410 .ErrorSet => {
4408 // TODO resolve inferred error sets4411 // TODO resolve inferred error sets
...@@ -4416,42 +4419,87 @@ fn namedFieldPtr(...@@ -4416,42 +4419,87 @@ fn namedFieldPtr(
4416 break :blk name;4419 break :blk name;
4417 }4420 }
4418 }4421 }
4419 return sema.mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{4422 return mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{
4420 field_name,4423 field_name,
4421 child_type,4424 child_type,
4422 });4425 });
4423 } else (try sema.mod.getErrorValue(field_name)).key;4426 } else (try mod.getErrorValue(field_name)).key;
44244427
4425 return sema.mod.constInst(sema.arena, src, .{4428 return mod.constInst(arena, src, .{
4426 .ty = try sema.mod.simplePtrType(sema.arena, child_type, false, .One),4429 .ty = try mod.simplePtrType(arena, child_type, false, .One),
4427 .val = try Value.Tag.ref_val.create(4430 .val = try Value.Tag.ref_val.create(
4428 sema.arena,4431 arena,
4429 try Value.Tag.@"error".create(sema.arena, .{4432 try Value.Tag.@"error".create(arena, .{
4430 .name = name,4433 .name = name,
4431 }),4434 }),
4432 ),4435 ),
4433 });4436 });
4434 },4437 },
4435 .Struct => {4438 .Struct, .Opaque, .Union => {
4436 const container_scope = child_type.getContainerScope();4439 if (child_type.getContainerScope()) |container_scope| {
4437 if (sema.mod.lookupDeclName(&container_scope.base, field_name)) |decl| {4440 if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
4438 // TODO if !decl.is_pub and inDifferentFiles() "{} is private"4441 // TODO if !decl.is_pub and inDifferentFiles() "{} is private"
4439 return sema.analyzeDeclRef(block, src, decl);4442 return sema.analyzeDeclRef(block, src, decl);
4440 }4443 }
44414444
4442 if (container_scope.file_scope == sema.mod.root_scope) {4445 // TODO this will give false positives for structs inside the root file
4443 return sema.mod.fail(&block.base, src, "root source file has no member called '{s}'", .{field_name});4446 if (container_scope.file_scope == mod.root_scope) {
4444 } else {4447 return mod.fail(
4445 return sema.mod.fail(&block.base, src, "container '{}' has no member called '{s}'", .{ child_type, field_name });4448 &block.base,
4449 src,
4450 "root source file has no member named '{s}'",
4451 .{field_name},
4452 );
4453 }
4454 }
4455 // TODO add note: declared here
4456 const kw_name = switch (child_type.zigTypeTag()) {
4457 .Struct => "struct",
4458 .Opaque => "opaque",
4459 .Union => "union",
4460 else => unreachable,
4461 };
4462 return mod.fail(&block.base, src, "{s} '{}' has no member named '{s}'", .{
4463 kw_name, child_type, field_name,
4464 });
4465 },
4466 .Enum => {
4467 if (child_type.getContainerScope()) |container_scope| {
4468 if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
4469 // TODO if !decl.is_pub and inDifferentFiles() "{} is private"
4470 return sema.analyzeDeclRef(block, src, decl);
4471 }
4446 }4472 }
4473 const maybe_field_index: ?usize = switch (child_type.tag()) {
4474 .enum_full, .enum_nonexhaustive => blk: {
4475 const enum_full = child_type.castTag(.enum_full).?.data;
4476 break :blk enum_full.fields.getIndex(field_name);
4477 },
4478 .enum_simple => blk: {
4479 const enum_simple = child_type.castTag(.enum_simple).?.data;
4480 break :blk enum_simple.fields.getIndex(field_name);
4481 },
4482 else => unreachable,
4483 };
4484 const field_index = maybe_field_index orelse {
4485 return mod.fail(&block.base, src, "enum '{}' has no member named '{s}'", .{
4486 child_type, field_name,
4487 });
4488 };
4489 const field_index_u32 = @intCast(u32, field_index);
4490 const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);
4491 return mod.constInst(arena, src, .{
4492 .ty = try mod.simplePtrType(arena, child_type, false, .One),
4493 .val = try Value.Tag.ref_val.create(arena, enum_val),
4494 });
4447 },4495 },
4448 else => return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{child_type}),4496 else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),
4449 }4497 }
4450 },4498 },
4451 .Struct => return sema.analyzeStructFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty),4499 .Struct => return sema.analyzeStructFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty),
4452 else => {},4500 else => {},
4453 }4501 }
4454 return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty});4502 return mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty});
4455}4503}
44564504
4457fn analyzeStructFieldPtr(4505fn analyzeStructFieldPtr(
src/type.zig+13-13
...@@ -634,13 +634,13 @@ pub const Type = extern union {...@@ -634,13 +634,13 @@ pub const Type = extern union {
634 }634 }
635635
636 pub fn format(636 pub fn format(
637 self: Type,637 start_type: Type,
638 comptime fmt: []const u8,638 comptime fmt: []const u8,
639 options: std.fmt.FormatOptions,639 options: std.fmt.FormatOptions,
640 writer: anytype,640 writer: anytype,
641 ) @TypeOf(writer).Error!void {641 ) @TypeOf(writer).Error!void {
642 comptime assert(fmt.len == 0);642 comptime assert(fmt.len == 0);
643 var ty = self;643 var ty = start_type;
644 while (true) {644 while (true) {
645 const t = ty.tag();645 const t = ty.tag();
646 switch (t) {646 switch (t) {
...@@ -687,15 +687,15 @@ pub const Type = extern union {...@@ -687,15 +687,15 @@ pub const Type = extern union {
687 .empty_struct, .empty_struct_literal => return writer.writeAll("struct {}"),687 .empty_struct, .empty_struct_literal => return writer.writeAll("struct {}"),
688688
689 .@"struct" => {689 .@"struct" => {
690 const struct_obj = self.castTag(.@"struct").?.data;690 const struct_obj = ty.castTag(.@"struct").?.data;
691 return struct_obj.owner_decl.renderFullyQualifiedName(writer);691 return struct_obj.owner_decl.renderFullyQualifiedName(writer);
692 },692 },
693 .enum_full, .enum_nonexhaustive => {693 .enum_full, .enum_nonexhaustive => {
694 const enum_full = self.castTag(.enum_full).?.data;694 const enum_full = ty.castTag(.enum_full).?.data;
695 return enum_full.owner_decl.renderFullyQualifiedName(writer);695 return enum_full.owner_decl.renderFullyQualifiedName(writer);
696 },696 },
697 .enum_simple => {697 .enum_simple => {
698 const enum_simple = self.castTag(.enum_simple).?.data;698 const enum_simple = ty.castTag(.enum_simple).?.data;
699 return enum_simple.owner_decl.renderFullyQualifiedName(writer);699 return enum_simple.owner_decl.renderFullyQualifiedName(writer);
700 },700 },
701 .@"opaque" => {701 .@"opaque" => {
...@@ -1886,8 +1886,8 @@ pub const Type = extern union {...@@ -1886,8 +1886,8 @@ pub const Type = extern union {
1886 };1886 };
1887 }1887 }
18881888
1889 pub fn onePossibleValue(self: Type) ?Value {1889 pub fn onePossibleValue(starting_type: Type) ?Value {
1890 var ty = self;1890 var ty = starting_type;
1891 while (true) switch (ty.tag()) {1891 while (true) switch (ty.tag()) {
1892 .f16,1892 .f16,
1893 .f32,1893 .f32,
...@@ -1954,7 +1954,7 @@ pub const Type = extern union {...@@ -1954,7 +1954,7 @@ pub const Type = extern union {
1954 return Value.initTag(.empty_struct_value);1954 return Value.initTag(.empty_struct_value);
1955 },1955 },
1956 .enum_full => {1956 .enum_full => {
1957 const enum_full = self.castTag(.enum_full).?.data;1957 const enum_full = ty.castTag(.enum_full).?.data;
1958 if (enum_full.fields.count() == 1) {1958 if (enum_full.fields.count() == 1) {
1959 return enum_full.values.entries.items[0].key;1959 return enum_full.values.entries.items[0].key;
1960 } else {1960 } else {
...@@ -1962,14 +1962,14 @@ pub const Type = extern union {...@@ -1962,14 +1962,14 @@ pub const Type = extern union {
1962 }1962 }
1963 },1963 },
1964 .enum_simple => {1964 .enum_simple => {
1965 const enum_simple = self.castTag(.enum_simple).?.data;1965 const enum_simple = ty.castTag(.enum_simple).?.data;
1966 if (enum_simple.fields.count() == 1) {1966 if (enum_simple.fields.count() == 1) {
1967 return Value.initTag(.zero);1967 return Value.initTag(.zero);
1968 } else {1968 } else {
1969 return null;1969 return null;
1970 }1970 }
1971 },1971 },
1972 .enum_nonexhaustive => return self.castTag(.enum_full).?.data.tag_ty.onePossibleValue(),1972 .enum_nonexhaustive => ty = ty.castTag(.enum_full).?.data.tag_ty,
19731973
1974 .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),1974 .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
1975 .void => return Value.initTag(.void_value),1975 .void => return Value.initTag(.void_value),
...@@ -2016,15 +2016,15 @@ pub const Type = extern union {...@@ -2016,15 +2016,15 @@ pub const Type = extern union {
2016 (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);2016 (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);
2017 }2017 }
20182018
2019 /// Asserts that the type is a container. (note: ErrorSet is not a container).2019 /// Returns null if the type has no container.
2020 pub fn getContainerScope(self: Type) *Module.Scope.Container {2020 pub fn getContainerScope(self: Type) ?*Module.Scope.Container {
2021 return switch (self.tag()) {2021 return switch (self.tag()) {
2022 .@"struct" => &self.castTag(.@"struct").?.data.container,2022 .@"struct" => &self.castTag(.@"struct").?.data.container,
2023 .enum_full => &self.castTag(.enum_full).?.data.container,2023 .enum_full => &self.castTag(.enum_full).?.data.container,
2024 .empty_struct => self.castTag(.empty_struct).?.data,2024 .empty_struct => self.castTag(.empty_struct).?.data,
2025 .@"opaque" => &self.castTag(.@"opaque").?.data,2025 .@"opaque" => &self.castTag(.@"opaque").?.data,
20262026
2027 else => unreachable,2027 else => null,
2028 };2028 };
2029 }2029 }
20302030
src/zir.zig+1-1
...@@ -1572,6 +1572,7 @@ const Writer = struct {...@@ -1572,6 +1572,7 @@ const Writer = struct {
1572 .intcast,1572 .intcast,
1573 .store,1573 .store,
1574 .store_to_block_ptr,1574 .store_to_block_ptr,
1575 .store_to_inferred_ptr,
1575 => try self.writeBin(stream, inst),1576 => try self.writeBin(stream, inst),
15761577
1577 .alloc,1578 .alloc,
...@@ -1769,7 +1770,6 @@ const Writer = struct {...@@ -1769,7 +1770,6 @@ const Writer = struct {
17691770
1770 .bitcast,1771 .bitcast,
1771 .bitcast_result_ptr,1772 .bitcast_result_ptr,
1772 .store_to_inferred_ptr,
1773 => try stream.writeAll("TODO)"),1773 => try stream.writeAll("TODO)"),
1774 }1774 }
1775 }1775 }