Commit 9f4649b197

kcbanner <kcbanner@gmail.com>
2023-09-23 20:33:31
codegen/sema: handle unions with unknown tags in more places
1 parent 4e9f5f2
src/codegen.zig
@@ -583,24 +583,33 @@ pub fn generateSymbol(
             }
 
             const union_obj = mod.typeToUnion(typed_value.ty).?;
-            const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
+            if (un.tag != .none) {
+                const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
+                const field_ty = union_obj.field_types.get(ip)[field_index].toType();
+                if (!field_ty.hasRuntimeBits(mod)) {
+                    try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
+                } else {
+                    switch (try generateSymbol(bin_file, src_loc, .{
+                        .ty = field_ty,
+                        .val = un.val.toValue(),
+                    }, code, debug_output, reloc_info)) {
+                        .ok => {},
+                        .fail => |em| return Result{ .fail = em },
+                    }
 
-            const field_ty = union_obj.field_types.get(ip)[field_index].toType();
-            if (!field_ty.hasRuntimeBits(mod)) {
-                try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
+                    const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(mod)) orelse return error.Overflow;
+                    if (padding > 0) {
+                        try code.appendNTimes(0, padding);
+                    }
+                }
             } else {
                 switch (try generateSymbol(bin_file, src_loc, .{
-                    .ty = field_ty,
+                    .ty = ip.typeOf(un.val).toType(),
                     .val = un.val.toValue(),
                 }, code, debug_output, reloc_info)) {
                     .ok => {},
                     .fail => |em| return Result{ .fail = em },
                 }
-
-                const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(mod)) orelse return error.Overflow;
-                if (padding > 0) {
-                    try code.appendNTimes(0, padding);
-                }
             }
 
             if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {
src/Sema.zig
@@ -32879,7 +32879,7 @@ fn unionToTag(
         return Air.internedToRef(opv.toIntern());
     }
     if (try sema.resolveMaybeUndefVal(un)) |un_val| {
-        return Air.internedToRef(un_val.unionTag(mod).toIntern());
+        return Air.internedToRef(un_val.unionTag(mod).?.toIntern());
     }
     try sema.requireRuntimeBlock(block, un_src, null);
     return block.addTyOp(.get_union_tag, enum_ty, un);
src/TypedValue.zig
@@ -87,18 +87,19 @@ pub fn print(
                 const union_val = val.castTag(.@"union").?.data;
                 try writer.writeAll(".{ ");
 
-                try print(.{
-                    .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(),
-                    .val = union_val.tag,
-                }, writer, level - 1, mod);
-                try writer.writeAll(" = ");
-                if (ty.unionFieldType(union_val.tag, mod)) |field_ty| {
+                if (union_val.tag.toIntern() != .none) {
+                    try print(.{
+                        .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(),
+                        .val = union_val.tag,
+                    }, writer, level - 1, mod);
+                    try writer.writeAll(" = ");
+                    const field_ty = ty.unionFieldType(union_val.tag, mod).?;
                     try print(.{
                         .ty = field_ty,
                         .val = union_val.val,
                     }, writer, level - 1, mod);
                 } else {
-                    return writer.writeAll("(no tag)");
+                    return writer.writeAll("(unknown tag)");
                 }
 
                 return writer.writeAll(" }");
@@ -408,18 +409,19 @@ pub fn print(
             .un => |un| {
                 try writer.writeAll(".{ ");
                 if (level > 0) {
-                    try print(.{
-                        .ty = ty.unionTagTypeHypothetical(mod),
-                        .val = un.tag.toValue(),
-                    }, writer, level - 1, mod);
-                    try writer.writeAll(" = ");
-                    if (ty.unionFieldType(un.tag.toValue(), mod)) |field_ty| {
+                    if (un.tag != .none) {
+                        try print(.{
+                            .ty = ty.unionTagTypeHypothetical(mod),
+                            .val = un.tag.toValue(),
+                        }, writer, level - 1, mod);
+                        try writer.writeAll(" = ");
+                        const field_ty = ty.unionFieldType(un.tag.toValue(), mod).?;
                         try print(.{
                             .ty = field_ty,
                             .val = un.val.toValue(),
                         }, writer, level - 1, mod);
                     } else {
-                        try writer.writeAll("(no tag)");
+                        try writer.writeAll("(unknown tag)");
                     }
                 } else try writer.writeAll("...");
                 return writer.writeAll(" }");
src/value.zig
@@ -706,8 +706,8 @@ pub const Value = struct {
                 .Auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already
                 .Extern => {
                     const union_obj = mod.typeToUnion(ty).?;
-                    const union_tag = val.unionTag(mod);
-                    if (mod.unionTagFieldIndex(union_obj, union_tag)) |field_index| {
+                    if (val.unionTag(mod)) |union_tag| {
+                        const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
                         const field_type = union_obj.field_types.get(&mod.intern_pool)[field_index].toType();
                         const field_val = try val.fieldValue(mod, field_index);
                         const byte_count = @as(usize, @intCast(field_type.abiSize(mod)));
@@ -715,7 +715,7 @@ pub const Value = struct {
                     } else {
                         const union_size = ty.abiSize(mod);
                         const array_type = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
-                        return writeToMemory(val.unionValue(mod), array_type, mod, buffer[0..union_size]);
+                        return writeToMemory(val.unionValue(mod), array_type, mod, buffer[0..@as(usize, @intCast(union_size))]);
                     }
                 },
                 .Packed => {
@@ -832,10 +832,16 @@ pub const Value = struct {
                 switch (union_obj.getLayout(ip)) {
                     .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory
                     .Packed => {
-                        const field_index = mod.unionTagFieldIndex(union_obj, val.unionTag(mod)).?;
-                        const field_type = union_obj.field_types.get(ip)[field_index].toType();
-                        const field_val = try val.fieldValue(mod, field_index);
-                        return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
+                        if (val.unionTag(mod)) |union_tag| {
+                            const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
+                            const field_type = union_obj.field_types.get(ip)[field_index].toType();
+                            const field_val = try val.fieldValue(mod, field_index);
+                            return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
+                        } else {
+                            const union_bits: u16 = @intCast(ty.bitSize(mod));
+                            const int_ty = try mod.intType(.unsigned, union_bits);
+                            return val.unionValue(mod).writeToPackedMemory(int_ty, mod, buffer, bit_offset);
+                        }
                     },
                 }
             },
@@ -1137,7 +1143,6 @@ pub const Value = struct {
                 .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory
                 .Packed => {
                     const union_bits: u16 = @intCast(ty.bitSize(mod));
-                    // TODO: Remove after tests pass
                     assert(union_bits != 0);
                     const int_ty = try mod.intType(.unsigned, union_bits);
                     const val = (try readFromPackedMemory(int_ty, mod, buffer, bit_offset, arena)).toIntern();
@@ -1754,11 +1759,11 @@ pub const Value = struct {
         };
     }
 
-    pub fn unionTag(val: Value, mod: *Module) Value {
+    pub fn unionTag(val: Value, mod: *Module) ?Value {
         if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag;
         return switch (mod.intern_pool.indexToKey(val.toIntern())) {
             .undef, .enum_tag => val,
-            .un => |un| un.tag.toValue(),
+            .un => |un| if (un.tag != .none) un.tag.toValue() else return null,
             else => unreachable,
         };
     }