Commit 5de9aac749

Vexu <git@vexu.eu>
2020-08-24 15:24:23
stage2: error set types
1 parent bc1d55a
Changed files (4)
src-self-hosted/type.zig
@@ -3,6 +3,7 @@ const Value = @import("value.zig").Value;
 const assert = std.debug.assert;
 const Allocator = std.mem.Allocator;
 const Target = std.Target;
+const Module = @import("Module.zig");
 
 /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
 /// It's important for this type to be small.
@@ -52,7 +53,7 @@ pub const Type = extern union {
             .bool => return .Bool,
             .void => return .Void,
             .type => return .Type,
-            .anyerror => return .ErrorSet,
+            .error_set, .error_set_single, .anyerror => return .ErrorSet,
             .comptime_int => return .ComptimeInt,
             .comptime_float => return .ComptimeFloat,
             .noreturn => return .NoReturn,
@@ -436,6 +437,8 @@ pub const Type = extern union {
                 };
                 return Type{ .ptr_otherwise = &new_payload.base };
             },
+            .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
+            .error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle),
         }
     }
 
@@ -657,6 +660,14 @@ pub const Type = extern union {
                     ty = payload.payload;
                     continue;
                 },
+                .error_set => {
+                    const payload = @fieldParentPtr(Payload.ErrorSet, "base", ty.ptr_otherwise);
+                    return out_stream.writeAll(std.mem.spanZ(payload.decl.name));
+                },
+                .error_set_single => {
+                    const payload = @fieldParentPtr(Payload.ErrorSetSingle, "base", ty.ptr_otherwise);
+                    return out_stream.print("error{{{}}}", .{payload.name});
+                },
             }
             unreachable;
         }
@@ -753,6 +764,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => true,
             // TODO lazy types
             .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
@@ -848,7 +861,11 @@ pub const Type = extern union {
             .f128 => return 16,
             .c_longdouble => return 16,
 
-            .anyerror_void_error_union, .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type
+            .error_set,
+            .error_set_single,
+            .anyerror_void_error_union,
+            .anyerror,
+            => return 2, // TODO revisit this when we have the concept of the error tag type
 
             .array, .array_sentinel => return self.elemType().abiAlignment(target),
 
@@ -981,7 +998,11 @@ pub const Type = extern union {
             .f128 => return 16,
             .c_longdouble => return 16,
 
-            .anyerror_void_error_union, .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type
+            .error_set,
+            .error_set_single,
+            .anyerror_void_error_union,
+            .anyerror,
+            => return 2, // TODO revisit this when we have the concept of the error tag type
 
             .int_signed, .int_unsigned => {
                 const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
@@ -1084,6 +1105,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => false,
 
             .single_const_pointer,
@@ -1156,6 +1179,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => false,
 
             .const_slice,
@@ -1225,6 +1250,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => false,
 
             .single_const_pointer,
@@ -1303,6 +1330,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => false,
 
             .pointer => {
@@ -1418,6 +1447,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => unreachable,
 
             .array => self.cast(Payload.Array).?.elem_type,
@@ -1543,6 +1574,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => unreachable,
 
             .array => self.cast(Payload.Array).?.len,
@@ -1614,6 +1647,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => unreachable,
 
             .array, .array_u8 => return null,
@@ -1683,6 +1718,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => false,
 
             .int_signed,
@@ -1755,6 +1792,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => false,
 
             .int_unsigned,
@@ -1817,6 +1856,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => unreachable,
 
             .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
@@ -1897,6 +1938,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => false,
 
             .usize,
@@ -2006,6 +2049,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => unreachable,
         };
     }
@@ -2081,6 +2126,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => unreachable,
         }
     }
@@ -2155,6 +2202,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => unreachable,
         }
     }
@@ -2229,6 +2278,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => unreachable,
         };
     }
@@ -2300,6 +2351,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => unreachable,
         };
     }
@@ -2371,6 +2424,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => unreachable,
         };
     }
@@ -2442,6 +2497,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => false,
         };
     }
@@ -2497,6 +2554,8 @@ pub const Type = extern union {
             .anyframe_T,
             .@"anyframe",
             .error_union,
+            .error_set,
+            .error_set_single,
             => return null,
 
             .void => return Value.initTag(.void_value),
@@ -2604,6 +2663,8 @@ pub const Type = extern union {
             .@"anyframe",
             .anyframe_T,
             .anyerror_void_error_union,
+            .error_set,
+            .error_set_single,
             => return false,
 
             .c_const_pointer,
@@ -2687,6 +2748,8 @@ pub const Type = extern union {
         optional_single_const_pointer,
         error_union,
         anyframe_T,
+        error_set,
+        error_set_single,
 
         pub const last_no_payload_tag = Tag.const_slice_u8;
         pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
@@ -2781,6 +2844,19 @@ pub const Type = extern union {
 
             return_type: Type,
         };
+
+        pub const ErrorSet = struct {
+            base: Payload = .{ .tag = .error_set },
+
+            decl: *Module.Decl,
+        };
+
+        pub const ErrorSetSingle = struct {
+            base: Payload = .{ .tag = .error_set_single },
+
+            /// memory is owned by `Module`
+            name: []const u8,
+        };
     };
 };
 
src-self-hosted/value.zig
@@ -381,11 +381,9 @@ pub const Value = extern union {
     }
 
     /// Asserts that the value is representable as a type.
-    pub fn toType(self: Value) Type {
+    pub fn toType(self: Value, allocator: *Allocator) !Type {
         return switch (self.tag()) {
             .ty => self.cast(Payload.Ty).?.ty,
-            .int_type => @panic("TODO int type to type"),
-
             .u8_type => Type.initTag(.u8),
             .i8_type => Type.initTag(.i8),
             .u16_type => Type.initTag(.u16),
@@ -427,7 +425,25 @@ pub const Value = extern union {
             .const_slice_u8_type => Type.initTag(.const_slice_u8),
             .enum_literal_type => Type.initTag(.enum_literal),
             .anyframe_type => Type.initTag(.@"anyframe"),
-            .error_set => @panic("TODO error set to type"),
+
+            .int_type => {
+                const payload = self.cast(Payload.IntType).?;
+                if (payload.signed) {
+                    const new = try allocator.create(Type.Payload.IntSigned);
+                    new.* = .{ .bits = payload.bits };
+                    return Type.initPayload(&new.base);
+                } else {
+                    const new = try allocator.create(Type.Payload.IntUnsigned);
+                    new.* = .{ .bits = payload.bits };
+                    return Type.initPayload(&new.base);
+                }
+            },
+            .error_set => {
+                const payload = self.cast(Payload.ErrorSet).?;
+                const new = try allocator.create(Type.Payload.ErrorSet);
+                new.* = .{ .decl = payload.decl };
+                return Type.initPayload(&new.base);
+            },
 
             .undef,
             .zero,
@@ -1579,6 +1595,7 @@ pub const Value = extern union {
 
             // TODO revisit this when we have the concept of the error tag type
             fields: std.StringHashMapUnmanaged(u16),
+            decl: *Module.Decl,
         };
 
         pub const Error = struct {
src-self-hosted/zir.zig
@@ -2016,7 +2016,7 @@ const EmitZIR = struct {
                 return self.emitUnnamedDecl(&as_inst.base);
             },
             .Type => {
-                const ty = typed_value.val.toType();
+                const ty = try typed_value.val.toType(&self.arena.allocator);
                 return self.emitType(src, ty);
             },
             .Fn => {
src-self-hosted/zir_sema.zig
@@ -150,7 +150,7 @@ pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir
     for (block_scope.instructions.items) |inst| {
         if (inst.castTag(.ret)) |ret| {
             const val = try mod.resolveConstValue(&block_scope.base, ret.operand);
-            return val.toType();
+            return val.toType(block_scope.base.arena());
         } else {
             return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
         }
@@ -275,7 +275,7 @@ fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
     const wanted_type = Type.initTag(.@"type");
     const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
     const val = try mod.resolveConstValue(scope, coerced_inst);
-    return val.toType();
+    return val.toType(scope.arena());
 }
 
 fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 {
@@ -745,7 +745,10 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In
     errdefer new_decl_arena.deinit();
 
     const payload = try scope.arena().create(Value.Payload.ErrorSet);
-    payload.* = .{ .fields = .{} };
+    payload.* = .{
+        .fields = .{},
+        .decl = undefined, // populated below
+    };
     try payload.fields.ensureCapacity(&new_decl_arena.allocator, inst.positionals.fields.len);
 
     for (inst.positionals.fields) |field_name| {
@@ -759,6 +762,7 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In
         .ty = Type.initTag(.type),
         .val = Value.initPayload(&payload.base),
     });
+    payload.decl = new_decl;
     return mod.analyzeDeclRef(scope, inst.base.src, new_decl);
 }
 
@@ -939,18 +943,17 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr
             _ = try mod.resolveConstValue(scope, object_ptr);
             const result = try mod.analyzeDeref(scope, fieldptr.base.src, object_ptr, object_ptr.src);
             const val = result.value().?;
-            const child_type = val.toType();
+            const child_type = try val.toType(scope.arena());
             switch (child_type.zigTypeTag()) {
                 .ErrorSet => {
                     // TODO resolve inferred error sets
                     const entry = if (val.cast(Value.Payload.ErrorSet)) |payload|
                         (payload.fields.getEntry(field_name) orelse
                             return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).*
-                    else
-                        try mod.getErrorValue(field_name);
+                    else try mod.getErrorValue(field_name);
 
                     const error_payload = try scope.arena().create(Value.Payload.Error);
-                    error_payload.* = .{ 
+                    error_payload.* = .{
                         .name = entry.key,
                         .value = entry.value,
                     };
@@ -958,9 +961,14 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr
                     const ref_payload = try scope.arena().create(Value.Payload.RefVal);
                     ref_payload.* = .{ .val = Value.initPayload(&error_payload.base) };
 
-                    // TODO if this is accessing the global error set create a `error{field_name}` type
+                    const result_type = if (child_type.tag() == .anyerror) blk: {
+                        const result_payload = try scope.arena().create(Type.Payload.ErrorSetSingle);
+                        result_payload.* = .{ .name = entry.key };
+                        break :blk Type.initPayload(&result_payload.base);
+                    } else child_type;
+
                     return mod.constInst(scope, fieldptr.base.src, .{
-                        .ty = try mod.simplePtrType(scope, fieldptr.base.src, child_type, false, .One),
+                        .ty = try mod.simplePtrType(scope, fieldptr.base.src, result_type, false, .One),
                         .val = Value.initPayload(&ref_payload.base),
                     });
                 },