Commit acf9151008

Andrew Kelley <andrew@ziglang.org>
2021-04-07 07:36:28
stage2: implement field access for `Enum.tag` syntax
1 parent 2adeace
Changed files (3)
src/Sema.zig
@@ -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,
+                    });
+                },
+                .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 sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{child_type}),
+                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(
src/type.zig
@@ -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,
         };
     }
 
src/zir.zig
@@ -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)"),
         }
     }