From 6fd0dddf186f6435f422f2992f44ec9a35e09f20 Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 15 Jan 2020 20:23:49 +0200 Subject: implement non-exhaustive enums --- src/ir.cpp | 110 +++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 32 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index d871aa27a0..6876214510 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3451,7 +3451,7 @@ static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count, - bool have_else_prong) + bool have_else_prong, bool have_underscore_prong) { IrInstructionCheckSwitchProngs *instruction = ir_build_instruction( irb, scope, source_node); @@ -3459,6 +3459,7 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, instruction->ranges = ranges; instruction->range_count = range_count; instruction->have_else_prong = have_else_prong; + instruction->have_underscore_prong = have_underscore_prong; ir_ref_instruction(target_value, irb->current_basic_block); for (size_t i = 0; i < range_count; i += 1) { @@ -8090,34 +8091,11 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); AstNode *else_prong = nullptr; + AstNode *underscore_prong = nullptr; for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); size_t prong_item_count = prong_node->data.switch_prong.items.length; - if (prong_item_count == 0) { - ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); - if (else_prong) { - ErrorMsg *msg = add_node_error(irb->codegen, prong_node, - buf_sprintf("multiple else prongs in switch expression")); - add_error_note(irb->codegen, msg, else_prong, - buf_sprintf("previous else prong is here")); - return irb->codegen->invalid_instruction; - } - else_prong = prong_node; - - IrBasicBlock *prev_block = irb->current_basic_block; - if (peer_parent->peers.length > 0) { - peer_parent->peers.last()->next_bb = else_block; - } - peer_parent->peers.append(this_peer_result_loc); - ir_set_cursor_at_end_and_append_block(irb, else_block); - if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, - is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values, - &switch_else_var, LValNone, &this_peer_result_loc->base)) - { - return irb->codegen->invalid_instruction; - } - ir_set_cursor_at_end(irb, prev_block); - } else if (prong_node->data.switch_prong.any_items_are_range) { + if (prong_node->data.switch_prong.any_items_are_range) { ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); IrInstruction *ok_bit = nullptr; @@ -8195,6 +8173,59 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * } ir_set_cursor_at_end_and_append_block(irb, range_block_no); + } else { + if (prong_item_count == 0) { + if (else_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("multiple else prongs in switch expression")); + add_error_note(irb->codegen, msg, else_prong, + buf_sprintf("previous else prong is here")); + return irb->codegen->invalid_instruction; + } + else_prong = prong_node; + if (underscore_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("else and '_' prong in switch expression")); + add_error_note(irb->codegen, msg, underscore_prong, + buf_sprintf("'_' prong is here")); + return irb->codegen->invalid_instruction; + } + } else if (prong_item_count == 1 && + prong_node->data.switch_prong.items.at(0)->type == NodeTypeSymbol && + buf_eql_str(prong_node->data.switch_prong.items.at(0)->data.symbol_expr.symbol, "_")) { + if (underscore_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("multiple '_' prongs in switch expression")); + add_error_note(irb->codegen, msg, underscore_prong, + buf_sprintf("previous '_' prong is here")); + return irb->codegen->invalid_instruction; + } + underscore_prong = prong_node; + if (else_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("else and '_' prong in switch expression")); + add_error_note(irb->codegen, msg, else_prong, + buf_sprintf("else prong is here")); + return irb->codegen->invalid_instruction; + } + } else { + continue; + } + ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); + + IrBasicBlock *prev_block = irb->current_basic_block; + if (peer_parent->peers.length > 0) { + peer_parent->peers.last()->next_bb = else_block; + } + peer_parent->peers.append(this_peer_result_loc); + ir_set_cursor_at_end_and_append_block(irb, else_block); + if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, + is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values, + &switch_else_var, LValNone, &this_peer_result_loc->base)) + { + return irb->codegen->invalid_instruction; + } + ir_set_cursor_at_end(irb, prev_block); } } @@ -8206,6 +8237,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * continue; if (prong_node->data.switch_prong.any_items_are_range) continue; + if (underscore_prong == prong_node) + continue; ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); @@ -8249,7 +8282,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * } IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, - check_ranges.items, check_ranges.length, else_prong != nullptr); + check_ranges.items, check_ranges.length, else_prong != nullptr, underscore_prong != nullptr); IrInstruction *br_instruction; if (cases.length == 0) { @@ -8269,7 +8302,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction; } - if (!else_prong) { + if (!else_prong && !underscore_prong) { if (peer_parent->peers.length != 0) { peer_parent->peers.last()->next_bb = else_block; } @@ -12790,7 +12823,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour return ira->codegen->invalid_instruction; TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); - if (field == nullptr && wanted_type->data.enumeration.layout != ContainerLayoutExtern) { + if (field == nullptr && !wanted_type->data.enumeration.non_exhaustive) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); ErrorMsg *msg = ir_add_error(ira, source_instr, @@ -26433,10 +26466,23 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, bigint_incr(&field_index); } } - if (!instruction->have_else_prong) { - if (switch_type->data.enumeration.layout == ContainerLayoutExtern) { + if (switch_type->data.enumeration.non_exhaustive && instruction->have_underscore_prong) { + for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; + if (buf_eql_str(enum_field->name, "_")) + continue; + + auto entry = field_prev_uses.maybe_get(enum_field->value); + if (!entry) { + ir_add_error(ira, &instruction->base, + buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), + buf_ptr(enum_field->name))); + } + } + } else if (!instruction->have_else_prong) { + if (switch_type->data.enumeration.non_exhaustive) { ir_add_error(ira, &instruction->base, - buf_sprintf("switch on an extern enum must have an else prong")); + buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong")); } for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; -- cgit v1.2.3 From c57784aa15b50a9f38482154170924babab19c03 Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 15 Jan 2020 21:50:12 +0200 Subject: add is_exhaustive field to typeinfo --- lib/std/builtin.zig | 1 + src/ir.cpp | 7 ++++++- test/stage1/behavior/enum.zig | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src/ir.cpp') diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index dbd19fbadf..4b80b38100 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -253,6 +253,7 @@ pub const TypeInfo = union(enum) { tag_type: type, fields: []EnumField, decls: []Declaration, + is_exhaustive: bool, }; /// This data structure is used by the Zig language code generation and diff --git a/src/ir.cpp b/src/ir.cpp index 6876214510..08f80e0647 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -23107,7 +23107,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Enum", nullptr); - ZigValue **fields = alloc_const_vals_ptrs(4); + ZigValue **fields = alloc_const_vals_ptrs(5); result->data.x_struct.fields = fields; // layout: ContainerLayout @@ -23153,6 +23153,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr { return err; } + // is_exhaustive: bool + ensure_field_index(result->type, "is_exhaustive", 4); + fields[4]->special = ConstValSpecialStatic; + fields[4]->type = ira->codegen->builtin_types.entry_bool; + fields[4]->data.x_bool = !type_entry->data.enumeration.non_exhaustive; break; } diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index c888722f27..82e57a3a38 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -46,6 +46,7 @@ test "non-exhaustive enum" { expect(@enumToInt(e) == 12); e = @intToEnum(E, y); expect(@enumToInt(e) == 52); + expect(@typeInfo(E).Enum.is_exhaustive == false); } }; S.doTheTest(52); -- cgit v1.2.3 From 5c2238fc4ad1d10f0620c931d369005b53742eb7 Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 15 Jan 2020 22:09:19 +0200 Subject: small fixes * error for '_' prong on exhaustive enum * todo panic for `@tagName` on non-exhaustive enum * don't require '_' field on tagged unions --- src/analyze.cpp | 2 +- src/codegen.cpp | 2 ++ src/ir.cpp | 8 +++++++- 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/analyze.cpp b/src/analyze.cpp index 0b2b6ddeaa..7669e0890b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3293,7 +3293,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { } else if (enum_type_node != nullptr) { for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i]; - if (!covered_enum_fields[i]) { + if (!covered_enum_fields[i] && !buf_eql_str(enum_field->name, "_")) { AstNode *enum_decl_node = tag_type->data.enumeration.decl_node; AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i); ErrorMsg *msg = add_node_error(g, decl_node, diff --git a/src/codegen.cpp b/src/codegen.cpp index 0dc820be51..42fd188824 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5065,6 +5065,8 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable { ZigType *enum_type = instruction->target->value->type; assert(enum_type->id == ZigTypeIdEnum); + if (enum_type->data.enumeration.non_exhaustive) + zig_panic("TODO @tagName on non-exhaustive enum"); LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); diff --git a/src/ir.cpp b/src/ir.cpp index 08f80e0647..7aa3243c34 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -22357,6 +22357,8 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns if (instr_is_comptime(target)) { if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; + if (target->value->type->data.enumeration.non_exhaustive) + zig_panic("TODO @tagName on non-exhaustive enum"); TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; IrInstruction *result = ir_const(ira, &instruction->base, nullptr); @@ -26471,7 +26473,11 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, bigint_incr(&field_index); } } - if (switch_type->data.enumeration.non_exhaustive && instruction->have_underscore_prong) { + if (instruction->have_underscore_prong) { + if (!switch_type->data.enumeration.non_exhaustive){ + ir_add_error(ira, &instruction->base, + buf_sprintf("switch on non-exhaustive enum has `_` prong")); + } for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; if (buf_eql_str(enum_field->name, "_")) -- cgit v1.2.3 From d84569895c80136d9b081a319301a737f342d251 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 16 Jan 2020 09:04:11 +0200 Subject: turn panics into compile errors, require at least 1 field in non-exhaustive enum --- doc/langref.html.in | 7 ++----- src/analyze.cpp | 3 ++- src/codegen.cpp | 7 +++++-- src/ir.cpp | 32 ++++++++++++++++---------------- 4 files changed, 25 insertions(+), 24 deletions(-) (limited to 'src/ir.cpp') diff --git a/doc/langref.html.in b/doc/langref.html.in index dbe98ce708..cac01c5686 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2903,11 +2903,8 @@ test "switch using enum literals" { {#link|@intToEnum#} on a non-exhaustive enum cannot fail.

- A switch on a non-exhaustive enum can include a '_' prong with the following properties: -

    -
  • makes it a compile error if all the known tag names are not handled by the switch
  • -
  • allows omitting {#syntax#}else{#endsyntax#}
  • -
+ A switch on a non-exhaustive enum can include a '_' prong as an alternative to an {#syntax#}else{#endsyntax#} prong + with the difference being that it makes it a compile error if all the known tag names are not handled by the switch.

{#code_begin|test#} const std = @import("std"); diff --git a/src/analyze.cpp b/src/analyze.cpp index 7669e0890b..a9aaf74a85 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2560,7 +2560,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { assert(!enum_type->data.enumeration.fields); uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; - if (field_count == 0) { + if (field_count == 0 || (field_count == 1 && + buf_eql_str(decl_node->data.container_decl.fields.at(0)->data.struct_field.name, "_"))) { add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields")); enum_type->data.enumeration.src_field_count = field_count; diff --git a/src/codegen.cpp b/src/codegen.cpp index 42fd188824..030e892d45 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5065,8 +5065,11 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable { ZigType *enum_type = instruction->target->value->type; assert(enum_type->id == ZigTypeIdEnum); - if (enum_type->data.enumeration.non_exhaustive) - zig_panic("TODO @tagName on non-exhaustive enum"); + if (enum_type->data.enumeration.non_exhaustive) { + add_node_error(g, instruction->base.source_node, + buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991")); + codegen_report_errors_and_exit(g); + } LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); diff --git a/src/ir.cpp b/src/ir.cpp index 7aa3243c34..43ca01113b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8183,13 +8183,6 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * return irb->codegen->invalid_instruction; } else_prong = prong_node; - if (underscore_prong) { - ErrorMsg *msg = add_node_error(irb->codegen, prong_node, - buf_sprintf("else and '_' prong in switch expression")); - add_error_note(irb->codegen, msg, underscore_prong, - buf_sprintf("'_' prong is here")); - return irb->codegen->invalid_instruction; - } } else if (prong_item_count == 1 && prong_node->data.switch_prong.items.at(0)->type == NodeTypeSymbol && buf_eql_str(prong_node->data.switch_prong.items.at(0)->data.symbol_expr.symbol, "_")) { @@ -8201,16 +8194,20 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * return irb->codegen->invalid_instruction; } underscore_prong = prong_node; - if (else_prong) { - ErrorMsg *msg = add_node_error(irb->codegen, prong_node, - buf_sprintf("else and '_' prong in switch expression")); - add_error_note(irb->codegen, msg, else_prong, - buf_sprintf("else prong is here")); - return irb->codegen->invalid_instruction; - } } else { continue; } + if (underscore_prong && else_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("else and '_' prong in switch expression")); + if (underscore_prong == prong_node) + add_error_note(irb->codegen, msg, else_prong, + buf_sprintf("else prong is here")); + else + add_error_note(irb->codegen, msg, underscore_prong, + buf_sprintf("'_' prong is here")); + return irb->codegen->invalid_instruction; + } ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); IrBasicBlock *prev_block = irb->current_basic_block; @@ -22357,8 +22354,11 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns if (instr_is_comptime(target)) { if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - if (target->value->type->data.enumeration.non_exhaustive) - zig_panic("TODO @tagName on non-exhaustive enum"); + if (target->value->type->data.enumeration.non_exhaustive) { + add_node_error(ira->codegen, instruction->base.source_node, + buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991")); + return ira->codegen->invalid_instruction; + } TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; IrInstruction *result = ir_const(ira, &instruction->base, nullptr); -- cgit v1.2.3 From df03fcf5f07e0d5d16e5b837658265f6c468cbe4 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 16 Jan 2020 17:12:43 +0200 Subject: implement `@bitSizeOf` --- doc/langref.html.in | 15 ++++++++++++++- src/all_types.hpp | 4 ++++ src/codegen.cpp | 1 + src/ir.cpp | 12 +++++++++--- src/ir_print.cpp | 5 ++++- test/stage1/behavior/sizeof_and_typeof.zig | 11 +++++++++++ 6 files changed, 43 insertions(+), 5 deletions(-) (limited to 'src/ir.cpp') diff --git a/doc/langref.html.in b/doc/langref.html.in index 092e303013..88e6b41983 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6815,6 +6815,19 @@ async fn func(y: *i32) void {

{#header_close#} + {#header_open|@bitSizeOf#} +
{#syntax#}@bitSizeOf(comptime T: type) comptime_int{#endsyntax#}
+

+ This function returns the number of bits it takes to store {#syntax#}T{#endsyntax#} in memory. + The result is a target-specific compile time constant. +

+

+ This function measures the size at runtime. For types that are disallowed at runtime, such as + {#syntax#}comptime_int{#endsyntax#} and {#syntax#}type{#endsyntax#}, the result is {#syntax#}0{#endsyntax#}. +

+ {#see_also|@sizeOf|@typeInfo#} + {#header_close#} + {#header_open|@breakpoint#}
{#syntax#}@breakpoint(){#endsyntax#}

@@ -8044,7 +8057,7 @@ test "@setRuntimeSafety" { This function measures the size at runtime. For types that are disallowed at runtime, such as {#syntax#}comptime_int{#endsyntax#} and {#syntax#}type{#endsyntax#}, the result is {#syntax#}0{#endsyntax#}.

- {#see_also|@typeInfo#} + {#see_also|@bitSizeOf|@typeInfo#} {#header_close#} {#header_open|@sliceToBytes#} diff --git a/src/all_types.hpp b/src/all_types.hpp index 0fed73f7b2..4d6f68daff 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -358,6 +358,8 @@ struct LazyValueSizeOf { IrAnalyze *ira; IrInstruction *target_type; + + bool bit_size; }; struct LazyValueSliceType { @@ -1754,6 +1756,7 @@ enum BuiltinFnId { BuiltinFnIdFrameSize, BuiltinFnIdAs, BuiltinFnIdCall, + BuiltinFnIdBitSizeof, }; struct BuiltinFnEntry { @@ -3146,6 +3149,7 @@ struct IrInstructionAsmGen { struct IrInstructionSizeOf { IrInstruction base; + bool bit_size; IrInstruction *type_value; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 95b55675bb..cd009b3bea 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8299,6 +8299,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdFrameSize, "frameSize", 1); create_builtin_fn(g, BuiltinFnIdAs, "as", 2); create_builtin_fn(g, BuiltinFnIdCall, "call", 3); + create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index d871aa27a0..c3ca366456 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2392,9 +2392,10 @@ static IrInstruction *ir_build_asm_gen(IrAnalyze *ira, Scope *scope, AstNode *so return &instruction->base; } -static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { +static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, bool bit_size) { IrInstructionSizeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; + instruction->bit_size = bit_size; ir_ref_instruction(type_value, irb->current_basic_block); @@ -5249,13 +5250,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_lval_wrap(irb, scope, set_float_mode, lval, result_loc); } case BuiltinFnIdSizeof: + case BuiltinFnIdBitSizeof: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value); + IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value, builtin_fn->id == BuiltinFnIdBitSizeof); return ir_lval_wrap(irb, scope, size_of, lval, result_loc); } case BuiltinFnIdImport: @@ -21065,6 +21067,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructi lazy_size_of->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_size_of->base; lazy_size_of->base.id = LazyValueIdSizeOf; + lazy_size_of->bit_size = instruction->bit_size; lazy_size_of->target_type = instruction->type_value->child; if (ir_resolve_type_lazy(ira, lazy_size_of->target_type) == nullptr) @@ -29415,7 +29418,10 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); - bigint_init_unsigned(&val->data.x_bigint, abi_size); + if (lazy_size_of->bit_size) + bigint_init_unsigned(&val->data.x_bigint, size_in_bits); + else + bigint_init_unsigned(&val->data.x_bigint, abi_size); // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 90ab410e4d..24e030f501 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1039,7 +1039,10 @@ static void ir_print_asm_gen(IrPrint *irp, IrInstructionAsmGen *instruction) { } static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) { - fprintf(irp->f, "@sizeOf("); + if (instruction->bit_size) + fprintf(irp->f, "@bitSizeOf("); + else + fprintf(irp->f, "@sizeOf("); ir_print_other_instruction(irp, instruction->type_value); fprintf(irp->f, ")"); } diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig index d46cdccd0d..a738dbbbf9 100644 --- a/test/stage1/behavior/sizeof_and_typeof.zig +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -124,3 +124,14 @@ fn fn1(alpha: bool) void { test "lazy @sizeOf result is checked for definedness" { const f = fn1; } + +test "@bitSizeOf" { + expect(@bitSizeOf(u2) == 2); + expect(@bitSizeOf(u8) == @sizeOf(u8) * 8); + expect(@bitSizeOf(struct { + a: u2 + }) == 8); + expect(@bitSizeOf(packed struct { + a: u2 + }) == 2); +} -- cgit v1.2.3 From bac27731e30cbea76997fa3547791b3462ac8697 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 16 Jan 2020 13:55:39 +0200 Subject: add struct field default value to typeinfo --- lib/std/builtin.zig | 1 + src/ir.cpp | 8 +++++++- test/stage1/behavior/type_info.zig | 5 ++++- 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src/ir.cpp') diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index dbd19fbadf..65604a5b20 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -205,6 +205,7 @@ pub const TypeInfo = union(enum) { name: []const u8, offset: ?comptime_int, field_type: type, + default_value: var, }; /// This data structure is used by the Zig language code generation and diff --git a/src/ir.cpp b/src/ir.cpp index c3ca366456..818644f501 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -23338,7 +23338,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr struct_field_val->special = ConstValSpecialStatic; struct_field_val->type = type_info_struct_field_type; - ZigValue **inner_fields = alloc_const_vals_ptrs(3); + ZigValue **inner_fields = alloc_const_vals_ptrs(4); inner_fields[1]->special = ConstValSpecialStatic; inner_fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int); @@ -23361,6 +23361,12 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr inner_fields[2]->type = ira->codegen->builtin_types.entry_type; inner_fields[2]->data.x_type = struct_field->type_entry; + // default_value: var + inner_fields[3]->special = ConstValSpecialStatic; + inner_fields[3]->type = get_optional_type(ira->codegen, struct_field->type_entry); + memoize_field_init_val(ira->codegen, type_entry, struct_field); + set_optional_payload(inner_fields[3], struct_field->init_val); + ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(struct_field->name), true); diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig index d35cc8831f..9d577ec0b4 100644 --- a/test/stage1/behavior/type_info.zig +++ b/test/stage1/behavior/type_info.zig @@ -237,9 +237,11 @@ fn testStruct() void { const struct_info = @typeInfo(TestStruct); expect(@as(TypeId, struct_info) == TypeId.Struct); expect(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); - expect(struct_info.Struct.fields.len == 3); + expect(struct_info.Struct.fields.len == 4); expect(struct_info.Struct.fields[1].offset == null); expect(struct_info.Struct.fields[2].field_type == *TestStruct); + expect(struct_info.Struct.fields[2].default_value == null); + expect(struct_info.Struct.fields[3].default_value.? == 4); expect(struct_info.Struct.decls.len == 2); expect(struct_info.Struct.decls[0].is_pub); expect(!struct_info.Struct.decls[0].data.Fn.is_extern); @@ -254,6 +256,7 @@ const TestStruct = packed struct { fieldA: usize, fieldB: void, fieldC: *Self, + fieldD: u32 = 4, pub fn foo(self: *const Self) void {} }; -- cgit v1.2.3 From 5f2bac010df0a2cf1e36622d6742baf13f4ffe75 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 18 Jan 2020 09:55:18 +0100 Subject: Allow @tagName on enum literals Closes #4214 --- src/ir.cpp | 12 ++++++++++-- test/stage1/behavior/enum.zig | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 88b4c1a832..7f6a1963c5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -21389,9 +21389,9 @@ static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; - if (value->value->type->id == ZigTypeIdEnum) { + if (value->value->type->id == ZigTypeIdEnum || + value->value->type->id == ZigTypeIdEnumLiteral) return value; - } if (value->value->type->id != ZigTypeIdUnion) { ir_add_error(ira, value, @@ -22352,6 +22352,14 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; + if (target->value->type->id == ZigTypeIdEnumLiteral) { + IrInstruction *result = ir_const(ira, &instruction->base, nullptr); + Buf *field_name = target->value->data.x_enum_literal; + ZigValue *array_val = create_const_str_lit(ira->codegen, field_name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field_name), true); + return result; + } + assert(target->value->type->id == ZigTypeIdEnum); if (instr_is_comptime(target)) { diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index 83ad76b72c..5532a3ddf5 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -1094,3 +1094,8 @@ test "enum with one member default to u0 tag type" { }; comptime expect(@TagType(E0) == u0); } + +test "tagName on enum literals" { + expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); + comptime expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); +} -- cgit v1.2.3 From c53d94e5127a8dcfefd906c5be0e6b81eaf3d22c Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 18 Jan 2020 15:13:21 +0100 Subject: Prevent crash with empty non-exhaustive enum --- src/analyze.cpp | 2 +- src/ir.cpp | 2 +- test/stage1/behavior/enum.zig | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/analyze.cpp b/src/analyze.cpp index e064677a09..0bbec66a9b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -8312,7 +8312,7 @@ static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatu uint32_t field_count = enum_type->data.enumeration.src_field_count; - assert(enum_type->data.enumeration.fields); + assert(field_count == 0 || enum_type->data.enumeration.fields != nullptr); ZigLLVMDIEnumerator **di_enumerators = allocate(field_count); for (uint32_t i = 0; i < field_count; i += 1) { diff --git a/src/ir.cpp b/src/ir.cpp index 88b4c1a832..ae02ac9cae 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -21631,7 +21631,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, case ZigTypeIdEnum: { if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - if (target_type->data.enumeration.src_field_count < 2) { + if (target_type->data.enumeration.src_field_count == 1) { TypeEnumField *only_field = &target_type->data.enumeration.fields[0]; IrInstruction *result = ir_const(ira, &switch_target_instruction->base, target_type); bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value); diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index 83ad76b72c..a478729003 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -65,6 +65,26 @@ test "non-exhaustive enum" { comptime S.doTheTest(52); } +test "empty non-exhaustive enum" { + const S = struct { + const E = enum(u8) { + _, + }; + fn doTheTest(y: u8) void { + var e = @intToEnum(E, y); + expect(switch (e) { + _ => true, + }); + expect(@enumToInt(e) == y); + + expect(@typeInfo(E).Enum.fields.len == 0); + expect(@typeInfo(E).Enum.is_exhaustive == false); + } + }; + S.doTheTest(42); + comptime S.doTheTest(42); +} + test "enum type" { const foo1 = Foo{ .One = 13 }; const foo2 = Foo{ -- cgit v1.2.3 From f456b88baecc0a841520035973e6887eb2573319 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 18 Jan 2020 19:24:04 +0100 Subject: Get rid of some dead logic --- src/ir.cpp | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 7f6a1963c5..42a5043279 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6030,8 +6030,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value); - IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag); + IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, tag_name, lval, result_loc); } case BuiltinFnIdTagType: @@ -21389,10 +21388,6 @@ static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; - if (value->value->type->id == ZigTypeIdEnum || - value->value->type->id == ZigTypeIdEnumLiteral) - return value; - if (value->value->type->id != ZigTypeIdUnion) { ir_add_error(ira, value, buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value->type->name))); @@ -21460,12 +21455,6 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, if (type_is_invalid(case_value->value->type)) return ir_unreach_error(ira); - if (case_value->value->type->id == ZigTypeIdEnum) { - case_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, case_value); - if (type_is_invalid(case_value->value->type)) - return ir_unreach_error(ira); - } - IrInstruction *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value->type); if (type_is_invalid(casted_case_value->value->type)) return ir_unreach_error(ira); @@ -21510,12 +21499,6 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, if (type_is_invalid(new_value->value->type)) continue; - if (new_value->value->type->id == ZigTypeIdEnum) { - new_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, new_value); - if (type_is_invalid(new_value->value->type)) - continue; - } - IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, target_value->value->type); if (type_is_invalid(casted_new_value->value->type)) continue; @@ -22360,6 +22343,12 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns return result; } + if (target->value->type->id == ZigTypeIdUnion) { + target = ir_analyze_union_tag(ira, &instruction->base, target); + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_instruction; + } + assert(target->value->type->id == ZigTypeIdEnum); if (instr_is_comptime(target)) { -- cgit v1.2.3 From b0f753e21d6fcaafd0b35dc02fdfe23b14e310d6 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 18 Jan 2020 19:58:05 +0100 Subject: Fix edge case in tagName handling of unions Closes #4226 --- src/ir.cpp | 9 +++++++++ test/stage1/behavior/union.zig | 9 +++++++++ 2 files changed, 18 insertions(+) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index ae02ac9cae..ff4b552a9b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -22354,6 +22354,15 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns assert(target->value->type->id == ZigTypeIdEnum); + if (target->value->type->data.enumeration.src_field_count == 1 && + !target->value->type->data.enumeration.non_exhaustive) { + TypeEnumField *only_field = &target->value->type->data.enumeration.fields[0]; + ZigValue *array_val = create_const_str_lit(ira->codegen, only_field->name)->data.x_ptr.data.ref.pointee; + IrInstruction *result = ir_const(ira, &instruction->base, nullptr); + init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(only_field->name), true); + return result; + } + if (instr_is_comptime(target)) { if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index a24bee03e6..4625b7573a 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -629,3 +629,12 @@ test "union initializer generates padding only if needed" { var v = U{ .A = 532 }; expect(v.A == 532); } + +test "runtime tag name with single field" { + const U = union(enum) { + A: i32, + }; + + var v = U{ .A = 42 }; + expect(std.mem.eql(u8, @tagName(v), "A")); +} -- cgit v1.2.3