aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2017-12-05 20:46:58 -0500
committerAndrew Kelley <superjoe30@gmail.com>2017-12-05 20:46:58 -0500
commit960914a073c367883c9fdf54e900890a6aefc05f (patch)
tree4dca5efa7967f0ecca20bd0d852005008a1ec925 /src
parent63a2f9a8b2251ffdc37d5f28dfbd3f6be1bd7908 (diff)
downloadzig-960914a073c367883c9fdf54e900890a6aefc05f.tar.gz
zig-960914a073c367883c9fdf54e900890a6aefc05f.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/analyze.cpp15
-rw-r--r--src/codegen.cpp5
-rw-r--r--src/ir.cpp79
3 files changed, 91 insertions, 8 deletions
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 96f0eb44db..61f7bedcbb 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2244,12 +2244,10 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node);
if (type_is_invalid(enum_type)) {
union_type->data.unionation.is_invalid = true;
- union_type->data.unionation.embedded_in_current = false;
return;
}
if (enum_type->id != TypeTableEntryIdEnum) {
union_type->data.unionation.is_invalid = true;
- union_type->data.unionation.embedded_in_current = false;
add_node_error(g, enum_type_node,
buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
return;
@@ -3319,7 +3317,7 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
assert(type_entry->id == TypeTableEntryIdUnion);
- assert(type_entry->data.unionation.complete);
+ assert(type_entry->data.unionation.zero_bits_known);
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
TypeUnionField *field = &type_entry->data.unionation.fields[i];
if (buf_eql_buf(field->enum_field->name, name)) {
@@ -3331,7 +3329,7 @@ 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.complete);
+ 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];
@@ -3888,7 +3886,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
return false;
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
- case TypeTableEntryIdUnion:
return type_has_bits(type_entry);
case TypeTableEntryIdErrorUnion:
return type_has_bits(type_entry->data.error.child_type);
@@ -3896,6 +3893,14 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
return type_has_bits(type_entry->data.maybe.child_type) &&
type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer &&
type_entry->data.maybe.child_type->id != TypeTableEntryIdFn;
+ case TypeTableEntryIdUnion:
+ assert(type_entry->data.unionation.complete);
+ if (type_entry->data.unionation.gen_field_count == 0)
+ return false;
+ if (!type_has_bits(type_entry))
+ return false;
+ return true;
+
}
zig_unreachable();
}
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 35974a73dd..9941238f8f 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -3946,8 +3946,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
case TypeTableEntryIdUnion:
{
LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
- ConstExprValue *payload_value = const_val->data.x_union.payload;
- assert(payload_value != nullptr);
if (type_entry->data.unionation.gen_field_count == 0) {
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) {
@@ -3960,7 +3958,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
LLVMValueRef union_value_ref;
bool make_unnamed_struct;
- if (!type_has_bits(payload_value->type)) {
+ ConstExprValue *payload_value = const_val->data.x_union.payload;
+ if (payload_value == nullptr || !type_has_bits(payload_value->type)) {
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX)
return LLVMGetUndef(type_entry->type_ref);
diff --git a/src/ir.cpp b/src/ir.cpp
index 7c933ae950..86fa8c3566 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -7468,6 +7468,17 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
}
}
+ // implicit enum to union which has the enum as the tag type
+ if (expected_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
+ (expected_type->data.unionation.decl_node->data.container_decl.auto_enum ||
+ expected_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
+ {
+ type_ensure_zero_bits_known(ira->codegen, expected_type);
+ if (expected_type->data.unionation.tag_type == actual_type) {
+ return ImplicitCastMatchResultYes;
+ }
+ }
+
// implicit undefined literal to anything
if (actual_type->id == TypeTableEntryIdUndefLit) {
return ImplicitCastMatchResultYes;
@@ -8370,6 +8381,63 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc
return result;
}
+static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr,
+ IrInstruction *target, TypeTableEntry *wanted_type)
+{
+ assert(wanted_type->id == TypeTableEntryIdUnion);
+ assert(target->value.type->id == TypeTableEntryIdEnum);
+
+ if (instr_is_comptime(target)) {
+ ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
+ if (!val)
+ return ira->codegen->invalid_instruction;
+ TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
+ assert(union_field != nullptr);
+ type_ensure_zero_bits_known(ira->codegen, union_field->type_entry);
+ if (!union_field->type_entry->zero_bits) {
+ AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(
+ union_field->enum_field->decl_index);
+ ErrorMsg *msg = ir_add_error(ira, source_instr,
+ buf_sprintf("cast to union '%s' must initialize '%s' field '%s'",
+ buf_ptr(&wanted_type->name),
+ buf_ptr(&union_field->type_entry->name),
+ buf_ptr(union_field->name)));
+ add_error_note(ira->codegen, msg, field_node,
+ buf_sprintf("field '%s' declared here", buf_ptr(union_field->name)));
+ return ira->codegen->invalid_instruction;
+ }
+ IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
+ source_instr->source_node, wanted_type);
+ result->value.special = ConstValSpecialStatic;
+ result->value.type = wanted_type;
+ bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag);
+ return result;
+ }
+
+ // if the union has all fields 0 bits, we can do it
+ // and in fact it's a noop cast because the union value is just the enum value
+ if (wanted_type->data.unionation.gen_field_count == 0) {
+ IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, wanted_type, target, CastOpNoop);
+ result->value.type = wanted_type;
+ return result;
+ }
+
+ ErrorMsg *msg = ir_add_error(ira, source_instr,
+ buf_sprintf("runtime cast to union '%s' which has non-void fields",
+ buf_ptr(&wanted_type->name)));
+ for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) {
+ TypeUnionField *union_field = &wanted_type->data.unionation.fields[i];
+ if (type_has_bits(union_field->type_entry)) {
+ AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i);
+ add_error_note(ira->codegen, msg, field_node,
+ buf_sprintf("field '%s' has type '%s'",
+ buf_ptr(union_field->name),
+ buf_ptr(&union_field->type_entry->name)));
+ }
+ }
+ return ira->codegen->invalid_instruction;
+}
+
static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
@@ -8919,6 +8987,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
}
}
+ // explicit enum to union which has the enum as the tag type
+ if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
+ (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
+ wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
+ {
+ type_ensure_zero_bits_known(ira->codegen, wanted_type);
+ if (wanted_type->data.unionation.tag_type == actual_type) {
+ return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
+ }
+ }
+
// explicit cast from undefined to anything
if (actual_type->id == TypeTableEntryIdUndefLit) {
return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);