Commit 7b97f6792f

Robin Voetter <robin@voetter.nl>
2021-10-19 23:19:56
stage2: add Value.the_only_possible_value
1 parent b65582e
Changed files (1)
src/value.zig
@@ -86,6 +86,8 @@ pub const Value = extern union {
         one,
         void_value,
         unreachable_value,
+        /// The only possible value for a particular type, which is stored externally.
+        the_only_possible_value,
         null_value,
         bool_true,
         bool_false,
@@ -226,6 +228,7 @@ pub const Value = extern union {
                 .one,
                 .void_value,
                 .unreachable_value,
+                .the_only_possible_value,
                 .empty_struct_value,
                 .empty_array,
                 .null_value,
@@ -415,6 +418,7 @@ pub const Value = extern union {
             .one,
             .void_value,
             .unreachable_value,
+            .the_only_possible_value,
             .empty_array,
             .null_value,
             .bool_true,
@@ -664,6 +668,7 @@ pub const Value = extern union {
             .one => return out_stream.writeAll("1"),
             .void_value => return out_stream.writeAll("{}"),
             .unreachable_value => return out_stream.writeAll("unreachable"),
+            .the_only_possible_value => return out_stream.writeAll("(the only possible value)"),
             .bool_true => return out_stream.writeAll("true"),
             .bool_false => return out_stream.writeAll("false"),
             .ty => return val.castTag(.ty).?.data.format("", options, out_stream),
@@ -755,6 +760,7 @@ pub const Value = extern union {
                 const decl_val = try decl.value();
                 return decl_val.toAllocatedBytes(decl.ty, allocator);
             },
+            .the_only_possible_value => return &[_]u8{},
             else => unreachable,
         }
     }
@@ -847,53 +853,63 @@ pub const Value = extern union {
                 // TODO should `@intToEnum` do this `@intCast` for you?
                 return @intToEnum(E, @intCast(@typeInfo(E).Enum.tag_type, field_index));
             },
+            .the_only_possible_value => {
+                const fields = std.meta.fields(E);
+                assert(fields.len == 1);
+                return @intToEnum(E, fields[0].value);
+            },
             else => unreachable,
         }
     }
 
     pub fn enumToInt(val: Value, ty: Type, buffer: *Payload.U64) Value {
-        if (val.castTag(.enum_field_index)) |enum_field_payload| {
-            const field_index = enum_field_payload.data;
-            switch (ty.tag()) {
-                .enum_full, .enum_nonexhaustive => {
-                    const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
-                    if (enum_full.values.count() != 0) {
-                        return enum_full.values.keys()[field_index];
-                    } else {
-                        // Field index and integer values are the same.
-                        buffer.* = .{
-                            .base = .{ .tag = .int_u64 },
-                            .data = field_index,
-                        };
-                        return Value.initPayload(&buffer.base);
-                    }
-                },
-                .enum_numbered => {
-                    const enum_obj = ty.castTag(.enum_numbered).?.data;
-                    if (enum_obj.values.count() != 0) {
-                        return enum_obj.values.keys()[field_index];
-                    } else {
-                        // Field index and integer values are the same.
-                        buffer.* = .{
-                            .base = .{ .tag = .int_u64 },
-                            .data = field_index,
-                        };
-                        return Value.initPayload(&buffer.base);
-                    }
-                },
-                .enum_simple => {
+        const field_index = switch (val.tag()) {
+            .enum_field_index => val.castTag(.enum_field_index).?.data,
+            .the_only_possible_value => blk: {
+                assert(ty.enumFieldCount() == 1);
+                break :blk 0;
+            },
+            // Assume it is already an integer and return it directly.
+            else => return val,
+        };
+
+        switch (ty.tag()) {
+            .enum_full, .enum_nonexhaustive => {
+                const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
+                if (enum_full.values.count() != 0) {
+                    return enum_full.values.keys()[field_index];
+                } else {
                     // Field index and integer values are the same.
                     buffer.* = .{
                         .base = .{ .tag = .int_u64 },
                         .data = field_index,
                     };
                     return Value.initPayload(&buffer.base);
-                },
-                else => unreachable,
-            }
+                }
+            },
+            .enum_numbered => {
+                const enum_obj = ty.castTag(.enum_numbered).?.data;
+                if (enum_obj.values.count() != 0) {
+                    return enum_obj.values.keys()[field_index];
+                } else {
+                    // Field index and integer values are the same.
+                    buffer.* = .{
+                        .base = .{ .tag = .int_u64 },
+                        .data = field_index,
+                    };
+                    return Value.initPayload(&buffer.base);
+                }
+            },
+            .enum_simple => {
+                // Field index and integer values are the same.
+                buffer.* = .{
+                    .base = .{ .tag = .int_u64 },
+                    .data = field_index,
+                };
+                return Value.initPayload(&buffer.base);
+            },
+            else => unreachable,
         }
-        // Assume it is already an integer and return it directly.
-        return val;
     }
 
     /// Asserts the value is an integer.
@@ -901,6 +917,7 @@ pub const Value = extern union {
         switch (self.tag()) {
             .zero,
             .bool_false,
+            .the_only_possible_value, // i0, u0
             => return BigIntMutable.init(&space.limbs, 0).toConst(),
 
             .one,
@@ -922,6 +939,7 @@ pub const Value = extern union {
         switch (self.tag()) {
             .zero,
             .bool_false,
+            .the_only_possible_value, // i0, u0
             => return 0,
 
             .one,
@@ -943,6 +961,7 @@ pub const Value = extern union {
         switch (self.tag()) {
             .zero,
             .bool_false,
+            .the_only_possible_value, // i0, u0
             => return 0,
 
             .one,
@@ -1124,6 +1143,11 @@ pub const Value = extern union {
                 @panic("TODO implement int_big_negative Value clz");
             },
 
+            .the_only_possible_value => {
+                assert(ty_bits == 0);
+                return ty_bits;
+            },
+
             else => unreachable,
         }
     }
@@ -1134,6 +1158,7 @@ pub const Value = extern union {
         switch (self.tag()) {
             .zero,
             .bool_false,
+            .the_only_possible_value,
             => return 0,
 
             .one,
@@ -1213,6 +1238,11 @@ pub const Value = extern union {
                 else => unreachable,
             },
 
+            .the_only_possible_value => {
+                assert(ty.intInfo(target).bits == 0);
+                return true;
+            },
+
             else => unreachable,
         }
     }
@@ -1251,7 +1281,7 @@ pub const Value = extern union {
     /// Asserts the value is numeric
     pub fn isZero(self: Value) bool {
         return switch (self.tag()) {
-            .zero => true,
+            .zero, .the_only_possible_value => true,
             .one => false,
 
             .int_u64 => self.castTag(.int_u64).?.data == 0,
@@ -1272,6 +1302,7 @@ pub const Value = extern union {
         return switch (lhs.tag()) {
             .zero,
             .bool_false,
+            .the_only_possible_value,
             => .eq,
 
             .one,
@@ -1354,7 +1385,7 @@ pub const Value = extern union {
         assert(b_tag != .undef);
         if (a_tag == b_tag) {
             switch (a_tag) {
-                .void_value, .null_value => return true,
+                .void_value, .null_value, .the_only_possible_value => return true,
                 .enum_literal => {
                     const a_name = a.castTag(.enum_literal).?.data;
                     const b_name = b.castTag(.enum_literal).?.data;
@@ -1706,6 +1737,9 @@ pub const Value = extern union {
             .decl_ref => return val.castTag(.decl_ref).?.data.val.elemValueAdvanced(index, arena, buffer),
             .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.val.elemValueAdvanced(index, arena, buffer),
 
+            // The child type of arrays which have only one possible value need to have only one possible value itself.
+            .the_only_possible_value => return val,
+
             else => unreachable,
         }
     }
@@ -1722,6 +1756,8 @@ pub const Value = extern union {
                 // TODO assert the tag is correct
                 return payload.val;
             },
+            // Structs which have only one possible value need to consist of members which have only one possible value.
+            .the_only_possible_value => return val,
 
             else => unreachable,
         }
@@ -1820,6 +1856,7 @@ pub const Value = extern union {
     pub fn intToFloat(val: Value, allocator: *Allocator, dest_ty: Type, target: Target) !Value {
         switch (val.tag()) {
             .undef, .zero, .one => return val,
+            .the_only_possible_value => return Value.initTag(.zero), // for i0, u0
             .int_u64 => {
                 return intToFloatInner(val.castTag(.int_u64).?.data, allocator, dest_ty, target);
             },