| author | |
| committer | |
| log | 6018dbd3391d1e71eef650aff700eab53074c308 |
| tree | 4d3bd0c5bb0a55a7ad144270eed99d90855efd45 |
| parent | 08d531143f0b373cbc54e037fa526fb00d9db398 |
| parent | 960914a073c367883c9fdf54e900890a6aefc05f |
6 files changed, 171 insertions(+), 20 deletions(-)
src/analyze.cpp+10-5| ... | ... | @@ -2244,12 +2244,10 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { |
| 2244 | 2244 | TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node); |
| 2245 | 2245 | if (type_is_invalid(enum_type)) { |
| 2246 | 2246 | union_type->data.unionation.is_invalid = true; |
| 2247 | union_type->data.unionation.embedded_in_current = false; | |
| 2248 | 2247 | return; |
| 2249 | 2248 | } |
| 2250 | 2249 | if (enum_type->id != TypeTableEntryIdEnum) { |
| 2251 | 2250 | union_type->data.unionation.is_invalid = true; |
| 2252 | union_type->data.unionation.embedded_in_current = false; | |
| 2253 | 2251 | add_node_error(g, enum_type_node, |
| 2254 | 2252 | buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); |
| 2255 | 2253 | return; |
| ... | ... | @@ -3319,7 +3317,7 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { |
| 3319 | 3317 | |
| 3320 | 3318 | TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { |
| 3321 | 3319 | assert(type_entry->id == TypeTableEntryIdUnion); |
| 3322 | assert(type_entry->data.unionation.complete); | |
| 3320 | assert(type_entry->data.unionation.zero_bits_known); | |
| 3323 | 3321 | for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { |
| 3324 | 3322 | TypeUnionField *field = &type_entry->data.unionation.fields[i]; |
| 3325 | 3323 | if (buf_eql_buf(field->enum_field->name, name)) { |
| ... | ... | @@ -3331,7 +3329,7 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { |
| 3331 | 3329 | |
| 3332 | 3330 | TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) { |
| 3333 | 3331 | assert(type_entry->id == TypeTableEntryIdUnion); |
| 3334 | assert(type_entry->data.unionation.complete); | |
| 3332 | assert(type_entry->data.unionation.zero_bits_known); | |
| 3335 | 3333 | assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX); |
| 3336 | 3334 | for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { |
| 3337 | 3335 | TypeUnionField *field = &type_entry->data.unionation.fields[i]; |
| ... | ... | @@ -3888,7 +3886,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 3888 | 3886 | return false; |
| 3889 | 3887 | case TypeTableEntryIdArray: |
| 3890 | 3888 | case TypeTableEntryIdStruct: |
| 3891 | case TypeTableEntryIdUnion: | |
| 3892 | 3889 | return type_has_bits(type_entry); |
| 3893 | 3890 | case TypeTableEntryIdErrorUnion: |
| 3894 | 3891 | return type_has_bits(type_entry->data.error.child_type); |
| ... | ... | @@ -3896,6 +3893,14 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 3896 | 3893 | return type_has_bits(type_entry->data.maybe.child_type) && |
| 3897 | 3894 | type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && |
| 3898 | 3895 | 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 | ||
| 3899 | 3904 | } |
| 3900 | 3905 | zig_unreachable(); |
| 3901 | 3906 | } |
src/codegen.cpp+8-8| ... | ... | @@ -3946,8 +3946,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { |
| 3946 | 3946 | case TypeTableEntryIdUnion: |
| 3947 | 3947 | { |
| 3948 | 3948 | 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); | |
| 3951 | 3949 | |
| 3952 | 3950 | if (type_entry->data.unionation.gen_field_count == 0) { |
| 3953 | 3951 | if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { |
| ... | ... | @@ -3960,7 +3958,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { |
| 3960 | 3958 | |
| 3961 | 3959 | LLVMValueRef union_value_ref; |
| 3962 | 3960 | 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)) { | |
| 3964 | 3963 | if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) |
| 3965 | 3964 | return LLVMGetUndef(type_entry->type_ref); |
| 3966 | 3965 | |
| ... | ... | @@ -5298,18 +5297,19 @@ void codegen_translate_c(CodeGen *g, Buf *full_path) { |
| 5298 | 5297 | |
| 5299 | 5298 | ZigList<ErrorMsg *> errors = {0}; |
| 5300 | 5299 | int err = parse_h_file(import, &errors, buf_ptr(full_path), g, nullptr); |
| 5301 | if (err) { | |
| 5302 | fprintf(stderr, "unable to parse C file: %s\n", err_str(err)); | |
| 5303 | exit(1); | |
| 5304 | } | |
| 5305 | 5300 | |
| 5306 | if (errors.length > 0) { | |
| 5301 | if (err == ErrorCCompileErrors && errors.length > 0) { | |
| 5307 | 5302 | for (size_t i = 0; i < errors.length; i += 1) { |
| 5308 | 5303 | ErrorMsg *err_msg = errors.at(i); |
| 5309 | 5304 | print_err_msg(err_msg, g->err_color); |
| 5310 | 5305 | } |
| 5311 | 5306 | exit(1); |
| 5312 | 5307 | } |
| 5308 | ||
| 5309 | if (err) { | |
| 5310 | fprintf(stderr, "unable to parse C file: %s\n", err_str(err)); | |
| 5311 | exit(1); | |
| 5312 | } | |
| 5313 | 5313 | } |
| 5314 | 5314 | |
| 5315 | 5315 | static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package, const char *basename) { |
src/ir.cpp+98-7| ... | ... | @@ -7468,6 +7468,17 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, |
| 7468 | 7468 | } |
| 7469 | 7469 | } |
| 7470 | 7470 | |
| 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 | ||
| 7471 | 7482 | // implicit undefined literal to anything |
| 7472 | 7483 | if (actual_type->id == TypeTableEntryIdUndefLit) { |
| 7473 | 7484 | return ImplicitCastMatchResultYes; |
| ... | ... | @@ -8370,6 +8381,63 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc |
| 8370 | 8381 | return result; |
| 8371 | 8382 | } |
| 8372 | 8383 | |
| 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 | ||
| 8373 | 8441 | static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr, |
| 8374 | 8442 | IrInstruction *target, TypeTableEntry *wanted_type) |
| 8375 | 8443 | { |
| ... | ... | @@ -8426,14 +8494,16 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour |
| 8426 | 8494 | ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); |
| 8427 | 8495 | if (!val) |
| 8428 | 8496 | return ira->codegen->invalid_instruction; |
| 8429 | BigInt enum_member_count; | |
| 8430 | bigint_init_unsigned(&enum_member_count, wanted_type->data.enumeration.src_field_count); | |
| 8431 | if (bigint_cmp(&val->data.x_bigint, &enum_member_count) != CmpLT) { | |
| 8497 | ||
| 8498 | TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); | |
| 8499 | if (field == nullptr) { | |
| 8432 | 8500 | Buf *val_buf = buf_alloc(); |
| 8433 | 8501 | bigint_append_buf(val_buf, &val->data.x_bigint, 10); |
| 8434 | ir_add_error(ira, source_instr, | |
| 8435 | buf_sprintf("integer value %s too big for enum '%s' which has %" PRIu32 " fields", | |
| 8436 | buf_ptr(val_buf), buf_ptr(&wanted_type->name), wanted_type->data.enumeration.src_field_count)); | |
| 8502 | ErrorMsg *msg = ir_add_error(ira, source_instr, | |
| 8503 | buf_sprintf("enum '%s' has no tag matching integer value %s", | |
| 8504 | buf_ptr(&wanted_type->name), buf_ptr(val_buf))); | |
| 8505 | add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node, | |
| 8506 | buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name))); | |
| 8437 | 8507 | return ira->codegen->invalid_instruction; |
| 8438 | 8508 | } |
| 8439 | 8509 | |
| ... | ... | @@ -8842,7 +8912,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 8842 | 8912 | if (actual_type->id == TypeTableEntryIdNumLitFloat || |
| 8843 | 8913 | actual_type->id == TypeTableEntryIdNumLitInt) |
| 8844 | 8914 | { |
| 8845 | if (wanted_type->id == TypeTableEntryIdPointer && | |
| 8915 | if (wanted_type->id == TypeTableEntryIdEnum) { | |
| 8916 | IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value); | |
| 8917 | if (type_is_invalid(cast1->value.type)) | |
| 8918 | return ira->codegen->invalid_instruction; | |
| 8919 | ||
| 8920 | IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); | |
| 8921 | if (type_is_invalid(cast2->value.type)) | |
| 8922 | return ira->codegen->invalid_instruction; | |
| 8923 | ||
| 8924 | return cast2; | |
| 8925 | } else if (wanted_type->id == TypeTableEntryIdPointer && | |
| 8846 | 8926 | wanted_type->data.pointer.is_const) |
| 8847 | 8927 | { |
| 8848 | 8928 | IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); |
| ... | ... | @@ -8912,6 +8992,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 8912 | 8992 | } |
| 8913 | 8993 | } |
| 8914 | 8994 | |
| 8995 | // explicit enum to union which has the enum as the tag type | |
| 8996 | if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && | |
| 8997 | (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || | |
| 8998 | wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) | |
| 8999 | { | |
| 9000 | type_ensure_zero_bits_known(ira->codegen, wanted_type); | |
| 9001 | if (wanted_type->data.unionation.tag_type == actual_type) { | |
| 9002 | return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type); | |
| 9003 | } | |
| 9004 | } | |
| 9005 | ||
| 8915 | 9006 | // explicit cast from undefined to anything |
| 8916 | 9007 | if (actual_type->id == TypeTableEntryIdUndefLit) { |
| 8917 | 9008 | return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); |
test/cases/enum.zig+5| ... | ... | @@ -344,3 +344,8 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) { |
| 344 | 344 | MultipleChoice2.Unspecified5 => 9, |
| 345 | 345 | }); |
| 346 | 346 | } |
| 347 | ||
| 348 | test "cast integer literal to enum" { | |
| 349 | assert(MultipleChoice2(0) == MultipleChoice2.Unspecified1); | |
| 350 | assert(MultipleChoice2(40) == MultipleChoice2.B); | |
| 351 | } |
test/cases/union.zig+7| ... | ... | @@ -190,3 +190,10 @@ test "cast union to tag type of union" { |
| 190 | 190 | fn testCastUnionToTagType(x: &const TheUnion) { |
| 191 | 191 | assert(TheTag(*x) == TheTag.B); |
| 192 | 192 | } |
| 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+43| ... | ... | @@ -2684,4 +2684,47 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 2684 | 2684 | , |
| 2685 | 2685 | ".tmp_source.zig:11:13: error: switch on union which has no attached enum", |
| 2686 | 2686 | ".tmp_source.zig:1:17: note: consider 'union(enum)' here"); |
| 2687 | ||
| 2688 | cases.add("enum in field count range but not matching tag", | |
| 2689 | \\const Foo = enum(u32) { | |
| 2690 | \\ A = 10, | |
| 2691 | \\ B = 11, | |
| 2692 | \\}; | |
| 2693 | \\export fn entry() { | |
| 2694 | \\ var x = Foo(0); | |
| 2695 | \\} | |
| 2696 | , | |
| 2697 | ".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0", | |
| 2698 | ".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'"); | |
| 2687 | 2730 | } |