| author | |
| committer | |
| log | 9b616820370c08a97d95805ca31eae5f8ca554f3 |
| tree | 02156ccb1d8a72c747a08b4312820e5ab649cdfa |
| parent | 1f6dacbb2f9a91a20309b271a31e781f11f81819 |
5 files changed, 90 insertions(+), 19 deletions(-)
src/analyze.cpp+2| ... | @@ -749,6 +749,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { | ... | @@ -749,6 +749,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 749 | // next, loop over the parameters again and compute debug information | 749 | // next, loop over the parameters again and compute debug information |
| 750 | // and codegen information | 750 | // and codegen information |
| 751 | if (!skip_debug_info) { | 751 | if (!skip_debug_info) { |
| 752 | ensure_complete_type(g, fn_type_id->return_type); | ||
| 752 | bool first_arg_return = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type); | 753 | bool first_arg_return = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type); |
| 753 | // +1 for maybe making the first argument the return value | 754 | // +1 for maybe making the first argument the return value |
| 754 | LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count); | 755 | LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count); |
| ... | @@ -2534,6 +2535,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { | ... | @@ -2534,6 +2535,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 2534 | case TypeTableEntryIdErrorUnion: | 2535 | case TypeTableEntryIdErrorUnion: |
| 2535 | return type_has_bits(type_entry->data.error.child_type); | 2536 | return type_has_bits(type_entry->data.error.child_type); |
| 2536 | case TypeTableEntryIdEnum: | 2537 | case TypeTableEntryIdEnum: |
| 2538 | assert(type_entry->data.enumeration.complete); | ||
| 2537 | return type_entry->data.enumeration.gen_field_count != 0; | 2539 | return type_entry->data.enumeration.gen_field_count != 0; |
| 2538 | case TypeTableEntryIdMaybe: | 2540 | case TypeTableEntryIdMaybe: |
| 2539 | return type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && | 2541 | return type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && |
src/codegen.cpp+32-10| ... | @@ -645,7 +645,7 @@ static LLVMValueRef gen_struct_memcpy(CodeGen *g, LLVMValueRef src, LLVMValueRef | ... | @@ -645,7 +645,7 @@ static LLVMValueRef gen_struct_memcpy(CodeGen *g, LLVMValueRef src, LLVMValueRef |
| 645 | return LLVMBuildCall(g->builder, g->memcpy_fn_val, params, 5, ""); | 645 | return LLVMBuildCall(g->builder, g->memcpy_fn_val, params, 5, ""); |
| 646 | } | 646 | } |
| 647 | 647 | ||
| 648 | static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, | 648 | static LLVMValueRef gen_assign_raw(CodeGen *g, |
| 649 | LLVMValueRef target_ref, LLVMValueRef value, | 649 | LLVMValueRef target_ref, LLVMValueRef value, |
| 650 | TypeTableEntry *op1_type, TypeTableEntry *op2_type) | 650 | TypeTableEntry *op1_type, TypeTableEntry *op2_type) |
| 651 | { | 651 | { |
| ... | @@ -698,8 +698,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns | ... | @@ -698,8 +698,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns |
| 698 | LLVMBuildRet(g->builder, by_val_value); | 698 | LLVMBuildRet(g->builder, by_val_value); |
| 699 | } else { | 699 | } else { |
| 700 | assert(g->cur_ret_ptr); | 700 | assert(g->cur_ret_ptr); |
| 701 | gen_assign_raw(g, return_instruction->base.source_node, g->cur_ret_ptr, value, | 701 | gen_assign_raw(g, g->cur_ret_ptr, value, return_type, return_instruction->value->type_entry); |
| 702 | return_type, return_instruction->value->type_entry); | ||
| 703 | LLVMBuildRetVoid(g->builder); | 702 | LLVMBuildRetVoid(g->builder); |
| 704 | } | 703 | } |
| 705 | } else { | 704 | } else { |
| ... | @@ -1232,8 +1231,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, | ... | @@ -1232,8 +1231,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, |
| 1232 | want_zeroes = true; | 1231 | want_zeroes = true; |
| 1233 | 1232 | ||
| 1234 | if (have_init_expr) { | 1233 | if (have_init_expr) { |
| 1235 | gen_assign_raw(g, init_value->source_node, var->value_ref, | 1234 | gen_assign_raw(g, var->value_ref, ir_llvm_value(g, init_value), var->type, init_value->type_entry); |
| 1236 | ir_llvm_value(g, init_value), var->type, init_value->type_entry); | ||
| 1237 | } else { | 1235 | } else { |
| 1238 | bool ignore_uninit = false; | 1236 | bool ignore_uninit = false; |
| 1239 | // handle runtime stack allocation | 1237 | // handle runtime stack allocation |
| ... | @@ -2078,8 +2076,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I | ... | @@ -2078,8 +2076,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I |
| 2078 | assert(instruction->tmp_ptr); | 2076 | assert(instruction->tmp_ptr); |
| 2079 | 2077 | ||
| 2080 | LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); | 2078 | LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); |
| 2081 | gen_assign_raw(g, instruction->base.source_node, val_ptr, payload_val, child_type, instruction->value->type_entry); | 2079 | gen_assign_raw(g, val_ptr, payload_val, child_type, instruction->value->type_entry); |
| 2082 | |||
| 2083 | LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); | 2080 | LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); |
| 2084 | LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr); | 2081 | LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr); |
| 2085 | 2082 | ||
| ... | @@ -2125,7 +2122,7 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa | ... | @@ -2125,7 +2122,7 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa |
| 2125 | LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr); | 2122 | LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr); |
| 2126 | 2123 | ||
| 2127 | LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); | 2124 | LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); |
| 2128 | gen_assign_raw(g, instruction->base.source_node, payload_ptr, payload_val, child_type, instruction->value->type_entry); | 2125 | gen_assign_raw(g, payload_ptr, payload_val, child_type, instruction->value->type_entry); |
| 2129 | 2126 | ||
| 2130 | return instruction->tmp_ptr; | 2127 | return instruction->tmp_ptr; |
| 2131 | } | 2128 | } |
| ... | @@ -2145,7 +2142,30 @@ static LLVMValueRef ir_render_enum_tag(CodeGen *g, IrExecutable *executable, IrI | ... | @@ -2145,7 +2142,30 @@ static LLVMValueRef ir_render_enum_tag(CodeGen *g, IrExecutable *executable, IrI |
| 2145 | } | 2142 | } |
| 2146 | 2143 | ||
| 2147 | static LLVMValueRef ir_render_init_enum(CodeGen *g, IrExecutable *executable, IrInstructionInitEnum *instruction) { | 2144 | static LLVMValueRef ir_render_init_enum(CodeGen *g, IrExecutable *executable, IrInstructionInitEnum *instruction) { |
| 2148 | zig_panic("TODO ir_render_init_enum"); | 2145 | TypeTableEntry *enum_type = instruction->enum_type; |
| 2146 | uint32_t value = instruction->field->value; | ||
| 2147 | LLVMTypeRef tag_type_ref = enum_type->data.enumeration.tag_type->type_ref; | ||
| 2148 | LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, value, false); | ||
| 2149 | |||
| 2150 | if (enum_type->data.enumeration.gen_field_count == 0) | ||
| 2151 | return tag_value; | ||
| 2152 | |||
| 2153 | LLVMValueRef tmp_struct_ptr = instruction->tmp_ptr; | ||
| 2154 | |||
| 2155 | LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, enum_gen_tag_index, ""); | ||
| 2156 | LLVMBuildStore(g->builder, tag_value, tag_field_ptr); | ||
| 2157 | |||
| 2158 | TypeTableEntry *union_val_type = instruction->field->type_entry; | ||
| 2159 | if (type_has_bits(union_val_type)) { | ||
| 2160 | LLVMValueRef new_union_val = ir_llvm_value(g, instruction->init_value); | ||
| 2161 | LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, enum_gen_union_index, ""); | ||
| 2162 | LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, | ||
| 2163 | LLVMPointerType(union_val_type->type_ref, 0), ""); | ||
| 2164 | |||
| 2165 | gen_assign_raw(g, bitcasted_union_field_ptr, new_union_val, union_val_type, union_val_type); | ||
| 2166 | } | ||
| 2167 | |||
| 2168 | return tmp_struct_ptr; | ||
| 2149 | } | 2169 | } |
| 2150 | 2170 | ||
| 2151 | static void set_debug_location(CodeGen *g, IrInstruction *instruction) { | 2171 | static void set_debug_location(CodeGen *g, IrInstruction *instruction) { |
| ... | @@ -2750,7 +2770,9 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -2750,7 +2770,9 @@ static void do_code_gen(CodeGen *g) { |
| 2750 | 2770 | ||
| 2751 | if (!type_has_bits(fn_type->data.fn.fn_type_id.return_type)) { | 2771 | if (!type_has_bits(fn_type->data.fn.fn_type_id.return_type)) { |
| 2752 | // nothing to do | 2772 | // nothing to do |
| 2753 | } else if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdPointer) { | 2773 | } else if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdPointer || |
| 2774 | fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdFn) | ||
| 2775 | { | ||
| 2754 | ZigLLVMAddNonNullAttr(fn_val, 0); | 2776 | ZigLLVMAddNonNullAttr(fn_val, 0); |
| 2755 | } else if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type) && | 2777 | } else if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type) && |
| 2756 | !fn_type->data.fn.fn_type_id.is_extern) | 2778 | !fn_type->data.fn.fn_type_id.is_extern) |
src/ir.cpp+39-7| ... | @@ -4324,6 +4324,13 @@ static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction, | ... | @@ -4324,6 +4324,13 @@ static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction, |
| 4324 | return ir_add_error_node(ira, source_instruction->source_node, msg); | 4324 | return ir_add_error_node(ira, source_instruction->source_node, msg); |
| 4325 | } | 4325 | } |
| 4326 | 4326 | ||
| 4327 | static void ir_add_typedef_err_note(IrAnalyze *ira, ErrorMsg *msg, TypeTableEntry *type_entry) { | ||
| 4328 | if (type_entry->id == TypeTableEntryIdTypeDecl) { | ||
| 4329 | // requires tracking source_node in the typedecl type | ||
| 4330 | zig_panic("TODO add error note about typedecls"); | ||
| 4331 | } | ||
| 4332 | } | ||
| 4333 | |||
| 4327 | static IrInstruction *ir_exec_const_result(IrExecutable *exec) { | 4334 | static IrInstruction *ir_exec_const_result(IrExecutable *exec) { |
| 4328 | if (exec->basic_block_list.length != 1) | 4335 | if (exec->basic_block_list.length != 1) |
| 4329 | return nullptr; | 4336 | return nullptr; |
| ... | @@ -5451,8 +5458,7 @@ static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_inst | ... | @@ -5451,8 +5458,7 @@ static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_inst |
| 5451 | if (value->type_entry->id == TypeTableEntryIdInvalid) | 5458 | if (value->type_entry->id == TypeTableEntryIdInvalid) |
| 5452 | return ira->codegen->builtin_types.entry_invalid; | 5459 | return ira->codegen->builtin_types.entry_invalid; |
| 5453 | 5460 | ||
| 5454 | bool is_inline = ir_should_inline(&ira->new_irb); | 5461 | if (instr_is_comptime(value)) { |
| 5455 | if (is_inline || instr_is_comptime(value)) { | ||
| 5456 | ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); | 5462 | ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); |
| 5457 | if (!val) | 5463 | if (!val) |
| 5458 | return ira->codegen->builtin_types.entry_invalid; | 5464 | return ira->codegen->builtin_types.entry_invalid; |
| ... | @@ -8223,7 +8229,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, | ... | @@ -8223,7 +8229,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, |
| 8223 | case TypeTableEntryIdUnion: | 8229 | case TypeTableEntryIdUnion: |
| 8224 | case TypeTableEntryIdBlock: | 8230 | case TypeTableEntryIdBlock: |
| 8225 | case TypeTableEntryIdBoundFn: | 8231 | case TypeTableEntryIdBoundFn: |
| 8226 | ir_add_error_node(ira, switch_target_instruction->base.source_node, | 8232 | ir_add_error(ira, &switch_target_instruction->base, |
| 8227 | buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); | 8233 | buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); |
| 8228 | // TODO if this is a typedecl, add error note showing the declaration of the type decl | 8234 | // TODO if this is a typedecl, add error note showing the declaration of the type decl |
| 8229 | return ira->codegen->builtin_types.entry_invalid; | 8235 | return ira->codegen->builtin_types.entry_invalid; |
| ... | @@ -8231,10 +8237,36 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, | ... | @@ -8231,10 +8237,36 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, |
| 8231 | zig_unreachable(); | 8237 | zig_unreachable(); |
| 8232 | } | 8238 | } |
| 8233 | 8239 | ||
| 8234 | static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, | 8240 | static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstructionSwitchVar *instruction) { |
| 8235 | IrInstructionSwitchVar *switch_var_instruction) | 8241 | IrInstruction *target_value_ptr = instruction->target_value_ptr->other; |
| 8236 | { | 8242 | if (target_value_ptr->type_entry->id == TypeTableEntryIdInvalid) |
| 8237 | zig_panic("TODO switch var analyze"); | 8243 | return ira->codegen->builtin_types.entry_invalid; |
| 8244 | |||
| 8245 | IrInstruction *prong_value = instruction->prong_value->other; | ||
| 8246 | if (prong_value->type_entry->id == TypeTableEntryIdInvalid) | ||
| 8247 | return ira->codegen->builtin_types.entry_invalid; | ||
| 8248 | |||
| 8249 | assert(target_value_ptr->type_entry->id == TypeTableEntryIdPointer); | ||
| 8250 | TypeTableEntry *target_type = target_value_ptr->type_entry->data.pointer.child_type; | ||
| 8251 | if (target_type->id == TypeTableEntryIdEnum) { | ||
| 8252 | ConstExprValue *prong_val = ir_resolve_const(ira, prong_value, UndefBad); | ||
| 8253 | if (!prong_val) | ||
| 8254 | return ira->codegen->builtin_types.entry_invalid; | ||
| 8255 | |||
| 8256 | TypeEnumField *field = &target_type->data.enumeration.fields[prong_val->data.x_bignum.data.x_uint]; | ||
| 8257 | if (instr_is_comptime(target_value_ptr)) { | ||
| 8258 | zig_panic("TODO comptime switch var"); | ||
| 8259 | } | ||
| 8260 | |||
| 8261 | ir_build_enum_field_ptr_from(&ira->new_irb, &instruction->base, target_value_ptr, field); | ||
| 8262 | return get_pointer_to_type(ira->codegen, field->type_entry, | ||
| 8263 | target_value_ptr->type_entry->data.pointer.is_const); | ||
| 8264 | } else { | ||
| 8265 | ErrorMsg *msg = ir_add_error(ira, &instruction->base, | ||
| 8266 | buf_sprintf("switch on type '%s' provides no expression parameter", buf_ptr(&target_type->name))); | ||
| 8267 | ir_add_typedef_err_note(ira, msg, target_type); | ||
| 8268 | return ira->codegen->builtin_types.entry_invalid; | ||
| 8269 | } | ||
| 8238 | } | 8270 | } |
| 8239 | 8271 | ||
| 8240 | static TypeTableEntry *ir_analyze_instruction_enum_tag(IrAnalyze *ira, IrInstructionEnumTag *enum_tag_instruction) { | 8272 | static TypeTableEntry *ir_analyze_instruction_enum_tag(IrAnalyze *ira, IrInstructionEnumTag *enum_tag_instruction) { |
src/ir_print.cpp+2-2| ... | @@ -919,9 +919,9 @@ static void ir_print_test_comptime(IrPrint *irp, IrInstructionTestComptime *inst | ... | @@ -919,9 +919,9 @@ static void ir_print_test_comptime(IrPrint *irp, IrInstructionTestComptime *inst |
| 919 | } | 919 | } |
| 920 | 920 | ||
| 921 | static void ir_print_init_enum(IrPrint *irp, IrInstructionInitEnum *instruction) { | 921 | static void ir_print_init_enum(IrPrint *irp, IrInstructionInitEnum *instruction) { |
| 922 | fprintf(irp->f, "%s.%s { ", buf_ptr(&instruction->enum_type->name), buf_ptr(instruction->field->name)); | 922 | fprintf(irp->f, "%s.%s {", buf_ptr(&instruction->enum_type->name), buf_ptr(instruction->field->name)); |
| 923 | ir_print_other_instruction(irp, instruction->init_value); | 923 | ir_print_other_instruction(irp, instruction->init_value); |
| 924 | fprintf(irp->f, "{"); | 924 | fprintf(irp->f, "}"); |
| 925 | } | 925 | } |
| 926 | 926 | ||
| 927 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | 927 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
test/cases3/enum.zig+15| ... | @@ -12,6 +12,16 @@ fn enumType() { | ... | @@ -12,6 +12,16 @@ fn enumType() { |
| 12 | assert(@sizeOf(Foo) == expected_foo_size); | 12 | assert(@sizeOf(Foo) == expected_foo_size); |
| 13 | assert(@sizeOf(Bar) == 1); | 13 | assert(@sizeOf(Bar) == 1); |
| 14 | } | 14 | } |
| 15 | |||
| 16 | fn enumAsReturnValue () { | ||
| 17 | @setFnTest(this); | ||
| 18 | |||
| 19 | switch (returnAnInt(13)) { | ||
| 20 | Foo.One => |value| assert(value == 13), | ||
| 21 | else => @unreachable(), | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 15 | const Point = struct { | 25 | const Point = struct { |
| 16 | x: u64, | 26 | x: u64, |
| 17 | y: u64, | 27 | y: u64, |
| ... | @@ -28,6 +38,11 @@ const Bar = enum { | ... | @@ -28,6 +38,11 @@ const Bar = enum { |
| 28 | D, | 38 | D, |
| 29 | }; | 39 | }; |
| 30 | 40 | ||
| 41 | fn returnAnInt(x: i32) -> Foo { | ||
| 42 | Foo.One { x } | ||
| 43 | } | ||
| 44 | |||
| 45 | |||
| 31 | fn assert(ok: bool) { | 46 | fn assert(ok: bool) { |
| 32 | if (!ok) | 47 | if (!ok) |
| 33 | @unreachable(); | 48 | @unreachable(); |