From 74cea89fce2af41d2af7379ff526d7046c4d8d77 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Dec 2017 12:28:59 -0500 Subject: [PATCH 1/3] translate-c: fix not printing clang errors --- src/codegen.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index f84636502d..35974a73dd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5298,18 +5298,19 @@ void codegen_translate_c(CodeGen *g, Buf *full_path) { ZigList errors = {0}; int err = parse_h_file(import, &errors, buf_ptr(full_path), g, nullptr); - if (err) { - fprintf(stderr, "unable to parse C file: %s\n", err_str(err)); - exit(1); - } - if (errors.length > 0) { + if (err == ErrorCCompileErrors && errors.length > 0) { for (size_t i = 0; i < errors.length; i += 1) { ErrorMsg *err_msg = errors.at(i); print_err_msg(err_msg, g->err_color); } exit(1); } + + if (err) { + fprintf(stderr, "unable to parse C file: %s\n", err_str(err)); + exit(1); + } } static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package, const char *basename) { From 63a2f9a8b2251ffdc37d5f28dfbd3f6be1bd7908 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Dec 2017 18:09:22 -0500 Subject: [PATCH 2/3] fix casting integer literal to enum --- src/ir.cpp | 26 +++++++++++++++++++------- test/cases/enum.zig | 5 +++++ test/compile_errors.zig | 12 ++++++++++++ 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index facd7087f0..7c933ae950 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8436,14 +8436,16 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - BigInt enum_member_count; - bigint_init_unsigned(&enum_member_count, wanted_type->data.enumeration.src_field_count); - if (bigint_cmp(&val->data.x_bigint, &enum_member_count) != CmpLT) { + + TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); + if (field == nullptr) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); - ir_add_error(ira, source_instr, - buf_sprintf("integer value %s too big for enum '%s' which has %" PRIu32 " fields", - buf_ptr(val_buf), buf_ptr(&wanted_type->name), wanted_type->data.enumeration.src_field_count)); + ErrorMsg *msg = ir_add_error(ira, source_instr, + buf_sprintf("enum '%s' has no tag matching integer value %s", + buf_ptr(&wanted_type->name), buf_ptr(val_buf))); + add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node, + buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name))); return ira->codegen->invalid_instruction; } @@ -8827,7 +8829,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (actual_type->id == TypeTableEntryIdNumLitFloat || actual_type->id == TypeTableEntryIdNumLitInt) { - if (wanted_type->id == TypeTableEntryIdPointer && + if (wanted_type->id == TypeTableEntryIdEnum) { + IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value); + if (type_is_invalid(cast1->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + if (type_is_invalid(cast2->value.type)) + return ira->codegen->invalid_instruction; + + return cast2; + } else if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.is_const) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); diff --git a/test/cases/enum.zig b/test/cases/enum.zig index ec900511eb..d15614f9fd 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -344,3 +344,8 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) { MultipleChoice2.Unspecified5 => 9, }); } + +test "cast integer literal to enum" { + assert(MultipleChoice2(0) == MultipleChoice2.Unspecified1); + assert(MultipleChoice2(40) == MultipleChoice2.B); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 5d13ed8d48..162109f4d8 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2684,4 +2684,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:11:13: error: switch on union which has no attached enum", ".tmp_source.zig:1:17: note: consider 'union(enum)' here"); + + cases.add("enum in field count range but not matching tag", + \\const Foo = enum(u32) { + \\ A = 10, + \\ B = 11, + \\}; + \\export fn entry() { + \\ var x = Foo(0); + \\} + , + ".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0", + ".tmp_source.zig:1:13: note: 'Foo' declared here"); } From 960914a073c367883c9fdf54e900890a6aefc05f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Dec 2017 20:46:58 -0500 Subject: [PATCH 3/3] 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 --- src/analyze.cpp | 15 +++++--- src/codegen.cpp | 5 ++- src/ir.cpp | 79 +++++++++++++++++++++++++++++++++++++++++ test/cases/union.zig | 7 ++++ test/compile_errors.zig | 31 ++++++++++++++++ 5 files changed, 129 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); diff --git a/test/cases/union.zig b/test/cases/union.zig index 1db9a1bef1..cd7fad4c88 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -190,3 +190,10 @@ test "cast union to tag type of union" { fn testCastUnionToTagType(x: &const TheUnion) { assert(TheTag(*x) == TheTag.B); } + +test "cast tag type of union to union" { + var x: Value2 = Letter2.B; + assert(Letter2(x) == Letter2.B); +} +const Letter2 = enum { A, B, C }; +const Value2 = union(Letter2) { A: i32, B, C, }; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 162109f4d8..8dbb8171c2 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2696,4 +2696,35 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0", ".tmp_source.zig:1:13: note: 'Foo' declared here"); + + cases.add("comptime cast enum to union but field has payload", + \\const Letter = enum { A, B, C }; + \\const Value = union(Letter) { + \\ A: i32, + \\ B, + \\ C, + \\}; + \\export fn entry() { + \\ var x: Value = Letter.A; + \\} + , + ".tmp_source.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A'", + ".tmp_source.zig:3:5: note: field 'A' declared here"); + + cases.add("runtime cast to union which has non-void fields", + \\const Letter = enum { A, B, C }; + \\const Value = union(Letter) { + \\ A: i32, + \\ B, + \\ C, + \\}; + \\export fn entry() { + \\ foo(Letter.A); + \\} + \\fn foo(l: Letter) { + \\ var x: Value = l; + \\} + , + ".tmp_source.zig:11:20: error: runtime cast to union 'Value' which has non-void fields", + ".tmp_source.zig:3:5: note: field 'A' has type 'i32'"); }