Skip to content

Commit 960914a

Browse files
committedDec 6, 2017
add implicit cast from enum to union
when the enum is the tag type of the union and is comptime known to be of a void field of the union See #642
·
0.15.20.2.0
1 parent 63a2f9a commit 960914a

File tree

5 files changed

+129
-8
lines changed

5 files changed

+129
-8
lines changed
 

‎src/analyze.cpp‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2244,12 +2244,10 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
22442244
TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node);
22452245
if (type_is_invalid(enum_type)) {
22462246
union_type->data.unionation.is_invalid = true;
2247-
union_type->data.unionation.embedded_in_current = false;
22482247
return;
22492248
}
22502249
if (enum_type->id != TypeTableEntryIdEnum) {
22512250
union_type->data.unionation.is_invalid = true;
2252-
union_type->data.unionation.embedded_in_current = false;
22532251
add_node_error(g, enum_type_node,
22542252
buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
22552253
return;
@@ -3319,7 +3317,7 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
33193317

33203318
TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
33213319
assert(type_entry->id == TypeTableEntryIdUnion);
3322-
assert(type_entry->data.unionation.complete);
3320+
assert(type_entry->data.unionation.zero_bits_known);
33233321
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
33243322
TypeUnionField *field = &type_entry->data.unionation.fields[i];
33253323
if (buf_eql_buf(field->enum_field->name, name)) {
@@ -3331,7 +3329,7 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
33313329

33323330
TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) {
33333331
assert(type_entry->id == TypeTableEntryIdUnion);
3334-
assert(type_entry->data.unionation.complete);
3332+
assert(type_entry->data.unionation.zero_bits_known);
33353333
assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX);
33363334
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
33373335
TypeUnionField *field = &type_entry->data.unionation.fields[i];
@@ -3888,14 +3886,21 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
38883886
return false;
38893887
case TypeTableEntryIdArray:
38903888
case TypeTableEntryIdStruct:
3891-
case TypeTableEntryIdUnion:
38923889
return type_has_bits(type_entry);
38933890
case TypeTableEntryIdErrorUnion:
38943891
return type_has_bits(type_entry->data.error.child_type);
38953892
case TypeTableEntryIdMaybe:
38963893
return type_has_bits(type_entry->data.maybe.child_type) &&
38973894
type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer &&
38983895
type_entry->data.maybe.child_type->id != TypeTableEntryIdFn;
3896+
case TypeTableEntryIdUnion:
3897+
assert(type_entry->data.unionation.complete);
3898+
if (type_entry->data.unionation.gen_field_count == 0)
3899+
return false;
3900+
if (!type_has_bits(type_entry))
3901+
return false;
3902+
return true;
3903+
38993904
}
39003905
zig_unreachable();
39013906
}

‎src/codegen.cpp‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3946,8 +3946,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
39463946
case TypeTableEntryIdUnion:
39473947
{
39483948
LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
3949-
ConstExprValue *payload_value = const_val->data.x_union.payload;
3950-
assert(payload_value != nullptr);
39513949

39523950
if (type_entry->data.unionation.gen_field_count == 0) {
39533951
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) {
@@ -3960,7 +3958,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
39603958

39613959
LLVMValueRef union_value_ref;
39623960
bool make_unnamed_struct;
3963-
if (!type_has_bits(payload_value->type)) {
3961+
ConstExprValue *payload_value = const_val->data.x_union.payload;
3962+
if (payload_value == nullptr || !type_has_bits(payload_value->type)) {
39643963
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX)
39653964
return LLVMGetUndef(type_entry->type_ref);
39663965

‎src/ir.cpp‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7468,6 +7468,17 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
74687468
}
74697469
}
74707470

7471+
// implicit enum to union which has the enum as the tag type
7472+
if (expected_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
7473+
(expected_type->data.unionation.decl_node->data.container_decl.auto_enum ||
7474+
expected_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
7475+
{
7476+
type_ensure_zero_bits_known(ira->codegen, expected_type);
7477+
if (expected_type->data.unionation.tag_type == actual_type) {
7478+
return ImplicitCastMatchResultYes;
7479+
}
7480+
}
7481+
74717482
// implicit undefined literal to anything
74727483
if (actual_type->id == TypeTableEntryIdUndefLit) {
74737484
return ImplicitCastMatchResultYes;
@@ -8370,6 +8381,63 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc
83708381
return result;
83718382
}
83728383

8384+
static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr,
8385+
IrInstruction *target, TypeTableEntry *wanted_type)
8386+
{
8387+
assert(wanted_type->id == TypeTableEntryIdUnion);
8388+
assert(target->value.type->id == TypeTableEntryIdEnum);
8389+
8390+
if (instr_is_comptime(target)) {
8391+
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
8392+
if (!val)
8393+
return ira->codegen->invalid_instruction;
8394+
TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
8395+
assert(union_field != nullptr);
8396+
type_ensure_zero_bits_known(ira->codegen, union_field->type_entry);
8397+
if (!union_field->type_entry->zero_bits) {
8398+
AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(
8399+
union_field->enum_field->decl_index);
8400+
ErrorMsg *msg = ir_add_error(ira, source_instr,
8401+
buf_sprintf("cast to union '%s' must initialize '%s' field '%s'",
8402+
buf_ptr(&wanted_type->name),
8403+
buf_ptr(&union_field->type_entry->name),
8404+
buf_ptr(union_field->name)));
8405+
add_error_note(ira->codegen, msg, field_node,
8406+
buf_sprintf("field '%s' declared here", buf_ptr(union_field->name)));
8407+
return ira->codegen->invalid_instruction;
8408+
}
8409+
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
8410+
source_instr->source_node, wanted_type);
8411+
result->value.special = ConstValSpecialStatic;
8412+
result->value.type = wanted_type;
8413+
bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag);
8414+
return result;
8415+
}
8416+
8417+
// if the union has all fields 0 bits, we can do it
8418+
// and in fact it's a noop cast because the union value is just the enum value
8419+
if (wanted_type->data.unionation.gen_field_count == 0) {
8420+
IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, wanted_type, target, CastOpNoop);
8421+
result->value.type = wanted_type;
8422+
return result;
8423+
}
8424+
8425+
ErrorMsg *msg = ir_add_error(ira, source_instr,
8426+
buf_sprintf("runtime cast to union '%s' which has non-void fields",
8427+
buf_ptr(&wanted_type->name)));
8428+
for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) {
8429+
TypeUnionField *union_field = &wanted_type->data.unionation.fields[i];
8430+
if (type_has_bits(union_field->type_entry)) {
8431+
AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i);
8432+
add_error_note(ira->codegen, msg, field_node,
8433+
buf_sprintf("field '%s' has type '%s'",
8434+
buf_ptr(union_field->name),
8435+
buf_ptr(&union_field->type_entry->name)));
8436+
}
8437+
}
8438+
return ira->codegen->invalid_instruction;
8439+
}
8440+
83738441
static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr,
83748442
IrInstruction *target, TypeTableEntry *wanted_type)
83758443
{
@@ -8919,6 +8987,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
89198987
}
89208988
}
89218989

8990+
// explicit enum to union which has the enum as the tag type
8991+
if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
8992+
(wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
8993+
wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
8994+
{
8995+
type_ensure_zero_bits_known(ira->codegen, wanted_type);
8996+
if (wanted_type->data.unionation.tag_type == actual_type) {
8997+
return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
8998+
}
8999+
}
9000+
89229001
// explicit cast from undefined to anything
89239002
if (actual_type->id == TypeTableEntryIdUndefLit) {
89249003
return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);

‎test/cases/union.zig‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,10 @@ test "cast union to tag type of union" {
190190
fn testCastUnionToTagType(x: &const TheUnion) {
191191
assert(TheTag(*x) == TheTag.B);
192192
}
193+
194+
test "cast tag type of union to union" {
195+
var x: Value2 = Letter2.B;
196+
assert(Letter2(x) == Letter2.B);
197+
}
198+
const Letter2 = enum { A, B, C };
199+
const Value2 = union(Letter2) { A: i32, B, C, };

‎test/compile_errors.zig‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2696,4 +2696,35 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
26962696
,
26972697
".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0",
26982698
".tmp_source.zig:1:13: note: 'Foo' declared here");
2699+
2700+
cases.add("comptime cast enum to union but field has payload",
2701+
\\const Letter = enum { A, B, C };
2702+
\\const Value = union(Letter) {
2703+
\\ A: i32,
2704+
\\ B,
2705+
\\ C,
2706+
\\};
2707+
\\export fn entry() {
2708+
\\ var x: Value = Letter.A;
2709+
\\}
2710+
,
2711+
".tmp_source.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A'",
2712+
".tmp_source.zig:3:5: note: field 'A' declared here");
2713+
2714+
cases.add("runtime cast to union which has non-void fields",
2715+
\\const Letter = enum { A, B, C };
2716+
\\const Value = union(Letter) {
2717+
\\ A: i32,
2718+
\\ B,
2719+
\\ C,
2720+
\\};
2721+
\\export fn entry() {
2722+
\\ foo(Letter.A);
2723+
\\}
2724+
\\fn foo(l: Letter) {
2725+
\\ var x: Value = l;
2726+
\\}
2727+
,
2728+
".tmp_source.zig:11:20: error: runtime cast to union 'Value' which has non-void fields",
2729+
".tmp_source.zig:3:5: note: field 'A' has type 'i32'");
26992730
}

0 commit comments

Comments
 (0)
Please sign in to comment.