Commit cacba6f435

Andrew Kelley <superjoe30@gmail.com>
2018-01-22 23:23:23
fix crash on union-enums with only 1 field
closes #713
1 parent b52bffc
Changed files (3)
src/analyze.cpp
@@ -1867,7 +1867,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
     uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits;
 
     TypeTableEntry *tag_type = union_type->data.unionation.tag_type;
-    if (tag_type == nullptr) {
+    if (tag_type == nullptr || tag_type->zero_bits) {
         assert(most_aligned_union_member != nullptr);
 
         if (padding_in_bits > 0) {
@@ -2509,8 +2509,10 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
 
     if (create_enum_type) {
         ImportTableEntry *import = get_scope_import(scope);
-        uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref);
-        uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref);
+        uint64_t tag_debug_size_in_bits = tag_type->zero_bits ? 0 :
+            8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref);
+        uint64_t tag_debug_align_in_bits = tag_type->zero_bits ? 0 :
+            8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref);
         // TODO get a more accurate debug scope
         ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
                 ZigLLVMFileToScope(import->di_file), buf_ptr(&tag_type->name),
@@ -3451,7 +3453,6 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
 TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) {
     assert(type_entry->id == TypeTableEntryIdUnion);
     assert(type_entry->data.unionation.zero_bits_known);
-    assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX);
     for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
         TypeUnionField *field = &type_entry->data.unionation.fields[i];
         if (bigint_cmp(&field->enum_field->value, tag) == CmpEQ) {
src/ir.cpp
@@ -4769,7 +4769,8 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *
 }
 
 static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node,
-        IrBasicBlock *end_block, IrInstruction *is_comptime, IrInstruction *target_value_ptr, IrInstruction *prong_value,
+        IrBasicBlock *end_block, IrInstruction *is_comptime, IrInstruction *var_is_comptime,
+        IrInstruction *target_value_ptr, IrInstruction *prong_value,
         ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values)
 {
     assert(switch_node->type == NodeTypeSwitchExpr);
@@ -4786,7 +4787,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit
         bool is_shadowable = false;
         bool is_const = true;
         VariableTableEntry *var = ir_create_var(irb, var_symbol_node, scope,
-                var_name, is_const, is_const, is_shadowable, is_comptime);
+                var_name, is_const, is_const, is_shadowable, var_is_comptime);
         child_scope = var->child_scope;
         IrInstruction *var_value;
         if (prong_value) {
@@ -4827,10 +4828,13 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
     ZigList<IrInstructionSwitchBrCase> cases = {0};
 
     IrInstruction *is_comptime;
+    IrInstruction *var_is_comptime;
     if (ir_should_inline(irb->exec, scope)) {
         is_comptime = ir_build_const_bool(irb, scope, node, true);
+        var_is_comptime = is_comptime;
     } else {
         is_comptime = ir_build_test_comptime(irb, scope, node, target_value);
+        var_is_comptime = ir_build_test_comptime(irb, scope, node, target_value_ptr);
     }
 
     ZigList<IrInstruction *> incoming_values = {0};
@@ -4856,7 +4860,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
             IrBasicBlock *prev_block = irb->current_basic_block;
             ir_set_cursor_at_end_and_append_block(irb, else_block);
             if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block,
-                is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values))
+                is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values))
             {
                 return irb->codegen->invalid_instruction;
             }
@@ -4923,7 +4927,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
 
             ir_set_cursor_at_end_and_append_block(irb, range_block_yes);
             if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block,
-                is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values))
+                is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values))
             {
                 return irb->codegen->invalid_instruction;
             }
@@ -4967,7 +4971,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
         IrBasicBlock *prev_block = irb->current_basic_block;
         ir_set_cursor_at_end_and_append_block(irb, prong_block);
         if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block,
-            is_comptime, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values))
+            is_comptime, var_is_comptime, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values))
         {
             return irb->codegen->invalid_instruction;
         }
@@ -12254,11 +12258,18 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
             }
             TypeTableEntry *tag_type = target_type->data.unionation.tag_type;
             assert(tag_type != nullptr);
+            assert(tag_type->id == TypeTableEntryIdEnum);
             if (pointee_val) {
                 ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);
                 bigint_init_bigint(&out_val->data.x_enum_tag, &pointee_val->data.x_union.tag);
                 return tag_type;
             }
+            if (tag_type->data.enumeration.src_field_count == 1) {
+                ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);
+                TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];
+                bigint_init_bigint(&out_val->data.x_enum_tag, &only_field->value);
+                return tag_type;
+            }
 
             IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope,
                 switch_target_instruction->base.source_node, target_value_ptr);
test/cases/union.zig
@@ -235,3 +235,30 @@ test "constant packed union" {
 fn testConstPackedUnion(expected_tokens: []const PackThis) {
     assert(expected_tokens[0].StringLiteral == 1);
 }
+
+test "switch on union with only 1 field" {
+    var r: PartialInst = undefined;
+    r = PartialInst.Compiled;
+    switch (r) {
+        PartialInst.Compiled => {
+            var z: PartialInstWithPayload = undefined;
+            z = PartialInstWithPayload { .Compiled = 1234 };
+            switch (z) {
+                PartialInstWithPayload.Compiled => |x| {
+                    assert(x == 1234);
+                    return;
+                },
+            }
+        },
+    }
+    unreachable;
+}
+
+const PartialInst = union(enum) {
+    Compiled,
+};
+
+const PartialInstWithPayload = union(enum) {
+    Compiled: i32,
+};
+