From 1bf72c693b50cc050021a858511b302f560d3c2a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 18 Oct 2018 19:32:44 -0400 Subject: [PATCH 001/190] copy elision using the ir system --- doc/langref.html.in | 11 +- src/all_types.hpp | 147 +++-- src/codegen.cpp | 216 +++----- src/ir.cpp | 1274 +++++++++++++++++++++++-------------------- src/ir_print.cpp | 107 +++- 5 files changed, 967 insertions(+), 788 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 0a5ff19389b7..51d90911f869 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5855,13 +5855,13 @@ fn add(a: i32, b: i32) i32 { return a + b; } This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this:

-
{#syntax#}for (source[0...byte_count]) |b, i| dest[i] = b;{#endsyntax#}
+
{#syntax#}for (source[0..byte_count]) |b, i| dest[i] = b;{#endsyntax#}

The optimizer is intelligent enough to turn the above snippet into a memcpy.

There is also a standard library function for this:

{#syntax#}const mem = @import("std").mem;
-mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#}
+mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#} {#header_close#} {#header_open|@memset#}
{#syntax#}@memset(dest: [*]u8, c: u8, byte_count: usize){#endsyntax#}
@@ -5872,7 +5872,7 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#} This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this:

-
{#syntax#}for (dest[0...byte_count]) |*b| b.* = c;{#endsyntax#}
+
{#syntax#}for (dest[0..byte_count]) |*b| b.* = c;{#endsyntax#}

The optimizer is intelligent enough to turn the above snippet into a memset.

@@ -6514,9 +6514,10 @@ pub const TypeInfo = union(TypeId).{ {#code_end#} {#header_close#} {#header_open|@typeName#} -
{#syntax#}@typeName(T: type) []u8{#endsyntax#}
+
{#syntax#}@typeName(T: type) [N]u8{#endsyntax#}

- This function returns the string representation of a type. + This function returns the string representation of a type, as + an array. It is equivalent to a string literal of the type name.

{#header_close#} diff --git a/src/all_types.hpp b/src/all_types.hpp index c490999a2bb6..c05c48f95f51 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -33,7 +33,7 @@ struct TypeStructField; struct CodeGen; struct ConstExprValue; struct IrInstruction; -struct IrInstructionCast; +struct IrInstructionAlloca; struct IrBasicBlock; struct ScopeDecls; struct ZigWindowsSDK; @@ -68,6 +68,8 @@ struct IrExecutable { Scope *begin_scope; ZigList tld_list; + IrInstruction *return_result_loc; + IrInstruction *coro_handle; IrInstruction *atomic_state_field_ptr; // this one is shared and in the promise IrInstruction *coro_result_ptr_field_ptr; @@ -1320,7 +1322,7 @@ struct ZigFn { AstNode *fn_no_inline_set_node; AstNode *fn_static_eval_set_node; - ZigList alloca_list; + ZigList alloca_list; ZigList variable_list; Buf *section_name; @@ -1885,6 +1887,7 @@ struct ScopeBlock { Buf *name; IrBasicBlock *end_block; IrInstruction *is_comptime; + IrInstruction *result_loc; ZigList *incoming_values; ZigList *incoming_blocks; @@ -1937,6 +1940,7 @@ struct ScopeLoop { IrBasicBlock *break_block; IrBasicBlock *continue_block; IrInstruction *is_comptime; + IrInstruction *result_loc; ZigList *incoming_values; ZigList *incoming_blocks; }; @@ -2029,6 +2033,8 @@ struct IrBasicBlock { enum IrInstructionId { IrInstructionIdInvalid, + IrInstructionIdDeclVarSrc, + IrInstructionIdDeclVarGen, IrInstructionIdBr, IrInstructionIdCondBr, IrInstructionIdSwitchBr, @@ -2037,7 +2043,6 @@ enum IrInstructionId { IrInstructionIdPhi, IrInstructionIdUnOp, IrInstructionIdBinOp, - IrInstructionIdDeclVar, IrInstructionIdLoadPtr, IrInstructionIdStorePtr, IrInstructionIdFieldPtr, @@ -2166,9 +2171,16 @@ enum IrInstructionId { IrInstructionIdMarkErrRetTracePtr, IrInstructionIdSqrt, IrInstructionIdErrSetCast, - IrInstructionIdToBytes, - IrInstructionIdFromBytes, IrInstructionIdCheckRuntimeScope, + IrInstructionIdResultErrorUnionPayload, + IrInstructionIdResultReturn, + IrInstructionIdResultBytesToSlice, + IrInstructionIdResultSliceToBytes, + IrInstructionIdResultParam, + IrInstructionIdResultPtrCast, + IrInstructionIdLoadResult, + IrInstructionIdStoreResult, + IrInstructionIdAlloca, }; struct IrInstruction { @@ -2190,6 +2202,21 @@ struct IrInstruction { bool is_gen; }; +struct IrInstructionDeclVarSrc { + IrInstruction base; + + ZigVar *var; + IrInstruction *var_type; + IrInstruction *align_value; + IrInstruction *ptr; +}; + +struct IrInstructionDeclVarGen { + IrInstruction base; + + ZigVar *var; +}; + struct IrInstructionCondBr { IrInstruction base; @@ -2303,25 +2330,30 @@ struct IrInstructionBinOp { bool safety_check_on; }; -struct IrInstructionDeclVar { +struct IrInstructionLoadPtr { IrInstruction base; - ZigVar *var; - IrInstruction *var_type; - IrInstruction *align_value; - IrInstruction *init_value; + IrInstruction *ptr; }; -struct IrInstructionLoadPtr { +struct IrInstructionStorePtr { IrInstruction base; IrInstruction *ptr; + IrInstruction *value; }; -struct IrInstructionStorePtr { +struct IrInstructionLoadResult { IrInstruction base; IrInstruction *ptr; + IrInstruction *result_loc; +}; + +struct IrInstructionStoreResult { + IrInstruction base; + + IrInstruction *result_loc; IrInstruction *value; }; @@ -2374,13 +2406,12 @@ struct IrInstructionCall { ZigFn *fn_entry; size_t arg_count; IrInstruction **args; - bool is_comptime; - LLVMValueRef tmp_ptr; - FnInline fn_inline; - bool is_async; - IrInstruction *async_allocator; IrInstruction *new_stack; + IrInstruction *result_loc; + FnInline fn_inline; + bool is_async; + bool is_comptime; }; struct IrInstructionConst { @@ -2401,23 +2432,24 @@ struct IrInstructionCast { IrInstruction base; IrInstruction *value; + IrInstruction *result_loc; ZigType *dest_type; CastOp cast_op; - LLVMValueRef tmp_ptr; }; struct IrInstructionContainerInitList { IrInstruction base; IrInstruction *container_type; + IrInstruction *result_loc; size_t item_count; IrInstruction **items; - LLVMValueRef tmp_ptr; }; struct IrInstructionContainerInitFieldsField { Buf *name; IrInstruction *value; + IrInstruction *result_loc; AstNode *source_node; TypeStructField *type_struct_field; }; @@ -2439,9 +2471,9 @@ struct IrInstructionStructInit { IrInstruction base; ZigType *struct_type; + IrInstruction *result_loc; size_t field_count; IrInstructionStructInitField *fields; - LLVMValueRef tmp_ptr; }; struct IrInstructionUnionInit { @@ -2450,7 +2482,7 @@ struct IrInstructionUnionInit { ZigType *union_type; TypeUnionField *field; IrInstruction *init_value; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; }; struct IrInstructionUnreachable { @@ -2523,9 +2555,9 @@ struct IrInstructionSliceType { IrInstruction base; IrInstruction *align_value; + IrInstruction *child_type; bool is_const; bool is_volatile; - IrInstruction *child_type; }; struct IrInstructionAsm { @@ -2600,7 +2632,7 @@ struct IrInstructionRef { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; bool is_const; bool is_volatile; }; @@ -2662,6 +2694,7 @@ struct IrInstructionCmpxchg { IrInstruction *new_value; IrInstruction *success_order_value; IrInstruction *failure_order_value; + IrInstruction *result_loc; // if this instruction gets to runtime then we know these values: ZigType *type; @@ -2669,8 +2702,6 @@ struct IrInstructionCmpxchg { AtomicOrder failure_order; bool is_weak; - - LLVMValueRef tmp_ptr; }; struct IrInstructionFence { @@ -2710,19 +2741,6 @@ struct IrInstructionErrSetCast { IrInstruction *target; }; -struct IrInstructionToBytes { - IrInstruction base; - - IrInstruction *target; -}; - -struct IrInstructionFromBytes { - IrInstruction base; - - IrInstruction *dest_child_type; - IrInstruction *target; -}; - struct IrInstructionIntToFloat { IrInstruction base; @@ -2778,8 +2796,8 @@ struct IrInstructionSlice { IrInstruction *ptr; IrInstruction *start; IrInstruction *end; + IrInstruction *result_loc; bool safety_check_on; - LLVMValueRef tmp_ptr; }; struct IrInstructionMemberCount { @@ -2867,21 +2885,21 @@ struct IrInstructionOptionalWrap { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; }; struct IrInstructionErrWrapPayload { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; }; struct IrInstructionErrWrapCode { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; }; struct IrInstructionFnProto { @@ -2983,6 +3001,7 @@ struct IrInstructionTypeName { IrInstruction base; IrInstruction *type_value; + IrInstruction *result_loc; }; enum LVal { @@ -3007,6 +3026,7 @@ struct IrInstructionTagName { IrInstruction base; IrInstruction *target; + IrInstruction *result_loc; }; struct IrInstructionTagType { @@ -3264,6 +3284,49 @@ struct IrInstructionCheckRuntimeScope { IrInstruction *is_comptime; }; +struct IrInstructionResultErrorUnionPayload { + IrInstruction base; + + IrInstruction *prev_result_loc; +}; + +struct IrInstructionResultBytesToSlice { + IrInstruction base; + + IrInstruction *prev_result_loc; +}; + +struct IrInstructionResultSliceToBytes { + IrInstruction base; + + IrInstruction *elem_type; + IrInstruction *prev_result_loc; +}; + +struct IrInstructionResultReturn { + IrInstruction base; +}; + +struct IrInstructionResultParam { + IrInstruction base; + + IrInstruction *fn_ref; + size_t param_index; +}; + +struct IrInstructionResultPtrCast { + IrInstruction base; + + IrInstruction *elem_type; + IrInstruction *prev_result_loc; +}; + +struct IrInstructionAlloca { + IrInstruction base; + + IrInstruction *ty; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index 71de547353d6..e4df2de81572 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2818,12 +2818,13 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, return expr_val; case CastOpResizeSlice: { - assert(cast_instruction->tmp_ptr); assert(wanted_type->id == ZigTypeIdStruct); assert(wanted_type->data.structure.is_slice); assert(actual_type->id == ZigTypeIdStruct); assert(actual_type->data.structure.is_slice); + LLVMValueRef result_ptr = ir_llvm_value(g, cast_instruction->result_loc); + ZigType *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; ZigType *actual_child_type = actual_pointer_type->data.pointer.child_type; ZigType *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; @@ -2839,7 +2840,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, wanted_type->data.structure.fields[0].type_entry->type_ref, ""); - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, result_ptr, (unsigned)wanted_ptr_index, ""); gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); @@ -2872,38 +2873,39 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, zig_unreachable(); } - LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, result_ptr, (unsigned)wanted_len_index, ""); gen_store_untyped(g, new_len, dest_len_ptr, 0, false); - return cast_instruction->tmp_ptr; + return result_ptr; } case CastOpBytesToSlice: { - assert(cast_instruction->tmp_ptr); assert(wanted_type->id == ZigTypeIdStruct); assert(wanted_type->data.structure.is_slice); assert(actual_type->id == ZigTypeIdArray); + LLVMValueRef result_ptr = ir_llvm_value(g, cast_instruction->result_loc); + ZigType *wanted_pointer_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index; - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, result_ptr, (unsigned)wanted_ptr_index, ""); LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, expr_val, wanted_pointer_type->type_ref, ""); gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index; - LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, result_ptr, (unsigned)wanted_len_index, ""); LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, actual_type->data.array.len / type_size(g, wanted_child_type), false); gen_store_untyped(g, len_val, len_ptr, 0, false); - return cast_instruction->tmp_ptr; + return result_ptr; } case CastOpIntToFloat: assert(actual_type->id == ZigTypeIdInt); @@ -2959,12 +2961,13 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, case CastOpBitCast: return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); case CastOpPtrOfArrayToSlice: { - assert(cast_instruction->tmp_ptr); assert(actual_type->id == ZigTypeIdPointer); ZigType *array_type = actual_type->data.pointer.child_type; assert(array_type->id == ZigTypeIdArray); - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef result_ptr = ir_llvm_value(g, cast_instruction->result_loc); + + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, slice_ptr_index, ""); LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->type_ref), @@ -2973,13 +2976,12 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - slice_len_index, ""); + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, slice_len_index, ""); LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false); gen_store_untyped(g, len_value, len_field_ptr, 0, false); - return cast_instruction->tmp_ptr; + return result_ptr; } } zig_unreachable(); @@ -3174,7 +3176,7 @@ static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutable *executable, IrI } static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, - IrInstructionDeclVar *decl_var_instruction) + IrInstructionDeclVarGen *decl_var_instruction) { ZigVar *var = decl_var_instruction->var; @@ -3184,38 +3186,6 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, if (var->ref_count == 0 && g->build_mode != BuildModeDebug) return nullptr; - IrInstruction *init_value = decl_var_instruction->init_value; - - bool have_init_expr = false; - - ConstExprValue *const_val = &init_value->value; - if (const_val->special == ConstValSpecialRuntime || const_val->special == ConstValSpecialStatic) - have_init_expr = true; - - if (have_init_expr) { - assert(var->value->type == init_value->value.type); - ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, - PtrLenSingle, var->align_bytes, 0, 0); - LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); - gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); - } else { - bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); - if (want_safe) { - ZigType *usize = g->builtin_types.entry_usize; - uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, var->value->type->type_ref); - assert(size_bytes > 0); - - assert(var->align_bytes > 0); - - // memset uninitialized memory to 0xa - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); - LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, var->value_ref, ptr_u8, ""); - LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); - ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, var->align_bytes, false); - } - } - gen_var_debug_decl(g, var); return nullptr; } @@ -3444,8 +3414,13 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id); bool is_var_args = fn_type_id->is_var_args; ZigList gen_param_values = {}; + LLVMValueRef result_ptr = nullptr; + if (first_arg_ret || instruction->is_async) { + result_ptr = ir_llvm_value(g, instruction->result_loc); + } + if (first_arg_ret) { - gen_param_values.append(instruction->tmp_ptr); + gen_param_values.append(result_ptr); } if (prefix_arg_err_ret_stack) { gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope)); @@ -3453,7 +3428,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (instruction->is_async) { gen_param_values.append(ir_llvm_value(g, instruction->async_allocator)); - LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, result_ptr, err_union_err_index, ""); gen_param_values.append(err_val_ptr); } FnWalk fn_walk = {}; @@ -3496,9 +3471,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (instruction->is_async) { - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); + LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, result_ptr, err_union_payload_index, ""); LLVMBuildStore(g->builder, result, payload_ptr); - return instruction->tmp_ptr; + return result_ptr; } if (src_return_type->id == ZigTypeIdUnreachable) { @@ -3507,11 +3482,11 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr return nullptr; } else if (first_arg_ret) { set_call_instr_sret(g, result); - return instruction->tmp_ptr; + return result_ptr; } else if (handle_is_ptr(src_return_type)) { - auto store_instr = LLVMBuildStore(g->builder, result, instruction->tmp_ptr); - LLVMSetAlignment(store_instr, LLVMGetAlignment(instruction->tmp_ptr)); - return instruction->tmp_ptr; + auto store_instr = LLVMBuildStore(g->builder, result, result_ptr); + LLVMSetAlignment(store_instr, LLVMGetAlignment(result_ptr)); + return result_ptr; } else { return result; } @@ -3883,9 +3858,9 @@ static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstru if (handle_is_ptr(instruction->value->value.type)) { return value; } else { - assert(instruction->tmp_ptr); - gen_store_untyped(g, value, instruction->tmp_ptr, 0, false); - return instruction->tmp_ptr; + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + gen_store_untyped(g, value, result_ptr, 0, false); + return result_ptr; } } @@ -4187,18 +4162,19 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, ""); } - assert(instruction->tmp_ptr != nullptr); assert(type_has_bits(instruction->type)); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_ptr, maybe_child_index, ""); gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val); LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_ptr, maybe_null_index, ""); gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false); - return instruction->tmp_ptr; + return result_ptr; } static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) { @@ -4261,15 +4237,13 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrIns } static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInstructionSlice *instruction) { - assert(instruction->tmp_ptr); - LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); ZigType *array_ptr_type = instruction->ptr->value.type; assert(array_ptr_type->id == ZigTypeIdPointer); ZigType *array_type = array_ptr_type->data.pointer.child_type; LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); - LLVMValueRef tmp_struct_ptr = instruction->tmp_ptr; + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); @@ -4603,15 +4577,15 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I return payload_val; } - assert(instruction->tmp_ptr); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_ptr, maybe_child_index, ""); // child_type and instruction->value->value.type may differ by constness gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_ptr, maybe_null_index, ""); gen_store_untyped(g, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr, 0, false); - return instruction->tmp_ptr; + return result_ptr; } static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapCode *instruction) { @@ -4627,12 +4601,12 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable if (!type_has_bits(payload_type) || !type_has_bits(err_set_type)) return err_val; - assert(instruction->tmp_ptr); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); + LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_ptr, err_union_err_index, ""); gen_store_untyped(g, err_val, err_tag_ptr, 0, false); - return instruction->tmp_ptr; + return result_ptr; } static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapPayload *instruction) { @@ -4652,17 +4626,17 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa if (!type_has_bits(payload_type)) return ok_err_val; - assert(instruction->tmp_ptr); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); + LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_ptr, err_union_err_index, ""); gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false); - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); + LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, result_ptr, err_union_payload_index, ""); gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, payload_type, false), payload_val); - return instruction->tmp_ptr; + return result_ptr; } static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, IrInstructionUnionTag *instruction) { @@ -4683,14 +4657,18 @@ static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, Ir return get_handle_value(g, tag_field_ptr, tag_type, ptr_type); } -static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, IrInstructionStructInit *instruction) { +static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, + IrInstructionStructInit *instruction) +{ + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + for (size_t i = 0; i < instruction->field_count; i += 1) { IrInstructionStructInitField *field = &instruction->fields[i]; TypeStructField *type_struct_field = field->type_struct_field; if (!type_has_bits(type_struct_field->type_entry)) continue; - LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, + LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, (unsigned)type_struct_field->gen_index, ""); LLVMValueRef value = ir_llvm_value(g, field->value); @@ -4703,7 +4681,7 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, gen_assign_raw(g, field_ptr, ptr_type, value); } - return instruction->tmp_ptr; + return result_ptr; } static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, IrInstructionUnionInit *instruction) { @@ -4717,22 +4695,24 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I false, false, PtrLenSingle, field_align_bytes, 0, 0); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef uncasted_union_ptr; // Even if safety is off in this block, if the union type has the safety field, we have to populate it // correctly. Otherwise safety code somewhere other than here could fail. ZigType *union_type = instruction->union_type; if (union_type->data.unionation.gen_tag_index != SIZE_MAX) { - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, + LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, union_type->data.unionation.gen_tag_index, ""); LLVMValueRef tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, &type_union_field->enum_field->value); gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); - uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, + uncasted_union_ptr = LLVMBuildStructGEP(g->builder, result_ptr, (unsigned)union_type->data.unionation.gen_union_index, ""); } else { - uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, (unsigned)0, ""); + uncasted_union_ptr = LLVMBuildStructGEP(g->builder, result_ptr, (unsigned)0, ""); } LLVMValueRef field_ptr = LLVMBuildBitCast(g->builder, uncasted_union_ptr, ptr_type->type_ref, ""); @@ -4740,7 +4720,7 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I gen_assign_raw(g, field_ptr, ptr_type, value); - return instruction->tmp_ptr; + return result_ptr; } static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *executable, @@ -4748,7 +4728,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec { ZigType *array_type = instruction->base.value.type; assert(array_type->id == ZigTypeIdArray); - LLVMValueRef tmp_array_ptr = instruction->tmp_ptr; + LLVMValueRef tmp_array_ptr = ir_llvm_value(g, instruction->result_loc); assert(tmp_array_ptr); size_t field_count = instruction->item_count; @@ -5142,16 +5122,15 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: case IrInstructionIdErrSetCast: - case IrInstructionIdFromBytes: - case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: case IrInstructionIdCheckRuntimeScope: + case IrInstructionIdDeclVarSrc: zig_unreachable(); + case IrInstructionIdDeclVarGen: + return ir_render_decl_var(g, executable, (IrInstructionDeclVarGen *)instruction); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); - case IrInstructionIdDeclVar: - return ir_render_decl_var(g, executable, (IrInstructionDeclVar *)instruction); case IrInstructionIdBinOp: return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction); case IrInstructionIdCast: @@ -5308,6 +5287,24 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); case IrInstructionIdSqrt: return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); + case IrInstructionIdResultErrorUnionPayload: + zig_panic("TODO"); + case IrInstructionIdResultReturn: + zig_panic("TODO"); + case IrInstructionIdResultSliceToBytes: + zig_panic("TODO"); + case IrInstructionIdResultBytesToSlice: + zig_panic("TODO"); + case IrInstructionIdResultParam: + zig_panic("TODO"); + case IrInstructionIdResultPtrCast: + zig_panic("TODO"); + case IrInstructionIdLoadResult: + zig_panic("TODO"); + case IrInstructionIdStoreResult: + zig_panic("TODO"); + case IrInstructionIdAlloca: + zig_panic("TODO"); } zig_unreachable(); } @@ -6196,48 +6193,9 @@ static void do_code_gen(CodeGen *g) { // allocate temporary stack data for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) { - IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i); - LLVMValueRef *slot; - ZigType *slot_type = instruction->value.type; - if (instruction->id == IrInstructionIdCast) { - IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction; - slot = &cast_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdRef) { - IrInstructionRef *ref_instruction = (IrInstructionRef *)instruction; - slot = &ref_instruction->tmp_ptr; - assert(instruction->value.type->id == ZigTypeIdPointer); - slot_type = instruction->value.type->data.pointer.child_type; - } else if (instruction->id == IrInstructionIdContainerInitList) { - IrInstructionContainerInitList *container_init_list_instruction = (IrInstructionContainerInitList *)instruction; - slot = &container_init_list_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdStructInit) { - IrInstructionStructInit *struct_init_instruction = (IrInstructionStructInit *)instruction; - slot = &struct_init_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdUnionInit) { - IrInstructionUnionInit *union_init_instruction = (IrInstructionUnionInit *)instruction; - slot = &union_init_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdCall) { - IrInstructionCall *call_instruction = (IrInstructionCall *)instruction; - slot = &call_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdSlice) { - IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction; - slot = &slice_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdOptionalWrap) { - IrInstructionOptionalWrap *maybe_wrap_instruction = (IrInstructionOptionalWrap *)instruction; - slot = &maybe_wrap_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdErrWrapPayload) { - IrInstructionErrWrapPayload *err_wrap_payload_instruction = (IrInstructionErrWrapPayload *)instruction; - slot = &err_wrap_payload_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdErrWrapCode) { - IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; - slot = &err_wrap_code_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdCmpxchg) { - IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction; - slot = &cmpxchg_instruction->tmp_ptr; - } else { - zig_unreachable(); - } - *slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type)); + IrInstructionAlloca *instruction = fn_table_entry->alloca_list.at(alloca_i); + ZigType *slot_type = instruction->base.value.type; + instruction->base.llvm_value = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type)); } ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); diff --git a/src/ir.cpp b/src/ir.cpp index 83ad9c020dc2..dbb2554ba03b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -138,8 +138,8 @@ struct ConstCastErrSetMismatch { ZigList missing_errors; }; -static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); -static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval); +static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope, + LVal lval, IrInstruction *result_loc); static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction); static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type); static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr); @@ -295,6 +295,14 @@ static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) { return new_bb; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarSrc *) { + return IrInstructionIdDeclVarSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarGen *) { + return IrInstructionIdDeclVarGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) { return IrInstructionIdCondBr; } @@ -327,10 +335,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) { return IrInstructionIdBinOp; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVar *) { - return IrInstructionIdDeclVar; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionExport *) { return IrInstructionIdExport; } @@ -343,6 +347,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) { return IrInstructionIdStorePtr; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadResult *) { + return IrInstructionIdLoadResult; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionStoreResult *) { + return IrInstructionIdStoreResult; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldPtr *) { return IrInstructionIdFieldPtr; } @@ -535,14 +547,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrSetCast *) { return IrInstructionIdErrSetCast; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionToBytes *) { - return IrInstructionIdToBytes; -} - -static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytes *) { - return IrInstructionIdFromBytes; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToFloat *) { return IrInstructionIdIntToFloat; } @@ -855,6 +859,34 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScop return IrInstructionIdCheckRuntimeScope; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultErrorUnionPayload *) { + return IrInstructionIdResultErrorUnionPayload; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultReturn *) { + return IrInstructionIdResultReturn; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultParam *) { + return IrInstructionIdResultParam; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultPtrCast *) { + return IrInstructionIdResultPtrCast; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSliceToBytes *) { + return IrInstructionIdResultSliceToBytes; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultBytesToSlice *) { + return IrInstructionIdResultBytesToSlice; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) { + return IrInstructionIdAlloca; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -1161,7 +1193,7 @@ static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, Ast static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, - IrInstruction *new_stack) + IrInstruction *new_stack, IrInstruction *result_loc) { IrInstructionCall *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; @@ -1173,6 +1205,7 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->is_async = is_async; call_instruction->async_allocator = async_allocator; call_instruction->new_stack = new_stack; + call_instruction->result_loc = result_loc; if (fn_ref) ir_ref_instruction(fn_ref, irb->current_basic_block); @@ -1183,6 +1216,8 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); + return &call_instruction->base; } @@ -1341,20 +1376,29 @@ static IrInstruction *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, - ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value) +static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *ptr) { - IrInstructionDeclVar *decl_var_instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionDeclVarSrc *decl_var_instruction = ir_build_instruction(irb, scope, source_node); decl_var_instruction->base.value.special = ConstValSpecialStatic; decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void; decl_var_instruction->var = var; decl_var_instruction->var_type = var_type; decl_var_instruction->align_value = align_value; - decl_var_instruction->init_value = init_value; + decl_var_instruction->ptr = ptr; if (var_type) ir_ref_instruction(var_type, irb->current_basic_block); if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); - ir_ref_instruction(init_value, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + + return &decl_var_instruction->base; +} + +static IrInstruction *ir_build_var_decl_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var) { + IrInstructionDeclVarGen *decl_var_instruction = ir_build_instruction(irb, scope, source_node); + decl_var_instruction->base.value.special = ConstValSpecialStatic; + decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void; + decl_var_instruction->var = var; return &decl_var_instruction->base; } @@ -1386,6 +1430,32 @@ static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } +static IrInstruction *ir_build_load_result(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *ptr, IrInstruction *result_loc) +{ + IrInstructionLoadResult *instruction = ir_build_instruction(irb, scope, source_node); + instruction->ptr = ptr; + instruction->result_loc = result_loc; + + ir_ref_instruction(ptr, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_store_result(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *result_loc, IrInstruction *value) +{ + IrInstructionStoreResult *instruction = ir_build_instruction(irb, scope, source_node); + instruction->result_loc = result_loc; + instruction->value = value; + + ir_ref_instruction(result_loc, irb->current_basic_block); + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_typeof(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionTypeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; @@ -1760,11 +1830,10 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, - IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, - IrInstruction *success_order_value, IrInstruction *failure_order_value, - bool is_weak, - ZigType *type, AtomicOrder success_order, AtomicOrder failure_order) +static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *type_value, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, + IrInstruction *success_order_value, IrInstruction *failure_order_value, bool is_weak, + ZigType *type, AtomicOrder success_order, AtomicOrder failure_order, IrInstruction *result_loc) { IrInstructionCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; @@ -1777,6 +1846,7 @@ static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *so instruction->type = type; instruction->success_order = success_order; instruction->failure_order = failure_order; + instruction->result_loc = result_loc; if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); @@ -1784,6 +1854,7 @@ static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *so ir_ref_instruction(new_value, irb->current_basic_block); if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -1842,26 +1913,6 @@ static IrInstruction *ir_build_err_set_cast(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } -static IrInstruction *ir_build_to_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { - IrInstructionToBytes *instruction = ir_build_instruction(irb, scope, source_node); - instruction->target = target; - - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstruction *ir_build_from_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_child_type, IrInstruction *target) { - IrInstructionFromBytes *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_child_type = dest_child_type; - instruction->target = target; - - ir_ref_instruction(dest_child_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { IrInstructionIntToFloat *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_type = dest_type; @@ -1944,16 +1995,18 @@ static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *sou } static IrInstruction *ir_build_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on) + IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on, IrInstruction *result_loc) { IrInstructionSlice *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; instruction->start = start; instruction->end = end; + instruction->result_loc = result_loc; instruction->safety_check_on = safety_check_on; ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(start, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); if (end) ir_ref_instruction(end, irb->current_basic_block); return &instruction->base; @@ -2261,13 +2314,15 @@ static IrInstruction *ir_build_check_statement_is_void(IrBuilder *irb, Scope *sc } static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *type_value) + IrInstruction *type_value, IrInstruction *result_loc) { IrInstructionTypeName *instruction = ir_build_instruction( irb, scope, source_node); instruction->type_value = type_value; + instruction->result_loc = result_loc; ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -2295,12 +2350,14 @@ static IrInstruction *ir_build_panic(IrBuilder *irb, Scope *scope, AstNode *sour } static IrInstruction *ir_build_tag_name(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *target) + IrInstruction *target, IrInstruction *result_loc) { IrInstructionTagName *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; + instruction->result_loc = result_loc; ir_ref_instruction(target, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -2720,6 +2777,79 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, return &instruction->base; } +static IrInstruction *ir_build_result_error_union_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc) +{ + IrInstructionResultErrorUnionPayload *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_return(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionResultReturn *instruction = ir_build_instruction(irb, scope, source_node); + return &instruction->base; +} + +static IrInstruction *ir_build_result_param(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *fn_ref, size_t param_index) +{ + IrInstructionResultParam *instruction = ir_build_instruction(irb, scope, source_node); + instruction->fn_ref = fn_ref; + + ir_ref_instruction(fn_ref, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *elem_type, IrInstruction *prev_result_loc) +{ + IrInstructionResultPtrCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->elem_type = elem_type; + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(elem_type, irb->current_basic_block); + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_slice_to_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *elem_type, IrInstruction *prev_result_loc) +{ + IrInstructionResultSliceToBytes *instruction = ir_build_instruction(irb, scope, source_node); + instruction->elem_type = elem_type; + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(elem_type, irb->current_basic_block); + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_bytes_to_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc) +{ + IrInstructionResultBytesToSlice *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ty) { + IrInstructionAlloca *instruction = ir_build_instruction(irb, scope, source_node); + instruction->ty = ty; + + if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -2778,12 +2908,16 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o { AstNode *defer_expr_node = defer_node->data.defer.expr; Scope *defer_expr_scope = defer_node->data.defer.expr_scope; - IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); + IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope, + LValNone, nullptr); if (defer_expr_value != irb->codegen->invalid_instruction) { - if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == ZigTypeIdUnreachable) { + if (defer_expr_value->value.type != nullptr && + defer_expr_value->value.type->id == ZigTypeIdUnreachable) + { is_noreturn = true; } else { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, + defer_expr_value)); } } } @@ -2905,7 +3039,9 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime); } -static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeReturnExpr); ZigFn *fn_entry = exec_fn_entry(irb->exec); @@ -2934,7 +3070,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, // Temporarily set this so that if we return a type it gets the name of the function ZigFn *prev_name_fn = irb->exec->name_fn; irb->exec->name_fn = exec_fn_entry(irb->exec); - return_value = ir_gen_node(irb, expr_node, scope); + return_value = ir_gen_node(irb, expr_node, scope, LValNone, irb->exec->return_result_loc); irb->exec->name_fn = prev_name_fn; if (return_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -2991,7 +3127,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindError: { assert(expr_node); - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); + IrInstruction *new_result_loc = ir_build_result_error_union_payload(irb, scope, node, result_loc); + IrInstruction *err_union_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, new_result_loc); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr); @@ -3104,7 +3241,9 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n return var; } -static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) { +static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node, + IrInstruction *result_loc) +{ assert(block_node->type == NodeTypeBlock); ZigList incoming_values = {0}; @@ -3126,6 +3265,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode } if (block_node->data.block.name != nullptr) { + scope_block->result_loc = result_loc; scope_block->incoming_blocks = &incoming_blocks; scope_block->incoming_values = &incoming_values; scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); @@ -3137,7 +3277,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) { AstNode *statement_node = block_node->data.block.statements.at(i); - IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope); + IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope, LValNone, nullptr); is_continuation_unreachable = instr_is_unreachable(statement_value); if (is_continuation_unreachable) { // keep the last noreturn statement value around in case we need to return it @@ -3147,9 +3287,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode // defer starts a new scope child_scope = statement_node->data.defer.child_scope; assert(child_scope); - } else if (statement_value->id == IrInstructionIdDeclVar) { + } else if (statement_value->id == IrInstructionIdDeclVarSrc) { // variable declarations start a new scope - IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)statement_value; + IrInstructionDeclVarSrc *decl_var_instruction = (IrInstructionDeclVarSrc *)statement_value; child_scope = decl_var_instruction->var->child_scope; } else if (statement_value != irb->codegen->invalid_instruction && !is_continuation_unreachable) { // this statement's value must be void @@ -3182,8 +3322,10 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode } static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { - IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); - IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + // All the binary ops operate either on scalar values or comptime values, so + // we do not need a result location. + IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValNone, nullptr); + IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); if (op1 == irb->codegen->invalid_instruction || op2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -3192,22 +3334,24 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no } static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) { - IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); - IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + IrInstruction *lvalue = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); + IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, lvalue); if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - ir_build_store_ptr(irb, scope, node, lvalue, rvalue); + ir_build_store_result(irb, scope, node, lvalue, rvalue); return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { - IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); + // All the assign ops operate on scalar values or comptime values, so + // we do not need a result location. + IrInstruction *lvalue = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); if (lvalue == irb->codegen->invalid_instruction) return lvalue; IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); - IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); if (op2 == irb->codegen->invalid_instruction) return op2; IrInstruction *result = ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); @@ -3218,7 +3362,7 @@ static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *no static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); - IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); + IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValNone, nullptr); if (val1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val1_block = irb->current_basic_block; @@ -3238,7 +3382,7 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, false_block); - IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); if (val2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val2_block = irb->current_basic_block; @@ -3260,7 +3404,7 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); - IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); + IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValNone, nullptr); if (val1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val1_block = irb->current_basic_block; @@ -3280,7 +3424,7 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, true_block); - IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); if (val2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val2_block = irb->current_basic_block; @@ -3299,13 +3443,13 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } -static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode *node, IrInstruction *result_loc) { assert(node->type == NodeTypeBinOpExpr); AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); + IrInstruction *maybe_ptr = ir_gen_node(irb, op1_node, parent_scope, LValPtr, nullptr); if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -3325,7 +3469,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, null_block); - IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope); + IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope, LValNone, result_loc); if (null_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *after_null_block = irb->current_basic_block; @@ -3334,7 +3478,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As ir_set_cursor_at_end_and_append_block(irb, ok_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, parent_scope, node, maybe_ptr, false); - IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); + IrInstruction *unwrapped_payload = ir_build_load_result(irb, parent_scope, node, unwrapped_ptr, result_loc); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); @@ -3354,18 +3498,18 @@ static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, As AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; - IrInstruction *err_set = ir_gen_node(irb, op1_node, parent_scope); + IrInstruction *err_set = ir_gen_node(irb, op1_node, parent_scope, LValNone, nullptr); if (err_set == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *payload = ir_gen_node(irb, op2_node, parent_scope); + IrInstruction *payload = ir_gen_node(irb, op2_node, parent_scope, LValNone, nullptr); if (payload == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; return ir_build_error_union(irb, parent_scope, node, err_set, payload); } -static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { assert(node->type == NodeTypeBinOpExpr); BinOpType bin_op_type = node->data.bin_op_expr.bin_op; @@ -3451,7 +3595,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) case BinOpTypeMergeErrorSets: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeUnwrapOptional: - return ir_gen_maybe_ok_or(irb, scope, node); + return ir_gen_orelse(irb, scope, node, result_loc); case BinOpTypeErrorUnion: return ir_gen_error_union(irb, scope, node); } @@ -3537,16 +3681,18 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, return irb->codegen->invalid_instruction; } -static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeArrayAccessExpr); AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; - IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr); + IrInstruction *array_ref_instruction = ir_gen_node(irb, array_ref_node, scope, LValPtr, nullptr); if (array_ref_instruction == irb->codegen->invalid_instruction) return array_ref_instruction; AstNode *subscript_node = node->data.array_access_expr.subscript; - IrInstruction *subscript_instruction = ir_gen_node(irb, subscript_node, scope); + IrInstruction *subscript_instruction = ir_gen_node(irb, subscript_node, scope, LValNone, nullptr); if (subscript_instruction == irb->codegen->invalid_instruction) return subscript_instruction; @@ -3555,7 +3701,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode if (lval == LValPtr) return ptr_instruction; - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_build_load_result(irb, scope, node, ptr_instruction, result_loc); } static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -3564,7 +3710,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode AstNode *container_ref_node = node->data.field_access_expr.struct_expr; Buf *field_name = node->data.field_access_expr.field_name; - IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr); + IrInstruction *container_ref_instruction = ir_gen_node(irb, container_ref_node, scope, LValPtr, nullptr); if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; @@ -3579,20 +3725,19 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * AstNode *op2_node = node->data.fn_call_expr.params.at(2); AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3); - - IrInstruction *type_value = ir_gen_node(irb, type_node, scope); + IrInstruction *type_value = ir_gen_node(irb, type_node, scope, LValNone, nullptr); if (type_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *op1 = ir_gen_node(irb, op1_node, scope); + IrInstruction *op1 = ir_gen_node(irb, op1_node, scope, LValNone, nullptr); if (op1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *op2 = ir_gen_node(irb, op2_node, scope); + IrInstruction *op2 = ir_gen_node(irb, op2_node, scope, LValNone, nullptr); if (op2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *result_ptr = ir_gen_node(irb, result_ptr_node, scope); + IrInstruction *result_ptr = ir_gen_node(irb, result_ptr_node, scope, LValNone, nullptr); if (result_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -3614,7 +3759,9 @@ static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *no zig_unreachable(); } -static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; @@ -3645,7 +3792,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdTypeof: { AstNode *arg_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg = ir_gen_node(irb, arg_node, scope); + IrInstruction *arg = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); if (arg == irb->codegen->invalid_instruction) return arg; @@ -3655,7 +3802,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdSetCold: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3665,7 +3812,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdSetRuntimeSafety: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3675,7 +3822,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdSetFloatMode: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3685,7 +3832,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdSizeof: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3695,7 +3842,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdCtz: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3705,7 +3852,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdPopCount: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3715,7 +3862,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdClz: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3725,7 +3872,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdImport: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3740,7 +3887,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdCInclude: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3755,12 +3902,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdCDefine: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -3775,7 +3922,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdCUndef: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3790,7 +3937,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdCompileErr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3803,7 +3950,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo for (size_t i = 0; i < actual_param_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i); - args[i] = ir_gen_node(irb, arg_node, scope); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); if (args[i] == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } @@ -3814,7 +3961,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdErrName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3824,7 +3971,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdEmbedFile: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3835,44 +3982,44 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdCmpxchgStrong: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); - IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); + IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope, LValNone, nullptr); if (arg3_value == irb->codegen->invalid_instruction) return arg3_value; AstNode *arg4_node = node->data.fn_call_expr.params.at(4); - IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope); + IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope, LValNone, nullptr); if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; AstNode *arg5_node = node->data.fn_call_expr.params.at(5); - IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope); + IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope, LValNone, nullptr); if (arg5_value == irb->codegen->invalid_instruction) return arg5_value; IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), - nullptr, AtomicOrderUnordered, AtomicOrderUnordered); + nullptr, AtomicOrderUnordered, AtomicOrderUnordered, result_loc); return ir_lval_wrap(irb, scope, cmpxchg, lval); } case BuiltinFnIdFence: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3882,12 +4029,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdDivExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -3897,12 +4044,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdDivTrunc: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -3912,12 +4059,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdDivFloor: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -3927,12 +4074,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdRem: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -3942,12 +4089,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdMod: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -3957,12 +4104,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdSqrt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -3972,12 +4119,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdTruncate: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -3987,12 +4134,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdIntCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4002,12 +4149,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdFloatCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4017,12 +4164,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdErrSetCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4032,37 +4179,43 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdFromBytes: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; + IrInstruction *new_result_loc = ir_build_result_slice_to_bytes(irb, scope, node, arg0_value, result_loc); + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, new_result_loc); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *result = ir_build_from_bytes(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + if (lval == LValPtr) + return result_loc; + return ir_build_load_result(irb, scope, node, result_loc, result_loc); } case BuiltinFnIdToBytes: { + IrInstruction *new_result_loc = ir_build_result_bytes_to_slice(irb, scope, node, result_loc); + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, new_result_loc); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *result = ir_build_to_bytes(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval); + if (lval == LValPtr) + return result_loc; + return ir_build_load_result(irb, scope, node, result_loc, result_loc); } case BuiltinFnIdIntToFloat: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4072,12 +4225,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdFloatToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4087,7 +4240,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdErrToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4097,7 +4250,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdIntToErr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4107,7 +4260,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdBoolToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4117,12 +4270,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdIntType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4132,17 +4285,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdMemcpy: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; @@ -4152,17 +4305,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdMemset: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; @@ -4172,7 +4325,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdMemberCount: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4182,12 +4335,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdMemberType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4198,12 +4351,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdMemberName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4214,12 +4367,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdField: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValPtr, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4228,12 +4381,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (lval == LValPtr) return ptr_instruction; - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_build_load_result(irb, scope, node, ptr_instruction, result_loc); } case BuiltinFnIdTypeInfo: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4259,7 +4412,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4277,17 +4430,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdTypeName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value); + IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value, result_loc); return ir_lval_wrap(irb, scope, type_name, lval); } case BuiltinFnIdPanic: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4297,12 +4450,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdPtrCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4312,27 +4465,31 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdBitCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; + IrInstruction *new_result_loc = ir_build_result_ptr_cast(irb, scope, node, arg0_value, result_loc); + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, new_result_loc); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *bit_cast = ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, bit_cast, lval); + if (lval == LValPtr) + return result_loc; + + return ir_build_load_result(irb, scope, node, result_loc, result_loc); } case BuiltinFnIdIntToPtr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4342,7 +4499,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdPtrToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4352,18 +4509,18 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdTagName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); 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, actual_tag, result_loc); return ir_lval_wrap(irb, scope, tag_name, lval); } case BuiltinFnIdTagType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4373,17 +4530,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdFieldParentPtr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; @@ -4393,12 +4550,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdByteOffsetOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4408,12 +4565,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdBitOffsetOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4429,7 +4586,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } AstNode *fn_ref_node = node->data.fn_call_expr.params.at(0); - IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope, LValNone, nullptr); if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; @@ -4437,14 +4594,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { + IrInstruction *param_result_loc = ir_build_result_param(irb, scope, node, fn_ref, i); AstNode *arg_node = node->data.fn_call_expr.params.at(i + 1); - args[i] = ir_gen_node(irb, arg_node, scope); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, param_result_loc); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; - IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr); + IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, + fn_inline, false, nullptr, nullptr, result_loc); return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdNewStackCall: @@ -4455,12 +4614,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } AstNode *new_stack_node = node->data.fn_call_expr.params.at(0); - IrInstruction *new_stack = ir_gen_node(irb, new_stack_node, scope); + IrInstruction *new_stack = ir_gen_node(irb, new_stack_node, scope, LValNone, nullptr); if (new_stack == irb->codegen->invalid_instruction) return new_stack; AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1); - IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope, LValNone, nullptr); if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; @@ -4468,19 +4627,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { + IrInstruction *param_result_loc = ir_build_result_param(irb, scope, node, fn_ref, i); AstNode *arg_node = node->data.fn_call_expr.params.at(i + 2); - args[i] = ir_gen_node(irb, arg_node, scope); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, param_result_loc); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } - IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack); + IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, + false, FnInlineAuto, false, nullptr, new_stack, result_loc); return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdTypeId: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4490,12 +4651,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdShlExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4505,12 +4666,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdShrExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4520,7 +4681,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdSetEvalBranchQuota: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4530,12 +4691,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdAlignCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4555,7 +4716,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdSetAlignStack: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4565,12 +4726,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdArgType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4580,17 +4741,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdExport: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; @@ -4605,27 +4766,27 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdAtomicRmw: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); - IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); + IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope, LValNone, nullptr); if (arg3_value == irb->codegen->invalid_instruction) return arg3_value; AstNode *arg4_node = node->data.fn_call_expr.params.at(4); - IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope); + IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope, LValNone, nullptr); if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; @@ -4637,17 +4798,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdAtomicLoad: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; @@ -4658,12 +4819,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdIntToEnum: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -4673,7 +4834,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdEnumToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4684,22 +4845,25 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo zig_unreachable(); } -static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeFnCallExpr); if (node->data.fn_call_expr.is_builtin) - return ir_gen_builtin_fn_call(irb, scope, node, lval); + return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc); AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; - IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope, LValNone, nullptr); if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; size_t arg_count = node->data.fn_call_expr.params.length; IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { + IrInstruction *param_result_loc = ir_build_result_param(irb, scope, node, fn_ref, i); AstNode *arg_node = node->data.fn_call_expr.params.at(i); - args[i] = ir_gen_node(irb, arg_node, scope); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, param_result_loc); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } @@ -4708,20 +4872,21 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node IrInstruction *async_allocator = nullptr; if (is_async) { if (node->data.fn_call_expr.async_allocator) { - async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope); + async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope, LValNone, nullptr); if (async_allocator == irb->codegen->invalid_instruction) return async_allocator; } } - IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, + is_async, async_allocator, nullptr, result_loc); return ir_lval_wrap(irb, scope, fn_call, lval); } -static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { assert(node->type == NodeTypeIfBoolExpr); - IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); + IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope, LValNone, nullptr); if (condition == irb->codegen->invalid_instruction) return condition; @@ -4744,7 +4909,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode ir_set_cursor_at_end_and_append_block(irb, then_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope, LValNone, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; @@ -4754,7 +4919,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { - else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); + else_expr_result = ir_gen_node(irb, else_node, subexpr_scope, LValNone, result_loc); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { @@ -4775,21 +4940,17 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } -static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { +static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); + IrInstruction *value = ir_gen_node(irb, expr_node, scope, LValNone, nullptr); if (value == irb->codegen->invalid_instruction) return value; return ir_build_un_op(irb, scope, node, op_id, value); } -static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { - return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone); -} - static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) { if (lval != LValPtr) return value; @@ -4812,14 +4973,14 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode IrInstruction *align_value; if (align_expr != nullptr) { - align_value = ir_gen_node(irb, align_expr, scope); + align_value = ir_gen_node(irb, align_expr, scope, LValNone, nullptr); if (align_value == irb->codegen->invalid_instruction) return align_value; } else { align_value = nullptr; } - IrInstruction *child_type = ir_gen_node(irb, expr_node, scope); + IrInstruction *child_type = ir_gen_node(irb, expr_node, scope, LValNone, nullptr); if (child_type == irb->codegen->invalid_instruction) return child_type; @@ -4860,7 +5021,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval) { - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); + IrInstruction *err_union_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, nullptr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -4878,7 +5039,7 @@ static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *nod assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - IrInstruction *value = ir_gen_node(irb, expr_node, scope); + IrInstruction *value = ir_gen_node(irb, expr_node, scope, LValNone, nullptr); if (value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -4905,19 +5066,21 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr), lval); + return ir_lval_wrap(irb, scope, ir_gen_node(irb, expr_node, scope, LValPtr, nullptr), lval); } } zig_unreachable(); } -static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeContainerInitExpr); AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; ContainerInitKind kind = container_init_expr->kind; - IrInstruction *container_type = ir_gen_node(irb, container_init_expr->type, scope); + IrInstruction *container_type = ir_gen_node(irb, container_init_expr->type, scope, LValNone, nullptr); if (container_type == irb->codegen->invalid_instruction) return container_type; @@ -4929,8 +5092,10 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A assert(entry_node->type == NodeTypeStructValueField); Buf *name = entry_node->data.struct_val_field.name; + IrInstruction *field_result_loc = ir_build_field_ptr(irb, scope, entry_node, result_loc, name); + AstNode *expr_node = entry_node->data.struct_val_field.expr; - IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope); + IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope, LValNone, field_result_loc); if (expr_value == irb->codegen->invalid_instruction) return expr_value; @@ -4944,7 +5109,10 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A IrInstruction **values = allocate(item_count); for (size_t i = 0; i < item_count; i += 1) { AstNode *expr_node = container_init_expr->entries.at(i); - IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope); + IrInstruction *index_value = ir_build_const_usize(irb, scope, expr_node, i); + IrInstruction *elem_result_loc = ir_build_elem_ptr(irb, scope, expr_node, result_loc, index_value, true, + PtrLenSingle); + IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope, LValNone, elem_result_loc); if (expr_value == irb->codegen->invalid_instruction) return expr_value; @@ -4968,7 +5136,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod IrInstruction *type_instruction; if (variable_declaration->type != nullptr) { - type_instruction = ir_gen_node(irb, variable_declaration->type, scope); + type_instruction = ir_gen_node(irb, variable_declaration->type, scope, LValNone, nullptr); if (type_instruction == irb->codegen->invalid_instruction) return type_instruction; } else { @@ -4983,7 +5151,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol, is_const, is_const, is_shadowable, is_comptime); - // we detect IrInstructionIdDeclVar in gen_block to make sure the next node + // we detect IrInstructionIdDeclVarSrc in gen_block to make sure the next node // is inside var->child_scope if (!is_extern && !variable_declaration->expr) { @@ -4994,7 +5162,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod IrInstruction *align_value = nullptr; if (variable_declaration->align_expr != nullptr) { - align_value = ir_gen_node(irb, variable_declaration->align_expr, scope); + align_value = ir_gen_node(irb, variable_declaration->align_expr, scope, LValNone, nullptr); if (align_value == irb->codegen->invalid_instruction) return align_value; } @@ -5004,20 +5172,23 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); } + IrInstruction *alloca = ir_build_alloca(irb, scope, node, type_instruction); + // Temporarily set the name of the IrExecutable to the VariableDeclaration // so that the struct or enum from the init expression inherits the name. Buf *old_exec_name = irb->exec->name; irb->exec->name = variable_declaration->symbol; - IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope); + IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope, LValNone, alloca); irb->exec->name = old_exec_name; if (init_value == irb->codegen->invalid_instruction) return init_value; - return ir_build_var_decl(irb, scope, node, var, type_instruction, align_value, init_value); + ir_build_store_result(irb, scope, node, alloca, init_value); + return ir_build_var_decl_src(irb, scope, node, var, type_instruction, align_value, alloca); } -static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { assert(node->type == NodeTypeWhileExpr); AstNode *continue_expr_node = node->data.while_expr.continue_expr; @@ -5052,7 +5223,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } else { payload_scope = subexpr_scope; } - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); + IrInstruction *err_val_ptr = ir_gen_node(irb, node->data.while_expr.condition, subexpr_scope, + LValPtr, nullptr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); @@ -5069,8 +5241,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node, err_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? - var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + ir_build_ref(irb, payload_scope, symbol_node, var_ptr_value, true, false) : var_ptr_value; + ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); } ZigList incoming_values = {0}; @@ -5080,10 +5252,12 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; + loop_scope->result_loc = result_loc; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; - IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); + IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, + LValNone, result_loc); if (body_result == irb->codegen->invalid_instruction) return body_result; @@ -5094,7 +5268,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope); + IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope, LValNone, nullptr); if (expr_result == irb->codegen->invalid_instruction) return expr_result; if (!instr_is_unreachable(expr_result)) @@ -5111,9 +5285,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n true, false, false, is_comptime); Scope *err_scope = err_var->child_scope; IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr); - ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); + ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); - else_result = ir_gen_node(irb, else_node, err_scope); + else_result = ir_gen_node(irb, else_node, err_scope, LValNone, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) @@ -5139,7 +5313,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, true, false, false, is_comptime); Scope *child_scope = payload_var->child_scope; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); + IrInstruction *maybe_val_ptr = ir_gen_node(irb, node->data.while_expr.condition, subexpr_scope, + LValPtr, nullptr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); @@ -5154,8 +5329,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? - var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + ir_build_ref(irb, child_scope, symbol_node, var_ptr_value, true, false) : var_ptr_value; + ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5164,10 +5339,12 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; + loop_scope->result_loc = result_loc; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; - IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); + IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, + LValNone, result_loc); if (body_result == irb->codegen->invalid_instruction) return body_result; @@ -5178,7 +5355,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, child_scope); + IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, child_scope, LValNone, nullptr); if (expr_result == irb->codegen->invalid_instruction) return expr_result; if (!instr_is_unreachable(expr_result)) @@ -5189,7 +5366,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); - else_result = ir_gen_node(irb, else_node, scope); + else_result = ir_gen_node(irb, else_node, scope, LValNone, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) @@ -5208,7 +5385,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else { ir_set_cursor_at_end_and_append_block(irb, cond_block); - IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope); + IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope, LValNone, nullptr); if (cond_val == irb->codegen->invalid_instruction) return cond_val; IrBasicBlock *after_cond_block = irb->current_basic_block; @@ -5229,10 +5406,12 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; + loop_scope->result_loc = result_loc; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; - IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); + IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, + LValNone, result_loc); if (body_result == irb->codegen->invalid_instruction) return body_result; @@ -5243,7 +5422,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope); + IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope, LValNone, nullptr); if (expr_result == irb->codegen->invalid_instruction) return expr_result; if (!instr_is_unreachable(expr_result)) @@ -5254,7 +5433,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); - else_result = ir_gen_node(irb, else_node, subexpr_scope); + else_result = ir_gen_node(irb, else_node, subexpr_scope, LValNone, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) @@ -5274,7 +5453,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } } -static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeForExpr); AstNode *array_node = node->data.for_expr.array_expr; @@ -5289,7 +5470,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo } assert(elem_node->type == NodeTypeSymbol); - IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPtr); + IrInstruction *array_val_ptr = ir_gen_node(irb, array_node, parent_scope, LValPtr, nullptr); if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; @@ -5311,9 +5492,8 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime); Scope *child_scope = elem_var->child_scope; - IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node); - ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); - IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var); + IrInstruction *elem_alloca = ir_build_alloca(irb, child_scope, elem_node, elem_var_type); + ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, elem_alloca); AstNode *index_var_source_node; ZigVar *index_var; @@ -5328,10 +5508,11 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo child_scope = index_var->child_scope; IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *index_alloca = ir_build_alloca(irb, child_scope, node, usize); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); - ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); - IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var); + ir_build_store_ptr(irb, child_scope, index_var_source_node, index_alloca, zero); + ir_build_var_decl_src(irb, child_scope, index_var_source_node, index_var, usize, nullptr, index_alloca); IrBasicBlock *cond_block = ir_create_basic_block(irb, child_scope, "ForCond"); @@ -5344,7 +5525,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_build_br(irb, child_scope, node, cond_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, cond_block); - IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_ptr); + IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca); IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); @@ -5358,7 +5539,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo } else { elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr); } - ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val)); + ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_alloca, elem_val)); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5366,24 +5547,25 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; + loop_scope->result_loc = result_loc; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; - IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base); + IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base, LValNone, result_loc); if (!instr_is_unreachable(body_result)) ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false); - ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)); + ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_alloca, new_index_val)); ir_build_br(irb, child_scope, node, cond_block, is_comptime); IrInstruction *else_result = nullptr; if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); - else_result = ir_gen_node(irb, else_node, parent_scope); + else_result = ir_gen_node(irb, else_node, parent_scope, LValNone, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) @@ -5441,11 +5623,11 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n return irb->codegen->invalid_instruction; } - IrInstruction *size_value = ir_gen_node(irb, size_node, scope); + IrInstruction *size_value = ir_gen_node(irb, size_node, scope, LValNone, nullptr); if (size_value == irb->codegen->invalid_instruction) return size_value; - IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope); + IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope, LValNone, nullptr); if (child_type == irb->codegen->invalid_instruction) return child_type; @@ -5453,14 +5635,14 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n } else { IrInstruction *align_value; if (align_expr != nullptr) { - align_value = ir_gen_node(irb, align_expr, scope); + align_value = ir_gen_node(irb, align_expr, scope, LValNone, nullptr); if (align_value == irb->codegen->invalid_instruction) return align_value; } else { align_value = nullptr; } - IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope); + IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope, LValNone, nullptr); if (child_type == irb->codegen->invalid_instruction) return child_type; @@ -5475,7 +5657,7 @@ static IrInstruction *ir_gen_promise_type(IrBuilder *irb, Scope *scope, AstNode IrInstruction *payload_type_value = nullptr; if (payload_type_node != nullptr) { - payload_type_value = ir_gen_node(irb, payload_type_node, scope); + payload_type_value = ir_gen_node(irb, payload_type_node, scope, LValNone, nullptr); if (payload_type_value == irb->codegen->invalid_instruction) return payload_type_value; @@ -5507,7 +5689,7 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod if (asm_output->return_type) { return_count += 1; - IrInstruction *return_type = ir_gen_node(irb, asm_output->return_type, scope); + IrInstruction *return_type = ir_gen_node(irb, asm_output->return_type, scope, LValNone, nullptr); if (return_type == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; if (return_count > 1) { @@ -5532,7 +5714,7 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod } for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) { AsmInput *asm_input = node->data.asm_expr.input_list.at(i); - IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope); + IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope, LValNone, nullptr); if (input_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5542,7 +5724,9 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile); } -static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeTestExpr); Buf *var_symbol = node->data.test_expr.var_symbol; @@ -5551,7 +5735,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no AstNode *else_node = node->data.test_expr.else_node; bool var_is_ptr = node->data.test_expr.var_is_ptr; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); + IrInstruction *maybe_val_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, nullptr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; @@ -5581,14 +5765,17 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); + IrInstruction *payload_alloca = ir_build_alloca(irb, scope, node, nullptr); IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); - IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_result(irb, subexpr_scope, node, + var_ptr_value, payload_alloca); + ir_build_store_result(irb, subexpr_scope, node, payload_alloca, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, payload_alloca); var_scope = var->child_scope; } else { var_scope = subexpr_scope; } - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope, LValNone, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; @@ -5598,7 +5785,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { - else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); + else_expr_result = ir_gen_node(irb, else_node, subexpr_scope, LValNone, result_loc); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { @@ -5619,7 +5806,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } -static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { assert(node->type == NodeTypeIfErrorExpr); AstNode *target_node = node->data.if_err_expr.target_node; @@ -5630,7 +5817,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * Buf *var_symbol = node->data.if_err_expr.var_symbol; Buf *err_symbol = node->data.if_err_expr.err_symbol; - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); + IrInstruction *err_val_ptr = ir_gen_node(irb, target_node, scope, LValPtr, nullptr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; @@ -5656,14 +5843,17 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); + IrInstruction *payload_alloca = ir_build_alloca(irb, scope, node, nullptr); IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); - IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + IrInstruction *var_value = var_is_ptr ? + var_ptr_value : ir_build_load_result(irb, subexpr_scope, node, var_ptr_value, payload_alloca); + ir_build_store_result(irb, subexpr_scope, node, payload_alloca, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, payload_alloca); var_scope = var->child_scope; } else { var_scope = subexpr_scope; } - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope, LValNone, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; @@ -5683,12 +5873,12 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * err_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstruction *var_value = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); err_var_scope = var->child_scope; } else { err_var_scope = subexpr_scope; } - else_expr_result = ir_gen_node(irb, else_node, err_var_scope); + else_expr_result = ir_gen_node(irb, else_node, err_var_scope, LValNone, result_loc); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { @@ -5712,7 +5902,8 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, IrBasicBlock *end_block, IrInstruction *is_comptime, IrInstruction *var_is_comptime, IrInstruction *target_value_ptr, IrInstruction *prong_value, - ZigList *incoming_blocks, ZigList *incoming_values) + ZigList *incoming_blocks, ZigList *incoming_values, + IrInstruction *result_loc) { assert(switch_node->type == NodeTypeSwitchExpr); assert(prong_node->type == NodeTypeSwitchProng); @@ -5731,19 +5922,23 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit var_name, is_const, is_const, is_shadowable, var_is_comptime); child_scope = var->child_scope; IrInstruction *var_value; + IrInstruction *payload_alloca = ir_build_alloca(irb, scope, var_symbol_node, nullptr); if (prong_value) { IrInstruction *var_ptr_value = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); - var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, var_symbol_node, var_ptr_value); + var_value = var_is_ptr ? + var_ptr_value : ir_build_load_result(irb, scope, var_symbol_node, var_ptr_value, payload_alloca); } else { - var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); + var_value = var_is_ptr ? + target_value_ptr : ir_build_load_result(irb, scope, var_symbol_node, target_value_ptr, payload_alloca); } IrInstruction *var_type = nullptr; // infer the type - ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); + ir_build_store_result(irb, scope, var_symbol_node, payload_alloca, var_value); + ir_build_var_decl_src(irb, scope, var_symbol_node, var, var_type, nullptr, payload_alloca); } else { child_scope = scope; } - IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope); + IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope, LValNone, result_loc); if (expr_result == irb->codegen->invalid_instruction) return false; if (!instr_is_unreachable(expr_result)) @@ -5753,11 +5948,11 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit return true; } -static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { assert(node->type == NodeTypeSwitchExpr); AstNode *target_node = node->data.switch_expr.expr; - IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); + IrInstruction *target_value_ptr = ir_gen_node(irb, target_node, scope, LValPtr, nullptr); if (target_value_ptr == irb->codegen->invalid_instruction) return target_value_ptr; IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr); @@ -5802,7 +5997,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * IrBasicBlock *prev_block = irb->current_basic_block; 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, &incoming_blocks, &incoming_values)) + is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values, + result_loc)) { return irb->codegen->invalid_instruction; } @@ -5817,11 +6013,11 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * AstNode *start_node = item_node->data.switch_range.start; AstNode *end_node = item_node->data.switch_range.end; - IrInstruction *start_value = ir_gen_node(irb, start_node, comptime_scope); + IrInstruction *start_value = ir_gen_node(irb, start_node, comptime_scope, LValNone, nullptr); if (start_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *end_value = ir_gen_node(irb, end_node, comptime_scope); + IrInstruction *end_value = ir_gen_node(irb, end_node, comptime_scope, LValNone, nullptr); if (end_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5841,7 +6037,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * ok_bit = both_ok; } } else { - IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope); + IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope, LValNone, nullptr); if (item_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5869,7 +6065,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * ir_set_cursor_at_end_and_append_block(irb, range_block_yes); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, - is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) + is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values, + result_loc)) { return irb->codegen->invalid_instruction; } @@ -5894,7 +6091,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); assert(item_node->type != NodeTypeSwitchRange); - IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope); + IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope, LValNone, nullptr); if (item_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5913,7 +6110,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * IrBasicBlock *prev_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, prong_block); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, - is_comptime, var_is_comptime, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values)) + is_comptime, var_is_comptime, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values, + result_loc)) { return irb->codegen->invalid_instruction; } @@ -5945,14 +6143,18 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * } } -static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, + LVal lval, IrInstruction *result_loc) +{ assert(node->type == NodeTypeCompTime); Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope); - return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval); + return ir_gen_node(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); } -static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { +static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, + ScopeBlock *block_scope) +{ IrInstruction *is_comptime; if (ir_should_inline(irb->exec, break_scope)) { is_comptime = ir_build_const_bool(irb, break_scope, node, true); @@ -5962,7 +6164,7 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop IrInstruction *result_value; if (node->data.break_expr.expr) { - result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope); + result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope, LValNone, block_scope->result_loc); if (result_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { @@ -6032,7 +6234,7 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode * IrInstruction *result_value; if (node->data.break_expr.expr) { - result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope); + result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope, LValNone, loop_scope->result_loc); if (result_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { @@ -6120,7 +6322,7 @@ static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode return ir_build_const_void(irb, parent_scope, node); } -static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { assert(node->type == NodeTypeSliceExpr); AstNodeSliceExpr *slice_expr = &node->data.slice_expr; @@ -6128,27 +6330,27 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) AstNode *start_node = slice_expr->start; AstNode *end_node = slice_expr->end; - IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr); + IrInstruction *ptr_value = ir_gen_node(irb, array_node, scope, LValPtr, nullptr); if (ptr_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *start_value = ir_gen_node(irb, start_node, scope); + IrInstruction *start_value = ir_gen_node(irb, start_node, scope, LValNone, nullptr); if (start_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *end_value; if (end_node) { - end_value = ir_gen_node(irb, end_node, scope); + end_value = ir_gen_node(irb, end_node, scope, LValNone, nullptr); if (end_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { end_value = nullptr; } - return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true); + return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true, result_loc); } -static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node, IrInstruction *result_loc) { assert(node->type == NodeTypeUnwrapErrorExpr); AstNode *op1_node = node->data.unwrap_err_expr.op1; @@ -6165,8 +6367,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LValNone); } - - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); + IrInstruction *err_union_ptr = ir_gen_node(irb, op1_node, parent_scope, LValPtr, nullptr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6180,9 +6381,9 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_err); } - IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrOk"); - IrBasicBlock *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError"); - IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrEnd"); + IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "CatchOk"); + IrBasicBlock *err_block = ir_create_basic_block(irb, parent_scope, "CatchError"); + IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "CatchEnd"); ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, err_block); @@ -6196,11 +6397,11 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr); - ir_build_var_decl(irb, err_scope, var_node, var, nullptr, nullptr, err_val); + ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, err_val); } else { err_scope = parent_scope; } - IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope); + IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope, LValNone, result_loc); if (err_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *after_err_block = irb->current_basic_block; @@ -6421,7 +6622,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo } if (param_node->data.param_decl.var_token == nullptr) { AstNode *type_node = param_node->data.param_decl.type; - IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope); + IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope, LValNone, nullptr); if (type_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; param_types[i] = type_value; @@ -6432,7 +6633,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *align_value = nullptr; if (node->data.fn_proto.align_expr != nullptr) { - align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope); + align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope, LValNone, nullptr); if (align_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } @@ -6442,7 +6643,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo if (node->data.fn_proto.return_type == nullptr) { return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void); } else { - return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope); + return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope, LValNone, nullptr); if (return_type == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } @@ -6455,7 +6656,8 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *async_allocator_type_value = nullptr; if (node->data.fn_proto.async_allocator_type != nullptr) { - async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope); + async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope, + LValNone, nullptr); if (async_allocator_type_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } @@ -6538,7 +6740,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeCancel); - IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope, LValNone, nullptr); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6597,7 +6799,7 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeResume); - IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope, LValNone, nullptr); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6607,7 +6809,7 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAwaitExpr); - IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope, LValNone, nullptr); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6665,7 +6867,6 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); - IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 @@ -6679,9 +6880,9 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); - ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); - IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var); - ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr); + IrInstruction *result_var_alloca = ir_build_alloca(irb, scope, node, promise_result_type); + ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, result_var_alloca); + ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, result_var_alloca); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle); @@ -6723,7 +6924,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr); - ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result); + ir_build_store_ptr(irb, scope, node, result_var_alloca, no_suspend_result); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, merge_block, const_bool_false); @@ -6782,7 +6983,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); - return ir_build_load_ptr(irb, scope, node, my_result_var_ptr); + return ir_build_load_ptr(irb, scope, node, result_var_alloca); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -6875,7 +7076,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod suspend_scope->resume_block = resume_block; child_scope = &suspend_scope->base; IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle); - ir_gen_node(irb, node->data.suspend.block, child_scope); + ir_gen_node(irb, node->data.suspend.block, child_scope, LValNone, nullptr); suspend_code = ir_mark_gen(ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false)); } @@ -6902,8 +7103,8 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); } -static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, - LVal lval) +static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval, + IrInstruction *result_loc) { assert(scope); switch (node->type) { @@ -6918,11 +7119,11 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeTestDecl: zig_unreachable(); case NodeTypeBlock: - return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node, result_loc), lval); case NodeTypeGroupedExpr: - return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval); + return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc); case NodeTypeBinOpExpr: - return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node, result_loc), lval); case NodeTypeIntLiteral: return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval); case NodeTypeFloatLiteral: @@ -6932,23 +7133,23 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSymbol: return ir_gen_symbol(irb, scope, node, lval); case NodeTypeFnCallExpr: - return ir_gen_fn_call(irb, scope, node, lval); + return ir_gen_fn_call(irb, scope, node, lval, result_loc); case NodeTypeIfBoolExpr: - return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node, result_loc), lval); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval); case NodeTypeContainerInitExpr: - return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node, result_loc), lval); case NodeTypeVariableDeclaration: return ir_lval_wrap(irb, scope, ir_gen_var_decl(irb, scope, node), lval); case NodeTypeWhileExpr: - return ir_lval_wrap(irb, scope, ir_gen_while_expr(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_while_expr(irb, scope, node, result_loc), lval); case NodeTypeForExpr: - return ir_lval_wrap(irb, scope, ir_gen_for_expr(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_for_expr(irb, scope, node, result_loc), lval); case NodeTypeArrayAccessExpr: - return ir_gen_array_access(irb, scope, node, lval); + return ir_gen_array_access(irb, scope, node, lval, result_loc); case NodeTypeReturnExpr: - return ir_gen_return(irb, scope, node, lval); + return ir_gen_return(irb, scope, node, lval, result_loc); case NodeTypeFieldAccessExpr: { IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node); @@ -6961,7 +7162,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop } case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; - IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); + IrInstruction *value = ir_gen_node(irb, expr_node, scope, lval, nullptr); if (value == irb->codegen->invalid_instruction) return value; @@ -6970,7 +7171,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeUnwrapOptional: { AstNode *expr_node = node->data.unwrap_optional.expr; - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); + IrInstruction *maybe_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, nullptr); if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6997,13 +7198,13 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeNullLiteral: return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); case NodeTypeIfErrorExpr: - return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node, result_loc), lval); case NodeTypeTestExpr: - return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_if_optional_expr(irb, scope, node, result_loc), lval); case NodeTypeSwitchExpr: - return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node, result_loc), lval); case NodeTypeCompTime: - return ir_gen_comptime(irb, scope, node, lval); + return ir_gen_comptime(irb, scope, node, lval, result_loc); case NodeTypeErrorType: return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval); case NodeTypeBreak: @@ -7015,9 +7216,9 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeDefer: return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval); case NodeTypeSliceExpr: - return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node, result_loc), lval); case NodeTypeUnwrapErrorExpr: - return ir_lval_wrap(irb, scope, ir_gen_err_ok_or(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_catch(irb, scope, node, result_loc), lval); case NodeTypeContainerDecl: return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: @@ -7036,16 +7237,14 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop zig_unreachable(); } -static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval) { - IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval); +static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval, + IrInstruction *result_loc) +{ + IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval, result_loc); irb->exec->invalid = irb->exec->invalid || (result == irb->codegen->invalid_instruction); return result; } -static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) { - return ir_gen_node_extra(irb, node, scope, LValNone); -} - static void invalidate_exec(IrExecutable *exec) { if (exec->invalid) return; @@ -7076,6 +7275,9 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_ref_bb(irb->current_basic_block); ZigFn *fn_entry = exec_fn_entry(irb->exec); + + irb->exec->return_result_loc = ir_build_result_return(irb, scope, node); + bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; IrInstruction *coro_id; IrInstruction *u8_ptr_type; @@ -7096,15 +7298,17 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type); IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa - ir_build_var_decl(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); - coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var); + coro_promise_ptr = ir_build_alloca(irb, coro_scope, node, coro_frame_type_value); + ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, coro_promise_ptr); ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); - irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); + irb->exec->await_handle_var_ptr = ir_build_alloca(irb, coro_scope, node, await_handle_type_val); + ir_build_store_ptr(irb, coro_scope, node, irb->exec->await_handle_var_ptr, null_value); + ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, + irb->exec->await_handle_var_ptr); u8_ptr_type = ir_build_const_type(irb, coro_scope, node, get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); @@ -7112,11 +7316,16 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr); coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node); - ir_build_var_decl(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); + IrInstruction *coro_size_var_alloca = ir_build_alloca(irb, coro_scope, node, nullptr); + ir_build_store_ptr(irb, coro_scope, node, coro_size_var_alloca, coro_size); + ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size_var_alloca); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node, ImplicitAllocatorIdArg); irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); - ir_build_var_decl(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); + IrInstruction *coro_allocator_var_alloca = ir_build_alloca(irb, coro_scope, node, nullptr); + ir_build_store_ptr(irb, coro_scope, node, coro_allocator_var_alloca, implicit_allocator_ptr); + ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, + coro_allocator_var_alloca); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); @@ -7162,8 +7371,9 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses"); IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, instruction_addresses_name); - IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false); - ir_build_store_ptr(irb, scope, node, addrs_slice_ptr, slice_value); + IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, + nullptr, false, addrs_slice_ptr); + ir_build_store_result(irb, scope, node, addrs_slice_ptr, slice_value); } @@ -7173,7 +7383,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup"); } - IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone); + IrInstruction *result = ir_gen_node(irb, node, scope, LValNone, nullptr); assert(result); if (irb->exec->invalid) return false; @@ -7266,12 +7476,15 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); - IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false); + IrInstruction *mem_slice_alloca = ir_build_alloca(irb, scope, node, nullptr); + IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false, + mem_slice_alloca); size_t arg_count = 2; IrInstruction **args = allocate(arg_count); args[0] = implicit_allocator_ptr; // self args[1] = mem_slice; // old_mem - ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr); + ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, + nullptr, nullptr, nullptr); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); @@ -9068,15 +9281,6 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } } -static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry) { - if (type_has_bits(type_entry) && handle_is_ptr(type_entry)) { - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - if (fn_entry != nullptr) { - fn_entry->alloca_list.append(instruction); - } - } -} - static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs) { ConstGlobalRefs *global_refs = dest->global_refs; *dest = *src; @@ -9208,9 +9412,6 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst } else { IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op); result->value.type = wanted_type; - if (need_alloca) { - ir_add_alloca(ira, result, wanted_type); - } return result; } } @@ -9289,7 +9490,6 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpPtrOfArrayToSlice); result->value.type = wanted_type; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9580,7 +9780,9 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { return const_val->data.x_ptr.data.fn.fn_entry; } -static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { +static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *wanted_type) +{ assert(wanted_type->id == ZigTypeIdOptional); if (instr_is_comptime(value)) { @@ -9608,7 +9810,6 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_maybe = RuntimeHintOptionalNonNull; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9639,7 +9840,6 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction IrInstruction *result = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9708,7 +9908,6 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionError; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9756,11 +9955,6 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi source_instruction->source_node, value, is_const, is_volatile); new_instruction->value.type = ptr_type; new_instruction->value.data.rh_ptr = RuntimeHintPtrStack; - if (type_has_bits(ptr_type)) { - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - assert(fn_entry); - fn_entry->alloca_list.append(new_instruction); - } return new_instruction; } @@ -9800,12 +9994,13 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); + // TODO don't pass nullptr to ir_build_slice + BREAKPOINT; IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope, - source_instr->source_node, array_ptr, start, end, false); + source_instr->source_node, array_ptr, start, end, false, nullptr); result->value.type = wanted_type; result->value.data.rh_slice.id = RuntimeHintSliceIdLen; result->value.data.rh_slice.len = array_type->data.array.len; - ir_add_alloca(ira, result, result->value.type); return result; } @@ -12266,16 +12461,12 @@ static IrInstruction *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructio zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { +static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, + IrInstructionDeclVarSrc *decl_var_instruction) +{ Error err; ZigVar *var = decl_var_instruction->var; - IrInstruction *init_value = decl_var_instruction->init_value->child; - if (type_is_invalid(init_value->value.type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; - return ira->codegen->invalid_instruction; - } - ZigType *explicit_type = nullptr; IrInstruction *var_type = nullptr; if (decl_var_instruction->var_type != nullptr) { @@ -12290,12 +12481,11 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct AstNode *source_node = decl_var_instruction->base.source_node; - IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, explicit_type); bool is_comptime_var = ir_get_var_is_comptime(var); bool var_class_requires_const = false; - ZigType *result_type = casted_init_value->value.type; + ZigType *result_type = decl_var_instruction->ptr->value.type->data.pointer.child_type; if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; } else { @@ -12319,20 +12509,6 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; } - } else { - if (casted_init_value->value.special == ConstValSpecialStatic && - casted_init_value->value.type->id == ZigTypeIdFn && - casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) - { - var_class_requires_const = true; - if (!var->src_is_const && !is_comptime_var) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("functions marked inline must be stored in const or comptime var")); - AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node; - add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); - result_type = ira->codegen->builtin_types.entry_invalid; - } - } } } @@ -12377,15 +12553,9 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct } } - if (casted_init_value->value.special != ConstValSpecialRuntime) { - if (var->mem_slot_index != SIZE_MAX) { - assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length); - ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index); - copy_const_val(mem_slot, &casted_init_value->value, !is_comptime_var || var->gen_is_const); - - if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { - return ir_const_void(ira, &decl_var_instruction->base); - } + if (var->mem_slot_index != SIZE_MAX) { + if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { + return ir_const_void(ira, &decl_var_instruction->base); } } else if (is_comptime_var) { ir_add_error(ira, &decl_var_instruction->base, @@ -12398,9 +12568,8 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct if (fn_entry) fn_entry->variable_list.append(var); - IrInstruction *result = ir_build_var_decl(&ira->new_irb, - decl_var_instruction->base.scope, decl_var_instruction->base.source_node, - var, var_type, nullptr, casted_init_value); + IrInstruction *result = ir_build_var_decl_gen(&ira->new_irb, + decl_var_instruction->base.scope, decl_var_instruction->base.source_node, var); result->value.type = ira->codegen->builtin_types.entry_void; return result; } @@ -12700,8 +12869,9 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c ZigType *promise_type = get_promise_type(ira->codegen, return_type); ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); - IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, - fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst, nullptr); + IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, + call_instruction->base.source_node, fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, + async_allocator_inst, nullptr, nullptr); result->value.type = async_return_type; return result; } @@ -13345,7 +13515,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call if (call_instruction->is_async) { IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, fn_ref, casted_args, impl_param_count, async_allocator_inst); - ir_add_alloca(ira, result, result->value.type); return ir_finish_anal(ira, result); } @@ -13353,10 +13522,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, - call_instruction->is_async, nullptr, casted_new_stack); + call_instruction->is_async, nullptr, casted_new_stack, nullptr); new_call_instruction->value.type = return_type; - ir_add_alloca(ira, new_call_instruction, return_type); return ir_finish_anal(ira, new_call_instruction); } @@ -13441,7 +13609,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref, casted_args, call_param_count, async_allocator_inst); - ir_add_alloca(ira, result, result->value.type); return ir_finish_anal(ira, result); } @@ -13453,9 +13620,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, - fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack); + fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, + casted_new_stack, nullptr); new_call_instruction->value.type = return_type; - ir_add_alloca(ira, new_call_instruction, return_type); return ir_finish_anal(ira, new_call_instruction); } @@ -14897,6 +15064,10 @@ static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstruct return ir_get_deref(ira, &load_ptr_instruction->base, ptr); } +static IrInstruction *ir_analyze_instruction_load_result(IrAnalyze *ira, IrInstructionLoadResult *instruction) { + zig_panic("TODO"); +} + static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { IrInstruction *ptr = store_ptr_instruction->ptr->child; if (type_is_invalid(ptr->value.type)) @@ -14960,6 +15131,10 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc return result; } +static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInstructionStoreResult *instruction) { + zig_panic("TODO"); +} + static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { IrInstruction *expr_value = typeof_instruction->value->child; ZigType *type_entry = expr_value->value.type; @@ -16100,7 +16275,6 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI instruction->scope, instruction->source_node, container_type, type_field, casted_field_value); new_instruction->value.type = container_type; - ir_add_alloca(ira, new_instruction, container_type); return new_instruction; } @@ -16224,7 +16398,6 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc instruction->scope, instruction->source_node, container_type, actual_field_count, new_fields); new_instruction->value.type = container_type; - ir_add_alloca(ira, new_instruction, container_type); return new_instruction; } @@ -16329,7 +16502,6 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, instruction->base.scope, instruction->base.source_node, container_type_value, elem_count, new_items); new_instruction->value.type = fixed_size_array_type; - ir_add_alloca(ira, new_instruction, fixed_size_array_type); return new_instruction; } else if (container_type->id == ZigTypeIdVoid) { if (elem_count != 0) { @@ -16444,6 +16616,10 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns if (type_is_invalid(target->value.type)) return ira->codegen->invalid_instruction; + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + assert(target->value.type->id == ZigTypeIdEnum); if (instr_is_comptime(target)) { @@ -16457,7 +16633,7 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns } IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, target); + instruction->base.source_node, target, result_loc); ZigType *u8_ptr_type = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, @@ -17820,6 +17996,10 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + // TODO let this be volatile ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type); @@ -17885,9 +18065,8 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, - operand_type, success_order, failure_order); + operand_type, success_order, failure_order, result_loc); result->value.type = get_optional_type(ira->codegen, operand_type); - ir_add_alloca(ira, result, result->value.type); return result; } @@ -18061,124 +18240,6 @@ static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_ali return ErrorNone; } -static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { - Error err; - - ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->child); - if (type_is_invalid(dest_child_type)) - return ira->codegen->invalid_instruction; - - IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) - return ira->codegen->invalid_instruction; - - bool src_ptr_const; - bool src_ptr_volatile; - uint32_t src_ptr_align; - if (target->value.type->id == ZigTypeIdPointer) { - src_ptr_const = target->value.type->data.pointer.is_const; - src_ptr_volatile = target->value.type->data.pointer.is_volatile; - - if ((err = resolve_ptr_align(ira, target->value.type, &src_ptr_align))) - return ira->codegen->invalid_instruction; - } else if (is_slice(target->value.type)) { - ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; - src_ptr_const = src_ptr_type->data.pointer.is_const; - src_ptr_volatile = src_ptr_type->data.pointer.is_volatile; - - if ((err = resolve_ptr_align(ira, src_ptr_type, &src_ptr_align))) - return ira->codegen->invalid_instruction; - } else { - src_ptr_const = true; - src_ptr_volatile = false; - - if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_instruction; - - src_ptr_align = get_abi_alignment(ira->codegen, target->value.type); - } - - if ((err = type_resolve(ira->codegen, dest_child_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_instruction; - - ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_child_type, - src_ptr_const, src_ptr_volatile, PtrLenUnknown, - src_ptr_align, 0, 0); - ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); - - ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - src_ptr_const, src_ptr_volatile, PtrLenUnknown, - src_ptr_align, 0, 0); - ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); - - IrInstruction *casted_value = ir_implicit_cast(ira, target, u8_slice); - if (type_is_invalid(casted_value->value.type)) - return ira->codegen->invalid_instruction; - - bool have_known_len = false; - uint64_t known_len; - - if (instr_is_comptime(casted_value)) { - ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad); - if (!val) - return ira->codegen->invalid_instruction; - - ConstExprValue *len_val = &val->data.x_struct.fields[slice_len_index]; - if (value_is_comptime(len_val)) { - known_len = bigint_as_unsigned(&len_val->data.x_bigint); - have_known_len = true; - } - } - - if (casted_value->value.data.rh_slice.id == RuntimeHintSliceIdLen) { - known_len = casted_value->value.data.rh_slice.len; - have_known_len = true; - } - - if (have_known_len) { - uint64_t child_type_size = type_size(ira->codegen, dest_child_type); - uint64_t remainder = known_len % child_type_size; - if (remainder != 0) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base, - buf_sprintf("unable to convert [%" ZIG_PRI_u64 "]u8 to %s: size mismatch", - known_len, buf_ptr(&dest_slice_type->name))); - add_error_note(ira->codegen, msg, instruction->dest_child_type->source_node, - buf_sprintf("%s has size %" ZIG_PRI_u64 "; remaining bytes: %" ZIG_PRI_u64, - buf_ptr(&dest_child_type->name), child_type_size, remainder)); - return ira->codegen->invalid_instruction; - } - } - - return ir_resolve_cast(ira, &instruction->base, casted_value, dest_slice_type, CastOpResizeSlice, true); -} - -static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) { - Error err; - - IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) - return ira->codegen->invalid_instruction; - - if (!is_slice(target->value.type)) { - ir_add_error(ira, instruction->target, - buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value.type->name))); - return ira->codegen->invalid_instruction; - } - - ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; - - uint32_t alignment; - if ((err = resolve_ptr_align(ira, src_ptr_type, &alignment))) - return ira->codegen->invalid_instruction; - - ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - src_ptr_type->data.pointer.is_const, src_ptr_type->data.pointer.is_volatile, PtrLenUnknown, - alignment, 0, 0); - ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); - - return ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); -} - static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) @@ -18565,6 +18626,10 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (type_is_invalid(ptr_ptr->value.type)) return ira->codegen->invalid_instruction; + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + ZigType *ptr_type = ptr_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *array_type = ptr_type->data.pointer.child_type; @@ -18824,9 +18889,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction IrInstruction *new_instruction = ir_build_slice(&ira->new_irb, instruction->base.scope, instruction->base.source_node, - ptr_ptr, casted_start, end, instruction->safety_check_on); + ptr_ptr, casted_start, end, instruction->safety_check_on, + result_loc); new_instruction->value.type = return_type; - ir_add_alloca(ira, new_instruction, return_type); return new_instruction; } @@ -20844,6 +20909,24 @@ static IrInstruction *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, return ir_const_void(ira, &instruction->base); } +static IrInstruction *ir_analyze_instruction_result_error_union_payload(IrAnalyze *ira, + IrInstructionResultErrorUnionPayload *instruction) +{ + zig_panic("TODO"); +} + +static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrInstructionResultReturn *instruction) { + zig_panic("TODO"); +} + +static IrInstruction *ir_analyze_instruction_result_param(IrAnalyze *ira, IrInstructionResultParam *instruction) { + zig_panic("TODO"); +} + +static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, IrInstructionResultPtrCast *instruction) { + zig_panic("TODO"); +} + static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -20856,6 +20939,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: + case IrInstructionIdDeclVarGen: zig_unreachable(); case IrInstructionIdReturn: @@ -20866,12 +20950,16 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction); case IrInstructionIdBinOp: return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); - case IrInstructionIdDeclVar: - return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction); + case IrInstructionIdDeclVarSrc: + return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVarSrc *)instruction); case IrInstructionIdLoadPtr: return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); case IrInstructionIdStorePtr: return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction); + case IrInstructionIdLoadResult: + return ir_analyze_instruction_load_result(ira, (IrInstructionLoadResult *)instruction); + case IrInstructionIdStoreResult: + return ir_analyze_instruction_store_result(ira, (IrInstructionStoreResult *)instruction); case IrInstructionIdElemPtr: return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction); case IrInstructionIdVarPtr: @@ -20968,10 +21056,6 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_float_cast(ira, (IrInstructionFloatCast *)instruction); case IrInstructionIdErrSetCast: return ir_analyze_instruction_err_set_cast(ira, (IrInstructionErrSetCast *)instruction); - case IrInstructionIdFromBytes: - return ir_analyze_instruction_from_bytes(ira, (IrInstructionFromBytes *)instruction); - case IrInstructionIdToBytes: - return ir_analyze_instruction_to_bytes(ira, (IrInstructionToBytes *)instruction); case IrInstructionIdIntToFloat: return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); case IrInstructionIdFloatToInt: @@ -21120,6 +21204,20 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction); case IrInstructionIdCheckRuntimeScope: return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction); + case IrInstructionIdResultErrorUnionPayload: + return ir_analyze_instruction_result_error_union_payload(ira, (IrInstructionResultErrorUnionPayload *)instruction); + case IrInstructionIdResultReturn: + return ir_analyze_instruction_result_return(ira, (IrInstructionResultReturn *)instruction); + case IrInstructionIdResultParam: + return ir_analyze_instruction_result_param(ira, (IrInstructionResultParam *)instruction); + case IrInstructionIdResultPtrCast: + return ir_analyze_instruction_result_ptr_cast(ira, (IrInstructionResultPtrCast *)instruction); + case IrInstructionIdResultBytesToSlice: + zig_panic("TODO"); + case IrInstructionIdResultSliceToBytes: + zig_panic("TODO"); + case IrInstructionIdAlloca: + zig_panic("TODO"); } zig_unreachable(); } @@ -21204,7 +21302,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBr: case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: - case IrInstructionIdDeclVar: + case IrInstructionIdDeclVarSrc: + case IrInstructionIdDeclVarGen: case IrInstructionIdStorePtr: case IrInstructionIdCall: case IrInstructionIdReturn: @@ -21247,6 +21346,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdMergeErrRetTraces: case IrInstructionIdMarkErrRetTracePtr: case IrInstructionIdAtomicRmw: + case IrInstructionIdLoadResult: + case IrInstructionIdStoreResult: return true; case IrInstructionIdPhi: @@ -21338,9 +21439,14 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: - case IrInstructionIdFromBytes: - case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: + case IrInstructionIdResultErrorUnionPayload: + case IrInstructionIdResultReturn: + case IrInstructionIdResultParam: + case IrInstructionIdResultPtrCast: + case IrInstructionIdResultSliceToBytes: + case IrInstructionIdResultBytesToSlice: + case IrInstructionIdAlloca: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 8e51d1c6f369..489bac8bd4eb 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -172,7 +172,12 @@ static void ir_print_bin_op(IrPrint *irp, IrInstructionBinOp *bin_op_instruction } } -static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instruction) { +static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVarSrc *decl_var_instruction) { + if (decl_var_instruction->var->is_comptime != nullptr) { + fprintf(irp->f, "comptime<"); + ir_print_other_instruction(irp, decl_var_instruction->var->is_comptime); + fprintf(irp->f, "> "); + } const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; const char *name = buf_ptr(&decl_var_instruction->var->name); if (decl_var_instruction->var_type) { @@ -188,11 +193,7 @@ static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instr fprintf(irp->f, " "); } fprintf(irp->f, "= "); - ir_print_other_instruction(irp, decl_var_instruction->init_value); - if (decl_var_instruction->var->is_comptime != nullptr) { - fprintf(irp->f, " // comptime = "); - ir_print_other_instruction(irp, decl_var_instruction->var->is_comptime); - } + ir_print_other_instruction(irp, decl_var_instruction->ptr); } static void ir_print_cast(IrPrint *irp, IrInstructionCast *cast_instruction) { @@ -670,20 +671,6 @@ static void ir_print_err_set_cast(IrPrint *irp, IrInstructionErrSetCast *instruc fprintf(irp->f, ")"); } -static void ir_print_from_bytes(IrPrint *irp, IrInstructionFromBytes *instruction) { - fprintf(irp->f, "@bytesToSlice("); - ir_print_other_instruction(irp, instruction->dest_child_type); - fprintf(irp->f, ", "); - ir_print_other_instruction(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_to_bytes(IrPrint *irp, IrInstructionToBytes *instruction) { - fprintf(irp->f, "@sliceToBytes("); - ir_print_other_instruction(irp, instruction->target); - fprintf(irp->f, ")"); -} - static void ir_print_int_to_float(IrPrint *irp, IrInstructionIntToFloat *instruction) { fprintf(irp->f, "@intToFloat("); ir_print_other_instruction(irp, instruction->dest_type); @@ -1328,6 +1315,46 @@ static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { fprintf(irp->f, ")"); } +static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *instruction) { + fprintf(irp->f, "DeclVarGen"); +} + +static void ir_print_result_error_union_payload(IrPrint *irp, IrInstructionResultErrorUnionPayload *instruction) { + fprintf(irp->f, "ResultErrorUnionPayload"); +} + +static void ir_print_result_return(IrPrint *irp, IrInstructionResultReturn *instruction) { + fprintf(irp->f, "ResultReturn"); +} + +static void ir_print_result_bytes_to_slice(IrPrint *irp, IrInstructionResultBytesToSlice *instruction) { + fprintf(irp->f, "ResultBytesToSlice"); +} + +static void ir_print_result_slice_to_bytes(IrPrint *irp, IrInstructionResultSliceToBytes *instruction) { + fprintf(irp->f, "ResultSliceToBytes"); +} + +static void ir_print_result_param(IrPrint *irp, IrInstructionResultParam *instruction) { + fprintf(irp->f, "ResultParam"); +} + +static void ir_print_result_ptr_cast(IrPrint *irp, IrInstructionResultPtrCast *instruction) { + fprintf(irp->f, "ResultPtrCast"); +} + +static void ir_print_load_result(IrPrint *irp, IrInstructionLoadResult *instruction) { + fprintf(irp->f, "LoadResult"); +} + +static void ir_print_store_result(IrPrint *irp, IrInstructionStoreResult *instruction) { + fprintf(irp->f, "StoreResult"); +} + +static void ir_print_alloca(IrPrint *irp, IrInstructionAlloca *instruction) { + fprintf(irp->f, "Alloca"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1342,8 +1369,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdBinOp: ir_print_bin_op(irp, (IrInstructionBinOp *)instruction); break; - case IrInstructionIdDeclVar: - ir_print_decl_var(irp, (IrInstructionDeclVar *)instruction); + case IrInstructionIdDeclVarSrc: + ir_print_decl_var(irp, (IrInstructionDeclVarSrc *)instruction); break; case IrInstructionIdCast: ir_print_cast(irp, (IrInstructionCast *)instruction); @@ -1510,12 +1537,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdErrSetCast: ir_print_err_set_cast(irp, (IrInstructionErrSetCast *)instruction); break; - case IrInstructionIdFromBytes: - ir_print_from_bytes(irp, (IrInstructionFromBytes *)instruction); - break; - case IrInstructionIdToBytes: - ir_print_to_bytes(irp, (IrInstructionToBytes *)instruction); - break; case IrInstructionIdIntToFloat: ir_print_int_to_float(irp, (IrInstructionIntToFloat *)instruction); break; @@ -1753,6 +1774,36 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCheckRuntimeScope: ir_print_check_runtime_scope(irp, (IrInstructionCheckRuntimeScope *)instruction); break; + case IrInstructionIdDeclVarGen: + ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction); + break; + case IrInstructionIdResultErrorUnionPayload: + ir_print_result_error_union_payload(irp, (IrInstructionResultErrorUnionPayload *)instruction); + break; + case IrInstructionIdResultReturn: + ir_print_result_return(irp, (IrInstructionResultReturn *)instruction); + break; + case IrInstructionIdResultBytesToSlice: + ir_print_result_bytes_to_slice(irp, (IrInstructionResultBytesToSlice *)instruction); + break; + case IrInstructionIdResultSliceToBytes: + ir_print_result_slice_to_bytes(irp, (IrInstructionResultSliceToBytes *)instruction); + break; + case IrInstructionIdResultParam: + ir_print_result_param(irp, (IrInstructionResultParam *)instruction); + break; + case IrInstructionIdResultPtrCast: + ir_print_result_ptr_cast(irp, (IrInstructionResultPtrCast *)instruction); + break; + case IrInstructionIdLoadResult: + ir_print_load_result(irp, (IrInstructionLoadResult *)instruction); + break; + case IrInstructionIdStoreResult: + ir_print_store_result(irp, (IrInstructionStoreResult *)instruction); + break; + case IrInstructionIdAlloca: + ir_print_alloca(irp, (IrInstructionAlloca *)instruction); + break; } fprintf(irp->f, "\n"); } From 3037be186718938cca68807c9544e22dbcd94d01 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Oct 2018 16:10:13 -0400 Subject: [PATCH 002/190] copy elision: test case: scalar variable declaration ```zig export fn entry() void { var x: i32 = 1234; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca i32, align 4 store i32 1234, i32* %x, align 4, !dbg !48 call void @llvm.dbg.declare(metadata i32* %x, metadata !45, metadata !DIExpression()), !dbg !48 ret void, !dbg !49 } ``` --- src/all_types.hpp | 4 +- src/codegen.cpp | 33 ++++++----- src/ir.cpp | 136 ++++++++++++++++++++++++++++++++-------------- src/ir_print.cpp | 14 ++++- 4 files changed, 125 insertions(+), 62 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index c05c48f95f51..239bf8d992d4 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2215,6 +2215,7 @@ struct IrInstructionDeclVarGen { IrInstruction base; ZigVar *var; + IrInstruction *var_ptr; }; struct IrInstructionCondBr { @@ -3324,7 +3325,8 @@ struct IrInstructionResultPtrCast { struct IrInstructionAlloca { IrInstruction base; - IrInstruction *ty; + IrInstruction *child_type; + const char *name_hint; }; static const size_t slice_ptr_index = 0; diff --git a/src/codegen.cpp b/src/codegen.cpp index e4df2de81572..0b54ae5c6315 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1844,12 +1844,12 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_ty return nullptr; } -static void gen_var_debug_decl(CodeGen *g, ZigVar *var) { +static void gen_var_debug_decl(CodeGen *g, ZigVar *var, LLVMValueRef value_ref) { assert(var->di_loc_var != nullptr); AstNode *source_node = var->decl_node; ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc((unsigned)source_node->line + 1, (unsigned)source_node->column + 1, get_di_scope(g, var->parent_scope)); - ZigLLVMInsertDeclareAtEnd(g->dbuilder, var->value_ref, var->di_loc_var, debug_loc, + ZigLLVMInsertDeclareAtEnd(g->dbuilder, value_ref, var->di_loc_var, debug_loc, LLVMGetInsertBlock(g->builder)); } @@ -2000,7 +2000,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ clear_debug_source_node(g); gen_store_untyped(g, LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i), var->value_ref, var->align_bytes, false); if (var->decl_node) { - gen_var_debug_decl(g, var); + gen_var_debug_decl(g, var, var->value_ref); } fn_walk->data.inits.gen_i += 1; break; @@ -2035,7 +2035,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ } case FnWalkIdInits: if (var->decl_node) { - gen_var_debug_decl(g, var); + gen_var_debug_decl(g, var, var->value_ref); } fn_walk->data.inits.gen_i += 1; break; @@ -2073,7 +2073,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ } case FnWalkIdInits: if (var->decl_node) { - gen_var_debug_decl(g, var); + gen_var_debug_decl(g, var, var->value_ref); } fn_walk->data.inits.gen_i += 1; break; @@ -2111,7 +2111,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); if (var->decl_node) { - gen_var_debug_decl(g, var); + gen_var_debug_decl(g, var, var->value_ref); } fn_walk->data.inits.gen_i += 1; break; @@ -2206,7 +2206,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { } if (variable->decl_node) { - gen_var_debug_decl(g, variable); + gen_var_debug_decl(g, variable, variable->value_ref); } break; } @@ -3175,10 +3175,8 @@ static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildICmp(g->builder, LLVMIntEQ, value, zero, ""); } -static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, - IrInstructionDeclVarGen *decl_var_instruction) -{ - ZigVar *var = decl_var_instruction->var; +static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrInstructionDeclVarGen *instruction) { + ZigVar *var = instruction->var; if (!type_has_bits(var->value->type)) return nullptr; @@ -3186,7 +3184,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, if (var->ref_count == 0 && g->build_mode != BuildModeDebug) return nullptr; - gen_var_debug_decl(g, var); + gen_var_debug_decl(g, var, ir_llvm_value(g, instruction->var_ptr)); return nullptr; } @@ -5304,7 +5302,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdStoreResult: zig_panic("TODO"); case IrInstructionIdAlloca: - zig_panic("TODO"); + return instruction->llvm_value; // handled before function code generation } zig_unreachable(); } @@ -6194,8 +6192,11 @@ static void do_code_gen(CodeGen *g) { // allocate temporary stack data for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) { IrInstructionAlloca *instruction = fn_table_entry->alloca_list.at(alloca_i); - ZigType *slot_type = instruction->base.value.type; - instruction->base.llvm_value = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type)); + ZigType *ptr_type = instruction->base.value.type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *child_type = ptr_type->data.pointer.child_type; + instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, + get_ptr_align(g, ptr_type)); } ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); @@ -6221,8 +6222,6 @@ static void do_code_gen(CodeGen *g) { continue; if (var->src_arg_index == SIZE_MAX) { - var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); - var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), var->value->type->di_type, !g->strip_debug_symbols, 0); diff --git a/src/ir.cpp b/src/ir.cpp index dbb2554ba03b..9e5d672c95e3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1394,11 +1394,16 @@ static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNod return &decl_var_instruction->base; } -static IrInstruction *ir_build_var_decl_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var) { +static IrInstruction *ir_build_var_decl_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, + IrInstruction *var_ptr) +{ IrInstructionDeclVarGen *decl_var_instruction = ir_build_instruction(irb, scope, source_node); decl_var_instruction->base.value.special = ConstValSpecialStatic; decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void; decl_var_instruction->var = var; + decl_var_instruction->var_ptr = var_ptr; + + ir_ref_instruction(var_ptr, irb->current_basic_block); return &decl_var_instruction->base; } @@ -2841,11 +2846,14 @@ static IrInstruction *ir_build_result_bytes_to_slice(IrBuilder *irb, Scope *scop return &instruction->base; } -static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ty) { +static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *child_type, const char *name_hint) +{ IrInstructionAlloca *instruction = ir_build_instruction(irb, scope, source_node); - instruction->ty = ty; + instruction->child_type = child_type; + instruction->name_hint = name_hint; - if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block); + if (child_type != nullptr) ir_ref_instruction(child_type, irb->current_basic_block); return &instruction->base; } @@ -5172,7 +5180,8 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); } - IrInstruction *alloca = ir_build_alloca(irb, scope, node, type_instruction); + IrInstruction *alloca = ir_build_alloca(irb, scope, node, type_instruction, + buf_ptr(variable_declaration->symbol)); // Temporarily set the name of the IrExecutable to the VariableDeclaration // so that the struct or enum from the init expression inherits the name. @@ -5492,7 +5501,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime); Scope *child_scope = elem_var->child_scope; - IrInstruction *elem_alloca = ir_build_alloca(irb, child_scope, elem_node, elem_var_type); + IrInstruction *elem_alloca = ir_build_alloca(irb, child_scope, elem_node, elem_var_type, "for_elem"); ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, elem_alloca); AstNode *index_var_source_node; @@ -5508,7 +5517,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo child_scope = index_var->child_scope; IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); - IrInstruction *index_alloca = ir_build_alloca(irb, child_scope, node, usize); + IrInstruction *index_alloca = ir_build_alloca(irb, child_scope, node, usize, "for_index"); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); ir_build_store_ptr(irb, child_scope, index_var_source_node, index_alloca, zero); @@ -5765,7 +5774,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *payload_alloca = ir_build_alloca(irb, scope, node, nullptr); + IrInstruction *payload_alloca = ir_build_alloca(irb, scope, node, nullptr, "optional_payload"); IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_result(irb, subexpr_scope, node, var_ptr_value, payload_alloca); @@ -5843,7 +5852,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); - IrInstruction *payload_alloca = ir_build_alloca(irb, scope, node, nullptr); + IrInstruction *payload_alloca = ir_build_alloca(irb, scope, node, nullptr, "errunion_payload"); IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_result(irb, subexpr_scope, node, var_ptr_value, payload_alloca); @@ -5922,7 +5931,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit var_name, is_const, is_const, is_shadowable, var_is_comptime); child_scope = var->child_scope; IrInstruction *var_value; - IrInstruction *payload_alloca = ir_build_alloca(irb, scope, var_symbol_node, nullptr); + IrInstruction *payload_alloca = ir_build_alloca(irb, scope, var_symbol_node, nullptr, "switch_payload"); if (prong_value) { IrInstruction *var_ptr_value = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); var_value = var_is_ptr ? @@ -6880,7 +6889,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); - IrInstruction *result_var_alloca = ir_build_alloca(irb, scope, node, promise_result_type); + IrInstruction *result_var_alloca = ir_build_alloca(irb, scope, node, promise_result_type, ""); ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, result_var_alloca); ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, result_var_alloca); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); @@ -7298,14 +7307,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type); IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa - coro_promise_ptr = ir_build_alloca(irb, coro_scope, node, coro_frame_type_value); + coro_promise_ptr = ir_build_alloca(irb, coro_scope, node, coro_frame_type_value, ""); ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, coro_promise_ptr); ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - irb->exec->await_handle_var_ptr = ir_build_alloca(irb, coro_scope, node, await_handle_type_val); + irb->exec->await_handle_var_ptr = ir_build_alloca(irb, coro_scope, node, await_handle_type_val, ""); ir_build_store_ptr(irb, coro_scope, node, irb->exec->await_handle_var_ptr, null_value); ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, irb->exec->await_handle_var_ptr); @@ -7316,13 +7325,13 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr); coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node); - IrInstruction *coro_size_var_alloca = ir_build_alloca(irb, coro_scope, node, nullptr); + IrInstruction *coro_size_var_alloca = ir_build_alloca(irb, coro_scope, node, nullptr, ""); ir_build_store_ptr(irb, coro_scope, node, coro_size_var_alloca, coro_size); ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size_var_alloca); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node, ImplicitAllocatorIdArg); irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); - IrInstruction *coro_allocator_var_alloca = ir_build_alloca(irb, coro_scope, node, nullptr); + IrInstruction *coro_allocator_var_alloca = ir_build_alloca(irb, coro_scope, node, nullptr, ""); ir_build_store_ptr(irb, coro_scope, node, coro_allocator_var_alloca, implicit_allocator_ptr); ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, coro_allocator_var_alloca); @@ -7476,7 +7485,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); - IrInstruction *mem_slice_alloca = ir_build_alloca(irb, scope, node, nullptr); + IrInstruction *mem_slice_alloca = ir_build_alloca(irb, scope, node, nullptr, ""); IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false, mem_slice_alloca); size_t arg_count = 2; @@ -12485,7 +12494,14 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, bool var_class_requires_const = false; - ZigType *result_type = decl_var_instruction->ptr->value.type->data.pointer.child_type; + IrInstruction *var_ptr = decl_var_instruction->ptr->child; + if (type_is_invalid(var_ptr->value.type)) { + var->value->type = ira->codegen->builtin_types.entry_invalid; + return ira->codegen->invalid_instruction; + } + + assert(var_ptr->value.type->id == ZigTypeIdPointer); + ZigType *result_type = var_ptr->value.type->data.pointer.child_type; if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; } else { @@ -12568,8 +12584,8 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, if (fn_entry) fn_entry->variable_list.append(var); - IrInstruction *result = ir_build_var_decl_gen(&ira->new_irb, - decl_var_instruction->base.scope, decl_var_instruction->base.source_node, var); + IrInstruction *result = ir_build_var_decl_gen(&ira->new_irb, decl_var_instruction->base.scope, + decl_var_instruction->base.source_node, var, var_ptr); result->value.type = ira->codegen->builtin_types.entry_void; return result; } @@ -15068,15 +15084,9 @@ static IrInstruction *ir_analyze_instruction_load_result(IrAnalyze *ira, IrInstr zig_panic("TODO"); } -static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { - IrInstruction *ptr = store_ptr_instruction->ptr->child; - if (type_is_invalid(ptr->value.type)) - return ira->codegen->invalid_instruction; - - IrInstruction *value = store_ptr_instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - +static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *ptr, IrInstruction *value) +{ if (ptr->value.type->id != ZigTypeIdPointer) { ir_add_error(ira, ptr, buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); @@ -15084,11 +15094,11 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc } if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { - return ir_const_void(ira, &store_ptr_instruction->base); + return ir_const_void(ira, source_instr); } - if (ptr->value.type->data.pointer.is_const && !store_ptr_instruction->base.is_gen) { - ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); + if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_instruction; } @@ -15099,23 +15109,23 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { - ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_instruction; } if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { if (instr_is_comptime(casted_value)) { - ConstExprValue *dest_val = ir_const_ptr_pointee(ira, &ptr->value, store_ptr_instruction->base.source_node); + ConstExprValue *dest_val = ir_const_ptr_pointee(ira, &ptr->value, source_instr->source_node); if (dest_val == nullptr) return ira->codegen->invalid_instruction; if (dest_val->special != ConstValSpecialRuntime) { *dest_val = casted_value->value; if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { - ira->new_irb.current_basic_block->must_be_comptime_source_instr = &store_ptr_instruction->base; + ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; } - return ir_const_void(ira, &store_ptr_instruction->base); + return ir_const_void(ira, source_instr); } } - ir_add_error(ira, &store_ptr_instruction->base, + ir_add_error(ira, source_instr, buf_sprintf("cannot store runtime value in compile time variable")); ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); dest_val->type = ira->codegen->builtin_types.entry_invalid; @@ -15124,15 +15134,40 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc } } - IrInstruction *result = ir_build_store_ptr(&ira->new_irb, - store_ptr_instruction->base.scope, store_ptr_instruction->base.source_node, + IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, ptr, casted_value); result->value.type = ira->codegen->builtin_types.entry_void; return result; } +static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) { + IrInstruction *ptr = instruction->ptr->child; + if (type_is_invalid(ptr->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *value = instruction->value->child; + if (type_is_invalid(value->value.type)) + return ira->codegen->invalid_instruction; + + return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); +} + static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInstructionStoreResult *instruction) { - zig_panic("TODO"); + IrInstruction *value = instruction->value->child; + if (type_is_invalid(value->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + + // If the type is a scalar value, treat this instruction as a normal store pointer instruction. + if (!type_has_bits(value->value.type) || !handle_is_ptr(value->value.type)) { + return ir_analyze_store_ptr(ira, &instruction->base, result_loc, value); + } + + // Otherwise, trust that the result location was populated by the expression that computed value. + return ir_const_void(ira, &instruction->base); } static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { @@ -20927,6 +20962,25 @@ static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, IrI zig_panic("TODO"); } +static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAlloca *instruction) { + ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child); + if (type_is_invalid(child_type)) + return ira->codegen->invalid_instruction; + + // TODO alignment + ZigType *pointer_type = get_pointer_to_type(ira->codegen, child_type, false); + + IrInstruction *result = ir_build_alloca(&ira->new_irb, instruction->base.scope, instruction->base.source_node, + nullptr, instruction->name_hint); + result->value.type = pointer_type; + + ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); + if (fn_entry != nullptr) { + fn_entry->alloca_list.append(reinterpret_cast(result)); + } + return result; +} + static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -21212,12 +21266,12 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_result_param(ira, (IrInstructionResultParam *)instruction); case IrInstructionIdResultPtrCast: return ir_analyze_instruction_result_ptr_cast(ira, (IrInstructionResultPtrCast *)instruction); + case IrInstructionIdAlloca: + return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction); case IrInstructionIdResultBytesToSlice: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: zig_panic("TODO"); - case IrInstructionIdAlloca: - zig_panic("TODO"); } zig_unreachable(); } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 489bac8bd4eb..640bf9102290 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1316,7 +1316,9 @@ static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { } static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *instruction) { - fprintf(irp->f, "DeclVarGen"); + fprintf(irp->f, "DeclVarGen(name=%s,ptr=", buf_ptr(&instruction->var->name)); + ir_print_other_instruction(irp, instruction->var_ptr); + fprintf(irp->f, ")"); } static void ir_print_result_error_union_payload(IrPrint *irp, IrInstructionResultErrorUnionPayload *instruction) { @@ -1348,11 +1350,17 @@ static void ir_print_load_result(IrPrint *irp, IrInstructionLoadResult *instruct } static void ir_print_store_result(IrPrint *irp, IrInstructionStoreResult *instruction) { - fprintf(irp->f, "StoreResult"); + fprintf(irp->f, "StoreResult(result_loc="); + ir_print_other_instruction(irp, instruction->result_loc); + fprintf(irp->f, ",value="); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ")"); } static void ir_print_alloca(IrPrint *irp, IrInstructionAlloca *instruction) { - fprintf(irp->f, "Alloca"); + fprintf(irp->f, "Alloca("); + ir_print_other_instruction(irp, instruction->child_type); + fprintf(irp->f, ")"); } static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { From 1dfca202ad27674d38361e6238d75ffe0037a992 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Oct 2018 16:26:52 -0400 Subject: [PATCH 003/190] copy elision: test case: initialize scalar undefined ```zig export fn entry() void { var x: i32 = undefined; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca i32, align 4 %0 = bitcast i32* %x to i8*, !dbg !48 call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 -86, i64 4, i1 false), !dbg !48 call void @llvm.dbg.declare(metadata i32* %x, metadata !45, metadata !DIExpression()), !dbg !48 ret void, !dbg !49 } ``` --- src/codegen.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 0b54ae5c6315..90c801f17127 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3218,14 +3218,54 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, ""); } -static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef value = ir_llvm_value(g, instruction->value); +static bool value_is_all_undef(ConstExprValue *const_val) { + switch (const_val->special) { + case ConstValSpecialRuntime: + return false; + case ConstValSpecialUndef: + return true; + case ConstValSpecialStatic: + if (const_val->type->id == ZigTypeIdStruct) { + for (size_t i = 0; i < const_val->type->data.structure.src_field_count; i += 1) { + if (!value_is_all_undef(&const_val->data.x_struct.fields[i])) + return false; + } + return true; + } else { + return false; + } + } + zig_unreachable(); +} + +static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { + ZigType *usize = g->builtin_types.entry_usize; + uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref); + assert(size_bytes > 0); + assert(ptr_align_bytes > 0); + // memset uninitialized memory to 0xaa + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); + LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); + LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); + ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); +} - assert(instruction->ptr->value.type->id == ZigTypeIdPointer); +static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { ZigType *ptr_type = instruction->ptr->value.type; + assert(ptr_type->id == ZigTypeIdPointer); + + bool have_init_expr = !value_is_all_undef(&instruction->value->value); + if (have_init_expr) { + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef value = ir_llvm_value(g, instruction->value); - gen_assign_raw(g, ptr, ptr_type, value); + gen_assign_raw(g, ptr, ptr_type, value); + } + if (ir_want_runtime_safety(g, &instruction->base)) { + gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value.type, + ir_llvm_value(g, instruction->ptr)); + } return nullptr; } From dd320eea83ecdc04c0a72e0c7d2fb10ecb4a173d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 26 Oct 2018 16:55:20 -0400 Subject: [PATCH 004/190] copy elision: scalar variable decl with type inference ```zig export fn entry() void { var x = i32(1234); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca i32, align 4 store i32 1234, i32* %x, align 4, !dbg !48 %0 = bitcast i32* %x to i8*, !dbg !48 call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 -86, i64 4, i1 false), !dbg !48 call void @llvm.dbg.declare(metadata i32* %x, metadata !45, metadata !DIExpression()), !dbg !48 ret void, !dbg !49 } ``` --- src/all_types.hpp | 1 + src/codegen.cpp | 3 +++ src/ir.cpp | 38 +++++++++++++++++++++++++++++--------- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 239bf8d992d4..866c9347bff1 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1645,6 +1645,7 @@ struct CodeGen { ZigType *entry_c_int[CIntTypeCount]; ZigType *entry_c_longdouble; ZigType *entry_c_void; + ZigType *entry_infer; ZigType *entry_u8; ZigType *entry_u16; ZigType *entry_u32; diff --git a/src/codegen.cpp b/src/codegen.cpp index 90c801f17127..24066b17dd59 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6597,6 +6597,9 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_c_void = get_opaque_type(g, nullptr, nullptr, "c_void"); g->primitive_type_table.put(&g->builtin_types.entry_c_void->name, g->builtin_types.entry_c_void); } + { + g->builtin_types.entry_infer = get_opaque_type(g, nullptr, nullptr, "(infer)"); + } { ZigType *entry = new_type_table_entry(ZigTypeIdErrorSet); diff --git a/src/ir.cpp b/src/ir.cpp index 9e5d672c95e3..3d8cb6085b53 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15152,6 +15152,19 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); } +static void resolve_alloca_inference(IrAnalyze *ira, IrInstructionAlloca *alloca, ZigType *child_type) { + // TODO alignment + alloca->base.value.type = get_pointer_to_type(ira->codegen, child_type, false); +} + +static void resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type) { + assert(base->value.type->id == ZigTypeIdPointer); + ZigType *infer_child = base->value.type->data.pointer.child_type; + if (base->id == IrInstructionIdAlloca && infer_child == ira->codegen->builtin_types.entry_infer) { + resolve_alloca_inference(ira, reinterpret_cast(base), child_type); + } +} + static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInstructionStoreResult *instruction) { IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) @@ -15161,6 +15174,8 @@ static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInst if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; + resolve_possible_alloca_inference(ira, result_loc, value->value.type); + // If the type is a scalar value, treat this instruction as a normal store pointer instruction. if (!type_has_bits(value->value.type) || !handle_is_ptr(value->value.type)) { return ir_analyze_store_ptr(ira, &instruction->base, result_loc, value); @@ -20963,20 +20978,25 @@ static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, IrI } static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAlloca *instruction) { - ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child); - if (type_is_invalid(child_type)) - return ira->codegen->invalid_instruction; - - // TODO alignment - ZigType *pointer_type = get_pointer_to_type(ira->codegen, child_type, false); - IrInstruction *result = ir_build_alloca(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, instruction->name_hint); - result->value.type = pointer_type; + IrInstructionAlloca *alloca_result = reinterpret_cast(result); + + if (instruction->child_type == nullptr) { + // We use a pointer to a special opaque type to signal that we want type inference. + // TODO alignment + result->value.type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_infer, false); + } else { + ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child); + if (type_is_invalid(child_type)) + return ira->codegen->invalid_instruction; + + resolve_alloca_inference(ira, alloca_result, child_type); + } ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry != nullptr) { - fn_entry->alloca_list.append(reinterpret_cast(result)); + fn_entry->alloca_list.append(alloca_result); } return result; } From 29ce2b68564ccabe1c93fd7e0612caf06680448c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 27 Oct 2018 10:42:09 -0400 Subject: [PATCH 005/190] copy elision: handle alignment --- src/all_types.hpp | 17 ++++++--- src/codegen.cpp | 6 ++-- src/ir.cpp | 92 +++++++++++++++++++++++++++++------------------ src/ir_print.cpp | 17 ++++++--- 4 files changed, 87 insertions(+), 45 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 866c9347bff1..b533f1426a92 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -33,7 +33,7 @@ struct TypeStructField; struct CodeGen; struct ConstExprValue; struct IrInstruction; -struct IrInstructionAlloca; +struct IrInstructionAllocaGen; struct IrBasicBlock; struct ScopeDecls; struct ZigWindowsSDK; @@ -1322,7 +1322,7 @@ struct ZigFn { AstNode *fn_no_inline_set_node; AstNode *fn_static_eval_set_node; - ZigList alloca_list; + ZigList alloca_list; ZigList variable_list; Buf *section_name; @@ -2181,7 +2181,8 @@ enum IrInstructionId { IrInstructionIdResultPtrCast, IrInstructionIdLoadResult, IrInstructionIdStoreResult, - IrInstructionIdAlloca, + IrInstructionIdAllocaSrc, + IrInstructionIdAllocaGen, }; struct IrInstruction { @@ -3323,10 +3324,18 @@ struct IrInstructionResultPtrCast { IrInstruction *prev_result_loc; }; -struct IrInstructionAlloca { +struct IrInstructionAllocaSrc { IrInstruction base; IrInstruction *child_type; + IrInstruction *align; + const char *name_hint; +}; + +struct IrInstructionAllocaGen { + IrInstruction base; + + uint32_t align; const char *name_hint; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 24066b17dd59..438abba71956 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5163,6 +5163,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdEnumToInt: case IrInstructionIdCheckRuntimeScope: case IrInstructionIdDeclVarSrc: + case IrInstructionIdAllocaSrc: + case IrInstructionIdAllocaGen: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5341,8 +5343,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, zig_panic("TODO"); case IrInstructionIdStoreResult: zig_panic("TODO"); - case IrInstructionIdAlloca: - return instruction->llvm_value; // handled before function code generation } zig_unreachable(); } @@ -6231,7 +6231,7 @@ static void do_code_gen(CodeGen *g) { // allocate temporary stack data for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) { - IrInstructionAlloca *instruction = fn_table_entry->alloca_list.at(alloca_i); + IrInstructionAllocaGen *instruction = fn_table_entry->alloca_list.at(alloca_i); ZigType *ptr_type = instruction->base.value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; diff --git a/src/ir.cpp b/src/ir.cpp index 3d8cb6085b53..8a9f3a11d6cc 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -883,8 +883,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionResultBytesToSli return IrInstructionIdResultBytesToSlice; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) { - return IrInstructionIdAlloca; +static constexpr IrInstructionId ir_instruction_id(IrInstructionAllocaSrc *) { + return IrInstructionIdAllocaSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionAllocaGen *) { + return IrInstructionIdAllocaGen; } template @@ -2846,18 +2850,30 @@ static IrInstruction *ir_build_result_bytes_to_slice(IrBuilder *irb, Scope *scop return &instruction->base; } -static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *child_type, const char *name_hint) +static IrInstruction *ir_build_alloca_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *child_type, IrInstruction *align, const char *name_hint) { - IrInstructionAlloca *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionAllocaSrc *instruction = ir_build_instruction(irb, scope, source_node); instruction->child_type = child_type; + instruction->align = align; instruction->name_hint = name_hint; if (child_type != nullptr) ir_ref_instruction(child_type, irb->current_basic_block); + if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block); return &instruction->base; } +static IrInstructionAllocaGen *ir_create_alloca_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, + uint32_t align, const char *name_hint) +{ + IrInstructionAllocaGen *instruction = ir_create_instruction(irb, scope, source_node); + instruction->align = align; + instruction->name_hint = name_hint; + + return instruction; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -5180,7 +5196,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); } - IrInstruction *alloca = ir_build_alloca(irb, scope, node, type_instruction, + IrInstruction *alloca = ir_build_alloca_src(irb, scope, node, type_instruction, align_value, buf_ptr(variable_declaration->symbol)); // Temporarily set the name of the IrExecutable to the VariableDeclaration @@ -5501,7 +5517,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime); Scope *child_scope = elem_var->child_scope; - IrInstruction *elem_alloca = ir_build_alloca(irb, child_scope, elem_node, elem_var_type, "for_elem"); + IrInstruction *elem_alloca = ir_build_alloca_src(irb, child_scope, elem_node, elem_var_type, nullptr, "for_elem"); ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, elem_alloca); AstNode *index_var_source_node; @@ -5517,7 +5533,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo child_scope = index_var->child_scope; IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); - IrInstruction *index_alloca = ir_build_alloca(irb, child_scope, node, usize, "for_index"); + IrInstruction *index_alloca = ir_build_alloca_src(irb, child_scope, node, usize, nullptr, "for_index"); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); ir_build_store_ptr(irb, child_scope, index_var_source_node, index_alloca, zero); @@ -5774,7 +5790,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *payload_alloca = ir_build_alloca(irb, scope, node, nullptr, "optional_payload"); + IrInstruction *payload_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "optional_payload"); IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_result(irb, subexpr_scope, node, var_ptr_value, payload_alloca); @@ -5852,7 +5868,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); - IrInstruction *payload_alloca = ir_build_alloca(irb, scope, node, nullptr, "errunion_payload"); + IrInstruction *payload_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "errunion_payload"); IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_result(irb, subexpr_scope, node, var_ptr_value, payload_alloca); @@ -5931,7 +5947,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit var_name, is_const, is_const, is_shadowable, var_is_comptime); child_scope = var->child_scope; IrInstruction *var_value; - IrInstruction *payload_alloca = ir_build_alloca(irb, scope, var_symbol_node, nullptr, "switch_payload"); + IrInstruction *payload_alloca = ir_build_alloca_src(irb, scope, var_symbol_node, nullptr, nullptr, "switch_payload"); if (prong_value) { IrInstruction *var_ptr_value = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); var_value = var_is_ptr ? @@ -6889,7 +6905,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); - IrInstruction *result_var_alloca = ir_build_alloca(irb, scope, node, promise_result_type, ""); + IrInstruction *result_var_alloca = ir_build_alloca_src(irb, scope, node, promise_result_type, nullptr, ""); ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, result_var_alloca); ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, result_var_alloca); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); @@ -7307,14 +7323,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type); IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa - coro_promise_ptr = ir_build_alloca(irb, coro_scope, node, coro_frame_type_value, ""); + coro_promise_ptr = ir_build_alloca_src(irb, coro_scope, node, coro_frame_type_value, nullptr, ""); ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, coro_promise_ptr); ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - irb->exec->await_handle_var_ptr = ir_build_alloca(irb, coro_scope, node, await_handle_type_val, ""); + irb->exec->await_handle_var_ptr = ir_build_alloca_src(irb, coro_scope, node, await_handle_type_val, nullptr, ""); ir_build_store_ptr(irb, coro_scope, node, irb->exec->await_handle_var_ptr, null_value); ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, irb->exec->await_handle_var_ptr); @@ -7325,13 +7341,13 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr); coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node); - IrInstruction *coro_size_var_alloca = ir_build_alloca(irb, coro_scope, node, nullptr, ""); + IrInstruction *coro_size_var_alloca = ir_build_alloca_src(irb, coro_scope, node, nullptr, nullptr, ""); ir_build_store_ptr(irb, coro_scope, node, coro_size_var_alloca, coro_size); ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size_var_alloca); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node, ImplicitAllocatorIdArg); irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); - IrInstruction *coro_allocator_var_alloca = ir_build_alloca(irb, coro_scope, node, nullptr, ""); + IrInstruction *coro_allocator_var_alloca = ir_build_alloca_src(irb, coro_scope, node, nullptr, nullptr, ""); ir_build_store_ptr(irb, coro_scope, node, coro_allocator_var_alloca, implicit_allocator_ptr); ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, coro_allocator_var_alloca); @@ -7485,7 +7501,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); - IrInstruction *mem_slice_alloca = ir_build_alloca(irb, scope, node, nullptr, ""); + IrInstruction *mem_slice_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false, mem_slice_alloca); size_t arg_count = 2; @@ -15152,16 +15168,16 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); } -static void resolve_alloca_inference(IrAnalyze *ira, IrInstructionAlloca *alloca, ZigType *child_type) { - // TODO alignment - alloca->base.value.type = get_pointer_to_type(ira->codegen, child_type, false); +static void resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *alloca, ZigType *child_type) { + alloca->base.value.type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, + PtrLenSingle, alloca->align, 0, 0); } static void resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type) { assert(base->value.type->id == ZigTypeIdPointer); ZigType *infer_child = base->value.type->data.pointer.child_type; - if (base->id == IrInstructionIdAlloca && infer_child == ira->codegen->builtin_types.entry_infer) { - resolve_alloca_inference(ira, reinterpret_cast(base), child_type); + if (base->id == IrInstructionIdAllocaGen && infer_child == ira->codegen->builtin_types.entry_infer) { + resolve_alloca_inference(ira, reinterpret_cast(base), child_type); } } @@ -20977,28 +20993,34 @@ static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, IrI zig_panic("TODO"); } -static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAlloca *instruction) { - IrInstruction *result = ir_build_alloca(&ira->new_irb, instruction->base.scope, instruction->base.source_node, - nullptr, instruction->name_hint); - IrInstructionAlloca *alloca_result = reinterpret_cast(result); +static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAllocaSrc *instruction) { + uint32_t align = 0; + if (instruction->align != nullptr) { + if (!ir_resolve_align(ira, instruction->align->child, &align)) { + return ira->codegen->invalid_instruction; + } + } + + IrInstructionAllocaGen *result = ir_create_alloca_gen(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, align, instruction->name_hint); if (instruction->child_type == nullptr) { // We use a pointer to a special opaque type to signal that we want type inference. - // TODO alignment - result->value.type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_infer, false); + result->base.value.type = get_pointer_to_type_extra(ira->codegen, + ira->codegen->builtin_types.entry_infer, false, false, PtrLenSingle, align, 0, 0); } else { ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; - resolve_alloca_inference(ira, alloca_result, child_type); + resolve_alloca_inference(ira, result, child_type); } ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry != nullptr) { - fn_entry->alloca_list.append(alloca_result); + fn_entry->alloca_list.append(result); } - return result; + return &result->base; } static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { @@ -21014,6 +21036,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: case IrInstructionIdDeclVarGen: + case IrInstructionIdAllocaGen: zig_unreachable(); case IrInstructionIdReturn: @@ -21286,8 +21309,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_result_param(ira, (IrInstructionResultParam *)instruction); case IrInstructionIdResultPtrCast: return ir_analyze_instruction_result_ptr_cast(ira, (IrInstructionResultPtrCast *)instruction); - case IrInstructionIdAlloca: - return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction); + case IrInstructionIdAllocaSrc: + return ir_analyze_instruction_alloca(ira, (IrInstructionAllocaSrc *)instruction); case IrInstructionIdResultBytesToSlice: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: @@ -21520,7 +21543,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdResultPtrCast: case IrInstructionIdResultSliceToBytes: case IrInstructionIdResultBytesToSlice: - case IrInstructionIdAlloca: + case IrInstructionIdAllocaSrc: + case IrInstructionIdAllocaGen: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 640bf9102290..56dcc00c8665 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1357,12 +1357,18 @@ static void ir_print_store_result(IrPrint *irp, IrInstructionStoreResult *instru fprintf(irp->f, ")"); } -static void ir_print_alloca(IrPrint *irp, IrInstructionAlloca *instruction) { - fprintf(irp->f, "Alloca("); +static void ir_print_alloca_src(IrPrint *irp, IrInstructionAllocaSrc *instruction) { + fprintf(irp->f, "AllocaSrc(ty="); ir_print_other_instruction(irp, instruction->child_type); + fprintf(irp->f, ",align="); + ir_print_other_instruction(irp, instruction->align); fprintf(irp->f, ")"); } +static void ir_print_alloca_gen(IrPrint *irp, IrInstructionAllocaGen *instruction) { + fprintf(irp->f, "AllocaGen(align=%" PRIu32 ",name=%s)", instruction->align, instruction->name_hint); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1809,8 +1815,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdStoreResult: ir_print_store_result(irp, (IrInstructionStoreResult *)instruction); break; - case IrInstructionIdAlloca: - ir_print_alloca(irp, (IrInstructionAlloca *)instruction); + case IrInstructionIdAllocaSrc: + ir_print_alloca_src(irp, (IrInstructionAllocaSrc *)instruction); + break; + case IrInstructionIdAllocaGen: + ir_print_alloca_gen(irp, (IrInstructionAllocaGen *)instruction); break; } fprintf(irp->f, "\n"); From 9a60a65b248e0234e2c9b822f3c1ad4ee85fa684 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 27 Oct 2018 11:00:33 -0400 Subject: [PATCH 006/190] copy elision: initialization of variable to constant ```zig export fn entry() void { var x = Foo.{ .x = 1, .y = 2, }; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca %Foo, align 4 %0 = bitcast %Foo* %x to i8*, !dbg !52 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%Foo* @0 to i8*), i64 8, i1 false), !dbg !52 call void @llvm.dbg.declare(metadata %Foo* %x, metadata !45, metadata !DIExpression()), !dbg !52 ret void, !dbg !53 } ``` --- src/codegen.cpp | 3 +-- src/ir.cpp | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 438abba71956..a09594448017 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3261,8 +3261,7 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir LLVMValueRef value = ir_llvm_value(g, instruction->value); gen_assign_raw(g, ptr, ptr_type, value); - } - if (ir_want_runtime_safety(g, &instruction->base)) { + } else if (ir_want_runtime_safety(g, &instruction->base)) { gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value.type, ir_llvm_value(g, instruction->ptr)); } diff --git a/src/ir.cpp b/src/ir.cpp index 8a9f3a11d6cc..10776e36339b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15193,7 +15193,9 @@ static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInst resolve_possible_alloca_inference(ira, result_loc, value->value.type); // If the type is a scalar value, treat this instruction as a normal store pointer instruction. - if (!type_has_bits(value->value.type) || !handle_is_ptr(value->value.type)) { + // Or if the value is comptime known, analyze it as a store pointer, so that the + // const can be memcpy'd. + if (!type_has_bits(value->value.type) || !handle_is_ptr(value->value.type) || instr_is_comptime(value)) { return ir_analyze_store_ptr(ira, &instruction->base, result_loc, value); } From c7c3cfcc6ca59c68fc1b0bb61d2eef92fd7a8415 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 27 Oct 2018 12:04:21 -0400 Subject: [PATCH 007/190] copy elision - var decl init to aggregate fn call ```zig export fn entry() void { var y = foo(); } fn foo() Foo { return Foo.{ .x = 1, .y = 2, }; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %y = alloca %Foo, align 4 call fastcc void @foo(%Foo* sret %y), !dbg !52 call void @llvm.dbg.declare(metadata %Foo* %y, metadata !45, metadata !DIExpression()), !dbg !53 ret void, !dbg !54 } define internal fastcc void @foo(%Foo* nonnull sret) unnamed_addr #2 !dbg !55 { Entry: %1 = bitcast %Foo* %0 to i8*, !dbg !60 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 4 bitcast (%Foo* @0 to i8*), i64 8, i1 false), !dbg !60 ret void, !dbg !60 } ``` --- src/ir.cpp | 77 ++++++++++++++++++++++++++++++++---------------- src/ir_print.cpp | 3 +- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 10776e36339b..00f67c549e9e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1211,16 +1211,12 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->new_stack = new_stack; call_instruction->result_loc = result_loc; - if (fn_ref) - ir_ref_instruction(fn_ref, irb->current_basic_block); + if (fn_ref) ir_ref_instruction(fn_ref, irb->current_basic_block); for (size_t i = 0; i < arg_count; i += 1) ir_ref_instruction(args[i], irb->current_basic_block); - if (async_allocator) - ir_ref_instruction(async_allocator, irb->current_basic_block); - if (new_stack != nullptr) - ir_ref_instruction(new_stack, irb->current_basic_block); - - ir_ref_instruction(result_loc, irb->current_basic_block); + if (async_allocator != nullptr) ir_ref_instruction(async_allocator, irb->current_basic_block); + if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &call_instruction->base; } @@ -13108,6 +13104,26 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, return var_ptr_instruction; } +static Error resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *alloca, ZigType *child_type) { + Error err; + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) + return err; + alloca->base.value.type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, + PtrLenSingle, alloca->align, 0, 0); + return ErrorNone; +} + +static Error resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type) { + Error err; + assert(base->value.type->id == ZigTypeIdPointer); + ZigType *infer_child = base->value.type->data.pointer.child_type; + if (base->id == IrInstructionIdAllocaGen && infer_child == ira->codegen->builtin_types.entry_infer) { + if ((err = resolve_alloca_inference(ira, reinterpret_cast(base), child_type))) + return err; + } + return ErrorNone; +} + static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline) @@ -13650,10 +13666,20 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; } + IrInstruction *result_loc = nullptr; + + if (handle_is_ptr(return_type)) { + result_loc = call_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + if ((err = resolve_possible_alloca_inference(ira, result_loc, return_type))) + return ira->codegen->invalid_instruction; + } + IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, - casted_new_stack, nullptr); + casted_new_stack, result_loc); new_call_instruction->value.type = return_type; return ir_finish_anal(ira, new_call_instruction); } @@ -15168,20 +15194,9 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); } -static void resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *alloca, ZigType *child_type) { - alloca->base.value.type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, - PtrLenSingle, alloca->align, 0, 0); -} - -static void resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type) { - assert(base->value.type->id == ZigTypeIdPointer); - ZigType *infer_child = base->value.type->data.pointer.child_type; - if (base->id == IrInstructionIdAllocaGen && infer_child == ira->codegen->builtin_types.entry_infer) { - resolve_alloca_inference(ira, reinterpret_cast(base), child_type); - } -} - static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInstructionStoreResult *instruction) { + Error err; + IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; @@ -15190,7 +15205,8 @@ static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInst if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; - resolve_possible_alloca_inference(ira, result_loc, value->value.type); + if ((err = resolve_possible_alloca_inference(ira, result_loc, value->value.type))) + return ira->codegen->invalid_instruction; // If the type is a scalar value, treat this instruction as a normal store pointer instruction. // Or if the value is comptime known, analyze it as a store pointer, so that the @@ -20984,7 +21000,15 @@ static IrInstruction *ir_analyze_instruction_result_error_union_payload(IrAnalyz } static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrInstructionResultReturn *instruction) { - zig_panic("TODO"); + ZigFn *fn = exec_fn_entry(ira->new_irb.exec); + if (fn == nullptr) { + ir_add_error(ira, &instruction->base, buf_sprintf("return outside function")); + return ira->codegen->invalid_instruction; + } + ZigType *result_type = get_pointer_to_type(ira->codegen, fn->type_entry->data.fn.fn_type_id.return_type, false); + IrInstruction *result = ir_build_result_return(&ira->new_irb, instruction->base.scope, instruction->base.source_node); + result->value.type = result_type; + return result; } static IrInstruction *ir_analyze_instruction_result_param(IrAnalyze *ira, IrInstructionResultParam *instruction) { @@ -20996,6 +21020,8 @@ static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, IrI } static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAllocaSrc *instruction) { + Error err; + uint32_t align = 0; if (instruction->align != nullptr) { if (!ir_resolve_align(ira, instruction->align->child, &align)) { @@ -21015,7 +21041,8 @@ static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructio if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; - resolve_alloca_inference(ira, result, child_type); + if ((err = resolve_alloca_inference(ira, result, child_type))) + return ira->codegen->invalid_instruction; } ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 56dcc00c8665..24c73c17b226 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -225,7 +225,8 @@ static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) { fprintf(irp->f, ", "); ir_print_other_instruction(irp, arg); } - fprintf(irp->f, ")"); + fprintf(irp->f, ") result="); + ir_print_other_instruction(irp, call_instruction->result_loc); } static void ir_print_cond_br(IrPrint *irp, IrInstructionCondBr *cond_br_instruction) { From c6dea5fa7c99da7e00c0a90928c40b0cd5b77732 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 27 Oct 2018 12:18:55 -0400 Subject: [PATCH 008/190] copy elision - fn returning result of other fn ```zig fn bar() Foo { return foo(); } ``` ```llvm define internal fastcc void @bar(%Foo* nonnull sret) unnamed_addr #2 !dbg !55 { Entry: call fastcc void @foo(%Foo* sret %0), !dbg !60 ret void, !dbg !62 } ``` --- src/codegen.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index a09594448017..8839c71a688f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2242,8 +2242,12 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns ZigType *return_type = return_instruction->value->value.type; if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { - assert(g->cur_ret_ptr); - gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); + // Assume that the result location mechanism populated the value, + // unless the value is a comptime const. + if (return_instruction->value->value.special == ConstValSpecialStatic) { + assert(g->cur_ret_ptr); + gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); + } LLVMBuildRetVoid(g->builder); } else if (handle_is_ptr(return_type)) { LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); @@ -5087,6 +5091,13 @@ static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstr return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); } +static LLVMValueRef ir_render_result_return(CodeGen *g, IrExecutable *executable, + IrInstructionResultReturn *instruction) +{ + assert(g->cur_ret_ptr != nullptr); + return g->cur_ret_ptr; +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5326,9 +5337,9 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); case IrInstructionIdSqrt: return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); - case IrInstructionIdResultErrorUnionPayload: - zig_panic("TODO"); case IrInstructionIdResultReturn: + return ir_render_result_return(g, executable, (IrInstructionResultReturn *)instruction); + case IrInstructionIdResultErrorUnionPayload: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: zig_panic("TODO"); From ca69d477f00f4a8b529915e08a6ca0d50508ffaf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 29 Oct 2018 14:52:54 -0400 Subject: [PATCH 009/190] copy elision - var decl initializing to aggregate runtime var ```zig export fn entry() void { var x = bar(); var y = x; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca %Foo, align 4 %y = alloca %Foo, align 4 call fastcc void @bar(%Foo* sret %x), !dbg !54 call void @llvm.dbg.declare(metadata %Foo* %x, metadata !45, metadata !DIExpression()), !dbg !55 %0 = bitcast %Foo* %x to i8*, !dbg !56 %1 = bitcast %Foo* %y to i8*, !dbg !56 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 4 %0, i64 8, i1 false), !dbg !56 call void @llvm.dbg.declare(metadata %Foo* %y, metadata !52, metadata !DIExpression()), !dbg !57 ret void, !dbg !58 } ``` --- src/all_types.hpp | 1 + src/codegen.cpp | 17 +++--- src/ir.cpp | 130 ++++++++++++++++++++++++++++------------------ src/ir_print.cpp | 7 ++- 4 files changed, 95 insertions(+), 60 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index b533f1426a92..e7e22d814445 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2337,6 +2337,7 @@ struct IrInstructionLoadPtr { IrInstruction base; IrInstruction *ptr; + IrInstruction *result_loc; }; struct IrInstructionStorePtr { diff --git a/src/codegen.cpp b/src/codegen.cpp index 8839c71a688f..856b567f2392 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1844,12 +1844,12 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_ty return nullptr; } -static void gen_var_debug_decl(CodeGen *g, ZigVar *var, LLVMValueRef value_ref) { +static void gen_var_debug_decl(CodeGen *g, ZigVar *var) { assert(var->di_loc_var != nullptr); AstNode *source_node = var->decl_node; ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc((unsigned)source_node->line + 1, (unsigned)source_node->column + 1, get_di_scope(g, var->parent_scope)); - ZigLLVMInsertDeclareAtEnd(g->dbuilder, value_ref, var->di_loc_var, debug_loc, + ZigLLVMInsertDeclareAtEnd(g->dbuilder, var->value_ref, var->di_loc_var, debug_loc, LLVMGetInsertBlock(g->builder)); } @@ -2000,7 +2000,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ clear_debug_source_node(g); gen_store_untyped(g, LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i), var->value_ref, var->align_bytes, false); if (var->decl_node) { - gen_var_debug_decl(g, var, var->value_ref); + gen_var_debug_decl(g, var); } fn_walk->data.inits.gen_i += 1; break; @@ -2035,7 +2035,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ } case FnWalkIdInits: if (var->decl_node) { - gen_var_debug_decl(g, var, var->value_ref); + gen_var_debug_decl(g, var); } fn_walk->data.inits.gen_i += 1; break; @@ -2073,7 +2073,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ } case FnWalkIdInits: if (var->decl_node) { - gen_var_debug_decl(g, var, var->value_ref); + gen_var_debug_decl(g, var); } fn_walk->data.inits.gen_i += 1; break; @@ -2111,7 +2111,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); if (var->decl_node) { - gen_var_debug_decl(g, var, var->value_ref); + gen_var_debug_decl(g, var); } fn_walk->data.inits.gen_i += 1; break; @@ -2206,7 +2206,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { } if (variable->decl_node) { - gen_var_debug_decl(g, variable, variable->value_ref); + gen_var_debug_decl(g, variable); } break; } @@ -3188,7 +3188,8 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrI if (var->ref_count == 0 && g->build_mode != BuildModeDebug) return nullptr; - gen_var_debug_decl(g, var, ir_llvm_value(g, instruction->var_ptr)); + var->value_ref = ir_llvm_value(g, instruction->var_ptr); + gen_var_debug_decl(g, var); return nullptr; } diff --git a/src/ir.cpp b/src/ir.cpp index 00f67c549e9e..4bdbfd3c9c70 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1426,11 +1426,15 @@ static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *sou return &export_instruction->base; } -static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) { +static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, + IrInstruction *result_loc) +{ IrInstructionLoadPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; + instruction->result_loc = result_loc; ir_ref_instruction(ptr, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -3151,7 +3155,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *err_union_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, new_result_loc); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr); + IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr, nullptr); IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_val); IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); @@ -3179,7 +3183,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, if (lval == LValPtr) return unwrapped_ptr; else - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); + return ir_build_load_ptr(irb, scope, node, unwrapped_ptr, nullptr); } } zig_unreachable(); @@ -3370,7 +3374,7 @@ static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *no IrInstruction *lvalue = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); if (lvalue == irb->codegen->invalid_instruction) return lvalue; - IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); + IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue, nullptr); IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); if (op2 == irb->codegen->invalid_instruction) return op2; @@ -3473,7 +3477,7 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_val); IrInstruction *is_comptime; @@ -3651,7 +3655,9 @@ static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode return ir_build_const_null(irb, scope, node); } -static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeSymbol); Buf *variable_name = node->data.symbol_expr.symbol; @@ -3682,7 +3688,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, if (lval == LValPtr) return var_ptr; else - return ir_build_load_ptr(irb, scope, node, var_ptr); + return ir_build_load_ptr(irb, scope, node, var_ptr, result_loc); } Tld *tld = find_decl(irb->codegen, scope, variable_name); @@ -5052,7 +5058,7 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode if (lval == LValPtr) return payload_ptr; - return ir_build_load_ptr(irb, scope, source_node, payload_ptr); + return ir_build_load_ptr(irb, scope, source_node, payload_ptr, nullptr); } static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5248,7 +5254,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n LValPtr, nullptr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); + IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr, nullptr); IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); @@ -5338,7 +5344,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n LValPtr, nullptr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; - IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); @@ -5495,7 +5501,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; - IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr); + IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr, nullptr); IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_val); IrInstruction *elem_var_type; @@ -5546,7 +5552,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_build_br(irb, child_scope, node, cond_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, cond_block); - IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca); + IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca, nullptr); IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); @@ -5558,7 +5564,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo if (node->data.for_expr.elem_is_ptr) { elem_val = elem_ptr; } else { - elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr); + elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr, nullptr); } ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_alloca, elem_val)); @@ -5760,7 +5766,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; - IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val); IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); @@ -5842,7 +5848,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); + IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr, nullptr); IrInstruction *is_err = ir_build_test_err(irb, scope, node, err_val); IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "TryOk"); @@ -6392,7 +6398,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr); + IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr, nullptr); IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_val); IrInstruction *is_comptime; @@ -6431,7 +6437,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode ir_set_cursor_at_end_and_append_block(irb, ok_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, parent_scope, node, err_union_ptr, false); - IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); + IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr, nullptr); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); @@ -6944,7 +6950,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. - IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr); + IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr, nullptr); ir_build_store_ptr(irb, scope, node, result_var_alloca, no_suspend_result); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, merge_block, const_bool_false); @@ -7004,7 +7010,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); - return ir_build_load_ptr(irb, scope, node, result_var_alloca); + return ir_build_load_ptr(irb, scope, node, result_var_alloca, nullptr); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -7152,7 +7158,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeCharLiteral: return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval); case NodeTypeSymbol: - return ir_gen_symbol(irb, scope, node, lval); + return ir_gen_symbol(irb, scope, node, lval, result_loc); case NodeTypeFnCallExpr: return ir_gen_fn_call(irb, scope, node, lval, result_loc); case NodeTypeIfBoolExpr: @@ -7179,7 +7185,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (lval == LValPtr) return ptr_instruction; - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_build_load_ptr(irb, scope, node, ptr_instruction, result_loc); } case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; @@ -7200,7 +7206,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (lval == LValPtr) return unwrapped_ptr; - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); + return ir_build_load_ptr(irb, scope, node, unwrapped_ptr, result_loc); } case NodeTypeBoolLiteral: return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); @@ -7349,7 +7355,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec coro_allocator_var_alloca); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); - IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); + IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr, nullptr); IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size); IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError"); @@ -7440,7 +7446,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); - IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); + IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, nullptr); IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); @@ -7452,7 +7458,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); - IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr); + IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, nullptr); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr); } // Before we destroy the coroutine frame, we need to load the target promise into @@ -7460,7 +7466,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // otherwise llvm tries to access memory inside the destroyed frame. IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, irb->exec->await_handle_var_ptr, false); - IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); + IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr, nullptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block); @@ -7487,7 +7493,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, ImplicitAllocatorIdLocalVar); IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, free_field_name); - IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr); + IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr, nullptr); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, @@ -7496,7 +7502,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); - IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); + IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr, nullptr); IrInstruction *mem_slice_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false, mem_slice_alloca); @@ -11061,7 +11067,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } // TODO if the instruction is a const ref instruction we can skip it IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, ptr); + source_instruction->source_node, ptr, nullptr); load_ptr_instruction->value.type = child_type; return load_ptr_instruction; } else { @@ -15115,13 +15121,6 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc } } -static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { - IrInstruction *ptr = load_ptr_instruction->ptr->child; - if (type_is_invalid(ptr->value.type)) - return ira->codegen->invalid_instruction; - return ir_get_deref(ira, &load_ptr_instruction->base, ptr); -} - static IrInstruction *ir_analyze_instruction_load_result(IrAnalyze *ira, IrInstructionLoadResult *instruction) { zig_panic("TODO"); } @@ -15194,9 +15193,26 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); } -static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInstructionStoreResult *instruction) { +static IrInstruction *ir_analyze_store_result(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + IrInstruction *result_loc) +{ Error err; + if ((err = resolve_possible_alloca_inference(ira, result_loc, value->value.type))) + return ira->codegen->invalid_instruction; + + // If the type is a scalar value, treat this instruction as a normal store pointer instruction. + // Or if the value is comptime known, analyze it as a store pointer, so that the + // const can be memcpy'd. + if (!type_has_bits(value->value.type) || !handle_is_ptr(value->value.type) || instr_is_comptime(value)) { + return ir_analyze_store_ptr(ira, source_instr, result_loc, value); + } + + // Otherwise, trust that the result location was populated by the expression that computed value. + return ir_const_void(ira, source_instr); +} + +static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInstructionStoreResult *instruction) { IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; @@ -15205,18 +15221,32 @@ static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInst if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; - if ((err = resolve_possible_alloca_inference(ira, result_loc, value->value.type))) + return ir_analyze_store_result(ira, &instruction->base, value, result_loc); +} + +static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) { + Error err; + + IrInstruction *ptr = instruction->ptr->child; + if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - // If the type is a scalar value, treat this instruction as a normal store pointer instruction. - // Or if the value is comptime known, analyze it as a store pointer, so that the - // const can be memcpy'd. - if (!type_has_bits(value->value.type) || !handle_is_ptr(value->value.type) || instr_is_comptime(value)) { - return ir_analyze_store_ptr(ira, &instruction->base, result_loc, value); + IrInstruction *deref = ir_get_deref(ira, &instruction->base, ptr); + if (instruction->result_loc == nullptr) + return deref; + + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + + if ((err = resolve_possible_alloca_inference(ira, result_loc, deref->value.type))) + return ira->codegen->invalid_instruction; + + if (handle_is_ptr(deref->value.type)) { + ir_analyze_store_ptr(ira, &instruction->base, result_loc, deref); } - - // Otherwise, trust that the result location was populated by the expression that computed value. - return ir_const_void(ira, &instruction->base); + + return deref; } static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { @@ -16055,7 +16085,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, IrInstruction *result = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, switch_target_instruction->base.source_node, - target_value_ptr); + target_value_ptr, nullptr); result->value.type = target_type; return result; } @@ -16086,7 +16116,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, } IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + switch_target_instruction->base.source_node, target_value_ptr, nullptr); union_value->value.type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, @@ -16111,7 +16141,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, } IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + switch_target_instruction->base.source_node, target_value_ptr, nullptr); enum_value->value.type = target_type; return enum_value; } @@ -16278,7 +16308,7 @@ static IrInstruction *ir_analyze_instruction_array_len(IrAnalyze *ira, array_len_instruction->base.source_node, array_value, field); len_ptr->value.type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_usize, true); IrInstruction *result = ir_build_load_ptr(&ira->new_irb, - array_len_instruction->base.scope, array_len_instruction->base.source_node, len_ptr); + array_len_instruction->base.scope, array_len_instruction->base.source_node, len_ptr, nullptr); result->value.type = ira->codegen->builtin_types.entry_usize; return result; } else { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 24c73c17b226..eb0054a20886 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -334,8 +334,11 @@ static void ir_print_var_ptr(IrPrint *irp, IrInstructionVarPtr *instruction) { } static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { - fprintf(irp->f, "*"); + fprintf(irp->f, "LoadPtr(ptr="); ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ",result="); + ir_print_other_instruction(irp, instruction->result_loc); + fprintf(irp->f, ")"); } static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) { @@ -1351,7 +1354,7 @@ static void ir_print_load_result(IrPrint *irp, IrInstructionLoadResult *instruct } static void ir_print_store_result(IrPrint *irp, IrInstructionStoreResult *instruction) { - fprintf(irp->f, "StoreResult(result_loc="); + fprintf(irp->f, "StoreResult(result="); ir_print_other_instruction(irp, instruction->result_loc); fprintf(irp->f, ",value="); ir_print_other_instruction(irp, instruction->value); From dcf1a5799b184dcd27e28689fe4d9d761a83b557 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 29 Oct 2018 17:14:41 -0400 Subject: [PATCH 010/190] copy elision: implicit cast optional wrap aggregate ```zig export fn entry() void { var x: ?Foo = foo(); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca { %Foo, i1 }, align 4 %0 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 1, !dbg !57 store i1 true, i1* %0, align 1, !dbg !57 %1 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 0, !dbg !57 call fastcc void @foo(%Foo* sret %1), !dbg !58 call void @llvm.dbg.declare(metadata { %Foo, i1 }* %x, metadata !45, metadata !DIExpression()), !dbg !57 ret void, !dbg !59 } ``` --- src/all_types.hpp | 7 ++ src/codegen.cpp | 11 +++ src/ir.cpp | 200 +++++++++++++++++++++++++++++++++------------- src/ir_print.cpp | 9 +++ 4 files changed, 173 insertions(+), 54 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index e7e22d814445..91259cc79f0a 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2173,6 +2173,7 @@ enum IrInstructionId { IrInstructionIdSqrt, IrInstructionIdErrSetCast, IrInstructionIdCheckRuntimeScope, + IrInstructionIdResultOptionalPayload, IrInstructionIdResultErrorUnionPayload, IrInstructionIdResultReturn, IrInstructionIdResultBytesToSlice, @@ -3294,6 +3295,12 @@ struct IrInstructionResultErrorUnionPayload { IrInstruction *prev_result_loc; }; +struct IrInstructionResultOptionalPayload { + IrInstruction base; + + IrInstruction *prev_result_loc; +}; + struct IrInstructionResultBytesToSlice { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 856b567f2392..f3aa5eb103f1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5099,6 +5099,15 @@ static LLVMValueRef ir_render_result_return(CodeGen *g, IrExecutable *executable return g->cur_ret_ptr; } +static LLVMValueRef ir_render_result_optional_payload(CodeGen *g, IrExecutable *executable, + IrInstructionResultOptionalPayload *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_null_index, ""); + gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); + return LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_child_index, ""); +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5340,6 +5349,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); case IrInstructionIdResultReturn: return ir_render_result_return(g, executable, (IrInstructionResultReturn *)instruction); + case IrInstructionIdResultOptionalPayload: + return ir_render_result_optional_payload(g, executable, (IrInstructionResultOptionalPayload *)instruction); case IrInstructionIdResultErrorUnionPayload: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: diff --git a/src/ir.cpp b/src/ir.cpp index 4bdbfd3c9c70..421d1b27302b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -863,6 +863,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionResultErrorUnion return IrInstructionIdResultErrorUnionPayload; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultOptionalPayload *) { + return IrInstructionIdResultOptionalPayload; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionResultReturn *) { return IrInstructionIdResultReturn; } @@ -2797,6 +2801,17 @@ static IrInstruction *ir_build_result_error_union_payload(IrBuilder *irb, Scope return &instruction->base; } +static IrInstruction *ir_build_result_optional_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc) +{ + IrInstructionResultOptionalPayload *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_result_return(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionResultReturn *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; @@ -9807,7 +9822,27 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { return const_val->data.x_ptr.data.fn.fn_entry; } -static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, +static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstruction *result_loc, + ZigType *needed_child_type) +{ + if (instr_is_comptime(result_loc)) { + zig_panic("TODO comptime ir_analyze_result_optional_payload"); + } + ZigType *old_ptr_type = result_loc->value.type; + assert(old_ptr_type->id == ZigTypeIdPointer); + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, + old_ptr_type->data.pointer.is_const, + old_ptr_type->data.pointer.is_volatile, + old_ptr_type->data.pointer.ptr_len, + 0, 0, 0); + + IrInstruction *result = ir_build_result_optional_payload(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc); + result->value.type = new_ptr_type; + return result; +} + +static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdOptional); @@ -10730,12 +10765,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk) { - return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type); } else if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { - return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type); } else { return ira->codegen->invalid_instruction; } @@ -10772,7 +10807,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_child_type); if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; - return ir_analyze_maybe_wrap(ira, source_instr, cast1, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type); } } } @@ -11028,6 +11063,110 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Zig return ir_analyze_cast(ira, value, expected_type, value); } +static Error resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *alloca, ZigType *child_type) { + Error err; + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) + return err; + alloca->base.value.type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, + PtrLenSingle, alloca->align, 0, 0); + return ErrorNone; +} + +static Error resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type) { + Error err; + assert(base->value.type->id == ZigTypeIdPointer); + ZigType *infer_child = base->value.type->data.pointer.child_type; + if (base->id == IrInstructionIdAllocaGen && infer_child == ira->codegen->builtin_types.entry_infer) { + if ((err = resolve_alloca_inference(ira, reinterpret_cast(base), child_type))) + return err; + } + return ErrorNone; +} + +static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + new_align, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); +} + +static ZigType *adjust_ptr_child(CodeGen *g, ZigType *ptr_type, ZigType *new_child) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra(g, + new_child, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + ptr_type->data.pointer.explicit_alignment, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); +} + +static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { + assert(is_slice(slice_type)); + ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry, + new_align); + return get_slice_type(g, ptr_type); +} + +static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_len, + ptr_type->data.pointer.explicit_alignment, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); +} + +static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *result_loc, + ZigType *needed_child_type) +{ + Error err; + if (type_is_invalid(result_loc->value.type) || type_is_invalid(needed_child_type)) + return ira->codegen->invalid_instruction; + if ((err = resolve_possible_alloca_inference(ira, result_loc, needed_child_type))) + return ira->codegen->invalid_instruction; + + if (needed_child_type == nullptr) + return result_loc; + + assert(result_loc->value.type->id == ZigTypeIdPointer); + ZigType *have_child_type = result_loc->value.type->data.pointer.child_type; + if (have_child_type == needed_child_type) + return result_loc; + + IrInstruction *source_instr = result_loc; + AstNode *source_node = source_instr->source_node; + + // perfect match or non-const to const + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, needed_child_type, have_child_type, + source_node, false); + if (const_cast_result.id == ConstCastResultIdInvalid) + return ira->codegen->invalid_instruction; + if (const_cast_result.id == ConstCastResultIdOk) { + ZigType *new_ptr_type = adjust_ptr_child(ira->codegen, result_loc->value.type, needed_child_type); + return ir_analyze_ptr_cast(ira, result_loc, result_loc, new_ptr_type, result_loc); + } + + // cast from T to ?T + if (have_child_type->id == ZigTypeIdOptional) { + if (types_match_const_cast_only(ira, needed_child_type, have_child_type->data.maybe.child_type, source_node, + false).id == ConstCastResultIdOk) + { + return ir_analyze_result_optional_payload(ira, result_loc, needed_child_type); + } + } + + ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, + buf_sprintf("expected type '%s', found '%s'", + buf_ptr(&needed_child_type->name), + buf_ptr(&have_child_type->name))); + report_recursive_error(ira, source_node, &const_cast_result, parent_msg); + return ira->codegen->invalid_instruction; +} + static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) { Error err; ZigType *type_entry = ptr->value.type; @@ -13110,26 +13249,6 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, return var_ptr_instruction; } -static Error resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *alloca, ZigType *child_type) { - Error err; - if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) - return err; - alloca->base.value.type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, - PtrLenSingle, alloca->align, 0, 0); - return ErrorNone; -} - -static Error resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type) { - Error err; - assert(base->value.type->id == ZigTypeIdPointer); - ZigType *infer_child = base->value.type->data.pointer.child_type; - if (base->id == IrInstructionIdAllocaGen && infer_child == ira->codegen->builtin_types.entry_infer) { - if ((err = resolve_alloca_inference(ira, reinterpret_cast(base), child_type))) - return err; - } - return ErrorNone; -} - static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline) @@ -13675,11 +13794,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *result_loc = nullptr; if (handle_is_ptr(return_type)) { - result_loc = call_instruction->result_loc->child; + result_loc = ir_implicit_cast_result(ira, call_instruction->result_loc->child, return_type); if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; - if ((err = resolve_possible_alloca_inference(ira, result_loc, return_type))) - return ira->codegen->invalid_instruction; } IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, @@ -14149,33 +14266,6 @@ static IrInstruction *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructi return result; } -static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { - assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra(g, - ptr_type->data.pointer.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - ptr_type->data.pointer.ptr_len, - new_align, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); -} - -static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { - assert(is_slice(slice_type)); - ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry, - new_align); - return get_slice_type(g, ptr_type); -} - -static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { - assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra(g, - ptr_type->data.pointer.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - ptr_len, - ptr_type->data.pointer.explicit_alignment, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); -} - static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) { Error err; IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->child; @@ -21096,6 +21186,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdCast: case IrInstructionIdDeclVarGen: case IrInstructionIdAllocaGen: + case IrInstructionIdResultOptionalPayload: zig_unreachable(); case IrInstructionIdReturn: @@ -21596,6 +21687,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: case IrInstructionIdEnumToInt: + case IrInstructionIdResultOptionalPayload: case IrInstructionIdResultErrorUnionPayload: case IrInstructionIdResultReturn: case IrInstructionIdResultParam: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index eb0054a20886..98d29fa79e5e 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1325,6 +1325,12 @@ static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *instruc fprintf(irp->f, ")"); } +static void ir_print_result_optional_payload(IrPrint *irp, IrInstructionResultOptionalPayload *instruction) { + fprintf(irp->f, "ResultOptionalPayload("); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); +} + static void ir_print_result_error_union_payload(IrPrint *irp, IrInstructionResultErrorUnionPayload *instruction) { fprintf(irp->f, "ResultErrorUnionPayload"); } @@ -1795,6 +1801,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdDeclVarGen: ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction); break; + case IrInstructionIdResultOptionalPayload: + ir_print_result_optional_payload(irp, (IrInstructionResultOptionalPayload *)instruction); + break; case IrInstructionIdResultErrorUnionPayload: ir_print_result_error_union_payload(irp, (IrInstructionResultErrorUnionPayload *)instruction); break; From ff943d918ffdd49406ec8897bc992f4aadde6fdf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 29 Oct 2018 18:57:29 -0400 Subject: [PATCH 011/190] copy elision: implicit cast optional wrap when payload is integer ```zig export fn entry() void { var a: i32 = 1234; var b: ?i32 = a; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %a = alloca i32, align 4 %b = alloca { i32, i1 }, align 4 store i32 1234, i32* %a, align 4, !dbg !55 call void @llvm.dbg.declare(metadata i32* %a, metadata !45, metadata !DIExpression()), !dbg !55 %0 = load i32, i32* %a, align 4, !dbg !56 %1 = getelementptr inbounds { i32, i1 }, { i32, i1 }* %b, i32 0, i32 1, !dbg !57 store i1 true, i1* %1, align 1, !dbg !57 %2 = getelementptr inbounds { i32, i1 }, { i32, i1 }* %b, i32 0, i32 0, !dbg !57 store i32 %0, i32* %2, align 4, !dbg !57 call void @llvm.dbg.declare(metadata { i32, i1 }* %b, metadata !48, metadata !DIExpression()), !dbg !57 ret void, !dbg !58 } ``` --- src/all_types.hpp | 1 - src/ir.cpp | 116 +++++++++++++++++++--------------------------- src/ir_print.cpp | 2 - 3 files changed, 48 insertions(+), 71 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 91259cc79f0a..3d004be788eb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2338,7 +2338,6 @@ struct IrInstructionLoadPtr { IrInstruction base; IrInstruction *ptr; - IrInstruction *result_loc; }; struct IrInstructionStorePtr { diff --git a/src/ir.cpp b/src/ir.cpp index 48448bdfdc4e..08646d908582 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1430,15 +1430,11 @@ static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *sou return &export_instruction->base; } -static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, - IrInstruction *result_loc) -{ +static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) { IrInstructionLoadPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; - instruction->result_loc = result_loc; ir_ref_instruction(ptr, irb->current_basic_block); - if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -3170,7 +3166,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *err_union_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, new_result_loc); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr, nullptr); + IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr); IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_val); IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); @@ -3198,7 +3194,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, if (lval == LValPtr) return unwrapped_ptr; else - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr, nullptr); + return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); } } zig_unreachable(); @@ -3389,7 +3385,7 @@ static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *no IrInstruction *lvalue = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); if (lvalue == irb->codegen->invalid_instruction) return lvalue; - IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue, nullptr); + IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); if (op2 == irb->codegen->invalid_instruction) return op2; @@ -3492,7 +3488,7 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr, nullptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_val); IrInstruction *is_comptime; @@ -3703,7 +3699,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, if (lval == LValPtr) return var_ptr; else - return ir_build_load_ptr(irb, scope, node, var_ptr, result_loc); + return ir_build_load_ptr(irb, scope, node, var_ptr); } Tld *tld = find_decl(irb->codegen, scope, variable_name); @@ -5073,7 +5069,7 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode if (lval == LValPtr) return payload_ptr; - return ir_build_load_ptr(irb, scope, source_node, payload_ptr, nullptr); + return ir_build_load_ptr(irb, scope, source_node, payload_ptr); } static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5269,7 +5265,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n LValPtr, nullptr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr, nullptr); + IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); @@ -5359,7 +5355,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n LValPtr, nullptr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; - IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr, nullptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); @@ -5516,7 +5512,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; - IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr, nullptr); + IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr); IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_val); IrInstruction *elem_var_type; @@ -5567,7 +5563,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_build_br(irb, child_scope, node, cond_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, cond_block); - IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca, nullptr); + IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca); IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); @@ -5579,7 +5575,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo if (node->data.for_expr.elem_is_ptr) { elem_val = elem_ptr; } else { - elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr, nullptr); + elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr); } ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_alloca, elem_val)); @@ -5781,7 +5777,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; - IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr, nullptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val); IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); @@ -5863,7 +5859,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr, nullptr); + IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); IrInstruction *is_err = ir_build_test_err(irb, scope, node, err_val); IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "TryOk"); @@ -6413,7 +6409,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr, nullptr); + IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr); IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_val); IrInstruction *is_comptime; @@ -6452,7 +6448,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode ir_set_cursor_at_end_and_append_block(irb, ok_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, parent_scope, node, err_union_ptr, false); - IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr, nullptr); + IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); @@ -6965,7 +6961,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. - IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr, nullptr); + IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr); ir_build_store_ptr(irb, scope, node, result_var_alloca, no_suspend_result); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, merge_block, const_bool_false); @@ -7025,7 +7021,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); - return ir_build_load_ptr(irb, scope, node, result_var_alloca, nullptr); + return ir_build_load_ptr(irb, scope, node, result_var_alloca); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -7200,7 +7196,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (lval == LValPtr) return ptr_instruction; - return ir_build_load_ptr(irb, scope, node, ptr_instruction, result_loc); + return ir_build_load_ptr(irb, scope, node, ptr_instruction); } case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; @@ -7221,7 +7217,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (lval == LValPtr) return unwrapped_ptr; - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr, result_loc); + return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); } case NodeTypeBoolLiteral: return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); @@ -7370,7 +7366,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec coro_allocator_var_alloca); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); - IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr, nullptr); + IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size); IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError"); @@ -7461,7 +7457,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); - IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, nullptr); + IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); @@ -7473,7 +7469,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); - IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, nullptr); + IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr); } // Before we destroy the coroutine frame, we need to load the target promise into @@ -7481,7 +7477,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // otherwise llvm tries to access memory inside the destroyed frame. IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, irb->exec->await_handle_var_ptr, false); - IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr, nullptr); + IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block); @@ -7508,7 +7504,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, ImplicitAllocatorIdLocalVar); IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, free_field_name); - IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr, nullptr); + IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, @@ -7517,7 +7513,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); - IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr, nullptr); + IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); IrInstruction *mem_slice_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false, mem_slice_alloca); @@ -11139,7 +11135,7 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *res // cast from T to ?T if (have_child_type->id == ZigTypeIdOptional) { - if (types_match_const_cast_only(ira, needed_child_type, have_child_type->data.maybe.child_type, source_node, + if (types_match_const_cast_only(ira, have_child_type->data.maybe.child_type, needed_child_type, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_result_optional_payload(ira, result_loc, needed_child_type); @@ -11148,8 +11144,8 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *res ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, buf_sprintf("expected type '%s', found '%s'", - buf_ptr(&needed_child_type->name), - buf_ptr(&have_child_type->name))); + buf_ptr(&have_child_type->name), + buf_ptr(&needed_child_type->name))); report_recursive_error(ira, source_node, &const_cast_result, parent_msg); return ira->codegen->invalid_instruction; } @@ -11193,7 +11189,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } // TODO if the instruction is a const ref instruction we can skip it IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, ptr, nullptr); + source_instruction->source_node, ptr); load_ptr_instruction->value.type = child_type; return load_ptr_instruction; } else { @@ -15271,22 +15267,23 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc } static IrInstruction *ir_analyze_store_result(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, - IrInstruction *result_loc) + IrInstruction *uncasted_result_loc) { - Error err; + if (instr_is_comptime(value)) { + // No need for the ir_implicit_cast_result for comptime values. + return ir_analyze_store_ptr(ira, source_instr, uncasted_result_loc, value); + } + + if (value->id == IrInstructionIdCall && handle_is_ptr(value->value.type)) { + // Elide this copy because it was the sret pointer in the function call. + return ir_const_void(ira, source_instr); + } - if ((err = resolve_possible_alloca_inference(ira, result_loc, value->value.type))) + IrInstruction *result_loc = ir_implicit_cast_result(ira, uncasted_result_loc, value->value.type); + if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; - // If the type is a scalar value, treat this instruction as a normal store pointer instruction. - // Or if the value is comptime known, analyze it as a store pointer, so that the - // const can be memcpy'd. - if (!type_has_bits(value->value.type) || !handle_is_ptr(value->value.type) || instr_is_comptime(value)) { - return ir_analyze_store_ptr(ira, source_instr, result_loc, value); - } - - // Otherwise, trust that the result location was populated by the expression that computed value. - return ir_const_void(ira, source_instr); + return ir_analyze_store_ptr(ira, source_instr, result_loc, value); } static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInstructionStoreResult *instruction) { @@ -15302,28 +15299,11 @@ static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInst } static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) { - Error err; - IrInstruction *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *deref = ir_get_deref(ira, &instruction->base, ptr); - if (instruction->result_loc == nullptr) - return deref; - - IrInstruction *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value.type)) - return ira->codegen->invalid_instruction; - - if ((err = resolve_possible_alloca_inference(ira, result_loc, deref->value.type))) - return ira->codegen->invalid_instruction; - - if (handle_is_ptr(deref->value.type)) { - ir_analyze_store_ptr(ira, &instruction->base, result_loc, deref); - } - - return deref; + return ir_get_deref(ira, &instruction->base, ptr); } static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { @@ -16162,7 +16142,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, IrInstruction *result = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, switch_target_instruction->base.source_node, - target_value_ptr, nullptr); + target_value_ptr); result->value.type = target_type; return result; } @@ -16193,7 +16173,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, } IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr, nullptr); + switch_target_instruction->base.source_node, target_value_ptr); union_value->value.type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, @@ -16218,7 +16198,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, } IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr, nullptr); + switch_target_instruction->base.source_node, target_value_ptr); enum_value->value.type = target_type; return enum_value; } @@ -16385,7 +16365,7 @@ static IrInstruction *ir_analyze_instruction_array_len(IrAnalyze *ira, array_len_instruction->base.source_node, array_value, field); len_ptr->value.type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_usize, true); IrInstruction *result = ir_build_load_ptr(&ira->new_irb, - array_len_instruction->base.scope, array_len_instruction->base.source_node, len_ptr, nullptr); + array_len_instruction->base.scope, array_len_instruction->base.source_node, len_ptr); result->value.type = ira->codegen->builtin_types.entry_usize; return result; } else { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 98d29fa79e5e..b02381d08673 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -336,8 +336,6 @@ static void ir_print_var_ptr(IrPrint *irp, IrInstructionVarPtr *instruction) { static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { fprintf(irp->f, "LoadPtr(ptr="); ir_print_other_instruction(irp, instruction->ptr); - fprintf(irp->f, ",result="); - ir_print_other_instruction(irp, instruction->result_loc); fprintf(irp->f, ")"); } From d0dcfea68a3cfa96f34b26c9eefe76f1b2ca94de Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 00:43:06 -0400 Subject: [PATCH 012/190] copy elision: if bool expression with aggregate ```zig export fn entry() void { var y = true; var z = bar(); var x = if (y) foo() else z; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %y = alloca i1, align 1 %z = alloca %Foo, align 4 %x = alloca %Foo, align 4 store i1 true, i1* %y, align 1, !dbg !57 call void @llvm.dbg.declare(metadata i1* %y, metadata !45, metadata !DIExpression()), !dbg !58 call fastcc void @bar(%Foo* sret %z), !dbg !59 call void @llvm.dbg.declare(metadata %Foo* %z, metadata !48, metadata !DIExpression()), !dbg !60 %0 = load i1, i1* %y, align 1, !dbg !61 br i1 %0, label %Then, label %Else, !dbg !61 Then: ; preds = %Entry call fastcc void @foo(%Foo* sret %x), !dbg !62 br label %EndIf, !dbg !63 Else: ; preds = %Entry %1 = bitcast %Foo* %z to i8*, !dbg !64 %2 = bitcast %Foo* %x to i8*, !dbg !64 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %2, i8* align 4 %1, i64 8, i1 false), !dbg !64 br label %EndIf, !dbg !63 EndIf: ; preds = %Else, %Then call void @llvm.dbg.declare(metadata %Foo* %x, metadata !55, metadata !DIExpression()), !dbg !65 ret void, !dbg !66 } ``` --- src/all_types.hpp | 10 ++ src/codegen.cpp | 22 +++ src/ir.cpp | 375 +++++++++++++++++++++++++--------------------- src/ir_print.cpp | 13 +- 4 files changed, 247 insertions(+), 173 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 3d004be788eb..ae411793ecca 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2184,6 +2184,7 @@ enum IrInstructionId { IrInstructionIdStoreResult, IrInstructionIdAllocaSrc, IrInstructionIdAllocaGen, + IrInstructionIdAssertNonError, }; struct IrInstruction { @@ -2338,6 +2339,7 @@ struct IrInstructionLoadPtr { IrInstruction base; IrInstruction *ptr; + IrInstruction *result_loc; }; struct IrInstructionStorePtr { @@ -3011,6 +3013,8 @@ struct IrInstructionTypeName { enum LVal { LValNone, LValPtr, + LValErrorUnion, + LValOptional, }; struct IrInstructionDeclRef { @@ -3346,6 +3350,12 @@ struct IrInstructionAllocaGen { const char *name_hint; }; +struct IrInstructionAssertNonError { + IrInstruction base; + + IrInstruction *err_code; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index f3aa5eb103f1..2d2dc4c7a03b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5108,6 +5108,26 @@ static LLVMValueRef ir_render_result_optional_payload(CodeGen *g, IrExecutable * return LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_child_index, ""); } +static LLVMValueRef ir_render_assert_non_error(CodeGen *g, IrExecutable *executable, + IrInstructionAssertNonError *instruction) +{ + if (!ir_want_runtime_safety(g, &instruction->base) || g->errors_by_index.length <= 1) { + return nullptr; + } + LLVMValueRef err_val = ir_llvm_value(g, instruction->err_code); + LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref); + LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); + LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrError"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrOk"); + LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); + + LLVMPositionBuilderAtEnd(g->builder, err_block); + gen_safety_crash_for_err(g, err_val, instruction->base.scope); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return nullptr; +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5351,6 +5371,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_result_return(g, executable, (IrInstructionResultReturn *)instruction); case IrInstructionIdResultOptionalPayload: return ir_render_result_optional_payload(g, executable, (IrInstructionResultOptionalPayload *)instruction); + case IrInstructionIdAssertNonError: + return ir_render_assert_non_error(g, executable, (IrInstructionAssertNonError *)instruction); case IrInstructionIdResultErrorUnionPayload: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: diff --git a/src/ir.cpp b/src/ir.cpp index 08646d908582..7a1f76b3fb8b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -895,6 +895,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAllocaGen *) { return IrInstructionIdAllocaGen; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonError *) { + return IrInstructionIdAssertNonError; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -1430,11 +1434,15 @@ static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *sou return &export_instruction->base; } -static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) { +static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, + IrInstruction *result_loc) +{ IrInstructionLoadPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; + instruction->result_loc = result_loc; ir_ref_instruction(ptr, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -2786,17 +2794,6 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, return &instruction->base; } -static IrInstruction *ir_build_result_error_union_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *prev_result_loc) -{ - IrInstructionResultErrorUnionPayload *instruction = ir_build_instruction(irb, scope, source_node); - instruction->prev_result_loc = prev_result_loc; - - ir_ref_instruction(prev_result_loc, irb->current_basic_block); - - return &instruction->base; -} - static IrInstruction *ir_build_result_optional_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *prev_result_loc) { @@ -2885,6 +2882,17 @@ static IrInstructionAllocaGen *ir_create_alloca_gen(IrBuilder *irb, Scope *scope return instruction; } +static IrInstruction *ir_build_assert_non_error(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *err_code) +{ + IrInstructionAssertNonError *instruction = ir_build_instruction(irb, scope, source_node); + instruction->err_code = err_code; + + ir_ref_instruction(err_code, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3074,6 +3082,34 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime); } +static IrInstruction *ir_gen_lval_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ + switch (lval) { + case LValNone: + return ir_build_load_ptr(irb, scope, node, result_loc, nullptr); + case LValPtr: + return result_loc; + case LValErrorUnion: + case LValOptional: + zig_unreachable(); + } + zig_unreachable(); +} + +static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc, IrInstruction *value) +{ + if (result_loc != nullptr) { + ir_build_store_ptr(irb, scope, node, result_loc, value); + if (lval != LValNone) { + assert(lval != LValPtr); + return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); + } + } + return ir_lval_wrap(irb, scope, value, lval); +} + static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, IrInstruction *result_loc) { @@ -3162,12 +3198,10 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindError: { assert(expr_node); - IrInstruction *new_result_loc = ir_build_result_error_union_payload(irb, scope, node, result_loc); - IrInstruction *err_union_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, new_result_loc); - if (err_union_ptr == irb->codegen->invalid_instruction) + IrInstruction *err_val = ir_gen_node(irb, expr_node, scope, LValErrorUnion, result_loc); + if (err_val == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr); - IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_val); + IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_val); IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue"); @@ -3182,7 +3216,6 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, ir_set_cursor_at_end_and_append_block(irb, return_block); if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) { - IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr); if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr(irb, scope, node); } @@ -3190,11 +3223,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, } ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false); - if (lval == LValPtr) - return unwrapped_ptr; - else - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); + return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); } } zig_unreachable(); @@ -3375,7 +3404,6 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - ir_build_store_result(irb, scope, node, lvalue, rvalue); return ir_build_const_void(irb, scope, node); } @@ -3385,7 +3413,7 @@ static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *no IrInstruction *lvalue = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); if (lvalue == irb->codegen->invalid_instruction) return lvalue; - IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); + IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue, nullptr); IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); if (op2 == irb->codegen->invalid_instruction) return op2; @@ -3478,19 +3506,18 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } -static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode *node, IrInstruction *result_loc) { +static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeBinOpExpr); AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; - IrInstruction *maybe_ptr = ir_gen_node(irb, op1_node, parent_scope, LValPtr, nullptr); - if (maybe_ptr == irb->codegen->invalid_instruction) + IrInstruction *is_non_null = ir_gen_node(irb, op1_node, parent_scope, LValOptional, result_loc); + if (is_non_null == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr); - IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_val); - IrInstruction *is_comptime; if (ir_should_inline(irb->exec, parent_scope)) { is_comptime = ir_build_const_bool(irb, parent_scope, node, true); @@ -3498,33 +3525,19 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null); } - IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull"); - IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull"); - IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd"); - ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime); + IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OrElseNull"); + IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OrElseEnd"); + ir_build_cond_br(irb, parent_scope, node, is_non_null, end_block, null_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, null_block); IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope, LValNone, result_loc); if (null_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrBasicBlock *after_null_block = irb->current_basic_block; if (!instr_is_unreachable(null_result)) ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); - ir_set_cursor_at_end_and_append_block(irb, ok_block); - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, parent_scope, node, maybe_ptr, false); - IrInstruction *unwrapped_payload = ir_build_load_result(irb, parent_scope, node, unwrapped_ptr, result_loc); - IrBasicBlock *after_ok_block = irb->current_basic_block; - ir_build_br(irb, parent_scope, node, end_block, is_comptime); - ir_set_cursor_at_end_and_append_block(irb, end_block); - IrInstruction **incoming_values = allocate(2); - incoming_values[0] = null_result; - incoming_values[1] = unwrapped_payload; - IrBasicBlock **incoming_blocks = allocate(2); - incoming_blocks[0] = after_null_block; - incoming_blocks[1] = after_ok_block; - return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); + return ir_gen_lval_ptr(irb, parent_scope, node, lval, result_loc); } static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -3544,7 +3557,9 @@ static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, As return ir_build_error_union(irb, parent_scope, node, err_set, payload); } -static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { +static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeBinOpExpr); BinOpType bin_op_type = node->data.bin_op_expr.bin_op; @@ -3552,87 +3567,87 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, case BinOpTypeInvalid: zig_unreachable(); case BinOpTypeAssign: - return ir_gen_assign(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_gen_assign(irb, scope, node), lval); case BinOpTypeAssignTimes: - return ir_gen_assign_op(irb, scope, node, IrBinOpMult); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMult), lval); case BinOpTypeAssignTimesWrap: - return ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap), lval); case BinOpTypeAssignDiv: - return ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified), lval); case BinOpTypeAssignMod: - return ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified), lval); case BinOpTypeAssignPlus: - return ir_gen_assign_op(irb, scope, node, IrBinOpAdd); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAdd), lval); case BinOpTypeAssignPlusWrap: - return ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap), lval); case BinOpTypeAssignMinus: - return ir_gen_assign_op(irb, scope, node, IrBinOpSub); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSub), lval); case BinOpTypeAssignMinusWrap: - return ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap), lval); case BinOpTypeAssignBitShiftLeft: - return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy), lval); case BinOpTypeAssignBitShiftRight: - return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy), lval); case BinOpTypeAssignBitAnd: - return ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd), lval); case BinOpTypeAssignBitXor: - return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinXor), lval); case BinOpTypeAssignBitOr: - return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval); case BinOpTypeAssignMergeErrorSets: - return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets); + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets), lval); case BinOpTypeBoolOr: - return ir_gen_bool_or(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval); case BinOpTypeBoolAnd: - return ir_gen_bool_and(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_gen_bool_and(irb, scope, node), lval); case BinOpTypeCmpEq: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq), lval); case BinOpTypeCmpNotEq: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq), lval); case BinOpTypeCmpLessThan: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan), lval); case BinOpTypeCmpGreaterThan: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan), lval); case BinOpTypeCmpLessOrEq: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq), lval); case BinOpTypeCmpGreaterOrEq: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq), lval); case BinOpTypeBinOr: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr), lval); case BinOpTypeBinXor: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor), lval); case BinOpTypeBinAnd: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd), lval); case BinOpTypeBitShiftLeft: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy), lval); case BinOpTypeBitShiftRight: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy), lval); case BinOpTypeAdd: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd), lval); case BinOpTypeAddWrap: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap), lval); case BinOpTypeSub: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpSub); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSub), lval); case BinOpTypeSubWrap: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap), lval); case BinOpTypeMult: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpMult); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMult), lval); case BinOpTypeMultWrap: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap), lval); case BinOpTypeDiv: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified), lval); case BinOpTypeMod: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified), lval); case BinOpTypeArrayCat: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat), lval); case BinOpTypeArrayMult: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval); case BinOpTypeMergeErrorSets: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets), lval); case BinOpTypeUnwrapOptional: - return ir_gen_orelse(irb, scope, node, result_loc); + return ir_gen_orelse(irb, scope, node, lval, result_loc); case BinOpTypeErrorUnion: - return ir_gen_error_union(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_gen_error_union(irb, scope, node), lval); } zig_unreachable(); } @@ -3685,11 +3700,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, ZigType *primitive_type = get_primitive_type(irb->codegen, variable_name); if (primitive_type != nullptr) { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); - if (lval == LValPtr) { - return ir_build_ref(irb, scope, node, value, false, false); - } else { - return value; - } + return ir_lval_wrap(irb, scope, value, lval); } ScopeFnDef *crossed_fndef_scope; @@ -3698,8 +3709,8 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope); if (lval == LValPtr) return var_ptr; - else - return ir_build_load_ptr(irb, scope, node, var_ptr); + IrInstruction *loaded = ir_build_load_ptr(irb, scope, node, var_ptr, result_loc); + return ir_lval_wrap(irb, scope, loaded, lval); } Tld *tld = find_decl(irb->codegen, scope, variable_name); @@ -4949,7 +4960,6 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope, LValNone, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; - IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -4962,19 +4972,11 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode } else { else_expr_result = ir_build_const_void(irb, scope, node); } - IrBasicBlock *after_else_block = irb->current_basic_block; if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); - IrInstruction **incoming_values = allocate(2); - incoming_values[0] = then_expr_result; - incoming_values[1] = else_expr_result; - IrBasicBlock **incoming_blocks = allocate(2); - incoming_blocks[0] = after_then_block; - incoming_blocks[1] = after_else_block; - - return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); + return ir_build_load_ptr(irb, scope, node, result_loc, nullptr); } static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { @@ -5055,21 +5057,16 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode ptr_len, align_value, bit_offset_start, host_int_bytes); } -static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, - LVal lval) +static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, + AstNode *expr_node, LVal lval, IrInstruction *result_loc) { - IrInstruction *err_union_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, nullptr); - if (err_union_ptr == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; - - IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, source_node, err_union_ptr, true); - if (payload_ptr == irb->codegen->invalid_instruction) + IrInstruction *err_val = ir_gen_node(irb, expr_node, scope, LValErrorUnion, result_loc); + if (err_val == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - if (lval == LValPtr) - return payload_ptr; + ir_build_assert_non_error(irb, scope, source_node, err_val); - return ir_build_load_ptr(irb, scope, source_node, payload_ptr); + return ir_gen_lval_ptr(irb, scope, source_node, lval, result_loc); } static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5222,7 +5219,6 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod if (init_value == irb->codegen->invalid_instruction) return init_value; - ir_build_store_result(irb, scope, node, alloca, init_value); return ir_build_var_decl_src(irb, scope, node, var, type_instruction, align_value, alloca); } @@ -5265,7 +5261,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n LValPtr, nullptr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); + IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr, nullptr); IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); @@ -5355,7 +5351,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n LValPtr, nullptr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; - IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); @@ -5512,7 +5508,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; - IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr); + IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr, nullptr); IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_val); IrInstruction *elem_var_type; @@ -5563,7 +5559,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_build_br(irb, child_scope, node, cond_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, cond_block); - IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca); + IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca, nullptr); IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); @@ -5575,7 +5571,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo if (node->data.for_expr.elem_is_ptr) { elem_val = elem_ptr; } else { - elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr); + elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr, nullptr); } ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_alloca, elem_val)); @@ -5623,9 +5619,12 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } -static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeBoolLiteral); - return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value); + IrInstruction *const_bool_val = ir_build_const_bool(irb, scope, node, node->data.bool_literal.value); + return ir_gen_value(irb, scope, node, lval, result_loc, const_bool_val); } static IrInstruction *ir_gen_string_literal(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5777,7 +5776,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; - IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val); IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); @@ -5859,7 +5858,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); + IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr, nullptr); IrInstruction *is_err = ir_build_test_err(irb, scope, node, err_val); IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "TryOk"); @@ -6388,7 +6387,9 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true, result_loc); } -static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node, IrInstruction *result_loc) { +static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeUnwrapErrorExpr); AstNode *op1_node = node->data.unwrap_err_expr.op1; @@ -6402,15 +6403,14 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); return irb->codegen->invalid_instruction; } - return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LValNone); + return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc); } - IrInstruction *err_union_ptr = ir_gen_node(irb, op1_node, parent_scope, LValPtr, nullptr); - if (err_union_ptr == irb->codegen->invalid_instruction) + IrInstruction *err_val = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnion, result_loc); + if (err_val == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr); - IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_val); + IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_val); IrInstruction *is_comptime; if (ir_should_inline(irb->exec, parent_scope)) { @@ -6419,10 +6419,9 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_err); } - IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "CatchOk"); IrBasicBlock *err_block = ir_create_basic_block(irb, parent_scope, "CatchError"); IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "CatchEnd"); - ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime); + ir_build_cond_br(irb, parent_scope, node, is_err, err_block, end_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, err_block); Scope *err_scope; @@ -6434,7 +6433,6 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode ZigVar *var = ir_create_var(irb, node, parent_scope, var_name, is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; - IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr); ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, err_val); } else { err_scope = parent_scope; @@ -6442,24 +6440,11 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope, LValNone, result_loc); if (err_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrBasicBlock *after_err_block = irb->current_basic_block; if (!instr_is_unreachable(err_result)) ir_mark_gen(ir_build_br(irb, err_scope, node, end_block, is_comptime)); - ir_set_cursor_at_end_and_append_block(irb, ok_block); - IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, parent_scope, node, err_union_ptr, false); - IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); - IrBasicBlock *after_ok_block = irb->current_basic_block; - ir_build_br(irb, parent_scope, node, end_block, is_comptime); - ir_set_cursor_at_end_and_append_block(irb, end_block); - IrInstruction **incoming_values = allocate(2); - incoming_values[0] = err_result; - incoming_values[1] = unwrapped_payload; - IrBasicBlock **incoming_blocks = allocate(2); - incoming_blocks[0] = after_err_block; - incoming_blocks[1] = after_ok_block; - return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); + return ir_gen_lval_ptr(irb, parent_scope, node, lval, result_loc); } static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) { @@ -6961,7 +6946,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. - IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr); + IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr, nullptr); ir_build_store_ptr(irb, scope, node, result_var_alloca, no_suspend_result); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, merge_block, const_bool_false); @@ -7021,7 +7006,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); - return ir_build_load_ptr(irb, scope, node, result_var_alloca); + return ir_build_load_ptr(irb, scope, node, result_var_alloca, nullptr); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -7161,7 +7146,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeGroupedExpr: return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc); case NodeTypeBinOpExpr: - return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node, result_loc), lval); + return ir_gen_bin_op(irb, scope, node, lval, result_loc); case NodeTypeIntLiteral: return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval); case NodeTypeFloatLiteral: @@ -7196,7 +7181,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (lval == LValPtr) return ptr_instruction; - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_build_load_ptr(irb, scope, node, ptr_instruction, nullptr); } case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; @@ -7217,10 +7202,10 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (lval == LValPtr) return unwrapped_ptr; - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); + return ir_build_load_ptr(irb, scope, node, unwrapped_ptr, nullptr); } case NodeTypeBoolLiteral: - return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); + return ir_gen_bool_literal(irb, scope, node, lval, result_loc); case NodeTypeArrayType: return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval); case NodeTypePointerType: @@ -7256,7 +7241,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSliceExpr: return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node, result_loc), lval); case NodeTypeUnwrapErrorExpr: - return ir_lval_wrap(irb, scope, ir_gen_catch(irb, scope, node, result_loc), lval); + return ir_gen_catch(irb, scope, node, lval, result_loc); case NodeTypeContainerDecl: return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: @@ -7366,7 +7351,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec coro_allocator_var_alloca); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); - IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); + IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr, nullptr); IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size); IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError"); @@ -7457,7 +7442,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); - IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); + IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, nullptr); IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); @@ -7469,7 +7454,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); - IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr); + IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, nullptr); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr); } // Before we destroy the coroutine frame, we need to load the target promise into @@ -7477,7 +7462,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // otherwise llvm tries to access memory inside the destroyed frame. IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, irb->exec->await_handle_var_ptr, false); - IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); + IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr, nullptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block); @@ -7504,7 +7489,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, ImplicitAllocatorIdLocalVar); IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, free_field_name); - IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr); + IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr, nullptr); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, @@ -7513,7 +7498,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); - IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); + IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr, nullptr); IrInstruction *mem_slice_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false, mem_slice_alloca); @@ -11189,7 +11174,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } // TODO if the instruction is a const ref instruction we can skip it IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, ptr); + source_instruction->source_node, ptr, nullptr); load_ptr_instruction->value.type = child_type; return load_ptr_instruction; } else { @@ -15201,6 +15186,10 @@ static IrInstruction *ir_analyze_instruction_load_result(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, IrInstruction *value) { + Error err; + if ((err = resolve_possible_alloca_inference(ira, ptr, value->value.type))) + return ira->codegen->invalid_instruction; + if (ptr->value.type->id != ZigTypeIdPointer) { ir_add_error(ira, ptr, buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); @@ -15303,7 +15292,17 @@ static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstruct if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - return ir_get_deref(ira, &instruction->base, ptr); + IrInstruction *deref = ir_get_deref(ira, &instruction->base, ptr); + if (instruction->result_loc == nullptr) + return deref; + + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + + ir_analyze_store_ptr(ira, &instruction->base, result_loc, deref); + + return deref; } static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { @@ -16142,7 +16141,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, IrInstruction *result = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, switch_target_instruction->base.source_node, - target_value_ptr); + target_value_ptr, nullptr); result->value.type = target_type; return result; } @@ -16173,7 +16172,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, } IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + switch_target_instruction->base.source_node, target_value_ptr, nullptr); union_value->value.type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, @@ -16198,7 +16197,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, } IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + switch_target_instruction->base.source_node, target_value_ptr, nullptr); enum_value->value.type = target_type; return enum_value; } @@ -16365,7 +16364,7 @@ static IrInstruction *ir_analyze_instruction_array_len(IrAnalyze *ira, array_len_instruction->base.source_node, array_value, field); len_ptr->value.type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_usize, true); IrInstruction *result = ir_build_load_ptr(&ira->new_irb, - array_len_instruction->base.scope, array_len_instruction->base.source_node, len_ptr); + array_len_instruction->base.scope, array_len_instruction->base.source_node, len_ptr, nullptr); result->value.type = ira->codegen->builtin_types.entry_usize; return result; } else { @@ -19532,6 +19531,33 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, } +static IrInstruction *ir_analyze_instruction_assert_non_error(IrAnalyze *ira, + IrInstructionAssertNonError *instruction) +{ + IrInstruction *err_code = instruction->err_code->child; + if (type_is_invalid(err_code->value.type)) + return ira->codegen->invalid_instruction; + + assert(err_code->value.type->id == ZigTypeIdErrorSet); + + if (instr_is_comptime(err_code)) { + ConstExprValue *err_val = ir_resolve_const(ira, err_code, UndefBad); + if (err_val == nullptr) + return ira->codegen->invalid_instruction; + ErrorTableEntry *err = err_val->data.x_err_set; + if (err != nullptr) { + ir_add_error(ira, &instruction->base, + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + return ira->codegen->invalid_instruction; + } + return ir_const_void(ira, &instruction->base); + } + + ir_build_assert_non_error(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, err_code); + return ir_const_void(ira, &instruction->base); +} + static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) { Error err; AstNode *proto_node = instruction->base.source_node; @@ -21310,6 +21336,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_unwrap_err_code(ira, (IrInstructionUnwrapErrCode *)instruction); case IrInstructionIdUnwrapErrPayload: return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstructionUnwrapErrPayload *)instruction); + case IrInstructionIdAssertNonError: + return ir_analyze_instruction_assert_non_error(ira, (IrInstructionAssertNonError *)instruction); case IrInstructionIdFnProto: return ir_analyze_instruction_fn_proto(ira, (IrInstructionFnProto *)instruction); case IrInstructionIdTestComptime: @@ -21562,12 +21590,12 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAtomicRmw: case IrInstructionIdLoadResult: case IrInstructionIdStoreResult: + case IrInstructionIdAssertNonError: return true; case IrInstructionIdPhi: case IrInstructionIdUnOp: case IrInstructionIdBinOp: - case IrInstructionIdLoadPtr: case IrInstructionIdConst: case IrInstructionIdCast: case IrInstructionIdContainerInitList: @@ -21665,6 +21693,9 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAllocaGen: return false; + case IrInstructionIdLoadPtr: + return reinterpret_cast(instruction)->result_loc != nullptr; + case IrInstructionIdAsm: { IrInstructionAsm *asm_instruction = (IrInstructionAsm *)instruction; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b02381d08673..7fa77445a4cd 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -336,6 +336,8 @@ static void ir_print_var_ptr(IrPrint *irp, IrInstructionVarPtr *instruction) { static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { fprintf(irp->f, "LoadPtr(ptr="); ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ",result="); + ir_print_other_instruction(irp, instruction->result_loc); fprintf(irp->f, ")"); } @@ -1370,13 +1372,19 @@ static void ir_print_alloca_src(IrPrint *irp, IrInstructionAllocaSrc *instructio ir_print_other_instruction(irp, instruction->child_type); fprintf(irp->f, ",align="); ir_print_other_instruction(irp, instruction->align); - fprintf(irp->f, ")"); + fprintf(irp->f, ",name=%s)", instruction->name_hint); } static void ir_print_alloca_gen(IrPrint *irp, IrInstructionAllocaGen *instruction) { fprintf(irp->f, "AllocaGen(align=%" PRIu32 ",name=%s)", instruction->align, instruction->name_hint); } +static void ir_print_assert_non_error(IrPrint *irp, IrInstructionAssertNonError *instruction) { + fprintf(irp->f, "AssertNonError("); + ir_print_other_instruction(irp, instruction->err_code); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1832,6 +1840,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdAllocaGen: ir_print_alloca_gen(irp, (IrInstructionAllocaGen *)instruction); break; + case IrInstructionIdAssertNonError: + ir_print_assert_non_error(irp, (IrInstructionAssertNonError *)instruction); + break; } fprintf(irp->f, "\n"); } From 7608505c5709b3403ecfc0173bff5f7c038f7423 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 01:06:00 -0400 Subject: [PATCH 013/190] copy elision: implicit cast payload to error union ```zig export fn entry() void { var x: error!Foo = foo(); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca { i16, %Foo }, align 4 %0 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 0, !dbg !56 store i16 0, i16* %0, align 2, !dbg !56 %1 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 1, !dbg !56 call fastcc void @foo(%Foo* sret %1), !dbg !57 call void @llvm.dbg.declare(metadata { i16, %Foo }* %x, metadata !45, metadata !DIExpression()), !dbg !56 ret void, !dbg !58 } ``` --- src/codegen.cpp | 14 ++++++++++++-- src/ir.cpp | 44 +++++++++++++++++++++++++++++++++++++++----- src/ir_print.cpp | 4 +++- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 2d2dc4c7a03b..8e5a0b20f711 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5108,6 +5108,16 @@ static LLVMValueRef ir_render_result_optional_payload(CodeGen *g, IrExecutable * return LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_child_index, ""); } +static LLVMValueRef ir_render_result_error_union_payload(CodeGen *g, IrExecutable *executable, + IrInstructionResultErrorUnionPayload *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, err_union_err_index, ""); + LLVMTypeRef err_type_ref = g->builtin_types.entry_global_error_set->type_ref; + gen_store_untyped(g, LLVMConstInt(err_type_ref, 0, false), err_val_ptr, 0, false); + return LLVMBuildStructGEP(g->builder, prev_result_loc, err_union_payload_index, ""); +} + static LLVMValueRef ir_render_assert_non_error(CodeGen *g, IrExecutable *executable, IrInstructionAssertNonError *instruction) { @@ -5371,10 +5381,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_result_return(g, executable, (IrInstructionResultReturn *)instruction); case IrInstructionIdResultOptionalPayload: return ir_render_result_optional_payload(g, executable, (IrInstructionResultOptionalPayload *)instruction); + case IrInstructionIdResultErrorUnionPayload: + return ir_render_result_error_union_payload(g, executable, (IrInstructionResultErrorUnionPayload *)instruction); case IrInstructionIdAssertNonError: return ir_render_assert_non_error(g, executable, (IrInstructionAssertNonError *)instruction); - case IrInstructionIdResultErrorUnionPayload: - zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: zig_panic("TODO"); case IrInstructionIdResultBytesToSlice: diff --git a/src/ir.cpp b/src/ir.cpp index 7a1f76b3fb8b..d401770629ab 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2805,6 +2805,17 @@ static IrInstruction *ir_build_result_optional_payload(IrBuilder *irb, Scope *sc return &instruction->base; } +static IrInstruction *ir_build_result_error_union_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc) +{ + IrInstructionResultErrorUnionPayload *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_result_return(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionResultReturn *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; @@ -9812,10 +9823,7 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr ZigType *old_ptr_type = result_loc->value.type; assert(old_ptr_type->id == ZigTypeIdPointer); ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, - old_ptr_type->data.pointer.is_const, - old_ptr_type->data.pointer.is_volatile, - old_ptr_type->data.pointer.ptr_len, - 0, 0, 0); + false, old_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); IrInstruction *result = ir_build_result_optional_payload(&ira->new_irb, result_loc->scope, result_loc->source_node, result_loc); @@ -9823,6 +9831,23 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr return result; } +static IrInstruction *ir_analyze_result_error_union_payload(IrAnalyze *ira, IrInstruction *result_loc, + ZigType *needed_child_type) +{ + if (instr_is_comptime(result_loc)) { + zig_panic("TODO comptime ir_analyze_result_error_union_payload"); + } + ZigType *old_ptr_type = result_loc->value.type; + assert(old_ptr_type->id == ZigTypeIdPointer); + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, + false, old_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + + IrInstruction *result = ir_build_result_error_union_payload(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc); + result->value.type = new_ptr_type; + return result; +} + static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { @@ -10787,7 +10812,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); } - // cast from child type of error type to error type + // cast from T to E!T if (wanted_type->id == ZigTypeIdErrorUnion) { if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node, false).id == ConstCastResultIdOk) @@ -11127,6 +11152,15 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *res } } + // cast from T to E!T + if (have_child_type->id == ZigTypeIdErrorUnion) { + if (types_match_const_cast_only(ira, have_child_type->data.error_union.payload_type, needed_child_type, + source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_result_error_union_payload(ira, result_loc, needed_child_type); + } + } + ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, buf_sprintf("expected type '%s', found '%s'", buf_ptr(&have_child_type->name), diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 7fa77445a4cd..647e77d2a04f 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1332,7 +1332,9 @@ static void ir_print_result_optional_payload(IrPrint *irp, IrInstructionResultOp } static void ir_print_result_error_union_payload(IrPrint *irp, IrInstructionResultErrorUnionPayload *instruction) { - fprintf(irp->f, "ResultErrorUnionPayload"); + fprintf(irp->f, "ResultErrorUnionPayload("); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); } static void ir_print_result_return(IrPrint *irp, IrInstructionResultReturn *instruction) { From 5106eac6dc0bc6fc4cf565acddd5441e98b3da6b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 01:28:55 -0400 Subject: [PATCH 014/190] copy elision: implicit cast error set to error union ```zig export fn entry() void { var x: error!Foo = fail(); } ``` ```llvm define void @entry() #2 !dbg !42 { Entry: %x = alloca { i16, %Foo }, align 4 %0 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 0, !dbg !57 %1 = call fastcc i16 @fail(), !dbg !58 store i16 %1, i16* %0, align 2, !dbg !58 ret void, !dbg !59 } ``` --- src/all_types.hpp | 7 ++ src/codegen.cpp | 10 +++ src/ir.cpp | 178 ++++++++++++++++++++++++++++------------------ src/ir_print.cpp | 9 +++ 4 files changed, 135 insertions(+), 69 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index ae411793ecca..0cfd27899933 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2175,6 +2175,7 @@ enum IrInstructionId { IrInstructionIdCheckRuntimeScope, IrInstructionIdResultOptionalPayload, IrInstructionIdResultErrorUnionPayload, + IrInstructionIdResultErrorUnionCode, IrInstructionIdResultReturn, IrInstructionIdResultBytesToSlice, IrInstructionIdResultSliceToBytes, @@ -3298,6 +3299,12 @@ struct IrInstructionResultErrorUnionPayload { IrInstruction *prev_result_loc; }; +struct IrInstructionResultErrorUnionCode { + IrInstruction base; + + IrInstruction *prev_result_loc; +}; + struct IrInstructionResultOptionalPayload { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 8e5a0b20f711..0f0166be1670 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5118,6 +5118,14 @@ static LLVMValueRef ir_render_result_error_union_payload(CodeGen *g, IrExecutabl return LLVMBuildStructGEP(g->builder, prev_result_loc, err_union_payload_index, ""); } +static LLVMValueRef ir_render_result_error_union_code(CodeGen *g, IrExecutable *executable, + IrInstructionResultErrorUnionCode *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + // TODO write 0xaa in debug mode to the undefined payload + return LLVMBuildStructGEP(g->builder, prev_result_loc, err_union_err_index, ""); +} + static LLVMValueRef ir_render_assert_non_error(CodeGen *g, IrExecutable *executable, IrInstructionAssertNonError *instruction) { @@ -5383,6 +5391,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_result_optional_payload(g, executable, (IrInstructionResultOptionalPayload *)instruction); case IrInstructionIdResultErrorUnionPayload: return ir_render_result_error_union_payload(g, executable, (IrInstructionResultErrorUnionPayload *)instruction); + case IrInstructionIdResultErrorUnionCode: + return ir_render_result_error_union_code(g, executable, (IrInstructionResultErrorUnionCode *)instruction); case IrInstructionIdAssertNonError: return ir_render_assert_non_error(g, executable, (IrInstructionAssertNonError *)instruction); case IrInstructionIdResultSliceToBytes: diff --git a/src/ir.cpp b/src/ir.cpp index d401770629ab..2d3fca9a835e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -863,6 +863,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionResultErrorUnion return IrInstructionIdResultErrorUnionPayload; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultErrorUnionCode *) { + return IrInstructionIdResultErrorUnionCode; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionResultOptionalPayload *) { return IrInstructionIdResultOptionalPayload; } @@ -2816,6 +2820,17 @@ static IrInstruction *ir_build_result_error_union_payload(IrBuilder *irb, Scope return &instruction->base; } +static IrInstruction *ir_build_result_error_union_code(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc) +{ + IrInstructionResultErrorUnionCode *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_result_return(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionResultReturn *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; @@ -9848,6 +9863,23 @@ static IrInstruction *ir_analyze_result_error_union_payload(IrAnalyze *ira, IrIn return result; } +static IrInstruction *ir_analyze_result_error_union_code(IrAnalyze *ira, IrInstruction *result_loc, + ZigType *needed_child_type) +{ + if (instr_is_comptime(result_loc)) { + zig_panic("TODO comptime ir_analyze_result_error_union_code"); + } + ZigType *old_ptr_type = result_loc->value.type; + assert(old_ptr_type->id == ZigTypeIdPointer); + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, + false, old_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + + IrInstruction *result = ir_build_result_error_union_code(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc); + result->value.type = new_ptr_type; + return result; +} + static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { @@ -11161,6 +11193,13 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *res } } + // cast from E to E!T + if (have_child_type->id == ZigTypeIdErrorUnion && + needed_child_type->id == ZigTypeIdErrorSet) + { + return ir_analyze_result_error_union_code(ira, result_loc, needed_child_type); + } + ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, buf_sprintf("expected type '%s', found '%s'", buf_ptr(&have_child_type->name), @@ -13251,6 +13290,67 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, return var_ptr_instruction; } +static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *ptr, IrInstruction *value) +{ + Error err; + if ((err = resolve_possible_alloca_inference(ira, ptr, value->value.type))) + return ira->codegen->invalid_instruction; + + if (ptr->value.type->id != ZigTypeIdPointer) { + ir_add_error(ira, ptr, + buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); + return ira->codegen->invalid_instruction; + } + + if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { + return ir_const_void(ira, source_instr); + } + + if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_instruction; + } + + ZigType *child_type = ptr->value.type->data.pointer.child_type; + IrInstruction *casted_value = ir_implicit_cast(ira, value, child_type); + if (casted_value == ira->codegen->invalid_instruction) + return ira->codegen->invalid_instruction; + + if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_instruction; + } + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { + if (instr_is_comptime(casted_value)) { + ConstExprValue *dest_val = ir_const_ptr_pointee(ira, &ptr->value, source_instr->source_node); + if (dest_val == nullptr) + return ira->codegen->invalid_instruction; + if (dest_val->special != ConstValSpecialRuntime) { + *dest_val = casted_value->value; + if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { + ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; + } + return ir_const_void(ira, source_instr); + } + } + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in compile time variable")); + ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); + dest_val->type = ira->codegen->builtin_types.entry_invalid; + + return ira->codegen->invalid_instruction; + } + } + + IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, + ptr, casted_value); + result->value.type = ira->codegen->builtin_types.entry_void; + return result; +} + + static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline) @@ -13795,7 +13895,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *result_loc = nullptr; - if (handle_is_ptr(return_type)) { + if (call_instruction->result_loc != nullptr) { result_loc = ir_implicit_cast_result(ira, call_instruction->result_loc->child, return_type); if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -13806,6 +13906,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack, result_loc); new_call_instruction->value.type = return_type; + + if (!handle_is_ptr(return_type) && result_loc != nullptr) { + ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, new_call_instruction); + } + return ir_finish_anal(ira, new_call_instruction); } @@ -15217,66 +15322,6 @@ static IrInstruction *ir_analyze_instruction_load_result(IrAnalyze *ira, IrInstr zig_panic("TODO"); } -static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *ptr, IrInstruction *value) -{ - Error err; - if ((err = resolve_possible_alloca_inference(ira, ptr, value->value.type))) - return ira->codegen->invalid_instruction; - - if (ptr->value.type->id != ZigTypeIdPointer) { - ir_add_error(ira, ptr, - buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); - return ira->codegen->invalid_instruction; - } - - if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { - return ir_const_void(ira, source_instr); - } - - if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { - ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_instruction; - } - - ZigType *child_type = ptr->value.type->data.pointer.child_type; - IrInstruction *casted_value = ir_implicit_cast(ira, value, child_type); - if (casted_value == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; - - if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { - ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_instruction; - } - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { - if (instr_is_comptime(casted_value)) { - ConstExprValue *dest_val = ir_const_ptr_pointee(ira, &ptr->value, source_instr->source_node); - if (dest_val == nullptr) - return ira->codegen->invalid_instruction; - if (dest_val->special != ConstValSpecialRuntime) { - *dest_val = casted_value->value; - if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { - ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; - } - return ir_const_void(ira, source_instr); - } - } - ir_add_error(ira, source_instr, - buf_sprintf("cannot store runtime value in compile time variable")); - ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); - dest_val->type = ira->codegen->builtin_types.entry_invalid; - - return ira->codegen->invalid_instruction; - } - } - - IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, - ptr, casted_value); - result->value.type = ira->codegen->builtin_types.entry_void; - return result; -} - static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) { IrInstruction *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value.type)) @@ -21140,12 +21185,6 @@ static IrInstruction *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, return ir_const_void(ira, &instruction->base); } -static IrInstruction *ir_analyze_instruction_result_error_union_payload(IrAnalyze *ira, - IrInstructionResultErrorUnionPayload *instruction) -{ - zig_panic("TODO"); -} - static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrInstructionResultReturn *instruction) { ZigFn *fn = exec_fn_entry(ira->new_irb.exec); if (fn == nullptr) { @@ -21214,6 +21253,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdDeclVarGen: case IrInstructionIdAllocaGen: case IrInstructionIdResultOptionalPayload: + case IrInstructionIdResultErrorUnionPayload: + case IrInstructionIdResultErrorUnionCode: zig_unreachable(); case IrInstructionIdReturn: @@ -21480,8 +21521,6 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction); case IrInstructionIdCheckRuntimeScope: return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction); - case IrInstructionIdResultErrorUnionPayload: - return ir_analyze_instruction_result_error_union_payload(ira, (IrInstructionResultErrorUnionPayload *)instruction); case IrInstructionIdResultReturn: return ir_analyze_instruction_result_return(ira, (IrInstructionResultReturn *)instruction); case IrInstructionIdResultParam: @@ -21718,6 +21757,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdEnumToInt: case IrInstructionIdResultOptionalPayload: case IrInstructionIdResultErrorUnionPayload: + case IrInstructionIdResultErrorUnionCode: case IrInstructionIdResultReturn: case IrInstructionIdResultParam: case IrInstructionIdResultPtrCast: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 647e77d2a04f..1966fff747d0 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1337,6 +1337,12 @@ static void ir_print_result_error_union_payload(IrPrint *irp, IrInstructionResul fprintf(irp->f, ")"); } +static void ir_print_result_error_union_code(IrPrint *irp, IrInstructionResultErrorUnionCode *instruction) { + fprintf(irp->f, "ResultErrorUnionCode("); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); +} + static void ir_print_result_return(IrPrint *irp, IrInstructionResultReturn *instruction) { fprintf(irp->f, "ResultReturn"); } @@ -1815,6 +1821,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdResultErrorUnionPayload: ir_print_result_error_union_payload(irp, (IrInstructionResultErrorUnionPayload *)instruction); break; + case IrInstructionIdResultErrorUnionCode: + ir_print_result_error_union_code(irp, (IrInstructionResultErrorUnionCode *)instruction); + break; case IrInstructionIdResultReturn: ir_print_result_return(irp, (IrInstructionResultReturn *)instruction); break; From e1dfb5f4ae5d6cec9dc8ece0ff20053f25a4e91a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 01:47:35 -0400 Subject: [PATCH 015/190] copy elision: double implicit cast ```zig export fn entry() void { var x: error!?Foo = bar(); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca { i16, { %Foo, i1 } }, align 4 %0 = getelementptr inbounds { i16, { %Foo, i1 } }, { i16, { %Foo, i1 } }* %x, i32 0, i32 0, !dbg !61 store i16 0, i16* %0, align 2, !dbg !61 %1 = getelementptr inbounds { i16, { %Foo, i1 } }, { i16, { %Foo, i1 } }* %x, i32 0, i32 1, !dbg !61 %2 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %1, i32 0, i32 1, !dbg !61 store i1 true, i1* %2, align 1, !dbg !61 %3 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %1, i32 0, i32 0, !dbg !61 call fastcc void @bar(%Foo* sret %3), !dbg !62 ret void, !dbg !63 } ``` --- src/ir.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 2d3fca9a835e..8a027681e87a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4935,9 +4935,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node size_t arg_count = node->data.fn_call_expr.params.length; IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { - IrInstruction *param_result_loc = ir_build_result_param(irb, scope, node, fn_ref, i); AstNode *arg_node = node->data.fn_call_expr.params.at(i); - args[i] = ir_gen_node(irb, arg_node, scope, LValNone, param_result_loc); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } @@ -10885,7 +10884,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from error set to error union type + // cast from E to E!T if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorSet) { @@ -11200,6 +11199,27 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *res return ir_analyze_result_error_union_code(ira, result_loc, needed_child_type); } + // cast from T to E!?T + if (have_child_type->id == ZigTypeIdErrorUnion && + have_child_type->data.error_union.payload_type->id == ZigTypeIdOptional && + needed_child_type->id != ZigTypeIdOptional) + { + ZigType *wanted_child_type = have_child_type->data.error_union.payload_type->data.maybe.child_type; + if (types_match_const_cast_only(ira, wanted_child_type, needed_child_type, source_node, + false).id == ConstCastResultIdOk || + needed_child_type->id == ZigTypeIdNull || + needed_child_type->id == ZigTypeIdComptimeInt || + needed_child_type->id == ZigTypeIdComptimeFloat) + { + IrInstruction *cast1 = ir_implicit_cast_result(ira, result_loc, + have_child_type->data.error_union.payload_type); + if (type_is_invalid(cast1->value.type)) + return ira->codegen->invalid_instruction; + + return ir_implicit_cast_result(ira, cast1, needed_child_type); + } + } + ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, buf_sprintf("expected type '%s', found '%s'", buf_ptr(&have_child_type->name), From 5ee4e5ae3a9f4ecec3c339e49f1c2d3c59da7e66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 02:22:19 -0400 Subject: [PATCH 016/190] copy elision: runtime struct init ```zig export fn entry() void { var x = Foo.{ .x = 1, .y = bar(), }; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca %Foo, align 4 %0 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0, !dbg !56 store i32 1, i32* %0, align 4, !dbg !57 %1 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 1, !dbg !58 call fastcc void @bar(%Bar* sret %1), !dbg !59 ret void, !dbg !60 } ``` --- src/all_types.hpp | 3 +- src/analyze.cpp | 2 +- src/ast_render.cpp | 6 +-- src/ir.cpp | 126 +++++++++++++++++++++++++++------------------ src/parser.cpp | 6 +-- 5 files changed, 84 insertions(+), 59 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 0cfd27899933..a1f5911580cf 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -436,7 +436,7 @@ enum NodeType { NodeTypeArrayType, NodeTypeErrorType, NodeTypeIfErrorExpr, - NodeTypeTestExpr, + NodeTypeIfOptional, NodeTypeErrorSetDecl, NodeTypeCancel, NodeTypeResume, @@ -2370,6 +2370,7 @@ struct IrInstructionFieldPtr { IrInstruction *container_ptr; Buf *field_name_buffer; IrInstruction *field_name_expr; + IrInstruction *container_type; bool is_const; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index e71369eac934..ca5eba58d197 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3517,7 +3517,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeIfErrorExpr: - case NodeTypeTestExpr: + case NodeTypeIfOptional: case NodeTypeErrorSetDecl: case NodeTypeCancel: case NodeTypeResume: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index f74c156de246..621edc9512f0 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -235,8 +235,8 @@ static const char *node_type_str(NodeType node_type) { return "ErrorType"; case NodeTypeIfErrorExpr: return "IfErrorExpr"; - case NodeTypeTestExpr: - return "TestExpr"; + case NodeTypeIfOptional: + return "IfOptional"; case NodeTypeErrorSetDecl: return "ErrorSetDecl"; case NodeTypeCancel: @@ -938,7 +938,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } break; } - case NodeTypeTestExpr: + case NodeTypeIfOptional: { fprintf(ar->f, "if ("); render_node_grouped(ar, node->data.test_expr.target_node); diff --git a/src/ir.cpp b/src/ir.cpp index 8a027681e87a..997dc1e1996b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1170,14 +1170,16 @@ static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scop } static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_ptr, Buf *field_name) + IrInstruction *container_ptr, IrInstruction *container_type, Buf *field_name) { IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; + instruction->container_type = container_type; instruction->field_name_buffer = field_name; instruction->field_name_expr = nullptr; ir_ref_instruction(container_ptr, irb->current_basic_block); + if (container_type != nullptr) ir_ref_instruction(container_type, irb->current_basic_block); return &instruction->base; } @@ -3788,7 +3790,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; - return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name); + return ir_build_field_ptr(irb, scope, node, container_ref_instruction, nullptr, field_name); } static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) { @@ -4956,12 +4958,14 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node return ir_lval_wrap(irb, scope, fn_call, lval); } -static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { +static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeIfBoolExpr); IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope, LValNone, nullptr); if (condition == irb->codegen->invalid_instruction) - return condition; + return irb->codegen->invalid_instruction; IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { @@ -4984,7 +4988,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope, LValNone, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) - return then_expr_result; + return irb->codegen->invalid_instruction; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -4993,7 +4997,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode if (else_node) { else_expr_result = ir_gen_node(irb, else_node, subexpr_scope, LValNone, result_loc); if (else_expr_result == irb->codegen->invalid_instruction) - return else_expr_result; + return irb->codegen->invalid_instruction; } else { else_expr_result = ir_build_const_void(irb, scope, node); } @@ -5001,7 +5005,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); - return ir_build_load_ptr(irb, scope, node, result_loc, nullptr); + return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); } static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { @@ -5151,7 +5155,8 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A assert(entry_node->type == NodeTypeStructValueField); Buf *name = entry_node->data.struct_val_field.name; - IrInstruction *field_result_loc = ir_build_field_ptr(irb, scope, entry_node, result_loc, name); + IrInstruction *field_result_loc = ir_build_field_ptr(irb, scope, entry_node, result_loc, + container_type, name); AstNode *expr_node = entry_node->data.struct_val_field.expr; IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope, LValNone, field_result_loc); @@ -5644,12 +5649,9 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } -static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, - IrInstruction *result_loc) -{ +static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBoolLiteral); - IrInstruction *const_bool_val = ir_build_const_bool(irb, scope, node, node->data.bool_literal.value); - return ir_gen_value(irb, scope, node, lval, result_loc, const_bool_val); + return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value); } static IrInstruction *ir_gen_string_literal(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5789,7 +5791,7 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { - assert(node->type == NodeTypeTestExpr); + assert(node->type == NodeTypeIfOptional); Buf *var_symbol = node->data.test_expr.var_symbol; AstNode *expr_node = node->data.test_expr.target_node; @@ -6738,7 +6740,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); - IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, atomic_state_field_name); // set the is_canceled bit @@ -6816,7 +6818,7 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); - IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, atomic_state_field_name); // clear the is_suspended bit @@ -6884,12 +6886,14 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst); Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); - IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); + IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, + result_ptr_field_name); if (irb->codegen->have_err_ret_tracing) { IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); - IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); + IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, err_ret_trace_ptr_field_name); ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); } @@ -6911,7 +6915,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - atomic_state_field_name); + nullptr, atomic_state_field_name); IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); @@ -6963,12 +6967,14 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); - IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name); + IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, err_ret_trace_field_name); IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr); } Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); - IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); + IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, + result_field_name); // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr, nullptr); @@ -7173,17 +7179,17 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeBinOpExpr: return ir_gen_bin_op(irb, scope, node, lval, result_loc); case NodeTypeIntLiteral: - return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_int_lit(irb, scope, node)); case NodeTypeFloatLiteral: - return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_float_lit(irb, scope, node)); case NodeTypeCharLiteral: - return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_char_lit(irb, scope, node)); case NodeTypeSymbol: return ir_gen_symbol(irb, scope, node, lval, result_loc); case NodeTypeFnCallExpr: return ir_gen_fn_call(irb, scope, node, lval, result_loc); case NodeTypeIfBoolExpr: - return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node, result_loc), lval); + return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval); case NodeTypeContainerInitExpr: @@ -7230,31 +7236,31 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_build_load_ptr(irb, scope, node, unwrapped_ptr, nullptr); } case NodeTypeBoolLiteral: - return ir_gen_bool_literal(irb, scope, node, lval, result_loc); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_bool_literal(irb, scope, node)); case NodeTypeArrayType: - return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_array_type(irb, scope, node)); case NodeTypePointerType: - return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_pointer_type(irb, scope, node)); case NodeTypePromiseType: - return ir_lval_wrap(irb, scope, ir_gen_promise_type(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_promise_type(irb, scope, node)); case NodeTypeStringLiteral: - return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_string_literal(irb, scope, node)); case NodeTypeUndefinedLiteral: - return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_undefined_literal(irb, scope, node)); case NodeTypeAsmExpr: - return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_asm_expr(irb, scope, node)); case NodeTypeNullLiteral: - return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_null_literal(irb, scope, node)); case NodeTypeIfErrorExpr: return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node, result_loc), lval); - case NodeTypeTestExpr: + case NodeTypeIfOptional: return ir_lval_wrap(irb, scope, ir_gen_if_optional_expr(irb, scope, node, result_loc), lval); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node, result_loc), lval); case NodeTypeCompTime: return ir_gen_comptime(irb, scope, node, lval, result_loc); case NodeTypeErrorType: - return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc,ir_gen_error_type(irb, scope, node)); case NodeTypeBreak: return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval); case NodeTypeContinue: @@ -7270,9 +7276,9 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeContainerDecl: return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: - return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_fn_proto(irb, scope, node)); case NodeTypeErrorSetDecl: - return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_err_set_decl(irb, scope, node)); case NodeTypeCancel: return ir_lval_wrap(irb, scope, ir_gen_cancel(irb, scope, node), lval); case NodeTypeResume: @@ -7375,7 +7381,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, coro_allocator_var_alloca); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); - IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); + IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, + nullptr, alloc_field_name); IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr, nullptr); IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size); IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); @@ -7394,30 +7401,36 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - atomic_state_field_name); + nullptr, atomic_state_field_name); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero); Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); - irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); + irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, result_field_name); result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); - irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); + irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, result_ptr_field_name); ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr); if (irb->codegen->have_err_ret_tracing) { // initialize the error return trace Buf *return_addresses_field_name = buf_create_from_str(RETURN_ADDRESSES_FIELD_NAME); - IrInstruction *return_addresses_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, return_addresses_field_name); + IrInstruction *return_addresses_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, return_addresses_field_name); Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); - err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name); + err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, + err_ret_trace_field_name); ir_build_mark_err_ret_trace_ptr(irb, scope, node, err_ret_trace_ptr); // coordinate with builtin.zig Buf *index_name = buf_create_from_str("index"); - IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name); + IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, nullptr, + index_name); ir_build_store_ptr(irb, scope, node, index_ptr, zero); Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses"); - IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, instruction_addresses_name); + IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, nullptr, + instruction_addresses_name); IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false, addrs_slice_ptr); @@ -7478,7 +7491,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec } if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); - IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); + IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, err_ret_trace_ptr_field_name); IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, nullptr); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr); } @@ -7513,7 +7527,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, ImplicitAllocatorIdLocalVar); - IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, free_field_name); + IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, nullptr, + free_field_name); IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr, nullptr); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); @@ -13313,16 +13328,16 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, IrInstruction *value) { - Error err; - if ((err = resolve_possible_alloca_inference(ira, ptr, value->value.type))) - return ira->codegen->invalid_instruction; - if (ptr->value.type->id != ZigTypeIdPointer) { ir_add_error(ira, ptr, buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); return ira->codegen->invalid_instruction; } + Error err; + if ((err = resolve_possible_alloca_inference(ira, ptr, value->value.type))) + return ira->codegen->invalid_instruction; + if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { return ir_const_void(ira, source_instr); } @@ -14953,6 +14968,15 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc buf_ptr(&container_ptr->value.type->name))); return ira->codegen->invalid_instruction; } + + if (field_ptr_instruction->container_type != nullptr) { + ZigType *container_type = ir_resolve_type(ira, field_ptr_instruction->container_type->child); + if (type_is_invalid(container_type)) + return ira->codegen->invalid_instruction; + if ((err = resolve_possible_alloca_inference(ira, container_ptr, container_type))) + return ira->codegen->invalid_instruction; + } + ZigType *container_type = container_ptr->value.type->data.pointer.child_type; Buf *field_name = field_ptr_instruction->field_name_buffer; diff --git a/src/parser.cpp b/src/parser.cpp index 59e70713ab80..b8822c2d379b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1604,7 +1604,7 @@ static AstNode *ast_parse_if_try_test_expr(ParseContext *pc, size_t *token_index node->data.if_err_expr.else_node = else_node; return node; } else if (var_name_tok != nullptr) { - AstNode *node = ast_create_node(pc, NodeTypeTestExpr, if_token); + AstNode *node = ast_create_node(pc, NodeTypeIfOptional, if_token); node->data.test_expr.target_node = condition; node->data.test_expr.var_is_ptr = var_is_ptr; node->data.test_expr.var_symbol = token_buf(var_name_tok); @@ -2407,7 +2407,7 @@ bool statement_terminates_without_semicolon(AstNode *node) { if (node->data.if_err_expr.else_node) return statement_terminates_without_semicolon(node->data.if_err_expr.else_node); return node->data.if_err_expr.then_node->type == NodeTypeBlock; - case NodeTypeTestExpr: + case NodeTypeIfOptional: if (node->data.test_expr.else_node) return statement_terminates_without_semicolon(node->data.test_expr.else_node); return node->data.test_expr.then_node->type == NodeTypeBlock; @@ -3027,7 +3027,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.if_err_expr.then_node, visit, context); visit_field(&node->data.if_err_expr.else_node, visit, context); break; - case NodeTypeTestExpr: + case NodeTypeIfOptional: visit_field(&node->data.test_expr.target_node, visit, context); visit_field(&node->data.test_expr.then_node, visit, context); visit_field(&node->data.test_expr.else_node, visit, context); From 952da3c1ff9a71ce413b26b4b15f4ef33171e768 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 03:01:38 -0400 Subject: [PATCH 017/190] copy elision: catch unreachable with identifier expr ```zig export fn entry() void { var x: error!Bar = fail(); var z = x catch unreachable; } ``` ```llvm define void @entry() #2 !dbg !42 { Entry: %error_return_trace_addresses = alloca [30 x i64], align 8 %error_return_trace = alloca %StackTrace, align 8 %x = alloca { i16, %Bar }, align 4 %z = alloca %Bar, align 4 %0 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 0 store i64 0, i64* %0, align 8 %1 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 1 %2 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 0 %3 = getelementptr inbounds [30 x i64], [30 x i64]* %error_return_trace_addresses, i64 0, i64 0 store i64* %3, i64** %2, align 8 %4 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 1 store i64 30, i64* %4, align 8 %5 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !59 %6 = call fastcc i16 @fail(%StackTrace* %error_return_trace), !dbg !60 store i16 %6, i16* %5, align 2, !dbg !60 call void @llvm.dbg.declare(metadata { i16, %Bar }* %x, metadata !46, metadata !DIExpression()), !dbg !59 %7 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 1, !dbg !61 %8 = bitcast %Bar* %7 to i8*, !dbg !61 %9 = bitcast %Bar* %z to i8*, !dbg !61 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %9, i8* align 4 %8, i64 8, i1 false), !dbg !61 %10 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !61 %11 = load i16, i16* %10, align 2, !dbg !61 %12 = icmp eq i16 %11, 0, !dbg !62 br i1 %12, label %UnwrapErrOk, label %UnwrapErrError, !dbg !62 UnwrapErrError: ; preds = %Entry tail call fastcc void @__zig_fail_unwrap(%StackTrace* %error_return_trace, i16 %11), !dbg !62 unreachable, !dbg !62 UnwrapErrOk: ; preds = %Entry call void @llvm.dbg.declare(metadata %Bar* %z, metadata !57, metadata !DIExpression()), !dbg !63 ret void, !dbg !64 } ``` --- src/ir.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 997dc1e1996b..7b3997677ff0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3138,6 +3138,29 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, return ir_lval_wrap(irb, scope, value, lval); } +static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc, IrInstruction *ptr) +{ + switch (lval) { + case LValPtr: + assert(result_loc == nullptr); + return ptr; + case LValNone: + return ir_build_load_ptr(irb, scope, node, ptr, result_loc); + case LValErrorUnion: { + // ptr points to an error union; + // result_loc points to the result payload + // must return the error code + IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, ptr, false); + ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); + return ir_build_unwrap_err_code(irb, scope, node, ptr); + } + case LValOptional: + zig_panic("TODO"); + } + zig_unreachable(); +} + static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, IrInstruction *result_loc) { @@ -3735,10 +3758,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope); if (var) { IrInstruction *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope); - if (lval == LValPtr) - return var_ptr; - IrInstruction *loaded = ir_build_load_ptr(irb, scope, node, var_ptr, result_loc); - return ir_lval_wrap(irb, scope, loaded, lval); + return ir_gen_ptr(irb, scope, node, lval, result_loc, var_ptr); } Tld *tld = find_decl(irb->codegen, scope, variable_name); From d77b368ea6feb42327612941dd96e0a92cddcca1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 04:17:54 -0400 Subject: [PATCH 018/190] copy elision: catch with identifier expression This commit also makes `@sizeOf(?error) == @sizeOf(error)`. Since 0 is not a valid error code, 0 can be used for the null value, in the same way that 0 is used for the null value of pointers. ```zig export fn entry() void { var x: error!Bar = fail(); var y = x catch bar2(); } ``` ```llvm define void @entry() #2 !dbg !42 { Entry: %error_return_trace_addresses = alloca [30 x i64], align 8 %error_return_trace = alloca %StackTrace, align 8 %x = alloca { i16, %Bar }, align 4 %y = alloca %Bar, align 4 %0 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 0 store i64 0, i64* %0, align 8 %1 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 1 %2 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 0 %3 = getelementptr inbounds [30 x i64], [30 x i64]* %error_return_trace_addresses, i64 0, i64 0 store i64* %3, i64** %2, align 8 %4 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 1 store i64 30, i64* %4, align 8 %5 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !59 %6 = call fastcc i16 @fail(%StackTrace* %error_return_trace), !dbg !60 store i16 %6, i16* %5, align 2, !dbg !60 call void @llvm.dbg.declare(metadata { i16, %Bar }* %x, metadata !46, metadata !DIExpression()), !dbg !59 %7 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 1, !dbg !61 %8 = bitcast %Bar* %7 to i8*, !dbg !61 %9 = bitcast %Bar* %y to i8*, !dbg !61 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %9, i8* align 4 %8, i64 8, i1 false), !dbg !61 %10 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !61 %11 = load i16, i16* %10, align 2, !dbg !62 %12 = icmp ne i16 %11, 0, !dbg !62 br i1 %12, label %CatchError, label %CatchEnd, !dbg !62 CatchError: ; preds = %Entry call fastcc void @bar2(%Bar* sret %y), !dbg !63 br label %CatchEnd, !dbg !62 CatchEnd: ; preds = %CatchError, %Entry call void @llvm.dbg.declare(metadata %Bar* %y, metadata !57, metadata !DIExpression()), !dbg !64 ret void, !dbg !65 } ``` --- src/all_types.hpp | 7 ++++ src/analyze.cpp | 5 +-- src/codegen.cpp | 32 ++++++++++++++--- src/ir.cpp | 92 ++++++++++++++++++++++++++++++++++++++--------- src/ir_print.cpp | 9 +++++ 5 files changed, 121 insertions(+), 24 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index a1f5911580cf..da072e837332 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2186,6 +2186,7 @@ enum IrInstructionId { IrInstructionIdAllocaSrc, IrInstructionIdAllocaGen, IrInstructionIdAssertNonError, + IrInstructionIdErrorUnionFieldErrorSet, }; struct IrInstruction { @@ -2882,6 +2883,12 @@ struct IrInstructionUnwrapErrCode { IrInstruction *value; }; +struct IrInstructionErrorUnionFieldErrorSet { + IrInstruction base; + + IrInstruction *ptr; +}; + struct IrInstructionUnwrapErrPayload { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index ca5eba58d197..ed8b286d6759 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -589,7 +589,7 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) { if (child_type->zero_bits) { entry->type_ref = LLVMInt1Type(); entry->di_type = g->builtin_types.entry_bool->di_type; - } else if (type_is_codegen_pointer(child_type)) { + } else if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { assert(child_type->di_type); // this is an optimization but also is necessary for calling C // functions where all pointers are maybe pointers @@ -4490,7 +4490,8 @@ bool handle_is_ptr(ZigType *type_entry) { return type_has_bits(type_entry->data.error_union.payload_type); case ZigTypeIdOptional: return type_has_bits(type_entry->data.maybe.child_type) && - !type_is_codegen_pointer(type_entry->data.maybe.child_type); + !type_is_codegen_pointer(type_entry->data.maybe.child_type) && + type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet; case ZigTypeIdUnion: assert(type_entry->data.unionation.zero_bits_known); if (type_entry->data.unionation.gen_field_count == 0) diff --git a/src/codegen.cpp b/src/codegen.cpp index 0f0166be1670..b27409f2a630 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3734,8 +3734,8 @@ static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueR if (child_type->zero_bits) { return maybe_handle; } else { - bool maybe_is_ptr = type_is_codegen_pointer(child_type); - if (maybe_is_ptr) { + bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; + if (is_scalar) { return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(maybe_type->type_ref), ""); } else { LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, ""); @@ -3774,8 +3774,8 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, if (child_type->zero_bits) { return nullptr; } else { - bool maybe_is_ptr = type_is_codegen_pointer(child_type); - if (maybe_is_ptr) { + bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; + if (is_scalar) { return maybe_ptr; } else { LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); @@ -4552,6 +4552,7 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab ZigType *ptr_type = instruction->value->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *err_union_type = ptr_type->data.pointer.child_type; + assert(err_union_type->id == ZigTypeIdErrorUnion); ZigType *payload_type = err_union_type->data.error_union.payload_type; LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); @@ -4564,6 +4565,23 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab } } +static LLVMValueRef ir_render_error_union_field_error_set(CodeGen *g, IrExecutable *executable, + IrInstructionErrorUnionFieldErrorSet *instruction) +{ + ZigType *ptr_type = instruction->ptr->value.type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *err_union_type = ptr_type->data.pointer.child_type; + assert(err_union_type->id == ZigTypeIdErrorUnion); + ZigType *payload_type = err_union_type->data.error_union.payload_type; + LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->ptr); + + if (type_has_bits(payload_type)) { + return LLVMBuildStructGEP(g->builder, err_union_ptr, err_union_err_index, ""); + } else { + return err_union_ptr; + } +} + static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) { ZigType *ptr_type = instruction->value->value.type; assert(ptr_type->id == ZigTypeIdPointer); @@ -4615,7 +4633,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I } LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); - if (type_is_codegen_pointer(child_type)) { + if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { return payload_val; } @@ -5301,6 +5319,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction); case IrInstructionIdTestErr: return ir_render_test_err(g, executable, (IrInstructionTestErr *)instruction); + case IrInstructionIdErrorUnionFieldErrorSet: + return ir_render_error_union_field_error_set(g, executable, (IrInstructionErrorUnionFieldErrorSet *)instruction); case IrInstructionIdUnwrapErrCode: return ir_render_unwrap_err_code(g, executable, (IrInstructionUnwrapErrCode *)instruction); case IrInstructionIdUnwrapErrPayload: @@ -5735,6 +5755,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false); } else if (type_is_codegen_pointer(child_type)) { return gen_const_val_ptr(g, const_val, name); + } else if (child_type->id == ZigTypeIdErrorSet) { + zig_panic("TODO"); } else { LLVMValueRef child_val; LLVMValueRef maybe_val; diff --git a/src/ir.cpp b/src/ir.cpp index 7b3997677ff0..ab79223106f2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -903,6 +903,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonError * return IrInstructionIdAssertNonError; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnionFieldErrorSet *) { + return IrInstructionIdErrorUnionFieldErrorSet; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2921,6 +2925,17 @@ static IrInstruction *ir_build_assert_non_error(IrBuilder *irb, Scope *scope, As return &instruction->base; } +static IrInstruction *ir_build_error_union_field_error_set(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *ptr) +{ + IrInstructionErrorUnionFieldErrorSet *instruction = ir_build_instruction(irb, scope, source_node); + instruction->ptr = ptr; + + ir_ref_instruction(ptr, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3153,7 +3168,7 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV // must return the error code IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, ptr, false); ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); - return ir_build_unwrap_err_code(irb, scope, node, ptr); + return ir_build_error_union_field_error_set(irb, scope, node, ptr); } case LValOptional: zig_panic("TODO"); @@ -5109,11 +5124,12 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval, IrInstruction *result_loc) { - IrInstruction *err_val = ir_gen_node(irb, expr_node, scope, LValErrorUnion, result_loc); - if (err_val == irb->codegen->invalid_instruction) + IrInstruction *err_code_ptr = ir_gen_node(irb, expr_node, scope, LValErrorUnion, result_loc); + if (err_code_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - ir_build_assert_non_error(irb, scope, source_node, err_val); + IrInstruction *err_code = ir_build_load_ptr(irb, scope, source_node, err_code_ptr, nullptr); + ir_build_assert_non_error(irb, scope, source_node, err_code); return ir_gen_lval_ptr(irb, scope, source_node, lval, result_loc); } @@ -6453,11 +6469,12 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc); } - IrInstruction *err_val = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnion, result_loc); - if (err_val == irb->codegen->invalid_instruction) + IrInstruction *err_code_ptr = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnion, result_loc); + if (err_code_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_val); + IrInstruction *opt_err_code = ir_build_load_ptr(irb, parent_scope, node, err_code_ptr, nullptr); + IrInstruction *is_err = ir_build_test_nonnull(irb, parent_scope, node, opt_err_code); IrInstruction *is_comptime; if (ir_should_inline(irb->exec, parent_scope)) { @@ -6480,7 +6497,9 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode ZigVar *var = ir_create_var(irb, node, parent_scope, var_name, is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; - ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, err_val); + IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, err_scope, var_node, + err_code_ptr, false); + ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, unwrapped_err_code_ptr); } else { err_scope = parent_scope; } @@ -19573,6 +19592,40 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct } } +static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnalyze *ira, + IrInstructionErrorUnionFieldErrorSet *instruction) +{ + IrInstruction *ptr = instruction->ptr->child; + if (type_is_invalid(ptr->value.type)) + return ira->codegen->invalid_instruction; + ZigType *ptr_type = ptr->value.type; + + assert(ptr_type->id == ZigTypeIdPointer); + + ZigType *type_entry = ptr_type->data.pointer.child_type; + if (type_is_invalid(type_entry)) + return ira->codegen->invalid_instruction; + + if (type_entry->id != ZigTypeIdErrorUnion) { + ir_add_error(ira, ptr, + buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->invalid_instruction; + } + + ZigType *opt_err_set = get_optional_type(ira->codegen, type_entry->data.error_union.err_set_type); + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, opt_err_set, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + + if (instr_is_comptime(ptr)) { + zig_panic("TODO need to change ConstExprValue x_err_union to have a ConstExprValue for error set value"); + } + + IrInstruction *result = ir_build_error_union_field_error_set(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, ptr); + result->value.type = result_type; + return result; +} + static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstructionUnwrapErrCode *instruction) { @@ -19681,19 +19734,21 @@ static IrInstruction *ir_analyze_instruction_assert_non_error(IrAnalyze *ira, if (type_is_invalid(err_code->value.type)) return ira->codegen->invalid_instruction; - assert(err_code->value.type->id == ZigTypeIdErrorSet); + assert(err_code->value.type->id == ZigTypeIdOptional); + assert(err_code->value.type->data.maybe.child_type->id == ZigTypeIdErrorSet); if (instr_is_comptime(err_code)) { - ConstExprValue *err_val = ir_resolve_const(ira, err_code, UndefBad); - if (err_val == nullptr) + ConstExprValue *opt_val = ir_resolve_const(ira, err_code, UndefBad); + if (opt_val == nullptr) return ira->codegen->invalid_instruction; + ConstExprValue *err_val = opt_val->data.x_optional; + if (err_val == nullptr) + return ir_const_void(ira, &instruction->base); ErrorTableEntry *err = err_val->data.x_err_set; - if (err != nullptr) { - ir_add_error(ira, &instruction->base, - buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); - return ira->codegen->invalid_instruction; - } - return ir_const_void(ira, &instruction->base); + assert(err != nullptr); + ir_add_error(ira, &instruction->base, + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + return ira->codegen->invalid_instruction; } ir_build_assert_non_error(&ira->new_irb, instruction->base.scope, @@ -21593,6 +21648,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_result_ptr_cast(ira, (IrInstructionResultPtrCast *)instruction); case IrInstructionIdAllocaSrc: return ir_analyze_instruction_alloca(ira, (IrInstructionAllocaSrc *)instruction); + case IrInstructionIdErrorUnionFieldErrorSet: + return ir_analyze_instruction_error_union_field_error_set(ira, (IrInstructionErrorUnionFieldErrorSet *)instruction); case IrInstructionIdResultBytesToSlice: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: @@ -21829,6 +21886,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdResultBytesToSlice: case IrInstructionIdAllocaSrc: case IrInstructionIdAllocaGen: + case IrInstructionIdErrorUnionFieldErrorSet: return false; case IrInstructionIdLoadPtr: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 1966fff747d0..aea1e9702f15 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1393,6 +1393,12 @@ static void ir_print_assert_non_error(IrPrint *irp, IrInstructionAssertNonError fprintf(irp->f, ")"); } +static void ir_print_error_union_field_error_set(IrPrint *irp, IrInstructionErrorUnionFieldErrorSet *instruction) { + fprintf(irp->f, "ErrorUnionFieldErrorSet("); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1854,6 +1860,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdAssertNonError: ir_print_assert_non_error(irp, (IrInstructionAssertNonError *)instruction); break; + case IrInstructionIdErrorUnionFieldErrorSet: + ir_print_error_union_field_error_set(irp, (IrInstructionErrorUnionFieldErrorSet *)instruction); + break; } fprintf(irp->f, "\n"); } From 94abb0d803a7c0a5c5a3cbc267c8f47b0eec8fd0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 13:40:26 -0400 Subject: [PATCH 019/190] copy elision: if-bool with scalar types ```zig export fn entry() void { var c = true; var y: i32 = if (c) 1234 else 5678; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %c = alloca i1, align 1 %y = alloca i32, align 4 store i1 true, i1* %c, align 1, !dbg !51 call void @llvm.dbg.declare(metadata i1* %c, metadata !45, metadata !DIExpression()), !dbg !52 %0 = load i1, i1* %c, align 1, !dbg !53 br i1 %0, label %Then, label %Else, !dbg !53 Then: ; preds = %Entry store i32 1234, i32* %y, align 4, !dbg !54 br label %EndIf, !dbg !55 Else: ; preds = %Entry store i32 5678, i32* %y, align 4, !dbg !56 br label %EndIf, !dbg !55 EndIf: ; preds = %Else, %Then call void @llvm.dbg.declare(metadata i32* %y, metadata !48, metadata !DIExpression()), !dbg !57 ret void, !dbg !58 } ``` --- src/all_types.hpp | 8 ++++ src/codegen.cpp | 1 + src/ir.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++++--- src/ir_print.cpp | 11 ++++++ 4 files changed, 108 insertions(+), 5 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index da072e837332..506615e59c29 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2187,6 +2187,7 @@ enum IrInstructionId { IrInstructionIdAllocaGen, IrInstructionIdAssertNonError, IrInstructionIdErrorUnionFieldErrorSet, + IrInstructionIdFirstArgResultLoc, }; struct IrInstruction { @@ -3371,6 +3372,13 @@ struct IrInstructionAssertNonError { IrInstruction *err_code; }; +struct IrInstructionFirstArgResultLoc { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *fn_ref; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index b27409f2a630..88f72dd5ca8e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5241,6 +5241,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdDeclVarSrc: case IrInstructionIdAllocaSrc: case IrInstructionIdAllocaGen: + case IrInstructionIdFirstArgResultLoc: zig_unreachable(); case IrInstructionIdDeclVarGen: diff --git a/src/ir.cpp b/src/ir.cpp index ab79223106f2..9b2be0f5e27e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -907,6 +907,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnionFieldE return IrInstructionIdErrorUnionFieldErrorSet; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionFirstArgResultLoc *) { + return IrInstructionIdFirstArgResultLoc; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2936,6 +2940,19 @@ static IrInstruction *ir_build_error_union_field_error_set(IrBuilder *irb, Scope return &instruction->base; } +static IrInstruction *ir_build_first_arg_result_loc(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *fn_ref) +{ + IrInstructionFirstArgResultLoc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->fn_ref = fn_ref; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + ir_ref_instruction(fn_ref, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -4969,8 +4986,24 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; + // In pass 1, we can't tell the difference between a function call with a single argument + // and an implicit cast. So if there is only one argument, we emit a special instruction. size_t arg_count = node->data.fn_call_expr.params.length; IrInstruction **args = allocate(arg_count); + bool is_async = node->data.fn_call_expr.is_async; + if (arg_count == 1 && !is_async) { + AstNode *arg_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg_result_loc = ir_build_first_arg_result_loc(irb, scope, arg_node, result_loc, fn_ref); + args[0] = ir_gen_node(irb, arg_node, scope, LValNone, arg_result_loc); + if (args[0] == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + // In the analysis, this call instruction will be a simple LoadPtr instruction if + // it turns out to be an implicit cast. + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, + FnInlineAuto, false, nullptr, nullptr, result_loc); + return ir_lval_wrap(irb, scope, fn_call, lval); + } + for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i); args[i] = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); @@ -4978,7 +5011,6 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node return args[i]; } - bool is_async = node->data.fn_call_expr.is_async; IrInstruction *async_allocator = nullptr; if (is_async) { if (node->data.fn_call_expr.async_allocator) { @@ -13989,6 +14021,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) { + Error err; IrInstruction *fn_ref = call_instruction->fn_ref->child; if (type_is_invalid(fn_ref->value.type)) return ira->codegen->invalid_instruction; @@ -14010,12 +14043,18 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC return ira->codegen->invalid_instruction; } - IrInstruction *arg = call_instruction->args[0]->child; + // This is handled with the result location mechanism. So all we need to do here is + // a LoadPtr on the result location. + IrInstruction *result_loc = call_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + if ((err = resolve_possible_alloca_inference(ira, result_loc, dest_type))) + return ira->codegen->invalid_instruction; - IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, dest_type, arg); - if (type_is_invalid(cast_instruction->value.type)) + IrInstruction *deref = ir_get_deref(ira, &call_instruction->base, result_loc); + if (type_is_invalid(deref->value.type)) return ira->codegen->invalid_instruction; - return ir_finish_anal(ira, cast_instruction); + return ir_finish_anal(ira, deref); } else if (fn_ref->value.type->id == ZigTypeIdFn) { ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref); if (fn_table_entry == nullptr) @@ -21357,6 +21396,47 @@ static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructio return &result->base; } +static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira, + IrInstructionFirstArgResultLoc *instruction) +{ + Error err; + + IrInstruction *fn_ref = instruction->fn_ref->child; + if (type_is_invalid(fn_ref->value.type)) + return ira->codegen->invalid_instruction; + + if (fn_ref->value.type->id == ZigTypeIdMetaType) { + // Result of this instruction should be the implicitly casted result location. + + ZigType *dest_type = ir_resolve_type(ira, fn_ref); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->prev_result_loc->child, + dest_type); + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + return new_result_loc; + } + + // Result of this instruction should be the result location for the first argument of the function call. + // This means it should be a stack allocation. + IrInstructionAllocaGen *result = ir_create_alloca_gen(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, 0, ""); + + assert(fn_ref->value.type->id == ZigTypeIdFn); + ZigType *param_type = fn_ref->value.type->data.fn.fn_type_id.param_info[0].type; + if (type_is_invalid(param_type)) + return ira->codegen->invalid_instruction; + if ((err = resolve_alloca_inference(ira, result, param_type))) + return ira->codegen->invalid_instruction; + ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); + if (fn_entry != nullptr) { + fn_entry->alloca_list.append(result); + } + return &result->base; +} + static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -21650,6 +21730,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_alloca(ira, (IrInstructionAllocaSrc *)instruction); case IrInstructionIdErrorUnionFieldErrorSet: return ir_analyze_instruction_error_union_field_error_set(ira, (IrInstructionErrorUnionFieldErrorSet *)instruction); + case IrInstructionIdFirstArgResultLoc: + return ir_analyze_instruction_first_arg_result_loc(ira, (IrInstructionFirstArgResultLoc *)instruction); case IrInstructionIdResultBytesToSlice: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: @@ -21887,6 +21969,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAllocaSrc: case IrInstructionIdAllocaGen: case IrInstructionIdErrorUnionFieldErrorSet: + case IrInstructionIdFirstArgResultLoc: return false; case IrInstructionIdLoadPtr: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index aea1e9702f15..43ca9850262f 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1399,6 +1399,14 @@ static void ir_print_error_union_field_error_set(IrPrint *irp, IrInstructionErro fprintf(irp->f, ")"); } +static void ir_print_first_arg_result_loc(IrPrint *irp, IrInstructionFirstArgResultLoc *instruction) { + fprintf(irp->f, "FirstArgResultLoc(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",fn="); + ir_print_other_instruction(irp, instruction->fn_ref); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1863,6 +1871,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdErrorUnionFieldErrorSet: ir_print_error_union_field_error_set(irp, (IrInstructionErrorUnionFieldErrorSet *)instruction); break; + case IrInstructionIdFirstArgResultLoc: + ir_print_first_arg_result_loc(irp, (IrInstructionFirstArgResultLoc *)instruction); + break; } fprintf(irp->f, "\n"); } From 1bda9705aad83877e66d7eab6d9f5ce426d61ddb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 14:36:36 -0400 Subject: [PATCH 020/190] copy elision: if-optional with aggregate ```zig export fn entry() void { var x: ?Foo = foo(); var y = if (x) |a| a.y else bar(); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca { %Foo, i1 }, align 4 %y = alloca %Bar, align 4 %0 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 1, !dbg !64 store i1 true, i1* %0, align 1, !dbg !64 %1 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 0, !dbg !64 call fastcc void @foo(%Foo* sret %1), !dbg !65 call void @llvm.dbg.declare(metadata { %Foo, i1 }* %x, metadata !45, metadata !DIExpression()), !dbg !64 %2 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 1, !dbg !66 %3 = load i1, i1* %2, align 1, !dbg !66 br i1 %3, label %OptionalThen, label %OptionalElse, !dbg !66 OptionalThen: ; preds = %Entry %4 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 0, !dbg !66 call void @llvm.dbg.declare(metadata %Foo* %4, metadata !61, metadata !DIExpression()), !dbg !66 %5 = getelementptr inbounds %Foo, %Foo* %4, i32 0, i32 1, !dbg !67 %6 = bitcast %Bar* %5 to i8*, !dbg !67 %7 = bitcast %Bar* %y to i8*, !dbg !67 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %7, i8* align 4 %6, i64 8, i1 false), !dbg !67 br label %OptionalEndIf, !dbg !66 OptionalElse: ; preds = %Entry call fastcc void @bar(%Bar* sret %y), !dbg !69 br label %OptionalEndIf, !dbg !66 OptionalEndIf: ; preds = %OptionalElse, %OptionalThen call void @llvm.dbg.declare(metadata %Bar* %y, metadata !63, metadata !DIExpression()), !dbg !70 ret void, !dbg !71 } ``` --- src/ir.cpp | 42 +++++++++++++----------------------------- src/ir_print.cpp | 3 ++- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 9b2be0f5e27e..ef0184d7983b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3832,7 +3832,9 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode return ir_build_load_result(irb, scope, node, ptr_instruction, result_loc); } -static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeFieldAccessExpr); AstNode *container_ref_node = node->data.field_access_expr.struct_expr; @@ -3842,7 +3844,8 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; - return ir_build_field_ptr(irb, scope, node, container_ref_instruction, nullptr, field_name); + IrInstruction *field_ptr = ir_build_field_ptr(irb, scope, node, container_ref_instruction, nullptr, field_name); + return ir_gen_ptr(irb, scope, node, lval, result_loc, field_ptr); } static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) { @@ -5856,7 +5859,7 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile); } -static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node, +static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, IrInstruction *result_loc) { assert(node->type == NodeTypeIfOptional); @@ -5897,12 +5900,10 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *payload_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "optional_payload"); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); - IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_result(irb, subexpr_scope, node, - var_ptr_value, payload_alloca); - ir_build_store_result(irb, subexpr_scope, node, payload_alloca, var_value); - ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, payload_alloca); + IrInstruction *payload_ptr = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); + IrInstruction *var_ptr = var_is_ptr ? + ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false) : payload_ptr; + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_ptr); var_scope = var->child_scope; } else { var_scope = subexpr_scope; @@ -5910,7 +5911,6 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope, LValNone, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; - IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -5923,19 +5923,11 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN } else { else_expr_result = ir_build_const_void(irb, scope, node); } - IrBasicBlock *after_else_block = irb->current_basic_block; if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); - IrInstruction **incoming_values = allocate(2); - incoming_values[0] = then_expr_result; - incoming_values[1] = else_expr_result; - IrBasicBlock **incoming_blocks = allocate(2); - incoming_blocks[0] = after_then_block; - incoming_blocks[1] = after_else_block; - - return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); + return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); } static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { @@ -7276,15 +7268,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeReturnExpr: return ir_gen_return(irb, scope, node, lval, result_loc); case NodeTypeFieldAccessExpr: - { - IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node); - if (ptr_instruction == irb->codegen->invalid_instruction) - return ptr_instruction; - if (lval == LValPtr) - return ptr_instruction; - - return ir_build_load_ptr(irb, scope, node, ptr_instruction, nullptr); - } + return ir_gen_field_access(irb, scope, node, lval, result_loc); case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; IrInstruction *value = ir_gen_node(irb, expr_node, scope, lval, nullptr); @@ -7325,7 +7309,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeIfErrorExpr: return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node, result_loc), lval); case NodeTypeIfOptional: - return ir_lval_wrap(irb, scope, ir_gen_if_optional_expr(irb, scope, node, result_loc), lval); + return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node, result_loc), lval); case NodeTypeCompTime: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 43ca9850262f..f999b98b8d32 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -491,8 +491,9 @@ static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instructi } static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) { - fprintf(irp->f, "&??*"); + fprintf(irp->f, "UnwrapOptional("); ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ")"); if (!instruction->safety_check_on) { fprintf(irp->f, " // no safety"); } From 88427ae04b1c817331ab9104778f1d356ecb9e15 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 16:19:43 -0400 Subject: [PATCH 021/190] copy elision: if-err with aggregate ```zig export fn entry() void { var x: error!Foo = foo(); var y = if (x) |a| a.y else |e| bar(); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca { i16, %Foo }, align 4 %y = alloca %Bar, align 4 %0 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 0, !dbg !64 store i16 0, i16* %0, align 2, !dbg !64 %1 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 1, !dbg !64 call fastcc void @foo(%Foo* sret %1), !dbg !65 call void @llvm.dbg.declare(metadata { i16, %Foo }* %x, metadata !45, metadata !DIExpression()), !dbg !64 %2 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 0, !dbg !66 %3 = load i16, i16* %2, align 2, !dbg !66 %4 = icmp ne i16 %3, 0, !dbg !66 br i1 %4, label %IfErrElse, label %IfErrThen, !dbg !66 IfErrThen: ; preds = %Entry %5 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 1, !dbg !66 call void @llvm.dbg.declare(metadata %Foo* %5, metadata !60, metadata !DIExpression()), !dbg !66 %6 = getelementptr inbounds %Foo, %Foo* %5, i32 0, i32 1, !dbg !67 %7 = bitcast %Bar* %6 to i8*, !dbg !67 %8 = bitcast %Bar* %y to i8*, !dbg !67 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %8, i8* align 4 %7, i64 8, i1 false), !dbg !67 br label %IfErrEnd, !dbg !66 IfErrElse: ; preds = %Entry %9 = load i16, i16* %2, align 2, !dbg !66 call void @llvm.dbg.declare(metadata i16* %2, metadata !62, metadata !DIExpression()), !dbg !66 call fastcc void @bar(%Bar* sret %y), !dbg !69 br label %IfErrEnd, !dbg !66 IfErrEnd: ; preds = %IfErrElse, %IfErrThen call void @llvm.dbg.declare(metadata %Bar* %y, metadata !63, metadata !DIExpression()), !dbg !71 ret void, !dbg !72 } ``` --- src/ir.cpp | 66 +++++++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index ef0184d7983b..89a57e1c7a6b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5930,7 +5930,9 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); } -static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { +static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeIfErrorExpr); AstNode *target_node = node->data.if_err_expr.target_node; @@ -5941,19 +5943,21 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * Buf *var_symbol = node->data.if_err_expr.var_symbol; Buf *err_symbol = node->data.if_err_expr.err_symbol; - IrInstruction *err_val_ptr = ir_gen_node(irb, target_node, scope, LValPtr, nullptr); - if (err_val_ptr == irb->codegen->invalid_instruction) - return err_val_ptr; + IrInstruction *err_union_ptr = ir_gen_node(irb, target_node, scope, LValPtr, nullptr); + if (err_union_ptr == irb->codegen->invalid_instruction) + return err_union_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr, nullptr); - IrInstruction *is_err = ir_build_test_err(irb, scope, node, err_val); + IrInstruction *ptr_opt_err_code = ir_build_error_union_field_error_set(irb, scope, node, err_union_ptr); + IrInstruction *opt_err_code = ir_build_load_ptr(irb, scope, node, ptr_opt_err_code, nullptr); + IrInstruction *is_err = ir_build_test_nonnull(irb, scope, node, opt_err_code); - IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "TryOk"); - IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "TryElse"); - IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "TryEnd"); + IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "IfErrThen"); + IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "IfErrElse"); + IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "IfErrEnd"); bool force_comptime = ir_should_inline(irb->exec, scope); - IrInstruction *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err); + IrInstruction *is_comptime = force_comptime ? + ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err); ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ok_block); @@ -5961,18 +5965,19 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); Scope *var_scope; if (var_symbol) { + IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_union_ptr, false); + IrInstruction *var_type = nullptr; bool is_shadowable = false; - IrInstruction *var_is_comptime = force_comptime ? ir_build_const_bool(irb, subexpr_scope, node, true) : ir_build_test_comptime(irb, subexpr_scope, node, err_val); + IrInstruction *var_is_comptime = force_comptime ? + ir_build_const_bool(irb, subexpr_scope, node, true) : + ir_build_test_comptime(irb, subexpr_scope, node, payload_ptr); ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); - IrInstruction *payload_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "errunion_payload"); - IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); - IrInstruction *var_value = var_is_ptr ? - var_ptr_value : ir_build_load_result(irb, subexpr_scope, node, var_ptr_value, payload_alloca); - ir_build_store_result(irb, subexpr_scope, node, payload_alloca, var_value); - ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, payload_alloca); + IrInstruction *var_ptr = var_is_ptr ? + ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false) : payload_ptr; + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_ptr); var_scope = var->child_scope; } else { var_scope = subexpr_scope; @@ -5980,7 +5985,6 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope, LValNone, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; - IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -5990,14 +5994,14 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * if (else_node) { Scope *err_var_scope; if (err_symbol) { - IrInstruction *var_type = nullptr; bool is_shadowable = false; bool is_const = true; ZigVar *var = ir_create_var(irb, node, subexpr_scope, err_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *var_value = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr); - ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, subexpr_scope, node, + ptr_opt_err_code, false); + ir_build_var_decl_src(irb, subexpr_scope, node, var, nullptr, nullptr, unwrapped_err_code_ptr); err_var_scope = var->child_scope; } else { err_var_scope = subexpr_scope; @@ -6008,19 +6012,11 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * } else { else_expr_result = ir_build_const_void(irb, scope, node); } - IrBasicBlock *after_else_block = irb->current_basic_block; if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); - IrInstruction **incoming_values = allocate(2); - incoming_values[0] = then_expr_result; - incoming_values[1] = else_expr_result; - IrBasicBlock **incoming_blocks = allocate(2); - incoming_blocks[0] = after_then_block; - incoming_blocks[1] = after_else_block; - - return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); + return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); } static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, @@ -6493,11 +6489,11 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc); } - IrInstruction *err_code_ptr = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnion, result_loc); - if (err_code_ptr == irb->codegen->invalid_instruction) + IrInstruction *ptr_opt_err_code = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnion, result_loc); + if (ptr_opt_err_code == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *opt_err_code = ir_build_load_ptr(irb, parent_scope, node, err_code_ptr, nullptr); + IrInstruction *opt_err_code = ir_build_load_ptr(irb, parent_scope, node, ptr_opt_err_code, nullptr); IrInstruction *is_err = ir_build_test_nonnull(irb, parent_scope, node, opt_err_code); IrInstruction *is_comptime; @@ -6522,7 +6518,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, err_scope, var_node, - err_code_ptr, false); + ptr_opt_err_code, false); ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, unwrapped_err_code_ptr); } else { err_scope = parent_scope; @@ -7307,7 +7303,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeNullLiteral: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_null_literal(irb, scope, node)); case NodeTypeIfErrorExpr: - return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node, result_loc), lval); + return ir_gen_if_err_expr(irb, scope, node, lval, result_loc); case NodeTypeIfOptional: return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc); case NodeTypeSwitchExpr: From 7d6c9671e3224b1a63b8746eb5538c468441fbcf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 16:46:59 -0400 Subject: [PATCH 022/190] copy elision: implicit cast error set to error union runtime ```zig export fn entry() void { var x = error.Failure; var y: error!Foo = x; } ``` ```llvm define void @entry() #2 !dbg !42 { Entry: %x = alloca i16, align 2 %y = alloca { i16, %Foo }, align 4 store i16 1, i16* %x, align 2, !dbg !63 call void @llvm.dbg.declare(metadata i16* %x, metadata !46, metadata !DIExpression()), !dbg !64 %0 = load i16, i16* %x, align 2, !dbg !65 %1 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %y, i32 0, i32 0, !dbg !66 store i16 %0, i16* %1, align 2, !dbg !65 call void @llvm.dbg.declare(metadata { i16, %Foo }* %y, metadata !48, metadata !DIExpression()), !dbg !66 ret void, !dbg !67 } ``` --- src/ir.cpp | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 89a57e1c7a6b..ef7b4a6a0ce0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13377,30 +13377,25 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, } static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *ptr, IrInstruction *value) + IrInstruction *uncasted_ptr, IrInstruction *value) { - if (ptr->value.type->id != ZigTypeIdPointer) { - ir_add_error(ira, ptr, - buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); + if (uncasted_ptr->value.type->id != ZigTypeIdPointer) { + ir_add_error(ira, uncasted_ptr, + buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&uncasted_ptr->value.type->name))); return ira->codegen->invalid_instruction; } - Error err; - if ((err = resolve_possible_alloca_inference(ira, ptr, value->value.type))) - return ira->codegen->invalid_instruction; - - if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { + if (uncasted_ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { return ir_const_void(ira, source_instr); } - if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { + if (uncasted_ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_instruction; } - ZigType *child_type = ptr->value.type->data.pointer.child_type; - IrInstruction *casted_value = ir_implicit_cast(ira, value, child_type); - if (casted_value == ira->codegen->invalid_instruction) + IrInstruction *ptr = ir_implicit_cast_result(ira, uncasted_ptr, value->value.type); + if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { @@ -13409,12 +13404,12 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return ira->codegen->invalid_instruction; } if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { - if (instr_is_comptime(casted_value)) { + if (instr_is_comptime(value)) { ConstExprValue *dest_val = ir_const_ptr_pointee(ira, &ptr->value, source_instr->source_node); if (dest_val == nullptr) return ira->codegen->invalid_instruction; if (dest_val->special != ConstValSpecialRuntime) { - *dest_val = casted_value->value; + *dest_val = value->value; if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; } @@ -13431,7 +13426,7 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source } IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, - ptr, casted_value); + ptr, value); result->value.type = ira->codegen->builtin_types.entry_void; return result; } From 58a3d997f0bf3f62d91ae9dc33949c66888189eb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 18:06:01 -0400 Subject: [PATCH 023/190] copy elision: if-err with scalar types ```zig export fn entry() void { var x: error!i32 = 1234; var y = if (x) |a| a else |e| 5678; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca { i16, i32 }, align 4 %y = alloca i32, align 4 %0 = bitcast { i16, i32 }* %x to i8*, !dbg !56 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast ({ i16, i32 }* @0 to i8*), i64 8, i1 false), !dbg !56 call void @llvm.dbg.declare(metadata { i16, i32 }* %x, metadata !45, metadata !DIExpression()), !dbg !57 %1 = getelementptr inbounds { i16, i32 }, { i16, i32 }* %x, i32 0, i32 0, !dbg !58 %2 = load i16, i16* %1, align 2, !dbg !58 %3 = icmp ne i16 %2, 0, !dbg !58 br i1 %3, label %IfErrElse, label %IfErrThen, !dbg !58 IfErrThen: ; preds = %Entry %4 = getelementptr inbounds { i16, i32 }, { i16, i32 }* %x, i32 0, i32 1, !dbg !58 call void @llvm.dbg.declare(metadata i32* %4, metadata !52, metadata !DIExpression()), !dbg !58 %5 = load i32, i32* %4, align 4, !dbg !59 store i32 %5, i32* %y, align 4, !dbg !59 br label %IfErrEnd, !dbg !58 IfErrElse: ; preds = %Entry %6 = load i16, i16* %1, align 2, !dbg !58 call void @llvm.dbg.declare(metadata i16* %1, metadata !54, metadata !DIExpression()), !dbg !58 store i32 5678, i32* %y, align 4, !dbg !61 br label %IfErrEnd, !dbg !58 IfErrEnd: ; preds = %IfErrElse, %IfErrThen call void @llvm.dbg.declare(metadata i32* %y, metadata !55, metadata !DIExpression()), !dbg !63 ret void, !dbg !64 } ``` --- src/ir.cpp | 53 ++++++++++++++++++----------------------------------- 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index ef7b4a6a0ce0..c0c0170c28ba 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10984,19 +10984,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (actual_type->id == ZigTypeIdComptimeFloat || actual_type->id == ZigTypeIdComptimeInt) { - if ((err = ensure_complete_type(ira->codegen, wanted_type))) - return ira->codegen->invalid_instruction; - if (wanted_type->id == ZigTypeIdEnum) { - 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 (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { + if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { CastOp op; if ((actual_type->id == ZigTypeIdComptimeFloat && wanted_type->id == ZigTypeIdFloat) || @@ -11211,7 +11199,7 @@ static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { } static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *result_loc, - ZigType *needed_child_type) + ZigType *needed_child_type, bool allow_failure) { Error err; if (type_is_invalid(result_loc->value.type) || type_is_invalid(needed_child_type)) @@ -11278,14 +11266,18 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *res needed_child_type->id == ZigTypeIdComptimeFloat) { IrInstruction *cast1 = ir_implicit_cast_result(ira, result_loc, - have_child_type->data.error_union.payload_type); + have_child_type->data.error_union.payload_type, allow_failure); if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; - return ir_implicit_cast_result(ira, cast1, needed_child_type); + return ir_implicit_cast_result(ira, cast1, needed_child_type, allow_failure); } } + if (allow_failure) { + return result_loc; + } + ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, buf_sprintf("expected type '%s', found '%s'", buf_ptr(&have_child_type->name), @@ -13377,7 +13369,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, } static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *uncasted_ptr, IrInstruction *value) + IrInstruction *uncasted_ptr, IrInstruction *uncasted_value) { if (uncasted_ptr->value.type->id != ZigTypeIdPointer) { ir_add_error(ira, uncasted_ptr, @@ -13394,10 +13386,15 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return ira->codegen->invalid_instruction; } - IrInstruction *ptr = ir_implicit_cast_result(ira, uncasted_ptr, value->value.type); + IrInstruction *ptr = ir_implicit_cast_result(ira, uncasted_ptr, uncasted_value->value.type, true); if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; + ZigType *child_type = ptr->value.type->data.pointer.child_type; + IrInstruction *value = ir_implicit_cast(ira, uncasted_value, child_type); + if (type_is_invalid(value->value.type)) + return ira->codegen->invalid_instruction; + if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); @@ -13977,7 +13974,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *result_loc = nullptr; if (call_instruction->result_loc != nullptr) { - result_loc = ir_implicit_cast_result(ira, call_instruction->result_loc->child, return_type); + result_loc = ir_implicit_cast_result(ira, call_instruction->result_loc->child, return_type, false); if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; } @@ -15434,21 +15431,7 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_store_result(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, IrInstruction *uncasted_result_loc) { - if (instr_is_comptime(value)) { - // No need for the ir_implicit_cast_result for comptime values. - return ir_analyze_store_ptr(ira, source_instr, uncasted_result_loc, value); - } - - if (value->id == IrInstructionIdCall && handle_is_ptr(value->value.type)) { - // Elide this copy because it was the sret pointer in the function call. - return ir_const_void(ira, source_instr); - } - - IrInstruction *result_loc = ir_implicit_cast_result(ira, uncasted_result_loc, value->value.type); - if (type_is_invalid(result_loc->value.type)) - return ira->codegen->invalid_instruction; - - return ir_analyze_store_ptr(ira, source_instr, result_loc, value); + zig_panic("TODO delete this instruction"); } static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInstructionStoreResult *instruction) { @@ -21388,7 +21371,7 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira return ira->codegen->invalid_instruction; IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->prev_result_loc->child, - dest_type); + dest_type, false); if (type_is_invalid(new_result_loc->value.type)) return ira->codegen->invalid_instruction; return new_result_loc; From a69436304b3a83fc96e99712b1f39b6d1fd32c5d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 18:15:13 -0400 Subject: [PATCH 024/190] copy elision: returning scalar from function ```zig fn fail() error { return error.Failure; } ``` ```llvm define internal fastcc i16 @fail(%StackTrace* nonnull) unnamed_addr #2 !dbg !56 { Entry: call fastcc void @__zig_return_error(%StackTrace* %0), !dbg !60 ret i16 1, !dbg !60 } ``` --- src/ir.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index c0c0170c28ba..32919857d0ce 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7381,7 +7381,9 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ZigFn *fn_entry = exec_fn_entry(irb->exec); - irb->exec->return_result_loc = ir_build_result_return(irb, scope, node); + if (fn_entry != nullptr && handle_is_ptr(fn_entry->type_entry->data.fn.fn_type_id.return_type)) { + irb->exec->return_result_loc = ir_build_result_return(irb, scope, node); + } bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; IrInstruction *coro_id; From 00a5b728567ba410359535750ee8710b911232fd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Oct 2018 19:16:37 -0400 Subject: [PATCH 025/190] copy elision: while-optional with aggregate ```zig export fn entry() void { var c = false; var x: ?Foo = foo(); var z = while (x) |a| { _ = if (c) break a.y; } else bar(); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %c = alloca i1, align 1 %x = alloca { %Foo, i1 }, align 4 %z = alloca %Bar, align 4 store i1 false, i1* %c, align 1, !dbg !66 call void @llvm.dbg.declare(metadata i1* %c, metadata !45, metadata !DIExpression()), !dbg !67 %0 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 1, !dbg !68 store i1 true, i1* %0, align 1, !dbg !68 %1 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 0, !dbg !68 call fastcc void @foo(%Foo* sret %1), !dbg !69 call void @llvm.dbg.declare(metadata { %Foo, i1 }* %x, metadata !48, metadata !DIExpression()), !dbg !68 br label %WhileCond, !dbg !70 WhileCond: ; preds = %Else, %Entry %2 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 1, !dbg !71 %3 = load i1, i1* %2, align 1, !dbg !71 br i1 %3, label %WhileBody, label %WhileElse, !dbg !71 WhileBody: ; preds = %WhileCond %4 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 0, !dbg !72 call void @llvm.dbg.declare(metadata %Foo* %4, metadata !63, metadata !DIExpression()), !dbg !70 %5 = load i1, i1* %c, align 1, !dbg !74 br i1 %5, label %Then, label %Else, !dbg !74 Then: ; preds = %WhileBody %6 = getelementptr inbounds %Foo, %Foo* %4, i32 0, i32 1, !dbg !76 %7 = bitcast %Bar* %6 to i8*, !dbg !76 %8 = bitcast %Bar* %z to i8*, !dbg !76 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %8, i8* align 4 %7, i64 8, i1 false), !dbg !76 br label %WhileEnd, !dbg !77 Else: ; preds = %WhileBody br label %WhileCond, !dbg !72 WhileElse: ; preds = %WhileCond call fastcc void @bar(%Bar* sret %z), !dbg !78 br label %WhileEnd, !dbg !70 WhileEnd: ; preds = %WhileElse, %Then call void @llvm.dbg.declare(metadata %Bar* %z, metadata !65, metadata !DIExpression()), !dbg !79 ret void, !dbg !80 } ``` --- src/codegen.cpp | 6 ++- src/ir.cpp | 99 +++++++++++++++++++++---------------------------- 2 files changed, 46 insertions(+), 59 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 88f72dd5ca8e..ec710304d145 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6322,8 +6322,10 @@ static void do_code_gen(CodeGen *g) { ZigType *ptr_type = instruction->base.value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; - instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, - get_ptr_align(g, ptr_type)); + if (type_has_bits(child_type)) { + instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, + get_ptr_align(g, ptr_type)); + } } ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); diff --git a/src/ir.cpp b/src/ir.cpp index 32919857d0ce..aadc4a2e1c25 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5323,7 +5323,9 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_var_decl_src(irb, scope, node, var, type_instruction, align_value, alloca); } -static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { +static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeWhileExpr); AstNode *continue_expr_node = node->data.while_expr.continue_expr; @@ -5358,14 +5360,17 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } else { payload_scope = subexpr_scope; } - IrInstruction *err_val_ptr = ir_gen_node(irb, node->data.while_expr.condition, subexpr_scope, + IrInstruction *err_union_ptr = ir_gen_node(irb, node->data.while_expr.condition, subexpr_scope, LValPtr, nullptr); - if (err_val_ptr == irb->codegen->invalid_instruction) - return err_val_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr, nullptr); - IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val); - IrBasicBlock *after_cond_block = irb->current_basic_block; - IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); + if (type_is_invalid(err_union_ptr->value.type)) + return err_union_ptr; + + IrInstruction *ptr_opt_err_code = ir_build_error_union_field_error_set(irb, scope, + node->data.while_expr.condition, err_union_ptr); + IrInstruction *opt_err_code = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, + ptr_opt_err_code, nullptr); + IrInstruction *is_err = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, opt_err_code); + if (!instr_is_unreachable(is_err)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err, else_block, body_block, is_comptime)); @@ -5373,11 +5378,11 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, body_block); if (var_symbol) { - IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node, - err_val_ptr, false); - IrInstruction *var_value = node->data.while_expr.var_is_ptr ? - ir_build_ref(irb, payload_scope, symbol_node, var_ptr_value, true, false) : var_ptr_value; - ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node, + err_union_ptr, false); + IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ? + ir_build_ref(irb, payload_scope, symbol_node, payload_ptr, true, false) : payload_ptr; + ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_ptr); } ZigList incoming_values = {0}; @@ -5419,8 +5424,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol, true, false, false, is_comptime); Scope *err_scope = err_var->child_scope; - IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr); - ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); + IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, err_scope, err_symbol_node, + ptr_opt_err_code, false); + ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, unwrapped_err_code_ptr); else_result = ir_gen_node(irb, else_node, err_scope, LValNone, result_loc); if (else_result == irb->codegen->invalid_instruction) @@ -5428,17 +5434,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } - IrBasicBlock *after_else_block = irb->current_basic_block; - ir_set_cursor_at_end_and_append_block(irb, end_block); - if (else_result) { - incoming_blocks.append(after_else_block); - incoming_values.append(else_result); - } else { - incoming_blocks.append(after_cond_block); - incoming_values.append(void_else_result); - } - return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); + ir_set_cursor_at_end_and_append_block(irb, end_block); + return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); } else if (var_symbol != nullptr) { ir_set_cursor_at_end_and_append_block(irb, cond_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); @@ -5454,18 +5452,16 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n return maybe_val_ptr; IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val); - IrBasicBlock *after_cond_block = irb->current_basic_block; - IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); if (!instr_is_unreachable(is_non_null)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null, body_block, else_block, is_comptime)); } ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); - IrInstruction *var_value = node->data.while_expr.var_is_ptr ? - ir_build_ref(irb, child_scope, symbol_node, var_ptr_value, true, false) : var_ptr_value; - ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + IrInstruction *payload_ptr = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); + IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ? + ir_build_ref(irb, child_scope, symbol_node, payload_ptr, true, false) : payload_ptr; + ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_ptr); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5480,7 +5476,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, LValNone, result_loc); - if (body_result == irb->codegen->invalid_instruction) + if (type_is_invalid(body_result->value.type)) return body_result; if (!instr_is_unreachable(body_result)) { @@ -5507,24 +5503,13 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } - IrBasicBlock *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); - if (else_result) { - incoming_blocks.append(after_else_block); - incoming_values.append(else_result); - } else { - incoming_blocks.append(after_cond_block); - incoming_values.append(void_else_result); - } - - return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); + return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); } else { ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope, LValNone, nullptr); if (cond_val == irb->codegen->invalid_instruction) return cond_val; - IrBasicBlock *after_cond_block = irb->current_basic_block; - IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); if (!instr_is_unreachable(cond_val)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val, body_block, else_block, is_comptime)); @@ -5574,17 +5559,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } - IrBasicBlock *after_else_block = irb->current_basic_block; - ir_set_cursor_at_end_and_append_block(irb, end_block); - if (else_result) { - incoming_blocks.append(after_else_block); - incoming_values.append(else_result); - } else { - incoming_blocks.append(after_cond_block); - incoming_values.append(void_else_result); - } - return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); + ir_set_cursor_at_end_and_append_block(irb, end_block); + return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); } } @@ -5944,7 +5921,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * Buf *err_symbol = node->data.if_err_expr.err_symbol; IrInstruction *err_union_ptr = ir_gen_node(irb, target_node, scope, LValPtr, nullptr); - if (err_union_ptr == irb->codegen->invalid_instruction) + if (type_is_invalid(err_union_ptr->value.type)) return err_union_ptr; IrInstruction *ptr_opt_err_code = ir_build_error_union_field_error_set(irb, scope, node, err_union_ptr); @@ -7216,6 +7193,12 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); } +static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { + if (result_loc) + return result_loc; + return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); +} + static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval, IrInstruction *result_loc) { @@ -7248,7 +7231,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeFnCallExpr: return ir_gen_fn_call(irb, scope, node, lval, result_loc); case NodeTypeIfBoolExpr: - return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc); + return ir_gen_if_bool_expr(irb, scope, node, lval, + ensure_result_loc(irb, scope, node, result_loc)); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval); case NodeTypeContainerInitExpr: @@ -7256,7 +7240,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeVariableDeclaration: return ir_lval_wrap(irb, scope, ir_gen_var_decl(irb, scope, node), lval); case NodeTypeWhileExpr: - return ir_lval_wrap(irb, scope, ir_gen_while_expr(irb, scope, node, result_loc), lval); + return ir_gen_while_expr(irb, scope, node, lval, + ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeForExpr: return ir_lval_wrap(irb, scope, ir_gen_for_expr(irb, scope, node, result_loc), lval); case NodeTypeArrayAccessExpr: From 167c37ee553e709129df2796d96d5a9124ae0829 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Oct 2018 10:20:45 -0400 Subject: [PATCH 026/190] copy elision: while-bool with aggregate ```zig export fn entry() void { var c = false; var z = while (c) { if (c) break bar2(); } else bar(); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %c = alloca i1, align 1 %z = alloca %Bar, align 4 store i1 false, i1* %c, align 1, !dbg !55 call void @llvm.dbg.declare(metadata i1* %c, metadata !45, metadata !DIExpression()), !dbg !56 br label %WhileCond, !dbg !57 WhileCond: ; preds = %Else, %Entry %0 = load i1, i1* %c, align 1, !dbg !58 br i1 %0, label %WhileBody, label %WhileElse, !dbg !58 WhileBody: ; preds = %WhileCond %1 = load i1, i1* %c, align 1, !dbg !59 br i1 %1, label %Then, label %Else, !dbg !59 Then: ; preds = %WhileBody call fastcc void @bar2(%Bar* sret %z), !dbg !61 br label %WhileEnd, !dbg !62 Else: ; preds = %WhileBody br label %WhileCond, !dbg !57 WhileElse: ; preds = %WhileCond call fastcc void @bar(%Bar* sret %z), !dbg !63 br label %WhileEnd, !dbg !57 WhileEnd: ; preds = %WhileElse, %Then call void @llvm.dbg.declare(metadata %Bar* %z, metadata !48, metadata !DIExpression()), !dbg !64 ret void, !dbg !65 } ``` --- src/ir.cpp | 64 +++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index aadc4a2e1c25..1a5a276d3304 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5070,6 +5070,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode return irb->codegen->invalid_instruction; } else { else_expr_result = ir_build_const_void(irb, scope, node); + ir_build_store_ptr(irb, scope, node, result_loc, else_expr_result); } if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -5336,8 +5337,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrBasicBlock *continue_block = continue_expr_node ? ir_create_basic_block(irb, scope, "WhileContinue") : cond_block; IrBasicBlock *end_block = ir_create_basic_block(irb, scope, "WhileEnd"); - IrBasicBlock *else_block = else_node ? - ir_create_basic_block(irb, scope, "WhileElse") : end_block; + IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "WhileElse"); IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline); @@ -5415,25 +5415,22 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime)); } - IrInstruction *else_result = nullptr; - if (else_node) { - ir_set_cursor_at_end_and_append_block(irb, else_block); + ir_set_cursor_at_end_and_append_block(irb, else_block); - // TODO make it an error to write to error variable - AstNode *err_symbol_node = else_node; // TODO make more accurate - ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol, - true, false, false, is_comptime); - Scope *err_scope = err_var->child_scope; - IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, err_scope, err_symbol_node, - ptr_opt_err_code, false); - ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, unwrapped_err_code_ptr); + // TODO make it an error to write to error variable + AstNode *err_symbol_node = else_node; // TODO make more accurate + ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol, + true, false, false, is_comptime); + Scope *err_scope = err_var->child_scope; + IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, err_scope, err_symbol_node, + ptr_opt_err_code, false); + ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, unwrapped_err_code_ptr); - else_result = ir_gen_node(irb, else_node, err_scope, LValNone, result_loc); - if (else_result == irb->codegen->invalid_instruction) - return else_result; - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); - } + IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope, LValNone, result_loc); + if (else_result == irb->codegen->invalid_instruction) + return else_result; + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, end_block); return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); @@ -5493,16 +5490,19 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime)); } + ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_result = nullptr; if (else_node) { - ir_set_cursor_at_end_and_append_block(irb, else_block); - else_result = ir_gen_node(irb, else_node, scope, LValNone, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); + } else { + else_result = ir_build_const_void(irb, scope, node); + ir_build_store_ptr(irb, scope, node, result_loc, else_result); } + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); + ir_set_cursor_at_end_and_append_block(irb, end_block); return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); } else { @@ -5549,16 +5549,18 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime)); } + ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_result = nullptr; if (else_node) { - ir_set_cursor_at_end_and_append_block(irb, else_block); - else_result = ir_gen_node(irb, else_node, subexpr_scope, LValNone, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); + } else { + else_result = ir_build_const_void(irb, subexpr_scope, node); + ir_build_store_ptr(irb, subexpr_scope, node, result_loc, else_result); } + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, end_block); return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); @@ -5899,6 +5901,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); + ir_build_store_ptr(irb, scope, node, result_loc, else_expr_result); } if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -5988,6 +5991,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); + ir_build_store_ptr(irb, scope, node, result_loc, else_expr_result); } if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -7288,9 +7292,11 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeNullLiteral: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_null_literal(irb, scope, node)); case NodeTypeIfErrorExpr: - return ir_gen_if_err_expr(irb, scope, node, lval, result_loc); + return ir_gen_if_err_expr(irb, scope, node, lval, + ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeIfOptional: - return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc); + return ir_gen_if_optional_expr(irb, scope, node, lval, + ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node, result_loc), lval); case NodeTypeCompTime: From 6af39bc370a03213ff5ca7e8f66c24b3b4ca4caf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Oct 2018 11:03:21 -0400 Subject: [PATCH 027/190] copy elision: while-err with aggregates ```zig export fn entry() void { var x = true; var y: error!Foo = foo(); var z = while (y) |a| { if (x) break a.y; } else |e| bar(); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca i1, align 1 %y = alloca { i16, %Foo }, align 4 %z = alloca %Bar, align 4 store i1 true, i1* %x, align 1, !dbg !67 call void @llvm.dbg.declare(metadata i1* %x, metadata !45, metadata !DIExpression()), !dbg !68 %0 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %y, i32 0, i32 0, !dbg !69 store i16 0, i16* %0, align 2, !dbg !69 %1 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %y, i32 0, i32 1, !dbg !69 call fastcc void @foo(%Foo* sret %1), !dbg !70 call void @llvm.dbg.declare(metadata { i16, %Foo }* %y, metadata !48, metadata !DIExpression()), !dbg !69 br label %WhileCond, !dbg !71 WhileCond: ; preds = %Else, %Entry %2 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %y, i32 0, i32 0, !dbg !72 %3 = load i16, i16* %2, align 2, !dbg !72 %4 = icmp ne i16 %3, 0, !dbg !72 br i1 %4, label %WhileElse, label %WhileBody, !dbg !72 WhileBody: ; preds = %WhileCond %5 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %y, i32 0, i32 1, !dbg !73 call void @llvm.dbg.declare(metadata %Foo* %5, metadata !63, metadata !DIExpression()), !dbg !71 %6 = load i1, i1* %x, align 1, !dbg !75 br i1 %6, label %Then, label %Else, !dbg !75 Then: ; preds = %WhileBody %7 = getelementptr inbounds %Foo, %Foo* %5, i32 0, i32 1, !dbg !77 %8 = bitcast %Bar* %7 to i8*, !dbg !77 %9 = bitcast %Bar* %z to i8*, !dbg !77 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %9, i8* align 4 %8, i64 8, i1 false), !dbg !77 br label %WhileEnd, !dbg !78 Else: ; preds = %WhileBody br label %WhileCond, !dbg !73 WhileElse: ; preds = %WhileCond %10 = load i16, i16* %2, align 2, !dbg !79 call void @llvm.dbg.declare(metadata i16* %2, metadata !65, metadata !DIExpression()), !dbg !81 call fastcc void @bar(%Bar* sret %z), !dbg !79 br label %WhileEnd, !dbg !71 WhileEnd: ; preds = %WhileElse, %Then call void @llvm.dbg.declare(metadata %Bar* %z, metadata !66, metadata !DIExpression()), !dbg !82 ret void, !dbg !83 } ``` --- src/ir.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 1a5a276d3304..64e623ec44ba 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5362,7 +5362,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } IrInstruction *err_union_ptr = ir_gen_node(irb, node->data.while_expr.condition, subexpr_scope, LValPtr, nullptr); - if (type_is_invalid(err_union_ptr->value.type)) + if (err_union_ptr == irb->codegen->invalid_instruction) return err_union_ptr; IrInstruction *ptr_opt_err_code = ir_build_error_union_field_error_set(irb, scope, @@ -5473,7 +5473,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, LValNone, result_loc); - if (type_is_invalid(body_result->value.type)) + if (body_result == irb->codegen->invalid_instruction) return body_result; if (!instr_is_unreachable(body_result)) { @@ -5924,7 +5924,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * Buf *err_symbol = node->data.if_err_expr.err_symbol; IrInstruction *err_union_ptr = ir_gen_node(irb, target_node, scope, LValPtr, nullptr); - if (type_is_invalid(err_union_ptr->value.type)) + if (err_union_ptr == irb->codegen->invalid_instruction) return err_union_ptr; IrInstruction *ptr_opt_err_code = ir_build_error_union_field_error_set(irb, scope, node, err_union_ptr); From 25306e3c606f6260feccd18a3a234657e01acc05 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 2 Nov 2018 15:19:26 -0400 Subject: [PATCH 028/190] result location mechanism gains ability to infer comptime * introduce ConstPtrMutInfer * `?error` is represented in comptime values the same as `error` * this test case works now: ```zig export fn entry() void { if ((Bar.{ .x = 3, .y = 4, }).x != 3) { @compileError("this compile error should not trigger"); } } ``` This commit comments out code and makes StructFieldInit instruction obselete. Come back and clean this up. --- src/all_types.hpp | 7 +- src/analyze.cpp | 9 +- src/codegen.cpp | 21 ++- src/ir.cpp | 446 ++++++++++++++++++++++++++-------------------- src/ir_print.cpp | 5 +- 5 files changed, 276 insertions(+), 212 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 506615e59c29..310a3292f581 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -180,6 +180,9 @@ enum ConstPtrMut { // The pointer points to memory that is known only at runtime. // For example it may point to the initializer value of a variable. ConstPtrMutRuntimeVar, + // The pointer points to memory for which it must be inferred whether the + // value is comptime known or not. + ConstPtrMutInfer, }; struct ConstPtrValue { @@ -211,7 +214,7 @@ struct ConstPtrValue { }; struct ConstErrValue { - ErrorTableEntry *err; + ConstExprValue *error_set; ConstExprValue *payload; }; @@ -2458,7 +2461,6 @@ struct IrInstructionContainerInitList { struct IrInstructionContainerInitFieldsField { Buf *name; - IrInstruction *value; IrInstruction *result_loc; AstNode *source_node; TypeStructField *type_struct_field; @@ -2470,6 +2472,7 @@ struct IrInstructionContainerInitFields { IrInstruction *container_type; size_t field_count; IrInstructionContainerInitFieldsField *fields; + IrInstruction *result_loc; }; struct IrInstructionStructInitField { diff --git a/src/analyze.cpp b/src/analyze.cpp index ed8b286d6759..b4a8e10f7809 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4739,6 +4739,8 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { case ConstPtrMutComptimeVar: hash_val += (uint32_t)1103195694; break; + case ConstPtrMutInfer: + zig_unreachable(); } switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: @@ -4982,7 +4984,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { return can_mutate_comptime_var_state(value->data.x_optional); case ZigTypeIdErrorUnion: - if (value->data.x_err_union.err != nullptr) + if (value->data.x_err_union.error_set->data.x_err_set != nullptr) return false; assert(value->data.x_err_union.payload != nullptr); return can_mutate_comptime_var_state(value->data.x_err_union.payload); @@ -5943,11 +5945,12 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { case ZigTypeIdErrorUnion: { buf_appendf(buf, "%s(", buf_ptr(&type_entry->name)); - if (const_val->data.x_err_union.err == nullptr) { + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + if (err_set == nullptr) { render_const_value(g, buf, const_val->data.x_err_union.payload); } else { buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name), - buf_ptr(&const_val->data.x_err_union.err->name)); + buf_ptr(&err_set->name)); } buf_appendf(buf, ")"); return; diff --git a/src/codegen.cpp b/src/codegen.cpp index ec710304d145..f27a46922f9b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5703,6 +5703,11 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con zig_unreachable(); } +static LLVMValueRef gen_const_val_err_set(CodeGen *g, ConstExprValue *const_val, const char *name) { + uint64_t value = (const_val->data.x_err_set == nullptr) ? 0 : const_val->data.x_err_set->value; + return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref, value, false); +} + static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { ZigType *type_entry = const_val->type; assert(!type_entry->zero_bits); @@ -5720,9 +5725,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case ZigTypeIdInt: return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint); case ZigTypeIdErrorSet: - assert(const_val->data.x_err_set != nullptr); - return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref, - const_val->data.x_err_set->value, false); + return gen_const_val_err_set(g, const_val, name); case ZigTypeIdFloat: switch (type_entry->data.floating.bit_count) { case 16: @@ -5757,7 +5760,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c } else if (type_is_codegen_pointer(child_type)) { return gen_const_val_ptr(g, const_val, name); } else if (child_type->id == ZigTypeIdErrorSet) { - zig_panic("TODO"); + return gen_const_val_err_set(g, const_val, name); } else { LLVMValueRef child_val; LLVMValueRef maybe_val; @@ -5986,7 +5989,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c ZigType *err_set_type = type_entry->data.error_union.err_set_type; if (!type_has_bits(payload_type)) { assert(type_has_bits(err_set_type)); - uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0; + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + uint64_t value = (err_set == nullptr) ? 0 : err_set->value; return LLVMConstInt(g->err_tag_type->type_ref, value, false); } else if (!type_has_bits(err_set_type)) { assert(type_has_bits(payload_type)); @@ -5995,8 +5999,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c LLVMValueRef err_tag_value; LLVMValueRef err_payload_value; bool make_unnamed_struct; - if (const_val->data.x_err_union.err) { - err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err_union.err->value, false); + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + if (err_set != nullptr) { + err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, err_set->value, false); err_payload_value = LLVMConstNull(payload_type->type_ref); make_unnamed_struct = false; } else { @@ -6322,7 +6327,7 @@ static void do_code_gen(CodeGen *g) { ZigType *ptr_type = instruction->base.value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; - if (type_has_bits(child_type)) { + if (type_has_bits(child_type) && instruction->base.value.special == ConstValSpecialRuntime) { instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, get_ptr_align(g, ptr_type)); } diff --git a/src/ir.cpp b/src/ir.cpp index 64e623ec44ba..9d3e7d0b4158 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -188,6 +188,11 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c return result; } +static bool is_opt_err_set(ZigType *ty) { + return ty->id == ZigTypeIdErrorSet || + (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet); +} + static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (a == b) return true; @@ -198,6 +203,9 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) return true; + if (is_opt_err_set(a) && is_opt_err_set(b)) + return true; + return false; } @@ -1332,48 +1340,51 @@ static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, } static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields) + IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields, + IrInstruction *result_loc) { IrInstructionContainerInitFields *container_init_fields_instruction = ir_build_instruction(irb, scope, source_node); container_init_fields_instruction->container_type = container_type; container_init_fields_instruction->field_count = field_count; container_init_fields_instruction->fields = fields; + container_init_fields_instruction->result_loc = result_loc; ir_ref_instruction(container_type, irb->current_basic_block); for (size_t i = 0; i < field_count; i += 1) { - ir_ref_instruction(fields[i].value, irb->current_basic_block); + ir_ref_instruction(fields[i].result_loc, irb->current_basic_block); } + ir_ref_instruction(result_loc, irb->current_basic_block); return &container_init_fields_instruction->base; } -static IrInstruction *ir_build_struct_init(IrBuilder *irb, Scope *scope, AstNode *source_node, - ZigType *struct_type, size_t field_count, IrInstructionStructInitField *fields) -{ - IrInstructionStructInit *struct_init_instruction = ir_build_instruction(irb, scope, source_node); - struct_init_instruction->struct_type = struct_type; - struct_init_instruction->field_count = field_count; - struct_init_instruction->fields = fields; - - for (size_t i = 0; i < field_count; i += 1) - ir_ref_instruction(fields[i].value, irb->current_basic_block); - - return &struct_init_instruction->base; -} - -static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node, - ZigType *union_type, TypeUnionField *field, IrInstruction *init_value) -{ - IrInstructionUnionInit *union_init_instruction = ir_build_instruction(irb, scope, source_node); - union_init_instruction->union_type = union_type; - union_init_instruction->field = field; - union_init_instruction->init_value = init_value; - - ir_ref_instruction(init_value, irb->current_basic_block); - - return &union_init_instruction->base; -} +//static IrInstruction *ir_build_struct_init(IrBuilder *irb, Scope *scope, AstNode *source_node, +// ZigType *struct_type, size_t field_count, IrInstructionStructInitField *fields) +//{ +// IrInstructionStructInit *struct_init_instruction = ir_build_instruction(irb, scope, source_node); +// struct_init_instruction->struct_type = struct_type; +// struct_init_instruction->field_count = field_count; +// struct_init_instruction->fields = fields; +// +// for (size_t i = 0; i < field_count; i += 1) +// ir_ref_instruction(fields[i].value, irb->current_basic_block); +// +// return &struct_init_instruction->base; +//} + +//static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node, +// ZigType *union_type, TypeUnionField *field, IrInstruction *init_value) +//{ +// IrInstructionUnionInit *union_init_instruction = ir_build_instruction(irb, scope, source_node); +// union_init_instruction->union_type = union_type; +// union_init_instruction->field = field; +// union_init_instruction->init_value = init_value; +// +// ir_ref_instruction(init_value, irb->current_basic_block); +// +// return &union_init_instruction->base; +//} static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionUnreachable *unreachable_instruction = @@ -3142,7 +3153,7 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime); } -static IrInstruction *ir_gen_lval_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, +static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, IrInstruction *result_loc) { switch (lval) { @@ -3164,7 +3175,7 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, ir_build_store_ptr(irb, scope, node, result_loc, value); if (lval != LValNone) { assert(lval != LValPtr); - return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } } return ir_lval_wrap(irb, scope, value, lval); @@ -3306,7 +3317,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, } ir_set_cursor_at_end_and_append_block(irb, continue_block); - return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } } zig_unreachable(); @@ -3620,7 +3631,7 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_lval_ptr(irb, parent_scope, node, lval, result_loc); + return ir_gen_result(irb, parent_scope, node, lval, result_loc); } static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -5076,7 +5087,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); - return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { @@ -5167,7 +5178,7 @@ static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, Ast IrInstruction *err_code = ir_build_load_ptr(irb, scope, source_node, err_code_ptr, nullptr); ir_build_assert_non_error(irb, scope, source_node, err_code); - return ir_gen_lval_ptr(irb, scope, source_node, lval, result_loc); + return ir_gen_result(irb, scope, source_node, lval, result_loc); } static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5207,7 +5218,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod zig_unreachable(); } -static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node, +static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, IrInstruction *result_loc) { assert(node->type == NodeTypeContainerInitExpr); @@ -5236,10 +5247,11 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A return expr_value; fields[i].name = name; - fields[i].value = expr_value; + fields[i].result_loc = field_result_loc; fields[i].source_node = entry_node; } - return ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields); + ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } else if (kind == ContainerInitKindArray) { size_t item_count = container_init_expr->entries.length; IrInstruction **values = allocate(item_count); @@ -5254,7 +5266,8 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A values[i] = expr_value; } - return ir_build_container_init_list(irb, scope, node, container_type, item_count, values); + ir_build_container_init_list(irb, scope, node, container_type, item_count, values); + return ir_gen_result(irb, scope, node, lval, result_loc); } else { zig_unreachable(); } @@ -5433,7 +5446,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } else if (var_symbol != nullptr) { ir_set_cursor_at_end_and_append_block(irb, cond_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); @@ -5504,7 +5517,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } else { ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope, LValNone, nullptr); @@ -5563,7 +5576,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } } @@ -5907,7 +5920,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); - return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -5997,7 +6010,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); - return ir_gen_lval_ptr(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, @@ -6511,7 +6524,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode ir_mark_gen(ir_build_br(irb, err_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_lval_ptr(irb, parent_scope, node, lval, result_loc); + return ir_gen_result(irb, parent_scope, node, lval, result_loc); } static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) { @@ -7240,7 +7253,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval); case NodeTypeContainerInitExpr: - return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node, result_loc), lval); + return ir_gen_container_init_expr(irb, scope, node, lval, + ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeVariableDeclaration: return ir_lval_wrap(irb, scope, ir_gen_var_decl(irb, scope, node), lval); case NodeTypeWhileExpr: @@ -9821,7 +9835,7 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node fprintf(stderr, "\nSource: "); ast_render(codegen, stderr, node, 4); fprintf(stderr, "\n{ // (IR)\n"); - ir_print(codegen, stderr, ir_executable, 4); + ir_print(codegen, stderr, ir_executable, 2); fprintf(stderr, "}\n"); } IrExecutable *analyzed_executable = allocate(1); @@ -9841,7 +9855,7 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node if (codegen->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); - ir_print(codegen, stderr, analyzed_executable, 4); + ir_print(codegen, stderr, analyzed_executable, 2); fprintf(stderr, "}\n"); } @@ -9924,14 +9938,36 @@ static IrInstruction *ir_analyze_result_error_union_payload(IrAnalyze *ira, IrIn static IrInstruction *ir_analyze_result_error_union_code(IrAnalyze *ira, IrInstruction *result_loc, ZigType *needed_child_type) { - if (instr_is_comptime(result_loc)) { - zig_panic("TODO comptime ir_analyze_result_error_union_code"); - } ZigType *old_ptr_type = result_loc->value.type; assert(old_ptr_type->id == ZigTypeIdPointer); ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, false, old_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + if (instr_is_comptime(result_loc) && result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { + ConstExprValue *const_val = ir_resolve_const(ira, result_loc, UndefBad); + if (const_val == nullptr) + return ira->codegen->invalid_instruction; + ConstExprValue *error_union_val = const_ptr_pointee(ira->codegen, const_val); + assert(error_union_val->type->id == ZigTypeIdErrorUnion); + + if (error_union_val->special == ConstValSpecialUndef) { + ConstExprValue *err_set_val = create_const_vals(1); + err_set_val->type = error_union_val->type->data.error_union.err_set_type; + err_set_val->special = ConstValSpecialUndef; + + error_union_val->data.x_err_union.error_set = err_set_val; + error_union_val->special = ConstValSpecialStatic; + } + assert(error_union_val->data.x_err_union.error_set != nullptr); + + IrInstruction *result = ir_const(ira, result_loc, new_ptr_type); + ConstExprValue *result_val = &result->value; + result_val->data.x_ptr.special = ConstPtrSpecialRef; + result_val->data.x_ptr.mut = result_loc->value.data.x_ptr.mut; + result_val->data.x_ptr.data.ref.pointee = error_union_val->data.x_err_union.error_set; + return result; + } + IrInstruction *result = ir_build_result_error_union_code(&ira->new_irb, result_loc->scope, result_loc->source_node, result_loc); result->value.type = new_ptr_type; @@ -9986,11 +10022,16 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; + ConstExprValue *err_set_val = create_const_vals(1); + err_set_val->type = wanted_type->data.error_union.err_set_type; + err_set_val->special = ConstValSpecialStatic; + err_set_val->data.x_err_set = nullptr; + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.err = nullptr; + const_instruction->base.value.data.x_err_union.error_set = err_set_val; const_instruction->base.value.data.x_err_union.payload = val; return &const_instruction->base; } @@ -10054,11 +10095,16 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so if (!val) return ira->codegen->invalid_instruction; + ConstExprValue *err_set_val = create_const_vals(1); + err_set_val->special = ConstValSpecialStatic; + err_set_val->type = wanted_type->data.error_union.err_set_type; + err_set_val->data.x_err_set = val->data.x_err_set; + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.err = val->data.x_err_set; + const_instruction->base.value.data.x_err_union.error_set = err_set_val; const_instruction->base.value.data.x_err_union.payload = nullptr; return &const_instruction->base; } @@ -10509,7 +10555,7 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc ErrorTableEntry *err; if (err_type->id == ZigTypeIdErrorUnion) { - err = val->data.x_err_union.err; + err = val->data.x_err_union.error_set->data.x_err_set; } else if (err_type->id == ZigTypeIdErrorSet) { err = val->data.x_err_set; } else { @@ -11138,6 +11184,7 @@ static Error resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *al Error err; if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return err; + alloca->base.value.data.x_ptr.data.ref.pointee->type = child_type; alloca->base.value.type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, PtrLenSingle, alloca->align, 0, 0); return ErrorNone; @@ -13294,9 +13341,7 @@ static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) { return fn_entry->variable_list.at(next_var_i); } -static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - ZigVar *var) -{ +static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) { while (var->next_var != nullptr) { var = var->next_var; } @@ -13393,7 +13438,9 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_instruction; } - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar || + ptr->value.data.x_ptr.mut == ConstPtrMutInfer) + { if (instr_is_comptime(value)) { ConstExprValue *dest_val = ir_const_ptr_pointee(ira, &ptr->value, source_instr->source_node); if (dest_val == nullptr) @@ -13403,15 +13450,22 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; } + if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { + ptr->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } return ir_const_void(ira, source_instr); } } - ir_add_error(ira, source_instr, - buf_sprintf("cannot store runtime value in compile time variable")); - ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); - dest_val->type = ira->codegen->builtin_types.entry_invalid; + if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { + ptr->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; + } else { + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in compile time variable")); + ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); + dest_val->type = ira->codegen->builtin_types.entry_invalid; - return ira->codegen->invalid_instruction; + return ira->codegen->invalid_instruction; + } } } @@ -13583,10 +13637,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call if (inferred_err_set_type != nullptr) { inferred_err_set_type->data.error_set.infer_fn = nullptr; if (result->value.type->id == ZigTypeIdErrorUnion) { - if (result->value.data.x_err_union.err != nullptr) { + ErrorTableEntry *err = result->value.data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { inferred_err_set_type->data.error_set.err_count = 1; inferred_err_set_type->data.error_set.errors = allocate(1); - inferred_err_set_type->data.error_set.errors[0] = result->value.data.x_err_union.err; + inferred_err_set_type->data.error_set.errors[0] = err; } ZigType *fn_inferred_err_set_type = result->value.type->data.error_union.err_set_type; inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; @@ -14828,6 +14883,18 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ return ira->codegen->invalid_instruction; if (type_is_invalid(struct_val->type)) return ira->codegen->invalid_instruction; + + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer && + struct_val->special == ConstValSpecialUndef) + { + struct_val->data.x_struct.fields = create_const_vals(bare_type->data.structure.src_field_count); + struct_val->special = ConstValSpecialStatic; + for (size_t i = 0; i < bare_type->data.structure.src_field_count; i += 1) { + ConstExprValue *field_val = &struct_val->data.x_struct.fields[i]; + field_val->special = ConstValSpecialUndef; + field_val->type = bare_type->data.structure.fields[i].type_entry; + } + } ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index]; ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_val->type, is_const, is_volatile, PtrLenSingle, align_bytes, @@ -14836,7 +14903,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *result = ir_const(ira, source_instr, ptr_type); ConstExprValue *const_val = &result->value; const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; - const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; + const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; const_val->data.x_ptr.data.base_struct.struct_val = struct_val; const_val->data.x_ptr.data.base_struct.field_index = field->src_index; return result; @@ -16536,74 +16603,75 @@ static IrInstruction *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRe static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction, ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields) { - Error err; - assert(container_type->id == ZigTypeIdUnion); - - if ((err = ensure_complete_type(ira->codegen, container_type))) - return ira->codegen->invalid_instruction; - - if (instr_field_count != 1) { - ir_add_error(ira, instruction, - buf_sprintf("union initialization expects exactly one field")); - return ira->codegen->invalid_instruction; - } - - IrInstructionContainerInitFieldsField *field = &fields[0]; - IrInstruction *field_value = field->value->child; - if (type_is_invalid(field_value->value.type)) - return ira->codegen->invalid_instruction; - - TypeUnionField *type_field = find_union_type_field(container_type, field->name); - if (!type_field) { - ir_add_error_node(ira, field->source_node, - buf_sprintf("no member named '%s' in union '%s'", - buf_ptr(field->name), buf_ptr(&container_type->name))); - return ira->codegen->invalid_instruction; - } - - if (type_is_invalid(type_field->type_entry)) - return ira->codegen->invalid_instruction; - - IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry); - if (casted_field_value == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; - - if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_instruction; - - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); - if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || - !type_has_bits(casted_field_value->value.type)) - { - ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); - if (!field_val) - return ira->codegen->invalid_instruction; - - IrInstruction *result = ir_const(ira, instruction, container_type); - ConstExprValue *out_val = &result->value; - out_val->data.x_union.payload = field_val; - out_val->data.x_union.tag = type_field->enum_field->value; - - ConstParent *parent = get_const_val_parent(ira->codegen, field_val); - if (parent != nullptr) { - parent->id = ConstParentIdUnion; - parent->data.p_union.union_val = out_val; - } - - return result; - } - - IrInstruction *new_instruction = ir_build_union_init(&ira->new_irb, - instruction->scope, instruction->source_node, - container_type, type_field, casted_field_value); - new_instruction->value.type = container_type; - return new_instruction; + zig_panic("TODO"); + //Error err; + //assert(container_type->id == ZigTypeIdUnion); + + //if ((err = ensure_complete_type(ira->codegen, container_type))) + // return ira->codegen->invalid_instruction; + + //if (instr_field_count != 1) { + // ir_add_error(ira, instruction, + // buf_sprintf("union initialization expects exactly one field")); + // return ira->codegen->invalid_instruction; + //} + + //IrInstructionContainerInitFieldsField *field = &fields[0]; + //IrInstruction *field_value = field->value->child; + //if (type_is_invalid(field_value->value.type)) + // return ira->codegen->invalid_instruction; + + //TypeUnionField *type_field = find_union_type_field(container_type, field->name); + //if (!type_field) { + // ir_add_error_node(ira, field->source_node, + // buf_sprintf("no member named '%s' in union '%s'", + // buf_ptr(field->name), buf_ptr(&container_type->name))); + // return ira->codegen->invalid_instruction; + //} + + //if (type_is_invalid(type_field->type_entry)) + // return ira->codegen->invalid_instruction; + + //IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry); + //if (casted_field_value == ira->codegen->invalid_instruction) + // return ira->codegen->invalid_instruction; + + //if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown))) + // return ira->codegen->invalid_instruction; + + //bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); + //if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || + // !type_has_bits(casted_field_value->value.type)) + //{ + // ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); + // if (!field_val) + // return ira->codegen->invalid_instruction; + + // IrInstruction *result = ir_const(ira, instruction, container_type); + // ConstExprValue *out_val = &result->value; + // out_val->data.x_union.payload = field_val; + // out_val->data.x_union.tag = type_field->enum_field->value; + + // ConstParent *parent = get_const_val_parent(ira->codegen, field_val); + // if (parent != nullptr) { + // parent->id = ConstParentIdUnion; + // parent->data.p_union.union_val = out_val; + // } + + // return result; + //} + + //IrInstruction *new_instruction = ir_build_union_init(&ira->new_irb, + // instruction->scope, instruction->source_node, + // container_type, type_field, casted_field_value); + //new_instruction->value.type = container_type; + //return new_instruction; } static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction, - ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields) + ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields, + IrInstruction *result_loc) { - Error err; if (container_type->id == ZigTypeIdUnion) { return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields); } @@ -16614,28 +16682,26 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; } - if ((err = ensure_complete_type(ira->codegen, container_type))) - return ira->codegen->invalid_instruction; - size_t actual_field_count = container_type->data.structure.src_field_count; - - IrInstruction *first_non_const_instruction = nullptr; - AstNode **field_assign_nodes = allocate(actual_field_count); + ZigList const_ptrs = {}; + + // Here we iterate over the fields that have been initialized, and emit + // compile errors for missing fields and duplicate fields. + // It is only now that we find out whether the struct initialization can be a comptime + // value, but we have already emitted runtime instructions for the fields that + // were initialized with runtime values, and have omitted instructions that would have + // initialized fields with comptime values. + // So now we must clean up this situation. If it turns out the struct initialization can + // be a comptime value, overwrite ConstPtrMutInfer with ConstPtrMutComptimeConst. + // Otherwise, we must emit instructions to runtime-initialize the fields that have + // comptime-known values. - IrInstructionStructInitField *new_fields = allocate(actual_field_count); - - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); - - ConstExprValue const_val = {}; - const_val.special = ConstValSpecialStatic; - const_val.type = container_type; - const_val.data.x_struct.fields = create_const_vals(actual_field_count); for (size_t i = 0; i < instr_field_count; i += 1) { IrInstructionContainerInitFieldsField *field = &fields[i]; - IrInstruction *field_value = field->value->child; - if (type_is_invalid(field_value->value.type)) + IrInstruction *field_result_loc = field->result_loc->child; + if (type_is_invalid(field_result_loc->value.type)) return ira->codegen->invalid_instruction; TypeStructField *type_field = find_struct_type_field(container_type, field->name); @@ -16649,10 +16715,6 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (type_is_invalid(type_field->type_entry)) return ira->codegen->invalid_instruction; - IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry); - if (casted_field_value == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; - size_t field_index = type_field->src_index; AstNode *existing_assign_node = field_assign_nodes[field_index]; if (existing_assign_node) { @@ -16662,19 +16724,10 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc } field_assign_nodes[field_index] = field->source_node; - new_fields[field_index].value = casted_field_value; - new_fields[field_index].type_struct_field = type_field; - - if (const_val.special == ConstValSpecialStatic) { - if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) { - ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); - if (!field_val) - return ira->codegen->invalid_instruction; - - copy_const_val(&const_val.data.x_struct.fields[field_index], field_val, true); - } else { - first_non_const_instruction = casted_field_value; - const_val.special = ConstValSpecialRuntime; + if (instr_is_comptime(field_result_loc)) { + ConstExprValue *field_value = field_result_loc->value.data.x_ptr.data.ref.pointee; + if (field_value->special != ConstValSpecialRuntime) { + const_ptrs.append(field_result_loc); } } } @@ -16690,37 +16743,21 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (any_missing) return ira->codegen->invalid_instruction; - if (const_val.special == ConstValSpecialStatic) { - IrInstruction *result = ir_const(ira, instruction, nullptr); - ConstExprValue *out_val = &result->value; - // TODO copy_const_val? - *out_val = const_val; - result->value.type = container_type; - - for (size_t i = 0; i < instr_field_count; i += 1) { - ConstExprValue *field_val = &out_val->data.x_struct.fields[i]; - ConstParent *parent = get_const_val_parent(ira->codegen, field_val); - if (parent != nullptr) { - parent->id = ConstParentIdStruct; - parent->data.p_struct.field_index = i; - parent->data.p_struct.struct_val = out_val; + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (const_ptrs.length == actual_field_count) { + result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + result_loc->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; + for (size_t i = 0; i < const_ptrs.length; i += 1) { + IrInstruction *field_result_loc = const_ptrs.at(i); + IrInstruction *deref = ir_get_deref(ira, field_result_loc, field_result_loc); + field_result_loc->value.special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, field_result_loc, field_result_loc, deref); } } - - return result; - } - - if (is_comptime) { - ir_add_error_node(ira, first_non_const_instruction->source_node, - buf_sprintf("unable to evaluate constant expression")); - return ira->codegen->invalid_instruction; } - IrInstruction *new_instruction = ir_build_struct_init(&ira->new_irb, - instruction->scope, instruction->source_node, - container_type, actual_field_count, new_fields); - new_instruction->value.type = container_type; - return new_instruction; + return ir_const_void(ira, instruction); } static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, @@ -16738,7 +16775,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { return ir_analyze_container_init_fields(ira, &instruction->base, container_type, - 0, nullptr); + 0, nullptr, nullptr); } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { // array is same as slice init but we make a compile error if the length is wrong ZigType *child_type; @@ -16845,14 +16882,20 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, } } -static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { +static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, + IrInstructionContainerInitFields *instruction) +{ IrInstruction *container_type_value = instruction->container_type->child; ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + return ir_analyze_container_init_fields(ira, &instruction->base, container_type, - instruction->field_count, instruction->fields); + instruction->field_count, instruction->fields, result_loc); } static IrInstruction *ir_analyze_instruction_compile_err(IrAnalyze *ira, @@ -19556,7 +19599,8 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; if (err_union_val->special != ConstValSpecialRuntime) { - return ir_const_bool(ira, &instruction->base, (err_union_val->data.x_err_union.err != nullptr)); + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + return ir_const_bool(ira, &instruction->base, (err != nullptr)); } } @@ -19639,7 +19683,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, if (err_union_val == nullptr) return ira->codegen->invalid_instruction; if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; assert(err); IrInstruction *result = ir_const(ira, &instruction->base, @@ -19691,7 +19735,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, if (err_union_val == nullptr) return ira->codegen->invalid_instruction; if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; if (err != nullptr) { ir_add_error(ira, &instruction->base, buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); @@ -21324,13 +21368,21 @@ static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructio } } + ConstExprValue *pointee = create_const_vals(1); + pointee->special = ConstValSpecialUndef; + IrInstructionAllocaGen *result = ir_create_alloca_gen(&ira->new_irb, instruction->base.scope, instruction->base.source_node, align, instruction->name_hint); + result->base.value.special = ConstValSpecialStatic; + result->base.value.data.x_ptr.special = ConstPtrSpecialRef; + result->base.value.data.x_ptr.mut = ConstPtrMutInfer; + result->base.value.data.x_ptr.data.ref.pointee = pointee; if (instruction->child_type == nullptr) { // We use a pointer to a special opaque type to signal that we want type inference. result->base.value.type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_infer, false, false, PtrLenSingle, align, 0, 0); + result->base.value.data.x_ptr.data.ref.pointee->type = ira->codegen->builtin_types.entry_infer; } else { ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child); if (type_is_invalid(child_type)) @@ -21818,6 +21870,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdLoadResult: case IrInstructionIdStoreResult: case IrInstructionIdAssertNonError: + case IrInstructionIdContainerInitFields: + case IrInstructionIdContainerInitList: return true; case IrInstructionIdPhi: @@ -21825,8 +21879,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBinOp: case IrInstructionIdConst: case IrInstructionIdCast: - case IrInstructionIdContainerInitList: - case IrInstructionIdContainerInitFields: case IrInstructionIdStructInit: case IrInstructionIdUnionInit: case IrInstructionIdFieldPtr: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index f999b98b8d32..360af612c374 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -288,9 +288,10 @@ static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerI IrInstructionContainerInitFieldsField *field = &instruction->fields[i]; const char *comma = (i == 0) ? "" : ", "; fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field->name)); - ir_print_other_instruction(irp, field->value); + ir_print_other_instruction(irp, field->result_loc); } - fprintf(irp->f, "} // container init"); + fprintf(irp->f, "} // result="); + ir_print_other_instruction(irp, instruction->result_loc); } static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruction) { From e36d7372294f93540f3a000de8475779a8c6904d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 2 Nov 2018 21:01:01 -0400 Subject: [PATCH 029/190] copy elision: fix var decls and return result instructions --- src/all_types.hpp | 2 - src/ir.cpp | 106 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 90 insertions(+), 18 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 310a3292f581..420c5d5fba64 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -68,8 +68,6 @@ struct IrExecutable { Scope *begin_scope; ZigList tld_list; - IrInstruction *return_result_loc; - IrInstruction *coro_handle; IrInstruction *atomic_state_field_ptr; // this one is shared and in the promise IrInstruction *coro_result_ptr_field_ptr; diff --git a/src/ir.cpp b/src/ir.cpp index 9d3e7d0b4158..95eecf02d728 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -157,6 +157,8 @@ static Error ir_read_const_ptr(IrAnalyze *ira, AstNode *source_node, ConstExprValue *out_val, ConstExprValue *ptr_val); static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, ZigType *dest_type, IrInstruction *dest_type_src); +static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *uncasted_ptr, IrInstruction *uncasted_value); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -3232,10 +3234,14 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, { IrInstruction *return_value; if (expr_node) { + IrInstruction *return_result_loc = nullptr; + if (handle_is_ptr(fn_entry->type_entry->data.fn.fn_type_id.return_type)) { + return_result_loc = ir_build_result_return(irb, scope, node); + } // Temporarily set this so that if we return a type it gets the name of the function ZigFn *prev_name_fn = irb->exec->name_fn; irb->exec->name_fn = exec_fn_entry(irb->exec); - return_value = ir_gen_node(irb, expr_node, scope, LValNone, irb->exec->return_result_loc); + return_value = ir_gen_node(irb, expr_node, scope, LValNone, return_result_loc); irb->exec->name_fn = prev_name_fn; if (return_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -7386,10 +7392,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ZigFn *fn_entry = exec_fn_entry(irb->exec); - if (fn_entry != nullptr && handle_is_ptr(fn_entry->type_entry->data.fn.fn_type_id.return_type)) { - irb->exec->return_result_loc = ir_build_result_return(irb, scope, node); - } - bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; IrInstruction *coro_id; IrInstruction *u8_ptr_type; @@ -12817,6 +12819,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, } assert(var_ptr->value.type->id == ZigTypeIdPointer); + ZigType *result_type = var_ptr->value.type->data.pointer.child_type; if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; @@ -12826,7 +12829,21 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, } } + ConstExprValue *init_val = nullptr; + if (instr_is_comptime(var_ptr) && var_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { + init_val = const_ptr_pointee(ira->codegen, &var_ptr->value); + } + if (!type_is_invalid(result_type)) { + // Resolve ConstPtrMutInfer + if (instr_is_comptime(var_ptr)) { + if (var_ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (!is_comptime_var && !var->gen_is_const) { + var_ptr->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; + } + } + } + if (result_type->id == ZigTypeIdUnreachable || result_type->id == ZigTypeIdOpaque) { @@ -12841,6 +12858,20 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; } + } else if (init_val != nullptr) { + if (init_val->special == ConstValSpecialStatic && + init_val->type->id == ZigTypeIdFn && + init_val->data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) + { + var_class_requires_const = true; + if (!var->src_is_const && !is_comptime_var) { + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("functions marked inline must be stored in const or comptime var")); + AstNode *proto_node = init_val->data.x_ptr.data.fn.fn_entry->proto_node; + add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); + result_type = ira->codegen->builtin_types.entry_invalid; + } + } } } @@ -12885,9 +12916,14 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, } } - if (var->mem_slot_index != SIZE_MAX) { - if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { - return ir_const_void(ira, &decl_var_instruction->base); + if (init_val != nullptr && init_val->special != ConstValSpecialRuntime) { + if (var->mem_slot_index != SIZE_MAX) { + assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length); + ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index); + copy_const_val(mem_slot, init_val, !is_comptime_var || var->gen_is_const); + if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { + return ir_const_void(ira, &decl_var_instruction->base); + } } } else if (is_comptime_var) { ir_add_error(ira, &decl_var_instruction->base, @@ -12896,6 +12932,21 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, return ira->codegen->invalid_instruction; } + if (init_val != nullptr && init_val->special != ConstValSpecialRuntime) { + if (var->gen_is_const) { + var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else if (is_comptime_var) { + var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeVar; + } else { + // we need a runtime ptr but we have a comptime val. + // since it's a comptime val there are no instructions for it. + // we memcpy the init value here + IrInstruction *deref = ir_get_deref(ira, var_ptr, var_ptr); + var_ptr->value.special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref); + } + } + ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry) fn_entry->variable_list.append(var); @@ -14900,7 +14951,15 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ is_const, is_volatile, PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), (uint32_t)host_int_bytes_for_result_type); - IrInstruction *result = ir_const(ira, source_instr, ptr_type); + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, container_ptr, field); + result->value.type = ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, ptr_type); + } ConstExprValue *const_val = &result->value; const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; @@ -16724,11 +16783,10 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc } field_assign_nodes[field_index] = field->source_node; - if (instr_is_comptime(field_result_loc)) { - ConstExprValue *field_value = field_result_loc->value.data.x_ptr.data.ref.pointee; - if (field_value->special != ConstValSpecialRuntime) { - const_ptrs.append(field_result_loc); - } + if (instr_is_comptime(field_result_loc) && + field_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + const_ptrs.append(field_result_loc); } } @@ -16748,6 +16806,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; } else { result_loc->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; + result_loc->value.special = ConstValSpecialRuntime; for (size_t i = 0; i < const_ptrs.length; i += 1) { IrInstruction *field_result_loc = const_ptrs.at(i); IrInstruction *deref = ir_get_deref(ira, field_result_loc, field_result_loc); @@ -21344,9 +21403,24 @@ static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrIns ir_add_error(ira, &instruction->base, buf_sprintf("return outside function")); return ira->codegen->invalid_instruction; } - ZigType *result_type = get_pointer_to_type(ira->codegen, fn->type_entry->data.fn.fn_type_id.return_type, false); - IrInstruction *result = ir_build_result_return(&ira->new_irb, instruction->base.scope, instruction->base.source_node); + + // Here we create a pass2 instruction for getting the return value pointer. + // However we create a const value for it and mark it with ConstPtrMutInfer + // so that if the return expression is comptime-known, we can emit a memcpy + // rather than runtime instructions instructions. + + ConstExprValue *pointee = create_const_vals(1); + pointee->special = ConstValSpecialUndef; + pointee->type = fn->type_entry->data.fn.fn_type_id.return_type; + + ZigType *result_type = get_pointer_to_type(ira->codegen, pointee->type, false); + IrInstruction *result = ir_build_result_return(&ira->new_irb, instruction->base.scope, + instruction->base.source_node); result->value.type = result_type; + result->value.special = ConstValSpecialStatic; + result->value.data.x_ptr.special = ConstPtrSpecialRef; + result->value.data.x_ptr.data.ref.pointee = pointee; + result->value.data.x_ptr.mut = ConstPtrMutInfer; return result; } From 7d4d8b1f8b848870c71c17d133e9c12fdd072707 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Nov 2018 10:50:34 -0500 Subject: [PATCH 030/190] copy elision: implicit cast error set to error union --- src/all_types.hpp | 10 ++++ src/analyze.cpp | 14 ++++++ src/codegen.cpp | 34 ++++++++++++++ src/ir.cpp | 117 ++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 156 insertions(+), 19 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 420c5d5fba64..35deaf694203 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -90,6 +90,7 @@ enum OutType { enum ConstParentId { ConstParentIdNone, ConstParentIdStruct, + ConstParentIdErrUnionCode, ConstParentIdArray, ConstParentIdUnion, ConstParentIdScalar, @@ -107,6 +108,9 @@ struct ConstParent { ConstExprValue *struct_val; size_t field_index; } p_struct; + struct { + ConstExprValue *err_union_val; + } p_err_union_code; struct { ConstExprValue *union_val; } p_union; @@ -153,6 +157,8 @@ enum ConstPtrSpecial { ConstPtrSpecialBaseArray, // The pointer points to a field in an underlying struct. ConstPtrSpecialBaseStruct, + // The pointer points to the error set field of an error union + ConstPtrSpecialBaseErrorUnionCode, // This means that we did a compile-time pointer reinterpret and we cannot // understand the value of pointee at compile time. However, we will still // emit a binary with a compile time known address. @@ -202,6 +208,9 @@ struct ConstPtrValue { ConstExprValue *struct_val; size_t field_index; } base_struct; + struct { + ConstExprValue *err_union_val; + } base_err_union_code; struct { uint64_t addr; } hard_coded_addr; @@ -214,6 +223,7 @@ struct ConstPtrValue { struct ConstErrValue { ConstExprValue *error_set; ConstExprValue *payload; + ConstParent parent; }; struct ConstBoundFnValue { diff --git a/src/analyze.cpp b/src/analyze.cpp index b4a8e10f7809..aad631f6f2c4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4760,6 +4760,10 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index); return hash_val; + case ConstPtrSpecialBaseErrorUnionCode: + hash_val += (uint32_t)2994743799; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_code.err_union_val); + return hash_val; case ConstPtrSpecialHardCodedAddr: hash_val += (uint32_t)4048518294; hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); @@ -5570,6 +5574,15 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index) return false; return true; + case ConstPtrSpecialBaseErrorUnionCode: + if (a->data.x_ptr.data.base_err_union_code.err_union_val != + b->data.x_ptr.data.base_err_union_code.err_union_val && + a->data.x_ptr.data.base_err_union_code.err_union_val->global_refs != + b->data.x_ptr.data.base_err_union_code.err_union_val->global_refs) + { + return false; + } + return true; case ConstPtrSpecialHardCodedAddr: if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) return false; @@ -5752,6 +5765,7 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy zig_unreachable(); case ConstPtrSpecialRef: case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialBaseErrorUnionCode: buf_appendf(buf, "*"); render_const_value(g, buf, const_ptr_pointee(g, const_val)); return; diff --git a/src/codegen.cpp b/src/codegen.cpp index f27a46922f9b..eb28e92b6bb5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5455,6 +5455,7 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) { static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index); static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index); static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val); +static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val); static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { switch (parent->id) { @@ -5465,6 +5466,8 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent case ConstParentIdStruct: return gen_const_ptr_struct_recursive(g, parent->data.p_struct.struct_val, parent->data.p_struct.field_index); + case ConstParentIdErrUnionCode: + return gen_const_ptr_err_union_code_recursive(g, parent->data.p_err_union_code.err_union_val); case ConstParentIdArray: return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val, parent->data.p_array.elem_index); @@ -5516,6 +5519,18 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s return LLVMConstInBoundsGEP(base_ptr, indices, 2); } +static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { + ConstParent *parent = &err_union_const_val->data.x_err_union.parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, err_union_err_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) { ConstParent *parent = &union_const_val->data.x_union.parent; LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); @@ -5687,6 +5702,25 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con render_const_val_global(g, const_val, ""); return ptr_val; } + case ConstPtrSpecialBaseErrorUnionCode: + { + render_const_val_global(g, const_val, name); + ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val; + assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); + if (err_union_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_code_recursive(g, err_union_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } case ConstPtrSpecialHardCodedAddr: { render_const_val_global(g, const_val, name); diff --git a/src/ir.cpp b/src/ir.cpp index 95eecf02d728..1e3f121a8418 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -179,6 +179,9 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ const_val->data.x_ptr.data.base_struct.field_index]; break; + case ConstPtrSpecialBaseErrorUnionCode: + result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; + break; case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialDiscard: @@ -14763,6 +14766,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a const inner error union"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -14816,6 +14821,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a slice backed by const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a slice backed by const inner error union"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -18868,6 +18875,8 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memset on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memset on const inner error union"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -18983,6 +18992,8 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -19019,6 +19030,8 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -19186,6 +19199,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -19223,6 +19238,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -19295,6 +19312,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_unreachable(); case ConstPtrSpecialBaseStruct: zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO"); case ConstPtrSpecialHardCodedAddr: init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, parent_ptr->type->data.pointer.child_type, @@ -19710,7 +19729,44 @@ static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnaly ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); if (instr_is_comptime(ptr)) { - zig_panic("TODO need to change ConstExprValue x_err_union to have a ConstExprValue for error set value"); + ConstExprValue *ptr_val = ir_resolve_const(ira, ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ConstExprValue *err_union_val = const_ptr_pointee(ira->codegen, ptr_val); + if (err_union_val->data.x_ptr.mut == ConstPtrMutInfer && + err_union_val->special == ConstValSpecialUndef) + { + ConstExprValue *vals = create_const_vals(2); + ConstExprValue *err_set_val = &vals[0]; + ConstExprValue *payload_val = &vals[1]; + + err_set_val->special = ConstValSpecialUndef; + err_set_val->type = opt_err_set; + + payload_val->special = ConstValSpecialUndef; + payload_val->type = type_entry->data.error_union.payload_type; + + err_union_val->special = ConstValSpecialStatic; + err_union_val->data.x_err_union.error_set = err_set_val; + err_union_val->data.x_err_union.payload = payload_val; + } + + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_error_union_field_error_set(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, ptr); + result->value.type = result_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, &instruction->base, result_type); + } + ConstExprValue *const_val = &result->value; + const_val->data.x_ptr.special = ConstPtrSpecialBaseErrorUnionCode; + const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + const_val->data.x_ptr.data.base_err_union_code.err_union_val = err_union_val; + return result; + } } IrInstruction *result = ir_build_error_union_field_error_set(&ira->new_irb, @@ -19790,21 +19846,42 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = ir_const_ptr_pointee(ira, ptr_val, instruction->base.source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; - if (err != nullptr) { - ir_add_error(ira, &instruction->base, - buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ConstExprValue *err_union_val = ir_const_ptr_pointee(ira, ptr_val, instruction->base.source_node); + if (err_union_val == nullptr) return ira->codegen->invalid_instruction; + if (err_union_val->data.x_ptr.mut == ConstPtrMutInfer && + err_union_val->special == ConstValSpecialUndef) + { + ConstExprValue *vals = create_const_vals(2); + ConstExprValue *err_set_val = &vals[0]; + ConstExprValue *payload_val = &vals[1]; + + ZigType *opt_err_set = get_optional_type(ira->codegen, type_entry->data.error_union.err_set_type); + err_set_val->special = ConstValSpecialStatic; + err_set_val->type = opt_err_set; + err_set_val->data.x_err_set = nullptr; + + payload_val->special = ConstValSpecialUndef; + payload_val->type = payload_type; + + err_union_val->special = ConstValSpecialStatic; + err_union_val->data.x_err_union.error_set = err_set_val; + err_union_val->data.x_err_union.payload = payload_val; } + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { + ir_add_error(ira, &instruction->base, + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + return ira->codegen->invalid_instruction; + } - IrInstruction *result = ir_const(ira, &instruction->base, result_type); - result->value.data.x_ptr.special = ConstPtrSpecialRef; - result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; - return result; + IrInstruction *result = ir_const(ira, &instruction->base, result_type); + result->value.data.x_ptr.special = ConstPtrSpecialRef; + result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; + return result; + } } } @@ -19831,14 +19908,16 @@ static IrInstruction *ir_analyze_instruction_assert_non_error(IrAnalyze *ira, assert(err_code->value.type->data.maybe.child_type->id == ZigTypeIdErrorSet); if (instr_is_comptime(err_code)) { - ConstExprValue *opt_val = ir_resolve_const(ira, err_code, UndefBad); - if (opt_val == nullptr) - return ira->codegen->invalid_instruction; - ConstExprValue *err_val = opt_val->data.x_optional; + ConstExprValue *err_val = ir_resolve_const(ira, err_code, UndefBad); if (err_val == nullptr) - return ir_const_void(ira, &instruction->base); + return ira->codegen->invalid_instruction; + // just like optional pointers are represented with the same comptime + // value representation as normal pointers, + // optional error sets are represented with the same comptime value + // representation as normal error sets. ErrorTableEntry *err = err_val->data.x_err_set; - assert(err != nullptr); + if (err == nullptr) + return ir_const_void(ira, &instruction->base); ir_add_error(ira, &instruction->base, buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); return ira->codegen->invalid_instruction; From 9e9ec3b416d87fdf16c1e319626279e11480f6a1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Nov 2018 17:14:32 -0500 Subject: [PATCH 031/190] copy elision: implicit optional wrap ```zig export fn entry() void { var a: ?Bar = Bar.{.x = 1, .y = 2}; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %a = alloca { %Bar, i1 }, align 4 %0 = bitcast { %Bar, i1 }* %a to i8*, !dbg !57 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast ({ %Bar, i1 }* @0 to i8*), i64 12, i1 false), !dbg !57 call void @llvm.dbg.declare(metadata { %Bar, i1 }* %a, metadata !45, metadata !DIExpression()), !dbg !57 ret void, !dbg !58 } ``` --- src/all_types.hpp | 8 +++++ src/codegen.cpp | 1 + src/ir.cpp | 78 ++++++++++++++++++++++++++++++++++++++++++++--- src/ir_print.cpp | 11 +++++++ 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 35deaf694203..64887eed0a12 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2192,6 +2192,7 @@ enum IrInstructionId { IrInstructionIdResultSliceToBytes, IrInstructionIdResultParam, IrInstructionIdResultPtrCast, + IrInstructionIdResultCast, IrInstructionIdLoadResult, IrInstructionIdStoreResult, IrInstructionIdAllocaSrc, @@ -3362,6 +3363,13 @@ struct IrInstructionResultPtrCast { IrInstruction *prev_result_loc; }; +struct IrInstructionResultCast { + IrInstruction base; + + IrInstruction *elem_type; + IrInstruction *prev_result_loc; +}; + struct IrInstructionAllocaSrc { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index eb28e92b6bb5..904bc6bf5938 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5242,6 +5242,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdAllocaSrc: case IrInstructionIdAllocaGen: case IrInstructionIdFirstArgResultLoc: + case IrInstructionIdResultCast: zig_unreachable(); case IrInstructionIdDeclVarGen: diff --git a/src/ir.cpp b/src/ir.cpp index 1e3f121a8418..d0699aeb95e8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -896,6 +896,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionResultPtrCast *) return IrInstructionIdResultPtrCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultCast *) { + return IrInstructionIdResultCast; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSliceToBytes *) { return IrInstructionIdResultSliceToBytes; } @@ -2886,6 +2890,19 @@ static IrInstruction *ir_build_result_ptr_cast(IrBuilder *irb, Scope *scope, Ast return &instruction->base; } +static IrInstruction *ir_build_result_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *elem_type, IrInstruction *prev_result_loc) +{ + IrInstructionResultCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->elem_type = elem_type; + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(elem_type, irb->current_basic_block); + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_result_slice_to_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *elem_type, IrInstruction *prev_result_loc) { @@ -5240,6 +5257,8 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A return container_type; if (kind == ContainerInitKindStruct) { + IrInstruction *new_result_loc = ir_build_result_cast(irb, scope, node, container_type, result_loc); + size_t field_count = container_init_expr->entries.length; IrInstructionContainerInitFieldsField *fields = allocate(field_count); for (size_t i = 0; i < field_count; i += 1) { @@ -5247,7 +5266,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A assert(entry_node->type == NodeTypeStructValueField); Buf *name = entry_node->data.struct_val_field.name; - IrInstruction *field_result_loc = ir_build_field_ptr(irb, scope, entry_node, result_loc, + IrInstruction *field_result_loc = ir_build_field_ptr(irb, scope, entry_node, new_result_loc, container_type, name); AstNode *expr_node = entry_node->data.struct_val_field.expr; @@ -5259,7 +5278,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A fields[i].result_loc = field_result_loc; fields[i].source_node = entry_node; } - ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields, result_loc); + ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields, new_result_loc); return ir_gen_result(irb, scope, node, lval, result_loc); } else if (kind == ContainerInitKindArray) { size_t item_count = container_init_expr->entries.length; @@ -9909,14 +9928,44 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstruction *result_loc, ZigType *needed_child_type) { - if (instr_is_comptime(result_loc)) { - zig_panic("TODO comptime ir_analyze_result_optional_payload"); - } ZigType *old_ptr_type = result_loc->value.type; assert(old_ptr_type->id == ZigTypeIdPointer); ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, false, old_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + if (instr_is_comptime(result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, result_loc, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *optional_val = const_ptr_pointee(ira->codegen, ptr_val); + assert(optional_val->type->id == ZigTypeIdOptional); + if (optional_val->special == ConstValSpecialUndef) { + ConstExprValue *payload_val = create_const_vals(1); + payload_val->type = needed_child_type; + payload_val->special = ConstValSpecialUndef; + + optional_val->data.x_optional = payload_val; + optional_val->special = ConstValSpecialStatic; + } + assert(optional_val->data.x_optional != nullptr); + + IrInstruction *result = ir_const(ira, result_loc, new_ptr_type); + ConstExprValue *result_val = &result->value; + result_val->data.x_ptr.special = ConstPtrSpecialRef; + result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; + + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + ptr_val->data.x_ptr.mut = ConstPtrMutComptimeConst; + } + + return result; + } + } + IrInstruction *result = ir_build_result_optional_payload(&ira->new_irb, result_loc->scope, result_loc->source_node, result_loc); result->value.type = new_ptr_type; @@ -21511,6 +21560,22 @@ static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, IrI zig_panic("TODO"); } +static IrInstruction *ir_analyze_instruction_result_cast(IrAnalyze *ira, IrInstructionResultCast *instruction) { + ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, prev_result_loc, elem_type, false); + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + return new_result_loc; +} + static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAllocaSrc *instruction) { Error err; @@ -21882,6 +21947,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_result_param(ira, (IrInstructionResultParam *)instruction); case IrInstructionIdResultPtrCast: return ir_analyze_instruction_result_ptr_cast(ira, (IrInstructionResultPtrCast *)instruction); + case IrInstructionIdResultCast: + return ir_analyze_instruction_result_cast(ira, (IrInstructionResultCast *)instruction); case IrInstructionIdAllocaSrc: return ir_analyze_instruction_alloca(ira, (IrInstructionAllocaSrc *)instruction); case IrInstructionIdErrorUnionFieldErrorSet: @@ -22120,6 +22187,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdResultReturn: case IrInstructionIdResultParam: case IrInstructionIdResultPtrCast: + case IrInstructionIdResultCast: case IrInstructionIdResultSliceToBytes: case IrInstructionIdResultBytesToSlice: case IrInstructionIdAllocaSrc: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 360af612c374..fd6c2d3c26bb 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1365,6 +1365,14 @@ static void ir_print_result_ptr_cast(IrPrint *irp, IrInstructionResultPtrCast *i fprintf(irp->f, "ResultPtrCast"); } +static void ir_print_result_cast(IrPrint *irp, IrInstructionResultCast *instruction) { + fprintf(irp->f, "ResultCast(child_ty="); + ir_print_other_instruction(irp, instruction->elem_type); + fprintf(irp->f, ",prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); +} + static void ir_print_load_result(IrPrint *irp, IrInstructionLoadResult *instruction) { fprintf(irp->f, "LoadResult"); } @@ -1855,6 +1863,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdResultPtrCast: ir_print_result_ptr_cast(irp, (IrInstructionResultPtrCast *)instruction); break; + case IrInstructionIdResultCast: + ir_print_result_cast(irp, (IrInstructionResultCast *)instruction); + break; case IrInstructionIdLoadResult: ir_print_load_result(irp, (IrInstructionLoadResult *)instruction); break; From 7b86ff5798947e0fe2f1f19cf5209886baa7bd94 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Nov 2018 20:43:05 -0500 Subject: [PATCH 032/190] const expr supports parent pointers for error union payloads --- src/all_types.hpp | 9 +++++ src/analyze.cpp | 16 ++++++++ src/codegen.cpp | 34 +++++++++++++++++ src/ir.cpp | 93 +++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 141 insertions(+), 11 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 64887eed0a12..a958111ba3f4 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -91,6 +91,7 @@ enum ConstParentId { ConstParentIdNone, ConstParentIdStruct, ConstParentIdErrUnionCode, + ConstParentIdErrUnionPayload, ConstParentIdArray, ConstParentIdUnion, ConstParentIdScalar, @@ -111,6 +112,9 @@ struct ConstParent { struct { ConstExprValue *err_union_val; } p_err_union_code; + struct { + ConstExprValue *err_union_val; + } p_err_union_payload; struct { ConstExprValue *union_val; } p_union; @@ -159,6 +163,8 @@ enum ConstPtrSpecial { ConstPtrSpecialBaseStruct, // The pointer points to the error set field of an error union ConstPtrSpecialBaseErrorUnionCode, + // The pointer points to the payload field of an error union + ConstPtrSpecialBaseErrorUnionPayload, // This means that we did a compile-time pointer reinterpret and we cannot // understand the value of pointee at compile time. However, we will still // emit a binary with a compile time known address. @@ -211,6 +217,9 @@ struct ConstPtrValue { struct { ConstExprValue *err_union_val; } base_err_union_code; + struct { + ConstExprValue *err_union_val; + } base_err_union_payload; struct { uint64_t addr; } hard_coded_addr; diff --git a/src/analyze.cpp b/src/analyze.cpp index aad631f6f2c4..2ad058904d1c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4764,6 +4764,10 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += (uint32_t)2994743799; hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_code.err_union_val); return hash_val; + case ConstPtrSpecialBaseErrorUnionPayload: + hash_val += (uint32_t)3456080131; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_payload.err_union_val); + return hash_val; case ConstPtrSpecialHardCodedAddr: hash_val += (uint32_t)4048518294; hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); @@ -5583,6 +5587,15 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { return false; } return true; + case ConstPtrSpecialBaseErrorUnionPayload: + if (a->data.x_ptr.data.base_err_union_payload.err_union_val != + b->data.x_ptr.data.base_err_union_payload.err_union_val && + a->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs != + b->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs) + { + return false; + } + return true; case ConstPtrSpecialHardCodedAddr: if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) return false; @@ -5766,6 +5779,7 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy case ConstPtrSpecialRef: case ConstPtrSpecialBaseStruct: case ConstPtrSpecialBaseErrorUnionCode: + case ConstPtrSpecialBaseErrorUnionPayload: buf_appendf(buf, "*"); render_const_value(g, buf, const_ptr_pointee(g, const_val)); return; @@ -6218,6 +6232,8 @@ ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) { return &value->data.x_struct.parent; } else if (type_entry->id == ZigTypeIdUnion) { return &value->data.x_union.parent; + } else if (type_entry->id == ZigTypeIdErrorUnion) { + return &value->data.x_err_union.parent; } return nullptr; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 904bc6bf5938..266b65890f89 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5457,6 +5457,7 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index); static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val); static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val); +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val); static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { switch (parent->id) { @@ -5469,6 +5470,8 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent parent->data.p_struct.field_index); case ConstParentIdErrUnionCode: return gen_const_ptr_err_union_code_recursive(g, parent->data.p_err_union_code.err_union_val); + case ConstParentIdErrUnionPayload: + return gen_const_ptr_err_union_payload_recursive(g, parent->data.p_err_union_payload.err_union_val); case ConstParentIdArray: return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val, parent->data.p_array.elem_index); @@ -5532,6 +5535,18 @@ static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExpr return LLVMConstInBoundsGEP(base_ptr, indices, 2); } +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { + ConstParent *parent = &err_union_const_val->data.x_err_union.parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, err_union_payload_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) { ConstParent *parent = &union_const_val->data.x_union.parent; LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); @@ -5722,6 +5737,25 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con render_const_val_global(g, const_val, ""); return ptr_val; } + case ConstPtrSpecialBaseErrorUnionPayload: + { + render_const_val_global(g, const_val, name); + ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val; + assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); + if (err_union_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_payload_recursive(g, err_union_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } case ConstPtrSpecialHardCodedAddr: { render_const_val_global(g, const_val, name); diff --git a/src/ir.cpp b/src/ir.cpp index d0699aeb95e8..2a94acd5d79f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -182,6 +182,9 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c case ConstPtrSpecialBaseErrorUnionCode: result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; break; + case ConstPtrSpecialBaseErrorUnionPayload: + result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload; + break; case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialDiscard: @@ -9975,14 +9978,65 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_result_error_union_payload(IrAnalyze *ira, IrInstruction *result_loc, ZigType *needed_child_type) { - if (instr_is_comptime(result_loc)) { - zig_panic("TODO comptime ir_analyze_result_error_union_payload"); - } ZigType *old_ptr_type = result_loc->value.type; assert(old_ptr_type->id == ZigTypeIdPointer); ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, false, old_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + if (instr_is_comptime(result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, result_loc, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *error_union_val = const_ptr_pointee(ira->codegen, ptr_val); + assert(error_union_val->type->id == ZigTypeIdErrorUnion); + + if (error_union_val->special == ConstValSpecialUndef) { + ConstExprValue *vals = create_const_vals(2); + ConstExprValue *err_set_val = &vals[0]; + ConstExprValue *payload_val = &vals[1]; + + err_set_val->type = get_optional_type(ira->codegen, + error_union_val->type->data.error_union.err_set_type); + err_set_val->special = ConstValSpecialStatic; + err_set_val->data.x_err_set = nullptr; + + payload_val->type = error_union_val->type->data.error_union.payload_type; + payload_val->special = ConstValSpecialUndef; + ConstParent *payload_parent = get_const_val_parent(ira->codegen, payload_val); + if (payload_parent != nullptr) { + payload_parent->id = ConstParentIdErrUnionPayload; + payload_parent->data.p_err_union_payload.err_union_val = error_union_val; + } + + error_union_val->data.x_err_union.error_set = err_set_val; + error_union_val->data.x_err_union.payload = payload_val; + error_union_val->special = ConstValSpecialStatic; + ConstParent *err_set_parent = get_const_val_parent(ira->codegen, err_set_val); + if (err_set_parent != nullptr) { + err_set_parent->id = ConstParentIdErrUnionCode; + err_set_parent->data.p_err_union_code.err_union_val = error_union_val; + } + } + + assert(error_union_val->data.x_err_union.payload != nullptr); + + IrInstruction *result = ir_const(ira, result_loc, new_ptr_type); + ConstExprValue *result_val = &result->value; + result_val->data.x_ptr.special = ConstPtrSpecialRef; + result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + result_val->data.x_ptr.data.ref.pointee = error_union_val->data.x_err_union.payload; + + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + ptr_val->data.x_ptr.mut = ConstPtrMutComptimeConst; + } + + return result; + } + } + IrInstruction *result = ir_build_result_error_union_payload(&ira->new_irb, result_loc->scope, result_loc->source_node, result_loc); result->value.type = new_ptr_type; @@ -10006,7 +10060,8 @@ static IrInstruction *ir_analyze_result_error_union_code(IrAnalyze *ira, IrInstr if (error_union_val->special == ConstValSpecialUndef) { ConstExprValue *err_set_val = create_const_vals(1); - err_set_val->type = error_union_val->type->data.error_union.err_set_type; + err_set_val->type = get_optional_type(ira->codegen, + error_union_val->type->data.error_union.err_set_type); err_set_val->special = ConstValSpecialUndef; error_union_val->data.x_err_union.error_set = err_set_val; @@ -14816,7 +14871,9 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO elem ptr on a const inner error union"); + zig_panic("TODO elem ptr on a const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a const inner error union payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -14871,7 +14928,9 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a slice backed by const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO elem ptr on a slice backed by const inner error union"); + zig_panic("TODO elem ptr on a slice backed by const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a slice backed by const inner error union payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -18925,7 +18984,9 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio case ConstPtrSpecialBaseStruct: zig_panic("TODO memset on const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO memset on const inner error union"); + zig_panic("TODO memset on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memset on const inner error union payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -19042,7 +19103,9 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO memcpy on const inner error union"); + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -19080,7 +19143,9 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO memcpy on const inner error union"); + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -19249,7 +19314,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO slice const inner error union"); + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -19288,7 +19355,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO slice const inner error union"); + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -19363,6 +19432,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_panic("TODO"); case ConstPtrSpecialBaseErrorUnionCode: zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO"); case ConstPtrSpecialHardCodedAddr: init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, parent_ptr->type->data.pointer.child_type, From c136bfff05d4582c8c3efdab9844ae096276aecd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Nov 2018 21:52:44 -0500 Subject: [PATCH 033/190] copy elision: handle store ptr with runtime value better ```zig export fn entry() void { var a: error!Bar = Bar.{.x = 1, .y = 2}; var b = a catch unreachable; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %a = alloca { i16, %Bar }, align 4 %b = alloca %Bar, align 4 %0 = bitcast { i16, %Bar }* %a to i8*, !dbg !58 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast ({ i16, %Bar }* @0 to i8*), i64 12, i1 false), !dbg !58 call void @llvm.dbg.declare(metadata { i16, %Bar }* %a, metadata !45, metadata !DIExpression()), !dbg !58 %1 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %a, i32 0, i32 1, !dbg !59 %2 = bitcast %Bar* %1 to i8*, !dbg !59 %3 = bitcast %Bar* %b to i8*, !dbg !59 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %3, i8* align 4 %2, i64 8, i1 false), !dbg !59 %4 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %a, i32 0, i32 0, !dbg !59 %5 = load i16, i16* %4, align 2, !dbg !60 call void @llvm.dbg.declare(metadata %Bar* %b, metadata !56, metadata !DIExpression()), !dbg !61 ret void, !dbg !62 } ``` --- src/ir.cpp | 44 +++++++++++++++++++++++++------------------- src/ir_print.cpp | 4 ++-- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 2a94acd5d79f..53fe58ef0e25 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13023,22 +13023,6 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, } } - if (init_val != nullptr && init_val->special != ConstValSpecialRuntime) { - if (var->mem_slot_index != SIZE_MAX) { - assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length); - ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index); - copy_const_val(mem_slot, init_val, !is_comptime_var || var->gen_is_const); - if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { - return ir_const_void(ira, &decl_var_instruction->base); - } - } - } else if (is_comptime_var) { - ir_add_error(ira, &decl_var_instruction->base, - buf_sprintf("cannot store runtime value in compile time variable")); - var->value->type = ira->codegen->builtin_types.entry_invalid; - return ira->codegen->invalid_instruction; - } - if (init_val != nullptr && init_val->special != ConstValSpecialRuntime) { if (var->gen_is_const) { var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeConst; @@ -13052,6 +13036,19 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, var_ptr->value.special = ConstValSpecialRuntime; ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref); } + if (var_ptr->value.special == ConstValSpecialStatic && var->mem_slot_index != SIZE_MAX) { + assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length); + ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index); + copy_const_val(mem_slot, init_val, !is_comptime_var || var->gen_is_const); + if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { + return ir_const_void(ira, &decl_var_instruction->base); + } + } + } else if (is_comptime_var) { + ir_add_error(ira, &decl_var_instruction->base, + buf_sprintf("cannot store runtime value in compile time variable")); + var->value->type = ira->codegen->builtin_types.entry_invalid; + return ira->codegen->invalid_instruction; } ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); @@ -13615,7 +13612,7 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source } } if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { - ptr->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; + ptr->value.special = ConstValSpecialRuntime; } else { ir_add_error(ira, source_instr, buf_sprintf("cannot store runtime value in compile time variable")); @@ -19970,7 +19967,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, ConstExprValue *err_union_val = ir_const_ptr_pointee(ira, ptr_val, instruction->base.source_node); if (err_union_val == nullptr) return ira->codegen->invalid_instruction; - if (err_union_val->data.x_ptr.mut == ConstPtrMutInfer && + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer && err_union_val->special == ConstValSpecialUndef) { ConstExprValue *vals = create_const_vals(2); @@ -19997,9 +19994,18 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_const(ira, &instruction->base, result_type); + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_unwrap_err_payload(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, value, instruction->safety_check_on); + result->value.type = result_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, &instruction->base, result_type); + } result->value.data.x_ptr.special = ConstPtrSpecialRef; result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; + result->value.data.x_ptr.mut = ptr_val->data.x_ptr.mut; return result; } } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index fd6c2d3c26bb..aeacce36301b 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -818,13 +818,13 @@ static void ir_print_test_err(IrPrint *irp, IrInstructionTestErr *instruction) { } static void ir_print_unwrap_err_code(IrPrint *irp, IrInstructionUnwrapErrCode *instruction) { - fprintf(irp->f, "@unwrapErrorCode("); + fprintf(irp->f, "UnwrapErrorCode("); ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, ")"); } static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayload *instruction) { - fprintf(irp->f, "@unwrapErrorPayload("); + fprintf(irp->f, "ErrorUnionFieldPayload("); ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, ")"); if (!instruction->safety_check_on) { From c17c46ca4373001469972fb6fa29403df64c79d9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 6 Nov 2018 16:08:01 -0500 Subject: [PATCH 034/190] copy elision: implicit cast fn call to error union ```zig export fn entry() void { var x: error!i32 = fail(); } ``` ```llvm define void @entry() #2 !dbg !42 { Entry: %x = alloca { i16, i32 }, align 4 %0 = getelementptr inbounds { i16, i32 }, { i16, i32 }* %x, i32 0, i32 0, !dbg !53 %1 = call fastcc i16 @fail(), !dbg !54 store i16 %1, i16* %0, align 2, !dbg !54 ret void, !dbg !55 } ``` --- src/codegen.cpp | 6 +- src/ir.cpp | 142 +++++++++++++++++++++++++++++------------------- 2 files changed, 91 insertions(+), 57 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 266b65890f89..bc1375028364 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5445,8 +5445,10 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) { LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block); for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { IrInstruction *instruction = current_block->instruction_list.at(instr_i); - if (instruction->ref_count == 0 && !ir_has_side_effects(instruction)) - continue; + if (!ir_has_side_effects(instruction)) { + if (instruction->ref_count == 0 || instruction->value.special != ConstValSpecialRuntime) + continue; + } instruction->llvm_value = ir_render_instruction(g, executable, instruction); } current_block->llvm_exit_block = LLVMGetInsertBlock(g->builder); diff --git a/src/ir.cpp b/src/ir.cpp index 53fe58ef0e25..c82059f97152 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9975,6 +9975,36 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr return result; } +static void init_if_undef_err_union(IrAnalyze *ira, ConstExprValue *error_union_val) { + if (error_union_val->special != ConstValSpecialUndef) + return; + + ConstExprValue *vals = create_const_vals(2); + ConstExprValue *err_set_val = &vals[0]; + ConstExprValue *payload_val = &vals[1]; + + err_set_val->type = get_optional_type(ira->codegen, + error_union_val->type->data.error_union.err_set_type); + err_set_val->special = ConstValSpecialUndef; + ConstParent *err_set_parent = get_const_val_parent(ira->codegen, err_set_val); + if (err_set_parent != nullptr) { + err_set_parent->id = ConstParentIdErrUnionCode; + err_set_parent->data.p_err_union_code.err_union_val = error_union_val; + } + + payload_val->type = error_union_val->type->data.error_union.payload_type; + payload_val->special = ConstValSpecialUndef; + ConstParent *payload_parent = get_const_val_parent(ira->codegen, payload_val); + if (payload_parent != nullptr) { + payload_parent->id = ConstParentIdErrUnionPayload; + payload_parent->data.p_err_union_payload.err_union_val = error_union_val; + } + + error_union_val->data.x_err_union.error_set = err_set_val; + error_union_val->data.x_err_union.payload = payload_val; + error_union_val->special = ConstValSpecialStatic; +} + static IrInstruction *ir_analyze_result_error_union_payload(IrAnalyze *ira, IrInstruction *result_loc, ZigType *needed_child_type) { @@ -9993,37 +10023,18 @@ static IrInstruction *ir_analyze_result_error_union_payload(IrAnalyze *ira, IrIn ConstExprValue *error_union_val = const_ptr_pointee(ira->codegen, ptr_val); assert(error_union_val->type->id == ZigTypeIdErrorUnion); - if (error_union_val->special == ConstValSpecialUndef) { - ConstExprValue *vals = create_const_vals(2); - ConstExprValue *err_set_val = &vals[0]; - ConstExprValue *payload_val = &vals[1]; - - err_set_val->type = get_optional_type(ira->codegen, - error_union_val->type->data.error_union.err_set_type); - err_set_val->special = ConstValSpecialStatic; - err_set_val->data.x_err_set = nullptr; - - payload_val->type = error_union_val->type->data.error_union.payload_type; - payload_val->special = ConstValSpecialUndef; - ConstParent *payload_parent = get_const_val_parent(ira->codegen, payload_val); - if (payload_parent != nullptr) { - payload_parent->id = ConstParentIdErrUnionPayload; - payload_parent->data.p_err_union_payload.err_union_val = error_union_val; - } - - error_union_val->data.x_err_union.error_set = err_set_val; - error_union_val->data.x_err_union.payload = payload_val; - error_union_val->special = ConstValSpecialStatic; - ConstParent *err_set_parent = get_const_val_parent(ira->codegen, err_set_val); - if (err_set_parent != nullptr) { - err_set_parent->id = ConstParentIdErrUnionCode; - err_set_parent->data.p_err_union_code.err_union_val = error_union_val; - } - } - + init_if_undef_err_union(ira, error_union_val); assert(error_union_val->data.x_err_union.payload != nullptr); - IrInstruction *result = ir_const(ira, result_loc, new_ptr_type); + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_result_error_union_payload(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc); + result->value.type = new_ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, result_loc, new_ptr_type); + } ConstExprValue *result_val = &result->value; result_val->data.x_ptr.special = ConstPtrSpecialRef; result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; @@ -10051,30 +10062,39 @@ static IrInstruction *ir_analyze_result_error_union_code(IrAnalyze *ira, IrInstr ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, false, old_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); - if (instr_is_comptime(result_loc) && result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ConstExprValue *const_val = ir_resolve_const(ira, result_loc, UndefBad); - if (const_val == nullptr) + if (instr_is_comptime(result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, result_loc, UndefBad); + if (ptr_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *error_union_val = const_ptr_pointee(ira->codegen, const_val); - assert(error_union_val->type->id == ZigTypeIdErrorUnion); + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *error_union_val = const_ptr_pointee(ira->codegen, ptr_val); + assert(error_union_val->type->id == ZigTypeIdErrorUnion); - if (error_union_val->special == ConstValSpecialUndef) { - ConstExprValue *err_set_val = create_const_vals(1); - err_set_val->type = get_optional_type(ira->codegen, - error_union_val->type->data.error_union.err_set_type); - err_set_val->special = ConstValSpecialUndef; + init_if_undef_err_union(ira, error_union_val); + assert(error_union_val->data.x_err_union.error_set != nullptr); - error_union_val->data.x_err_union.error_set = err_set_val; - error_union_val->special = ConstValSpecialStatic; - } - assert(error_union_val->data.x_err_union.error_set != nullptr); + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_result_error_union_code(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc); + result->value.type = new_ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, result_loc, new_ptr_type); + } + ConstExprValue *result_val = &result->value; + result_val->data.x_ptr.special = ConstPtrSpecialRef; + result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + result_val->data.x_ptr.data.ref.pointee = error_union_val->data.x_err_union.error_set; - IrInstruction *result = ir_const(ira, result_loc, new_ptr_type); - ConstExprValue *result_val = &result->value; - result_val->data.x_ptr.special = ConstPtrSpecialRef; - result_val->data.x_ptr.mut = result_loc->value.data.x_ptr.mut; - result_val->data.x_ptr.data.ref.pointee = error_union_val->data.x_err_union.error_set; - return result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + ptr_val->data.x_ptr.mut = ConstPtrMutComptimeConst; + } + + return result; + } } IrInstruction *result = ir_build_result_error_union_code(&ira->new_irb, result_loc->scope, @@ -14174,22 +14194,34 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; } - IrInstruction *result_loc = nullptr; + IrInstruction *casted_result_loc = nullptr; if (call_instruction->result_loc != nullptr) { - result_loc = ir_implicit_cast_result(ira, call_instruction->result_loc->child, return_type, false); - if (type_is_invalid(result_loc->value.type)) + IrInstruction *prev_result_loc = call_instruction->result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + if (instr_is_comptime(prev_result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, prev_result_loc, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + ptr_val->special = ConstValSpecialRuntime; + } + } + + casted_result_loc = ir_implicit_cast_result(ira, prev_result_loc, return_type, false); + if (type_is_invalid(casted_result_loc->value.type)) return ira->codegen->invalid_instruction; } IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, - casted_new_stack, result_loc); + casted_new_stack, casted_result_loc); new_call_instruction->value.type = return_type; - if (!handle_is_ptr(return_type) && result_loc != nullptr) { - ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, new_call_instruction); + if (!handle_is_ptr(return_type) && casted_result_loc != nullptr) { + ir_analyze_store_ptr(ira, &call_instruction->base, casted_result_loc, new_call_instruction); } return ir_finish_anal(ira, new_call_instruction); From b2222228541523c7d8e740d83070aa1c88c5c25c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 7 Nov 2018 10:39:13 -0500 Subject: [PATCH 035/190] copy elision: fix unwrap maybe and while ```zig export fn entry() void { const c = false; const x: error!i32 = error.Failure; var z = while (x) |a| { if (c) break a; } else |_| i32(3); } ``` --- src/ir.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index c82059f97152..4cf799298ce5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5464,7 +5464,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n // TODO make it an error to write to error variable AstNode *err_symbol_node = else_node; // TODO make more accurate ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol, - true, false, false, is_comptime); + true, true, false, is_comptime); Scope *err_scope = err_var->child_scope; IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, err_scope, err_symbol_node, ptr_opt_err_code, false); @@ -16245,7 +16245,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, ConstExprValue *out_val = &result->value; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = val->data.x_ptr.mut; - if (type_is_codegen_pointer(child_type)) { + if (types_have_same_zig_comptime_repr(type_entry, child_type)) { out_val->data.x_ptr.data.ref.pointee = maybe_val; } else { out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional; From 84f48a69c4060bf673d3ec62cd33d800654c37d7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 7 Nov 2018 13:40:27 -0500 Subject: [PATCH 036/190] copy elision: for with aggregates ```zig export fn entry() void { var array: [10]Bar = undefined; var x = for (array) |elem, i| { if (i == 1) break elem; } else bar2(); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %array = alloca [10 x %Bar], align 4 %x = alloca %Bar, align 4 %for_index = alloca i64, align 8 %0 = bitcast [10 x %Bar]* %array to i8*, !dbg !60 call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 -86, i64 80, i1 false), !dbg !60 call void @llvm.dbg.declare(metadata [10 x %Bar]* %array, metadata !45, metadata !DIExpression()), !dbg !60 store i64 0, i64* %for_index, align 8, !dbg !61 call void @llvm.dbg.declare(metadata i64* %for_index, metadata !55, metadata !DIExpression()), !dbg !63 br label %ForCond, !dbg !61 ForCond: ; preds = %Else, %Entry %1 = load i64, i64* %for_index, align 8, !dbg !61 %2 = icmp ne i64 %1, 10, !dbg !61 br i1 %2, label %ForBody, label %ForElse, !dbg !61 ForBody: ; preds = %ForCond %3 = getelementptr inbounds [10 x %Bar], [10 x %Bar]* %array, i64 0, i64 %1, !dbg !61 call void @llvm.dbg.declare(metadata %Bar* %3, metadata !58, metadata !DIExpression()), !dbg !64 %4 = load i64, i64* %for_index, align 8, !dbg !65 %5 = icmp eq i64 %4, 1, !dbg !67 br i1 %5, label %Then, label %Else, !dbg !67 Then: ; preds = %ForBody %6 = bitcast %Bar* %3 to i8*, !dbg !68 %7 = bitcast %Bar* %x to i8*, !dbg !68 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %7, i8* align 4 %6, i64 8, i1 false), !dbg !68 br label %ForEnd, !dbg !69 Else: ; preds = %ForBody %8 = add nuw i64 %1, 1, !dbg !61 store i64 %8, i64* %for_index, align 8, !dbg !61 br label %ForCond, !dbg !61 ForElse: ; preds = %ForCond call fastcc void @bar2(%Bar* sret %x), !dbg !70 br label %ForEnd, !dbg !71 ForEnd: ; preds = %ForElse, %Then call void @llvm.dbg.declare(metadata %Bar* %x, metadata !59, metadata !DIExpression()), !dbg !72 ret void, !dbg !73 } ``` --- src/codegen.cpp | 14 ++++++++++++++ src/ir.cpp | 44 ++++++++++++++------------------------------ 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index bc1375028364..78e137f5d552 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3236,6 +3236,20 @@ static bool value_is_all_undef(ConstExprValue *const_val) { return false; } return true; + } else if (const_val->type->id == ZigTypeIdArray) { + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + return true; + case ConstArraySpecialBuf: + return false; + case ConstArraySpecialNone: + for (size_t i = 0; i < const_val->type->data.array.len; i += 1) { + if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i])) + return false; + } + return true; + } + zig_unreachable(); } else { return false; } diff --git a/src/ir.cpp b/src/ir.cpp index 4cf799298ce5..bd6ce25309d3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5611,7 +5611,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } } -static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node, +static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval, IrInstruction *result_loc) { assert(node->type == NodeTypeForExpr); @@ -5650,9 +5650,6 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime); Scope *child_scope = elem_var->child_scope; - IrInstruction *elem_alloca = ir_build_alloca_src(irb, child_scope, elem_node, elem_var_type, nullptr, "for_elem"); - ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, elem_alloca); - AstNode *index_var_source_node; ZigVar *index_var; if (index_node) { @@ -5676,7 +5673,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrBasicBlock *cond_block = ir_create_basic_block(irb, child_scope, "ForCond"); IrBasicBlock *body_block = ir_create_basic_block(irb, child_scope, "ForBody"); IrBasicBlock *end_block = ir_create_basic_block(irb, child_scope, "ForEnd"); - IrBasicBlock *else_block = else_node ? ir_create_basic_block(irb, child_scope, "ForElse") : end_block; + IrBasicBlock *else_block = ir_create_basic_block(irb, child_scope, "ForElse"); IrBasicBlock *continue_block = ir_create_basic_block(irb, child_scope, "ForContinue"); IrInstruction *len_val = ir_build_array_len(irb, child_scope, node, array_val); @@ -5684,20 +5681,14 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca, nullptr); - IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); - IrBasicBlock *after_cond_block = irb->current_basic_block; - IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); + IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpNotEq, index_val, len_val, false); ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false, PtrLenSingle); - IrInstruction *elem_val; - if (node->data.for_expr.elem_is_ptr) { - elem_val = elem_ptr; - } else { - elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr, nullptr); - } - ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_alloca, elem_val)); + IrInstruction *elem_var_ptr = node->data.for_expr.elem_is_ptr ? + ir_build_ref(irb, child_scope, elem_node, elem_ptr, true, false) : elem_ptr; + ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, elem_var_ptr); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5719,28 +5710,21 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_alloca, new_index_val)); ir_build_br(irb, child_scope, node, cond_block, is_comptime); + ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_result = nullptr; if (else_node) { - ir_set_cursor_at_end_and_append_block(irb, else_block); - else_result = ir_gen_node(irb, else_node, parent_scope, LValNone, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); - } - IrBasicBlock *after_else_block = irb->current_basic_block; - ir_set_cursor_at_end_and_append_block(irb, end_block); - - if (else_result) { - incoming_blocks.append(after_else_block); - incoming_values.append(else_result); } else { - incoming_blocks.append(after_cond_block); - incoming_values.append(void_else_value); + else_result = ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); + ir_mark_gen(ir_build_store_ptr(irb, parent_scope, node, result_loc, else_result)); } + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); - return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); + ir_set_cursor_at_end_and_append_block(irb, end_block); + return ir_gen_result(irb, parent_scope, node, lval, result_loc); } static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -7292,7 +7276,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_while_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeForExpr: - return ir_lval_wrap(irb, scope, ir_gen_for_expr(irb, scope, node, result_loc), lval); + return ir_gen_for_expr(irb, scope, node, lval, result_loc); case NodeTypeArrayAccessExpr: return ir_gen_array_access(irb, scope, node, lval, result_loc); case NodeTypeReturnExpr: From 20f243919209c141618176f9a837d9fa08f50450 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Nov 2018 00:30:58 -0500 Subject: [PATCH 037/190] copy elision: runtime and comptime array initialization ```zig export fn entry() void { var array = []Bar.{Bar.{.x = 7, .y = 9}}; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %array = alloca [1 x %Bar], align 4 %0 = bitcast [1 x %Bar]* %array to i8*, !dbg !55 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast ([1 x %Bar]* @0 to i8*), i64 8, i1 false), !dbg !55 call void @llvm.dbg.declare(metadata [1 x %Bar]* %array, metadata !45, metadata !DIExpression()), !dbg !55 ret void, !dbg !56 } ``` ```zig export fn entry() void { var x = i32(7); var array = []Bar.{Bar.{.x = x, .y = 9}, bar2()}; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca i32, align 4 %array = alloca [2 x %Bar], align 4 store i32 7, i32* %x, align 4, !dbg !57 call void @llvm.dbg.declare(metadata i32* %x, metadata !45, metadata !DIExpression()), !dbg !57 %0 = getelementptr inbounds [2 x %Bar], [2 x %Bar]* %array, i64 0, i64 0, !dbg !58 %1 = getelementptr inbounds %Bar, %Bar* %0, i32 0, i32 0, !dbg !59 %2 = load i32, i32* %x, align 4, !dbg !60 store i32 %2, i32* %1, align 4, !dbg !60 %3 = getelementptr inbounds %Bar, %Bar* %0, i32 0, i32 1, !dbg !61 store i32 9, i32* %3, align 4, !dbg !61 %4 = getelementptr inbounds [2 x %Bar], [2 x %Bar]* %array, i64 0, i64 1, !dbg !62 call fastcc void @bar2(%Bar* sret %4), !dbg !62 call void @llvm.dbg.declare(metadata [2 x %Bar]* %array, metadata !48, metadata !DIExpression()), !dbg !63 ret void, !dbg !64 } ``` --- src/all_types.hpp | 12 ++- src/codegen.cpp | 28 +---- src/ir.cpp | 267 ++++++++++++++++++++++++++-------------------- src/ir_print.cpp | 19 +++- 4 files changed, 178 insertions(+), 148 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index a958111ba3f4..b059599d431f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2209,6 +2209,7 @@ enum IrInstructionId { IrInstructionIdAssertNonError, IrInstructionIdErrorUnionFieldErrorSet, IrInstructionIdFirstArgResultLoc, + IrInstructionIdInferArrayType, }; struct IrInstruction { @@ -2473,8 +2474,8 @@ struct IrInstructionContainerInitList { IrInstruction *container_type; IrInstruction *result_loc; - size_t item_count; - IrInstruction **items; + size_t elem_count; + IrInstruction **elem_result_loc_list; }; struct IrInstructionContainerInitFieldsField { @@ -3407,6 +3408,13 @@ struct IrInstructionFirstArgResultLoc { IrInstruction *fn_ref; }; +struct IrInstructionInferArrayType { + IrInstruction base; + + IrInstruction *src_type; + size_t elem_count; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index 78e137f5d552..d1244158decc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4797,30 +4797,6 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I return result_ptr; } -static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *executable, - IrInstructionContainerInitList *instruction) -{ - ZigType *array_type = instruction->base.value.type; - assert(array_type->id == ZigTypeIdArray); - LLVMValueRef tmp_array_ptr = ir_llvm_value(g, instruction->result_loc); - assert(tmp_array_ptr); - - size_t field_count = instruction->item_count; - - ZigType *child_type = array_type->data.array.child_type; - for (size_t i = 0; i < field_count; i += 1) { - LLVMValueRef elem_val = ir_llvm_value(g, instruction->items[i]); - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->type_ref), - LLVMConstInt(g->builtin_types.entry_usize->type_ref, i, false), - }; - LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, tmp_array_ptr, indices, 2, ""); - gen_assign_raw(g, elem_ptr, get_pointer_to_type(g, child_type, false), elem_val); - } - - return tmp_array_ptr; -} - static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) { gen_panic(g, ir_llvm_value(g, instruction->msg), get_cur_err_ret_trace_val(g, instruction->base.scope)); return nullptr; @@ -5257,6 +5233,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdAllocaGen: case IrInstructionIdFirstArgResultLoc: case IrInstructionIdResultCast: + case IrInstructionIdContainerInitList: + case IrInstructionIdInferArrayType: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5369,8 +5347,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_int_to_err(g, executable, (IrInstructionIntToErr *)instruction); case IrInstructionIdErrToInt: return ir_render_err_to_int(g, executable, (IrInstructionErrToInt *)instruction); - case IrInstructionIdContainerInitList: - return ir_render_container_init_list(g, executable, (IrInstructionContainerInitList *)instruction); case IrInstructionIdPanic: return ir_render_panic(g, executable, (IrInstructionPanic *)instruction); case IrInstructionIdTagName: diff --git a/src/ir.cpp b/src/ir.cpp index bd6ce25309d3..c3876e7afb00 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -931,6 +931,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFirstArgResultLo return IrInstructionIdFirstArgResultLoc; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionInferArrayType *) { + return IrInstructionIdInferArrayType; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -1335,17 +1339,20 @@ static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *sour } static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_type, size_t item_count, IrInstruction **items) + IrInstruction *container_type, size_t elem_count, IrInstruction **elem_result_loc_list, + IrInstruction *result_loc) { IrInstructionContainerInitList *container_init_list_instruction = ir_build_instruction(irb, scope, source_node); container_init_list_instruction->container_type = container_type; - container_init_list_instruction->item_count = item_count; - container_init_list_instruction->items = items; + container_init_list_instruction->elem_count = elem_count; + container_init_list_instruction->elem_result_loc_list = elem_result_loc_list; + container_init_list_instruction->result_loc = result_loc; + ir_ref_instruction(result_loc, irb->current_basic_block); ir_ref_instruction(container_type, irb->current_basic_block); - for (size_t i = 0; i < item_count; i += 1) { - ir_ref_instruction(items[i], irb->current_basic_block); + for (size_t i = 0; i < elem_count; i += 1) { + ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block); } return &container_init_list_instruction->base; @@ -2989,6 +2996,18 @@ static IrInstruction *ir_build_first_arg_result_loc(IrBuilder *irb, Scope *scope return &instruction->base; } +static IrInstruction *ir_build_infer_array_type(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *src_type, size_t elem_count) +{ + IrInstructionInferArrayType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->src_type = src_type; + instruction->elem_count = elem_count; + + ir_ref_instruction(src_type, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -5284,20 +5303,23 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields, new_result_loc); return ir_gen_result(irb, scope, node, lval, result_loc); } else if (kind == ContainerInitKindArray) { - size_t item_count = container_init_expr->entries.length; - IrInstruction **values = allocate(item_count); - for (size_t i = 0; i < item_count; i += 1) { + size_t elem_count = container_init_expr->entries.length; + IrInstruction *array_type = ir_build_infer_array_type(irb, scope, node, container_type, elem_count); + IrInstruction *new_result_loc = ir_build_result_cast(irb, scope, node, array_type, result_loc); + + IrInstruction **result_locs = allocate(elem_count); + for (size_t i = 0; i < elem_count; i += 1) { AstNode *expr_node = container_init_expr->entries.at(i); IrInstruction *index_value = ir_build_const_usize(irb, scope, expr_node, i); - IrInstruction *elem_result_loc = ir_build_elem_ptr(irb, scope, expr_node, result_loc, index_value, true, - PtrLenSingle); + IrInstruction *elem_result_loc = ir_build_elem_ptr(irb, scope, expr_node, new_result_loc, + index_value, true, PtrLenSingle); IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope, LValNone, elem_result_loc); if (expr_value == irb->codegen->invalid_instruction) return expr_value; - values[i] = expr_value; + result_locs[i] = expr_value; } - ir_build_container_init_list(irb, scope, node, container_type, item_count, values); + ir_build_container_init_list(irb, scope, node, array_type, elem_count, result_locs, new_result_loc); return ir_gen_result(irb, scope, node, lval, result_loc); } else { zig_unreachable(); @@ -14840,6 +14862,26 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct if (array_ptr_val == nullptr) return ira->codegen->invalid_instruction; + if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer && + array_ptr_val->special == ConstValSpecialUndef && + array_type->id == ZigTypeIdArray) + { + array_ptr_val->data.x_array.special = ConstArraySpecialNone; + array_ptr_val->data.x_array.data.s_none.elements = create_const_vals(array_type->data.array.len); + array_ptr_val->special = ConstValSpecialStatic; + for (size_t i = 0; i != array_type->data.array.len; i += 1) { + ConstExprValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i]; + elem_val->special = ConstValSpecialUndef; + elem_val->type = array_type->data.array.child_type; + ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); + if (parent != nullptr) { + parent->id = ConstParentIdArray; + parent->data.p_array.array_val = array_ptr_val; + parent->data.p_array.elem_index = i; + } + } + } + if (array_ptr_val->special != ConstValSpecialRuntime && (array_type->id != ZigTypeIdPointer || array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)) @@ -14951,7 +14993,16 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } return result; } else if (array_type->id == ZigTypeIdArray) { - IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type); + IrInstruction *result; + if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, + elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, + false, elem_ptr_instruction->ptr_len); + result->value.type = return_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, &elem_ptr_instruction->base, return_type); + } ConstExprValue *out_val = &result->value; out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.mut = orig_array_ptr_val->data.x_ptr.mut; @@ -15072,6 +15123,12 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ ConstExprValue *field_val = &struct_val->data.x_struct.fields[i]; field_val->special = ConstValSpecialUndef; field_val->type = bare_type->data.structure.fields[i].type_entry; + ConstParent *parent = get_const_val_parent(ira->codegen, field_val); + if (parent != nullptr) { + parent->id = ConstParentIdStruct; + parent->data.p_struct.struct_val = struct_val; + parent->data.p_struct.field_index = i; + } } } ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index]; @@ -16933,7 +16990,6 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (const_ptrs.length == actual_field_count) { result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; } else { - result_loc->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; result_loc->value.special = ConstValSpecialRuntime; for (size_t i = 0; i < const_ptrs.length; i += 1) { IrInstruction *field_result_loc = const_ptrs.at(i); @@ -16950,123 +17006,85 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstructionContainerInitList *instruction) { - IrInstruction *container_type_value = instruction->container_type->child; - if (type_is_invalid(container_type_value->value.type)) + ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child); + if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; - size_t elem_count = instruction->item_count; - if (container_type_value->value.type->id == ZigTypeIdMetaType) { - ZigType *container_type = ir_resolve_type(ira, container_type_value); - if (type_is_invalid(container_type)) - return ira->codegen->invalid_instruction; - - if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { - return ir_analyze_container_init_fields(ira, &instruction->base, container_type, - 0, nullptr, nullptr); - } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { - // array is same as slice init but we make a compile error if the length is wrong - ZigType *child_type; - if (container_type->id == ZigTypeIdArray) { - child_type = container_type->data.array.child_type; - if (container_type->data.array.len != elem_count) { - ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count); - - ir_add_error(ira, &instruction->base, - buf_sprintf("expected %s literal, found %s literal", - buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); - return ira->codegen->invalid_instruction; - } - } else { - ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; - assert(pointer_type->id == ZigTypeIdPointer); - child_type = pointer_type->data.pointer.child_type; - } - - ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); + // handled by IrInstructionInferArrayType + assert(!is_slice(container_type)); - ConstExprValue const_val = {}; - const_val.special = ConstValSpecialStatic; - const_val.type = fixed_size_array_type; - const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count); - - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; - IrInstruction **new_items = allocate(elem_count); + size_t elem_count = instruction->elem_count; - IrInstruction *first_non_const_instruction = nullptr; + if (container_type->id == ZigTypeIdStruct && elem_count == 0) + return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, nullptr); - for (size_t i = 0; i < elem_count; i += 1) { - IrInstruction *arg_value = instruction->items[i]->child; - if (type_is_invalid(arg_value->value.type)) - return ira->codegen->invalid_instruction; + if (container_type->id == ZigTypeIdVoid) { + if (elem_count != 0) { + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("void expression expects no arguments")); + return ira->codegen->invalid_instruction; + } + return ir_const_void(ira, &instruction->base); + } - IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); - if (casted_arg == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; + if (container_type->id != ZigTypeIdArray) { + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("type '%s' does not support array initialization", + buf_ptr(&container_type->name))); + return ira->codegen->invalid_instruction; + } - new_items[i] = casted_arg; + // array is same as slice init but we make a compile error if the length is wrong + ZigType *child_type = container_type->data.array.child_type; + if (container_type->data.array.len != elem_count) { + ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count); - if (const_val.special == ConstValSpecialStatic) { - if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) { - ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad); - if (!elem_val) - return ira->codegen->invalid_instruction; + ir_add_error(ira, &instruction->base, + buf_sprintf("expected %s literal, found %s literal", + buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); + return ira->codegen->invalid_instruction; + } - copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true); - } else { - first_non_const_instruction = casted_arg; - const_val.special = ConstValSpecialRuntime; - } - } - } + // The Result Location Mechanism has already emitted runtime instructions to + // initialize runtime elements and has omitted instructions for the comptime + // elements. However it is only now that we find out whether the array initialization + // can be a comptime value. So we must clean up the situation. If it turns out + // array initialization can be a comptime value, overwrite ConstPtrMutInfer with + // ConstPtrMutComptimeConst. Otherwise, emit instructions to runtime-initialize the + // elements that have comptime-known values. + ZigList const_ptrs = {}; - if (const_val.special == ConstValSpecialStatic) { - IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - ConstExprValue *out_val = &result->value; - // TODO copy_const_val? - *out_val = const_val; - result->value.type = fixed_size_array_type; - for (size_t i = 0; i < elem_count; i += 1) { - ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i]; - ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); - if (parent != nullptr) { - parent->id = ConstParentIdArray; - parent->data.p_array.array_val = out_val; - parent->data.p_array.elem_index = i; - } - } - return result; - } + for (size_t i = 0; i < elem_count; i += 1) { + IrInstruction *elem_result_loc = instruction->elem_result_loc_list[i]->child; + if (type_is_invalid(elem_result_loc->value.type)) + return ira->codegen->invalid_instruction; - if (is_comptime) { - ir_add_error_node(ira, first_non_const_instruction->source_node, - buf_sprintf("unable to evaluate constant expression")); - return ira->codegen->invalid_instruction; - } + if (instr_is_comptime(elem_result_loc) && + elem_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + const_ptrs.append(elem_result_loc); + } + } - IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, - container_type_value, elem_count, new_items); - new_instruction->value.type = fixed_size_array_type; - return new_instruction; - } else if (container_type->id == ZigTypeIdVoid) { - if (elem_count != 0) { - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("void expression expects no arguments")); - return ira->codegen->invalid_instruction; - } - return ir_const_void(ira, &instruction->base); + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (const_ptrs.length == elem_count) { + result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; } else { - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("type '%s' does not support array initialization", - buf_ptr(&container_type->name))); - return ira->codegen->invalid_instruction; + result_loc->value.special = ConstValSpecialRuntime; + for (size_t i = 0; i < const_ptrs.length; i += 1) { + IrInstruction *elem_result_loc = const_ptrs.at(i); + IrInstruction *deref = ir_get_deref(ira, elem_result_loc, elem_result_loc); + elem_result_loc->value.special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, elem_result_loc, elem_result_loc, deref); + } } - } else { - ir_add_error(ira, container_type_value, - buf_sprintf("expected type, found '%s' value", buf_ptr(&container_type_value->value.type->name))); - return ira->codegen->invalid_instruction; } + + return ir_const_void(ira, &instruction->base); } static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, @@ -21751,6 +21769,22 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira return &result->base; } +static IrInstruction *ir_analyze_instruction_infer_array_type(IrAnalyze *ira, + IrInstructionInferArrayType *instruction) +{ + ZigType *src_type = ir_resolve_type(ira, instruction->src_type->child); + if (type_is_invalid(src_type)) + return ira->codegen->invalid_instruction; + + if (!is_slice(src_type)) { + return ir_const_type(ira, &instruction->base, src_type); + } + + ZigType *child_type = src_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type; + ZigType *array_type = get_array_type(ira->codegen, child_type, instruction->elem_count); + return ir_const_type(ira, &instruction->base, array_type); +} + static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -22048,6 +22082,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_error_union_field_error_set(ira, (IrInstructionErrorUnionFieldErrorSet *)instruction); case IrInstructionIdFirstArgResultLoc: return ir_analyze_instruction_first_arg_result_loc(ira, (IrInstructionFirstArgResultLoc *)instruction); + case IrInstructionIdInferArrayType: + return ir_analyze_instruction_infer_array_type(ira, (IrInstructionInferArrayType *)instruction); case IrInstructionIdResultBytesToSlice: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: @@ -22287,6 +22323,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAllocaGen: case IrInstructionIdErrorUnionFieldErrorSet: case IrInstructionIdFirstArgResultLoc: + case IrInstructionIdInferArrayType: return false; case IrInstructionIdLoadPtr: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index aeacce36301b..07137c346c2f 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -268,14 +268,14 @@ static void ir_print_phi(IrPrint *irp, IrInstructionPhi *phi_instruction) { static void ir_print_container_init_list(IrPrint *irp, IrInstructionContainerInitList *instruction) { ir_print_other_instruction(irp, instruction->container_type); fprintf(irp->f, "{"); - if (instruction->item_count > 50) { - fprintf(irp->f, "...(%" ZIG_PRI_usize " items)...", instruction->item_count); + if (instruction->elem_count > 50) { + fprintf(irp->f, "...(%" ZIG_PRI_usize " items)...", instruction->elem_count); } else { - for (size_t i = 0; i < instruction->item_count; i += 1) { - IrInstruction *item = instruction->items[i]; + for (size_t i = 0; i < instruction->elem_count; i += 1) { + IrInstruction *elem_result_loc = instruction->elem_result_loc_list[i]; if (i != 0) fprintf(irp->f, ", "); - ir_print_other_instruction(irp, item); + ir_print_other_instruction(irp, elem_result_loc); } } fprintf(irp->f, "}"); @@ -1417,6 +1417,12 @@ static void ir_print_first_arg_result_loc(IrPrint *irp, IrInstructionFirstArgRes fprintf(irp->f, ")"); } +static void ir_print_infer_array_type(IrPrint *irp, IrInstructionInferArrayType *instruction) { + fprintf(irp->f, "InferArrayType(src_type="); + ir_print_other_instruction(irp, instruction->src_type); + fprintf(irp->f, ",elem_count=%zu)", instruction->elem_count); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1887,6 +1893,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFirstArgResultLoc: ir_print_first_arg_result_loc(irp, (IrInstructionFirstArgResultLoc *)instruction); break; + case IrInstructionIdInferArrayType: + ir_print_infer_array_type(irp, (IrInstructionInferArrayType *)instruction); + break; } fprintf(irp->f, "\n"); } From 2c65a5ad23d09d33836660e589b29b9d4ef35437 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Nov 2018 11:18:37 -0500 Subject: [PATCH 038/190] copy elision: runtime and comptime union init ```zig export fn entry() void { var u = U.{.Bar = bar()}; } ``` ```llvm define void @entry() #2 !dbg !47 { Entry: %u = alloca %U, align 4 %0 = getelementptr inbounds %U, %U* %u, i32 0, i32 1, !dbg !71 store i2 1, i2* %0, align 1, !dbg !71 %1 = getelementptr inbounds %U, %U* %u, i32 0, i32 0, !dbg !71 %2 = bitcast %Foo* %1 to %Bar*, !dbg !71 call fastcc void @bar(%Bar* sret %2), !dbg !72 call void @llvm.dbg.declare(metadata %U* %u, metadata !51, metadata !DIExpression()), !dbg !73 ret void, !dbg !74 } ``` --- src/all_types.hpp | 32 ++----- src/codegen.cpp | 86 ++++--------------- src/ir.cpp | 207 +++++++++++++++++++--------------------------- src/ir_print.cpp | 27 ------ 4 files changed, 107 insertions(+), 245 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index b059599d431f..fff3c4e33f4f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2077,8 +2077,6 @@ enum IrInstructionId { IrInstructionIdCast, IrInstructionIdContainerInitList, IrInstructionIdContainerInitFields, - IrInstructionIdStructInit, - IrInstructionIdUnionInit, IrInstructionIdUnreachable, IrInstructionIdTypeOf, IrInstructionIdToPtrType, @@ -2406,11 +2404,18 @@ struct IrInstructionStructFieldPtr { bool is_const; }; +enum IrInstructionUnionFieldPtrId { + IrInstructionUnionFieldPtrIdRef, + IrInstructionUnionFieldPtrIdSwitch, + IrInstructionUnionFieldPtrIdResultPtr, +}; + struct IrInstructionUnionFieldPtr { IrInstruction base; IrInstruction *union_ptr; TypeUnionField *field; + IrInstructionUnionFieldPtrId id; bool is_const; }; @@ -2494,29 +2499,6 @@ struct IrInstructionContainerInitFields { IrInstruction *result_loc; }; -struct IrInstructionStructInitField { - IrInstruction *value; - TypeStructField *type_struct_field; -}; - -struct IrInstructionStructInit { - IrInstruction base; - - ZigType *struct_type; - IrInstruction *result_loc; - size_t field_count; - IrInstructionStructInitField *fields; -}; - -struct IrInstructionUnionInit { - IrInstruction base; - - ZigType *union_type; - TypeUnionField *field; - IrInstruction *init_value; - IrInstruction *result_loc; -}; - struct IrInstructionUnreachable { IrInstruction base; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index d1244158decc..8c2461da4fe9 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3591,7 +3591,10 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab return bitcasted_union_field_ptr; } - if (ir_want_runtime_safety(g, &instruction->base)) { + bool safety_check_on = instruction->id == IrInstructionUnionFieldPtrIdRef && + ir_want_runtime_safety(g, &instruction->base); + + if (safety_check_on) { LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, ""); LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, ""); @@ -3609,7 +3612,16 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab LLVMPositionBuilderAtEnd(g->builder, ok_block); } - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, ""); + if (instruction->id == IrInstructionUnionFieldPtrIdResultPtr) { + LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, + union_type->data.unionation.gen_tag_index, ""); + LLVMValueRef tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, + &field->enum_field->value); + gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); + } + + LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, + union_type->data.unionation.gen_union_index, ""); LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); return bitcasted_union_field_ptr; } @@ -4731,72 +4743,6 @@ static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, Ir return get_handle_value(g, tag_field_ptr, tag_type, ptr_type); } -static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, - IrInstructionStructInit *instruction) -{ - LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); - - for (size_t i = 0; i < instruction->field_count; i += 1) { - IrInstructionStructInitField *field = &instruction->fields[i]; - TypeStructField *type_struct_field = field->type_struct_field; - if (!type_has_bits(type_struct_field->type_entry)) - continue; - - LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, - (unsigned)type_struct_field->gen_index, ""); - LLVMValueRef value = ir_llvm_value(g, field->value); - - uint32_t field_align_bytes = get_abi_alignment(g, type_struct_field->type_entry); - uint32_t host_int_bytes = get_host_int_bytes(g, instruction->struct_type, type_struct_field); - - ZigType *ptr_type = get_pointer_to_type_extra(g, type_struct_field->type_entry, - false, false, PtrLenSingle, field_align_bytes, - (uint32_t)type_struct_field->bit_offset_in_host, host_int_bytes); - - gen_assign_raw(g, field_ptr, ptr_type, value); - } - return result_ptr; -} - -static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, IrInstructionUnionInit *instruction) { - TypeUnionField *type_union_field = instruction->field; - - if (!type_has_bits(type_union_field->type_entry)) - return nullptr; - - uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry); - ZigType *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry, - false, false, PtrLenSingle, field_align_bytes, - 0, 0); - - LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); - - LLVMValueRef uncasted_union_ptr; - // Even if safety is off in this block, if the union type has the safety field, we have to populate it - // correctly. Otherwise safety code somewhere other than here could fail. - ZigType *union_type = instruction->union_type; - if (union_type->data.unionation.gen_tag_index != SIZE_MAX) { - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, - union_type->data.unionation.gen_tag_index, ""); - - LLVMValueRef tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, - &type_union_field->enum_field->value); - gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); - - uncasted_union_ptr = LLVMBuildStructGEP(g->builder, result_ptr, - (unsigned)union_type->data.unionation.gen_union_index, ""); - } else { - uncasted_union_ptr = LLVMBuildStructGEP(g->builder, result_ptr, (unsigned)0, ""); - } - - LLVMValueRef field_ptr = LLVMBuildBitCast(g->builder, uncasted_union_ptr, ptr_type->type_ref, ""); - LLVMValueRef value = ir_llvm_value(g, instruction->init_value); - - gen_assign_raw(g, field_ptr, ptr_type, value); - - return result_ptr; -} - static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) { gen_panic(g, ir_llvm_value(g, instruction->msg), get_cur_err_ret_trace_val(g, instruction->base.scope)); return nullptr; @@ -5327,10 +5273,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_err_wrap_payload(g, executable, (IrInstructionErrWrapPayload *)instruction); case IrInstructionIdUnionTag: return ir_render_union_tag(g, executable, (IrInstructionUnionTag *)instruction); - case IrInstructionIdStructInit: - return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction); - case IrInstructionIdUnionInit: - return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction); case IrInstructionIdPtrCast: return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction); case IrInstructionIdBitCast: diff --git a/src/ir.cpp b/src/ir.cpp index c3876e7afb00..cee633f87e9a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -515,14 +515,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionRef *) { return IrInstructionIdRef; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) { - return IrInstructionIdStructInit; -} - -static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInit *) { - return IrInstructionIdUnionInit; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileErr *) { return IrInstructionIdCompileErr; } @@ -1229,11 +1221,12 @@ static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, As } static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *union_ptr, TypeUnionField *field) + IrInstruction *union_ptr, TypeUnionField *field, IrInstructionUnionFieldPtrId id) { IrInstructionUnionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->union_ptr = union_ptr; instruction->field = field; + instruction->id = id; ir_ref_instruction(union_ptr, irb->current_basic_block); @@ -1378,33 +1371,6 @@ static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scop return &container_init_fields_instruction->base; } -//static IrInstruction *ir_build_struct_init(IrBuilder *irb, Scope *scope, AstNode *source_node, -// ZigType *struct_type, size_t field_count, IrInstructionStructInitField *fields) -//{ -// IrInstructionStructInit *struct_init_instruction = ir_build_instruction(irb, scope, source_node); -// struct_init_instruction->struct_type = struct_type; -// struct_init_instruction->field_count = field_count; -// struct_init_instruction->fields = fields; -// -// for (size_t i = 0; i < field_count; i += 1) -// ir_ref_instruction(fields[i].value, irb->current_basic_block); -// -// return &struct_init_instruction->base; -//} - -//static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node, -// ZigType *union_type, TypeUnionField *field, IrInstruction *init_value) -//{ -// IrInstructionUnionInit *union_init_instruction = ir_build_instruction(irb, scope, source_node); -// union_init_instruction->union_type = union_type; -// union_init_instruction->field = field; -// union_init_instruction->init_value = init_value; -// -// ir_ref_instruction(init_value, irb->current_basic_block); -// -// return &union_init_instruction->base; -//} - static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionUnreachable *unreachable_instruction = ir_build_instruction(irb, scope, source_node); @@ -4694,7 +4660,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *ptr_to_int = ir_build_ptr_to_int(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, ptr_to_int, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ptr_to_int); } case BuiltinFnIdTagName: { @@ -15183,22 +15149,37 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ if (type_is_invalid(union_val->type)) return ira->codegen->invalid_instruction; - TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); - if (actual_field == nullptr) - zig_unreachable(); + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer && + union_val->special == ConstValSpecialUndef) + { + ConstExprValue *payload_val = create_const_vals(1); + payload_val->special = ConstValSpecialUndef; + payload_val->type = field->type_entry; + ConstParent *parent = get_const_val_parent(ira->codegen, payload_val); + if (parent != nullptr) { + parent->id = ConstParentIdUnion; + parent->data.p_union.union_val = union_val; + } - if (field != actual_field) { - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name), - buf_ptr(actual_field->name))); - return ira->codegen->invalid_instruction; - } + union_val->special = ConstValSpecialStatic; + bigint_init_bigint(&union_val->data.x_union.tag, &field->enum_field->value); + union_val->data.x_union.payload = payload_val; + } else { + TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); + if (actual_field == nullptr) + zig_unreachable(); + if (field != actual_field) { + ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name), + buf_ptr(actual_field->name))); + return ira->codegen->invalid_instruction; + } + } ConstExprValue *payload_val = union_val->data.x_union.payload; ZigType *field_type = field->type_entry; - if (field_type->id == ZigTypeIdVoid) { - assert(payload_val == nullptr); + if (field_type->id == ZigTypeIdVoid && payload_val == nullptr) { payload_val = create_const_vals(1); payload_val->special = ConstValSpecialStatic; payload_val->type = field_type; @@ -15207,16 +15188,26 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, PtrLenSingle, 0, 0, 0); - IrInstruction *result = ir_const(ira, source_instr, ptr_type); + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, container_ptr, field, + IrInstructionUnionFieldPtrIdResultPtr); + result->value.type = ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, ptr_type); + } ConstExprValue *const_val = &result->value; const_val->data.x_ptr.special = ConstPtrSpecialRef; - const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; + const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; const_val->data.x_ptr.data.ref.pointee = payload_val; return result; } } - IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); + IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, container_ptr, field, IrInstructionUnionFieldPtrIdRef); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, PtrLenSingle, 0, 0, 0); return result; @@ -16720,7 +16711,8 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru } IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, target_value_ptr, field); + instruction->base.scope, instruction->base.source_node, target_value_ptr, field, + IrInstructionUnionFieldPtrIdSwitch); result->value.type = get_pointer_to_type(ira->codegen, field->type_entry, target_value_ptr->value.type->data.pointer.is_const); return result; @@ -16845,71 +16837,47 @@ static IrInstruction *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRe } static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction, - ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields) + ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields, + IrInstruction *result_loc) { - zig_panic("TODO"); - //Error err; - //assert(container_type->id == ZigTypeIdUnion); - - //if ((err = ensure_complete_type(ira->codegen, container_type))) - // return ira->codegen->invalid_instruction; - - //if (instr_field_count != 1) { - // ir_add_error(ira, instruction, - // buf_sprintf("union initialization expects exactly one field")); - // return ira->codegen->invalid_instruction; - //} - - //IrInstructionContainerInitFieldsField *field = &fields[0]; - //IrInstruction *field_value = field->value->child; - //if (type_is_invalid(field_value->value.type)) - // return ira->codegen->invalid_instruction; - - //TypeUnionField *type_field = find_union_type_field(container_type, field->name); - //if (!type_field) { - // ir_add_error_node(ira, field->source_node, - // buf_sprintf("no member named '%s' in union '%s'", - // buf_ptr(field->name), buf_ptr(&container_type->name))); - // return ira->codegen->invalid_instruction; - //} - - //if (type_is_invalid(type_field->type_entry)) - // return ira->codegen->invalid_instruction; - - //IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry); - //if (casted_field_value == ira->codegen->invalid_instruction) - // return ira->codegen->invalid_instruction; - - //if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown))) - // return ira->codegen->invalid_instruction; - - //bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); - //if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || - // !type_has_bits(casted_field_value->value.type)) - //{ - // ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); - // if (!field_val) - // return ira->codegen->invalid_instruction; - - // IrInstruction *result = ir_const(ira, instruction, container_type); - // ConstExprValue *out_val = &result->value; - // out_val->data.x_union.payload = field_val; - // out_val->data.x_union.tag = type_field->enum_field->value; - - // ConstParent *parent = get_const_val_parent(ira->codegen, field_val); - // if (parent != nullptr) { - // parent->id = ConstParentIdUnion; - // parent->data.p_union.union_val = out_val; - // } - - // return result; - //} - - //IrInstruction *new_instruction = ir_build_union_init(&ira->new_irb, - // instruction->scope, instruction->source_node, - // container_type, type_field, casted_field_value); - //new_instruction->value.type = container_type; - //return new_instruction; + Error err; + assert(container_type->id == ZigTypeIdUnion); + + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; + + if (instr_field_count != 1) { + ir_add_error(ira, instruction, + buf_sprintf("union initialization expects exactly one field")); + return ira->codegen->invalid_instruction; + } + + IrInstructionContainerInitFieldsField *field = &fields[0]; + IrInstruction *field_result_loc = field->result_loc->child; + if (type_is_invalid(field_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + TypeUnionField *type_field = find_union_type_field(container_type, field->name); + if (!type_field) { + ir_add_error_node(ira, field->source_node, + buf_sprintf("no member named '%s' in union '%s'", + buf_ptr(field->name), buf_ptr(&container_type->name))); + return ira->codegen->invalid_instruction; + } + + if (type_is_invalid(type_field->type_entry)) + return ira->codegen->invalid_instruction; + + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(field_result_loc) && + field_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + result_loc->value.special = ConstValSpecialRuntime; + } + } + return ir_const_void(ira, instruction); } static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction, @@ -16917,7 +16885,8 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc IrInstruction *result_loc) { if (container_type->id == ZigTypeIdUnion) { - return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields); + return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, + fields, result_loc); } if (container_type->id != ZigTypeIdStruct || is_slice(container_type)) { ir_add_error(ira, instruction, @@ -21789,8 +21758,6 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio switch (instruction->id) { case IrInstructionIdInvalid: case IrInstructionIdWidenOrShorten: - case IrInstructionIdStructInit: - case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: case IrInstructionIdUnionFieldPtr: case IrInstructionIdOptionalWrap: @@ -22228,8 +22195,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBinOp: case IrInstructionIdConst: case IrInstructionIdCast: - case IrInstructionIdStructInit: - case IrInstructionIdUnionInit: case IrInstructionIdFieldPtr: case IrInstructionIdElemPtr: case IrInstructionIdVarPtr: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 07137c346c2f..7c157272e235 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -294,27 +294,6 @@ static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerI ir_print_other_instruction(irp, instruction->result_loc); } -static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruction) { - fprintf(irp->f, "%s {", buf_ptr(&instruction->struct_type->name)); - for (size_t i = 0; i < instruction->field_count; i += 1) { - IrInstructionStructInitField *field = &instruction->fields[i]; - Buf *field_name = field->type_struct_field->name; - const char *comma = (i == 0) ? "" : ", "; - fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field_name)); - ir_print_other_instruction(irp, field->value); - } - fprintf(irp->f, "} // struct init"); -} - -static void ir_print_union_init(IrPrint *irp, IrInstructionUnionInit *instruction) { - Buf *field_name = instruction->field->enum_field->name; - - fprintf(irp->f, "%s {", buf_ptr(&instruction->union_type->name)); - fprintf(irp->f, ".%s = ", buf_ptr(field_name)); - ir_print_other_instruction(irp, instruction->init_value); - fprintf(irp->f, "} // union init"); -} - static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) { fprintf(irp->f, "unreachable"); } @@ -1464,12 +1443,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdContainerInitFields: ir_print_container_init_fields(irp, (IrInstructionContainerInitFields *)instruction); break; - case IrInstructionIdStructInit: - ir_print_struct_init(irp, (IrInstructionStructInit *)instruction); - break; - case IrInstructionIdUnionInit: - ir_print_union_init(irp, (IrInstructionUnionInit *)instruction); - break; case IrInstructionIdUnreachable: ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction); break; From 65501f03b6e218ab02448e1c156128213b7da5b9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Nov 2018 13:33:01 -0500 Subject: [PATCH 039/190] copy elision: handle inferred comptime const with if ```zig export fn entry() void { var c = true; var f = foo(); var x: Bar = if (c) undefined else f.y; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %c = alloca i1, align 1 %f = alloca %Foo, align 4 %x = alloca %Bar, align 4 store i1 true, i1* %c, align 1, !dbg !61 call void @llvm.dbg.declare(metadata i1* %c, metadata !45, metadata !DIExpression()), !dbg !61 call fastcc void @foo(%Foo* sret %f), !dbg !62 call void @llvm.dbg.declare(metadata %Foo* %f, metadata !48, metadata !DIExpression()), !dbg !63 %0 = load i1, i1* %c, align 1, !dbg !64 br i1 %0, label %Then, label %Else, !dbg !64 Then: ; preds = %Entry %1 = bitcast %Bar* %x to i8*, !dbg !65 call void @llvm.memset.p0i8.i64(i8* align 4 %1, i8 -86, i64 8, i1 false), !dbg !65 br label %EndIf, !dbg !66 Else: ; preds = %Entry %2 = getelementptr inbounds %Foo, %Foo* %f, i32 0, i32 1, !dbg !67 %3 = bitcast %Bar* %2 to i8*, !dbg !67 %4 = bitcast %Bar* %x to i8*, !dbg !67 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %4, i8* align 4 %3, i64 8, i1 false), !dbg !67 br label %EndIf, !dbg !66 EndIf: ; preds = %Else, %Then call void @llvm.dbg.declare(metadata %Bar* %x, metadata !59, metadata !DIExpression()), !dbg !68 ret void, !dbg !69 } ``` --- src/all_types.hpp | 1 + src/ir.cpp | 155 +++++++++++++++++++++++++--------------------- 2 files changed, 87 insertions(+), 69 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 1ea39fe652fa..08d3f40d69d4 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2252,6 +2252,7 @@ struct IrInstructionCondBr { IrBasicBlock *then_block; IrBasicBlock *else_block; IrInstruction *is_comptime; + IrInstruction *result_loc; }; struct IrInstructionBr { diff --git a/src/ir.cpp b/src/ir.cpp index ffe4b4da22f1..0bc79bf0ecae 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -960,7 +960,7 @@ static IrInstruction *ir_build_cast(IrBuilder *irb, Scope *scope, AstNode *sourc } static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *condition, - IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime) + IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime, IrInstruction *result_loc) { IrInstructionCondBr *cond_br_instruction = ir_build_instruction(irb, scope, source_node); cond_br_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; @@ -969,11 +969,13 @@ static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *so cond_br_instruction->then_block = then_block; cond_br_instruction->else_block = else_block; cond_br_instruction->is_comptime = is_comptime; + cond_br_instruction->result_loc = result_loc; ir_ref_instruction(condition, irb->current_basic_block); ir_ref_bb(then_block); ir_ref_bb(else_block); - if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); + if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &cond_br_instruction->base; } @@ -3141,7 +3143,7 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); - ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, suspended_block); ir_build_unreachable(irb, scope, node); @@ -3150,7 +3152,7 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); // if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here IrInstruction *have_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); - ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime); + ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, store_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); @@ -3160,7 +3162,7 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode ir_set_cursor_at_end_and_append_block(irb, check_canceled_block); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime); + return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime, nullptr); } static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -3277,7 +3279,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, is_comptime = ir_build_test_comptime(irb, scope, node, is_err); } - ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime)); + ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime, nullptr)); IrBasicBlock *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt"); ir_set_cursor_at_end_and_append_block(irb, err_block); @@ -3320,7 +3322,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, } else { is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val); } - ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime)); + ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime, nullptr)); ir_set_cursor_at_end_and_append_block(irb, return_block); if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) { @@ -3550,7 +3552,7 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node // block for when val1 == true (don't even evaluate the second part) IrBasicBlock *true_block = ir_create_basic_block(irb, scope, "BoolOrTrue"); - ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); + ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, false_block); IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); @@ -3592,7 +3594,7 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod // block for when val1 == false (don't even evaluate the second part) IrBasicBlock *false_block = ir_create_basic_block(irb, scope, "BoolAndFalse"); - ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); + ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, true_block); IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); @@ -3635,7 +3637,7 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OrElseNull"); IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OrElseEnd"); - ir_build_cond_br(irb, parent_scope, node, is_non_null, end_block, null_block, is_comptime); + ir_build_cond_br(irb, parent_scope, node, is_non_null, end_block, null_block, is_comptime, result_loc); ir_set_cursor_at_end_and_append_block(irb, null_block); IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope, LValNone, result_loc); @@ -5084,7 +5086,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "Else"); IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "EndIf"); - ir_build_cond_br(irb, scope, condition->source_node, condition, then_block, else_block, is_comptime); + ir_build_cond_br(irb, scope, condition->source_node, condition, then_block, else_block, is_comptime, result_loc); ir_set_cursor_at_end_and_append_block(irb, then_block); @@ -5413,7 +5415,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (!instr_is_unreachable(is_err)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err, - else_block, body_block, is_comptime)); + else_block, body_block, is_comptime, result_loc)); } ir_set_cursor_at_end_and_append_block(irb, body_block); @@ -5492,7 +5494,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val); if (!instr_is_unreachable(is_non_null)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null, - body_block, else_block, is_comptime)); + body_block, else_block, is_comptime, result_loc)); } ir_set_cursor_at_end_and_append_block(irb, body_block); @@ -5553,7 +5555,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n return cond_val; if (!instr_is_unreachable(cond_val)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val, - body_block, else_block, is_comptime)); + body_block, else_block, is_comptime, result_loc)); } ir_set_cursor_at_end_and_append_block(irb, body_block); @@ -5679,7 +5681,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca, nullptr); IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpNotEq, index_val, len_val, false); - ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime)); + ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime, result_loc)); ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false, PtrLenSingle); @@ -5891,7 +5893,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN } else { is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null); } - ir_build_cond_br(irb, scope, node, is_non_null, then_block, else_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_non_null, then_block, else_block, is_comptime, result_loc); ir_set_cursor_at_end_and_append_block(irb, then_block); @@ -5963,7 +5965,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * bool force_comptime = ir_should_inline(irb->exec, scope); IrInstruction *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err); - ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime, result_loc); ir_set_cursor_at_end_and_append_block(irb, ok_block); @@ -6042,24 +6044,18 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit Buf *var_name = var_symbol_node->data.symbol_expr.symbol; bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr; + IrInstruction *var_type = nullptr; // infer the type bool is_shadowable = false; bool is_const = true; ZigVar *var = ir_create_var(irb, var_symbol_node, scope, var_name, is_const, is_const, is_shadowable, var_is_comptime); + + IrInstruction *payload_ptr = (prong_value == nullptr) ? target_value_ptr : + ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); + IrInstruction *var_ptr = var_is_ptr ? + ir_build_ref(irb, scope, var_symbol_node, payload_ptr, true, false) : payload_ptr; + ir_build_var_decl_src(irb, scope, var_symbol_node, var, var_type, nullptr, var_ptr); child_scope = var->child_scope; - IrInstruction *var_value; - IrInstruction *payload_alloca = ir_build_alloca_src(irb, scope, var_symbol_node, nullptr, nullptr, "switch_payload"); - if (prong_value) { - IrInstruction *var_ptr_value = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); - var_value = var_is_ptr ? - var_ptr_value : ir_build_load_result(irb, scope, var_symbol_node, var_ptr_value, payload_alloca); - } else { - var_value = var_is_ptr ? - target_value_ptr : ir_build_load_result(irb, scope, var_symbol_node, target_value_ptr, payload_alloca); - } - IrInstruction *var_type = nullptr; // infer the type - ir_build_store_result(irb, scope, var_symbol_node, payload_alloca, var_value); - ir_build_var_decl_src(irb, scope, var_symbol_node, var, var_type, nullptr, payload_alloca); } else { child_scope = scope; } @@ -6074,7 +6070,9 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit return true; } -static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { +static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeSwitchExpr); AstNode *target_node = node->data.switch_expr.expr; @@ -6187,7 +6185,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * assert(ok_bit); assert(last_item_node); ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit, range_block_yes, - range_block_no, is_comptime)); + range_block_no, is_comptime, result_loc)); ir_set_cursor_at_end_and_append_block(irb, range_block_yes); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, @@ -6246,13 +6244,14 @@ 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); + IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, + check_ranges.items, check_ranges.length, else_prong != nullptr); if (cases.length == 0) { ir_build_br(irb, scope, node, else_block, is_comptime); } else { - ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime, switch_prongs_void); + ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, + is_comptime, switch_prongs_void); } if (!else_prong) { @@ -6262,11 +6261,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * ir_set_cursor_at_end_and_append_block(irb, end_block); assert(incoming_blocks.length == incoming_values.length); - if (incoming_blocks.length == 0) { - return ir_build_const_void(irb, scope, node); - } else { - return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); - } + return ir_gen_result(irb, scope, node, lval, result_loc); } static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, @@ -6511,7 +6506,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode IrBasicBlock *err_block = ir_create_basic_block(irb, parent_scope, "CatchError"); IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "CatchEnd"); - ir_build_cond_br(irb, parent_scope, node, is_err, err_block, end_block, is_comptime); + ir_build_cond_br(irb, parent_scope, node, is_err, err_block, end_block, is_comptime, result_loc); ir_set_cursor_at_end_and_append_block(irb, err_block); Scope *err_scope; @@ -6812,12 +6807,12 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false); - ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, post_return_block); if (cancel_awaited) { @@ -6825,7 +6820,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode } else { IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); - ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime, nullptr); } ir_set_cursor_at_end_and_append_block(irb, pre_return_block); @@ -6835,7 +6830,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode } else { IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); - ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime, nullptr); } } else { ir_build_br(irb, scope, node, done_block, is_comptime); @@ -6890,12 +6885,12 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); - ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); ir_build_unreachable(irb, scope, node); @@ -7007,7 +7002,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); - ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false); + ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, already_awaited_block); ir_build_unreachable(irb, scope, node); @@ -7017,10 +7012,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); - ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); + ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, cancel_target_block); ir_build_cancel(irb, scope, node, target_inst); @@ -7051,7 +7046,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false); IrInstruction *my_is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_suspended_value, zero, false); - ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false); + ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, my_suspended_block); ir_build_unreachable(irb, scope, node); @@ -7059,7 +7054,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, my_not_suspended_block); IrInstruction *my_is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_canceled_mask, false); IrInstruction *my_is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false); + ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, do_suspend_block); IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); @@ -7084,7 +7079,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false); IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false); IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false); - ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false); + ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr); @@ -7093,7 +7088,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, do_defers_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); - ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false)); + ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false, nullptr)); ir_set_cursor_at_end_and_append_block(irb, resume_block); ir_build_br(irb, scope, node, merge_block, const_bool_false); @@ -7160,13 +7155,13 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false); + ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, canceled_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrBasicBlock *post_canceled_block = irb->current_basic_block; - ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false); + ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr); @@ -7177,7 +7172,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); - ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false); + ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, suspended_block); ir_build_unreachable(irb, parent_scope, node); @@ -7213,7 +7208,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod incoming_values[1] = const_bool_false; IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); - ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false)); + ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false, nullptr)); ir_set_cursor_at_end_and_append_block(irb, resume_block); return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); @@ -7321,11 +7316,11 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_if_optional_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeSwitchExpr: - return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node, result_loc), lval); + return ir_gen_switch_expr(irb, scope, node, lval, result_loc); case NodeTypeCompTime: return ir_gen_comptime(irb, scope, node, lval, result_loc); case NodeTypeErrorType: - return ir_gen_value(irb, scope, node, lval, result_loc,ir_gen_error_type(irb, scope, node)); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_error_type(irb, scope, node)); case NodeTypeBreak: return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval); case NodeTypeContinue: @@ -7451,7 +7446,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError"); IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, coro_scope, "AllocOk"); - ir_build_cond_br(irb, coro_scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false); + ir_build_cond_br(irb, coro_scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, alloc_err_block); // we can return undefined here, because the caller passes a pointer to the error struct field @@ -7613,7 +7608,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec nullptr, nullptr, nullptr); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); - ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); + ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, resume_block); ir_gen_resume_target(irb, scope, node, awaiter_handle); @@ -11420,6 +11415,11 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *res } } + // cast from undefined to anything + if (needed_child_type->id == ZigTypeIdUndefined) { + return result_loc; + } + if (allow_failure) { return result_loc; } @@ -14492,9 +14492,14 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->child, &is_comptime)) return ir_unreach_error(ira); - if (is_comptime || instr_is_comptime(condition)) { + ZigType *bool_type = ira->codegen->builtin_types.entry_bool; + IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); + if (casted_condition == ira->codegen->invalid_instruction) + return ir_unreach_error(ira); + + if (is_comptime || instr_is_comptime(casted_condition)) { bool cond_is_true; - if (!ir_resolve_bool(ira, condition, &cond_is_true)) + if (!ir_resolve_bool(ira, casted_condition, &cond_is_true)) return ir_unreach_error(ira); IrBasicBlock *old_dest_block = cond_is_true ? @@ -14513,10 +14518,22 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi return ir_finish_anal(ira, result); } - ZigType *bool_type = ira->codegen->builtin_types.entry_bool; - IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); - if (casted_condition == ira->codegen->invalid_instruction) - return ir_unreach_error(ira); + // The if condition is not comptime known. This means we must mark the result location + // as runtime known, in case it is ConstPtrMutInfer. + if (cond_br_instruction->result_loc != nullptr) { + IrInstruction *result_loc = cond_br_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ir_unreach_error(ira); + if (instr_is_comptime(result_loc)) { + ConstExprValue *result_ptr = ir_resolve_const(ira, result_loc, UndefBad); + if (result_ptr == nullptr) + return ir_unreach_error(ira); + assert(result_ptr->type->id == ZigTypeIdPointer); + if (result_ptr->data.x_ptr.mut == ConstPtrMutInfer) { + result_ptr->special = ConstValSpecialRuntime; + } + } + } assert(cond_br_instruction->then_block != cond_br_instruction->else_block); IrBasicBlock *new_then_block = ir_get_new_bb_runtime(ira, cond_br_instruction->then_block, &cond_br_instruction->base); @@ -14529,7 +14546,7 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi IrInstruction *result = ir_build_cond_br(&ira->new_irb, cond_br_instruction->base.scope, cond_br_instruction->base.source_node, - casted_condition, new_then_block, new_else_block, nullptr); + casted_condition, new_then_block, new_else_block, nullptr, nullptr); result->value.type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } From 40d6ca4854c416b29a8b9f6cdcbb64974419bb92 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Nov 2018 14:32:30 -0500 Subject: [PATCH 040/190] copy elision: switch with aggregate ```zig export fn entry() void { var u: U = undefined; var x: Bar = switch (u) { U.Foo => |f| f.y, U.Int => |x| bar2(), else => undefined, }; } ``` ```llvm define void @entry() #2 !dbg !47 { Entry: %u = alloca %U, align 4 %x = alloca %Bar, align 4 %0 = bitcast %U* %u to i8*, !dbg !75 call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 -86, i64 16, i1 false), !dbg !75 call void @llvm.dbg.declare(metadata %U* %u, metadata !51, metadata !DIExpression()), !dbg !75 %1 = getelementptr inbounds %U, %U* %u, i32 0, i32 1, !dbg !76 %2 = load i2, i2* %1, align 1, !dbg !76 switch i2 %2, label %SwitchElse [ i2 0, label %SwitchProng i2 -2, label %SwitchProng1 ], !dbg !76 SwitchElse: ; preds = %Entry %3 = bitcast %Bar* %x to i8*, !dbg !77 call void @llvm.memset.p0i8.i64(i8* align 4 %3, i8 -86, i64 8, i1 false), !dbg !77 br label %SwitchEnd, !dbg !76 SwitchProng: ; preds = %Entry %4 = getelementptr inbounds %U, %U* %u, i32 0, i32 0, !dbg !78 call void @llvm.dbg.declare(metadata %Foo* %4, metadata !71, metadata !DIExpression()), !dbg !78 %5 = getelementptr inbounds %Foo, %Foo* %4, i32 0, i32 1, !dbg !79 %6 = bitcast %Bar* %5 to i8*, !dbg !79 %7 = bitcast %Bar* %x to i8*, !dbg !79 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %7, i8* align 4 %6, i64 8, i1 false), !dbg !79 br label %SwitchEnd, !dbg !76 SwitchProng1: ; preds = %Entry %8 = getelementptr inbounds %U, %U* %u, i32 0, i32 0, !dbg !81 %9 = bitcast %Foo* %8 to i32*, !dbg !81 call void @llvm.dbg.declare(metadata i32* %9, metadata !73, metadata !DIExpression()), !dbg !81 call fastcc void @bar2(%Bar* sret %x), !dbg !82 br label %SwitchEnd, !dbg !76 SwitchEnd: ; preds = %SwitchProng1, %SwitchProng, %SwitchElse call void @llvm.dbg.declare(metadata %Bar* %x, metadata !74, metadata !DIExpression()), !dbg !84 ret void, !dbg !85 } ``` --- src/all_types.hpp | 1 + src/ir.cpp | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 08d3f40d69d4..e7092b9e3f35 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2276,6 +2276,7 @@ struct IrInstructionSwitchBr { IrInstructionSwitchBrCase *cases; IrInstruction *is_comptime; IrInstruction *switch_prongs_void; + IrInstruction *result_loc; }; struct IrInstructionSwitchVar { diff --git a/src/ir.cpp b/src/ir.cpp index 0bc79bf0ecae..ce22701b52ae 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1693,9 +1693,10 @@ static IrInstruction *ir_build_pop_count(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, - IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, - IrInstruction *switch_prongs_void) +static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *target_value, IrBasicBlock *else_block, + size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, + IrInstruction *switch_prongs_void, IrInstruction *result_loc) { IrInstructionSwitchBr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; @@ -1706,11 +1707,13 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode * instruction->cases = cases; instruction->is_comptime = is_comptime; instruction->switch_prongs_void = switch_prongs_void; + instruction->result_loc = result_loc; ir_ref_instruction(target_value, irb->current_basic_block); if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); ir_ref_bb(else_block); if (switch_prongs_void) ir_ref_instruction(switch_prongs_void, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); for (size_t i = 0; i < case_count; i += 1) { ir_ref_instruction(cases[i].value, irb->current_basic_block); @@ -6251,7 +6254,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * ir_build_br(irb, scope, node, else_block, is_comptime); } else { ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, - is_comptime, switch_prongs_void); + is_comptime, switch_prongs_void, result_loc); } if (!else_prong) { @@ -7065,7 +7068,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n cases[1].value = ir_build_const_u8(irb, scope, node, 1); cases[1].block = destroy_block; ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, - 2, cases, const_bool_false, nullptr); + 2, cases, const_bool_false, nullptr, nullptr); ir_set_cursor_at_end_and_append_block(irb, destroy_block); ir_gen_cancel_target(irb, scope, node, target_inst, false, true); @@ -7197,7 +7200,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1)); cases[1].block = canceled_block; ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, - 2, cases, const_bool_false, nullptr)); + 2, cases, const_bool_false, nullptr, nullptr)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); IrBasicBlock **incoming_blocks = allocate(2); @@ -7524,7 +7527,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec cases[0].block = invalid_resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); cases[1].block = irb->exec->coro_final_cleanup_block; - ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); + ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr, nullptr); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_suspend_block); ir_build_coro_end(irb, scope, node); @@ -16552,10 +16555,27 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, new_case->block->ref_instruction = &switch_br_instruction->base; } + // The switch target is not comptime known. This means we must mark the result location + // as runtime known, in case it is ConstPtrMutInfer. + if (switch_br_instruction->result_loc != nullptr) { + IrInstruction *result_loc = switch_br_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ir_unreach_error(ira); + if (instr_is_comptime(result_loc)) { + ConstExprValue *result_ptr = ir_resolve_const(ira, result_loc, UndefBad); + if (result_ptr == nullptr) + return ir_unreach_error(ira); + assert(result_ptr->type->id == ZigTypeIdPointer); + if (result_ptr->data.x_ptr.mut == ConstPtrMutInfer) { + result_ptr->special = ConstValSpecialRuntime; + } + } + } + IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base); IrInstruction *result = ir_build_switch_br(&ira->new_irb, switch_br_instruction->base.scope, switch_br_instruction->base.source_node, - target_value, new_else_block, case_count, cases, nullptr, nullptr); + target_value, new_else_block, case_count, cases, nullptr, nullptr, nullptr); result->value.type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } From d5df75f13b09512370df311545d6c89cede22149 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Nov 2018 15:29:21 -0500 Subject: [PATCH 041/190] copy elision: fix invalid "unused expression" error --- src/codegen.cpp | 4 +++- src/ir.cpp | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 833882856a7b..5770aec0e3fa 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6330,7 +6330,9 @@ static void do_code_gen(CodeGen *g) { ZigType *ptr_type = instruction->base.value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; - if (type_has_bits(child_type) && instruction->base.value.special == ConstValSpecialRuntime) { + if (type_has_bits(child_type) && instruction->base.value.special == ConstValSpecialRuntime && + child_type != g->builtin_types.entry_infer) + { instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, get_ptr_align(g, ptr_type)); } diff --git a/src/ir.cpp b/src/ir.cpp index ce22701b52ae..e0a9116225ab 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7319,7 +7319,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_if_optional_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeSwitchExpr: - return ir_gen_switch_expr(irb, scope, node, lval, result_loc); + return ir_gen_switch_expr(irb, scope, node, lval, + ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeCompTime: return ir_gen_comptime(irb, scope, node, lval, result_loc); case NodeTypeErrorType: @@ -20390,7 +20391,7 @@ static IrInstruction *ir_analyze_instruction_check_statement_is_void(IrAnalyze * if (type_is_invalid(statement_type)) return ira->codegen->invalid_instruction; - if (statement_type->id != ZigTypeIdVoid) { + if (statement_type->id != ZigTypeIdVoid && statement_type != ira->codegen->builtin_types.entry_infer) { ir_add_error(ira, &instruction->base, buf_sprintf("expression value is ignored")); } From a2eb0fe1a1ff6bdd10d5113b42e4bdefb0d214dc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Nov 2018 18:03:31 -0500 Subject: [PATCH 042/190] copy elision: catch with error union function call ```zig export fn entry() void { var x = ebar() catch |err| bar2(); } ``` ```llvm define void @entry() #2 !dbg !42 { Entry: %error_return_trace_addresses = alloca [30 x i64], align 8 %error_return_trace = alloca %StackTrace, align 8 %x = alloca %Bar, align 4 %err = alloca i16, align 2 %0 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 0 store i64 0, i64* %0, align 8 %1 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 1 %2 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 0 %3 = getelementptr inbounds [30 x i64], [30 x i64]* %error_return_trace_addresses, i64 0, i64 0 store i64* %3, i64** %2, align 8 %4 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 1 store i64 30, i64* %4, align 8 %5 = call fastcc i16 @ebar(%Bar* %x, %StackTrace* %error_return_trace), !dbg !54 store i16 %5, i16* %err, align 2, !dbg !54 %6 = load i16, i16* %err, align 2, !dbg !55 %7 = icmp ne i16 %6, 0, !dbg !55 br i1 %7, label %CatchError, label %CatchEnd, !dbg !55 CatchError: ; preds = %Entry call void @llvm.dbg.declare(metadata i16* %err, metadata !46, metadata !DIExpression()), !dbg !55 call fastcc void @bar2(%Bar* sret %x), !dbg !56 br label %CatchEnd, !dbg !58 CatchEnd: ; preds = %CatchError, %Entry call void @llvm.dbg.declare(metadata %Bar* %x, metadata !48, metadata !DIExpression()), !dbg !59 ret void, !dbg !60 } ``` --- src/analyze.cpp | 8 +++++++ src/codegen.cpp | 50 +++++++++++++++++++++++++++++++++++------ src/ir.cpp | 60 +++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 102 insertions(+), 16 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 82e6a8831ffa..76bc58724acd 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1137,6 +1137,14 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { ZigType *gen_return_type; if (is_async) { gen_return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); + } else if (fn_type_id->return_type->id == ZigTypeIdErrorUnion) { + ZigType *payload_type = fn_type_id->return_type->data.error_union.payload_type; + if (type_has_bits(payload_type)) { + ZigType *gen_type = get_pointer_to_type(g, payload_type, false); + gen_param_types.append(gen_type->type_ref); + param_di_types.append(gen_type->di_type); + } + gen_return_type = g->builtin_types.entry_global_error_set; } else if (!type_has_bits(fn_type_id->return_type)) { gen_return_type = g->builtin_types.entry_void; } else if (first_arg_return) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 5770aec0e3fa..9f7bb38ea0c4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -460,6 +460,21 @@ static void maybe_import_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkag } } +static bool cc_want_sret_attr(CallingConvention cc) { + switch (cc) { + case CallingConventionNaked: + zig_unreachable(); + case CallingConventionC: + case CallingConventionCold: + case CallingConventionStdcall: + return true; + case CallingConventionAsync: + case CallingConventionUnspecified: + return false; + } + zig_unreachable(); +} + static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { if (fn_table_entry->llvm_value) return fn_table_entry->llvm_value; @@ -597,10 +612,18 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { } else if (type_is_codegen_pointer(return_type)) { addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) { - addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); - if (cc == CallingConventionC) { + if (cc_want_sret_attr(cc)) { + addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias"); + } else { + ZigType *ret_type = fn_type->data.fn.fn_type_id.return_type; + if (ret_type->id == ZigTypeIdErrorUnion) { + uint64_t sz = type_size(g, ret_type->data.error_union.payload_type); + addLLVMArgAttrInt(fn_table_entry->llvm_value, 0, "dereferenceable", sz); + } else { + addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); + } } init_gen_i = 1; } @@ -2240,8 +2263,17 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { LLVMValueRef value = ir_llvm_value(g, return_instruction->value); ZigType *return_type = return_instruction->value->value.type; + FnTypeId *fn_type_id = &g->cur_fn->type_entry->data.fn.fn_type_id; - if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { + if (fn_type_id->return_type->id == ZigTypeIdErrorUnion) { + ConstExprValue *val = &return_instruction->value->value; + if (val->special == ConstValSpecialStatic) { + LLVMValueRef const_error_val = gen_const_val(g, val->data.x_err_union.error_set, ""); + LLVMBuildRet(g->builder, const_error_val); + } else { + zig_panic("TODO"); + } + } else if (want_first_arg_sret(g, fn_type_id)) { // Assume that the result location mechanism populated the value, // unless the value is a comptime const. if (return_instruction->value->value.special == ConstValSpecialStatic) { @@ -3466,7 +3498,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr CallingConvention cc = fn_type->data.fn.fn_type_id.cc; - bool first_arg_ret = ret_has_bits && want_first_arg_sret(g, fn_type_id); + bool is_error_union = src_return_type->id == ZigTypeIdErrorUnion; + bool first_arg_ret = (is_error_union && type_has_bits(src_return_type->data.error_union.payload_type)) || + (ret_has_bits && want_first_arg_sret(g, fn_type_id)); bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id); bool is_var_args = fn_type_id->is_var_args; ZigList gen_param_values = {}; @@ -3537,8 +3571,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } else if (!ret_has_bits) { return nullptr; } else if (first_arg_ret) { - set_call_instr_sret(g, result); - return result_ptr; + if (cc_want_sret_attr(cc) || src_return_type->id != ZigTypeIdErrorUnion) { + set_call_instr_sret(g, result); + } + return is_error_union ? result : result_ptr; } else if (handle_is_ptr(src_return_type)) { auto store_instr = LLVMBuildStore(g->builder, result, result_ptr); LLVMSetAlignment(store_instr, LLVMGetAlignment(result_ptr)); @@ -3785,8 +3821,8 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, assert(maybe_type->id == ZigTypeIdOptional); ZigType *child_type = maybe_type->data.maybe.child_type; LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value); - LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { + LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); diff --git a/src/ir.cpp b/src/ir.cpp index e0a9116225ab..adc02c692f6c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3183,9 +3183,29 @@ static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, zig_unreachable(); } +static IrInstruction *ir_gen_multi(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc, IrInstruction *value) +{ + switch (lval) { + case LValNone: + case LValPtr: + return ir_gen_result(irb, scope, node, lval, result_loc); + case LValErrorUnion: + case LValOptional: { + IrInstruction *err_alloca = ir_build_alloca_src(irb, scope, value->source_node, + nullptr, nullptr, "err"); + ir_build_store_ptr(irb, scope, value->source_node, err_alloca, value); + return err_alloca; + } + } + zig_unreachable(); +} + static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, IrInstruction *result_loc, IrInstruction *value) { + if (value == irb->codegen->invalid_instruction) + return value; if (result_loc != nullptr) { ir_build_store_ptr(irb, scope, node, result_loc, value); if (lval != LValNone) { @@ -3193,7 +3213,18 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, return ir_gen_result(irb, scope, node, lval, result_loc); } } - return ir_lval_wrap(irb, scope, value, lval); + switch (lval) { + case LValPtr: + // We needed a pointer to a value, but we got a value. So we create + // an instruction which just makes a pointer of it. + return ir_build_ref(irb, scope, value->source_node, value, false, false); + case LValNone: + return value; + case LValOptional: + case LValErrorUnion: + zig_panic("TODO"); + } + zig_unreachable(); } static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -3208,7 +3239,7 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV case LValErrorUnion: { // ptr points to an error union; // result_loc points to the result payload - // must return the error code + // must return pointer to the error code IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, ptr, false); ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); return ir_build_error_union_field_error_set(irb, scope, node, ptr); @@ -5042,7 +5073,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node // it turns out to be an implicit cast. IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr, result_loc); - return ir_lval_wrap(irb, scope, fn_call, lval); + return ir_gen_multi(irb, scope, node, lval, result_loc, fn_call); } for (size_t i = 0; i < arg_count; i += 1) { @@ -5063,7 +5094,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr, result_loc); - return ir_lval_wrap(irb, scope, fn_call, lval); + return ir_gen_multi(irb, scope, node, lval, result_loc, fn_call); } static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -5135,7 +5166,7 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * return value; // We needed a pointer to a value, but we got a value. So we create - // an instruction which just makes a const pointer of it. + // an instruction which just makes a pointer of it. return ir_build_ref(irb, scope, value->source_node, value, false, false); } @@ -7336,7 +7367,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSliceExpr: return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node, result_loc), lval); case NodeTypeUnwrapErrorExpr: - return ir_gen_catch(irb, scope, node, lval, result_loc); + return ir_gen_catch(irb, scope, node, lval, + ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeContainerDecl: return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: @@ -14177,6 +14209,16 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *casted_result_loc = nullptr; + ZigType *scalar_result_type; + ZigType *payload_result_type; + if (return_type->id == ZigTypeIdErrorUnion) { + scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); + payload_result_type = return_type->data.error_union.payload_type; + } else { + payload_result_type = return_type; + scalar_result_type = return_type; + } + if (call_instruction->result_loc != nullptr) { IrInstruction *prev_result_loc = call_instruction->result_loc->child; if (type_is_invalid(prev_result_loc->value.type)) @@ -14190,7 +14232,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } } - casted_result_loc = ir_implicit_cast_result(ira, prev_result_loc, return_type, false); + casted_result_loc = ir_implicit_cast_result(ira, prev_result_loc, payload_result_type, false); if (type_is_invalid(casted_result_loc->value.type)) return ira->codegen->invalid_instruction; } @@ -14199,9 +14241,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call call_instruction->base.scope, call_instruction->base.source_node, fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack, casted_result_loc); - new_call_instruction->value.type = return_type; + new_call_instruction->value.type = scalar_result_type; - if (!handle_is_ptr(return_type) && casted_result_loc != nullptr) { + if (return_type == payload_result_type && !handle_is_ptr(return_type) && casted_result_loc != nullptr) { ir_analyze_store_ptr(ira, &call_instruction->base, casted_result_loc, new_call_instruction); } From f5c765478a10143426580ee6c879294737c218de Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 10 Nov 2018 22:28:42 -0500 Subject: [PATCH 043/190] copy elision: implicit `*[N]T` to `[]T` --- src/all_types.hpp | 10 +- src/codegen.cpp | 17 ++++ src/ir.cpp | 232 +++++++++++++++++++++++++++++++++------------- src/ir_print.cpp | 16 +++- 4 files changed, 210 insertions(+), 65 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index e7092b9e3f35..58cf08d6fd4b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2194,6 +2194,7 @@ enum IrInstructionId { IrInstructionIdResultOptionalPayload, IrInstructionIdResultErrorUnionPayload, IrInstructionIdResultErrorUnionCode, + IrInstructionIdResultSlicePtr, IrInstructionIdResultReturn, IrInstructionIdResultBytesToSlice, IrInstructionIdResultSliceToBytes, @@ -2448,6 +2449,7 @@ struct IrInstructionCall { IrInstruction *async_allocator; IrInstruction *new_stack; IrInstruction *result_loc; + IrInstruction *first_arg_result_loc; FnInline fn_inline; bool is_async; bool is_comptime; @@ -3037,7 +3039,6 @@ struct IrInstructionDeclRef { IrInstruction base; Tld *tld; - LVal lval; }; struct IrInstructionPanic { @@ -3339,6 +3340,13 @@ struct IrInstructionResultSliceToBytes { IrInstruction *prev_result_loc; }; +struct IrInstructionResultSlicePtr { + IrInstruction base; + + IrInstruction *prev_result_loc; + uint64_t len; +}; + struct IrInstructionResultReturn { IrInstruction base; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 9f7bb38ea0c4..ad5c1fa6d133 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5098,6 +5098,21 @@ static LLVMValueRef ir_render_result_optional_payload(CodeGen *g, IrExecutable * return LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_child_index, ""); } +static LLVMValueRef ir_render_result_slice_ptr(CodeGen *g, IrExecutable *executable, + IrInstructionResultSlicePtr *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + assert(instruction->prev_result_loc->value.type->id == ZigTypeIdPointer); + ZigType *slice_type = instruction->prev_result_loc->value.type->data.pointer.child_type; + assert(slice_type->id == ZigTypeIdStruct && slice_type->data.structure.is_slice); + size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, (unsigned)len_field_index, ""); + LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, instruction->len, false); + gen_store_untyped(g, len_val, len_field_ptr, 0, false); + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; + return LLVMBuildStructGEP(g->builder, prev_result_loc, (unsigned)ptr_field_index, ""); +} + static LLVMValueRef ir_render_result_error_union_payload(CodeGen *g, IrExecutable *executable, IrInstructionResultErrorUnionPayload *instruction) { @@ -5379,6 +5394,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_result_return(g, executable, (IrInstructionResultReturn *)instruction); case IrInstructionIdResultOptionalPayload: return ir_render_result_optional_payload(g, executable, (IrInstructionResultOptionalPayload *)instruction); + case IrInstructionIdResultSlicePtr: + return ir_render_result_slice_ptr(g, executable, (IrInstructionResultSlicePtr *)instruction); case IrInstructionIdResultErrorUnionPayload: return ir_render_result_error_union_payload(g, executable, (IrInstructionResultErrorUnionPayload *)instruction); case IrInstructionIdResultErrorUnionCode: diff --git a/src/ir.cpp b/src/ir.cpp index adc02c692f6c..56b91901f723 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -879,6 +879,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionResultOptionalPa return IrInstructionIdResultOptionalPayload; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSlicePtr *) { + return IrInstructionIdResultSlicePtr; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionResultReturn *) { return IrInstructionIdResultReturn; } @@ -1238,7 +1242,7 @@ static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, Ast static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, - IrInstruction *new_stack, IrInstruction *result_loc) + IrInstruction *new_stack, IrInstruction *result_loc, IrInstruction *first_arg_result_loc) { IrInstructionCall *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; @@ -1251,6 +1255,7 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->async_allocator = async_allocator; call_instruction->new_stack = new_stack; call_instruction->result_loc = result_loc; + call_instruction->first_arg_result_loc = first_arg_result_loc; if (fn_ref) ir_ref_instruction(fn_ref, irb->current_basic_block); for (size_t i = 0; i < arg_count; i += 1) @@ -1258,6 +1263,7 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc if (async_allocator != nullptr) ir_ref_instruction(async_allocator, irb->current_basic_block); if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); + if (first_arg_result_loc != nullptr) ir_ref_instruction(first_arg_result_loc, irb->current_basic_block); return &call_instruction->base; } @@ -2359,13 +2365,10 @@ static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, - Tld *tld, LVal lval) -{ +static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, Tld *tld) { IrInstructionDeclRef *instruction = ir_build_instruction( irb, scope, source_node); instruction->tld = tld; - instruction->lval = lval; return &instruction->base; } @@ -2820,6 +2823,18 @@ static IrInstruction *ir_build_result_optional_payload(IrBuilder *irb, Scope *sc return &instruction->base; } +static IrInstruction *ir_build_result_slice_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, uint64_t len) +{ + IrInstructionResultSlicePtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->len = len; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_result_error_union_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *prev_result_loc) { @@ -3232,7 +3247,9 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV { switch (lval) { case LValPtr: - assert(result_loc == nullptr); + if (result_loc != nullptr) { + ir_build_store_ptr(irb, scope, node, result_loc, ptr); + } return ptr; case LValNone: return ir_build_load_ptr(irb, scope, node, ptr, result_loc); @@ -3863,8 +3880,22 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, } Tld *tld = find_decl(irb->codegen, scope, variable_name); - if (tld) - return ir_build_decl_ref(irb, scope, node, tld, lval); + if (tld) { + switch (tld->id) { + case TldIdContainer: + case TldIdCompTime: + zig_unreachable(); + case TldIdVar: { + IrInstruction *var_ptr = ir_build_decl_ref(irb, scope, node, tld); + return ir_gen_ptr(irb, scope, node, lval, result_loc, var_ptr); + } + case TldIdFn: { + IrInstruction *fn_inst = ir_build_decl_ref(irb, scope, node, tld); + return ir_gen_value(irb, scope, node, lval, result_loc, fn_inst); + } + } + zig_unreachable(); + } if (node->owner->any_imports_failed) { // skip the error message since we had a failing import in this file @@ -4803,7 +4834,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - fn_inline, false, nullptr, nullptr, result_loc); + fn_inline, false, nullptr, nullptr, result_loc, nullptr); return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdNewStackCall: @@ -4835,7 +4866,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, - false, FnInlineAuto, false, nullptr, new_stack, result_loc); + false, FnInlineAuto, false, nullptr, new_stack, result_loc, nullptr); return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdTypeId: @@ -5072,7 +5103,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node // In the analysis, this call instruction will be a simple LoadPtr instruction if // it turns out to be an implicit cast. IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - FnInlineAuto, false, nullptr, nullptr, result_loc); + FnInlineAuto, false, nullptr, nullptr, result_loc, arg_result_loc); return ir_gen_multi(irb, scope, node, lval, result_loc, fn_call); } @@ -5093,7 +5124,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, - is_async, async_allocator, nullptr, result_loc); + is_async, async_allocator, nullptr, result_loc, nullptr); return ir_gen_multi(irb, scope, node, lval, result_loc, fn_call); } @@ -5250,7 +5281,9 @@ static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_bool_not(irb, scope, node, value); } -static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypePrefixOpExpr); PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; @@ -5270,7 +5303,10 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - return ir_lval_wrap(irb, scope, ir_gen_node(irb, expr_node, scope, LValPtr, nullptr), lval); + IrInstruction *inst = ir_gen_node(irb, expr_node, scope, LValPtr, result_loc); + if (inst == irb->codegen->invalid_instruction) + return inst; + return ir_gen_result(irb, scope, expr_node, lval, result_loc); } } zig_unreachable(); @@ -7289,7 +7325,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_if_bool_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); case NodeTypePrefixOpExpr: - return ir_gen_prefix_op_expr(irb, scope, node, lval); + return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc); case NodeTypeContainerInitExpr: return ir_gen_container_init_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); @@ -7641,7 +7677,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec args[0] = implicit_allocator_ptr; // self args[1] = mem_slice; // old_mem ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, - nullptr, nullptr, nullptr); + nullptr, nullptr, nullptr, nullptr); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false, nullptr); @@ -9983,6 +10019,74 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr return result; } +static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction *result_loc, + ZigType *ptr_to_array_type, uint64_t len) +{ + ZigType *slice_type = result_loc->value.type->data.pointer.child_type; + assert(is_slice(slice_type)); + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, slice_ptr_type, + false, result_loc->value.type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + if (instr_is_comptime(result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, result_loc, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *slice_val = const_ptr_pointee(ira->codegen, ptr_val); + assert(is_slice(slice_val->type)); + if (slice_val->special == ConstValSpecialUndef) { + slice_val->data.x_struct.fields = create_const_vals(2); + ConstExprValue *slice_ptr_val = &slice_val->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *slice_len_val = &slice_val->data.x_struct.fields[slice_len_index]; + + slice_ptr_val->type = slice_ptr_type; + slice_ptr_val->special = ConstValSpecialUndef; + ConstParent *slice_ptr_parent = get_const_val_parent(ira->codegen, slice_ptr_val); + if (slice_ptr_parent != nullptr) { + slice_ptr_parent->id = ConstParentIdStruct; + slice_ptr_parent->data.p_struct.struct_val = slice_val; + slice_ptr_parent->data.p_struct.field_index = slice_ptr_index; + } + + slice_len_val->type = ira->codegen->builtin_types.entry_usize; + slice_len_val->special = ConstValSpecialStatic; + bigint_init_unsigned(&slice_len_val->data.x_bigint, len); + ConstParent *slice_len_parent = get_const_val_parent(ira->codegen, slice_len_val); + if (slice_len_parent != nullptr) { + slice_len_parent->id = ConstParentIdStruct; + slice_len_parent->data.p_struct.struct_val = slice_val; + slice_len_parent->data.p_struct.field_index = slice_len_index; + } + slice_val->special = ConstValSpecialStatic; + } + assert(slice_val->data.x_struct.fields != nullptr); + + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_result_slice_ptr(&ira->new_irb, result_loc->scope, result_loc->source_node, + result_loc, len); + result->value.type = new_ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, result_loc, new_ptr_type); + } + ConstExprValue *result_val = &result->value; + result_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; + result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + result_val->data.x_ptr.data.base_struct.struct_val = slice_val; + result_val->data.x_ptr.data.base_struct.field_index = slice_ptr_index; + return result; + } + } + IrInstruction *result = ir_build_result_slice_ptr(&ira->new_irb, result_loc->scope, result_loc->source_node, + result_loc, len); + result->value.type = new_ptr_type; + return result; +} + static void init_if_undef_err_union(IrAnalyze *ira, ConstExprValue *error_union_val) { if (error_union_val->special != ConstValSpecialUndef) return; @@ -10954,25 +11058,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from *const [N]T to []const T - if (is_slice(wanted_type) && - actual_type->id == ZigTypeIdPointer && - actual_type->data.pointer.is_const && - actual_type->data.pointer.child_type->id == ZigTypeIdArray) - { - ZigType *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - assert(ptr_type->id == ZigTypeIdPointer); - - ZigType *array_type = actual_type->data.pointer.child_type; - - if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, - source_node, false).id == ConstCastResultIdOk) - { - return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); - } - } - // cast from [N]T to ?[]const T if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && @@ -11405,6 +11490,25 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *res return ir_analyze_ptr_cast(ira, result_loc, result_loc, new_ptr_type, result_loc); } + // cast from *[N]T to []T + if (is_slice(have_child_type) && + needed_child_type->id == ZigTypeIdPointer && + needed_child_type->data.pointer.ptr_len == PtrLenSingle && + needed_child_type->data.pointer.child_type->id == ZigTypeIdArray) + { + ZigType *slice_ptr_type = have_child_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = needed_child_type->data.pointer.child_type; + bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 + || !needed_child_type->data.pointer.is_const); + if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + array_type->data.array.child_type, source_node, + !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + return ir_analyze_result_slice_ptr(ira, result_loc, needed_child_type, array_type->data.array.len); + } + } + // cast from T to ?T if (have_child_type->id == ZigTypeIdOptional) { if (types_match_const_cast_only(ira, have_child_type->data.maybe.child_type, needed_child_type, source_node, @@ -13391,7 +13495,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, - async_allocator_inst, nullptr, nullptr); + async_allocator_inst, nullptr, nullptr, nullptr); result->value.type = async_return_type; return result; } @@ -13646,6 +13750,9 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source } if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { ptr->value.special = ConstValSpecialRuntime; + if (instr_is_comptime(uncasted_ptr) && uncasted_ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { + uncasted_ptr->value.special = ConstValSpecialRuntime; + } } else { ir_add_error(ira, source_instr, buf_sprintf("cannot store runtime value in compile time variable")); @@ -14111,7 +14218,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, - call_instruction->is_async, nullptr, casted_new_stack, nullptr); + call_instruction->is_async, nullptr, casted_new_stack, nullptr, nullptr); new_call_instruction->value.type = return_type; @@ -14240,7 +14347,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, - casted_new_stack, casted_result_loc); + casted_new_stack, casted_result_loc, nullptr); new_call_instruction->value.type = scalar_result_type; if (return_type == payload_result_type && !handle_is_ptr(return_type) && casted_result_loc != nullptr) { @@ -14273,14 +14380,30 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC return ira->codegen->invalid_instruction; } - // This is handled with the result location mechanism. So all we need to do here is - // a LoadPtr on the result location. IrInstruction *result_loc = call_instruction->result_loc->child; if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; if ((err = resolve_possible_alloca_inference(ira, result_loc, dest_type))) return ira->codegen->invalid_instruction; + if (is_slice(dest_type)) { + IrInstruction *first_arg_result_loc = call_instruction->first_arg_result_loc->child; + if (type_is_invalid(first_arg_result_loc->value.type)) + return ira->codegen->invalid_instruction; + // If this is a slice cast, this is the equivalent of a container init fields instruction. + // Deal with ConstPtrMutInfer. + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(first_arg_result_loc)) { + result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + result_loc->value.special = ConstValSpecialRuntime; + } + } + return ir_const_void(ira, &call_instruction->base); + } + + // This is handled with the result location mechanism. So all we need to do here is + // a LoadPtr on the result location. IrInstruction *deref = ir_get_deref(ira, &call_instruction->base, result_loc); if (type_is_invalid(deref->value.type)) return ira->codegen->invalid_instruction; @@ -20910,9 +21033,8 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstructionDeclRef *instruction) { Tld *tld = instruction->tld; - LVal lval = instruction->lval; - resolve_top_level_decl(ira->codegen, tld, lval == LValPtr, instruction->base.source_node); + resolve_top_level_decl(ira->codegen, tld, true, instruction->base.source_node); if (tld->resolution == TldResolutionInvalid) return ira->codegen->invalid_instruction; @@ -20920,27 +21042,17 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, case TldIdContainer: case TldIdCompTime: zig_unreachable(); - case TldIdVar: - { + case TldIdVar: { TldVar *tld_var = (TldVar *)tld; ZigVar *var = tld_var->var; - IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var); - if (type_is_invalid(var_ptr->value.type)) - return ira->codegen->invalid_instruction; - if (tld_var->extern_lib_name != nullptr) { add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node); } - if (lval == LValPtr) { - return var_ptr; - } else { - return ir_get_deref(ira, &instruction->base, var_ptr); - } + return ir_get_var_ptr(ira, &instruction->base, var); } - case TldIdFn: - { + case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; ZigFn *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry); @@ -20949,13 +21061,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, instruction->base.source_node); } - IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, fn_entry); - if (lval == LValPtr) { - return ir_get_ref(ira, &instruction->base, ref_instruction, true, false); - } else { - return ref_instruction; - } + return ir_create_const_fn(&ira->new_irb, instruction->base.scope, instruction->base.source_node, fn_entry); } } zig_unreachable(); @@ -21852,6 +21958,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdDeclVarGen: case IrInstructionIdAllocaGen: case IrInstructionIdResultOptionalPayload: + case IrInstructionIdResultSlicePtr: case IrInstructionIdResultErrorUnionPayload: case IrInstructionIdResultErrorUnionCode: zig_unreachable(); @@ -22361,6 +22468,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBoolToInt: case IrInstructionIdEnumToInt: case IrInstructionIdResultOptionalPayload: + case IrInstructionIdResultSlicePtr: case IrInstructionIdResultErrorUnionPayload: case IrInstructionIdResultErrorUnionCode: case IrInstructionIdResultReturn: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 7c157272e235..fb4113ad974d 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -227,6 +227,10 @@ static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) { } fprintf(irp->f, ") result="); ir_print_other_instruction(irp, call_instruction->result_loc); + if (call_instruction->first_arg_result_loc != nullptr) { + fprintf(irp->f, ", first_arg_result="); + ir_print_other_instruction(irp, call_instruction->first_arg_result_loc); + } } static void ir_print_cond_br(IrPrint *irp, IrInstructionCondBr *cond_br_instruction) { @@ -981,8 +985,7 @@ static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) { } static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) { - const char *ptr_str = (instruction->lval == LValPtr) ? "ptr " : ""; - fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name)); + fprintf(irp->f, "declref %s", buf_ptr(instruction->tld->name)); } static void ir_print_panic(IrPrint *irp, IrInstructionPanic *instruction) { @@ -1312,6 +1315,12 @@ static void ir_print_result_optional_payload(IrPrint *irp, IrInstructionResultOp fprintf(irp->f, ")"); } +static void ir_print_result_slice_ptr(IrPrint *irp, IrInstructionResultSlicePtr *instruction) { + fprintf(irp->f, "ResultSlicePtr("); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",len=%" ZIG_PRI_u64 ")", instruction->len); +} + static void ir_print_result_error_union_payload(IrPrint *irp, IrInstructionResultErrorUnionPayload *instruction) { fprintf(irp->f, "ResultErrorUnionPayload("); ir_print_other_instruction(irp, instruction->prev_result_loc); @@ -1821,6 +1830,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdResultOptionalPayload: ir_print_result_optional_payload(irp, (IrInstructionResultOptionalPayload *)instruction); break; + case IrInstructionIdResultSlicePtr: + ir_print_result_slice_ptr(irp, (IrInstructionResultSlicePtr *)instruction); + break; case IrInstructionIdResultErrorUnionPayload: ir_print_result_error_union_payload(irp, (IrInstructionResultErrorUnionPayload *)instruction); break; From aa1b99d58c9292db8c1d2866bcf7e7638c98ac22 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 14 Nov 2018 21:26:31 -0500 Subject: [PATCH 044/190] copy elision: multiple implicit cast of scalar ```zig export fn entry() void { var x = i32(u16(nine())); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca i32, align 4 %0 = call fastcc i8 @nine(), !dbg !48 %1 = zext i8 %0 to i16, !dbg !48 %2 = zext i16 %1 to i32, !dbg !48 store i32 %2, i32* %x, align 4, !dbg !49 call void @llvm.dbg.declare(metadata i32* %x, metadata !45, metadata !DIExpression()), !dbg !50 ret void, !dbg !51 } ``` --- src/ir.cpp | 103 +++++++++++++++++++++++++++++++---------------- src/ir_print.cpp | 2 +- 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e381eee4fc92..067eaaaca6d5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3203,8 +3203,9 @@ static IrInstruction *ir_gen_multi(IrBuilder *irb, Scope *scope, AstNode *node, { switch (lval) { case LValNone: + return value; case LValPtr: - return ir_gen_result(irb, scope, node, lval, result_loc); + return result_loc; case LValErrorUnion: case LValOptional: { IrInstruction *err_alloca = ir_build_alloca_src(irb, scope, value->source_node, @@ -11553,6 +11554,8 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *res { IrInstruction *cast1 = ir_implicit_cast_result(ira, result_loc, have_child_type->data.error_union.payload_type, allow_failure); + if (cast1 == nullptr) + return nullptr; if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; @@ -11566,7 +11569,7 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *res } if (allow_failure) { - return result_loc; + return nullptr; } ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, @@ -13722,6 +13725,8 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source } IrInstruction *ptr = ir_implicit_cast_result(ira, uncasted_ptr, uncasted_value->value.type, true); + if (ptr == nullptr) + ptr = uncasted_ptr; if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; @@ -14331,7 +14336,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call scalar_result_type = return_type; } - if (call_instruction->result_loc != nullptr) { + bool need_store_ptr = (return_type == payload_result_type) && !handle_is_ptr(return_type) && + call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr; + + if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { IrInstruction *prev_result_loc = call_instruction->result_loc->child; if (type_is_invalid(prev_result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -14344,7 +14352,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } } - casted_result_loc = ir_implicit_cast_result(ira, prev_result_loc, payload_result_type, false); + casted_result_loc = ir_implicit_cast_result(ira, prev_result_loc, payload_result_type, need_store_ptr); + if (casted_result_loc == nullptr) + casted_result_loc = prev_result_loc; if (type_is_invalid(casted_result_loc->value.type)) return ira->codegen->invalid_instruction; } @@ -14355,7 +14365,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call casted_new_stack, casted_result_loc, nullptr); new_call_instruction->value.type = scalar_result_type; - if (return_type == payload_result_type && !handle_is_ptr(return_type) && casted_result_loc != nullptr) { + if (need_store_ptr) { ir_analyze_store_ptr(ira, &call_instruction->base, casted_result_loc, new_call_instruction); } @@ -14385,34 +14395,53 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC return ira->codegen->invalid_instruction; } - IrInstruction *result_loc = call_instruction->result_loc->child; - if (type_is_invalid(result_loc->value.type)) - return ira->codegen->invalid_instruction; - if ((err = resolve_possible_alloca_inference(ira, result_loc, dest_type))) - return ira->codegen->invalid_instruction; - - if (is_slice(dest_type)) { - IrInstruction *first_arg_result_loc = call_instruction->first_arg_result_loc->child; - if (type_is_invalid(first_arg_result_loc->value.type)) + IrInstruction *first_arg_result_loc = call_instruction->first_arg_result_loc->child; + if (first_arg_result_loc != nullptr) { + IrInstruction *result_loc = call_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; - // If this is a slice cast, this is the equivalent of a container init fields instruction. - // Deal with ConstPtrMutInfer. - if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { - if (instr_is_comptime(first_arg_result_loc)) { - result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; - } else { - result_loc->value.special = ConstValSpecialRuntime; + if ((err = resolve_possible_alloca_inference(ira, result_loc, dest_type))) + return ira->codegen->invalid_instruction; + + if (is_slice(dest_type)) { + if (type_is_invalid(first_arg_result_loc->value.type)) + return ira->codegen->invalid_instruction; + // If this is a slice cast, this is the equivalent of a container init fields instruction. + // Deal with ConstPtrMutInfer. + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(first_arg_result_loc)) { + result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + result_loc->value.special = ConstValSpecialRuntime; + } } + return ir_const_void(ira, &call_instruction->base); } - return ir_const_void(ira, &call_instruction->base); + + // This is handled with the result location mechanism. So all we need to do here is + // a LoadPtr on the result location. + IrInstruction *deref = ir_get_deref(ira, &call_instruction->base, result_loc); + if (type_is_invalid(deref->value.type)) + return ira->codegen->invalid_instruction; + return ir_finish_anal(ira, deref); } + IrInstruction *arg_value = call_instruction->args[0]->child; + if (type_is_invalid(arg_value->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *result = ir_implicit_cast(ira, arg_value, dest_type); - // This is handled with the result location mechanism. So all we need to do here is - // a LoadPtr on the result location. - IrInstruction *deref = ir_get_deref(ira, &call_instruction->base, result_loc); - if (type_is_invalid(deref->value.type)) + if (call_instruction->result_loc == nullptr || call_instruction->result_loc->child == nullptr) { + return ir_finish_anal(ira, result); + } + + // we must store into the result loc + IrInstruction *result_loc = call_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + if ((err = resolve_possible_alloca_inference(ira, result_loc, dest_type))) return ira->codegen->invalid_instruction; - return ir_finish_anal(ira, deref); + ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, result); + return result; } else if (fn_ref->value.type->id == ZigTypeIdFn) { ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref); if (fn_table_entry == nullptr) @@ -21909,6 +21938,10 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; + if (!handle_is_ptr(dest_type)) { + return nullptr; + } + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->prev_result_loc->child, dest_type, false); if (type_is_invalid(new_result_loc->value.type)) @@ -22258,7 +22291,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *old_instruction) { IrInstruction *new_instruction = ir_analyze_instruction_nocast(ira, old_instruction); - assert(new_instruction->value.type != nullptr); + assert(new_instruction == nullptr || new_instruction->value.type != nullptr); old_instruction->child = new_instruction; return new_instruction; } @@ -22308,13 +22341,15 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ } IrInstruction *new_instruction = ir_analyze_instruction(ira, old_instruction); - if (type_is_invalid(new_instruction->value.type) && ir_should_inline(new_exec, old_instruction->scope)) { - return ira->codegen->builtin_types.entry_invalid; - } + if (new_instruction != nullptr) { + if (type_is_invalid(new_instruction->value.type) && ir_should_inline(new_exec, old_instruction->scope)) { + return ira->codegen->builtin_types.entry_invalid; + } - // unreachable instructions do their own control flow. - if (new_instruction->value.type->id == ZigTypeIdUnreachable) - continue; + // unreachable instructions do their own control flow. + if (new_instruction->value.type->id == ZigTypeIdUnreachable) + continue; + } ira->instruction_index += 1; } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index fb4113ad974d..9e01ec537701 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -881,7 +881,7 @@ static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { } static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) { - fprintf(irp->f, "@widenOrShorten("); + fprintf(irp->f, "WidenOrShorten("); ir_print_other_instruction(irp, instruction->target); fprintf(irp->f, ")"); } From 80fb0f662eff5eed0e77d7884ea5f4772301b008 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 15 Nov 2018 13:57:53 -0500 Subject: [PATCH 045/190] copy elision: fix a couple regressions --- src/ir.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 067eaaaca6d5..1079b69b3754 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5100,7 +5100,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node size_t arg_count = node->data.fn_call_expr.params.length; IrInstruction **args = allocate(arg_count); bool is_async = node->data.fn_call_expr.is_async; - if (arg_count == 1 && !is_async) { + if (arg_count == 1 && !is_async && result_loc != nullptr) { AstNode *arg_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg_result_loc = ir_build_first_arg_result_loc(irb, scope, arg_node, result_loc, fn_ref); args[0] = ir_gen_node(irb, arg_node, scope, LValNone, arg_result_loc); @@ -14395,7 +14395,8 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC return ira->codegen->invalid_instruction; } - IrInstruction *first_arg_result_loc = call_instruction->first_arg_result_loc->child; + IrInstruction *first_arg_result_loc = (call_instruction->first_arg_result_loc == nullptr) ? + nullptr : call_instruction->first_arg_result_loc->child; if (first_arg_result_loc != nullptr) { IrInstruction *result_loc = call_instruction->result_loc->child; if (type_is_invalid(result_loc->value.type)) @@ -15938,6 +15939,8 @@ static IrInstruction *ir_analyze_instruction_load_result(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) { IrInstruction *ptr = instruction->ptr->child; + if (ptr == nullptr) + return ir_const_void(ira, &instruction->base); if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; From 99a2e8b88fe83532e93080106d247c323db47f1f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 15 Nov 2018 15:11:00 -0500 Subject: [PATCH 046/190] copy elision: null result loc for addr-of ```zig export fn entry() void { if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { abort(); } } ``` ```llvm define void @entry() #2 !dbg !62 { Entry: %0 = atomicrmw xchg i8* @panicking, i8 1 seq_cst, !dbg !66 %1 = icmp eq i8 %0, 1, !dbg !68 br i1 %1, label %Then, label %Else, !dbg !68 Then: ; preds = %Entry call void @abort(), !dbg !69 unreachable, !dbg !69 Else: ; preds = %Entry br label %EndIf, !dbg !71 EndIf: ; preds = %Else ret void, !dbg !72 } ``` --- src/ir.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 1079b69b3754..72983b7cc99f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5287,6 +5287,12 @@ static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_bool_not(irb, scope, node, value); } +static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { + if (result_loc) + return result_loc; + return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); +} + static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, IrInstruction *result_loc) { @@ -5309,10 +5315,11 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - IrInstruction *inst = ir_gen_node(irb, expr_node, scope, LValPtr, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, result_loc); + IrInstruction *inst = ir_gen_node(irb, expr_node, scope, LValPtr, ensured_result_loc); if (inst == irb->codegen->invalid_instruction) return inst; - return ir_gen_result(irb, scope, expr_node, lval, result_loc); + return ir_gen_result(irb, scope, expr_node, lval, ensured_result_loc); } } zig_unreachable(); @@ -7291,12 +7298,6 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); } -static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { - if (result_loc) - return result_loc; - return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); -} - static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval, IrInstruction *result_loc) { From b81abfa559c982244e426e1a6ffbf3d346e588a8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 16 Nov 2018 11:30:08 -0500 Subject: [PATCH 047/190] copy elision: fix comptime fn call regression ```zig export fn entry() void { const T = Type(); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: ret void, !dbg !45 } ``` --- src/ir.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index f6deb143d4f7..0420421d9cc8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3284,7 +3284,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value; if (expr_node) { IrInstruction *return_result_loc = nullptr; - if (handle_is_ptr(fn_entry->type_entry->data.fn.fn_type_id.return_type)) { + ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + if (type_has_bits(return_type) && handle_is_ptr(return_type)) { return_result_loc = ir_build_result_return(irb, scope, node); } // Temporarily set this so that if we return a type it gets the name of the function @@ -13949,10 +13950,46 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; } + ZigType *scalar_result_type; + ZigType *payload_result_type; + if (return_type->id == ZigTypeIdErrorUnion) { + scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); + payload_result_type = return_type->data.error_union.payload_type; + } else { + payload_result_type = return_type; + scalar_result_type = return_type; + } + IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->value.type); // TODO should we use copy_const_val? new_instruction->value = result->value; - new_instruction->value.type = return_type; + new_instruction->value.type = scalar_result_type; + + if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { + IrInstruction *prev_result_loc = call_instruction->result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + if (instr_is_comptime(prev_result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, prev_result_loc, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + ptr_val->special = ConstValSpecialStatic; + } + } + + bool need_store_ptr = (return_type == payload_result_type) && + (!type_has_bits(return_type) || !handle_is_ptr(return_type)); + IrInstruction *casted_result_loc = ir_implicit_cast_result(ira, prev_result_loc, payload_result_type, need_store_ptr); + if (casted_result_loc == nullptr) + casted_result_loc = prev_result_loc; + if (type_is_invalid(casted_result_loc->value.type)) + return ira->codegen->invalid_instruction; + if (need_store_ptr) { + ir_analyze_store_ptr(ira, &call_instruction->base, casted_result_loc, new_instruction); + } + } + return ir_finish_anal(ira, new_instruction); } From 5f206368fcbc629fa497bd4bcb43c1ba4987ed71 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 16 Nov 2018 14:57:00 -0500 Subject: [PATCH 048/190] copy elision: fix regression: function call with 1 arg ```zig export fn entry() void { _ = raise(1); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %0 = call fastcc i64 @raise(i32 1), !dbg !45 ret void, !dbg !47 } ``` --- src/ir.cpp | 10 +++++++++- test.zig | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) delete mode 100644 test.zig diff --git a/src/ir.cpp b/src/ir.cpp index 0420421d9cc8..8061cd382137 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3285,7 +3285,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, if (expr_node) { IrInstruction *return_result_loc = nullptr; ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; - if (type_has_bits(return_type) && handle_is_ptr(return_type)) { + if (return_type != nullptr && type_has_bits(return_type) && handle_is_ptr(return_type)) { return_result_loc = ir_build_result_return(irb, scope, node); } // Temporarily set this so that if we return a type it gets the name of the function @@ -11397,6 +11397,7 @@ static Error resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *al Error err; if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return err; + assert(alloca->base.value.data.x_ptr.special != ConstPtrSpecialInvalid); alloca->base.value.data.x_ptr.data.ref.pointee->type = child_type; alloca->base.value.type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, PtrLenSingle, alloca->align, 0, 0); @@ -21948,8 +21949,15 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira // Result of this instruction should be the result location for the first argument of the function call. // This means it should be a stack allocation. + ConstExprValue *pointee = create_const_vals(1); + pointee->special = ConstValSpecialUndef; + IrInstructionAllocaGen *result = ir_create_alloca_gen(&ira->new_irb, instruction->base.scope, instruction->base.source_node, 0, ""); + result->base.value.special = ConstValSpecialStatic; + result->base.value.data.x_ptr.special = ConstPtrSpecialRef; + result->base.value.data.x_ptr.mut = ConstPtrMutInfer; + result->base.value.data.x_ptr.data.ref.pointee = pointee; assert(fn_ref->value.type->id == ZigTypeIdFn); ZigType *param_type = fn_ref->value.type->data.fn.fn_type_id.param_info[0].type; diff --git a/test.zig b/test.zig deleted file mode 100644 index 566bb24d16bc..000000000000 --- a/test.zig +++ /dev/null @@ -1 +0,0 @@ -const a = [][]const u8{}; From c30908dbaccb6c2613989b63160b539247e454eb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 16 Nov 2018 16:57:12 -0500 Subject: [PATCH 049/190] copy elision: slice syntax with no result loc ```zig export fn entry() void { const fmt: []const u8 = "oaeu"; var i: usize = 1; slice(fmt[0..i]); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %i = alloca i64, align 8 %0 = alloca %"[]u8", align 8 call void @llvm.dbg.declare(metadata %"[]u8"* @3, metadata !45, metadata !DIExpression()), !dbg !49 store i64 1, i64* %i, align 8, !dbg !50 call void @llvm.dbg.declare(metadata i64* %i, metadata !47, metadata !DIExpression()), !dbg !50 %1 = load i64, i64* %i, align 8, !dbg !51 %2 = load i64, i64* getelementptr inbounds (%"[]u8", %"[]u8"* @3, i32 0, i32 1), align 8, !dbg !53 %3 = icmp ule i64 0, %1, !dbg !53 br i1 %3, label %BoundsCheckOk, label %BoundsCheckFail, !dbg !53 BoundsCheckFail: ; preds = %Entry tail call fastcc void @panic(%"[]u8"* @7, %StackTrace* null), !dbg !53 unreachable, !dbg !53 BoundsCheckOk: ; preds = %Entry %4 = icmp ule i64 %1, %2, !dbg !53 br i1 %4, label %BoundsCheckOk2, label %BoundsCheckFail1, !dbg !53 BoundsCheckFail1: ; preds = %BoundsCheckOk tail call fastcc void @panic(%"[]u8"* @7, %StackTrace* null), !dbg !53 unreachable, !dbg !53 BoundsCheckOk2: ; preds = %BoundsCheckOk %5 = load i8*, i8** getelementptr inbounds (%"[]u8", %"[]u8"* @3, i32 0, i32 0), align 8, !dbg !53 %6 = getelementptr inbounds %"[]u8", %"[]u8"* %0, i32 0, i32 0, !dbg !53 %7 = getelementptr inbounds i8, i8* %5, i64 0, !dbg !53 store i8* %7, i8** %6, align 8, !dbg !53 %8 = getelementptr inbounds %"[]u8", %"[]u8"* %0, i32 0, i32 1, !dbg !53 %9 = sub nsw i64 %1, 0, !dbg !53 store i64 %9, i64* %8, align 8, !dbg !53 call fastcc void @slice(%"[]u8"* %0), !dbg !54 ret void, !dbg !55 } ``` --- src/codegen.cpp | 2 ++ src/ir.cpp | 36 +++++++++++++++++++++++++++++------- src/ir_print.cpp | 1 + 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 7069f57fa3a1..06db8d6cea5a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4348,6 +4348,7 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + assert(LLVMGetTypeKind(LLVMTypeOf(tmp_struct_ptr)) == LLVMPointerTypeKind); bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); @@ -4421,6 +4422,7 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst assert(array_type->data.structure.is_slice); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); + assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind); size_t ptr_index = array_type->data.structure.fields[slice_ptr_index].gen_index; assert(ptr_index != SIZE_MAX); diff --git a/src/ir.cpp b/src/ir.cpp index 8061cd382137..57d985fc057f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6517,8 +6517,11 @@ static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode return ir_build_const_void(irb, parent_scope, node); } -static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { +static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeSliceExpr); + assert(result_loc != nullptr); AstNodeSliceExpr *slice_expr = &node->data.slice_expr; AstNode *array_node = slice_expr->array_ref_expr; @@ -6542,7 +6545,8 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, end_value = nullptr; } - return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true, result_loc); + IrInstruction *result = ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true, result_loc); + return ir_gen_multi(irb, scope, node, lval, result_loc, result); } static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval, @@ -7398,7 +7402,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeDefer: return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval); case NodeTypeSliceExpr: - return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node, result_loc), lval); + return ir_gen_slice(irb, scope, node, lval, + ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeUnwrapErrorExpr: return ir_gen_catch(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); @@ -19413,14 +19418,12 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice *instruction) { + Error err; + IrInstruction *ptr_ptr = instruction->ptr->child; if (type_is_invalid(ptr_ptr->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value.type)) - return ira->codegen->invalid_instruction; - ZigType *ptr_type = ptr_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *array_type = ptr_type->data.pointer.child_type; @@ -19487,6 +19490,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; } + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + if ((err = resolve_possible_alloca_inference(ira, result_loc, return_type))) + return ira->codegen->invalid_instruction; + if (instr_is_comptime(ptr_ptr) && value_is_comptime(&casted_start->value) && (!end || value_is_comptime(&end->value))) @@ -19637,6 +19646,10 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; } + if (result_loc->value.special != ConstValSpecialRuntime) { + zig_panic("TODO slice with comptime ptr and values"); + } + IrInstruction *result = ir_const(ira, &instruction->base, return_type); ConstExprValue *out_val = &result->value; out_val->data.x_struct.fields = create_const_vals(2); @@ -19690,6 +19703,15 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction return result; } + if (instr_is_comptime(result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, result_loc, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + ptr_val->special = ConstValSpecialRuntime; + } + } + IrInstruction *new_instruction = ir_build_slice(&ira->new_irb, instruction->base.scope, instruction->base.source_node, ptr_ptr, casted_start, end, instruction->safety_check_on, diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 291e161372cd..5a5e378b66b8 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -203,6 +203,7 @@ static void ir_print_cast(IrPrint *irp, IrInstructionCast *cast_instruction) { } static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) { + fprintf(irp->f, "Call "); if (call_instruction->is_async) { fprintf(irp->f, "async"); if (call_instruction->async_allocator != nullptr) { From d534f38b9ee9ba18e727049c9b431d25f9bce17e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 19 Nov 2018 12:10:19 -0500 Subject: [PATCH 050/190] copy elision: remove LoadResult and StoreResult instructions --- src/all_types.hpp | 16 --------- src/codegen.cpp | 4 --- src/ir.cpp | 90 +++++------------------------------------------ src/ir_print.cpp | 18 ---------- 4 files changed, 8 insertions(+), 120 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index e67fe74a198f..e029a6e43b75 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2194,8 +2194,6 @@ enum IrInstructionId { IrInstructionIdResultParam, IrInstructionIdResultPtrCast, IrInstructionIdResultCast, - IrInstructionIdLoadResult, - IrInstructionIdStoreResult, IrInstructionIdAllocaSrc, IrInstructionIdAllocaGen, IrInstructionIdAssertNonError, @@ -2368,20 +2366,6 @@ struct IrInstructionStorePtr { IrInstruction *value; }; -struct IrInstructionLoadResult { - IrInstruction base; - - IrInstruction *ptr; - IrInstruction *result_loc; -}; - -struct IrInstructionStoreResult { - IrInstruction base; - - IrInstruction *result_loc; - IrInstruction *value; -}; - struct IrInstructionFieldPtr { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 06db8d6cea5a..a6b98312d186 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5411,10 +5411,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, zig_panic("TODO"); case IrInstructionIdResultPtrCast: zig_panic("TODO"); - case IrInstructionIdLoadResult: - zig_panic("TODO"); - case IrInstructionIdStoreResult: - zig_panic("TODO"); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index 57d985fc057f..deb397a863a3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -363,14 +363,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) { return IrInstructionIdStorePtr; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadResult *) { - return IrInstructionIdLoadResult; -} - -static constexpr IrInstructionId ir_instruction_id(IrInstructionStoreResult *) { - return IrInstructionIdStoreResult; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldPtr *) { return IrInstructionIdFieldPtr; } @@ -1461,32 +1453,6 @@ static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } -static IrInstruction *ir_build_load_result(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *ptr, IrInstruction *result_loc) -{ - IrInstructionLoadResult *instruction = ir_build_instruction(irb, scope, source_node); - instruction->ptr = ptr; - instruction->result_loc = result_loc; - - ir_ref_instruction(ptr, irb->current_basic_block); - ir_ref_instruction(result_loc, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstruction *ir_build_store_result(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *result_loc, IrInstruction *value) -{ - IrInstructionStoreResult *instruction = ir_build_instruction(irb, scope, source_node); - instruction->result_loc = result_loc; - instruction->value = value; - - ir_ref_instruction(result_loc, irb->current_basic_block); - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - static IrInstruction *ir_build_typeof(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionTypeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; @@ -3920,10 +3886,8 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, subscript_instruction, true, PtrLenSingle); - if (lval == LValPtr) - return ptr_instruction; - return ir_build_load_result(irb, scope, node, ptr_instruction, result_loc); + return ir_gen_ptr(irb, scope, node, lval, result_loc, ptr_instruction); } static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -4415,9 +4379,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - if (lval == LValPtr) - return result_loc; - return ir_build_load_result(irb, scope, node, result_loc, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } case BuiltinFnIdToBytes: { @@ -4428,9 +4390,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - if (lval == LValPtr) - return result_loc; - return ir_build_load_result(irb, scope, node, result_loc, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } case BuiltinFnIdIntToFloat: { @@ -4601,12 +4561,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value); - - if (lval == LValPtr) - return ptr_instruction; - - return ir_build_load_result(irb, scope, node, ptr_instruction, result_loc); + IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, + arg0_value, arg1_value); + return ir_gen_ptr(irb, scope, node, lval, result_loc, ptr_instruction); } case BuiltinFnIdTypeInfo: { @@ -4701,10 +4658,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - if (lval == LValPtr) - return result_loc; - - return ir_build_load_result(irb, scope, node, result_loc, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } case BuiltinFnIdIntToPtr: { @@ -7566,7 +7520,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false, addrs_slice_ptr); - ir_build_store_result(irb, scope, node, addrs_slice_ptr, slice_value); + ir_build_store_ptr(irb, scope, node, addrs_slice_ptr, slice_value); } @@ -15960,10 +15914,6 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc } } -static IrInstruction *ir_analyze_instruction_load_result(IrAnalyze *ira, IrInstructionLoadResult *instruction) { - zig_panic("TODO"); -} - static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) { IrInstruction *ptr = instruction->ptr->child; if (ptr == nullptr) @@ -15978,24 +15928,6 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); } -static IrInstruction *ir_analyze_store_result(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, - IrInstruction *uncasted_result_loc) -{ - zig_panic("TODO delete this instruction"); -} - -static IrInstruction *ir_analyze_instruction_store_result(IrAnalyze *ira, IrInstructionStoreResult *instruction) { - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - - IrInstruction *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value.type)) - return ira->codegen->invalid_instruction; - - return ir_analyze_store_result(ira, &instruction->base, value, result_loc); -} - static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) { IrInstruction *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value.type)) @@ -22042,10 +21974,6 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); case IrInstructionIdStorePtr: return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction); - case IrInstructionIdLoadResult: - return ir_analyze_instruction_load_result(ira, (IrInstructionLoadResult *)instruction); - case IrInstructionIdStoreResult: - return ir_analyze_instruction_store_result(ira, (IrInstructionStoreResult *)instruction); case IrInstructionIdElemPtr: return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction); case IrInstructionIdVarPtr: @@ -22440,8 +22368,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdMergeErrRetTraces: case IrInstructionIdMarkErrRetTracePtr: case IrInstructionIdAtomicRmw: - case IrInstructionIdLoadResult: - case IrInstructionIdStoreResult: case IrInstructionIdAssertNonError: case IrInstructionIdContainerInitFields: case IrInstructionIdContainerInitList: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 5a5e378b66b8..75ea96bbe0d5 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1357,18 +1357,6 @@ static void ir_print_result_cast(IrPrint *irp, IrInstructionResultCast *instruct fprintf(irp->f, ")"); } -static void ir_print_load_result(IrPrint *irp, IrInstructionLoadResult *instruction) { - fprintf(irp->f, "LoadResult"); -} - -static void ir_print_store_result(IrPrint *irp, IrInstructionStoreResult *instruction) { - fprintf(irp->f, "StoreResult(result="); - ir_print_other_instruction(irp, instruction->result_loc); - fprintf(irp->f, ",value="); - ir_print_other_instruction(irp, instruction->value); - fprintf(irp->f, ")"); -} - static void ir_print_alloca_src(IrPrint *irp, IrInstructionAllocaSrc *instruction) { fprintf(irp->f, "AllocaSrc(ty="); ir_print_other_instruction(irp, instruction->child_type); @@ -1850,12 +1838,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdResultCast: ir_print_result_cast(irp, (IrInstructionResultCast *)instruction); break; - case IrInstructionIdLoadResult: - ir_print_load_result(irp, (IrInstructionLoadResult *)instruction); - break; - case IrInstructionIdStoreResult: - ir_print_store_result(irp, (IrInstructionStoreResult *)instruction); - break; case IrInstructionIdAllocaSrc: ir_print_alloca_src(irp, (IrInstructionAllocaSrc *)instruction); break; From 0e41e9098dfadea68ea1a3a36cc3b2c6a8d1229e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 19 Nov 2018 16:51:22 -0500 Subject: [PATCH 051/190] copy elision: remove all uses of ir_lval_wrap --- src/all_types.hpp | 1 - src/ir.cpp | 378 +++++++++++++++++++++++++--------------------- 2 files changed, 207 insertions(+), 172 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index e029a6e43b75..4c8477497708 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3022,7 +3022,6 @@ struct IrInstructionTagName { IrInstruction base; IrInstruction *target; - IrInstruction *result_loc; }; struct IrInstructionTagType { diff --git a/src/ir.cpp b/src/ir.cpp index deb397a863a3..02fe8cc681b3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -148,7 +148,6 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type); static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var); static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); -static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align); static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align); static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val); @@ -2338,14 +2337,12 @@ static IrInstruction *ir_build_panic(IrBuilder *irb, Scope *scope, AstNode *sour } static IrInstruction *ir_build_tag_name(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *target, IrInstruction *result_loc) + IrInstruction *target) { IrInstructionTagName *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; - instruction->result_loc = result_loc; ir_ref_instruction(target, irb->current_basic_block); - ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -3421,7 +3418,7 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n return var; } -static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node, +static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node, LVal lval, IrInstruction *result_loc) { assert(block_node->type == NodeTypeBlock); @@ -3441,7 +3438,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode if (block_node->data.block.statements.length == 0) { // {} - return ir_build_const_void(irb, child_scope, block_node); + return ir_gen_value(irb, parent_scope, block_node, lval, result_loc, + ir_build_const_void(irb, child_scope, block_node)); } if (block_node->data.block.name != nullptr) { @@ -3449,7 +3447,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode scope_block->incoming_blocks = &incoming_blocks; scope_block->incoming_values = &incoming_values; scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); - scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, ir_should_inline(irb->exec, parent_scope)); + scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, + ir_should_inline(irb->exec, parent_scope)); } bool is_continuation_unreachable = false; @@ -3484,20 +3483,21 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode } ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); - return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); - } else { - incoming_blocks.append(irb->current_basic_block); - incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node))); + return ir_gen_result(irb, parent_scope, block_node, lval, result_loc); } if (block_node->data.block.name != nullptr) { + IrInstruction *continuation_expr_result = ir_build_const_void(irb, parent_scope, block_node); + ir_build_store_ptr(irb, parent_scope, block_node, result_loc, continuation_expr_result); + ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime)); ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); - return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); + return ir_gen_result(irb, parent_scope, block_node, lval, result_loc); } else { ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); - return ir_mark_gen(ir_mark_gen(ir_build_const_void(irb, child_scope, block_node))); + return ir_gen_value(irb, parent_scope, block_node, lval, result_loc, + ir_mark_gen(ir_build_const_void(irb, child_scope, block_node))); } } @@ -3688,87 +3688,125 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, case BinOpTypeInvalid: zig_unreachable(); case BinOpTypeAssign: - return ir_lval_wrap(irb, scope, ir_gen_assign(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_assign(irb, scope, node)); case BinOpTypeAssignTimes: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMult), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpMult)); case BinOpTypeAssignTimesWrap: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap)); case BinOpTypeAssignDiv: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified)); case BinOpTypeAssignMod: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified)); case BinOpTypeAssignPlus: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAdd), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpAdd)); case BinOpTypeAssignPlusWrap: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap)); case BinOpTypeAssignMinus: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSub), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpSub)); case BinOpTypeAssignMinusWrap: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap)); case BinOpTypeAssignBitShiftLeft: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy)); case BinOpTypeAssignBitShiftRight: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy)); case BinOpTypeAssignBitAnd: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd)); case BinOpTypeAssignBitXor: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinXor), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpBinXor)); case BinOpTypeAssignBitOr: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpBinOr)); case BinOpTypeAssignMergeErrorSets: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets)); case BinOpTypeBoolOr: - return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_bool_or(irb, scope, node)); case BinOpTypeBoolAnd: - return ir_lval_wrap(irb, scope, ir_gen_bool_and(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_bool_and(irb, scope, node)); case BinOpTypeCmpEq: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq)); case BinOpTypeCmpNotEq: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq)); case BinOpTypeCmpLessThan: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan)); case BinOpTypeCmpGreaterThan: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan)); case BinOpTypeCmpLessOrEq: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq)); case BinOpTypeCmpGreaterOrEq: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq)); case BinOpTypeBinOr: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr)); case BinOpTypeBinXor: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor)); case BinOpTypeBinAnd: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd)); case BinOpTypeBitShiftLeft: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy)); case BinOpTypeBitShiftRight: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy)); case BinOpTypeAdd: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd)); case BinOpTypeAddWrap: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap)); case BinOpTypeSub: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSub), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpSub)); case BinOpTypeSubWrap: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap)); case BinOpTypeMult: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMult), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpMult)); case BinOpTypeMultWrap: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap)); case BinOpTypeDiv: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified)); case BinOpTypeMod: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified)); case BinOpTypeArrayCat: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat)); case BinOpTypeArrayMult: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult)); case BinOpTypeMergeErrorSets: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets)); + case BinOpTypeErrorUnion: + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_error_union(irb, scope, node)); + case BinOpTypeUnwrapOptional: return ir_gen_orelse(irb, scope, node, lval, result_loc); - case BinOpTypeErrorUnion: - return ir_lval_wrap(irb, scope, ir_gen_error_union(irb, scope, node), lval); } zig_unreachable(); } @@ -3829,7 +3867,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, } } else { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); - return ir_lval_wrap(irb, scope, value, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, value); } ScopeFnDef *crossed_fndef_scope; @@ -3986,7 +4024,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg; IrInstruction *type_of = ir_build_typeof(irb, scope, node, arg); - return ir_lval_wrap(irb, scope, type_of, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, type_of); } case BuiltinFnIdSetCold: { @@ -3996,7 +4034,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *set_cold = ir_build_set_cold(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_cold, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, set_cold); } case BuiltinFnIdSetRuntimeSafety: { @@ -4006,7 +4044,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_safety, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, set_safety); } case BuiltinFnIdSetFloatMode: { @@ -4016,7 +4054,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_float_mode, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, set_float_mode); } case BuiltinFnIdSizeof: { @@ -4026,7 +4064,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, size_of, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, size_of); } case BuiltinFnIdCtz: { @@ -4036,7 +4074,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, ctz, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ctz); } case BuiltinFnIdPopCount: { @@ -4046,7 +4084,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *instr = ir_build_pop_count(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, instr, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, instr); } case BuiltinFnIdClz: { @@ -4056,7 +4094,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *clz = ir_build_clz(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, clz, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, clz); } case BuiltinFnIdImport: { @@ -4066,12 +4104,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *import = ir_build_import(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, import, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, import); } case BuiltinFnIdCImport: { IrInstruction *c_import = ir_build_c_import(irb, scope, node); - return ir_lval_wrap(irb, scope, c_import, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, c_import); } case BuiltinFnIdCInclude: { @@ -4086,7 +4124,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } IrInstruction *c_include = ir_build_c_include(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, c_include, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, c_include); } case BuiltinFnIdCDefine: { @@ -4106,7 +4144,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } IrInstruction *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, c_define, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, c_define); } case BuiltinFnIdCUndef: { @@ -4121,7 +4159,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } IrInstruction *c_undef = ir_build_c_undef(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, c_undef, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, c_undef); } case BuiltinFnIdCompileErr: { @@ -4131,7 +4169,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *compile_err = ir_build_compile_err(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, compile_err, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, compile_err); } case BuiltinFnIdCompileLog: { @@ -4145,7 +4183,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } IrInstruction *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args); - return ir_lval_wrap(irb, scope, compile_log, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, compile_log); } case BuiltinFnIdErrName: { @@ -4155,7 +4193,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *err_name = ir_build_err_name(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, err_name, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, err_name); } case BuiltinFnIdEmbedFile: { @@ -4165,7 +4203,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *embed_file = ir_build_embed_file(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, embed_file, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, embed_file); } case BuiltinFnIdCmpxchgWeak: case BuiltinFnIdCmpxchgStrong: @@ -4203,7 +4241,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), nullptr, AtomicOrderUnordered, AtomicOrderUnordered, result_loc); - return ir_lval_wrap(irb, scope, cmpxchg, lval); + return ir_gen_multi(irb, scope, node, lval, result_loc, cmpxchg); } case BuiltinFnIdFence: { @@ -4213,7 +4251,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *fence = ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered); - return ir_lval_wrap(irb, scope, fence, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, fence); } case BuiltinFnIdDivExact: { @@ -4228,7 +4266,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdDivTrunc: { @@ -4243,7 +4281,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdDivFloor: { @@ -4258,7 +4296,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdRem: { @@ -4273,7 +4311,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdMod: { @@ -4288,7 +4326,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdSqrt: { @@ -4303,7 +4341,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, ir_sqrt, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_sqrt); } case BuiltinFnIdTruncate: { @@ -4318,7 +4356,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, truncate, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, truncate); } case BuiltinFnIdIntCast: { @@ -4333,7 +4371,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdFloatCast: { @@ -4348,7 +4386,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdErrSetCast: { @@ -4363,7 +4401,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdFromBytes: { @@ -4405,7 +4443,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdFloatToInt: { @@ -4420,7 +4458,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdErrToInt: { @@ -4430,7 +4468,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *result = ir_build_err_to_int(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdIntToErr: { @@ -4440,7 +4478,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *result = ir_build_int_to_err(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdBoolToInt: { @@ -4450,7 +4488,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *result = ir_build_bool_to_int(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdIntType: { @@ -4465,7 +4503,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, int_type, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, int_type); } case BuiltinFnIdMemcpy: { @@ -4485,7 +4523,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg2_value; IrInstruction *ir_memcpy = ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value); - return ir_lval_wrap(irb, scope, ir_memcpy, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_memcpy); } case BuiltinFnIdMemset: { @@ -4505,7 +4543,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg2_value; IrInstruction *ir_memset = ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value); - return ir_lval_wrap(irb, scope, ir_memset, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_memset); } case BuiltinFnIdMemberCount: { @@ -4515,7 +4553,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *member_count = ir_build_member_count(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, member_count, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, member_count); } case BuiltinFnIdMemberType: { @@ -4531,7 +4569,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *member_type = ir_build_member_type(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, member_type, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, member_type); } case BuiltinFnIdMemberName: { @@ -4545,9 +4583,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *member_name = ir_build_member_name(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, member_name, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, member_name); } case BuiltinFnIdField: { @@ -4573,14 +4610,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *type_info = ir_build_type_info(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, type_info, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, type_info); } case BuiltinFnIdBreakpoint: - return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_breakpoint(irb, scope, node)); case BuiltinFnIdReturnAddress: - return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_return_address(irb, scope, node)); case BuiltinFnIdFrameAddress: - return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_frame_address(irb, scope, node)); case BuiltinFnIdHandle: if (!irb->exec->fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition")); @@ -4590,7 +4627,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function")); return irb->codegen->invalid_instruction; } - return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_handle(irb, scope, node)); case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -4599,16 +4636,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *align_of = ir_build_align_of(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, align_of, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, align_of); } case BuiltinFnIdAddWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd)); case BuiltinFnIdSubWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub)); case BuiltinFnIdMulWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul)); case BuiltinFnIdShlWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl)); case BuiltinFnIdTypeName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -4617,7 +4658,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value, result_loc); - return ir_lval_wrap(irb, scope, type_name, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, type_name); } case BuiltinFnIdPanic: { @@ -4627,7 +4668,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *panic = ir_build_panic(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, panic, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, panic); } case BuiltinFnIdPtrCast: { @@ -4642,7 +4683,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, ptr_cast, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ptr_cast); } case BuiltinFnIdBitCast: { @@ -4673,7 +4714,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *int_to_ptr = ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, int_to_ptr, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, int_to_ptr); } case BuiltinFnIdPtrToInt: { @@ -4693,8 +4734,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo 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, result_loc); - return ir_lval_wrap(irb, scope, tag_name, lval); + IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag); + return ir_gen_value(irb, scope, node, lval, result_loc, tag_name); } case BuiltinFnIdTagType: { @@ -4704,7 +4745,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *tag_type = ir_build_tag_type(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, tag_type, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, tag_type); } case BuiltinFnIdFieldParentPtr: { @@ -4723,8 +4764,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr); - return ir_lval_wrap(irb, scope, field_parent_ptr, lval); + IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, + arg0_value, arg1_value, arg2_value, nullptr); + return ir_gen_value(irb, scope, node, lval, result_loc, field_parent_ptr); } case BuiltinFnIdByteOffsetOf: { @@ -4739,7 +4781,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, offset_of, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, offset_of); } case BuiltinFnIdBitOffsetOf: { @@ -4754,7 +4796,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, offset_of, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, offset_of); } case BuiltinFnIdInlineCall: case BuiltinFnIdNoInlineCall: @@ -4783,7 +4825,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr, result_loc, nullptr); - return ir_lval_wrap(irb, scope, call, lval); + return ir_gen_multi(irb, scope, node, lval, result_loc, call); } case BuiltinFnIdNewStackCall: { @@ -4815,7 +4857,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack, result_loc, nullptr); - return ir_lval_wrap(irb, scope, call, lval); + return ir_gen_multi(irb, scope, node, lval, result_loc, call); } case BuiltinFnIdTypeId: { @@ -4825,7 +4867,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *type_id = ir_build_type_id(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, type_id, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, type_id); } case BuiltinFnIdShlExact: { @@ -4839,8 +4881,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, + arg0_value, arg1_value, true); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdShrExact: { @@ -4854,8 +4897,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, + arg0_value, arg1_value, true); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdSetEvalBranchQuota: { @@ -4865,7 +4909,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, set_eval_branch_quota); } case BuiltinFnIdAlignCast: { @@ -4880,17 +4924,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *align_cast = ir_build_align_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, align_cast, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, align_cast); } case BuiltinFnIdOpaqueType: { IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node); - return ir_lval_wrap(irb, scope, opaque_type, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, opaque_type); } case BuiltinFnIdThis: { IrInstruction *this_inst = ir_gen_this(irb, scope, node); - return ir_lval_wrap(irb, scope, this_inst, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, this_inst); } case BuiltinFnIdSetAlignStack: { @@ -4900,7 +4944,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_align_stack, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, set_align_stack); } case BuiltinFnIdArgType: { @@ -4915,7 +4959,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, arg_type, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, arg_type); } case BuiltinFnIdExport: { @@ -4935,12 +4979,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg2_value; IrInstruction *ir_export = ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); - return ir_lval_wrap(irb, scope, ir_export, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_export); } case BuiltinFnIdErrorReturnTrace: { - IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null); - return ir_lval_wrap(irb, scope, error_return_trace, lval); + IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, + IrInstructionErrorReturnTrace::Null); + return ir_gen_value(irb, scope, node, lval, result_loc, error_return_trace); } case BuiltinFnIdAtomicRmw: { @@ -4969,10 +5014,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; - return ir_build_atomic_rmw(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, - arg4_value, + IrInstruction *result = ir_build_atomic_rmw(irb, scope, node, arg0_value, arg1_value, + arg2_value, arg3_value, arg4_value, // these 2 values don't mean anything since we passed non-null values for other args AtomicRmwOp_xchg, AtomicOrderMonotonic); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdAtomicLoad: { @@ -4991,9 +5037,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - return ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value, + IrInstruction *result = ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value, // this value does not mean anything since we passed non-null values for other arg AtomicOrderMonotonic); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdIntToEnum: { @@ -5008,7 +5055,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; IrInstruction *result = ir_build_int_to_enum(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdEnumToInt: { @@ -5018,7 +5065,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *result = ir_build_enum_to_int(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } } zig_unreachable(); @@ -5138,17 +5185,6 @@ static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode return ir_build_un_op(irb, scope, node, op_id, value); } -static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) { - if (lval != LValPtr) - return value; - if (value == irb->codegen->invalid_instruction) - return value; - - // We needed a pointer to a value, but we got a value. So we create - // an instruction which just makes a pointer of it. - return ir_build_ref(irb, scope, value->source_node, value, false, false); -} - static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePointerType); PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar || @@ -5246,15 +5282,19 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod case PrefixOpInvalid: zig_unreachable(); case PrefixOpBoolNot: - return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_bool_not(irb, scope, node)); case PrefixOpBinNot: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot)); case PrefixOpNegation: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation)); case PrefixOpNegationWrap: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap)); case PrefixOpOptional: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional)); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, result_loc); @@ -7261,7 +7301,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeTestDecl: zig_unreachable(); case NodeTypeBlock: - return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node, result_loc), lval); + return ir_gen_block(irb, scope, node, lval, result_loc); case NodeTypeGroupedExpr: return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc); case NodeTypeBinOpExpr: @@ -7285,7 +7325,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_container_init_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeVariableDeclaration: - return ir_lval_wrap(irb, scope, ir_gen_var_decl(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_var_decl(irb, scope, node)); case NodeTypeWhileExpr: return ir_gen_while_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); @@ -7348,13 +7388,13 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeErrorType: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_error_type(irb, scope, node)); case NodeTypeBreak: - return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_break(irb, scope, node)); case NodeTypeContinue: - return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_continue(irb, scope, node)); case NodeTypeUnreachable: - return ir_lval_wrap(irb, scope, ir_build_unreachable(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_unreachable(irb, scope, node)); case NodeTypeDefer: - return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_defer(irb, scope, node)); case NodeTypeSliceExpr: return ir_gen_slice(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); @@ -7362,19 +7402,19 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_catch(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeContainerDecl: - return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_container_decl(irb, scope, node)); case NodeTypeFnProto: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_fn_proto(irb, scope, node)); case NodeTypeErrorSetDecl: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_err_set_decl(irb, scope, node)); case NodeTypeCancel: - return ir_lval_wrap(irb, scope, ir_gen_cancel(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_cancel(irb, scope, node)); case NodeTypeResume: - return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_resume(irb, scope, node)); case NodeTypeAwaitExpr: - return ir_lval_wrap(irb, scope, ir_gen_await_expr(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_await_expr(irb, scope, node)); case NodeTypeSuspend: - return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_suspend(irb, scope, node)); } zig_unreachable(); } @@ -17299,7 +17339,7 @@ static IrInstruction *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruct if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *casted_value = ir_implicit_cast(ira, value, value->value.type); + IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set); if (type_is_invalid(casted_value->value.type)) return ira->codegen->invalid_instruction; @@ -17332,10 +17372,6 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns if (type_is_invalid(target->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value.type)) - return ira->codegen->invalid_instruction; - assert(target->value.type->id == ZigTypeIdEnum); if (instr_is_comptime(target)) { @@ -17349,7 +17385,7 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns } IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, target, result_loc); + instruction->base.source_node, target); ZigType *u8_ptr_type = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, From 6299526c466e6496cd10968c181bd92cd316ad11 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 19 Nov 2018 17:00:18 -0500 Subject: [PATCH 052/190] copy elision: match ir_gen_result with ensure_result_loc --- src/ir.cpp | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 02fe8cc681b3..a794ae80d338 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3418,6 +3418,12 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n return var; } +static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { + if (result_loc) + return result_loc; + return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); +} + static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node, LVal lval, IrInstruction *result_loc) { @@ -3443,7 +3449,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode } if (block_node->data.block.name != nullptr) { - scope_block->result_loc = result_loc; + scope_block->result_loc = ensure_result_loc(irb, parent_scope, block_node, result_loc); scope_block->incoming_blocks = &incoming_blocks; scope_block->incoming_values = &incoming_values; scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); @@ -3483,17 +3489,17 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode } ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); - return ir_gen_result(irb, parent_scope, block_node, lval, result_loc); + return ir_gen_result(irb, parent_scope, block_node, lval, scope_block->result_loc); } if (block_node->data.block.name != nullptr) { IrInstruction *continuation_expr_result = ir_build_const_void(irb, parent_scope, block_node); - ir_build_store_ptr(irb, parent_scope, block_node, result_loc, continuation_expr_result); + ir_build_store_ptr(irb, parent_scope, block_node, scope_block->result_loc, continuation_expr_result); ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime)); ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); - return ir_gen_result(irb, parent_scope, block_node, lval, result_loc); + return ir_gen_result(irb, parent_scope, block_node, lval, scope_block->result_loc); } else { ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); return ir_gen_value(irb, parent_scope, block_node, lval, result_loc, @@ -3806,7 +3812,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, ir_gen_error_union(irb, scope, node)); case BinOpTypeUnwrapOptional: - return ir_gen_orelse(irb, scope, node, lval, result_loc); + return ir_gen_orelse(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); } zig_unreachable(); } @@ -4410,25 +4416,28 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *new_result_loc = ir_build_result_slice_to_bytes(irb, scope, node, arg0_value, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + IrInstruction *new_result_loc = ir_build_result_slice_to_bytes(irb, scope, node, arg0_value, + ensured_result_loc); AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, new_result_loc); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_gen_result(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, ensured_result_loc); } case BuiltinFnIdToBytes: { - IrInstruction *new_result_loc = ir_build_result_bytes_to_slice(irb, scope, node, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + IrInstruction *new_result_loc = ir_build_result_bytes_to_slice(irb, scope, node, ensured_result_loc); AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, new_result_loc); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_gen_result(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, ensured_result_loc); } case BuiltinFnIdIntToFloat: { @@ -4692,14 +4701,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *new_result_loc = ir_build_result_ptr_cast(irb, scope, node, arg0_value, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + IrInstruction *new_result_loc = ir_build_result_ptr_cast(irb, scope, node, arg0_value, + ensured_result_loc); AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, new_result_loc); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_gen_result(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, ensured_result_loc); } case BuiltinFnIdIntToPtr: { @@ -5265,12 +5276,6 @@ static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_bool_not(irb, scope, node, value); } -static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { - if (result_loc) - return result_loc; - return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); -} - static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, IrInstruction *result_loc) { @@ -7330,7 +7335,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_while_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeForExpr: - return ir_gen_for_expr(irb, scope, node, lval, result_loc); + return ir_gen_for_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeArrayAccessExpr: return ir_gen_array_access(irb, scope, node, lval, result_loc); case NodeTypeReturnExpr: From 2cd43513913d6817d41434fc5673840b6da330d4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 21 Nov 2018 16:43:29 -0500 Subject: [PATCH 053/190] copy elision: @bitCast with runtime value ```zig export fn entry() void { var y: u32 = 1234; var x = @bitCast(f32, y); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %y = alloca i32, align 4 %x = alloca float, align 4 store i32 1234, i32* %y, align 4, !dbg !51 call void @llvm.dbg.declare(metadata i32* %y, metadata !45, metadata !DIExpression()), !dbg !51 %0 = load i32, i32* %y, align 4, !dbg !52 %1 = bitcast float* %x to i32*, !dbg !53 store i32 %0, i32* %1, align 4, !dbg !52 call void @llvm.dbg.declare(metadata float* %x, metadata !48, metadata !DIExpression()), !dbg !54 ret void, !dbg !55 } ``` --- src/all_types.hpp | 27 ++++- src/codegen.cpp | 8 +- src/ir.cpp | 294 +++++++++++++++++++++++++++++----------------- src/ir_print.cpp | 32 ++++- 4 files changed, 241 insertions(+), 120 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 4c8477497708..82b289e65845 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -34,6 +34,7 @@ struct CodeGen; struct ConstExprValue; struct IrInstruction; struct IrInstructionAllocaGen; +struct IrInstructionResultPtrCast; struct IrBasicBlock; struct ScopeDecls; struct ZigWindowsSDK; @@ -2129,7 +2130,8 @@ enum IrInstructionId { IrInstructionIdErrWrapPayload, IrInstructionIdFnProto, IrInstructionIdTestComptime, - IrInstructionIdPtrCast, + IrInstructionIdPtrCastSrc, + IrInstructionIdPtrCastGen, IrInstructionIdBitCast, IrInstructionIdWidenOrShorten, IrInstructionIdIntToPtr, @@ -2200,6 +2202,7 @@ enum IrInstructionId { IrInstructionIdErrorUnionFieldErrorSet, IrInstructionIdFirstArgResultLoc, IrInstructionIdInferArrayType, + IrInstructionIdInferCompTime, }; struct IrInstruction { @@ -2914,13 +2917,26 @@ struct IrInstructionTestComptime { IrInstruction *value; }; -struct IrInstructionPtrCast { +struct IrInstructionPtrCastSrc { IrInstruction base; IrInstruction *dest_type; IrInstruction *ptr; }; +struct IrInstructionPtrCastGen { + IrInstruction base; + + IrInstruction *ptr; + + // This instruction supports being created with the child + // type of the pointer being "infer" which means that a later + // instruction will replace this one. + // so we have a reference here to the pass-1 instruction so that + // the child pointer can be updated to the new pass-2 instruction. + IrInstructionResultPtrCast *pass1_parent; +}; + struct IrInstructionBitCast { IrInstruction base; @@ -3377,6 +3393,13 @@ struct IrInstructionInferArrayType { size_t elem_count; }; +struct IrInstructionInferCompTime { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *new_result_loc; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index a6b98312d186..badad23471dd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3024,7 +3024,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, } static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, - IrInstructionPtrCast *instruction) + IrInstructionPtrCastGen *instruction) { ZigType *wanted_type = instruction->base.value.type; if (!type_has_bits(wanted_type)) { @@ -5233,6 +5233,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdResultCast: case IrInstructionIdContainerInitList: case IrInstructionIdInferArrayType: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdInferCompTime: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5325,8 +5327,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_err_wrap_payload(g, executable, (IrInstructionErrWrapPayload *)instruction); case IrInstructionIdUnionTag: return ir_render_union_tag(g, executable, (IrInstructionUnionTag *)instruction); - case IrInstructionIdPtrCast: - return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastGen: + return ir_render_ptr_cast(g, executable, (IrInstructionPtrCastGen *)instruction); case IrInstructionIdBitCast: return ir_render_bit_cast(g, executable, (IrInstructionBitCast *)instruction); case IrInstructionIdWidenOrShorten: diff --git a/src/ir.cpp b/src/ir.cpp index a794ae80d338..620b53ad3db2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -642,8 +642,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestComptime *) return IrInstructionIdTestComptime; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCast *) { - return IrInstructionIdPtrCast; +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastSrc *) { + return IrInstructionIdPtrCastSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastGen *) { + return IrInstructionIdPtrCastGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCast *) { @@ -918,6 +922,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionInferArrayType * return IrInstructionIdInferArrayType; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionInferCompTime *) { + return IrInstructionIdInferCompTime; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -982,16 +990,6 @@ static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *sou return &return_instruction->base; } -static IrInstruction *ir_create_const(IrBuilder *irb, Scope *scope, AstNode *source_node, - ZigType *type_entry) -{ - assert(type_entry); - IrInstructionConst *const_instruction = ir_create_instruction(irb, scope, source_node); - const_instruction->base.value.type = type_entry; - const_instruction->base.value.special = ConstValSpecialStatic; - return &const_instruction->base; -} - static IrInstruction *ir_build_const_void(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_void; @@ -2153,20 +2151,33 @@ static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } -static IrInstruction *ir_build_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *ptr) { - IrInstructionPtrCast *instruction = ir_build_instruction( + IrInstructionPtrCastSrc *instruction = ir_build_instruction( irb, scope, source_node); instruction->dest_type = dest_type; instruction->ptr = ptr; - if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); return &instruction->base; } +static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instr, + ZigType *ptr_type, IrInstruction *ptr) +{ + IrInstructionPtrCastGen *instruction = ir_build_instruction( + &ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->ptr = ptr; + instruction->base.value.type = ptr_type; + + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *value) { @@ -2944,6 +2955,19 @@ static IrInstruction *ir_build_infer_array_type(IrBuilder *irb, Scope *scope, As return &instruction->base; } +static IrInstruction *ir_build_infer_comptime(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *new_result_loc) +{ + IrInstructionInferCompTime *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->new_result_loc = new_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + ir_ref_instruction(new_result_loc, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -4691,7 +4715,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value); return ir_gen_value(irb, scope, node, lval, result_loc, ptr_cast); } case BuiltinFnIdBitCast: @@ -4710,6 +4734,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; + ir_build_infer_comptime(irb, scope, node, ensured_result_loc, new_result_loc); + return ir_gen_result(irb, scope, node, lval, ensured_result_loc); } case BuiltinFnIdIntToPtr: @@ -6871,7 +6897,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 // TODO relies on Zig not re-ordering fields - IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, @@ -6949,7 +6975,7 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); // TODO relies on Zig not re-ordering fields - IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, @@ -7344,11 +7370,10 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_field_access(irb, scope, node, lval, result_loc); case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; - IrInstruction *value = ir_gen_node(irb, expr_node, scope, lval, nullptr); - if (value == irb->codegen->invalid_instruction) - return value; - - return ir_build_un_op(irb, scope, node, IrUnOpDereference, value); + IrInstruction *ptr_inst = ir_gen_node(irb, expr_node, scope, LValNone, nullptr); + if (ptr_inst == irb->codegen->invalid_instruction) + return ptr_inst; + return ir_gen_ptr(irb, scope, node, lval, result_loc, ptr_inst); } case NodeTypeUnwrapOptional: { AstNode *expr_node = node->data.unwrap_optional.expr; @@ -7497,7 +7522,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec u8_ptr_type = ir_build_const_type(irb, coro_scope, node, get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); - IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr); + IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr); coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr); coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node); @@ -7527,7 +7552,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_return(irb, coro_scope, node, undef); ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); @@ -7612,8 +7637,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, nullptr); - IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); - IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, + IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); + IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node, fn_entry->type_entry->data.fn.fn_type_id.return_type); @@ -7666,7 +7691,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr, nullptr); @@ -9588,14 +9613,23 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ } return true; } + +static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) { + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + old_instruction->scope, old_instruction->source_node); + IrInstruction *new_instruction = &const_instruction->base; + new_instruction->value.type = ty; + new_instruction->value.special = ConstValSpecialStatic; + return new_instruction; +} + static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op, bool need_alloca) { if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && cast_op != CastOpResizeSlice && cast_op != CastOpBytesToSlice) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, &result->value, wanted_type)) { @@ -9629,8 +9663,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.type = wanted_type; result->value.data.x_ptr.special = ConstPtrSpecialBaseArray; result->value.data.x_ptr.mut = value->value.data.x_ptr.mut; @@ -9670,8 +9703,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc assert(is_slice(wanted_type)); bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const); result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = value->value.data.x_ptr.mut; @@ -9805,15 +9837,6 @@ static IrInstruction *ir_finish_anal(IrAnalyze *ira, IrInstruction *instruction) return instruction; } -static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) { - IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - IrInstruction *new_instruction = &const_instruction->base; - new_instruction->value.type = ty; - new_instruction->value.special = ConstValSpecialStatic; - return new_instruction; -} - static IrInstruction *ir_const_type(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) { IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_type); result->value.data.x_type = ty; @@ -10417,19 +10440,16 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s assert(array_type->id == ZigTypeIdArray); if (instr_is_comptime(array)) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, &result->value, &array->value, 0, array_type->data.array.len, true); result->value.type = wanted_type; return result; } - IrInstruction *start = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_usize); + IrInstruction *start = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &start->value, 0); - IrInstruction *end = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_usize); + IrInstruction *end = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &end->value, array_type->data.array.len); if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); @@ -10469,8 +10489,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); return result; } @@ -10480,8 +10499,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour actual_type->data.enumeration.src_field_count == 1) { assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int); - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, &actual_type->data.enumeration.fields[0].value); return result; @@ -10504,8 +10522,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_union.tag); @@ -10516,8 +10533,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou if (wanted_type->data.enumeration.layout == ContainerLayoutAuto && wanted_type->data.enumeration.src_field_count == 1) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; TypeEnumField *enum_field = target->value.type->data.unionation.fields[0].enum_field; @@ -10534,8 +10550,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_undefined(ira->codegen, &result->value); return result; } @@ -10567,8 +10582,7 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so 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); + IrInstruction *result = ir_const(ira, source_instr, 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); @@ -10623,8 +10637,7 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; } } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.type = wanted_type; if (wanted_type->id == ZigTypeIdInt) { bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); @@ -10678,8 +10691,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint); return result; } @@ -10697,8 +10709,7 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (wanted_type->id == ZigTypeIdComptimeFloat) { float_init_float(&result->value, val); } else if (wanted_type->id == ZigTypeIdComptimeInt) { @@ -10721,8 +10732,7 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; @@ -10786,8 +10796,7 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err; if (err_type->id == ZigTypeIdErrorUnion) { @@ -10826,14 +10835,12 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc return ira->codegen->invalid_instruction; } if (err_set_type->data.error_set.err_count == 0) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.type = wanted_type; bigint_init_unsigned(&result->value.data.x_bigint, 0); return result; } else if (err_set_type->data.error_set.err_count == 1) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.type = wanted_type; ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; bigint_init_unsigned(&result->value.data.x_bigint, err->value); @@ -11397,7 +11404,9 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Zig return ir_analyze_cast(ira, value, expected_type, value); } -static Error resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *alloca, ZigType *child_type) { +static Error resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *alloca, + ZigType *child_type) +{ Error err; if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return err; @@ -11408,15 +11417,29 @@ static Error resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *al return ErrorNone; } -static Error resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type) { +static IrInstruction *resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type) { Error err; assert(base->value.type->id == ZigTypeIdPointer); ZigType *infer_child = base->value.type->data.pointer.child_type; - if (base->id == IrInstructionIdAllocaGen && infer_child == ira->codegen->builtin_types.entry_infer) { - if ((err = resolve_alloca_inference(ira, reinterpret_cast(base), child_type))) - return err; + if (infer_child == ira->codegen->builtin_types.entry_infer) { + if (base->id == IrInstructionIdAllocaGen) { + if ((err = resolve_alloca_inference(ira, reinterpret_cast(base), child_type))) + return ira->codegen->invalid_instruction; + } else if (base->id == IrInstructionIdPtrCastGen) { + IrInstructionPtrCastGen *ptr_cast = reinterpret_cast(base); + // When the ptr cast instruction was made it didn't have the destination type, so + // we had to set it to "infer". Now we have the destination type, so we re-do + // analysis, and rely on the ref_count being 0 so that it is not emitted. + assert(ptr_cast->base.ref_count == 0); + ZigType *prev_ptr_type = ptr_cast->ptr->value.type; + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, + PtrLenSingle, prev_ptr_type->data.pointer.explicit_alignment, 0, 0); + IrInstruction *new_ptr_cast = ir_analyze_ptr_cast(ira, base, ptr_cast->ptr, new_ptr_type, base); + ptr_cast->pass1_parent->base.child = new_ptr_cast; + return new_ptr_cast; + } } - return ErrorNone; + return base; } static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { @@ -11456,13 +11479,13 @@ static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); } -static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *result_loc, +static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *unresolved_result_loc, ZigType *needed_child_type, bool allow_failure) { - Error err; - if (type_is_invalid(result_loc->value.type) || type_is_invalid(needed_child_type)) + if (type_is_invalid(unresolved_result_loc->value.type) || type_is_invalid(needed_child_type)) return ira->codegen->invalid_instruction; - if ((err = resolve_possible_alloca_inference(ira, result_loc, needed_child_type))) + IrInstruction *result_loc = resolve_possible_alloca_inference(ira, unresolved_result_loc, needed_child_type); + if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; if (needed_child_type == nullptr) @@ -11579,8 +11602,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc ZigType *child_type = type_entry->data.pointer.child_type; // dereferencing a *u0 is comptime known to be 0 if (child_type->id == ZigTypeIdInt && child_type->data.integral.bit_count == 0) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); + IrInstruction *result = ir_const(ira, source_instruction, child_type); init_const_unsigned_negative(&result->value, child_type, 0, false); return result; } @@ -11594,8 +11616,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc { ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); if (pointee->special != ConstValSpecialRuntime) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); + IrInstruction *result = ir_const(ira, source_instruction, child_type); if ((err = ir_read_const_ptr(ira, source_instruction->source_node, &result->value, &ptr->value))) @@ -13739,7 +13760,9 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return ira->codegen->invalid_instruction; if (dest_val->special != ConstValSpecialRuntime) { *dest_val = value->value; - if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { + if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr && + type_has_bits(child_type)) + { ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; } if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { @@ -14399,7 +14422,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) { - Error err; IrInstruction *fn_ref = call_instruction->fn_ref->child; if (type_is_invalid(fn_ref->value.type)) return ira->codegen->invalid_instruction; @@ -14427,7 +14449,8 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC IrInstruction *result_loc = call_instruction->result_loc->child; if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; - if ((err = resolve_possible_alloca_inference(ira, result_loc, dest_type))) + result_loc = resolve_possible_alloca_inference(ira, result_loc, dest_type); + if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; if (is_slice(dest_type)) { @@ -14465,7 +14488,8 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC IrInstruction *result_loc = call_instruction->result_loc->child; if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; - if ((err = resolve_possible_alloca_inference(ira, result_loc, dest_type))) + result_loc = resolve_possible_alloca_inference(ira, result_loc, dest_type); + if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, result); return result; @@ -15570,7 +15594,8 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc ZigType *container_type = ir_resolve_type(ira, field_ptr_instruction->container_type->child); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; - if ((err = resolve_possible_alloca_inference(ira, container_ptr, container_type))) + container_ptr = resolve_possible_alloca_inference(ira, container_ptr, container_type); + if (type_is_invalid(container_ptr->value.type)) return ira->codegen->invalid_instruction; } @@ -19391,8 +19416,6 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice *instruction) { - Error err; - IrInstruction *ptr_ptr = instruction->ptr->child; if (type_is_invalid(ptr_ptr->value.type)) return ira->codegen->invalid_instruction; @@ -19466,7 +19489,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction IrInstruction *result_loc = instruction->result_loc->child; if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; - if ((err = resolve_possible_alloca_inference(ira, result_loc, return_type))) + result_loc = resolve_possible_alloca_inference(ira, result_loc, return_type); + if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(ptr_ptr) && @@ -20673,7 +20697,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type); + IrInstruction *result = ir_const(ira, target, result_type); copy_const_val(&result->value, val, false); result->value.type = result_type; return result; @@ -20721,8 +20745,12 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, - dest_type); + IrInstruction *result; + if (val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr); + } else { + result = ir_const(ira, source_instr, dest_type); + } copy_const_val(&result->value, val, false); result->value.type = dest_type; return result; @@ -20745,9 +20773,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ return ira->codegen->invalid_instruction; } - IrInstruction *casted_ptr = ir_build_ptr_cast(&ira->new_irb, source_instr->scope, - source_instr->source_node, nullptr, ptr); - casted_ptr->value.type = dest_type; + IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr); if (type_has_bits(dest_type) && !type_has_bits(src_type)) { ErrorMsg *msg = ir_add_error(ira, source_instr, @@ -20773,7 +20799,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ return result; } -static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { +static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCastSrc *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) @@ -21119,8 +21145,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru if (!val) return ira->codegen->invalid_instruction; if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, usize); + IrInstruction *result = ir_const(ira, &instruction->base, usize); bigint_init_unsigned(&result->value.data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); result->value.type = usize; return result; @@ -21854,8 +21879,27 @@ static IrInstruction *ir_analyze_instruction_result_param(IrAnalyze *ira, IrInst zig_panic("TODO"); } -static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, IrInstructionResultPtrCast *instruction) { - zig_panic("TODO"); +static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, + IrInstructionResultPtrCast *instruction) +{ + ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, prev_result_loc, elem_type, false); + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + ZigType *new_ptr_type = adjust_ptr_child(ira->codegen, new_result_loc->value.type, + ira->codegen->builtin_types.entry_infer); + IrInstruction *ptr_cast = ir_build_ptr_cast_gen(ira, &instruction->base, new_ptr_type, new_result_loc); + reinterpret_cast(ptr_cast)->pass1_parent = instruction; + + return ptr_cast; } static IrInstruction *ir_analyze_instruction_result_cast(IrAnalyze *ira, IrInstructionResultCast *instruction) { @@ -21983,6 +22027,29 @@ static IrInstruction *ir_analyze_instruction_infer_array_type(IrAnalyze *ira, return ir_const_type(ira, &instruction->base, array_type); } +static IrInstruction *ir_analyze_instruction_infer_comptime(IrAnalyze *ira, + IrInstructionInferCompTime *instruction) +{ + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = instruction->new_result_loc->child; + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + if (prev_result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(new_result_loc) && + new_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + prev_result_loc->value.special = ConstValSpecialRuntime; + } + } + return ir_const_void(ira, &instruction->base); +} + static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -21999,6 +22066,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdResultSlicePtr: case IrInstructionIdResultErrorUnionPayload: case IrInstructionIdResultErrorUnionCode: + case IrInstructionIdPtrCastGen: zig_unreachable(); case IrInstructionIdReturn: @@ -22163,8 +22231,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction); case IrInstructionIdPanic: return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction); - case IrInstructionIdPtrCast: - return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastSrc: + return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction); case IrInstructionIdBitCast: return ir_analyze_instruction_bit_cast(ira, (IrInstructionBitCast *)instruction); case IrInstructionIdIntToPtr: @@ -22275,6 +22343,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_first_arg_result_loc(ira, (IrInstructionFirstArgResultLoc *)instruction); case IrInstructionIdInferArrayType: return ir_analyze_instruction_infer_array_type(ira, (IrInstructionInferArrayType *)instruction); + case IrInstructionIdInferCompTime: + return ir_analyze_instruction_infer_comptime(ira, (IrInstructionInferCompTime *)instruction); case IrInstructionIdResultBytesToSlice: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: @@ -22412,6 +22482,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAssertNonError: case IrInstructionIdContainerInitFields: case IrInstructionIdContainerInitList: + case IrInstructionIdInferCompTime: return true; case IrInstructionIdPhi: @@ -22459,7 +22530,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: - case IrInstructionIdPtrCast: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdPtrCastGen: case IrInstructionIdBitCast: case IrInstructionIdWidenOrShorten: case IrInstructionIdPtrToInt: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 75ea96bbe0d5..8c208c79c8db 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -856,7 +856,7 @@ static void ir_print_test_comptime(IrPrint *irp, IrInstructionTestComptime *inst fprintf(irp->f, ")"); } -static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { +static void ir_print_ptr_cast_src(IrPrint *irp, IrInstructionPtrCastSrc *instruction) { fprintf(irp->f, "@ptrCast("); if (instruction->dest_type) { ir_print_other_instruction(irp, instruction->dest_type); @@ -866,6 +866,12 @@ static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { fprintf(irp->f, ")"); } +static void ir_print_ptr_cast_gen(IrPrint *irp, IrInstructionPtrCastGen *instruction) { + fprintf(irp->f, "@ptrCast("); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ")"); +} + static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { fprintf(irp->f, "@bitCast("); if (instruction->dest_type) { @@ -1346,7 +1352,11 @@ static void ir_print_result_param(IrPrint *irp, IrInstructionResultParam *instru } static void ir_print_result_ptr_cast(IrPrint *irp, IrInstructionResultPtrCast *instruction) { - fprintf(irp->f, "ResultPtrCast"); + fprintf(irp->f, "ResultPtrCast(ty="); + ir_print_other_instruction(irp, instruction->elem_type); + fprintf(irp->f, ",prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); } static void ir_print_result_cast(IrPrint *irp, IrInstructionResultCast *instruction) { @@ -1395,6 +1405,14 @@ static void ir_print_infer_array_type(IrPrint *irp, IrInstructionInferArrayType fprintf(irp->f, ",elem_count=%zu)", instruction->elem_count); } +static void ir_print_infer_comptime(IrPrint *irp, IrInstructionInferCompTime *instruction) { + fprintf(irp->f, "InferCompTime(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",new_result="); + ir_print_other_instruction(irp, instruction->new_result_loc); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1643,8 +1661,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTestComptime: ir_print_test_comptime(irp, (IrInstructionTestComptime *)instruction); break; - case IrInstructionIdPtrCast: - ir_print_ptr_cast(irp, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastSrc: + ir_print_ptr_cast_src(irp, (IrInstructionPtrCastSrc *)instruction); + break; + case IrInstructionIdPtrCastGen: + ir_print_ptr_cast_gen(irp, (IrInstructionPtrCastGen *)instruction); break; case IrInstructionIdBitCast: ir_print_bit_cast(irp, (IrInstructionBitCast *)instruction); @@ -1856,6 +1877,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdInferArrayType: ir_print_infer_array_type(irp, (IrInstructionInferArrayType *)instruction); break; + case IrInstructionIdInferCompTime: + ir_print_infer_comptime(irp, (IrInstructionInferCompTime *)instruction); + break; } fprintf(irp->f, "\n"); } From 522780e6d637ab92dfdee1677ccf46df88f83618 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 21 Nov 2018 17:28:47 -0500 Subject: [PATCH 054/190] copy elision: @bitCast with comptime value ```zig export fn entry() void { const y: u32 = 1234; var x = @bitCast(f32, y); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca float, align 4 call void @llvm.dbg.declare(metadata i32* @1, metadata !45, metadata !DIExpression()), !dbg !51 store float 0x3743480000000000, float* %x, align 4, !dbg !52 call void @llvm.dbg.declare(metadata float* %x, metadata !48, metadata !DIExpression()), !dbg !52 ret void, !dbg !53 } ``` --- src/all_types.hpp | 16 ---- src/codegen.cpp | 15 +--- src/ir.cpp | 212 ++++++---------------------------------------- src/ir_print.cpp | 20 ----- 4 files changed, 28 insertions(+), 235 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 82b289e65845..e02161a485b6 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2132,7 +2132,6 @@ enum IrInstructionId { IrInstructionIdTestComptime, IrInstructionIdPtrCastSrc, IrInstructionIdPtrCastGen, - IrInstructionIdBitCast, IrInstructionIdWidenOrShorten, IrInstructionIdIntToPtr, IrInstructionIdPtrToInt, @@ -2193,7 +2192,6 @@ enum IrInstructionId { IrInstructionIdResultReturn, IrInstructionIdResultBytesToSlice, IrInstructionIdResultSliceToBytes, - IrInstructionIdResultParam, IrInstructionIdResultPtrCast, IrInstructionIdResultCast, IrInstructionIdAllocaSrc, @@ -2937,13 +2935,6 @@ struct IrInstructionPtrCastGen { IrInstructionResultPtrCast *pass1_parent; }; -struct IrInstructionBitCast { - IrInstruction base; - - IrInstruction *dest_type; - IrInstruction *value; -}; - struct IrInstructionWidenOrShorten { IrInstruction base; @@ -3337,13 +3328,6 @@ struct IrInstructionResultReturn { IrInstruction base; }; -struct IrInstructionResultParam { - IrInstruction base; - - IrInstruction *fn_ref; - size_t param_index; -}; - struct IrInstructionResultPtrCast { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index badad23471dd..249d2963b318 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3034,14 +3034,6 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, ""); } -static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable, - IrInstructionBitCast *instruction) -{ - ZigType *wanted_type = instruction->base.value.type; - LLVMValueRef value = ir_llvm_value(g, instruction->value); - return LLVMBuildBitCast(g->builder, value, wanted_type->type_ref, ""); -} - static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable, IrInstructionWidenOrShorten *instruction) { @@ -5235,6 +5227,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdInferArrayType: case IrInstructionIdPtrCastSrc: case IrInstructionIdInferCompTime: + case IrInstructionIdResultPtrCast: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5329,8 +5322,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_union_tag(g, executable, (IrInstructionUnionTag *)instruction); case IrInstructionIdPtrCastGen: return ir_render_ptr_cast(g, executable, (IrInstructionPtrCastGen *)instruction); - case IrInstructionIdBitCast: - return ir_render_bit_cast(g, executable, (IrInstructionBitCast *)instruction); case IrInstructionIdWidenOrShorten: return ir_render_widen_or_shorten(g, executable, (IrInstructionWidenOrShorten *)instruction); case IrInstructionIdPtrToInt: @@ -5409,10 +5400,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, zig_panic("TODO"); case IrInstructionIdResultBytesToSlice: zig_panic("TODO"); - case IrInstructionIdResultParam: - zig_panic("TODO"); - case IrInstructionIdResultPtrCast: - zig_panic("TODO"); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index 620b53ad3db2..b6fc4536224e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -218,9 +218,8 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { ConstExprValue *result = const_ptr_pointee_unchecked(g, const_val); - if (const_val->type->id == ZigTypeIdPointer) { - assert(types_have_same_zig_comptime_repr(const_val->type->data.pointer.child_type, result->type)); - } + assert(result != nullptr); + assert(const_val->type->id == ZigTypeIdPointer); return result; } @@ -650,10 +649,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastGen *) { return IrInstructionIdPtrCastGen; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCast *) { - return IrInstructionIdBitCast; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionWidenOrShorten *) { return IrInstructionIdWidenOrShorten; } @@ -878,10 +873,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionResultReturn *) return IrInstructionIdResultReturn; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionResultParam *) { - return IrInstructionIdResultParam; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionResultPtrCast *) { return IrInstructionIdResultPtrCast; } @@ -2178,20 +2169,6 @@ static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *sourc return &instruction->base; } -static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *dest_type, IrInstruction *value) -{ - IrInstructionBitCast *instruction = ir_build_instruction( - irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->value = value; - - if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - static IrInstruction *ir_build_widen_or_shorten(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { @@ -2823,17 +2800,6 @@ static IrInstruction *ir_build_result_return(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } -static IrInstruction *ir_build_result_param(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *fn_ref, size_t param_index) -{ - IrInstructionResultParam *instruction = ir_build_instruction(irb, scope, source_node); - instruction->fn_ref = fn_ref; - - ir_ref_instruction(fn_ref, irb->current_basic_block); - - return &instruction->base; -} - static IrInstruction *ir_build_result_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *elem_type, IrInstruction *prev_result_loc) { @@ -4852,9 +4818,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { - IrInstruction *param_result_loc = ir_build_result_param(irb, scope, node, fn_ref, i); AstNode *arg_node = node->data.fn_call_expr.params.at(i + 1); - args[i] = ir_gen_node(irb, arg_node, scope, LValNone, param_result_loc); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } @@ -4885,9 +4850,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { - IrInstruction *param_result_loc = ir_build_result_param(irb, scope, node, fn_ref, i); AstNode *arg_node = node->data.fn_call_expr.params.at(i + 2); - args[i] = ir_gen_node(irb, arg_node, scope, LValNone, param_result_loc); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } @@ -7751,19 +7715,6 @@ static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction, return ir_add_error_node(ira, source_instruction->source_node, msg); } -static ConstExprValue *ir_const_ptr_pointee(IrAnalyze *ira, ConstExprValue *const_val, AstNode *source_node) { - ConstExprValue *val = const_ptr_pointee_unchecked(ira->codegen, const_val); - assert(val != nullptr); - assert(const_val->type->id == ZigTypeIdPointer); - ZigType *expected_type = const_val->type->data.pointer.child_type; - if (!types_have_same_zig_comptime_repr(val->type, expected_type)) { - ir_add_error_node(ira, source_node, - buf_sprintf("TODO handle comptime reinterpreted pointer. See https://github.com/ziglang/zig/issues/955")); - return nullptr; - } - return val; -} - static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { IrBasicBlock *bb = exec->basic_block_list.at(0); for (size_t i = 0; i < bb->instruction_list.length; i += 1) { @@ -9659,7 +9610,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); if (instr_is_comptime(value)) { - ConstExprValue *pointee = ir_const_ptr_pointee(ira, &value->value, source_instr->source_node); + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { @@ -9694,7 +9645,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); if (instr_is_comptime(value)) { - ConstExprValue *pointee = ir_const_ptr_pointee(ira, &value->value, source_instr->source_node); + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { @@ -10879,7 +10830,7 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou return ira->codegen->invalid_instruction; assert(val->type->id == ZigTypeIdPointer); - ConstExprValue *pointee = ir_const_ptr_pointee(ira, val, source_instr->source_node); + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, val); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { @@ -13755,7 +13706,7 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { if (instr_is_comptime(value)) { - ConstExprValue *dest_val = ir_const_ptr_pointee(ira, &ptr->value, source_instr->source_node); + ConstExprValue *dest_val = const_ptr_pointee(ira->codegen, &ptr->value); if (dest_val == nullptr) return ira->codegen->invalid_instruction; if (dest_val->special != ConstValSpecialRuntime) { @@ -14973,8 +14924,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct array_type = array_type->data.pointer.child_type; ptr_type = ptr_type->data.pointer.child_type; if (orig_array_ptr_val->special != ConstValSpecialRuntime) { - orig_array_ptr_val = ir_const_ptr_pointee(ira, orig_array_ptr_val, - elem_ptr_instruction->base.source_node); + orig_array_ptr_val = const_ptr_pointee(ira->codegen, orig_array_ptr_val); if (orig_array_ptr_val == nullptr) return ira->codegen->invalid_instruction; } @@ -15017,7 +14967,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct ConstExprValue *ptr_val = ir_resolve_const(ira, array_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; - ConstExprValue *args_val = ir_const_ptr_pointee(ira, ptr_val, elem_ptr_instruction->base.source_node); + ConstExprValue *args_val = const_ptr_pointee(ira->codegen, ptr_val); if (args_val == nullptr) return ira->codegen->invalid_instruction; size_t start = args_val->data.x_arg_tuple.start_index; @@ -15099,8 +15049,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct (orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == ZigTypeIdArray)) { - ConstExprValue *array_ptr_val = ir_const_ptr_pointee(ira, orig_array_ptr_val, - elem_ptr_instruction->base.source_node); + ConstExprValue *array_ptr_val = const_ptr_pointee(ira->codegen, orig_array_ptr_val); if (array_ptr_val == nullptr) return ira->codegen->invalid_instruction; @@ -15350,7 +15299,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ConstExprValue *struct_val = ir_const_ptr_pointee(ira, ptr_val, source_instr->source_node); + ConstExprValue *struct_val = const_ptr_pointee(ira->codegen, ptr_val); if (struct_val == nullptr) return ira->codegen->invalid_instruction; if (type_is_invalid(struct_val->type)) @@ -15419,7 +15368,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ConstExprValue *union_val = ir_const_ptr_pointee(ira, ptr_val, source_instr->source_node); + ConstExprValue *union_val = const_ptr_pointee(ira->codegen, ptr_val); if (union_val == nullptr) return ira->codegen->invalid_instruction; if (type_is_invalid(union_val->type)) @@ -15651,7 +15600,7 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; assert(container_ptr->value.type->id == ZigTypeIdPointer); - ConstExprValue *child_val = ir_const_ptr_pointee(ira, container_ptr_val, source_node); + ConstExprValue *child_val = const_ptr_pointee(ira->codegen, container_ptr_val); if (child_val == nullptr) return ira->codegen->invalid_instruction; @@ -15677,7 +15626,7 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; assert(container_ptr->value.type->id == ZigTypeIdPointer); - ConstExprValue *child_val = ir_const_ptr_pointee(ira, container_ptr_val, source_node); + ConstExprValue *child_val = const_ptr_pointee(ira->codegen, container_ptr_val); if (child_val == nullptr) return ira->codegen->invalid_instruction; ZigType *child_type = child_val->data.x_type; @@ -15952,8 +15901,7 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc if (!container_ptr_val) return ira->codegen->invalid_instruction; - ConstExprValue *namespace_val = ir_const_ptr_pointee(ira, container_ptr_val, - field_ptr_instruction->base.source_node); + ConstExprValue *namespace_val = const_ptr_pointee(ira->codegen, container_ptr_val); if (namespace_val == nullptr) return ira->codegen->invalid_instruction; assert(namespace_val->special == ConstValSpecialStatic); @@ -16527,7 +16475,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_instruction; - ConstExprValue *maybe_val = ir_const_ptr_pointee(ira, val, unwrap_maybe_instruction->base.source_node); + ConstExprValue *maybe_val = const_ptr_pointee(ira->codegen, val); if (maybe_val == nullptr) return ira->codegen->invalid_instruction; @@ -16841,7 +16789,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type; ConstExprValue *pointee_val = nullptr; if (instr_is_comptime(target_value_ptr)) { - pointee_val = ir_const_ptr_pointee(ira, &target_value_ptr->value, target_value_ptr->source_node); + pointee_val = const_ptr_pointee(ira->codegen, &target_value_ptr->value); if (pointee_val == nullptr) return ira->codegen->invalid_instruction; @@ -16976,7 +16924,7 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru if (!target_value_ptr) return ira->codegen->invalid_instruction; - ConstExprValue *pointee_val = ir_const_ptr_pointee(ira, target_val_ptr, instruction->base.source_node); + ConstExprValue *pointee_val = const_ptr_pointee(ira->codegen, target_val_ptr); if (pointee_val == nullptr) return ira->codegen->invalid_instruction; @@ -19508,18 +19456,18 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (array_type->id == ZigTypeIdPointer) { ZigType *child_array_type = array_type->data.pointer.child_type; assert(child_array_type->id == ZigTypeIdArray); - parent_ptr = ir_const_ptr_pointee(ira, &ptr_ptr->value, instruction->base.source_node); + parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value); if (parent_ptr == nullptr) return ira->codegen->invalid_instruction; - array_val = ir_const_ptr_pointee(ira, parent_ptr, instruction->base.source_node); + array_val = const_ptr_pointee(ira->codegen, parent_ptr); if (array_val == nullptr) return ira->codegen->invalid_instruction; rel_end = child_array_type->data.array.len; abs_offset = 0; } else { - array_val = ir_const_ptr_pointee(ira, &ptr_ptr->value, instruction->base.source_node); + array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value); if (array_val == nullptr) return ira->codegen->invalid_instruction; rel_end = array_type->data.array.len; @@ -19528,7 +19476,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction } } else if (array_type->id == ZigTypeIdPointer) { assert(array_type->data.pointer.ptr_len == PtrLenUnknown); - parent_ptr = ir_const_ptr_pointee(ira, &ptr_ptr->value, instruction->base.source_node); + parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value); if (parent_ptr == nullptr) return ira->codegen->invalid_instruction; @@ -19572,7 +19520,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_panic("TODO slice of ptr cast from function"); } } else if (is_slice(array_type)) { - ConstExprValue *slice_ptr = ir_const_ptr_pointee(ira, &ptr_ptr->value, instruction->base.source_node); + ConstExprValue *slice_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value); if (slice_ptr == nullptr) return ira->codegen->invalid_instruction; @@ -20002,7 +19950,7 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr { BigInt *op1_bigint = &casted_op1->value.data.x_bigint; BigInt *op2_bigint = &casted_op2->value.data.x_bigint; - ConstExprValue *pointee_val = ir_const_ptr_pointee(ira, &casted_result_ptr->value, casted_result_ptr->source_node); + ConstExprValue *pointee_val = const_ptr_pointee(ira->codegen, &casted_result_ptr->value); if (pointee_val == nullptr) return ira->codegen->invalid_instruction; BigInt *dest_bigint = &pointee_val->data.x_bigint; @@ -20173,7 +20121,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = ir_const_ptr_pointee(ira, ptr_val, instruction->base.source_node); + ConstExprValue *err_union_val = const_ptr_pointee(ira->codegen, ptr_val); if (err_union_val == nullptr) return ira->codegen->invalid_instruction; if (err_union_val->special != ConstValSpecialRuntime) { @@ -20226,7 +20174,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, if (!ptr_val) return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ConstExprValue *err_union_val = ir_const_ptr_pointee(ira, ptr_val, instruction->base.source_node); + ConstExprValue *err_union_val = const_ptr_pointee(ira->codegen, ptr_val); if (err_union_val == nullptr) return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer && @@ -20940,102 +20888,6 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { - Error err; - IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_instruction; - - IrInstruction *value = instruction->value->child; - ZigType *src_type = value->value.type; - if (type_is_invalid(src_type)) - return ira->codegen->invalid_instruction; - - if ((err = ensure_complete_type(ira->codegen, dest_type))) - return ira->codegen->invalid_instruction; - - if ((err = ensure_complete_type(ira->codegen, src_type))) - return ira->codegen->invalid_instruction; - - if (get_codegen_ptr_type(src_type) != nullptr) { - ir_add_error(ira, value, - buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_instruction; - } - - switch (src_type->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: - case ZigTypeIdNamespace: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_instruction; - default: - break; - } - - if (get_codegen_ptr_type(dest_type) != nullptr) { - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_instruction; - } - - switch (dest_type->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: - case ZigTypeIdNamespace: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_instruction; - default: - break; - } - - uint64_t dest_size_bytes = type_size(ira->codegen, dest_type); - uint64_t src_size_bytes = type_size(ira->codegen, src_type); - if (dest_size_bytes != src_size_bytes) { - ir_add_error(ira, &instruction->base, - buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64, - buf_ptr(&dest_type->name), dest_size_bytes, - buf_ptr(&src_type->name), src_size_bytes)); - return ira->codegen->invalid_instruction; - } - - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); - if (!val) - return ira->codegen->invalid_instruction; - - IrInstruction *result = ir_const(ira, &instruction->base, dest_type); - uint8_t *buf = allocate_nonzero(src_size_bytes); - buf_write_value_bytes(ira->codegen, buf, val); - buf_read_value_bytes(ira->codegen, buf, &result->value); - return result; - } - - IrInstruction *result = ir_build_bit_cast(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, nullptr, value); - result->value.type = dest_type; - return result; -} - static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->child; @@ -21875,10 +21727,6 @@ static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrIns return result; } -static IrInstruction *ir_analyze_instruction_result_param(IrAnalyze *ira, IrInstructionResultParam *instruction) { - zig_panic("TODO"); -} - static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, IrInstructionResultPtrCast *instruction) { @@ -22233,8 +22081,6 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction); case IrInstructionIdPtrCastSrc: return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction); - case IrInstructionIdBitCast: - return ir_analyze_instruction_bit_cast(ira, (IrInstructionBitCast *)instruction); case IrInstructionIdIntToPtr: return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction); case IrInstructionIdPtrToInt: @@ -22329,8 +22175,6 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction); case IrInstructionIdResultReturn: return ir_analyze_instruction_result_return(ira, (IrInstructionResultReturn *)instruction); - case IrInstructionIdResultParam: - return ir_analyze_instruction_result_param(ira, (IrInstructionResultParam *)instruction); case IrInstructionIdResultPtrCast: return ir_analyze_instruction_result_ptr_cast(ira, (IrInstructionResultPtrCast *)instruction); case IrInstructionIdResultCast: @@ -22532,7 +22376,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdTestComptime: case IrInstructionIdPtrCastSrc: case IrInstructionIdPtrCastGen: - case IrInstructionIdBitCast: case IrInstructionIdWidenOrShorten: case IrInstructionIdPtrToInt: case IrInstructionIdIntToPtr: @@ -22575,7 +22418,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdResultErrorUnionPayload: case IrInstructionIdResultErrorUnionCode: case IrInstructionIdResultReturn: - case IrInstructionIdResultParam: case IrInstructionIdResultPtrCast: case IrInstructionIdResultCast: case IrInstructionIdResultSliceToBytes: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 8c208c79c8db..afc3452c0e67 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -872,16 +872,6 @@ static void ir_print_ptr_cast_gen(IrPrint *irp, IrInstructionPtrCastGen *instruc fprintf(irp->f, ")"); } -static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { - fprintf(irp->f, "@bitCast("); - if (instruction->dest_type) { - ir_print_other_instruction(irp, instruction->dest_type); - } - fprintf(irp->f, ","); - ir_print_other_instruction(irp, instruction->value); - fprintf(irp->f, ")"); -} - static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) { fprintf(irp->f, "WidenOrShorten("); ir_print_other_instruction(irp, instruction->target); @@ -1347,10 +1337,6 @@ static void ir_print_result_slice_to_bytes(IrPrint *irp, IrInstructionResultSlic fprintf(irp->f, "ResultSliceToBytes"); } -static void ir_print_result_param(IrPrint *irp, IrInstructionResultParam *instruction) { - fprintf(irp->f, "ResultParam"); -} - static void ir_print_result_ptr_cast(IrPrint *irp, IrInstructionResultPtrCast *instruction) { fprintf(irp->f, "ResultPtrCast(ty="); ir_print_other_instruction(irp, instruction->elem_type); @@ -1667,9 +1653,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdPtrCastGen: ir_print_ptr_cast_gen(irp, (IrInstructionPtrCastGen *)instruction); break; - case IrInstructionIdBitCast: - ir_print_bit_cast(irp, (IrInstructionBitCast *)instruction); - break; case IrInstructionIdWidenOrShorten: ir_print_widen_or_shorten(irp, (IrInstructionWidenOrShorten *)instruction); break; @@ -1850,9 +1833,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdResultSliceToBytes: ir_print_result_slice_to_bytes(irp, (IrInstructionResultSliceToBytes *)instruction); break; - case IrInstructionIdResultParam: - ir_print_result_param(irp, (IrInstructionResultParam *)instruction); - break; case IrInstructionIdResultPtrCast: ir_print_result_ptr_cast(irp, (IrInstructionResultPtrCast *)instruction); break; From 34f3d74cad873df9ba787bcedcf79e4a86f3d948 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 23 Nov 2018 10:46:56 -0500 Subject: [PATCH 055/190] copy elision: fix simple int cast ```zig export fn entry() void { var status: i32 = 12; var x = isize(status); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %status = alloca i32, align 4 %x = alloca i64, align 8 store i32 12, i32* %status, align 4, !dbg !51 call void @llvm.dbg.declare(metadata i32* %status, metadata !45, metadata !DIExpression()), !dbg !51 %0 = load i32, i32* %status, align 4, !dbg !52 %1 = sext i32 %0 to i64, !dbg !52 store i64 %1, i64* %x, align 8, !dbg !53 call void @llvm.dbg.declare(metadata i64* %x, metadata !48, metadata !DIExpression()), !dbg !54 ret void, !dbg !55 } ``` --- src/ir.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index b6fc4536224e..55ef5480c6bb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3861,6 +3861,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, buf_ptr(variable_name))); return irb->codegen->invalid_instruction; } + assert(err == ErrorPrimitiveTypeNotFound); } else { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); return ir_gen_value(irb, scope, node, lval, result_loc, value); @@ -15952,7 +15953,7 @@ static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; IrInstruction *deref = ir_get_deref(ira, &instruction->base, ptr); - if (instruction->result_loc == nullptr) + if (instruction->result_loc == nullptr || instruction->result_loc->child == nullptr) return deref; IrInstruction *result_loc = instruction->result_loc->child; From b190b8f407d9285903d8b468fba61d52a5bceb1d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 23 Nov 2018 10:55:12 -0500 Subject: [PATCH 056/190] copy elision: fix @typeName --- src/all_types.hpp | 1 - src/ir.cpp | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index e02161a485b6..d293c7e06675 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3003,7 +3003,6 @@ struct IrInstructionTypeName { IrInstruction base; IrInstruction *type_value; - IrInstruction *result_loc; }; enum LVal { diff --git a/src/ir.cpp b/src/ir.cpp index 55ef5480c6bb..91a3de0ddebf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2292,15 +2292,13 @@ static IrInstruction *ir_build_check_statement_is_void(IrBuilder *irb, Scope *sc } static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *type_value, IrInstruction *result_loc) + IrInstruction *type_value) { IrInstructionTypeName *instruction = ir_build_instruction( irb, scope, source_node); instruction->type_value = type_value; - instruction->result_loc = result_loc; ir_ref_instruction(type_value, irb->current_basic_block); - ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -4657,7 +4655,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value, result_loc); + IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value); return ir_gen_value(irb, scope, node, lval, result_loc, type_name); } case BuiltinFnIdPanic: @@ -21824,7 +21822,7 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; - if (!handle_is_ptr(dest_type)) { + if (!type_has_bits(dest_type) || !handle_is_ptr(dest_type)) { return nullptr; } From 094d3f2df7da25ffeb7ffd17973251bc7eec58ae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 23 Nov 2018 11:11:56 -0500 Subject: [PATCH 057/190] copy elision: fix slicing the slice ir instruction now is specified to have side effects. --- src/ir.cpp | 2 +- src/ir_print.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 91a3de0ddebf..354be08c5ba6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -22326,6 +22326,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdContainerInitFields: case IrInstructionIdContainerInitList: case IrInstructionIdInferCompTime: + case IrInstructionIdSlice: return true; case IrInstructionIdPhi: @@ -22358,7 +22359,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdTruncate: case IrInstructionIdIntType: case IrInstructionIdBoolNot: - case IrInstructionIdSlice: case IrInstructionIdMemberCount: case IrInstructionIdMemberType: case IrInstructionIdMemberName: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index afc3452c0e67..0b48de20a883 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -718,7 +718,8 @@ static void ir_print_slice(IrPrint *irp, IrInstructionSlice *instruction) { fprintf(irp->f, ".."); if (instruction->end) ir_print_other_instruction(irp, instruction->end); - fprintf(irp->f, "]"); + fprintf(irp->f, "] result="); + ir_print_other_instruction(irp, instruction->result_loc); } static void ir_print_member_count(IrPrint *irp, IrInstructionMemberCount *instruction) { From e15e31094bc4b183e481e4a5b6d4edb69baef490 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 23 Nov 2018 12:48:22 -0500 Subject: [PATCH 058/190] copy elision: better slice codegen ```zig export fn entry() void { var envp_optional: [*]?[*]u8 = undefined; var envp_count: usize = 0; var envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count]; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %envp_optional = alloca i8**, align 8 %envp_count = alloca i64, align 8 %envp = alloca %"[][*]u8", align 8 call void @llvm.dbg.declare(metadata i8*** %envp_optional, metadata !45, metadata !DIExpression()), !dbg !57 store i64 0, i64* %envp_count, align 8, !dbg !58 call void @llvm.dbg.declare(metadata i64* %envp_count, metadata !48, metadata !DIExpression()), !dbg !58 %0 = load i8**, i8*** %envp_optional, align 8, !dbg !59 %1 = load i64, i64* %envp_count, align 8, !dbg !60 %2 = getelementptr inbounds %"[][*]u8", %"[][*]u8"* %envp, i32 0, i32 0, !dbg !61 %3 = getelementptr inbounds i8*, i8** %0, i64 0, !dbg !61 store i8** %3, i8*** %2, align 8, !dbg !61 %4 = getelementptr inbounds %"[][*]u8", %"[][*]u8"* %envp, i32 0, i32 1, !dbg !61 %5 = sub nsw i64 %1, 0, !dbg !61 store i64 %5, i64* %4, align 8, !dbg !61 ret void, !dbg !62 } ``` --- src/codegen.cpp | 7 ++---- src/ir.cpp | 63 +++++++++++++++++++++++++++++++----------------- src/ir_print.cpp | 2 ++ 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 249d2963b318..0e72daffe439 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4333,11 +4333,8 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrIns } static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInstructionSlice *instruction) { - LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); - ZigType *array_ptr_type = instruction->ptr->value.type; - assert(array_ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = array_ptr_type->data.pointer.child_type; - LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); + LLVMValueRef array_ptr = ir_llvm_value(g, instruction->ptr); + ZigType *array_type = instruction->ptr->value.type; LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); assert(LLVMGetTypeKind(LLVMTypeOf(tmp_struct_ptr)) == LLVMPointerTypeKind); diff --git a/src/ir.cpp b/src/ir.cpp index 354be08c5ba6..3687b221b14f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1722,14 +1722,16 @@ static IrInstruction *ir_build_import(IrBuilder *irb, Scope *scope, AstNode *sou } static IrInstruction *ir_build_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, - bool is_const, bool is_volatile) + bool is_const, bool is_volatile, IrInstruction *result_loc) { IrInstructionRef *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; instruction->is_const = is_const; instruction->is_volatile = is_volatile; + instruction->result_loc = result_loc; ir_ref_instruction(value, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -3155,6 +3157,12 @@ static IrInstruction *ir_gen_multi(IrBuilder *irb, Scope *scope, AstNode *node, zig_unreachable(); } +static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { + if (result_loc) + return result_loc; + return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); +} + static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, IrInstruction *result_loc, IrInstruction *value) { @@ -3171,7 +3179,7 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, case LValPtr: // We needed a pointer to a value, but we got a value. So we create // an instruction which just makes a pointer of it. - return ir_build_ref(irb, scope, value->source_node, value, false, false); + return ir_build_ref(irb, scope, value->source_node, value, false, false, result_loc); case LValNone: return value; case LValOptional: @@ -3406,12 +3414,6 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n return var; } -static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { - if (result_loc) - return result_loc; - return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); -} - static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node, LVal lval, IrInstruction *result_loc) { @@ -5482,7 +5484,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node, err_union_ptr, false); IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ? - ir_build_ref(irb, payload_scope, symbol_node, payload_ptr, true, false) : payload_ptr; + ir_build_ref(irb, payload_scope, symbol_node, payload_ptr, true, false, nullptr) : payload_ptr; ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_ptr); } @@ -5559,7 +5561,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstruction *payload_ptr = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ? - ir_build_ref(irb, child_scope, symbol_node, payload_ptr, true, false) : payload_ptr; + ir_build_ref(irb, child_scope, symbol_node, payload_ptr, true, false, nullptr) : payload_ptr; ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_ptr); ZigList incoming_values = {0}; @@ -5747,7 +5749,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false, PtrLenSingle); IrInstruction *elem_var_ptr = node->data.for_expr.elem_is_ptr ? - ir_build_ref(irb, child_scope, elem_node, elem_ptr, true, false) : elem_ptr; + ir_build_ref(irb, child_scope, elem_node, elem_ptr, true, false, nullptr) : elem_ptr; ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, elem_var_ptr); ZigList incoming_values = {0}; @@ -5970,7 +5972,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN IrInstruction *payload_ptr = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); IrInstruction *var_ptr = var_is_ptr ? - ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false) : payload_ptr; + ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false, nullptr) : payload_ptr; ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_ptr); var_scope = var->child_scope; } else { @@ -6045,7 +6047,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); IrInstruction *var_ptr = var_is_ptr ? - ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false) : payload_ptr; + ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false, nullptr) : payload_ptr; ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_ptr); var_scope = var->child_scope; } else { @@ -6115,7 +6117,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit IrInstruction *payload_ptr = (prong_value == nullptr) ? target_value_ptr : ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); IrInstruction *var_ptr = var_is_ptr ? - ir_build_ref(irb, scope, var_symbol_node, payload_ptr, true, false) : payload_ptr; + ir_build_ref(irb, scope, var_symbol_node, payload_ptr, true, false, nullptr) : payload_ptr; ir_build_var_decl_src(irb, scope, var_symbol_node, var, var_type, nullptr, var_ptr); child_scope = var->child_scope; } else { @@ -7655,7 +7657,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); - IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); + IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false, nullptr); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr, nullptr); IrInstruction *mem_slice_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); @@ -10344,7 +10346,7 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so } static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value, - bool is_const, bool is_volatile) + bool is_const, bool is_volatile, IrInstruction *result_loc) { Error err; @@ -10365,7 +10367,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, is_const, is_volatile, PtrLenSingle, 0, 0, 0); IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, value, is_const, is_volatile); + source_instruction->source_node, value, is_const, is_volatile, result_loc); new_instruction->value.type = ptr_type; new_instruction->value.data.rh_ptr = RuntimeHintPtrStack; return new_instruction; @@ -10402,7 +10404,7 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s IrInstruction *end = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &end->value, array_type->data.array.len); - if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); + if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false, nullptr); // TODO don't pass nullptr to ir_build_slice BREAKPOINT; @@ -11320,7 +11322,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ira->codegen->invalid_instruction; } if (!type_has_bits(actual_type)) { - return ir_get_ref(ira, source_instr, value, false, false); + return ir_get_ref(ira, source_instr, value, false, false, nullptr); } } @@ -15251,7 +15253,7 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, IrInstruction *bound_fn_value = ir_build_const_bound_fn(&ira->new_irb, source_instr->scope, source_instr->source_node, fn_entry, container_ptr); - return ir_get_ref(ira, source_instr, bound_fn_value, true, false); + return ir_get_ref(ira, source_instr, bound_fn_value, true, false, nullptr); } } const char *prefix_name; @@ -17027,7 +17029,14 @@ static IrInstruction *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRe IrInstruction *value = ref_instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - return ir_get_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile); + IrInstruction *result_loc = nullptr; + if (ref_instruction->result_loc != nullptr) { + result_loc = ref_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + } + return ir_get_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile, + result_loc); } static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction, @@ -19393,6 +19402,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction } ZigType *return_type; + IrInstruction *ptr; if (array_type->id == ZigTypeIdArray) { bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic && @@ -19403,7 +19413,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction PtrLenUnknown, ptr_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); + ptr = ptr_ptr; } else if (array_type->id == ZigTypeIdPointer) { + assert(ptr_ptr->id == IrInstructionIdRef); + IrInstructionRef *ref_inst = reinterpret_cast(ptr_ptr); + ptr = ref_inst->value; + if (array_type->data.pointer.ptr_len == PtrLenSingle) { ZigType *main_type = array_type->data.pointer.child_type; if (main_type->id == ZigTypeIdArray) { @@ -19425,6 +19440,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction } } } else if (is_slice(array_type)) { + assert(ptr_ptr->id == IrInstructionIdRef); + IrInstructionRef *ref_inst = reinterpret_cast(ptr_ptr); + ptr = ref_inst->value; ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; return_type = get_slice_type(ira->codegen, ptr_type); } else { @@ -19440,6 +19458,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; + // TODO make this use ptr not ptr_ptr if (instr_is_comptime(ptr_ptr) && value_is_comptime(&casted_start->value) && (!end || value_is_comptime(&end->value))) @@ -19658,7 +19677,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction IrInstruction *new_instruction = ir_build_slice(&ira->new_irb, instruction->base.scope, instruction->base.source_node, - ptr_ptr, casted_start, end, instruction->safety_check_on, + ptr, casted_start, end, instruction->safety_check_on, result_loc); new_instruction->value.type = return_type; return new_instruction; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 0b48de20a883..121b5b0b1935 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -549,6 +549,8 @@ static void ir_print_ref(IrPrint *irp, IrInstructionRef *instruction) { const char *volatile_str = instruction->is_volatile ? "volatile " : ""; fprintf(irp->f, "%s%sref ", const_str, volatile_str); ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, " result="); + ir_print_other_instruction(irp, instruction->result_loc); } static void ir_print_compile_err(IrPrint *irp, IrInstructionCompileErr *instruction) { From baedbf5de5692d487c37ff55dc93f338f6a5d8a6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 23 Nov 2018 12:56:47 -0500 Subject: [PATCH 059/190] copy elision: better slice analysis when slicing slices and pointers, don't assume the ptr instruction is a ref, but handle it optimally if it is --- src/ir.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 3687b221b14f..8ea8b4340748 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11580,7 +11580,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } } } - // TODO if the instruction is a const ref instruction we can skip it + // if the instruction is a const ref instruction we can skip it + if (ptr->id == IrInstructionIdRef) { + IrInstructionRef *ref_inst = reinterpret_cast(ptr); + return ref_inst->value; + } IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, source_instruction->source_node, ptr, nullptr); load_ptr_instruction->value.type = child_type; @@ -19415,9 +19419,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction return_type = get_slice_type(ira->codegen, slice_ptr_type); ptr = ptr_ptr; } else if (array_type->id == ZigTypeIdPointer) { - assert(ptr_ptr->id == IrInstructionIdRef); - IrInstructionRef *ref_inst = reinterpret_cast(ptr_ptr); - ptr = ref_inst->value; + ptr = ir_get_deref(ira, &instruction->base, ptr_ptr); + if (type_is_invalid(ptr->value.type)) + return ira->codegen->invalid_instruction; if (array_type->data.pointer.ptr_len == PtrLenSingle) { ZigType *main_type = array_type->data.pointer.child_type; @@ -19440,9 +19444,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction } } } else if (is_slice(array_type)) { - assert(ptr_ptr->id == IrInstructionIdRef); - IrInstructionRef *ref_inst = reinterpret_cast(ptr_ptr); - ptr = ref_inst->value; + ptr = ir_get_deref(ira, &instruction->base, ptr_ptr); + if (type_is_invalid(ptr->value.type)) + return ira->codegen->invalid_instruction; ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; return_type = get_slice_type(ira->codegen, ptr_type); } else { From 2eda967e0a3db4dd41a8d8d294dc7c938c9bf123 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 23 Nov 2018 13:34:21 -0500 Subject: [PATCH 060/190] copy elision: fix `try` when no result location --- src/ir.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 8ea8b4340748..6b1768a989e3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3307,7 +3307,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindError: { assert(expr_node); - IrInstruction *err_val = ir_gen_node(irb, expr_node, scope, LValErrorUnion, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, result_loc); + IrInstruction *err_val = ir_gen_node(irb, expr_node, scope, LValErrorUnion, ensured_result_loc); if (err_val == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_val); @@ -3332,7 +3333,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, } ir_set_cursor_at_end_and_append_block(irb, continue_block); - return ir_gen_result(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, ensured_result_loc); } } zig_unreachable(); From c53a5572deb78ea98b231cdb0c1a8ba935363bd7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 23 Nov 2018 15:39:29 -0500 Subject: [PATCH 061/190] copy elision: fix for loop --- src/ir.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 0c26e4aada22..8c0b395c13ac 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3175,6 +3175,8 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, { if (value == irb->codegen->invalid_instruction) return value; + if (instr_is_unreachable(value)) + return value; if (result_loc != nullptr) { ir_build_store_ptr(irb, scope, node, result_loc, value); if (lval != LValNone) { @@ -3500,6 +3502,13 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode return ir_gen_result(irb, parent_scope, block_node, lval, scope_block->result_loc); } else { ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); + if (parent_scope->id == ScopeIdLoop) { + if (reinterpret_cast(parent_scope)->result_loc == result_loc) { + // This block is a loop. We do not need to generate a value for the block + // returning as it is handled by the loop generation code. + return ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)); + } + } return ir_gen_value(irb, parent_scope, block_node, lval, result_loc, ir_mark_gen(ir_build_const_void(irb, child_scope, block_node))); } From 7ab89835dbe994af2283b4870d79a2e8615cd4a7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 23 Nov 2018 17:21:19 -0500 Subject: [PATCH 062/190] for loops: use i < N rather than i != N empirically, LLVM handles i < N better. You can even observe it in clang with this C code: ```c void entry(void) { for (unsigned i = 0; i != 10; i += 1) { if (i == 1) break; } } ``` --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 8c0b395c13ac..cdaab42d7e35 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5760,7 +5760,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca, nullptr); - IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpNotEq, index_val, len_val, false); + IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime, result_loc)); ir_set_cursor_at_end_and_append_block(irb, body_block); From 8236c5ae172b4d087449caa1bca351dca288656d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 24 Nov 2018 11:04:40 -0500 Subject: [PATCH 063/190] copy elision: fix result optional payload for pointer types --- src/codegen.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 4a1fd5daf379..6ede465e57ac 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5107,9 +5107,14 @@ static LLVMValueRef ir_render_result_optional_payload(CodeGen *g, IrExecutable * IrInstructionResultOptionalPayload *instruction) { LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); - LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_null_index, ""); - gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); - return LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_child_index, ""); + ZigType *child_type = instruction->base.value.type; + if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { + return prev_result_loc; + } else { + LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_null_index, ""); + gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); + return LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_child_index, ""); + } } static LLVMValueRef ir_render_result_slice_ptr(CodeGen *g, IrExecutable *executable, From 82c8617ca62e2f86bdc6d363ea1785e05f43b12f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 24 Nov 2018 13:39:13 -0500 Subject: [PATCH 064/190] copy elision: fix generic functions --- src/ir.cpp | 110 ++++++++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index cdaab42d7e35..568becdb5317 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13921,6 +13921,57 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return result; } +static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, + IrInstructionCall *call_instruction, ZigFn *fn_entry, IrInstruction *fn_ref, size_t call_param_count, + IrInstruction **casted_args, FnInline fn_inline, IrInstruction *casted_new_stack) +{ + IrInstruction *casted_result_loc = nullptr; + + ZigType *scalar_result_type; + ZigType *payload_result_type; + if (return_type->id == ZigTypeIdErrorUnion) { + scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); + payload_result_type = return_type->data.error_union.payload_type; + } else { + payload_result_type = return_type; + scalar_result_type = return_type; + } + + bool need_store_ptr = (return_type == payload_result_type) && !handle_is_ptr(return_type) && + call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr; + + if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { + IrInstruction *prev_result_loc = call_instruction->result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + if (instr_is_comptime(prev_result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, prev_result_loc, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + ptr_val->special = ConstValSpecialRuntime; + } + } + + casted_result_loc = ir_implicit_cast_result(ira, prev_result_loc, payload_result_type, need_store_ptr); + if (casted_result_loc == nullptr) + casted_result_loc = prev_result_loc; + if (type_is_invalid(casted_result_loc->value.type)) + return ira->codegen->invalid_instruction; + } + + IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, + call_instruction->base.scope, call_instruction->base.source_node, + fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, + casted_new_stack, casted_result_loc, nullptr); + new_call_instruction->value.type = scalar_result_type; + + if (need_store_ptr) { + ir_analyze_store_ptr(ira, &call_instruction->base, casted_result_loc, new_call_instruction); + } + + return ir_finish_anal(ira, new_call_instruction); +} static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, @@ -14401,15 +14452,8 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ir_finish_anal(ira, result); } - assert(async_allocator_inst == nullptr); - IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, - call_instruction->base.scope, call_instruction->base.source_node, - impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, - call_instruction->is_async, nullptr, casted_new_stack, nullptr, nullptr); - new_call_instruction->value.type = return_type; - - - return ir_finish_anal(ira, new_call_instruction); + return analyze_runtime_call(ira, return_type, call_instruction, impl_fn, nullptr, + impl_param_count, casted_args, fn_inline, casted_new_stack); } ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); @@ -14501,52 +14545,8 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; } - IrInstruction *casted_result_loc = nullptr; - - ZigType *scalar_result_type; - ZigType *payload_result_type; - if (return_type->id == ZigTypeIdErrorUnion) { - scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); - payload_result_type = return_type->data.error_union.payload_type; - } else { - payload_result_type = return_type; - scalar_result_type = return_type; - } - - bool need_store_ptr = (return_type == payload_result_type) && !handle_is_ptr(return_type) && - call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr; - - if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { - IrInstruction *prev_result_loc = call_instruction->result_loc->child; - if (type_is_invalid(prev_result_loc->value.type)) - return ira->codegen->invalid_instruction; - if (instr_is_comptime(prev_result_loc)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, prev_result_loc, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - ptr_val->special = ConstValSpecialRuntime; - } - } - - casted_result_loc = ir_implicit_cast_result(ira, prev_result_loc, payload_result_type, need_store_ptr); - if (casted_result_loc == nullptr) - casted_result_loc = prev_result_loc; - if (type_is_invalid(casted_result_loc->value.type)) - return ira->codegen->invalid_instruction; - } - - IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, - call_instruction->base.scope, call_instruction->base.source_node, - fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, - casted_new_stack, casted_result_loc, nullptr); - new_call_instruction->value.type = scalar_result_type; - - if (need_store_ptr) { - ir_analyze_store_ptr(ira, &call_instruction->base, casted_result_loc, new_call_instruction); - } - - return ir_finish_anal(ira, new_call_instruction); + return analyze_runtime_call(ira, return_type, call_instruction, fn_entry, fn_ref, + call_param_count, casted_args, fn_inline, casted_new_stack); } static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) { From 05a325e81bbe5d8a1951f86406aa0c4800bd2256 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 25 Nov 2018 11:05:38 -0500 Subject: [PATCH 065/190] copy elision: fix switch when no result location for target --- src/ir.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 568becdb5317..c7eeb5ddc34d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3133,6 +3133,7 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, IrInstruction *result_loc) { + assert(result_loc != nullptr); switch (lval) { case LValNone: return ir_build_load_ptr(irb, scope, node, result_loc, nullptr); @@ -3151,8 +3152,13 @@ static IrInstruction *ir_gen_multi(IrBuilder *irb, Scope *scope, AstNode *node, switch (lval) { case LValNone: return value; - case LValPtr: - return result_loc; + case LValPtr: { + if (result_loc != nullptr) + return result_loc; + // We needed a pointer to a value, but we got a value. So we create + // an instruction which just makes a pointer of it. + return ir_build_ref(irb, scope, value->source_node, value, false, false, nullptr); + } case LValErrorUnion: case LValOptional: { IrInstruction *err_alloca = ir_build_alloca_src(irb, scope, value->source_node, @@ -17008,9 +17014,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *result = ir_build_load_ptr(&ira->new_irb, - switch_target_instruction->base.scope, switch_target_instruction->base.source_node, - target_value_ptr, nullptr); + IrInstruction *result = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); result->value.type = target_type; return result; } @@ -17040,8 +17044,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr, nullptr); + IrInstruction *union_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); union_value->value.type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, @@ -17065,8 +17068,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr, nullptr); + IrInstruction *enum_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); enum_value->value.type = target_type; return enum_value; } From 252938c8b223970ae06814ed60b7f128b6bc6d70 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 25 Nov 2018 11:36:10 -0500 Subject: [PATCH 066/190] copy elision: comptime slice ```zig export fn entry() void { var slice = "aoeu"[0..1]; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %slice = alloca %"[]u8", align 8 %0 = bitcast %"[]u8"* %slice to i8*, !dbg !47 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%"[]u8"* @2 to i8*), i64 16, i1 false), !dbg !47 call void @llvm.dbg.declare(metadata %"[]u8"* %slice, metadata !45, metadata !DIExpression()), !dbg !47 ret void, !dbg !48 } ``` --- src/ir.cpp | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index c7eeb5ddc34d..84b45c938e31 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19566,9 +19566,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (type_is_invalid(ptr_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = ptr_ptr->value.type; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = ptr_type->data.pointer.child_type; + ZigType *ptr_ptr_type = ptr_ptr->value.type; + assert(ptr_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = ptr_ptr_type->data.pointer.child_type; IrInstruction *start = instruction->start->child; if (type_is_invalid(start->value.type)) @@ -19591,18 +19591,17 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction end = nullptr; } - ZigType *return_type; + ZigType *slice_ptr_type; IrInstruction *ptr; if (array_type->id == ZigTypeIdArray) { bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic && ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst; - ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, - ptr_type->data.pointer.is_const || is_comptime_const, - ptr_type->data.pointer.is_volatile, + slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, + ptr_ptr_type->data.pointer.is_const || is_comptime_const, + ptr_ptr_type->data.pointer.is_volatile, PtrLenUnknown, - ptr_type->data.pointer.explicit_alignment, 0, 0); - return_type = get_slice_type(ira->codegen, slice_ptr_type); + ptr_ptr_type->data.pointer.explicit_alignment, 0, 0); ptr = ptr_ptr; } else if (array_type->id == ZigTypeIdPointer) { ptr = ir_get_deref(ira, &instruction->base, ptr_ptr); @@ -19612,18 +19611,17 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (array_type->data.pointer.ptr_len == PtrLenSingle) { ZigType *main_type = array_type->data.pointer.child_type; if (main_type->id == ZigTypeIdArray) { - ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, + slice_ptr_type = get_pointer_to_type_extra(ira->codegen, main_type->data.pointer.child_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, PtrLenUnknown, array_type->data.pointer.explicit_alignment, 0, 0); - return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer")); return ira->codegen->invalid_instruction; } } else { - return_type = get_slice_type(ira->codegen, array_type); + slice_ptr_type = array_type; if (!end) { ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); return ira->codegen->invalid_instruction; @@ -19633,13 +19631,13 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction ptr = ir_get_deref(ira, &instruction->base, ptr_ptr); if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; - return_type = get_slice_type(ira->codegen, ptr_type); + slice_ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); return ira->codegen->invalid_instruction; } + ZigType *return_type = get_slice_type(ira->codegen, slice_ptr_type); IrInstruction *result_loc = instruction->result_loc->child; if (type_is_invalid(result_loc->value.type)) @@ -19648,7 +19646,6 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; - // TODO make this use ptr not ptr_ptr if (instr_is_comptime(ptr_ptr) && value_is_comptime(&casted_start->value) && (!end || value_is_comptime(&end->value))) @@ -19799,10 +19796,6 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; } - if (result_loc->value.special != ConstValSpecialRuntime) { - zig_panic("TODO slice with comptime ptr and values"); - } - IrInstruction *result = ir_const(ira, &instruction->base, return_type); ConstExprValue *out_val = &result->value; out_val->data.x_struct.fields = create_const_vals(2); @@ -19853,6 +19846,10 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); + if (result_loc->value.special != ConstValSpecialRuntime) { + return ir_analyze_store_ptr(ira, &instruction->base, result_loc, result); + } + return result; } From be17a1e3eafb2580ad59439b69b7d4f75dda0098 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 25 Nov 2018 19:48:55 -0500 Subject: [PATCH 067/190] copy elision: orelse ```zig export fn entry() void { var y: ?Bar = null; var x = y orelse return; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %y = alloca { %Bar, i1 }, align 4 %x = alloca %Bar, align 4 %0 = bitcast { %Bar, i1 }* %y to i8*, !dbg !59 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast ({ %Bar, i1 }* @0 to i8*), i64 12, i1 false), !dbg !59 call void @llvm.dbg.declare(metadata { %Bar, i1 }* %y, metadata !45, metadata !DIExpression()), !dbg !59 %1 = getelementptr inbounds { %Bar, i1 }, { %Bar, i1 }* %y, i32 0, i32 0, !dbg !60 %2 = bitcast %Bar* %1 to i8*, !dbg !60 %3 = bitcast %Bar* %x to i8*, !dbg !60 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %3, i8* align 4 %2, i64 8, i1 false), !dbg !60 %4 = getelementptr inbounds { %Bar, i1 }, { %Bar, i1 }* %y, i32 0, i32 1, !dbg !60 %5 = load i1, i1* %4, align 1, !dbg !60 br i1 %5, label %OrElseEnd, label %OrElseNull, !dbg !61 OrElseNull: ; preds = %Entry ret void, !dbg !62 OrElseEnd: ; preds = %Entry call void @llvm.dbg.declare(metadata %Bar* %x, metadata !57, metadata !DIExpression()), !dbg !63 ret void, !dbg !64 } ``` --- src/all_types.hpp | 8 +++++ src/codegen.cpp | 20 +++++++++++++ src/ir.cpp | 76 +++++++++++++++++++++++++++++++++++++++-------- src/ir_print.cpp | 9 ++++++ 4 files changed, 100 insertions(+), 13 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index d293c7e06675..d7ff32f8924f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2197,6 +2197,7 @@ enum IrInstructionId { IrInstructionIdAllocaSrc, IrInstructionIdAllocaGen, IrInstructionIdAssertNonError, + IrInstructionIdAssertNonNull, IrInstructionIdErrorUnionFieldErrorSet, IrInstructionIdFirstArgResultLoc, IrInstructionIdInferArrayType, @@ -3362,6 +3363,13 @@ struct IrInstructionAssertNonError { IrInstruction *err_code; }; +// This is the safety check when using `.?`. +struct IrInstructionAssertNonNull { + IrInstruction base; + + IrInstruction *is_non_null; +}; + struct IrInstructionFirstArgResultLoc { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index afbf5d79be60..02bd2a55a38d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5170,6 +5170,24 @@ static LLVMValueRef ir_render_assert_non_error(CodeGen *g, IrExecutable *executa return nullptr; } +static LLVMValueRef ir_render_assert_non_null(CodeGen *g, IrExecutable *executable, + IrInstructionAssertNonNull *instruction) +{ + if (!ir_want_runtime_safety(g, &instruction->base)) { + return nullptr; + } + LLVMValueRef is_non_null = ir_llvm_value(g, instruction->is_non_null); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); + LLVMBuildCondBr(g->builder, is_non_null, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return nullptr; +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5421,6 +5439,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_result_error_union_code(g, executable, (IrInstructionResultErrorUnionCode *)instruction); case IrInstructionIdAssertNonError: return ir_render_assert_non_error(g, executable, (IrInstructionAssertNonError *)instruction); + case IrInstructionIdAssertNonNull: + return ir_render_assert_non_null(g, executable, (IrInstructionAssertNonNull *)instruction); case IrInstructionIdResultSliceToBytes: zig_panic("TODO"); case IrInstructionIdResultBytesToSlice: diff --git a/src/ir.cpp b/src/ir.cpp index 84b45c938e31..411d2ea526a5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -908,6 +908,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonError * return IrInstructionIdAssertNonError; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonNull *) { + return IrInstructionIdAssertNonNull; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnionFieldErrorSet *) { return IrInstructionIdErrorUnionFieldErrorSet; } @@ -2892,6 +2896,17 @@ static IrInstruction *ir_build_assert_non_error(IrBuilder *irb, Scope *scope, As return &instruction->base; } +static IrInstruction *ir_build_assert_non_null(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *is_non_null) +{ + IrInstructionAssertNonNull *instruction = ir_build_instruction(irb, scope, source_node); + instruction->is_non_null = is_non_null; + + ir_ref_instruction(is_non_null, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_error_union_field_error_set(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) { @@ -3223,8 +3238,15 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); return ir_build_error_union_field_error_set(irb, scope, node, ptr); } - case LValOptional: - zig_panic("TODO"); + case LValOptional: { + // ptr points to an optional; + // result_loc points to the result payload + // must return is_non_null bool + IrInstruction *payload_ptr = ir_build_unwrap_maybe(irb, scope, node, ptr, false); + ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); + IrInstruction *loaded_ptr = ir_build_load_ptr(irb, scope, node, ptr, nullptr); + return ir_build_test_nonnull(irb, scope, node, loaded_ptr); + } } zig_unreachable(); } @@ -3323,10 +3345,12 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, { assert(expr_node); IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, result_loc); - IrInstruction *err_val = ir_gen_node(irb, expr_node, scope, LValErrorUnion, ensured_result_loc); - if (err_val == irb->codegen->invalid_instruction) + IrInstruction *ptr_opt_err_code = ir_gen_node(irb, expr_node, scope, LValErrorUnion, ensured_result_loc); + if (ptr_opt_err_code == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_val); + + IrInstruction *opt_err_code = ir_build_load_ptr(irb, scope, node, ptr_opt_err_code, nullptr); + IrInstruction *is_err_val = ir_build_test_nonnull(irb, scope, node, opt_err_code); IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue"); @@ -3340,6 +3364,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime, nullptr)); ir_set_cursor_at_end_and_append_block(irb, return_block); + IrInstruction *ptr_err_code = ir_build_unwrap_maybe(irb, scope, node, ptr_opt_err_code, false); + IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, ptr_err_code, nullptr); if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) { if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr(irb, scope, node); @@ -7373,17 +7399,14 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_ptr(irb, scope, node, lval, result_loc, ptr_inst); } case NodeTypeUnwrapOptional: { + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); AstNode *expr_node = node->data.unwrap_optional.expr; - - IrInstruction *maybe_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, nullptr); - if (maybe_ptr == irb->codegen->invalid_instruction) + IrInstruction *is_non_null = ir_gen_node(irb, expr_node, scope, LValOptional, ensured_result_loc); + if (is_non_null == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); - if (lval == LValPtr) - return unwrapped_ptr; - - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr, nullptr); + ir_build_assert_non_null(irb, scope, expr_node, is_non_null); + return ir_gen_result(irb, scope, node, lval, ensured_result_loc); } case NodeTypeBoolLiteral: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_bool_literal(irb, scope, node)); @@ -20469,6 +20492,30 @@ static IrInstruction *ir_analyze_instruction_assert_non_error(IrAnalyze *ira, return ir_const_void(ira, &instruction->base); } +static IrInstruction *ir_analyze_instruction_assert_non_null(IrAnalyze *ira, + IrInstructionAssertNonNull *instruction) +{ + IrInstruction *is_non_null = instruction->is_non_null->child; + if (type_is_invalid(is_non_null->value.type)) + return ira->codegen->invalid_instruction; + + assert(is_non_null->value.type->id == ZigTypeIdBool); + + if (instr_is_comptime(is_non_null)) { + bool is_non_null_bool; + if (!ir_resolve_bool(ira, is_non_null, &is_non_null_bool)) + return ira->codegen->invalid_instruction; + if (is_non_null_bool) + return ir_const_void(ira, &instruction->base); + ir_add_error(ira, &instruction->base, buf_sprintf("unable to unwrap null")); + return ira->codegen->invalid_instruction; + } + + ir_build_assert_non_null(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, is_non_null); + return ir_const_void(ira, &instruction->base); +} + static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) { Error err; AstNode *proto_node = instruction->base.source_node; @@ -22272,6 +22319,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstructionUnwrapErrPayload *)instruction); case IrInstructionIdAssertNonError: return ir_analyze_instruction_assert_non_error(ira, (IrInstructionAssertNonError *)instruction); + case IrInstructionIdAssertNonNull: + return ir_analyze_instruction_assert_non_null(ira, (IrInstructionAssertNonNull *)instruction); case IrInstructionIdFnProto: return ir_analyze_instruction_fn_proto(ira, (IrInstructionFnProto *)instruction); case IrInstructionIdTestComptime: @@ -22530,6 +22579,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdMarkErrRetTracePtr: case IrInstructionIdAtomicRmw: case IrInstructionIdAssertNonError: + case IrInstructionIdAssertNonNull: case IrInstructionIdContainerInitFields: case IrInstructionIdContainerInitList: case IrInstructionIdInferCompTime: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 121b5b0b1935..b0a6b7bf82bf 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1374,6 +1374,12 @@ static void ir_print_assert_non_error(IrPrint *irp, IrInstructionAssertNonError fprintf(irp->f, ")"); } +static void ir_print_assert_non_null(IrPrint *irp, IrInstructionAssertNonNull *instruction) { + fprintf(irp->f, "AssertNonNull("); + ir_print_other_instruction(irp, instruction->is_non_null); + fprintf(irp->f, ")"); +} + static void ir_print_error_union_field_error_set(IrPrint *irp, IrInstructionErrorUnionFieldErrorSet *instruction) { fprintf(irp->f, "ErrorUnionFieldErrorSet("); ir_print_other_instruction(irp, instruction->ptr); @@ -1851,6 +1857,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdAssertNonError: ir_print_assert_non_error(irp, (IrInstructionAssertNonError *)instruction); break; + case IrInstructionIdAssertNonNull: + ir_print_assert_non_null(irp, (IrInstructionAssertNonNull *)instruction); + break; case IrInstructionIdErrorUnionFieldErrorSet: ir_print_error_union_field_error_set(irp, (IrInstructionErrorUnionFieldErrorSet *)instruction); break; From ca440b3c16d00cdef3c7cefc777bfd0b354314e2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 27 Nov 2018 20:31:55 -0500 Subject: [PATCH 068/190] copy elision: @cmpxchg ```zig export fn entry() void { var linux_lock: i32 = undefined; var c = @cmpxchgWeak(i32, &linux_lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse return; } ``` ```llvm define void @entry() #2 !dbg !50 { Entry: %linux_lock = alloca i32, align 4 %c = alloca i32, align 4 %0 = bitcast i32* %linux_lock to i8*, !dbg !59 call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 -86, i64 4, i1 false), !dbg !59 call void @llvm.dbg.declare(metadata i32* %linux_lock, metadata !54, metadata !DIExpression()), !dbg !59 %1 = cmpxchg weak i32* %linux_lock, i32 0, i32 1 acquire monotonic, !dbg !60 %2 = extractvalue { i32, i1 } %1, 1, !dbg !60 br i1 %2, label %CmpXchgPhi, label %CmpXchgNonNull, !dbg !60 OrElseNull: ; preds = %CmpXchgPhi ret void, !dbg !61 OrElseEnd: ; preds = %CmpXchgPhi call void @llvm.dbg.declare(metadata i32* %c, metadata !57, metadata !DIExpression()), !dbg !62 ret void, !dbg !63 CmpXchgNonNull: ; preds = %Entry %3 = extractvalue { i32, i1 } %1, 0, !dbg !60 store i32 %3, i32* %c, align 4, !dbg !60 br label %CmpXchgPhi, !dbg !60 CmpXchgPhi: ; preds = %CmpXchgNonNull, %Entry %4 = phi i1 [ true, %CmpXchgNonNull ], [ false, %Entry ], !dbg !60 br i1 %4, label %OrElseEnd, label %OrElseNull, !dbg !64 } ``` --- src/all_types.hpp | 28 +++++- src/codegen.cpp | 71 ++++++++------ src/ir.cpp | 244 +++++++++++++++++++++++++++++++++++++--------- src/ir_print.cpp | 42 ++++++-- 4 files changed, 300 insertions(+), 85 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index d7ff32f8924f..ca43b9424e92 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2101,7 +2101,8 @@ enum IrInstructionId { IrInstructionIdCompileLog, IrInstructionIdErrName, IrInstructionIdEmbedFile, - IrInstructionIdCmpxchg, + IrInstructionIdCmpxchgSrc, + IrInstructionIdCmpxchgGen, IrInstructionIdFence, IrInstructionIdTruncate, IrInstructionIdIntCast, @@ -2202,6 +2203,7 @@ enum IrInstructionId { IrInstructionIdFirstArgResultLoc, IrInstructionIdInferArrayType, IrInstructionIdInferCompTime, + IrInstructionIdSetNonNullBit, }; struct IrInstruction { @@ -2676,7 +2678,7 @@ struct IrInstructionEmbedFile { IrInstruction *name; }; -struct IrInstructionCmpxchg { +struct IrInstructionCmpxchgSrc { IrInstruction base; IrInstruction *type_value; @@ -2687,8 +2689,18 @@ struct IrInstructionCmpxchg { IrInstruction *failure_order_value; IrInstruction *result_loc; - // if this instruction gets to runtime then we know these values: + bool is_weak; +}; + +struct IrInstructionCmpxchgGen { + IrInstruction base; + ZigType *type; + IrInstruction *ptr; + IrInstruction *cmp_value; + IrInstruction *new_value; + IrInstruction *result_loc; + AtomicOrder success_order; AtomicOrder failure_order; @@ -3302,6 +3314,8 @@ struct IrInstructionResultOptionalPayload { IrInstruction base; IrInstruction *prev_result_loc; + IrInstruction *payload_type; + bool make_non_null; }; struct IrInstructionResultBytesToSlice { @@ -3391,6 +3405,14 @@ struct IrInstructionInferCompTime { IrInstruction *new_result_loc; }; +struct IrInstructionSetNonNullBit { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *non_null_bit; + IrInstruction *new_result_loc; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index 02bd2a55a38d..90171985d318 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3827,7 +3827,7 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable return gen_non_null_bit(g, instruction->value->value.type, ir_llvm_value(g, instruction->value)); } -static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, +static LLVMValueRef ir_render_unwrap_optional(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapOptional *instruction) { ZigType *ptr_type = instruction->value->value.type; @@ -4260,7 +4260,7 @@ static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed) zig_unreachable(); } -static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchg *instruction) { +static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchgGen *instruction) { LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr); LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value); LLVMValueRef new_val = ir_llvm_value(g, instruction->new_value); @@ -4268,32 +4268,29 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn LLVMAtomicOrdering success_order = to_LLVMAtomicOrdering(instruction->success_order); LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order); - LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, + LLVMValueRef cmpxchg_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, success_order, failure_order, instruction->is_weak); - ZigType *maybe_type = instruction->base.value.type; - assert(maybe_type->id == ZigTypeIdOptional); - ZigType *child_type = maybe_type->data.maybe.child_type; - - if (type_is_codegen_pointer(child_type)) { - LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); - LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); - return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, ""); - } - assert(type_has_bits(instruction->type)); - LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, cmpxchg_val, 1, ""); + LLVMBasicBlockRef null_block = LLVMGetInsertBlock(g->builder); + LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn_val, "CmpXchgNonNull"); + LLVMBasicBlockRef phi_block = LLVMAppendBasicBlock(g->cur_fn_val, "CmpXchgPhi"); + LLVMBuildCondBr(g->builder, success_bit, phi_block, non_null_block); - LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_ptr, maybe_child_index, ""); - gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val); + LLVMPositionBuilderAtEnd(g->builder, non_null_block); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, cmpxchg_val, 0, ""); + gen_assign_raw(g, result_ptr, instruction->result_loc->value.type, payload_val); + LLVMBuildBr(g->builder, phi_block); - LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); - LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_ptr, maybe_null_index, ""); - gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false); - return result_ptr; + LLVMPositionBuilderAtEnd(g->builder, phi_block); + LLVMValueRef non_null_bit = LLVMBuildPhi(g->builder, LLVMInt1Type(), ""); + LLVMValueRef incoming_values[] = {LLVMConstAllOnes(LLVMInt1Type()), LLVMConstNull(LLVMInt1Type())}; + LLVMBasicBlockRef incoming_blocks[] = {non_null_block, null_block}; + LLVMAddIncoming(non_null_bit, incoming_values, incoming_blocks, 2); + return non_null_bit; } static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) { @@ -5107,12 +5104,16 @@ static LLVMValueRef ir_render_result_optional_payload(CodeGen *g, IrExecutable * IrInstructionResultOptionalPayload *instruction) { LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); - ZigType *child_type = instruction->base.value.type; + ZigType *ptr_type = instruction->base.value.type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *child_type = ptr_type->data.pointer.child_type; if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { return prev_result_loc; } else { - LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_null_index, ""); - gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); + if (instruction->make_non_null) { + LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_null_index, ""); + gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); + } return LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_child_index, ""); } } @@ -5188,6 +5189,17 @@ static LLVMValueRef ir_render_assert_non_null(CodeGen *g, IrExecutable *executab return nullptr; } +static LLVMValueRef ir_render_set_non_null_bit(CodeGen *g, IrExecutable *executable, + IrInstructionSetNonNullBit *instruction) +{ + LLVMValueRef ptr_to_optional = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef non_null_bit = ir_llvm_value(g, instruction->non_null_bit); + + LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, ptr_to_optional, maybe_null_index, ""); + gen_store_untyped(g, non_null_bit, nonnull_ptr, 0, false); + return nullptr; +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5271,6 +5283,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdPtrCastSrc: case IrInstructionIdInferCompTime: case IrInstructionIdResultPtrCast: + case IrInstructionIdCmpxchgSrc: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5308,7 +5321,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdTestNonNull: return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction); case IrInstructionIdUnwrapOptional: - return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapOptional *)instruction); + return ir_render_unwrap_optional(g, executable, (IrInstructionUnwrapOptional *)instruction); case IrInstructionIdClz: return ir_render_clz(g, executable, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -5323,8 +5336,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_ref(g, executable, (IrInstructionRef *)instruction); case IrInstructionIdErrName: return ir_render_err_name(g, executable, (IrInstructionErrName *)instruction); - case IrInstructionIdCmpxchg: - return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgGen: + return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchgGen *)instruction); case IrInstructionIdFence: return ir_render_fence(g, executable, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: @@ -5441,6 +5454,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_assert_non_error(g, executable, (IrInstructionAssertNonError *)instruction); case IrInstructionIdAssertNonNull: return ir_render_assert_non_null(g, executable, (IrInstructionAssertNonNull *)instruction); + case IrInstructionIdSetNonNullBit: + return ir_render_set_non_null_bit(g, executable, (IrInstructionSetNonNullBit *)instruction); case IrInstructionIdResultSliceToBytes: zig_panic("TODO"); case IrInstructionIdResultBytesToSlice: diff --git a/src/ir.cpp b/src/ir.cpp index 411d2ea526a5..942026960080 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -165,6 +165,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *uncasted_ptr, IrInstruction *uncasted_value); static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed); +static IrInstruction *resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -524,8 +525,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEmbedFile *) { return IrInstructionIdEmbedFile; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchg *) { - return IrInstructionIdCmpxchg; +static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgSrc *) { + return IrInstructionIdCmpxchgSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgGen *) { + return IrInstructionIdCmpxchgGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) { @@ -928,6 +933,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionInferCompTime *) return IrInstructionIdInferCompTime; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionSetNonNullBit *) { + return IrInstructionIdSetNonNullBit; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -1822,31 +1831,49 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_cmpxchg_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, - IrInstruction *success_order_value, IrInstruction *failure_order_value, bool is_weak, - ZigType *type, AtomicOrder success_order, AtomicOrder failure_order, IrInstruction *result_loc) + IrInstruction *success_order_value, IrInstruction *failure_order_value, IrInstruction *result_loc, bool is_weak) { - IrInstructionCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionCmpxchgSrc *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; instruction->ptr = ptr; instruction->cmp_value = cmp_value; instruction->new_value = new_value; instruction->success_order_value = success_order_value; instruction->failure_order_value = failure_order_value; + instruction->result_loc = result_loc; instruction->is_weak = is_weak; + + ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + ir_ref_instruction(cmp_value, irb->current_basic_block); + ir_ref_instruction(new_value, irb->current_basic_block); + ir_ref_instruction(success_order_value, irb->current_basic_block); + ir_ref_instruction(failure_order_value, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_cmpxchg_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, + ZigType *type, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, + AtomicOrder success_order, AtomicOrder failure_order, IrInstruction *result_loc, bool is_weak) +{ + IrInstructionCmpxchgGen *instruction = ir_build_instruction(irb, scope, source_node); instruction->type = type; + instruction->ptr = ptr; + instruction->cmp_value = cmp_value; + instruction->new_value = new_value; instruction->success_order = success_order; instruction->failure_order = failure_order; instruction->result_loc = result_loc; + instruction->is_weak = is_weak; - if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(cmp_value, irb->current_basic_block); ir_ref_instruction(new_value, irb->current_basic_block); - if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); - if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block); - ir_ref_instruction(result_loc, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -2762,12 +2789,15 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, } static IrInstruction *ir_build_result_optional_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *prev_result_loc) + IrInstruction *prev_result_loc, IrInstruction *payload_type, bool make_non_null) { IrInstructionResultOptionalPayload *instruction = ir_build_instruction(irb, scope, source_node); instruction->prev_result_loc = prev_result_loc; + instruction->payload_type = payload_type; + instruction->make_non_null = make_non_null; ir_ref_instruction(prev_result_loc, irb->current_basic_block); + if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block); return &instruction->base; } @@ -2956,6 +2986,21 @@ static IrInstruction *ir_build_infer_comptime(IrBuilder *irb, Scope *scope, AstN return &instruction->base; } +static IrInstruction *ir_build_set_nonnull_bit(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *non_null_bit, IrInstruction *new_result_loc) +{ + IrInstructionSetNonNullBit *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->non_null_bit = non_null_bit; + instruction->new_result_loc = new_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + ir_ref_instruction(non_null_bit, irb->current_basic_block); + if (new_result_loc != nullptr) ir_ref_instruction(new_result_loc, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3174,13 +3219,14 @@ static IrInstruction *ir_gen_multi(IrBuilder *irb, Scope *scope, AstNode *node, // an instruction which just makes a pointer of it. return ir_build_ref(irb, scope, value->source_node, value, false, false, nullptr); } - case LValErrorUnion: - case LValOptional: { + case LValErrorUnion: { IrInstruction *err_alloca = ir_build_alloca_src(irb, scope, value->source_node, nullptr, nullptr, "err"); ir_build_store_ptr(irb, scope, value->source_node, err_alloca, value); return err_alloca; } + case LValOptional: + return value; } zig_unreachable(); } @@ -3199,10 +3245,26 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, if (instr_is_unreachable(value)) return value; if (result_loc != nullptr) { - ir_build_store_ptr(irb, scope, node, result_loc, value); - if (lval != LValNone) { - assert(lval != LValPtr); - return ir_gen_result(irb, scope, node, lval, result_loc); + switch (lval) { + case LValNone: + ir_build_store_ptr(irb, scope, node, result_loc, value); + break; + case LValPtr: + zig_unreachable(); + case LValErrorUnion: + zig_panic("TODO"); + //ir_build_store_ptr(irb, scope, node, result_loc, value); + //return ir_gen_result(irb, scope, node, lval, result_loc); + case LValOptional: { + zig_panic("TODO"); + //// result_loc is a pointer to child type + //// value is an optional value + //// need to return non-null bit + //IrInstruction *opt_ptr = ir_build_ref(irb, scope, node, value, false, false, nullptr); + //IrInstruction *payload_ptr = ir_build_unwrap_maybe(irb, scope, node, opt_ptr, false); + //ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); + //return ir_build_test_nonnull(irb, scope, node, value); + } } } switch (lval) { @@ -4284,10 +4346,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg5_value == irb->codegen->invalid_instruction) return arg5_value; - IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, - arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), - nullptr, AtomicOrderUnordered, AtomicOrderUnordered, result_loc); - return ir_gen_multi(irb, scope, node, lval, result_loc, cmpxchg); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + IrInstruction *payload_ptr = (lval == LValOptional) ? ensured_result_loc : + ir_build_result_optional_payload(irb, scope, node, ensured_result_loc, arg0_value, false); + + IrInstruction *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value, + arg2_value, arg3_value, arg4_value, arg5_value, payload_ptr, + (builtin_fn->id == BuiltinFnIdCmpxchgWeak)); + + if (lval == LValOptional) + return cmpxchg; + + ir_build_set_nonnull_bit(irb, scope, node, ensured_result_loc, cmpxchg, payload_ptr); + return ir_gen_result(irb, scope, node, lval, ensured_result_loc); } case BuiltinFnIdFence: { @@ -5342,11 +5413,14 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional)); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, result_loc); - IrInstruction *inst = ir_gen_node(irb, expr_node, scope, LValPtr, ensured_result_loc); + IrInstruction *inst = ir_gen_node(irb, expr_node, scope, LValPtr, result_loc); if (inst == irb->codegen->invalid_instruction) return inst; - return ir_gen_result(irb, scope, expr_node, lval, ensured_result_loc); + if (result_loc == nullptr) { + return ir_gen_value(irb, scope, expr_node, lval, nullptr, inst); + } else { + return ir_gen_result(irb, scope, expr_node, lval, result_loc); + } } } zig_unreachable(); @@ -10131,8 +10205,13 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { } static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstruction *result_loc, - ZigType *needed_child_type) + ZigType *needed_child_type, bool make_non_null) { + ZigType *opt_type = get_optional_type(ira->codegen, needed_child_type); + result_loc = resolve_possible_alloca_inference(ira, result_loc, opt_type); + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + ZigType *old_ptr_type = result_loc->value.type; assert(old_ptr_type->id == ZigTypeIdPointer); ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, @@ -10157,26 +10236,42 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr } assert(optional_val->data.x_optional != nullptr); - IrInstruction *result = ir_const(ira, result_loc, new_ptr_type); + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_result_optional_payload(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc, nullptr, make_non_null); + result->value.type = new_ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, result_loc, new_ptr_type); + } ConstExprValue *result_val = &result->value; result_val->data.x_ptr.special = ConstPtrSpecialRef; result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - ptr_val->data.x_ptr.mut = ConstPtrMutComptimeConst; - } - return result; } } IrInstruction *result = ir_build_result_optional_payload(&ira->new_irb, result_loc->scope, - result_loc->source_node, result_loc); + result_loc->source_node, result_loc, nullptr, make_non_null); result->value.type = new_ptr_type; return result; } +static IrInstruction *ir_analyze_instruction_result_optional_payload(IrAnalyze *ira, + IrInstructionResultOptionalPayload *instruction) +{ + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->child); + if (type_is_invalid(payload_type)) + return ira->codegen->invalid_instruction; + return ir_analyze_result_optional_payload(ira, prev_result_loc, payload_type, instruction->make_non_null); +} + static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction *result_loc, ZigType *ptr_to_array_type, uint64_t len) { @@ -10359,10 +10454,6 @@ static IrInstruction *ir_analyze_result_error_union_code(IrAnalyze *ira, IrInstr result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; result_val->data.x_ptr.data.ref.pointee = error_union_val->data.x_err_union.error_set; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - ptr_val->data.x_ptr.mut = ConstPtrMutComptimeConst; - } - return result; } } @@ -11682,7 +11773,7 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *unr if (types_match_const_cast_only(ira, have_child_type->data.maybe.child_type, needed_child_type, source_node, false).id == ConstCastResultIdOk) { - return ir_analyze_result_optional_payload(ira, result_loc, needed_child_type); + return ir_analyze_result_optional_payload(ira, result_loc, needed_child_type, true); } } @@ -16665,7 +16756,7 @@ static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIns } } -static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, +static IrInstruction *ir_analyze_instruction_unwrap_optional(IrAnalyze *ira, IrInstructionUnwrapOptional *unwrap_maybe_instruction) { IrInstruction *value = unwrap_maybe_instruction->value->child; @@ -18936,7 +19027,7 @@ static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstru return result; } -static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { +static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchgSrc *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->child); if (type_is_invalid(operand_type)) return ira->codegen->invalid_instruction; @@ -18946,6 +19037,9 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi return ira->codegen->invalid_instruction; IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + result_loc = resolve_possible_alloca_inference(ira, result_loc, operand_type); if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -19012,10 +19106,15 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi zig_panic("TODO compile-time execution of cmpxchg"); } - IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, - nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, - operand_type, success_order, failure_order, result_loc); - result->value.type = get_optional_type(ira->codegen, operand_type); + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + result_loc->value.special = ConstValSpecialRuntime; + } + + IrInstruction *result = ir_build_cmpxchg_gen(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, + operand_type, casted_ptr, casted_cmp_value, casted_new_value, + success_order, failure_order, result_loc, instruction->is_weak); + result->value.type = ira->codegen->builtin_types.entry_bool; return result; } @@ -22150,6 +22249,51 @@ static IrInstruction *ir_analyze_instruction_infer_comptime(IrAnalyze *ira, return ir_const_void(ira, &instruction->base); } +static IrInstruction *ir_analyze_instruction_set_non_null_bit(IrAnalyze *ira, + IrInstructionSetNonNullBit *instruction) +{ + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *non_null_bit = instruction->non_null_bit->child; + if (type_is_invalid(non_null_bit->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = instruction->new_result_loc->child; + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + assert(prev_result_loc->value.type->id == ZigTypeIdPointer); + ZigType *opt_type = prev_result_loc->value.type->data.pointer.child_type; + assert(opt_type->id == ZigTypeIdOptional); + + if (!handle_is_ptr(opt_type)) { + if (prev_result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(new_result_loc) && + new_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + prev_result_loc->value.special = ConstValSpecialRuntime; + } + } + return ir_const_void(ira, &instruction->base); + } + + if (instr_is_comptime(prev_result_loc) && instr_is_comptime(new_result_loc) && + instr_is_comptime(non_null_bit)) + { + zig_panic("TODO comptime cmpxchg"); + } + + prev_result_loc->value.special = ConstValSpecialRuntime; + IrInstruction *result = ir_build_set_nonnull_bit(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, prev_result_loc, non_null_bit, nullptr); + result->value.type = ira->codegen->builtin_types.entry_void; + return result; +} + static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -22162,11 +22306,11 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdCast: case IrInstructionIdDeclVarGen: case IrInstructionIdAllocaGen: - case IrInstructionIdResultOptionalPayload: case IrInstructionIdResultSlicePtr: case IrInstructionIdResultErrorUnionPayload: case IrInstructionIdResultErrorUnionCode: case IrInstructionIdPtrCastGen: + case IrInstructionIdCmpxchgGen: zig_unreachable(); case IrInstructionIdReturn: @@ -22224,7 +22368,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdTestNonNull: return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction); case IrInstructionIdUnwrapOptional: - return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapOptional *)instruction); + return ir_analyze_instruction_unwrap_optional(ira, (IrInstructionUnwrapOptional *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -22265,8 +22409,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_c_undef(ira, (IrInstructionCUndef *)instruction); case IrInstructionIdEmbedFile: return ir_analyze_instruction_embed_file(ira, (IrInstructionEmbedFile *)instruction); - case IrInstructionIdCmpxchg: - return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgSrc: + return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchgSrc *)instruction); case IrInstructionIdFence: return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: @@ -22443,6 +22587,10 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_infer_array_type(ira, (IrInstructionInferArrayType *)instruction); case IrInstructionIdInferCompTime: return ir_analyze_instruction_infer_comptime(ira, (IrInstructionInferCompTime *)instruction); + case IrInstructionIdResultOptionalPayload: + return ir_analyze_instruction_result_optional_payload(ira, (IrInstructionResultOptionalPayload *)instruction); + case IrInstructionIdSetNonNullBit: + return ir_analyze_instruction_set_non_null_bit(ira, (IrInstructionSetNonNullBit *)instruction); case IrInstructionIdResultBytesToSlice: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: @@ -22550,7 +22698,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCInclude: case IrInstructionIdCDefine: case IrInstructionIdCUndef: - case IrInstructionIdCmpxchg: case IrInstructionIdFence: case IrInstructionIdMemset: case IrInstructionIdMemcpy: @@ -22583,7 +22730,10 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdContainerInitFields: case IrInstructionIdContainerInitList: case IrInstructionIdInferCompTime: + case IrInstructionIdSetNonNullBit: case IrInstructionIdSlice: + case IrInstructionIdCmpxchgGen: + case IrInstructionIdCmpxchgSrc: return true; case IrInstructionIdPhi: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b0a6b7bf82bf..0966fe13eb49 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -469,8 +469,7 @@ static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) { fprintf(irp->f, ")"); } -static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instruction) { - fprintf(irp->f, "*"); +static void ir_print_test_non_null(IrPrint *irp, IrInstructionTestNonNull *instruction) { ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, " != null"); } @@ -606,7 +605,7 @@ static void ir_print_embed_file(IrPrint *irp, IrInstructionEmbedFile *instructio fprintf(irp->f, ")"); } -static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) { +static void ir_print_cmpxchg_src(IrPrint *irp, IrInstructionCmpxchgSrc *instruction) { fprintf(irp->f, "@cmpxchg("); ir_print_other_instruction(irp, instruction->ptr); fprintf(irp->f, ", "); @@ -620,6 +619,17 @@ static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) { fprintf(irp->f, ")"); } +static void ir_print_cmpxchg_gen(IrPrint *irp, IrInstructionCmpxchgGen *instruction) { + fprintf(irp->f, "@cmpxchg(%s,", buf_ptr(&instruction->type->name)); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->cmp_value); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->new_value); + fprintf(irp->f, ") result="); + ir_print_other_instruction(irp, instruction->result_loc); +} + static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) { fprintf(irp->f, "@fence("); ir_print_other_instruction(irp, instruction->order_value); @@ -1307,7 +1317,9 @@ static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *instruc static void ir_print_result_optional_payload(IrPrint *irp, IrInstructionResultOptionalPayload *instruction) { fprintf(irp->f, "ResultOptionalPayload("); ir_print_other_instruction(irp, instruction->prev_result_loc); - fprintf(irp->f, ")"); + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->payload_type); + fprintf(irp->f, "make_non_null=%s)", instruction->make_non_null ? "yes" : "no"); } static void ir_print_result_slice_ptr(IrPrint *irp, IrInstructionResultSlicePtr *instruction) { @@ -1408,6 +1420,16 @@ static void ir_print_infer_comptime(IrPrint *irp, IrInstructionInferCompTime *in fprintf(irp->f, ")"); } +static void ir_print_set_non_null_bit(IrPrint *irp, IrInstructionSetNonNullBit *instruction) { + fprintf(irp->f, "SetNonNullBit(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",new_result="); + ir_print_other_instruction(irp, instruction->new_result_loc); + fprintf(irp->f, ",non_null_bit="); + ir_print_other_instruction(irp, instruction->non_null_bit); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1507,7 +1529,7 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_size_of(irp, (IrInstructionSizeOf *)instruction); break; case IrInstructionIdTestNonNull: - ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction); + ir_print_test_non_null(irp, (IrInstructionTestNonNull *)instruction); break; case IrInstructionIdUnwrapOptional: ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction); @@ -1563,8 +1585,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdEmbedFile: ir_print_embed_file(irp, (IrInstructionEmbedFile *)instruction); break; - case IrInstructionIdCmpxchg: - ir_print_cmpxchg(irp, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgSrc: + ir_print_cmpxchg_src(irp, (IrInstructionCmpxchgSrc *)instruction); + break; + case IrInstructionIdCmpxchgGen: + ir_print_cmpxchg_gen(irp, (IrInstructionCmpxchgGen *)instruction); break; case IrInstructionIdFence: ir_print_fence(irp, (IrInstructionFence *)instruction); @@ -1872,6 +1897,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdInferCompTime: ir_print_infer_comptime(irp, (IrInstructionInferCompTime *)instruction); break; + case IrInstructionIdSetNonNullBit: + ir_print_set_non_null_bit(irp, (IrInstructionSetNonNullBit *)instruction); + break; } fprintf(irp->f, "\n"); } From f4b27a34e7176c5be2daa45b4a5f9e576ad78bf5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 28 Nov 2018 16:48:24 -0500 Subject: [PATCH 069/190] copy elision: function call with error union return ```zig export fn entry() void { var x = ebar(); } ``` ```llvm define void @entry() #2 !dbg !42 { Entry: %error_return_trace_addresses = alloca [30 x i64], align 8 %error_return_trace = alloca %StackTrace, align 8 %x = alloca { i16, %Bar }, align 4 %0 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 0 store i64 0, i64* %0, align 8 %1 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 1 %2 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 0 %3 = getelementptr inbounds [30 x i64], [30 x i64]* %error_return_trace_addresses, i64 0, i64 0 store i64* %3, i64** %2, align 8 %4 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 1 store i64 30, i64* %4, align 8 %5 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 1, !dbg !57 %6 = call fastcc i16 @ebar(%Bar* %5, %StackTrace* %error_return_trace), !dbg !57 %7 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !57 store i16 %6, i16* %7, align 2, !dbg !57 call void @llvm.dbg.declare(metadata { i16, %Bar }* %x, metadata !46, metadata !DIExpression()), !dbg !58 ret void, !dbg !59 } ``` --- src/all_types.hpp | 15 +- src/codegen.cpp | 2 + src/ir.cpp | 351 +++++++++++++++++++++++++++++++--------------- 3 files changed, 245 insertions(+), 123 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index ca43b9424e92..8f1334e4f5e8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2047,6 +2047,13 @@ struct IrBasicBlock { IrInstruction *must_be_comptime_source_instr; }; +enum LVal { + LValNone, + LValPtr, + LValErrorUnion, + LValOptional, +}; + enum IrInstructionId { IrInstructionIdInvalid, IrInstructionIdDeclVarSrc, @@ -2432,6 +2439,7 @@ struct IrInstructionCall { IrInstruction *result_loc; IrInstruction *first_arg_result_loc; FnInline fn_inline; + LVal lval; bool is_async; bool is_comptime; }; @@ -3018,13 +3026,6 @@ struct IrInstructionTypeName { IrInstruction *type_value; }; -enum LVal { - LValNone, - LValPtr, - LValErrorUnion, - LValOptional, -}; - struct IrInstructionDeclRef { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 90171985d318..ee17ba72f7c1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2270,6 +2270,8 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns if (val->special == ConstValSpecialStatic) { LLVMValueRef const_error_val = gen_const_val(g, val->data.x_err_union.error_set, ""); LLVMBuildRet(g->builder, const_error_val); + } else if (!type_has_bits(fn_type_id->return_type->data.error_union.payload_type)) { + LLVMBuildRet(g->builder, value); } else { zig_panic("TODO"); } diff --git a/src/ir.cpp b/src/ir.cpp index 942026960080..e0fdf9a629b0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -166,6 +166,12 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source IrInstruction *uncasted_ptr, IrInstruction *uncasted_value); static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed); static IrInstruction *resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type); +static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, bool safety_check_on); +static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, bool safety_check_on); +static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, + IrInstruction *source_instr, IrInstruction *base_ptr); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -1238,7 +1244,7 @@ static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, Ast static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, - IrInstruction *new_stack, IrInstruction *result_loc, IrInstruction *first_arg_result_loc) + IrInstruction *new_stack, IrInstruction *result_loc, IrInstruction *first_arg_result_loc, LVal lval) { IrInstructionCall *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; @@ -1252,6 +1258,7 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->new_stack = new_stack; call_instruction->result_loc = result_loc; call_instruction->first_arg_result_loc = first_arg_result_loc; + call_instruction->lval = lval; if (fn_ref) ir_ref_instruction(fn_ref, irb->current_basic_block); for (size_t i = 0; i < arg_count; i += 1) @@ -4947,7 +4954,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - fn_inline, false, nullptr, nullptr, result_loc, nullptr); + fn_inline, false, nullptr, nullptr, result_loc, nullptr, lval); return ir_gen_multi(irb, scope, node, lval, result_loc, call); } case BuiltinFnIdNewStackCall: @@ -4978,7 +4985,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, - false, FnInlineAuto, false, nullptr, new_stack, result_loc, nullptr); + false, FnInlineAuto, false, nullptr, new_stack, result_loc, nullptr, lval); return ir_gen_multi(irb, scope, node, lval, result_loc, call); } case BuiltinFnIdTypeId: @@ -5220,7 +5227,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node // In the analysis, this call instruction will be a simple LoadPtr instruction if // it turns out to be an implicit cast. IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - FnInlineAuto, false, nullptr, nullptr, result_loc, arg_result_loc); + FnInlineAuto, false, nullptr, nullptr, result_loc, arg_result_loc, lval); return ir_gen_multi(irb, scope, node, lval, result_loc, fn_call); } @@ -5241,7 +5248,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, - is_async, async_allocator, nullptr, result_loc, nullptr); + is_async, async_allocator, nullptr, result_loc, nullptr, lval); return ir_gen_multi(irb, scope, node, lval, result_loc, fn_call); } @@ -7797,7 +7804,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec args[0] = implicit_allocator_ptr; // self args[1] = mem_slice; // old_mem ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, - nullptr, nullptr, nullptr, nullptr); + nullptr, nullptr, nullptr, nullptr, LValNone); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false, nullptr); @@ -13762,7 +13769,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, - async_allocator_inst, nullptr, nullptr, nullptr); + async_allocator_inst, nullptr, nullptr, nullptr, LValNone); result->value.type = async_return_type; return result; } @@ -14041,23 +14048,61 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return result; } +// Note this is semantically different from what gets run with ir_build_set_nonnull_bit +static IrInstruction *ir_analyze_set_non_null_bit(IrAnalyze *ira, + IrInstruction *source_instr, IrInstruction *base_ptr, IrInstruction *non_null_bit) +{ + ZigType *ptr_type = base_ptr->value.type; + assert(ptr_type->id == ZigTypeIdPointer); + + ZigType *opt_type = ptr_type->data.pointer.child_type; + if (type_is_invalid(opt_type)) + return ira->codegen->invalid_instruction; + + if (opt_type->id != ZigTypeIdErrorUnion) { + ir_add_error(ira, base_ptr, + buf_sprintf("expected optional type, found '%s'", buf_ptr(&opt_type->name))); + return ira->codegen->invalid_instruction; + } + + if (instr_is_comptime(base_ptr)) { + zig_panic("TODO comptime set non null bit"); + } + + return ir_build_set_nonnull_bit(&ira->new_irb, + source_instr->scope, source_instr->source_node, base_ptr, non_null_bit, nullptr); +} + static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, IrInstructionCall *call_instruction, ZigFn *fn_entry, IrInstruction *fn_ref, size_t call_param_count, IrInstruction **casted_args, FnInline fn_inline, IrInstruction *casted_new_stack) { - IrInstruction *casted_result_loc = nullptr; - + // For an optional or error union, this result location is a pointer to the payload field. + // For other types this is a base pointer to the value. + IrInstruction *payload_result_loc = nullptr; + // For an optional or error union, this is a base pointer. + // For other types this is the same as payload_result_loc. + IrInstruction *base_result_loc = nullptr; + + bool convert_to_value; ZigType *scalar_result_type; ZigType *payload_result_type; - if (return_type->id == ZigTypeIdErrorUnion) { + if (return_type->id == ZigTypeIdErrorUnion && handle_is_ptr(return_type)) { scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); payload_result_type = return_type->data.error_union.payload_type; + convert_to_value = call_instruction->lval != LValErrorUnion; + } else if (return_type->id == ZigTypeIdOptional && handle_is_ptr(return_type)) { + scalar_result_type = ira->codegen->builtin_types.entry_bool; + payload_result_type = return_type->data.maybe.child_type; + convert_to_value = call_instruction->lval != LValOptional; } else { payload_result_type = return_type; scalar_result_type = return_type; + convert_to_value = false; } - bool need_store_ptr = (return_type == payload_result_type) && !handle_is_ptr(return_type) && + bool need_store_ptr = !convert_to_value && + (return_type == payload_result_type) && !handle_is_ptr(return_type) && call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr; if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { @@ -14073,24 +14118,80 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, } } - casted_result_loc = ir_implicit_cast_result(ira, prev_result_loc, payload_result_type, need_store_ptr); - if (casted_result_loc == nullptr) - casted_result_loc = prev_result_loc; - if (type_is_invalid(casted_result_loc->value.type)) + if (convert_to_value) { + base_result_loc = ir_implicit_cast_result(ira, prev_result_loc, return_type, false); + if (type_is_invalid(base_result_loc->value.type)) + return ira->codegen->invalid_instruction; + if (return_type->id == ZigTypeIdErrorUnion) { + payload_result_loc = ir_analyze_unwrap_err_payload(ira, &call_instruction->base, + base_result_loc, false); + } else if (return_type->id == ZigTypeIdOptional) { + payload_result_loc = ir_analyze_unwrap_optional_payload(ira, &call_instruction->base, + base_result_loc, false); + } else { + zig_unreachable(); + } + } else { + payload_result_loc = ir_implicit_cast_result(ira, prev_result_loc, payload_result_type, need_store_ptr); + if (payload_result_loc == nullptr) + payload_result_loc = prev_result_loc; + base_result_loc = payload_result_loc; + } + if (type_is_invalid(payload_result_loc->value.type)) return ira->codegen->invalid_instruction; } + // This instruction, in the case of optional and error union, is always the error code / bool value. + // So if the LVal does not match this, we have to emit more instructions to adhere to the expected + // types. IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, - casted_new_stack, casted_result_loc, nullptr); + casted_new_stack, payload_result_loc, nullptr, LValNone); new_call_instruction->value.type = scalar_result_type; + if (scalar_result_type->id == ZigTypeIdUnreachable) + return ir_finish_anal(ira, new_call_instruction); if (need_store_ptr) { - ir_analyze_store_ptr(ira, &call_instruction->base, casted_result_loc, new_call_instruction); + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); + return new_call_instruction; } + if (!convert_to_value) + return new_call_instruction; - return ir_finish_anal(ira, new_call_instruction); + if (return_type->id == ZigTypeIdErrorUnion) { + IrInstruction *err_code_ptr = ir_analyze_error_union_field_error_set(ira, + &call_instruction->base, base_result_loc); + ir_analyze_store_ptr(ira, &call_instruction->base, err_code_ptr, new_call_instruction); + switch (call_instruction->lval) { + case LValNone: + return ir_get_deref(ira, &call_instruction->base, base_result_loc); + case LValPtr: + return base_result_loc; + case LValErrorUnion: + zig_unreachable(); + case LValOptional: + // This means incorrectly used optional syntax for error union. + zig_panic("TODO"); + } + zig_unreachable(); + } else if (return_type->id == ZigTypeIdOptional) { + ir_analyze_set_non_null_bit(ira, &call_instruction->base, base_result_loc, new_call_instruction); + switch (call_instruction->lval) { + case LValNone: + return ir_get_deref(ira, &call_instruction->base, base_result_loc); + case LValPtr: + return base_result_loc; + case LValOptional: + zig_unreachable(); + case LValErrorUnion: + // This means incorrectly used error union for optional. + zig_panic("TODO"); + } + zig_unreachable(); + } else { + zig_unreachable(); + } } static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, @@ -16756,30 +16857,28 @@ static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIns } } -static IrInstruction *ir_analyze_instruction_unwrap_optional(IrAnalyze *ira, - IrInstructionUnwrapOptional *unwrap_maybe_instruction) +static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, bool safety_check_on) { - IrInstruction *value = unwrap_maybe_instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - - ZigType *ptr_type = value->value.type; + ZigType *ptr_type = base_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id != ZigTypeIdOptional) { - ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, + + if (type_entry->id != ZigTypeIdOptional) { + ir_add_error_node(ira, base_ptr->source_node, buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + ZigType *child_type = type_entry->data.maybe.child_type; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + if (instr_is_comptime(base_ptr)) { + ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad); if (!val) return ira->codegen->invalid_instruction; ConstExprValue *maybe_val = const_ptr_pointee(ira->codegen, val); @@ -16788,10 +16887,10 @@ static IrInstruction *ir_analyze_instruction_unwrap_optional(IrAnalyze *ira, if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { if (optional_value_is_null(maybe_val)) { - ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); + ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_const(ira, &unwrap_maybe_instruction->base, result_type); + IrInstruction *result = ir_const(ira, source_instr, result_type); ConstExprValue *out_val = &result->value; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = val->data.x_ptr.mut; @@ -16804,13 +16903,22 @@ static IrInstruction *ir_analyze_instruction_unwrap_optional(IrAnalyze *ira, } } - IrInstruction *result = ir_build_unwrap_maybe(&ira->new_irb, - unwrap_maybe_instruction->base.scope, unwrap_maybe_instruction->base.source_node, - value, unwrap_maybe_instruction->safety_check_on); + IrInstruction *result = ir_build_unwrap_maybe(&ira->new_irb, source_instr->scope, source_instr->source_node, + base_ptr, safety_check_on); result->value.type = result_type; return result; } +static IrInstruction *ir_analyze_instruction_unwrap_optional(IrAnalyze *ira, + IrInstructionUnwrapOptional *instruction) +{ + IrInstruction *base_ptr = instruction->value->child; + if (type_is_invalid(base_ptr->value.type)) + return ira->codegen->invalid_instruction; + + return ir_analyze_unwrap_optional_payload(ira, &instruction->base, base_ptr, instruction->safety_check_on); +} + static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) { IrInstruction *value = ctz_instruction->value->child; if (type_is_invalid(value->value.type)) { @@ -20358,14 +20466,10 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct } } -static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnalyze *ira, - IrInstructionErrorUnionFieldErrorSet *instruction) +static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, + IrInstruction *source_instr, IrInstruction *base_ptr) { - IrInstruction *ptr = instruction->ptr->child; - if (type_is_invalid(ptr->value.type)) - return ira->codegen->invalid_instruction; - ZigType *ptr_type = ptr->value.type; - + ZigType *ptr_type = base_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; @@ -20373,17 +20477,18 @@ static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnaly return ira->codegen->invalid_instruction; if (type_entry->id != ZigTypeIdErrorUnion) { - ir_add_error(ira, ptr, + ir_add_error(ira, base_ptr, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } ZigType *opt_err_set = get_optional_type(ira->codegen, type_entry->data.error_union.err_set_type); ZigType *result_type = get_pointer_to_type_extra(ira->codegen, opt_err_set, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, + ptr_type->data.pointer.explicit_alignment, 0, 0); - if (instr_is_comptime(ptr)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, ptr, UndefBad); + if (instr_is_comptime(base_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { @@ -20408,12 +20513,12 @@ static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnaly IrInstruction *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - result = ir_build_error_union_field_error_set(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, ptr); + result = ir_build_error_union_field_error_set(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr); result->value.type = result_type; result->value.special = ConstValSpecialStatic; } else { - result = ir_const(ira, &instruction->base, result_type); + result = ir_const(ira, source_instr, result_type); } ConstExprValue *const_val = &result->value; const_val->data.x_ptr.special = ConstPtrSpecialBaseErrorUnionCode; @@ -20424,11 +20529,21 @@ static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnaly } IrInstruction *result = ir_build_error_union_field_error_set(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, ptr); + source_instr->scope, source_instr->source_node, base_ptr); result->value.type = result_type; return result; } +static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnalyze *ira, + IrInstructionErrorUnionFieldErrorSet *instruction) +{ + IrInstruction *base_ptr = instruction->ptr->child; + if (type_is_invalid(base_ptr->value.type)) + return ira->codegen->invalid_instruction; + + return ir_analyze_error_union_field_error_set(ira, &instruction->base, base_ptr); +} + static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstructionUnwrapErrCode *instruction) { @@ -20473,91 +20588,95 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, } } -static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, - IrInstructionUnwrapErrPayload *instruction) +static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, bool safety_check_on) { - assert(instruction->value->child); - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; - + ZigType *ptr_type = base_ptr->value.type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - ZigType *payload_type = type_entry->data.error_union.payload_type; - if (type_is_invalid(payload_type)) { + if (type_entry->id != ZigTypeIdErrorUnion) { + ir_add_error(ira, base_ptr, + buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->invalid_instruction; + } + + ZigType *payload_type = type_entry->data.error_union.payload_type; + if (type_is_invalid(payload_type)) + return ira->codegen->invalid_instruction; + + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + PtrLenSingle, 0, 0, 0); + if (instr_is_comptime(base_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + if (!ptr_val) return ira->codegen->invalid_instruction; - } - ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - PtrLenSingle, 0, 0, 0); - if (instr_is_comptime(value)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); - if (!ptr_val) + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ConstExprValue *err_union_val = const_ptr_pointee(ira->codegen, ptr_val); + if (err_union_val == nullptr) return ira->codegen->invalid_instruction; - if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ConstExprValue *err_union_val = const_ptr_pointee(ira->codegen, ptr_val); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer && - err_union_val->special == ConstValSpecialUndef) - { - ConstExprValue *vals = create_const_vals(2); - ConstExprValue *err_set_val = &vals[0]; - ConstExprValue *payload_val = &vals[1]; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer && + err_union_val->special == ConstValSpecialUndef) + { + ConstExprValue *vals = create_const_vals(2); + ConstExprValue *err_set_val = &vals[0]; + ConstExprValue *payload_val = &vals[1]; - ZigType *opt_err_set = get_optional_type(ira->codegen, type_entry->data.error_union.err_set_type); - err_set_val->special = ConstValSpecialStatic; - err_set_val->type = opt_err_set; - err_set_val->data.x_err_set = nullptr; + ZigType *opt_err_set = get_optional_type(ira->codegen, type_entry->data.error_union.err_set_type); + err_set_val->special = ConstValSpecialStatic; + err_set_val->type = opt_err_set; + err_set_val->data.x_err_set = nullptr; - payload_val->special = ConstValSpecialUndef; - payload_val->type = payload_type; + payload_val->special = ConstValSpecialUndef; + payload_val->type = payload_type; - err_union_val->special = ConstValSpecialStatic; - err_union_val->data.x_err_union.error_set = err_set_val; - err_union_val->data.x_err_union.payload = payload_val; + err_union_val->special = ConstValSpecialStatic; + err_union_val->data.x_err_union.error_set = err_set_val; + err_union_val->data.x_err_union.payload = payload_val; + } + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { + ir_add_error(ira, source_instr, + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + return ira->codegen->invalid_instruction; } - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; - if (err != nullptr) { - ir_add_error(ira, &instruction->base, - buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); - return ira->codegen->invalid_instruction; - } - IrInstruction *result; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - result = ir_build_unwrap_err_payload(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, value, instruction->safety_check_on); - result->value.type = result_type; - result->value.special = ConstValSpecialStatic; - } else { - result = ir_const(ira, &instruction->base, result_type); - } - result->value.data.x_ptr.special = ConstPtrSpecialRef; - result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; - result->value.data.x_ptr.mut = ptr_val->data.x_ptr.mut; - return result; + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr, safety_check_on); + result->value.type = result_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, result_type); } + result->value.data.x_ptr.special = ConstPtrSpecialRef; + result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; + result->value.data.x_ptr.mut = ptr_val->data.x_ptr.mut; + return result; } } + } - IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on); - result->value.type = result_type; - return result; - } else { - ir_add_error(ira, value, - buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); + IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr, safety_check_on); + result->value.type = result_type; + return result; +} + +static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, + IrInstructionUnwrapErrPayload *instruction) +{ + IrInstruction *base_ptr = instruction->value->child; + if (type_is_invalid(base_ptr->value.type)) return ira->codegen->invalid_instruction; - } + return ir_analyze_unwrap_err_payload(ira, &instruction->base, base_ptr, instruction->safety_check_on); } static IrInstruction *ir_analyze_instruction_assert_non_error(IrAnalyze *ira, From eebfb2eaf3758138f6a55195fa4b985e1c0e3c24 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 28 Nov 2018 18:15:33 -0500 Subject: [PATCH 070/190] copy elision: handle direct assignment of anyerror!void ```zig export fn entry() void { var x = evoid(); } ``` ```llvm define void @entry() #2 !dbg !42 { Entry: %error_return_trace_addresses = alloca [30 x i64], align 8 %error_return_trace = alloca %StackTrace, align 8 %x = alloca i16, align 2 %0 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 0 store i64 0, i64* %0, align 8 %1 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 1 %2 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 0 %3 = getelementptr inbounds [30 x i64], [30 x i64]* %error_return_trace_addresses, i64 0, i64 0 store i64* %3, i64** %2, align 8 %4 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 1 store i64 30, i64* %4, align 8 %5 = call fastcc i16 @evoid(%StackTrace* %error_return_trace), !dbg !48 store i16 %5, i16* %x, align 2, !dbg !48 call void @llvm.dbg.declare(metadata i16* %x, metadata !46, metadata !DIExpression()), !dbg !49 ret void, !dbg !50 } ``` --- src/codegen.cpp | 10 ++++++++-- src/ir.cpp | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index ee17ba72f7c1..74a2e15e79ff 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4657,7 +4657,13 @@ static LLVMValueRef ir_render_error_union_field_error_set(CodeGen *g, IrExecutab } } -static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) { +static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, + IrInstructionUnwrapErrPayload *instruction) +{ + bool want_safety = ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && + g->errors_by_index.length > 1; + if (!want_safety && !type_has_bits(instruction->base.value.type)) + return nullptr; ZigType *ptr_type = instruction->value->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *err_union_type = ptr_type->data.pointer.child_type; @@ -4669,7 +4675,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu return err_union_handle; } - if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) { + if (want_safety) { LLVMValueRef err_val; if (type_has_bits(payload_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); diff --git a/src/ir.cpp b/src/ir.cpp index e0fdf9a629b0..31029b1c444a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14087,11 +14087,11 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, bool convert_to_value; ZigType *scalar_result_type; ZigType *payload_result_type; - if (return_type->id == ZigTypeIdErrorUnion && handle_is_ptr(return_type)) { + if (return_type->id == ZigTypeIdErrorUnion) { scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); payload_result_type = return_type->data.error_union.payload_type; convert_to_value = call_instruction->lval != LValErrorUnion; - } else if (return_type->id == ZigTypeIdOptional && handle_is_ptr(return_type)) { + } else if (return_type->id == ZigTypeIdOptional) { scalar_result_type = ira->codegen->builtin_types.entry_bool; payload_result_type = return_type->data.maybe.child_type; convert_to_value = call_instruction->lval != LValOptional; @@ -20611,6 +20611,7 @@ static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstructio ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + if (instr_is_comptime(base_ptr)) { ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); if (!ptr_val) From edd48a58db17f33733d61986e3a3e758fd0b50e7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 29 Nov 2018 12:36:20 -0500 Subject: [PATCH 071/190] copy elision: introduce LValErrorUnionVal, LValErrorUnionPtr ```zig fn tryfail() anyerror!void { try evoid(); } ``` ```llvm define internal fastcc i16 @tryfail(%StackTrace* nonnull) unnamed_addr #2 !dbg !51 { Entry: %1 = call fastcc i16 @evoid(%StackTrace* %0), !dbg !55 %2 = icmp ne i16 %1, 0, !dbg !57 br i1 %2, label %ErrRetReturn, label %ErrRetContinue, !dbg !57 ErrRetReturn: ; preds = %Entry call fastcc void @__zig_return_error(%StackTrace* %0), !dbg !57 ret i16 %1, !dbg !57 ErrRetContinue: ; preds = %Entry ret i16 0, !dbg !58 } ``` --- src/all_types.hpp | 28 ++++++-- src/codegen.cpp | 39 +++++++++-- src/ir.cpp | 171 ++++++++++++++++++++++++++++++++++------------ src/ir_print.cpp | 22 ++++-- 4 files changed, 200 insertions(+), 60 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 8f1334e4f5e8..695266901780 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2048,9 +2048,18 @@ struct IrBasicBlock { }; enum LVal { + // The instruction must return the actual value. LValNone, + // The instruction must return a pointer to the actual value. LValPtr, - LValErrorUnion, + // The instruction must populate the result location, which is + // an error union, and return the optional error code value. + LValErrorUnionVal, + // The instruction must populate the result location, which is + // an error union, and return a pointer to optional error code value. + LValErrorUnionPtr, + // The instruction must populate the result location, which is + // an optional, and return non-null bool value. LValOptional, }; @@ -2092,7 +2101,8 @@ enum IrInstructionId { IrInstructionIdAsm, IrInstructionIdSizeOf, IrInstructionIdTestNonNull, - IrInstructionIdUnwrapOptional, + IrInstructionIdOptionalUnwrapPtr, + IrInstructionIdOptionalUnwrapVal, IrInstructionIdOptionalWrap, IrInstructionIdUnionTag, IrInstructionIdClz, @@ -2592,10 +2602,20 @@ struct IrInstructionTestNonNull { IrInstruction *value; }; -struct IrInstructionUnwrapOptional { +// Takes a pointer to an optional value, returns a pointer +// to the payload. +struct IrInstructionOptionalUnwrapPtr { IrInstruction base; - IrInstruction *value; + IrInstruction *base_ptr; + bool safety_check_on; +}; + +// Takes an optional value, returns the payload. +struct IrInstructionOptionalUnwrapVal { + IrInstruction base; + + IrInstruction *opt; bool safety_check_on; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 74a2e15e79ff..889d4d632661 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3829,15 +3829,15 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable return gen_non_null_bit(g, instruction->value->value.type, ir_llvm_value(g, instruction->value)); } -static LLVMValueRef ir_render_unwrap_optional(CodeGen *g, IrExecutable *executable, - IrInstructionUnwrapOptional *instruction) +static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *executable, + IrInstructionOptionalUnwrapPtr *instruction) { - ZigType *ptr_type = instruction->value->value.type; + ZigType *ptr_type = instruction->base_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *maybe_type = ptr_type->data.pointer.child_type; assert(maybe_type->id == ZigTypeIdOptional); ZigType *child_type = maybe_type->data.maybe.child_type; - LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value); + LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->base_ptr); if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); @@ -3863,6 +3863,31 @@ static LLVMValueRef ir_render_unwrap_optional(CodeGen *g, IrExecutable *executab } } +static LLVMValueRef ir_render_optional_unwrap_val(CodeGen *g, IrExecutable *executable, + IrInstructionOptionalUnwrapVal *instruction) +{ + ZigType *opt_type = instruction->opt->value.type; + assert(opt_type->id == ZigTypeIdOptional); + LLVMValueRef opt_handle = ir_llvm_value(g, instruction->opt); + if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { + LLVMValueRef non_null_bit = gen_non_null_bit(g, opt_type, opt_handle); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); + LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + ZigType *payload_type = opt_type->data.maybe.child_type; + if (!type_has_bits(payload_type)) + return nullptr; + if (!handle_is_ptr(opt_type)) + return opt_handle; + return LLVMBuildStructGEP(g->builder, opt_handle, maybe_child_index, ""); +} + static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *int_type, BuiltinFnId fn_id) { ZigLLVMFnKey key = {}; const char *fn_name; @@ -5328,8 +5353,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_asm(g, executable, (IrInstructionAsm *)instruction); case IrInstructionIdTestNonNull: return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapOptional: - return ir_render_unwrap_optional(g, executable, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + return ir_render_optional_unwrap_ptr(g, executable, (IrInstructionOptionalUnwrapPtr *)instruction); + case IrInstructionIdOptionalUnwrapVal: + return ir_render_optional_unwrap_val(g, executable, (IrInstructionOptionalUnwrapVal *)instruction); case IrInstructionIdClz: return ir_render_clz(g, executable, (IrInstructionClz *)instruction); case IrInstructionIdCtz: diff --git a/src/ir.cpp b/src/ir.cpp index 31029b1c444a..87d2c6abe0f2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -471,8 +471,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) { return IrInstructionIdTestNonNull; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapOptional *) { - return IrInstructionIdUnwrapOptional; +static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalUnwrapPtr *) { + return IrInstructionIdOptionalUnwrapPtr; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalUnwrapVal *) { + return IrInstructionIdOptionalUnwrapVal; } static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { @@ -1610,14 +1614,26 @@ static IrInstruction *ir_build_test_nonnull(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } -static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, - bool safety_check_on) +static IrInstruction *ir_build_optional_unwrap_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *base_ptr, bool safety_check_on) { - IrInstructionUnwrapOptional *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + IrInstructionOptionalUnwrapPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base_ptr = base_ptr; instruction->safety_check_on = safety_check_on; - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(base_ptr, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_optional_unwrap_val(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *opt, bool safety_check_on) +{ + IrInstructionOptionalUnwrapVal *instruction = ir_build_instruction(irb, scope, source_node); + instruction->opt = opt; + instruction->safety_check_on = safety_check_on; + + ir_ref_instruction(opt, irb->current_basic_block); return &instruction->base; } @@ -3206,7 +3222,8 @@ static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, return ir_build_load_ptr(irb, scope, node, result_loc, nullptr); case LValPtr: return result_loc; - case LValErrorUnion: + case LValErrorUnionVal: + case LValErrorUnionPtr: case LValOptional: zig_unreachable(); } @@ -3226,12 +3243,14 @@ static IrInstruction *ir_gen_multi(IrBuilder *irb, Scope *scope, AstNode *node, // an instruction which just makes a pointer of it. return ir_build_ref(irb, scope, value->source_node, value, false, false, nullptr); } - case LValErrorUnion: { + case LValErrorUnionPtr: { IrInstruction *err_alloca = ir_build_alloca_src(irb, scope, value->source_node, nullptr, nullptr, "err"); ir_build_store_ptr(irb, scope, value->source_node, err_alloca, value); return err_alloca; } + case LValErrorUnionVal: + return value; case LValOptional: return value; } @@ -3258,7 +3277,8 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, break; case LValPtr: zig_unreachable(); - case LValErrorUnion: + case LValErrorUnionVal: + case LValErrorUnionPtr: zig_panic("TODO"); //ir_build_store_ptr(irb, scope, node, result_loc, value); //return ir_gen_result(irb, scope, node, lval, result_loc); @@ -3268,7 +3288,7 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, //// value is an optional value //// need to return non-null bit //IrInstruction *opt_ptr = ir_build_ref(irb, scope, node, value, false, false, nullptr); - //IrInstruction *payload_ptr = ir_build_unwrap_maybe(irb, scope, node, opt_ptr, false); + //IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, opt_ptr, false); //ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); //return ir_build_test_nonnull(irb, scope, node, value); } @@ -3282,7 +3302,8 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, case LValNone: return value; case LValOptional: - case LValErrorUnion: + case LValErrorUnionVal: + case LValErrorUnionPtr: zig_panic("TODO"); } zig_unreachable(); @@ -3299,7 +3320,7 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV return ptr; case LValNone: return ir_build_load_ptr(irb, scope, node, ptr, result_loc); - case LValErrorUnion: { + case LValErrorUnionPtr: { // ptr points to an error union; // result_loc points to the result payload // must return pointer to the error code @@ -3307,11 +3328,20 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); return ir_build_error_union_field_error_set(irb, scope, node, ptr); } + case LValErrorUnionVal: { + // ptr points to an error union; + // result_loc points to the result payload + // must return the error code + IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, ptr, false); + ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); + IrInstruction *err_ptr = ir_build_error_union_field_error_set(irb, scope, node, ptr); + return ir_build_load_ptr(irb, scope, node, err_ptr, nullptr); + } case LValOptional: { // ptr points to an optional; // result_loc points to the result payload // must return is_non_null bool - IrInstruction *payload_ptr = ir_build_unwrap_maybe(irb, scope, node, ptr, false); + IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, ptr, false); ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); IrInstruction *loaded_ptr = ir_build_load_ptr(irb, scope, node, ptr, nullptr); return ir_build_test_nonnull(irb, scope, node, loaded_ptr); @@ -3414,11 +3444,11 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, { assert(expr_node); IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, result_loc); - IrInstruction *ptr_opt_err_code = ir_gen_node(irb, expr_node, scope, LValErrorUnion, ensured_result_loc); - if (ptr_opt_err_code == irb->codegen->invalid_instruction) + IrInstruction *opt_err_code = ir_gen_node(irb, expr_node, scope, + LValErrorUnionVal, ensured_result_loc); + if (opt_err_code == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *opt_err_code = ir_build_load_ptr(irb, scope, node, ptr_opt_err_code, nullptr); IrInstruction *is_err_val = ir_build_test_nonnull(irb, scope, node, opt_err_code); IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); @@ -3433,8 +3463,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime, nullptr)); ir_set_cursor_at_end_and_append_block(irb, return_block); - IrInstruction *ptr_err_code = ir_build_unwrap_maybe(irb, scope, node, ptr_opt_err_code, false); - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, ptr_err_code, nullptr); + IrInstruction *err_val = ir_build_optional_unwrap_val(irb, scope, node, opt_err_code, false); if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) { if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr(irb, scope, node); @@ -5373,12 +5402,11 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval, IrInstruction *result_loc) { - IrInstruction *err_code_ptr = ir_gen_node(irb, expr_node, scope, LValErrorUnion, result_loc); - if (err_code_ptr == irb->codegen->invalid_instruction) + IrInstruction *opt_err_code = ir_gen_node(irb, expr_node, scope, LValErrorUnionVal, result_loc); + if (opt_err_code == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *err_code = ir_build_load_ptr(irb, scope, source_node, err_code_ptr, nullptr); - ir_build_assert_non_error(irb, scope, source_node, err_code); + ir_build_assert_non_error(irb, scope, source_node, opt_err_code); return ir_gen_result(irb, scope, source_node, lval, result_loc); } @@ -5656,7 +5684,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol, true, true, false, is_comptime); Scope *err_scope = err_var->child_scope; - IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, err_scope, err_symbol_node, + IrInstruction *unwrapped_err_code_ptr = ir_build_optional_unwrap_ptr(irb, err_scope, err_symbol_node, ptr_opt_err_code, false); ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, unwrapped_err_code_ptr); @@ -5689,7 +5717,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstruction *payload_ptr = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); + IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false); IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ? ir_build_ref(irb, child_scope, symbol_node, payload_ptr, true, false, nullptr) : payload_ptr; ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_ptr); @@ -6109,7 +6137,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *payload_ptr = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); + IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false); IrInstruction *var_ptr = var_is_ptr ? ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false, nullptr) : payload_ptr; ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_ptr); @@ -6209,7 +6237,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * ZigVar *var = ir_create_var(irb, node, subexpr_scope, err_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, subexpr_scope, node, + IrInstruction *unwrapped_err_code_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, ptr_opt_err_code, false); ir_build_var_decl_src(irb, subexpr_scope, node, var, nullptr, nullptr, unwrapped_err_code_ptr); err_var_scope = var->child_scope; @@ -6697,11 +6725,18 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc); } - IrInstruction *ptr_opt_err_code = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnion, result_loc); - if (ptr_opt_err_code == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; - - IrInstruction *opt_err_code = ir_build_load_ptr(irb, parent_scope, node, ptr_opt_err_code, nullptr); + IrInstruction *ptr_opt_err_code; + IrInstruction *opt_err_code; + if (var_node) { + ptr_opt_err_code = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnionPtr, result_loc); + if (ptr_opt_err_code == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + opt_err_code = ir_build_load_ptr(irb, parent_scope, node, ptr_opt_err_code, nullptr); + } else { + opt_err_code = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnionVal, result_loc); + if (opt_err_code == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + } IrInstruction *is_err = ir_build_test_nonnull(irb, parent_scope, node, opt_err_code); IrInstruction *is_comptime; @@ -6725,7 +6760,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode ZigVar *var = ir_create_var(irb, node, parent_scope, var_name, is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; - IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, err_scope, var_node, + IrInstruction *unwrapped_err_code_ptr = ir_build_optional_unwrap_ptr(irb, err_scope, var_node, ptr_opt_err_code, false); ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, unwrapped_err_code_ptr); } else { @@ -7756,7 +7791,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // Before we destroy the coroutine frame, we need to load the target promise into // a register or local variable which does not get spilled into the frame, // otherwise llvm tries to access memory inside the destroyed frame. - IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, + IrInstruction *unwrapped_await_handle_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, false); IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr, nullptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); @@ -14084,13 +14119,15 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, // For other types this is the same as payload_result_loc. IrInstruction *base_result_loc = nullptr; + // LValErrorUnionPtr is treated the same as LValErrorUnionVal. + bool convert_to_value; ZigType *scalar_result_type; ZigType *payload_result_type; if (return_type->id == ZigTypeIdErrorUnion) { scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); payload_result_type = return_type->data.error_union.payload_type; - convert_to_value = call_instruction->lval != LValErrorUnion; + convert_to_value = call_instruction->lval != LValErrorUnionVal && call_instruction->lval != LValErrorUnionPtr; } else if (return_type->id == ZigTypeIdOptional) { scalar_result_type = ira->codegen->builtin_types.entry_bool; payload_result_type = return_type->data.maybe.child_type; @@ -14168,7 +14205,8 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, return ir_get_deref(ira, &call_instruction->base, base_result_loc); case LValPtr: return base_result_loc; - case LValErrorUnion: + case LValErrorUnionVal: + case LValErrorUnionPtr: zig_unreachable(); case LValOptional: // This means incorrectly used optional syntax for error union. @@ -14184,7 +14222,8 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, return base_result_loc; case LValOptional: zig_unreachable(); - case LValErrorUnion: + case LValErrorUnionVal: + case LValErrorUnionPtr: // This means incorrectly used error union for optional. zig_panic("TODO"); } @@ -16903,22 +16942,61 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr } } - IrInstruction *result = ir_build_unwrap_maybe(&ira->new_irb, source_instr->scope, source_instr->source_node, - base_ptr, safety_check_on); + IrInstruction *result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr, safety_check_on); result->value.type = result_type; return result; } -static IrInstruction *ir_analyze_instruction_unwrap_optional(IrAnalyze *ira, - IrInstructionUnwrapOptional *instruction) +static IrInstruction *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira, + IrInstructionOptionalUnwrapPtr *instruction) { - IrInstruction *base_ptr = instruction->value->child; + IrInstruction *base_ptr = instruction->base_ptr->child; if (type_is_invalid(base_ptr->value.type)) return ira->codegen->invalid_instruction; return ir_analyze_unwrap_optional_payload(ira, &instruction->base, base_ptr, instruction->safety_check_on); } +static IrInstruction *ir_analyze_instruction_optional_unwrap_val(IrAnalyze *ira, + IrInstructionOptionalUnwrapVal *instruction) +{ + IrInstruction *opt = instruction->opt->child; + if (type_is_invalid(opt->value.type)) + return ira->codegen->invalid_instruction; + + ZigType *opt_type = opt->value.type; + if (opt_type->id != ZigTypeIdOptional) { + ir_add_error_node(ira, opt->source_node, + buf_sprintf("expected optional type, found '%s'", buf_ptr(&opt_type->name))); + return ira->codegen->invalid_instruction; + } + + ZigType *child_type = opt_type->data.maybe.child_type; + if (instr_is_comptime(opt)) { + ConstExprValue *opt_val = ir_resolve_const(ira, opt, UndefBad); + if (!opt_val) + return ira->codegen->invalid_instruction; + if (optional_value_is_null(opt_val)) { + ir_add_error(ira, &instruction->base, buf_sprintf("unable to unwrap null")); + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_const(ira, &instruction->base, child_type); + if (get_codegen_ptr_type(opt_val->type) != nullptr) { + copy_const_val(&result->value, opt_val, false); + } else { + copy_const_val(&result->value, opt_val->data.x_optional, false); + } + result->value.type = child_type; + return result; + } + + IrInstruction *result = ir_build_optional_unwrap_val(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, opt, instruction->safety_check_on); + result->value.type = child_type; + return result; +} + static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) { IrInstruction *value = ctz_instruction->value->child; if (type_is_invalid(value->value.type)) { @@ -22487,8 +22565,10 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); case IrInstructionIdTestNonNull: return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapOptional: - return ir_analyze_instruction_unwrap_optional(ira, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + return ir_analyze_instruction_optional_unwrap_ptr(ira, (IrInstructionOptionalUnwrapPtr *)instruction); + case IrInstructionIdOptionalUnwrapVal: + return ir_analyze_instruction_optional_unwrap_val(ira, (IrInstructionOptionalUnwrapVal *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -22874,7 +22954,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdTestNonNull: - case IrInstructionIdUnwrapOptional: + case IrInstructionIdOptionalUnwrapPtr: + case IrInstructionIdOptionalUnwrapVal: case IrInstructionIdClz: case IrInstructionIdCtz: case IrInstructionIdPopCount: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 0966fe13eb49..5eca13215c81 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -474,9 +474,18 @@ static void ir_print_test_non_null(IrPrint *irp, IrInstructionTestNonNull *instr fprintf(irp->f, " != null"); } -static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) { - fprintf(irp->f, "UnwrapOptional("); - ir_print_other_instruction(irp, instruction->value); +static void ir_print_optional_unwrap_ptr(IrPrint *irp, IrInstructionOptionalUnwrapPtr *instruction) { + fprintf(irp->f, "OptionalUnwrapPtr("); + ir_print_other_instruction(irp, instruction->base_ptr); + fprintf(irp->f, ")"); + if (!instruction->safety_check_on) { + fprintf(irp->f, " // no safety"); + } +} + +static void ir_print_optional_unwrap_val(IrPrint *irp, IrInstructionOptionalUnwrapVal *instruction) { + fprintf(irp->f, "OptionalUnwrapVal("); + ir_print_other_instruction(irp, instruction->opt); fprintf(irp->f, ")"); if (!instruction->safety_check_on) { fprintf(irp->f, " // no safety"); @@ -1531,8 +1540,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTestNonNull: ir_print_test_non_null(irp, (IrInstructionTestNonNull *)instruction); break; - case IrInstructionIdUnwrapOptional: - ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + ir_print_optional_unwrap_ptr(irp, (IrInstructionOptionalUnwrapPtr *)instruction); + break; + case IrInstructionIdOptionalUnwrapVal: + ir_print_optional_unwrap_val(irp, (IrInstructionOptionalUnwrapVal *)instruction); break; case IrInstructionIdCtz: ir_print_ctz(irp, (IrInstructionCtz *)instruction); From 696fcd5bd7d3ba290bf720f2c0971997ad83cb48 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 29 Nov 2018 16:57:27 -0500 Subject: [PATCH 072/190] copy elision: return pointers for error union functions ```zig fn ebar2() anyerror!Bar { return ebar(); } ``` ```llvm define internal fastcc i16 @ebar2(%Bar* nonnull dereferenceable(8), %StackTrace* nonnull) unnamed_addr #2 !dbg !60 { Entry: %2 = call fastcc i16 @ebar(%Bar* %0, %StackTrace* %1), !dbg !65 ret i16 %2, !dbg !67 } ``` --- src/all_types.hpp | 15 +++++ src/analyze.cpp | 1 + src/ast_render.cpp | 7 +++ src/codegen.cpp | 31 +--------- src/ir.cpp | 142 ++++++++++++++++++++++++++++++++------------- src/ir_print.cpp | 7 +++ src/parser.cpp | 9 +-- 7 files changed, 139 insertions(+), 73 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 695266901780..8231d032b2c3 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -463,6 +463,7 @@ enum NodeType { NodeTypeAwaitExpr, NodeTypeSuspend, NodeTypePromiseType, + NodeTypeErrorLiteral, }; enum CallingConvention { @@ -934,6 +935,10 @@ struct AstNodePromiseType { AstNode *payload_type; // can be NULL }; +struct AstNodeErrorLiteral { + Buf *name; +}; + struct AstNode { enum NodeType type; size_t line; @@ -994,6 +999,7 @@ struct AstNode { AstNodeAwaitExpr await_expr; AstNodeSuspend suspend; AstNodePromiseType promise_type; + AstNodeErrorLiteral error_literal; } data; }; @@ -2221,6 +2227,7 @@ enum IrInstructionId { IrInstructionIdInferArrayType, IrInstructionIdInferCompTime, IrInstructionIdSetNonNullBit, + IrInstructionIdErrorLiteral, }; struct IrInstruction { @@ -3434,6 +3441,14 @@ struct IrInstructionSetNonNullBit { IrInstruction *new_result_loc; }; +// This is the instruction for the syntax: +// `error.Foo` +struct IrInstructionErrorLiteral { + IrInstruction base; + + Buf *name; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index b29657736c0d..79ded06013ff 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3502,6 +3502,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeAwaitExpr: case NodeTypeSuspend: case NodeTypePromiseType: + case NodeTypeErrorLiteral: zig_unreachable(); } } diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 994ba5f5b139..904018a66845 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -249,6 +249,8 @@ static const char *node_type_str(NodeType node_type) { return "PromiseType"; case NodeTypePointerType: return "PointerType"; + case NodeTypeErrorLiteral: + return "ErrorLiteral"; } zig_unreachable(); } @@ -1145,6 +1147,11 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } break; } + case NodeTypeErrorLiteral: + { + fprintf(ar->f, "error.%s", buf_ptr(node->data.error_literal.name)); + break; + } case NodeTypeParamDecl: case NodeTypeTestDecl: case NodeTypeStructField: diff --git a/src/codegen.cpp b/src/codegen.cpp index 889d4d632661..c0e116086f50 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2262,33 +2262,7 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { LLVMValueRef value = ir_llvm_value(g, return_instruction->value); - ZigType *return_type = return_instruction->value->value.type; - FnTypeId *fn_type_id = &g->cur_fn->type_entry->data.fn.fn_type_id; - - if (fn_type_id->return_type->id == ZigTypeIdErrorUnion) { - ConstExprValue *val = &return_instruction->value->value; - if (val->special == ConstValSpecialStatic) { - LLVMValueRef const_error_val = gen_const_val(g, val->data.x_err_union.error_set, ""); - LLVMBuildRet(g->builder, const_error_val); - } else if (!type_has_bits(fn_type_id->return_type->data.error_union.payload_type)) { - LLVMBuildRet(g->builder, value); - } else { - zig_panic("TODO"); - } - } else if (want_first_arg_sret(g, fn_type_id)) { - // Assume that the result location mechanism populated the value, - // unless the value is a comptime const. - if (return_instruction->value->value.special == ConstValSpecialStatic) { - assert(g->cur_ret_ptr); - gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); - } - LLVMBuildRetVoid(g->builder); - } else if (handle_is_ptr(return_type)) { - LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); - LLVMBuildRet(g->builder, by_val_value); - } else { - LLVMBuildRet(g->builder, value); - } + LLVMBuildRet(g->builder, value); return nullptr; } @@ -5129,7 +5103,7 @@ static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstr static LLVMValueRef ir_render_result_return(CodeGen *g, IrExecutable *executable, IrInstructionResultReturn *instruction) { - assert(g->cur_ret_ptr != nullptr); + assert(g->cur_ret_ptr != nullptr || !type_has_bits(instruction->base.value.type)); return g->cur_ret_ptr; } @@ -5317,6 +5291,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdInferCompTime: case IrInstructionIdResultPtrCast: case IrInstructionIdCmpxchgSrc: + case IrInstructionIdErrorLiteral: zig_unreachable(); case IrInstructionIdDeclVarGen: diff --git a/src/ir.cpp b/src/ir.cpp index 87d2c6abe0f2..efcbe1597ed5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -35,6 +35,8 @@ struct IrAnalyze { size_t instruction_index; ZigType *explicit_return_type; AstNode *explicit_return_type_source_node; + ZigType *scalar_return_type; + ZigType *payload_return_type; ZigList src_implicit_return_type_list; IrBasicBlock *const_predecessor_bb; }; @@ -947,6 +949,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetNonNullBit *) return IrInstructionIdSetNonNullBit; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorLiteral *) { + return IrInstructionIdErrorLiteral; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -3024,6 +3030,13 @@ static IrInstruction *ir_build_set_nonnull_bit(IrBuilder *irb, Scope *scope, Ast return &instruction->base; } +static IrInstruction *ir_build_error_literal(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *name) { + IrInstructionErrorLiteral *instruction = ir_build_instruction(irb, scope, source_node); + instruction->name = name; + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3380,13 +3393,22 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, if (expr_node) { IrInstruction *return_result_loc = nullptr; ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; - if (return_type != nullptr && type_has_bits(return_type) && handle_is_ptr(return_type)) { - return_result_loc = ir_build_result_return(irb, scope, node); + LVal expr_lval = LValNone; + if (return_type != nullptr) { + if (return_type->id == ZigTypeIdErrorUnion) { + return_result_loc = ir_build_result_return(irb, scope, node); + expr_lval = LValErrorUnionVal; + } else if (return_type->id == ZigTypeIdOptional) { + return_result_loc = ir_build_result_return(irb, scope, node); + expr_lval = LValOptional; + } else if (type_has_bits(return_type) && handle_is_ptr(return_type)) { + return_result_loc = ir_build_result_return(irb, scope, node); + } } // Temporarily set this so that if we return a type it gets the name of the function ZigFn *prev_name_fn = irb->exec->name_fn; irb->exec->name_fn = exec_fn_entry(irb->exec); - return_value = ir_gen_node(irb, expr_node, scope, LValNone, return_result_loc); + return_value = ir_gen_node(irb, expr_node, scope, expr_lval, return_result_loc); irb->exec->name_fn = prev_name_fn; if (return_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -7581,6 +7603,17 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_await_expr(irb, scope, node)); case NodeTypeSuspend: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_suspend(irb, scope, node)); + case NodeTypeErrorLiteral: { + IrInstruction *err_code = ir_build_error_literal(irb, scope, node, node->data.error_literal.name); + switch (lval) { + case LValErrorUnionVal: + return err_code; + case LValErrorUnionPtr: + return ir_build_ref(irb, scope, node, err_code, true, false, nullptr); + default: + return ir_gen_value(irb, scope, node, lval, result_loc, err_code); + } + } } zig_unreachable(); } @@ -12138,7 +12171,7 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio if (type_is_invalid(value->value.type)) return ir_unreach_error(ira); - IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type); + IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->scalar_return_type); if (type_is_invalid(casted_value->value.type) && ira->explicit_return_type_source_node != nullptr) { ErrorMsg *msg = ira->codegen->errors.last(); add_error_note(ira->codegen, msg, ira->explicit_return_type_source_node, @@ -15963,6 +15996,34 @@ static ErrorTableEntry *find_err_table_entry(ZigType *err_set_type, Buf *field_n return nullptr; } +static IrInstruction *ir_analyze_error_literal(IrAnalyze *ira, IrInstruction *source_instr, Buf *name) { + ErrorTableEntry *err_entry; + auto existing_entry = ira->codegen->error_table.maybe_get(name); + if (existing_entry) { + err_entry = existing_entry->value; + } else { + err_entry = allocate(1); + err_entry->decl_node = source_instr->source_node; + buf_init_from_buf(&err_entry->name, name); + size_t error_value_count = ira->codegen->errors_by_index.length; + assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count)); + err_entry->value = error_value_count; + ira->codegen->errors_by_index.append(err_entry); + ira->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(ira->codegen->dbuilder, + buf_ptr(name), error_value_count)); + ira->codegen->error_table.put(name, err_entry); + } + if (err_entry->set_with_only_this_in_it == nullptr) { + err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen, + source_instr->scope, source_instr->source_node, err_entry); + } + ZigType *err_set_type = err_entry->set_with_only_this_in_it; + + IrInstruction *result = ir_const(ira, source_instr, err_set_type); + result->value.data.x_err_set = err_entry; + return result; +} + static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) { Error err; IrInstruction *container_ptr = field_ptr_instruction->container_ptr->child; @@ -16124,49 +16185,28 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->invalid_instruction; } else if (child_type->id == ZigTypeIdErrorSet) { - ErrorTableEntry *err_entry; - ZigType *err_set_type; + bool ptr_is_const = true; + bool ptr_is_volatile = false; if (type_is_global_error_set(child_type)) { - auto existing_entry = ira->codegen->error_table.maybe_get(field_name); - if (existing_entry) { - err_entry = existing_entry->value; - } else { - err_entry = allocate(1); - err_entry->decl_node = field_ptr_instruction->base.source_node; - buf_init_from_buf(&err_entry->name, field_name); - size_t error_value_count = ira->codegen->errors_by_index.length; - assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count)); - err_entry->value = error_value_count; - ira->codegen->errors_by_index.append(err_entry); - ira->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(ira->codegen->dbuilder, - buf_ptr(field_name), error_value_count)); - ira->codegen->error_table.put(field_name, err_entry); - } - if (err_entry->set_with_only_this_in_it == nullptr) { - err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen, - field_ptr_instruction->base.scope, field_ptr_instruction->base.source_node, - err_entry); - } - err_set_type = err_entry->set_with_only_this_in_it; - } else { - if (!resolve_inferred_error_set(ira->codegen, child_type, field_ptr_instruction->base.source_node)) { - return ira->codegen->invalid_instruction; - } - err_entry = find_err_table_entry(child_type, field_name); - if (err_entry == nullptr) { - ir_add_error(ira, &field_ptr_instruction->base, - buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name))); - return ira->codegen->invalid_instruction; - } - err_set_type = child_type; + IrInstruction *value = ir_analyze_error_literal(ira, &field_ptr_instruction->base, field_name); + return ir_get_ref(ira, &field_ptr_instruction->base, value, + ptr_is_const, ptr_is_volatile, nullptr); + } + if (!resolve_inferred_error_set(ira->codegen, child_type, field_ptr_instruction->base.source_node)) { + return ira->codegen->invalid_instruction; } + ErrorTableEntry *err_entry = find_err_table_entry(child_type, field_name); + if (err_entry == nullptr) { + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name))); + return ira->codegen->invalid_instruction; + } + ZigType *err_set_type = child_type; ConstExprValue *const_val = create_const_vals(1); const_val->special = ConstValSpecialStatic; const_val->type = err_set_type; const_val->data.x_err_set = err_entry; - bool ptr_is_const = true; - bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base, const_val, err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else if (child_type->id == ZigTypeIdInt) { @@ -22263,7 +22303,7 @@ static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrIns ConstExprValue *pointee = create_const_vals(1); pointee->special = ConstValSpecialUndef; - pointee->type = fn->type_entry->data.fn.fn_type_id.return_type; + pointee->type = ira->payload_return_type; ZigType *result_type = get_pointer_to_type(ira->codegen, pointee->type, false); IrInstruction *result = ir_build_result_return(&ira->new_irb, instruction->base.scope, @@ -22492,6 +22532,10 @@ static IrInstruction *ir_analyze_instruction_set_non_null_bit(IrAnalyze *ira, return result; } +static IrInstruction *ir_analyze_instruction_error_literal(IrAnalyze *ira, IrInstructionErrorLiteral *instruction) { + return ir_analyze_error_literal(ira, &instruction->base, instruction->name); +} + static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -22791,6 +22835,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_result_optional_payload(ira, (IrInstructionResultOptionalPayload *)instruction); case IrInstructionIdSetNonNullBit: return ir_analyze_instruction_set_non_null_bit(ira, (IrInstructionSetNonNullBit *)instruction); + case IrInstructionIdErrorLiteral: + return ir_analyze_instruction_error_literal(ira, (IrInstructionErrorLiteral *)instruction); case IrInstructionIdResultBytesToSlice: zig_panic("TODO"); case IrInstructionIdResultSliceToBytes: @@ -22822,6 +22868,19 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; ira->explicit_return_type = is_async ? get_promise_type(codegen, expected_type) : expected_type; ira->explicit_return_type_source_node = expected_type_source_node; + ira->scalar_return_type = ira->explicit_return_type; + ira->payload_return_type = ira->explicit_return_type; + + if (fn_entry != nullptr) { + if (ira->explicit_return_type->id == ZigTypeIdErrorUnion) { + ira->scalar_return_type = get_optional_type(ira->codegen, + ira->explicit_return_type->data.error_union.err_set_type); + ira->payload_return_type = ira->explicit_return_type->data.error_union.payload_type; + } else if (ira->explicit_return_type->id == ZigTypeIdOptional) { + ira->scalar_return_type = ira->codegen->builtin_types.entry_bool; + ira->payload_return_type = ira->explicit_return_type->data.maybe.child_type; + } + } ira->old_irb.codegen = codegen; ira->old_irb.exec = old_exec; @@ -23034,6 +23093,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdErrorUnionFieldErrorSet: case IrInstructionIdFirstArgResultLoc: case IrInstructionIdInferArrayType: + case IrInstructionIdErrorLiteral: return false; case IrInstructionIdLoadPtr: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 5eca13215c81..41a0a7acbd1a 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1439,6 +1439,10 @@ static void ir_print_set_non_null_bit(IrPrint *irp, IrInstructionSetNonNullBit * fprintf(irp->f, ")"); } +static void ir_print_error_literal(IrPrint *irp, IrInstructionErrorLiteral *instruction) { + fprintf(irp->f, "error.%s", buf_ptr(instruction->name)); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1912,6 +1916,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdSetNonNullBit: ir_print_set_non_null_bit(irp, (IrInstructionSetNonNullBit *)instruction); break; + case IrInstructionIdErrorLiteral: + ir_print_error_literal(irp, (IrInstructionErrorLiteral *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/src/parser.cpp b/src/parser.cpp index 81bd469d1c08..d272e1077fb9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1717,10 +1717,8 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { if (error != nullptr) { Token *dot = expect_token(pc, TokenIdDot); Token *name = expect_token(pc, TokenIdSymbol); - AstNode *left = ast_create_node(pc, NodeTypeErrorType, error); - AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot); - res->data.field_access_expr.struct_expr = left; - res->data.field_access_expr.field_name = token_buf(name); + AstNode *res = ast_create_node(pc, NodeTypeErrorLiteral, dot); + res->data.error_literal.name = token_buf(name); return res; } @@ -3095,5 +3093,8 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeSuspend: visit_field(&node->data.suspend.block, visit, context); break; + case NodeTypeErrorLiteral: + // none + break; } } From 1ad72aa05790baf8f3985ac39d8c022c5b6a9c19 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 29 Nov 2018 19:22:26 -0500 Subject: [PATCH 073/190] copy elision: function with optional pointer return type ```zig export fn entry(dest: ?[*]u8) ?[*]u8 { return dest; } ``` ```llvm define i8* @entry(i8*) #2 !dbg !41 { Entry: %dest = alloca i8*, align 8 store i8* %0, i8** %dest, align 8 call void @llvm.dbg.declare(metadata i8** %dest, metadata !45, metadata !DIExpression()), !dbg !46 %1 = load i8*, i8** %dest, align 8, !dbg !47 ret i8* %1, !dbg !50 } ``` --- src/analyze.cpp | 10 ++++++++++ src/codegen.cpp | 9 +++++++-- src/ir.cpp | 8 +++++--- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 79ded06013ff..9162f661f6e5 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1144,6 +1144,16 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { param_di_types.append(gen_type->di_type); } gen_return_type = g->builtin_types.entry_global_error_set; + } else if (fn_type_id->return_type->id == ZigTypeIdOptional) { + if (handle_is_ptr(fn_type_id->return_type)) { + ZigType *payload_type = fn_type_id->return_type->data.maybe.child_type; + ZigType *gen_type = get_pointer_to_type(g, payload_type, false); + gen_param_types.append(gen_type->type_ref); + param_di_types.append(gen_type->di_type); + gen_return_type = g->builtin_types.entry_bool; + } else { + gen_return_type = fn_type_id->return_type; + } } else if (!type_has_bits(fn_type_id->return_type)) { gen_return_type = g->builtin_types.entry_void; } else if (first_arg_return) { diff --git a/src/codegen.cpp b/src/codegen.cpp index c0e116086f50..2456a18d7837 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -621,6 +621,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { if (ret_type->id == ZigTypeIdErrorUnion) { uint64_t sz = type_size(g, ret_type->data.error_union.payload_type); addLLVMArgAttrInt(fn_table_entry->llvm_value, 0, "dereferenceable", sz); + } else if (ret_type->id == ZigTypeIdOptional) { + uint64_t sz = type_size(g, ret_type->data.maybe.child_type); + addLLVMArgAttrInt(fn_table_entry->llvm_value, 0, "dereferenceable", sz); } else { addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); } @@ -3467,7 +3470,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr CallingConvention cc = fn_type->data.fn.fn_type_id.cc; bool is_error_union = src_return_type->id == ZigTypeIdErrorUnion; + bool is_optional = src_return_type->id == ZigTypeIdOptional; bool first_arg_ret = (is_error_union && type_has_bits(src_return_type->data.error_union.payload_type)) || + (is_optional && type_has_bits(src_return_type->data.maybe.child_type)) || (ret_has_bits && want_first_arg_sret(g, fn_type_id)); bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id); bool is_var_args = fn_type_id->is_var_args; @@ -3539,10 +3544,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } else if (!ret_has_bits) { return nullptr; } else if (first_arg_ret) { - if (cc_want_sret_attr(cc) || src_return_type->id != ZigTypeIdErrorUnion) { + if (cc_want_sret_attr(cc) || (!is_error_union && !is_optional)) { set_call_instr_sret(g, result); } - return is_error_union ? result : result_ptr; + return (is_error_union || is_optional) ? result : result_ptr; } else if (handle_is_ptr(src_return_type)) { auto store_instr = LLVMBuildStore(g->builder, result, result_ptr); LLVMSetAlignment(store_instr, LLVMGetAlignment(result_ptr)); diff --git a/src/ir.cpp b/src/ir.cpp index efcbe1597ed5..064b17540199 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3398,7 +3398,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, if (return_type->id == ZigTypeIdErrorUnion) { return_result_loc = ir_build_result_return(irb, scope, node); expr_lval = LValErrorUnionVal; - } else if (return_type->id == ZigTypeIdOptional) { + } else if (return_type->id == ZigTypeIdOptional && handle_is_ptr(return_type)) { return_result_loc = ir_build_result_return(irb, scope, node); expr_lval = LValOptional; } else if (type_has_bits(return_type) && handle_is_ptr(return_type)) { @@ -22877,8 +22877,10 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ ira->explicit_return_type->data.error_union.err_set_type); ira->payload_return_type = ira->explicit_return_type->data.error_union.payload_type; } else if (ira->explicit_return_type->id == ZigTypeIdOptional) { - ira->scalar_return_type = ira->codegen->builtin_types.entry_bool; - ira->payload_return_type = ira->explicit_return_type->data.maybe.child_type; + if (handle_is_ptr(ira->explicit_return_type)) { + ira->scalar_return_type = ira->codegen->builtin_types.entry_bool; + ira->payload_return_type = ira->explicit_return_type->data.maybe.child_type; + } } } From aff8677180cc39ef3963db8c32ea414b0c9bcaad Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 29 Nov 2018 20:17:14 -0500 Subject: [PATCH 074/190] copy elision: return a var which casts to error union ```zig pub fn bar3() anyerror!Bar { var b: Bar = undefined; return b; } ``` ```llvm define internal fastcc i16 @bar3(%Bar* nonnull dereferenceable(8), %StackTrace* nonnull) unnamed_addr #2 !dbg !59 { Entry: %b = alloca %Bar, align 4 %2 = bitcast %Bar* %b to i8*, !dbg !66 call void @llvm.memset.p0i8.i64(i8* align 4 %2, i8 -86, i64 8, i1 false), !dbg !66 call void @llvm.dbg.declare(metadata %Bar* %b, metadata !64, metadata !DIExpression()), !dbg !66 %3 = bitcast %Bar* %b to i8*, !dbg !67 %4 = bitcast %Bar* %0 to i8*, !dbg !67 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %4, i8* align 4 %3, i64 8, i1 false), !dbg !67 ret i16 0, !dbg !69 } ``` --- src/all_types.hpp | 1 + src/ir.cpp | 30 +++++++++++++++++++----------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 8231d032b2c3..70177e42dcab 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2916,6 +2916,7 @@ struct IrInstructionErrorUnionFieldErrorSet { IrInstruction base; IrInstruction *ptr; + bool safety_check_on; }; struct IrInstructionUnwrapErrPayload { diff --git a/src/ir.cpp b/src/ir.cpp index 064b17540199..c697cd59efde 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -173,7 +173,7 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on); static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, - IrInstruction *source_instr, IrInstruction *base_ptr); + IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -2967,10 +2967,11 @@ static IrInstruction *ir_build_assert_non_null(IrBuilder *irb, Scope *scope, Ast } static IrInstruction *ir_build_error_union_field_error_set(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *ptr) + IrInstruction *ptr, bool safety_check_on) { IrInstructionErrorUnionFieldErrorSet *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; + instruction->safety_check_on = safety_check_on; ir_ref_instruction(ptr, irb->current_basic_block); @@ -3339,7 +3340,7 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV // must return pointer to the error code IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, ptr, false); ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); - return ir_build_error_union_field_error_set(irb, scope, node, ptr); + return ir_build_error_union_field_error_set(irb, scope, node, ptr, false); } case LValErrorUnionVal: { // ptr points to an error union; @@ -3347,7 +3348,7 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV // must return the error code IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, ptr, false); ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); - IrInstruction *err_ptr = ir_build_error_union_field_error_set(irb, scope, node, ptr); + IrInstruction *err_ptr = ir_build_error_union_field_error_set(irb, scope, node, ptr, false); return ir_build_load_ptr(irb, scope, node, err_ptr, nullptr); } case LValOptional: { @@ -5649,7 +5650,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n return err_union_ptr; IrInstruction *ptr_opt_err_code = ir_build_error_union_field_error_set(irb, scope, - node->data.while_expr.condition, err_union_ptr); + node->data.while_expr.condition, err_union_ptr, false); IrInstruction *opt_err_code = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, ptr_opt_err_code, nullptr); IrInstruction *is_err = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, opt_err_code); @@ -6207,7 +6208,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * if (err_union_ptr == irb->codegen->invalid_instruction) return err_union_ptr; - IrInstruction *ptr_opt_err_code = ir_build_error_union_field_error_set(irb, scope, node, err_union_ptr); + IrInstruction *ptr_opt_err_code = ir_build_error_union_field_error_set(irb, scope, node, err_union_ptr, false); IrInstruction *opt_err_code = ir_build_load_ptr(irb, scope, node, ptr_opt_err_code, nullptr); IrInstruction *is_err = ir_build_test_nonnull(irb, scope, node, opt_err_code); @@ -14231,7 +14232,7 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, if (return_type->id == ZigTypeIdErrorUnion) { IrInstruction *err_code_ptr = ir_analyze_error_union_field_error_set(ira, - &call_instruction->base, base_result_loc); + &call_instruction->base, base_result_loc, false); ir_analyze_store_ptr(ira, &call_instruction->base, err_code_ptr, new_call_instruction); switch (call_instruction->lval) { case LValNone: @@ -20585,7 +20586,7 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, - IrInstruction *source_instr, IrInstruction *base_ptr) + IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on) { ZigType *ptr_type = base_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); @@ -20595,6 +20596,10 @@ static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, return ira->codegen->invalid_instruction; if (type_entry->id != ZigTypeIdErrorUnion) { + if (!safety_check_on) { + IrInstruction *null = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_null); + return ir_get_ref(ira, source_instr, null, true, true, nullptr); + } ir_add_error(ira, base_ptr, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; @@ -20632,7 +20637,7 @@ static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, IrInstruction *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_error_union_field_error_set(&ira->new_irb, source_instr->scope, - source_instr->source_node, base_ptr); + source_instr->source_node, base_ptr, safety_check_on); result->value.type = result_type; result->value.special = ConstValSpecialStatic; } else { @@ -20647,7 +20652,7 @@ static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, } IrInstruction *result = ir_build_error_union_field_error_set(&ira->new_irb, - source_instr->scope, source_instr->source_node, base_ptr); + source_instr->scope, source_instr->source_node, base_ptr, safety_check_on); result->value.type = result_type; return result; } @@ -20659,7 +20664,7 @@ static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnaly if (type_is_invalid(base_ptr->value.type)) return ira->codegen->invalid_instruction; - return ir_analyze_error_union_field_error_set(ira, &instruction->base, base_ptr); + return ir_analyze_error_union_field_error_set(ira, &instruction->base, base_ptr, instruction->safety_check_on); } static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, @@ -20717,6 +20722,9 @@ static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstructio if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; if (type_entry->id != ZigTypeIdErrorUnion) { + if (!safety_check_on) { + return base_ptr; + } ir_add_error(ira, base_ptr, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; From 92c976d2df3c3ca2ced9bdff1d3913fe38f7220e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 29 Nov 2018 23:22:30 -0500 Subject: [PATCH 075/190] copy elision: return T from fn with return type E!T ```zig pub fn getStdErr() anyerror!Bar { return bar(); } ``` ```llvm define internal fastcc i16 @getStdErr(%Bar* nonnull dereferenceable(8), %StackTrace* nonnull) unnamed_addr #2 !dbg !59 { Entry: call fastcc void @bar(%Bar* sret %0), !dbg !64 ret i16 0, !dbg !66 } ``` --- src/codegen.cpp | 12 +++++++++++- src/ir.cpp | 30 ++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 2456a18d7837..0fab13594a03 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2265,7 +2265,17 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { LLVMValueRef value = ir_llvm_value(g, return_instruction->value); - LLVMBuildRet(g->builder, value); + ZigType *return_type = return_instruction->value->value.type; + if (type_has_bits(return_type) && + handle_is_ptr(return_type) && + return_instruction->value->value.special == ConstValSpecialStatic) + { + assert(g->cur_ret_ptr); + gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); + LLVMBuildRetVoid(g->builder); + } else { + LLVMBuildRet(g->builder, value); + } return nullptr; } diff --git a/src/ir.cpp b/src/ir.cpp index c697cd59efde..f8d9b1981fc2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14227,8 +14227,21 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); return new_call_instruction; } - if (!convert_to_value) - return new_call_instruction; + if (!convert_to_value) { + switch (call_instruction->lval) { + case LValNone: + return new_call_instruction; + case LValPtr: + zig_panic("TODO"); + case LValErrorUnionVal: + return ir_const(ira, &call_instruction->base, ira->codegen->builtin_types.entry_null); + case LValErrorUnionPtr: + zig_panic("TODO"); + case LValOptional: + return ir_const_bool(ira, &call_instruction->base, false); + } + zig_unreachable(); + } if (return_type->id == ZigTypeIdErrorUnion) { IrInstruction *err_code_ptr = ir_analyze_error_union_field_error_set(ira, @@ -22443,8 +22456,17 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira result->base.value.data.x_ptr.mut = ConstPtrMutInfer; result->base.value.data.x_ptr.data.ref.pointee = pointee; - assert(fn_ref->value.type->id == ZigTypeIdFn); - ZigType *param_type = fn_ref->value.type->data.fn.fn_type_id.param_info[0].type; + ZigType *param_type; + if (fn_ref->value.type->id == ZigTypeIdFn) { + ZigType *fn_type = fn_ref->value.type; + param_type = fn_type->data.fn.fn_type_id.param_info[0].type; + } else if (fn_ref->value.type->id == ZigTypeIdBoundFn) { + ZigType *fn_type = fn_ref->value.type->data.bound_fn.fn_type; + param_type = fn_type->data.fn.fn_type_id.param_info[1].type; + } else { + zig_unreachable(); + } + if (type_is_invalid(param_type)) return ira->codegen->invalid_instruction; if ((err = resolve_alloca_inference(ira, result, param_type))) From 5fdcf68141487dd37a8ddccb68ddd51b64845de3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 30 Nov 2018 00:20:38 -0500 Subject: [PATCH 076/190] copy elision: fix switch statements on bare number --- src/ir.cpp | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index f8d9b1981fc2..aae811786957 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5005,9 +5005,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; - IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, + return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr, result_loc, nullptr, lval); - return ir_gen_multi(irb, scope, node, lval, result_loc, call); } case BuiltinFnIdNewStackCall: { @@ -5036,9 +5035,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return args[i]; } - IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, + return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack, result_loc, nullptr, lval); - return ir_gen_multi(irb, scope, node, lval, result_loc, call); } case BuiltinFnIdTypeId: { @@ -5278,9 +5276,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node return irb->codegen->invalid_instruction; // In the analysis, this call instruction will be a simple LoadPtr instruction if // it turns out to be an implicit cast. - IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, + return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr, result_loc, arg_result_loc, lval); - return ir_gen_multi(irb, scope, node, lval, result_loc, fn_call); } for (size_t i = 0; i < arg_count; i += 1) { @@ -5299,9 +5296,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } } - IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, + return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr, result_loc, nullptr, lval); - return ir_gen_multi(irb, scope, node, lval, result_loc, fn_call); } static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -14153,8 +14149,6 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, // For other types this is the same as payload_result_loc. IrInstruction *base_result_loc = nullptr; - // LValErrorUnionPtr is treated the same as LValErrorUnionVal. - bool convert_to_value; ZigType *scalar_result_type; ZigType *payload_result_type; @@ -14225,18 +14219,40 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, if (need_store_ptr) { ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); - return new_call_instruction; + switch (call_instruction->lval) { + case LValNone: + return new_call_instruction; + case LValPtr: + if (payload_result_loc != nullptr) + return payload_result_loc; + return ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); + case LValErrorUnionVal: + return ir_const(ira, &call_instruction->base, ira->codegen->builtin_types.entry_null); + case LValErrorUnionPtr: { + IrInstruction *null = ir_const(ira, &call_instruction->base, + ira->codegen->builtin_types.entry_null); + return ir_get_ref(ira, &call_instruction->base, null, true, false, nullptr); + } + case LValOptional: + return ir_const_bool(ira, &call_instruction->base, false); + } + zig_unreachable(); } if (!convert_to_value) { switch (call_instruction->lval) { case LValNone: return new_call_instruction; case LValPtr: - zig_panic("TODO"); + if (payload_result_loc != nullptr) + return payload_result_loc; + return ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); case LValErrorUnionVal: return ir_const(ira, &call_instruction->base, ira->codegen->builtin_types.entry_null); - case LValErrorUnionPtr: - zig_panic("TODO"); + case LValErrorUnionPtr: { + IrInstruction *null = ir_const(ira, &call_instruction->base, + ira->codegen->builtin_types.entry_null); + return ir_get_ref(ira, &call_instruction->base, null, true, false, nullptr); + } case LValOptional: return ir_const_bool(ira, &call_instruction->base, false); } From af2e55e70584ea729cabff42e3a98c5a654166ee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 5 Dec 2018 16:28:33 -0500 Subject: [PATCH 077/190] copy elision: fix try on an error union fn call ```zig pub fn getStderrStream() !i32 { stderr_file = try getStdErr(); return 53; } ``` ```llvm define internal fastcc i16 @getStderrStream(i32* nonnull dereferenceable(4), %StackTrace* nonnull) unnamed_addr #2 !dbg !55 { Entry: %2 = call fastcc i16 @getStdErr(i32* @stderr_file, %StackTrace* %1), !dbg !60 %3 = icmp ne i16 %2, 0, !dbg !62 br i1 %3, label %ErrRetReturn, label %ErrRetContinue, !dbg !62 ErrRetReturn: ; preds = %Entry call fastcc void @__zig_return_error(%StackTrace* %1), !dbg !62 ret i16 %2, !dbg !62 ErrRetContinue: ; preds = %Entry store i32 53, i32* %0, align 4, !dbg !63 ret i16 0, !dbg !64 } ``` --- src/codegen.cpp | 20 ++++++++++------- src/ir.cpp | 57 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 0fab13594a03..d70f6471ddc1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2264,19 +2264,23 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut } static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { - LLVMValueRef value = ir_llvm_value(g, return_instruction->value); ZigType *return_type = return_instruction->value->value.type; - if (type_has_bits(return_type) && - handle_is_ptr(return_type) && - return_instruction->value->value.special == ConstValSpecialStatic) - { - assert(g->cur_ret_ptr); - gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); + if (!type_has_bits(return_type)) { LLVMBuildRetVoid(g->builder); + return nullptr; + } + LLVMValueRef value = ir_llvm_value(g, return_instruction->value); + if (handle_is_ptr(return_type)) { + if (return_instruction->value->value.special == ConstValSpecialStatic) { + assert(g->cur_ret_ptr); + gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); + } + LLVMBuildRetVoid(g->builder); + return nullptr; } else { LLVMBuildRet(g->builder, value); + return nullptr; } - return nullptr; } static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, diff --git a/src/ir.cpp b/src/ir.cpp index aae811786957..e66caccf34f4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3236,8 +3236,13 @@ static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, return ir_build_load_ptr(irb, scope, node, result_loc, nullptr); case LValPtr: return result_loc; - case LValErrorUnionVal: - case LValErrorUnionPtr: + case LValErrorUnionVal: { + IrInstruction *err_ptr = ir_build_error_union_field_error_set(irb, scope, node, result_loc, false); + return ir_build_load_ptr(irb, scope, node, err_ptr, nullptr); + } + case LValErrorUnionPtr: { + return ir_build_error_union_field_error_set(irb, scope, node, result_loc, false); + } case LValOptional: zig_unreachable(); } @@ -3292,10 +3297,13 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, case LValPtr: zig_unreachable(); case LValErrorUnionVal: - case LValErrorUnionPtr: - zig_panic("TODO"); - //ir_build_store_ptr(irb, scope, node, result_loc, value); - //return ir_gen_result(irb, scope, node, lval, result_loc); + ir_build_store_ptr(irb, scope, node, result_loc, value); + return ir_build_const_null(irb, scope, node); + case LValErrorUnionPtr: { + ir_build_store_ptr(irb, scope, node, result_loc, value); + IrInstruction *null = ir_build_const_null(irb, scope, node); + return ir_build_ref(irb, scope, node, null, true, false, nullptr); + } case LValOptional: { zig_panic("TODO"); //// result_loc is a pointer to child type @@ -14246,15 +14254,28 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, if (payload_result_loc != nullptr) return payload_result_loc; return ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); - case LValErrorUnionVal: - return ir_const(ira, &call_instruction->base, ira->codegen->builtin_types.entry_null); + case LValErrorUnionVal: { + if (return_type->id == ZigTypeIdErrorUnion) { + return new_call_instruction; + } else { + return ir_const(ira, &call_instruction->base, ira->codegen->builtin_types.entry_null); + } + } case LValErrorUnionPtr: { - IrInstruction *null = ir_const(ira, &call_instruction->base, - ira->codegen->builtin_types.entry_null); - return ir_get_ref(ira, &call_instruction->base, null, true, false, nullptr); + if (return_type->id == ZigTypeIdErrorUnion) { + return ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); + } else { + IrInstruction *null = ir_const(ira, &call_instruction->base, + ira->codegen->builtin_types.entry_null); + return ir_get_ref(ira, &call_instruction->base, null, true, false, nullptr); + } } case LValOptional: - return ir_const_bool(ira, &call_instruction->base, false); + if (return_type->id == ZigTypeIdOptional) { + return new_call_instruction; + } else { + return ir_const_bool(ira, &call_instruction->base, false); + } } zig_unreachable(); } @@ -22346,10 +22367,14 @@ static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrIns IrInstruction *result = ir_build_result_return(&ira->new_irb, instruction->base.scope, instruction->base.source_node); result->value.type = result_type; - result->value.special = ConstValSpecialStatic; - result->value.data.x_ptr.special = ConstPtrSpecialRef; - result->value.data.x_ptr.data.ref.pointee = pointee; - result->value.data.x_ptr.mut = ConstPtrMutInfer; + if (type_has_bits(pointee->type) && handle_is_ptr(pointee->type)) { + result->value.special = ConstValSpecialStatic; + result->value.data.x_ptr.special = ConstPtrSpecialRef; + result->value.data.x_ptr.data.ref.pointee = pointee; + result->value.data.x_ptr.mut = ConstPtrMutInfer; + } else { + result->value.special = ConstValSpecialRuntime; + } return result; } From 38b560f4c51998894b4d6d0247e8e20bd65d473c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 6 Dec 2018 13:03:32 -0500 Subject: [PATCH 078/190] copy elision: const slice ```zig export fn entry() void { const fmt = "aoeu"; const start_index: usize = 0; output(fmt[start_index..]); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: call void @llvm.dbg.declare(metadata [4 x i8]* @1, metadata !45, metadata !DIExpression()), !dbg !52 call void @llvm.dbg.declare(metadata i64* @3, metadata !50, metadata !DIExpression()), !dbg !53 call fastcc void @output(%"[]u8"* @5), !dbg !54 ret void, !dbg !56 } ``` --- src/ir.cpp | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e66caccf34f4..ac777a3e6c67 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3249,33 +3249,6 @@ static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, zig_unreachable(); } -static IrInstruction *ir_gen_multi(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, - IrInstruction *result_loc, IrInstruction *value) -{ - switch (lval) { - case LValNone: - return value; - case LValPtr: { - if (result_loc != nullptr) - return result_loc; - // We needed a pointer to a value, but we got a value. So we create - // an instruction which just makes a pointer of it. - return ir_build_ref(irb, scope, value->source_node, value, false, false, nullptr); - } - case LValErrorUnionPtr: { - IrInstruction *err_alloca = ir_build_alloca_src(irb, scope, value->source_node, - nullptr, nullptr, "err"); - ir_build_store_ptr(irb, scope, value->source_node, err_alloca, value); - return err_alloca; - } - case LValErrorUnionVal: - return value; - case LValOptional: - return value; - } - zig_unreachable(); -} - static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { if (result_loc) return result_loc; @@ -6729,8 +6702,8 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, end_value = nullptr; } - IrInstruction *result = ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true, result_loc); - return ir_gen_multi(irb, scope, node, lval, result_loc, result); + ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true, result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc); } static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval, From 581ab45bc7e84aa301d57b776aba7538cc1c2998 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 6 Dec 2018 13:21:19 -0500 Subject: [PATCH 079/190] copy elision: implicit return void in an error union fn ```zig fn write() anyerror!void { try evoid(); } ``` ```llvm define internal fastcc i16 @write(%StackTrace* nonnull) unnamed_addr #2 !dbg !51 { Entry: %1 = call fastcc i16 @evoid(%StackTrace* %0), !dbg !55 %2 = icmp ne i16 %1, 0, !dbg !57 br i1 %2, label %ErrRetReturn, label %ErrRetContinue, !dbg !57 ErrRetReturn: ; preds = %Entry call fastcc void @__zig_return_error(%StackTrace* %0), !dbg !57 ret i16 %1, !dbg !57 ErrRetContinue: ; preds = %Entry ret i16 0, !dbg !58 } ``` --- src/ir.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index ac777a3e6c67..cb92db552a1a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7754,7 +7754,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec if (!instr_is_unreachable(result)) { // no need for save_err_ret_addr because this cannot return error - ir_gen_async_return(irb, scope, result->source_node, result, true); + IrInstruction *implicit_return_result = result; + if (fn_entry != nullptr) { + ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + if (return_type->id == ZigTypeIdErrorUnion) { + implicit_return_result = ir_build_const_null(irb, scope, result->source_node); + } + } + ir_gen_async_return(irb, scope, result->source_node, implicit_return_result, true); } if (is_async) { From 58aed421929f1adeedfc0a6e2a598894dbc89af9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 6 Dec 2018 14:56:52 -0500 Subject: [PATCH 080/190] copy elision: fix returning explicit error set as error union ```zig pub fn thing() E!void { return E.BrokenPipe; } ``` ```llvm define internal fastcc i16 @thing(%StackTrace* nonnull) unnamed_addr #2 !dbg !51 { Entry: call fastcc void @__zig_return_error(%StackTrace* %0), !dbg !55 ret i16 1, !dbg !55 } ``` --- src/all_types.hpp | 1 + src/analyze.cpp | 17 ++++++++--- src/ir.cpp | 78 +++++++++++++++++++++++++++++++++-------------- src/ir_print.cpp | 3 +- 4 files changed, 70 insertions(+), 29 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 70177e42dcab..8e69d06ff818 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2923,6 +2923,7 @@ struct IrInstructionUnwrapErrPayload { IrInstruction base; IrInstruction *value; + IrInstruction *result_loc; bool safety_check_on; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index 9162f661f6e5..5ba822d6a4e8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5766,7 +5766,7 @@ void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_v } } -void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { +static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); @@ -5803,6 +5803,14 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy zig_unreachable(); } +static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { + if (const_val->data.x_err_set == nullptr) { + buf_append_str(buf, "null"); + } else { + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); + } +} + void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { switch (const_val->special) { case ConstValSpecialRuntime: @@ -5930,6 +5938,8 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { { if (get_codegen_ptr_type(const_val->type) != nullptr) return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type); + if (type_entry->data.maybe.child_type->id == ZigTypeIdErrorSet) + return render_const_val_err_set(g, buf, const_val, type_entry->data.maybe.child_type); if (const_val->data.x_optional) { render_const_value(g, buf, const_val->data.x_optional); } else { @@ -5987,10 +5997,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { return; } case ZigTypeIdErrorSet: - { - buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); - return; - } + return render_const_val_err_set(g, buf, const_val, type_entry); case ZigTypeIdArgTuple: { buf_appendf(buf, "(args value)"); diff --git a/src/ir.cpp b/src/ir.cpp index cb92db552a1a..82e3c84435cb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -171,7 +171,7 @@ static IrInstruction *resolve_possible_alloca_inference(IrAnalyze *ira, IrInstru static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on); static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *base_ptr, bool safety_check_on); + IrInstruction *base_ptr, IrInstruction *result_loc, bool safety_check_on); static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on); @@ -2167,13 +2167,15 @@ static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, Ast } static IrInstruction *ir_build_unwrap_err_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *value, bool safety_check_on) + IrInstruction *value, IrInstruction *result_loc, bool safety_check_on) { IrInstructionUnwrapErrPayload *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; + instruction->result_loc = result_loc; instruction->safety_check_on = safety_check_on; ir_ref_instruction(value, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -3319,16 +3321,14 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV // ptr points to an error union; // result_loc points to the result payload // must return pointer to the error code - IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, ptr, false); - ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); + ir_build_unwrap_err_payload(irb, scope, node, ptr, result_loc, false); return ir_build_error_union_field_error_set(irb, scope, node, ptr, false); } case LValErrorUnionVal: { // ptr points to an error union; // result_loc points to the result payload // must return the error code - IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, ptr, false); - ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); + ir_build_unwrap_err_payload(irb, scope, node, ptr, result_loc, false); IrInstruction *err_ptr = ir_build_error_union_field_error_set(irb, scope, node, ptr, false); return ir_build_load_ptr(irb, scope, node, err_ptr, nullptr); } @@ -5640,7 +5640,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, body_block); if (var_symbol) { IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node, - err_union_ptr, false); + err_union_ptr, nullptr, false); IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ? ir_build_ref(irb, payload_scope, symbol_node, payload_ptr, true, false, nullptr) : payload_ptr; ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_ptr); @@ -6203,7 +6203,8 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); Scope *var_scope; if (var_symbol) { - IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_union_ptr, false); + IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_union_ptr, + nullptr, false); IrInstruction *var_type = nullptr; bool is_shadowable = false; @@ -10542,7 +10543,7 @@ static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *so IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; - if (get_codegen_ptr_type(wanted_type) != nullptr) { + if (types_have_same_zig_comptime_repr(wanted_type, payload_type)) { copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); } else { const_instruction->base.value.data.x_optional = val; @@ -10677,6 +10678,8 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so if (get_codegen_ptr_type(wanted_type) != nullptr) { const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; + } else if (is_opt_err_set(wanted_type)) { + const_instruction->base.value.data.x_err_set = nullptr; } else { const_instruction->base.value.data.x_optional = nullptr; } @@ -12255,6 +12258,8 @@ static bool optional_value_is_null(ConstExprValue *val) { if (get_codegen_ptr_type(val->type) != nullptr) { return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && val->data.x_ptr.data.hard_coded_addr.addr == 0; + } else if (is_opt_err_set(val->type)) { + return val->data.x_err_set == nullptr; } else { return val->data.x_optional == nullptr; } @@ -14177,7 +14182,7 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, return ira->codegen->invalid_instruction; if (return_type->id == ZigTypeIdErrorUnion) { payload_result_loc = ir_analyze_unwrap_err_payload(ira, &call_instruction->base, - base_result_loc, false); + base_result_loc, nullptr, false); } else if (return_type->id == ZigTypeIdOptional) { payload_result_loc = ir_analyze_unwrap_optional_payload(ira, &call_instruction->base, base_result_loc, false); @@ -17053,7 +17058,7 @@ static IrInstruction *ir_analyze_instruction_optional_unwrap_val(IrAnalyze *ira, return ira->codegen->invalid_instruction; } IrInstruction *result = ir_const(ira, &instruction->base, child_type); - if (get_codegen_ptr_type(opt_val->type) != nullptr) { + if (types_have_same_zig_comptime_repr(opt_val->type, child_type)) { copy_const_val(&result->value, opt_val, false); } else { copy_const_val(&result->value, opt_val->data.x_optional, false); @@ -20625,6 +20630,9 @@ static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; + if (type_entry->id == ZigTypeIdErrorSet) { + return base_ptr; + } if (type_entry->id != ZigTypeIdErrorUnion) { if (!safety_check_on) { IrInstruction *null = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_null); @@ -20742,7 +20750,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, } static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *base_ptr, bool safety_check_on) + IrInstruction *base_ptr, IrInstruction *result_loc, bool safety_check_on) { ZigType *ptr_type = base_ptr->value.type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. @@ -20751,13 +20759,19 @@ static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstructio ZigType *type_entry = ptr_type->data.pointer.child_type; if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; + if (result_loc != nullptr && type_entry->id == ZigTypeIdErrorSet) { + IrInstruction *undef = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_undef); + undef->value.special = ConstValSpecialUndef; + return ir_analyze_store_ptr(ira, source_instr, result_loc, undef); + } if (type_entry->id != ZigTypeIdErrorUnion) { - if (!safety_check_on) { - return base_ptr; + if (result_loc == nullptr) { + ir_add_error(ira, base_ptr, + buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->invalid_instruction; } - ir_add_error(ira, base_ptr, - buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); - return ira->codegen->invalid_instruction; + IrInstruction *deref = ir_get_deref(ira, source_instr, base_ptr); + return ir_analyze_store_ptr(ira, source_instr, result_loc, deref); } ZigType *payload_type = type_entry->data.error_union.payload_type; @@ -20806,7 +20820,7 @@ static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstructio IrInstruction *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope, - source_instr->source_node, base_ptr, safety_check_on); + source_instr->source_node, base_ptr, nullptr, safety_check_on); result->value.type = result_type; result->value.special = ConstValSpecialStatic; } else { @@ -20815,15 +20829,25 @@ static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstructio result->value.data.x_ptr.special = ConstPtrSpecialRef; result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; result->value.data.x_ptr.mut = ptr_val->data.x_ptr.mut; - return result; + if (result_loc == nullptr) { + return result; + } else { + IrInstruction *deref = ir_get_deref(ira, source_instr, result); + return ir_analyze_store_ptr(ira, source_instr, result_loc, deref); + } } } } IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope, - source_instr->source_node, base_ptr, safety_check_on); + source_instr->source_node, base_ptr, nullptr, safety_check_on); result->value.type = result_type; - return result; + if (result_loc == nullptr) { + return result; + } else { + IrInstruction *deref = ir_get_deref(ira, source_instr, result); + return ir_analyze_store_ptr(ira, source_instr, result_loc, deref); + } } static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, @@ -20833,7 +20857,14 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, if (type_is_invalid(base_ptr->value.type)) return ira->codegen->invalid_instruction; - return ir_analyze_unwrap_err_payload(ira, &instruction->base, base_ptr, instruction->safety_check_on); + IrInstruction *result_loc = nullptr; + if (instruction->result_loc != nullptr) { + result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + } + + return ir_analyze_unwrap_err_payload(ira, &instruction->base, base_ptr, result_loc, instruction->safety_check_on); } static IrInstruction *ir_analyze_instruction_assert_non_error(IrAnalyze *ira, @@ -23161,7 +23192,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { { IrInstructionUnwrapErrPayload *unwrap_err_payload_instruction = (IrInstructionUnwrapErrPayload *)instruction; - return unwrap_err_payload_instruction->safety_check_on; + return unwrap_err_payload_instruction->safety_check_on || + unwrap_err_payload_instruction->result_loc != nullptr; } } zig_unreachable(); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 41a0a7acbd1a..65a2c783ef7c 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -827,7 +827,8 @@ static void ir_print_unwrap_err_code(IrPrint *irp, IrInstructionUnwrapErrCode *i static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayload *instruction) { fprintf(irp->f, "ErrorUnionFieldPayload("); ir_print_other_instruction(irp, instruction->value); - fprintf(irp->f, ")"); + fprintf(irp->f, ") result="); + ir_print_other_instruction(irp, instruction->result_loc); if (!instruction->safety_check_on) { fprintf(irp->f, " // no safety"); } From 64e8677b9b65171657cb2334d55e477e1ae71469 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 11 Dec 2018 16:29:18 -0500 Subject: [PATCH 081/190] copy elision: return fn call with error set return value ```zig pub fn thing() anyerror!void { return fail(); } ``` ```llvm define internal fastcc i16 @thing(%StackTrace* nonnull) unnamed_addr #2 !dbg !51 { Entry: %1 = call fastcc i16 @fail(%StackTrace* %0), !dbg !55 call fastcc void @__zig_return_error(%StackTrace* %0), !dbg !57 ret i16 %1, !dbg !57 } ``` --- src/ir.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 82e3c84435cb..ed9a3d69dfba 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9903,6 +9903,12 @@ static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, Z return new_instruction; } +static IrInstruction *ir_const_undef(IrAnalyze *ira, IrInstruction *source_instr) { + IrInstruction *undef = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_undef); + undef->value.special = ConstValSpecialUndef; + return undef; +} + static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op, bool need_alloca) { @@ -14149,6 +14155,10 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); payload_result_type = return_type->data.error_union.payload_type; convert_to_value = call_instruction->lval != LValErrorUnionVal && call_instruction->lval != LValErrorUnionPtr; + } else if (return_type->id == ZigTypeIdErrorSet) { + scalar_result_type = return_type; + payload_result_type = ira->codegen->builtin_types.entry_void; + convert_to_value = false; } else if (return_type->id == ZigTypeIdOptional) { scalar_result_type = ira->codegen->builtin_types.entry_bool; payload_result_type = return_type->data.maybe.child_type; @@ -14240,14 +14250,14 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, return payload_result_loc; return ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); case LValErrorUnionVal: { - if (return_type->id == ZigTypeIdErrorUnion) { + if (return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) { return new_call_instruction; } else { return ir_const(ira, &call_instruction->base, ira->codegen->builtin_types.entry_null); } } case LValErrorUnionPtr: { - if (return_type->id == ZigTypeIdErrorUnion) { + if (return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) { return ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); } else { IrInstruction *null = ir_const(ira, &call_instruction->base, @@ -20760,8 +20770,7 @@ static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstructio if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; if (result_loc != nullptr && type_entry->id == ZigTypeIdErrorSet) { - IrInstruction *undef = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_undef); - undef->value.special = ConstValSpecialUndef; + IrInstruction *undef = ir_const_undef(ira, source_instr); return ir_analyze_store_ptr(ira, source_instr, result_loc, undef); } if (type_entry->id != ZigTypeIdErrorUnion) { From 34687cabb78bc77673581d5259959dc4d1dd8f82 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 11 Dec 2018 16:40:07 -0500 Subject: [PATCH 082/190] copy elision: hello world is working ```zig pub fn main() void { std.debug.warn("hello\n"); } ``` --- src/codegen.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index d70f6471ddc1..c6448d5eb5d0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3275,7 +3275,7 @@ static bool value_is_all_undef(ConstExprValue *const_val) { } static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { - ZigType *usize = g->builtin_types.entry_usize; + assert(type_has_bits(value_type)); uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref); assert(size_bytes > 0); assert(ptr_align_bytes > 0); @@ -3283,6 +3283,7 @@ static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_ LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); + ZigType *usize = g->builtin_types.entry_usize; LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); } @@ -3290,6 +3291,8 @@ static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { ZigType *ptr_type = instruction->ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); + if (!type_has_bits(ptr_type)) + return nullptr; bool have_init_expr = !value_is_all_undef(&instruction->value->value); if (have_init_expr) { From 5579e9aad044e37ce759a99ebcd05686ff75ebbb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 11 Dec 2018 16:53:48 -0500 Subject: [PATCH 083/190] fix assertion regarding unresolved inferred error set --- src/ir.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index ed9a3d69dfba..bf5678015e41 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9237,13 +9237,13 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT size_t errors_count = 0; ZigType *err_set_type = nullptr; if (prev_inst->value.type->id == ZigTypeIdErrorSet) { + if (!resolve_inferred_error_set(ira->codegen, prev_inst->value.type, prev_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } if (type_is_global_error_set(prev_inst->value.type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; } else { err_set_type = prev_inst->value.type; - if (!resolve_inferred_error_set(ira->codegen, err_set_type, prev_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } update_errors_helper(ira->codegen, &errors, &errors_count); for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { From 0ff9c9d629f9280f21413d215c17a1cdd2cb0b36 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 14 Dec 2018 19:36:40 -0500 Subject: [PATCH 084/190] copy elision: misc fixes --- src/ir.cpp | 28 +++++++++++++++------------- test/behavior.zig | 2 -- test/stage1/behavior.zig | 3 +++ test/stage1/behavior/misc.zig | 16 ++++++++++++++++ 4 files changed, 34 insertions(+), 15 deletions(-) create mode 100644 test/stage1/behavior.zig create mode 100644 test/stage1/behavior/misc.zig diff --git a/src/ir.cpp b/src/ir.cpp index bf5678015e41..eb4e65302e26 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3245,8 +3245,10 @@ static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, case LValErrorUnionPtr: { return ir_build_error_union_field_error_set(irb, scope, node, result_loc, false); } - case LValOptional: - zig_unreachable(); + case LValOptional: { + IrInstruction *loaded_ptr = ir_build_load_ptr(irb, scope, node, result_loc, nullptr); + return ir_build_test_nonnull(irb, scope, node, loaded_ptr); + } } zig_unreachable(); } @@ -3280,14 +3282,8 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, return ir_build_ref(irb, scope, node, null, true, false, nullptr); } case LValOptional: { - zig_panic("TODO"); - //// result_loc is a pointer to child type - //// value is an optional value - //// need to return non-null bit - //IrInstruction *opt_ptr = ir_build_ref(irb, scope, node, value, false, false, nullptr); - //IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, opt_ptr, false); - //ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); - //return ir_build_test_nonnull(irb, scope, node, value); + ir_build_store_ptr(irb, scope, node, result_loc, value); + return ir_build_const_bool(irb, scope, node, true); } } } @@ -3447,6 +3443,12 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindError: { assert(expr_node); + if (lval == LValOptional) { + add_node_error(irb->codegen, expr_node, + buf_sprintf("TODO: compiler bug: copy elision not advanced enough to handle nested try")); + return irb->codegen->invalid_instruction; + } + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, result_loc); IrInstruction *opt_err_code = ir_gen_node(irb, expr_node, scope, LValErrorUnionVal, ensured_result_loc); @@ -9402,6 +9404,9 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (prev_type->id == ZigTypeIdArray) { convert_to_const_slice = true; } + if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } if (type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; @@ -9409,9 +9414,6 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { continue; } - if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } update_errors_helper(ira->codegen, &errors, &errors_count); diff --git a/test/behavior.zig b/test/behavior.zig index 1d031343d684..b961fa019428 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,3 @@ -const builtin = @import("builtin"); - comptime { _ = @import("cases/align.zig"); _ = @import("cases/alignof.zig"); diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig new file mode 100644 index 000000000000..3aa73f4adc40 --- /dev/null +++ b/test/stage1/behavior.zig @@ -0,0 +1,3 @@ +comptime { + _ = @import("behavior/misc.zig"); +} diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig new file mode 100644 index 000000000000..156433c804fd --- /dev/null +++ b/test/stage1/behavior/misc.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; +const cstr = std.cstr; +const builtin = @import("builtin"); +const maxInt = std.math.maxInt; + +// normal comment + +/// this is a documentation comment +/// doc comment line 2 +fn emptyFunctionWithComments() void {} + +test "empty function with comments" { + emptyFunctionWithComments(); +} From 2b39363ce5e4dee78542f67c1e45bcb76a5cf2a9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 17 Dec 2018 19:13:05 -0500 Subject: [PATCH 085/190] copy elision: @sliceToBytes ```zig export fn entry() void { var z = []u32{1234}; var y: []const u32 = z[0..]; var x = @sliceToBytes(y); } ``` don't worry about the bounds check, that's #1839 ```llvm define void @entry() #2 !dbg !41 { Entry: %z = alloca [1 x i32], align 4 %y = alloca %"[]u32", align 8 %x = alloca %"[]u8", align 8 %0 = bitcast [1 x i32]* %z to i8*, !dbg !60 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast ([1 x i32]* @0 to i8*), i64 4, i1 false), !dbg !60 call void @llvm.dbg.declare(metadata [1 x i32]* %z, metadata !45, metadata !DIExpression()), !dbg !60 br i1 true, label %BoundsCheckOk, label %BoundsCheckFail, !dbg !61 BoundsCheckFail: ; preds = %Entry tail call fastcc void @panic(%"[]u8"* @3, %StackTrace* null), !dbg !61 unreachable, !dbg !61 BoundsCheckOk: ; preds = %Entry %1 = getelementptr inbounds %"[]u32", %"[]u32"* %y, i32 0, i32 0, !dbg !61 %2 = getelementptr inbounds [1 x i32], [1 x i32]* %z, i64 0, i64 0, !dbg !61 store i32* %2, i32** %1, align 8, !dbg !61 %3 = getelementptr inbounds %"[]u32", %"[]u32"* %y, i32 0, i32 1, !dbg !61 store i64 1, i64* %3, align 8, !dbg !61 call void @llvm.dbg.declare(metadata %"[]u32"* %y, metadata !51, metadata !DIExpression()), !dbg !62 %4 = bitcast %"[]u8"* %x to %"[]u32"*, !dbg !63 %5 = bitcast %"[]u32"* %y to i8*, !dbg !64 %6 = bitcast %"[]u32"* %4 to i8*, !dbg !64 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %6, i8* align 8 %5, i64 16, i1 false), !dbg !64 call void @llvm.dbg.declare(metadata %"[]u8"* %x, metadata !58, metadata !DIExpression()), !dbg !65 ret void, !dbg !66 } ``` --- src/all_types.hpp | 2 +- src/analyze.cpp | 4 +++- src/ir.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++++--- src/ir_print.cpp | 4 +++- 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 8e69d06ff818..565d234a4b1e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2982,7 +2982,7 @@ struct IrInstructionPtrCastGen { // instruction will replace this one. // so we have a reference here to the pass-1 instruction so that // the child pointer can be updated to the new pass-2 instruction. - IrInstructionResultPtrCast *pass1_parent; + IrInstruction *pass1_parent; }; struct IrInstructionWidenOrShorten { diff --git a/src/analyze.cpp b/src/analyze.cpp index 5ba822d6a4e8..1af7fa3f556e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -404,7 +404,9 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset_in_host, uint32_t host_int_bytes) { assert(!type_is_invalid(child_type)); - assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque); + assert(ptr_len == PtrLenSingle || + child_type->id != ZigTypeIdOpaque || + child_type == g->builtin_types.entry_infer); if (byte_alignment != 0) { uint32_t abi_alignment = get_abi_alignment(g, child_type); diff --git a/src/ir.cpp b/src/ir.cpp index eb4e65302e26..fe85489e65ba 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -174,6 +174,7 @@ static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstructio IrInstruction *base_ptr, IrInstruction *result_loc, bool safety_check_on); static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on); +static ZigType *adjust_ptr_child(CodeGen *g, ZigType *ptr_type, ZigType *new_child); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -4590,6 +4591,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; + ir_build_infer_comptime(irb, scope, node, ensured_result_loc, new_result_loc); + return ir_gen_result(irb, scope, node, lval, ensured_result_loc); } case BuiltinFnIdIntToFloat: @@ -11743,12 +11746,25 @@ static IrInstruction *resolve_possible_alloca_inference(IrAnalyze *ira, IrInstru // analysis, and rely on the ref_count being 0 so that it is not emitted. assert(ptr_cast->base.ref_count == 0); ZigType *prev_ptr_type = ptr_cast->ptr->value.type; + // TODO should this be adjust_ptr_child? ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, PtrLenSingle, prev_ptr_type->data.pointer.explicit_alignment, 0, 0); IrInstruction *new_ptr_cast = ir_analyze_ptr_cast(ira, base, ptr_cast->ptr, new_ptr_type, base); - ptr_cast->pass1_parent->base.child = new_ptr_cast; + ptr_cast->pass1_parent->child = new_ptr_cast; return new_ptr_cast; } + } else if (is_slice(infer_child)) { + ZigType *slice_ptr_type = infer_child->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + if (slice_ptr_type->data.pointer.child_type == ira->codegen->builtin_types.entry_infer) { + assert(base->id == IrInstructionIdPtrCastGen); + IrInstructionPtrCastGen *ptr_cast = reinterpret_cast(base); + // See above comment + assert(ptr_cast->base.ref_count == 0); + ZigType *prev_ptr_type = ptr_cast->ptr->value.type; + ZigType *new_ptr_type = adjust_ptr_child(ira->codegen, prev_ptr_type, child_type); + ptr_cast->base.value.type = new_ptr_type; + } } return base; } @@ -22418,7 +22434,7 @@ static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, ZigType *new_ptr_type = adjust_ptr_child(ira->codegen, new_result_loc->value.type, ira->codegen->builtin_types.entry_infer); IrInstruction *ptr_cast = ir_build_ptr_cast_gen(ira, &instruction->base, new_ptr_type, new_result_loc); - reinterpret_cast(ptr_cast)->pass1_parent = instruction; + reinterpret_cast(ptr_cast)->pass1_parent = &instruction->base; return ptr_cast; } @@ -22629,6 +22645,43 @@ static IrInstruction *ir_analyze_instruction_error_literal(IrAnalyze *ira, IrIns return ir_analyze_error_literal(ira, &instruction->base, instruction->name); } +static IrInstruction *ir_analyze_instruction_result_bytes_to_slice(IrAnalyze *ira, + IrInstructionResultBytesToSlice *instruction) +{ + // Here the pointer properties are wrong except for the child element type. + // This gets corrected later by resolve_alloca_inference + ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, 0, 0, 0); + ZigType *slice_of_bytes = get_slice_type(ira->codegen, slice_ptr_type); + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, prev_result_loc, + slice_of_bytes, false); + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + // This pointer type is also wrong and we don't even know the child type. + // Also corrected later by resolve_alloca_inference + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_infer, + true, false, PtrLenUnknown, 0, 0, 0); + ZigType *new_slice_type = get_slice_type(ira->codegen, new_ptr_type); + ZigType *new_result_loc_type = adjust_ptr_child(ira->codegen, prev_result_loc->value.type, new_slice_type); + + IrInstruction *result = ir_build_ptr_cast_gen(ira, &instruction->base, new_result_loc_type, new_result_loc); + reinterpret_cast(result)->pass1_parent = &instruction->base; + + if (instr_is_comptime(new_result_loc)) { + ConstExprValue *val = ir_resolve_const(ira, new_result_loc, UndefOk); + if (!val) + return ira->codegen->invalid_instruction; + + copy_const_val(&result->value, val, false); + result->value.type = new_result_loc_type; + return result; + } + + return result; +} + static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -22931,7 +22984,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrorLiteral: return ir_analyze_instruction_error_literal(ira, (IrInstructionErrorLiteral *)instruction); case IrInstructionIdResultBytesToSlice: - zig_panic("TODO"); + return ir_analyze_instruction_result_bytes_to_slice(ira, (IrInstructionResultBytesToSlice *)instruction); case IrInstructionIdResultSliceToBytes: zig_panic("TODO"); } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 65a2c783ef7c..54e9931c9e9c 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1355,7 +1355,9 @@ static void ir_print_result_return(IrPrint *irp, IrInstructionResultReturn *inst } static void ir_print_result_bytes_to_slice(IrPrint *irp, IrInstructionResultBytesToSlice *instruction) { - fprintf(irp->f, "ResultBytesToSlice"); + fprintf(irp->f, "ResultBytesToSlice("); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); } static void ir_print_result_slice_to_bytes(IrPrint *irp, IrInstructionResultSliceToBytes *instruction) { From 8692cc99b8f7ec3e516c421c4f75d5cfa3e1768d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 18 Dec 2018 15:21:39 -0500 Subject: [PATCH 086/190] copy elision: @bytesToSlice with identifier parameter ```zig export fn entry() void { var z = []u8{ 0x12, 0x34 }; var y: []u8 = z[0..]; var x = @bytesToSlice(u16, y); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %z = alloca [2 x i8], align 1 %y = alloca %"[]u8", align 8 %x = alloca %"[]u16", align 8 %0 = bitcast [2 x i8]* %z to i8*, !dbg !59 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 getelementptr inbounds ([2 x i8], [2 x i8]* @0, i32 0, i32 0), i64 2, i1 false), !dbg !59 call void @llvm.dbg.declare(metadata [2 x i8]* %z, metadata !45, metadata !DIExpression()), !dbg !59 br i1 true, label %BoundsCheckOk, label %BoundsCheckFail, !dbg !60 BoundsCheckFail: ; preds = %Entry tail call fastcc void @panic(%"[]u8"* @3, %StackTrace* null), !dbg !60 unreachable, !dbg !60 BoundsCheckOk: ; preds = %Entry %1 = getelementptr inbounds %"[]u8", %"[]u8"* %y, i32 0, i32 0, !dbg !60 %2 = getelementptr inbounds [2 x i8], [2 x i8]* %z, i64 0, i64 0, !dbg !60 store i8* %2, i8** %1, align 8, !dbg !60 %3 = getelementptr inbounds %"[]u8", %"[]u8"* %y, i32 0, i32 1, !dbg !60 store i64 2, i64* %3, align 8, !dbg !60 call void @llvm.dbg.declare(metadata %"[]u8"* %y, metadata !50, metadata !DIExpression()), !dbg !61 %4 = bitcast %"[]u16"* %x to %"[]u8"*, !dbg !62 %5 = bitcast %"[]u8"* %y to i8*, !dbg !63 %6 = bitcast %"[]u8"* %4 to i8*, !dbg !63 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %6, i8* align 8 %5, i64 16, i1 false), !dbg !63 %7 = getelementptr inbounds %"[]u8", %"[]u8"* %4, i32 0, i32 1, !dbg !62 %8 = load i64, i64* %7, !dbg !62 %9 = urem i64 %8, 2, !dbg !62 %10 = icmp eq i64 %9, 0, !dbg !62 br i1 %10, label %SliceWidenOk, label %SliceWidenFail, !dbg !62 SliceWidenOk: ; preds = %BoundsCheckOk %11 = udiv exact i64 %8, 2, !dbg !62 store i64 %11, i64* %7, !dbg !62 call void @llvm.dbg.declare(metadata %"[]u16"* %x, metadata !52, metadata !DIExpression()), !dbg !64 ret void, !dbg !65 SliceWidenFail: ; preds = %BoundsCheckOk tail call fastcc void @panic(%"[]u8"* @6, %StackTrace* null), !dbg !62 unreachable, !dbg !62 } ``` --- src/all_types.hpp | 47 +++++++++- src/codegen.cpp | 138 +++++++++-------------------- src/ir.cpp | 221 ++++++++++++++++++++++++++++++++++++++++++---- src/ir_print.cpp | 42 ++++++++- 4 files changed, 328 insertions(+), 120 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 565d234a4b1e..f671a330866f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -628,8 +628,6 @@ enum CastOp { CastOpIntToFloat, CastOpFloatToInt, CastOpBoolToInt, - CastOpResizeSlice, - CastOpBytesToSlice, CastOpNumLitToConcrete, CastOpErrSet, CastOpBitCast, @@ -2069,6 +2067,15 @@ enum LVal { LValOptional, }; +// These instructions are in transition to having "pass 1" instructions +// and "pass 2" instructions. The pass 1 instructions are suffixed with Src +// and pass 2 are suffixed with Gen. +// Once all instructions are separated in this way, they'll have different +// base types for better type safety. +// Src instructions are generated by ir_gen_* functions in ir.cpp from AST. +// ir_analyze_* functions consume Src instructions and produce Gen instructions. +// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR. +// Src instructions do not have type information; Gen instructions do. enum IrInstructionId { IrInstructionIdInvalid, IrInstructionIdDeclVarSrc, @@ -2215,7 +2222,9 @@ enum IrInstructionId { IrInstructionIdResultSlicePtr, IrInstructionIdResultReturn, IrInstructionIdResultBytesToSlice, - IrInstructionIdResultSliceToBytes, + IrInstructionIdResultSliceToBytesSrc, + IrInstructionIdResultSliceToBytesPlaceholder, + IrInstructionIdResultSliceToBytesGen, IrInstructionIdResultPtrCast, IrInstructionIdResultCast, IrInstructionIdAllocaSrc, @@ -2226,6 +2235,8 @@ enum IrInstructionId { IrInstructionIdFirstArgResultLoc, IrInstructionIdInferArrayType, IrInstructionIdInferCompTime, + IrInstructionIdFromBytesLenSrc, + IrInstructionIdFromBytesLenGen, IrInstructionIdSetNonNullBit, IrInstructionIdErrorLiteral, }; @@ -3354,13 +3365,27 @@ struct IrInstructionResultBytesToSlice { IrInstruction *prev_result_loc; }; -struct IrInstructionResultSliceToBytes { +struct IrInstructionResultSliceToBytesSrc { IrInstruction base; IrInstruction *elem_type; IrInstruction *prev_result_loc; }; +struct IrInstructionResultSliceToBytesPlaceholder { + IrInstruction base; + + ZigType *elem_type; + IrInstruction *prev_result_loc; + IrInstruction *pass1_parent; +}; + +struct IrInstructionResultSliceToBytesGen { + IrInstruction base; + + IrInstruction *prev_result_loc; +}; + struct IrInstructionResultSlicePtr { IrInstruction base; @@ -3451,6 +3476,20 @@ struct IrInstructionErrorLiteral { Buf *name; }; +struct IrInstructionFromBytesLenSrc { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *new_result_loc; +}; + +struct IrInstructionFromBytesLenGen { + IrInstruction base; + + ZigType *elem_type; + IrInstruction *new_result_loc; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index c6448d5eb5d0..9c9748907ce6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2845,97 +2845,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, zig_unreachable(); case CastOpNoop: return expr_val; - case CastOpResizeSlice: - { - assert(wanted_type->id == ZigTypeIdStruct); - assert(wanted_type->data.structure.is_slice); - assert(actual_type->id == ZigTypeIdStruct); - assert(actual_type->data.structure.is_slice); - - LLVMValueRef result_ptr = ir_llvm_value(g, cast_instruction->result_loc); - - ZigType *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; - ZigType *actual_child_type = actual_pointer_type->data.pointer.child_type; - ZigType *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; - ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; - - - size_t actual_ptr_index = actual_type->data.structure.fields[slice_ptr_index].gen_index; - size_t actual_len_index = actual_type->data.structure.fields[slice_len_index].gen_index; - size_t wanted_ptr_index = wanted_type->data.structure.fields[slice_ptr_index].gen_index; - size_t wanted_len_index = wanted_type->data.structure.fields[slice_len_index].gen_index; - - LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_ptr_index, ""); - LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, - wanted_type->data.structure.fields[0].type_entry->type_ref, ""); - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, result_ptr, - (unsigned)wanted_ptr_index, ""); - gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); - - LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_len_index, ""); - LLVMValueRef src_len = gen_load_untyped(g, src_len_ptr, 0, false, ""); - uint64_t src_size = type_size(g, actual_child_type); - uint64_t dest_size = type_size(g, wanted_child_type); - - LLVMValueRef new_len; - if (dest_size == 1) { - LLVMValueRef src_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, src_size, false); - new_len = LLVMBuildMul(g->builder, src_len, src_size_val, ""); - } else if (src_size == 1) { - LLVMValueRef dest_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, dest_size, false); - if (ir_want_runtime_safety(g, &cast_instruction->base)) { - LLVMValueRef remainder_val = LLVMBuildURem(g->builder, src_len, dest_size_val, ""); - LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenFail"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdSliceWidenRemainder); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - new_len = LLVMBuildExactUDiv(g->builder, src_len, dest_size_val, ""); - } else { - zig_unreachable(); - } - - LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, result_ptr, - (unsigned)wanted_len_index, ""); - gen_store_untyped(g, new_len, dest_len_ptr, 0, false); - - - return result_ptr; - } - case CastOpBytesToSlice: - { - assert(wanted_type->id == ZigTypeIdStruct); - assert(wanted_type->data.structure.is_slice); - assert(actual_type->id == ZigTypeIdArray); - - LLVMValueRef result_ptr = ir_llvm_value(g, cast_instruction->result_loc); - - ZigType *wanted_pointer_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; - - - size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index; - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, result_ptr, - (unsigned)wanted_ptr_index, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, expr_val, wanted_pointer_type->type_ref, ""); - gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); - - size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index; - LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, result_ptr, - (unsigned)wanted_len_index, ""); - LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, - actual_type->data.array.len / type_size(g, wanted_child_type), false); - gen_store_untyped(g, len_val, len_ptr, 0, false); - - return result_ptr; - } case CastOpIntToFloat: assert(actual_type->id == ZigTypeIdInt); if (actual_type->data.integral.is_signed) { @@ -5229,6 +5138,41 @@ static LLVMValueRef ir_render_set_non_null_bit(CodeGen *g, IrExecutable *executa return nullptr; } +static LLVMValueRef ir_render_result_slice_to_bytes(CodeGen *g, IrExecutable *executable, + IrInstructionResultSliceToBytesGen *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef new_result_loc = LLVMBuildBitCast(g->builder, prev_result_loc, + instruction->base.value.type->type_ref, ""); + return new_result_loc; +} + +static LLVMValueRef ir_render_from_bytes_len(CodeGen *g, IrExecutable *executable, + IrInstructionFromBytesLenGen *instruction) +{ + LLVMValueRef new_result_loc = ir_llvm_value(g, instruction->new_result_loc); + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, new_result_loc, slice_len_index, ""); + LLVMValueRef prev_len = LLVMBuildLoad(g->builder, len_ptr, ""); + uint64_t elem_size = type_size(g, instruction->elem_type); + LLVMValueRef elem_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, elem_size, false); + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef remainder_val = LLVMBuildURem(g->builder, prev_len, elem_size_val, ""); + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenOk"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdSliceWidenRemainder); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + LLVMValueRef new_len = LLVMBuildExactUDiv(g->builder, prev_len, elem_size_val, ""); + LLVMBuildStore(g->builder, new_len, len_ptr); + return nullptr; +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5314,6 +5258,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdResultPtrCast: case IrInstructionIdCmpxchgSrc: case IrInstructionIdErrorLiteral: + case IrInstructionIdResultSliceToBytesSrc: + case IrInstructionIdResultSliceToBytesPlaceholder: + case IrInstructionIdResultBytesToSlice: + case IrInstructionIdFromBytesLenSrc: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5488,10 +5436,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_assert_non_null(g, executable, (IrInstructionAssertNonNull *)instruction); case IrInstructionIdSetNonNullBit: return ir_render_set_non_null_bit(g, executable, (IrInstructionSetNonNullBit *)instruction); - case IrInstructionIdResultSliceToBytes: - zig_panic("TODO"); - case IrInstructionIdResultBytesToSlice: - zig_panic("TODO"); + case IrInstructionIdResultSliceToBytesGen: + return ir_render_result_slice_to_bytes(g, executable, (IrInstructionResultSliceToBytesGen *)instruction); + case IrInstructionIdFromBytesLenGen: + return ir_render_from_bytes_len(g, executable, (IrInstructionFromBytesLenGen *)instruction); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index fe85489e65ba..f5abcba35513 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -175,6 +175,8 @@ static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstructio static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on); static ZigType *adjust_ptr_child(CodeGen *g, ZigType *ptr_type, ZigType *new_child); +static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, + IrInstruction *source_inst, IrInstruction *prev_result_loc, ZigType *elem_type, ZigType *child_type); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -906,8 +908,16 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionResultCast *) { return IrInstructionIdResultCast; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSliceToBytes *) { - return IrInstructionIdResultSliceToBytes; +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSliceToBytesSrc *) { + return IrInstructionIdResultSliceToBytesSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSliceToBytesPlaceholder *) { + return IrInstructionIdResultSliceToBytesPlaceholder; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSliceToBytesGen *) { + return IrInstructionIdResultSliceToBytesGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionResultBytesToSlice *) { @@ -946,6 +956,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionInferCompTime *) return IrInstructionIdInferCompTime; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytesLenSrc *) { + return IrInstructionIdFromBytesLenSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytesLenGen *) { + return IrInstructionIdFromBytesLenGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionSetNonNullBit *) { return IrInstructionIdSetNonNullBit; } @@ -2899,10 +2917,10 @@ static IrInstruction *ir_build_result_cast(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_result_slice_to_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_result_slice_to_bytes_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *elem_type, IrInstruction *prev_result_loc) { - IrInstructionResultSliceToBytes *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionResultSliceToBytesSrc *instruction = ir_build_instruction(irb, scope, source_node); instruction->elem_type = elem_type; instruction->prev_result_loc = prev_result_loc; @@ -2912,6 +2930,36 @@ static IrInstruction *ir_build_result_slice_to_bytes(IrBuilder *irb, Scope *scop return &instruction->base; } +static IrInstruction *ir_build_result_slice_to_bytes_placeholder(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *prev_result_loc, ZigType *elem_type) +{ + IrInstructionResultSliceToBytesPlaceholder *instruction = + ir_build_instruction( + &ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->base.value.type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_infer, false); + instruction->prev_result_loc = prev_result_loc; + instruction->elem_type = elem_type; + instruction->pass1_parent = source_instr; + + ir_ref_instruction(prev_result_loc, ira->new_irb.current_basic_block); + // no ref for pass1_parent + + return &instruction->base; +} + +static IrInstruction *ir_build_result_slice_to_bytes_gen(IrAnalyze *ira, IrInstruction *source_instr, + ZigType *result_type, IrInstruction *prev_result_loc) +{ + IrInstructionResultSliceToBytesGen *instruction = ir_build_instruction( + &ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->base.value.type = result_type; + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(prev_result_loc, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_result_bytes_to_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *prev_result_loc) { @@ -3019,6 +3067,35 @@ static IrInstruction *ir_build_infer_comptime(IrBuilder *irb, Scope *scope, AstN return &instruction->base; } +static IrInstruction *ir_build_from_bytes_len_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *new_result_loc) +{ + IrInstructionFromBytesLenSrc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->new_result_loc = new_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + ir_ref_instruction(new_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_from_bytes_len_gen(IrAnalyze *ira, IrInstruction *source_inst, + IrInstruction *new_result_loc, ZigType *elem_type) +{ + IrInstructionFromBytesLenGen *instruction = ir_build_instruction( + &ira->new_irb, source_inst->scope, source_inst->source_node); + instruction->base.value.special = ConstValSpecialStatic; + instruction->base.value.type = ira->codegen->builtin_types.entry_void; + + instruction->new_result_loc = new_result_loc; + instruction->elem_type = elem_type; + + ir_ref_instruction(new_result_loc, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_set_nonnull_bit(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *prev_result_loc, IrInstruction *non_null_bit, IrInstruction *new_result_loc) { @@ -4571,7 +4648,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); - IrInstruction *new_result_loc = ir_build_result_slice_to_bytes(irb, scope, node, arg0_value, + IrInstruction *new_result_loc = ir_build_result_slice_to_bytes_src(irb, scope, node, arg0_value, ensured_result_loc); AstNode *arg1_node = node->data.fn_call_expr.params.at(1); @@ -4579,6 +4656,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; + ir_build_from_bytes_len_src(irb, scope, node, ensured_result_loc, new_result_loc); + return ir_gen_result(irb, scope, node, lval, ensured_result_loc); } case BuiltinFnIdToBytes: @@ -9844,10 +9923,6 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ } const_val->type = new_type; break; - case CastOpResizeSlice: - case CastOpBytesToSlice: - // can't do it - zig_unreachable(); case CastOpIntToFloat: { assert(new_type->id == ZigTypeIdFloat); @@ -9917,9 +9992,7 @@ static IrInstruction *ir_const_undef(IrAnalyze *ira, IrInstruction *source_instr static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op, bool need_alloca) { - if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && - cast_op != CastOpResizeSlice && cast_op != CastOpBytesToSlice) - { + if (instr_is_comptime(value) || !type_has_bits(wanted_type)) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, &result->value, wanted_type)) @@ -11735,6 +11808,19 @@ static IrInstruction *resolve_possible_alloca_inference(IrAnalyze *ira, IrInstru Error err; assert(base->value.type->id == ZigTypeIdPointer); ZigType *infer_child = base->value.type->data.pointer.child_type; + if (base->id == IrInstructionIdResultSliceToBytesPlaceholder) { + IrInstructionResultSliceToBytesPlaceholder *res_slice_to_bytes = + reinterpret_cast(base); + // When the instruction was made it didn't have enough information to do analysis, + // so it emitted a placeholder. Now we have the information, so we do + // the analysis, and rely on the ref_count being 0 so that it is not emitted. + assert(res_slice_to_bytes->base.ref_count == 0); + assert(res_slice_to_bytes->pass1_parent != nullptr); + IrInstruction *new_res_slice_to_bytes = ir_analyze_result_slice_to_bytes(ira, base, + res_slice_to_bytes->prev_result_loc, res_slice_to_bytes->elem_type, child_type); + res_slice_to_bytes->pass1_parent->child = new_res_slice_to_bytes; + return new_res_slice_to_bytes; + } if (infer_child == ira->codegen->builtin_types.entry_infer) { if (base->id == IrInstructionIdAllocaGen) { if ((err = resolve_alloca_inference(ira, reinterpret_cast(base), child_type))) @@ -22596,6 +22682,38 @@ static IrInstruction *ir_analyze_instruction_infer_comptime(IrAnalyze *ira, return ir_const_void(ira, &instruction->base); } +static IrInstruction *ir_analyze_instruction_from_bytes_len(IrAnalyze *ira, + IrInstructionFromBytesLenSrc *instruction) +{ + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = instruction->new_result_loc->child; + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + BREAKPOINT; + + if (prev_result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(new_result_loc) && + new_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } + prev_result_loc->value.special = ConstValSpecialRuntime; + } + + assert(prev_result_loc->value.type->id == ZigTypeIdPointer); + ZigType *slice_type = prev_result_loc->value.type->data.pointer.child_type; + assert(is_slice(slice_type)); + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *elem_type = slice_ptr_type->data.pointer.child_type; + + return ir_build_from_bytes_len_gen(ira, &instruction->base, new_result_loc, elem_type); +} + static IrInstruction *ir_analyze_instruction_set_non_null_bit(IrAnalyze *ira, IrInstructionSetNonNullBit *instruction) { @@ -22654,8 +22772,7 @@ static IrInstruction *ir_analyze_instruction_result_bytes_to_slice(IrAnalyze *ir true, false, PtrLenUnknown, 0, 0, 0); ZigType *slice_of_bytes = get_slice_type(ira->codegen, slice_ptr_type); IrInstruction *prev_result_loc = instruction->prev_result_loc->child; - IrInstruction *new_result_loc = ir_implicit_cast_result(ira, prev_result_loc, - slice_of_bytes, false); + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, prev_result_loc, slice_of_bytes, false); if (type_is_invalid(new_result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -22669,6 +22786,8 @@ static IrInstruction *ir_analyze_instruction_result_bytes_to_slice(IrAnalyze *ir IrInstruction *result = ir_build_ptr_cast_gen(ira, &instruction->base, new_result_loc_type, new_result_loc); reinterpret_cast(result)->pass1_parent = &instruction->base; + // At compile time pointer casts are represented as the pointer type disagreeing + // with the element type, so that's easy to do here. if (instr_is_comptime(new_result_loc)) { ConstExprValue *val = ir_resolve_const(ira, new_result_loc, UndefOk); if (!val) @@ -22682,6 +22801,65 @@ static IrInstruction *ir_analyze_instruction_result_bytes_to_slice(IrAnalyze *ir return result; } +// This function is called once we have enough information to do the analysis. +static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, + IrInstruction *source_inst, IrInstruction *prev_result_loc, ZigType *elem_type, ZigType *bytes_slice_type) +{ + if (!is_slice(bytes_slice_type)) { + ir_add_error(ira, source_inst, + buf_sprintf("expected slice, found '%s'", buf_ptr(&bytes_slice_type->name))); + return ira->codegen->invalid_instruction; + } + + ZigType *bytes_ptr_type = bytes_slice_type->data.structure.fields[slice_ptr_index].type_entry; + if (bytes_ptr_type->data.pointer.child_type != ira->codegen->builtin_types.entry_u8) { + ir_add_error(ira, source_inst, + buf_sprintf("expected slice of u8, found '%s'", buf_ptr(&bytes_slice_type->name))); + return ira->codegen->invalid_instruction; + } + + ZigType *elem_ptr_type = adjust_ptr_child(ira->codegen, bytes_ptr_type, elem_type); + ZigType *slice_of_elem = get_slice_type(ira->codegen, elem_ptr_type); + IrInstruction *casted_prev_result_loc = ir_implicit_cast_result(ira, prev_result_loc, slice_of_elem, false); + if (type_is_invalid(casted_prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + ZigType *new_result_loc_type = adjust_ptr_child(ira->codegen, casted_prev_result_loc->value.type, + bytes_slice_type); + IrInstruction *result = ir_build_result_slice_to_bytes_gen(ira, source_inst, new_result_loc_type, + casted_prev_result_loc); + + if (instr_is_comptime(casted_prev_result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, casted_prev_result_loc, UndefOk); + if (!ptr_val) + return ira->codegen->invalid_instruction; + + copy_const_val(&result->value, ptr_val, false); + result->value.type = new_result_loc_type; + return result; + } + + return result; +} + +static IrInstruction *ir_analyze_instruction_result_slice_to_bytes(IrAnalyze *ira, + IrInstructionResultSliceToBytesSrc *instruction) +{ + ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + // We don't have enough type information to do anything. So we emit a placeholder instruction + // and then will fix things later when resolve_alloca_inference is called. + // This works because this is a result location instruction and so the first reference to it + // will be from resolve_alloca_inference. + return ir_build_result_slice_to_bytes_placeholder(ira, &instruction->base, prev_result_loc, elem_type); +} + static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -22699,6 +22877,9 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdResultErrorUnionCode: case IrInstructionIdPtrCastGen: case IrInstructionIdCmpxchgGen: + case IrInstructionIdResultSliceToBytesPlaceholder: + case IrInstructionIdResultSliceToBytesGen: + case IrInstructionIdFromBytesLenGen: zig_unreachable(); case IrInstructionIdReturn: @@ -22977,6 +23158,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_infer_array_type(ira, (IrInstructionInferArrayType *)instruction); case IrInstructionIdInferCompTime: return ir_analyze_instruction_infer_comptime(ira, (IrInstructionInferCompTime *)instruction); + case IrInstructionIdFromBytesLenSrc: + return ir_analyze_instruction_from_bytes_len(ira, (IrInstructionFromBytesLenSrc *)instruction); case IrInstructionIdResultOptionalPayload: return ir_analyze_instruction_result_optional_payload(ira, (IrInstructionResultOptionalPayload *)instruction); case IrInstructionIdSetNonNullBit: @@ -22985,8 +23168,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_error_literal(ira, (IrInstructionErrorLiteral *)instruction); case IrInstructionIdResultBytesToSlice: return ir_analyze_instruction_result_bytes_to_slice(ira, (IrInstructionResultBytesToSlice *)instruction); - case IrInstructionIdResultSliceToBytes: - zig_panic("TODO"); + case IrInstructionIdResultSliceToBytesSrc: + return ir_analyze_instruction_result_slice_to_bytes(ira, (IrInstructionResultSliceToBytesSrc *)instruction); } zig_unreachable(); } @@ -23137,6 +23320,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdContainerInitFields: case IrInstructionIdContainerInitList: case IrInstructionIdInferCompTime: + case IrInstructionIdFromBytesLenSrc: + case IrInstructionIdFromBytesLenGen: case IrInstructionIdSetNonNullBit: case IrInstructionIdSlice: case IrInstructionIdCmpxchgGen: @@ -23234,7 +23419,9 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdResultReturn: case IrInstructionIdResultPtrCast: case IrInstructionIdResultCast: - case IrInstructionIdResultSliceToBytes: + case IrInstructionIdResultSliceToBytesSrc: + case IrInstructionIdResultSliceToBytesPlaceholder: + case IrInstructionIdResultSliceToBytesGen: case IrInstructionIdResultBytesToSlice: case IrInstructionIdAllocaSrc: case IrInstructionIdAllocaGen: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 54e9931c9e9c..0e8181fa5e46 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1360,8 +1360,16 @@ static void ir_print_result_bytes_to_slice(IrPrint *irp, IrInstructionResultByte fprintf(irp->f, ")"); } -static void ir_print_result_slice_to_bytes(IrPrint *irp, IrInstructionResultSliceToBytes *instruction) { - fprintf(irp->f, "ResultSliceToBytes"); +static void ir_print_result_slice_to_bytes_src(IrPrint *irp, IrInstructionResultSliceToBytesSrc *instruction) { + fprintf(irp->f, "ResultSliceToBytesSrc"); +} + +static void ir_print_result_slice_to_bytes_placeholder(IrPrint *irp, IrInstructionResultSliceToBytesPlaceholder *instruction) { + fprintf(irp->f, "ResultSliceToBytesPlaceholder"); +} + +static void ir_print_result_slice_to_bytes_gen(IrPrint *irp, IrInstructionResultSliceToBytesGen *instruction) { + fprintf(irp->f, "ResultSliceToBytesGen"); } static void ir_print_result_ptr_cast(IrPrint *irp, IrInstructionResultPtrCast *instruction) { @@ -1432,6 +1440,20 @@ static void ir_print_infer_comptime(IrPrint *irp, IrInstructionInferCompTime *in fprintf(irp->f, ")"); } +static void ir_print_from_bytes_len_src(IrPrint *irp, IrInstructionFromBytesLenSrc *instruction) { + fprintf(irp->f, "FromBytesLenSrc(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",new_result="); + ir_print_other_instruction(irp, instruction->new_result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_from_bytes_len_gen(IrPrint *irp, IrInstructionFromBytesLenGen *instruction) { + fprintf(irp->f, "FromBytesLenGen(new_result="); + ir_print_other_instruction(irp, instruction->new_result_loc); + fprintf(irp->f, ",elem_type=%s)", buf_ptr(&instruction->elem_type->name)); +} + static void ir_print_set_non_null_bit(IrPrint *irp, IrInstructionSetNonNullBit *instruction) { fprintf(irp->f, "SetNonNullBit(prev_result="); ir_print_other_instruction(irp, instruction->prev_result_loc); @@ -1883,8 +1905,14 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdResultBytesToSlice: ir_print_result_bytes_to_slice(irp, (IrInstructionResultBytesToSlice *)instruction); break; - case IrInstructionIdResultSliceToBytes: - ir_print_result_slice_to_bytes(irp, (IrInstructionResultSliceToBytes *)instruction); + case IrInstructionIdResultSliceToBytesSrc: + ir_print_result_slice_to_bytes_src(irp, (IrInstructionResultSliceToBytesSrc *)instruction); + break; + case IrInstructionIdResultSliceToBytesPlaceholder: + ir_print_result_slice_to_bytes_placeholder(irp, (IrInstructionResultSliceToBytesPlaceholder *)instruction); + break; + case IrInstructionIdResultSliceToBytesGen: + ir_print_result_slice_to_bytes_gen(irp, (IrInstructionResultSliceToBytesGen *)instruction); break; case IrInstructionIdResultPtrCast: ir_print_result_ptr_cast(irp, (IrInstructionResultPtrCast *)instruction); @@ -1916,6 +1944,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdInferCompTime: ir_print_infer_comptime(irp, (IrInstructionInferCompTime *)instruction); break; + case IrInstructionIdFromBytesLenSrc: + ir_print_from_bytes_len_src(irp, (IrInstructionFromBytesLenSrc *)instruction); + break; + case IrInstructionIdFromBytesLenGen: + ir_print_from_bytes_len_gen(irp, (IrInstructionFromBytesLenGen *)instruction); + break; case IrInstructionIdSetNonNullBit: ir_print_set_non_null_bit(irp, (IrInstructionSetNonNullBit *)instruction); break; From 29b90ac847e0ccbb841e42462c9d15cec6a01aaf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 18 Dec 2018 16:42:30 -0500 Subject: [PATCH 087/190] copy elision: @bytesToSlice with fn call ```zig export fn entry() void { var z = []u8{ 0x12, 0x34 }; var y: []u8 = z[0..]; var x = @bytesToSlice(u16, slice()); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %z = alloca [2 x i8], align 1 %y = alloca %"[]u8", align 8 %x = alloca %"[]u16", align 8 %0 = bitcast [2 x i8]* %z to i8*, !dbg !59 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 getelementptr inbounds ([2 x i8], [2 x i8]* @0, i32 0, i32 0), i64 2, i1 false), !dbg !59 call void @llvm.dbg.declare(metadata [2 x i8]* %z, metadata !45, metadata !DIExpression()), !dbg !59 br i1 true, label %BoundsCheckOk, label %BoundsCheckFail, !dbg !60 BoundsCheckFail: ; preds = %Entry tail call fastcc void @panic(%"[]u8"* @3, %StackTrace* null), !dbg !60 unreachable, !dbg !60 BoundsCheckOk: ; preds = %Entry %1 = getelementptr inbounds %"[]u8", %"[]u8"* %y, i32 0, i32 0, !dbg !60 %2 = getelementptr inbounds [2 x i8], [2 x i8]* %z, i64 0, i64 0, !dbg !60 store i8* %2, i8** %1, align 8, !dbg !60 %3 = getelementptr inbounds %"[]u8", %"[]u8"* %y, i32 0, i32 1, !dbg !60 store i64 2, i64* %3, align 8, !dbg !60 call void @llvm.dbg.declare(metadata %"[]u8"* %y, metadata !50, metadata !DIExpression()), !dbg !61 %4 = bitcast %"[]u16"* %x to %"[]u8"*, !dbg !62 call fastcc void @slice(%"[]u8"* sret %4), !dbg !63 %5 = getelementptr inbounds %"[]u16", %"[]u16"* %x, i32 0, i32 1, !dbg !62 %6 = load i64, i64* %5, !dbg !62 %7 = urem i64 %6, 2, !dbg !62 %8 = icmp eq i64 %7, 0, !dbg !62 br i1 %8, label %SliceWidenOk, label %SliceWidenFail, !dbg !62 SliceWidenFail: ; preds = %BoundsCheckOk tail call fastcc void @panic(%"[]u8"* @6, %StackTrace* null), !dbg !62 unreachable, !dbg !62 SliceWidenOk: ; preds = %BoundsCheckOk %9 = udiv exact i64 %6, 2, !dbg !62 store i64 %9, i64* %5, !dbg !62 call void @llvm.dbg.declare(metadata %"[]u16"* %x, metadata !52, metadata !DIExpression()), !dbg !64 ret void, !dbg !65 } ``` --- src/all_types.hpp | 2 +- src/codegen.cpp | 4 +-- src/ir.cpp | 66 +++++++++++++++++++++++++++++++++++++---------- src/ir_print.cpp | 4 +-- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index f671a330866f..f80df516ef36 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3487,7 +3487,7 @@ struct IrInstructionFromBytesLenGen { IrInstruction base; ZigType *elem_type; - IrInstruction *new_result_loc; + IrInstruction *prev_result_loc; }; static const size_t slice_ptr_index = 0; diff --git a/src/codegen.cpp b/src/codegen.cpp index 9c9748907ce6..dd1976b20197 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5150,8 +5150,8 @@ static LLVMValueRef ir_render_result_slice_to_bytes(CodeGen *g, IrExecutable *ex static LLVMValueRef ir_render_from_bytes_len(CodeGen *g, IrExecutable *executable, IrInstructionFromBytesLenGen *instruction) { - LLVMValueRef new_result_loc = ir_llvm_value(g, instruction->new_result_loc); - LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, new_result_loc, slice_len_index, ""); + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, slice_len_index, ""); LLVMValueRef prev_len = LLVMBuildLoad(g->builder, len_ptr, ""); uint64_t elem_size = type_size(g, instruction->elem_type); LLVMValueRef elem_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, elem_size, false); diff --git a/src/ir.cpp b/src/ir.cpp index f5abcba35513..5e122524c693 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3081,17 +3081,17 @@ static IrInstruction *ir_build_from_bytes_len_src(IrBuilder *irb, Scope *scope, } static IrInstruction *ir_build_from_bytes_len_gen(IrAnalyze *ira, IrInstruction *source_inst, - IrInstruction *new_result_loc, ZigType *elem_type) + IrInstruction *prev_result_loc, ZigType *elem_type) { IrInstructionFromBytesLenGen *instruction = ir_build_instruction( &ira->new_irb, source_inst->scope, source_inst->source_node); instruction->base.value.special = ConstValSpecialStatic; instruction->base.value.type = ira->codegen->builtin_types.entry_void; - instruction->new_result_loc = new_result_loc; + instruction->prev_result_loc = prev_result_loc; instruction->elem_type = elem_type; - ir_ref_instruction(new_result_loc, ira->new_irb.current_basic_block); + ir_ref_instruction(prev_result_loc, ira->new_irb.current_basic_block); return &instruction->base; } @@ -22685,6 +22685,8 @@ static IrInstruction *ir_analyze_instruction_infer_comptime(IrAnalyze *ira, static IrInstruction *ir_analyze_instruction_from_bytes_len(IrAnalyze *ira, IrInstructionFromBytesLenSrc *instruction) { + Error err; + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; if (type_is_invalid(prev_result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -22693,25 +22695,63 @@ static IrInstruction *ir_analyze_instruction_from_bytes_len(IrAnalyze *ira, if (type_is_invalid(new_result_loc->value.type)) return ira->codegen->invalid_instruction; - BREAKPOINT; + assert(prev_result_loc->value.type->id == ZigTypeIdPointer); + ZigType *slice_type = prev_result_loc->value.type->data.pointer.child_type; + assert(is_slice(slice_type)); + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *elem_type = slice_ptr_type->data.pointer.child_type; if (prev_result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { if (instr_is_comptime(new_result_loc) && new_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { - prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + ConstExprValue *slice_val = const_ptr_pointee(ira->codegen, &new_result_loc->value); + if (slice_val == nullptr) + return ira->codegen->invalid_instruction; + + if (slice_val->special != ConstValSpecialUndef) { + ConstExprValue *ptr_val = &slice_val->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *len_val = &slice_val->data.x_struct.fields[slice_len_index]; + if (ptr_val->special == ConstValSpecialStatic && len_val->special == ConstValSpecialStatic) { + // copy the length and divide it from the new_result_loc + // we need a new const val though, because if we modify the length it will + // modify the length of the prev_result_loc + slice_val->data.x_struct.fields = create_const_vals(2); + copy_const_val(&slice_val->data.x_struct.fields[slice_ptr_index], ptr_val, false); + + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; + + size_t elem_size = type_size(ira->codegen, elem_type); + BigInt elem_size_bigint; + bigint_init_unsigned(&elem_size_bigint, elem_size); + + BigInt tmp_bigint; + bigint_rem(&tmp_bigint, &len_val->data.x_bigint, &elem_size_bigint); + + if (bigint_cmp_zero(&tmp_bigint) != CmpEQ) { + // TODO improve err msg + ir_add_error(ira, &instruction->base, + buf_sprintf("slice widen remainder")); + return ira->codegen->invalid_instruction; + } + + ConstExprValue *new_len_val = &slice_val->data.x_struct.fields[slice_len_index]; + new_len_val->special = ConstValSpecialStatic; + bigint_div_trunc(&new_len_val->data.x_bigint, &len_val->data.x_bigint, &elem_size_bigint); + + prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + return ir_const_void(ira, &instruction->base); + } + } } + // comptime didn't work, mark the instructions as runtime and fall through prev_result_loc->value.special = ConstValSpecialRuntime; + new_result_loc->value.special = ConstValSpecialRuntime; } - assert(prev_result_loc->value.type->id == ZigTypeIdPointer); - ZigType *slice_type = prev_result_loc->value.type->data.pointer.child_type; - assert(is_slice(slice_type)); - ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; - assert(slice_ptr_type->id == ZigTypeIdPointer); - ZigType *elem_type = slice_ptr_type->data.pointer.child_type; - - return ir_build_from_bytes_len_gen(ira, &instruction->base, new_result_loc, elem_type); + return ir_build_from_bytes_len_gen(ira, &instruction->base, prev_result_loc, elem_type); } static IrInstruction *ir_analyze_instruction_set_non_null_bit(IrAnalyze *ira, diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 0e8181fa5e46..ea7e558f998c 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1449,8 +1449,8 @@ static void ir_print_from_bytes_len_src(IrPrint *irp, IrInstructionFromBytesLenS } static void ir_print_from_bytes_len_gen(IrPrint *irp, IrInstructionFromBytesLenGen *instruction) { - fprintf(irp->f, "FromBytesLenGen(new_result="); - ir_print_other_instruction(irp, instruction->new_result_loc); + fprintf(irp->f, "FromBytesLenGen(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); fprintf(irp->f, ",elem_type=%s)", buf_ptr(&instruction->elem_type->name)); } From 95473b96dda88f1e0763f56d700f749e6b7ce826 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 18 Dec 2018 17:06:59 -0500 Subject: [PATCH 088/190] copy elision: @bytesToSlice with consts example 1: ```zig export fn entry() void { const z = []u8{ 0x12, 0x34 }; const y: []u8 = z[0..]; const x = @bytesToSlice(u16, y); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: call void @llvm.dbg.declare(metadata [2 x i8]* @1, metadata !45, metadata !DIExpression()), !dbg !59 call void @llvm.dbg.declare(metadata %"[]u8"* @4, metadata !50, metadata !DIExpression()), !dbg !60 call void @llvm.dbg.declare(metadata %"[]u16"* bitcast (%"[]u8"* @6 to %"[]u16"*), metadata !52, metadata !DIExpression()), !dbg !61 ret void, !dbg !62 } ``` example 2: ```zig export fn entry() void { const z = []u8{ 0x12, 0x34 }; const y: []u8 = z[0..]; var x = @bytesToSlice(u16, y); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca %"[]u16", align 8 call void @llvm.dbg.declare(metadata [2 x i8]* @1, metadata !45, metadata !DIExpression()), !dbg !59 call void @llvm.dbg.declare(metadata %"[]u8"* @4, metadata !50, metadata !DIExpression()), !dbg !60 %0 = bitcast %"[]u16"* %x to i8*, !dbg !61 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%"[]u16"* @5 to i8*), i64 16, i1 false), !dbg !61 call void @llvm.dbg.declare(metadata %"[]u16"* %x, metadata !52, metadata !DIExpression()), !dbg !61 ret void, !dbg !62 } ``` --- src/ir.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 5e122524c693..dc28f307ec03 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -22715,10 +22715,12 @@ static IrInstruction *ir_analyze_instruction_from_bytes_len(IrAnalyze *ira, ConstExprValue *len_val = &slice_val->data.x_struct.fields[slice_len_index]; if (ptr_val->special == ConstValSpecialStatic && len_val->special == ConstValSpecialStatic) { // copy the length and divide it from the new_result_loc - // we need a new const val though, because if we modify the length it will - // modify the length of the prev_result_loc - slice_val->data.x_struct.fields = create_const_vals(2); - copy_const_val(&slice_val->data.x_struct.fields[slice_ptr_index], ptr_val, false); + // we need a new slice const val though, because if we modify the length it will + // modify the length of the new_result_loc + ConstExprValue *new_slice_val = create_const_vals(1); + copy_const_val(new_slice_val, slice_val, false); + prev_result_loc->value.data.x_ptr.special = ConstPtrSpecialRef; + prev_result_loc->value.data.x_ptr.data.ref.pointee = new_slice_val; if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; @@ -22737,11 +22739,12 @@ static IrInstruction *ir_analyze_instruction_from_bytes_len(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - ConstExprValue *new_len_val = &slice_val->data.x_struct.fields[slice_len_index]; + ConstExprValue *new_len_val = &new_slice_val->data.x_struct.fields[slice_len_index]; new_len_val->special = ConstValSpecialStatic; bigint_div_trunc(&new_len_val->data.x_bigint, &len_val->data.x_bigint, &elem_size_bigint); prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + return ir_const_void(ira, &instruction->base); } } From 66f5643f46ccad46f6aeef8288f42203f58dd233 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 18 Dec 2018 17:17:53 -0500 Subject: [PATCH 089/190] @bytesToSlice: better compile error message for slice widen remainder --- src/ir.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index dc28f307ec03..c365c4320852 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -22733,9 +22733,16 @@ static IrInstruction *ir_analyze_instruction_from_bytes_len(IrAnalyze *ira, bigint_rem(&tmp_bigint, &len_val->data.x_bigint, &elem_size_bigint); if (bigint_cmp_zero(&tmp_bigint) != CmpEQ) { - // TODO improve err msg - ir_add_error(ira, &instruction->base, - buf_sprintf("slice widen remainder")); + Buf *remaining_text = buf_alloc(); + bigint_append_buf(remaining_text, &tmp_bigint, 10); + Buf *bytes_text = buf_alloc(); + bigint_append_buf(bytes_text, &len_val->data.x_bigint, 10); + ir_add_error(ira, &instruction->base, buf_sprintf( + "converting slice of %s bytes to '%s' (element size %zu) leaves %s bytes remaining", + buf_ptr(bytes_text), + buf_ptr(&slice_type->name), + elem_size, + buf_ptr(remaining_text))); return ira->codegen->invalid_instruction; } From c86736f92d3ce5aa1ab6134ae551164fbd54ee36 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 18 Dec 2018 23:01:49 -0500 Subject: [PATCH 090/190] copy elision: fix @sliceToBytes not updating the length --- src/all_types.hpp | 16 +++++++ src/codegen.cpp | 16 +++++++ src/ir.cpp | 109 +++++++++++++++++++++++++++++++++++++++++++++- src/ir_print.cpp | 20 +++++++++ 4 files changed, 160 insertions(+), 1 deletion(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index f80df516ef36..29baf7bc07c3 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2237,6 +2237,8 @@ enum IrInstructionId { IrInstructionIdInferCompTime, IrInstructionIdFromBytesLenSrc, IrInstructionIdFromBytesLenGen, + IrInstructionIdToBytesLenSrc, + IrInstructionIdToBytesLenGen, IrInstructionIdSetNonNullBit, IrInstructionIdErrorLiteral, }; @@ -3490,6 +3492,20 @@ struct IrInstructionFromBytesLenGen { IrInstruction *prev_result_loc; }; +struct IrInstructionToBytesLenSrc { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *new_result_loc; +}; + +struct IrInstructionToBytesLenGen { + IrInstruction base; + + ZigType *elem_type; + IrInstruction *prev_result_loc; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index dd1976b20197..f8c0be5e179c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5173,6 +5173,19 @@ static LLVMValueRef ir_render_from_bytes_len(CodeGen *g, IrExecutable *executabl return nullptr; } +static LLVMValueRef ir_render_to_bytes_len(CodeGen *g, IrExecutable *executable, + IrInstructionToBytesLenGen *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, slice_len_index, ""); + LLVMValueRef prev_len = LLVMBuildLoad(g->builder, len_ptr, ""); + uint64_t elem_size = type_size(g, instruction->elem_type); + LLVMValueRef elem_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, elem_size, false); + LLVMValueRef new_len = LLVMBuildMul(g->builder, prev_len, elem_size_val, ""); + LLVMBuildStore(g->builder, new_len, len_ptr); + return nullptr; +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5262,6 +5275,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdResultSliceToBytesPlaceholder: case IrInstructionIdResultBytesToSlice: case IrInstructionIdFromBytesLenSrc: + case IrInstructionIdToBytesLenSrc: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5440,6 +5454,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_result_slice_to_bytes(g, executable, (IrInstructionResultSliceToBytesGen *)instruction); case IrInstructionIdFromBytesLenGen: return ir_render_from_bytes_len(g, executable, (IrInstructionFromBytesLenGen *)instruction); + case IrInstructionIdToBytesLenGen: + return ir_render_to_bytes_len(g, executable, (IrInstructionToBytesLenGen *)instruction); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index c365c4320852..e2a28103bf6f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -964,6 +964,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytesLenGen return IrInstructionIdFromBytesLenGen; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionToBytesLenSrc *) { + return IrInstructionIdToBytesLenSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionToBytesLenGen *) { + return IrInstructionIdToBytesLenGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionSetNonNullBit *) { return IrInstructionIdSetNonNullBit; } @@ -3096,6 +3104,35 @@ static IrInstruction *ir_build_from_bytes_len_gen(IrAnalyze *ira, IrInstruction return &instruction->base; } +static IrInstruction *ir_build_to_bytes_len_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *new_result_loc) +{ + IrInstructionToBytesLenSrc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->new_result_loc = new_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + ir_ref_instruction(new_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_to_bytes_len_gen(IrAnalyze *ira, IrInstruction *source_inst, + IrInstruction *prev_result_loc, ZigType *elem_type) +{ + IrInstructionToBytesLenGen *instruction = ir_build_instruction( + &ira->new_irb, source_inst->scope, source_inst->source_node); + instruction->base.value.special = ConstValSpecialStatic; + instruction->base.value.type = ira->codegen->builtin_types.entry_void; + + instruction->prev_result_loc = prev_result_loc; + instruction->elem_type = elem_type; + + ir_ref_instruction(prev_result_loc, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_set_nonnull_bit(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *prev_result_loc, IrInstruction *non_null_bit, IrInstruction *new_result_loc) { @@ -4670,7 +4707,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - ir_build_infer_comptime(irb, scope, node, ensured_result_loc, new_result_loc); + ir_build_to_bytes_len_src(irb, scope, node, ensured_result_loc, new_result_loc); return ir_gen_result(irb, scope, node, lval, ensured_result_loc); } @@ -22764,6 +22801,71 @@ static IrInstruction *ir_analyze_instruction_from_bytes_len(IrAnalyze *ira, return ir_build_from_bytes_len_gen(ira, &instruction->base, prev_result_loc, elem_type); } +static IrInstruction *ir_analyze_instruction_to_bytes_len(IrAnalyze *ira, + IrInstructionToBytesLenSrc *instruction) +{ + Error err; + + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = instruction->new_result_loc->child; + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + assert(new_result_loc->value.type->id == ZigTypeIdPointer); + ZigType *slice_type = new_result_loc->value.type->data.pointer.child_type; + assert(is_slice(slice_type)); + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *elem_type = slice_ptr_type->data.pointer.child_type; + + if (prev_result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(new_result_loc) && + new_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *slice_val = const_ptr_pointee(ira->codegen, &new_result_loc->value); + if (slice_val == nullptr) + return ira->codegen->invalid_instruction; + + if (slice_val->special != ConstValSpecialUndef) { + ConstExprValue *ptr_val = &slice_val->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *len_val = &slice_val->data.x_struct.fields[slice_len_index]; + if (ptr_val->special == ConstValSpecialStatic && len_val->special == ConstValSpecialStatic) { + // copy the length from the new_result_loc and multiply it by the element size + // we need a new slice const val though, because if we modify the length it will + // modify the length of the new_result_loc + ConstExprValue *new_slice_val = create_const_vals(1); + copy_const_val(new_slice_val, slice_val, false); + prev_result_loc->value.data.x_ptr.special = ConstPtrSpecialRef; + prev_result_loc->value.data.x_ptr.data.ref.pointee = new_slice_val; + + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; + + size_t elem_size = type_size(ira->codegen, elem_type); + BigInt elem_size_bigint; + bigint_init_unsigned(&elem_size_bigint, elem_size); + + ConstExprValue *new_len_val = &new_slice_val->data.x_struct.fields[slice_len_index]; + new_len_val->special = ConstValSpecialStatic; + bigint_mul(&new_len_val->data.x_bigint, &len_val->data.x_bigint, &elem_size_bigint); + + prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + return ir_const_void(ira, &instruction->base); + } + } + } + // comptime didn't work, mark the instructions as runtime and fall through + prev_result_loc->value.special = ConstValSpecialRuntime; + new_result_loc->value.special = ConstValSpecialRuntime; + } + + return ir_build_to_bytes_len_gen(ira, &instruction->base, prev_result_loc, elem_type); +} + + static IrInstruction *ir_analyze_instruction_set_non_null_bit(IrAnalyze *ira, IrInstructionSetNonNullBit *instruction) { @@ -22930,6 +23032,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdResultSliceToBytesPlaceholder: case IrInstructionIdResultSliceToBytesGen: case IrInstructionIdFromBytesLenGen: + case IrInstructionIdToBytesLenGen: zig_unreachable(); case IrInstructionIdReturn: @@ -23210,6 +23313,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_infer_comptime(ira, (IrInstructionInferCompTime *)instruction); case IrInstructionIdFromBytesLenSrc: return ir_analyze_instruction_from_bytes_len(ira, (IrInstructionFromBytesLenSrc *)instruction); + case IrInstructionIdToBytesLenSrc: + return ir_analyze_instruction_to_bytes_len(ira, (IrInstructionToBytesLenSrc *)instruction); case IrInstructionIdResultOptionalPayload: return ir_analyze_instruction_result_optional_payload(ira, (IrInstructionResultOptionalPayload *)instruction); case IrInstructionIdSetNonNullBit: @@ -23372,6 +23477,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdInferCompTime: case IrInstructionIdFromBytesLenSrc: case IrInstructionIdFromBytesLenGen: + case IrInstructionIdToBytesLenSrc: + case IrInstructionIdToBytesLenGen: case IrInstructionIdSetNonNullBit: case IrInstructionIdSlice: case IrInstructionIdCmpxchgGen: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index ea7e558f998c..f9a7da6ff4e4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1454,6 +1454,20 @@ static void ir_print_from_bytes_len_gen(IrPrint *irp, IrInstructionFromBytesLenG fprintf(irp->f, ",elem_type=%s)", buf_ptr(&instruction->elem_type->name)); } +static void ir_print_to_bytes_len_src(IrPrint *irp, IrInstructionToBytesLenSrc *instruction) { + fprintf(irp->f, "ToBytesLenSrc(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",new_result="); + ir_print_other_instruction(irp, instruction->new_result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_to_bytes_len_gen(IrPrint *irp, IrInstructionToBytesLenGen *instruction) { + fprintf(irp->f, "ToBytesLenGen(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",elem_type=%s)", buf_ptr(&instruction->elem_type->name)); +} + static void ir_print_set_non_null_bit(IrPrint *irp, IrInstructionSetNonNullBit *instruction) { fprintf(irp->f, "SetNonNullBit(prev_result="); ir_print_other_instruction(irp, instruction->prev_result_loc); @@ -1950,6 +1964,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFromBytesLenGen: ir_print_from_bytes_len_gen(irp, (IrInstructionFromBytesLenGen *)instruction); break; + case IrInstructionIdToBytesLenSrc: + ir_print_to_bytes_len_src(irp, (IrInstructionToBytesLenSrc *)instruction); + break; + case IrInstructionIdToBytesLenGen: + ir_print_to_bytes_len_gen(irp, (IrInstructionToBytesLenGen *)instruction); + break; case IrInstructionIdSetNonNullBit: ir_print_set_non_null_bit(irp, (IrInstructionSetNonNullBit *)instruction); break; From 7c7cd25df43cde242ceb27633e30de30b8e40b03 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 18 Dec 2018 23:25:38 -0500 Subject: [PATCH 091/190] copy elision: fn call creates result loc for args when necessary ```zig export fn entry() void { if (enumber()) |n| {} else |e| {} } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %error_return_trace_addresses = alloca [30 x i64], align 8 %error_return_trace = alloca %StackTrace, align 8 %param = alloca { i16, i32 }, align 4 %0 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 0 store i64 0, i64* %0, align 8 %1 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 1 %2 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 0 %3 = getelementptr inbounds [30 x i64], [30 x i64]* %error_return_trace_addresses, i64 0, i64 0 store i64* %3, i64** %2, align 8 %4 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 1 store i64 30, i64* %4, align 8 %5 = getelementptr inbounds { i16, i32 }, { i16, i32 }* %param, i32 0, i32 1, !dbg !49 %6 = call fastcc i16 @enumber(i32* %5, %StackTrace* %error_return_trace), !dbg !49 %7 = getelementptr inbounds { i16, i32 }, { i16, i32 }* %param, i32 0, i32 0, !dbg !49 store i16 %6, i16* %7, align 2, !dbg !49 %8 = getelementptr inbounds { i16, i32 }, { i16, i32 }* %param, i32 0, i32 0, !dbg !50 %9 = load i16, i16* %8, align 2, !dbg !50 %10 = icmp ne i16 %9, 0, !dbg !50 br i1 %10, label %IfErrElse, label %IfErrThen, !dbg !50 IfErrThen: ; preds = %Entry %11 = getelementptr inbounds { i16, i32 }, { i16, i32 }* %param, i32 0, i32 1, !dbg !50 call void @llvm.dbg.declare(metadata i32* %11, metadata !45, metadata !DIExpression()), !dbg !50 br label %IfErrEnd, !dbg !50 IfErrElse: ; preds = %Entry call void @llvm.dbg.declare(metadata i16* %8, metadata !48, metadata !DIExpression()), !dbg !50 br label %IfErrEnd, !dbg !50 IfErrEnd: ; preds = %IfErrElse, %IfErrThen ret void, !dbg !51 } ``` --- src/ir.cpp | 53 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e2a28103bf6f..fce5f81dd144 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -177,6 +177,8 @@ static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, static ZigType *adjust_ptr_child(CodeGen *g, ZigType *ptr_type, ZigType *new_child); static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *prev_result_loc, ZigType *elem_type, ZigType *child_type); +static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *child_type_inst, + uint32_t align, const char *name_hint); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -14311,11 +14313,21 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, } bool need_store_ptr = !convert_to_value && - (return_type == payload_result_type) && !handle_is_ptr(return_type) && - call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr; + (return_type == payload_result_type) && !handle_is_ptr(return_type); + IrInstruction *prev_result_loc = nullptr; if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { - IrInstruction *prev_result_loc = call_instruction->result_loc->child; + prev_result_loc = call_instruction->result_loc->child; + } else { + need_store_ptr = false; + if (return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdOptional) { + prev_result_loc = ir_analyze_alloca(ira, &call_instruction->base, nullptr, 0, "param"); + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + } + } + + if (prev_result_loc != nullptr) { if (type_is_invalid(prev_result_loc->value.type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(prev_result_loc)) { @@ -22578,33 +22590,28 @@ static IrInstruction *ir_analyze_instruction_result_cast(IrAnalyze *ira, IrInstr return new_result_loc; } -static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAllocaSrc *instruction) { +static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *child_type_inst, + uint32_t align, const char *name_hint) +{ Error err; - uint32_t align = 0; - if (instruction->align != nullptr) { - if (!ir_resolve_align(ira, instruction->align->child, &align)) { - return ira->codegen->invalid_instruction; - } - } - ConstExprValue *pointee = create_const_vals(1); pointee->special = ConstValSpecialUndef; - IrInstructionAllocaGen *result = ir_create_alloca_gen(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, align, instruction->name_hint); + IrInstructionAllocaGen *result = ir_create_alloca_gen(&ira->new_irb, source_inst->scope, + source_inst->source_node, align, name_hint); result->base.value.special = ConstValSpecialStatic; result->base.value.data.x_ptr.special = ConstPtrSpecialRef; result->base.value.data.x_ptr.mut = ConstPtrMutInfer; result->base.value.data.x_ptr.data.ref.pointee = pointee; - if (instruction->child_type == nullptr) { + if (child_type_inst == nullptr) { // We use a pointer to a special opaque type to signal that we want type inference. result->base.value.type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_infer, false, false, PtrLenSingle, align, 0, 0); result->base.value.data.x_ptr.data.ref.pointee->type = ira->codegen->builtin_types.entry_infer; } else { - ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child); + ZigType *child_type = ir_resolve_type(ira, child_type_inst); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; @@ -22619,6 +22626,22 @@ static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructio return &result->base; } +static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAllocaSrc *instruction) { + uint32_t align = 0; + if (instruction->align != nullptr) { + if (!ir_resolve_align(ira, instruction->align->child, &align)) { + return ira->codegen->invalid_instruction; + } + } + + IrInstruction *child_type_inst = nullptr; + if (instruction->child_type != nullptr) { + child_type_inst = instruction->child_type->child; + } + + return ir_analyze_alloca(ira, &instruction->base, child_type_inst, align, instruction->name_hint); +} + static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira, IrInstructionFirstArgResultLoc *instruction) { From b67975541b5429eff93fdb8ccbcd9e5d2a8fcfbf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 19 Dec 2018 14:36:53 -0500 Subject: [PATCH 092/190] copy elision: array to slice implicit cast I relaxed the casting rules to make this work. This commit makes Zig allow an implicit cast from `[*]const T` to `[]const T`. This definitely should not be allowed. In order to fix the problem, we need #265 to be implemented, and then we can change string literals to be `*const [N]null u8`, and then we can remove these implicit casts: * `[*]const T` to `[]const T` (the temporary one introduced by this commit) * `[N]T` to `[]const T`. This makes sense to remove, with the main motivation of string literals being gone. ```zig export fn entry() void { var y = "aoeu"; var x: []const u8 = y; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %y = alloca [4 x i8], align 1 %x = alloca %"[]u8", align 8 %0 = bitcast [4 x i8]* %y to i8*, !dbg !52 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0), i64 4, i1 false), !dbg !52 call void @llvm.dbg.declare(metadata [4 x i8]* %y, metadata !45, metadata !DIExpression()), !dbg !52 %1 = getelementptr inbounds %"[]u8", %"[]u8"* %x, i32 0, i32 1, !dbg !53 store i64 4, i64* %1, align 8, !dbg !53 %2 = getelementptr inbounds %"[]u8", %"[]u8"* %x, i32 0, i32 0, !dbg !53 %3 = bitcast [4 x i8]* %y to i8*, !dbg !54 store i8* %3, i8** %2, align 8, !dbg !54 call void @llvm.dbg.declare(metadata %"[]u8"* %x, metadata !50, metadata !DIExpression()), !dbg !53 ret void, !dbg !55 } ``` --- src/ir.cpp | 58 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 46d88776157f..9115823f7e84 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10524,9 +10524,7 @@ static IrInstruction *ir_analyze_instruction_result_optional_payload(IrAnalyze * return ir_analyze_result_optional_payload(ira, prev_result_loc, payload_type, instruction->make_non_null); } -static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction *result_loc, - ZigType *ptr_to_array_type, uint64_t len) -{ +static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction *result_loc, uint64_t len) { ZigType *slice_type = result_loc->value.type->data.pointer.child_type; assert(is_slice(slice_type)); ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; @@ -10913,11 +10911,9 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s // In this function we honor the const-ness of wanted_type, because // we may be casting [0]T to []const T which is perfectly valid. - IrInstruction *array_ptr = nullptr; IrInstruction *array; if (array_arg->value.type->id == ZigTypeIdPointer) { array = ir_get_deref(ira, source_instr, array_arg); - array_ptr = array_arg; } else { array = array_arg; } @@ -10931,23 +10927,9 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s return result; } - IrInstruction *start = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); - init_const_usize(ira->codegen, &start->value, 0); - - IrInstruction *end = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); - init_const_usize(ira->codegen, &end->value, array_type->data.array.len); - - if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false, nullptr); - - // TODO don't pass nullptr to ir_build_slice - BREAKPOINT; - IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope, - source_instr->source_node, array_ptr, start, end, false, nullptr); - result->value.type = wanted_type; - result->value.data.rh_slice.id = RuntimeHintSliceIdLen; - result->value.data.rh_slice.len = array_type->data.array.len; - - return result; + // Only the comptime branch above is used here. Once https://github.com/ziglang/zig/issues/265 + // lands this entire function will be deleted. + zig_unreachable(); } static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, @@ -11657,6 +11639,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // cast from [N]T to []const T + // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this + // only the comptime branch here is used if (is_slice(wanted_type) && actual_type->id == ZigTypeIdArray) { ZigType *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == ZigTypeIdPointer); @@ -11668,7 +11652,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // cast from [N]T to [*]const T + // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this + // []const T in this situation gets turned into [*]const T with the result location cast + if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdPointer && + wanted_type->data.pointer.ptr_len == PtrLenUnknown) + { + if ((wanted_type->data.pointer.is_const || actual_type->data.array.len == 0) && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) + { + IrInstruction *array_ref = ir_get_ref(ira, source_instr, value, true, false, nullptr); + return ir_implicit_cast(ira, array_ref, wanted_type); + } + } + // cast from [N]T to ?[]const T + // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == ZigTypeIdArray) @@ -12052,7 +12052,19 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *unr array_type->data.array.child_type, source_node, !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) { - return ir_analyze_result_slice_ptr(ira, result_loc, needed_child_type, array_type->data.array.len); + return ir_analyze_result_slice_ptr(ira, result_loc, array_type->data.array.len); + } + } + + // cast from [N]T to []const T + if (is_slice(have_child_type) && needed_child_type->id == ZigTypeIdArray) { + ZigType *ptr_type = have_child_type->data.structure.fields[slice_ptr_index].type_entry; + assert(ptr_type->id == ZigTypeIdPointer); + if ((ptr_type->data.pointer.is_const || needed_child_type->data.array.len == 0) && + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, + needed_child_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_result_slice_ptr(ira, result_loc, needed_child_type->data.array.len); } } From 4d4fcb1676e81f3ba950d9e2990a5659901a64d0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 19 Dec 2018 17:02:51 -0500 Subject: [PATCH 093/190] copy elision: fix cast array to slice mixing const,var ```zig export fn entry() void { const y = "aoeu"; var x: []const u8 = y; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %x = alloca %"[]u8", align 8 call void @llvm.dbg.declare(metadata [4 x i8]* @1, metadata !45, metadata !DIExpression()), !dbg !52 %0 = bitcast %"[]u8"* %x to i8*, !dbg !53 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%"[]u8"* @3 to i8*), i64 16, i1 false), !dbg !53 call void @llvm.dbg.declare(metadata %"[]u8"* %x, metadata !50, metadata !DIExpression()), !dbg !53 ret void, !dbg !54 } ``` --- src/ir.cpp | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 9115823f7e84..24a584db70e0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10129,13 +10129,16 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, + wanted_type, value, CastOpBitCast); + result->value.type = wanted_type; + if (instr_is_comptime(value)) { ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, &value->value, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { - IrInstruction *result = ir_const(ira, source_instr, wanted_type); - result->value.type = wanted_type; + result->value.special = ConstValSpecialStatic; result->value.data.x_ptr.special = ConstPtrSpecialBaseArray; result->value.data.x_ptr.mut = value->value.data.x_ptr.mut; result->value.data.x_ptr.data.base_array.array_val = pointee; @@ -10145,9 +10148,6 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, } } - IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, - wanted_type, value, CastOpBitCast); - result->value.type = wanted_type; return result; } @@ -11663,7 +11663,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { IrInstruction *array_ref = ir_get_ref(ira, source_instr, value, true, false, nullptr); - return ir_implicit_cast(ira, array_ref, wanted_type); + return ir_analyze_cast(ira, source_instr, wanted_type, array_ref); } } @@ -12151,9 +12151,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc ir_add_error(ira, ptr, buf_sprintf("attempt to dereference undefined value")); return ira->codegen->invalid_instruction; } - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst || - ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) - { + if (ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); if (pointee->special != ConstValSpecialRuntime) { IrInstruction *result = ir_const(ira, source_instruction, child_type); @@ -13691,16 +13689,6 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, break; } - if (!type_is_invalid(result_type)) { - // Resolve ConstPtrMutInfer - if (instr_is_comptime(var_ptr)) { - if (var_ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { - if (!is_comptime_var && !var->gen_is_const) { - var_ptr->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; - } - } - } - } if (var->value->type != nullptr && !is_comptime_var) { // This is at least the second time we've seen this variable declaration during analysis. // This means that this is actually a different variable due to, e.g. an inline while loop. @@ -13743,6 +13731,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, } if (init_val != nullptr && init_val->special != ConstValSpecialRuntime) { + // Resolve ConstPtrMutInfer if (var->gen_is_const) { var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeConst; } else if (is_comptime_var) { @@ -13752,6 +13741,10 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, // since it's a comptime val there are no instructions for it. // we memcpy the init value here IrInstruction *deref = ir_get_deref(ira, var_ptr, var_ptr); + // If this assertion trips, something is wrong with the IR instructions, because + // we expected the above deref to return a constant value, but it created a runtime + // instruction. + assert(deref->value.special == ConstValSpecialStatic); var_ptr->value.special = ConstValSpecialRuntime; ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref); } From 4998ca98b564c51fdd1bb844cece9f0ff26a65d8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 19 Dec 2018 17:31:13 -0500 Subject: [PATCH 094/190] copy elision: fix while loop with break ```zig export fn entry() void { while (true) { break; } } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: br label %WhileEnd, !dbg !45 WhileEnd: ; preds = %Entry ret void, !dbg !48 } ``` --- src/ir.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 24a584db70e0..98b5db5dfd42 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9973,7 +9973,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs) { ConstGlobalRefs *global_refs = dest->global_refs; *dest = *src; - if (!same_global_refs) { + if (!same_global_refs && src->special != ConstValSpecialUndef) { dest->global_refs = global_refs; if (dest->type->id == ZigTypeIdStruct) { dest->data.x_struct.fields = allocate_nonzero(dest->type->data.structure.src_field_count); @@ -12146,6 +12146,10 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc init_const_unsigned_negative(&result->value, child_type, 0, false); return result; } + // if it's infer, that means it's void + if (child_type == ira->codegen->builtin_types.entry_infer) { + return ir_const_void(ira, source_instruction); + } if (instr_is_comptime(ptr)) { if (ptr->value.special == ConstValSpecialUndef) { ir_add_error(ira, ptr, buf_sprintf("attempt to dereference undefined value")); @@ -13744,7 +13748,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, // If this assertion trips, something is wrong with the IR instructions, because // we expected the above deref to return a constant value, but it created a runtime // instruction. - assert(deref->value.special == ConstValSpecialStatic); + assert(deref->value.special != ConstValSpecialRuntime); var_ptr->value.special = ConstValSpecialRuntime; ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref); } From 4e9f73da37a61c55ed6e1fac371aa543e85da953 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 19 Dec 2018 17:54:50 -0500 Subject: [PATCH 095/190] cherry pick 8768816d69dd from master --- std/io.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/std/io.zig b/std/io.zig index c40ededc00a5..428d95725d2f 100644 --- a/std/io.zig +++ b/std/io.zig @@ -155,32 +155,32 @@ pub fn InStream(comptime ReadError: type) type { pub fn readIntNative(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntSliceNative(T, bytes); + return mem.readIntNative(T, &bytes); } /// Reads a foreign-endian integer pub fn readIntForeign(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntSliceForeign(T, bytes); + return mem.readIntForeign(T, &bytes); } pub fn readIntLittle(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntSliceLittle(T, bytes); + return mem.readIntLittle(T, &bytes); } pub fn readIntBig(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntSliceBig(T, bytes); + return mem.readIntBig(T, &bytes); } pub fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntSlice(T, bytes, endian); + return mem.readInt(T, &bytes, endian); } pub fn readVarInt(self: *Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType { From 073f627fadb445eaeea29d5ed2ee0987945983b9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 19 Dec 2018 19:05:02 -0500 Subject: [PATCH 096/190] copy elision: fix some crashes for example: ```zig comptime { var b = bar(); if (b.x != 3) unreachable; } ``` --- src/ir.cpp | 42 +++++++++++++++++++++--------------------- std/math/index.zig | 2 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 98b5db5dfd42..e505a206b9c3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14752,20 +14752,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; } - ZigType *scalar_result_type; - ZigType *payload_result_type; - if (return_type->id == ZigTypeIdErrorUnion) { - scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); - payload_result_type = return_type->data.error_union.payload_type; - } else { - payload_result_type = return_type; - scalar_result_type = return_type; - } - + // TODO test with optionals and error unions IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->value.type); // TODO should we use copy_const_val? new_instruction->value = result->value; - new_instruction->value.type = scalar_result_type; if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { IrInstruction *prev_result_loc = call_instruction->result_loc->child; @@ -14780,19 +14770,28 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } } - bool need_store_ptr = (return_type == payload_result_type) && - (!type_has_bits(return_type) || !handle_is_ptr(return_type)); - IrInstruction *casted_result_loc = ir_implicit_cast_result(ira, prev_result_loc, payload_result_type, need_store_ptr); - if (casted_result_loc == nullptr) - casted_result_loc = prev_result_loc; - if (type_is_invalid(casted_result_loc->value.type)) + IrInstruction *store_inst = ir_analyze_store_ptr(ira, &call_instruction->base, + prev_result_loc, new_instruction); + if (type_is_invalid(store_inst->value.type)) return ira->codegen->invalid_instruction; - if (need_store_ptr) { - ir_analyze_store_ptr(ira, &call_instruction->base, casted_result_loc, new_instruction); - } } - return ir_finish_anal(ira, new_instruction); + switch (call_instruction->lval) { + case LValNone: + return ir_finish_anal(ira, new_instruction); + case LValPtr: { + IrInstruction *ref_inst = ir_get_ref(ira, &call_instruction->base, new_instruction, + true, false, nullptr); + return ir_finish_anal(ira, ref_inst); + } + case LValErrorUnionVal: + zig_unreachable(); + case LValErrorUnionPtr: + zig_unreachable(); + case LValOptional: + zig_unreachable(); + } + zig_unreachable(); } IrInstruction *casted_new_stack = nullptr; @@ -16144,6 +16143,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ } } } + assert(struct_val->special != ConstValSpecialUndef); ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index]; ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_val->type, is_const, is_volatile, PtrLenSingle, align_bytes, diff --git a/std/math/index.zig b/std/math/index.zig index f37de2505b24..d254c50b3840 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -378,7 +378,7 @@ pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) t return u0; } const is_signed = from < 0; - const largest_positive_integer = max(if (from<0) (-from)-1 else from, to); // two's complement + const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement const base = log2(largest_positive_integer); const upper = (1 << base) - 1; var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; From 928b84f20cf549746aa46092cedfb5c9c2492ce6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 21 Dec 2018 13:01:16 -0500 Subject: [PATCH 097/190] copy elision: fix generic fn call with 1 arg This also makes function calls with only 1 argument not stack allocate their parameter unless necessary. ```zig export fn entry() void { var z: u32 = 1; var x = absCast(z, z); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %z = alloca i32, align 4 %x = alloca i32, align 4 store i32 1, i32* %z, align 4, !dbg !50 call void @llvm.dbg.declare(metadata i32* %z, metadata !45, metadata !DIExpression()), !dbg !50 %0 = load i32, i32* %z, align 4, !dbg !51 %1 = load i32, i32* %z, align 4, !dbg !52 %2 = call fastcc i32 @absCast(i32 %0, i32 %1), !dbg !53 store i32 %2, i32* %x, align 4, !dbg !53 call void @llvm.dbg.declare(metadata i32* %x, metadata !48, metadata !DIExpression()), !dbg !54 ret void, !dbg !55 } ``` --- src/ir.cpp | 56 ++++++++++++------------------------------------------ 1 file changed, 12 insertions(+), 44 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e505a206b9c3..2956532203d1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -179,7 +179,7 @@ static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, static ZigType *adjust_ptr_child(CodeGen *g, ZigType *ptr_type, ZigType *new_child); static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *prev_result_loc, ZigType *elem_type, ZigType *child_type); -static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *child_type_inst, +static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, ZigType *child_type_inst, uint32_t align, const char *name_hint); static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); @@ -22818,7 +22818,7 @@ static IrInstruction *ir_analyze_instruction_result_cast(IrAnalyze *ira, IrInstr return new_result_loc; } -static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *child_type_inst, +static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, ZigType *var_type, uint32_t align, const char *name_hint) { Error err; @@ -22833,17 +22833,13 @@ static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_in result->base.value.data.x_ptr.mut = ConstPtrMutInfer; result->base.value.data.x_ptr.data.ref.pointee = pointee; - if (child_type_inst == nullptr) { + if (var_type == nullptr) { // We use a pointer to a special opaque type to signal that we want type inference. result->base.value.type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_infer, false, false, PtrLenSingle, align, 0, 0); result->base.value.data.x_ptr.data.ref.pointee->type = ira->codegen->builtin_types.entry_infer; } else { - ZigType *child_type = ir_resolve_type(ira, child_type_inst); - if (type_is_invalid(child_type)) - return ira->codegen->invalid_instruction; - - if ((err = resolve_alloca_inference(ira, result, child_type))) + if ((err = resolve_alloca_inference(ira, result, var_type))) return ira->codegen->invalid_instruction; } @@ -22862,19 +22858,19 @@ static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructio } } - IrInstruction *child_type_inst = nullptr; + ZigType *var_type = nullptr; if (instruction->child_type != nullptr) { - child_type_inst = instruction->child_type->child; + var_type = ir_resolve_type(ira, instruction->child_type->child); + if (type_is_invalid(var_type)) + return ira->codegen->invalid_instruction; } - return ir_analyze_alloca(ira, &instruction->base, child_type_inst, align, instruction->name_hint); + return ir_analyze_alloca(ira, &instruction->base, var_type, align, instruction->name_hint); } static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira, IrInstructionFirstArgResultLoc *instruction) { - Error err; - IrInstruction *fn_ref = instruction->fn_ref->child; if (type_is_invalid(fn_ref->value.type)) return ira->codegen->invalid_instruction; @@ -22898,37 +22894,9 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira } // Result of this instruction should be the result location for the first argument of the function call. - // This means it should be a stack allocation. - ConstExprValue *pointee = create_const_vals(1); - pointee->special = ConstValSpecialUndef; - - IrInstructionAllocaGen *result = ir_create_alloca_gen(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, 0, ""); - result->base.value.special = ConstValSpecialStatic; - result->base.value.data.x_ptr.special = ConstPtrSpecialRef; - result->base.value.data.x_ptr.mut = ConstPtrMutInfer; - result->base.value.data.x_ptr.data.ref.pointee = pointee; - - ZigType *param_type; - if (fn_ref->value.type->id == ZigTypeIdFn) { - ZigType *fn_type = fn_ref->value.type; - param_type = fn_type->data.fn.fn_type_id.param_info[0].type; - } else if (fn_ref->value.type->id == ZigTypeIdBoundFn) { - ZigType *fn_type = fn_ref->value.type->data.bound_fn.fn_type; - param_type = fn_type->data.fn.fn_type_id.param_info[1].type; - } else { - zig_unreachable(); - } - - if (type_is_invalid(param_type)) - return ira->codegen->invalid_instruction; - if ((err = resolve_alloca_inference(ira, result, param_type))) - return ira->codegen->invalid_instruction; - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - if (fn_entry != nullptr) { - fn_entry->alloca_list.append(result); - } - return &result->base; + // We give null because this instruction is only ever consumed by the result location of a call + // instruction, which knows how to stack allocate if necessary. + return nullptr; } static IrInstruction *ir_analyze_instruction_infer_array_type(IrAnalyze *ira, From abe5fe81b7bae96d242c4c09d45d90db5e49b838 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 21 Dec 2018 13:17:31 -0500 Subject: [PATCH 098/190] copy elision: actually we need a result loc for first arg See #1757 --- src/ir.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 2956532203d1..d904cdb0812a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -22894,9 +22894,21 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira } // Result of this instruction should be the result location for the first argument of the function call. - // We give null because this instruction is only ever consumed by the result location of a call - // instruction, which knows how to stack allocate if necessary. - return nullptr; + ZigType *param_type; + if (fn_ref->value.type->id == ZigTypeIdFn) { + ZigType *fn_type = fn_ref->value.type; + param_type = fn_type->data.fn.fn_type_id.param_info[0].type; + } else if (fn_ref->value.type->id == ZigTypeIdBoundFn) { + ZigType *fn_type = fn_ref->value.type->data.bound_fn.fn_type; + param_type = fn_type->data.fn.fn_type_id.param_info[1].type; + } else { + zig_unreachable(); + } + + if (param_type != nullptr && type_is_invalid(param_type)) + return ira->codegen->invalid_instruction; + + return ir_analyze_alloca(ira, &instruction->base, param_type, 0, ""); } static IrInstruction *ir_analyze_instruction_infer_array_type(IrAnalyze *ira, From 87024937f2fda9f4608b2854aec24e63f8dfce33 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 21 Dec 2018 14:14:53 -0500 Subject: [PATCH 099/190] fix switch expression incorrectly giving unreachable code error --- src/ir.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index d904cdb0812a..58ed19bfabf7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3396,9 +3396,11 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, return value; if (result_loc != nullptr) { switch (lval) { - case LValNone: - ir_build_store_ptr(irb, scope, node, result_loc, value); - break; + case LValNone: { + IrInstruction *store_inst = ir_build_store_ptr(irb, scope, node, result_loc, value); + store_inst->is_gen = value->is_gen; + return value; + } case LValPtr: zig_unreachable(); case LValErrorUnionVal: @@ -3773,8 +3775,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode return ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)); } } - return ir_gen_value(irb, parent_scope, block_node, lval, result_loc, - ir_mark_gen(ir_build_const_void(irb, child_scope, block_node))); + IrInstruction *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)); + return ir_gen_value(irb, parent_scope, block_node, lval, result_loc, void_inst); } } From d839cf93bca02402b7eef5a5ab5fe5c32f574399 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 21 Dec 2018 14:25:55 -0500 Subject: [PATCH 100/190] compiler_rt compiles again --- std/special/compiler_rt/fixint.zig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/std/special/compiler_rt/fixint.zig b/std/special/compiler_rt/fixint.zig index fd31798cc2f9..2c85bc73d7c9 100644 --- a/std/special/compiler_rt/fixint.zig +++ b/std/special/compiler_rt/fixint.zig @@ -63,9 +63,17 @@ pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t { // Cast to final signed result if (negative) { - return if (uint_result >= -math.minInt(fixint_t)) math.minInt(fixint_t) else -@intCast(fixint_t, uint_result); + if (uint_result >= -math.minInt(fixint_t)) { + return math.minInt(fixint_t); + } else { + return -@intCast(fixint_t, uint_result); + } } else { - return if (uint_result >= math.maxInt(fixint_t)) math.maxInt(fixint_t) else @intCast(fixint_t, uint_result); + if (uint_result >= math.maxInt(fixint_t)) { + return math.maxInt(fixint_t); + } else { + return @intCast(fixint_t, uint_result); + } } } From 250c13202bc46fa6a68a6b0b5c7db1bd9fa6a308 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 23 Dec 2018 11:48:05 -0500 Subject: [PATCH 101/190] copy-elision: fix comptime blocks --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 58ed19bfabf7..87213b92730d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6653,7 +6653,7 @@ static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNo assert(node->type == NodeTypeCompTime); Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope); - return ir_gen_node(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); + return ir_gen_node(irb, node->data.comptime_expr.expr, child_scope, lval, result_loc); } static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, From d2e91d01e9fbf120e773201d678d4e1408ab9c58 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 23 Dec 2018 13:30:01 -0500 Subject: [PATCH 102/190] copy elision: catch function call ```zig export fn entry() void { foo() catch |e| { return; }; } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %error_return_trace_addresses = alloca [30 x i64], align 8 %error_return_trace = alloca %StackTrace, align 8 %err = alloca i16, align 2 %0 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 0 store i64 0, i64* %0, align 8 %1 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 1 %2 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 0 %3 = getelementptr inbounds [30 x i64], [30 x i64]* %error_return_trace_addresses, i64 0, i64 0 store i64* %3, i64** %2, align 8 %4 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 1 store i64 30, i64* %4, align 8 %5 = call fastcc i16 @foo(%StackTrace* %error_return_trace), !dbg !47 store i16 %5, i16* %err, align 2, !dbg !47 %6 = load i16, i16* %err, align 2, !dbg !48 %7 = icmp ne i16 %6, 0, !dbg !48 br i1 %7, label %CatchError, label %CatchEnd, !dbg !48 CatchError: ; preds = %Entry call void @llvm.dbg.declare(metadata i16* %err, metadata !45, metadata !DIExpression()), !dbg !48 ret void, !dbg !49 CatchEnd: ; preds = %Entry ret void, !dbg !52 } ``` --- src/codegen.cpp | 1 + src/ir.cpp | 13 ++++++++++++- std/fmt/index.zig | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 4e4dcc8d3afa..df5093489e21 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3919,6 +3919,7 @@ static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstru if (handle_is_ptr(instruction->value->value.type)) { return value; } else { + assert(instruction->result_loc != nullptr); LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); gen_store_untyped(g, value, result_ptr, 0, false); return result_ptr; diff --git a/src/ir.cpp b/src/ir.cpp index 87213b92730d..3233cc0c926f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14516,7 +14516,18 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, } case LValErrorUnionPtr: { if (return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) { - return ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); + ZigType *err_type; + if (return_type->id == ZigTypeIdErrorUnion) { + err_type = return_type->data.error_union.err_set_type; + } else { + assert(return_type->id == ZigTypeIdErrorSet); + err_type = return_type; + } + ZigType *opt_err_type = get_optional_type(ira->codegen, err_type); + IrInstruction *err_code_alloca = ir_analyze_alloca(ira, &call_instruction->base, + opt_err_type, 0, "err"); + ir_analyze_store_ptr(ira, &call_instruction->base, err_code_alloca, new_call_instruction); + return err_code_alloca; } else { IrInstruction *null = ir_const(ira, &call_instruction->base, ira->codegen->builtin_types.entry_null); diff --git a/std/fmt/index.zig b/std/fmt/index.zig index eda0bfae032d..cfc4031a8c99 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -752,7 +752,7 @@ fn formatIntUnsigned( if (leftover_padding == 0) break; } mem.set(u8, buf[0..index], '0'); - return output(context, buf); + return output(context, &buf); } else { const padded_buf = buf[index - padding ..]; mem.set(u8, padded_buf[0..padding], '0'); From cc2a1b2270b1b5e022bd3dc241517ccc99051d9e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 23 Dec 2018 14:12:01 -0500 Subject: [PATCH 103/190] copy elision: fix ptr of array to slice as a param ```zig export fn entry() void { var array: [10]u8 = undefined; foo(&array); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %array = alloca [10 x i8], align 1 %slice = alloca %"[]u8", align 8 %0 = bitcast [10 x i8]* %array to i8*, !dbg !50 call void @llvm.memset.p0i8.i64(i8* align 1 %0, i8 -86, i64 10, i1 false), !dbg !50 call void @llvm.dbg.declare(metadata [10 x i8]* %array, metadata !45, metadata !DIExpression()), !dbg !50 %1 = getelementptr inbounds %"[]u8", %"[]u8"* %slice, i32 0, i32 0, !dbg !51 %2 = getelementptr inbounds [10 x i8], [10 x i8]* %array, i64 0, i64 0, !dbg !51 store i8* %2, i8** %1, align 8, !dbg !51 %3 = getelementptr inbounds %"[]u8", %"[]u8"* %slice, i32 0, i32 1, !dbg !51 store i64 10, i64* %3, align 8, !dbg !51 call fastcc void @foo(%"[]u8"* %slice), !dbg !53 ret void, !dbg !54 } ``` --- src/all_types.hpp | 9 +++++++- src/codegen.cpp | 55 +++++++++++++++++++++++++++-------------------- src/ir.cpp | 29 ++++++++++++++++++++++--- src/ir_print.cpp | 11 ++++++++++ 4 files changed, 77 insertions(+), 27 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index c70c91d136e3..827e47a8f90c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -631,7 +631,6 @@ enum CastOp { CastOpNumLitToConcrete, CastOpErrSet, CastOpBitCast, - CastOpPtrOfArrayToSlice, }; struct AstNodeFnCallExpr { @@ -2247,6 +2246,7 @@ enum IrInstructionId { IrInstructionIdToBytesLenGen, IrInstructionIdSetNonNullBit, IrInstructionIdErrorLiteral, + IrInstructionIdPtrOfArrayToSlice, }; struct IrInstruction { @@ -2503,6 +2503,13 @@ struct IrInstructionCast { CastOp cast_op; }; +struct IrInstructionPtrOfArrayToSlice { + IrInstruction base; + + IrInstruction *value; + IrInstruction *result_loc; +}; + struct IrInstructionContainerInitList { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index df5093489e21..029af302d24e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2903,29 +2903,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, return expr_val; case CastOpBitCast: return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); - case CastOpPtrOfArrayToSlice: { - assert(actual_type->id == ZigTypeIdPointer); - ZigType *array_type = actual_type->data.pointer.child_type; - assert(array_type->id == ZigTypeIdArray); - - LLVMValueRef result_ptr = ir_llvm_value(g, cast_instruction->result_loc); - - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, - slice_ptr_index, ""); - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->type_ref), - LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), - }; - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, slice_len_index, ""); - LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, - array_type->data.array.len, false); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - - return result_ptr; - } } zig_unreachable(); } @@ -5220,6 +5197,36 @@ static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInst return LLVMBuildTrunc(g->builder, shifted, int_type->type_ref, ""); } +static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutable *executable, + IrInstructionPtrOfArrayToSlice *instruction) +{ + ZigType *actual_type = instruction->value->value.type; + LLVMValueRef expr_val = ir_llvm_value(g, instruction->value); + assert(expr_val); + + assert(actual_type->id == ZigTypeIdPointer); + ZigType *array_type = actual_type->data.pointer.child_type; + assert(array_type->id == ZigTypeIdArray); + + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, + slice_ptr_index, ""); + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->type_ref), + LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), + }; + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, slice_len_index, ""); + LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, + array_type->data.array.len, false); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + + return result_ptr; +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5492,6 +5499,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_to_bytes_len(g, executable, (IrInstructionToBytesLenGen *)instruction); case IrInstructionIdBswap: return ir_render_bswap(g, executable, (IrInstructionBswap *)instruction); + case IrInstructionIdPtrOfArrayToSlice: + return ir_render_ptr_of_array_to_slice(g, executable, (IrInstructionPtrOfArrayToSlice *)instruction); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index 3233cc0c926f..be141a78601f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -414,6 +414,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) { return IrInstructionIdCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrOfArrayToSlice *) { + return IrInstructionIdPtrOfArrayToSlice; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) { return IrInstructionIdContainerInitList; } @@ -1014,6 +1018,20 @@ static IrInstruction *ir_build_cast(IrBuilder *irb, Scope *scope, AstNode *sourc return &cast_instruction->base; } +static IrInstruction *ir_build_ptr_of_array_to_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *value, IrInstruction *result_loc) +{ + IrInstructionPtrOfArrayToSlice *instruction = ir_build_instruction( + irb, scope, source_node); + instruction->value = value; + instruction->result_loc = result_loc; + + ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *condition, IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime, IrInstruction *result_loc) { @@ -9997,7 +10015,6 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ zig_unreachable(); case CastOpErrSet: case CastOpBitCast: - case CastOpPtrOfArrayToSlice: zig_panic("TODO"); case CastOpNoop: { @@ -10185,8 +10202,10 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc } } - IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, - wanted_type, value, CastOpPtrOfArrayToSlice); + IrInstruction *result_loc = ir_analyze_alloca(ira, source_instr, wanted_type, 0, "slice"); + result_loc->value.special = ConstValSpecialRuntime; + IrInstruction *result = ir_build_ptr_of_array_to_slice(&ira->new_irb, source_instr->scope, + source_instr->source_node, value, result_loc); result->value.type = wanted_type; return result; } @@ -23268,6 +23287,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: + case IrInstructionIdPtrOfArrayToSlice: case IrInstructionIdDeclVarGen: case IrInstructionIdAllocaGen: case IrInstructionIdResultSlicePtr: @@ -23840,6 +23860,9 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdLoadPtr: return reinterpret_cast(instruction)->result_loc != nullptr; + case IrInstructionIdPtrOfArrayToSlice: + return reinterpret_cast(instruction)->result_loc != nullptr; + case IrInstructionIdAsm: { IrInstructionAsm *asm_instruction = (IrInstructionAsm *)instruction; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index d3a77d0035db..6afc735dc46a 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1482,6 +1482,14 @@ static void ir_print_error_literal(IrPrint *irp, IrInstructionErrorLiteral *inst fprintf(irp->f, "error.%s", buf_ptr(instruction->name)); } +static void ir_print_ptr_of_array_to_slice(IrPrint *irp, IrInstructionPtrOfArrayToSlice *instruction) { + fprintf(irp->f, "PtrOfArrayToSlice(value="); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ",result="); + ir_print_other_instruction(irp, instruction->result_loc); + fprintf(irp->f, ")"); +} + static void ir_print_bswap(IrPrint *irp, IrInstructionBswap *instruction) { fprintf(irp->f, "@bswap("); if (instruction->type != nullptr) { @@ -1991,6 +1999,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdErrorLiteral: ir_print_error_literal(irp, (IrInstructionErrorLiteral *)instruction); break; + case IrInstructionIdPtrOfArrayToSlice: + ir_print_ptr_of_array_to_slice(irp, (IrInstructionPtrOfArrayToSlice *)instruction); + break; } fprintf(irp->f, "\n"); } From ba80dd5a0acbcf67a50a2610226e9078889427e7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 23 Dec 2018 15:16:40 -0500 Subject: [PATCH 104/190] copy elision: fix ref instruction having no result loc ```zig pub fn set(comptime T: type, dest: []T, value: T) void { for (dest) |*d| d.* = value; } ``` ```llvm define internal fastcc void @set(%"[]u8"* nonnull readonly, i8) unnamed_addr #2 !dbg !55 { Entry: %for_index = alloca i64, align 8 %2 = alloca i8*, align 8 %value = alloca i8, align 1 call void @llvm.dbg.declare(metadata %"[]u8"* %0, metadata !60, metadata !DIExpression()), !dbg !71 store i8 %1, i8* %value, align 1 call void @llvm.dbg.declare(metadata i8* %value, metadata !64, metadata !DIExpression()), !dbg !72 store i64 0, i64* %for_index, align 8, !dbg !73 call void @llvm.dbg.declare(metadata i64* %for_index, metadata !66, metadata !DIExpression()), !dbg !75 %3 = getelementptr inbounds %"[]u8", %"[]u8"* %0, i32 0, i32 1, !dbg !73 %4 = load i64, i64* %3, align 8, !dbg !73 br label %ForCond, !dbg !73 ForCond: ; preds = %ForBody, %Entry %5 = load i64, i64* %for_index, align 8, !dbg !73 %6 = icmp ult i64 %5, %4, !dbg !73 br i1 %6, label %ForBody, label %ForElse, !dbg !73 ForBody: ; preds = %ForCond %7 = getelementptr inbounds %"[]u8", %"[]u8"* %0, i32 0, i32 0, !dbg !73 %8 = load i8*, i8** %7, align 8, !dbg !73 %9 = getelementptr inbounds i8, i8* %8, i64 %5, !dbg !73 store i8* %9, i8** %2, align 8, !dbg !76 call void @llvm.dbg.declare(metadata i8** %2, metadata !70, metadata !DIExpression()), !dbg !77 %10 = load i8*, i8** %2, align 8, !dbg !78 %11 = load i8, i8* %value, align 1, !dbg !79 store i8 %11, i8* %10, align 1, !dbg !79 %12 = add nuw i64 %5, 1, !dbg !73 store i64 %12, i64* %for_index, align 8, !dbg !73 br label %ForCond, !dbg !73 ForElse: ; preds = %ForCond ret void, !dbg !80 } ``` --- src/ir.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index be141a78601f..ae59a4bf006e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10916,6 +10916,10 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi ConstPtrMutComptimeConst, is_const, is_volatile, 0); } + if (result_loc == nullptr) { + result_loc = ir_analyze_alloca(ira, source_instruction, value->value.type, 0, ""); + result_loc->value.special = ConstValSpecialRuntime; + } ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, is_const, is_volatile, PtrLenSingle, 0, 0, 0); IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope, From 9839b3a85765d9349596d721a8050692afd83c08 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 23 Dec 2018 20:07:36 -0500 Subject: [PATCH 105/190] copy elision: fix inline for ```zig export fn entry() void { const bytes = "abcd"; inline for (bytes) |c, i| { @compileLog("i", i, "c", c); } } ``` Previously this gave i=0 for all 4 iterations, but now: ``` | "i", 0, "c", 97 | "i", 1, "c", 98 | "i", 2, "c", 99 | "i", 3, "c", 100 ``` --- src/all_types.hpp | 1 + src/ir.cpp | 52 ++++++++++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 827e47a8f90c..f51222d6d5b8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3431,6 +3431,7 @@ struct IrInstructionAllocaSrc { IrInstruction *child_type; IrInstruction *align; + IrInstruction *is_comptime; const char *name_hint; }; diff --git a/src/ir.cpp b/src/ir.cpp index ae59a4bf006e..0f4e3856465d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -180,7 +180,7 @@ static ZigType *adjust_ptr_child(CodeGen *g, ZigType *ptr_type, ZigType *new_chi static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *prev_result_loc, ZigType *elem_type, ZigType *child_type); static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, ZigType *child_type_inst, - uint32_t align, const char *name_hint); + uint32_t align, const char *name_hint, bool is_comptime); static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { @@ -3011,15 +3011,17 @@ static IrInstruction *ir_build_result_bytes_to_slice(IrBuilder *irb, Scope *scop } static IrInstruction *ir_build_alloca_src(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *child_type, IrInstruction *align, const char *name_hint) + IrInstruction *child_type, IrInstruction *align, const char *name_hint, IrInstruction *is_comptime) { IrInstructionAllocaSrc *instruction = ir_build_instruction(irb, scope, source_node); instruction->child_type = child_type; instruction->align = align; instruction->name_hint = name_hint; + instruction->is_comptime = is_comptime; if (child_type != nullptr) ir_ref_instruction(child_type, irb->current_basic_block); if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block); + if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); return &instruction->base; } @@ -3402,7 +3404,7 @@ static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { if (result_loc) return result_loc; - return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); + return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "", nullptr); } static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -5739,7 +5741,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod } IrInstruction *alloca = ir_build_alloca_src(irb, scope, node, type_instruction, align_value, - buf_ptr(variable_declaration->symbol)); + buf_ptr(variable_declaration->symbol), is_comptime); // Temporarily set the name of the IrExecutable to the VariableDeclaration // so that the struct or enum from the init expression inherits the name. @@ -6050,7 +6052,8 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo child_scope = index_var->child_scope; IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); - IrInstruction *index_alloca = ir_build_alloca_src(irb, child_scope, node, usize, nullptr, "for_index"); + IrInstruction *index_alloca = ir_build_alloca_src(irb, child_scope, node, usize, nullptr, "for_index", + is_comptime); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); ir_build_store_ptr(irb, child_scope, index_var_source_node, index_alloca, zero); @@ -7401,7 +7404,8 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); - IrInstruction *result_var_alloca = ir_build_alloca_src(irb, scope, node, promise_result_type, nullptr, ""); + IrInstruction *result_var_alloca = ir_build_alloca_src(irb, scope, node, promise_result_type, nullptr, "", + nullptr); ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, result_var_alloca); ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, result_var_alloca); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); @@ -7825,14 +7829,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type); IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa - coro_promise_ptr = ir_build_alloca_src(irb, coro_scope, node, coro_frame_type_value, nullptr, ""); + coro_promise_ptr = ir_build_alloca_src(irb, coro_scope, node, coro_frame_type_value, nullptr, "", nullptr); ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, coro_promise_ptr); ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - irb->exec->await_handle_var_ptr = ir_build_alloca_src(irb, coro_scope, node, await_handle_type_val, nullptr, ""); + irb->exec->await_handle_var_ptr = ir_build_alloca_src(irb, coro_scope, node, await_handle_type_val, nullptr, "", nullptr); ir_build_store_ptr(irb, coro_scope, node, irb->exec->await_handle_var_ptr, null_value); ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, irb->exec->await_handle_var_ptr); @@ -7843,13 +7847,13 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr); coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node); - IrInstruction *coro_size_var_alloca = ir_build_alloca_src(irb, coro_scope, node, nullptr, nullptr, ""); + IrInstruction *coro_size_var_alloca = ir_build_alloca_src(irb, coro_scope, node, nullptr, nullptr, "", nullptr); ir_build_store_ptr(irb, coro_scope, node, coro_size_var_alloca, coro_size); ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size_var_alloca); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node, ImplicitAllocatorIdArg); irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); - IrInstruction *coro_allocator_var_alloca = ir_build_alloca_src(irb, coro_scope, node, nullptr, nullptr, ""); + IrInstruction *coro_allocator_var_alloca = ir_build_alloca_src(irb, coro_scope, node, nullptr, nullptr, "", nullptr); ir_build_store_ptr(irb, coro_scope, node, coro_allocator_var_alloca, implicit_allocator_ptr); ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, coro_allocator_var_alloca); @@ -8019,7 +8023,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false, nullptr); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr, nullptr); - IrInstruction *mem_slice_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, ""); + IrInstruction *mem_slice_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "", nullptr); IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false, mem_slice_alloca); size_t arg_count = 2; @@ -10202,7 +10206,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc } } - IrInstruction *result_loc = ir_analyze_alloca(ira, source_instr, wanted_type, 0, "slice"); + IrInstruction *result_loc = ir_analyze_alloca(ira, source_instr, wanted_type, 0, "slice", false); result_loc->value.special = ConstValSpecialRuntime; IrInstruction *result = ir_build_ptr_of_array_to_slice(&ira->new_irb, source_instr->scope, source_instr->source_node, value, result_loc); @@ -10917,7 +10921,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi } if (result_loc == nullptr) { - result_loc = ir_analyze_alloca(ira, source_instruction, value->value.type, 0, ""); + result_loc = ir_analyze_alloca(ira, source_instruction, value->value.type, 0, "", false); result_loc->value.special = ConstValSpecialRuntime; } ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, @@ -13684,6 +13688,9 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, ConstExprValue *init_val = nullptr; if (instr_is_comptime(var_ptr) && var_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { init_val = const_ptr_pointee(ira, ira->codegen, &var_ptr->value, decl_var_instruction->base.source_node); + if (is_comptime_var) { + var->value = init_val; + } } switch (type_requires_comptime(ira->codegen, result_type)) { @@ -14449,7 +14456,7 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, } else { need_store_ptr = false; if (return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdOptional) { - prev_result_loc = ir_analyze_alloca(ira, &call_instruction->base, nullptr, 0, "param"); + prev_result_loc = ir_analyze_alloca(ira, &call_instruction->base, nullptr, 0, "param", false); if (type_is_invalid(prev_result_loc->value.type)) return ira->codegen->invalid_instruction; } @@ -14548,7 +14555,7 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, } ZigType *opt_err_type = get_optional_type(ira->codegen, err_type); IrInstruction *err_code_alloca = ir_analyze_alloca(ira, &call_instruction->base, - opt_err_type, 0, "err"); + opt_err_type, 0, "err", false); ir_analyze_store_ptr(ira, &call_instruction->base, err_code_alloca, new_call_instruction); return err_code_alloca; } else { @@ -22855,7 +22862,7 @@ static IrInstruction *ir_analyze_instruction_result_cast(IrAnalyze *ira, IrInstr } static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, ZigType *var_type, - uint32_t align, const char *name_hint) + uint32_t align, const char *name_hint, bool force_comptime) { Error err; @@ -22866,7 +22873,7 @@ static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_in source_inst->source_node, align, name_hint); result->base.value.special = ConstValSpecialStatic; result->base.value.data.x_ptr.special = ConstPtrSpecialRef; - result->base.value.data.x_ptr.mut = ConstPtrMutInfer; + result->base.value.data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutInfer; result->base.value.data.x_ptr.data.ref.pointee = pointee; if (var_type == nullptr) { @@ -22894,6 +22901,12 @@ static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructio } } + bool is_comptime = false; + if (instruction->is_comptime != nullptr) { + if (!ir_resolve_bool(ira, instruction->is_comptime->child, &is_comptime)) + return ira->codegen->invalid_instruction; + } + ZigType *var_type = nullptr; if (instruction->child_type != nullptr) { var_type = ir_resolve_type(ira, instruction->child_type->child); @@ -22901,7 +22914,7 @@ static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructio return ira->codegen->invalid_instruction; } - return ir_analyze_alloca(ira, &instruction->base, var_type, align, instruction->name_hint); + return ir_analyze_alloca(ira, &instruction->base, var_type, align, instruction->name_hint, is_comptime); } static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira, @@ -22944,7 +22957,8 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira if (param_type != nullptr && type_is_invalid(param_type)) return ira->codegen->invalid_instruction; - return ir_analyze_alloca(ira, &instruction->base, param_type, 0, ""); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); + return ir_analyze_alloca(ira, &instruction->base, param_type, 0, "", is_comptime); } static IrInstruction *ir_analyze_instruction_infer_array_type(IrAnalyze *ira, From bf41ccc581a25364a0b295692f5e47c8aaca4072 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 23 Dec 2018 20:10:13 -0500 Subject: [PATCH 106/190] copy elision: move the first behavior test to copy elision This commit begins a process of migrating from * test/behavior.zig * test/cases/* to * test/stage1/behavior.zig * test/stage1/behavior/* Each test that this copy-elision branch is able to pass will be moved to the new location. Once all tests pass, it's time to merge into master. This commit simplifies the default panic handler and bootstrap code to disable stack traces, since those are not yet working in the copy-elision branch. Once that code works again, they can be re-enabled. That must happen before merging into master. --- std/special/bootstrap.zig | 11 ++++++----- std/special/panic.zig | 7 +++++-- test/cases/misc.zig | 10 ---------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index a15be317ab08..c91ecb0b31f0 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -111,11 +111,12 @@ inline fn callMain() u8 { builtin.TypeId.ErrorUnion => { root.main() catch |err| { std.debug.warn("error: {}\n", @errorName(err)); - if (builtin.os != builtin.Os.zen) { - if (@errorReturnTrace()) |trace| { - std.debug.dumpStackTrace(trace); - } - } + // TODO restore this + //if (builtin.os != builtin.Os.zen) { + // if (@errorReturnTrace()) |trace| { + // std.debug.dumpStackTrace(trace); + // } + //} return 1; }; return 0; diff --git a/std/special/panic.zig b/std/special/panic.zig index ca1caea73cae..d9cf23779425 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -14,8 +14,11 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn while (true) {} }, else => { - const first_trace_addr = @ptrToInt(@returnAddress()); - std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); + // TODO put the original panic code back + std.debug.warn("panic: {}\n", msg); + std.os.abort(); + //const first_trace_addr = @ptrToInt(@returnAddress()); + //std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); }, } } diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 1a34d54e9e15..ee4cfb87d549 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -5,16 +5,6 @@ const cstr = std.cstr; const builtin = @import("builtin"); const maxInt = std.math.maxInt; -// normal comment - -/// this is a documentation comment -/// doc comment line 2 -fn emptyFunctionWithComments() void {} - -test "empty function with comments" { - emptyFunctionWithComments(); -} - comptime { @export("disabledExternFn", disabledExternFn, builtin.GlobalLinkage.Internal); } From d0bd1a6bdde2b50db8c839b18a5dae7535ee36e4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Dec 2018 14:40:06 -0500 Subject: [PATCH 107/190] copy elision: move passing tests --- test/cases/misc.zig | 96 --------------------------------- test/stage1/behavior/misc.zig | 99 ++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 97 deletions(-) diff --git a/test/cases/misc.zig b/test/cases/misc.zig index ee4cfb87d549..edc2e57d8f10 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -5,102 +5,6 @@ const cstr = std.cstr; const builtin = @import("builtin"); const maxInt = std.math.maxInt; -comptime { - @export("disabledExternFn", disabledExternFn, builtin.GlobalLinkage.Internal); -} - -extern fn disabledExternFn() void {} - -test "call disabled extern fn" { - disabledExternFn(); -} - -test "@IntType builtin" { - assert(@IntType(true, 8) == i8); - assert(@IntType(true, 16) == i16); - assert(@IntType(true, 32) == i32); - assert(@IntType(true, 64) == i64); - - assert(@IntType(false, 8) == u8); - assert(@IntType(false, 16) == u16); - assert(@IntType(false, 32) == u32); - assert(@IntType(false, 64) == u64); - - assert(i8.bit_count == 8); - assert(i16.bit_count == 16); - assert(i32.bit_count == 32); - assert(i64.bit_count == 64); - - assert(i8.is_signed); - assert(i16.is_signed); - assert(i32.is_signed); - assert(i64.is_signed); - assert(isize.is_signed); - - assert(!u8.is_signed); - assert(!u16.is_signed); - assert(!u32.is_signed); - assert(!u64.is_signed); - assert(!usize.is_signed); -} - -test "floating point primitive bit counts" { - assert(f16.bit_count == 16); - assert(f32.bit_count == 32); - assert(f64.bit_count == 64); -} - -test "short circuit" { - testShortCircuit(false, true); - comptime testShortCircuit(false, true); -} - -fn testShortCircuit(f: bool, t: bool) void { - var hit_1 = f; - var hit_2 = f; - var hit_3 = f; - var hit_4 = f; - - if (t or x: { - assert(f); - break :x f; - }) { - hit_1 = t; - } - if (f or x: { - hit_2 = t; - break :x f; - }) { - assert(f); - } - - if (t and x: { - hit_3 = t; - break :x f; - }) { - assert(f); - } - if (f and x: { - assert(f); - break :x f; - }) { - assert(f); - } else { - hit_4 = t; - } - assert(hit_1); - assert(hit_2); - assert(hit_3); - assert(hit_4); -} - -test "truncate" { - assert(testTruncate(0x10fd) == 0xfd); -} -fn testTruncate(x: u32) u8 { - return @truncate(u8, x); -} - fn first4KeysOfHomeRow() []const u8 { return "aoeu"; } diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 156433c804fd..dc7bcea204fd 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const cstr = std.cstr; const builtin = @import("builtin"); @@ -14,3 +14,100 @@ fn emptyFunctionWithComments() void {} test "empty function with comments" { emptyFunctionWithComments(); } + +comptime { + @export("disabledExternFn", disabledExternFn, builtin.GlobalLinkage.Internal); +} + +extern fn disabledExternFn() void {} + +test "call disabled extern fn" { + disabledExternFn(); +} + +test "@IntType builtin" { + assertOrPanic(@IntType(true, 8) == i8); + assertOrPanic(@IntType(true, 16) == i16); + assertOrPanic(@IntType(true, 32) == i32); + assertOrPanic(@IntType(true, 64) == i64); + + assertOrPanic(@IntType(false, 8) == u8); + assertOrPanic(@IntType(false, 16) == u16); + assertOrPanic(@IntType(false, 32) == u32); + assertOrPanic(@IntType(false, 64) == u64); + + assertOrPanic(i8.bit_count == 8); + assertOrPanic(i16.bit_count == 16); + assertOrPanic(i32.bit_count == 32); + assertOrPanic(i64.bit_count == 64); + + assertOrPanic(i8.is_signed); + assertOrPanic(i16.is_signed); + assertOrPanic(i32.is_signed); + assertOrPanic(i64.is_signed); + assertOrPanic(isize.is_signed); + + assertOrPanic(!u8.is_signed); + assertOrPanic(!u16.is_signed); + assertOrPanic(!u32.is_signed); + assertOrPanic(!u64.is_signed); + assertOrPanic(!usize.is_signed); +} + +test "floating point primitive bit counts" { + assertOrPanic(f16.bit_count == 16); + assertOrPanic(f32.bit_count == 32); + assertOrPanic(f64.bit_count == 64); +} + +test "short circuit" { + testShortCircuit(false, true); + comptime testShortCircuit(false, true); +} + +fn testShortCircuit(f: bool, t: bool) void { + var hit_1 = f; + var hit_2 = f; + var hit_3 = f; + var hit_4 = f; + + if (t or x: { + assertOrPanic(f); + break :x f; + }) { + hit_1 = t; + } + if (f or x: { + hit_2 = t; + break :x f; + }) { + assertOrPanic(f); + } + + if (t and x: { + hit_3 = t; + break :x f; + }) { + assertOrPanic(f); + } + if (f and x: { + assertOrPanic(f); + break :x f; + }) { + assertOrPanic(f); + } else { + hit_4 = t; + } + assertOrPanic(hit_1); + assertOrPanic(hit_2); + assertOrPanic(hit_3); + assertOrPanic(hit_4); +} + +test "truncate" { + assertOrPanic(testTruncate(0x10fd) == 0xfd); +} +fn testTruncate(x: u32) u8 { + return @truncate(u8, x); +} + From 36506e622c8f12faaa3ccf87f6a711832c6b112b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Dec 2018 15:20:25 -0500 Subject: [PATCH 108/190] copy elision: fix fn call as fn parameter ```zig export fn entry() void { foo(first4KeysOfHomeRow()); } ``` ```llvm define void @entry() #2 !dbg !41 { Entry: %0 = alloca %"[]u8", align 8 call fastcc void @first4KeysOfHomeRow(%"[]u8"* sret %0), !dbg !45 call fastcc void @foo(%"[]u8"* %0), !dbg !47 ret void, !dbg !48 } ``` --- src/ir.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 0f4e3856465d..6f5557394848 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14415,10 +14415,12 @@ static IrInstruction *ir_analyze_set_non_null_bit(IrAnalyze *ira, source_instr->scope, source_instr->source_node, base_ptr, non_null_bit, nullptr); } -static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, +static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, IrInstructionCall *call_instruction, ZigFn *fn_entry, IrInstruction *fn_ref, size_t call_param_count, IrInstruction **casted_args, FnInline fn_inline, IrInstruction *casted_new_stack) { + ZigType *return_type = fn_type_id->return_type; + // For an optional or error union, this result location is a pointer to the payload field. // For other types this is a base pointer to the value. IrInstruction *payload_result_loc = nullptr; @@ -14455,8 +14457,11 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, ZigType *return_type, prev_result_loc = call_instruction->result_loc->child; } else { need_store_ptr = false; - if (return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdOptional) { - prev_result_loc = ir_analyze_alloca(ira, &call_instruction->base, nullptr, 0, "param", false); + if (return_type->id == ZigTypeIdErrorUnion || + return_type->id == ZigTypeIdOptional || + (type_has_bits(return_type) && want_first_arg_sret(ira->codegen, fn_type_id))) + { + prev_result_loc = ir_analyze_alloca(ira, &call_instruction->base, nullptr, 0, "", false); if (type_is_invalid(prev_result_loc->value.type)) return ira->codegen->invalid_instruction; } @@ -15080,20 +15085,20 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ira->codegen->fn_defs.append(impl_fn); } - ZigType *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; - if (fn_type_can_fail(&impl_fn->type_entry->data.fn.fn_type_id)) { + FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id; + if (fn_type_can_fail(impl_fn_type_id)) { parent_fn_entry->calls_or_awaits_errorable_fn = true; } - size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count; + size_t impl_param_count = impl_fn_type_id->param_count; if (call_instruction->is_async) { IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, fn_ref, casted_args, impl_param_count, async_allocator_inst); return ir_finish_anal(ira, result); } - return analyze_runtime_call(ira, return_type, call_instruction, impl_fn, nullptr, - impl_param_count, casted_args, fn_inline, casted_new_stack); + return analyze_runtime_call(ira, impl_fn_type_id, call_instruction, impl_fn, + nullptr, impl_param_count, casted_args, fn_inline, casted_new_stack); } ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); @@ -15185,7 +15190,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; } - return analyze_runtime_call(ira, return_type, call_instruction, fn_entry, fn_ref, + return analyze_runtime_call(ira, fn_type_id, call_instruction, fn_entry, fn_ref, call_param_count, casted_args, fn_inline, casted_new_stack); } From a460153e7e699862f5223035e68a994d7a36d9bc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Dec 2018 15:26:12 -0500 Subject: [PATCH 109/190] copy elision: move passing tests --- test/cases/misc.zig | 385 ++++++---------------------------- test/stage1/behavior/misc.zig | 263 +++++++++++++++++++++++ 2 files changed, 324 insertions(+), 324 deletions(-) diff --git a/test/cases/misc.zig b/test/cases/misc.zig index edc2e57d8f10..77bbe21a07c7 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -1,275 +1,12 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const cstr = std.cstr; const builtin = @import("builtin"); const maxInt = std.math.maxInt; -fn first4KeysOfHomeRow() []const u8 { - return "aoeu"; -} - -test "return string from function" { - assert(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); -} - -const g1: i32 = 1233 + 1; -var g2: i32 = 0; - -test "global variables" { - assert(g2 == 0); - g2 = g1; - assert(g2 == 1234); -} - -test "memcpy and memset intrinsics" { - var foo: [20]u8 = undefined; - var bar: [20]u8 = undefined; - - @memset(foo[0..].ptr, 'A', foo.len); - @memcpy(bar[0..].ptr, foo[0..].ptr, bar.len); - - if (bar[11] != 'A') unreachable; -} - -test "builtin static eval" { - const x: i32 = comptime x: { - break :x 1 + 2 + 3; - }; - assert(x == comptime 6); -} - -test "slicing" { - var array: [20]i32 = undefined; - - array[5] = 1234; - - var slice = array[5..10]; - - if (slice.len != 5) unreachable; - - const ptr = &slice[0]; - if (ptr.* != 1234) unreachable; - - var slice_rest = array[10..]; - if (slice_rest.len != 10) unreachable; -} - -test "constant equal function pointers" { - const alias = emptyFn; - assert(comptime x: { - break :x emptyFn == alias; - }); -} - -fn emptyFn() void {} - -test "hex escape" { - assert(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); -} - -test "string concatenation" { - assert(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); -} - -test "array mult operator" { - assert(mem.eql(u8, "ab" ** 5, "ababababab")); -} - -test "string escapes" { - assert(mem.eql(u8, "\"", "\x22")); - assert(mem.eql(u8, "\'", "\x27")); - assert(mem.eql(u8, "\n", "\x0a")); - assert(mem.eql(u8, "\r", "\x0d")); - assert(mem.eql(u8, "\t", "\x09")); - assert(mem.eql(u8, "\\", "\x5c")); - assert(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); -} - -test "multiline string" { - const s1 = - \\one - \\two) - \\three - ; - const s2 = "one\ntwo)\nthree"; - assert(mem.eql(u8, s1, s2)); -} - -test "multiline C string" { - const s1 = - c\\one - c\\two) - c\\three - ; - const s2 = c"one\ntwo)\nthree"; - assert(cstr.cmp(s1, s2) == 0); -} - -test "type equality" { - assert(*const u8 != *u8); -} - -const global_a: i32 = 1234; -const global_b: *const i32 = &global_a; -const global_c: *const f32 = @ptrCast(*const f32, global_b); -test "compile time global reinterpret" { - const d = @ptrCast(*const i32, global_c); - assert(d.* == 1234); -} - -test "explicit cast maybe pointers" { - const a: ?*i32 = undefined; - const b: ?*f32 = @ptrCast(?*f32, a); -} - -test "generic malloc free" { - const a = memAlloc(u8, 10) catch unreachable; - memFree(u8, a); -} -var some_mem: [100]u8 = undefined; -fn memAlloc(comptime T: type, n: usize) anyerror![]T { - return @ptrCast([*]T, &some_mem[0])[0..n]; -} -fn memFree(comptime T: type, memory: []T) void {} - -test "cast undefined" { - const array: [100]u8 = undefined; - const slice = ([]const u8)(array); - testCastUndefined(slice); -} -fn testCastUndefined(x: []const u8) void {} - -test "cast small unsigned to larger signed" { - assert(castSmallUnsignedToLargerSigned1(200) == i16(200)); - assert(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); -} -fn castSmallUnsignedToLargerSigned1(x: u8) i16 { - return x; -} -fn castSmallUnsignedToLargerSigned2(x: u16) i64 { - return x; -} - -test "implicit cast after unreachable" { - assert(outer() == 1234); -} -fn inner() i32 { - return 1234; -} -fn outer() i64 { - return inner(); -} - -test "pointer dereferencing" { - var x = i32(3); - const y = &x; - - y.* += 1; - - assert(x == 4); - assert(y.* == 4); -} - -test "call result of if else expression" { - assert(mem.eql(u8, f2(true), "a")); - assert(mem.eql(u8, f2(false), "b")); -} -fn f2(x: bool) []const u8 { - return (if (x) fA else fB)(); -} -fn fA() []const u8 { - return "a"; -} -fn fB() []const u8 { - return "b"; -} - -test "const expression eval handling of variables" { - var x = true; - while (x) { - x = false; - } -} - -test "constant enum initialization with differing sizes" { - test3_1(test3_foo); - test3_2(test3_bar); -} -const Test3Foo = union(enum) { - One: void, - Two: f32, - Three: Test3Point, -}; -const Test3Point = struct { - x: i32, - y: i32, -}; -const test3_foo = Test3Foo{ - .Three = Test3Point{ - .x = 3, - .y = 4, - }, -}; -const test3_bar = Test3Foo{ .Two = 13 }; -fn test3_1(f: Test3Foo) void { - switch (f) { - Test3Foo.Three => |pt| { - assert(pt.x == 3); - assert(pt.y == 4); - }, - else => unreachable, - } -} -fn test3_2(f: Test3Foo) void { - switch (f) { - Test3Foo.Two => |x| { - assert(x == 13); - }, - else => unreachable, - } -} - -test "character literals" { - assert('\'' == single_quote); -} -const single_quote = '\''; - -test "take address of parameter" { - testTakeAddressOfParameter(12.34); -} -fn testTakeAddressOfParameter(f: f32) void { - const f_ptr = &f; - assert(f_ptr.* == 12.34); -} - -test "pointer comparison" { - const a = ([]const u8)("a"); - const b = &a; - assert(ptrEql(b, b)); -} -fn ptrEql(a: *const []const u8, b: *const []const u8) bool { - return a == b; -} - -test "C string concatenation" { - const a = c"OK" ++ c" IT " ++ c"WORKED"; - const b = c"OK IT WORKED"; - - const len = cstr.len(b); - const len_with_null = len + 1; - { - var i: u32 = 0; - while (i < len_with_null) : (i += 1) { - assert(a[i] == b[i]); - } - } - assert(a[len] == 0); - assert(b[len] == 0); -} - test "cast slice to u8 slice" { - assert(@sizeOf(i32) == 4); + assertOrPanic(@sizeOf(i32) == 4); var big_thing_array = []i32{ 1, 2, @@ -278,19 +15,19 @@ test "cast slice to u8 slice" { }; const big_thing_slice: []i32 = big_thing_array[0..]; const bytes = @sliceToBytes(big_thing_slice); - assert(bytes.len == 4 * 4); + assertOrPanic(bytes.len == 4 * 4); bytes[4] = 0; bytes[5] = 0; bytes[6] = 0; bytes[7] = 0; - assert(big_thing_slice[1] == 0); + assertOrPanic(big_thing_slice[1] == 0); const big_thing_again = @bytesToSlice(i32, bytes); - assert(big_thing_again[2] == 3); + assertOrPanic(big_thing_again[2] == 3); big_thing_again[2] = -1; - assert(bytes[8] == maxInt(u8)); - assert(bytes[9] == maxInt(u8)); - assert(bytes[10] == maxInt(u8)); - assert(bytes[11] == maxInt(u8)); + assertOrPanic(bytes[8] == maxInt(u8)); + assertOrPanic(bytes[9] == maxInt(u8)); + assertOrPanic(bytes[10] == maxInt(u8)); + assertOrPanic(bytes[11] == maxInt(u8)); } test "pointer to void return type" { @@ -307,7 +44,7 @@ fn testPointerToVoidReturnType2() *const void { test "non const ptr to aliased type" { const int = i32; - assert(?*int == ?*i32); + assertOrPanic(?*int == ?*i32); } test "array 2D const double ptr" { @@ -320,8 +57,8 @@ test "array 2D const double ptr" { fn testArray2DConstDoublePtr(ptr: *const f32) void { const ptr2 = @ptrCast([*]const f32, ptr); - assert(ptr2[0] == 1.0); - assert(ptr2[1] == 2.0); + assertOrPanic(ptr2[0] == 1.0); + assertOrPanic(ptr2[1] == 2.0); } const Tid = builtin.TypeId; @@ -343,32 +80,32 @@ const AUnion = union { test "@typeId" { comptime { - assert(@typeId(type) == Tid.Type); - assert(@typeId(void) == Tid.Void); - assert(@typeId(bool) == Tid.Bool); - assert(@typeId(noreturn) == Tid.NoReturn); - assert(@typeId(i8) == Tid.Int); - assert(@typeId(u8) == Tid.Int); - assert(@typeId(i64) == Tid.Int); - assert(@typeId(u64) == Tid.Int); - assert(@typeId(f32) == Tid.Float); - assert(@typeId(f64) == Tid.Float); - assert(@typeId(*f32) == Tid.Pointer); - assert(@typeId([2]u8) == Tid.Array); - assert(@typeId(AStruct) == Tid.Struct); - assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); - assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); - assert(@typeId(@typeOf(undefined)) == Tid.Undefined); - assert(@typeId(@typeOf(null)) == Tid.Null); - assert(@typeId(?i32) == Tid.Optional); - assert(@typeId(anyerror!i32) == Tid.ErrorUnion); - assert(@typeId(anyerror) == Tid.ErrorSet); - assert(@typeId(AnEnum) == Tid.Enum); - assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); - assert(@typeId(AUnionEnum) == Tid.Union); - assert(@typeId(AUnion) == Tid.Union); - assert(@typeId(fn () void) == Tid.Fn); - assert(@typeId(@typeOf(builtin)) == Tid.Namespace); + assertOrPanic(@typeId(type) == Tid.Type); + assertOrPanic(@typeId(void) == Tid.Void); + assertOrPanic(@typeId(bool) == Tid.Bool); + assertOrPanic(@typeId(noreturn) == Tid.NoReturn); + assertOrPanic(@typeId(i8) == Tid.Int); + assertOrPanic(@typeId(u8) == Tid.Int); + assertOrPanic(@typeId(i64) == Tid.Int); + assertOrPanic(@typeId(u64) == Tid.Int); + assertOrPanic(@typeId(f32) == Tid.Float); + assertOrPanic(@typeId(f64) == Tid.Float); + assertOrPanic(@typeId(*f32) == Tid.Pointer); + assertOrPanic(@typeId([2]u8) == Tid.Array); + assertOrPanic(@typeId(AStruct) == Tid.Struct); + assertOrPanic(@typeId(@typeOf(1)) == Tid.ComptimeInt); + assertOrPanic(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); + assertOrPanic(@typeId(@typeOf(undefined)) == Tid.Undefined); + assertOrPanic(@typeId(@typeOf(null)) == Tid.Null); + assertOrPanic(@typeId(?i32) == Tid.Optional); + assertOrPanic(@typeId(anyerror!i32) == Tid.ErrorUnion); + assertOrPanic(@typeId(anyerror) == Tid.ErrorSet); + assertOrPanic(@typeId(AnEnum) == Tid.Enum); + assertOrPanic(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); + assertOrPanic(@typeId(AUnionEnum) == Tid.Union); + assertOrPanic(@typeId(AUnion) == Tid.Union); + assertOrPanic(@typeId(fn () void) == Tid.Fn); + assertOrPanic(@typeId(@typeOf(builtin)) == Tid.Namespace); // TODO bound fn // TODO arg tuple // TODO opaque @@ -384,13 +121,13 @@ test "@typeName" { Unused, }; comptime { - assert(mem.eql(u8, @typeName(i64), "i64")); - assert(mem.eql(u8, @typeName(*usize), "*usize")); + assertOrPanic(mem.eql(u8, @typeName(i64), "i64")); + assertOrPanic(mem.eql(u8, @typeName(*usize), "*usize")); // https://github.com/ziglang/zig/issues/675 - assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); - assert(mem.eql(u8, @typeName(Struct), "Struct")); - assert(mem.eql(u8, @typeName(Union), "Union")); - assert(mem.eql(u8, @typeName(Enum), "Enum")); + assertOrPanic(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); + assertOrPanic(mem.eql(u8, @typeName(Struct), "Struct")); + assertOrPanic(mem.eql(u8, @typeName(Union), "Union")); + assertOrPanic(mem.eql(u8, @typeName(Enum), "Enum")); } } @@ -402,24 +139,24 @@ test "volatile load and store" { var number: i32 = 1234; const ptr = (*volatile i32)(&number); ptr.* += 1; - assert(ptr.* == 1235); + assertOrPanic(ptr.* == 1235); } test "slice string literal has type []const u8" { comptime { - assert(@typeOf("aoeu"[0..]) == []const u8); + assertOrPanic(@typeOf("aoeu"[0..]) == []const u8); const array = []i32{ 1, 2, 3, 4, }; - assert(@typeOf(array[0..]) == []const i32); + assertOrPanic(@typeOf(array[0..]) == []const i32); } } test "global variable initialized to global variable array element" { - assert(global_ptr == &gdt[0]); + assertOrPanic(global_ptr == &gdt[0]); } const GDTEntry = struct { field: i32, @@ -438,15 +175,15 @@ export fn writeToVRam() void { } test "pointer child field" { - assert((*u32).Child == u32); + assertOrPanic((*u32).Child == u32); } const OpaqueA = @OpaqueType(); const OpaqueB = @OpaqueType(); test "@OpaqueType" { - assert(*OpaqueA != *OpaqueB); - assert(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); - assert(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); + assertOrPanic(*OpaqueA != *OpaqueB); + assertOrPanic(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); + assertOrPanic(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); } test "variable is allowed to be a pointer to an opaque type" { @@ -491,7 +228,7 @@ fn testStructInFn() void { block.kind += 1; - assert(block.kind == 1235); + assertOrPanic(block.kind == 1235); } fn fnThatClosesOverLocalConst() type { @@ -505,7 +242,7 @@ fn fnThatClosesOverLocalConst() type { test "function closes over local const" { const x = fnThatClosesOverLocalConst().g(); - assert(x == 1); + assertOrPanic(x == 1); } test "cold function" { @@ -542,21 +279,21 @@ export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: Pack test "slicing zero length array" { const s1 = ""[0..]; const s2 = ([]u32{})[0..]; - assert(s1.len == 0); - assert(s2.len == 0); - assert(mem.eql(u8, s1, "")); - assert(mem.eql(u32, s2, []u32{})); + assertOrPanic(s1.len == 0); + assertOrPanic(s2.len == 0); + assertOrPanic(mem.eql(u8, s1, "")); + assertOrPanic(mem.eql(u32, s2, []u32{})); } const addr1 = @ptrCast(*const u8, emptyFn); test "comptime cast fn to ptr" { const addr2 = @ptrCast(*const u8, emptyFn); - comptime assert(addr1 == addr2); + comptime assertOrPanic(addr1 == addr2); } test "equality compare fn ptrs" { var a = emptyFn; - assert(a == a); + assertOrPanic(a == a); } test "self reference through fn ptr field" { @@ -571,5 +308,5 @@ test "self reference through fn ptr field" { }; var a: S.A = undefined; a.f = S.foo; - assert(a.f(a) == 12); + assertOrPanic(a.f(a) == 12); } diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index dc7bcea204fd..eac3fe8e7466 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -111,3 +111,266 @@ fn testTruncate(x: u32) u8 { return @truncate(u8, x); } +fn first4KeysOfHomeRow() []const u8 { + return "aoeu"; +} + +test "return string from function" { + assertOrPanic(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); +} + +const g1: i32 = 1233 + 1; +var g2: i32 = 0; + +test "global variables" { + assertOrPanic(g2 == 0); + g2 = g1; + assertOrPanic(g2 == 1234); +} + +test "memcpy and memset intrinsics" { + var foo: [20]u8 = undefined; + var bar: [20]u8 = undefined; + + @memset(foo[0..].ptr, 'A', foo.len); + @memcpy(bar[0..].ptr, foo[0..].ptr, bar.len); + + if (bar[11] != 'A') unreachable; +} + +test "builtin static eval" { + const x: i32 = comptime x: { + break :x 1 + 2 + 3; + }; + assertOrPanic(x == comptime 6); +} + +test "slicing" { + var array: [20]i32 = undefined; + + array[5] = 1234; + + var slice = array[5..10]; + + if (slice.len != 5) unreachable; + + const ptr = &slice[0]; + if (ptr.* != 1234) unreachable; + + var slice_rest = array[10..]; + if (slice_rest.len != 10) unreachable; +} + +test "constant equal function pointers" { + const alias = emptyFn; + assertOrPanic(comptime x: { + break :x emptyFn == alias; + }); +} + +fn emptyFn() void {} + +test "hex escape" { + assertOrPanic(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); +} + +test "string concatenation" { + assertOrPanic(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); +} + +test "array mult operator" { + assertOrPanic(mem.eql(u8, "ab" ** 5, "ababababab")); +} + +test "string escapes" { + assertOrPanic(mem.eql(u8, "\"", "\x22")); + assertOrPanic(mem.eql(u8, "\'", "\x27")); + assertOrPanic(mem.eql(u8, "\n", "\x0a")); + assertOrPanic(mem.eql(u8, "\r", "\x0d")); + assertOrPanic(mem.eql(u8, "\t", "\x09")); + assertOrPanic(mem.eql(u8, "\\", "\x5c")); + assertOrPanic(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); +} + +test "multiline string" { + const s1 = + \\one + \\two) + \\three + ; + const s2 = "one\ntwo)\nthree"; + assertOrPanic(mem.eql(u8, s1, s2)); +} + +test "multiline C string" { + const s1 = + c\\one + c\\two) + c\\three + ; + const s2 = c"one\ntwo)\nthree"; + assertOrPanic(cstr.cmp(s1, s2) == 0); +} + +test "type equality" { + assertOrPanic(*const u8 != *u8); +} + +const global_a: i32 = 1234; +const global_b: *const i32 = &global_a; +const global_c: *const f32 = @ptrCast(*const f32, global_b); +test "compile time global reinterpret" { + const d = @ptrCast(*const i32, global_c); + assertOrPanic(d.* == 1234); +} + +test "explicit cast maybe pointers" { + const a: ?*i32 = undefined; + const b: ?*f32 = @ptrCast(?*f32, a); +} + +test "generic malloc free" { + const a = memAlloc(u8, 10) catch unreachable; + memFree(u8, a); +} +var some_mem: [100]u8 = undefined; +fn memAlloc(comptime T: type, n: usize) anyerror![]T { + return @ptrCast([*]T, &some_mem[0])[0..n]; +} +fn memFree(comptime T: type, memory: []T) void {} + +test "cast undefined" { + const array: [100]u8 = undefined; + const slice = ([]const u8)(array); + testCastUndefined(slice); +} +fn testCastUndefined(x: []const u8) void {} + +test "cast small unsigned to larger signed" { + assertOrPanic(castSmallUnsignedToLargerSigned1(200) == i16(200)); + assertOrPanic(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); +} +fn castSmallUnsignedToLargerSigned1(x: u8) i16 { + return x; +} +fn castSmallUnsignedToLargerSigned2(x: u16) i64 { + return x; +} + +test "implicit cast after unreachable" { + assertOrPanic(outer() == 1234); +} +fn inner() i32 { + return 1234; +} +fn outer() i64 { + return inner(); +} + +test "pointer dereferencing" { + var x = i32(3); + const y = &x; + + y.* += 1; + + assertOrPanic(x == 4); + assertOrPanic(y.* == 4); +} + +test "call result of if else expression" { + assertOrPanic(mem.eql(u8, f2(true), "a")); + assertOrPanic(mem.eql(u8, f2(false), "b")); +} +fn f2(x: bool) []const u8 { + return (if (x) fA else fB)(); +} +fn fA() []const u8 { + return "a"; +} +fn fB() []const u8 { + return "b"; +} + +test "const expression eval handling of variables" { + var x = true; + while (x) { + x = false; + } +} + +test "constant enum initialization with differing sizes" { + test3_1(test3_foo); + test3_2(test3_bar); +} +const Test3Foo = union(enum) { + One: void, + Two: f32, + Three: Test3Point, +}; +const Test3Point = struct { + x: i32, + y: i32, +}; +const test3_foo = Test3Foo{ + .Three = Test3Point{ + .x = 3, + .y = 4, + }, +}; +const test3_bar = Test3Foo{ .Two = 13 }; +fn test3_1(f: Test3Foo) void { + switch (f) { + Test3Foo.Three => |pt| { + assertOrPanic(pt.x == 3); + assertOrPanic(pt.y == 4); + }, + else => unreachable, + } +} +fn test3_2(f: Test3Foo) void { + switch (f) { + Test3Foo.Two => |x| { + assertOrPanic(x == 13); + }, + else => unreachable, + } +} + +test "character literals" { + assertOrPanic('\'' == single_quote); +} +const single_quote = '\''; + +test "take address of parameter" { + testTakeAddressOfParameter(12.34); +} +fn testTakeAddressOfParameter(f: f32) void { + const f_ptr = &f; + assertOrPanic(f_ptr.* == 12.34); +} + +test "pointer comparison" { + const a = ([]const u8)("a"); + const b = &a; + assertOrPanic(ptrEql(b, b)); +} +fn ptrEql(a: *const []const u8, b: *const []const u8) bool { + return a == b; +} + +test "C string concatenation" { + const a = c"OK" ++ c" IT " ++ c"WORKED"; + const b = c"OK IT WORKED"; + + const len = cstr.len(b); + const len_with_null = len + 1; + { + var i: u32 = 0; + while (i < len_with_null) : (i += 1) { + assertOrPanic(a[i] == b[i]); + } + } + assertOrPanic(a[len] == 0); + assertOrPanic(b[len] == 0); +} + From 09e5fbbfb94c04090398d094531277ccda2c8458 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Dec 2018 16:59:12 -0500 Subject: [PATCH 110/190] copy elision: fix mutability of result slice of @sliceToBytes --- src/ir.cpp | 9 ++++++++- test/cases/misc.zig | 7 +------ test/stage1/behavior/misc.zig | 20 ++++++++++++++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 6f5557394848..ab8d20c80348 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11991,7 +11991,14 @@ static IrInstruction *resolve_possible_alloca_inference(IrAnalyze *ira, IrInstru IrInstructionPtrCastGen *ptr_cast = reinterpret_cast(base); // See above comment assert(ptr_cast->base.ref_count == 0); + assert(ptr_cast->ptr->ref_count == 1); // The ref is this PtrCast instruction ZigType *prev_ptr_type = ptr_cast->ptr->value.type; + assert(is_slice(child_type)); + ZigType *good_slice_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry; + ZigType *good_u8_ptr_type = adjust_ptr_child(ira->codegen, good_slice_ptr_type, + ira->codegen->builtin_types.entry_u8); + ZigType *good_u8_slice_type = get_slice_type(ira->codegen, good_u8_ptr_type); + ptr_cast->ptr->value.type = adjust_ptr_child(ira->codegen, prev_ptr_type, good_u8_slice_type); ZigType *new_ptr_type = adjust_ptr_child(ira->codegen, prev_ptr_type, child_type); ptr_cast->base.value.type = new_ptr_type; } @@ -23228,7 +23235,7 @@ static IrInstruction *ir_analyze_instruction_result_bytes_to_slice(IrAnalyze *ir // At compile time pointer casts are represented as the pointer type disagreeing // with the element type, so that's easy to do here. - if (instr_is_comptime(new_result_loc)) { + if (instr_is_comptime(new_result_loc) && new_result_loc->value.data.x_ptr.mut != ConstPtrMutInfer) { ConstExprValue *val = ir_resolve_const(ira, new_result_loc, UndefOk); if (!val) return ira->codegen->invalid_instruction; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 77bbe21a07c7..9ab66f1e4e2e 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -7,12 +7,7 @@ const maxInt = std.math.maxInt; test "cast slice to u8 slice" { assertOrPanic(@sizeOf(i32) == 4); - var big_thing_array = []i32{ - 1, - 2, - 3, - 4, - }; + var big_thing_array = []i32{ 1, 2, 3, 4 }; const big_thing_slice: []i32 = big_thing_array[0..]; const bytes = @sliceToBytes(big_thing_slice); assertOrPanic(bytes.len == 4 * 4); diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index eac3fe8e7466..7e43924de711 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -374,3 +374,23 @@ test "C string concatenation" { assertOrPanic(b[len] == 0); } +test "cast slice to u8 slice" { + assertOrPanic(@sizeOf(i32) == 4); + var big_thing_array = []i32{ 1, 2, 3, 4 }; + const big_thing_slice: []i32 = big_thing_array[0..]; + const bytes = @sliceToBytes(big_thing_slice); + assertOrPanic(bytes.len == 4 * 4); + bytes[4] = 0; + bytes[5] = 0; + bytes[6] = 0; + bytes[7] = 0; + assertOrPanic(big_thing_slice[1] == 0); + const big_thing_again = @bytesToSlice(i32, bytes); + assertOrPanic(big_thing_again[2] == 3); + big_thing_again[2] = -1; + assertOrPanic(bytes[8] == maxInt(u8)); + assertOrPanic(bytes[9] == maxInt(u8)); + assertOrPanic(bytes[10] == maxInt(u8)); + assertOrPanic(bytes[11] == maxInt(u8)); +} + From e3f17dee89263a24a2b228749421781e05d54802 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Dec 2018 17:00:51 -0500 Subject: [PATCH 111/190] copy elision: move passing tests --- test/cases/misc.zig | 125 ---------------------------------- test/stage1/behavior/misc.zig | 105 ++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 125 deletions(-) diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 9ab66f1e4e2e..e198d393f8bc 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -5,131 +5,6 @@ const cstr = std.cstr; const builtin = @import("builtin"); const maxInt = std.math.maxInt; -test "cast slice to u8 slice" { - assertOrPanic(@sizeOf(i32) == 4); - var big_thing_array = []i32{ 1, 2, 3, 4 }; - const big_thing_slice: []i32 = big_thing_array[0..]; - const bytes = @sliceToBytes(big_thing_slice); - assertOrPanic(bytes.len == 4 * 4); - bytes[4] = 0; - bytes[5] = 0; - bytes[6] = 0; - bytes[7] = 0; - assertOrPanic(big_thing_slice[1] == 0); - const big_thing_again = @bytesToSlice(i32, bytes); - assertOrPanic(big_thing_again[2] == 3); - big_thing_again[2] = -1; - assertOrPanic(bytes[8] == maxInt(u8)); - assertOrPanic(bytes[9] == maxInt(u8)); - assertOrPanic(bytes[10] == maxInt(u8)); - assertOrPanic(bytes[11] == maxInt(u8)); -} - -test "pointer to void return type" { - testPointerToVoidReturnType() catch unreachable; -} -fn testPointerToVoidReturnType() anyerror!void { - const a = testPointerToVoidReturnType2(); - return a.*; -} -const test_pointer_to_void_return_type_x = void{}; -fn testPointerToVoidReturnType2() *const void { - return &test_pointer_to_void_return_type_x; -} - -test "non const ptr to aliased type" { - const int = i32; - assertOrPanic(?*int == ?*i32); -} - -test "array 2D const double ptr" { - const rect_2d_vertexes = [][1]f32{ - []f32{1.0}, - []f32{2.0}, - }; - testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]); -} - -fn testArray2DConstDoublePtr(ptr: *const f32) void { - const ptr2 = @ptrCast([*]const f32, ptr); - assertOrPanic(ptr2[0] == 1.0); - assertOrPanic(ptr2[1] == 2.0); -} - -const Tid = builtin.TypeId; -const AStruct = struct { - x: i32, -}; -const AnEnum = enum { - One, - Two, -}; -const AUnionEnum = union(enum) { - One: i32, - Two: void, -}; -const AUnion = union { - One: void, - Two: void, -}; - -test "@typeId" { - comptime { - assertOrPanic(@typeId(type) == Tid.Type); - assertOrPanic(@typeId(void) == Tid.Void); - assertOrPanic(@typeId(bool) == Tid.Bool); - assertOrPanic(@typeId(noreturn) == Tid.NoReturn); - assertOrPanic(@typeId(i8) == Tid.Int); - assertOrPanic(@typeId(u8) == Tid.Int); - assertOrPanic(@typeId(i64) == Tid.Int); - assertOrPanic(@typeId(u64) == Tid.Int); - assertOrPanic(@typeId(f32) == Tid.Float); - assertOrPanic(@typeId(f64) == Tid.Float); - assertOrPanic(@typeId(*f32) == Tid.Pointer); - assertOrPanic(@typeId([2]u8) == Tid.Array); - assertOrPanic(@typeId(AStruct) == Tid.Struct); - assertOrPanic(@typeId(@typeOf(1)) == Tid.ComptimeInt); - assertOrPanic(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); - assertOrPanic(@typeId(@typeOf(undefined)) == Tid.Undefined); - assertOrPanic(@typeId(@typeOf(null)) == Tid.Null); - assertOrPanic(@typeId(?i32) == Tid.Optional); - assertOrPanic(@typeId(anyerror!i32) == Tid.ErrorUnion); - assertOrPanic(@typeId(anyerror) == Tid.ErrorSet); - assertOrPanic(@typeId(AnEnum) == Tid.Enum); - assertOrPanic(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); - assertOrPanic(@typeId(AUnionEnum) == Tid.Union); - assertOrPanic(@typeId(AUnion) == Tid.Union); - assertOrPanic(@typeId(fn () void) == Tid.Fn); - assertOrPanic(@typeId(@typeOf(builtin)) == Tid.Namespace); - // TODO bound fn - // TODO arg tuple - // TODO opaque - } -} - -test "@typeName" { - const Struct = struct {}; - const Union = union { - unused: u8, - }; - const Enum = enum { - Unused, - }; - comptime { - assertOrPanic(mem.eql(u8, @typeName(i64), "i64")); - assertOrPanic(mem.eql(u8, @typeName(*usize), "*usize")); - // https://github.com/ziglang/zig/issues/675 - assertOrPanic(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); - assertOrPanic(mem.eql(u8, @typeName(Struct), "Struct")); - assertOrPanic(mem.eql(u8, @typeName(Union), "Union")); - assertOrPanic(mem.eql(u8, @typeName(Enum), "Enum")); - } -} - -fn TypeFromFn(comptime T: type) type { - return struct {}; -} - test "volatile load and store" { var number: i32 = 1234; const ptr = (*volatile i32)(&number); diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 7e43924de711..796578a3ed21 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -394,3 +394,108 @@ test "cast slice to u8 slice" { assertOrPanic(bytes[11] == maxInt(u8)); } +test "pointer to void return type" { + testPointerToVoidReturnType() catch unreachable; +} +fn testPointerToVoidReturnType() anyerror!void { + const a = testPointerToVoidReturnType2(); + return a.*; +} +const test_pointer_to_void_return_type_x = void{}; +fn testPointerToVoidReturnType2() *const void { + return &test_pointer_to_void_return_type_x; +} + +test "non const ptr to aliased type" { + const int = i32; + assertOrPanic(?*int == ?*i32); +} + +test "array 2D const double ptr" { + const rect_2d_vertexes = [][1]f32{ + []f32{1.0}, + []f32{2.0}, + }; + testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]); +} + +fn testArray2DConstDoublePtr(ptr: *const f32) void { + const ptr2 = @ptrCast([*]const f32, ptr); + assertOrPanic(ptr2[0] == 1.0); + assertOrPanic(ptr2[1] == 2.0); +} + +const Tid = builtin.TypeId; +const AStruct = struct { + x: i32, +}; +const AnEnum = enum { + One, + Two, +}; +const AUnionEnum = union(enum) { + One: i32, + Two: void, +}; +const AUnion = union { + One: void, + Two: void, +}; + +test "@typeId" { + comptime { + assertOrPanic(@typeId(type) == Tid.Type); + assertOrPanic(@typeId(void) == Tid.Void); + assertOrPanic(@typeId(bool) == Tid.Bool); + assertOrPanic(@typeId(noreturn) == Tid.NoReturn); + assertOrPanic(@typeId(i8) == Tid.Int); + assertOrPanic(@typeId(u8) == Tid.Int); + assertOrPanic(@typeId(i64) == Tid.Int); + assertOrPanic(@typeId(u64) == Tid.Int); + assertOrPanic(@typeId(f32) == Tid.Float); + assertOrPanic(@typeId(f64) == Tid.Float); + assertOrPanic(@typeId(*f32) == Tid.Pointer); + assertOrPanic(@typeId([2]u8) == Tid.Array); + assertOrPanic(@typeId(AStruct) == Tid.Struct); + assertOrPanic(@typeId(@typeOf(1)) == Tid.ComptimeInt); + assertOrPanic(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); + assertOrPanic(@typeId(@typeOf(undefined)) == Tid.Undefined); + assertOrPanic(@typeId(@typeOf(null)) == Tid.Null); + assertOrPanic(@typeId(?i32) == Tid.Optional); + assertOrPanic(@typeId(anyerror!i32) == Tid.ErrorUnion); + assertOrPanic(@typeId(anyerror) == Tid.ErrorSet); + assertOrPanic(@typeId(AnEnum) == Tid.Enum); + assertOrPanic(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); + assertOrPanic(@typeId(AUnionEnum) == Tid.Union); + assertOrPanic(@typeId(AUnion) == Tid.Union); + assertOrPanic(@typeId(fn () void) == Tid.Fn); + assertOrPanic(@typeId(@typeOf(builtin)) == Tid.Namespace); + // TODO bound fn + // TODO arg tuple + // TODO opaque + } +} + +test "@typeName" { + const Struct = struct {}; + const Union = union { + unused: u8, + }; + const Enum = enum { + Unused, + }; + comptime { + assertOrPanic(mem.eql(u8, @typeName(i64), "i64")); + assertOrPanic(mem.eql(u8, @typeName(*usize), "*usize")); + // https://github.com/ziglang/zig/issues/675 + assertOrPanic(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); + assertOrPanic(mem.eql(u8, @typeName(Struct), "Struct")); + assertOrPanic(mem.eql(u8, @typeName(Union), "Union")); + assertOrPanic(mem.eql(u8, @typeName(Enum), "Enum")); + } +} + +fn TypeFromFn(comptime T: type) type { + return struct {}; +} + From 750c2dd32894d159cfe17ec87f467723ecc1b001 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Dec 2018 23:46:05 -0500 Subject: [PATCH 112/190] copy elision: move passing tests --- test/behavior.zig | 4 - test/cases/asm.zig | 48 ------- test/cases/atomics.zig | 58 +------- test/cases/misc.zig | 133 ------------------- test/stage1/behavior.zig | 8 ++ test/{cases => stage1/behavior}/align.zig | 72 +++++----- test/{cases => stage1/behavior}/alignof.zig | 7 +- test/stage1/behavior/array.zig | 3 + test/stage1/behavior/asm.zig | 92 +++++++++++++ test/stage1/behavior/atomics.zig | 51 +++++++ test/stage1/behavior/bitcast.zig | 4 + test/{cases => stage1/behavior}/bool.zig | 20 +-- test/stage1/behavior/misc.zig | 140 ++++++++++++++++++++ 13 files changed, 354 insertions(+), 286 deletions(-) delete mode 100644 test/cases/asm.zig rename test/{cases => stage1/behavior}/align.zig (69%) rename test/{cases => stage1/behavior}/alignof.zig (61%) create mode 100644 test/stage1/behavior/array.zig create mode 100644 test/stage1/behavior/asm.zig create mode 100644 test/stage1/behavior/atomics.zig create mode 100644 test/stage1/behavior/bitcast.zig rename test/{cases => stage1/behavior}/bool.zig (50%) diff --git a/test/behavior.zig b/test/behavior.zig index 2748b52067fe..e54433de821e 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,11 +1,7 @@ comptime { - _ = @import("cases/align.zig"); - _ = @import("cases/alignof.zig"); _ = @import("cases/array.zig"); - _ = @import("cases/asm.zig"); _ = @import("cases/atomics.zig"); _ = @import("cases/bitcast.zig"); - _ = @import("cases/bool.zig"); _ = @import("cases/bswap.zig"); _ = @import("cases/bugs/1076.zig"); _ = @import("cases/bugs/1111.zig"); diff --git a/test/cases/asm.zig b/test/cases/asm.zig deleted file mode 100644 index 63e37c857c1d..000000000000 --- a/test/cases/asm.zig +++ /dev/null @@ -1,48 +0,0 @@ -const config = @import("builtin"); -const assert = @import("std").debug.assert; - -comptime { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { - asm volatile ( - \\.globl aoeu; - \\.type aoeu, @function; - \\.set aoeu, derp; - ); - } -} - -test "module level assembly" { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { - assert(aoeu() == 1234); - } -} - -test "output constraint modifiers" { - // This is only testing compilation. - var a: u32 = 3; - asm volatile ("" : [_]"=m,r"(a) : : ""); - asm volatile ("" : [_]"=r,m"(a) : : ""); -} - -test "alternative constraints" { - // Make sure we allow commas as a separator for alternative constraints. - var a: u32 = 3; - asm volatile ("" : [_]"=r,m"(a) : [_]"r,m"(a) : ""); -} - -test "sized integer/float in asm input" { - asm volatile ("" : : [_]"m"(usize(3)) : ""); - asm volatile ("" : : [_]"m"(i15(-3)) : ""); - asm volatile ("" : : [_]"m"(u3(3)) : ""); - asm volatile ("" : : [_]"m"(i3(3)) : ""); - asm volatile ("" : : [_]"m"(u121(3)) : ""); - asm volatile ("" : : [_]"m"(i121(3)) : ""); - asm volatile ("" : : [_]"m"(f32(3.17)) : ""); - asm volatile ("" : : [_]"m"(f64(3.17)) : ""); -} - -extern fn aoeu() i32; - -export fn derp() i32 { - return 1234; -} diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig index 67c9ab3dd117..1e70cefbff09 100644 --- a/test/cases/atomics.zig +++ b/test/cases/atomics.zig @@ -1,71 +1,25 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; -test "cmpxchg" { - var x: i32 = 1234; - if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == 1234); - } else { - @panic("cmpxchg should have failed"); - } - - while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == 1234); - } - assert(x == 5678); - - assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assert(x == 42); -} - -test "fence" { - var x: i32 = 1234; - @fence(AtomicOrder.SeqCst); - x = 5678; -} - -test "atomicrmw and atomicload" { - var data: u8 = 200; - testAtomicRmw(&data); - assert(data == 42); - testAtomicLoad(&data); -} - -fn testAtomicRmw(ptr: *u8) void { - const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); - assert(prev_value == 200); - comptime { - var x: i32 = 1234; - const y: i32 = 12345; - assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234); - assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345); - } -} - -fn testAtomicLoad(ptr: *u8) void { - const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); - assert(x == 42); -} - test "cmpxchg with ptr" { var data1: i32 = 1234; var data2: i32 = 5678; var data3: i32 = 9101; var x: *i32 = &data1; if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == &data1); + assertOrPanic(x1 == &data1); } else { @panic("cmpxchg should have failed"); } while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == &data1); + assertOrPanic(x1 == &data1); } - assert(x == &data3); + assertOrPanic(x == &data3); - assert(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assert(x == &data2); + assertOrPanic(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assertOrPanic(x == &data2); } diff --git a/test/cases/misc.zig b/test/cases/misc.zig index e198d393f8bc..caccd573d577 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -25,63 +25,10 @@ test "slice string literal has type []const u8" { } } -test "global variable initialized to global variable array element" { - assertOrPanic(global_ptr == &gdt[0]); -} -const GDTEntry = struct { - field: i32, -}; -var gdt = []GDTEntry{ - GDTEntry{ .field = 1 }, - GDTEntry{ .field = 2 }, -}; -var global_ptr = &gdt[0]; - -// can't really run this test but we can make sure it has no compile error -// and generates code -const vram = @intToPtr([*]volatile u8, 0x20000000)[0..0x8000]; -export fn writeToVRam() void { - vram[0] = 'X'; -} - test "pointer child field" { assertOrPanic((*u32).Child == u32); } -const OpaqueA = @OpaqueType(); -const OpaqueB = @OpaqueType(); -test "@OpaqueType" { - assertOrPanic(*OpaqueA != *OpaqueB); - assertOrPanic(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); - assertOrPanic(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); -} - -test "variable is allowed to be a pointer to an opaque type" { - var x: i32 = 1234; - _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x)); -} -fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { - var a = ptr; - return a; -} - -test "comptime if inside runtime while which unconditionally breaks" { - testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true); - comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true); -} -fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void { - while (cond) { - if (false) {} - break; - } -} - -test "implicit comptime while" { - while (false) { - @compileError("bad"); - } -} - test "struct inside function" { testStructInFn(); comptime testStructInFn(); @@ -100,83 +47,3 @@ fn testStructInFn() void { assertOrPanic(block.kind == 1235); } - -fn fnThatClosesOverLocalConst() type { - const c = 1; - return struct { - fn g() i32 { - return c; - } - }; -} - -test "function closes over local const" { - const x = fnThatClosesOverLocalConst().g(); - assertOrPanic(x == 1); -} - -test "cold function" { - thisIsAColdFn(); - comptime thisIsAColdFn(); -} - -fn thisIsAColdFn() void { - @setCold(true); -} - -const PackedStruct = packed struct { - a: u8, - b: u8, -}; -const PackedUnion = packed union { - a: u8, - b: u32, -}; -const PackedEnum = packed enum { - A, - B, -}; - -test "packed struct, enum, union parameters in extern function" { - testPackedStuff(&(PackedStruct{ - .a = 1, - .b = 2, - }), &(PackedUnion{ .a = 1 }), PackedEnum.A); -} - -export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: PackedEnum) void {} - -test "slicing zero length array" { - const s1 = ""[0..]; - const s2 = ([]u32{})[0..]; - assertOrPanic(s1.len == 0); - assertOrPanic(s2.len == 0); - assertOrPanic(mem.eql(u8, s1, "")); - assertOrPanic(mem.eql(u32, s2, []u32{})); -} - -const addr1 = @ptrCast(*const u8, emptyFn); -test "comptime cast fn to ptr" { - const addr2 = @ptrCast(*const u8, emptyFn); - comptime assertOrPanic(addr1 == addr2); -} - -test "equality compare fn ptrs" { - var a = emptyFn; - assertOrPanic(a == a); -} - -test "self reference through fn ptr field" { - const S = struct { - const A = struct { - f: fn (A) u8, - }; - - fn foo(a: A) u8 { - return 12; - } - }; - var a: S.A = undefined; - a.f = S.foo; - assertOrPanic(a.f(a) == 12); -} diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 3aa73f4adc40..828c73be2d52 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -1,3 +1,11 @@ comptime { + _ = @import("behavior/align.zig"); + _ = @import("behavior/alignof.zig"); + _ = @import("behavior/asm.zig"); + _ = @import("behavior/array.zig"); + _ = @import("behavior/atomics.zig"); + _ = @import("behavior/bitcast.zig"); + _ = @import("behavior/bool.zig"); + _ = @import("behavior/misc.zig"); } diff --git a/test/cases/align.zig b/test/stage1/behavior/align.zig similarity index 69% rename from test/cases/align.zig rename to test/stage1/behavior/align.zig index 3dff57feb882..aa7a93ad8482 100644 --- a/test/cases/align.zig +++ b/test/stage1/behavior/align.zig @@ -1,13 +1,13 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const builtin = @import("builtin"); var foo: u8 align(4) = 100; test "global variable alignment" { - assert(@typeOf(&foo).alignment == 4); - assert(@typeOf(&foo) == *align(4) u8); + assertOrPanic(@typeOf(&foo).alignment == 4); + assertOrPanic(@typeOf(&foo) == *align(4) u8); const slice = (*[1]u8)(&foo)[0..]; - assert(@typeOf(slice) == []align(4) u8); + assertOrPanic(@typeOf(slice) == []align(4) u8); } fn derp() align(@sizeOf(usize) * 2) i32 { @@ -17,9 +17,9 @@ fn noop1() align(1) void {} fn noop4() align(4) void {} test "function alignment" { - assert(derp() == 1234); - assert(@typeOf(noop1) == fn () align(1) void); - assert(@typeOf(noop4) == fn () align(4) void); + assertOrPanic(derp() == 1234); + assertOrPanic(@typeOf(noop1) == fn () align(1) void); + assertOrPanic(@typeOf(noop4) == fn () align(4) void); noop1(); noop4(); } @@ -30,7 +30,7 @@ var baz: packed struct { } = undefined; test "packed struct alignment" { - assert(@typeOf(&baz.b) == *align(1) u32); + assertOrPanic(@typeOf(&baz.b) == *align(1) u32); } const blah: packed struct { @@ -40,17 +40,17 @@ const blah: packed struct { } = undefined; test "bit field alignment" { - assert(@typeOf(&blah.b) == *align(1:3:1) const u3); + assertOrPanic(@typeOf(&blah.b) == *align(1:3:1) const u3); } test "default alignment allows unspecified in type syntax" { - assert(*u32 == *align(@alignOf(u32)) u32); + assertOrPanic(*u32 == *align(@alignOf(u32)) u32); } test "implicitly decreasing pointer alignment" { const a: u32 align(4) = 3; const b: u32 align(8) = 4; - assert(addUnaligned(&a, &b) == 7); + assertOrPanic(addUnaligned(&a, &b) == 7); } fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { @@ -60,7 +60,7 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { test "implicitly decreasing slice alignment" { const a: u32 align(4) = 3; const b: u32 align(8) = 4; - assert(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7); + assertOrPanic(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7); } fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { return a[0] + b[0]; @@ -77,7 +77,7 @@ fn testBytesAlign(b: u8) void { b, }; const ptr = @ptrCast(*u32, &bytes[0]); - assert(ptr.* == 0x33333333); + assertOrPanic(ptr.* == 0x33333333); } test "specifying alignment allows slice cast" { @@ -91,13 +91,13 @@ fn testBytesAlignSlice(b: u8) void { b, }; const slice: []u32 = @bytesToSlice(u32, bytes[0..]); - assert(slice[0] == 0x33333333); + assertOrPanic(slice[0] == 0x33333333); } test "@alignCast pointers" { var x: u32 align(4) = 1; expectsOnly1(&x); - assert(x == 2); + assertOrPanic(x == 2); } fn expectsOnly1(x: *align(1) u32) void { expects4(@alignCast(4, x)); @@ -113,7 +113,7 @@ test "@alignCast slices" { }; const slice = array[0..]; sliceExpectsOnly1(slice); - assert(slice[0] == 2); + assertOrPanic(slice[0] == 2); } fn sliceExpectsOnly1(slice: []align(1) u32) void { sliceExpects4(@alignCast(4, slice)); @@ -128,7 +128,7 @@ test "implicitly decreasing fn alignment" { } fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void { - assert(ptr() == answer); + assertOrPanic(ptr() == answer); } fn alignedSmall() align(8) i32 { @@ -139,7 +139,7 @@ fn alignedBig() align(16) i32 { } test "@alignCast functions" { - assert(fnExpectsOnly1(simple4) == 0x19); + assertOrPanic(fnExpectsOnly1(simple4) == 0x19); } fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 { return fnExpects4(@alignCast(4, ptr)); @@ -152,9 +152,9 @@ fn simple4() align(4) i32 { } test "generic function with align param" { - assert(whyWouldYouEverDoThis(1) == 0x1); - assert(whyWouldYouEverDoThis(4) == 0x1); - assert(whyWouldYouEverDoThis(8) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(1) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(4) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(8) == 0x1); } fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { @@ -164,28 +164,28 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { test "@ptrCast preserves alignment of bigger source" { var x: u32 align(16) = 1234; const ptr = @ptrCast(*u8, &x); - assert(@typeOf(ptr) == *align(16) u8); + assertOrPanic(@typeOf(ptr) == *align(16) u8); } test "runtime known array index has best alignment possible" { // take full advantage of over-alignment var array align(4) = []u8{ 1, 2, 3, 4 }; - assert(@typeOf(&array[0]) == *align(4) u8); - assert(@typeOf(&array[1]) == *u8); - assert(@typeOf(&array[2]) == *align(2) u8); - assert(@typeOf(&array[3]) == *u8); + assertOrPanic(@typeOf(&array[0]) == *align(4) u8); + assertOrPanic(@typeOf(&array[1]) == *u8); + assertOrPanic(@typeOf(&array[2]) == *align(2) u8); + assertOrPanic(@typeOf(&array[3]) == *u8); // because align is too small but we still figure out to use 2 var bigger align(2) = []u64{ 1, 2, 3, 4 }; - assert(@typeOf(&bigger[0]) == *align(2) u64); - assert(@typeOf(&bigger[1]) == *align(2) u64); - assert(@typeOf(&bigger[2]) == *align(2) u64); - assert(@typeOf(&bigger[3]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[0]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[1]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[2]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[3]) == *align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 var smaller align(2) = []u32{ 1, 2, 3, 4 }; - comptime assert(@typeOf(smaller[0..]) == []align(2) u32); - comptime assert(@typeOf(smaller[0..].ptr) == [*]align(2) u32); + comptime assertOrPanic(@typeOf(smaller[0..]) == []align(2) u32); + comptime assertOrPanic(@typeOf(smaller[0..].ptr) == [*]align(2) u32); testIndex(smaller[0..].ptr, 0, *align(2) u32); testIndex(smaller[0..].ptr, 1, *align(2) u32); testIndex(smaller[0..].ptr, 2, *align(2) u32); @@ -198,14 +198,14 @@ test "runtime known array index has best alignment possible" { testIndex2(array[0..].ptr, 3, *u8); } fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void { - comptime assert(@typeOf(&smaller[index]) == T); + comptime assertOrPanic(@typeOf(&smaller[index]) == T); } fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void { - comptime assert(@typeOf(&ptr[index]) == T); + comptime assertOrPanic(@typeOf(&ptr[index]) == T); } test "alignstack" { - assert(fnWithAlignedStack() == 1234); + assertOrPanic(fnWithAlignedStack() == 1234); } fn fnWithAlignedStack() i32 { @@ -214,7 +214,7 @@ fn fnWithAlignedStack() i32 { } test "alignment of structs" { - assert(@alignOf(struct { + assertOrPanic(@alignOf(struct { a: i32, b: *i32, }) == @alignOf(usize)); diff --git a/test/cases/alignof.zig b/test/stage1/behavior/alignof.zig similarity index 61% rename from test/cases/alignof.zig rename to test/stage1/behavior/alignof.zig index 433e86e45edf..98c805908b72 100644 --- a/test/cases/alignof.zig +++ b/test/stage1/behavior/alignof.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); const maxInt = std.math.maxInt; @@ -10,8 +10,9 @@ const Foo = struct { }; test "@alignOf(T) before referencing T" { - comptime assert(@alignOf(Foo) != maxInt(usize)); + comptime assertOrPanic(@alignOf(Foo) != maxInt(usize)); if (builtin.arch == builtin.Arch.x86_64) { - comptime assert(@alignOf(Foo) == 4); + comptime assertOrPanic(@alignOf(Foo) == 4); } } + diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig new file mode 100644 index 000000000000..2fd8053af2d5 --- /dev/null +++ b/test/stage1/behavior/array.zig @@ -0,0 +1,3 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; + diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig new file mode 100644 index 000000000000..48701c583621 --- /dev/null +++ b/test/stage1/behavior/asm.zig @@ -0,0 +1,92 @@ +const config = @import("builtin"); +const assertOrPanic = @import("std").debug.assertOrPanic; + +comptime { + if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + asm volatile ( + \\.globl aoeu; + \\.type aoeu, @function; + \\.set aoeu, derp; + ); + } +} + +test "module level assembly" { + if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + assertOrPanic(aoeu() == 1234); + } +} + +test "output constraint modifiers" { + // This is only testing compilation. + var a: u32 = 3; + asm volatile ("" + : [_] "=m,r" (a) + : + : "" + ); + asm volatile ("" + : [_] "=r,m" (a) + : + : "" + ); +} + +test "alternative constraints" { + // Make sure we allow commas as a separator for alternative constraints. + var a: u32 = 3; + asm volatile ("" + : [_] "=r,m" (a) + : [_] "r,m" (a) + : "" + ); +} + +test "sized integer/float in asm input" { + asm volatile ("" + : + : [_] "m" (usize(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i15(-3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (u3(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i3(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (u121(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i121(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (f32(3.17)) + : "" + ); + asm volatile ("" + : + : [_] "m" (f64(3.17)) + : "" + ); +} + +extern fn aoeu() i32; + +export fn derp() i32 { + return 1234; +} diff --git a/test/stage1/behavior/atomics.zig b/test/stage1/behavior/atomics.zig new file mode 100644 index 000000000000..8d1d67f6994e --- /dev/null +++ b/test/stage1/behavior/atomics.zig @@ -0,0 +1,51 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const builtin = @import("builtin"); +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; + +test "cmpxchg" { + var x: i32 = 1234; + if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assertOrPanic(x1 == 1234); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assertOrPanic(x1 == 1234); + } + assertOrPanic(x == 5678); + + assertOrPanic(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assertOrPanic(x == 42); +} + +test "fence" { + var x: i32 = 1234; + @fence(AtomicOrder.SeqCst); + x = 5678; +} + +test "atomicrmw and atomicload" { + var data: u8 = 200; + testAtomicRmw(&data); + assertOrPanic(data == 42); + testAtomicLoad(&data); +} + +fn testAtomicRmw(ptr: *u8) void { + const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); + assertOrPanic(prev_value == 200); + comptime { + var x: i32 = 1234; + const y: i32 = 12345; + assertOrPanic(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234); + assertOrPanic(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345); + } +} + +fn testAtomicLoad(ptr: *u8) void { + const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); + assertOrPanic(x == 42); +} diff --git a/test/stage1/behavior/bitcast.zig b/test/stage1/behavior/bitcast.zig new file mode 100644 index 000000000000..409261452616 --- /dev/null +++ b/test/stage1/behavior/bitcast.zig @@ -0,0 +1,4 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const maxInt = std.math.maxInt; + diff --git a/test/cases/bool.zig b/test/stage1/behavior/bool.zig similarity index 50% rename from test/cases/bool.zig rename to test/stage1/behavior/bool.zig index 3e4ac9c1cfe4..2d7241526f42 100644 --- a/test/cases/bool.zig +++ b/test/stage1/behavior/bool.zig @@ -1,25 +1,25 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "bool literals" { - assert(true); - assert(!false); + assertOrPanic(true); + assertOrPanic(!false); } test "cast bool to int" { const t = true; const f = false; - assert(@boolToInt(t) == u32(1)); - assert(@boolToInt(f) == u32(0)); + assertOrPanic(@boolToInt(t) == u32(1)); + assertOrPanic(@boolToInt(f) == u32(0)); nonConstCastBoolToInt(t, f); } fn nonConstCastBoolToInt(t: bool, f: bool) void { - assert(@boolToInt(t) == u32(1)); - assert(@boolToInt(f) == u32(0)); + assertOrPanic(@boolToInt(t) == u32(1)); + assertOrPanic(@boolToInt(f) == u32(0)); } test "bool cmp" { - assert(testBoolCmp(true, false) == false); + assertOrPanic(testBoolCmp(true, false) == false); } fn testBoolCmp(a: bool, b: bool) bool { return a == b; @@ -30,6 +30,6 @@ const global_t = true; const not_global_f = !global_f; const not_global_t = !global_t; test "compile time bool not" { - assert(not_global_f); - assert(!not_global_t); + assertOrPanic(not_global_f); + assertOrPanic(!not_global_t); } diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 796578a3ed21..05ac7d9fae5a 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -499,3 +499,143 @@ fn TypeFromFn(comptime T: type) type { return struct {}; } +test "double implicit cast in same expression" { + var x = i32(u16(nine())); + assertOrPanic(x == 9); +} +fn nine() u8 { + return 9; +} + +test "global variable initialized to global variable array element" { + assertOrPanic(global_ptr == &gdt[0]); +} +const GDTEntry = struct { + field: i32, +}; +var gdt = []GDTEntry{ + GDTEntry{ .field = 1 }, + GDTEntry{ .field = 2 }, +}; +var global_ptr = &gdt[0]; + +// can't really run this test but we can make sure it has no compile error +// and generates code +const vram = @intToPtr([*]volatile u8, 0x20000000)[0..0x8000]; +export fn writeToVRam() void { + vram[0] = 'X'; +} + +const OpaqueA = @OpaqueType(); +const OpaqueB = @OpaqueType(); +test "@OpaqueType" { + assertOrPanic(*OpaqueA != *OpaqueB); + assertOrPanic(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); + assertOrPanic(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); +} + +test "variable is allowed to be a pointer to an opaque type" { + var x: i32 = 1234; + _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x)); +} +fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { + var a = ptr; + return a; +} + +test "comptime if inside runtime while which unconditionally breaks" { + testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true); + comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true); +} +fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void { + while (cond) { + if (false) {} + break; + } +} + +test "implicit comptime while" { + while (false) { + @compileError("bad"); + } +} + +fn fnThatClosesOverLocalConst() type { + const c = 1; + return struct { + fn g() i32 { + return c; + } + }; +} + +test "function closes over local const" { + const x = fnThatClosesOverLocalConst().g(); + assertOrPanic(x == 1); +} + +test "cold function" { + thisIsAColdFn(); + comptime thisIsAColdFn(); +} + +fn thisIsAColdFn() void { + @setCold(true); +} + +const PackedStruct = packed struct { + a: u8, + b: u8, +}; +const PackedUnion = packed union { + a: u8, + b: u32, +}; +const PackedEnum = packed enum { + A, + B, +}; + +test "packed struct, enum, union parameters in extern function" { + testPackedStuff(&(PackedStruct{ + .a = 1, + .b = 2, + }), &(PackedUnion{ .a = 1 }), PackedEnum.A); +} + +export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: PackedEnum) void {} + +test "slicing zero length array" { + const s1 = ""[0..]; + const s2 = ([]u32{})[0..]; + assertOrPanic(s1.len == 0); + assertOrPanic(s2.len == 0); + assertOrPanic(mem.eql(u8, s1, "")); + assertOrPanic(mem.eql(u32, s2, []u32{})); +} + +const addr1 = @ptrCast(*const u8, emptyFn); +test "comptime cast fn to ptr" { + const addr2 = @ptrCast(*const u8, emptyFn); + comptime assertOrPanic(addr1 == addr2); +} + +test "equality compare fn ptrs" { + var a = emptyFn; + assertOrPanic(a == a); +} + +test "self reference through fn ptr field" { + const S = struct { + const A = struct { + f: fn (A) u8, + }; + + fn foo(a: A) u8 { + return 12; + } + }; + var a: S.A = undefined; + a.f = S.foo; + assertOrPanic(a.f(a) == 12); +} From d80e5d7ff45989e9d59fb18ec69a90753136a4d6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Dec 2018 01:15:18 -0500 Subject: [PATCH 113/190] copy elision: move passing tests --- test/behavior.zig | 17 ------- test/cases/import.zig | 10 ---- test/stage1/behavior.zig | 19 +++++++- test/stage1/behavior/bswap.zig | 3 ++ test/{cases => stage1/behavior}/bugs/1076.zig | 4 +- test/{cases => stage1/behavior}/bugs/1381.zig | 0 test/{cases => stage1/behavior}/bugs/1421.zig | 4 +- test/{cases => stage1/behavior}/bugs/1442.zig | 0 test/{cases => stage1/behavior}/bugs/1486.zig | 6 +-- test/{cases => stage1/behavior}/bugs/394.zig | 4 +- test/{cases => stage1/behavior}/bugs/655.zig | 4 +- .../behavior}/bugs/655_other_file.zig | 0 test/{cases => stage1/behavior}/bugs/656.zig | 4 +- test/{cases => stage1/behavior}/bugs/726.zig | 6 +-- .../behavior}/byval_arg_var.zig | 4 +- .../behavior}/field_parent_ptr.zig | 14 +++--- .../behavior}/fn_in_struct_in_comptime.zig | 4 +- test/{cases => stage1/behavior}/generics.zig | 46 +++++++++---------- test/{cases => stage1/behavior}/if.zig | 4 +- test/stage1/behavior/import.zig | 10 ++++ .../behavior}/import/a_namespace.zig | 0 .../behavior}/incomplete_struct_param_tld.zig | 4 +- .../behavior}/merge_error_sets.zig | 0 23 files changed, 85 insertions(+), 82 deletions(-) delete mode 100644 test/cases/import.zig create mode 100644 test/stage1/behavior/bswap.zig rename test/{cases => stage1/behavior}/bugs/1076.zig (75%) rename test/{cases => stage1/behavior}/bugs/1381.zig (100%) rename test/{cases => stage1/behavior}/bugs/1421.zig (70%) rename test/{cases => stage1/behavior}/bugs/1442.zig (100%) rename test/{cases => stage1/behavior}/bugs/1486.zig (51%) rename test/{cases => stage1/behavior}/bugs/394.zig (68%) rename test/{cases => stage1/behavior}/bugs/655.zig (67%) rename test/{cases => stage1/behavior}/bugs/655_other_file.zig (100%) rename test/{cases => stage1/behavior}/bugs/656.zig (84%) rename test/{cases => stage1/behavior}/bugs/726.zig (71%) rename test/{cases => stage1/behavior}/byval_arg_var.zig (70%) rename test/{cases => stage1/behavior}/field_parent_ptr.zig (69%) rename test/{cases => stage1/behavior}/fn_in_struct_in_comptime.zig (72%) rename test/{cases => stage1/behavior}/generics.zig (68%) rename test/{cases => stage1/behavior}/if.zig (85%) create mode 100644 test/stage1/behavior/import.zig rename test/{cases => stage1/behavior}/import/a_namespace.zig (100%) rename test/{cases => stage1/behavior}/incomplete_struct_param_tld.zig (78%) rename test/{cases => stage1/behavior}/merge_error_sets.zig (100%) diff --git a/test/behavior.zig b/test/behavior.zig index e54433de821e..955707c0724c 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -3,21 +3,11 @@ comptime { _ = @import("cases/atomics.zig"); _ = @import("cases/bitcast.zig"); _ = @import("cases/bswap.zig"); - _ = @import("cases/bugs/1076.zig"); _ = @import("cases/bugs/1111.zig"); _ = @import("cases/bugs/1277.zig"); _ = @import("cases/bugs/1322.zig"); - _ = @import("cases/bugs/1381.zig"); - _ = @import("cases/bugs/1421.zig"); - _ = @import("cases/bugs/1442.zig"); - _ = @import("cases/bugs/1486.zig"); - _ = @import("cases/bugs/394.zig"); - _ = @import("cases/bugs/655.zig"); - _ = @import("cases/bugs/656.zig"); - _ = @import("cases/bugs/726.zig"); _ = @import("cases/bugs/828.zig"); _ = @import("cases/bugs/920.zig"); - _ = @import("cases/byval_arg_var.zig"); _ = @import("cases/cancel.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); @@ -28,17 +18,10 @@ comptime { _ = @import("cases/enum_with_members.zig"); _ = @import("cases/error.zig"); _ = @import("cases/eval.zig"); - _ = @import("cases/field_parent_ptr.zig"); _ = @import("cases/fn.zig"); - _ = @import("cases/fn_in_struct_in_comptime.zig"); _ = @import("cases/for.zig"); - _ = @import("cases/generics.zig"); - _ = @import("cases/if.zig"); - _ = @import("cases/import.zig"); - _ = @import("cases/incomplete_struct_param_tld.zig"); _ = @import("cases/ir_block_deps.zig"); _ = @import("cases/math.zig"); - _ = @import("cases/merge_error_sets.zig"); _ = @import("cases/misc.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); _ = @import("cases/new_stack_call.zig"); diff --git a/test/cases/import.zig b/test/cases/import.zig deleted file mode 100644 index 6d6d4b020860..000000000000 --- a/test/cases/import.zig +++ /dev/null @@ -1,10 +0,0 @@ -const assert = @import("std").debug.assert; -const a_namespace = @import("import/a_namespace.zig"); - -test "call fn via namespace lookup" { - assert(a_namespace.foo() == 1234); -} - -test "importing the same thing gives the same import" { - assert(@import("std") == @import("std")); -} diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 828c73be2d52..accfca52bfad 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -6,6 +6,23 @@ comptime { _ = @import("behavior/atomics.zig"); _ = @import("behavior/bitcast.zig"); _ = @import("behavior/bool.zig"); - + _ = @import("behavior/bswap.zig"); + _ = @import("behavior/bugs/1076.zig"); + _ = @import("behavior/bugs/1381.zig"); + _ = @import("behavior/bugs/1421.zig"); + _ = @import("behavior/bugs/1442.zig"); + _ = @import("behavior/bugs/1486.zig"); + _ = @import("behavior/bugs/394.zig"); + _ = @import("behavior/bugs/655.zig"); + _ = @import("behavior/bugs/656.zig"); + _ = @import("behavior/bugs/726.zig"); + _ = @import("behavior/byval_arg_var.zig"); + _ = @import("behavior/field_parent_ptr.zig"); + _ = @import("behavior/fn_in_struct_in_comptime.zig"); + _ = @import("behavior/generics.zig"); + _ = @import("behavior/if.zig"); + _ = @import("behavior/import.zig"); + _ = @import("behavior/incomplete_struct_param_tld.zig"); + _ = @import("behavior/merge_error_sets.zig"); _ = @import("behavior/misc.zig"); } diff --git a/test/stage1/behavior/bswap.zig b/test/stage1/behavior/bswap.zig new file mode 100644 index 000000000000..d0a23c2653db --- /dev/null +++ b/test/stage1/behavior/bswap.zig @@ -0,0 +1,3 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + diff --git a/test/cases/bugs/1076.zig b/test/stage1/behavior/bugs/1076.zig similarity index 75% rename from test/cases/bugs/1076.zig rename to test/stage1/behavior/bugs/1076.zig index 7b843123104e..69a7e70f7dbb 100644 --- a/test/cases/bugs/1076.zig +++ b/test/stage1/behavior/bugs/1076.zig @@ -1,6 +1,6 @@ const std = @import("std"); const mem = std.mem; -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "comptime code should not modify constant data" { testCastPtrOfArrayToSliceAndPtr(); @@ -11,6 +11,6 @@ fn testCastPtrOfArrayToSliceAndPtr() void { var array = "aoeu"; const x: [*]u8 = &array; x[0] += 1; - assert(mem.eql(u8, array[0..], "boeu")); + assertOrPanic(mem.eql(u8, array[0..], "boeu")); } diff --git a/test/cases/bugs/1381.zig b/test/stage1/behavior/bugs/1381.zig similarity index 100% rename from test/cases/bugs/1381.zig rename to test/stage1/behavior/bugs/1381.zig diff --git a/test/cases/bugs/1421.zig b/test/stage1/behavior/bugs/1421.zig similarity index 70% rename from test/cases/bugs/1421.zig rename to test/stage1/behavior/bugs/1421.zig index fcbb8b70e4f8..fbc932781ab9 100644 --- a/test/cases/bugs/1421.zig +++ b/test/stage1/behavior/bugs/1421.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const S = struct { fn method() builtin.TypeInfo { @@ -10,5 +10,5 @@ const S = struct { test "functions with return type required to be comptime are generic" { const ti = S.method(); - assert(builtin.TypeId(ti) == builtin.TypeId.Struct); + assertOrPanic(builtin.TypeId(ti) == builtin.TypeId.Struct); } diff --git a/test/cases/bugs/1442.zig b/test/stage1/behavior/bugs/1442.zig similarity index 100% rename from test/cases/bugs/1442.zig rename to test/stage1/behavior/bugs/1442.zig diff --git a/test/cases/bugs/1486.zig b/test/stage1/behavior/bugs/1486.zig similarity index 51% rename from test/cases/bugs/1486.zig rename to test/stage1/behavior/bugs/1486.zig index 98fae36d3a0f..0483e3828cde 100644 --- a/test/cases/bugs/1486.zig +++ b/test/stage1/behavior/bugs/1486.zig @@ -1,11 +1,11 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const ptr = &global; var global: u64 = 123; test "constant pointer to global variable causes runtime load" { global = 1234; - assert(&global == ptr); - assert(ptr.* == 1234); + assertOrPanic(&global == ptr); + assertOrPanic(ptr.* == 1234); } diff --git a/test/cases/bugs/394.zig b/test/stage1/behavior/bugs/394.zig similarity index 68% rename from test/cases/bugs/394.zig rename to test/stage1/behavior/bugs/394.zig index b0afec23572a..766ad9e15761 100644 --- a/test/cases/bugs/394.zig +++ b/test/stage1/behavior/bugs/394.zig @@ -7,12 +7,12 @@ const S = struct { y: E, }; -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "bug 394 fixed" { const x = S{ .x = 3, .y = E{ .B = 1 }, }; - assert(x.x == 3); + assertOrPanic(x.x == 3); } diff --git a/test/cases/bugs/655.zig b/test/stage1/behavior/bugs/655.zig similarity index 67% rename from test/cases/bugs/655.zig rename to test/stage1/behavior/bugs/655.zig index ebb8da0658d4..67ba6a231f33 100644 --- a/test/cases/bugs/655.zig +++ b/test/stage1/behavior/bugs/655.zig @@ -3,10 +3,10 @@ const other_file = @import("655_other_file.zig"); test "function with *const parameter with type dereferenced by namespace" { const x: other_file.Integer = 1234; - comptime std.debug.assert(@typeOf(&x) == *const other_file.Integer); + comptime std.debug.assertOrPanic(@typeOf(&x) == *const other_file.Integer); foo(&x); } fn foo(x: *const other_file.Integer) void { - std.debug.assert(x.* == 1234); + std.debug.assertOrPanic(x.* == 1234); } diff --git a/test/cases/bugs/655_other_file.zig b/test/stage1/behavior/bugs/655_other_file.zig similarity index 100% rename from test/cases/bugs/655_other_file.zig rename to test/stage1/behavior/bugs/655_other_file.zig diff --git a/test/cases/bugs/656.zig b/test/stage1/behavior/bugs/656.zig similarity index 84% rename from test/cases/bugs/656.zig rename to test/stage1/behavior/bugs/656.zig index f93f0ac4d51f..cb37fe67fe84 100644 --- a/test/cases/bugs/656.zig +++ b/test/stage1/behavior/bugs/656.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const PrefixOp = union(enum) { Return, @@ -22,7 +22,7 @@ fn foo(a: bool, b: bool) void { PrefixOp.AddrOf => |addr_of_info| { if (b) {} if (addr_of_info.align_expr) |align_expr| { - assert(align_expr == 1234); + assertOrPanic(align_expr == 1234); } }, PrefixOp.Return => {}, diff --git a/test/cases/bugs/726.zig b/test/stage1/behavior/bugs/726.zig similarity index 71% rename from test/cases/bugs/726.zig rename to test/stage1/behavior/bugs/726.zig index 2acc91eb26c1..ce20480c6332 100644 --- a/test/cases/bugs/726.zig +++ b/test/stage1/behavior/bugs/726.zig @@ -1,9 +1,9 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "@ptrCast from const to nullable" { const c: u8 = 4; var x: ?*const u8 = @ptrCast(?*const u8, &c); - assert(x.?.* == 4); + assertOrPanic(x.?.* == 4); } test "@ptrCast from var in empty struct to nullable" { @@ -11,6 +11,6 @@ test "@ptrCast from var in empty struct to nullable" { var c: u8 = 4; }; var x: ?*const u8 = @ptrCast(?*const u8, &container.c); - assert(x.?.* == 4); + assertOrPanic(x.?.* == 4); } diff --git a/test/cases/byval_arg_var.zig b/test/stage1/behavior/byval_arg_var.zig similarity index 70% rename from test/cases/byval_arg_var.zig rename to test/stage1/behavior/byval_arg_var.zig index 826b9cc9e593..14ee212ce07c 100644 --- a/test/cases/byval_arg_var.zig +++ b/test/stage1/behavior/byval_arg_var.zig @@ -2,11 +2,11 @@ const std = @import("std"); var result: []const u8 = "wrong"; -test "aoeu" { +test "pass string literal byvalue to a generic var param" { start(); blowUpStack(10); - std.debug.assert(std.mem.eql(u8, result, "string literal")); + std.debug.assertOrPanic(std.mem.eql(u8, result, "string literal")); } fn start() void { diff --git a/test/cases/field_parent_ptr.zig b/test/stage1/behavior/field_parent_ptr.zig similarity index 69% rename from test/cases/field_parent_ptr.zig rename to test/stage1/behavior/field_parent_ptr.zig index 00d4e0f36721..ed2487c02081 100644 --- a/test/cases/field_parent_ptr.zig +++ b/test/stage1/behavior/field_parent_ptr.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "@fieldParentPtr non-first field" { testParentFieldPtr(&foo.c); @@ -25,17 +25,17 @@ const foo = Foo{ }; fn testParentFieldPtr(c: *const i32) void { - assert(c == &foo.c); + assertOrPanic(c == &foo.c); const base = @fieldParentPtr(Foo, "c", c); - assert(base == &foo); - assert(&base.c == c); + assertOrPanic(base == &foo); + assertOrPanic(&base.c == c); } fn testParentFieldPtrFirst(a: *const bool) void { - assert(a == &foo.a); + assertOrPanic(a == &foo.a); const base = @fieldParentPtr(Foo, "a", a); - assert(base == &foo); - assert(&base.a == a); + assertOrPanic(base == &foo); + assertOrPanic(&base.a == a); } diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/stage1/behavior/fn_in_struct_in_comptime.zig similarity index 72% rename from test/cases/fn_in_struct_in_comptime.zig rename to test/stage1/behavior/fn_in_struct_in_comptime.zig index fabb57e9cbcb..0af076d40acf 100644 --- a/test/cases/fn_in_struct_in_comptime.zig +++ b/test/stage1/behavior/fn_in_struct_in_comptime.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn get_foo() fn (*u8) usize { comptime { @@ -13,5 +13,5 @@ fn get_foo() fn (*u8) usize { test "define a function in an anonymous struct in comptime" { const foo = get_foo(); - assert(foo(@intToPtr(*u8, 12345)) == 12345); + assertOrPanic(foo(@intToPtr(*u8, 12345)) == 12345); } diff --git a/test/cases/generics.zig b/test/stage1/behavior/generics.zig similarity index 68% rename from test/cases/generics.zig rename to test/stage1/behavior/generics.zig index 52aa013989e1..a0928634a7c3 100644 --- a/test/cases/generics.zig +++ b/test/stage1/behavior/generics.zig @@ -1,9 +1,9 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "simple generic fn" { - assert(max(i32, 3, -1) == 3); - assert(max(f32, 0.123, 0.456) == 0.456); - assert(add(2, 3) == 5); + assertOrPanic(max(i32, 3, -1) == 3); + assertOrPanic(max(f32, 0.123, 0.456) == 0.456); + assertOrPanic(add(2, 3) == 5); } fn max(comptime T: type, a: T, b: T) T { @@ -16,7 +16,7 @@ fn add(comptime a: i32, b: i32) i32 { const the_max = max(u32, 1234, 5678); test "compile time generic eval" { - assert(the_max == 5678); + assertOrPanic(the_max == 5678); } fn gimmeTheBigOne(a: u32, b: u32) u32 { @@ -32,19 +32,19 @@ fn sameButWithFloats(a: f64, b: f64) f64 { } test "fn with comptime args" { - assert(gimmeTheBigOne(1234, 5678) == 5678); - assert(shouldCallSameInstance(34, 12) == 34); - assert(sameButWithFloats(0.43, 0.49) == 0.49); + assertOrPanic(gimmeTheBigOne(1234, 5678) == 5678); + assertOrPanic(shouldCallSameInstance(34, 12) == 34); + assertOrPanic(sameButWithFloats(0.43, 0.49) == 0.49); } test "var params" { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); + assertOrPanic(max_i32(12, 34) == 34); + assertOrPanic(max_f64(1.2, 3.4) == 3.4); } comptime { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); + assertOrPanic(max_i32(12, 34) == 34); + assertOrPanic(max_f64(1.2, 3.4) == 3.4); } fn max_var(a: var, b: var) @typeOf(a + b) { @@ -76,8 +76,8 @@ test "function with return type type" { var list2: List(i32) = undefined; list.length = 10; list2.length = 10; - assert(list.prealloc_items.len == 8); - assert(list2.prealloc_items.len == 8); + assertOrPanic(list.prealloc_items.len == 8); + assertOrPanic(list2.prealloc_items.len == 8); } test "generic struct" { @@ -89,9 +89,9 @@ test "generic struct" { .value = true, .next = null, }; - assert(a1.value == 13); - assert(a1.value == a1.getVal()); - assert(b1.getVal()); + assertOrPanic(a1.value == 13); + assertOrPanic(a1.value == a1.getVal()); + assertOrPanic(b1.getVal()); } fn GenNode(comptime T: type) type { return struct { @@ -104,7 +104,7 @@ fn GenNode(comptime T: type) type { } test "const decls in struct" { - assert(GenericDataThing(3).count_plus_one == 4); + assertOrPanic(GenericDataThing(3).count_plus_one == 4); } fn GenericDataThing(comptime count: isize) type { return struct { @@ -113,15 +113,15 @@ fn GenericDataThing(comptime count: isize) type { } test "use generic param in generic param" { - assert(aGenericFn(i32, 3, 4) == 7); + assertOrPanic(aGenericFn(i32, 3, 4) == 7); } fn aGenericFn(comptime T: type, comptime a: T, b: T) T { return a + b; } test "generic fn with implicit cast" { - assert(getFirstByte(u8, []u8{13}) == 13); - assert(getFirstByte(u16, []u16{ + assertOrPanic(getFirstByte(u8, []u8{13}) == 13); + assertOrPanic(getFirstByte(u16, []u16{ 0, 13, }) == 0); @@ -146,6 +146,6 @@ fn foo2(arg: var) bool { } test "array of generic fns" { - assert(foos[0](true)); - assert(!foos[1](true)); + assertOrPanic(foos[0](true)); + assertOrPanic(!foos[1](true)); } diff --git a/test/cases/if.zig b/test/stage1/behavior/if.zig similarity index 85% rename from test/cases/if.zig rename to test/stage1/behavior/if.zig index 808936bfa5fc..58d1b8fd734f 100644 --- a/test/cases/if.zig +++ b/test/stage1/behavior/if.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "if statements" { shouldBeEqual(1, 1); @@ -24,7 +24,7 @@ fn firstEqlThird(a: i32, b: i32, c: i32) void { } test "else if expression" { - assert(elseIfExpressionF(1) == 1); + assertOrPanic(elseIfExpressionF(1) == 1); } fn elseIfExpressionF(c: u8) u8 { if (c == 0) { diff --git a/test/stage1/behavior/import.zig b/test/stage1/behavior/import.zig new file mode 100644 index 000000000000..736e4c219ddc --- /dev/null +++ b/test/stage1/behavior/import.zig @@ -0,0 +1,10 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const a_namespace = @import("import/a_namespace.zig"); + +test "call fn via namespace lookup" { + assertOrPanic(a_namespace.foo() == 1234); +} + +test "importing the same thing gives the same import" { + assertOrPanic(@import("std") == @import("std")); +} diff --git a/test/cases/import/a_namespace.zig b/test/stage1/behavior/import/a_namespace.zig similarity index 100% rename from test/cases/import/a_namespace.zig rename to test/stage1/behavior/import/a_namespace.zig diff --git a/test/cases/incomplete_struct_param_tld.zig b/test/stage1/behavior/incomplete_struct_param_tld.zig similarity index 78% rename from test/cases/incomplete_struct_param_tld.zig rename to test/stage1/behavior/incomplete_struct_param_tld.zig index f1ac03a29279..d062311b2e9f 100644 --- a/test/cases/incomplete_struct_param_tld.zig +++ b/test/stage1/behavior/incomplete_struct_param_tld.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const A = struct { b: B, @@ -26,5 +26,5 @@ test "incomplete struct param top level declaration" { .c = C{ .x = 13 }, }, }; - assert(foo(a) == 13); + assertOrPanic(foo(a) == 13); } diff --git a/test/cases/merge_error_sets.zig b/test/stage1/behavior/merge_error_sets.zig similarity index 100% rename from test/cases/merge_error_sets.zig rename to test/stage1/behavior/merge_error_sets.zig From 73f96bee814812ff2d22e3016ee0592b98c0a77c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Dec 2018 21:42:13 -0500 Subject: [PATCH 114/190] copy elision: fix runtime array to slice --- src/all_types.hpp | 8 ++ src/codegen.cpp | 45 ++++++--- src/ir.cpp | 31 +++++- src/ir_print.cpp | 11 +++ test/behavior.zig | 1 - test/cases/array.zig | 173 --------------------------------- test/stage1/behavior/array.zig | 170 ++++++++++++++++++++++++++++++++ 7 files changed, 249 insertions(+), 190 deletions(-) delete mode 100644 test/cases/array.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index f51222d6d5b8..7298d0005f30 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2247,6 +2247,7 @@ enum IrInstructionId { IrInstructionIdSetNonNullBit, IrInstructionIdErrorLiteral, IrInstructionIdPtrOfArrayToSlice, + IrInstructionIdArrayToSlice, }; struct IrInstruction { @@ -2510,6 +2511,13 @@ struct IrInstructionPtrOfArrayToSlice { IrInstruction *result_loc; }; +struct IrInstructionArrayToSlice { + IrInstruction base; + + IrInstruction *array; + IrInstruction *result_loc; +}; + struct IrInstructionContainerInitList { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 029af302d24e..bfe64c9a78c8 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5197,26 +5197,16 @@ static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInst return LLVMBuildTrunc(g->builder, shifted, int_type->type_ref, ""); } -static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutable *executable, - IrInstructionPtrOfArrayToSlice *instruction) +static LLVMValueRef gen_array_to_slice(CodeGen *g, LLVMValueRef result_ptr, LLVMValueRef array_ptr, + ZigType *array_type) { - ZigType *actual_type = instruction->value->value.type; - LLVMValueRef expr_val = ir_llvm_value(g, instruction->value); - assert(expr_val); - - assert(actual_type->id == ZigTypeIdPointer); - ZigType *array_type = actual_type->data.pointer.child_type; - assert(array_type->id == ZigTypeIdArray); - - LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, slice_ptr_index, ""); LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->type_ref), LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), }; - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, slice_len_index, ""); @@ -5227,6 +5217,33 @@ static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutable *ex return result_ptr; } +static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutable *executable, + IrInstructionPtrOfArrayToSlice *instruction) +{ + ZigType *actual_type = instruction->value->value.type; + LLVMValueRef array_ptr = ir_llvm_value(g, instruction->value); + assert(array_ptr); + + assert(actual_type->id == ZigTypeIdPointer); + ZigType *array_type = actual_type->data.pointer.child_type; + assert(array_type->id == ZigTypeIdArray); + + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + return gen_array_to_slice(g, result_ptr, array_ptr, array_type); +} + +static LLVMValueRef ir_render_array_to_slice(CodeGen *g, IrExecutable *executable, + IrInstructionArrayToSlice *instruction) +{ + ZigType *array_type = instruction->array->value.type; + assert(array_type->id == ZigTypeIdArray); + assert(handle_is_ptr(array_type)); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array); + + return gen_array_to_slice(g, result_ptr, array_ptr, array_type); +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5501,6 +5518,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_bswap(g, executable, (IrInstructionBswap *)instruction); case IrInstructionIdPtrOfArrayToSlice: return ir_render_ptr_of_array_to_slice(g, executable, (IrInstructionPtrOfArrayToSlice *)instruction); + case IrInstructionIdArrayToSlice: + return ir_render_array_to_slice(g, executable, (IrInstructionArrayToSlice *)instruction); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index ab8d20c80348..89151530231d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -418,6 +418,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrOfArrayToSlic return IrInstructionIdPtrOfArrayToSlice; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToSlice *) { + return IrInstructionIdArrayToSlice; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) { return IrInstructionIdContainerInitList; } @@ -1032,6 +1036,23 @@ static IrInstruction *ir_build_ptr_of_array_to_slice(IrBuilder *irb, Scope *scop return &instruction->base; } +static IrInstruction *ir_build_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, ZigType *slice_type, + IrInstruction *array, IrInstruction *result_loc) +{ + IrInstructionArrayToSlice *instruction = ir_build_instruction( + &ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->base.value.special = ConstValSpecialRuntime; + instruction->base.value.type = slice_type; + + instruction->array = array; + instruction->result_loc = result_loc; + + ir_ref_instruction(array, ira->new_irb.current_basic_block); + ir_ref_instruction(result_loc, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *condition, IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime, IrInstruction *result_loc) { @@ -10949,16 +10970,18 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s ZigType *array_type = array->value.type; assert(array_type->id == ZigTypeIdArray); - if (instr_is_comptime(array)) { + if (instr_is_comptime(array) || array_type->data.array.len == 0) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, &result->value, &array->value, 0, array_type->data.array.len, true); result->value.type = wanted_type; return result; } - // Only the comptime branch above is used here. Once https://github.com/ziglang/zig/issues/265 + // TODO once https://github.com/ziglang/zig/issues/265 // lands this entire function will be deleted. - zig_unreachable(); + IrInstruction *slice_alloca = ir_analyze_alloca(ira, source_instr, wanted_type, 0, "", false); + slice_alloca->value.special = ConstValSpecialRuntime; + return ir_build_array_to_slice(ira, source_instr, wanted_type, array, slice_alloca); } static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, @@ -23318,6 +23341,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: case IrInstructionIdPtrOfArrayToSlice: + case IrInstructionIdArrayToSlice: case IrInstructionIdDeclVarGen: case IrInstructionIdAllocaGen: case IrInstructionIdResultSlicePtr: @@ -23781,6 +23805,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSlice: case IrInstructionIdCmpxchgGen: case IrInstructionIdCmpxchgSrc: + case IrInstructionIdArrayToSlice: return true; case IrInstructionIdPhi: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 6afc735dc46a..9362c5e54cd8 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1490,6 +1490,14 @@ static void ir_print_ptr_of_array_to_slice(IrPrint *irp, IrInstructionPtrOfArray fprintf(irp->f, ")"); } +static void ir_print_array_to_slice(IrPrint *irp, IrInstructionArrayToSlice *instruction) { + fprintf(irp->f, "ArrayToSlice(array="); + ir_print_other_instruction(irp, instruction->array); + fprintf(irp->f, ",result="); + ir_print_other_instruction(irp, instruction->result_loc); + fprintf(irp->f, ")"); +} + static void ir_print_bswap(IrPrint *irp, IrInstructionBswap *instruction) { fprintf(irp->f, "@bswap("); if (instruction->type != nullptr) { @@ -2002,6 +2010,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdPtrOfArrayToSlice: ir_print_ptr_of_array_to_slice(irp, (IrInstructionPtrOfArrayToSlice *)instruction); break; + case IrInstructionIdArrayToSlice: + ir_print_array_to_slice(irp, (IrInstructionArrayToSlice *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/test/behavior.zig b/test/behavior.zig index 955707c0724c..cbfb15b284ff 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,4 @@ comptime { - _ = @import("cases/array.zig"); _ = @import("cases/atomics.zig"); _ = @import("cases/bitcast.zig"); _ = @import("cases/bswap.zig"); diff --git a/test/cases/array.zig b/test/cases/array.zig deleted file mode 100644 index 7c63a649a8f3..000000000000 --- a/test/cases/array.zig +++ /dev/null @@ -1,173 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; - -test "arrays" { - var array: [5]u32 = undefined; - - var i: u32 = 0; - while (i < 5) { - array[i] = i + 1; - i = array[i]; - } - - i = 0; - var accumulator = u32(0); - while (i < 5) { - accumulator += array[i]; - - i += 1; - } - - assert(accumulator == 15); - assert(getArrayLen(array) == 5); -} -fn getArrayLen(a: []const u32) usize { - return a.len; -} - -test "void arrays" { - var array: [4]void = undefined; - array[0] = void{}; - array[1] = array[2]; - assert(@sizeOf(@typeOf(array)) == 0); - assert(array.len == 4); -} - -test "array literal" { - const hex_mult = []u16{ - 4096, - 256, - 16, - 1, - }; - - assert(hex_mult.len == 4); - assert(hex_mult[1] == 256); -} - -test "array dot len const expr" { - assert(comptime x: { - break :x some_array.len == 4; - }); -} - -const ArrayDotLenConstExpr = struct { - y: [some_array.len]u8, -}; -const some_array = []u8{ - 0, - 1, - 2, - 3, -}; - -test "nested arrays" { - const array_of_strings = [][]const u8{ - "hello", - "this", - "is", - "my", - "thing", - }; - for (array_of_strings) |s, i| { - if (i == 0) assert(mem.eql(u8, s, "hello")); - if (i == 1) assert(mem.eql(u8, s, "this")); - if (i == 2) assert(mem.eql(u8, s, "is")); - if (i == 3) assert(mem.eql(u8, s, "my")); - if (i == 4) assert(mem.eql(u8, s, "thing")); - } -} - -var s_array: [8]Sub = undefined; -const Sub = struct { - b: u8, -}; -const Str = struct { - a: []Sub, -}; -test "set global var array via slice embedded in struct" { - var s = Str{ .a = s_array[0..] }; - - s.a[0].b = 1; - s.a[1].b = 2; - s.a[2].b = 3; - - assert(s_array[0].b == 1); - assert(s_array[1].b == 2); - assert(s_array[2].b == 3); -} - -test "array literal with specified size" { - var array = [2]u8{ - 1, - 2, - }; - assert(array[0] == 1); - assert(array[1] == 2); -} - -test "array child property" { - var x: [5]i32 = undefined; - assert(@typeOf(x).Child == i32); -} - -test "array len property" { - var x: [5]i32 = undefined; - assert(@typeOf(x).len == 5); -} - -test "array len field" { - var arr = [4]u8{ 0, 0, 0, 0 }; - var ptr = &arr; - assert(arr.len == 4); - comptime assert(arr.len == 4); - assert(ptr.len == 4); - comptime assert(ptr.len == 4); -} - -test "single-item pointer to array indexing and slicing" { - testSingleItemPtrArrayIndexSlice(); - comptime testSingleItemPtrArrayIndexSlice(); -} - -fn testSingleItemPtrArrayIndexSlice() void { - var array = "aaaa"; - doSomeMangling(&array); - assert(mem.eql(u8, "azya", array)); -} - -fn doSomeMangling(array: *[4]u8) void { - array[1] = 'z'; - array[2..3][0] = 'y'; -} - -test "implicit cast single-item pointer" { - testImplicitCastSingleItemPtr(); - comptime testImplicitCastSingleItemPtr(); -} - -fn testImplicitCastSingleItemPtr() void { - var byte: u8 = 100; - const slice = (*[1]u8)(&byte)[0..]; - slice[0] += 1; - assert(byte == 101); -} - -fn testArrayByValAtComptime(b: [2]u8) u8 { - return b[0]; -} - -test "comptime evalutating function that takes array by value" { - const arr = []u8{ 0, 1 }; - _ = comptime testArrayByValAtComptime(arr); - _ = comptime testArrayByValAtComptime(arr); -} - -test "implicit comptime in array type size" { - var arr: [plusOne(10)]bool = undefined; - assert(arr.len == 11); -} - -fn plusOne(x: u32) u32 { - return x + 1; -} diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig index 2fd8053af2d5..414f537d01a9 100644 --- a/test/stage1/behavior/array.zig +++ b/test/stage1/behavior/array.zig @@ -1,3 +1,173 @@ const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; +test "arrays" { + var array: [5]u32 = undefined; + + var i: u32 = 0; + while (i < 5) { + array[i] = i + 1; + i = array[i]; + } + + i = 0; + var accumulator = u32(0); + while (i < 5) { + accumulator += array[i]; + + i += 1; + } + + assertOrPanic(accumulator == 15); + assertOrPanic(getArrayLen(array) == 5); +} +fn getArrayLen(a: []const u32) usize { + return a.len; +} + +test "void arrays" { + var array: [4]void = undefined; + array[0] = void{}; + array[1] = array[2]; + assertOrPanic(@sizeOf(@typeOf(array)) == 0); + assertOrPanic(array.len == 4); +} + +test "array literal" { + const hex_mult = []u16{ + 4096, + 256, + 16, + 1, + }; + + assertOrPanic(hex_mult.len == 4); + assertOrPanic(hex_mult[1] == 256); +} + +test "array dot len const expr" { + assertOrPanic(comptime x: { + break :x some_array.len == 4; + }); +} + +const ArrayDotLenConstExpr = struct { + y: [some_array.len]u8, +}; +const some_array = []u8{ + 0, + 1, + 2, + 3, +}; + +test "nested arrays" { + const array_of_strings = [][]const u8{ + "hello", + "this", + "is", + "my", + "thing", + }; + for (array_of_strings) |s, i| { + if (i == 0) assertOrPanic(mem.eql(u8, s, "hello")); + if (i == 1) assertOrPanic(mem.eql(u8, s, "this")); + if (i == 2) assertOrPanic(mem.eql(u8, s, "is")); + if (i == 3) assertOrPanic(mem.eql(u8, s, "my")); + if (i == 4) assertOrPanic(mem.eql(u8, s, "thing")); + } +} + +var s_array: [8]Sub = undefined; +const Sub = struct { + b: u8, +}; +const Str = struct { + a: []Sub, +}; +test "set global var array via slice embedded in struct" { + var s = Str{ .a = s_array[0..] }; + + s.a[0].b = 1; + s.a[1].b = 2; + s.a[2].b = 3; + + assertOrPanic(s_array[0].b == 1); + assertOrPanic(s_array[1].b == 2); + assertOrPanic(s_array[2].b == 3); +} + +test "array literal with specified size" { + var array = [2]u8{ + 1, + 2, + }; + assertOrPanic(array[0] == 1); + assertOrPanic(array[1] == 2); +} + +test "array child property" { + var x: [5]i32 = undefined; + assertOrPanic(@typeOf(x).Child == i32); +} + +test "array len property" { + var x: [5]i32 = undefined; + assertOrPanic(@typeOf(x).len == 5); +} + +test "array len field" { + var arr = [4]u8{ 0, 0, 0, 0 }; + var ptr = &arr; + assertOrPanic(arr.len == 4); + comptime assertOrPanic(arr.len == 4); + assertOrPanic(ptr.len == 4); + comptime assertOrPanic(ptr.len == 4); +} + +test "single-item pointer to array indexing and slicing" { + testSingleItemPtrArrayIndexSlice(); + comptime testSingleItemPtrArrayIndexSlice(); +} + +fn testSingleItemPtrArrayIndexSlice() void { + var array = "aaaa"; + doSomeMangling(&array); + assertOrPanic(mem.eql(u8, "azya", array)); +} + +fn doSomeMangling(array: *[4]u8) void { + array[1] = 'z'; + array[2..3][0] = 'y'; +} + +test "implicit cast single-item pointer" { + testImplicitCastSingleItemPtr(); + comptime testImplicitCastSingleItemPtr(); +} + +fn testImplicitCastSingleItemPtr() void { + var byte: u8 = 100; + const slice = (*[1]u8)(&byte)[0..]; + slice[0] += 1; + assertOrPanic(byte == 101); +} + +fn testArrayByValAtComptime(b: [2]u8) u8 { + return b[0]; +} + +test "comptime evalutating function that takes array by value" { + const arr = []u8{ 0, 1 }; + _ = comptime testArrayByValAtComptime(arr); + _ = comptime testArrayByValAtComptime(arr); +} + +test "implicit comptime in array type size" { + var arr: [plusOne(10)]bool = undefined; + assertOrPanic(arr.len == 11); +} + +fn plusOne(x: u32) u32 { + return x + 1; +} From 2cf1857d40380dc6618d258acd0d2b5a1b2bc24d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Dec 2018 12:33:54 -0500 Subject: [PATCH 115/190] copy elision: fix cmpxchg with pointer --- src/codegen.cpp | 25 ++++++++++++++++++++----- test/behavior.zig | 1 - test/cases/atomics.zig | 25 ------------------------- test/stage1/behavior/atomics.zig | 20 ++++++++++++++++++++ 4 files changed, 40 insertions(+), 31 deletions(-) delete mode 100644 test/cases/atomics.zig diff --git a/src/codegen.cpp b/src/codegen.cpp index bfe64c9a78c8..57667de35df8 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4185,22 +4185,37 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value); LLVMValueRef new_val = ir_llvm_value(g, instruction->new_value); + assert(type_has_bits(instruction->type)); + ZigType *ptr_type = instruction->result_loc->value.type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *child_type = ptr_type->data.pointer.child_type; + bool is_scalar = !handle_is_ptr(child_type); + LLVMAtomicOrdering success_order = to_LLVMAtomicOrdering(instruction->success_order); LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order); LLVMValueRef cmpxchg_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, success_order, failure_order, instruction->is_weak); - assert(type_has_bits(instruction->type)); - + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, cmpxchg_val, 1, ""); - LLVMBasicBlockRef null_block = LLVMGetInsertBlock(g->builder); + LLVMBasicBlockRef null_block = is_scalar ? LLVMAppendBasicBlock(g->cur_fn_val, "CmpXchgNull") : + LLVMGetInsertBlock(g->builder); LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn_val, "CmpXchgNonNull"); LLVMBasicBlockRef phi_block = LLVMAppendBasicBlock(g->cur_fn_val, "CmpXchgPhi"); - LLVMBuildCondBr(g->builder, success_bit, phi_block, non_null_block); + + if (is_scalar) { + LLVMBuildCondBr(g->builder, success_bit, null_block, non_null_block); + + LLVMPositionBuilderAtEnd(g->builder, null_block); + LLVMValueRef zeroes = LLVMConstNull(child_type->type_ref); + gen_assign_raw(g, result_ptr, instruction->result_loc->value.type, zeroes); + LLVMBuildBr(g->builder, phi_block); + } else { + LLVMBuildCondBr(g->builder, success_bit, phi_block, non_null_block); + } LLVMPositionBuilderAtEnd(g->builder, non_null_block); - LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, cmpxchg_val, 0, ""); gen_assign_raw(g, result_ptr, instruction->result_loc->value.type, payload_val); LLVMBuildBr(g->builder, phi_block); diff --git a/test/behavior.zig b/test/behavior.zig index cbfb15b284ff..3c8a34b82fe6 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,4 @@ comptime { - _ = @import("cases/atomics.zig"); _ = @import("cases/bitcast.zig"); _ = @import("cases/bswap.zig"); _ = @import("cases/bugs/1111.zig"); diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig deleted file mode 100644 index 1e70cefbff09..000000000000 --- a/test/cases/atomics.zig +++ /dev/null @@ -1,25 +0,0 @@ -const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; -const builtin = @import("builtin"); -const AtomicRmwOp = builtin.AtomicRmwOp; -const AtomicOrder = builtin.AtomicOrder; - -test "cmpxchg with ptr" { - var data1: i32 = 1234; - var data2: i32 = 5678; - var data3: i32 = 9101; - var x: *i32 = &data1; - if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assertOrPanic(x1 == &data1); - } else { - @panic("cmpxchg should have failed"); - } - - while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assertOrPanic(x1 == &data1); - } - assertOrPanic(x == &data3); - - assertOrPanic(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assertOrPanic(x == &data2); -} diff --git a/test/stage1/behavior/atomics.zig b/test/stage1/behavior/atomics.zig index 8d1d67f6994e..fa3c5f29a603 100644 --- a/test/stage1/behavior/atomics.zig +++ b/test/stage1/behavior/atomics.zig @@ -49,3 +49,23 @@ fn testAtomicLoad(ptr: *u8) void { const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); assertOrPanic(x == 42); } + +test "cmpxchg with ptr" { + var data1: i32 = 1234; + var data2: i32 = 5678; + var data3: i32 = 9101; + var x: *i32 = &data1; + if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assertOrPanic(x1 == &data1); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assertOrPanic(x1 == &data1); + } + assertOrPanic(x == &data3); + + assertOrPanic(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assertOrPanic(x == &data2); +} From c0df4bffeb33cb43328025b8d3d6bd4f5226371a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Dec 2018 12:51:22 -0500 Subject: [PATCH 116/190] copy elision: @bitCast between signed/unsigned int --- src/ir.cpp | 9 +++++++-- test/cases/bitcast.zig | 21 ++------------------- test/stage1/behavior/bitcast.zig | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 89151530231d..694366b66dff 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -228,8 +228,13 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (a == b) return true; - if (a->id == b->id) - return true; + if (a->id == b->id) { + if (a->id == ZigTypeIdInt) { + return a->data.integral.is_signed == b->data.integral.is_signed; + } else { + return true; + } + } if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) return true; diff --git a/test/cases/bitcast.zig b/test/cases/bitcast.zig index d85a84ed225e..e9d1f0ae064b 100644 --- a/test/cases/bitcast.zig +++ b/test/cases/bitcast.zig @@ -1,24 +1,7 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const maxInt = std.math.maxInt; -test "@bitCast i32 -> u32" { - testBitCast_i32_u32(); - comptime testBitCast_i32_u32(); -} - -fn testBitCast_i32_u32() void { - assert(conv(-1) == maxInt(u32)); - assert(conv2(maxInt(u32)) == -1); -} - -fn conv(x: i32) u32 { - return @bitCast(u32, x); -} -fn conv2(x: u32) i32 { - return @bitCast(i32, x); -} - test "@bitCast extern enum to its integer type" { const SOCK = extern enum { A, @@ -27,7 +10,7 @@ test "@bitCast extern enum to its integer type" { fn testBitCastExternEnum() void { var SOCK_DGRAM = @This().B; var sock_dgram = @bitCast(c_int, SOCK_DGRAM); - assert(sock_dgram == 1); + assertOrPanic(sock_dgram == 1); } }; diff --git a/test/stage1/behavior/bitcast.zig b/test/stage1/behavior/bitcast.zig index 409261452616..c7854d080822 100644 --- a/test/stage1/behavior/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -2,3 +2,20 @@ const std = @import("std"); const assertOrPanic = std.debug.assertOrPanic; const maxInt = std.math.maxInt; +test "@bitCast i32 -> u32" { + testBitCast_i32_u32(); + comptime testBitCast_i32_u32(); +} + +fn testBitCast_i32_u32() void { + assertOrPanic(conv(-1) == maxInt(u32)); + assertOrPanic(conv2(maxInt(u32)) == -1); +} + +fn conv(x: i32) u32 { + return @bitCast(u32, x); +} +fn conv2(x: u32) i32 { + return @bitCast(i32, x); +} + From 5be3d6386eccdd346f65a0589d8fbff40eb67924 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Dec 2018 12:59:19 -0500 Subject: [PATCH 117/190] copy elision: comptime @bitCast extern enum to int --- src/ir.cpp | 18 ++++++++++++++++-- test/behavior.zig | 1 - test/cases/bitcast.zig | 20 -------------------- test/stage1/behavior/bitcast.zig | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 23 deletions(-) delete mode 100644 test/cases/bitcast.zig diff --git a/src/ir.cpp b/src/ir.cpp index 694366b66dff..892bb602d21d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -21939,6 +21939,22 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn); return; } + case ZigTypeIdEnum: { + switch (val->type->data.enumeration.layout) { + case ContainerLayoutAuto: + zig_panic("TODO buf_read_value_bytes enum auto"); + case ContainerLayoutPacked: + zig_panic("TODO buf_read_value_bytes enum packed"); + case ContainerLayoutExtern: { + ZigType *tag_int_type = val->type->data.enumeration.tag_int_type; + assert(tag_int_type->id == ZigTypeIdInt); + bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count, + codegen->is_big_endian, tag_int_type->data.integral.is_signed); + return; + } + } + zig_unreachable(); + } case ZigTypeIdArray: zig_panic("TODO buf_read_value_bytes array type"); case ZigTypeIdStruct: @@ -21949,8 +21965,6 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_panic("TODO buf_read_value_bytes error union"); case ZigTypeIdErrorSet: zig_panic("TODO buf_read_value_bytes pure error type"); - case ZigTypeIdEnum: - zig_panic("TODO buf_read_value_bytes enum type"); case ZigTypeIdFn: zig_panic("TODO buf_read_value_bytes fn type"); case ZigTypeIdUnion: diff --git a/test/behavior.zig b/test/behavior.zig index 3c8a34b82fe6..e41bb60fcd5f 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,4 @@ comptime { - _ = @import("cases/bitcast.zig"); _ = @import("cases/bswap.zig"); _ = @import("cases/bugs/1111.zig"); _ = @import("cases/bugs/1277.zig"); diff --git a/test/cases/bitcast.zig b/test/cases/bitcast.zig deleted file mode 100644 index e9d1f0ae064b..000000000000 --- a/test/cases/bitcast.zig +++ /dev/null @@ -1,20 +0,0 @@ -const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; -const maxInt = std.math.maxInt; - -test "@bitCast extern enum to its integer type" { - const SOCK = extern enum { - A, - B, - - fn testBitCastExternEnum() void { - var SOCK_DGRAM = @This().B; - var sock_dgram = @bitCast(c_int, SOCK_DGRAM); - assertOrPanic(sock_dgram == 1); - } - }; - - SOCK.testBitCastExternEnum(); - comptime SOCK.testBitCastExternEnum(); -} - diff --git a/test/stage1/behavior/bitcast.zig b/test/stage1/behavior/bitcast.zig index c7854d080822..19030255e4f7 100644 --- a/test/stage1/behavior/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -19,3 +19,18 @@ fn conv2(x: u32) i32 { return @bitCast(i32, x); } +test "@bitCast extern enum to its integer type" { + const SOCK = extern enum { + A, + B, + + fn testBitCastExternEnum() void { + var SOCK_DGRAM = @This().B; + var sock_dgram = @bitCast(c_int, SOCK_DGRAM); + assertOrPanic(sock_dgram == 1); + } + }; + + SOCK.testBitCastExternEnum(); + comptime SOCK.testBitCastExternEnum(); +} From c1673a4a01760921b97396ba2d4e0412d6dce925 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Dec 2018 13:02:33 -0500 Subject: [PATCH 118/190] copy elision: move passing tests --- test/behavior.zig | 1 - test/cases/bswap.zig | 32 -------------------------------- test/stage1/behavior/bswap.zig | 29 +++++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 33 deletions(-) delete mode 100644 test/cases/bswap.zig diff --git a/test/behavior.zig b/test/behavior.zig index e41bb60fcd5f..469942a72272 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,4 @@ comptime { - _ = @import("cases/bswap.zig"); _ = @import("cases/bugs/1111.zig"); _ = @import("cases/bugs/1277.zig"); _ = @import("cases/bugs/1322.zig"); diff --git a/test/cases/bswap.zig b/test/cases/bswap.zig deleted file mode 100644 index 57993077e1a8..000000000000 --- a/test/cases/bswap.zig +++ /dev/null @@ -1,32 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; - -test "@bswap" { - comptime testByteSwap(); - testByteSwap(); -} - -fn testByteSwap() void { - assert(@bswap(u0, 0) == 0); - assert(@bswap(u8, 0x12) == 0x12); - assert(@bswap(u16, 0x1234) == 0x3412); - assert(@bswap(u24, 0x123456) == 0x563412); - assert(@bswap(u32, 0x12345678) == 0x78563412); - assert(@bswap(u40, 0x123456789a) == 0x9a78563412); - assert(@bswap(u48, 0x123456789abc) == 0xbc9a78563412); - assert(@bswap(u56, 0x123456789abcde) == 0xdebc9a78563412); - assert(@bswap(u64, 0x123456789abcdef1) == 0xf1debc9a78563412); - assert(@bswap(u128, 0x123456789abcdef11121314151617181) == 0x8171615141312111f1debc9a78563412); - - assert(@bswap(i0, 0) == 0); - assert(@bswap(i8, -50) == -50); - assert(@bswap(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x3412))); - assert(@bswap(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x563412))); - assert(@bswap(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x78563412))); - assert(@bswap(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x9a78563412))); - assert(@bswap(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0xbc9a78563412))); - assert(@bswap(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0xdebc9a78563412))); - assert(@bswap(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0xf1debc9a78563412))); - assert(@bswap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == - @bitCast(i128, u128(0x8171615141312111f1debc9a78563412))); -} diff --git a/test/stage1/behavior/bswap.zig b/test/stage1/behavior/bswap.zig index d0a23c2653db..8084538e03c0 100644 --- a/test/stage1/behavior/bswap.zig +++ b/test/stage1/behavior/bswap.zig @@ -1,3 +1,32 @@ const std = @import("std"); const assertOrPanic = std.debug.assertOrPanic; +test "@bswap" { + comptime testByteSwap(); + testByteSwap(); +} + +fn testByteSwap() void { + assertOrPanic(@bswap(u0, 0) == 0); + assertOrPanic(@bswap(u8, 0x12) == 0x12); + assertOrPanic(@bswap(u16, 0x1234) == 0x3412); + assertOrPanic(@bswap(u24, 0x123456) == 0x563412); + assertOrPanic(@bswap(u32, 0x12345678) == 0x78563412); + assertOrPanic(@bswap(u40, 0x123456789a) == 0x9a78563412); + assertOrPanic(@bswap(u48, 0x123456789abc) == 0xbc9a78563412); + assertOrPanic(@bswap(u56, 0x123456789abcde) == 0xdebc9a78563412); + assertOrPanic(@bswap(u64, 0x123456789abcdef1) == 0xf1debc9a78563412); + assertOrPanic(@bswap(u128, 0x123456789abcdef11121314151617181) == 0x8171615141312111f1debc9a78563412); + + assertOrPanic(@bswap(i0, 0) == 0); + assertOrPanic(@bswap(i8, -50) == -50); + assertOrPanic(@bswap(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x3412))); + assertOrPanic(@bswap(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x563412))); + assertOrPanic(@bswap(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x78563412))); + assertOrPanic(@bswap(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x9a78563412))); + assertOrPanic(@bswap(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0xbc9a78563412))); + assertOrPanic(@bswap(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0xdebc9a78563412))); + assertOrPanic(@bswap(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0xf1debc9a78563412))); + assertOrPanic(@bswap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == + @bitCast(i128, u128(0x8171615141312111f1debc9a78563412))); +} From a5d83d0024579f7cc3c450298bed46bb5fc9af4c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Dec 2018 13:41:56 -0500 Subject: [PATCH 119/190] copy elision: fix implicit cast --- src/ir.cpp | 8 +++----- test/cases/misc.zig | 7 ------- test/stage1/behavior/misc.zig | 8 ++++++++ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 892bb602d21d..70c9fe079bc1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -22985,12 +22985,10 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; - if (!type_has_bits(dest_type) || !handle_is_ptr(dest_type)) { - return nullptr; - } - IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->prev_result_loc->child, - dest_type, false); + dest_type, true); + if (new_result_loc == nullptr) + return nullptr; if (type_is_invalid(new_result_loc->value.type)) return ira->codegen->invalid_instruction; return new_result_loc; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index caccd573d577..ae559b71aa30 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -5,13 +5,6 @@ const cstr = std.cstr; const builtin = @import("builtin"); const maxInt = std.math.maxInt; -test "volatile load and store" { - var number: i32 = 1234; - const ptr = (*volatile i32)(&number); - ptr.* += 1; - assertOrPanic(ptr.* == 1235); -} - test "slice string literal has type []const u8" { comptime { assertOrPanic(@typeOf("aoeu"[0..]) == []const u8); diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 05ac7d9fae5a..85a35414fd10 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -639,3 +639,11 @@ test "self reference through fn ptr field" { a.f = S.foo; assertOrPanic(a.f(a) == 12); } + +test "volatile load and store" { + var number: i32 = 1234; + const ptr = (*volatile i32)(&number); + ptr.* += 1; + assertOrPanic(ptr.* == 1235); +} + From 31f79c4306e78b1f2730a9511604ae4119f4a6ff Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Dec 2018 17:13:38 -0500 Subject: [PATCH 120/190] copy elision: comptime const array init --- src/analyze.cpp | 4 ++++ src/ir.cpp | 9 +++++---- test/cases/misc.zig | 17 ----------------- test/stage1/behavior/misc.zig | 12 ++++++++++++ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 5a50da3632c5..bfd4cf602b87 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6210,6 +6210,10 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { // Canonicalize the array value as ConstArraySpecialNone void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == ZigTypeIdArray); + if (const_val->special == ConstValSpecialUndef) { + const_val->special = ConstValSpecialStatic; + const_val->data.x_array.special = ConstArraySpecialUndef; + } switch (const_val->data.x_array.special) { case ConstArraySpecialNone: return; diff --git a/src/ir.cpp b/src/ir.cpp index 70c9fe079bc1..e3bb34e0fba5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -193,11 +193,12 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c case ConstPtrSpecialRef: result = const_val->data.x_ptr.data.ref.pointee; break; - case ConstPtrSpecialBaseArray: - expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val); - result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.data.s_none.elements[ - const_val->data.x_ptr.data.base_array.elem_index]; + case ConstPtrSpecialBaseArray: { + ConstExprValue *array_val = const_val->data.x_ptr.data.base_array.array_val; + expand_undef_array(g, array_val); + result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; break; + } case ConstPtrSpecialBaseStruct: result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ const_val->data.x_ptr.data.base_struct.field_index]; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index ae559b71aa30..cbbd00df755b 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -5,23 +5,6 @@ const cstr = std.cstr; const builtin = @import("builtin"); const maxInt = std.math.maxInt; -test "slice string literal has type []const u8" { - comptime { - assertOrPanic(@typeOf("aoeu"[0..]) == []const u8); - const array = []i32{ - 1, - 2, - 3, - 4, - }; - assertOrPanic(@typeOf(array[0..]) == []const i32); - } -} - -test "pointer child field" { - assertOrPanic((*u32).Child == u32); -} - test "struct inside function" { testStructInFn(); comptime testStructInFn(); diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 85a35414fd10..5ce071cf9235 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -647,3 +647,15 @@ test "volatile load and store" { assertOrPanic(ptr.* == 1235); } +test "slice string literal has type []const u8" { + comptime { + assertOrPanic(@typeOf("aoeu"[0..]) == []const u8); + const array = []i32{ 1, 2, 3, 4 }; + assertOrPanic(@typeOf(array[0..]) == []const i32); + } +} + +test "pointer child field" { + assertOrPanic((*u32).Child == u32); +} + From fb65abbea46c84bc2f0d270f5d3d5d5504becbbc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Dec 2018 17:25:09 -0500 Subject: [PATCH 121/190] copy elision: fix comptime struct init --- src/ir.cpp | 4 +--- test/behavior.zig | 1 - test/cases/misc.zig | 25 ------------------------- test/stage1/behavior/misc.zig | 18 ++++++++++++++++++ 4 files changed, 19 insertions(+), 29 deletions(-) delete mode 100644 test/cases/misc.zig diff --git a/src/ir.cpp b/src/ir.cpp index e3bb34e0fba5..4276fc729712 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16210,9 +16210,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ if (type_is_invalid(struct_val->type)) return ira->codegen->invalid_instruction; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer && - struct_val->special == ConstValSpecialUndef) - { + if (struct_val->special == ConstValSpecialUndef) { struct_val->data.x_struct.fields = create_const_vals(bare_type->data.structure.src_field_count); struct_val->special = ConstValSpecialStatic; for (size_t i = 0; i < bare_type->data.structure.src_field_count; i += 1) { diff --git a/test/behavior.zig b/test/behavior.zig index 469942a72272..87d9f626c3a5 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -18,7 +18,6 @@ comptime { _ = @import("cases/for.zig"); _ = @import("cases/ir_block_deps.zig"); _ = @import("cases/math.zig"); - _ = @import("cases/misc.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); _ = @import("cases/new_stack_call.zig"); _ = @import("cases/null.zig"); diff --git a/test/cases/misc.zig b/test/cases/misc.zig deleted file mode 100644 index cbbd00df755b..000000000000 --- a/test/cases/misc.zig +++ /dev/null @@ -1,25 +0,0 @@ -const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; -const mem = std.mem; -const cstr = std.cstr; -const builtin = @import("builtin"); -const maxInt = std.math.maxInt; - -test "struct inside function" { - testStructInFn(); - comptime testStructInFn(); -} - -fn testStructInFn() void { - const BlockKind = u32; - - const Block = struct { - kind: BlockKind, - }; - - var block = Block{ .kind = 1234 }; - - block.kind += 1; - - assertOrPanic(block.kind == 1235); -} diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 5ce071cf9235..d5563ec4060e 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -659,3 +659,21 @@ test "pointer child field" { assertOrPanic((*u32).Child == u32); } +test "struct inside function" { + testStructInFn(); + comptime testStructInFn(); +} + +fn testStructInFn() void { + const BlockKind = u32; + + const Block = struct { + kind: BlockKind, + }; + + var block = Block{ .kind = 1234 }; + + block.kind += 1; + + assertOrPanic(block.kind == 1235); +} From 8f2931cfdcccc3639d498b07c2ad39b82d3b8951 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Dec 2018 18:02:09 -0500 Subject: [PATCH 122/190] copy elision: fn call returning scalar optional in equality expression --- src/codegen.cpp | 1 - src/ir.cpp | 7 ++++--- test/stage1/behavior/misc.zig | 8 ++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 57667de35df8..2a0d11944423 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3380,7 +3380,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr bool is_error_union = src_return_type->id == ZigTypeIdErrorUnion; bool is_optional = src_return_type->id == ZigTypeIdOptional; bool first_arg_ret = (is_error_union && type_has_bits(src_return_type->data.error_union.payload_type)) || - (is_optional && type_has_bits(src_return_type->data.maybe.child_type)) || (ret_has_bits && want_first_arg_sret(g, fn_type_id)); bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id); bool is_var_args = fn_type_id->is_var_args; diff --git a/src/ir.cpp b/src/ir.cpp index 4276fc729712..dea8b814e379 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14437,7 +14437,7 @@ static IrInstruction *ir_analyze_set_non_null_bit(IrAnalyze *ira, if (type_is_invalid(opt_type)) return ira->codegen->invalid_instruction; - if (opt_type->id != ZigTypeIdErrorUnion) { + if (opt_type->id != ZigTypeIdOptional) { ir_add_error(ira, base_ptr, buf_sprintf("expected optional type, found '%s'", buf_ptr(&opt_type->name))); return ira->codegen->invalid_instruction; @@ -14447,6 +14447,8 @@ static IrInstruction *ir_analyze_set_non_null_bit(IrAnalyze *ira, zig_panic("TODO comptime set non null bit"); } + assert(handle_is_ptr(opt_type)); + return ir_build_set_nonnull_bit(&ira->new_irb, source_instr->scope, source_instr->source_node, base_ptr, non_null_bit, nullptr); } @@ -14475,7 +14477,7 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, scalar_result_type = return_type; payload_result_type = ira->codegen->builtin_types.entry_void; convert_to_value = false; - } else if (return_type->id == ZigTypeIdOptional) { + } else if (return_type->id == ZigTypeIdOptional && handle_is_ptr(return_type)) { scalar_result_type = ira->codegen->builtin_types.entry_bool; payload_result_type = return_type->data.maybe.child_type; convert_to_value = call_instruction->lval != LValOptional; @@ -14494,7 +14496,6 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, } else { need_store_ptr = false; if (return_type->id == ZigTypeIdErrorUnion || - return_type->id == ZigTypeIdOptional || (type_has_bits(return_type) && want_first_arg_sret(ira->codegen, fn_type_id))) { prev_result_loc = ir_analyze_alloca(ira, &call_instruction->base, nullptr, 0, "", false); diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index d5563ec4060e..8d2555ddddb3 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -677,3 +677,11 @@ fn testStructInFn() void { assertOrPanic(block.kind == 1235); } + +test "fn call returning scalar optional in equality expression" { + assertOrPanic(getNull() == null); +} + +fn getNull() ?*i32 { + return null; +} From 22332d7c664d13da5f7df19968fc3805c613ce1e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Dec 2018 19:37:19 -0500 Subject: [PATCH 123/190] copy elision: fix slicing result of fn call --- src/ir.cpp | 14 +++++++++++++- test/behavior.zig | 2 -- test/stage1/behavior.zig | 4 +++- test/{cases => stage1/behavior}/bit_shifting.zig | 10 +++++----- test/{cases => stage1/behavior}/void.zig | 6 +++--- 5 files changed, 24 insertions(+), 12 deletions(-) rename test/{cases => stage1/behavior}/bit_shifting.zig (90%) rename test/{cases => stage1/behavior}/void.zig (77%) diff --git a/src/ir.cpp b/src/ir.cpp index dea8b814e379..09e49eaa6003 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15291,7 +15291,19 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC IrInstruction *result = ir_implicit_cast(ira, arg_value, dest_type); if (call_instruction->result_loc == nullptr || call_instruction->result_loc->child == nullptr) { - return ir_finish_anal(ira, result); + switch (call_instruction->lval) { + case LValNone: + return ir_finish_anal(ira, result); + case LValPtr: { + IrInstruction *ptr_to_result = ir_get_ref(ira, &call_instruction->base, result, + true, false, nullptr); + return ir_finish_anal(ira, ptr_to_result); + } + case LValErrorUnionVal: + case LValErrorUnionPtr: + case LValOptional: + zig_unreachable(); + } } // we must store into the result loc diff --git a/test/behavior.zig b/test/behavior.zig index 87d9f626c3a5..c9d002e760fa 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -45,8 +45,6 @@ comptime { _ = @import("cases/underscore.zig"); _ = @import("cases/union.zig"); _ = @import("cases/var_args.zig"); - _ = @import("cases/void.zig"); _ = @import("cases/while.zig"); _ = @import("cases/widening.zig"); - _ = @import("cases/bit_shifting.zig"); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index accfca52bfad..f36b7e199325 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -1,9 +1,10 @@ comptime { _ = @import("behavior/align.zig"); _ = @import("behavior/alignof.zig"); - _ = @import("behavior/asm.zig"); _ = @import("behavior/array.zig"); + _ = @import("behavior/asm.zig"); _ = @import("behavior/atomics.zig"); + _ = @import("behavior/bit_shifting.zig"); _ = @import("behavior/bitcast.zig"); _ = @import("behavior/bool.zig"); _ = @import("behavior/bswap.zig"); @@ -25,4 +26,5 @@ comptime { _ = @import("behavior/incomplete_struct_param_tld.zig"); _ = @import("behavior/merge_error_sets.zig"); _ = @import("behavior/misc.zig"); + _ = @import("behavior/void.zig"); } diff --git a/test/cases/bit_shifting.zig b/test/stage1/behavior/bit_shifting.zig similarity index 90% rename from test/cases/bit_shifting.zig rename to test/stage1/behavior/bit_shifting.zig index 325e765bb0a5..3290688358fe 100644 --- a/test/cases/bit_shifting.zig +++ b/test/stage1/behavior/bit_shifting.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type { - assert(Key == @IntType(false, Key.bit_count)); - assert(Key.bit_count >= mask_bit_count); + assertOrPanic(Key == @IntType(false, Key.bit_count)); + assertOrPanic(Key.bit_count >= mask_bit_count); const ShardKey = @IntType(false, mask_bit_count); const shift_amount = Key.bit_count - ShardKey.bit_count; return struct { @@ -77,12 +77,12 @@ fn testShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, c var node_buffer: [node_count]Table.Node = undefined; for (node_buffer) |*node, i| { const key = @intCast(Key, i); - assert(table.get(key) == null); + assertOrPanic(table.get(key) == null); node.init(key, {}); table.put(node); } for (node_buffer) |*node, i| { - assert(table.get(@intCast(Key, i)) == node); + assertOrPanic(table.get(@intCast(Key, i)) == node); } } diff --git a/test/cases/void.zig b/test/stage1/behavior/void.zig similarity index 77% rename from test/cases/void.zig rename to test/stage1/behavior/void.zig index 7121ac664b94..af8b913534c3 100644 --- a/test/cases/void.zig +++ b/test/stage1/behavior/void.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Foo = struct { a: void, @@ -13,14 +13,14 @@ test "compare void with void compile time known" { .b = 1, .c = {}, }; - assert(foo.a == {}); + assertOrPanic(foo.a == {}); } } test "iterate over a void slice" { var j: usize = 0; for (times(10)) |_, i| { - assert(i == j); + assertOrPanic(i == j); j += 1; } } From a1fad2bab9790be1da96f2e7d4f12d19a5719be4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Dec 2018 19:42:18 -0500 Subject: [PATCH 124/190] copy elision: move passing tests --- test/behavior.zig | 1 - test/cases/while.zig | 81 ++++---------------- test/stage1/behavior.zig | 2 + test/stage1/behavior/while.zig | 50 ++++++++++++ test/{cases => stage1/behavior}/widening.zig | 9 ++- 5 files changed, 74 insertions(+), 69 deletions(-) create mode 100644 test/stage1/behavior/while.zig rename test/{cases => stage1/behavior}/widening.zig (75%) diff --git a/test/behavior.zig b/test/behavior.zig index c9d002e760fa..19f43bbe2907 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -46,5 +46,4 @@ comptime { _ = @import("cases/union.zig"); _ = @import("cases/var_args.zig"); _ = @import("cases/while.zig"); - _ = @import("cases/widening.zig"); } diff --git a/test/cases/while.zig b/test/cases/while.zig index f774e0ec6b32..c024af9fdf87 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -1,51 +1,4 @@ -const assert = @import("std").debug.assert; - -test "while loop" { - var i: i32 = 0; - while (i < 4) { - i += 1; - } - assert(i == 4); - assert(whileLoop1() == 1); -} -fn whileLoop1() i32 { - return whileLoop2(); -} -fn whileLoop2() i32 { - while (true) { - return 1; - } -} -test "static eval while" { - assert(static_eval_while_number == 1); -} -const static_eval_while_number = staticWhileLoop1(); -fn staticWhileLoop1() i32 { - return whileLoop2(); -} -fn staticWhileLoop2() i32 { - while (true) { - return 1; - } -} - -test "continue and break" { - runContinueAndBreakTest(); - assert(continue_and_break_counter == 8); -} -var continue_and_break_counter: i32 = 0; -fn runContinueAndBreakTest() void { - var i: i32 = 0; - while (true) { - continue_and_break_counter += 2; - i += 1; - if (i < 4) { - continue; - } - break; - } - assert(i == 4); -} +const assertOrPanic = @import("std").debug.assertOrPanic; test "return with implicit cast from while loop" { returnWithImplicitCastFromWhileLoopTest() catch unreachable; @@ -65,7 +18,7 @@ test "while with continue expression" { sum += i; } } - assert(sum == 40); + assertOrPanic(sum == 40); } test "while with else" { @@ -77,8 +30,8 @@ test "while with else" { } else { got_else += 1; } - assert(sum == 10); - assert(got_else == 1); + assertOrPanic(sum == 10); + assertOrPanic(got_else == 1); } test "while with optional as condition" { @@ -87,7 +40,7 @@ test "while with optional as condition" { while (getNumberOrNull()) |value| { sum += value; } - assert(sum == 45); + assertOrPanic(sum == 45); } test "while with optional as condition with else" { @@ -96,12 +49,12 @@ test "while with optional as condition with else" { var got_else: i32 = 0; while (getNumberOrNull()) |value| { sum += value; - assert(got_else == 0); + assertOrPanic(got_else == 0); } else { got_else += 1; } - assert(sum == 45); - assert(got_else == 1); + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); } test "while with error union condition" { @@ -111,11 +64,11 @@ test "while with error union condition" { while (getNumberOrErr()) |value| { sum += value; } else |err| { - assert(err == error.OutOfNumbers); + assertOrPanic(err == error.OutOfNumbers); got_else += 1; } - assert(sum == 45); - assert(got_else == 1); + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); } var numbers_left: i32 = undefined; @@ -137,7 +90,7 @@ test "while on optional with else result follow else prong" { break value; } else i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on optional with else result follow break prong" { @@ -145,7 +98,7 @@ test "while on optional with else result follow break prong" { break value; } else i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "while on error union with else result follow else prong" { @@ -153,7 +106,7 @@ test "while on error union with else result follow else prong" { break value; } else |err| i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on error union with else result follow break prong" { @@ -161,7 +114,7 @@ test "while on error union with else result follow break prong" { break value; } else |err| i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "while on bool with else result follow else prong" { @@ -169,7 +122,7 @@ test "while on bool with else result follow else prong" { break i32(10); } else i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on bool with else result follow break prong" { @@ -177,7 +130,7 @@ test "while on bool with else result follow break prong" { break i32(10); } else i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "break from outer while loop" { diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index f36b7e199325..f4f389299f6b 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -27,4 +27,6 @@ comptime { _ = @import("behavior/merge_error_sets.zig"); _ = @import("behavior/misc.zig"); _ = @import("behavior/void.zig"); + _ = @import("behavior/while.zig"); + _ = @import("behavior/widening.zig"); } diff --git a/test/stage1/behavior/while.zig b/test/stage1/behavior/while.zig new file mode 100644 index 000000000000..a3111dd8eb74 --- /dev/null +++ b/test/stage1/behavior/while.zig @@ -0,0 +1,50 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "while loop" { + var i: i32 = 0; + while (i < 4) { + i += 1; + } + assertOrPanic(i == 4); + assertOrPanic(whileLoop1() == 1); +} +fn whileLoop1() i32 { + return whileLoop2(); +} +fn whileLoop2() i32 { + while (true) { + return 1; + } +} + +test "static eval while" { + assertOrPanic(static_eval_while_number == 1); +} +const static_eval_while_number = staticWhileLoop1(); +fn staticWhileLoop1() i32 { + return whileLoop2(); +} +fn staticWhileLoop2() i32 { + while (true) { + return 1; + } +} + +test "continue and break" { + runContinueAndBreakTest(); + assertOrPanic(continue_and_break_counter == 8); +} +var continue_and_break_counter: i32 = 0; +fn runContinueAndBreakTest() void { + var i: i32 = 0; + while (true) { + continue_and_break_counter += 2; + i += 1; + if (i < 4) { + continue; + } + break; + } + assertOrPanic(i == 4); +} + diff --git a/test/cases/widening.zig b/test/stage1/behavior/widening.zig similarity index 75% rename from test/cases/widening.zig rename to test/stage1/behavior/widening.zig index cf6ab4ca0fb4..7577868aff54 100644 --- a/test/cases/widening.zig +++ b/test/stage1/behavior/widening.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; test "integer widening" { @@ -9,13 +9,13 @@ test "integer widening" { var d: u64 = c; var e: u64 = d; var f: u128 = e; - assert(f == a); + assertOrPanic(f == a); } test "implicit unsigned integer to signed integer" { var a: u8 = 250; var b: i16 = a; - assert(b == 250); + assertOrPanic(b == 250); } test "float widening" { @@ -23,5 +23,6 @@ test "float widening" { var b: f32 = a; var c: f64 = b; var d: f128 = c; - assert(d == a); + assertOrPanic(d == a); } + From 1941975c5613fde752a2c323512fbfc1dae023ad Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Dec 2018 19:56:36 -0500 Subject: [PATCH 125/190] copy elision: fix returning implicit void from a function with error union return type --- src/ir.cpp | 6 +++++- test/cases/while.zig | 34 ---------------------------------- test/stage1/behavior/while.zig | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 09e49eaa6003..50da6c1884a2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3545,9 +3545,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindUnconditional: { IrInstruction *return_value; + ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; if (expr_node) { IrInstruction *return_result_loc = nullptr; - ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; LVal expr_lval = LValNone; if (return_type != nullptr) { if (return_type->id == ZigTypeIdErrorUnion) { @@ -3567,6 +3567,10 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, irb->exec->name_fn = prev_name_fn; if (return_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; + } else if (return_type->id == ZigTypeIdErrorUnion && + return_type->data.error_union.payload_type == irb->codegen->builtin_types.entry_void) + { + return_value = ir_build_const_null(irb, scope, node); } else { return_value = ir_build_const_void(irb, scope, node); } diff --git a/test/cases/while.zig b/test/cases/while.zig index c024af9fdf87..b309b2fb0441 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -1,39 +1,5 @@ const assertOrPanic = @import("std").debug.assertOrPanic; -test "return with implicit cast from while loop" { - returnWithImplicitCastFromWhileLoopTest() catch unreachable; -} -fn returnWithImplicitCastFromWhileLoopTest() anyerror!void { - while (true) { - return; - } -} - -test "while with continue expression" { - var sum: i32 = 0; - { - var i: i32 = 0; - while (i < 10) : (i += 1) { - if (i == 5) continue; - sum += i; - } - } - assertOrPanic(sum == 40); -} - -test "while with else" { - var sum: i32 = 0; - var i: i32 = 0; - var got_else: i32 = 0; - while (i < 10) : (i += 1) { - sum += 1; - } else { - got_else += 1; - } - assertOrPanic(sum == 10); - assertOrPanic(got_else == 1); -} - test "while with optional as condition" { numbers_left = 10; var sum: i32 = 0; diff --git a/test/stage1/behavior/while.zig b/test/stage1/behavior/while.zig index a3111dd8eb74..cfaac6814db2 100644 --- a/test/stage1/behavior/while.zig +++ b/test/stage1/behavior/while.zig @@ -48,3 +48,37 @@ fn runContinueAndBreakTest() void { assertOrPanic(i == 4); } +test "return with implicit cast from while loop" { + returnWithImplicitCastFromWhileLoopTest() catch unreachable; +} +fn returnWithImplicitCastFromWhileLoopTest() anyerror!void { + while (true) { + return; + } +} + +test "while with continue expression" { + var sum: i32 = 0; + { + var i: i32 = 0; + while (i < 10) : (i += 1) { + if (i == 5) continue; + sum += i; + } + } + assertOrPanic(sum == 40); +} + +test "while with else" { + var sum: i32 = 0; + var i: i32 = 0; + var got_else: i32 = 0; + while (i < 10) : (i += 1) { + sum += 1; + } else { + got_else += 1; + } + assertOrPanic(sum == 10); + assertOrPanic(got_else == 1); +} + From 49cd82ac46d0b4b6ee6974e4159ff2883e60b5b7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Dec 2018 14:19:09 -0500 Subject: [PATCH 126/190] copy elision: fix returning null literal from fn with optional return type --- src/ir.cpp | 68 ++++++++++++++++++++++++++-------- test/cases/while.zig | 23 ------------ test/stage1/behavior/while.zig | 50 +++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 38 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 50da6c1884a2..07e099b95330 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4164,10 +4164,24 @@ static IrInstruction *ir_gen_char_lit(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_const_uint(irb, scope, node, node->data.char_literal.value); } -static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeNullLiteral); - return ir_build_const_null(irb, scope, node); + if (result_loc == nullptr) { + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_const_null(irb, scope, node)); + } + switch (lval) { + case LValNone: + case LValPtr: + case LValErrorUnionVal: + case LValErrorUnionPtr: + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_const_null(irb, scope, node)); + case LValOptional: + return ir_build_const_bool(irb, scope, node, false); + } + zig_unreachable(); } static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -5511,27 +5525,35 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode ir_set_cursor_at_end_and_append_block(irb, then_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope, LValNone, result_loc); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope, lval, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; + IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { - else_expr_result = ir_gen_node(irb, else_node, subexpr_scope, LValNone, result_loc); + else_expr_result = ir_gen_node(irb, else_node, subexpr_scope, lval, result_loc); if (else_expr_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { else_expr_result = ir_build_const_void(irb, scope, node); - ir_build_store_ptr(irb, scope, node, result_loc, else_expr_result); } + IrBasicBlock *after_else_block = irb->current_basic_block; if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); - return ir_gen_result(irb, scope, node, lval, result_loc); + IrInstruction **incoming_values = allocate(2); + incoming_values[0] = then_expr_result; + incoming_values[1] = else_expr_result; + IrBasicBlock **incoming_blocks = allocate(2); + incoming_blocks[0] = after_then_block; + incoming_blocks[1] = after_else_block; + + return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { @@ -6348,27 +6370,35 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN } else { var_scope = subexpr_scope; } - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope, LValNone, result_loc); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope, lval, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; + IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { - else_expr_result = ir_gen_node(irb, else_node, subexpr_scope, LValNone, result_loc); + else_expr_result = ir_gen_node(irb, else_node, subexpr_scope, lval, result_loc); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); - ir_build_store_ptr(irb, scope, node, result_loc, else_expr_result); } + IrBasicBlock *after_else_block = irb->current_basic_block; if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); - return ir_gen_result(irb, scope, node, lval, result_loc); + IrInstruction **incoming_values = allocate(2); + incoming_values[0] = then_expr_result; + incoming_values[1] = else_expr_result; + IrBasicBlock **incoming_blocks = allocate(2); + incoming_blocks[0] = after_then_block; + incoming_blocks[1] = after_else_block; + + return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -6424,9 +6454,10 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * } else { var_scope = subexpr_scope; } - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope, LValNone, result_loc); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope, lval, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; + IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -6448,18 +6479,25 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * } else { err_var_scope = subexpr_scope; } - else_expr_result = ir_gen_node(irb, else_node, err_var_scope, LValNone, result_loc); + else_expr_result = ir_gen_node(irb, else_node, err_var_scope, lval, result_loc); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); - ir_build_store_ptr(irb, scope, node, result_loc, else_expr_result); } + IrBasicBlock *after_else_block = irb->current_basic_block; if (!instr_is_unreachable(else_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, endif_block); - return ir_gen_result(irb, scope, node, lval, result_loc); + IrInstruction **incoming_values = allocate(2); + incoming_values[0] = then_expr_result; + incoming_values[1] = else_expr_result; + IrBasicBlock **incoming_blocks = allocate(2); + incoming_blocks[0] = after_then_block; + incoming_blocks[1] = after_else_block; + + return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, @@ -7744,7 +7782,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeAsmExpr: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_asm_expr(irb, scope, node)); case NodeTypeNullLiteral: - return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_null_literal(irb, scope, node)); + return ir_gen_null_literal(irb, scope, node, lval, result_loc); case NodeTypeIfErrorExpr: return ir_gen_if_err_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); diff --git a/test/cases/while.zig b/test/cases/while.zig index b309b2fb0441..1dbadbc06a2f 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -1,28 +1,5 @@ const assertOrPanic = @import("std").debug.assertOrPanic; -test "while with optional as condition" { - numbers_left = 10; - var sum: i32 = 0; - while (getNumberOrNull()) |value| { - sum += value; - } - assertOrPanic(sum == 45); -} - -test "while with optional as condition with else" { - numbers_left = 10; - var sum: i32 = 0; - var got_else: i32 = 0; - while (getNumberOrNull()) |value| { - sum += value; - assertOrPanic(got_else == 0); - } else { - got_else += 1; - } - assertOrPanic(sum == 45); - assertOrPanic(got_else == 1); -} - test "while with error union condition" { numbers_left = 10; var sum: i32 = 0; diff --git a/test/stage1/behavior/while.zig b/test/stage1/behavior/while.zig index cfaac6814db2..95d973a90e71 100644 --- a/test/stage1/behavior/while.zig +++ b/test/stage1/behavior/while.zig @@ -82,3 +82,53 @@ test "while with else" { assertOrPanic(got_else == 1); } +test "while with optional as condition" { + numbers_left = 10; + var sum: i32 = 0; + while (getNumberOrNull()) |value| { + sum += value; + } + assertOrPanic(sum == 45); +} + +test "while with optional as condition with else" { + numbers_left = 10; + var sum: i32 = 0; + var got_else: i32 = 0; + while (getNumberOrNull()) |value| { + sum += value; + assertOrPanic(got_else == 0); + } else { + got_else += 1; + } + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); +} + +//test "while with error union condition" { +// numbers_left = 10; +// var sum: i32 = 0; +// var got_else: i32 = 0; +// while (getNumberOrErr()) |value| { +// sum += value; +// } else |err| { +// assertOrPanic(err == error.OutOfNumbers); +// got_else += 1; +// } +// assertOrPanic(sum == 45); +// assertOrPanic(got_else == 1); +//} + +var numbers_left: i32 = undefined; +fn getNumberOrErr() anyerror!i32 { + return if (numbers_left == 0) error.OutOfNumbers else x: { + numbers_left -= 1; + break :x numbers_left; + }; +} +fn getNumberOrNull() ?i32 { + return if (numbers_left == 0) null else x: { + numbers_left -= 1; + break :x numbers_left; + }; +} From ce6862f70f4ae560ea2b4e759cfa2d1d734ab9a7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Dec 2018 14:42:04 -0500 Subject: [PATCH 127/190] copy elision: fix while with error union condition --- src/ir.cpp | 4 +-- test/cases/while.zig | 36 ----------------------- test/stage1/behavior/while.zig | 53 +++++++++++++++++++++++++--------- 3 files changed, 41 insertions(+), 52 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 07e099b95330..4372ac933c14 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10037,9 +10037,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT buf_sprintf("unable to make error union out of number literal")); return ira->codegen->builtin_types.entry_invalid; } else if (prev_inst->value.type->id == ZigTypeIdNull) { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to make error union out of null literal")); - return ira->codegen->builtin_types.entry_invalid; + return get_optional_type(ira->codegen, err_set_type); } else { if ((err = type_resolve(ira->codegen, prev_inst->value.type, ResolveStatusSizeKnown))) return ira->codegen->builtin_types.entry_invalid; diff --git a/test/cases/while.zig b/test/cases/while.zig index 1dbadbc06a2f..7249c04e6046 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -1,41 +1,5 @@ const assertOrPanic = @import("std").debug.assertOrPanic; -test "while with error union condition" { - numbers_left = 10; - var sum: i32 = 0; - var got_else: i32 = 0; - while (getNumberOrErr()) |value| { - sum += value; - } else |err| { - assertOrPanic(err == error.OutOfNumbers); - got_else += 1; - } - assertOrPanic(sum == 45); - assertOrPanic(got_else == 1); -} - -var numbers_left: i32 = undefined; -fn getNumberOrErr() anyerror!i32 { - return if (numbers_left == 0) error.OutOfNumbers else x: { - numbers_left -= 1; - break :x numbers_left; - }; -} -fn getNumberOrNull() ?i32 { - return if (numbers_left == 0) null else x: { - numbers_left -= 1; - break :x numbers_left; - }; -} - -test "while on optional with else result follow else prong" { - const result = while (returnNull()) |value| { - break value; - } else - i32(2); - assertOrPanic(result == 2); -} - test "while on optional with else result follow break prong" { const result = while (returnOptional(10)) |value| { break value; diff --git a/test/stage1/behavior/while.zig b/test/stage1/behavior/while.zig index 95d973a90e71..af46df3f7d21 100644 --- a/test/stage1/behavior/while.zig +++ b/test/stage1/behavior/while.zig @@ -105,19 +105,19 @@ test "while with optional as condition with else" { assertOrPanic(got_else == 1); } -//test "while with error union condition" { -// numbers_left = 10; -// var sum: i32 = 0; -// var got_else: i32 = 0; -// while (getNumberOrErr()) |value| { -// sum += value; -// } else |err| { -// assertOrPanic(err == error.OutOfNumbers); -// got_else += 1; -// } -// assertOrPanic(sum == 45); -// assertOrPanic(got_else == 1); -//} +test "while with error union condition" { + numbers_left = 10; + var sum: i32 = 0; + var got_else: i32 = 0; + while (getNumberOrErr()) |value| { + sum += value; + } else |err| { + assertOrPanic(err == error.OutOfNumbers); + got_else += 1; + } + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); +} var numbers_left: i32 = undefined; fn getNumberOrErr() anyerror!i32 { @@ -132,3 +132,30 @@ fn getNumberOrNull() ?i32 { break :x numbers_left; }; } + +test "while on optional with else result follow else prong" { + const result = while (returnNull()) |value| { + break value; + } else + i32(2); + assertOrPanic(result == 2); +} + +fn returnNull() ?i32 { + return null; +} +fn returnOptional(x: i32) ?i32 { + return x; +} +fn returnError() anyerror!i32 { + return error.YouWantedAnError; +} +fn returnSuccess(x: i32) anyerror!i32 { + return x; +} +fn returnFalse() bool { + return false; +} +fn returnTrue() bool { + return true; +} From d3c8bbd1ab1f81c1cce92227270501111c13bb07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Dec 2018 15:14:12 -0500 Subject: [PATCH 128/190] copy elision: fix returning payload from optional function --- src/ir.cpp | 2 + test/behavior.zig | 1 - test/cases/while.zig | 87 ---------------------------------- test/stage1/behavior/while.zig | 67 ++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 88 deletions(-) delete mode 100644 test/cases/while.zig diff --git a/src/ir.cpp b/src/ir.cpp index 4372ac933c14..81212599f258 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17438,6 +17438,8 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr return ira->codegen->invalid_instruction; if (type_entry->id != ZigTypeIdOptional) { + if (!safety_check_on) + return base_ptr; ir_add_error_node(ira, base_ptr->source_node, buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; diff --git a/test/behavior.zig b/test/behavior.zig index 19f43bbe2907..08d9ce70834a 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -45,5 +45,4 @@ comptime { _ = @import("cases/underscore.zig"); _ = @import("cases/union.zig"); _ = @import("cases/var_args.zig"); - _ = @import("cases/while.zig"); } diff --git a/test/cases/while.zig b/test/cases/while.zig deleted file mode 100644 index 7249c04e6046..000000000000 --- a/test/cases/while.zig +++ /dev/null @@ -1,87 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -test "while on optional with else result follow break prong" { - const result = while (returnOptional(10)) |value| { - break value; - } else - i32(2); - assertOrPanic(result == 10); -} - -test "while on error union with else result follow else prong" { - const result = while (returnError()) |value| { - break value; - } else |err| - i32(2); - assertOrPanic(result == 2); -} - -test "while on error union with else result follow break prong" { - const result = while (returnSuccess(10)) |value| { - break value; - } else |err| - i32(2); - assertOrPanic(result == 10); -} - -test "while on bool with else result follow else prong" { - const result = while (returnFalse()) { - break i32(10); - } else - i32(2); - assertOrPanic(result == 2); -} - -test "while on bool with else result follow break prong" { - const result = while (returnTrue()) { - break i32(10); - } else - i32(2); - assertOrPanic(result == 10); -} - -test "break from outer while loop" { - testBreakOuter(); - comptime testBreakOuter(); -} - -fn testBreakOuter() void { - outer: while (true) { - while (true) { - break :outer; - } - } -} - -test "continue outer while loop" { - testContinueOuter(); - comptime testContinueOuter(); -} - -fn testContinueOuter() void { - var i: usize = 0; - outer: while (i < 10) : (i += 1) { - while (true) { - continue :outer; - } - } -} - -fn returnNull() ?i32 { - return null; -} -fn returnOptional(x: i32) ?i32 { - return x; -} -fn returnError() anyerror!i32 { - return error.YouWantedAnError; -} -fn returnSuccess(x: i32) anyerror!i32 { - return x; -} -fn returnFalse() bool { - return false; -} -fn returnTrue() bool { - return true; -} diff --git a/test/stage1/behavior/while.zig b/test/stage1/behavior/while.zig index af46df3f7d21..579b4e4db852 100644 --- a/test/stage1/behavior/while.zig +++ b/test/stage1/behavior/while.zig @@ -141,6 +141,73 @@ test "while on optional with else result follow else prong" { assertOrPanic(result == 2); } +test "while on optional with else result follow break prong" { + const result = while (returnOptional(10)) |value| { + break value; + } else + i32(2); + assertOrPanic(result == 10); +} + +test "while on error union with else result follow else prong" { + const result = while (returnError()) |value| { + break value; + } else |err| + i32(2); + assertOrPanic(result == 2); +} + +test "while on error union with else result follow break prong" { + const result = while (returnSuccess(10)) |value| { + break value; + } else |err| + i32(2); + assertOrPanic(result == 10); +} + +test "while on bool with else result follow else prong" { + const result = while (returnFalse()) { + break i32(10); + } else + i32(2); + assertOrPanic(result == 2); +} + +test "while on bool with else result follow break prong" { + const result = while (returnTrue()) { + break i32(10); + } else + i32(2); + assertOrPanic(result == 10); +} + +test "break from outer while loop" { + testBreakOuter(); + comptime testBreakOuter(); +} + +fn testBreakOuter() void { + outer: while (true) { + while (true) { + break :outer; + } + } +} + +test "continue outer while loop" { + testContinueOuter(); + comptime testContinueOuter(); +} + +fn testContinueOuter() void { + var i: usize = 0; + outer: while (i < 10) : (i += 1) { + while (true) { + continue :outer; + } + } +} + fn returnNull() ?i32 { return null; } From 7a026c7b4dcababeed8517dab1ee297c81a9b184 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Dec 2018 15:25:28 -0500 Subject: [PATCH 129/190] copy elision: move passing tests --- test/behavior.zig | 3 - test/cases/math.zig | 465 +-------------------- test/cases/reflection.zig | 95 ----- test/stage1/behavior.zig | 4 + test/stage1/behavior/math.zig | 456 ++++++++++++++++++++ test/stage1/behavior/reflection.zig | 96 +++++ test/{cases => stage1/behavior}/syntax.zig | 1 + test/{cases => stage1/behavior}/this.zig | 9 +- 8 files changed, 569 insertions(+), 560 deletions(-) delete mode 100644 test/cases/reflection.zig create mode 100644 test/stage1/behavior/math.zig create mode 100644 test/stage1/behavior/reflection.zig rename test/{cases => stage1/behavior}/syntax.zig (99%) rename test/{cases => stage1/behavior}/this.zig (74%) diff --git a/test/behavior.zig b/test/behavior.zig index 08d9ce70834a..0d36b3d37cf7 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -27,7 +27,6 @@ comptime { _ = @import("cases/ptrcast.zig"); _ = @import("cases/pub_enum/index.zig"); _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); - _ = @import("cases/reflection.zig"); _ = @import("cases/sizeof_and_typeof.zig"); _ = @import("cases/slice.zig"); _ = @import("cases/struct.zig"); @@ -36,8 +35,6 @@ comptime { _ = @import("cases/switch.zig"); _ = @import("cases/switch_prong_err_enum.zig"); _ = @import("cases/switch_prong_implicit_cast.zig"); - _ = @import("cases/syntax.zig"); - _ = @import("cases/this.zig"); _ = @import("cases/truncate.zig"); _ = @import("cases/try.zig"); _ = @import("cases/type_info.zig"); diff --git a/test/cases/math.zig b/test/cases/math.zig index 7d6b1bd9acbc..9ab02f60d2cb 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -1,459 +1,8 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const maxInt = std.math.maxInt; const minInt = std.math.minInt; -test "division" { - testDivision(); - comptime testDivision(); -} -fn testDivision() void { - assert(div(u32, 13, 3) == 4); - assert(div(f16, 1.0, 2.0) == 0.5); - assert(div(f32, 1.0, 2.0) == 0.5); - - assert(divExact(u32, 55, 11) == 5); - assert(divExact(i32, -55, 11) == -5); - assert(divExact(f16, 55.0, 11.0) == 5.0); - assert(divExact(f16, -55.0, 11.0) == -5.0); - assert(divExact(f32, 55.0, 11.0) == 5.0); - assert(divExact(f32, -55.0, 11.0) == -5.0); - - assert(divFloor(i32, 5, 3) == 1); - assert(divFloor(i32, -5, 3) == -2); - assert(divFloor(f16, 5.0, 3.0) == 1.0); - assert(divFloor(f16, -5.0, 3.0) == -2.0); - assert(divFloor(f32, 5.0, 3.0) == 1.0); - assert(divFloor(f32, -5.0, 3.0) == -2.0); - assert(divFloor(i32, -0x80000000, -2) == 0x40000000); - assert(divFloor(i32, 0, -0x80000000) == 0); - assert(divFloor(i32, -0x40000001, 0x40000000) == -2); - assert(divFloor(i32, -0x80000000, 1) == -0x80000000); - - assert(divTrunc(i32, 5, 3) == 1); - assert(divTrunc(i32, -5, 3) == -1); - assert(divTrunc(f16, 5.0, 3.0) == 1.0); - assert(divTrunc(f16, -5.0, 3.0) == -1.0); - assert(divTrunc(f32, 5.0, 3.0) == 1.0); - assert(divTrunc(f32, -5.0, 3.0) == -1.0); - assert(divTrunc(f64, 5.0, 3.0) == 1.0); - assert(divTrunc(f64, -5.0, 3.0) == -1.0); - - comptime { - assert( - 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, - ); - assert( - @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, - ); - assert( - 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, - ); - assert( - @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, - ); - assert( - @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, - ); - assert( - @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, - ); - assert( - 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, - ); - } -} -fn div(comptime T: type, a: T, b: T) T { - return a / b; -} -fn divExact(comptime T: type, a: T, b: T) T { - return @divExact(a, b); -} -fn divFloor(comptime T: type, a: T, b: T) T { - return @divFloor(a, b); -} -fn divTrunc(comptime T: type, a: T, b: T) T { - return @divTrunc(a, b); -} - -test "@addWithOverflow" { - var result: u8 = undefined; - assert(@addWithOverflow(u8, 250, 100, &result)); - assert(!@addWithOverflow(u8, 100, 150, &result)); - assert(result == 250); -} - -// TODO test mulWithOverflow -// TODO test subWithOverflow - -test "@shlWithOverflow" { - var result: u16 = undefined; - assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); - assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); - assert(result == 0b1011111111111100); -} - -test "@clz" { - testClz(); - comptime testClz(); -} - -fn testClz() void { - assert(clz(u8(0b00001010)) == 4); - assert(clz(u8(0b10001010)) == 0); - assert(clz(u8(0b00000000)) == 8); - assert(clz(u128(0xffffffffffffffff)) == 64); - assert(clz(u128(0x10000000000000000)) == 63); -} - -fn clz(x: var) usize { - return @clz(x); -} - -test "@ctz" { - testCtz(); - comptime testCtz(); -} - -fn testCtz() void { - assert(ctz(u8(0b10100000)) == 5); - assert(ctz(u8(0b10001010)) == 1); - assert(ctz(u8(0b00000000)) == 8); -} - -fn ctz(x: var) usize { - return @ctz(x); -} - -test "assignment operators" { - var i: u32 = 0; - i += 5; - assert(i == 5); - i -= 2; - assert(i == 3); - i *= 20; - assert(i == 60); - i /= 3; - assert(i == 20); - i %= 11; - assert(i == 9); - i <<= 1; - assert(i == 18); - i >>= 2; - assert(i == 4); - i = 6; - i &= 5; - assert(i == 4); - i ^= 6; - assert(i == 2); - i = 6; - i |= 3; - assert(i == 7); -} - -test "three expr in a row" { - testThreeExprInARow(false, true); - comptime testThreeExprInARow(false, true); -} -fn testThreeExprInARow(f: bool, t: bool) void { - assertFalse(f or f or f); - assertFalse(t and t and f); - assertFalse(1 | 2 | 4 != 7); - assertFalse(3 ^ 6 ^ 8 != 13); - assertFalse(7 & 14 & 28 != 4); - assertFalse(9 << 1 << 2 != 9 << 3); - assertFalse(90 >> 1 >> 2 != 90 >> 3); - assertFalse(100 - 1 + 1000 != 1099); - assertFalse(5 * 4 / 2 % 3 != 1); - assertFalse(i32(i32(5)) != 5); - assertFalse(!!false); - assertFalse(i32(7) != --(i32(7))); -} -fn assertFalse(b: bool) void { - assert(!b); -} - -test "const number literal" { - const one = 1; - const eleven = ten + one; - - assert(eleven == 11); -} -const ten = 10; - -test "unsigned wrapping" { - testUnsignedWrappingEval(maxInt(u32)); - comptime testUnsignedWrappingEval(maxInt(u32)); -} -fn testUnsignedWrappingEval(x: u32) void { - const zero = x +% 1; - assert(zero == 0); - const orig = zero -% 1; - assert(orig == maxInt(u32)); -} - -test "signed wrapping" { - testSignedWrappingEval(maxInt(i32)); - comptime testSignedWrappingEval(maxInt(i32)); -} -fn testSignedWrappingEval(x: i32) void { - const min_val = x +% 1; - assert(min_val == minInt(i32)); - const max_val = min_val -% 1; - assert(max_val == maxInt(i32)); -} - -test "negation wrapping" { - testNegationWrappingEval(minInt(i16)); - comptime testNegationWrappingEval(minInt(i16)); -} -fn testNegationWrappingEval(x: i16) void { - assert(x == -32768); - const neg = -%x; - assert(neg == -32768); -} - -test "unsigned 64-bit division" { - test_u64_div(); - comptime test_u64_div(); -} -fn test_u64_div() void { - const result = divWithResult(1152921504606846976, 34359738365); - assert(result.quotient == 33554432); - assert(result.remainder == 100663296); -} -fn divWithResult(a: u64, b: u64) DivResult { - return DivResult{ - .quotient = a / b, - .remainder = a % b, - }; -} -const DivResult = struct { - quotient: u64, - remainder: u64, -}; - -test "binary not" { - assert(comptime x: { - break :x ~u16(0b1010101010101010) == 0b0101010101010101; - }); - assert(comptime x: { - break :x ~u64(2147483647) == 18446744071562067968; - }); - testBinaryNot(0b1010101010101010); -} - -fn testBinaryNot(x: u16) void { - assert(~x == 0b0101010101010101); -} - -test "small int addition" { - var x: @IntType(false, 2) = 0; - assert(x == 0); - - x += 1; - assert(x == 1); - - x += 1; - assert(x == 2); - - x += 1; - assert(x == 3); - - var result: @typeOf(x) = 3; - assert(@addWithOverflow(@typeOf(x), x, 1, &result)); - - assert(result == 0); -} - -test "float equality" { - const x: f64 = 0.012; - const y: f64 = x + 1.0; - - testFloatEqualityImpl(x, y); - comptime testFloatEqualityImpl(x, y); -} - -fn testFloatEqualityImpl(x: f64, y: f64) void { - const y2 = x + 1.0; - assert(y == y2); -} - -test "allow signed integer division/remainder when values are comptime known and positive or exact" { - assert(5 / 3 == 1); - assert(-5 / -3 == 1); - assert(-6 / 3 == -2); - - assert(5 % 3 == 2); - assert(-6 % 3 == 0); -} - -test "hex float literal parsing" { - comptime assert(0x1.0 == 1.0); -} - -test "quad hex float literal parsing in range" { - const a = 0x1.af23456789bbaaab347645365cdep+5; - const b = 0x1.dedafcff354b6ae9758763545432p-9; - const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534; - const d = 0x1.edcbff8ad76ab5bf46463233214fp-435; -} - -test "quad hex float literal parsing accurate" { - const a: f128 = 0x1.1111222233334444555566667777p+0; - - // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. - const expected: u128 = 0x3fff1111222233334444555566667777; - assert(@bitCast(u128, a) == expected); -} - -test "hex float literal within range" { - const a = 0x1.0p16383; - const b = 0x0.1p16387; - const c = 0x1.0p-16382; -} - -test "truncating shift left" { - testShlTrunc(maxInt(u16)); - comptime testShlTrunc(maxInt(u16)); -} -fn testShlTrunc(x: u16) void { - const shifted = x << 1; - assert(shifted == 65534); -} - -test "truncating shift right" { - testShrTrunc(maxInt(u16)); - comptime testShrTrunc(maxInt(u16)); -} -fn testShrTrunc(x: u16) void { - const shifted = x >> 1; - assert(shifted == 32767); -} - -test "exact shift left" { - testShlExact(0b00110101); - comptime testShlExact(0b00110101); -} -fn testShlExact(x: u8) void { - const shifted = @shlExact(x, 2); - assert(shifted == 0b11010100); -} - -test "exact shift right" { - testShrExact(0b10110100); - comptime testShrExact(0b10110100); -} -fn testShrExact(x: u8) void { - const shifted = @shrExact(x, 2); - assert(shifted == 0b00101101); -} - -test "comptime_int addition" { - comptime { - assert(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); - assert(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); - } -} - -test "comptime_int multiplication" { - comptime { - assert( - 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, - ); - assert( - 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, - ); - } -} - -test "comptime_int shifting" { - comptime { - assert((u128(1) << 127) == 0x80000000000000000000000000000000); - } -} - -test "comptime_int multi-limb shift and mask" { - comptime { - var a = 0xefffffffa0000001eeeeeeefaaaaaaab; - - assert(u32(a & 0xffffffff) == 0xaaaaaaab); - a >>= 32; - assert(u32(a & 0xffffffff) == 0xeeeeeeef); - a >>= 32; - assert(u32(a & 0xffffffff) == 0xa0000001); - a >>= 32; - assert(u32(a & 0xffffffff) == 0xefffffff); - a >>= 32; - - assert(a == 0); - } -} - -test "comptime_int multi-limb partial shift right" { - comptime { - var a = 0x1ffffffffeeeeeeee; - a >>= 16; - assert(a == 0x1ffffffffeeee); - } -} - -test "xor" { - test_xor(); - comptime test_xor(); -} - -fn test_xor() void { - assert(0xFF ^ 0x00 == 0xFF); - assert(0xF0 ^ 0x0F == 0xFF); - assert(0xFF ^ 0xF0 == 0x0F); - assert(0xFF ^ 0x0F == 0xF0); - assert(0xFF ^ 0xFF == 0x00); -} - -test "comptime_int xor" { - comptime { - assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); - assert(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); - assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); - assert(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); - assert(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); - } -} - -test "f128" { - test_f128(); - comptime test_f128(); -} - -fn make_f128(x: f128) f128 { - return x; -} - -fn test_f128() void { - assert(@sizeOf(f128) == 16); - assert(make_f128(1.0) == 1.0); - assert(make_f128(1.0) != 1.1); - assert(make_f128(1.0) > 0.9); - assert(make_f128(1.0) >= 0.9); - assert(make_f128(1.0) >= 1.0); - should_not_be_zero(1.0); -} - -fn should_not_be_zero(x: f128) void { - assert(x != 0.0); -} - -test "comptime float rem int" { - comptime { - var x = f32(1) % 2; - assert(x == 1.0); - } -} - test "remainder division" { comptime remdiv(f16); comptime remdiv(f32); @@ -465,8 +14,8 @@ test "remainder division" { } fn remdiv(comptime T: type) void { - assert(T(1) == T(1) % T(2)); - assert(T(1) == T(7) % T(3)); + assertOrPanic(T(1) == T(1) % T(2)); + assertOrPanic(T(1) == T(7) % T(3)); } test "@sqrt" { @@ -480,19 +29,19 @@ test "@sqrt" { const x = 14.0; const y = x * x; const z = @sqrt(@typeOf(y), y); - comptime assert(z == x); + comptime assertOrPanic(z == x); } fn testSqrt(comptime T: type, x: T) void { - assert(@sqrt(T, x * x) == x); + assertOrPanic(@sqrt(T, x * x) == x); } test "comptime_int param and return" { const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); - assert(a == 137114567242441932203689521744947848950); + assertOrPanic(a == 137114567242441932203689521744947848950); const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); - assert(b == 985095453608931032642182098849559179469148836107390954364380); + assertOrPanic(b == 985095453608931032642182098849559179469148836107390954364380); } fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig deleted file mode 100644 index b9b8aff4e16a..000000000000 --- a/test/cases/reflection.zig +++ /dev/null @@ -1,95 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const reflection = @This(); - -test "reflection: array, pointer, optional, error union type child" { - comptime { - assert(([10]u8).Child == u8); - assert((*u8).Child == u8); - assert((anyerror!u8).Payload == u8); - assert((?u8).Child == u8); - } -} - -test "reflection: function return type, var args, and param types" { - comptime { - assert(@typeOf(dummy).ReturnType == i32); - assert(!@typeOf(dummy).is_var_args); - assert(@typeOf(dummy_varargs).is_var_args); - assert(@typeOf(dummy).arg_count == 3); - assert(@ArgType(@typeOf(dummy), 0) == bool); - assert(@ArgType(@typeOf(dummy), 1) == i32); - assert(@ArgType(@typeOf(dummy), 2) == f32); - } -} - -fn dummy(a: bool, b: i32, c: f32) i32 { - return 1234; -} -fn dummy_varargs(args: ...) void {} - -test "reflection: struct member types and names" { - comptime { - assert(@memberCount(Foo) == 3); - - assert(@memberType(Foo, 0) == i32); - assert(@memberType(Foo, 1) == bool); - assert(@memberType(Foo, 2) == void); - - assert(mem.eql(u8, @memberName(Foo, 0), "one")); - assert(mem.eql(u8, @memberName(Foo, 1), "two")); - assert(mem.eql(u8, @memberName(Foo, 2), "three")); - } -} - -test "reflection: enum member types and names" { - comptime { - assert(@memberCount(Bar) == 4); - - assert(@memberType(Bar, 0) == void); - assert(@memberType(Bar, 1) == i32); - assert(@memberType(Bar, 2) == bool); - assert(@memberType(Bar, 3) == f64); - - assert(mem.eql(u8, @memberName(Bar, 0), "One")); - assert(mem.eql(u8, @memberName(Bar, 1), "Two")); - assert(mem.eql(u8, @memberName(Bar, 2), "Three")); - assert(mem.eql(u8, @memberName(Bar, 3), "Four")); - } -} - -test "reflection: @field" { - var f = Foo{ - .one = 42, - .two = true, - .three = void{}, - }; - - assert(f.one == f.one); - assert(@field(f, "o" ++ "ne") == f.one); - assert(@field(f, "t" ++ "wo") == f.two); - assert(@field(f, "th" ++ "ree") == f.three); - assert(@field(Foo, "const" ++ "ant") == Foo.constant); - assert(@field(Bar, "O" ++ "ne") == Bar.One); - assert(@field(Bar, "T" ++ "wo") == Bar.Two); - assert(@field(Bar, "Th" ++ "ree") == Bar.Three); - assert(@field(Bar, "F" ++ "our") == Bar.Four); - assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); - @field(f, "o" ++ "ne") = 4; - assert(f.one == 4); -} - -const Foo = struct { - const constant = 52; - - one: i32, - two: bool, - three: void, -}; - -const Bar = union(enum) { - One: void, - Two: i32, - Three: bool, - Four: f64, -}; diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index f4f389299f6b..afee212d2a62 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -29,4 +29,8 @@ comptime { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); + _ = @import("behavior/this.zig"); + _ = @import("behavior/syntax.zig"); + _ = @import("behavior/reflection.zig"); + _ = @import("behavior/math.zig"); } diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig new file mode 100644 index 000000000000..efb3b05fce4d --- /dev/null +++ b/test/stage1/behavior/math.zig @@ -0,0 +1,456 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const maxInt = std.math.maxInt; +const minInt = std.math.minInt; + +test "division" { + testDivision(); + comptime testDivision(); +} +fn testDivision() void { + assertOrPanic(div(u32, 13, 3) == 4); + assertOrPanic(div(f16, 1.0, 2.0) == 0.5); + assertOrPanic(div(f32, 1.0, 2.0) == 0.5); + + assertOrPanic(divExact(u32, 55, 11) == 5); + assertOrPanic(divExact(i32, -55, 11) == -5); + assertOrPanic(divExact(f16, 55.0, 11.0) == 5.0); + assertOrPanic(divExact(f16, -55.0, 11.0) == -5.0); + assertOrPanic(divExact(f32, 55.0, 11.0) == 5.0); + assertOrPanic(divExact(f32, -55.0, 11.0) == -5.0); + + assertOrPanic(divFloor(i32, 5, 3) == 1); + assertOrPanic(divFloor(i32, -5, 3) == -2); + assertOrPanic(divFloor(f16, 5.0, 3.0) == 1.0); + assertOrPanic(divFloor(f16, -5.0, 3.0) == -2.0); + assertOrPanic(divFloor(f32, 5.0, 3.0) == 1.0); + assertOrPanic(divFloor(f32, -5.0, 3.0) == -2.0); + assertOrPanic(divFloor(i32, -0x80000000, -2) == 0x40000000); + assertOrPanic(divFloor(i32, 0, -0x80000000) == 0); + assertOrPanic(divFloor(i32, -0x40000001, 0x40000000) == -2); + assertOrPanic(divFloor(i32, -0x80000000, 1) == -0x80000000); + + assertOrPanic(divTrunc(i32, 5, 3) == 1); + assertOrPanic(divTrunc(i32, -5, 3) == -1); + assertOrPanic(divTrunc(f16, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f16, -5.0, 3.0) == -1.0); + assertOrPanic(divTrunc(f32, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f32, -5.0, 3.0) == -1.0); + assertOrPanic(divTrunc(f64, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f64, -5.0, 3.0) == -1.0); + + comptime { + assertOrPanic( + 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, + ); + assertOrPanic( + @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, + ); + assertOrPanic( + 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, + ); + assertOrPanic( + @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, + ); + assertOrPanic( + @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, + ); + assertOrPanic( + @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, + ); + assertOrPanic( + 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, + ); + } +} +fn div(comptime T: type, a: T, b: T) T { + return a / b; +} +fn divExact(comptime T: type, a: T, b: T) T { + return @divExact(a, b); +} +fn divFloor(comptime T: type, a: T, b: T) T { + return @divFloor(a, b); +} +fn divTrunc(comptime T: type, a: T, b: T) T { + return @divTrunc(a, b); +} + +test "@addWithOverflow" { + var result: u8 = undefined; + assertOrPanic(@addWithOverflow(u8, 250, 100, &result)); + assertOrPanic(!@addWithOverflow(u8, 100, 150, &result)); + assertOrPanic(result == 250); +} + +// TODO test mulWithOverflow +// TODO test subWithOverflow + +test "@shlWithOverflow" { + var result: u16 = undefined; + assertOrPanic(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); + assertOrPanic(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); + assertOrPanic(result == 0b1011111111111100); +} + +test "@clz" { + testClz(); + comptime testClz(); +} + +fn testClz() void { + assertOrPanic(clz(u8(0b00001010)) == 4); + assertOrPanic(clz(u8(0b10001010)) == 0); + assertOrPanic(clz(u8(0b00000000)) == 8); + assertOrPanic(clz(u128(0xffffffffffffffff)) == 64); + assertOrPanic(clz(u128(0x10000000000000000)) == 63); +} + +fn clz(x: var) usize { + return @clz(x); +} + +test "@ctz" { + testCtz(); + comptime testCtz(); +} + +fn testCtz() void { + assertOrPanic(ctz(u8(0b10100000)) == 5); + assertOrPanic(ctz(u8(0b10001010)) == 1); + assertOrPanic(ctz(u8(0b00000000)) == 8); +} + +fn ctz(x: var) usize { + return @ctz(x); +} + +test "assignment operators" { + var i: u32 = 0; + i += 5; + assertOrPanic(i == 5); + i -= 2; + assertOrPanic(i == 3); + i *= 20; + assertOrPanic(i == 60); + i /= 3; + assertOrPanic(i == 20); + i %= 11; + assertOrPanic(i == 9); + i <<= 1; + assertOrPanic(i == 18); + i >>= 2; + assertOrPanic(i == 4); + i = 6; + i &= 5; + assertOrPanic(i == 4); + i ^= 6; + assertOrPanic(i == 2); + i = 6; + i |= 3; + assertOrPanic(i == 7); +} + +test "three expr in a row" { + testThreeExprInARow(false, true); + comptime testThreeExprInARow(false, true); +} +fn testThreeExprInARow(f: bool, t: bool) void { + assertFalse(f or f or f); + assertFalse(t and t and f); + assertFalse(1 | 2 | 4 != 7); + assertFalse(3 ^ 6 ^ 8 != 13); + assertFalse(7 & 14 & 28 != 4); + assertFalse(9 << 1 << 2 != 9 << 3); + assertFalse(90 >> 1 >> 2 != 90 >> 3); + assertFalse(100 - 1 + 1000 != 1099); + assertFalse(5 * 4 / 2 % 3 != 1); + assertFalse(i32(i32(5)) != 5); + assertFalse(!!false); + assertFalse(i32(7) != --(i32(7))); +} +fn assertFalse(b: bool) void { + assertOrPanic(!b); +} + +test "const number literal" { + const one = 1; + const eleven = ten + one; + + assertOrPanic(eleven == 11); +} +const ten = 10; + +test "unsigned wrapping" { + testUnsignedWrappingEval(maxInt(u32)); + comptime testUnsignedWrappingEval(maxInt(u32)); +} +fn testUnsignedWrappingEval(x: u32) void { + const zero = x +% 1; + assertOrPanic(zero == 0); + const orig = zero -% 1; + assertOrPanic(orig == maxInt(u32)); +} + +test "signed wrapping" { + testSignedWrappingEval(maxInt(i32)); + comptime testSignedWrappingEval(maxInt(i32)); +} +fn testSignedWrappingEval(x: i32) void { + const min_val = x +% 1; + assertOrPanic(min_val == minInt(i32)); + const max_val = min_val -% 1; + assertOrPanic(max_val == maxInt(i32)); +} + +test "negation wrapping" { + testNegationWrappingEval(minInt(i16)); + comptime testNegationWrappingEval(minInt(i16)); +} +fn testNegationWrappingEval(x: i16) void { + assertOrPanic(x == -32768); + const neg = -%x; + assertOrPanic(neg == -32768); +} + +test "unsigned 64-bit division" { + test_u64_div(); + comptime test_u64_div(); +} +fn test_u64_div() void { + const result = divWithResult(1152921504606846976, 34359738365); + assertOrPanic(result.quotient == 33554432); + assertOrPanic(result.remainder == 100663296); +} +fn divWithResult(a: u64, b: u64) DivResult { + return DivResult{ + .quotient = a / b, + .remainder = a % b, + }; +} +const DivResult = struct { + quotient: u64, + remainder: u64, +}; + +test "binary not" { + assertOrPanic(comptime x: { + break :x ~u16(0b1010101010101010) == 0b0101010101010101; + }); + assertOrPanic(comptime x: { + break :x ~u64(2147483647) == 18446744071562067968; + }); + testBinaryNot(0b1010101010101010); +} + +fn testBinaryNot(x: u16) void { + assertOrPanic(~x == 0b0101010101010101); +} + +test "small int addition" { + var x: @IntType(false, 2) = 0; + assertOrPanic(x == 0); + + x += 1; + assertOrPanic(x == 1); + + x += 1; + assertOrPanic(x == 2); + + x += 1; + assertOrPanic(x == 3); + + var result: @typeOf(x) = 3; + assertOrPanic(@addWithOverflow(@typeOf(x), x, 1, &result)); + + assertOrPanic(result == 0); +} + +test "float equality" { + const x: f64 = 0.012; + const y: f64 = x + 1.0; + + testFloatEqualityImpl(x, y); + comptime testFloatEqualityImpl(x, y); +} + +fn testFloatEqualityImpl(x: f64, y: f64) void { + const y2 = x + 1.0; + assertOrPanic(y == y2); +} + +test "allow signed integer division/remainder when values are comptime known and positive or exact" { + assertOrPanic(5 / 3 == 1); + assertOrPanic(-5 / -3 == 1); + assertOrPanic(-6 / 3 == -2); + + assertOrPanic(5 % 3 == 2); + assertOrPanic(-6 % 3 == 0); +} + +test "hex float literal parsing" { + comptime assertOrPanic(0x1.0 == 1.0); +} + +test "quad hex float literal parsing in range" { + const a = 0x1.af23456789bbaaab347645365cdep+5; + const b = 0x1.dedafcff354b6ae9758763545432p-9; + const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534; + const d = 0x1.edcbff8ad76ab5bf46463233214fp-435; +} + +test "quad hex float literal parsing accurate" { + const a: f128 = 0x1.1111222233334444555566667777p+0; + + // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. + const expected: u128 = 0x3fff1111222233334444555566667777; + assertOrPanic(@bitCast(u128, a) == expected); +} + +test "hex float literal within range" { + const a = 0x1.0p16383; + const b = 0x0.1p16387; + const c = 0x1.0p-16382; +} + +test "truncating shift left" { + testShlTrunc(maxInt(u16)); + comptime testShlTrunc(maxInt(u16)); +} +fn testShlTrunc(x: u16) void { + const shifted = x << 1; + assertOrPanic(shifted == 65534); +} + +test "truncating shift right" { + testShrTrunc(maxInt(u16)); + comptime testShrTrunc(maxInt(u16)); +} +fn testShrTrunc(x: u16) void { + const shifted = x >> 1; + assertOrPanic(shifted == 32767); +} + +test "exact shift left" { + testShlExact(0b00110101); + comptime testShlExact(0b00110101); +} +fn testShlExact(x: u8) void { + const shifted = @shlExact(x, 2); + assertOrPanic(shifted == 0b11010100); +} + +test "exact shift right" { + testShrExact(0b10110100); + comptime testShrExact(0b10110100); +} +fn testShrExact(x: u8) void { + const shifted = @shrExact(x, 2); + assertOrPanic(shifted == 0b00101101); +} + +test "comptime_int addition" { + comptime { + assertOrPanic(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); + assertOrPanic(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); + } +} + +test "comptime_int multiplication" { + comptime { + assertOrPanic( + 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, + ); + assertOrPanic( + 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, + ); + } +} + +test "comptime_int shifting" { + comptime { + assertOrPanic((u128(1) << 127) == 0x80000000000000000000000000000000); + } +} + +test "comptime_int multi-limb shift and mask" { + comptime { + var a = 0xefffffffa0000001eeeeeeefaaaaaaab; + + assertOrPanic(u32(a & 0xffffffff) == 0xaaaaaaab); + a >>= 32; + assertOrPanic(u32(a & 0xffffffff) == 0xeeeeeeef); + a >>= 32; + assertOrPanic(u32(a & 0xffffffff) == 0xa0000001); + a >>= 32; + assertOrPanic(u32(a & 0xffffffff) == 0xefffffff); + a >>= 32; + + assertOrPanic(a == 0); + } +} + +test "comptime_int multi-limb partial shift right" { + comptime { + var a = 0x1ffffffffeeeeeeee; + a >>= 16; + assertOrPanic(a == 0x1ffffffffeeee); + } +} + +test "xor" { + test_xor(); + comptime test_xor(); +} + +fn test_xor() void { + assertOrPanic(0xFF ^ 0x00 == 0xFF); + assertOrPanic(0xF0 ^ 0x0F == 0xFF); + assertOrPanic(0xFF ^ 0xF0 == 0x0F); + assertOrPanic(0xFF ^ 0x0F == 0xF0); + assertOrPanic(0xFF ^ 0xFF == 0x00); +} + +test "comptime_int xor" { + comptime { + assertOrPanic(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); + assertOrPanic(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); + assertOrPanic(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); + assertOrPanic(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); + assertOrPanic(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); + } +} + +test "f128" { + test_f128(); + comptime test_f128(); +} + +fn make_f128(x: f128) f128 { + return x; +} + +fn test_f128() void { + assertOrPanic(@sizeOf(f128) == 16); + assertOrPanic(make_f128(1.0) == 1.0); + assertOrPanic(make_f128(1.0) != 1.1); + assertOrPanic(make_f128(1.0) > 0.9); + assertOrPanic(make_f128(1.0) >= 0.9); + assertOrPanic(make_f128(1.0) >= 1.0); + should_not_be_zero(1.0); +} + +fn should_not_be_zero(x: f128) void { + assertOrPanic(x != 0.0); +} + +test "comptime float rem int" { + comptime { + var x = f32(1) % 2; + assertOrPanic(x == 1.0); + } +} + diff --git a/test/stage1/behavior/reflection.zig b/test/stage1/behavior/reflection.zig new file mode 100644 index 000000000000..f4c142e0f755 --- /dev/null +++ b/test/stage1/behavior/reflection.zig @@ -0,0 +1,96 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; +const reflection = @This(); + +test "reflection: array, pointer, optional, error union type child" { + comptime { + assertOrPanic(([10]u8).Child == u8); + assertOrPanic((*u8).Child == u8); + assertOrPanic((anyerror!u8).Payload == u8); + assertOrPanic((?u8).Child == u8); + } +} + +test "reflection: function return type, var args, and param types" { + comptime { + assertOrPanic(@typeOf(dummy).ReturnType == i32); + assertOrPanic(!@typeOf(dummy).is_var_args); + assertOrPanic(@typeOf(dummy_varargs).is_var_args); + assertOrPanic(@typeOf(dummy).arg_count == 3); + assertOrPanic(@ArgType(@typeOf(dummy), 0) == bool); + assertOrPanic(@ArgType(@typeOf(dummy), 1) == i32); + assertOrPanic(@ArgType(@typeOf(dummy), 2) == f32); + } +} + +fn dummy(a: bool, b: i32, c: f32) i32 { + return 1234; +} +fn dummy_varargs(args: ...) void {} + +test "reflection: struct member types and names" { + comptime { + assertOrPanic(@memberCount(Foo) == 3); + + assertOrPanic(@memberType(Foo, 0) == i32); + assertOrPanic(@memberType(Foo, 1) == bool); + assertOrPanic(@memberType(Foo, 2) == void); + + assertOrPanic(mem.eql(u8, @memberName(Foo, 0), "one")); + assertOrPanic(mem.eql(u8, @memberName(Foo, 1), "two")); + assertOrPanic(mem.eql(u8, @memberName(Foo, 2), "three")); + } +} + +test "reflection: enum member types and names" { + comptime { + assertOrPanic(@memberCount(Bar) == 4); + + assertOrPanic(@memberType(Bar, 0) == void); + assertOrPanic(@memberType(Bar, 1) == i32); + assertOrPanic(@memberType(Bar, 2) == bool); + assertOrPanic(@memberType(Bar, 3) == f64); + + assertOrPanic(mem.eql(u8, @memberName(Bar, 0), "One")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 1), "Two")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 2), "Three")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 3), "Four")); + } +} + +test "reflection: @field" { + var f = Foo{ + .one = 42, + .two = true, + .three = void{}, + }; + + assertOrPanic(f.one == f.one); + assertOrPanic(@field(f, "o" ++ "ne") == f.one); + assertOrPanic(@field(f, "t" ++ "wo") == f.two); + assertOrPanic(@field(f, "th" ++ "ree") == f.three); + assertOrPanic(@field(Foo, "const" ++ "ant") == Foo.constant); + assertOrPanic(@field(Bar, "O" ++ "ne") == Bar.One); + assertOrPanic(@field(Bar, "T" ++ "wo") == Bar.Two); + assertOrPanic(@field(Bar, "Th" ++ "ree") == Bar.Three); + assertOrPanic(@field(Bar, "F" ++ "our") == Bar.Four); + assertOrPanic(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); + @field(f, "o" ++ "ne") = 4; + assertOrPanic(f.one == 4); +} + +const Foo = struct { + const constant = 52; + + one: i32, + two: bool, + three: void, +}; + +const Bar = union(enum) { + One: void, + Two: i32, + Three: bool, + Four: f64, +}; + diff --git a/test/cases/syntax.zig b/test/stage1/behavior/syntax.zig similarity index 99% rename from test/cases/syntax.zig rename to test/stage1/behavior/syntax.zig index 0c8c3c5ed314..451e39614292 100644 --- a/test/cases/syntax.zig +++ b/test/stage1/behavior/syntax.zig @@ -57,3 +57,4 @@ fn asm_lists() void { :::"a","b",); } } + diff --git a/test/cases/this.zig b/test/stage1/behavior/this.zig similarity index 74% rename from test/cases/this.zig rename to test/stage1/behavior/this.zig index c7be074f36c2..0e3a7a03ae07 100644 --- a/test/cases/this.zig +++ b/test/stage1/behavior/this.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const module = @This(); @@ -20,7 +20,7 @@ fn add(x: i32, y: i32) i32 { } test "this refer to module call private fn" { - assert(module.add(1, 2) == 3); + assertOrPanic(module.add(1, 2) == 3); } test "this refer to container" { @@ -29,6 +29,7 @@ test "this refer to container" { .y = 34, }; pt.addOne(); - assert(pt.x == 13); - assert(pt.y == 35); + assertOrPanic(pt.x == 13); + assertOrPanic(pt.y == 35); } + From f99ff488e15c34d5e7794e5f355e370328bdfb8e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Dec 2018 15:55:42 -0500 Subject: [PATCH 130/190] copy elision: move passing tests --- src/ir.cpp | 3 +- test/behavior.zig | 19 -- test/cases/defer.zig | 60 +--- test/cases/enum.zig | 74 ++--- test/cases/eval.zig | 239 ++++++++-------- test/cases/math.zig | 49 ---- test/cases/popcount.zig | 24 -- test/cases/sizeof_and_typeof.zig | 69 ----- test/cases/struct.zig | 194 ++++++------- test/cases/type_info.zig | 259 ----------------- test/stage1/behavior.zig | 30 +- test/{cases => stage1/behavior}/bugs/1111.zig | 0 test/stage1/behavior/defer.zig | 43 +++ test/stage1/behavior/enum.zig | 3 + test/stage1/behavior/eval.zig | 13 + test/{cases => stage1/behavior}/fn.zig | 39 +-- test/{cases => stage1/behavior}/for.zig | 0 test/stage1/behavior/math.zig | 44 +++ .../namespace_depends_on_compile_var/a.zig | 0 .../namespace_depends_on_compile_var/b.zig | 0 .../index.zig | 0 .../behavior}/new_stack_call.zig | 10 +- test/{cases => stage1/behavior}/pointers.zig | 0 test/stage1/behavior/popcount.zig | 25 ++ test/{cases => stage1/behavior}/ptrcast.zig | 0 .../behavior}/pub_enum/index.zig | 0 .../behavior}/pub_enum/other.zig | 0 ...ef_var_in_if_after_if_2nd_switch_prong.zig | 0 test/stage1/behavior/sizeof_and_typeof.zig | 69 +++++ test/{cases => stage1/behavior}/slice.zig | 0 test/stage1/behavior/struct.zig | 16 ++ .../struct_contains_null_ptr_itself.zig | 0 test/{cases => stage1/behavior}/truncate.zig | 0 test/stage1/behavior/type_info.zig | 260 ++++++++++++++++++ test/{cases => stage1/behavior}/undefined.zig | 29 +- .../{cases => stage1/behavior}/underscore.zig | 0 test/{cases => stage1/behavior}/var_args.zig | 0 37 files changed, 793 insertions(+), 778 deletions(-) delete mode 100644 test/cases/math.zig delete mode 100644 test/cases/popcount.zig delete mode 100644 test/cases/sizeof_and_typeof.zig delete mode 100644 test/cases/type_info.zig rename test/{cases => stage1/behavior}/bugs/1111.zig (100%) create mode 100644 test/stage1/behavior/defer.zig create mode 100644 test/stage1/behavior/enum.zig create mode 100644 test/stage1/behavior/eval.zig rename test/{cases => stage1/behavior}/fn.zig (78%) rename test/{cases => stage1/behavior}/for.zig (100%) rename test/{cases => stage1/behavior}/namespace_depends_on_compile_var/a.zig (100%) rename test/{cases => stage1/behavior}/namespace_depends_on_compile_var/b.zig (100%) rename test/{cases => stage1/behavior}/namespace_depends_on_compile_var/index.zig (100%) rename test/{cases => stage1/behavior}/new_stack_call.zig (72%) rename test/{cases => stage1/behavior}/pointers.zig (100%) create mode 100644 test/stage1/behavior/popcount.zig rename test/{cases => stage1/behavior}/ptrcast.zig (100%) rename test/{cases => stage1/behavior}/pub_enum/index.zig (100%) rename test/{cases => stage1/behavior}/pub_enum/other.zig (100%) rename test/{cases => stage1/behavior}/ref_var_in_if_after_if_2nd_switch_prong.zig (100%) create mode 100644 test/stage1/behavior/sizeof_and_typeof.zig rename test/{cases => stage1/behavior}/slice.zig (100%) create mode 100644 test/stage1/behavior/struct.zig rename test/{cases => stage1/behavior}/struct_contains_null_ptr_itself.zig (100%) rename test/{cases => stage1/behavior}/truncate.zig (100%) create mode 100644 test/stage1/behavior/type_info.zig rename test/{cases => stage1/behavior}/undefined.zig (59%) rename test/{cases => stage1/behavior}/underscore.zig (100%) rename test/{cases => stage1/behavior}/var_args.zig (100%) diff --git a/src/ir.cpp b/src/ir.cpp index 81212599f258..6ec9f3e0b8b2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8000,7 +8000,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *implicit_return_result = result; if (fn_entry != nullptr) { ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; - if (return_type->id == ZigTypeIdErrorUnion) { + // return_type can be null when this function is being executed at comptime + if (return_type != nullptr && return_type->id == ZigTypeIdErrorUnion) { implicit_return_result = ir_build_const_null(irb, scope, result->source_node); } } diff --git a/test/behavior.zig b/test/behavior.zig index 0d36b3d37cf7..0f5df0ff55ad 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,4 @@ comptime { - _ = @import("cases/bugs/1111.zig"); _ = @import("cases/bugs/1277.zig"); _ = @import("cases/bugs/1322.zig"); _ = @import("cases/bugs/828.zig"); @@ -14,32 +13,14 @@ comptime { _ = @import("cases/enum_with_members.zig"); _ = @import("cases/error.zig"); _ = @import("cases/eval.zig"); - _ = @import("cases/fn.zig"); - _ = @import("cases/for.zig"); _ = @import("cases/ir_block_deps.zig"); - _ = @import("cases/math.zig"); - _ = @import("cases/namespace_depends_on_compile_var/index.zig"); - _ = @import("cases/new_stack_call.zig"); _ = @import("cases/null.zig"); _ = @import("cases/optional.zig"); - _ = @import("cases/pointers.zig"); - _ = @import("cases/popcount.zig"); - _ = @import("cases/ptrcast.zig"); - _ = @import("cases/pub_enum/index.zig"); - _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); - _ = @import("cases/sizeof_and_typeof.zig"); - _ = @import("cases/slice.zig"); _ = @import("cases/struct.zig"); - _ = @import("cases/struct_contains_null_ptr_itself.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); _ = @import("cases/switch.zig"); _ = @import("cases/switch_prong_err_enum.zig"); _ = @import("cases/switch_prong_implicit_cast.zig"); - _ = @import("cases/truncate.zig"); _ = @import("cases/try.zig"); - _ = @import("cases/type_info.zig"); - _ = @import("cases/undefined.zig"); - _ = @import("cases/underscore.zig"); _ = @import("cases/union.zig"); - _ = @import("cases/var_args.zig"); } diff --git a/test/cases/defer.zig b/test/cases/defer.zig index f9a2b69cd94b..ed111ce248c4 100644 --- a/test/cases/defer.zig +++ b/test/cases/defer.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; var result: [3]u8 = undefined; var index: usize = undefined; @@ -21,58 +21,16 @@ fn runSomeErrorDefers(x: bool) !bool { } test "mixing normal and error defers" { - assert(runSomeErrorDefers(true) catch unreachable); - assert(result[0] == 'c'); - assert(result[1] == 'a'); + assertOrPanic(runSomeErrorDefers(true) catch unreachable); + assertOrPanic(result[0] == 'c'); + assertOrPanic(result[1] == 'a'); const ok = runSomeErrorDefers(false) catch |err| x: { - assert(err == error.FalseNotAllowed); + assertOrPanic(err == error.FalseNotAllowed); break :x true; }; - assert(ok); - assert(result[0] == 'c'); - assert(result[1] == 'b'); - assert(result[2] == 'a'); -} - -test "break and continue inside loop inside defer expression" { - testBreakContInDefer(10); - comptime testBreakContInDefer(10); -} - -fn testBreakContInDefer(x: usize) void { - defer { - var i: usize = 0; - while (i < x) : (i += 1) { - if (i < 5) continue; - if (i == 5) break; - } - assert(i == 5); - } -} - -test "defer and labeled break" { - var i = usize(0); - - blk: { - defer i += 1; - break :blk; - } - - assert(i == 1); -} - -test "errdefer does not apply to fn inside fn" { - if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad); -} - -fn testNestedFnErrDefer() anyerror!void { - var a: i32 = 0; - errdefer a += 1; - const S = struct { - fn baz() anyerror { - return error.Bad; - } - }; - return S.baz(); + assertOrPanic(ok); + assertOrPanic(result[0] == 'c'); + assertOrPanic(result[1] == 'b'); + assertOrPanic(result[2] == 'a'); } diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 2dd552488c94..c74cbcc3cf49 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; test "enum type" { @@ -11,16 +11,16 @@ test "enum type" { }; const bar = Bar.B; - assert(bar == Bar.B); - assert(@memberCount(Foo) == 3); - assert(@memberCount(Bar) == 4); - assert(@sizeOf(Foo) == @sizeOf(FooNoVoid)); - assert(@sizeOf(Bar) == 1); + assertOrPanic(bar == Bar.B); + assertOrPanic(@memberCount(Foo) == 3); + assertOrPanic(@memberCount(Bar) == 4); + assertOrPanic(@sizeOf(Foo) == @sizeOf(FooNoVoid)); + assertOrPanic(@sizeOf(Bar) == 1); } test "enum as return value" { switch (returnAnInt(13)) { - Foo.One => |value| assert(value == 13), + Foo.One => |value| assertOrPanic(value == 13), else => unreachable, } } @@ -92,14 +92,14 @@ test "enum to int" { } fn shouldEqual(n: Number, expected: u3) void { - assert(@enumToInt(n) == expected); + assertOrPanic(@enumToInt(n) == expected); } test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) void { - assert(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); + assertOrPanic(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, @@ -110,8 +110,8 @@ const IntToEnumNumber = enum { }; test "@tagName" { - assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); - comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + comptime assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); } fn testEnumTagNameBare(n: BareNumber) []const u8 { @@ -126,8 +126,8 @@ const BareNumber = enum { test "enum alignment" { comptime { - assert(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); - assert(@alignOf(AlignTestEnum) >= @alignOf(u64)); + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf(u64)); } } @@ -663,10 +663,10 @@ const ValueCount257 = enum { test "enum sizes" { comptime { - assert(@sizeOf(ValueCount1) == 0); - assert(@sizeOf(ValueCount2) == 1); - assert(@sizeOf(ValueCount256) == 1); - assert(@sizeOf(ValueCount257) == 2); + assertOrPanic(@sizeOf(ValueCount1) == 0); + assertOrPanic(@sizeOf(ValueCount2) == 1); + assertOrPanic(@sizeOf(ValueCount256) == 1); + assertOrPanic(@sizeOf(ValueCount257) == 2); } } @@ -685,12 +685,12 @@ test "set enum tag type" { { var x = Small.One; x = Small.Two; - comptime assert(@TagType(Small) == u2); + comptime assertOrPanic(@TagType(Small) == u2); } { var x = Small2.One; x = Small2.Two; - comptime assert(@TagType(Small2) == u2); + comptime assertOrPanic(@TagType(Small2) == u2); } } @@ -737,17 +737,17 @@ const bit_field_1 = BitFieldOfEnums{ test "bit field access with enum fields" { var data = bit_field_1; - assert(getA(&data) == A.Two); - assert(getB(&data) == B.Three3); - assert(getC(&data) == C.Four4); - comptime assert(@sizeOf(BitFieldOfEnums) == 1); + assertOrPanic(getA(&data) == A.Two); + assertOrPanic(getB(&data) == B.Three3); + assertOrPanic(getC(&data) == C.Four4); + comptime assertOrPanic(@sizeOf(BitFieldOfEnums) == 1); data.b = B.Four3; - assert(data.b == B.Four3); + assertOrPanic(data.b == B.Four3); data.a = A.Three; - assert(data.a == A.Three); - assert(data.b == B.Four3); + assertOrPanic(data.a == A.Three); + assertOrPanic(data.b == B.Four3); } fn getA(data: *const BitFieldOfEnums) A { @@ -768,7 +768,7 @@ test "casting enum to its tag type" { } fn testCastEnumToTagType(value: Small2) void { - assert(@enumToInt(value) == 1); + assertOrPanic(@enumToInt(value) == 1); } const MultipleChoice = enum(u32) { @@ -784,8 +784,8 @@ test "enum with specified tag values" { } fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { - assert(@enumToInt(x) == 60); - assert(1234 == switch (x) { + assertOrPanic(@enumToInt(x) == 60); + assertOrPanic(1234 == switch (x) { MultipleChoice.A => 1, MultipleChoice.B => 2, MultipleChoice.C => u32(1234), @@ -811,8 +811,8 @@ test "enum with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(x) == 1000); - assert(1234 == switch (x) { + assertOrPanic(@enumToInt(x) == 1000); + assertOrPanic(1234 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => 3, @@ -826,8 +826,8 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { } test "cast integer literal to enum" { - assert(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); - assert(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); + assertOrPanic(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); + assertOrPanic(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); } const EnumWithOneMember = enum { @@ -865,14 +865,14 @@ const EnumWithTagValues = enum(u4) { D = 1 << 3, }; test "enum with tag values don't require parens" { - assert(@enumToInt(EnumWithTagValues.C) == 0b0100); + assertOrPanic(@enumToInt(EnumWithTagValues.C) == 0b0100); } test "enum with 1 field but explicit tag type should still have the tag type" { const Enum = enum(u8) { B = 2, }; - comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8)); + comptime @import("std").debug.assertOrPanic(@sizeOf(Enum) == @sizeOf(u8)); } test "empty extern enum with members" { @@ -881,7 +881,7 @@ test "empty extern enum with members" { B, C, }; - assert(@sizeOf(E) == @sizeOf(c_int)); + assertOrPanic(@sizeOf(E) == @sizeOf(c_int)); } test "aoeu" { @@ -890,5 +890,5 @@ test "aoeu" { B = 0, }; var b = LocalFoo.B; - assert(mem.eql(u8, @tagName(b), "B")); + assertOrPanic(mem.eql(u8, @tagName(b), "B")); } diff --git a/test/cases/eval.zig b/test/cases/eval.zig index a9eded151ebc..180284ac515b 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -1,22 +1,13 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); -test "compile time recursion" { - assert(some_data.len == 21); -} -var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined; -fn fibonacci(x: i32) i32 { - if (x <= 1) return 1; - return fibonacci(x - 1) + fibonacci(x - 2); -} - fn unwrapAndAddOne(blah: ?i32) i32 { return blah.? + 1; } const should_be_1235 = unwrapAndAddOne(1234); test "static add one" { - assert(should_be_1235 == 1235); + assertOrPanic(should_be_1235 == 1235); } test "inlined loop" { @@ -24,7 +15,7 @@ test "inlined loop" { comptime var sum = 0; inline while (i <= 5) : (i += 1) sum += i; - assert(sum == 15); + assertOrPanic(sum == 15); } fn gimme1or2(comptime a: bool) i32 { @@ -34,12 +25,12 @@ fn gimme1or2(comptime a: bool) i32 { return z; } test "inline variable gets result of const if" { - assert(gimme1or2(true) == 1); - assert(gimme1or2(false) == 2); + assertOrPanic(gimme1or2(true) == 1); + assertOrPanic(gimme1or2(false) == 2); } test "static function evaluation" { - assert(statically_added_number == 3); + assertOrPanic(statically_added_number == 3); } const statically_added_number = staticAdd(1, 2); fn staticAdd(a: i32, b: i32) i32 { @@ -47,7 +38,7 @@ fn staticAdd(a: i32, b: i32) i32 { } test "const expr eval on single expr blocks" { - assert(constExprEvalOnSingleExprBlocksFn(1, true) == 3); + assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, true) == 3); } fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { @@ -63,10 +54,10 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { } test "statically initialized list" { - assert(static_point_list[0].x == 1); - assert(static_point_list[0].y == 2); - assert(static_point_list[1].x == 3); - assert(static_point_list[1].y == 4); + assertOrPanic(static_point_list[0].x == 1); + assertOrPanic(static_point_list[0].y == 2); + assertOrPanic(static_point_list[1].x == 3); + assertOrPanic(static_point_list[1].y == 4); } const Point = struct { x: i32, @@ -84,8 +75,8 @@ fn makePoint(x: i32, y: i32) Point { } test "static eval list init" { - assert(static_vec3.data[2] == 1.0); - assert(vec3(0.0, 0.0, 3.0).data[2] == 3.0); + assertOrPanic(static_vec3.data[2] == 1.0); + assertOrPanic(vec3(0.0, 0.0, 3.0).data[2] == 3.0); } const static_vec3 = vec3(0.0, 0.0, 1.0); pub const Vec3 = struct { @@ -101,12 +92,12 @@ pub fn vec3(x: f32, y: f32, z: f32) Vec3 { test "constant expressions" { var array: [array_size]u8 = undefined; - assert(@sizeOf(@typeOf(array)) == 20); + assertOrPanic(@sizeOf(@typeOf(array)) == 20); } const array_size: u8 = 20; test "constant struct with negation" { - assert(vertices[0].x == -0.6); + assertOrPanic(vertices[0].x == -0.6); } const Vertex = struct { x: f32, @@ -141,7 +132,7 @@ const vertices = []Vertex{ test "statically initialized struct" { st_init_str_foo.x += 1; - assert(st_init_str_foo.x == 14); + assertOrPanic(st_init_str_foo.x == 14); } const StInitStrFoo = struct { x: i32, @@ -154,7 +145,7 @@ var st_init_str_foo = StInitStrFoo{ test "statically initalized array literal" { const y: [4]u8 = st_init_arr_lit_x; - assert(y[3] == 4); + assertOrPanic(y[3] == 4); } const st_init_arr_lit_x = []u8{ 1, @@ -166,15 +157,15 @@ const st_init_arr_lit_x = []u8{ test "const slice" { comptime { const a = "1234567890"; - assert(a.len == 10); + assertOrPanic(a.len == 10); const b = a[1..2]; - assert(b.len == 1); - assert(b[0] == '2'); + assertOrPanic(b.len == 1); + assertOrPanic(b[0] == '2'); } } test "try to trick eval with runtime if" { - assert(testTryToTrickEvalWithRuntimeIf(true) == 10); + assertOrPanic(testTryToTrickEvalWithRuntimeIf(true) == 10); } fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { @@ -200,16 +191,16 @@ fn letsTryToCompareBools(a: bool, b: bool) bool { return max(bool, a, b); } test "inlined block and runtime block phi" { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); comptime { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); } } @@ -254,14 +245,14 @@ fn performFn(comptime prefix_char: u8, start_value: i32) i32 { } test "comptime iterate over fn ptr list" { - assert(performFn('t', 1) == 6); - assert(performFn('o', 0) == 1); - assert(performFn('w', 99) == 99); + assertOrPanic(performFn('t', 1) == 6); + assertOrPanic(performFn('o', 0) == 1); + assertOrPanic(performFn('w', 99) == 99); } test "eval @setRuntimeSafety at compile-time" { const result = comptime fnWithSetRuntimeSafety(); - assert(result == 1234); + assertOrPanic(result == 1234); } fn fnWithSetRuntimeSafety() i32 { @@ -271,7 +262,7 @@ fn fnWithSetRuntimeSafety() i32 { test "eval @setFloatMode at compile-time" { const result = comptime fnWithFloatMode(); - assert(result == 1234.0); + assertOrPanic(result == 1234.0); } fn fnWithFloatMode() f32 { @@ -292,15 +283,15 @@ var simple_struct = SimpleStruct{ .field = 1234 }; const bound_fn = simple_struct.method; test "call method on bound fn referring to var instance" { - assert(bound_fn() == 1237); + assertOrPanic(bound_fn() == 1237); } test "ptr to local array argument at comptime" { comptime { var bytes: [10]u8 = undefined; modifySomeBytes(bytes[0..]); - assert(bytes[0] == 'a'); - assert(bytes[9] == 'b'); + assertOrPanic(bytes[0] == 'a'); + assertOrPanic(bytes[9] == 'b'); } } @@ -328,9 +319,9 @@ fn testCompTimeUIntComparisons(x: u32) void { } test "const ptr to variable data changes at runtime" { - assert(foo_ref.name[0] == 'a'); + assertOrPanic(foo_ref.name[0] == 'a'); foo_ref.name = "b"; - assert(foo_ref.name[0] == 'b'); + assertOrPanic(foo_ref.name[0] == 'b'); } const Foo = struct { @@ -341,8 +332,8 @@ var foo_contents = Foo{ .name = "a" }; const foo_ref = &foo_contents; test "create global array with for loop" { - assert(global_array[5] == 5 * 5); - assert(global_array[9] == 9 * 9); + assertOrPanic(global_array[5] == 5 * 5); + assertOrPanic(global_array[9] == 9 * 9); } const global_array = x: { @@ -357,7 +348,7 @@ test "compile-time downcast when the bits fit" { comptime { const spartan_count: u16 = 255; const byte = @intCast(u8, spartan_count); - assert(byte == 255); + assertOrPanic(byte == 255); } } @@ -365,44 +356,44 @@ const hi1 = "hi"; const hi2 = hi1; test "const global shares pointer with other same one" { assertEqualPtrs(&hi1[0], &hi2[0]); - comptime assert(&hi1[0] == &hi2[0]); + comptime assertOrPanic(&hi1[0] == &hi2[0]); } fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { - assert(ptr1 == ptr2); + assertOrPanic(ptr1 == ptr2); } test "@setEvalBranchQuota" { comptime { - // 1001 for the loop and then 1 more for the assert fn call + // 1001 for the loop and then 1 more for the assertOrPanic fn call @setEvalBranchQuota(1002); var i = 0; var sum = 0; while (i < 1001) : (i += 1) { sum += i; } - assert(sum == 500500); + assertOrPanic(sum == 500500); } } // TODO test "float literal at compile time not lossy" { -// TODO assert(16777216.0 + 1.0 == 16777217.0); -// TODO assert(9007199254740992.0 + 1.0 == 9007199254740993.0); +// TODO assertOrPanic(16777216.0 + 1.0 == 16777217.0); +// TODO assertOrPanic(9007199254740992.0 + 1.0 == 9007199254740993.0); // TODO } test "f32 at compile time is lossy" { - assert(f32(1 << 24) + 1 == 1 << 24); + assertOrPanic(f32(1 << 24) + 1 == 1 << 24); } test "f64 at compile time is lossy" { - assert(f64(1 << 53) + 1 == 1 << 53); + assertOrPanic(f64(1 << 53) + 1 == 1 << 53); } test "f128 at compile time is lossy" { - assert(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); + assertOrPanic(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); } // TODO need a better implementation of bigfloat_init_bigint -// assert(f128(1 << 113) == 10384593717069655257060992658440192); +// assertOrPanic(f128(1 << 113) == 10384593717069655257060992658440192); pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { return struct { @@ -413,15 +404,15 @@ pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { test "string literal used as comptime slice is memoized" { const a = "link"; const b = "link"; - comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); - comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); + comptime assertOrPanic(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); + comptime assertOrPanic(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); } test "comptime slice of undefined pointer of length 0" { const slice1 = ([*]i32)(undefined)[0..0]; - assert(slice1.len == 0); + assertOrPanic(slice1.len == 0); const slice2 = ([*]i32)(undefined)[100..100]; - assert(slice2.len == 0); + assertOrPanic(slice2.len == 0); } fn copyWithPartialInline(s: []u32, b: []u8) void { @@ -443,16 +434,16 @@ test "binary math operator in partially inlined function" { r.* = @intCast(u8, i + 1); copyWithPartialInline(s[0..], b[0..]); - assert(s[0] == 0x1020304); - assert(s[1] == 0x5060708); - assert(s[2] == 0x90a0b0c); - assert(s[3] == 0xd0e0f10); + assertOrPanic(s[0] == 0x1020304); + assertOrPanic(s[1] == 0x5060708); + assertOrPanic(s[2] == 0x90a0b0c); + assertOrPanic(s[3] == 0xd0e0f10); } test "comptime function with the same args is memoized" { comptime { - assert(MakeType(i32) == MakeType(i32)); - assert(MakeType(i32) != MakeType(f64)); + assertOrPanic(MakeType(i32) == MakeType(i32)); + assertOrPanic(MakeType(i32) != MakeType(f64)); } } @@ -468,7 +459,7 @@ test "comptime function with mutable pointer is not memoized" { const ptr = &x; increment(ptr); increment(ptr); - assert(x == 3); + assertOrPanic(x == 3); } } @@ -494,14 +485,14 @@ fn doesAlotT(comptime T: type, value: usize) T { } test "@setEvalBranchQuota at same scope as generic function call" { - assert(doesAlotT(u32, 2) == 2); + assertOrPanic(doesAlotT(u32, 2) == 2); } test "comptime slice of slice preserves comptime var" { comptime { var buff: [10]u8 = undefined; buff[0..][0..][0] = 1; - assert(buff[0..][0..][0] == 1); + assertOrPanic(buff[0..][0..][0] == 1); } } @@ -510,7 +501,7 @@ test "comptime slice of pointer preserves comptime var" { var buff: [10]u8 = undefined; var a = buff[0..].ptr; a[0..1][0] = 1; - assert(buff[0..][0..][0] == 1); + assertOrPanic(buff[0..][0..][0] == 1); } } @@ -524,9 +515,9 @@ const SingleFieldStruct = struct { test "const ptr to comptime mutable data is not memoized" { comptime { var foo = SingleFieldStruct{ .x = 1 }; - assert(foo.read_x() == 1); + assertOrPanic(foo.read_x() == 1); foo.x = 2; - assert(foo.read_x() == 2); + assertOrPanic(foo.read_x() == 2); } } @@ -535,7 +526,7 @@ test "array concat of slices gives slice" { var a: []const u8 = "aoeu"; var b: []const u8 = "asdf"; const c = a ++ b; - assert(std.mem.eql(u8, c, "aoeuasdf")); + assertOrPanic(std.mem.eql(u8, c, "aoeuasdf")); } } @@ -552,14 +543,14 @@ test "comptime shlWithOverflow" { break :amt amt; }; - assert(ct_shifted == rt_shifted); + assertOrPanic(ct_shifted == rt_shifted); } test "runtime 128 bit integer division" { var a: u128 = 152313999999999991610955792383; var b: u128 = 10000000000000000000; var c = a / b; - assert(c == 15231399999); + assertOrPanic(c == 15231399999); } pub const Info = struct { @@ -572,20 +563,20 @@ test "comptime modification of const struct field" { comptime { var res = diamond_info; res.version = 1; - assert(diamond_info.version == 0); - assert(res.version == 1); + assertOrPanic(diamond_info.version == 0); + assertOrPanic(res.version == 1); } } test "pointer to type" { comptime { var T: type = i32; - assert(T == i32); + assertOrPanic(T == i32); var ptr = &T; - assert(@typeOf(ptr) == *type); + assertOrPanic(@typeOf(ptr) == *type); ptr.* = f32; - assert(T == f32); - assert(*T == *f32); + assertOrPanic(T == f32); + assertOrPanic(*T == *f32); } } @@ -594,17 +585,17 @@ test "slice of type" { var types_array = []type{ i32, f64, type }; for (types_array) |T, i| { switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), else => unreachable, } } for (types_array[0..]) |T, i| { switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), else => unreachable, } } @@ -621,7 +612,7 @@ fn wrap(comptime T: type) Wrapper { test "function which returns struct with type field causes implicit comptime" { const ty = wrap(i32).T; - assert(ty == i32); + assertOrPanic(ty == i32); } test "call method with comptime pass-by-non-copying-value self parameter" { @@ -635,12 +626,12 @@ test "call method with comptime pass-by-non-copying-value self parameter" { const s = S{ .a = 2 }; var b = s.b(); - assert(b == 2); + assertOrPanic(b == 2); } test "@tagName of @typeId" { const str = @tagName(@typeId(u8)); - assert(std.mem.eql(u8, str, "Int")); + assertOrPanic(std.mem.eql(u8, str, "Int")); } test "setting backward branch quota just before a generic fn call" { @@ -661,8 +652,8 @@ fn testVarInsideInlineLoop(args: ...) void { comptime var i = 0; inline while (i < args.len) : (i += 1) { const x = args[i]; - if (i == 0) assert(x); - if (i == 1) assert(x == 42); + if (i == 0) assertOrPanic(x); + if (i == 1) assertOrPanic(x == 42); } } @@ -672,7 +663,7 @@ test "inline for with same type but different values" { var a: T = undefined; res += a.len; } - assert(res == 5); + assertOrPanic(res == 5); } test "refer to the type of a generic function" { @@ -686,19 +677,19 @@ fn doNothingWithType(comptime T: type) void {} test "zero extend from u0 to u1" { var zero_u0: u0 = 0; var zero_u1: u1 = zero_u0; - assert(zero_u1 == 0); + assertOrPanic(zero_u1 == 0); } test "bit shift a u1" { var x: u1 = 1; var y = x << 0; - assert(y == 1); + assertOrPanic(y == 1); } test "@intCast to a u0" { var x: u8 = 0; var y: u0 = @intCast(u0, x); - assert(y == 0); + assertOrPanic(y == 0); } test "@bytesToslice on a packed struct" { @@ -708,7 +699,7 @@ test "@bytesToslice on a packed struct" { var b = [1]u8{9}; var f = @bytesToSlice(F, b); - assert(f[0].a == 9); + assertOrPanic(f[0].a == 9); } test "comptime pointer cast array and then slice" { @@ -720,8 +711,8 @@ test "comptime pointer cast array and then slice" { const ptrB: [*]const u8 = &array; const sliceB: []const u8 = ptrB[0..2]; - assert(sliceA[1] == 2); - assert(sliceB[1] == 2); + assertOrPanic(sliceA[1] == 2); + assertOrPanic(sliceB[1] == 2); } test "slice bounds in comptime concatenation" { @@ -730,47 +721,47 @@ test "slice bounds in comptime concatenation" { break :blk b[0..1]; }; const str = "" ++ bs; - assert(str.len == 1); - assert(std.mem.eql(u8, str, "1")); + assertOrPanic(str.len == 1); + assertOrPanic(std.mem.eql(u8, str, "1")); const str2 = bs ++ ""; - assert(str2.len == 1); - assert(std.mem.eql(u8, str2, "1")); + assertOrPanic(str2.len == 1); + assertOrPanic(std.mem.eql(u8, str2, "1")); } test "comptime bitwise operators" { comptime { - assert(3 & 1 == 1); - assert(3 & -1 == 3); - assert(-3 & -1 == -3); - assert(3 | -1 == -1); - assert(-3 | -1 == -1); - assert(3 ^ -1 == -4); - assert(-3 ^ -1 == 2); - assert(~i8(-1) == 0); - assert(~i128(-1) == 0); - assert(18446744073709551615 & 18446744073709551611 == 18446744073709551611); - assert(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); - assert(~u128(0) == 0xffffffffffffffffffffffffffffffff); + assertOrPanic(3 & 1 == 1); + assertOrPanic(3 & -1 == 3); + assertOrPanic(-3 & -1 == -3); + assertOrPanic(3 | -1 == -1); + assertOrPanic(-3 | -1 == -1); + assertOrPanic(3 ^ -1 == -4); + assertOrPanic(-3 ^ -1 == 2); + assertOrPanic(~i8(-1) == 0); + assertOrPanic(~i128(-1) == 0); + assertOrPanic(18446744073709551615 & 18446744073709551611 == 18446744073709551611); + assertOrPanic(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); + assertOrPanic(~u128(0) == 0xffffffffffffffffffffffffffffffff); } } test "*align(1) u16 is the same as *align(1:0:2) u16" { comptime { - assert(*align(1:0:2) u16 == *align(1) u16); + assertOrPanic(*align(1:0:2) u16 == *align(1) u16); // TODO add parsing support for this syntax - //assert(*align(:0:2) u16 == *u16); + //assertOrPanic(*align(:0:2) u16 == *u16); } } test "array concatenation forces comptime" { var a = oneItem(3) ++ oneItem(4); - assert(std.mem.eql(i32, a, []i32{3, 4})); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 4 })); } test "array multiplication forces comptime" { var a = oneItem(3) ** scalar(2); - assert(std.mem.eql(i32, a, []i32{3, 3})); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 3 })); } fn oneItem(x: i32) [1]i32 { diff --git a/test/cases/math.zig b/test/cases/math.zig deleted file mode 100644 index 9ab02f60d2cb..000000000000 --- a/test/cases/math.zig +++ /dev/null @@ -1,49 +0,0 @@ -const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; -const maxInt = std.math.maxInt; -const minInt = std.math.minInt; - -test "remainder division" { - comptime remdiv(f16); - comptime remdiv(f32); - comptime remdiv(f64); - comptime remdiv(f128); - remdiv(f16); - remdiv(f64); - remdiv(f128); -} - -fn remdiv(comptime T: type) void { - assertOrPanic(T(1) == T(1) % T(2)); - assertOrPanic(T(1) == T(7) % T(3)); -} - -test "@sqrt" { - testSqrt(f64, 12.0); - comptime testSqrt(f64, 12.0); - testSqrt(f32, 13.0); - comptime testSqrt(f32, 13.0); - testSqrt(f16, 13.0); - comptime testSqrt(f16, 13.0); - - const x = 14.0; - const y = x * x; - const z = @sqrt(@typeOf(y), y); - comptime assertOrPanic(z == x); -} - -fn testSqrt(comptime T: type, x: T) void { - assertOrPanic(@sqrt(T, x * x) == x); -} - -test "comptime_int param and return" { - const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); - assertOrPanic(a == 137114567242441932203689521744947848950); - - const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); - assertOrPanic(b == 985095453608931032642182098849559179469148836107390954364380); -} - -fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { - return a + b; -} diff --git a/test/cases/popcount.zig b/test/cases/popcount.zig deleted file mode 100644 index 7dc7f28c0e9c..000000000000 --- a/test/cases/popcount.zig +++ /dev/null @@ -1,24 +0,0 @@ -const assert = @import("std").debug.assert; - -test "@popCount" { - comptime testPopCount(); - testPopCount(); -} - -fn testPopCount() void { - { - var x: u32 = 0xaa; - assert(@popCount(x) == 4); - } - { - var x: u32 = 0xaaaaaaaa; - assert(@popCount(x) == 16); - } - { - var x: i16 = -1; - assert(@popCount(x) == 16); - } - comptime { - assert(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); - } -} diff --git a/test/cases/sizeof_and_typeof.zig b/test/cases/sizeof_and_typeof.zig deleted file mode 100644 index 11c6b2f6ba36..000000000000 --- a/test/cases/sizeof_and_typeof.zig +++ /dev/null @@ -1,69 +0,0 @@ -const builtin = @import("builtin"); -const assert = @import("std").debug.assert; - -test "@sizeOf and @typeOf" { - const y: @typeOf(x) = 120; - assert(@sizeOf(@typeOf(y)) == 2); -} -const x: u16 = 13; -const z: @typeOf(x) = 19; - -const A = struct { - a: u8, - b: u32, - c: u8, - d: u3, - e: u5, - f: u16, - g: u16, -}; - -const P = packed struct { - a: u8, - b: u32, - c: u8, - d: u3, - e: u5, - f: u16, - g: u16, -}; - -test "@byteOffsetOf" { - // Packed structs have fixed memory layout - assert(@byteOffsetOf(P, "a") == 0); - assert(@byteOffsetOf(P, "b") == 1); - assert(@byteOffsetOf(P, "c") == 5); - assert(@byteOffsetOf(P, "d") == 6); - assert(@byteOffsetOf(P, "e") == 6); - assert(@byteOffsetOf(P, "f") == 7); - assert(@byteOffsetOf(P, "g") == 9); - - // Normal struct fields can be moved/padded - var a: A = undefined; - assert(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); - assert(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); - assert(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); - assert(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); - assert(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); - assert(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); - assert(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); -} - -test "@bitOffsetOf" { - // Packed structs have fixed memory layout - assert(@bitOffsetOf(P, "a") == 0); - assert(@bitOffsetOf(P, "b") == 8); - assert(@bitOffsetOf(P, "c") == 40); - assert(@bitOffsetOf(P, "d") == 48); - assert(@bitOffsetOf(P, "e") == 51); - assert(@bitOffsetOf(P, "f") == 56); - assert(@bitOffsetOf(P, "g") == 72); - - assert(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); - assert(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); - assert(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); - assert(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); - assert(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); - assert(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); - assert(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); -} diff --git a/test/cases/struct.zig b/test/cases/struct.zig index bbbd21912c32..0b1137a21d03 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -1,20 +1,8 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); const maxInt = std.math.maxInt; -const StructWithNoFields = struct { - fn add(a: i32, b: i32) i32 { - return a + b; - } -}; -const empty_global_instance = StructWithNoFields{}; - -test "call struct static method" { - const result = StructWithNoFields.add(3, 4); - assert(result == 7); -} - test "return empty struct instance" { _ = returnEmptyStructInstance(); } @@ -25,7 +13,7 @@ fn returnEmptyStructInstance() StructWithNoFields { const should_be_11 = StructWithNoFields.add(5, 6); test "invake static method in global scope" { - assert(should_be_11 == 11); + assertOrPanic(should_be_11 == 11); } test "void struct fields" { @@ -34,8 +22,8 @@ test "void struct fields" { .b = 1, .c = void{}, }; - assert(foo.b == 1); - assert(@sizeOf(VoidStructFieldsFoo) == 4); + assertOrPanic(foo.b == 1); + assertOrPanic(@sizeOf(VoidStructFieldsFoo) == 4); } const VoidStructFieldsFoo = struct { a: void, @@ -50,7 +38,7 @@ test "structs" { foo.b = foo.a == 1; testFoo(foo); testMutation(&foo); - assert(foo.c == 100); + assertOrPanic(foo.c == 100); } const StructFoo = struct { a: i32, @@ -58,7 +46,7 @@ const StructFoo = struct { c: f32, }; fn testFoo(foo: StructFoo) void { - assert(foo.b); + assertOrPanic(foo.b); } fn testMutation(foo: *StructFoo) void { foo.c = 100; @@ -83,7 +71,7 @@ test "struct point to self" { root.next = &node; - assert(node.next.next.next.val.x == 1); + assertOrPanic(node.next.next.next.val.x == 1); } test "struct byval assign" { @@ -92,18 +80,18 @@ test "struct byval assign" { foo1.a = 1234; foo2.a = 0; - assert(foo2.a == 0); + assertOrPanic(foo2.a == 0); foo2 = foo1; - assert(foo2.a == 1234); + assertOrPanic(foo2.a == 1234); } fn structInitializer() void { const val = Val{ .x = 42 }; - assert(val.x == 42); + assertOrPanic(val.x == 42); } test "fn call of struct field" { - assert(callStructField(Foo{ .ptr = aFunc }) == 13); + assertOrPanic(callStructField(Foo{ .ptr = aFunc }) == 13); } const Foo = struct { @@ -122,7 +110,7 @@ test "store member function in variable" { const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); - assert(result == 1234); + assertOrPanic(result == 1234); } const MemberFnTestFoo = struct { x: i32, @@ -134,12 +122,12 @@ const MemberFnTestFoo = struct { test "call member function directly" { const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); - assert(result == 1234); + assertOrPanic(result == 1234); } test "member functions" { const r = MemberFnRand{ .seed = 1234 }; - assert(r.getSeed() == 1234); + assertOrPanic(r.getSeed() == 1234); } const MemberFnRand = struct { seed: u32, @@ -150,7 +138,7 @@ const MemberFnRand = struct { test "return struct byval from function" { const bar = makeBar(1234, 5678); - assert(bar.y == 5678); + assertOrPanic(bar.y == 5678); } const Bar = struct { x: i32, @@ -165,7 +153,7 @@ fn makeBar(x: i32, y: i32) Bar { test "empty struct method call" { const es = EmptyStruct{}; - assert(es.method() == 1234); + assertOrPanic(es.method() == 1234); } const EmptyStruct = struct { fn method(es: *const EmptyStruct) i32 { @@ -182,7 +170,7 @@ fn testReturnEmptyStructFromFn() EmptyStruct2 { } test "pass slice of empty struct to fn" { - assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); + assertOrPanic(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); } fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { return slice.len; @@ -200,7 +188,7 @@ test "packed struct" { }; foo.y += 1; const four = foo.x + foo.y; - assert(four == 4); + assertOrPanic(four == 4); } const BitField1 = packed struct { @@ -217,17 +205,17 @@ const bit_field_1 = BitField1{ test "bit field access" { var data = bit_field_1; - assert(getA(&data) == 1); - assert(getB(&data) == 2); - assert(getC(&data) == 3); - comptime assert(@sizeOf(BitField1) == 1); + assertOrPanic(getA(&data) == 1); + assertOrPanic(getB(&data) == 2); + assertOrPanic(getC(&data) == 3); + comptime assertOrPanic(@sizeOf(BitField1) == 1); data.b += 1; - assert(data.b == 3); + assertOrPanic(data.b == 3); data.a += 1; - assert(data.a == 2); - assert(data.b == 3); + assertOrPanic(data.a == 2); + assertOrPanic(data.b == 3); } fn getA(data: *const BitField1) u3 { @@ -254,8 +242,8 @@ const Foo96Bits = packed struct { test "packed struct 24bits" { comptime { - assert(@sizeOf(Foo24Bits) == 3); - assert(@sizeOf(Foo96Bits) == 12); + assertOrPanic(@sizeOf(Foo24Bits) == 3); + assertOrPanic(@sizeOf(Foo96Bits) == 12); } var value = Foo96Bits{ @@ -265,28 +253,28 @@ test "packed struct 24bits" { .d = 0, }; value.a += 1; - assert(value.a == 1); - assert(value.b == 0); - assert(value.c == 0); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 0); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); value.b += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 0); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); value.c += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 0); value.d += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 1); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 1); } const FooArray24Bits = packed struct { @@ -297,43 +285,43 @@ const FooArray24Bits = packed struct { test "packed array 24bits" { comptime { - assert(@sizeOf([9]Foo24Bits) == 9 * 3); - assert(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); + assertOrPanic(@sizeOf([9]Foo24Bits) == 9 * 3); + assertOrPanic(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); } var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); bytes[bytes.len - 1] = 0xaa; const ptr = &@bytesToSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; - assert(ptr.a == 0); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + assertOrPanic(ptr.a == 0); + assertOrPanic(ptr.b[0].field == 0); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); ptr.a = maxInt(u16); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == 0); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); ptr.b[0].field = maxInt(u24); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); ptr.b[1].field = maxInt(u24); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == 0); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == maxInt(u24)); + assertOrPanic(ptr.c == 0); ptr.c = maxInt(u16); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == maxInt(u16)); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == maxInt(u24)); + assertOrPanic(ptr.c == maxInt(u16)); - assert(bytes[bytes.len - 1] == 0xaa); + assertOrPanic(bytes[bytes.len - 1] == 0xaa); } const FooStructAligned = packed struct { @@ -347,17 +335,17 @@ const FooArrayOfAligned = packed struct { test "aligned array of packed struct" { comptime { - assert(@sizeOf(FooStructAligned) == 2); - assert(@sizeOf(FooArrayOfAligned) == 2 * 2); + assertOrPanic(@sizeOf(FooStructAligned) == 2); + assertOrPanic(@sizeOf(FooArrayOfAligned) == 2 * 2); } var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); const ptr = &@bytesToSlice(FooArrayOfAligned, bytes[0..bytes.len])[0]; - assert(ptr.a[0].a == 0xbb); - assert(ptr.a[0].b == 0xbb); - assert(ptr.a[1].a == 0xbb); - assert(ptr.a[1].b == 0xbb); + assertOrPanic(ptr.a[0].a == 0xbb); + assertOrPanic(ptr.a[0].b == 0xbb); + assertOrPanic(ptr.a[1].a == 0xbb); + assertOrPanic(ptr.a[1].b == 0xbb); } test "runtime struct initialization of bitfield" { @@ -370,10 +358,10 @@ test "runtime struct initialization of bitfield" { .y = @intCast(u4, x2), }; - assert(s1.x == x1); - assert(s1.y == x1); - assert(s2.x == @intCast(u4, x2)); - assert(s2.y == @intCast(u4, x2)); + assertOrPanic(s1.x == x1); + assertOrPanic(s1.y == x1); + assertOrPanic(s2.x == @intCast(u4, x2)); + assertOrPanic(s2.y == @intCast(u4, x2)); } var x1 = u4(1); @@ -400,18 +388,18 @@ test "native bit field understands endianness" { @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; - assert(bitfields.f1 == 0x1111); - assert(bitfields.f2 == 0x2222); - assert(bitfields.f3 == 0x33); - assert(bitfields.f4 == 0x44); - assert(bitfields.f5 == 0x5); - assert(bitfields.f6 == 0x6); - assert(bitfields.f7 == 0x77); + assertOrPanic(bitfields.f1 == 0x1111); + assertOrPanic(bitfields.f2 == 0x2222); + assertOrPanic(bitfields.f3 == 0x33); + assertOrPanic(bitfields.f4 == 0x44); + assertOrPanic(bitfields.f5 == 0x5); + assertOrPanic(bitfields.f6 == 0x6); + assertOrPanic(bitfields.f7 == 0x77); } test "align 1 field before self referential align 8 field as slice return type" { const result = alloc(Expr); - assert(result.len == 0); + assertOrPanic(result.len == 0); } const Expr = union(enum) { @@ -434,10 +422,10 @@ test "call method with mutable reference to struct with no fields" { }; var s = S{}; - assert(S.doC(&s)); - assert(s.doC()); - assert(S.do(&s)); - assert(s.do()); + assertOrPanic(S.doC(&s)); + assertOrPanic(s.doC()); + assertOrPanic(S.do(&s)); + assertOrPanic(s.do()); } test "implicit cast packed struct field to const ptr" { @@ -453,7 +441,7 @@ test "implicit cast packed struct field to const ptr" { var lup: LevelUpMove = undefined; lup.level = 12; const res = LevelUpMove.toInt(lup.level); - assert(res == 12); + assertOrPanic(res == 12); } test "pointer to packed struct member in a stack variable" { @@ -464,7 +452,7 @@ test "pointer to packed struct member in a stack variable" { var s = S{ .a = 2, .b = 0 }; var b_ptr = &s.b; - assert(s.b == 0); + assertOrPanic(s.b == 0); b_ptr.* = 2; - assert(s.b == 2); + assertOrPanic(s.b == 2); } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig deleted file mode 100644 index 6f99268c0809..000000000000 --- a/test/cases/type_info.zig +++ /dev/null @@ -1,259 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const TypeInfo = @import("builtin").TypeInfo; -const TypeId = @import("builtin").TypeId; - -test "type info: tag type, void info" { - testBasic(); - comptime testBasic(); -} - -fn testBasic() void { - assert(@TagType(TypeInfo) == TypeId); - const void_info = @typeInfo(void); - assert(TypeId(void_info) == TypeId.Void); - assert(void_info.Void == {}); -} - -test "type info: integer, floating point type info" { - testIntFloat(); - comptime testIntFloat(); -} - -fn testIntFloat() void { - const u8_info = @typeInfo(u8); - assert(TypeId(u8_info) == TypeId.Int); - assert(!u8_info.Int.is_signed); - assert(u8_info.Int.bits == 8); - - const f64_info = @typeInfo(f64); - assert(TypeId(f64_info) == TypeId.Float); - assert(f64_info.Float.bits == 64); -} - -test "type info: pointer type info" { - testPointer(); - comptime testPointer(); -} - -fn testPointer() void { - const u32_ptr_info = @typeInfo(*u32); - assert(TypeId(u32_ptr_info) == TypeId.Pointer); - assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); - assert(u32_ptr_info.Pointer.is_const == false); - assert(u32_ptr_info.Pointer.is_volatile == false); - assert(u32_ptr_info.Pointer.alignment == @alignOf(u32)); - assert(u32_ptr_info.Pointer.child == u32); -} - -test "type info: unknown length pointer type info" { - testUnknownLenPtr(); - comptime testUnknownLenPtr(); -} - -fn testUnknownLenPtr() void { - const u32_ptr_info = @typeInfo([*]const volatile f64); - assert(TypeId(u32_ptr_info) == TypeId.Pointer); - assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); - assert(u32_ptr_info.Pointer.is_const == true); - assert(u32_ptr_info.Pointer.is_volatile == true); - assert(u32_ptr_info.Pointer.alignment == @alignOf(f64)); - assert(u32_ptr_info.Pointer.child == f64); -} - -test "type info: slice type info" { - testSlice(); - comptime testSlice(); -} - -fn testSlice() void { - const u32_slice_info = @typeInfo([]u32); - assert(TypeId(u32_slice_info) == TypeId.Pointer); - assert(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); - assert(u32_slice_info.Pointer.is_const == false); - assert(u32_slice_info.Pointer.is_volatile == false); - assert(u32_slice_info.Pointer.alignment == 4); - assert(u32_slice_info.Pointer.child == u32); -} - -test "type info: array type info" { - testArray(); - comptime testArray(); -} - -fn testArray() void { - const arr_info = @typeInfo([42]bool); - assert(TypeId(arr_info) == TypeId.Array); - assert(arr_info.Array.len == 42); - assert(arr_info.Array.child == bool); -} - -test "type info: optional type info" { - testOptional(); - comptime testOptional(); -} - -fn testOptional() void { - const null_info = @typeInfo(?void); - assert(TypeId(null_info) == TypeId.Optional); - assert(null_info.Optional.child == void); -} - -test "type info: promise info" { - testPromise(); - comptime testPromise(); -} - -fn testPromise() void { - const null_promise_info = @typeInfo(promise); - assert(TypeId(null_promise_info) == TypeId.Promise); - assert(null_promise_info.Promise.child == null); - - const promise_info = @typeInfo(promise->usize); - assert(TypeId(promise_info) == TypeId.Promise); - assert(promise_info.Promise.child.? == usize); -} - -test "type info: error set, error union info" { - testErrorSet(); - comptime testErrorSet(); -} - -fn testErrorSet() void { - const TestErrorSet = error{ - First, - Second, - Third, - }; - - const error_set_info = @typeInfo(TestErrorSet); - assert(TypeId(error_set_info) == TypeId.ErrorSet); - assert(error_set_info.ErrorSet.errors.len == 3); - assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); - assert(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); - - const error_union_info = @typeInfo(TestErrorSet!usize); - assert(TypeId(error_union_info) == TypeId.ErrorUnion); - assert(error_union_info.ErrorUnion.error_set == TestErrorSet); - assert(error_union_info.ErrorUnion.payload == usize); -} - -test "type info: enum info" { - testEnum(); - comptime testEnum(); -} - -fn testEnum() void { - const Os = @import("builtin").Os; - - const os_info = @typeInfo(Os); - assert(TypeId(os_info) == TypeId.Enum); - assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); - assert(os_info.Enum.fields.len == 32); - assert(mem.eql(u8, os_info.Enum.fields[1].name, "ananas")); - assert(os_info.Enum.fields[10].value == 10); - assert(os_info.Enum.tag_type == u5); - assert(os_info.Enum.defs.len == 0); -} - -test "type info: union info" { - testUnion(); - comptime testUnion(); -} - -fn testUnion() void { - const typeinfo_info = @typeInfo(TypeInfo); - assert(TypeId(typeinfo_info) == TypeId.Union); - assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(typeinfo_info.Union.tag_type.? == TypeId); - assert(typeinfo_info.Union.fields.len == 24); - assert(typeinfo_info.Union.fields[4].enum_field != null); - assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4); - assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assert(typeinfo_info.Union.defs.len == 20); - - const TestNoTagUnion = union { - Foo: void, - Bar: u32, - }; - - const notag_union_info = @typeInfo(TestNoTagUnion); - assert(TypeId(notag_union_info) == TypeId.Union); - assert(notag_union_info.Union.tag_type == null); - assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(notag_union_info.Union.fields.len == 2); - assert(notag_union_info.Union.fields[0].enum_field == null); - assert(notag_union_info.Union.fields[1].field_type == u32); - - const TestExternUnion = extern union { - foo: *c_void, - }; - - const extern_union_info = @typeInfo(TestExternUnion); - assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); - assert(extern_union_info.Union.tag_type == null); - assert(extern_union_info.Union.fields[0].enum_field == null); - assert(extern_union_info.Union.fields[0].field_type == *c_void); -} - -test "type info: struct info" { - testStruct(); - comptime testStruct(); -} - -fn testStruct() void { - const struct_info = @typeInfo(TestStruct); - assert(TypeId(struct_info) == TypeId.Struct); - assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); - assert(struct_info.Struct.fields.len == 3); - assert(struct_info.Struct.fields[1].offset == null); - assert(struct_info.Struct.fields[2].field_type == *TestStruct); - assert(struct_info.Struct.defs.len == 2); - assert(struct_info.Struct.defs[0].is_pub); - assert(!struct_info.Struct.defs[0].data.Fn.is_extern); - assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); - assert(struct_info.Struct.defs[0].data.Fn.return_type == void); - assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void); -} - -const TestStruct = packed struct { - const Self = @This(); - - fieldA: usize, - fieldB: void, - fieldC: *Self, - - pub fn foo(self: *const Self) void {} -}; - -test "type info: function type info" { - testFunction(); - comptime testFunction(); -} - -fn testFunction() void { - const fn_info = @typeInfo(@typeOf(foo)); - assert(TypeId(fn_info) == TypeId.Fn); - assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); - assert(fn_info.Fn.is_generic); - assert(fn_info.Fn.args.len == 2); - assert(fn_info.Fn.is_var_args); - assert(fn_info.Fn.return_type == null); - assert(fn_info.Fn.async_allocator_type == null); - - const test_instance: TestStruct = undefined; - const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); - assert(TypeId(bound_fn_info) == TypeId.BoundFn); - assert(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); -} - -fn foo(comptime a: usize, b: bool, args: ...) usize { - return 0; -} - -test "typeInfo with comptime parameter in struct fn def" { - const S = struct { - pub fn func(comptime x: f32) void {} - }; - comptime var info = @typeInfo(S); -} diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index afee212d2a62..5dd96e5ac856 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -9,6 +9,7 @@ comptime { _ = @import("behavior/bool.zig"); _ = @import("behavior/bswap.zig"); _ = @import("behavior/bugs/1076.zig"); + _ = @import("behavior/bugs/1111.zig"); _ = @import("behavior/bugs/1381.zig"); _ = @import("behavior/bugs/1421.zig"); _ = @import("behavior/bugs/1442.zig"); @@ -18,19 +19,40 @@ comptime { _ = @import("behavior/bugs/656.zig"); _ = @import("behavior/bugs/726.zig"); _ = @import("behavior/byval_arg_var.zig"); + _ = @import("behavior/defer.zig"); + _ = @import("behavior/enum.zig"); + _ = @import("behavior/eval.zig"); _ = @import("behavior/field_parent_ptr.zig"); + _ = @import("behavior/fn.zig"); _ = @import("behavior/fn_in_struct_in_comptime.zig"); + _ = @import("behavior/for.zig"); _ = @import("behavior/generics.zig"); _ = @import("behavior/if.zig"); _ = @import("behavior/import.zig"); _ = @import("behavior/incomplete_struct_param_tld.zig"); + _ = @import("behavior/math.zig"); _ = @import("behavior/merge_error_sets.zig"); _ = @import("behavior/misc.zig"); + _ = @import("behavior/namespace_depends_on_compile_var/index.zig"); + _ = @import("behavior/new_stack_call.zig"); + _ = @import("behavior/pointers.zig"); + _ = @import("behavior/popcount.zig"); + _ = @import("behavior/ptrcast.zig"); + _ = @import("behavior/pub_enum/index.zig"); + _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); + _ = @import("behavior/reflection.zig"); + _ = @import("behavior/sizeof_and_typeof.zig"); + _ = @import("behavior/slice.zig"); + _ = @import("behavior/struct.zig"); + _ = @import("behavior/struct_contains_null_ptr_itself.zig"); + _ = @import("behavior/syntax.zig"); + _ = @import("behavior/this.zig"); + _ = @import("behavior/truncate.zig"); + _ = @import("behavior/type_info.zig"); + _ = @import("behavior/undefined.zig"); + _ = @import("behavior/underscore.zig"); + _ = @import("behavior/var_args.zig"); _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); - _ = @import("behavior/this.zig"); - _ = @import("behavior/syntax.zig"); - _ = @import("behavior/reflection.zig"); - _ = @import("behavior/math.zig"); } diff --git a/test/cases/bugs/1111.zig b/test/stage1/behavior/bugs/1111.zig similarity index 100% rename from test/cases/bugs/1111.zig rename to test/stage1/behavior/bugs/1111.zig diff --git a/test/stage1/behavior/defer.zig b/test/stage1/behavior/defer.zig new file mode 100644 index 000000000000..d44b9e8354b4 --- /dev/null +++ b/test/stage1/behavior/defer.zig @@ -0,0 +1,43 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "break and continue inside loop inside defer expression" { + testBreakContInDefer(10); + comptime testBreakContInDefer(10); +} + +fn testBreakContInDefer(x: usize) void { + defer { + var i: usize = 0; + while (i < x) : (i += 1) { + if (i < 5) continue; + if (i == 5) break; + } + assertOrPanic(i == 5); + } +} + +test "defer and labeled break" { + var i = usize(0); + + blk: { + defer i += 1; + break :blk; + } + + assertOrPanic(i == 1); +} + +test "errdefer does not apply to fn inside fn" { + if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assertOrPanic(e == error.Bad); +} + +fn testNestedFnErrDefer() anyerror!void { + var a: i32 = 0; + errdefer a += 1; + const S = struct { + fn baz() anyerror { + return error.Bad; + } + }; + return S.baz(); +} diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig new file mode 100644 index 000000000000..2fd8053af2d5 --- /dev/null +++ b/test/stage1/behavior/enum.zig @@ -0,0 +1,3 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; + diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig new file mode 100644 index 000000000000..6144e1c2277f --- /dev/null +++ b/test/stage1/behavior/eval.zig @@ -0,0 +1,13 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const builtin = @import("builtin"); + +test "compile time recursion" { + assertOrPanic(some_data.len == 21); +} +var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined; +fn fibonacci(x: i32) i32 { + if (x <= 1) return 1; + return fibonacci(x - 1) + fibonacci(x - 2); +} + diff --git a/test/cases/fn.zig b/test/stage1/behavior/fn.zig similarity index 78% rename from test/cases/fn.zig rename to test/stage1/behavior/fn.zig index 8908bd785475..3011bc41d074 100644 --- a/test/cases/fn.zig +++ b/test/stage1/behavior/fn.zig @@ -1,7 +1,7 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "params" { - assert(testParamsAdd(22, 11) == 33); + assertOrPanic(testParamsAdd(22, 11) == 33); } fn testParamsAdd(a: i32, b: i32) i32 { return a + b; @@ -21,32 +21,32 @@ test "void parameters" { fn voidFun(a: i32, b: void, c: i32, d: void) void { const v = b; const vv: void = if (a == 1) v else {}; - assert(a + c == 3); + assertOrPanic(a + c == 3); return vv; } test "mutable local variables" { var zero: i32 = 0; - assert(zero == 0); + assertOrPanic(zero == 0); var i = i32(0); while (i != 3) { i += 1; } - assert(i == 3); + assertOrPanic(i == 3); } test "separate block scopes" { { const no_conflict: i32 = 5; - assert(no_conflict == 5); + assertOrPanic(no_conflict == 5); } const c = x: { const no_conflict = i32(10); break :x no_conflict; }; - assert(c == 10); + assertOrPanic(c == 10); } test "call function with empty string" { @@ -59,7 +59,7 @@ fn @"weird function name"() i32 { return 1234; } test "weird function name" { - assert(@"weird function name"() == 1234); + assertOrPanic(@"weird function name"() == 1234); } test "implicit cast function unreachable return" { @@ -80,7 +80,7 @@ test "function pointers" { fn4, }; for (fns) |f, i| { - assert(f() == @intCast(u32, i) + 5); + assertOrPanic(f() == @intCast(u32, i) + 5); } } fn fn1() u32 { @@ -97,7 +97,7 @@ fn fn4() u32 { } test "inline function call" { - assert(@inlineCall(add, 3, 9) == 12); + assertOrPanic(@inlineCall(add, 3, 9) == 12); } fn add(a: i32, b: i32) i32 { @@ -110,7 +110,7 @@ test "number literal as an argument" { } fn numberLiteralArg(a: var) void { - assert(a == 3); + assertOrPanic(a == 3); } test "assign inline fn to const variable" { @@ -121,7 +121,7 @@ test "assign inline fn to const variable" { inline fn inlineFn() void {} test "pass by non-copying value" { - assert(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); + assertOrPanic(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); } const Point = struct { @@ -134,17 +134,17 @@ fn addPointCoords(pt: Point) i32 { } test "pass by non-copying value through var arg" { - assert(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); + assertOrPanic(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); } fn addPointCoordsVar(pt: var) i32 { - comptime assert(@typeOf(pt) == Point); + comptime assertOrPanic(@typeOf(pt) == Point); return pt.x + pt.y; } test "pass by non-copying value as method" { var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); + assertOrPanic(pt.addPointCoords() == 3); } const Point2 = struct { @@ -158,7 +158,7 @@ const Point2 = struct { test "pass by non-copying value as method, which is generic" { var pt = Point3{ .x = 1, .y = 2 }; - assert(pt.addPointCoords(i32) == 3); + assertOrPanic(pt.addPointCoords(i32) == 3); } const Point3 = struct { @@ -173,7 +173,7 @@ const Point3 = struct { test "pass by non-copying value as method, at comptime" { comptime { var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); + assertOrPanic(pt.addPointCoords() == 3); } } @@ -189,7 +189,7 @@ fn outer(y: u32) fn (u32) u32 { test "return inner function which references comptime variable of outer function" { var func = outer(10); - assert(func(3) == 7); + assertOrPanic(func(3) == 7); } test "extern struct with stdcallcc fn pointer" { @@ -203,5 +203,6 @@ test "extern struct with stdcallcc fn pointer" { var s: S = undefined; s.ptr = S.foo; - assert(s.ptr() == 1234); + assertOrPanic(s.ptr() == 1234); } + diff --git a/test/cases/for.zig b/test/stage1/behavior/for.zig similarity index 100% rename from test/cases/for.zig rename to test/stage1/behavior/for.zig diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig index efb3b05fce4d..9d6a5a499799 100644 --- a/test/stage1/behavior/math.zig +++ b/test/stage1/behavior/math.zig @@ -454,3 +454,47 @@ test "comptime float rem int" { } } +test "remainder division" { + comptime remdiv(f16); + comptime remdiv(f32); + comptime remdiv(f64); + comptime remdiv(f128); + remdiv(f16); + remdiv(f64); + remdiv(f128); +} + +fn remdiv(comptime T: type) void { + assertOrPanic(T(1) == T(1) % T(2)); + assertOrPanic(T(1) == T(7) % T(3)); +} + +test "@sqrt" { + testSqrt(f64, 12.0); + comptime testSqrt(f64, 12.0); + testSqrt(f32, 13.0); + comptime testSqrt(f32, 13.0); + testSqrt(f16, 13.0); + comptime testSqrt(f16, 13.0); + + const x = 14.0; + const y = x * x; + const z = @sqrt(@typeOf(y), y); + comptime assertOrPanic(z == x); +} + +fn testSqrt(comptime T: type, x: T) void { + assertOrPanic(@sqrt(T, x * x) == x); +} + +test "comptime_int param and return" { + const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); + assertOrPanic(a == 137114567242441932203689521744947848950); + + const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); + assertOrPanic(b == 985095453608931032642182098849559179469148836107390954364380); +} + +fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { + return a + b; +} diff --git a/test/cases/namespace_depends_on_compile_var/a.zig b/test/stage1/behavior/namespace_depends_on_compile_var/a.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/a.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/a.zig diff --git a/test/cases/namespace_depends_on_compile_var/b.zig b/test/stage1/behavior/namespace_depends_on_compile_var/b.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/b.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/b.zig diff --git a/test/cases/namespace_depends_on_compile_var/index.zig b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/index.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/index.zig diff --git a/test/cases/new_stack_call.zig b/test/stage1/behavior/new_stack_call.zig similarity index 72% rename from test/cases/new_stack_call.zig rename to test/stage1/behavior/new_stack_call.zig index 5912550d545a..b9ae2d27cdb7 100644 --- a/test/cases/new_stack_call.zig +++ b/test/stage1/behavior/new_stack_call.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; var new_stack_bytes: [1024]u8 = undefined; @@ -10,17 +10,17 @@ test "calling a function with a new stack" { const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); _ = targetFunction(arg); - assert(arg == 1234); - assert(a < b); + assertOrPanic(arg == 1234); + assertOrPanic(a < b); } fn targetFunction(x: i32) usize { - assert(x == 1234); + assertOrPanic(x == 1234); var local_variable: i32 = 42; const ptr = &local_variable; ptr.* += 1; - assert(local_variable == 43); + assertOrPanic(local_variable == 43); return @ptrToInt(ptr); } diff --git a/test/cases/pointers.zig b/test/stage1/behavior/pointers.zig similarity index 100% rename from test/cases/pointers.zig rename to test/stage1/behavior/pointers.zig diff --git a/test/stage1/behavior/popcount.zig b/test/stage1/behavior/popcount.zig new file mode 100644 index 000000000000..f7f8bb523bf4 --- /dev/null +++ b/test/stage1/behavior/popcount.zig @@ -0,0 +1,25 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "@popCount" { + comptime testPopCount(); + testPopCount(); +} + +fn testPopCount() void { + { + var x: u32 = 0xaa; + assertOrPanic(@popCount(x) == 4); + } + { + var x: u32 = 0xaaaaaaaa; + assertOrPanic(@popCount(x) == 16); + } + { + var x: i16 = -1; + assertOrPanic(@popCount(x) == 16); + } + comptime { + assertOrPanic(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); + } +} + diff --git a/test/cases/ptrcast.zig b/test/stage1/behavior/ptrcast.zig similarity index 100% rename from test/cases/ptrcast.zig rename to test/stage1/behavior/ptrcast.zig diff --git a/test/cases/pub_enum/index.zig b/test/stage1/behavior/pub_enum/index.zig similarity index 100% rename from test/cases/pub_enum/index.zig rename to test/stage1/behavior/pub_enum/index.zig diff --git a/test/cases/pub_enum/other.zig b/test/stage1/behavior/pub_enum/other.zig similarity index 100% rename from test/cases/pub_enum/other.zig rename to test/stage1/behavior/pub_enum/other.zig diff --git a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig similarity index 100% rename from test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig rename to test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig new file mode 100644 index 000000000000..ddaea4c24299 --- /dev/null +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -0,0 +1,69 @@ +const builtin = @import("builtin"); +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "@sizeOf and @typeOf" { + const y: @typeOf(x) = 120; + assertOrPanic(@sizeOf(@typeOf(y)) == 2); +} +const x: u16 = 13; +const z: @typeOf(x) = 19; + +const A = struct { + a: u8, + b: u32, + c: u8, + d: u3, + e: u5, + f: u16, + g: u16, +}; + +const P = packed struct { + a: u8, + b: u32, + c: u8, + d: u3, + e: u5, + f: u16, + g: u16, +}; + +test "@byteOffsetOf" { + // Packed structs have fixed memory layout + assertOrPanic(@byteOffsetOf(P, "a") == 0); + assertOrPanic(@byteOffsetOf(P, "b") == 1); + assertOrPanic(@byteOffsetOf(P, "c") == 5); + assertOrPanic(@byteOffsetOf(P, "d") == 6); + assertOrPanic(@byteOffsetOf(P, "e") == 6); + assertOrPanic(@byteOffsetOf(P, "f") == 7); + assertOrPanic(@byteOffsetOf(P, "g") == 9); + + // Normal struct fields can be moved/padded + var a: A = undefined; + assertOrPanic(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); + assertOrPanic(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); + assertOrPanic(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); + assertOrPanic(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); + assertOrPanic(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); + assertOrPanic(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); + assertOrPanic(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); +} + +test "@bitOffsetOf" { + // Packed structs have fixed memory layout + assertOrPanic(@bitOffsetOf(P, "a") == 0); + assertOrPanic(@bitOffsetOf(P, "b") == 8); + assertOrPanic(@bitOffsetOf(P, "c") == 40); + assertOrPanic(@bitOffsetOf(P, "d") == 48); + assertOrPanic(@bitOffsetOf(P, "e") == 51); + assertOrPanic(@bitOffsetOf(P, "f") == 56); + assertOrPanic(@bitOffsetOf(P, "g") == 72); + + assertOrPanic(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); + assertOrPanic(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); + assertOrPanic(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); + assertOrPanic(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); + assertOrPanic(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); + assertOrPanic(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); + assertOrPanic(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); +} diff --git a/test/cases/slice.zig b/test/stage1/behavior/slice.zig similarity index 100% rename from test/cases/slice.zig rename to test/stage1/behavior/slice.zig diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig new file mode 100644 index 000000000000..ab82ff33616a --- /dev/null +++ b/test/stage1/behavior/struct.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const builtin = @import("builtin"); +const maxInt = std.math.maxInt; + +const StructWithNoFields = struct { + fn add(a: i32, b: i32) i32 { + return a + b; + } +}; +const empty_global_instance = StructWithNoFields{}; + +test "call struct static method" { + const result = StructWithNoFields.add(3, 4); + assertOrPanic(result == 7); +} diff --git a/test/cases/struct_contains_null_ptr_itself.zig b/test/stage1/behavior/struct_contains_null_ptr_itself.zig similarity index 100% rename from test/cases/struct_contains_null_ptr_itself.zig rename to test/stage1/behavior/struct_contains_null_ptr_itself.zig diff --git a/test/cases/truncate.zig b/test/stage1/behavior/truncate.zig similarity index 100% rename from test/cases/truncate.zig rename to test/stage1/behavior/truncate.zig diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig new file mode 100644 index 000000000000..9155923f8925 --- /dev/null +++ b/test/stage1/behavior/type_info.zig @@ -0,0 +1,260 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; +const TypeInfo = @import("builtin").TypeInfo; +const TypeId = @import("builtin").TypeId; + +test "type info: tag type, void info" { + testBasic(); + comptime testBasic(); +} + +fn testBasic() void { + assertOrPanic(@TagType(TypeInfo) == TypeId); + const void_info = @typeInfo(void); + assertOrPanic(TypeId(void_info) == TypeId.Void); + assertOrPanic(void_info.Void == {}); +} + +test "type info: integer, floating point type info" { + testIntFloat(); + comptime testIntFloat(); +} + +fn testIntFloat() void { + const u8_info = @typeInfo(u8); + assertOrPanic(TypeId(u8_info) == TypeId.Int); + assertOrPanic(!u8_info.Int.is_signed); + assertOrPanic(u8_info.Int.bits == 8); + + const f64_info = @typeInfo(f64); + assertOrPanic(TypeId(f64_info) == TypeId.Float); + assertOrPanic(f64_info.Float.bits == 64); +} + +test "type info: pointer type info" { + testPointer(); + comptime testPointer(); +} + +fn testPointer() void { + const u32_ptr_info = @typeInfo(*u32); + assertOrPanic(TypeId(u32_ptr_info) == TypeId.Pointer); + assertOrPanic(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); + assertOrPanic(u32_ptr_info.Pointer.is_const == false); + assertOrPanic(u32_ptr_info.Pointer.is_volatile == false); + assertOrPanic(u32_ptr_info.Pointer.alignment == @alignOf(u32)); + assertOrPanic(u32_ptr_info.Pointer.child == u32); +} + +test "type info: unknown length pointer type info" { + testUnknownLenPtr(); + comptime testUnknownLenPtr(); +} + +fn testUnknownLenPtr() void { + const u32_ptr_info = @typeInfo([*]const volatile f64); + assertOrPanic(TypeId(u32_ptr_info) == TypeId.Pointer); + assertOrPanic(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); + assertOrPanic(u32_ptr_info.Pointer.is_const == true); + assertOrPanic(u32_ptr_info.Pointer.is_volatile == true); + assertOrPanic(u32_ptr_info.Pointer.alignment == @alignOf(f64)); + assertOrPanic(u32_ptr_info.Pointer.child == f64); +} + +test "type info: slice type info" { + testSlice(); + comptime testSlice(); +} + +fn testSlice() void { + const u32_slice_info = @typeInfo([]u32); + assertOrPanic(TypeId(u32_slice_info) == TypeId.Pointer); + assertOrPanic(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); + assertOrPanic(u32_slice_info.Pointer.is_const == false); + assertOrPanic(u32_slice_info.Pointer.is_volatile == false); + assertOrPanic(u32_slice_info.Pointer.alignment == 4); + assertOrPanic(u32_slice_info.Pointer.child == u32); +} + +test "type info: array type info" { + testArray(); + comptime testArray(); +} + +fn testArray() void { + const arr_info = @typeInfo([42]bool); + assertOrPanic(TypeId(arr_info) == TypeId.Array); + assertOrPanic(arr_info.Array.len == 42); + assertOrPanic(arr_info.Array.child == bool); +} + +test "type info: optional type info" { + testOptional(); + comptime testOptional(); +} + +fn testOptional() void { + const null_info = @typeInfo(?void); + assertOrPanic(TypeId(null_info) == TypeId.Optional); + assertOrPanic(null_info.Optional.child == void); +} + +test "type info: promise info" { + testPromise(); + comptime testPromise(); +} + +fn testPromise() void { + const null_promise_info = @typeInfo(promise); + assertOrPanic(TypeId(null_promise_info) == TypeId.Promise); + assertOrPanic(null_promise_info.Promise.child == null); + + const promise_info = @typeInfo(promise->usize); + assertOrPanic(TypeId(promise_info) == TypeId.Promise); + assertOrPanic(promise_info.Promise.child.? == usize); +} + +test "type info: error set, error union info" { + testErrorSet(); + comptime testErrorSet(); +} + +fn testErrorSet() void { + const TestErrorSet = error{ + First, + Second, + Third, + }; + + const error_set_info = @typeInfo(TestErrorSet); + assertOrPanic(TypeId(error_set_info) == TypeId.ErrorSet); + assertOrPanic(error_set_info.ErrorSet.errors.len == 3); + assertOrPanic(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); + assertOrPanic(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); + + const error_union_info = @typeInfo(TestErrorSet!usize); + assertOrPanic(TypeId(error_union_info) == TypeId.ErrorUnion); + assertOrPanic(error_union_info.ErrorUnion.error_set == TestErrorSet); + assertOrPanic(error_union_info.ErrorUnion.payload == usize); +} + +test "type info: enum info" { + testEnum(); + comptime testEnum(); +} + +fn testEnum() void { + const Os = @import("builtin").Os; + + const os_info = @typeInfo(Os); + assertOrPanic(TypeId(os_info) == TypeId.Enum); + assertOrPanic(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(os_info.Enum.fields.len == 32); + assertOrPanic(mem.eql(u8, os_info.Enum.fields[1].name, "ananas")); + assertOrPanic(os_info.Enum.fields[10].value == 10); + assertOrPanic(os_info.Enum.tag_type == u5); + assertOrPanic(os_info.Enum.defs.len == 0); +} + +test "type info: union info" { + testUnion(); + comptime testUnion(); +} + +fn testUnion() void { + const typeinfo_info = @typeInfo(TypeInfo); + assertOrPanic(TypeId(typeinfo_info) == TypeId.Union); + assertOrPanic(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(typeinfo_info.Union.tag_type.? == TypeId); + assertOrPanic(typeinfo_info.Union.fields.len == 24); + assertOrPanic(typeinfo_info.Union.fields[4].enum_field != null); + assertOrPanic(typeinfo_info.Union.fields[4].enum_field.?.value == 4); + assertOrPanic(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); + assertOrPanic(typeinfo_info.Union.defs.len == 20); + + const TestNoTagUnion = union { + Foo: void, + Bar: u32, + }; + + const notag_union_info = @typeInfo(TestNoTagUnion); + assertOrPanic(TypeId(notag_union_info) == TypeId.Union); + assertOrPanic(notag_union_info.Union.tag_type == null); + assertOrPanic(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(notag_union_info.Union.fields.len == 2); + assertOrPanic(notag_union_info.Union.fields[0].enum_field == null); + assertOrPanic(notag_union_info.Union.fields[1].field_type == u32); + + const TestExternUnion = extern union { + foo: *c_void, + }; + + const extern_union_info = @typeInfo(TestExternUnion); + assertOrPanic(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); + assertOrPanic(extern_union_info.Union.tag_type == null); + assertOrPanic(extern_union_info.Union.fields[0].enum_field == null); + assertOrPanic(extern_union_info.Union.fields[0].field_type == *c_void); +} + +test "type info: struct info" { + testStruct(); + comptime testStruct(); +} + +fn testStruct() void { + const struct_info = @typeInfo(TestStruct); + assertOrPanic(TypeId(struct_info) == TypeId.Struct); + assertOrPanic(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); + assertOrPanic(struct_info.Struct.fields.len == 3); + assertOrPanic(struct_info.Struct.fields[1].offset == null); + assertOrPanic(struct_info.Struct.fields[2].field_type == *TestStruct); + assertOrPanic(struct_info.Struct.defs.len == 2); + assertOrPanic(struct_info.Struct.defs[0].is_pub); + assertOrPanic(!struct_info.Struct.defs[0].data.Fn.is_extern); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.lib_name == null); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.return_type == void); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void); +} + +const TestStruct = packed struct { + const Self = @This(); + + fieldA: usize, + fieldB: void, + fieldC: *Self, + + pub fn foo(self: *const Self) void {} +}; + +test "type info: function type info" { + testFunction(); + comptime testFunction(); +} + +fn testFunction() void { + const fn_info = @typeInfo(@typeOf(foo)); + assertOrPanic(TypeId(fn_info) == TypeId.Fn); + assertOrPanic(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); + assertOrPanic(fn_info.Fn.is_generic); + assertOrPanic(fn_info.Fn.args.len == 2); + assertOrPanic(fn_info.Fn.is_var_args); + assertOrPanic(fn_info.Fn.return_type == null); + assertOrPanic(fn_info.Fn.async_allocator_type == null); + + const test_instance: TestStruct = undefined; + const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); + assertOrPanic(TypeId(bound_fn_info) == TypeId.BoundFn); + assertOrPanic(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); +} + +fn foo(comptime a: usize, b: bool, args: ...) usize { + return 0; +} + +test "typeInfo with comptime parameter in struct fn def" { + const S = struct { + pub fn func(comptime x: f32) void {} + }; + comptime var info = @typeInfo(S); +} + diff --git a/test/cases/undefined.zig b/test/stage1/behavior/undefined.zig similarity index 59% rename from test/cases/undefined.zig rename to test/stage1/behavior/undefined.zig index 83c620d21159..333e217d493b 100644 --- a/test/cases/undefined.zig +++ b/test/stage1/behavior/undefined.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; fn initStaticArray() [10]i32 { @@ -11,16 +11,16 @@ fn initStaticArray() [10]i32 { } const static_array = initStaticArray(); test "init static array to undefined" { - assert(static_array[0] == 1); - assert(static_array[4] == 2); - assert(static_array[7] == 3); - assert(static_array[9] == 4); + assertOrPanic(static_array[0] == 1); + assertOrPanic(static_array[4] == 2); + assertOrPanic(static_array[7] == 3); + assertOrPanic(static_array[9] == 4); comptime { - assert(static_array[0] == 1); - assert(static_array[4] == 2); - assert(static_array[7] == 3); - assert(static_array[9] == 4); + assertOrPanic(static_array[0] == 1); + assertOrPanic(static_array[4] == 2); + assertOrPanic(static_array[7] == 3); + assertOrPanic(static_array[9] == 4); } } @@ -40,12 +40,12 @@ test "assign undefined to struct" { comptime { var foo: Foo = undefined; setFooX(&foo); - assert(foo.x == 2); + assertOrPanic(foo.x == 2); } { var foo: Foo = undefined; setFooX(&foo); - assert(foo.x == 2); + assertOrPanic(foo.x == 2); } } @@ -53,16 +53,17 @@ test "assign undefined to struct with method" { comptime { var foo: Foo = undefined; foo.setFooXMethod(); - assert(foo.x == 3); + assertOrPanic(foo.x == 3); } { var foo: Foo = undefined; foo.setFooXMethod(); - assert(foo.x == 3); + assertOrPanic(foo.x == 3); } } test "type name of undefined" { const x = undefined; - assert(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); + assertOrPanic(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); } + diff --git a/test/cases/underscore.zig b/test/stage1/behavior/underscore.zig similarity index 100% rename from test/cases/underscore.zig rename to test/stage1/behavior/underscore.zig diff --git a/test/cases/var_args.zig b/test/stage1/behavior/var_args.zig similarity index 100% rename from test/cases/var_args.zig rename to test/stage1/behavior/var_args.zig From 8749d05cf3de149aa98b6042a3b6b981eb015078 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Dec 2018 17:18:28 -0500 Subject: [PATCH 131/190] copy elision: optional pointer to size zero struct --- src/codegen.cpp | 10 ++++++++-- src/ir.cpp | 2 +- test/cases/optional.zig | 24 ++++++++---------------- test/stage1/behavior.zig | 1 + test/stage1/behavior/optional.zig | 9 +++++++++ 5 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 test/stage1/behavior/optional.zig diff --git a/src/codegen.cpp b/src/codegen.cpp index 2a0d11944423..ef5155835979 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5047,12 +5047,18 @@ static LLVMValueRef ir_render_result_optional_payload(CodeGen *g, IrExecutable * ZigType *ptr_type = instruction->base.value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; - if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { + if (!type_has_bits(child_type)) { + if (instruction->make_non_null) { + LLVMValueRef non_null_bit = LLVMConstInt(LLVMInt1Type(), 1, true); + gen_store_untyped(g, non_null_bit, prev_result_loc, 0, false); + } + return prev_result_loc; + } else if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { return prev_result_loc; } else { if (instruction->make_non_null) { LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_null_index, ""); - gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); + gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, true), nonnull_ptr, 0, false); } return LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_child_index, ""); } diff --git a/src/ir.cpp b/src/ir.cpp index 6ec9f3e0b8b2..09302b76f25e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18216,7 +18216,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, size_t elem_count = instruction->elem_count; if (container_type->id == ZigTypeIdStruct && elem_count == 0) - return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, nullptr); + return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, result_loc); if (container_type->id == ZigTypeIdVoid) { if (elem_count != 0) { diff --git a/test/cases/optional.zig b/test/cases/optional.zig index d43682bbec3c..73776b73c047 100644 --- a/test/cases/optional.zig +++ b/test/cases/optional.zig @@ -1,12 +1,4 @@ -const assert = @import("std").debug.assert; - -pub const EmptyStruct = struct {}; - -test "optional pointer to size zero struct" { - var e = EmptyStruct{}; - var o: ?*EmptyStruct = &e; - assert(o != null); -} +const assertOrPanic = @import("std").debug.assertOrPanic; test "equality compare nullable pointers" { testNullPtrsEql(); @@ -18,13 +10,13 @@ fn testNullPtrsEql() void { var x: ?*i32 = null; var y: ?*i32 = null; - assert(x == y); + assertOrPanic(x == y); y = &number; - assert(x != y); - assert(x != &number); - assert(&number != x); + assertOrPanic(x != y); + assertOrPanic(x != &number); + assertOrPanic(&number != x); x = &number; - assert(x == y); - assert(x == &number); - assert(&number == x); + assertOrPanic(x == y); + assertOrPanic(x == &number); + assertOrPanic(&number == x); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 5dd96e5ac856..d94aec423352 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -55,4 +55,5 @@ comptime { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); + _ = @import("behavior/optional.zig"); } diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig new file mode 100644 index 000000000000..772ea079a4aa --- /dev/null +++ b/test/stage1/behavior/optional.zig @@ -0,0 +1,9 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +pub const EmptyStruct = struct {}; + +test "optional pointer to size zero struct" { + var e = EmptyStruct{}; + var o: ?*EmptyStruct = &e; + assertOrPanic(o != null); +} From 7386fa320b8e28ffb2306e0f9b5e949e68a1a24a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Dec 2018 21:35:19 -0500 Subject: [PATCH 132/190] copy elision: fix comptime casting null to ?T --- src/all_types.hpp | 10 +++++--- src/analyze.cpp | 8 +++++++ src/codegen.cpp | 2 ++ src/ir.cpp | 39 +++++++++++++++++++++++++++---- test/behavior.zig | 1 - test/cases/optional.zig | 22 ----------------- test/stage1/behavior/optional.zig | 21 +++++++++++++++++ 7 files changed, 72 insertions(+), 31 deletions(-) delete mode 100644 test/cases/optional.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index 7298d0005f30..27365e2c991b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -170,15 +170,19 @@ enum ConstPtrSpecial { // understand the value of pointee at compile time. However, we will still // emit a binary with a compile time known address. // In this case index is the numeric address value. - // We also use this for null pointer. We need the data layout for ConstCastOnly == true - // types to be the same, so all optionals of pointer types use x_ptr - // instead of x_optional ConstPtrSpecialHardCodedAddr, // This means that the pointer represents memory of assigning to _. // That is, storing discards the data, and loading is invalid. ConstPtrSpecialDiscard, // This is actually a function. ConstPtrSpecialFunction, + // This means the pointer is null. This is only allowed when the type is ?*T. + // We use this instead of ConstPtrSpecialHardCodedAddr because often we check + // for that value to avoid doing comptime work. + // We need the data layout for ConstCastOnly == true + // types to be the same, so all optionals of pointer types use x_ptr + // instead of x_optional. + ConstPtrSpecialNull, }; enum ConstPtrMut { diff --git a/src/analyze.cpp b/src/analyze.cpp index bfd4cf602b87..3cd05d8f08b4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4795,6 +4795,9 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += (uint32_t)2590901619; hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); return hash_val; + case ConstPtrSpecialNull: + hash_val += (uint32_t)1486246455; + return hash_val; } zig_unreachable(); } @@ -5621,6 +5624,8 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { return true; case ConstPtrSpecialFunction: return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry; + case ConstPtrSpecialNull: + return true; } zig_unreachable(); } @@ -5818,6 +5823,9 @@ static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val case ConstPtrSpecialDiscard: buf_append_str(buf, "*_"); return; + case ConstPtrSpecialNull: + buf_append_str(buf, "null"); + return; case ConstPtrSpecialFunction: { ZigFn *fn_entry = const_val->data.x_ptr.data.fn.fn_entry; diff --git a/src/codegen.cpp b/src/codegen.cpp index ef5155835979..83cee790ecd6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5881,6 +5881,8 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con } case ConstPtrSpecialFunction: return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref); + case ConstPtrSpecialNull: + return LLVMConstNull(const_val->type->type_ref); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index 09302b76f25e..9194fc43d729 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -209,6 +209,9 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c case ConstPtrSpecialBaseErrorUnionPayload: result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload; break; + case ConstPtrSpecialNull: + result = const_val; + break; case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialDiscard: @@ -10571,7 +10574,7 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr { ConstExprValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, result_loc->source_node); assert(optional_val->type->id == ZigTypeIdOptional); - if (optional_val->special == ConstValSpecialUndef) { + if (optional_val->special == ConstValSpecialUndef && handle_is_ptr(optional_val->type)) { ConstExprValue *payload_val = create_const_vals(1); payload_val->type = needed_child_type; payload_val->special = ConstValSpecialUndef; @@ -10579,7 +10582,6 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr optional_val->data.x_optional = payload_val; optional_val->special = ConstValSpecialStatic; } - assert(optional_val->data.x_optional != nullptr); IrInstruction *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { @@ -10593,7 +10595,12 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr ConstExprValue *result_val = &result->value; result_val->data.x_ptr.special = ConstPtrSpecialRef; result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; - result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; + if (handle_is_ptr(optional_val->type)) { + assert(optional_val->data.x_optional != nullptr); + result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; + } else { + result_val->data.x_ptr.data.ref.pointee = optional_val; + } return result; } @@ -10958,8 +10965,7 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; if (get_codegen_ptr_type(wanted_type) != nullptr) { - const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; + const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialNull; } else if (is_opt_err_set(wanted_type)) { const_instruction->base.value.data.x_err_set = nullptr; } else { @@ -15419,6 +15425,13 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source switch (ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); + case ConstPtrSpecialNull: + if (dst_size == 0) + return ErrorNone; + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %zu bytes from null pointer", + dst_size)); + return ErrorSemanticAnalyzeFail; case ConstPtrSpecialRef: { opt_ir_add_error_node(ira, codegen, source_node, buf_sprintf("attempt to read %zu bytes from pointer to %s which is %zu bytes", @@ -16077,6 +16090,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO element ptr of a function casted to a ptr"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a null pointer"); } if (new_index >= mem_size) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, @@ -16134,6 +16149,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO elem ptr on a slice that was ptrcast from a function"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a slice has a null pointer"); } return result; } else if (array_type->id == ZigTypeIdArray) { @@ -20230,6 +20247,8 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memset on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memset on null ptr"); } size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint); @@ -20349,6 +20368,8 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); } if (dest_start + count > dest_end) { @@ -20389,6 +20410,8 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); } if (src_start + count > src_end) { @@ -20573,6 +20596,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO slice of ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null ptr"); } } else if (is_slice(array_type)) { ConstExprValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node); @@ -20614,6 +20639,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO slice of slice cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null"); } } else { zig_unreachable(); @@ -20691,6 +20718,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO"); + case ConstPtrSpecialNull: + zig_panic("TODO"); } ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; diff --git a/test/behavior.zig b/test/behavior.zig index 0f5df0ff55ad..9052832b9233 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -15,7 +15,6 @@ comptime { _ = @import("cases/eval.zig"); _ = @import("cases/ir_block_deps.zig"); _ = @import("cases/null.zig"); - _ = @import("cases/optional.zig"); _ = @import("cases/struct.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); _ = @import("cases/switch.zig"); diff --git a/test/cases/optional.zig b/test/cases/optional.zig deleted file mode 100644 index 73776b73c047..000000000000 --- a/test/cases/optional.zig +++ /dev/null @@ -1,22 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -test "equality compare nullable pointers" { - testNullPtrsEql(); - comptime testNullPtrsEql(); -} - -fn testNullPtrsEql() void { - var number: i32 = 1234; - - var x: ?*i32 = null; - var y: ?*i32 = null; - assertOrPanic(x == y); - y = &number; - assertOrPanic(x != y); - assertOrPanic(x != &number); - assertOrPanic(&number != x); - x = &number; - assertOrPanic(x == y); - assertOrPanic(x == &number); - assertOrPanic(&number == x); -} diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig index 772ea079a4aa..84a75395334c 100644 --- a/test/stage1/behavior/optional.zig +++ b/test/stage1/behavior/optional.zig @@ -7,3 +7,24 @@ test "optional pointer to size zero struct" { var o: ?*EmptyStruct = &e; assertOrPanic(o != null); } + +test "equality compare nullable pointers" { + testNullPtrsEql(); + comptime testNullPtrsEql(); +} + +fn testNullPtrsEql() void { + var number: i32 = 1234; + + var x: ?*i32 = null; + var y: ?*i32 = null; + assertOrPanic(x == y); + y = &number; + assertOrPanic(x != y); + assertOrPanic(x != &number); + assertOrPanic(&number != x); + x = &number; + assertOrPanic(x == y); + assertOrPanic(x == &number); + assertOrPanic(&number == x); +} From c929d36fddc6315111156b1756f8f253ed0bbbe9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Dec 2018 21:40:17 -0500 Subject: [PATCH 133/190] copy elision: move passing tests --- test/cases/enum.zig | 770 ---------------------------------- test/stage1/behavior/enum.zig | 770 ++++++++++++++++++++++++++++++++++ 2 files changed, 770 insertions(+), 770 deletions(-) diff --git a/test/cases/enum.zig b/test/cases/enum.zig index c74cbcc3cf49..73c9166d4cb5 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -1,776 +1,6 @@ const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; -test "enum type" { - const foo1 = Foo{ .One = 13 }; - const foo2 = Foo{ - .Two = Point{ - .x = 1234, - .y = 5678, - }, - }; - const bar = Bar.B; - - assertOrPanic(bar == Bar.B); - assertOrPanic(@memberCount(Foo) == 3); - assertOrPanic(@memberCount(Bar) == 4); - assertOrPanic(@sizeOf(Foo) == @sizeOf(FooNoVoid)); - assertOrPanic(@sizeOf(Bar) == 1); -} - -test "enum as return value" { - switch (returnAnInt(13)) { - Foo.One => |value| assertOrPanic(value == 13), - else => unreachable, - } -} - -const Point = struct { - x: u64, - y: u64, -}; -const Foo = union(enum) { - One: i32, - Two: Point, - Three: void, -}; -const FooNoVoid = union(enum) { - One: i32, - Two: Point, -}; -const Bar = enum { - A, - B, - C, - D, -}; - -fn returnAnInt(x: i32) Foo { - return Foo{ .One = x }; -} - -test "constant enum with payload" { - var empty = AnEnumWithPayload{ .Empty = {} }; - var full = AnEnumWithPayload{ .Full = 13 }; - shouldBeEmpty(empty); - shouldBeNotEmpty(full); -} - -fn shouldBeEmpty(x: AnEnumWithPayload) void { - switch (x) { - AnEnumWithPayload.Empty => {}, - else => unreachable, - } -} - -fn shouldBeNotEmpty(x: AnEnumWithPayload) void { - switch (x) { - AnEnumWithPayload.Empty => unreachable, - else => {}, - } -} - -const AnEnumWithPayload = union(enum) { - Empty: void, - Full: i32, -}; - -const Number = enum { - Zero, - One, - Two, - Three, - Four, -}; - -test "enum to int" { - shouldEqual(Number.Zero, 0); - shouldEqual(Number.One, 1); - shouldEqual(Number.Two, 2); - shouldEqual(Number.Three, 3); - shouldEqual(Number.Four, 4); -} - -fn shouldEqual(n: Number, expected: u3) void { - assertOrPanic(@enumToInt(n) == expected); -} - -test "int to enum" { - testIntToEnumEval(3); -} -fn testIntToEnumEval(x: i32) void { - assertOrPanic(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); -} -const IntToEnumNumber = enum { - Zero, - One, - Two, - Three, - Four, -}; - -test "@tagName" { - assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); - comptime assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); -} - -fn testEnumTagNameBare(n: BareNumber) []const u8 { - return @tagName(n); -} - -const BareNumber = enum { - One, - Two, - Three, -}; - -test "enum alignment" { - comptime { - assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); - assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf(u64)); - } -} - -const AlignTestEnum = union(enum) { - A: [9]u8, - B: u64, -}; - -const ValueCount1 = enum { - I0, -}; -const ValueCount2 = enum { - I0, - I1, -}; -const ValueCount256 = enum { - I0, - I1, - I2, - I3, - I4, - I5, - I6, - I7, - I8, - I9, - I10, - I11, - I12, - I13, - I14, - I15, - I16, - I17, - I18, - I19, - I20, - I21, - I22, - I23, - I24, - I25, - I26, - I27, - I28, - I29, - I30, - I31, - I32, - I33, - I34, - I35, - I36, - I37, - I38, - I39, - I40, - I41, - I42, - I43, - I44, - I45, - I46, - I47, - I48, - I49, - I50, - I51, - I52, - I53, - I54, - I55, - I56, - I57, - I58, - I59, - I60, - I61, - I62, - I63, - I64, - I65, - I66, - I67, - I68, - I69, - I70, - I71, - I72, - I73, - I74, - I75, - I76, - I77, - I78, - I79, - I80, - I81, - I82, - I83, - I84, - I85, - I86, - I87, - I88, - I89, - I90, - I91, - I92, - I93, - I94, - I95, - I96, - I97, - I98, - I99, - I100, - I101, - I102, - I103, - I104, - I105, - I106, - I107, - I108, - I109, - I110, - I111, - I112, - I113, - I114, - I115, - I116, - I117, - I118, - I119, - I120, - I121, - I122, - I123, - I124, - I125, - I126, - I127, - I128, - I129, - I130, - I131, - I132, - I133, - I134, - I135, - I136, - I137, - I138, - I139, - I140, - I141, - I142, - I143, - I144, - I145, - I146, - I147, - I148, - I149, - I150, - I151, - I152, - I153, - I154, - I155, - I156, - I157, - I158, - I159, - I160, - I161, - I162, - I163, - I164, - I165, - I166, - I167, - I168, - I169, - I170, - I171, - I172, - I173, - I174, - I175, - I176, - I177, - I178, - I179, - I180, - I181, - I182, - I183, - I184, - I185, - I186, - I187, - I188, - I189, - I190, - I191, - I192, - I193, - I194, - I195, - I196, - I197, - I198, - I199, - I200, - I201, - I202, - I203, - I204, - I205, - I206, - I207, - I208, - I209, - I210, - I211, - I212, - I213, - I214, - I215, - I216, - I217, - I218, - I219, - I220, - I221, - I222, - I223, - I224, - I225, - I226, - I227, - I228, - I229, - I230, - I231, - I232, - I233, - I234, - I235, - I236, - I237, - I238, - I239, - I240, - I241, - I242, - I243, - I244, - I245, - I246, - I247, - I248, - I249, - I250, - I251, - I252, - I253, - I254, - I255, -}; -const ValueCount257 = enum { - I0, - I1, - I2, - I3, - I4, - I5, - I6, - I7, - I8, - I9, - I10, - I11, - I12, - I13, - I14, - I15, - I16, - I17, - I18, - I19, - I20, - I21, - I22, - I23, - I24, - I25, - I26, - I27, - I28, - I29, - I30, - I31, - I32, - I33, - I34, - I35, - I36, - I37, - I38, - I39, - I40, - I41, - I42, - I43, - I44, - I45, - I46, - I47, - I48, - I49, - I50, - I51, - I52, - I53, - I54, - I55, - I56, - I57, - I58, - I59, - I60, - I61, - I62, - I63, - I64, - I65, - I66, - I67, - I68, - I69, - I70, - I71, - I72, - I73, - I74, - I75, - I76, - I77, - I78, - I79, - I80, - I81, - I82, - I83, - I84, - I85, - I86, - I87, - I88, - I89, - I90, - I91, - I92, - I93, - I94, - I95, - I96, - I97, - I98, - I99, - I100, - I101, - I102, - I103, - I104, - I105, - I106, - I107, - I108, - I109, - I110, - I111, - I112, - I113, - I114, - I115, - I116, - I117, - I118, - I119, - I120, - I121, - I122, - I123, - I124, - I125, - I126, - I127, - I128, - I129, - I130, - I131, - I132, - I133, - I134, - I135, - I136, - I137, - I138, - I139, - I140, - I141, - I142, - I143, - I144, - I145, - I146, - I147, - I148, - I149, - I150, - I151, - I152, - I153, - I154, - I155, - I156, - I157, - I158, - I159, - I160, - I161, - I162, - I163, - I164, - I165, - I166, - I167, - I168, - I169, - I170, - I171, - I172, - I173, - I174, - I175, - I176, - I177, - I178, - I179, - I180, - I181, - I182, - I183, - I184, - I185, - I186, - I187, - I188, - I189, - I190, - I191, - I192, - I193, - I194, - I195, - I196, - I197, - I198, - I199, - I200, - I201, - I202, - I203, - I204, - I205, - I206, - I207, - I208, - I209, - I210, - I211, - I212, - I213, - I214, - I215, - I216, - I217, - I218, - I219, - I220, - I221, - I222, - I223, - I224, - I225, - I226, - I227, - I228, - I229, - I230, - I231, - I232, - I233, - I234, - I235, - I236, - I237, - I238, - I239, - I240, - I241, - I242, - I243, - I244, - I245, - I246, - I247, - I248, - I249, - I250, - I251, - I252, - I253, - I254, - I255, - I256, -}; - -test "enum sizes" { - comptime { - assertOrPanic(@sizeOf(ValueCount1) == 0); - assertOrPanic(@sizeOf(ValueCount2) == 1); - assertOrPanic(@sizeOf(ValueCount256) == 1); - assertOrPanic(@sizeOf(ValueCount257) == 2); - } -} - -const Small2 = enum(u2) { - One, - Two, -}; -const Small = enum(u2) { - One, - Two, - Three, - Four, -}; - -test "set enum tag type" { - { - var x = Small.One; - x = Small.Two; - comptime assertOrPanic(@TagType(Small) == u2); - } - { - var x = Small2.One; - x = Small2.Two; - comptime assertOrPanic(@TagType(Small2) == u2); - } -} - -const A = enum(u3) { - One, - Two, - Three, - Four, - One2, - Two2, - Three2, - Four2, -}; - -const B = enum(u3) { - One3, - Two3, - Three3, - Four3, - One23, - Two23, - Three23, - Four23, -}; - -const C = enum(u2) { - One4, - Two4, - Three4, - Four4, -}; - -const BitFieldOfEnums = packed struct { - a: A, - b: B, - c: C, -}; - -const bit_field_1 = BitFieldOfEnums{ - .a = A.Two, - .b = B.Three3, - .c = C.Four4, -}; - -test "bit field access with enum fields" { - var data = bit_field_1; - assertOrPanic(getA(&data) == A.Two); - assertOrPanic(getB(&data) == B.Three3); - assertOrPanic(getC(&data) == C.Four4); - comptime assertOrPanic(@sizeOf(BitFieldOfEnums) == 1); - - data.b = B.Four3; - assertOrPanic(data.b == B.Four3); - - data.a = A.Three; - assertOrPanic(data.a == A.Three); - assertOrPanic(data.b == B.Four3); -} - -fn getA(data: *const BitFieldOfEnums) A { - return data.a; -} - -fn getB(data: *const BitFieldOfEnums) B { - return data.b; -} - -fn getC(data: *const BitFieldOfEnums) C { - return data.c; -} - -test "casting enum to its tag type" { - testCastEnumToTagType(Small2.Two); - comptime testCastEnumToTagType(Small2.Two); -} - -fn testCastEnumToTagType(value: Small2) void { - assertOrPanic(@enumToInt(value) == 1); -} - const MultipleChoice = enum(u32) { A = 20, B = 40, diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index 2fd8053af2d5..6583d54d3bfc 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -1,3 +1,773 @@ const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; +test "enum type" { + const foo1 = Foo{ .One = 13 }; + const foo2 = Foo{ + .Two = Point{ + .x = 1234, + .y = 5678, + }, + }; + const bar = Bar.B; + + assertOrPanic(bar == Bar.B); + assertOrPanic(@memberCount(Foo) == 3); + assertOrPanic(@memberCount(Bar) == 4); + assertOrPanic(@sizeOf(Foo) == @sizeOf(FooNoVoid)); + assertOrPanic(@sizeOf(Bar) == 1); +} + +test "enum as return value" { + switch (returnAnInt(13)) { + Foo.One => |value| assertOrPanic(value == 13), + else => unreachable, + } +} + +const Point = struct { + x: u64, + y: u64, +}; +const Foo = union(enum) { + One: i32, + Two: Point, + Three: void, +}; +const FooNoVoid = union(enum) { + One: i32, + Two: Point, +}; +const Bar = enum { + A, + B, + C, + D, +}; + +fn returnAnInt(x: i32) Foo { + return Foo{ .One = x }; +} + +test "constant enum with payload" { + var empty = AnEnumWithPayload{ .Empty = {} }; + var full = AnEnumWithPayload{ .Full = 13 }; + shouldBeEmpty(empty); + shouldBeNotEmpty(full); +} + +fn shouldBeEmpty(x: AnEnumWithPayload) void { + switch (x) { + AnEnumWithPayload.Empty => {}, + else => unreachable, + } +} + +fn shouldBeNotEmpty(x: AnEnumWithPayload) void { + switch (x) { + AnEnumWithPayload.Empty => unreachable, + else => {}, + } +} + +const AnEnumWithPayload = union(enum) { + Empty: void, + Full: i32, +}; + +const Number = enum { + Zero, + One, + Two, + Three, + Four, +}; + +test "enum to int" { + shouldEqual(Number.Zero, 0); + shouldEqual(Number.One, 1); + shouldEqual(Number.Two, 2); + shouldEqual(Number.Three, 3); + shouldEqual(Number.Four, 4); +} + +fn shouldEqual(n: Number, expected: u3) void { + assertOrPanic(@enumToInt(n) == expected); +} + +test "int to enum" { + testIntToEnumEval(3); +} +fn testIntToEnumEval(x: i32) void { + assertOrPanic(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); +} +const IntToEnumNumber = enum { + Zero, + One, + Two, + Three, + Four, +}; + +test "@tagName" { + assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + comptime assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); +} + +fn testEnumTagNameBare(n: BareNumber) []const u8 { + return @tagName(n); +} + +const BareNumber = enum { + One, + Two, + Three, +}; + +test "enum alignment" { + comptime { + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf(u64)); + } +} + +const AlignTestEnum = union(enum) { + A: [9]u8, + B: u64, +}; + +const ValueCount1 = enum { + I0, +}; +const ValueCount2 = enum { + I0, + I1, +}; +const ValueCount256 = enum { + I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17, + I18, + I19, + I20, + I21, + I22, + I23, + I24, + I25, + I26, + I27, + I28, + I29, + I30, + I31, + I32, + I33, + I34, + I35, + I36, + I37, + I38, + I39, + I40, + I41, + I42, + I43, + I44, + I45, + I46, + I47, + I48, + I49, + I50, + I51, + I52, + I53, + I54, + I55, + I56, + I57, + I58, + I59, + I60, + I61, + I62, + I63, + I64, + I65, + I66, + I67, + I68, + I69, + I70, + I71, + I72, + I73, + I74, + I75, + I76, + I77, + I78, + I79, + I80, + I81, + I82, + I83, + I84, + I85, + I86, + I87, + I88, + I89, + I90, + I91, + I92, + I93, + I94, + I95, + I96, + I97, + I98, + I99, + I100, + I101, + I102, + I103, + I104, + I105, + I106, + I107, + I108, + I109, + I110, + I111, + I112, + I113, + I114, + I115, + I116, + I117, + I118, + I119, + I120, + I121, + I122, + I123, + I124, + I125, + I126, + I127, + I128, + I129, + I130, + I131, + I132, + I133, + I134, + I135, + I136, + I137, + I138, + I139, + I140, + I141, + I142, + I143, + I144, + I145, + I146, + I147, + I148, + I149, + I150, + I151, + I152, + I153, + I154, + I155, + I156, + I157, + I158, + I159, + I160, + I161, + I162, + I163, + I164, + I165, + I166, + I167, + I168, + I169, + I170, + I171, + I172, + I173, + I174, + I175, + I176, + I177, + I178, + I179, + I180, + I181, + I182, + I183, + I184, + I185, + I186, + I187, + I188, + I189, + I190, + I191, + I192, + I193, + I194, + I195, + I196, + I197, + I198, + I199, + I200, + I201, + I202, + I203, + I204, + I205, + I206, + I207, + I208, + I209, + I210, + I211, + I212, + I213, + I214, + I215, + I216, + I217, + I218, + I219, + I220, + I221, + I222, + I223, + I224, + I225, + I226, + I227, + I228, + I229, + I230, + I231, + I232, + I233, + I234, + I235, + I236, + I237, + I238, + I239, + I240, + I241, + I242, + I243, + I244, + I245, + I246, + I247, + I248, + I249, + I250, + I251, + I252, + I253, + I254, + I255, +}; +const ValueCount257 = enum { + I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17, + I18, + I19, + I20, + I21, + I22, + I23, + I24, + I25, + I26, + I27, + I28, + I29, + I30, + I31, + I32, + I33, + I34, + I35, + I36, + I37, + I38, + I39, + I40, + I41, + I42, + I43, + I44, + I45, + I46, + I47, + I48, + I49, + I50, + I51, + I52, + I53, + I54, + I55, + I56, + I57, + I58, + I59, + I60, + I61, + I62, + I63, + I64, + I65, + I66, + I67, + I68, + I69, + I70, + I71, + I72, + I73, + I74, + I75, + I76, + I77, + I78, + I79, + I80, + I81, + I82, + I83, + I84, + I85, + I86, + I87, + I88, + I89, + I90, + I91, + I92, + I93, + I94, + I95, + I96, + I97, + I98, + I99, + I100, + I101, + I102, + I103, + I104, + I105, + I106, + I107, + I108, + I109, + I110, + I111, + I112, + I113, + I114, + I115, + I116, + I117, + I118, + I119, + I120, + I121, + I122, + I123, + I124, + I125, + I126, + I127, + I128, + I129, + I130, + I131, + I132, + I133, + I134, + I135, + I136, + I137, + I138, + I139, + I140, + I141, + I142, + I143, + I144, + I145, + I146, + I147, + I148, + I149, + I150, + I151, + I152, + I153, + I154, + I155, + I156, + I157, + I158, + I159, + I160, + I161, + I162, + I163, + I164, + I165, + I166, + I167, + I168, + I169, + I170, + I171, + I172, + I173, + I174, + I175, + I176, + I177, + I178, + I179, + I180, + I181, + I182, + I183, + I184, + I185, + I186, + I187, + I188, + I189, + I190, + I191, + I192, + I193, + I194, + I195, + I196, + I197, + I198, + I199, + I200, + I201, + I202, + I203, + I204, + I205, + I206, + I207, + I208, + I209, + I210, + I211, + I212, + I213, + I214, + I215, + I216, + I217, + I218, + I219, + I220, + I221, + I222, + I223, + I224, + I225, + I226, + I227, + I228, + I229, + I230, + I231, + I232, + I233, + I234, + I235, + I236, + I237, + I238, + I239, + I240, + I241, + I242, + I243, + I244, + I245, + I246, + I247, + I248, + I249, + I250, + I251, + I252, + I253, + I254, + I255, + I256, +}; + +test "enum sizes" { + comptime { + assertOrPanic(@sizeOf(ValueCount1) == 0); + assertOrPanic(@sizeOf(ValueCount2) == 1); + assertOrPanic(@sizeOf(ValueCount256) == 1); + assertOrPanic(@sizeOf(ValueCount257) == 2); + } +} + +const Small2 = enum(u2) { + One, + Two, +}; +const Small = enum(u2) { + One, + Two, + Three, + Four, +}; + +test "set enum tag type" { + { + var x = Small.One; + x = Small.Two; + comptime assertOrPanic(@TagType(Small) == u2); + } + { + var x = Small2.One; + x = Small2.Two; + comptime assertOrPanic(@TagType(Small2) == u2); + } +} + +const A = enum(u3) { + One, + Two, + Three, + Four, + One2, + Two2, + Three2, + Four2, +}; + +const B = enum(u3) { + One3, + Two3, + Three3, + Four3, + One23, + Two23, + Three23, + Four23, +}; + +const C = enum(u2) { + One4, + Two4, + Three4, + Four4, +}; + +const BitFieldOfEnums = packed struct { + a: A, + b: B, + c: C, +}; + +const bit_field_1 = BitFieldOfEnums{ + .a = A.Two, + .b = B.Three3, + .c = C.Four4, +}; + +test "bit field access with enum fields" { + var data = bit_field_1; + assertOrPanic(getA(&data) == A.Two); + assertOrPanic(getB(&data) == B.Three3); + assertOrPanic(getC(&data) == C.Four4); + comptime assertOrPanic(@sizeOf(BitFieldOfEnums) == 1); + + data.b = B.Four3; + assertOrPanic(data.b == B.Four3); + + data.a = A.Three; + assertOrPanic(data.a == A.Three); + assertOrPanic(data.b == B.Four3); +} + +fn getA(data: *const BitFieldOfEnums) A { + return data.a; +} + +fn getB(data: *const BitFieldOfEnums) B { + return data.b; +} + +fn getC(data: *const BitFieldOfEnums) C { + return data.c; +} + +test "casting enum to its tag type" { + testCastEnumToTagType(Small2.Two); + comptime testCastEnumToTagType(Small2.Two); +} + +fn testCastEnumToTagType(value: Small2) void { + assertOrPanic(@enumToInt(value) == 1); +} + From 81c3bbd245e87b80d8a9e76bdd9fb553810f23f9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 1 Jan 2019 19:45:09 -0500 Subject: [PATCH 134/190] add error for trying to store runtime value in comptime type --- src/analyze.cpp | 41 +++++++++++++++++++++++++++++++++++++++ src/analyze.hpp | 7 +++++++ src/codegen.cpp | 2 +- src/ir.cpp | 51 +++++++++++++++++++++++++++++++------------------ 4 files changed, 81 insertions(+), 20 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 3cd05d8f08b4..b7e5ebf9f2cc 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5137,6 +5137,7 @@ bool fn_eval_eql(Scope *a, Scope *b) { return false; } +// Whether the type has bits at runtime. bool type_has_bits(ZigType *type_entry) { assert(type_entry); assert(!type_is_invalid(type_entry)); @@ -5144,6 +5145,46 @@ bool type_has_bits(ZigType *type_entry) { return !type_entry->zero_bits; } +// Whether you can infer the value based solely on the type. +OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { + assert(type_entry != nullptr); + Error err; + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) + return OnePossibleValueInvalid; + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdOpaque: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdMetaType: + case ZigTypeIdNamespace: + case ZigTypeIdBoundFn: + case ZigTypeIdArgTuple: + case ZigTypeIdOptional: + case ZigTypeIdFn: + case ZigTypeIdBool: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdPromise: + return OnePossibleValueNo; + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdVoid: + case ZigTypeIdUnreachable: + return OnePossibleValueYes; + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdUnion: + case ZigTypeIdErrorUnion: + case ZigTypeIdPointer: + case ZigTypeIdEnum: + case ZigTypeIdErrorSet: + return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes; + } + zig_unreachable(); +} + ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) { Error err; if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) diff --git a/src/analyze.hpp b/src/analyze.hpp index b506b533caf9..aa3839969402 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -222,4 +222,11 @@ enum ReqCompTime { }; ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry); +enum OnePossibleValue { + OnePossibleValueInvalid, + OnePossibleValueNo, + OnePossibleValueYes, +}; +OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 83cee790ecd6..3dc2f605ccad 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6512,7 +6512,7 @@ static void do_code_gen(CodeGen *g) { assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; if (type_has_bits(child_type) && instruction->base.value.special == ConstValSpecialRuntime && - child_type != g->builtin_types.entry_infer) + child_type != g->builtin_types.entry_infer && instruction->base.ref_count != 0) { instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, get_ptr_align(g, ptr_type)); diff --git a/src/ir.cpp b/src/ir.cpp index 9194fc43d729..d4d5cb154187 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7733,8 +7733,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeFnCallExpr: return ir_gen_fn_call(irb, scope, node, lval, result_loc); case NodeTypeIfBoolExpr: - return ir_gen_if_bool_expr(irb, scope, node, lval, - ensure_result_loc(irb, scope, node, result_loc)); + return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc); case NodeTypeContainerInitExpr: @@ -7787,11 +7786,9 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeNullLiteral: return ir_gen_null_literal(irb, scope, node, lval, result_loc); case NodeTypeIfErrorExpr: - return ir_gen_if_err_expr(irb, scope, node, lval, - ensure_result_loc(irb, scope, node, result_loc)); + return ir_gen_if_err_expr(irb, scope, node, lval, result_loc); case NodeTypeIfOptional: - return ir_gen_if_optional_expr(irb, scope, node, lval, - ensure_result_loc(irb, scope, node, result_loc)); + return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc); case NodeTypeSwitchExpr: return ir_gen_switch_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); @@ -12810,19 +12807,18 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->invalid_instruction; - bool requires_comptime; - switch (type_requires_comptime(ira->codegen, resolved_type)) { - case ReqCompTimeYes: - requires_comptime = true; + bool one_possible_value; + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + one_possible_value = true; break; - case ReqCompTimeNo: - requires_comptime = false; + case OnePossibleValueNo: + one_possible_value = false; break; - case ReqCompTimeInvalid: - return ira->codegen->invalid_instruction; } - bool one_possible_value = !requires_comptime && !type_has_bits(resolved_type); if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) @@ -14440,10 +14436,16 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return ira->codegen->invalid_instruction; if (dest_val->special != ConstValSpecialRuntime) { *dest_val = value->value; - if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr && - type_has_bits(child_type)) - { - ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; + if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { + switch (type_has_one_possible_value(ira->codegen, child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; + break; + case OnePossibleValueYes: + break; + } } if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { ptr->value.data.x_ptr.mut = ConstPtrMutComptimeConst; @@ -14467,6 +14469,17 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source } } + switch (type_requires_comptime(ira->codegen, child_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_instruction; + case ReqCompTimeYes: + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name))); + return ira->codegen->invalid_instruction; + case ReqCompTimeNo: + break; + } + IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, ptr, value); result->value.type = ira->codegen->builtin_types.entry_void; From c54b0d509a72ee84c2155dfade478896543cfd45 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 1 Jan 2019 20:46:11 -0500 Subject: [PATCH 135/190] copy elision: while expressions support no-copy error unions / optionals --- src/ir.cpp | 78 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index d4d5cb154187..b19d8d343284 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5825,7 +5825,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrBasicBlock *continue_block = continue_expr_node ? ir_create_basic_block(irb, scope, "WhileContinue") : cond_block; IrBasicBlock *end_block = ir_create_basic_block(irb, scope, "WhileEnd"); - IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "WhileElse"); + IrBasicBlock *else_block = else_node ? + ir_create_basic_block(irb, scope, "WhileElse") : end_block; IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline); @@ -5858,6 +5859,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *opt_err_code = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, ptr_opt_err_code, nullptr); IrInstruction *is_err = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, opt_err_code); + IrBasicBlock *after_cond_block = irb->current_basic_block; + assert(else_node != nullptr); + IrInstruction *void_else_result = ir_mark_gen(ir_build_const_void(irb, scope, node)); if (!instr_is_unreachable(is_err)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err, @@ -5885,7 +5889,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n loop_scope->incoming_values = &incoming_values; IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, - LValNone, result_loc); + lval, result_loc); if (body_result == irb->codegen->invalid_instruction) return body_result; @@ -5904,7 +5908,6 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } ir_set_cursor_at_end_and_append_block(irb, else_block); - assert(else_node != nullptr); // TODO make it an error to write to error variable AstNode *err_symbol_node = else_node; // TODO make more accurate @@ -5915,14 +5918,23 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ptr_opt_err_code, false); ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, unwrapped_err_code_ptr); - IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope, LValNone, result_loc); + IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope, lval, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); + IrBasicBlock *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_result(irb, scope, node, lval, result_loc); + if (else_result) { + incoming_blocks.append(after_else_block); + incoming_values.append(else_result); + } else { + incoming_blocks.append(after_cond_block); + incoming_values.append(void_else_result); + } + + return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else if (var_symbol != nullptr) { ir_set_cursor_at_end_and_append_block(irb, cond_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); @@ -5938,6 +5950,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n return maybe_val_ptr; IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val); + IrBasicBlock *after_cond_block = irb->current_basic_block; + IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); if (!instr_is_unreachable(is_non_null)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null, body_block, else_block, is_comptime, result_loc)); @@ -5961,7 +5975,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n loop_scope->incoming_values = &incoming_values; IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, - LValNone, result_loc); + lval, result_loc); if (body_result == irb->codegen->invalid_instruction) return body_result; @@ -5979,26 +5993,35 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime)); } - ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_result = nullptr; if (else_node) { - else_result = ir_gen_node(irb, else_node, scope, LValNone, result_loc); + ir_set_cursor_at_end_and_append_block(irb, else_block); + + else_result = ir_gen_node(irb, else_node, scope, lval, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; - } else { - else_result = ir_build_const_void(irb, scope, node); - ir_build_store_ptr(irb, scope, node, result_loc, else_result); + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); + IrBasicBlock *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_result(irb, scope, node, lval, result_loc); + if (else_result) { + incoming_blocks.append(after_else_block); + incoming_values.append(else_result); + } else { + incoming_blocks.append(after_cond_block); + incoming_values.append(void_else_result); + } + + return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else { ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope, LValNone, nullptr); if (cond_val == irb->codegen->invalid_instruction) return cond_val; + IrBasicBlock *after_cond_block = irb->current_basic_block; + IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); if (!instr_is_unreachable(cond_val)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val, body_block, else_block, is_comptime, result_loc)); @@ -6020,7 +6043,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n loop_scope->incoming_values = &incoming_values; IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, - LValNone, result_loc); + lval, result_loc); if (body_result == irb->codegen->invalid_instruction) return body_result; @@ -6038,21 +6061,27 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime)); } - ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_result = nullptr; if (else_node) { - else_result = ir_gen_node(irb, else_node, subexpr_scope, LValNone, result_loc); + ir_set_cursor_at_end_and_append_block(irb, else_block); + + else_result = ir_gen_node(irb, else_node, subexpr_scope, lval, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; - } else { - else_result = ir_build_const_void(irb, subexpr_scope, node); - ir_build_store_ptr(irb, subexpr_scope, node, result_loc, else_result); + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); + IrBasicBlock *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_result(irb, scope, node, lval, result_loc); + if (else_result) { + incoming_blocks.append(after_else_block); + incoming_values.append(else_result); + } else { + incoming_blocks.append(after_cond_block); + incoming_values.append(void_else_result); + } + return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } } @@ -7742,8 +7771,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeVariableDeclaration: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_var_decl(irb, scope, node)); case NodeTypeWhileExpr: - return ir_gen_while_expr(irb, scope, node, lval, - ensure_result_loc(irb, scope, node, result_loc)); + return ir_gen_while_expr(irb, scope, node, lval, result_loc); case NodeTypeForExpr: return ir_gen_for_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); case NodeTypeArrayAccessExpr: From b168b8110525c601f31c6c936cb9a44fe999062a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 Jan 2019 00:46:38 -0500 Subject: [PATCH 136/190] copy elision: for loops support no-copy error unions / optionals --- src/ir.cpp | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index b19d8d343284..c3a7760f540b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6148,7 +6148,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrBasicBlock *cond_block = ir_create_basic_block(irb, child_scope, "ForCond"); IrBasicBlock *body_block = ir_create_basic_block(irb, child_scope, "ForBody"); IrBasicBlock *end_block = ir_create_basic_block(irb, child_scope, "ForEnd"); - IrBasicBlock *else_block = ir_create_basic_block(irb, child_scope, "ForElse"); + IrBasicBlock *else_block = else_node ? ir_create_basic_block(irb, child_scope, "ForElse") : end_block; IrBasicBlock *continue_block = ir_create_basic_block(irb, child_scope, "ForContinue"); Buf *len_field_name = buf_create_from_str("len"); @@ -6159,6 +6159,8 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_set_cursor_at_end_and_append_block(irb, cond_block); IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca, nullptr); IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); + IrBasicBlock *after_cond_block = irb->current_basic_block; + IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime, result_loc)); ir_set_cursor_at_end_and_append_block(irb, body_block); @@ -6177,7 +6179,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; - IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base, LValNone, result_loc); + IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base, lval, result_loc); if (!instr_is_unreachable(body_result)) ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); @@ -6187,21 +6189,27 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_alloca, new_index_val)); ir_build_br(irb, child_scope, node, cond_block, is_comptime); - ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_result = nullptr; if (else_node) { - else_result = ir_gen_node(irb, else_node, parent_scope, LValNone, result_loc); + ir_set_cursor_at_end_and_append_block(irb, else_block); + + else_result = ir_gen_node(irb, else_node, parent_scope, lval, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; - } else { - else_result = ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); - ir_mark_gen(ir_build_store_ptr(irb, parent_scope, node, result_loc, else_result)); + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); } - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); - + IrBasicBlock *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_result(irb, parent_scope, node, lval, result_loc); + + if (else_result) { + incoming_blocks.append(after_else_block); + incoming_values.append(else_result); + } else { + incoming_blocks.append(after_cond_block); + incoming_values.append(void_else_value); + } + return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -7773,7 +7781,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeWhileExpr: return ir_gen_while_expr(irb, scope, node, lval, result_loc); case NodeTypeForExpr: - return ir_gen_for_expr(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); + return ir_gen_for_expr(irb, scope, node, lval, result_loc); case NodeTypeArrayAccessExpr: return ir_gen_array_access(irb, scope, node, lval, result_loc); case NodeTypeReturnExpr: From 7cbd7ab966552ba3e71de9ade82dab00cc8e75be Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 Jan 2019 01:16:55 -0500 Subject: [PATCH 137/190] copy elision: switch support no-copy error unions / optionals --- src/ir.cpp | 19 +++--- test/behavior.zig | 1 - test/cases/enum.zig | 124 ---------------------------------- test/stage1/behavior/enum.zig | 121 +++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 133 deletions(-) delete mode 100644 test/cases/enum.zig diff --git a/src/ir.cpp b/src/ir.cpp index c3a7760f540b..9cb230497fcd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6544,7 +6544,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit IrBasicBlock *end_block, IrInstruction *is_comptime, IrInstruction *var_is_comptime, IrInstruction *target_value_ptr, IrInstruction *prong_value, ZigList *incoming_blocks, ZigList *incoming_values, - IrInstruction *result_loc) + LVal lval, IrInstruction *result_loc) { assert(switch_node->type == NodeTypeSwitchExpr); assert(prong_node->type == NodeTypeSwitchProng); @@ -6573,7 +6573,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit child_scope = scope; } - IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope, LValNone, result_loc); + IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope, lval, result_loc); if (expr_result == irb->codegen->invalid_instruction) return false; if (!instr_is_unreachable(expr_result)) @@ -6635,7 +6635,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * 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, &incoming_blocks, &incoming_values, - result_loc)) + lval, result_loc)) { return irb->codegen->invalid_instruction; } @@ -6703,7 +6703,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * ir_set_cursor_at_end_and_append_block(irb, range_block_yes); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values, - result_loc)) + lval, result_loc)) { return irb->codegen->invalid_instruction; } @@ -6748,7 +6748,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * ir_set_cursor_at_end_and_append_block(irb, prong_block); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values, - result_loc)) + lval, result_loc)) { return irb->codegen->invalid_instruction; } @@ -6774,7 +6774,11 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * ir_set_cursor_at_end_and_append_block(irb, end_block); assert(incoming_blocks.length == incoming_values.length); - return ir_gen_result(irb, scope, node, lval, result_loc); + if (incoming_blocks.length == 0) { + return ir_build_const_void(irb, scope, node); + } else { + return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); + } } static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, @@ -7826,8 +7830,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeIfOptional: return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc); case NodeTypeSwitchExpr: - return ir_gen_switch_expr(irb, scope, node, lval, - ensure_result_loc(irb, scope, node, result_loc)); + return ir_gen_switch_expr(irb, scope, node, lval, result_loc); case NodeTypeCompTime: return ir_gen_comptime(irb, scope, node, lval, result_loc); case NodeTypeErrorType: diff --git a/test/behavior.zig b/test/behavior.zig index 9052832b9233..d66f5ef4ee59 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -9,7 +9,6 @@ comptime { _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); _ = @import("cases/defer.zig"); - _ = @import("cases/enum.zig"); _ = @import("cases/enum_with_members.zig"); _ = @import("cases/error.zig"); _ = @import("cases/eval.zig"); diff --git a/test/cases/enum.zig b/test/cases/enum.zig deleted file mode 100644 index 73c9166d4cb5..000000000000 --- a/test/cases/enum.zig +++ /dev/null @@ -1,124 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; -const mem = @import("std").mem; - -const MultipleChoice = enum(u32) { - A = 20, - B = 40, - C = 60, - D = 1000, -}; - -test "enum with specified tag values" { - testEnumWithSpecifiedTagValues(MultipleChoice.C); - comptime testEnumWithSpecifiedTagValues(MultipleChoice.C); -} - -fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { - assertOrPanic(@enumToInt(x) == 60); - assertOrPanic(1234 == switch (x) { - MultipleChoice.A => 1, - MultipleChoice.B => 2, - MultipleChoice.C => u32(1234), - MultipleChoice.D => 4, - }); -} - -const MultipleChoice2 = enum(u32) { - Unspecified1, - A = 20, - Unspecified2, - B = 40, - Unspecified3, - C = 60, - Unspecified4, - D = 1000, - Unspecified5, -}; - -test "enum with specified and unspecified tag values" { - testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); - comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); -} - -fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assertOrPanic(@enumToInt(x) == 1000); - assertOrPanic(1234 == switch (x) { - MultipleChoice2.A => 1, - MultipleChoice2.B => 2, - MultipleChoice2.C => 3, - MultipleChoice2.D => u32(1234), - MultipleChoice2.Unspecified1 => 5, - MultipleChoice2.Unspecified2 => 6, - MultipleChoice2.Unspecified3 => 7, - MultipleChoice2.Unspecified4 => 8, - MultipleChoice2.Unspecified5 => 9, - }); -} - -test "cast integer literal to enum" { - assertOrPanic(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); - assertOrPanic(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); -} - -const EnumWithOneMember = enum { - Eof, -}; - -fn doALoopThing(id: EnumWithOneMember) void { - while (true) { - if (id == EnumWithOneMember.Eof) { - break; - } - @compileError("above if condition should be comptime"); - } -} - -test "comparison operator on enum with one member is comptime known" { - doALoopThing(EnumWithOneMember.Eof); -} - -const State = enum { - Start, -}; -test "switch on enum with one member is comptime known" { - var state = State.Start; - switch (state) { - State.Start => return, - } - @compileError("analysis should not reach here"); -} - -const EnumWithTagValues = enum(u4) { - A = 1 << 0, - B = 1 << 1, - C = 1 << 2, - D = 1 << 3, -}; -test "enum with tag values don't require parens" { - assertOrPanic(@enumToInt(EnumWithTagValues.C) == 0b0100); -} - -test "enum with 1 field but explicit tag type should still have the tag type" { - const Enum = enum(u8) { - B = 2, - }; - comptime @import("std").debug.assertOrPanic(@sizeOf(Enum) == @sizeOf(u8)); -} - -test "empty extern enum with members" { - const E = extern enum { - A, - B, - C, - }; - assertOrPanic(@sizeOf(E) == @sizeOf(c_int)); -} - -test "aoeu" { - const LocalFoo = enum { - A = 1, - B = 0, - }; - var b = LocalFoo.B; - assertOrPanic(mem.eql(u8, @tagName(b), "B")); -} diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index 6583d54d3bfc..9de138ef78cd 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -771,3 +771,124 @@ fn testCastEnumToTagType(value: Small2) void { assertOrPanic(@enumToInt(value) == 1); } +const MultipleChoice = enum(u32) { + A = 20, + B = 40, + C = 60, + D = 1000, +}; + +test "enum with specified tag values" { + testEnumWithSpecifiedTagValues(MultipleChoice.C); + comptime testEnumWithSpecifiedTagValues(MultipleChoice.C); +} + +fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { + assertOrPanic(@enumToInt(x) == 60); + assertOrPanic(1234 == switch (x) { + MultipleChoice.A => 1, + MultipleChoice.B => 2, + MultipleChoice.C => u32(1234), + MultipleChoice.D => 4, + }); +} + +const MultipleChoice2 = enum(u32) { + Unspecified1, + A = 20, + Unspecified2, + B = 40, + Unspecified3, + C = 60, + Unspecified4, + D = 1000, + Unspecified5, +}; + +test "enum with specified and unspecified tag values" { + testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); + comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); +} + +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { + assertOrPanic(@enumToInt(x) == 1000); + assertOrPanic(1234 == switch (x) { + MultipleChoice2.A => 1, + MultipleChoice2.B => 2, + MultipleChoice2.C => 3, + MultipleChoice2.D => u32(1234), + MultipleChoice2.Unspecified1 => 5, + MultipleChoice2.Unspecified2 => 6, + MultipleChoice2.Unspecified3 => 7, + MultipleChoice2.Unspecified4 => 8, + MultipleChoice2.Unspecified5 => 9, + }); +} + +test "cast integer literal to enum" { + assertOrPanic(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); + assertOrPanic(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); +} + +const EnumWithOneMember = enum { + Eof, +}; + +fn doALoopThing(id: EnumWithOneMember) void { + while (true) { + if (id == EnumWithOneMember.Eof) { + break; + } + @compileError("above if condition should be comptime"); + } +} + +test "comparison operator on enum with one member is comptime known" { + doALoopThing(EnumWithOneMember.Eof); +} + +const State = enum { + Start, +}; +test "switch on enum with one member is comptime known" { + var state = State.Start; + switch (state) { + State.Start => return, + } + @compileError("analysis should not reach here"); +} + +const EnumWithTagValues = enum(u4) { + A = 1 << 0, + B = 1 << 1, + C = 1 << 2, + D = 1 << 3, +}; +test "enum with tag values don't require parens" { + assertOrPanic(@enumToInt(EnumWithTagValues.C) == 0b0100); +} + +test "enum with 1 field but explicit tag type should still have the tag type" { + const Enum = enum(u8) { + B = 2, + }; + comptime @import("std").debug.assertOrPanic(@sizeOf(Enum) == @sizeOf(u8)); +} + +test "empty extern enum with members" { + const E = extern enum { + A, + B, + C, + }; + assertOrPanic(@sizeOf(E) == @sizeOf(c_int)); +} + +test "tag name with assigned enum values" { + const LocalFoo = enum { + A = 1, + B = 0, + }; + var b = LocalFoo.B; + assertOrPanic(mem.eql(u8, @tagName(b), "B")); +} From 937a60f4565c2115b25d77f2a8a27f09c31b9cfb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 Jan 2019 01:38:19 -0500 Subject: [PATCH 138/190] copy elision: move passing tests --- test/behavior.zig | 3 - test/cases/cast.zig | 197 ++++++------------ test/cases/enum_with_members.zig | 10 +- test/cases/error.zig | 66 ++---- test/cases/null.zig | 34 +-- .../cases/struct_contains_slice_of_itself.zig | 14 +- test/cases/switch.zig | 140 ++----------- test/cases/switch_prong_err_enum.zig | 6 +- test/cases/try.zig | 10 +- test/cases/union.zig | 108 ++++------ test/stage1/behavior.zig | 12 ++ test/{cases => stage1/behavior}/bugs/1277.zig | 0 test/{cases => stage1/behavior}/bugs/1322.zig | 4 +- test/{cases => stage1/behavior}/bugs/828.zig | 0 test/stage1/behavior/cast.zig | 72 +++++++ test/stage1/behavior/enum_with_members.zig | 4 + test/stage1/behavior/error.zig | 45 ++++ test/stage1/behavior/null.zig | 2 + .../struct_contains_slice_of_itself.zig | 2 + test/stage1/behavior/switch.zig | 106 ++++++++++ .../stage1/behavior/switch_prong_err_enum.zig | 2 + test/stage1/behavior/try.zig | 2 + test/stage1/behavior/union.zig | 37 ++++ 23 files changed, 455 insertions(+), 421 deletions(-) rename test/{cases => stage1/behavior}/bugs/1277.zig (100%) rename test/{cases => stage1/behavior}/bugs/1322.zig (66%) rename test/{cases => stage1/behavior}/bugs/828.zig (100%) create mode 100644 test/stage1/behavior/cast.zig create mode 100644 test/stage1/behavior/enum_with_members.zig create mode 100644 test/stage1/behavior/error.zig create mode 100644 test/stage1/behavior/null.zig create mode 100644 test/stage1/behavior/struct_contains_slice_of_itself.zig create mode 100644 test/stage1/behavior/switch.zig create mode 100644 test/stage1/behavior/switch_prong_err_enum.zig create mode 100644 test/stage1/behavior/try.zig create mode 100644 test/stage1/behavior/union.zig diff --git a/test/behavior.zig b/test/behavior.zig index d66f5ef4ee59..a7b926d4d774 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,7 +1,4 @@ comptime { - _ = @import("cases/bugs/1277.zig"); - _ = @import("cases/bugs/1322.zig"); - _ = @import("cases/bugs/828.zig"); _ = @import("cases/bugs/920.zig"); _ = @import("cases/cancel.zig"); _ = @import("cases/cast.zig"); diff --git a/test/cases/cast.zig b/test/cases/cast.zig index bd45bbc00f08..b0cb9de1e11a 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -1,75 +1,8 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; -test "int to ptr cast" { - const x = usize(13); - const y = @intToPtr(*u8, x); - const z = @ptrToInt(y); - assert(z == 13); -} - -test "integer literal to pointer cast" { - const vga_mem = @intToPtr(*u16, 0xB8000); - assert(@ptrToInt(vga_mem) == 0xB8000); -} - -test "pointer reinterpret const float to int" { - const float: f64 = 5.99999999999994648725e-01; - const float_ptr = &float; - const int_ptr = @ptrCast(*const i32, float_ptr); - const int_val = int_ptr.*; - assert(int_val == 858993411); -} - -test "implicitly cast indirect pointer to maybe-indirect pointer" { - const S = struct { - const Self = @This(); - x: u8, - fn constConst(p: *const *const Self) u8 { - return p.*.x; - } - fn maybeConstConst(p: ?*const *const Self) u8 { - return p.?.*.x; - } - fn constConstConst(p: *const *const *const Self) u8 { - return p.*.*.x; - } - fn maybeConstConstConst(p: ?*const *const *const Self) u8 { - return p.?.*.*.x; - } - }; - const s = S{ .x = 42 }; - const p = &s; - const q = &p; - const r = &q; - assert(42 == S.constConst(q)); - assert(42 == S.maybeConstConst(q)); - assert(42 == S.constConstConst(r)); - assert(42 == S.maybeConstConstConst(r)); -} - -test "explicit cast from integer to error type" { - testCastIntToErr(error.ItBroke); - comptime testCastIntToErr(error.ItBroke); -} -fn testCastIntToErr(err: anyerror) void { - const x = @errorToInt(err); - const y = @intToError(x); - assert(error.ItBroke == y); -} - -test "peer resolve arrays of different size to const slice" { - assert(mem.eql(u8, boolToStr(true), "true")); - assert(mem.eql(u8, boolToStr(false), "false")); - comptime assert(mem.eql(u8, boolToStr(true), "true")); - comptime assert(mem.eql(u8, boolToStr(false), "false")); -} -fn boolToStr(b: bool) []const u8 { - return if (b) "true" else "false"; -} - test "peer resolve array and const slice" { testPeerResolveArrayConstSlice(true); comptime testPeerResolveArrayConstSlice(true); @@ -77,8 +10,8 @@ test "peer resolve array and const slice" { fn testPeerResolveArrayConstSlice(b: bool) void { const value1 = if (b) "aoeu" else ([]const u8)("zz"); const value2 = if (b) ([]const u8)("zz") else "aoeu"; - assert(mem.eql(u8, value1, "aoeu")); - assert(mem.eql(u8, value2, "zz")); + assertOrPanic(mem.eql(u8, value1, "aoeu")); + assertOrPanic(mem.eql(u8, value2, "zz")); } test "implicitly cast from T to anyerror!?T" { @@ -91,14 +24,14 @@ const A = struct { fn castToOptionalTypeError(z: i32) void { const x = i32(1); const y: anyerror!?i32 = x; - assert((try y).? == 1); + assertOrPanic((try y).? == 1); const f = z; const g: anyerror!?i32 = f; const a = A{ .a = z }; const b: anyerror!?A = a; - assert((b catch unreachable).?.a == 1); + assertOrPanic((b catch unreachable).?.a == 1); } test "implicitly cast from int to anyerror!?T" { @@ -113,7 +46,7 @@ fn implicitIntLitToOptional() void { test "return null from fn() anyerror!?&T" { const a = returnNullFromOptionalTypeErrorRef(); const b = returnNullLitFromOptionalTypeErrorRef(); - assert((try a) == null and (try b) == null); + assertOrPanic((try a) == null and (try b) == null); } fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { const a: ?*A = null; @@ -124,11 +57,11 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { } test "peer type resolution: ?T and T" { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); comptime { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); } } fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { @@ -140,11 +73,11 @@ fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { } test "peer type resolution: [0]u8 and []const u8" { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); comptime { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); } } fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { @@ -156,8 +89,8 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { } test "implicitly cast from [N]T to ?[]const T" { - assert(mem.eql(u8, castToOptionalSlice().?, "hi")); - comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi")); + assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); + comptime assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); } fn castToOptionalSlice() ?[]const u8 { @@ -170,7 +103,7 @@ test "implicitly cast from [0]T to anyerror![]T" { } fn testCastZeroArrayToErrSliceMut() void { - assert((gimmeErrOrSlice() catch unreachable).len == 0); + assertOrPanic((gimmeErrOrSlice() catch unreachable).len == 0); } fn gimmeErrOrSlice() anyerror![]u8 { @@ -181,14 +114,14 @@ test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { { var data = "hi"; const slice = data[0..]; - assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } comptime { var data = "hi"; const slice = data[0..]; - assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } } fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { @@ -206,7 +139,7 @@ test "resolve undefined with integer" { fn testResolveUndefWithInt(b: bool, x: i32) void { const value = if (b) x else undefined; if (b) { - assert(value == x); + assertOrPanic(value == x); } } @@ -218,17 +151,17 @@ test "implicit cast from &const [N]T to []const T" { fn testCastConstArrayRefToConstSlice() void { const blah = "aoeu"; const const_array_ref = &blah; - assert(@typeOf(const_array_ref) == *const [4]u8); + assertOrPanic(@typeOf(const_array_ref) == *const [4]u8); const slice: []const u8 = const_array_ref; - assert(mem.eql(u8, slice, "aoeu")); + assertOrPanic(mem.eql(u8, slice, "aoeu")); } test "peer type resolution: error and [N]T" { // TODO: implicit error!T to error!U where T can implicitly cast to U - //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - //comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); - comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); + //assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + //comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); + comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); } //fn testPeerErrorAndArray(x: u8) error![]const u8 { @@ -252,9 +185,9 @@ test "@floatToInt" { fn testFloatToInts() void { const x = i32(1e4); - assert(x == 10000); + assertOrPanic(x == 10000); const y = @floatToInt(i32, f32(1e4)); - assert(y == 10000); + assertOrPanic(y == 10000); expectFloatToInt(f16, 255.1, u8, 255); expectFloatToInt(f16, 127.2, i8, 127); expectFloatToInt(f16, -128.2, i8, -128); @@ -265,7 +198,7 @@ fn testFloatToInts() void { } fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { - assert(@floatToInt(I, f) == i); + assertOrPanic(@floatToInt(I, f) == i); } test "cast u128 to f128 and back" { @@ -274,7 +207,7 @@ test "cast u128 to f128 and back" { } fn testCast128() void { - assert(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); + assertOrPanic(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); } fn cast128Int(x: f128) u128 { @@ -294,9 +227,9 @@ test "const slice widen cast" { }; const u32_value = @bytesToSlice(u32, bytes[0..])[0]; - assert(u32_value == 0x12121212); + assertOrPanic(u32_value == 0x12121212); - assert(@bitCast(u32, bytes) == 0x12121212); + assertOrPanic(@bitCast(u32, bytes) == 0x12121212); } test "single-item pointer of array to slice and to unknown length pointer" { @@ -308,76 +241,76 @@ fn testCastPtrOfArrayToSliceAndPtr() void { var array = "aoeu"; const x: [*]u8 = &array; x[0] += 1; - assert(mem.eql(u8, array[0..], "boeu")); + assertOrPanic(mem.eql(u8, array[0..], "boeu")); const y: []u8 = &array; y[0] += 1; - assert(mem.eql(u8, array[0..], "coeu")); + assertOrPanic(mem.eql(u8, array[0..], "coeu")); } test "cast *[1][*]const u8 to [*]const ?[*]const u8" { const window_name = [1][*]const u8{c"window name"}; const x: [*]const ?[*]const u8 = &window_name; - assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); + assertOrPanic(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); } test "@intCast comptime_int" { const result = @intCast(i32, 1234); - assert(@typeOf(result) == i32); - assert(result == 1234); + assertOrPanic(@typeOf(result) == i32); + assertOrPanic(result == 1234); } test "@floatCast comptime_int and comptime_float" { { const result = @floatCast(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f16, 1234.0); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f32, 1234.0); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } } test "comptime_int @intToFloat" { { const result = @intToFloat(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @intToFloat(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } } test "@bytesToSlice keeps pointer alignment" { var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; const numbers = @bytesToSlice(u32, bytes[0..]); - comptime assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); + comptime assertOrPanic(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); } test "@intCast i32 to u7" { var x: u128 = maxInt(u128); var y: i32 = 120; var z = x >> @intCast(u7, y); - assert(z == 0xff); + assertOrPanic(z == 0xff); } test "implicit cast undefined to optional" { - assert(MakeType(void).getNull() == null); - assert(MakeType(void).getNonNull() != null); + assertOrPanic(MakeType(void).getNull() == null); + assertOrPanic(MakeType(void).getNonNull() != null); } fn MakeType(comptime T: type) type { @@ -397,16 +330,16 @@ test "implicit cast from *[N]T to ?[*]T" { var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; x = &y; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); + assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); x.?[0] = 8; y[3] = 6; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); + assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); } test "implicit cast from *T to ?*c_void" { var a: u8 = 1; incrementVoidPtrValue(&a); - std.debug.assert(a == 2); + std.debug.assertOrPanic(a == 2); } fn incrementVoidPtrValue(value: ?*c_void) void { @@ -416,7 +349,7 @@ fn incrementVoidPtrValue(value: ?*c_void) void { test "implicit cast from [*]T to ?*c_void" { var a = []u8{ 3, 2, 1 }; incrementVoidPtrArray(a[0..].ptr, 3); - assert(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); + assertOrPanic(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); } fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { @@ -440,27 +373,27 @@ pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); pub const PFN_void = extern fn (*c_void) void; fn foobar(func: PFN_void) void { - std.debug.assert(@ptrToInt(func) == maxInt(usize)); + std.debug.assertOrPanic(@ptrToInt(func) == maxInt(usize)); } test "implicit ptr to *c_void" { var a: u32 = 1; var ptr: *c_void = &a; var b: *u32 = @ptrCast(*u32, ptr); - assert(b.* == 1); + assertOrPanic(b.* == 1); var ptr2: ?*c_void = &a; var c: *u32 = @ptrCast(*u32, ptr2.?); - assert(c.* == 1); + assertOrPanic(c.* == 1); } test "@intCast to comptime_int" { - assert(@intCast(comptime_int, 0) == 0); + assertOrPanic(@intCast(comptime_int, 0) == 0); } test "implicit cast comptime numbers to any type when the value fits" { const a: u64 = 255; var b: u8 = a; - assert(b == 255); + assertOrPanic(b == 255); } test "@intToEnum passed a comptime_int to an enum with one item" { @@ -468,5 +401,5 @@ test "@intToEnum passed a comptime_int to an enum with one item" { A, }; const x = @intToEnum(E, 0); - assert(x == E.A); + assertOrPanic(x == E.A); } diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 088496bd2f41..49af1ceae78e 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; const fmt = @import("std").fmt; @@ -19,9 +19,9 @@ test "enum with members" { const b = ET{ .UINT = 42 }; var buf: [20]u8 = undefined; - assert((a.print(buf[0..]) catch unreachable) == 3); - assert(mem.eql(u8, buf[0..3], "-42")); + assertOrPanic((a.print(buf[0..]) catch unreachable) == 3); + assertOrPanic(mem.eql(u8, buf[0..3], "-42")); - assert((b.print(buf[0..]) catch unreachable) == 2); - assert(mem.eql(u8, buf[0..2], "42")); + assertOrPanic((b.print(buf[0..]) catch unreachable) == 2); + assertOrPanic(mem.eql(u8, buf[0..2], "42")); } diff --git a/test/cases/error.zig b/test/cases/error.zig index a731f39021f7..d8ba8d804f95 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -1,53 +1,13 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const builtin = @import("builtin"); -pub fn foo() anyerror!i32 { - const x = try bar(); - return x + 1; -} - -pub fn bar() anyerror!i32 { - return 13; -} - -pub fn baz() anyerror!i32 { - const y = foo() catch 1234; - return y + 1; -} - -test "error wrapping" { - assert((baz() catch unreachable) == 15); -} - -fn gimmeItBroke() []const u8 { - return @errorName(error.ItBroke); -} - -test "@errorName" { - assert(mem.eql(u8, @errorName(error.AnError), "AnError")); - assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); -} - -test "error values" { - const a = @errorToInt(error.err1); - const b = @errorToInt(error.err2); - assert(a != b); -} - -test "redefinition of error values allowed" { - shouldBeNotEqual(error.AnError, error.SecondError); -} -fn shouldBeNotEqual(a: anyerror, b: anyerror) void { - if (a == b) unreachable; -} - test "error binary operator" { const a = errBinaryOperatorG(true) catch 3; const b = errBinaryOperatorG(false) catch 3; - assert(a == 3); - assert(b == 10); + assertOrPanic(a == 3); + assertOrPanic(b == 10); } fn errBinaryOperatorG(x: bool) anyerror!isize { return if (x) error.ItBroke else isize(10); @@ -55,7 +15,7 @@ fn errBinaryOperatorG(x: bool) anyerror!isize { test "unwrap simple value from error" { const i = unwrapSimpleValueFromErrorDo() catch unreachable; - assert(i == 13); + assertOrPanic(i == 13); } fn unwrapSimpleValueFromErrorDo() anyerror!isize { return 13; @@ -81,10 +41,10 @@ test "error union type " { fn testErrorUnionType() void { const x: anyerror!i32 = 1234; - if (x) |value| assert(value == 1234) else |_| unreachable; - assert(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); - assert(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); - assert(@typeOf(x).ErrorSet == anyerror); + if (x) |value| assertOrPanic(value == 1234) else |_| unreachable; + assertOrPanic(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); + assertOrPanic(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); + assertOrPanic(@typeOf(x).ErrorSet == anyerror); } test "error set type " { @@ -98,12 +58,12 @@ const MyErrSet = error{ }; fn testErrorSetType() void { - assert(@memberCount(MyErrSet) == 2); + assertOrPanic(@memberCount(MyErrSet) == 2); const a: MyErrSet!i32 = 5678; const b: MyErrSet!i32 = MyErrSet.OutOfMemory; - if (a) |value| assert(value == 5678) else |err| switch (err) { + if (a) |value| assertOrPanic(value == 5678) else |err| switch (err) { error.OutOfMemory => unreachable, error.FileNotFound => unreachable, } @@ -126,7 +86,7 @@ const Set2 = error{ fn testExplicitErrorSetCast(set1: Set1) void { var x = @errSetCast(Set2, set1); var y = @errSetCast(Set1, x); - assert(y == error.A); + assertOrPanic(y == error.A); } test "comptime test error for empty error set" { @@ -137,12 +97,12 @@ test "comptime test error for empty error set" { const EmptyErrorSet = error{}; fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { - if (x) |v| assert(v == 1234) else |err| @compileError("bad"); + if (x) |v| assertOrPanic(v == 1234) else |err| @compileError("bad"); } test "syntax: optional operator in front of error union operator" { comptime { - assert(?(anyerror!i32) == ?(anyerror!i32)); + assertOrPanic(?(anyerror!i32) == ?(anyerror!i32)); } } diff --git a/test/cases/null.zig b/test/cases/null.zig index 825db88b1e58..e2f86a05ba44 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "optional type" { const x: ?bool = true; @@ -17,13 +17,13 @@ test "optional type" { const z = next_x orelse 1234; - assert(z == 1234); + assertOrPanic(z == 1234); const final_x: ?i32 = 13; const num = final_x orelse unreachable; - assert(num == 13); + assertOrPanic(num == 13); } test "test maybe object and get a pointer to the inner value" { @@ -33,7 +33,7 @@ test "test maybe object and get a pointer to the inner value" { b.* = false; } - assert(maybe_bool.? == false); + assertOrPanic(maybe_bool.? == false); } test "rhs maybe unwrap return" { @@ -47,9 +47,9 @@ test "maybe return" { } fn maybeReturnImpl() void { - assert(foo(1235).?); + assertOrPanic(foo(1235).?); if (foo(null) != null) unreachable; - assert(!foo(1234).?); + assertOrPanic(!foo(1234).?); } fn foo(x: ?i32) ?bool { @@ -58,7 +58,7 @@ fn foo(x: ?i32) ?bool { } test "if var maybe pointer" { - assert(shouldBeAPlus1(Particle{ + assertOrPanic(shouldBeAPlus1(Particle{ .a = 14, .b = 1, .c = 1, @@ -84,10 +84,10 @@ const Particle = struct { test "null literal outside function" { const is_null = here_is_a_null_literal.context == null; - assert(is_null); + assertOrPanic(is_null); const is_non_null = here_is_a_null_literal.context != null; - assert(!is_non_null); + assertOrPanic(!is_non_null); } const SillyStruct = struct { context: ?i32, @@ -98,8 +98,8 @@ test "test null runtime" { testTestNullRuntime(null); } fn testTestNullRuntime(x: ?i32) void { - assert(x == null); - assert(!(x != null)); + assertOrPanic(x == null); + assertOrPanic(!(x != null)); } test "optional void" { @@ -108,8 +108,8 @@ test "optional void" { } fn optionalVoidImpl() void { - assert(bar(null) == null); - assert(bar({}) != null); + assertOrPanic(bar(null) == null); + assertOrPanic(bar({}) != null); } fn bar(x: ?void) ?void { @@ -133,7 +133,7 @@ test "unwrap optional which is field of global var" { } struct_with_optional.field = 1234; if (struct_with_optional.field) |payload| { - assert(payload == 1234); + assertOrPanic(payload == 1234); } else { unreachable; } @@ -141,13 +141,13 @@ test "unwrap optional which is field of global var" { test "null with default unwrap" { const x: i32 = null orelse 1; - assert(x == 1); + assertOrPanic(x == 1); } test "optional types" { comptime { const opt_type_struct = StructWithOptionalType{ .t = u8 }; - assert(opt_type_struct.t != null and opt_type_struct.t.? == u8); + assertOrPanic(opt_type_struct.t != null and opt_type_struct.t.? == u8); } } @@ -158,5 +158,5 @@ const StructWithOptionalType = struct { test "optional pointer to 0 bit type null value at runtime" { const EmptyStruct = struct {}; var x: ?*EmptyStruct = null; - assert(x == null); + assertOrPanic(x == null); } diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig index 07987ae32be3..3cdfcda729b6 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/cases/struct_contains_slice_of_itself.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Node = struct { payload: i32, @@ -34,10 +34,10 @@ test "struct contains slice of itself" { .payload = 1234, .children = nodes[0..], }; - assert(root.payload == 1234); - assert(root.children[0].payload == 1); - assert(root.children[1].payload == 2); - assert(root.children[2].payload == 3); - assert(root.children[2].children[0].payload == 31); - assert(root.children[2].children[1].payload == 32); + assertOrPanic(root.payload == 1234); + assertOrPanic(root.children[0].payload == 1); + assertOrPanic(root.children[1].payload == 2); + assertOrPanic(root.children[2].payload == 3); + assertOrPanic(root.children[2].children[0].payload == 31); + assertOrPanic(root.children[2].children[1].payload == 32); } diff --git a/test/cases/switch.zig b/test/cases/switch.zig index d5258f0bb145..ec040e2fd16d 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -1,108 +1,4 @@ -const assert = @import("std").debug.assert; - -test "switch with numbers" { - testSwitchWithNumbers(13); -} - -fn testSwitchWithNumbers(x: u32) void { - const result = switch (x) { - 1, 2, 3, 4...8 => false, - 13 => true, - else => false, - }; - assert(result); -} - -test "switch with all ranges" { - assert(testSwitchWithAllRanges(50, 3) == 1); - assert(testSwitchWithAllRanges(101, 0) == 2); - assert(testSwitchWithAllRanges(300, 5) == 3); - assert(testSwitchWithAllRanges(301, 6) == 6); -} - -fn testSwitchWithAllRanges(x: u32, y: u32) u32 { - return switch (x) { - 0...100 => 1, - 101...200 => 2, - 201...300 => 3, - else => y, - }; -} - -test "implicit comptime switch" { - const x = 3 + 4; - const result = switch (x) { - 3 => 10, - 4 => 11, - 5, 6 => 12, - 7, 8 => 13, - else => 14, - }; - - comptime { - assert(result + 1 == 14); - } -} - -test "switch on enum" { - const fruit = Fruit.Orange; - nonConstSwitchOnEnum(fruit); -} -const Fruit = enum { - Apple, - Orange, - Banana, -}; -fn nonConstSwitchOnEnum(fruit: Fruit) void { - switch (fruit) { - Fruit.Apple => unreachable, - Fruit.Orange => {}, - Fruit.Banana => unreachable, - } -} - -test "switch statement" { - nonConstSwitch(SwitchStatmentFoo.C); -} -fn nonConstSwitch(foo: SwitchStatmentFoo) void { - const val = switch (foo) { - SwitchStatmentFoo.A => i32(1), - SwitchStatmentFoo.B => 2, - SwitchStatmentFoo.C => 3, - SwitchStatmentFoo.D => 4, - }; - assert(val == 3); -} -const SwitchStatmentFoo = enum { - A, - B, - C, - D, -}; - -test "switch prong with variable" { - switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 }); - switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 }); - switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} }); -} -const SwitchProngWithVarEnum = union(enum) { - One: i32, - Two: f32, - Meh: void, -}; -fn switchProngWithVarFn(a: SwitchProngWithVarEnum) void { - switch (a) { - SwitchProngWithVarEnum.One => |x| { - assert(x == 13); - }, - SwitchProngWithVarEnum.Two => |x| { - assert(x == 13.0); - }, - SwitchProngWithVarEnum.Meh => |x| { - const v: void = x; - }, - } -} +const assertOrPanic = @import("std").debug.assertOrPanic; test "switch on enum using pointer capture" { testSwitchEnumPtrCapture(); @@ -116,7 +12,7 @@ fn testSwitchEnumPtrCapture() void { else => unreachable, } switch (value) { - SwitchProngWithVarEnum.One => |x| assert(x == 1235), + SwitchProngWithVarEnum.One => |x| assertOrPanic(x == 1235), else => unreachable, } } @@ -127,7 +23,7 @@ test "switch with multiple expressions" { 4, 5, 6 => 2, else => i32(3), }; - assert(x == 2); + assertOrPanic(x == 2); } fn returnsFive() i32 { return 5; @@ -149,12 +45,12 @@ fn returnsFalse() bool { } } test "switch on const enum with var" { - assert(!returnsFalse()); + assertOrPanic(!returnsFalse()); } test "switch on type" { - assert(trueIfBoolFalseOtherwise(bool)); - assert(!trueIfBoolFalseOtherwise(i32)); + assertOrPanic(trueIfBoolFalseOtherwise(bool)); + assertOrPanic(!trueIfBoolFalseOtherwise(i32)); } fn trueIfBoolFalseOtherwise(comptime T: type) bool { @@ -170,16 +66,16 @@ test "switch handles all cases of number" { } fn testSwitchHandleAllCases() void { - assert(testSwitchHandleAllCasesExhaustive(0) == 3); - assert(testSwitchHandleAllCasesExhaustive(1) == 2); - assert(testSwitchHandleAllCasesExhaustive(2) == 1); - assert(testSwitchHandleAllCasesExhaustive(3) == 0); + assertOrPanic(testSwitchHandleAllCasesExhaustive(0) == 3); + assertOrPanic(testSwitchHandleAllCasesExhaustive(1) == 2); + assertOrPanic(testSwitchHandleAllCasesExhaustive(2) == 1); + assertOrPanic(testSwitchHandleAllCasesExhaustive(3) == 0); - assert(testSwitchHandleAllCasesRange(100) == 0); - assert(testSwitchHandleAllCasesRange(200) == 1); - assert(testSwitchHandleAllCasesRange(201) == 2); - assert(testSwitchHandleAllCasesRange(202) == 4); - assert(testSwitchHandleAllCasesRange(230) == 3); + assertOrPanic(testSwitchHandleAllCasesRange(100) == 0); + assertOrPanic(testSwitchHandleAllCasesRange(200) == 1); + assertOrPanic(testSwitchHandleAllCasesRange(201) == 2); + assertOrPanic(testSwitchHandleAllCasesRange(202) == 4); + assertOrPanic(testSwitchHandleAllCasesRange(230) == 3); } fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { @@ -207,8 +103,8 @@ test "switch all prongs unreachable" { } fn testAllProngsUnreachable() void { - assert(switchWithUnreachable(1) == 2); - assert(switchWithUnreachable(2) == 10); + assertOrPanic(switchWithUnreachable(1) == 2); + assertOrPanic(switchWithUnreachable(2) == 10); } fn switchWithUnreachable(x: i32) i32 { @@ -230,5 +126,5 @@ test "capture value of switch with all unreachable prongs" { const x = return_a_number() catch |err| switch (err) { else => unreachable, }; - assert(x == 1); + assertOrPanic(x == 1); } diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig index 89060690fc01..6ac1919f0d5a 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/cases/switch_prong_err_enum.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; var read_count: u64 = 0; @@ -22,9 +22,9 @@ fn doThing(form_id: u64) anyerror!FormValue { test "switch prong returns error enum" { switch (doThing(17) catch unreachable) { FormValue.Address => |payload| { - assert(payload == 1); + assertOrPanic(payload == 1); }, else => unreachable, } - assert(read_count == 1); + assertOrPanic(read_count == 1); } diff --git a/test/cases/try.zig b/test/cases/try.zig index 450a9af6ac0b..ed48875eb466 100644 --- a/test/cases/try.zig +++ b/test/cases/try.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "try on error union" { tryOnErrorUnionImpl(); @@ -11,7 +11,7 @@ fn tryOnErrorUnionImpl() void { error.CrappedOut => i32(2), else => unreachable, }; - assert(x == 11); + assertOrPanic(x == 11); } fn returnsTen() anyerror!i32 { @@ -20,10 +20,10 @@ fn returnsTen() anyerror!i32 { test "try without vars" { const result1 = if (failIfTrue(true)) 1 else |_| i32(2); - assert(result1 == 2); + assertOrPanic(result1 == 2); const result2 = if (failIfTrue(false)) 1 else |_| i32(2); - assert(result2 == 1); + assertOrPanic(result2 == 1); } fn failIfTrue(ok: bool) anyerror!void { @@ -38,6 +38,6 @@ test "try then not executed with assignment" { if (failIfTrue(true)) { unreachable; } else |err| { - assert(err == error.ItBroke); + assertOrPanic(err == error.ItBroke); } } diff --git a/test/cases/union.zig b/test/cases/union.zig index 019a7012daf0..e08c7b0d2e02 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -1,40 +1,4 @@ -const assert = @import("std").debug.assert; - -const Value = union(enum) { - Int: u64, - Array: [9]u8, -}; - -const Agg = struct { - val1: Value, - val2: Value, -}; - -const v1 = Value{ .Int = 1234 }; -const v2 = Value{ .Array = []u8{3} ** 9 }; - -const err = (anyerror!Agg)(Agg{ - .val1 = v1, - .val2 = v2, -}); - -const array = []Value{ - v1, - v2, - v1, - v2, -}; - -test "unions embedded in aggregate types" { - switch (array[1]) { - Value.Array => |arr| assert(arr[4] == 3), - else => unreachable, - } - switch ((err catch unreachable).val1) { - Value.Int => |x| assert(x == 1234), - else => unreachable, - } -} +const assertOrPanic = @import("std").debug.assertOrPanic; const Foo = union { float: f64, @@ -43,18 +7,18 @@ const Foo = union { test "basic unions" { var foo = Foo{ .int = 1 }; - assert(foo.int == 1); + assertOrPanic(foo.int == 1); foo = Foo{ .float = 12.34 }; - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); } test "comptime union field access" { comptime { var foo = Foo{ .int = 0 }; - assert(foo.int == 0); + assertOrPanic(foo.int == 0); foo = Foo{ .float = 42.42 }; - assert(foo.float == 42.42); + assertOrPanic(foo.float == 42.42); } } @@ -62,10 +26,10 @@ test "init union with runtime value" { var foo: Foo = undefined; setFloat(&foo, 12.34); - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); setInt(&foo, 42); - assert(foo.int == 42); + assertOrPanic(foo.int == 42); } fn setFloat(foo: *Foo, x: f64) void { @@ -83,9 +47,9 @@ const FooExtern = extern union { test "basic extern unions" { var foo = FooExtern{ .int = 1 }; - assert(foo.int == 1); + assertOrPanic(foo.int == 1); foo.float = 12.34; - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); } const Letter = enum { @@ -105,11 +69,11 @@ test "union with specified enum tag" { } fn doTest() void { - assert(bar(Payload{ .A = 1234 }) == -10); + assertOrPanic(bar(Payload{ .A = 1234 }) == -10); } fn bar(value: Payload) i32 { - assert(Letter(value) == Letter.A); + assertOrPanic(Letter(value) == Letter.A); return switch (value) { Payload.A => |x| return x - 1244, Payload.B => |x| if (x == 12.34) i32(20) else 21, @@ -125,8 +89,8 @@ const MultipleChoice = union(enum(u32)) { }; test "simple union(enum(u32))" { var x = MultipleChoice.C; - assert(x == MultipleChoice.C); - assert(@enumToInt(@TagType(MultipleChoice)(x)) == 60); + assertOrPanic(x == MultipleChoice.C); + assertOrPanic(@enumToInt(@TagType(MultipleChoice)(x)) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -142,14 +106,14 @@ const MultipleChoice2 = union(enum(u32)) { }; test "union(enum(u32)) with specified and unspecified tag values" { - comptime assert(@TagType(@TagType(MultipleChoice2)) == u32); + comptime assertOrPanic(@TagType(@TagType(MultipleChoice2)) == u32); testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); - assert(1123 == switch (x) { + assertOrPanic(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); + assertOrPanic(1123 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => |v| i32(1000) + v, @@ -167,7 +131,7 @@ const ExternPtrOrInt = extern union { int: u64, }; test "extern union size" { - comptime assert(@sizeOf(ExternPtrOrInt) == 8); + comptime assertOrPanic(@sizeOf(ExternPtrOrInt) == 8); } const PackedPtrOrInt = packed union { @@ -175,14 +139,14 @@ const PackedPtrOrInt = packed union { int: u64, }; test "extern union size" { - comptime assert(@sizeOf(PackedPtrOrInt) == 8); + comptime assertOrPanic(@sizeOf(PackedPtrOrInt) == 8); } const ZeroBits = union { OnlyField: void, }; test "union with only 1 field which is void should be zero bits" { - comptime assert(@sizeOf(ZeroBits) == 0); + comptime assertOrPanic(@sizeOf(ZeroBits) == 0); } const TheTag = enum { @@ -196,9 +160,9 @@ const TheUnion = union(TheTag) { C: i32, }; test "union field access gives the enum values" { - assert(TheUnion.A == TheTag.A); - assert(TheUnion.B == TheTag.B); - assert(TheUnion.C == TheTag.C); + assertOrPanic(TheUnion.A == TheTag.A); + assertOrPanic(TheUnion.B == TheTag.B); + assertOrPanic(TheUnion.C == TheTag.C); } test "cast union to tag type of union" { @@ -207,12 +171,12 @@ test "cast union to tag type of union" { } fn testCastUnionToTagType(x: TheUnion) void { - assert(TheTag(x) == TheTag.B); + assertOrPanic(TheTag(x) == TheTag.B); } test "cast tag type of union to union" { var x: Value2 = Letter2.B; - assert(Letter2(x) == Letter2.B); + assertOrPanic(Letter2(x) == Letter2.B); } const Letter2 = enum { A, @@ -227,11 +191,11 @@ const Value2 = union(Letter2) { test "implicit cast union to its tag type" { var x: Value2 = Letter2.B; - assert(x == Letter2.B); + assertOrPanic(x == Letter2.B); giveMeLetterB(x); } fn giveMeLetterB(x: Letter2) void { - assert(x == Value2.B); + assertOrPanic(x == Value2.B); } pub const PackThis = union(enum) { @@ -244,7 +208,7 @@ test "constant packed union" { } fn testConstPackedUnion(expected_tokens: []const PackThis) void { - assert(expected_tokens[0].StringLiteral == 1); + assertOrPanic(expected_tokens[0].StringLiteral == 1); } test "switch on union with only 1 field" { @@ -256,7 +220,7 @@ test "switch on union with only 1 field" { z = PartialInstWithPayload{ .Compiled = 1234 }; switch (z) { PartialInstWithPayload.Compiled => |x| { - assert(x == 1234); + assertOrPanic(x == 1234); return; }, } @@ -282,11 +246,11 @@ test "access a member of tagged union with conflicting enum tag name" { const B = void; }; - comptime assert(Bar.A == u8); + comptime assertOrPanic(Bar.A == u8); } test "tagged union initialization with runtime void" { - assert(testTaggedUnionInit({})); + assertOrPanic(testTaggedUnionInit({})); } const TaggedUnionWithAVoid = union(enum) { @@ -324,9 +288,9 @@ test "union with only 1 field casted to its enum type" { var e = Expr{ .Literal = Literal{ .Bool = true } }; const Tag = @TagType(Expr); - comptime assert(@TagType(Tag) == comptime_int); + comptime assertOrPanic(@TagType(Tag) == comptime_int); var t = Tag(e); - assert(t == Expr.Literal); + assertOrPanic(t == Expr.Literal); } test "union with only 1 field casted to its enum type which has enum value specified" { @@ -344,9 +308,9 @@ test "union with only 1 field casted to its enum type which has enum value speci }; var e = Expr{ .Literal = Literal{ .Bool = true } }; - comptime assert(@TagType(Tag) == comptime_int); + comptime assertOrPanic(@TagType(Tag) == comptime_int); var t = Tag(e); - assert(t == Expr.Literal); - assert(@enumToInt(t) == 33); - comptime assert(@enumToInt(t) == 33); + assertOrPanic(t == Expr.Literal); + assertOrPanic(@enumToInt(t) == 33); + comptime assertOrPanic(@enumToInt(t) == 33); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index d94aec423352..82673623e782 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -11,6 +11,9 @@ comptime { _ = @import("behavior/bugs/1076.zig"); _ = @import("behavior/bugs/1111.zig"); _ = @import("behavior/bugs/1381.zig"); + _ = @import("behavior/bugs/1322.zig"); + _ = @import("behavior/bugs/828.zig"); + _ = @import("behavior/bugs/1277.zig"); _ = @import("behavior/bugs/1421.zig"); _ = @import("behavior/bugs/1442.zig"); _ = @import("behavior/bugs/1486.zig"); @@ -56,4 +59,13 @@ comptime { _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); _ = @import("behavior/optional.zig"); + _ = @import("behavior/switch.zig"); + _ = @import("behavior/null.zig"); + _ = @import("behavior/union.zig"); + _ = @import("behavior/error.zig"); + _ = @import("behavior/cast.zig"); + _ = @import("behavior/try.zig"); + _ = @import("behavior/enum_with_members.zig"); + _ = @import("behavior/struct_contains_slice_of_itself.zig"); + _ = @import("behavior/switch_prong_err_enum.zig"); } diff --git a/test/cases/bugs/1277.zig b/test/stage1/behavior/bugs/1277.zig similarity index 100% rename from test/cases/bugs/1277.zig rename to test/stage1/behavior/bugs/1277.zig diff --git a/test/cases/bugs/1322.zig b/test/stage1/behavior/bugs/1322.zig similarity index 66% rename from test/cases/bugs/1322.zig rename to test/stage1/behavior/bugs/1322.zig index 2de92191ec17..2e67f4473fec 100644 --- a/test/cases/bugs/1322.zig +++ b/test/stage1/behavior/bugs/1322.zig @@ -13,7 +13,7 @@ const C = struct {}; test "tagged union with all void fields but a meaningful tag" { var a: A = A{ .b = B{ .c = C{} } }; - std.debug.assert(@TagType(B)(a.b) == @TagType(B).c); + std.debug.assertOrPanic(@TagType(B)(a.b) == @TagType(B).c); a = A{ .b = B.None }; - std.debug.assert(@TagType(B)(a.b) == @TagType(B).None); + std.debug.assertOrPanic(@TagType(B)(a.b) == @TagType(B).None); } diff --git a/test/cases/bugs/828.zig b/test/stage1/behavior/bugs/828.zig similarity index 100% rename from test/cases/bugs/828.zig rename to test/stage1/behavior/bugs/828.zig diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig new file mode 100644 index 000000000000..e68ac9e24039 --- /dev/null +++ b/test/stage1/behavior/cast.zig @@ -0,0 +1,72 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const mem = std.mem; +const maxInt = std.math.maxInt; + +test "int to ptr cast" { + const x = usize(13); + const y = @intToPtr(*u8, x); + const z = @ptrToInt(y); + assertOrPanic(z == 13); +} + +test "integer literal to pointer cast" { + const vga_mem = @intToPtr(*u16, 0xB8000); + assertOrPanic(@ptrToInt(vga_mem) == 0xB8000); +} + +test "pointer reinterpret const float to int" { + const float: f64 = 5.99999999999994648725e-01; + const float_ptr = &float; + const int_ptr = @ptrCast(*const i32, float_ptr); + const int_val = int_ptr.*; + assertOrPanic(int_val == 858993411); +} + +test "implicitly cast indirect pointer to maybe-indirect pointer" { + const S = struct { + const Self = @This(); + x: u8, + fn constConst(p: *const *const Self) u8 { + return p.*.x; + } + fn maybeConstConst(p: ?*const *const Self) u8 { + return p.?.*.x; + } + fn constConstConst(p: *const *const *const Self) u8 { + return p.*.*.x; + } + fn maybeConstConstConst(p: ?*const *const *const Self) u8 { + return p.?.*.*.x; + } + }; + const s = S{ .x = 42 }; + const p = &s; + const q = &p; + const r = &q; + assertOrPanic(42 == S.constConst(q)); + assertOrPanic(42 == S.maybeConstConst(q)); + assertOrPanic(42 == S.constConstConst(r)); + assertOrPanic(42 == S.maybeConstConstConst(r)); +} + +test "explicit cast from integer to error type" { + testCastIntToErr(error.ItBroke); + comptime testCastIntToErr(error.ItBroke); +} +fn testCastIntToErr(err: anyerror) void { + const x = @errorToInt(err); + const y = @intToError(x); + assertOrPanic(error.ItBroke == y); +} + +test "peer resolve arrays of different size to const slice" { + assertOrPanic(mem.eql(u8, boolToStr(true), "true")); + assertOrPanic(mem.eql(u8, boolToStr(false), "false")); + comptime assertOrPanic(mem.eql(u8, boolToStr(true), "true")); + comptime assertOrPanic(mem.eql(u8, boolToStr(false), "false")); +} +fn boolToStr(b: bool) []const u8 { + return if (b) "true" else "false"; +} + diff --git a/test/stage1/behavior/enum_with_members.zig b/test/stage1/behavior/enum_with_members.zig new file mode 100644 index 000000000000..20f14c40c08e --- /dev/null +++ b/test/stage1/behavior/enum_with_members.zig @@ -0,0 +1,4 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; +const fmt = @import("std").fmt; + diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig new file mode 100644 index 000000000000..756ae324e7ea --- /dev/null +++ b/test/stage1/behavior/error.zig @@ -0,0 +1,45 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const mem = std.mem; +const builtin = @import("builtin"); + +pub fn foo() anyerror!i32 { + const x = try bar(); + return x + 1; +} + +pub fn bar() anyerror!i32 { + return 13; +} + +pub fn baz() anyerror!i32 { + const y = foo() catch 1234; + return y + 1; +} + +test "error wrapping" { + assertOrPanic((baz() catch unreachable) == 15); +} + +fn gimmeItBroke() []const u8 { + return @errorName(error.ItBroke); +} + +test "@errorName" { + assertOrPanic(mem.eql(u8, @errorName(error.AnError), "AnError")); + assertOrPanic(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); +} + +test "error values" { + const a = @errorToInt(error.err1); + const b = @errorToInt(error.err2); + assertOrPanic(a != b); +} + +test "redefinition of error values allowed" { + shouldBeNotEqual(error.AnError, error.SecondError); +} +fn shouldBeNotEqual(a: anyerror, b: anyerror) void { + if (a == b) unreachable; +} + diff --git a/test/stage1/behavior/null.zig b/test/stage1/behavior/null.zig new file mode 100644 index 000000000000..41c447315af6 --- /dev/null +++ b/test/stage1/behavior/null.zig @@ -0,0 +1,2 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + diff --git a/test/stage1/behavior/struct_contains_slice_of_itself.zig b/test/stage1/behavior/struct_contains_slice_of_itself.zig new file mode 100644 index 000000000000..41c447315af6 --- /dev/null +++ b/test/stage1/behavior/struct_contains_slice_of_itself.zig @@ -0,0 +1,2 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + diff --git a/test/stage1/behavior/switch.zig b/test/stage1/behavior/switch.zig new file mode 100644 index 000000000000..0c3cca67a79d --- /dev/null +++ b/test/stage1/behavior/switch.zig @@ -0,0 +1,106 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "switch with numbers" { + testSwitchWithNumbers(13); +} + +fn testSwitchWithNumbers(x: u32) void { + const result = switch (x) { + 1, 2, 3, 4...8 => false, + 13 => true, + else => false, + }; + assertOrPanic(result); +} + +test "switch with all ranges" { + assertOrPanic(testSwitchWithAllRanges(50, 3) == 1); + assertOrPanic(testSwitchWithAllRanges(101, 0) == 2); + assertOrPanic(testSwitchWithAllRanges(300, 5) == 3); + assertOrPanic(testSwitchWithAllRanges(301, 6) == 6); +} + +fn testSwitchWithAllRanges(x: u32, y: u32) u32 { + return switch (x) { + 0...100 => 1, + 101...200 => 2, + 201...300 => 3, + else => y, + }; +} + +test "implicit comptime switch" { + const x = 3 + 4; + const result = switch (x) { + 3 => 10, + 4 => 11, + 5, 6 => 12, + 7, 8 => 13, + else => 14, + }; + + comptime { + assertOrPanic(result + 1 == 14); + } +} + +test "switch on enum" { + const fruit = Fruit.Orange; + nonConstSwitchOnEnum(fruit); +} +const Fruit = enum { + Apple, + Orange, + Banana, +}; +fn nonConstSwitchOnEnum(fruit: Fruit) void { + switch (fruit) { + Fruit.Apple => unreachable, + Fruit.Orange => {}, + Fruit.Banana => unreachable, + } +} + +test "switch statement" { + nonConstSwitch(SwitchStatmentFoo.C); +} +fn nonConstSwitch(foo: SwitchStatmentFoo) void { + const val = switch (foo) { + SwitchStatmentFoo.A => i32(1), + SwitchStatmentFoo.B => 2, + SwitchStatmentFoo.C => 3, + SwitchStatmentFoo.D => 4, + }; + assertOrPanic(val == 3); +} +const SwitchStatmentFoo = enum { + A, + B, + C, + D, +}; + +test "switch prong with variable" { + switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 }); + switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 }); + switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} }); +} +const SwitchProngWithVarEnum = union(enum) { + One: i32, + Two: f32, + Meh: void, +}; +fn switchProngWithVarFn(a: SwitchProngWithVarEnum) void { + switch (a) { + SwitchProngWithVarEnum.One => |x| { + assertOrPanic(x == 13); + }, + SwitchProngWithVarEnum.Two => |x| { + assertOrPanic(x == 13.0); + }, + SwitchProngWithVarEnum.Meh => |x| { + const v: void = x; + }, + } +} + diff --git a/test/stage1/behavior/switch_prong_err_enum.zig b/test/stage1/behavior/switch_prong_err_enum.zig new file mode 100644 index 000000000000..41c447315af6 --- /dev/null +++ b/test/stage1/behavior/switch_prong_err_enum.zig @@ -0,0 +1,2 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + diff --git a/test/stage1/behavior/try.zig b/test/stage1/behavior/try.zig new file mode 100644 index 000000000000..41c447315af6 --- /dev/null +++ b/test/stage1/behavior/try.zig @@ -0,0 +1,2 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig new file mode 100644 index 000000000000..a32a4be374db --- /dev/null +++ b/test/stage1/behavior/union.zig @@ -0,0 +1,37 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +const Value = union(enum) { + Int: u64, + Array: [9]u8, +}; + +const Agg = struct { + val1: Value, + val2: Value, +}; + +const v1 = Value{ .Int = 1234 }; +const v2 = Value{ .Array = []u8{3} ** 9 }; + +const err = (anyerror!Agg)(Agg{ + .val1 = v1, + .val2 = v2, +}); + +const array = []Value{ + v1, + v2, + v1, + v2, +}; + +test "unions embedded in aggregate types" { + switch (array[1]) { + Value.Array => |arr| assertOrPanic(arr[4] == 3), + else => unreachable, + } + switch ((err catch unreachable).val1) { + Value.Int => |x| assertOrPanic(x == 1234), + else => unreachable, + } +} From d4f48e5e65f158ec031abba626475c9258ad5dec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 Jan 2019 13:56:18 -0500 Subject: [PATCH 139/190] test skeletons in place. change assert to assertOrPanic --- test/cases/bugs/920.zig | 2 +- test/cases/cancel.zig | 14 +++---- test/cases/const_slice_child.zig | 10 ++--- test/cases/coroutine_await_struct.zig | 6 +-- test/cases/coroutines.zig | 39 +++++++++---------- test/cases/ir_block_deps.zig | 6 +-- test/cases/switch_prong_implicit_cast.zig | 4 +- test/stage1/behavior.zig | 33 +++++++++------- test/stage1/behavior/bugs/920.zig | 5 +++ test/stage1/behavior/cancel.zig | 2 + test/stage1/behavior/const_slice_child.zig | 3 ++ .../behavior/coroutine_await_struct.zig | 4 ++ test/stage1/behavior/coroutines.zig | 7 ++++ test/stage1/behavior/ir_block_deps.zig | 2 + .../behavior/switch_prong_implicit_cast.zig | 2 + 15 files changed, 85 insertions(+), 54 deletions(-) create mode 100644 test/stage1/behavior/bugs/920.zig create mode 100644 test/stage1/behavior/cancel.zig create mode 100644 test/stage1/behavior/const_slice_child.zig create mode 100644 test/stage1/behavior/coroutine_await_struct.zig create mode 100644 test/stage1/behavior/coroutines.zig create mode 100644 test/stage1/behavior/ir_block_deps.zig create mode 100644 test/stage1/behavior/switch_prong_implicit_cast.zig diff --git a/test/cases/bugs/920.zig b/test/cases/bugs/920.zig index 2903f05a29ab..e29c5c4acf5a 100644 --- a/test/cases/bugs/920.zig +++ b/test/cases/bugs/920.zig @@ -60,6 +60,6 @@ test "bug 920 fixed" { }; for (NormalDist1.f) |_, i| { - std.debug.assert(NormalDist1.f[i] == NormalDist.f[i]); + std.debug.assertOrPanic(NormalDist1.f[i] == NormalDist.f[i]); } } diff --git a/test/cases/cancel.zig b/test/cases/cancel.zig index c0f74fd34f44..863da4bdb831 100644 --- a/test/cases/cancel.zig +++ b/test/cases/cancel.zig @@ -10,9 +10,9 @@ test "cancel forwards" { const p = async<&da.allocator> f1() catch unreachable; cancel p; - std.debug.assert(defer_f1); - std.debug.assert(defer_f2); - std.debug.assert(defer_f3); + std.debug.assertOrPanic(defer_f1); + std.debug.assertOrPanic(defer_f2); + std.debug.assertOrPanic(defer_f3); } async fn f1() void { @@ -47,10 +47,10 @@ test "cancel backwards" { const p = async<&da.allocator> b1() catch unreachable; cancel p; - std.debug.assert(defer_b1); - std.debug.assert(defer_b2); - std.debug.assert(defer_b3); - std.debug.assert(defer_b4); + std.debug.assertOrPanic(defer_b1); + std.debug.assertOrPanic(defer_b2); + std.debug.assertOrPanic(defer_b3); + std.debug.assertOrPanic(defer_b4); } async fn b1() void { diff --git a/test/cases/const_slice_child.zig b/test/cases/const_slice_child.zig index 07d02d5df06f..5b9b70a55801 100644 --- a/test/cases/const_slice_child.zig +++ b/test/cases/const_slice_child.zig @@ -1,5 +1,5 @@ const debug = @import("std").debug; -const assert = debug.assert; +const assertOrPanic = debug.assertOrPanic; var argv: [*]const [*]const u8 = undefined; @@ -15,10 +15,10 @@ test "const slice child" { } fn foo(args: [][]const u8) void { - assert(args.len == 3); - assert(streql(args[0], "one")); - assert(streql(args[1], "two")); - assert(streql(args[2], "three")); + assertOrPanic(args.len == 3); + assertOrPanic(streql(args[0], "one")); + assertOrPanic(streql(args[1], "two")); + assertOrPanic(streql(args[2], "three")); } fn bar(argc: usize) void { diff --git a/test/cases/coroutine_await_struct.zig b/test/cases/coroutine_await_struct.zig index 79168715d8e2..6ca2a301eca5 100644 --- a/test/cases/coroutine_await_struct.zig +++ b/test/cases/coroutine_await_struct.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const Foo = struct { x: i32, @@ -18,8 +18,8 @@ test "coroutine await struct" { await_seq('f'); resume await_a_promise; await_seq('i'); - assert(await_final_result.x == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); + assertOrPanic(await_final_result.x == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 89490ebc2c1c..b8dcf907d73a 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; var x: i32 = 1; @@ -9,9 +9,9 @@ test "create a coroutine and cancel it" { defer da.deinit(); const p = try async<&da.allocator> simpleAsyncFn(); - comptime assert(@typeOf(p) == promise->void); + comptime assertOrPanic(@typeOf(p) == promise->void); cancel p; - assert(x == 2); + assertOrPanic(x == 2); } async fn simpleAsyncFn() void { x += 1; @@ -31,7 +31,7 @@ test "coroutine suspend, resume, cancel" { cancel p; seq('g'); - assert(std.mem.eql(u8, points, "abcdefg")); + assertOrPanic(std.mem.eql(u8, points, "abcdefg")); } async fn testAsyncSeq() void { defer seq('e'); @@ -53,9 +53,9 @@ test "coroutine suspend with block" { defer da.deinit(); const p = try async<&da.allocator> testSuspendBlock(); - std.debug.assert(!result); + std.debug.assertOrPanic(!result); resume a_promise; - std.debug.assert(result); + std.debug.assertOrPanic(result); cancel p; } @@ -63,13 +63,13 @@ var a_promise: promise = undefined; var result = false; async fn testSuspendBlock() void { suspend { - comptime assert(@typeOf(@handle()) == promise->void); + comptime assertOrPanic(@typeOf(@handle()) == promise->void); a_promise = @handle(); } //Test to make sure that @handle() works as advertised (issue #1296) //var our_handle: promise = @handle(); - assert( a_promise == @handle() ); + assertOrPanic(a_promise == @handle()); result = true; } @@ -86,8 +86,8 @@ test "coroutine await" { await_seq('f'); resume await_a_promise; await_seq('i'); - assert(await_final_result == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); + assertOrPanic(await_final_result == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); @@ -123,8 +123,8 @@ test "coroutine await early return" { early_seq('a'); const p = async<&da.allocator> early_amain() catch @panic("out of memory"); early_seq('f'); - assert(early_final_result == 1234); - assert(std.mem.eql(u8, early_points, "abcdef")); + assertOrPanic(early_final_result == 1234); + assertOrPanic(std.mem.eql(u8, early_points, "abcdef")); } async fn early_amain() void { early_seq('b'); @@ -170,7 +170,7 @@ test "async function with dot syntax" { defer da.deinit(); const p = try async<&da.allocator> S.foo(); cancel p; - assert(S.y == 2); + assertOrPanic(S.y == 2); } test "async fn pointer in a struct field" { @@ -182,9 +182,9 @@ test "async fn pointer in a struct field" { var da = std.heap.DirectAllocator.init(); defer da.deinit(); const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; - assert(data == 2); + assertOrPanic(data == 2); cancel p; - assert(data == 4); + assertOrPanic(data == 4); } async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { defer y.* += 2; @@ -220,8 +220,7 @@ test "error return trace across suspend points - async return" { cancel p2; } -// TODO https://github.com/ziglang/zig/issues/760 -fn nonFailing() promise->(anyerror!void) { +fn nonFailing() (promise->anyerror!void) { return async suspendThenFail() catch unreachable; } async fn suspendThenFail() anyerror!void { @@ -230,9 +229,9 @@ async fn suspendThenFail() anyerror!void { } async fn printTrace(p: promise->(anyerror!void)) void { (await p) catch |e| { - std.debug.assert(e == error.Fail); + std.debug.assertOrPanic(e == error.Fail); if (@errorReturnTrace()) |trace| { - assert(trace.index == 1); + assertOrPanic(trace.index == 1); } else switch (builtin.mode) { builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"), builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {}, @@ -246,7 +245,7 @@ test "break from suspend" { var my_result: i32 = 1; const p = try async testBreakFromSuspend(&my_result); cancel p; - std.debug.assert(my_result == 2); + std.debug.assertOrPanic(my_result == 2); } async fn testBreakFromSuspend(my_result: *i32) void { suspend { diff --git a/test/cases/ir_block_deps.zig b/test/cases/ir_block_deps.zig index 5c1b18c00e45..bc61e11df7a2 100644 --- a/test/cases/ir_block_deps.zig +++ b/test/cases/ir_block_deps.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn foo(id: u64) !i32 { return switch (id) { @@ -16,6 +16,6 @@ fn getErrInt() anyerror!i32 { } test "ir block deps" { - assert((foo(1) catch unreachable) == 0); - assert((foo(2) catch unreachable) == 0); + assertOrPanic((foo(1) catch unreachable) == 0); + assertOrPanic((foo(2) catch unreachable) == 0); } diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig index 56d37e290f1c..4ca031e2e11b 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/cases/switch_prong_implicit_cast.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const FormValue = union(enum) { One: void, @@ -18,5 +18,5 @@ test "switch prong implicit cast" { FormValue.One => false, FormValue.Two => |x| x, }; - assert(result); + assertOrPanic(result); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 82673623e782..a0b8ea4fc215 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -10,10 +10,9 @@ comptime { _ = @import("behavior/bswap.zig"); _ = @import("behavior/bugs/1076.zig"); _ = @import("behavior/bugs/1111.zig"); - _ = @import("behavior/bugs/1381.zig"); - _ = @import("behavior/bugs/1322.zig"); - _ = @import("behavior/bugs/828.zig"); _ = @import("behavior/bugs/1277.zig"); + _ = @import("behavior/bugs/1322.zig"); + _ = @import("behavior/bugs/1381.zig"); _ = @import("behavior/bugs/1421.zig"); _ = @import("behavior/bugs/1442.zig"); _ = @import("behavior/bugs/1486.zig"); @@ -21,9 +20,14 @@ comptime { _ = @import("behavior/bugs/655.zig"); _ = @import("behavior/bugs/656.zig"); _ = @import("behavior/bugs/726.zig"); + _ = @import("behavior/bugs/828.zig"); + _ = @import("behavior/bugs/920.zig"); _ = @import("behavior/byval_arg_var.zig"); + _ = @import("behavior/cast.zig"); _ = @import("behavior/defer.zig"); _ = @import("behavior/enum.zig"); + _ = @import("behavior/enum_with_members.zig"); + _ = @import("behavior/error.zig"); _ = @import("behavior/eval.zig"); _ = @import("behavior/field_parent_ptr.zig"); _ = @import("behavior/fn.zig"); @@ -38,6 +42,8 @@ comptime { _ = @import("behavior/misc.zig"); _ = @import("behavior/namespace_depends_on_compile_var/index.zig"); _ = @import("behavior/new_stack_call.zig"); + _ = @import("behavior/null.zig"); + _ = @import("behavior/optional.zig"); _ = @import("behavior/pointers.zig"); _ = @import("behavior/popcount.zig"); _ = @import("behavior/ptrcast.zig"); @@ -48,24 +54,25 @@ comptime { _ = @import("behavior/slice.zig"); _ = @import("behavior/struct.zig"); _ = @import("behavior/struct_contains_null_ptr_itself.zig"); + _ = @import("behavior/struct_contains_slice_of_itself.zig"); + _ = @import("behavior/switch.zig"); + _ = @import("behavior/switch_prong_err_enum.zig"); _ = @import("behavior/syntax.zig"); _ = @import("behavior/this.zig"); _ = @import("behavior/truncate.zig"); + _ = @import("behavior/try.zig"); _ = @import("behavior/type_info.zig"); _ = @import("behavior/undefined.zig"); _ = @import("behavior/underscore.zig"); + _ = @import("behavior/union.zig"); _ = @import("behavior/var_args.zig"); _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); - _ = @import("behavior/optional.zig"); - _ = @import("behavior/switch.zig"); - _ = @import("behavior/null.zig"); - _ = @import("behavior/union.zig"); - _ = @import("behavior/error.zig"); - _ = @import("behavior/cast.zig"); - _ = @import("behavior/try.zig"); - _ = @import("behavior/enum_with_members.zig"); - _ = @import("behavior/struct_contains_slice_of_itself.zig"); - _ = @import("behavior/switch_prong_err_enum.zig"); + _ = @import("behavior/coroutines.zig"); + _ = @import("behavior/switch_prong_implicit_cast.zig"); + _ = @import("behavior/cancel.zig"); + _ = @import("behavior/ir_block_deps.zig"); + _ = @import("behavior/const_slice_child.zig"); + _ = @import("behavior/coroutine_await_struct.zig"); } diff --git a/test/stage1/behavior/bugs/920.zig b/test/stage1/behavior/bugs/920.zig new file mode 100644 index 000000000000..eb8607e5d0ee --- /dev/null +++ b/test/stage1/behavior/bugs/920.zig @@ -0,0 +1,5 @@ +const std = @import("std"); +const math = std.math; +const Random = std.rand.Random; + + diff --git a/test/stage1/behavior/cancel.zig b/test/stage1/behavior/cancel.zig new file mode 100644 index 000000000000..b31ab7d330d2 --- /dev/null +++ b/test/stage1/behavior/cancel.zig @@ -0,0 +1,2 @@ +const std = @import("std"); + diff --git a/test/stage1/behavior/const_slice_child.zig b/test/stage1/behavior/const_slice_child.zig new file mode 100644 index 000000000000..795268e4065a --- /dev/null +++ b/test/stage1/behavior/const_slice_child.zig @@ -0,0 +1,3 @@ +const debug = @import("std").debug; +const assertOrPanic = debug.assertOrPanic; + diff --git a/test/stage1/behavior/coroutine_await_struct.zig b/test/stage1/behavior/coroutine_await_struct.zig new file mode 100644 index 000000000000..c8037714fda6 --- /dev/null +++ b/test/stage1/behavior/coroutine_await_struct.zig @@ -0,0 +1,4 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assertOrPanic = std.debug.assertOrPanic; + diff --git a/test/stage1/behavior/coroutines.zig b/test/stage1/behavior/coroutines.zig new file mode 100644 index 000000000000..33dd3f01ae11 --- /dev/null +++ b/test/stage1/behavior/coroutines.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assertOrPanic = std.debug.assertOrPanic; + +var x: i32 = 1; + + diff --git a/test/stage1/behavior/ir_block_deps.zig b/test/stage1/behavior/ir_block_deps.zig new file mode 100644 index 000000000000..41c447315af6 --- /dev/null +++ b/test/stage1/behavior/ir_block_deps.zig @@ -0,0 +1,2 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + diff --git a/test/stage1/behavior/switch_prong_implicit_cast.zig b/test/stage1/behavior/switch_prong_implicit_cast.zig new file mode 100644 index 000000000000..41c447315af6 --- /dev/null +++ b/test/stage1/behavior/switch_prong_implicit_cast.zig @@ -0,0 +1,2 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + From 56a9b3b75c2cd9654356e0c4dd236309d6f45cc1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 Jan 2019 14:36:11 -0500 Subject: [PATCH 140/190] copy elision: fix comptime union init --- src/ir.cpp | 4 +- test/behavior.zig | 1 - test/cases/switch.zig | 130 -------------------------------- test/stage1/behavior.zig | 12 +-- test/stage1/behavior/switch.zig | 128 +++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+), 140 deletions(-) delete mode 100644 test/cases/switch.zig diff --git a/src/ir.cpp b/src/ir.cpp index 9cb230497fcd..7fd9b95fa795 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16402,9 +16402,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ if (type_is_invalid(union_val->type)) return ira->codegen->invalid_instruction; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer && - union_val->special == ConstValSpecialUndef) - { + if (union_val->special == ConstValSpecialUndef) { ConstExprValue *payload_val = create_const_vals(1); payload_val->special = ConstValSpecialUndef; payload_val->type = field->type_entry; diff --git a/test/behavior.zig b/test/behavior.zig index a7b926d4d774..9f5bd9025a6d 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -13,7 +13,6 @@ comptime { _ = @import("cases/null.zig"); _ = @import("cases/struct.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); - _ = @import("cases/switch.zig"); _ = @import("cases/switch_prong_err_enum.zig"); _ = @import("cases/switch_prong_implicit_cast.zig"); _ = @import("cases/try.zig"); diff --git a/test/cases/switch.zig b/test/cases/switch.zig deleted file mode 100644 index ec040e2fd16d..000000000000 --- a/test/cases/switch.zig +++ /dev/null @@ -1,130 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -test "switch on enum using pointer capture" { - testSwitchEnumPtrCapture(); - comptime testSwitchEnumPtrCapture(); -} - -fn testSwitchEnumPtrCapture() void { - var value = SwitchProngWithVarEnum{ .One = 1234 }; - switch (value) { - SwitchProngWithVarEnum.One => |*x| x.* += 1, - else => unreachable, - } - switch (value) { - SwitchProngWithVarEnum.One => |x| assertOrPanic(x == 1235), - else => unreachable, - } -} - -test "switch with multiple expressions" { - const x = switch (returnsFive()) { - 1, 2, 3 => 1, - 4, 5, 6 => 2, - else => i32(3), - }; - assertOrPanic(x == 2); -} -fn returnsFive() i32 { - return 5; -} - -const Number = union(enum) { - One: u64, - Two: u8, - Three: f32, -}; - -const number = Number{ .Three = 1.23 }; - -fn returnsFalse() bool { - switch (number) { - Number.One => |x| return x > 1234, - Number.Two => |x| return x == 'a', - Number.Three => |x| return x > 12.34, - } -} -test "switch on const enum with var" { - assertOrPanic(!returnsFalse()); -} - -test "switch on type" { - assertOrPanic(trueIfBoolFalseOtherwise(bool)); - assertOrPanic(!trueIfBoolFalseOtherwise(i32)); -} - -fn trueIfBoolFalseOtherwise(comptime T: type) bool { - return switch (T) { - bool => true, - else => false, - }; -} - -test "switch handles all cases of number" { - testSwitchHandleAllCases(); - comptime testSwitchHandleAllCases(); -} - -fn testSwitchHandleAllCases() void { - assertOrPanic(testSwitchHandleAllCasesExhaustive(0) == 3); - assertOrPanic(testSwitchHandleAllCasesExhaustive(1) == 2); - assertOrPanic(testSwitchHandleAllCasesExhaustive(2) == 1); - assertOrPanic(testSwitchHandleAllCasesExhaustive(3) == 0); - - assertOrPanic(testSwitchHandleAllCasesRange(100) == 0); - assertOrPanic(testSwitchHandleAllCasesRange(200) == 1); - assertOrPanic(testSwitchHandleAllCasesRange(201) == 2); - assertOrPanic(testSwitchHandleAllCasesRange(202) == 4); - assertOrPanic(testSwitchHandleAllCasesRange(230) == 3); -} - -fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { - return switch (x) { - 0 => u2(3), - 1 => 2, - 2 => 1, - 3 => 0, - }; -} - -fn testSwitchHandleAllCasesRange(x: u8) u8 { - return switch (x) { - 0...100 => u8(0), - 101...200 => 1, - 201, 203 => 2, - 202 => 4, - 204...255 => 3, - }; -} - -test "switch all prongs unreachable" { - testAllProngsUnreachable(); - comptime testAllProngsUnreachable(); -} - -fn testAllProngsUnreachable() void { - assertOrPanic(switchWithUnreachable(1) == 2); - assertOrPanic(switchWithUnreachable(2) == 10); -} - -fn switchWithUnreachable(x: i32) i32 { - while (true) { - switch (x) { - 1 => return 2, - 2 => break, - else => continue, - } - } - return 10; -} - -fn return_a_number() anyerror!i32 { - return 1; -} - -test "capture value of switch with all unreachable prongs" { - const x = return_a_number() catch |err| switch (err) { - else => unreachable, - }; - assertOrPanic(x == 1); -} diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index a0b8ea4fc215..80b87fc4cbc7 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -23,7 +23,11 @@ comptime { _ = @import("behavior/bugs/828.zig"); _ = @import("behavior/bugs/920.zig"); _ = @import("behavior/byval_arg_var.zig"); + _ = @import("behavior/cancel.zig"); _ = @import("behavior/cast.zig"); + _ = @import("behavior/const_slice_child.zig"); + _ = @import("behavior/coroutine_await_struct.zig"); + _ = @import("behavior/coroutines.zig"); _ = @import("behavior/defer.zig"); _ = @import("behavior/enum.zig"); _ = @import("behavior/enum_with_members.zig"); @@ -37,6 +41,7 @@ comptime { _ = @import("behavior/if.zig"); _ = @import("behavior/import.zig"); _ = @import("behavior/incomplete_struct_param_tld.zig"); + _ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/math.zig"); _ = @import("behavior/merge_error_sets.zig"); _ = @import("behavior/misc.zig"); @@ -57,6 +62,7 @@ comptime { _ = @import("behavior/struct_contains_slice_of_itself.zig"); _ = @import("behavior/switch.zig"); _ = @import("behavior/switch_prong_err_enum.zig"); + _ = @import("behavior/switch_prong_implicit_cast.zig"); _ = @import("behavior/syntax.zig"); _ = @import("behavior/this.zig"); _ = @import("behavior/truncate.zig"); @@ -69,10 +75,4 @@ comptime { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); - _ = @import("behavior/coroutines.zig"); - _ = @import("behavior/switch_prong_implicit_cast.zig"); - _ = @import("behavior/cancel.zig"); - _ = @import("behavior/ir_block_deps.zig"); - _ = @import("behavior/const_slice_child.zig"); - _ = @import("behavior/coroutine_await_struct.zig"); } diff --git a/test/stage1/behavior/switch.zig b/test/stage1/behavior/switch.zig index 0c3cca67a79d..9d24d894dc76 100644 --- a/test/stage1/behavior/switch.zig +++ b/test/stage1/behavior/switch.zig @@ -104,3 +104,131 @@ fn switchProngWithVarFn(a: SwitchProngWithVarEnum) void { } } +test "switch on enum using pointer capture" { + testSwitchEnumPtrCapture(); + comptime testSwitchEnumPtrCapture(); +} + +fn testSwitchEnumPtrCapture() void { + var value = SwitchProngWithVarEnum{ .One = 1234 }; + switch (value) { + SwitchProngWithVarEnum.One => |*x| x.* += 1, + else => unreachable, + } + switch (value) { + SwitchProngWithVarEnum.One => |x| assertOrPanic(x == 1235), + else => unreachable, + } +} + +test "switch with multiple expressions" { + const x = switch (returnsFive()) { + 1, 2, 3 => 1, + 4, 5, 6 => 2, + else => i32(3), + }; + assertOrPanic(x == 2); +} +fn returnsFive() i32 { + return 5; +} + +const Number = union(enum) { + One: u64, + Two: u8, + Three: f32, +}; + +const number = Number{ .Three = 1.23 }; + +fn returnsFalse() bool { + switch (number) { + Number.One => |x| return x > 1234, + Number.Two => |x| return x == 'a', + Number.Three => |x| return x > 12.34, + } +} +test "switch on const enum with var" { + assertOrPanic(!returnsFalse()); +} + +test "switch on type" { + assertOrPanic(trueIfBoolFalseOtherwise(bool)); + assertOrPanic(!trueIfBoolFalseOtherwise(i32)); +} + +fn trueIfBoolFalseOtherwise(comptime T: type) bool { + return switch (T) { + bool => true, + else => false, + }; +} + +test "switch handles all cases of number" { + testSwitchHandleAllCases(); + comptime testSwitchHandleAllCases(); +} + +fn testSwitchHandleAllCases() void { + assertOrPanic(testSwitchHandleAllCasesExhaustive(0) == 3); + assertOrPanic(testSwitchHandleAllCasesExhaustive(1) == 2); + assertOrPanic(testSwitchHandleAllCasesExhaustive(2) == 1); + assertOrPanic(testSwitchHandleAllCasesExhaustive(3) == 0); + + assertOrPanic(testSwitchHandleAllCasesRange(100) == 0); + assertOrPanic(testSwitchHandleAllCasesRange(200) == 1); + assertOrPanic(testSwitchHandleAllCasesRange(201) == 2); + assertOrPanic(testSwitchHandleAllCasesRange(202) == 4); + assertOrPanic(testSwitchHandleAllCasesRange(230) == 3); +} + +fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { + return switch (x) { + 0 => u2(3), + 1 => 2, + 2 => 1, + 3 => 0, + }; +} + +fn testSwitchHandleAllCasesRange(x: u8) u8 { + return switch (x) { + 0...100 => u8(0), + 101...200 => 1, + 201, 203 => 2, + 202 => 4, + 204...255 => 3, + }; +} + +test "switch all prongs unreachable" { + testAllProngsUnreachable(); + comptime testAllProngsUnreachable(); +} + +fn testAllProngsUnreachable() void { + assertOrPanic(switchWithUnreachable(1) == 2); + assertOrPanic(switchWithUnreachable(2) == 10); +} + +fn switchWithUnreachable(x: i32) i32 { + while (true) { + switch (x) { + 1 => return 2, + 2 => break, + else => continue, + } + } + return 10; +} + +fn return_a_number() anyerror!i32 { + return 1; +} + +test "capture value of switch with all unreachable prongs" { + const x = return_a_number() catch |err| switch (err) { + else => unreachable, + }; + assertOrPanic(x == 1); +} From 940194ddc07b97cd4a1677e2657855f9e094914a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 Jan 2019 17:08:53 -0500 Subject: [PATCH 141/190] copy elision: fix union assignment --- src/all_types.hpp | 3 +- src/ir.cpp | 66 ++++--- test/behavior.zig | 1 - test/cases/union.zig | 316 --------------------------------- test/stage1/behavior.zig | 2 +- test/stage1/behavior/union.zig | 315 ++++++++++++++++++++++++++++++++ 6 files changed, 354 insertions(+), 349 deletions(-) delete mode 100644 test/cases/union.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index 27365e2c991b..5e72de59bbb6 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2425,7 +2425,7 @@ struct IrInstructionFieldPtr { Buf *field_name_buffer; IrInstruction *field_name_expr; IrInstruction *container_type; - bool is_const; + bool initialize; }; struct IrInstructionStructFieldPtr { @@ -2448,7 +2448,6 @@ struct IrInstructionUnionFieldPtr { IrInstruction *union_ptr; TypeUnionField *field; IrInstructionUnionFieldPtrId id; - bool is_const; }; struct IrInstructionElemPtr { diff --git a/src/ir.cpp b/src/ir.cpp index 7fd9b95fa795..2b14b5feb973 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -155,7 +155,7 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Zig static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr); static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg); static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, - IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type); + IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type, bool initialize); static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var); static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align); @@ -1289,13 +1289,14 @@ static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scop } static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_ptr, IrInstruction *container_type, Buf *field_name) + IrInstruction *container_ptr, IrInstruction *container_type, Buf *field_name, bool initialize) { IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; instruction->container_type = container_type; instruction->field_name_buffer = field_name; instruction->field_name_expr = nullptr; + instruction->initialize = initialize; ir_ref_instruction(container_ptr, irb->current_basic_block); if (container_type != nullptr) ir_ref_instruction(container_type, irb->current_basic_block); @@ -4288,7 +4289,8 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; - IrInstruction *field_ptr = ir_build_field_ptr(irb, scope, node, container_ref_instruction, nullptr, field_name); + IrInstruction *field_ptr = ir_build_field_ptr(irb, scope, node, container_ref_instruction, nullptr, + field_name, false); return ir_gen_ptr(irb, scope, node, lval, result_loc, field_ptr); } @@ -5711,7 +5713,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A Buf *name = entry_node->data.struct_val_field.name; IrInstruction *field_result_loc = ir_build_field_ptr(irb, scope, entry_node, new_result_loc, - container_type, name); + container_type, name, true); AstNode *expr_node = entry_node->data.struct_val_field.expr; IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope, LValNone, field_result_loc); @@ -6152,7 +6154,8 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrBasicBlock *continue_block = ir_create_basic_block(irb, child_scope, "ForContinue"); Buf *len_field_name = buf_create_from_str("len"); - IrInstruction *len_ref = ir_build_field_ptr(irb, child_scope, node, array_val_ptr, nullptr, len_field_name); + IrInstruction *len_ref = ir_build_field_ptr(irb, child_scope, node, array_val_ptr, nullptr, + len_field_name, false); IrInstruction *len_val = ir_build_load_ptr(irb, child_scope, node, len_ref, nullptr); ir_build_br(irb, child_scope, node, cond_block, is_comptime); @@ -7326,7 +7329,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, - atomic_state_field_name); + atomic_state_field_name, false); // set the is_canceled bit IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, @@ -7404,7 +7407,7 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, - atomic_state_field_name); + atomic_state_field_name, false); // clear the is_suspended bit IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, @@ -7472,13 +7475,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst); Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, - result_ptr_field_name); + result_ptr_field_name, false); if (irb->codegen->have_err_ret_tracing) { IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - nullptr, err_ret_trace_ptr_field_name); + nullptr, err_ret_trace_ptr_field_name, false); ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); } @@ -7500,7 +7503,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - nullptr, atomic_state_field_name); + nullptr, atomic_state_field_name, false); IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); @@ -7554,13 +7557,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - nullptr, err_ret_trace_field_name); + nullptr, err_ret_trace_field_name, false); IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr); } Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, - result_field_name); + result_field_name, false); // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr, nullptr); @@ -7967,7 +7970,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec coro_allocator_var_alloca); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, - nullptr, alloc_field_name); + nullptr, alloc_field_name, false); IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr, nullptr); IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size); IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); @@ -7986,36 +7989,36 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - nullptr, atomic_state_field_name); + nullptr, atomic_state_field_name, false); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero); Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - nullptr, result_field_name); + nullptr, result_field_name, false); result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - nullptr, result_ptr_field_name); + nullptr, result_ptr_field_name, false); ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr); if (irb->codegen->have_err_ret_tracing) { // initialize the error return trace Buf *return_addresses_field_name = buf_create_from_str(RETURN_ADDRESSES_FIELD_NAME); IrInstruction *return_addresses_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - nullptr, return_addresses_field_name); + nullptr, return_addresses_field_name, false); Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, - err_ret_trace_field_name); + err_ret_trace_field_name, false); ir_build_mark_err_ret_trace_ptr(irb, scope, node, err_ret_trace_ptr); // coordinate with builtin.zig Buf *index_name = buf_create_from_str("index"); IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, nullptr, - index_name); + index_name, false); ir_build_store_ptr(irb, scope, node, index_ptr, zero); Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses"); IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, nullptr, - instruction_addresses_name); + instruction_addresses_name, false); IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false, addrs_slice_ptr); @@ -8085,7 +8088,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - nullptr, err_ret_trace_ptr_field_name); + nullptr, err_ret_trace_ptr_field_name, false); IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, nullptr); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr); } @@ -8121,7 +8124,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, ImplicitAllocatorIdLocalVar); IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, nullptr, - free_field_name); + free_field_name, false); IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr, nullptr); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); @@ -14197,7 +14200,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c assert(async_allocator_inst->value.type->id == ZigTypeIdPointer); ZigType *container_type = async_allocator_inst->value.type->data.pointer.child_type; IrInstruction *field_ptr_inst = ir_analyze_container_field_ptr(ira, alloc_field_name, &call_instruction->base, - async_allocator_inst, container_type); + async_allocator_inst, container_type, false); if (type_is_invalid(field_ptr_inst->value.type)) { return ira->codegen->invalid_instruction; } @@ -16300,7 +16303,7 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, } static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, - IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type) + IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type, bool initialize) { Error err; @@ -16402,7 +16405,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ if (type_is_invalid(union_val->type)) return ira->codegen->invalid_instruction; - if (union_val->special == ConstValSpecialUndef) { + if (union_val->special == ConstValSpecialUndef || initialize) { ConstExprValue *payload_val = create_const_vals(1); payload_val->special = ConstValSpecialUndef; payload_val->type = field->type_entry; @@ -16458,7 +16461,8 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ } IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, - source_instr->source_node, container_ptr, field, IrInstructionUnionFieldPtrIdRef); + source_instr->source_node, container_ptr, field, + initialize ? IrInstructionUnionFieldPtrIdResultPtr : IrInstructionUnionFieldPtrIdRef); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, PtrLenSingle, 0, 0, 0); return result; @@ -16580,7 +16584,9 @@ static IrInstruction *ir_analyze_error_literal(IrAnalyze *ira, IrInstruction *so return result; } -static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) { +static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, + IrInstructionFieldPtr *field_ptr_instruction) +{ Error err; IrInstruction *container_ptr = field_ptr_instruction->container_ptr->child; if (type_is_invalid(container_ptr->value.type)) @@ -16622,10 +16628,12 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc if (container_type->id == ZigTypeIdPointer) { ZigType *bare_type = container_ref_type(container_type); IrInstruction *container_child = ir_get_deref(ira, &field_ptr_instruction->base, container_ptr); - IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_child, bare_type); + IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, + container_child, bare_type, field_ptr_instruction->initialize); return result; } else { - IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_ptr, container_type); + IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, + container_ptr, container_type, field_ptr_instruction->initialize); return result; } } else if (is_array_ref(container_type)) { diff --git a/test/behavior.zig b/test/behavior.zig index 9f5bd9025a6d..64d3fe60a4ba 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -16,5 +16,4 @@ comptime { _ = @import("cases/switch_prong_err_enum.zig"); _ = @import("cases/switch_prong_implicit_cast.zig"); _ = @import("cases/try.zig"); - _ = @import("cases/union.zig"); } diff --git a/test/cases/union.zig b/test/cases/union.zig deleted file mode 100644 index e08c7b0d2e02..000000000000 --- a/test/cases/union.zig +++ /dev/null @@ -1,316 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -const Foo = union { - float: f64, - int: i32, -}; - -test "basic unions" { - var foo = Foo{ .int = 1 }; - assertOrPanic(foo.int == 1); - foo = Foo{ .float = 12.34 }; - assertOrPanic(foo.float == 12.34); -} - -test "comptime union field access" { - comptime { - var foo = Foo{ .int = 0 }; - assertOrPanic(foo.int == 0); - - foo = Foo{ .float = 42.42 }; - assertOrPanic(foo.float == 42.42); - } -} - -test "init union with runtime value" { - var foo: Foo = undefined; - - setFloat(&foo, 12.34); - assertOrPanic(foo.float == 12.34); - - setInt(&foo, 42); - assertOrPanic(foo.int == 42); -} - -fn setFloat(foo: *Foo, x: f64) void { - foo.* = Foo{ .float = x }; -} - -fn setInt(foo: *Foo, x: i32) void { - foo.* = Foo{ .int = x }; -} - -const FooExtern = extern union { - float: f64, - int: i32, -}; - -test "basic extern unions" { - var foo = FooExtern{ .int = 1 }; - assertOrPanic(foo.int == 1); - foo.float = 12.34; - assertOrPanic(foo.float == 12.34); -} - -const Letter = enum { - A, - B, - C, -}; -const Payload = union(Letter) { - A: i32, - B: f64, - C: bool, -}; - -test "union with specified enum tag" { - doTest(); - comptime doTest(); -} - -fn doTest() void { - assertOrPanic(bar(Payload{ .A = 1234 }) == -10); -} - -fn bar(value: Payload) i32 { - assertOrPanic(Letter(value) == Letter.A); - return switch (value) { - Payload.A => |x| return x - 1244, - Payload.B => |x| if (x == 12.34) i32(20) else 21, - Payload.C => |x| if (x) i32(30) else 31, - }; -} - -const MultipleChoice = union(enum(u32)) { - A = 20, - B = 40, - C = 60, - D = 1000, -}; -test "simple union(enum(u32))" { - var x = MultipleChoice.C; - assertOrPanic(x == MultipleChoice.C); - assertOrPanic(@enumToInt(@TagType(MultipleChoice)(x)) == 60); -} - -const MultipleChoice2 = union(enum(u32)) { - Unspecified1: i32, - A: f32 = 20, - Unspecified2: void, - B: bool = 40, - Unspecified3: i32, - C: i8 = 60, - Unspecified4: void, - D: void = 1000, - Unspecified5: i32, -}; - -test "union(enum(u32)) with specified and unspecified tag values" { - comptime assertOrPanic(@TagType(@TagType(MultipleChoice2)) == u32); - testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); - comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); -} - -fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assertOrPanic(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); - assertOrPanic(1123 == switch (x) { - MultipleChoice2.A => 1, - MultipleChoice2.B => 2, - MultipleChoice2.C => |v| i32(1000) + v, - MultipleChoice2.D => 4, - MultipleChoice2.Unspecified1 => 5, - MultipleChoice2.Unspecified2 => 6, - MultipleChoice2.Unspecified3 => 7, - MultipleChoice2.Unspecified4 => 8, - MultipleChoice2.Unspecified5 => 9, - }); -} - -const ExternPtrOrInt = extern union { - ptr: *u8, - int: u64, -}; -test "extern union size" { - comptime assertOrPanic(@sizeOf(ExternPtrOrInt) == 8); -} - -const PackedPtrOrInt = packed union { - ptr: *u8, - int: u64, -}; -test "extern union size" { - comptime assertOrPanic(@sizeOf(PackedPtrOrInt) == 8); -} - -const ZeroBits = union { - OnlyField: void, -}; -test "union with only 1 field which is void should be zero bits" { - comptime assertOrPanic(@sizeOf(ZeroBits) == 0); -} - -const TheTag = enum { - A, - B, - C, -}; -const TheUnion = union(TheTag) { - A: i32, - B: i32, - C: i32, -}; -test "union field access gives the enum values" { - assertOrPanic(TheUnion.A == TheTag.A); - assertOrPanic(TheUnion.B == TheTag.B); - assertOrPanic(TheUnion.C == TheTag.C); -} - -test "cast union to tag type of union" { - testCastUnionToTagType(TheUnion{ .B = 1234 }); - comptime testCastUnionToTagType(TheUnion{ .B = 1234 }); -} - -fn testCastUnionToTagType(x: TheUnion) void { - assertOrPanic(TheTag(x) == TheTag.B); -} - -test "cast tag type of union to union" { - var x: Value2 = Letter2.B; - assertOrPanic(Letter2(x) == Letter2.B); -} -const Letter2 = enum { - A, - B, - C, -}; -const Value2 = union(Letter2) { - A: i32, - B, - C, -}; - -test "implicit cast union to its tag type" { - var x: Value2 = Letter2.B; - assertOrPanic(x == Letter2.B); - giveMeLetterB(x); -} -fn giveMeLetterB(x: Letter2) void { - assertOrPanic(x == Value2.B); -} - -pub const PackThis = union(enum) { - Invalid: bool, - StringLiteral: u2, -}; - -test "constant packed union" { - testConstPackedUnion([]PackThis{PackThis{ .StringLiteral = 1 }}); -} - -fn testConstPackedUnion(expected_tokens: []const PackThis) void { - assertOrPanic(expected_tokens[0].StringLiteral == 1); -} - -test "switch on union with only 1 field" { - var r: PartialInst = undefined; - r = PartialInst.Compiled; - switch (r) { - PartialInst.Compiled => { - var z: PartialInstWithPayload = undefined; - z = PartialInstWithPayload{ .Compiled = 1234 }; - switch (z) { - PartialInstWithPayload.Compiled => |x| { - assertOrPanic(x == 1234); - return; - }, - } - }, - } - unreachable; -} - -const PartialInst = union(enum) { - Compiled, -}; - -const PartialInstWithPayload = union(enum) { - Compiled: i32, -}; - -test "access a member of tagged union with conflicting enum tag name" { - const Bar = union(enum) { - A: A, - B: B, - - const A = u8; - const B = void; - }; - - comptime assertOrPanic(Bar.A == u8); -} - -test "tagged union initialization with runtime void" { - assertOrPanic(testTaggedUnionInit({})); -} - -const TaggedUnionWithAVoid = union(enum) { - A, - B: i32, -}; - -fn testTaggedUnionInit(x: var) bool { - const y = TaggedUnionWithAVoid{ .A = x }; - return @TagType(TaggedUnionWithAVoid)(y) == TaggedUnionWithAVoid.A; -} - -pub const UnionEnumNoPayloads = union(enum) { - A, - B, -}; - -test "tagged union with no payloads" { - const a = UnionEnumNoPayloads{ .B = {} }; - switch (a) { - @TagType(UnionEnumNoPayloads).A => @panic("wrong"), - @TagType(UnionEnumNoPayloads).B => {}, - } -} - -test "union with only 1 field casted to its enum type" { - const Literal = union(enum) { - Number: f64, - Bool: bool, - }; - - const Expr = union(enum) { - Literal: Literal, - }; - - var e = Expr{ .Literal = Literal{ .Bool = true } }; - const Tag = @TagType(Expr); - comptime assertOrPanic(@TagType(Tag) == comptime_int); - var t = Tag(e); - assertOrPanic(t == Expr.Literal); -} - -test "union with only 1 field casted to its enum type which has enum value specified" { - const Literal = union(enum) { - Number: f64, - Bool: bool, - }; - - const Tag = enum { - Literal = 33, - }; - - const Expr = union(Tag) { - Literal: Literal, - }; - - var e = Expr{ .Literal = Literal{ .Bool = true } }; - comptime assertOrPanic(@TagType(Tag) == comptime_int); - var t = Tag(e); - assertOrPanic(t == Expr.Literal); - assertOrPanic(@enumToInt(t) == 33); - comptime assertOrPanic(@enumToInt(t) == 33); -} diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 80b87fc4cbc7..35858394566d 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -70,9 +70,9 @@ comptime { _ = @import("behavior/type_info.zig"); _ = @import("behavior/undefined.zig"); _ = @import("behavior/underscore.zig"); - _ = @import("behavior/union.zig"); _ = @import("behavior/var_args.zig"); _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); + _ = @import("behavior/union.zig"); } diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index a32a4be374db..c8e8feb11ea6 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -35,3 +35,318 @@ test "unions embedded in aggregate types" { else => unreachable, } } + +const Foo = union { + float: f64, + int: i32, +}; + +test "basic unions" { + var foo = Foo{ .int = 1 }; + assertOrPanic(foo.int == 1); + foo = Foo{ .float = 12.34 }; + assertOrPanic(foo.float == 12.34); +} + +test "comptime union field access" { + comptime { + var foo = Foo{ .int = 0 }; + assertOrPanic(foo.int == 0); + + foo = Foo{ .float = 42.42 }; + assertOrPanic(foo.float == 42.42); + } +} + +test "init union with runtime value" { + var foo: Foo = undefined; + + setFloat(&foo, 12.34); + assertOrPanic(foo.float == 12.34); + + setInt(&foo, 42); + assertOrPanic(foo.int == 42); +} + +fn setFloat(foo: *Foo, x: f64) void { + foo.* = Foo{ .float = x }; +} + +fn setInt(foo: *Foo, x: i32) void { + foo.* = Foo{ .int = x }; +} + +const FooExtern = extern union { + float: f64, + int: i32, +}; + +test "basic extern unions" { + var foo = FooExtern{ .int = 1 }; + assertOrPanic(foo.int == 1); + foo.float = 12.34; + assertOrPanic(foo.float == 12.34); +} + +const Letter = enum { + A, + B, + C, +}; +const Payload = union(Letter) { + A: i32, + B: f64, + C: bool, +}; + +test "union with specified enum tag" { + doTest(); + comptime doTest(); +} + +fn doTest() void { + assertOrPanic(bar(Payload{ .A = 1234 }) == -10); +} + +fn bar(value: Payload) i32 { + assertOrPanic(Letter(value) == Letter.A); + return switch (value) { + Payload.A => |x| return x - 1244, + Payload.B => |x| if (x == 12.34) i32(20) else 21, + Payload.C => |x| if (x) i32(30) else 31, + }; +} + +const MultipleChoice = union(enum(u32)) { + A = 20, + B = 40, + C = 60, + D = 1000, +}; +test "simple union(enum(u32))" { + var x = MultipleChoice.C; + assertOrPanic(x == MultipleChoice.C); + assertOrPanic(@enumToInt(@TagType(MultipleChoice)(x)) == 60); +} + +const MultipleChoice2 = union(enum(u32)) { + Unspecified1: i32, + A: f32 = 20, + Unspecified2: void, + B: bool = 40, + Unspecified3: i32, + C: i8 = 60, + Unspecified4: void, + D: void = 1000, + Unspecified5: i32, +}; + +test "union(enum(u32)) with specified and unspecified tag values" { + comptime assertOrPanic(@TagType(@TagType(MultipleChoice2)) == u32); + testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); + comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); +} + +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { + assertOrPanic(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); + assertOrPanic(1123 == switch (x) { + MultipleChoice2.A => 1, + MultipleChoice2.B => 2, + MultipleChoice2.C => |v| i32(1000) + v, + MultipleChoice2.D => 4, + MultipleChoice2.Unspecified1 => 5, + MultipleChoice2.Unspecified2 => 6, + MultipleChoice2.Unspecified3 => 7, + MultipleChoice2.Unspecified4 => 8, + MultipleChoice2.Unspecified5 => 9, + }); +} + +const ExternPtrOrInt = extern union { + ptr: *u8, + int: u64, +}; +test "extern union size" { + comptime assertOrPanic(@sizeOf(ExternPtrOrInt) == 8); +} + +const PackedPtrOrInt = packed union { + ptr: *u8, + int: u64, +}; +test "extern union size" { + comptime assertOrPanic(@sizeOf(PackedPtrOrInt) == 8); +} + +const ZeroBits = union { + OnlyField: void, +}; +test "union with only 1 field which is void should be zero bits" { + comptime assertOrPanic(@sizeOf(ZeroBits) == 0); +} + +const TheTag = enum { + A, + B, + C, +}; +const TheUnion = union(TheTag) { + A: i32, + B: i32, + C: i32, +}; +test "union field access gives the enum values" { + assertOrPanic(TheUnion.A == TheTag.A); + assertOrPanic(TheUnion.B == TheTag.B); + assertOrPanic(TheUnion.C == TheTag.C); +} + +test "cast union to tag type of union" { + testCastUnionToTagType(TheUnion{ .B = 1234 }); + comptime testCastUnionToTagType(TheUnion{ .B = 1234 }); +} + +fn testCastUnionToTagType(x: TheUnion) void { + assertOrPanic(TheTag(x) == TheTag.B); +} + +test "cast tag type of union to union" { + var x: Value2 = Letter2.B; + assertOrPanic(Letter2(x) == Letter2.B); +} +const Letter2 = enum { + A, + B, + C, +}; +const Value2 = union(Letter2) { + A: i32, + B, + C, +}; + +test "implicit cast union to its tag type" { + var x: Value2 = Letter2.B; + assertOrPanic(x == Letter2.B); + giveMeLetterB(x); +} +fn giveMeLetterB(x: Letter2) void { + assertOrPanic(x == Value2.B); +} + +pub const PackThis = union(enum) { + Invalid: bool, + StringLiteral: u2, +}; + +test "constant packed union" { + testConstPackedUnion([]PackThis{PackThis{ .StringLiteral = 1 }}); +} + +fn testConstPackedUnion(expected_tokens: []const PackThis) void { + assertOrPanic(expected_tokens[0].StringLiteral == 1); +} + +test "switch on union with only 1 field" { + var r: PartialInst = undefined; + r = PartialInst.Compiled; + switch (r) { + PartialInst.Compiled => { + var z: PartialInstWithPayload = undefined; + z = PartialInstWithPayload{ .Compiled = 1234 }; + switch (z) { + PartialInstWithPayload.Compiled => |x| { + assertOrPanic(x == 1234); + return; + }, + } + }, + } + unreachable; +} + +const PartialInst = union(enum) { + Compiled, +}; + +const PartialInstWithPayload = union(enum) { + Compiled: i32, +}; + +test "access a member of tagged union with conflicting enum tag name" { + const Bar = union(enum) { + A: A, + B: B, + + const A = u8; + const B = void; + }; + + comptime assertOrPanic(Bar.A == u8); +} + +test "tagged union initialization with runtime void" { + assertOrPanic(testTaggedUnionInit({})); +} + +const TaggedUnionWithAVoid = union(enum) { + A, + B: i32, +}; + +fn testTaggedUnionInit(x: var) bool { + const y = TaggedUnionWithAVoid{ .A = x }; + return @TagType(TaggedUnionWithAVoid)(y) == TaggedUnionWithAVoid.A; +} + +pub const UnionEnumNoPayloads = union(enum) { + A, + B, +}; + +test "tagged union with no payloads" { + const a = UnionEnumNoPayloads{ .B = {} }; + switch (a) { + @TagType(UnionEnumNoPayloads).A => @panic("wrong"), + @TagType(UnionEnumNoPayloads).B => {}, + } +} + +test "union with only 1 field casted to its enum type" { + const Literal = union(enum) { + Number: f64, + Bool: bool, + }; + + const Expr = union(enum) { + Literal: Literal, + }; + + var e = Expr{ .Literal = Literal{ .Bool = true } }; + const Tag = @TagType(Expr); + comptime assertOrPanic(@TagType(Tag) == comptime_int); + var t = Tag(e); + assertOrPanic(t == Expr.Literal); +} + +test "union with only 1 field casted to its enum type which has enum value specified" { + const Literal = union(enum) { + Number: f64, + Bool: bool, + }; + + const Tag = enum { + Literal = 33, + }; + + const Expr = union(Tag) { + Literal: Literal, + }; + + var e = Expr{ .Literal = Literal{ .Bool = true } }; + comptime assertOrPanic(@TagType(Tag) == comptime_int); + var t = Tag(e); + assertOrPanic(t == Expr.Literal); + assertOrPanic(@enumToInt(t) == 33); + comptime assertOrPanic(@enumToInt(t) == 33); +} From 01bf53bd6183244cfba1ea34b691277ed933aecd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 Jan 2019 17:12:19 -0500 Subject: [PATCH 142/190] copy elision: move passing tests --- test/behavior.zig | 1 - test/cases/switch_prong_err_enum.zig | 30 ------------------- test/stage1/behavior.zig | 2 +- .../stage1/behavior/switch_prong_err_enum.zig | 28 +++++++++++++++++ 4 files changed, 29 insertions(+), 32 deletions(-) delete mode 100644 test/cases/switch_prong_err_enum.zig diff --git a/test/behavior.zig b/test/behavior.zig index 64d3fe60a4ba..5e742e48a322 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -13,7 +13,6 @@ comptime { _ = @import("cases/null.zig"); _ = @import("cases/struct.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); - _ = @import("cases/switch_prong_err_enum.zig"); _ = @import("cases/switch_prong_implicit_cast.zig"); _ = @import("cases/try.zig"); } diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig deleted file mode 100644 index 6ac1919f0d5a..000000000000 --- a/test/cases/switch_prong_err_enum.zig +++ /dev/null @@ -1,30 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -var read_count: u64 = 0; - -fn readOnce() anyerror!u64 { - read_count += 1; - return read_count; -} - -const FormValue = union(enum) { - Address: u64, - Other: bool, -}; - -fn doThing(form_id: u64) anyerror!FormValue { - return switch (form_id) { - 17 => FormValue{ .Address = try readOnce() }, - else => error.InvalidDebugInfo, - }; -} - -test "switch prong returns error enum" { - switch (doThing(17) catch unreachable) { - FormValue.Address => |payload| { - assertOrPanic(payload == 1); - }, - else => unreachable, - } - assertOrPanic(read_count == 1); -} diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 35858394566d..80b87fc4cbc7 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -70,9 +70,9 @@ comptime { _ = @import("behavior/type_info.zig"); _ = @import("behavior/undefined.zig"); _ = @import("behavior/underscore.zig"); + _ = @import("behavior/union.zig"); _ = @import("behavior/var_args.zig"); _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); - _ = @import("behavior/union.zig"); } diff --git a/test/stage1/behavior/switch_prong_err_enum.zig b/test/stage1/behavior/switch_prong_err_enum.zig index 41c447315af6..6ac1919f0d5a 100644 --- a/test/stage1/behavior/switch_prong_err_enum.zig +++ b/test/stage1/behavior/switch_prong_err_enum.zig @@ -1,2 +1,30 @@ const assertOrPanic = @import("std").debug.assertOrPanic; +var read_count: u64 = 0; + +fn readOnce() anyerror!u64 { + read_count += 1; + return read_count; +} + +const FormValue = union(enum) { + Address: u64, + Other: bool, +}; + +fn doThing(form_id: u64) anyerror!FormValue { + return switch (form_id) { + 17 => FormValue{ .Address = try readOnce() }, + else => error.InvalidDebugInfo, + }; +} + +test "switch prong returns error enum" { + switch (doThing(17) catch unreachable) { + FormValue.Address => |payload| { + assertOrPanic(payload == 1); + }, + else => unreachable, + } + assertOrPanic(read_count == 1); +} From 3faae7498187293fc20c0d6b4215e72a1d2a4df6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 Jan 2019 17:41:44 -0500 Subject: [PATCH 143/190] copy elision: fix switch prong implicit cast --- src/ir.cpp | 22 ++++++++++++++----- test/behavior.zig | 1 - test/cases/switch_prong_implicit_cast.zig | 22 ------------------- .../behavior/switch_prong_implicit_cast.zig | 20 +++++++++++++++++ 4 files changed, 36 insertions(+), 29 deletions(-) delete mode 100644 test/cases/switch_prong_implicit_cast.zig diff --git a/src/ir.cpp b/src/ir.cpp index 2b14b5feb973..7d6d6168738c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15846,14 +15846,24 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (type_is_invalid(resolved_type)) return ira->codegen->invalid_instruction; - if (resolved_type->id == ZigTypeIdComptimeFloat || - resolved_type->id == ZigTypeIdComptimeInt || - resolved_type->id == ZigTypeIdNull || - resolved_type->id == ZigTypeIdUndefined) - { + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const(ira, &phi_instruction->base, resolved_type); + case OnePossibleValueNo: + break; + } + + switch (type_requires_comptime(ira->codegen, resolved_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_instruction; + case ReqCompTimeYes: ir_add_error_node(ira, phi_instruction->base.source_node, - buf_sprintf("unable to infer expression type")); + buf_sprintf("values of type '%s' must be comptime known", buf_ptr(&resolved_type->name))); return ira->codegen->invalid_instruction; + case ReqCompTimeNo: + break; } bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer); diff --git a/test/behavior.zig b/test/behavior.zig index 5e742e48a322..d2b2e9e23068 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -13,6 +13,5 @@ comptime { _ = @import("cases/null.zig"); _ = @import("cases/struct.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); - _ = @import("cases/switch_prong_implicit_cast.zig"); _ = @import("cases/try.zig"); } diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig deleted file mode 100644 index 4ca031e2e11b..000000000000 --- a/test/cases/switch_prong_implicit_cast.zig +++ /dev/null @@ -1,22 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -const FormValue = union(enum) { - One: void, - Two: bool, -}; - -fn foo(id: u64) !FormValue { - return switch (id) { - 2 => FormValue{ .Two = true }, - 1 => FormValue{ .One = {} }, - else => return error.Whatever, - }; -} - -test "switch prong implicit cast" { - const result = switch (foo(2) catch unreachable) { - FormValue.One => false, - FormValue.Two => |x| x, - }; - assertOrPanic(result); -} diff --git a/test/stage1/behavior/switch_prong_implicit_cast.zig b/test/stage1/behavior/switch_prong_implicit_cast.zig index 41c447315af6..4ca031e2e11b 100644 --- a/test/stage1/behavior/switch_prong_implicit_cast.zig +++ b/test/stage1/behavior/switch_prong_implicit_cast.zig @@ -1,2 +1,22 @@ const assertOrPanic = @import("std").debug.assertOrPanic; +const FormValue = union(enum) { + One: void, + Two: bool, +}; + +fn foo(id: u64) !FormValue { + return switch (id) { + 2 => FormValue{ .Two = true }, + 1 => FormValue{ .One = {} }, + else => return error.Whatever, + }; +} + +test "switch prong implicit cast" { + const result = switch (foo(2) catch unreachable) { + FormValue.One => false, + FormValue.Two => |x| x, + }; + assertOrPanic(result); +} From c357610f6f136b7953a9856d365c8ffc810227b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 Jan 2019 17:48:27 -0500 Subject: [PATCH 144/190] copy elision: move passing tests --- test/behavior.zig | 1 - test/cases/struct.zig | 458 -------------------------------- test/stage1/behavior/struct.zig | 454 +++++++++++++++++++++++++++++++ 3 files changed, 454 insertions(+), 459 deletions(-) delete mode 100644 test/cases/struct.zig diff --git a/test/behavior.zig b/test/behavior.zig index d2b2e9e23068..9c1b3f5be512 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -11,7 +11,6 @@ comptime { _ = @import("cases/eval.zig"); _ = @import("cases/ir_block_deps.zig"); _ = @import("cases/null.zig"); - _ = @import("cases/struct.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); _ = @import("cases/try.zig"); } diff --git a/test/cases/struct.zig b/test/cases/struct.zig deleted file mode 100644 index 0b1137a21d03..000000000000 --- a/test/cases/struct.zig +++ /dev/null @@ -1,458 +0,0 @@ -const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; -const builtin = @import("builtin"); -const maxInt = std.math.maxInt; - -test "return empty struct instance" { - _ = returnEmptyStructInstance(); -} -fn returnEmptyStructInstance() StructWithNoFields { - return empty_global_instance; -} - -const should_be_11 = StructWithNoFields.add(5, 6); - -test "invake static method in global scope" { - assertOrPanic(should_be_11 == 11); -} - -test "void struct fields" { - const foo = VoidStructFieldsFoo{ - .a = void{}, - .b = 1, - .c = void{}, - }; - assertOrPanic(foo.b == 1); - assertOrPanic(@sizeOf(VoidStructFieldsFoo) == 4); -} -const VoidStructFieldsFoo = struct { - a: void, - b: i32, - c: void, -}; - -test "structs" { - var foo: StructFoo = undefined; - @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); - foo.a += 1; - foo.b = foo.a == 1; - testFoo(foo); - testMutation(&foo); - assertOrPanic(foo.c == 100); -} -const StructFoo = struct { - a: i32, - b: bool, - c: f32, -}; -fn testFoo(foo: StructFoo) void { - assertOrPanic(foo.b); -} -fn testMutation(foo: *StructFoo) void { - foo.c = 100; -} - -const Node = struct { - val: Val, - next: *Node, -}; - -const Val = struct { - x: i32, -}; - -test "struct point to self" { - var root: Node = undefined; - root.val.x = 1; - - var node: Node = undefined; - node.next = &root; - node.val.x = 2; - - root.next = &node; - - assertOrPanic(node.next.next.next.val.x == 1); -} - -test "struct byval assign" { - var foo1: StructFoo = undefined; - var foo2: StructFoo = undefined; - - foo1.a = 1234; - foo2.a = 0; - assertOrPanic(foo2.a == 0); - foo2 = foo1; - assertOrPanic(foo2.a == 1234); -} - -fn structInitializer() void { - const val = Val{ .x = 42 }; - assertOrPanic(val.x == 42); -} - -test "fn call of struct field" { - assertOrPanic(callStructField(Foo{ .ptr = aFunc }) == 13); -} - -const Foo = struct { - ptr: fn () i32, -}; - -fn aFunc() i32 { - return 13; -} - -fn callStructField(foo: Foo) i32 { - return foo.ptr(); -} - -test "store member function in variable" { - const instance = MemberFnTestFoo{ .x = 1234 }; - const memberFn = MemberFnTestFoo.member; - const result = memberFn(instance); - assertOrPanic(result == 1234); -} -const MemberFnTestFoo = struct { - x: i32, - fn member(foo: MemberFnTestFoo) i32 { - return foo.x; - } -}; - -test "call member function directly" { - const instance = MemberFnTestFoo{ .x = 1234 }; - const result = MemberFnTestFoo.member(instance); - assertOrPanic(result == 1234); -} - -test "member functions" { - const r = MemberFnRand{ .seed = 1234 }; - assertOrPanic(r.getSeed() == 1234); -} -const MemberFnRand = struct { - seed: u32, - pub fn getSeed(r: *const MemberFnRand) u32 { - return r.seed; - } -}; - -test "return struct byval from function" { - const bar = makeBar(1234, 5678); - assertOrPanic(bar.y == 5678); -} -const Bar = struct { - x: i32, - y: i32, -}; -fn makeBar(x: i32, y: i32) Bar { - return Bar{ - .x = x, - .y = y, - }; -} - -test "empty struct method call" { - const es = EmptyStruct{}; - assertOrPanic(es.method() == 1234); -} -const EmptyStruct = struct { - fn method(es: *const EmptyStruct) i32 { - return 1234; - } -}; - -test "return empty struct from fn" { - _ = testReturnEmptyStructFromFn(); -} -const EmptyStruct2 = struct {}; -fn testReturnEmptyStructFromFn() EmptyStruct2 { - return EmptyStruct2{}; -} - -test "pass slice of empty struct to fn" { - assertOrPanic(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); -} -fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { - return slice.len; -} - -const APackedStruct = packed struct { - x: u8, - y: u8, -}; - -test "packed struct" { - var foo = APackedStruct{ - .x = 1, - .y = 2, - }; - foo.y += 1; - const four = foo.x + foo.y; - assertOrPanic(four == 4); -} - -const BitField1 = packed struct { - a: u3, - b: u3, - c: u2, -}; - -const bit_field_1 = BitField1{ - .a = 1, - .b = 2, - .c = 3, -}; - -test "bit field access" { - var data = bit_field_1; - assertOrPanic(getA(&data) == 1); - assertOrPanic(getB(&data) == 2); - assertOrPanic(getC(&data) == 3); - comptime assertOrPanic(@sizeOf(BitField1) == 1); - - data.b += 1; - assertOrPanic(data.b == 3); - - data.a += 1; - assertOrPanic(data.a == 2); - assertOrPanic(data.b == 3); -} - -fn getA(data: *const BitField1) u3 { - return data.a; -} - -fn getB(data: *const BitField1) u3 { - return data.b; -} - -fn getC(data: *const BitField1) u2 { - return data.c; -} - -const Foo24Bits = packed struct { - field: u24, -}; -const Foo96Bits = packed struct { - a: u24, - b: u24, - c: u24, - d: u24, -}; - -test "packed struct 24bits" { - comptime { - assertOrPanic(@sizeOf(Foo24Bits) == 3); - assertOrPanic(@sizeOf(Foo96Bits) == 12); - } - - var value = Foo96Bits{ - .a = 0, - .b = 0, - .c = 0, - .d = 0, - }; - value.a += 1; - assertOrPanic(value.a == 1); - assertOrPanic(value.b == 0); - assertOrPanic(value.c == 0); - assertOrPanic(value.d == 0); - - value.b += 1; - assertOrPanic(value.a == 1); - assertOrPanic(value.b == 1); - assertOrPanic(value.c == 0); - assertOrPanic(value.d == 0); - - value.c += 1; - assertOrPanic(value.a == 1); - assertOrPanic(value.b == 1); - assertOrPanic(value.c == 1); - assertOrPanic(value.d == 0); - - value.d += 1; - assertOrPanic(value.a == 1); - assertOrPanic(value.b == 1); - assertOrPanic(value.c == 1); - assertOrPanic(value.d == 1); -} - -const FooArray24Bits = packed struct { - a: u16, - b: [2]Foo24Bits, - c: u16, -}; - -test "packed array 24bits" { - comptime { - assertOrPanic(@sizeOf([9]Foo24Bits) == 9 * 3); - assertOrPanic(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); - } - - var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); - bytes[bytes.len - 1] = 0xaa; - const ptr = &@bytesToSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; - assertOrPanic(ptr.a == 0); - assertOrPanic(ptr.b[0].field == 0); - assertOrPanic(ptr.b[1].field == 0); - assertOrPanic(ptr.c == 0); - - ptr.a = maxInt(u16); - assertOrPanic(ptr.a == maxInt(u16)); - assertOrPanic(ptr.b[0].field == 0); - assertOrPanic(ptr.b[1].field == 0); - assertOrPanic(ptr.c == 0); - - ptr.b[0].field = maxInt(u24); - assertOrPanic(ptr.a == maxInt(u16)); - assertOrPanic(ptr.b[0].field == maxInt(u24)); - assertOrPanic(ptr.b[1].field == 0); - assertOrPanic(ptr.c == 0); - - ptr.b[1].field = maxInt(u24); - assertOrPanic(ptr.a == maxInt(u16)); - assertOrPanic(ptr.b[0].field == maxInt(u24)); - assertOrPanic(ptr.b[1].field == maxInt(u24)); - assertOrPanic(ptr.c == 0); - - ptr.c = maxInt(u16); - assertOrPanic(ptr.a == maxInt(u16)); - assertOrPanic(ptr.b[0].field == maxInt(u24)); - assertOrPanic(ptr.b[1].field == maxInt(u24)); - assertOrPanic(ptr.c == maxInt(u16)); - - assertOrPanic(bytes[bytes.len - 1] == 0xaa); -} - -const FooStructAligned = packed struct { - a: u8, - b: u8, -}; - -const FooArrayOfAligned = packed struct { - a: [2]FooStructAligned, -}; - -test "aligned array of packed struct" { - comptime { - assertOrPanic(@sizeOf(FooStructAligned) == 2); - assertOrPanic(@sizeOf(FooArrayOfAligned) == 2 * 2); - } - - var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); - const ptr = &@bytesToSlice(FooArrayOfAligned, bytes[0..bytes.len])[0]; - - assertOrPanic(ptr.a[0].a == 0xbb); - assertOrPanic(ptr.a[0].b == 0xbb); - assertOrPanic(ptr.a[1].a == 0xbb); - assertOrPanic(ptr.a[1].b == 0xbb); -} - -test "runtime struct initialization of bitfield" { - const s1 = Nibbles{ - .x = x1, - .y = x1, - }; - const s2 = Nibbles{ - .x = @intCast(u4, x2), - .y = @intCast(u4, x2), - }; - - assertOrPanic(s1.x == x1); - assertOrPanic(s1.y == x1); - assertOrPanic(s2.x == @intCast(u4, x2)); - assertOrPanic(s2.y == @intCast(u4, x2)); -} - -var x1 = u4(1); -var x2 = u8(2); - -const Nibbles = packed struct { - x: u4, - y: u4, -}; - -const Bitfields = packed struct { - f1: u16, - f2: u16, - f3: u8, - f4: u8, - f5: u4, - f6: u4, - f7: u8, -}; - -test "native bit field understands endianness" { - var all: u64 = 0x7765443322221111; - var bytes: [8]u8 = undefined; - @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); - var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; - - assertOrPanic(bitfields.f1 == 0x1111); - assertOrPanic(bitfields.f2 == 0x2222); - assertOrPanic(bitfields.f3 == 0x33); - assertOrPanic(bitfields.f4 == 0x44); - assertOrPanic(bitfields.f5 == 0x5); - assertOrPanic(bitfields.f6 == 0x6); - assertOrPanic(bitfields.f7 == 0x77); -} - -test "align 1 field before self referential align 8 field as slice return type" { - const result = alloc(Expr); - assertOrPanic(result.len == 0); -} - -const Expr = union(enum) { - Literal: u8, - Question: *Expr, -}; - -fn alloc(comptime T: type) []T { - return []T{}; -} - -test "call method with mutable reference to struct with no fields" { - const S = struct { - fn doC(s: *const @This()) bool { - return true; - } - fn do(s: *@This()) bool { - return true; - } - }; - - var s = S{}; - assertOrPanic(S.doC(&s)); - assertOrPanic(s.doC()); - assertOrPanic(S.do(&s)); - assertOrPanic(s.do()); -} - -test "implicit cast packed struct field to const ptr" { - const LevelUpMove = packed struct { - move_id: u9, - level: u7, - - fn toInt(value: u7) u7 { - return value; - } - }; - - var lup: LevelUpMove = undefined; - lup.level = 12; - const res = LevelUpMove.toInt(lup.level); - assertOrPanic(res == 12); -} - -test "pointer to packed struct member in a stack variable" { - const S = packed struct { - a: u2, - b: u2, - }; - - var s = S{ .a = 2, .b = 0 }; - var b_ptr = &s.b; - assertOrPanic(s.b == 0); - b_ptr.* = 2; - assertOrPanic(s.b == 2); -} diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig index ab82ff33616a..92ae2baa152d 100644 --- a/test/stage1/behavior/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -14,3 +14,457 @@ test "call struct static method" { const result = StructWithNoFields.add(3, 4); assertOrPanic(result == 7); } + +test "return empty struct instance" { + _ = returnEmptyStructInstance(); +} +fn returnEmptyStructInstance() StructWithNoFields { + return empty_global_instance; +} + +const should_be_11 = StructWithNoFields.add(5, 6); + +test "invoke static method in global scope" { + assertOrPanic(should_be_11 == 11); +} + +test "void struct fields" { + const foo = VoidStructFieldsFoo{ + .a = void{}, + .b = 1, + .c = void{}, + }; + assertOrPanic(foo.b == 1); + assertOrPanic(@sizeOf(VoidStructFieldsFoo) == 4); +} +const VoidStructFieldsFoo = struct { + a: void, + b: i32, + c: void, +}; + +test "structs" { + var foo: StructFoo = undefined; + @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); + foo.a += 1; + foo.b = foo.a == 1; + testFoo(foo); + testMutation(&foo); + assertOrPanic(foo.c == 100); +} +const StructFoo = struct { + a: i32, + b: bool, + c: f32, +}; +fn testFoo(foo: StructFoo) void { + assertOrPanic(foo.b); +} +fn testMutation(foo: *StructFoo) void { + foo.c = 100; +} + +const Node = struct { + val: Val, + next: *Node, +}; + +const Val = struct { + x: i32, +}; + +test "struct point to self" { + var root: Node = undefined; + root.val.x = 1; + + var node: Node = undefined; + node.next = &root; + node.val.x = 2; + + root.next = &node; + + assertOrPanic(node.next.next.next.val.x == 1); +} + +test "struct byval assign" { + var foo1: StructFoo = undefined; + var foo2: StructFoo = undefined; + + foo1.a = 1234; + foo2.a = 0; + assertOrPanic(foo2.a == 0); + foo2 = foo1; + assertOrPanic(foo2.a == 1234); +} + +fn structInitializer() void { + const val = Val{ .x = 42 }; + assertOrPanic(val.x == 42); +} + +test "fn call of struct field" { + assertOrPanic(callStructField(Foo{ .ptr = aFunc }) == 13); +} + +const Foo = struct { + ptr: fn () i32, +}; + +fn aFunc() i32 { + return 13; +} + +fn callStructField(foo: Foo) i32 { + return foo.ptr(); +} + +test "store member function in variable" { + const instance = MemberFnTestFoo{ .x = 1234 }; + const memberFn = MemberFnTestFoo.member; + const result = memberFn(instance); + assertOrPanic(result == 1234); +} +const MemberFnTestFoo = struct { + x: i32, + fn member(foo: MemberFnTestFoo) i32 { + return foo.x; + } +}; + +test "call member function directly" { + const instance = MemberFnTestFoo{ .x = 1234 }; + const result = MemberFnTestFoo.member(instance); + assertOrPanic(result == 1234); +} + +test "member functions" { + const r = MemberFnRand{ .seed = 1234 }; + assertOrPanic(r.getSeed() == 1234); +} +const MemberFnRand = struct { + seed: u32, + pub fn getSeed(r: *const MemberFnRand) u32 { + return r.seed; + } +}; + +test "return struct byval from function" { + const bar = makeBar(1234, 5678); + assertOrPanic(bar.y == 5678); +} +const Bar = struct { + x: i32, + y: i32, +}; +fn makeBar(x: i32, y: i32) Bar { + return Bar{ + .x = x, + .y = y, + }; +} + +test "empty struct method call" { + const es = EmptyStruct{}; + assertOrPanic(es.method() == 1234); +} +const EmptyStruct = struct { + fn method(es: *const EmptyStruct) i32 { + return 1234; + } +}; + +test "return empty struct from fn" { + _ = testReturnEmptyStructFromFn(); +} +const EmptyStruct2 = struct {}; +fn testReturnEmptyStructFromFn() EmptyStruct2 { + return EmptyStruct2{}; +} + +test "pass slice of empty struct to fn" { + assertOrPanic(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); +} +fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { + return slice.len; +} + +const APackedStruct = packed struct { + x: u8, + y: u8, +}; + +test "packed struct" { + var foo = APackedStruct{ + .x = 1, + .y = 2, + }; + foo.y += 1; + const four = foo.x + foo.y; + assertOrPanic(four == 4); +} + +const BitField1 = packed struct { + a: u3, + b: u3, + c: u2, +}; + +const bit_field_1 = BitField1{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "bit field access" { + var data = bit_field_1; + assertOrPanic(getA(&data) == 1); + assertOrPanic(getB(&data) == 2); + assertOrPanic(getC(&data) == 3); + comptime assertOrPanic(@sizeOf(BitField1) == 1); + + data.b += 1; + assertOrPanic(data.b == 3); + + data.a += 1; + assertOrPanic(data.a == 2); + assertOrPanic(data.b == 3); +} + +fn getA(data: *const BitField1) u3 { + return data.a; +} + +fn getB(data: *const BitField1) u3 { + return data.b; +} + +fn getC(data: *const BitField1) u2 { + return data.c; +} + +const Foo24Bits = packed struct { + field: u24, +}; +const Foo96Bits = packed struct { + a: u24, + b: u24, + c: u24, + d: u24, +}; + +test "packed struct 24bits" { + comptime { + assertOrPanic(@sizeOf(Foo24Bits) == 3); + assertOrPanic(@sizeOf(Foo96Bits) == 12); + } + + var value = Foo96Bits{ + .a = 0, + .b = 0, + .c = 0, + .d = 0, + }; + value.a += 1; + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 0); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); + + value.b += 1; + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); + + value.c += 1; + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 0); + + value.d += 1; + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 1); +} + +const FooArray24Bits = packed struct { + a: u16, + b: [2]Foo24Bits, + c: u16, +}; + +test "packed array 24bits" { + comptime { + assertOrPanic(@sizeOf([9]Foo24Bits) == 9 * 3); + assertOrPanic(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); + } + + var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); + bytes[bytes.len - 1] = 0xaa; + const ptr = &@bytesToSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; + assertOrPanic(ptr.a == 0); + assertOrPanic(ptr.b[0].field == 0); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); + + ptr.a = maxInt(u16); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == 0); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); + + ptr.b[0].field = maxInt(u24); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); + + ptr.b[1].field = maxInt(u24); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == maxInt(u24)); + assertOrPanic(ptr.c == 0); + + ptr.c = maxInt(u16); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == maxInt(u24)); + assertOrPanic(ptr.c == maxInt(u16)); + + assertOrPanic(bytes[bytes.len - 1] == 0xaa); +} + +const FooStructAligned = packed struct { + a: u8, + b: u8, +}; + +const FooArrayOfAligned = packed struct { + a: [2]FooStructAligned, +}; + +test "aligned array of packed struct" { + comptime { + assertOrPanic(@sizeOf(FooStructAligned) == 2); + assertOrPanic(@sizeOf(FooArrayOfAligned) == 2 * 2); + } + + var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); + const ptr = &@bytesToSlice(FooArrayOfAligned, bytes[0..bytes.len])[0]; + + assertOrPanic(ptr.a[0].a == 0xbb); + assertOrPanic(ptr.a[0].b == 0xbb); + assertOrPanic(ptr.a[1].a == 0xbb); + assertOrPanic(ptr.a[1].b == 0xbb); +} + +test "runtime struct initialization of bitfield" { + const s1 = Nibbles{ + .x = x1, + .y = x1, + }; + const s2 = Nibbles{ + .x = @intCast(u4, x2), + .y = @intCast(u4, x2), + }; + + assertOrPanic(s1.x == x1); + assertOrPanic(s1.y == x1); + assertOrPanic(s2.x == @intCast(u4, x2)); + assertOrPanic(s2.y == @intCast(u4, x2)); +} + +var x1 = u4(1); +var x2 = u8(2); + +const Nibbles = packed struct { + x: u4, + y: u4, +}; + +const Bitfields = packed struct { + f1: u16, + f2: u16, + f3: u8, + f4: u8, + f5: u4, + f6: u4, + f7: u8, +}; + +test "native bit field understands endianness" { + var all: u64 = 0x7765443322221111; + var bytes: [8]u8 = undefined; + @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); + var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; + + assertOrPanic(bitfields.f1 == 0x1111); + assertOrPanic(bitfields.f2 == 0x2222); + assertOrPanic(bitfields.f3 == 0x33); + assertOrPanic(bitfields.f4 == 0x44); + assertOrPanic(bitfields.f5 == 0x5); + assertOrPanic(bitfields.f6 == 0x6); + assertOrPanic(bitfields.f7 == 0x77); +} + +test "align 1 field before self referential align 8 field as slice return type" { + const result = alloc(Expr); + assertOrPanic(result.len == 0); +} + +const Expr = union(enum) { + Literal: u8, + Question: *Expr, +}; + +fn alloc(comptime T: type) []T { + return []T{}; +} + +test "call method with mutable reference to struct with no fields" { + const S = struct { + fn doC(s: *const @This()) bool { + return true; + } + fn do(s: *@This()) bool { + return true; + } + }; + + var s = S{}; + assertOrPanic(S.doC(&s)); + assertOrPanic(s.doC()); + assertOrPanic(S.do(&s)); + assertOrPanic(s.do()); +} + +test "implicit cast packed struct field to const ptr" { + const LevelUpMove = packed struct { + move_id: u9, + level: u7, + + fn toInt(value: u7) u7 { + return value; + } + }; + + var lup: LevelUpMove = undefined; + lup.level = 12; + const res = LevelUpMove.toInt(lup.level); + assertOrPanic(res == 12); +} + +test "pointer to packed struct member in a stack variable" { + const S = packed struct { + a: u2, + b: u2, + }; + + var s = S{ .a = 2, .b = 0 }; + var b_ptr = &s.b; + assertOrPanic(s.b == 0); + b_ptr.* = 2; + assertOrPanic(s.b == 2); +} From bbb2d78ac508d131e0423d749529e2173276be5b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 Jan 2019 17:51:11 -0500 Subject: [PATCH 145/190] copy elision: move passing tests --- test/cases/eval.zig | 35 ----------------------------------- test/stage1/behavior.zig | 2 +- test/stage1/behavior/eval.zig | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 180284ac515b..6597a9603b88 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -2,41 +2,6 @@ const std = @import("std"); const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); -fn unwrapAndAddOne(blah: ?i32) i32 { - return blah.? + 1; -} -const should_be_1235 = unwrapAndAddOne(1234); -test "static add one" { - assertOrPanic(should_be_1235 == 1235); -} - -test "inlined loop" { - comptime var i = 0; - comptime var sum = 0; - inline while (i <= 5) : (i += 1) - sum += i; - assertOrPanic(sum == 15); -} - -fn gimme1or2(comptime a: bool) i32 { - const x: i32 = 1; - const y: i32 = 2; - comptime var z: i32 = if (a) x else y; - return z; -} -test "inline variable gets result of const if" { - assertOrPanic(gimme1or2(true) == 1); - assertOrPanic(gimme1or2(false) == 2); -} - -test "static function evaluation" { - assertOrPanic(statically_added_number == 3); -} -const statically_added_number = staticAdd(1, 2); -fn staticAdd(a: i32, b: i32) i32 { - return a + b; -} - test "const expr eval on single expr blocks" { assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, true) == 3); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 80b87fc4cbc7..1eb6aa658847 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -32,7 +32,6 @@ comptime { _ = @import("behavior/enum.zig"); _ = @import("behavior/enum_with_members.zig"); _ = @import("behavior/error.zig"); - _ = @import("behavior/eval.zig"); _ = @import("behavior/field_parent_ptr.zig"); _ = @import("behavior/fn.zig"); _ = @import("behavior/fn_in_struct_in_comptime.zig"); @@ -75,4 +74,5 @@ comptime { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); + _ = @import("behavior/eval.zig"); } diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig index 6144e1c2277f..aac98cf6a802 100644 --- a/test/stage1/behavior/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -11,3 +11,38 @@ fn fibonacci(x: i32) i32 { return fibonacci(x - 1) + fibonacci(x - 2); } +fn unwrapAndAddOne(blah: ?i32) i32 { + return blah.? + 1; +} +const should_be_1235 = unwrapAndAddOne(1234); +test "static add one" { + assertOrPanic(should_be_1235 == 1235); +} + +test "inlined loop" { + comptime var i = 0; + comptime var sum = 0; + inline while (i <= 5) : (i += 1) + sum += i; + assertOrPanic(sum == 15); +} + +fn gimme1or2(comptime a: bool) i32 { + const x: i32 = 1; + const y: i32 = 2; + comptime var z: i32 = if (a) x else y; + return z; +} +test "inline variable gets result of const if" { + assertOrPanic(gimme1or2(true) == 1); + assertOrPanic(gimme1or2(false) == 2); +} + +test "static function evaluation" { + assertOrPanic(statically_added_number == 3); +} +const statically_added_number = staticAdd(1, 2); +fn staticAdd(a: i32, b: i32) i32 { + return a + b; +} + From 64ae78fb3007c930a2635b3cfa90fd0ba0480195 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 3 Jan 2019 17:59:27 -0500 Subject: [PATCH 146/190] allow writing null to pointer in runtime branch --- src/ir.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 7d6d6168738c..360287cb3865 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14515,9 +14515,17 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source case ReqCompTimeInvalid: return ira->codegen->invalid_instruction; case ReqCompTimeYes: - ir_add_error(ira, source_instr, - buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name))); - return ira->codegen->invalid_instruction; + switch (type_has_one_possible_value(ira->codegen, ptr->value.type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name))); + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const_void(ira, source_instr); + } + zig_unreachable(); case ReqCompTimeNo: break; } From 9e2de5c60f52558c67c755487bd4ad245ae1de84 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 3 Jan 2019 18:14:23 -0500 Subject: [PATCH 147/190] copy elision: move passing tests --- test/cases/eval.zig | 709 --------------------------------- test/stage1/behavior/eval.zig | 716 ++++++++++++++++++++++++++++++++++ 2 files changed, 716 insertions(+), 709 deletions(-) diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 6597a9603b88..9ebd38600363 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -2,522 +2,6 @@ const std = @import("std"); const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); -test "const expr eval on single expr blocks" { - assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, true) == 3); -} - -fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { - const literal = 3; - - const result = if (b) b: { - break :b literal; - } else b: { - break :b x; - }; - - return result; -} - -test "statically initialized list" { - assertOrPanic(static_point_list[0].x == 1); - assertOrPanic(static_point_list[0].y == 2); - assertOrPanic(static_point_list[1].x == 3); - assertOrPanic(static_point_list[1].y == 4); -} -const Point = struct { - x: i32, - y: i32, -}; -const static_point_list = []Point{ - makePoint(1, 2), - makePoint(3, 4), -}; -fn makePoint(x: i32, y: i32) Point { - return Point{ - .x = x, - .y = y, - }; -} - -test "static eval list init" { - assertOrPanic(static_vec3.data[2] == 1.0); - assertOrPanic(vec3(0.0, 0.0, 3.0).data[2] == 3.0); -} -const static_vec3 = vec3(0.0, 0.0, 1.0); -pub const Vec3 = struct { - data: [3]f32, -}; -pub fn vec3(x: f32, y: f32, z: f32) Vec3 { - return Vec3{ .data = []f32{ - x, - y, - z, - } }; -} - -test "constant expressions" { - var array: [array_size]u8 = undefined; - assertOrPanic(@sizeOf(@typeOf(array)) == 20); -} -const array_size: u8 = 20; - -test "constant struct with negation" { - assertOrPanic(vertices[0].x == -0.6); -} -const Vertex = struct { - x: f32, - y: f32, - r: f32, - g: f32, - b: f32, -}; -const vertices = []Vertex{ - Vertex{ - .x = -0.6, - .y = -0.4, - .r = 1.0, - .g = 0.0, - .b = 0.0, - }, - Vertex{ - .x = 0.6, - .y = -0.4, - .r = 0.0, - .g = 1.0, - .b = 0.0, - }, - Vertex{ - .x = 0.0, - .y = 0.6, - .r = 0.0, - .g = 0.0, - .b = 1.0, - }, -}; - -test "statically initialized struct" { - st_init_str_foo.x += 1; - assertOrPanic(st_init_str_foo.x == 14); -} -const StInitStrFoo = struct { - x: i32, - y: bool, -}; -var st_init_str_foo = StInitStrFoo{ - .x = 13, - .y = true, -}; - -test "statically initalized array literal" { - const y: [4]u8 = st_init_arr_lit_x; - assertOrPanic(y[3] == 4); -} -const st_init_arr_lit_x = []u8{ - 1, - 2, - 3, - 4, -}; - -test "const slice" { - comptime { - const a = "1234567890"; - assertOrPanic(a.len == 10); - const b = a[1..2]; - assertOrPanic(b.len == 1); - assertOrPanic(b[0] == '2'); - } -} - -test "try to trick eval with runtime if" { - assertOrPanic(testTryToTrickEvalWithRuntimeIf(true) == 10); -} - -fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { - comptime var i: usize = 0; - inline while (i < 10) : (i += 1) { - const result = if (b) false else true; - } - comptime { - return i; - } -} - -fn max(comptime T: type, a: T, b: T) T { - if (T == bool) { - return a or b; - } else if (a > b) { - return a; - } else { - return b; - } -} -fn letsTryToCompareBools(a: bool, b: bool) bool { - return max(bool, a, b); -} -test "inlined block and runtime block phi" { - assertOrPanic(letsTryToCompareBools(true, true)); - assertOrPanic(letsTryToCompareBools(true, false)); - assertOrPanic(letsTryToCompareBools(false, true)); - assertOrPanic(!letsTryToCompareBools(false, false)); - - comptime { - assertOrPanic(letsTryToCompareBools(true, true)); - assertOrPanic(letsTryToCompareBools(true, false)); - assertOrPanic(letsTryToCompareBools(false, true)); - assertOrPanic(!letsTryToCompareBools(false, false)); - } -} - -const CmdFn = struct { - name: []const u8, - func: fn (i32) i32, -}; - -const cmd_fns = []CmdFn{ - CmdFn{ - .name = "one", - .func = one, - }, - CmdFn{ - .name = "two", - .func = two, - }, - CmdFn{ - .name = "three", - .func = three, - }, -}; -fn one(value: i32) i32 { - return value + 1; -} -fn two(value: i32) i32 { - return value + 2; -} -fn three(value: i32) i32 { - return value + 3; -} - -fn performFn(comptime prefix_char: u8, start_value: i32) i32 { - var result: i32 = start_value; - comptime var i = 0; - inline while (i < cmd_fns.len) : (i += 1) { - if (cmd_fns[i].name[0] == prefix_char) { - result = cmd_fns[i].func(result); - } - } - return result; -} - -test "comptime iterate over fn ptr list" { - assertOrPanic(performFn('t', 1) == 6); - assertOrPanic(performFn('o', 0) == 1); - assertOrPanic(performFn('w', 99) == 99); -} - -test "eval @setRuntimeSafety at compile-time" { - const result = comptime fnWithSetRuntimeSafety(); - assertOrPanic(result == 1234); -} - -fn fnWithSetRuntimeSafety() i32 { - @setRuntimeSafety(true); - return 1234; -} - -test "eval @setFloatMode at compile-time" { - const result = comptime fnWithFloatMode(); - assertOrPanic(result == 1234.0); -} - -fn fnWithFloatMode() f32 { - @setFloatMode(builtin.FloatMode.Strict); - return 1234.0; -} - -const SimpleStruct = struct { - field: i32, - - fn method(self: *const SimpleStruct) i32 { - return self.field + 3; - } -}; - -var simple_struct = SimpleStruct{ .field = 1234 }; - -const bound_fn = simple_struct.method; - -test "call method on bound fn referring to var instance" { - assertOrPanic(bound_fn() == 1237); -} - -test "ptr to local array argument at comptime" { - comptime { - var bytes: [10]u8 = undefined; - modifySomeBytes(bytes[0..]); - assertOrPanic(bytes[0] == 'a'); - assertOrPanic(bytes[9] == 'b'); - } -} - -fn modifySomeBytes(bytes: []u8) void { - bytes[0] = 'a'; - bytes[9] = 'b'; -} - -test "comparisons 0 <= uint and 0 > uint should be comptime" { - testCompTimeUIntComparisons(1234); -} -fn testCompTimeUIntComparisons(x: u32) void { - if (!(0 <= x)) { - @compileError("this condition should be comptime known"); - } - if (0 > x) { - @compileError("this condition should be comptime known"); - } - if (!(x >= 0)) { - @compileError("this condition should be comptime known"); - } - if (x < 0) { - @compileError("this condition should be comptime known"); - } -} - -test "const ptr to variable data changes at runtime" { - assertOrPanic(foo_ref.name[0] == 'a'); - foo_ref.name = "b"; - assertOrPanic(foo_ref.name[0] == 'b'); -} - -const Foo = struct { - name: []const u8, -}; - -var foo_contents = Foo{ .name = "a" }; -const foo_ref = &foo_contents; - -test "create global array with for loop" { - assertOrPanic(global_array[5] == 5 * 5); - assertOrPanic(global_array[9] == 9 * 9); -} - -const global_array = x: { - var result: [10]usize = undefined; - for (result) |*item, index| { - item.* = index * index; - } - break :x result; -}; - -test "compile-time downcast when the bits fit" { - comptime { - const spartan_count: u16 = 255; - const byte = @intCast(u8, spartan_count); - assertOrPanic(byte == 255); - } -} - -const hi1 = "hi"; -const hi2 = hi1; -test "const global shares pointer with other same one" { - assertEqualPtrs(&hi1[0], &hi2[0]); - comptime assertOrPanic(&hi1[0] == &hi2[0]); -} -fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { - assertOrPanic(ptr1 == ptr2); -} - -test "@setEvalBranchQuota" { - comptime { - // 1001 for the loop and then 1 more for the assertOrPanic fn call - @setEvalBranchQuota(1002); - var i = 0; - var sum = 0; - while (i < 1001) : (i += 1) { - sum += i; - } - assertOrPanic(sum == 500500); - } -} - -// TODO test "float literal at compile time not lossy" { -// TODO assertOrPanic(16777216.0 + 1.0 == 16777217.0); -// TODO assertOrPanic(9007199254740992.0 + 1.0 == 9007199254740993.0); -// TODO } - -test "f32 at compile time is lossy" { - assertOrPanic(f32(1 << 24) + 1 == 1 << 24); -} - -test "f64 at compile time is lossy" { - assertOrPanic(f64(1 << 53) + 1 == 1 << 53); -} - -test "f128 at compile time is lossy" { - assertOrPanic(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); -} - -// TODO need a better implementation of bigfloat_init_bigint -// assertOrPanic(f128(1 << 113) == 10384593717069655257060992658440192); - -pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { - return struct { - pub const Node = struct {}; - }; -} - -test "string literal used as comptime slice is memoized" { - const a = "link"; - const b = "link"; - comptime assertOrPanic(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); - comptime assertOrPanic(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); -} - -test "comptime slice of undefined pointer of length 0" { - const slice1 = ([*]i32)(undefined)[0..0]; - assertOrPanic(slice1.len == 0); - const slice2 = ([*]i32)(undefined)[100..100]; - assertOrPanic(slice2.len == 0); -} - -fn copyWithPartialInline(s: []u32, b: []u8) void { - comptime var i: usize = 0; - inline while (i < 4) : (i += 1) { - s[i] = 0; - s[i] |= u32(b[i * 4 + 0]) << 24; - s[i] |= u32(b[i * 4 + 1]) << 16; - s[i] |= u32(b[i * 4 + 2]) << 8; - s[i] |= u32(b[i * 4 + 3]) << 0; - } -} - -test "binary math operator in partially inlined function" { - var s: [4]u32 = undefined; - var b: [16]u8 = undefined; - - for (b) |*r, i| - r.* = @intCast(u8, i + 1); - - copyWithPartialInline(s[0..], b[0..]); - assertOrPanic(s[0] == 0x1020304); - assertOrPanic(s[1] == 0x5060708); - assertOrPanic(s[2] == 0x90a0b0c); - assertOrPanic(s[3] == 0xd0e0f10); -} - -test "comptime function with the same args is memoized" { - comptime { - assertOrPanic(MakeType(i32) == MakeType(i32)); - assertOrPanic(MakeType(i32) != MakeType(f64)); - } -} - -fn MakeType(comptime T: type) type { - return struct { - field: T, - }; -} - -test "comptime function with mutable pointer is not memoized" { - comptime { - var x: i32 = 1; - const ptr = &x; - increment(ptr); - increment(ptr); - assertOrPanic(x == 3); - } -} - -fn increment(value: *i32) void { - value.* += 1; -} - -fn generateTable(comptime T: type) [1010]T { - var res: [1010]T = undefined; - var i: usize = 0; - while (i < 1010) : (i += 1) { - res[i] = @intCast(T, i); - } - return res; -} - -fn doesAlotT(comptime T: type, value: usize) T { - @setEvalBranchQuota(5000); - const table = comptime blk: { - break :blk generateTable(T); - }; - return table[value]; -} - -test "@setEvalBranchQuota at same scope as generic function call" { - assertOrPanic(doesAlotT(u32, 2) == 2); -} - -test "comptime slice of slice preserves comptime var" { - comptime { - var buff: [10]u8 = undefined; - buff[0..][0..][0] = 1; - assertOrPanic(buff[0..][0..][0] == 1); - } -} - -test "comptime slice of pointer preserves comptime var" { - comptime { - var buff: [10]u8 = undefined; - var a = buff[0..].ptr; - a[0..1][0] = 1; - assertOrPanic(buff[0..][0..][0] == 1); - } -} - -const SingleFieldStruct = struct { - x: i32, - - fn read_x(self: *const SingleFieldStruct) i32 { - return self.x; - } -}; -test "const ptr to comptime mutable data is not memoized" { - comptime { - var foo = SingleFieldStruct{ .x = 1 }; - assertOrPanic(foo.read_x() == 1); - foo.x = 2; - assertOrPanic(foo.read_x() == 2); - } -} - -test "array concat of slices gives slice" { - comptime { - var a: []const u8 = "aoeu"; - var b: []const u8 = "asdf"; - const c = a ++ b; - assertOrPanic(std.mem.eql(u8, c, "aoeuasdf")); - } -} - -test "comptime shlWithOverflow" { - const ct_shifted: u64 = comptime amt: { - var amt = u64(0); - _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); - break :amt amt; - }; - - const rt_shifted: u64 = amt: { - var amt = u64(0); - _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); - break :amt amt; - }; - - assertOrPanic(ct_shifted == rt_shifted); -} - -test "runtime 128 bit integer division" { - var a: u128 = 152313999999999991610955792383; - var b: u128 = 10000000000000000000; - var c = a / b; - assertOrPanic(c == 15231399999); -} - pub const Info = struct { version: u8, }; @@ -533,130 +17,6 @@ test "comptime modification of const struct field" { } } -test "pointer to type" { - comptime { - var T: type = i32; - assertOrPanic(T == i32); - var ptr = &T; - assertOrPanic(@typeOf(ptr) == *type); - ptr.* = f32; - assertOrPanic(T == f32); - assertOrPanic(*T == *f32); - } -} - -test "slice of type" { - comptime { - var types_array = []type{ i32, f64, type }; - for (types_array) |T, i| { - switch (i) { - 0 => assertOrPanic(T == i32), - 1 => assertOrPanic(T == f64), - 2 => assertOrPanic(T == type), - else => unreachable, - } - } - for (types_array[0..]) |T, i| { - switch (i) { - 0 => assertOrPanic(T == i32), - 1 => assertOrPanic(T == f64), - 2 => assertOrPanic(T == type), - else => unreachable, - } - } - } -} - -const Wrapper = struct { - T: type, -}; - -fn wrap(comptime T: type) Wrapper { - return Wrapper{ .T = T }; -} - -test "function which returns struct with type field causes implicit comptime" { - const ty = wrap(i32).T; - assertOrPanic(ty == i32); -} - -test "call method with comptime pass-by-non-copying-value self parameter" { - const S = struct { - a: u8, - - fn b(comptime s: @This()) u8 { - return s.a; - } - }; - - const s = S{ .a = 2 }; - var b = s.b(); - assertOrPanic(b == 2); -} - -test "@tagName of @typeId" { - const str = @tagName(@typeId(u8)); - assertOrPanic(std.mem.eql(u8, str, "Int")); -} - -test "setting backward branch quota just before a generic fn call" { - @setEvalBranchQuota(1001); - loopNTimes(1001); -} - -fn loopNTimes(comptime n: usize) void { - comptime var i = 0; - inline while (i < n) : (i += 1) {} -} - -test "variable inside inline loop that has different types on different iterations" { - testVarInsideInlineLoop(true, u32(42)); -} - -fn testVarInsideInlineLoop(args: ...) void { - comptime var i = 0; - inline while (i < args.len) : (i += 1) { - const x = args[i]; - if (i == 0) assertOrPanic(x); - if (i == 1) assertOrPanic(x == 42); - } -} - -test "inline for with same type but different values" { - var res: usize = 0; - inline for ([]type{ [2]u8, [1]u8, [2]u8 }) |T| { - var a: T = undefined; - res += a.len; - } - assertOrPanic(res == 5); -} - -test "refer to the type of a generic function" { - const Func = fn (type) void; - const f: Func = doNothingWithType; - f(i32); -} - -fn doNothingWithType(comptime T: type) void {} - -test "zero extend from u0 to u1" { - var zero_u0: u0 = 0; - var zero_u1: u1 = zero_u0; - assertOrPanic(zero_u1 == 0); -} - -test "bit shift a u1" { - var x: u1 = 1; - var y = x << 0; - assertOrPanic(y == 1); -} - -test "@intCast to a u0" { - var x: u8 = 0; - var y: u0 = @intCast(u0, x); - assertOrPanic(y == 0); -} - test "@bytesToslice on a packed struct" { const F = packed struct { a: u8, @@ -667,72 +27,3 @@ test "@bytesToslice on a packed struct" { assertOrPanic(f[0].a == 9); } -test "comptime pointer cast array and then slice" { - const array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; - - const ptrA: [*]const u8 = @ptrCast([*]const u8, &array); - const sliceA: []const u8 = ptrA[0..2]; - - const ptrB: [*]const u8 = &array; - const sliceB: []const u8 = ptrB[0..2]; - - assertOrPanic(sliceA[1] == 2); - assertOrPanic(sliceB[1] == 2); -} - -test "slice bounds in comptime concatenation" { - const bs = comptime blk: { - const b = c"11"; - break :blk b[0..1]; - }; - const str = "" ++ bs; - assertOrPanic(str.len == 1); - assertOrPanic(std.mem.eql(u8, str, "1")); - - const str2 = bs ++ ""; - assertOrPanic(str2.len == 1); - assertOrPanic(std.mem.eql(u8, str2, "1")); -} - -test "comptime bitwise operators" { - comptime { - assertOrPanic(3 & 1 == 1); - assertOrPanic(3 & -1 == 3); - assertOrPanic(-3 & -1 == -3); - assertOrPanic(3 | -1 == -1); - assertOrPanic(-3 | -1 == -1); - assertOrPanic(3 ^ -1 == -4); - assertOrPanic(-3 ^ -1 == 2); - assertOrPanic(~i8(-1) == 0); - assertOrPanic(~i128(-1) == 0); - assertOrPanic(18446744073709551615 & 18446744073709551611 == 18446744073709551611); - assertOrPanic(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); - assertOrPanic(~u128(0) == 0xffffffffffffffffffffffffffffffff); - } -} - -test "*align(1) u16 is the same as *align(1:0:2) u16" { - comptime { - assertOrPanic(*align(1:0:2) u16 == *align(1) u16); - // TODO add parsing support for this syntax - //assertOrPanic(*align(:0:2) u16 == *u16); - } -} - -test "array concatenation forces comptime" { - var a = oneItem(3) ++ oneItem(4); - assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 4 })); -} - -test "array multiplication forces comptime" { - var a = oneItem(3) ** scalar(2); - assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 3 })); -} - -fn oneItem(x: i32) [1]i32 { - return []i32{x}; -} - -fn scalar(x: u32) u32 { - return x; -} diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig index aac98cf6a802..0072a47bd35e 100644 --- a/test/stage1/behavior/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -46,3 +46,719 @@ fn staticAdd(a: i32, b: i32) i32 { return a + b; } +test "const expr eval on single expr blocks" { + assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, false) == 3); + comptime assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, false) == 3); +} + +fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { + const literal = 3; + + // TODO: See https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 + const result = if (b) b: { + break :b x; + } else b: { + break :b literal; + }; + + return result; +} + +test "statically initialized list" { + assertOrPanic(static_point_list[0].x == 1); + assertOrPanic(static_point_list[0].y == 2); + assertOrPanic(static_point_list[1].x == 3); + assertOrPanic(static_point_list[1].y == 4); +} +const Point = struct { + x: i32, + y: i32, +}; +const static_point_list = []Point{ + makePoint(1, 2), + makePoint(3, 4), +}; +fn makePoint(x: i32, y: i32) Point { + return Point{ + .x = x, + .y = y, + }; +} + +test "static eval list init" { + assertOrPanic(static_vec3.data[2] == 1.0); + assertOrPanic(vec3(0.0, 0.0, 3.0).data[2] == 3.0); +} +const static_vec3 = vec3(0.0, 0.0, 1.0); +pub const Vec3 = struct { + data: [3]f32, +}; +pub fn vec3(x: f32, y: f32, z: f32) Vec3 { + return Vec3{ .data = []f32{ + x, + y, + z, + } }; +} + +test "constant expressions" { + var array: [array_size]u8 = undefined; + assertOrPanic(@sizeOf(@typeOf(array)) == 20); +} +const array_size: u8 = 20; + +test "constant struct with negation" { + assertOrPanic(vertices[0].x == -0.6); +} +const Vertex = struct { + x: f32, + y: f32, + r: f32, + g: f32, + b: f32, +}; +const vertices = []Vertex{ + Vertex{ + .x = -0.6, + .y = -0.4, + .r = 1.0, + .g = 0.0, + .b = 0.0, + }, + Vertex{ + .x = 0.6, + .y = -0.4, + .r = 0.0, + .g = 1.0, + .b = 0.0, + }, + Vertex{ + .x = 0.0, + .y = 0.6, + .r = 0.0, + .g = 0.0, + .b = 1.0, + }, +}; + +test "statically initialized struct" { + st_init_str_foo.x += 1; + assertOrPanic(st_init_str_foo.x == 14); +} +const StInitStrFoo = struct { + x: i32, + y: bool, +}; +var st_init_str_foo = StInitStrFoo{ + .x = 13, + .y = true, +}; + +test "statically initalized array literal" { + const y: [4]u8 = st_init_arr_lit_x; + assertOrPanic(y[3] == 4); +} +const st_init_arr_lit_x = []u8{ + 1, + 2, + 3, + 4, +}; + +test "const slice" { + comptime { + const a = "1234567890"; + assertOrPanic(a.len == 10); + const b = a[1..2]; + assertOrPanic(b.len == 1); + assertOrPanic(b[0] == '2'); + } +} + +test "try to trick eval with runtime if" { + assertOrPanic(testTryToTrickEvalWithRuntimeIf(true) == 10); +} + +fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { + comptime var i: usize = 0; + inline while (i < 10) : (i += 1) { + const result = if (b) false else true; + } + comptime { + return i; + } +} + +fn max(comptime T: type, a: T, b: T) T { + if (T == bool) { + return a or b; + } else if (a > b) { + return a; + } else { + return b; + } +} +fn letsTryToCompareBools(a: bool, b: bool) bool { + return max(bool, a, b); +} +test "inlined block and runtime block phi" { + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); + + comptime { + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); + } +} + +const CmdFn = struct { + name: []const u8, + func: fn (i32) i32, +}; + +const cmd_fns = []CmdFn{ + CmdFn{ + .name = "one", + .func = one, + }, + CmdFn{ + .name = "two", + .func = two, + }, + CmdFn{ + .name = "three", + .func = three, + }, +}; +fn one(value: i32) i32 { + return value + 1; +} +fn two(value: i32) i32 { + return value + 2; +} +fn three(value: i32) i32 { + return value + 3; +} + +fn performFn(comptime prefix_char: u8, start_value: i32) i32 { + var result: i32 = start_value; + comptime var i = 0; + inline while (i < cmd_fns.len) : (i += 1) { + if (cmd_fns[i].name[0] == prefix_char) { + result = cmd_fns[i].func(result); + } + } + return result; +} + +test "comptime iterate over fn ptr list" { + assertOrPanic(performFn('t', 1) == 6); + assertOrPanic(performFn('o', 0) == 1); + assertOrPanic(performFn('w', 99) == 99); +} + +test "eval @setRuntimeSafety at compile-time" { + const result = comptime fnWithSetRuntimeSafety(); + assertOrPanic(result == 1234); +} + +fn fnWithSetRuntimeSafety() i32 { + @setRuntimeSafety(true); + return 1234; +} + +test "eval @setFloatMode at compile-time" { + const result = comptime fnWithFloatMode(); + assertOrPanic(result == 1234.0); +} + +fn fnWithFloatMode() f32 { + @setFloatMode(builtin.FloatMode.Strict); + return 1234.0; +} + +const SimpleStruct = struct { + field: i32, + + fn method(self: *const SimpleStruct) i32 { + return self.field + 3; + } +}; + +var simple_struct = SimpleStruct{ .field = 1234 }; + +const bound_fn = simple_struct.method; + +test "call method on bound fn referring to var instance" { + assertOrPanic(bound_fn() == 1237); +} + +test "ptr to local array argument at comptime" { + comptime { + var bytes: [10]u8 = undefined; + modifySomeBytes(bytes[0..]); + assertOrPanic(bytes[0] == 'a'); + assertOrPanic(bytes[9] == 'b'); + } +} + +fn modifySomeBytes(bytes: []u8) void { + bytes[0] = 'a'; + bytes[9] = 'b'; +} + +test "comparisons 0 <= uint and 0 > uint should be comptime" { + testCompTimeUIntComparisons(1234); +} +fn testCompTimeUIntComparisons(x: u32) void { + if (!(0 <= x)) { + @compileError("this condition should be comptime known"); + } + if (0 > x) { + @compileError("this condition should be comptime known"); + } + if (!(x >= 0)) { + @compileError("this condition should be comptime known"); + } + if (x < 0) { + @compileError("this condition should be comptime known"); + } +} + +test "const ptr to variable data changes at runtime" { + assertOrPanic(foo_ref.name[0] == 'a'); + foo_ref.name = "b"; + assertOrPanic(foo_ref.name[0] == 'b'); +} + +const Foo = struct { + name: []const u8, +}; + +var foo_contents = Foo{ .name = "a" }; +const foo_ref = &foo_contents; + +test "create global array with for loop" { + assertOrPanic(global_array[5] == 5 * 5); + assertOrPanic(global_array[9] == 9 * 9); +} + +const global_array = x: { + var result: [10]usize = undefined; + for (result) |*item, index| { + item.* = index * index; + } + break :x result; +}; + +test "compile-time downcast when the bits fit" { + comptime { + const spartan_count: u16 = 255; + const byte = @intCast(u8, spartan_count); + assertOrPanic(byte == 255); + } +} + +const hi1 = "hi"; +const hi2 = hi1; +test "const global shares pointer with other same one" { + assertEqualPtrs(&hi1[0], &hi2[0]); + comptime assertOrPanic(&hi1[0] == &hi2[0]); +} +fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { + assertOrPanic(ptr1 == ptr2); +} + +test "@setEvalBranchQuota" { + comptime { + // 1001 for the loop and then 1 more for the assertOrPanic fn call + @setEvalBranchQuota(1002); + var i = 0; + var sum = 0; + while (i < 1001) : (i += 1) { + sum += i; + } + assertOrPanic(sum == 500500); + } +} + +// TODO test "float literal at compile time not lossy" { +// TODO assertOrPanic(16777216.0 + 1.0 == 16777217.0); +// TODO assertOrPanic(9007199254740992.0 + 1.0 == 9007199254740993.0); +// TODO } + +test "f32 at compile time is lossy" { + assertOrPanic(f32(1 << 24) + 1 == 1 << 24); +} + +test "f64 at compile time is lossy" { + assertOrPanic(f64(1 << 53) + 1 == 1 << 53); +} + +test "f128 at compile time is lossy" { + assertOrPanic(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); +} + +comptime { + assertOrPanic(f128(1 << 113) == 10384593717069655257060992658440192); +} + +pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { + return struct { + pub const Node = struct {}; + }; +} + +test "string literal used as comptime slice is memoized" { + const a = "link"; + const b = "link"; + comptime assertOrPanic(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); + comptime assertOrPanic(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); +} + +test "comptime slice of undefined pointer of length 0" { + const slice1 = ([*]i32)(undefined)[0..0]; + assertOrPanic(slice1.len == 0); + const slice2 = ([*]i32)(undefined)[100..100]; + assertOrPanic(slice2.len == 0); +} + +fn copyWithPartialInline(s: []u32, b: []u8) void { + comptime var i: usize = 0; + inline while (i < 4) : (i += 1) { + s[i] = 0; + s[i] |= u32(b[i * 4 + 0]) << 24; + s[i] |= u32(b[i * 4 + 1]) << 16; + s[i] |= u32(b[i * 4 + 2]) << 8; + s[i] |= u32(b[i * 4 + 3]) << 0; + } +} + +test "binary math operator in partially inlined function" { + var s: [4]u32 = undefined; + var b: [16]u8 = undefined; + + for (b) |*r, i| + r.* = @intCast(u8, i + 1); + + copyWithPartialInline(s[0..], b[0..]); + assertOrPanic(s[0] == 0x1020304); + assertOrPanic(s[1] == 0x5060708); + assertOrPanic(s[2] == 0x90a0b0c); + assertOrPanic(s[3] == 0xd0e0f10); +} + +test "comptime function with the same args is memoized" { + comptime { + assertOrPanic(MakeType(i32) == MakeType(i32)); + assertOrPanic(MakeType(i32) != MakeType(f64)); + } +} + +fn MakeType(comptime T: type) type { + return struct { + field: T, + }; +} + +test "comptime function with mutable pointer is not memoized" { + comptime { + var x: i32 = 1; + const ptr = &x; + increment(ptr); + increment(ptr); + assertOrPanic(x == 3); + } +} + +fn increment(value: *i32) void { + value.* += 1; +} + +fn generateTable(comptime T: type) [1010]T { + var res: [1010]T = undefined; + var i: usize = 0; + while (i < 1010) : (i += 1) { + res[i] = @intCast(T, i); + } + return res; +} + +fn doesAlotT(comptime T: type, value: usize) T { + @setEvalBranchQuota(5000); + const table = comptime blk: { + break :blk generateTable(T); + }; + return table[value]; +} + +test "@setEvalBranchQuota at same scope as generic function call" { + assertOrPanic(doesAlotT(u32, 2) == 2); +} + +test "comptime slice of slice preserves comptime var" { + comptime { + var buff: [10]u8 = undefined; + buff[0..][0..][0] = 1; + assertOrPanic(buff[0..][0..][0] == 1); + } +} + +test "comptime slice of pointer preserves comptime var" { + comptime { + var buff: [10]u8 = undefined; + var a = buff[0..].ptr; + a[0..1][0] = 1; + assertOrPanic(buff[0..][0..][0] == 1); + } +} + +const SingleFieldStruct = struct { + x: i32, + + fn read_x(self: *const SingleFieldStruct) i32 { + return self.x; + } +}; +test "const ptr to comptime mutable data is not memoized" { + comptime { + var foo = SingleFieldStruct{ .x = 1 }; + assertOrPanic(foo.read_x() == 1); + foo.x = 2; + assertOrPanic(foo.read_x() == 2); + } +} + +test "array concat of slices gives slice" { + comptime { + var a: []const u8 = "aoeu"; + var b: []const u8 = "asdf"; + const c = a ++ b; + assertOrPanic(std.mem.eql(u8, c, "aoeuasdf")); + } +} + +test "comptime shlWithOverflow" { + const ct_shifted: u64 = comptime amt: { + var amt = u64(0); + _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); + break :amt amt; + }; + + const rt_shifted: u64 = amt: { + var amt = u64(0); + _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); + break :amt amt; + }; + + assertOrPanic(ct_shifted == rt_shifted); +} + +test "runtime 128 bit integer division" { + var a: u128 = 152313999999999991610955792383; + var b: u128 = 10000000000000000000; + var c = a / b; + assertOrPanic(c == 15231399999); +} + +// comptime modification of const struct field + +test "pointer to type" { + comptime { + var T: type = i32; + assertOrPanic(T == i32); + var ptr = &T; + assertOrPanic(@typeOf(ptr) == *type); + ptr.* = f32; + assertOrPanic(T == f32); + assertOrPanic(*T == *f32); + } +} + +test "slice of type" { + comptime { + var types_array = []type{ i32, f64, type }; + for (types_array) |T, i| { + switch (i) { + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), + else => unreachable, + } + } + for (types_array[0..]) |T, i| { + switch (i) { + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), + else => unreachable, + } + } + } +} + +const Wrapper = struct { + T: type, +}; + +fn wrap(comptime T: type) Wrapper { + return Wrapper{ .T = T }; +} + +test "function which returns struct with type field causes implicit comptime" { + const ty = wrap(i32).T; + assertOrPanic(ty == i32); +} + +test "call method with comptime pass-by-non-copying-value self parameter" { + const S = struct { + a: u8, + + fn b(comptime s: @This()) u8 { + return s.a; + } + }; + + const s = S{ .a = 2 }; + var b = s.b(); + assertOrPanic(b == 2); +} + +test "@tagName of @typeId" { + const str = @tagName(@typeId(u8)); + assertOrPanic(std.mem.eql(u8, str, "Int")); +} + +test "setting backward branch quota just before a generic fn call" { + @setEvalBranchQuota(1001); + loopNTimes(1001); +} + +fn loopNTimes(comptime n: usize) void { + comptime var i = 0; + inline while (i < n) : (i += 1) {} +} + +test "variable inside inline loop that has different types on different iterations" { + testVarInsideInlineLoop(true, u32(42)); +} + +fn testVarInsideInlineLoop(args: ...) void { + comptime var i = 0; + inline while (i < args.len) : (i += 1) { + const x = args[i]; + if (i == 0) assertOrPanic(x); + if (i == 1) assertOrPanic(x == 42); + } +} + +test "inline for with same type but different values" { + var res: usize = 0; + inline for ([]type{ [2]u8, [1]u8, [2]u8 }) |T| { + var a: T = undefined; + res += a.len; + } + assertOrPanic(res == 5); +} + +test "refer to the type of a generic function" { + const Func = fn (type) void; + const f: Func = doNothingWithType; + f(i32); +} + +fn doNothingWithType(comptime T: type) void {} + +test "zero extend from u0 to u1" { + var zero_u0: u0 = 0; + var zero_u1: u1 = zero_u0; + assertOrPanic(zero_u1 == 0); +} + +test "bit shift a u1" { + var x: u1 = 1; + var y = x << 0; + assertOrPanic(y == 1); +} + +test "@intCast to a u0" { + var x: u8 = 0; + var y: u0 = @intCast(u0, x); + assertOrPanic(y == 0); +} + +// test "@bytesToslice on a packed struct" + +test "comptime pointer cast array and then slice" { + const array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; + + const ptrA: [*]const u8 = @ptrCast([*]const u8, &array); + const sliceA: []const u8 = ptrA[0..2]; + + const ptrB: [*]const u8 = &array; + const sliceB: []const u8 = ptrB[0..2]; + + assertOrPanic(sliceA[1] == 2); + assertOrPanic(sliceB[1] == 2); +} + +test "slice bounds in comptime concatenation" { + const bs = comptime blk: { + const b = c"11"; + break :blk b[0..1]; + }; + const str = "" ++ bs; + assertOrPanic(str.len == 1); + assertOrPanic(std.mem.eql(u8, str, "1")); + + const str2 = bs ++ ""; + assertOrPanic(str2.len == 1); + assertOrPanic(std.mem.eql(u8, str2, "1")); +} + +test "comptime bitwise operators" { + comptime { + assertOrPanic(3 & 1 == 1); + assertOrPanic(3 & -1 == 3); + assertOrPanic(-3 & -1 == -3); + assertOrPanic(3 | -1 == -1); + assertOrPanic(-3 | -1 == -1); + assertOrPanic(3 ^ -1 == -4); + assertOrPanic(-3 ^ -1 == 2); + assertOrPanic(~i8(-1) == 0); + assertOrPanic(~i128(-1) == 0); + assertOrPanic(18446744073709551615 & 18446744073709551611 == 18446744073709551611); + assertOrPanic(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); + assertOrPanic(~u128(0) == 0xffffffffffffffffffffffffffffffff); + } +} + +test "*align(1) u16 is the same as *align(1:0:2) u16" { + comptime { + assertOrPanic(*align(1:0:2) u16 == *align(1) u16); + // TODO add parsing support for this syntax + //assertOrPanic(*align(:0:2) u16 == *u16); + } +} + +test "array concatenation forces comptime" { + var a = oneItem(3) ++ oneItem(4); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 4 })); +} + +test "array multiplication forces comptime" { + var a = oneItem(3) ** scalar(2); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 3 })); +} + +fn oneItem(x: i32) [1]i32 { + return []i32{x}; +} + +fn scalar(x: u32) u32 { + return x; +} From 7d578012a5a2d9f2db8faf5657e2751ed7d87384 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 3 Jan 2019 21:24:05 -0500 Subject: [PATCH 148/190] copy elision: move passing tests --- test/cases/cast.zig | 274 ------------------------------ test/cases/error.zig | 110 ------------ test/cases/null.zig | 102 ------------ test/stage1/behavior.zig | 2 +- test/stage1/behavior/cast.zig | 296 +++++++++++++++++++++++++++++++++ test/stage1/behavior/error.zig | 118 +++++++++++++ test/stage1/behavior/null.zig | 113 +++++++++++++ 7 files changed, 528 insertions(+), 487 deletions(-) diff --git a/test/cases/cast.zig b/test/cases/cast.zig index b0cb9de1e11a..4d7b5b03050f 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -3,36 +3,10 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; -test "peer resolve array and const slice" { - testPeerResolveArrayConstSlice(true); - comptime testPeerResolveArrayConstSlice(true); -} -fn testPeerResolveArrayConstSlice(b: bool) void { - const value1 = if (b) "aoeu" else ([]const u8)("zz"); - const value2 = if (b) ([]const u8)("zz") else "aoeu"; - assertOrPanic(mem.eql(u8, value1, "aoeu")); - assertOrPanic(mem.eql(u8, value2, "zz")); -} - test "implicitly cast from T to anyerror!?T" { castToOptionalTypeError(1); comptime castToOptionalTypeError(1); } -const A = struct { - a: i32, -}; -fn castToOptionalTypeError(z: i32) void { - const x = i32(1); - const y: anyerror!?i32 = x; - assertOrPanic((try y).? == 1); - - const f = z; - const g: anyerror!?i32 = f; - - const a = A{ .a = z }; - const b: anyerror!?A = a; - assertOrPanic((b catch unreachable).?.a == 1); -} test "implicitly cast from int to anyerror!?T" { implicitIntLitToOptional(); @@ -43,19 +17,6 @@ fn implicitIntLitToOptional() void { const g: anyerror!?i32 = 1; } -test "return null from fn() anyerror!?&T" { - const a = returnNullFromOptionalTypeErrorRef(); - const b = returnNullLitFromOptionalTypeErrorRef(); - assertOrPanic((try a) == null and (try b) == null); -} -fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { - const a: ?*A = null; - return a; -} -fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { - return null; -} - test "peer type resolution: ?T and T" { assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); @@ -72,22 +33,6 @@ fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { return usize(3); } -test "peer type resolution: [0]u8 and []const u8" { - assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); - comptime { - assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); - } -} -fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { - if (a) { - return []const u8{}; - } - - return slice[0..1]; -} - test "implicitly cast from [N]T to ?[]const T" { assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); comptime assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); @@ -132,30 +77,6 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { return slice[0..1]; } -test "resolve undefined with integer" { - testResolveUndefWithInt(true, 1234); - comptime testResolveUndefWithInt(true, 1234); -} -fn testResolveUndefWithInt(b: bool, x: i32) void { - const value = if (b) x else undefined; - if (b) { - assertOrPanic(value == x); - } -} - -test "implicit cast from &const [N]T to []const T" { - testCastConstArrayRefToConstSlice(); - comptime testCastConstArrayRefToConstSlice(); -} - -fn testCastConstArrayRefToConstSlice() void { - const blah = "aoeu"; - const const_array_ref = &blah; - assertOrPanic(@typeOf(const_array_ref) == *const [4]u8); - const slice: []const u8 = const_array_ref; - assertOrPanic(mem.eql(u8, slice, "aoeu")); -} - test "peer type resolution: error and [N]T" { // TODO: implicit error!T to error!U where T can implicitly cast to U //assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); @@ -178,46 +99,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { }; } -test "@floatToInt" { - testFloatToInts(); - comptime testFloatToInts(); -} - -fn testFloatToInts() void { - const x = i32(1e4); - assertOrPanic(x == 10000); - const y = @floatToInt(i32, f32(1e4)); - assertOrPanic(y == 10000); - expectFloatToInt(f16, 255.1, u8, 255); - expectFloatToInt(f16, 127.2, i8, 127); - expectFloatToInt(f16, -128.2, i8, -128); - expectFloatToInt(f32, 255.1, u8, 255); - expectFloatToInt(f32, 127.2, i8, 127); - expectFloatToInt(f32, -128.2, i8, -128); - expectFloatToInt(comptime_int, 1234, i16, 1234); -} - -fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { - assertOrPanic(@floatToInt(I, f) == i); -} - -test "cast u128 to f128 and back" { - comptime testCast128(); - testCast128(); -} - -fn testCast128() void { - assertOrPanic(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); -} - -fn cast128Int(x: f128) u128 { - return @bitCast(u128, x); -} - -fn cast128Float(x: u128) f128 { - return @bitCast(f128, x); -} - test "const slice widen cast" { const bytes align(4) = []u8{ 0x12, @@ -232,150 +113,12 @@ test "const slice widen cast" { assertOrPanic(@bitCast(u32, bytes) == 0x12121212); } -test "single-item pointer of array to slice and to unknown length pointer" { - testCastPtrOfArrayToSliceAndPtr(); - comptime testCastPtrOfArrayToSliceAndPtr(); -} - -fn testCastPtrOfArrayToSliceAndPtr() void { - var array = "aoeu"; - const x: [*]u8 = &array; - x[0] += 1; - assertOrPanic(mem.eql(u8, array[0..], "boeu")); - const y: []u8 = &array; - y[0] += 1; - assertOrPanic(mem.eql(u8, array[0..], "coeu")); -} - -test "cast *[1][*]const u8 to [*]const ?[*]const u8" { - const window_name = [1][*]const u8{c"window name"}; - const x: [*]const ?[*]const u8 = &window_name; - assertOrPanic(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); -} - -test "@intCast comptime_int" { - const result = @intCast(i32, 1234); - assertOrPanic(@typeOf(result) == i32); - assertOrPanic(result == 1234); -} - -test "@floatCast comptime_int and comptime_float" { - { - const result = @floatCast(f16, 1234); - assertOrPanic(@typeOf(result) == f16); - assertOrPanic(result == 1234.0); - } - { - const result = @floatCast(f16, 1234.0); - assertOrPanic(@typeOf(result) == f16); - assertOrPanic(result == 1234.0); - } - { - const result = @floatCast(f32, 1234); - assertOrPanic(@typeOf(result) == f32); - assertOrPanic(result == 1234.0); - } - { - const result = @floatCast(f32, 1234.0); - assertOrPanic(@typeOf(result) == f32); - assertOrPanic(result == 1234.0); - } -} - -test "comptime_int @intToFloat" { - { - const result = @intToFloat(f16, 1234); - assertOrPanic(@typeOf(result) == f16); - assertOrPanic(result == 1234.0); - } - { - const result = @intToFloat(f32, 1234); - assertOrPanic(@typeOf(result) == f32); - assertOrPanic(result == 1234.0); - } -} - test "@bytesToSlice keeps pointer alignment" { var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; const numbers = @bytesToSlice(u32, bytes[0..]); comptime assertOrPanic(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); } -test "@intCast i32 to u7" { - var x: u128 = maxInt(u128); - var y: i32 = 120; - var z = x >> @intCast(u7, y); - assertOrPanic(z == 0xff); -} - -test "implicit cast undefined to optional" { - assertOrPanic(MakeType(void).getNull() == null); - assertOrPanic(MakeType(void).getNonNull() != null); -} - -fn MakeType(comptime T: type) type { - return struct { - fn getNull() ?T { - return null; - } - - fn getNonNull() ?T { - return T(undefined); - } - }; -} - -test "implicit cast from *[N]T to ?[*]T" { - var x: ?[*]u16 = null; - var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; - - x = &y; - assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); - x.?[0] = 8; - y[3] = 6; - assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); -} - -test "implicit cast from *T to ?*c_void" { - var a: u8 = 1; - incrementVoidPtrValue(&a); - std.debug.assertOrPanic(a == 2); -} - -fn incrementVoidPtrValue(value: ?*c_void) void { - @ptrCast(*u8, value.?).* += 1; -} - -test "implicit cast from [*]T to ?*c_void" { - var a = []u8{ 3, 2, 1 }; - incrementVoidPtrArray(a[0..].ptr, 3); - assertOrPanic(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); -} - -fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { - var n: usize = 0; - while (n < len) : (n += 1) { - @ptrCast([*]u8, array.?)[n] += 1; - } -} - -test "*usize to *void" { - var i = usize(0); - var v = @ptrCast(*void, &i); - v.* = {}; -} - -test "compile time int to ptr of function" { - foobar(FUNCTION_CONSTANT); -} - -pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); -pub const PFN_void = extern fn (*c_void) void; - -fn foobar(func: PFN_void) void { - std.debug.assertOrPanic(@ptrToInt(func) == maxInt(usize)); -} - test "implicit ptr to *c_void" { var a: u32 = 1; var ptr: *c_void = &a; @@ -386,20 +129,3 @@ test "implicit ptr to *c_void" { assertOrPanic(c.* == 1); } -test "@intCast to comptime_int" { - assertOrPanic(@intCast(comptime_int, 0) == 0); -} - -test "implicit cast comptime numbers to any type when the value fits" { - const a: u64 = 255; - var b: u8 = a; - assertOrPanic(b == 255); -} - -test "@intToEnum passed a comptime_int to an enum with one item" { - const E = enum { - A, - }; - const x = @intToEnum(E, 0); - assertOrPanic(x == E.A); -} diff --git a/test/cases/error.zig b/test/cases/error.zig index d8ba8d804f95..b808d3f63a24 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -13,82 +13,6 @@ fn errBinaryOperatorG(x: bool) anyerror!isize { return if (x) error.ItBroke else isize(10); } -test "unwrap simple value from error" { - const i = unwrapSimpleValueFromErrorDo() catch unreachable; - assertOrPanic(i == 13); -} -fn unwrapSimpleValueFromErrorDo() anyerror!isize { - return 13; -} - -test "error return in assignment" { - doErrReturnInAssignment() catch unreachable; -} - -fn doErrReturnInAssignment() anyerror!void { - var x: i32 = undefined; - x = try makeANonErr(); -} - -fn makeANonErr() anyerror!i32 { - return 1; -} - -test "error union type " { - testErrorUnionType(); - comptime testErrorUnionType(); -} - -fn testErrorUnionType() void { - const x: anyerror!i32 = 1234; - if (x) |value| assertOrPanic(value == 1234) else |_| unreachable; - assertOrPanic(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); - assertOrPanic(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); - assertOrPanic(@typeOf(x).ErrorSet == anyerror); -} - -test "error set type " { - testErrorSetType(); - comptime testErrorSetType(); -} - -const MyErrSet = error{ - OutOfMemory, - FileNotFound, -}; - -fn testErrorSetType() void { - assertOrPanic(@memberCount(MyErrSet) == 2); - - const a: MyErrSet!i32 = 5678; - const b: MyErrSet!i32 = MyErrSet.OutOfMemory; - - if (a) |value| assertOrPanic(value == 5678) else |err| switch (err) { - error.OutOfMemory => unreachable, - error.FileNotFound => unreachable, - } -} - -test "explicit error set cast" { - testExplicitErrorSetCast(Set1.A); - comptime testExplicitErrorSetCast(Set1.A); -} - -const Set1 = error{ - A, - B, -}; -const Set2 = error{ - A, - C, -}; - -fn testExplicitErrorSetCast(set1: Set1) void { - var x = @errSetCast(Set2, set1); - var y = @errSetCast(Set1, x); - assertOrPanic(y == error.A); -} - test "comptime test error for empty error set" { testComptimeTestErrorEmptySet(1234); comptime testComptimeTestErrorEmptySet(1234); @@ -100,25 +24,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { if (x) |v| assertOrPanic(v == 1234) else |err| @compileError("bad"); } -test "syntax: optional operator in front of error union operator" { - comptime { - assertOrPanic(?(anyerror!i32) == ?(anyerror!i32)); - } -} - -test "comptime err to int of error set with only 1 possible value" { - testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); - comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); -} -fn testErrToIntWithOnePossibleValue( - x: error{A}, - comptime value: u32, -) void { - if (@errorToInt(x) != value) { - @compileError("bad"); - } -} - test "error union peer type resolution" { testErrorUnionPeerTypeResolution(1); comptime testErrorUnionPeerTypeResolution(1); @@ -144,21 +49,6 @@ fn quux_1() !i32 { return error.C; } -test "error: fn returning empty error set can be passed as fn returning any error" { - entry(); - comptime entry(); -} - -fn entry() void { - foo2(bar2); -} - -fn foo2(f: fn () anyerror!void) void { - const x = f(); -} - -fn bar2() (error{}!void) {} - test "error: Zero sized error set returned with value payload crash" { _ = foo3(0); _ = comptime foo3(0); diff --git a/test/cases/null.zig b/test/cases/null.zig index e2f86a05ba44..892a425a10d7 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -26,21 +26,6 @@ test "optional type" { assertOrPanic(num == 13); } -test "test maybe object and get a pointer to the inner value" { - var maybe_bool: ?bool = true; - - if (maybe_bool) |*b| { - b.* = false; - } - - assertOrPanic(maybe_bool.? == false); -} - -test "rhs maybe unwrap return" { - const x: ?bool = true; - const y = x orelse return; -} - test "maybe return" { maybeReturnImpl(); comptime maybeReturnImpl(); @@ -57,93 +42,6 @@ fn foo(x: ?i32) ?bool { return value > 1234; } -test "if var maybe pointer" { - assertOrPanic(shouldBeAPlus1(Particle{ - .a = 14, - .b = 1, - .c = 1, - .d = 1, - }) == 15); -} -fn shouldBeAPlus1(p: Particle) u64 { - var maybe_particle: ?Particle = p; - if (maybe_particle) |*particle| { - particle.a += 1; - } - if (maybe_particle) |particle| { - return particle.a; - } - return 0; -} -const Particle = struct { - a: u64, - b: u64, - c: u64, - d: u64, -}; - -test "null literal outside function" { - const is_null = here_is_a_null_literal.context == null; - assertOrPanic(is_null); - - const is_non_null = here_is_a_null_literal.context != null; - assertOrPanic(!is_non_null); -} -const SillyStruct = struct { - context: ?i32, -}; -const here_is_a_null_literal = SillyStruct{ .context = null }; - -test "test null runtime" { - testTestNullRuntime(null); -} -fn testTestNullRuntime(x: ?i32) void { - assertOrPanic(x == null); - assertOrPanic(!(x != null)); -} - -test "optional void" { - optionalVoidImpl(); - comptime optionalVoidImpl(); -} - -fn optionalVoidImpl() void { - assertOrPanic(bar(null) == null); - assertOrPanic(bar({}) != null); -} - -fn bar(x: ?void) ?void { - if (x) |_| { - return {}; - } else { - return null; - } -} - -const StructWithOptional = struct { - field: ?i32, -}; - -var struct_with_optional: StructWithOptional = undefined; - -test "unwrap optional which is field of global var" { - struct_with_optional.field = null; - if (struct_with_optional.field) |payload| { - unreachable; - } - struct_with_optional.field = 1234; - if (struct_with_optional.field) |payload| { - assertOrPanic(payload == 1234); - } else { - unreachable; - } -} - -test "null with default unwrap" { - const x: i32 = null orelse 1; - assertOrPanic(x == 1); -} - test "optional types" { comptime { const opt_type_struct = StructWithOptionalType{ .t = u8 }; diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 1eb6aa658847..80b87fc4cbc7 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -32,6 +32,7 @@ comptime { _ = @import("behavior/enum.zig"); _ = @import("behavior/enum_with_members.zig"); _ = @import("behavior/error.zig"); + _ = @import("behavior/eval.zig"); _ = @import("behavior/field_parent_ptr.zig"); _ = @import("behavior/fn.zig"); _ = @import("behavior/fn_in_struct_in_comptime.zig"); @@ -74,5 +75,4 @@ comptime { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); - _ = @import("behavior/eval.zig"); } diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index e68ac9e24039..5fe7a3c3022a 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -70,3 +70,299 @@ fn boolToStr(b: bool) []const u8 { return if (b) "true" else "false"; } +test "peer resolve array and const slice" { + testPeerResolveArrayConstSlice(true); + comptime testPeerResolveArrayConstSlice(true); +} +fn testPeerResolveArrayConstSlice(b: bool) void { + // TODO: https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 + const value1: []const u8 = if (b) "aoeu" else ([]const u8)("zz"); + const value2 = if (b) ([]const u8)("zz") else "aoeu"; + assertOrPanic(mem.eql(u8, value1, "aoeu")); + assertOrPanic(mem.eql(u8, value2, "zz")); +} + +// test "implicitly cast from T to anyerror!?T" + +const A = struct { + a: i32, +}; +fn castToOptionalTypeError(z: i32) void { + const x = i32(1); + const y: anyerror!?i32 = x; + assertOrPanic((try y).? == 1); + + const f = z; + const g: anyerror!?i32 = f; + + const a = A{ .a = z }; + const b: anyerror!?A = a; + assertOrPanic((b catch unreachable).?.a == 1); +} + +// test "implicitly cast from int to anyerror!?T" + +test "return null from fn() anyerror!?&T" { + const a = returnNullFromOptionalTypeErrorRef(); + const b = returnNullLitFromOptionalTypeErrorRef(); + assertOrPanic((try a) == null and (try b) == null); +} +fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { + const a: ?*A = null; + return a; +} +fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { + return null; +} + +//test "peer type resolution: ?T and T" { + +test "peer type resolution: [0]u8 and []const u8" { + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + comptime { + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + } +} +fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { + if (a) { + return []const u8{}; + } + + return slice[0..1]; +} + +// test "implicitly cast from [N]T to ?[]const T" { + +// test "implicitly cast from [0]T to anyerror![]T" { + +// test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" + +test "resolve undefined with integer" { + testResolveUndefWithInt(true, 1234); + comptime testResolveUndefWithInt(true, 1234); +} +fn testResolveUndefWithInt(b: bool, x: i32) void { + const value = if (b) x else undefined; + if (b) { + assertOrPanic(value == x); + } +} + +test "implicit cast from &const [N]T to []const T" { + testCastConstArrayRefToConstSlice(); + comptime testCastConstArrayRefToConstSlice(); +} + +fn testCastConstArrayRefToConstSlice() void { + const blah = "aoeu"; + const const_array_ref = &blah; + assertOrPanic(@typeOf(const_array_ref) == *const [4]u8); + const slice: []const u8 = const_array_ref; + assertOrPanic(mem.eql(u8, slice, "aoeu")); +} + +// test "peer type resolution: error and [N]T" { + +test "@floatToInt" { + testFloatToInts(); + comptime testFloatToInts(); +} + +fn testFloatToInts() void { + const x = i32(1e4); + assertOrPanic(x == 10000); + const y = @floatToInt(i32, f32(1e4)); + assertOrPanic(y == 10000); + expectFloatToInt(f16, 255.1, u8, 255); + expectFloatToInt(f16, 127.2, i8, 127); + expectFloatToInt(f16, -128.2, i8, -128); + expectFloatToInt(f32, 255.1, u8, 255); + expectFloatToInt(f32, 127.2, i8, 127); + expectFloatToInt(f32, -128.2, i8, -128); + expectFloatToInt(comptime_int, 1234, i16, 1234); +} + +fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { + assertOrPanic(@floatToInt(I, f) == i); +} + +test "cast u128 to f128 and back" { + comptime testCast128(); + testCast128(); +} + +fn testCast128() void { + assertOrPanic(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); +} + +fn cast128Int(x: f128) u128 { + return @bitCast(u128, x); +} + +fn cast128Float(x: u128) f128 { + return @bitCast(f128, x); +} + +// test "const slice widen cast" + +test "single-item pointer of array to slice and to unknown length pointer" { + testCastPtrOfArrayToSliceAndPtr(); + comptime testCastPtrOfArrayToSliceAndPtr(); +} + +fn testCastPtrOfArrayToSliceAndPtr() void { + var array = "aoeu"; + const x: [*]u8 = &array; + x[0] += 1; + assertOrPanic(mem.eql(u8, array[0..], "boeu")); + const y: []u8 = &array; + y[0] += 1; + assertOrPanic(mem.eql(u8, array[0..], "coeu")); +} + +test "cast *[1][*]const u8 to [*]const ?[*]const u8" { + const window_name = [1][*]const u8{c"window name"}; + const x: [*]const ?[*]const u8 = &window_name; + assertOrPanic(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); +} + +test "@intCast comptime_int" { + const result = @intCast(i32, 1234); + assertOrPanic(@typeOf(result) == i32); + assertOrPanic(result == 1234); +} + +test "@floatCast comptime_int and comptime_float" { + { + const result = @floatCast(f16, 1234); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); + } + { + const result = @floatCast(f16, 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); + } + { + const result = @floatCast(f32, 1234); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); + } + { + const result = @floatCast(f32, 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); + } +} + +test "comptime_int @intToFloat" { + { + const result = @intToFloat(f16, 1234); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); + } + { + const result = @intToFloat(f32, 1234); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); + } +} + +// test "@bytesToSlice keeps pointer alignment" { + +test "@intCast i32 to u7" { + var x: u128 = maxInt(u128); + var y: i32 = 120; + var z = x >> @intCast(u7, y); + assertOrPanic(z == 0xff); +} + +test "implicit cast undefined to optional" { + assertOrPanic(MakeType(void).getNull() == null); + assertOrPanic(MakeType(void).getNonNull() != null); +} + +fn MakeType(comptime T: type) type { + return struct { + fn getNull() ?T { + return null; + } + + fn getNonNull() ?T { + return T(undefined); + } + }; +} + +test "implicit cast from *[N]T to ?[*]T" { + var x: ?[*]u16 = null; + var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; + + x = &y; + assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); + x.?[0] = 8; + y[3] = 6; + assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); +} + +test "implicit cast from *T to ?*c_void" { + var a: u8 = 1; + incrementVoidPtrValue(&a); + std.debug.assertOrPanic(a == 2); +} + +fn incrementVoidPtrValue(value: ?*c_void) void { + @ptrCast(*u8, value.?).* += 1; +} + +test "implicit cast from [*]T to ?*c_void" { + var a = []u8{ 3, 2, 1 }; + incrementVoidPtrArray(a[0..].ptr, 3); + assertOrPanic(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); +} + +fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { + var n: usize = 0; + while (n < len) : (n += 1) { + @ptrCast([*]u8, array.?)[n] += 1; + } +} + +test "*usize to *void" { + var i = usize(0); + var v = @ptrCast(*void, &i); + v.* = {}; +} + +test "compile time int to ptr of function" { + foobar(FUNCTION_CONSTANT); +} + +pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); +pub const PFN_void = extern fn (*c_void) void; + +fn foobar(func: PFN_void) void { + std.debug.assertOrPanic(@ptrToInt(func) == maxInt(usize)); +} + +// test "implicit ptr to *c_void" + +test "@intCast to comptime_int" { + assertOrPanic(@intCast(comptime_int, 0) == 0); +} + +test "implicit cast comptime numbers to any type when the value fits" { + const a: u64 = 255; + var b: u8 = a; + assertOrPanic(b == 255); +} + +test "@intToEnum passed a comptime_int to an enum with one item" { + const E = enum { + A, + }; + const x = @intToEnum(E, 0); + assertOrPanic(x == E.A); +} diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig index 756ae324e7ea..dd84505c1db8 100644 --- a/test/stage1/behavior/error.zig +++ b/test/stage1/behavior/error.zig @@ -43,3 +43,121 @@ fn shouldBeNotEqual(a: anyerror, b: anyerror) void { if (a == b) unreachable; } +// test "error binary operator" + +test "unwrap simple value from error" { + const i = unwrapSimpleValueFromErrorDo() catch unreachable; + assertOrPanic(i == 13); +} +fn unwrapSimpleValueFromErrorDo() anyerror!isize { + return 13; +} + +test "error return in assignment" { + doErrReturnInAssignment() catch unreachable; +} + +fn doErrReturnInAssignment() anyerror!void { + var x: i32 = undefined; + x = try makeANonErr(); +} + +fn makeANonErr() anyerror!i32 { + return 1; +} + +test "error union type " { + testErrorUnionType(); + comptime testErrorUnionType(); +} + +fn testErrorUnionType() void { + const x: anyerror!i32 = 1234; + if (x) |value| assertOrPanic(value == 1234) else |_| unreachable; + assertOrPanic(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); + assertOrPanic(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); + assertOrPanic(@typeOf(x).ErrorSet == anyerror); +} + +test "error set type" { + testErrorSetType(); + comptime testErrorSetType(); +} + +const MyErrSet = error{ + OutOfMemory, + FileNotFound, +}; + +fn testErrorSetType() void { + assertOrPanic(@memberCount(MyErrSet) == 2); + + const a: MyErrSet!i32 = 5678; + const b: MyErrSet!i32 = MyErrSet.OutOfMemory; + + if (a) |value| assertOrPanic(value == 5678) else |err| switch (err) { + error.OutOfMemory => unreachable, + error.FileNotFound => unreachable, + } +} + +test "explicit error set cast" { + testExplicitErrorSetCast(Set1.A); + comptime testExplicitErrorSetCast(Set1.A); +} + +const Set1 = error{ + A, + B, +}; +const Set2 = error{ + A, + C, +}; + +fn testExplicitErrorSetCast(set1: Set1) void { + var x = @errSetCast(Set2, set1); + var y = @errSetCast(Set1, x); + assertOrPanic(y == error.A); +} + +// test "comptime test error for empty error set" { + +test "syntax: optional operator in front of error union operator" { + comptime { + assertOrPanic(?(anyerror!i32) == ?(anyerror!i32)); + } +} + +test "comptime err to int of error set with only 1 possible value" { + testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); + comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); +} +fn testErrToIntWithOnePossibleValue( + x: error{A}, + comptime value: u32, +) void { + if (@errorToInt(x) != value) { + @compileError("bad"); + } +} + +// test "error union peer type resolution" { + +test "error: fn returning empty error set can be passed as fn returning any error" { + entry(); + comptime entry(); +} + +fn entry() void { + foo2(bar2); +} + +fn foo2(f: fn () anyerror!void) void { + const x = f(); +} + +fn bar2() (error{}!void) {} + +// test "error: Zero sized error set returned with value payload crash" { +// test "error: Infer error set from literals" { diff --git a/test/stage1/behavior/null.zig b/test/stage1/behavior/null.zig index 41c447315af6..35f03e719869 100644 --- a/test/stage1/behavior/null.zig +++ b/test/stage1/behavior/null.zig @@ -1,2 +1,115 @@ const assertOrPanic = @import("std").debug.assertOrPanic; +//test "optional type" { + +test "test maybe object and get a pointer to the inner value" { + var maybe_bool: ?bool = true; + + if (maybe_bool) |*b| { + b.* = false; + } + + assertOrPanic(maybe_bool.? == false); +} + +test "rhs maybe unwrap return" { + const x: ?bool = true; + const y = x orelse return; +} + +// test "maybe return" { + +test "if var maybe pointer" { + assertOrPanic(shouldBeAPlus1(Particle{ + .a = 14, + .b = 1, + .c = 1, + .d = 1, + }) == 15); +} +fn shouldBeAPlus1(p: Particle) u64 { + var maybe_particle: ?Particle = p; + if (maybe_particle) |*particle| { + particle.a += 1; + } + if (maybe_particle) |particle| { + return particle.a; + } + return 0; +} +const Particle = struct { + a: u64, + b: u64, + c: u64, + d: u64, +}; + +test "null literal outside function" { + const is_null = here_is_a_null_literal.context == null; + assertOrPanic(is_null); + + const is_non_null = here_is_a_null_literal.context != null; + assertOrPanic(!is_non_null); +} +const SillyStruct = struct { + context: ?i32, +}; +const here_is_a_null_literal = SillyStruct{ .context = null }; + +test "test null runtime" { + testTestNullRuntime(null); +} +fn testTestNullRuntime(x: ?i32) void { + assertOrPanic(x == null); + assertOrPanic(!(x != null)); +} + +test "optional void" { + optionalVoidImpl(); + comptime optionalVoidImpl(); +} + +fn optionalVoidImpl() void { + assertOrPanic(bar(null) == null); + assertOrPanic(bar({}) != null); +} + +fn bar(x: ?void) ?void { + if (x) |_| { + return {}; + } else { + return null; + } +} + +const StructWithOptional = struct { + field: ?i32, +}; + +var struct_with_optional: StructWithOptional = undefined; + +test "unwrap optional which is field of global var" { + struct_with_optional.field = null; + if (struct_with_optional.field) |payload| { + unreachable; + } + struct_with_optional.field = 1234; + if (struct_with_optional.field) |payload| { + assertOrPanic(payload == 1234); + } else { + unreachable; + } +} + +test "null with default unwrap" { + const x: i32 = null orelse 1; + assertOrPanic(x == 1); +} + +// test "optional types" { + +test "optional pointer to 0 bit type null value at runtime" { + const EmptyStruct = struct {}; + var x: ?*EmptyStruct = null; + assertOrPanic(x == null); +} From 907563ec94656ae71370fd017d17811a21c28985 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 4 Jan 2019 11:42:29 -0500 Subject: [PATCH 149/190] fix comptime @bitCast incorrectly changing a variable's type --- src/all_types.hpp | 17 ++++---- src/analyze.cpp | 51 +++++++++++++---------- src/analyze.hpp | 2 +- src/codegen.cpp | 56 +++++++++++++++----------- src/ir.cpp | 67 +++++++++++++++---------------- test/behavior.zig | 1 - test/cases/bugs/920.zig | 65 ------------------------------ test/stage1/behavior/bugs/920.zig | 60 +++++++++++++++++++++++++++ 8 files changed, 166 insertions(+), 153 deletions(-) delete mode 100644 test/cases/bugs/920.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index 5e72de59bbb6..9dc534e6f4e1 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1829,10 +1829,9 @@ enum VarLinkage { struct ZigVar { Buf name; - ConstExprValue *value; + ConstExprValue *const_value; + ZigType *var_type; LLVMValueRef value_ref; - bool src_is_const; - bool gen_is_const; IrInstruction *is_comptime; // which node is the declaration of the variable AstNode *decl_node; @@ -1842,17 +1841,21 @@ struct ZigVar { Scope *parent_scope; Scope *child_scope; LLVMValueRef param_value_ref; - bool shadowable; size_t mem_slot_index; IrExecutable *owner_exec; size_t ref_count; - VarLinkage linkage; - uint32_t align_bytes; // In an inline loop, multiple variables may be created, // In this case, a reference to a variable should follow // this pointer to the redefined variable. ZigVar *next_var; + + uint32_t align_bytes; + VarLinkage linkage; + + bool shadowable; + bool src_is_const; + bool gen_is_const; }; struct ErrorTableEntry { @@ -2399,8 +2402,8 @@ struct IrInstructionBinOp { IrInstruction base; IrInstruction *op1; - IrBinOp op_id; IrInstruction *op2; + IrBinOp op_id; bool safety_check_on; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index b7e5ebf9f2cc..c912716bd6c2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3426,7 +3426,8 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) { resolve_top_level_decl(g, tld, false, tld->source_node); assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; - tld_var->var->value = value; + tld_var->var->const_value = value; + tld_var->var->var_type = value->type; tld_var->var->align_bytes = get_abi_alignment(g, value->type); } @@ -3590,13 +3591,15 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry // Set name to nullptr to make the variable anonymous (not visible to programmer). // TODO merge with definition of add_local_var in ir.cpp ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, - bool is_const, ConstExprValue *value, Tld *src_tld) + bool is_const, ConstExprValue *const_value, Tld *src_tld, ZigType *var_type) { Error err; - assert(value); + assert(const_value != nullptr); + assert(var_type != nullptr); ZigVar *variable_entry = allocate(1); - variable_entry->value = value; + variable_entry->const_value = const_value; + variable_entry->var_type = var_type; variable_entry->parent_scope = parent_scope; variable_entry->shadowable = false; variable_entry->mem_slot_index = SIZE_MAX; @@ -3605,23 +3608,23 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf assert(name); buf_init_from_buf(&variable_entry->name, name); - if ((err = type_resolve(g, value->type, ResolveStatusAlignmentKnown))) { - variable_entry->value->type = g->builtin_types.entry_invalid; + if ((err = type_resolve(g, var_type, ResolveStatusAlignmentKnown))) { + variable_entry->var_type = g->builtin_types.entry_invalid; } else { - variable_entry->align_bytes = get_abi_alignment(g, value->type); + variable_entry->align_bytes = get_abi_alignment(g, var_type); ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr); if (existing_var && !existing_var->shadowable) { ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } else { ZigType *type; if (get_primitive_type(g, name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(g, source_node, buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } else { Scope *search_scope = nullptr; if (src_tld == nullptr) { @@ -3635,7 +3638,7 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here")); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } } } @@ -3724,16 +3727,16 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { ConstExprValue *init_val = init_value ? &init_value->value : create_const_runtime(type); tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, - is_const, init_val, &tld_var->base); + is_const, init_val, &tld_var->base, type); tld_var->var->linkage = linkage; if (implicit_type != nullptr && type_is_invalid(implicit_type)) { - tld_var->var->value->type = g->builtin_types.entry_invalid; + tld_var->var->var_type = g->builtin_types.entry_invalid; } if (var_decl->align_expr != nullptr) { if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) { - tld_var->var->value->type = g->builtin_types.entry_invalid; + tld_var->var->var_type = g->builtin_types.entry_invalid; } } @@ -4098,7 +4101,7 @@ static void define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) { } ZigVar *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, - param_name, true, create_const_runtime(param_type), nullptr); + param_name, true, create_const_runtime(param_type), nullptr, param_type); var->src_arg_index = i; fn_table_entry->child_scope = var->child_scope; var->shadowable = var->shadowable || is_var_args; @@ -5072,9 +5075,9 @@ bool fn_eval_cacheable(Scope *scope, ZigType *return_type) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; - if (type_is_invalid(var_scope->var->value->type)) + if (type_is_invalid(var_scope->var->var_type)) return false; - if (can_mutate_comptime_var_state(var_scope->var->value)) + if (can_mutate_comptime_var_state(var_scope->var->const_value)) return false; } else if (scope->id == ScopeIdFnDef) { return true; @@ -5092,7 +5095,7 @@ uint32_t fn_eval_hash(Scope* scope) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; - result += hash_const_val(var_scope->var->value); + result += hash_const_val(var_scope->var->const_value); } else if (scope->id == ScopeIdFnDef) { ScopeFnDef *fn_scope = (ScopeFnDef *)scope; result += hash_ptr(fn_scope->fn_entry); @@ -5116,10 +5119,16 @@ bool fn_eval_eql(Scope *a, Scope *b) { if (a->id == ScopeIdVarDecl) { ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a; ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b; - if (a_var_scope->var->value->type != b_var_scope->var->value->type) - return false; - if (!const_values_equal(a->codegen, a_var_scope->var->value, b_var_scope->var->value)) + if (a_var_scope->var->var_type != b_var_scope->var->var_type) return false; + if (a_var_scope->var->var_type == a_var_scope->var->const_value->type && + b_var_scope->var->var_type == b_var_scope->var->const_value->type) + { + if (!const_values_equal(a->codegen, a_var_scope->var->const_value, b_var_scope->var->const_value)) + return false; + } else { + zig_panic("TODO comptime ptr reinterpret for fn_eval_eql"); + } } else if (a->id == ScopeIdFnDef) { ScopeFnDef *a_fn_scope = (ScopeFnDef *)a; ScopeFnDef *b_fn_scope = (ScopeFnDef *)b; @@ -6546,7 +6555,7 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { resolve_top_level_decl(codegen, tld, false, nullptr); assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; - ConstExprValue *var_value = tld_var->var->value; + ConstExprValue *var_value = tld_var->var->const_value; assert(var_value != nullptr); return var_value; } diff --git a/src/analyze.hpp b/src/analyze.hpp index aa3839969402..72bf2cb7bc8c 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -81,7 +81,7 @@ ZigFn *scope_fn_entry(Scope *scope); ImportTableEntry *get_scope_import(Scope *scope); void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope); ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, - bool is_const, ConstExprValue *init_value, Tld *src_tld); + bool is_const, ConstExprValue *init_value, Tld *src_tld, ZigType *var_type); ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node); ZigFn *create_fn(CodeGen *g, AstNode *proto_node); ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value); diff --git a/src/codegen.cpp b/src/codegen.cpp index 3dc2f605ccad..5d03fa7f21df 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2230,10 +2230,10 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { assert(variable); assert(variable->value_ref); - if (!handle_is_ptr(variable->value->type)) { + if (!handle_is_ptr(variable->var_type)) { clear_debug_source_node(g); - gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), variable->value_ref, - variable->align_bytes, false); + gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), + variable->value_ref, variable->align_bytes, false); } if (variable->decl_node) { @@ -3090,7 +3090,7 @@ static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutable *executable, IrI static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrInstructionDeclVarGen *instruction) { ZigVar *var = instruction->var; - if (!type_has_bits(var->value->type)) + if (!type_has_bits(var->var_type)) return nullptr; if (var->ref_count == 0 && g->build_mode != BuildModeDebug) @@ -3201,7 +3201,7 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) { ZigVar *var = instruction->var; - if (type_has_bits(var->value->type)) { + if (type_has_bits(var->var_type)) { assert(var->value_ref); return var->value_ref; } else { @@ -6391,10 +6391,13 @@ static void do_code_gen(CodeGen *g) { TldVar *tld_var = g->global_vars.at(i); ZigVar *var = tld_var->var; - if (var->value->type->id == ZigTypeIdComptimeFloat) { + if (var->var_type->id == ZigTypeIdComptimeFloat) { // Generate debug info for it but that's it. - ConstExprValue *const_val = var->value; + ConstExprValue *const_val = var->const_value; assert(const_val->special != ConstValSpecialRuntime); + if (const_val->type != var->var_type) { + zig_panic("TODO debug info for var with ptr casted value"); + } ZigType *var_type = g->builtin_types.entry_f128; ConstExprValue coerced_value; coerced_value.special = ConstValSpecialStatic; @@ -6405,10 +6408,13 @@ static void do_code_gen(CodeGen *g) { continue; } - if (var->value->type->id == ZigTypeIdComptimeInt) { + if (var->var_type->id == ZigTypeIdComptimeInt) { // Generate debug info for it but that's it. - ConstExprValue *const_val = var->value; + ConstExprValue *const_val = var->const_value; assert(const_val->special != ConstValSpecialRuntime); + if (const_val->type != var->var_type) { + zig_panic("TODO debug info for var with ptr casted value"); + } size_t bits_needed = bigint_bits_needed(&const_val->data.x_bigint); if (bits_needed < 8) { bits_needed = 8; @@ -6419,7 +6425,7 @@ static void do_code_gen(CodeGen *g) { continue; } - if (!type_has_bits(var->value->type)) + if (!type_has_bits(var->var_type)) continue; assert(var->decl_node); @@ -6428,9 +6434,9 @@ static void do_code_gen(CodeGen *g) { if (var->linkage == VarLinkageExternal) { LLVMValueRef existing_llvm_var = LLVMGetNamedGlobal(g->module, buf_ptr(&var->name)); if (existing_llvm_var) { - global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->value->type->type_ref, 0)); + global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->var_type->type_ref, 0)); } else { - global_value = LLVMAddGlobal(g->module, var->value->type->type_ref, buf_ptr(&var->name)); + global_value = LLVMAddGlobal(g->module, var->var_type->type_ref, buf_ptr(&var->name)); // TODO debug info for the extern variable LLVMSetLinkage(global_value, LLVMExternalLinkage); @@ -6441,9 +6447,9 @@ static void do_code_gen(CodeGen *g) { } else { bool exported = (var->linkage == VarLinkageExport); const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported)); - render_const_val(g, var->value, mangled_name); - render_const_val_global(g, var->value, mangled_name); - global_value = var->value->global_refs->llvm_global; + render_const_val(g, var->const_value, mangled_name); + render_const_val_global(g, var->const_value, mangled_name); + global_value = var->const_value->global_refs->llvm_global; if (exported) { LLVMSetLinkage(global_value, LLVMExternalLinkage); @@ -6455,8 +6461,10 @@ static void do_code_gen(CodeGen *g) { LLVMSetAlignment(global_value, var->align_bytes); // TODO debug info for function pointers - if (var->gen_is_const && var->value->type->id != ZigTypeIdFn) { - gen_global_var(g, var, var->value->global_refs->llvm_value, var->value->type); + // Here we use const_value->type because that's the type of the llvm global, + // which we const ptr cast upon use to whatever it needs to be. + if (var->gen_is_const && var->const_value->type->id != ZigTypeIdFn) { + gen_global_var(g, var, var->const_value->global_refs->llvm_value, var->const_value->type); } LLVMSetGlobalConstant(global_value, var->gen_is_const); @@ -6533,12 +6541,12 @@ static void do_code_gen(CodeGen *g) { for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { ZigVar *var = fn_table_entry->variable_list.at(var_i); - if (!type_has_bits(var->value->type)) { + if (!type_has_bits(var->var_type)) { continue; } if (ir_get_var_is_comptime(var)) continue; - switch (type_requires_comptime(g, var->value->type)) { + switch (type_requires_comptime(g, var->var_type)) { case ReqCompTimeInvalid: zig_unreachable(); case ReqCompTimeYes: @@ -6550,7 +6558,7 @@ static void do_code_gen(CodeGen *g) { if (var->src_arg_index == SIZE_MAX) { var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), - var->value->type->di_type, !g->strip_debug_symbols, 0); + var->var_type->di_type, !g->strip_debug_symbols, 0); } else if (is_c_abi) { fn_walk_var.data.vars.var = var; @@ -6560,16 +6568,16 @@ static void do_code_gen(CodeGen *g) { ZigType *gen_type; FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index]; - if (handle_is_ptr(var->value->type)) { + if (handle_is_ptr(var->var_type)) { if (gen_info->is_byval) { - gen_type = var->value->type; + gen_type = var->var_type; } else { gen_type = gen_info->type; } var->value_ref = LLVMGetParam(fn, (unsigned)var->gen_arg_index); } else { - gen_type = var->value->type; - var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); + gen_type = var->var_type; + var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes); } if (var->decl_node) { var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), diff --git a/src/ir.cpp b/src/ir.cpp index 360287cb3865..54946849f640 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3679,7 +3679,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s variable_entry->mem_slot_index = SIZE_MAX; variable_entry->is_comptime = is_comptime; variable_entry->src_arg_index = SIZE_MAX; - variable_entry->value = create_const_vals(1); + variable_entry->const_value = create_const_vals(1); if (is_comptime != nullptr) { is_comptime->ref_count += 1; @@ -3694,20 +3694,20 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { ZigType *type; if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(codegen, node, buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { Tld *tld = find_decl(codegen, parent_scope, name); if (tld != nullptr) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } } } @@ -5781,7 +5781,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod // is inside var->child_scope if (!is_extern && !variable_declaration->expr) { - var->value->type = irb->codegen->builtin_types.entry_invalid; + var->var_type = irb->codegen->builtin_types.entry_invalid; add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized")); return irb->codegen->invalid_instruction; } @@ -7074,7 +7074,8 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope; if (need_comma) buf_append_char(name, ','); - render_const_value(codegen, name, var_scope->var->value); + // TODO: const ptr reinterpret here to make the var type agree with the value? + render_const_value(codegen, name, var_scope->var->const_value); return true; } @@ -13774,7 +13775,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, ZigType *proposed_type = ir_resolve_type(ira, var_type); explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type); if (type_is_invalid(explicit_type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } } @@ -13787,7 +13788,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruction *var_ptr = decl_var_instruction->ptr->child; if (type_is_invalid(var_ptr->value.type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } @@ -13810,7 +13811,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, if (instr_is_comptime(var_ptr) && var_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { init_val = const_ptr_pointee(ira, ira->codegen, &var_ptr->value, decl_var_instruction->base.source_node); if (is_comptime_var) { - var->value = init_val; + var->const_value = init_val; } } @@ -13846,7 +13847,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, break; } - if (var->value->type != nullptr && !is_comptime_var) { + if (var->var_type != nullptr && !is_comptime_var) { // This is at least the second time we've seen this variable declaration during analysis. // This means that this is actually a different variable due to, e.g. an inline while loop. // We make a new variable so that it can hold a different type, and so the debug info can @@ -13868,8 +13869,8 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, // This must be done after possibly creating a new variable above var->ref_count = 0; - var->value->type = result_type; - assert(var->value->type); + var->var_type = result_type; + assert(var->var_type); if (type_is_invalid(result_type)) { return ir_const_void(ira, &decl_var_instruction->base); @@ -13877,13 +13878,13 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, if (decl_var_instruction->align_value == nullptr) { if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ir_const_void(ira, &decl_var_instruction->base); } var->align_bytes = get_abi_alignment(ira->codegen, result_type); } else { if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, &var->align_bytes)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; } } @@ -13916,7 +13917,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, } else if (is_comptime_var) { ir_add_error(ira, &decl_var_instruction->base, buf_sprintf("cannot store runtime value in compile time variable")); - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } @@ -14258,7 +14259,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node Buf *param_name = param_decl_node->data.param_decl.name; ZigVar *var = add_variable(ira->codegen, param_decl_node, - *exec_scope, param_name, true, arg_val, nullptr); + *exec_scope, param_name, true, arg_val, nullptr, arg_val->type); *exec_scope = var->child_scope; *next_proto_i += 1; @@ -14316,7 +14317,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod if (!param_name) return false; if (!is_var_args) { ZigVar *var = add_variable(ira->codegen, param_decl_node, - *child_scope, param_name, true, arg_val, nullptr); + *child_scope, param_name, true, arg_val, nullptr, arg_val->type); *child_scope = var->child_scope; var->shadowable = !comptime_arg; @@ -14379,14 +14380,14 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(ira->codegen->errors.length != 0); return ira->codegen->invalid_instruction; } - if (var->value->type == nullptr || type_is_invalid(var->value->type)) + if (var->var_type == nullptr || type_is_invalid(var->var_type)) return ira->codegen->invalid_instruction; bool comptime_var_mem = ir_get_var_is_comptime(var); ConstExprValue *mem_slot = nullptr; - if (var->value->special == ConstValSpecialStatic) { - mem_slot = var->value; + if (var->const_value->special == ConstValSpecialStatic) { + mem_slot = var->const_value; } else { if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const)) { // find the relevant exec_context @@ -14415,7 +14416,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(!comptime_var_mem); ptr_mut = ConstPtrMutRuntimeVar; } - return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type, + return ir_get_const_ptr(ira, instruction, mem_slot, var->var_type, ptr_mut, is_const, is_volatile, var->align_bytes); } } @@ -14426,7 +14427,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var); - var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, + var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->var_type, var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); @@ -15143,7 +15144,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen, first_var_arg, inst_fn_type_id.param_count); ZigVar *var = add_variable(ira->codegen, param_decl_node, - impl_fn->child_scope, param_name, true, var_args_val, nullptr); + impl_fn->child_scope, param_name, true, var_args_val, nullptr, var_args_val->type); impl_fn->child_scope = var->child_scope; } @@ -18707,10 +18708,11 @@ static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, Zig ZigVar *var = tld->var; - if ((err = ensure_complete_type(ira->codegen, var->value->type))) + if ((err = ensure_complete_type(ira->codegen, var->const_value->type))) return ira->codegen->builtin_types.entry_invalid; - assert(var->value->type->id == ZigTypeIdMetaType); - return var->value->data.x_type; + + assert(var->const_value->type->id == ZigTypeIdMetaType); + return var->const_value->data.x_type; } static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) { @@ -18804,25 +18806,22 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco case TldIdVar: { ZigVar *var = ((TldVar *)curr_entry->value)->var; - if ((err = ensure_complete_type(ira->codegen, var->value->type))) + if ((err = ensure_complete_type(ira->codegen, var->const_value->type))) return ErrorSemanticAnalyzeFail; - if (var->value->type->id == ZigTypeIdMetaType) - { + if (var->const_value->type->id == ZigTypeIdMetaType) { // We have a variable of type 'type', so it's actually a type definition. // 0: Data.Type: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); - inner_fields[2].data.x_union.payload = var->value; - } - else - { + inner_fields[2].data.x_union.payload = var->const_value; + } else { // We have a variable of another type, so we store the type of the variable. // 1: Data.Var: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1); ConstExprValue *payload = create_const_vals(1); payload->type = ira->codegen->builtin_types.entry_type; - payload->data.x_type = var->value->type; + payload->data.x_type = var->const_value->type; inner_fields[2].data.x_union.payload = payload; } diff --git a/test/behavior.zig b/test/behavior.zig index 9c1b3f5be512..8f89b50f5dcd 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,4 @@ comptime { - _ = @import("cases/bugs/920.zig"); _ = @import("cases/cancel.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); diff --git a/test/cases/bugs/920.zig b/test/cases/bugs/920.zig deleted file mode 100644 index e29c5c4acf5a..000000000000 --- a/test/cases/bugs/920.zig +++ /dev/null @@ -1,65 +0,0 @@ -const std = @import("std"); -const math = std.math; -const Random = std.rand.Random; - -const ZigTable = struct { - r: f64, - x: [257]f64, - f: [257]f64, - - pdf: fn (f64) f64, - is_symmetric: bool, - zero_case: fn (*Random, f64) f64, -}; - -fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (*Random, f64) f64) ZigTable { - var tables: ZigTable = undefined; - - tables.is_symmetric = is_symmetric; - tables.r = r; - tables.pdf = f; - tables.zero_case = zero_case; - - tables.x[0] = v / f(r); - tables.x[1] = r; - - for (tables.x[2..256]) |*entry, i| { - const last = tables.x[2 + i - 1]; - entry.* = f_inv(v / last + f(last)); - } - tables.x[256] = 0; - - for (tables.f[0..]) |*entry, i| { - entry.* = f(tables.x[i]); - } - - return tables; -} - -const norm_r = 3.6541528853610088; -const norm_v = 0.00492867323399; - -fn norm_f(x: f64) f64 { - return math.exp(-x * x / 2.0); -} -fn norm_f_inv(y: f64) f64 { - return math.sqrt(-2.0 * math.ln(y)); -} -fn norm_zero_case(random: *Random, u: f64) f64 { - return 0.0; -} - -const NormalDist = blk: { - @setEvalBranchQuota(30000); - break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); -}; - -test "bug 920 fixed" { - const NormalDist1 = blk: { - break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); - }; - - for (NormalDist1.f) |_, i| { - std.debug.assertOrPanic(NormalDist1.f[i] == NormalDist.f[i]); - } -} diff --git a/test/stage1/behavior/bugs/920.zig b/test/stage1/behavior/bugs/920.zig index eb8607e5d0ee..e29c5c4acf5a 100644 --- a/test/stage1/behavior/bugs/920.zig +++ b/test/stage1/behavior/bugs/920.zig @@ -2,4 +2,64 @@ const std = @import("std"); const math = std.math; const Random = std.rand.Random; +const ZigTable = struct { + r: f64, + x: [257]f64, + f: [257]f64, + pdf: fn (f64) f64, + is_symmetric: bool, + zero_case: fn (*Random, f64) f64, +}; + +fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (*Random, f64) f64) ZigTable { + var tables: ZigTable = undefined; + + tables.is_symmetric = is_symmetric; + tables.r = r; + tables.pdf = f; + tables.zero_case = zero_case; + + tables.x[0] = v / f(r); + tables.x[1] = r; + + for (tables.x[2..256]) |*entry, i| { + const last = tables.x[2 + i - 1]; + entry.* = f_inv(v / last + f(last)); + } + tables.x[256] = 0; + + for (tables.f[0..]) |*entry, i| { + entry.* = f(tables.x[i]); + } + + return tables; +} + +const norm_r = 3.6541528853610088; +const norm_v = 0.00492867323399; + +fn norm_f(x: f64) f64 { + return math.exp(-x * x / 2.0); +} +fn norm_f_inv(y: f64) f64 { + return math.sqrt(-2.0 * math.ln(y)); +} +fn norm_zero_case(random: *Random, u: f64) f64 { + return 0.0; +} + +const NormalDist = blk: { + @setEvalBranchQuota(30000); + break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); +}; + +test "bug 920 fixed" { + const NormalDist1 = blk: { + break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); + }; + + for (NormalDist1.f) |_, i| { + std.debug.assertOrPanic(NormalDist1.f[i] == NormalDist.f[i]); + } +} From 15dac3cd1adf1dddb421866415a939af414cc77f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 4 Jan 2019 13:40:19 -0500 Subject: [PATCH 150/190] copy elision: fix void optional --- src/analyze.cpp | 2 +- src/ir.cpp | 49 +++++++++++++++++++++++++++-------- test/stage1/behavior/void.zig | 5 ++++ 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index c912716bd6c2..974666e527d7 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5173,7 +5173,6 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { case ZigTypeIdOptional: case ZigTypeIdFn: case ZigTypeIdBool: - case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPromise: return OnePossibleValueNo; @@ -5189,6 +5188,7 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { case ZigTypeIdPointer: case ZigTypeIdEnum: case ZigTypeIdErrorSet: + case ZigTypeIdInt: return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes; } zig_unreachable(); diff --git a/src/ir.cpp b/src/ir.cpp index 54946849f640..8a83aae5964b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10614,13 +10614,30 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr { ConstExprValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, result_loc->source_node); assert(optional_val->type->id == ZigTypeIdOptional); - if (optional_val->special == ConstValSpecialUndef && handle_is_ptr(optional_val->type)) { - ConstExprValue *payload_val = create_const_vals(1); - payload_val->type = needed_child_type; - payload_val->special = ConstValSpecialUndef; + if (optional_val->special == ConstValSpecialUndef) { + switch (type_has_one_possible_value(ira->codegen, needed_child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + if (handle_is_ptr(optional_val->type)) { + ConstExprValue *payload_val = create_const_vals(1); + payload_val->type = needed_child_type; + payload_val->special = ConstValSpecialUndef; + + optional_val->data.x_optional = payload_val; + optional_val->special = ConstValSpecialStatic; + } + break; + case OnePossibleValueYes: { + ConstExprValue *pointee = create_const_vals(1); + pointee->special = ConstValSpecialStatic; + pointee->type = needed_child_type; - optional_val->data.x_optional = payload_val; - optional_val->special = ConstValSpecialStatic; + optional_val->special = ConstValSpecialStatic; + optional_val->data.x_optional = pointee; + break; + } + } } IrInstruction *result; @@ -10635,11 +10652,21 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr ConstExprValue *result_val = &result->value; result_val->data.x_ptr.special = ConstPtrSpecialRef; result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; - if (handle_is_ptr(optional_val->type)) { - assert(optional_val->data.x_optional != nullptr); - result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; - } else { - result_val->data.x_ptr.data.ref.pointee = optional_val; + switch (type_has_one_possible_value(ira->codegen, needed_child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + if (handle_is_ptr(optional_val->type)) { + assert(optional_val->data.x_optional != nullptr); + result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; + } else { + result_val->data.x_ptr.data.ref.pointee = optional_val; + } + break; + case OnePossibleValueYes: + assert(optional_val->data.x_optional != nullptr); + result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; + break; } return result; diff --git a/test/stage1/behavior/void.zig b/test/stage1/behavior/void.zig index af8b913534c3..431d3f4eb103 100644 --- a/test/stage1/behavior/void.zig +++ b/test/stage1/behavior/void.zig @@ -28,3 +28,8 @@ test "iterate over a void slice" { fn times(n: usize) []const void { return ([*]void)(undefined)[0..n]; } + +test "void optional" { + var x: ?void = {}; + assertOrPanic(x != null); +} From 3205c7597fcb881232196f7a10e2d9b25d2b366c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 4 Jan 2019 16:08:43 -0500 Subject: [PATCH 151/190] copy elision: fix coroutines --- src/all_types.hpp | 2 +- src/codegen.cpp | 17 ++-- src/ir.cpp | 105 ++++++++++---------- src/ir_print.cpp | 2 +- test/cases/coroutines.zig | 144 ---------------------------- test/stage1/behavior.zig | 2 +- test/stage1/behavior/coroutines.zig | 141 +++++++++++++++++++++++++++ 7 files changed, 199 insertions(+), 214 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 9dc534e6f4e1..cfa3ebe50ca9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2949,7 +2949,7 @@ struct IrInstructionTestErr { struct IrInstructionUnwrapErrCode { IrInstruction base; - IrInstruction *value; + IrInstruction *err_union; }; struct IrInstructionErrorUnionFieldErrorSet { diff --git a/src/codegen.cpp b/src/codegen.cpp index 5d03fa7f21df..af71e0886abc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4555,14 +4555,13 @@ static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildICmp(g->builder, LLVMIntNE, err_val, zero, ""); } -static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrCode *instruction) { - ZigType *ptr_type = instruction->value->value.type; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *err_union_type = ptr_type->data.pointer.child_type; +static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, + IrInstructionUnwrapErrCode *instruction) +{ + ZigType *err_union_type = instruction->err_union->value.type; assert(err_union_type->id == ZigTypeIdErrorUnion); ZigType *payload_type = err_union_type->data.error_union.payload_type; - LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); - LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); + LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->err_union); if (type_has_bits(payload_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); @@ -4914,9 +4913,7 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f args.append(alignment_val); LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, alloc_fn_val, args.items, args.length, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); - set_call_instr_sret(g, call_instruction); - LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_err_index, ""); - LLVMValueRef err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); + LLVMValueRef err_val = call_instruction; LLVMBuildStore(g->builder, err_val, err_code_ptr); LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, LLVMConstNull(LLVMTypeOf(err_val)), ""); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(fn_val, "AllocOk"); @@ -4924,7 +4921,7 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, ok_block); - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_payload_index, ""); + LLVMValueRef payload_ptr = sret_ptr; ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false, PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); ZigType *slice_type = get_slice_type(g, u8_ptr_type); diff --git a/src/ir.cpp b/src/ir.cpp index 8a83aae5964b..e8b3db298f95 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2234,12 +2234,12 @@ static IrInstruction *ir_build_test_err(IrBuilder *irb, Scope *scope, AstNode *s } static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *value) + IrInstruction *err_union) { IrInstructionUnwrapErrCode *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + instruction->err_union = err_union; - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(err_union, irb->current_basic_block); return &instruction->base; } @@ -14220,8 +14220,9 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i zig_unreachable(); } -static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, - IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst) +static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, + ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, + IrInstruction *async_allocator_inst) { Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); //Buf *free_field_name = buf_create_from_str("freeFn"); @@ -14253,11 +14254,47 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c ZigType *promise_type = get_promise_type(ira->codegen, return_type); ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); - IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, + IrInstruction *tmp_stack_space = ir_analyze_alloca(ira, &call_instruction->base, async_return_type, + 0, "", false); + tmp_stack_space->value.special = ConstValSpecialRuntime; + if (type_is_invalid(tmp_stack_space->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_call_inst = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, - async_allocator_inst, nullptr, nullptr, nullptr, LValNone); - result->value.type = async_return_type; - return result; + async_allocator_inst, nullptr, tmp_stack_space, nullptr, LValNone); + new_call_inst->value.type = async_return_type; + + IrInstruction *result_loc = nullptr; + if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { + result_loc = call_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + } + + switch (call_instruction->lval) { + case LValNone: + return new_call_inst; + case LValPtr: + return ir_get_ref(ira, &call_instruction->base, new_call_inst, true, false, nullptr); + case LValErrorUnionVal: { + IrInstruction *call_inst_ref = ir_get_ref(ira, &call_instruction->base, new_call_inst, + true, false, nullptr); + IrInstruction *payload_ptr = ir_analyze_unwrap_err_payload(ira, &call_instruction->base, + call_inst_ref, nullptr, false); + IrInstruction *payload_deref = ir_get_deref(ira, &call_instruction->base, payload_ptr); + ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, payload_deref); + IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, call_instruction->base.scope, + call_instruction->base.source_node, new_call_inst); + result->value.type = get_optional_type(ira->codegen, alloc_fn_error_set_type); + return result; + } + case LValErrorUnionPtr: + zig_panic("TODO"); + case LValOptional: + zig_panic("TODO"); + } + zig_unreachable(); } static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, @@ -21302,51 +21339,6 @@ static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnaly return ir_analyze_error_union_field_error_set(ira, &instruction->base, base_ptr, instruction->safety_check_on); } -static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, - IrInstructionUnwrapErrCode *instruction) -{ - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; - - // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. - assert(ptr_type->id == ZigTypeIdPointer); - - ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { - return ira->codegen->invalid_instruction; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - if (instr_is_comptime(value)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, - instruction->base.source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; - assert(err); - - IrInstruction *result = ir_const(ira, &instruction->base, - type_entry->data.error_union.err_set_type); - result->value.data.x_err_set = err; - return result; - } - } - - IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value); - result->value.type = type_entry->data.error_union.err_set_type; - return result; - } else { - ir_add_error(ira, value, - buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); - return ira->codegen->invalid_instruction; - } -} - static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, IrInstruction *result_loc, bool safety_check_on) { @@ -23556,6 +23548,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdResultSliceToBytesGen: case IrInstructionIdFromBytesLenGen: case IrInstructionIdToBytesLenGen: + case IrInstructionIdUnwrapErrCode: zig_unreachable(); case IrInstructionIdReturn: @@ -23704,8 +23697,6 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction); case IrInstructionIdTestErr: return ir_analyze_instruction_test_err(ira, (IrInstructionTestErr *)instruction); - case IrInstructionIdUnwrapErrCode: - return ir_analyze_instruction_unwrap_err_code(ira, (IrInstructionUnwrapErrCode *)instruction); case IrInstructionIdUnwrapErrPayload: return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstructionUnwrapErrPayload *)instruction); case IrInstructionIdAssertNonError: @@ -23880,7 +23871,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ ira->scalar_return_type = ira->explicit_return_type; ira->payload_return_type = ira->explicit_return_type; - if (fn_entry != nullptr) { + if (fn_entry != nullptr && !is_async) { if (ira->explicit_return_type->id == ZigTypeIdErrorUnion) { ira->scalar_return_type = get_optional_type(ira->codegen, ira->explicit_return_type->data.error_union.err_set_type); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 9362c5e54cd8..3a0bda4e0a45 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -820,7 +820,7 @@ static void ir_print_test_err(IrPrint *irp, IrInstructionTestErr *instruction) { static void ir_print_unwrap_err_code(IrPrint *irp, IrInstructionUnwrapErrCode *instruction) { fprintf(irp->f, "UnwrapErrorCode("); - ir_print_other_instruction(irp, instruction->value); + ir_print_other_instruction(irp, instruction->err_union); fprintf(irp->f, ")"); } diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index b8dcf907d73a..3406ac25778c 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -2,150 +2,6 @@ const std = @import("std"); const builtin = @import("builtin"); const assertOrPanic = std.debug.assertOrPanic; -var x: i32 = 1; - -test "create a coroutine and cancel it" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const p = try async<&da.allocator> simpleAsyncFn(); - comptime assertOrPanic(@typeOf(p) == promise->void); - cancel p; - assertOrPanic(x == 2); -} -async fn simpleAsyncFn() void { - x += 1; - suspend; - x += 1; -} - -test "coroutine suspend, resume, cancel" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - seq('a'); - const p = try async<&da.allocator> testAsyncSeq(); - seq('c'); - resume p; - seq('f'); - cancel p; - seq('g'); - - assertOrPanic(std.mem.eql(u8, points, "abcdefg")); -} -async fn testAsyncSeq() void { - defer seq('e'); - - seq('b'); - suspend; - seq('d'); -} -var points = []u8{0} ** "abcdefg".len; -var index: usize = 0; - -fn seq(c: u8) void { - points[index] = c; - index += 1; -} - -test "coroutine suspend with block" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const p = try async<&da.allocator> testSuspendBlock(); - std.debug.assertOrPanic(!result); - resume a_promise; - std.debug.assertOrPanic(result); - cancel p; -} - -var a_promise: promise = undefined; -var result = false; -async fn testSuspendBlock() void { - suspend { - comptime assertOrPanic(@typeOf(@handle()) == promise->void); - a_promise = @handle(); - } - - //Test to make sure that @handle() works as advertised (issue #1296) - //var our_handle: promise = @handle(); - assertOrPanic(a_promise == @handle()); - - result = true; -} - -var await_a_promise: promise = undefined; -var await_final_result: i32 = 0; - -test "coroutine await" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - await_seq('a'); - const p = async<&da.allocator> await_amain() catch unreachable; - await_seq('f'); - resume await_a_promise; - await_seq('i'); - assertOrPanic(await_final_result == 1234); - assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); -} -async fn await_amain() void { - await_seq('b'); - const p = async await_another() catch unreachable; - await_seq('e'); - await_final_result = await p; - await_seq('h'); -} -async fn await_another() i32 { - await_seq('c'); - suspend { - await_seq('d'); - await_a_promise = @handle(); - } - await_seq('g'); - return 1234; -} - -var await_points = []u8{0} ** "abcdefghi".len; -var await_seq_index: usize = 0; - -fn await_seq(c: u8) void { - await_points[await_seq_index] = c; - await_seq_index += 1; -} - -var early_final_result: i32 = 0; - -test "coroutine await early return" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - early_seq('a'); - const p = async<&da.allocator> early_amain() catch @panic("out of memory"); - early_seq('f'); - assertOrPanic(early_final_result == 1234); - assertOrPanic(std.mem.eql(u8, early_points, "abcdef")); -} -async fn early_amain() void { - early_seq('b'); - const p = async early_another() catch @panic("out of memory"); - early_seq('d'); - early_final_result = await p; - early_seq('e'); -} -async fn early_another() i32 { - early_seq('c'); - return 1234; -} - -var early_points = []u8{0} ** "abcdef".len; -var early_seq_index: usize = 0; - -fn early_seq(c: u8) void { - early_points[early_seq_index] = c; - early_seq_index += 1; -} - test "coro allocation failure" { var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0); if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) { diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 80b87fc4cbc7..e119b4afc743 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -27,7 +27,6 @@ comptime { _ = @import("behavior/cast.zig"); _ = @import("behavior/const_slice_child.zig"); _ = @import("behavior/coroutine_await_struct.zig"); - _ = @import("behavior/coroutines.zig"); _ = @import("behavior/defer.zig"); _ = @import("behavior/enum.zig"); _ = @import("behavior/enum_with_members.zig"); @@ -75,4 +74,5 @@ comptime { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); + _ = @import("behavior/coroutines.zig"); } diff --git a/test/stage1/behavior/coroutines.zig b/test/stage1/behavior/coroutines.zig index 33dd3f01ae11..be4d0fb98f0d 100644 --- a/test/stage1/behavior/coroutines.zig +++ b/test/stage1/behavior/coroutines.zig @@ -4,4 +4,145 @@ const assertOrPanic = std.debug.assertOrPanic; var x: i32 = 1; +test "create a coroutine and cancel it" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = try async<&da.allocator> simpleAsyncFn(); + comptime assertOrPanic(@typeOf(p) == promise->void); + cancel p; + assertOrPanic(x == 2); +} +async fn simpleAsyncFn() void { + x += 1; + suspend; + x += 1; +} + +test "coroutine suspend, resume, cancel" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + seq('a'); + const p = try async<&da.allocator> testAsyncSeq(); + seq('c'); + resume p; + seq('f'); + cancel p; + seq('g'); + + assertOrPanic(std.mem.eql(u8, points, "abcdefg")); +} +async fn testAsyncSeq() void { + defer seq('e'); + + seq('b'); + suspend; + seq('d'); +} +var points = []u8{0} ** "abcdefg".len; +var index: usize = 0; + +fn seq(c: u8) void { + points[index] = c; + index += 1; +} + +test "coroutine suspend with block" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = try async<&da.allocator> testSuspendBlock(); + std.debug.assertOrPanic(!result); + resume a_promise; + std.debug.assertOrPanic(result); + cancel p; +} + +var a_promise: promise = undefined; +var result = false; +async fn testSuspendBlock() void { + suspend { + comptime assertOrPanic(@typeOf(@handle()) == promise->void); + a_promise = @handle(); + } + + //Test to make sure that @handle() works as advertised (issue #1296) + //var our_handle: promise = @handle(); + assertOrPanic(a_promise == @handle()); + + result = true; +} + +var await_a_promise: promise = undefined; +var await_final_result: i32 = 0; + +test "coroutine await" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + await_seq('a'); + const p = async<&da.allocator> await_amain() catch unreachable; + await_seq('f'); + resume await_a_promise; + await_seq('i'); + assertOrPanic(await_final_result == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); +} +async fn await_amain() void { + await_seq('b'); + const p = async await_another() catch unreachable; + await_seq('e'); + await_final_result = await p; + await_seq('h'); +} +async fn await_another() i32 { + await_seq('c'); + suspend { + await_seq('d'); + await_a_promise = @handle(); + } + await_seq('g'); + return 1234; +} + +var await_points = []u8{0} ** "abcdefghi".len; +var await_seq_index: usize = 0; + +fn await_seq(c: u8) void { + await_points[await_seq_index] = c; + await_seq_index += 1; +} + +var early_final_result: i32 = 0; + +test "coroutine await early return" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + early_seq('a'); + const p = async<&da.allocator> early_amain() catch @panic("out of memory"); + early_seq('f'); + assertOrPanic(early_final_result == 1234); + assertOrPanic(std.mem.eql(u8, early_points, "abcdef")); +} +async fn early_amain() void { + early_seq('b'); + const p = async early_another() catch @panic("out of memory"); + early_seq('d'); + early_final_result = await p; + early_seq('e'); +} +async fn early_another() i32 { + early_seq('c'); + return 1234; +} + +var early_points = []u8{0} ** "abcdef".len; +var early_seq_index: usize = 0; + +fn early_seq(c: u8) void { + early_points[early_seq_index] = c; + early_seq_index += 1; +} From 434f67692da9686e810b2f4982bd995886650452 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 4 Jan 2019 16:15:21 -0500 Subject: [PATCH 152/190] copy elision: workaround for FixedBufferAllocator See https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 --- std/heap.zig | 6 ++- test/cases/coroutines.zig | 63 -------------------------- test/stage1/behavior/coroutines.zig | 68 +++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 65 deletions(-) diff --git a/std/heap.zig b/std/heap.zig index 46b247fa7ead..e0c3d1343a80 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -290,7 +290,8 @@ pub const FixedBufferAllocator = struct { const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); const addr = @ptrToInt(self.buffer.ptr) + self.end_index; const rem = @rem(addr, alignment); - const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); + // TODO: https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 + const march_forward_bytes: usize = if (rem == 0) 0 else (alignment - rem); const adjusted_index = self.end_index + march_forward_bytes; const new_end_index = adjusted_index + n; if (new_end_index > self.buffer.len) { @@ -348,7 +349,8 @@ pub const ThreadSafeFixedBufferAllocator = struct { while (true) { const addr = @ptrToInt(self.buffer.ptr) + end_index; const rem = @rem(addr, alignment); - const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); + // TODO: https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 + const march_forward_bytes: usize = if (rem == 0) 0 else (alignment - rem); const adjusted_index = end_index + march_forward_bytes; const new_end_index = adjusted_index + n; if (new_end_index > self.buffer.len) { diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 3406ac25778c..e648ad3959c6 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -2,52 +2,6 @@ const std = @import("std"); const builtin = @import("builtin"); const assertOrPanic = std.debug.assertOrPanic; -test "coro allocation failure" { - var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0); - if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) { - @panic("expected allocation failure"); - } else |err| switch (err) { - error.OutOfMemory => {}, - } -} -async fn asyncFuncThatNeverGetsRun() void { - @panic("coro frame allocation should fail"); -} - -test "async function with dot syntax" { - const S = struct { - var y: i32 = 1; - async fn foo() void { - y += 1; - suspend; - } - }; - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - const p = try async<&da.allocator> S.foo(); - cancel p; - assertOrPanic(S.y == 2); -} - -test "async fn pointer in a struct field" { - var data: i32 = 1; - const Foo = struct { - bar: async<*std.mem.Allocator> fn (*i32) void, - }; - var foo = Foo{ .bar = simpleAsyncFn2 }; - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; - assertOrPanic(data == 2); - cancel p; - assertOrPanic(data == 4); -} -async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { - defer y.* += 2; - y.* += 1; - suspend; -} - test "async fn with inferred error set" { var da = std.heap.DirectAllocator.init(); defer da.deinit(); @@ -94,20 +48,3 @@ async fn printTrace(p: promise->(anyerror!void)) void { } }; } - -test "break from suspend" { - var buf: [500]u8 = undefined; - var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; - var my_result: i32 = 1; - const p = try async testBreakFromSuspend(&my_result); - cancel p; - std.debug.assertOrPanic(my_result == 2); -} -async fn testBreakFromSuspend(my_result: *i32) void { - suspend { - resume @handle(); - } - my_result.* += 1; - suspend; - my_result.* += 1; -} diff --git a/test/stage1/behavior/coroutines.zig b/test/stage1/behavior/coroutines.zig index be4d0fb98f0d..5603054eeda7 100644 --- a/test/stage1/behavior/coroutines.zig +++ b/test/stage1/behavior/coroutines.zig @@ -146,3 +146,71 @@ fn early_seq(c: u8) void { early_seq_index += 1; } +test "coro allocation failure" { + var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0); + if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) { + @panic("expected allocation failure"); + } else |err| switch (err) { + error.OutOfMemory => {}, + } +} +async fn asyncFuncThatNeverGetsRun() void { + @panic("coro frame allocation should fail"); +} + +test "async function with dot syntax" { + const S = struct { + var y: i32 = 1; + async fn foo() void { + y += 1; + suspend; + } + }; + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = try async<&da.allocator> S.foo(); + cancel p; + assertOrPanic(S.y == 2); +} + +test "async fn pointer in a struct field" { + var data: i32 = 1; + const Foo = struct { + bar: async<*std.mem.Allocator> fn (*i32) void, + }; + var foo = Foo{ .bar = simpleAsyncFn2 }; + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; + assertOrPanic(data == 2); + cancel p; + assertOrPanic(data == 4); +} +async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { + defer y.* += 2; + y.* += 1; + suspend; +} + +// test "async fn with inferred error set" { + +// test "error return trace across suspend points - early return" { + +// test "error return trace across suspend points - async return" { + +test "break from suspend" { + var buf: [500]u8 = undefined; + var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + var my_result: i32 = 1; + const p = try async testBreakFromSuspend(&my_result); + cancel p; + std.debug.assertOrPanic(my_result == 2); +} +async fn testBreakFromSuspend(my_result: *i32) void { + suspend { + resume @handle(); + } + my_result.* += 1; + suspend; + my_result.* += 1; +} From ea6acef6219a0e40d5083cd0f0e8da98c63145e0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Jan 2019 10:26:55 -0500 Subject: [PATCH 153/190] copy elision: fix error binary operator --- src/ir.cpp | 46 +++++++++++++++++++++++++++++----- test/cases/error.zig | 10 -------- test/stage1/behavior/error.zig | 10 +++++++- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e8b3db298f95..fcea6370cd00 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7871,8 +7871,16 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop IrInstruction *err_code = ir_build_error_literal(irb, scope, node, node->data.error_literal.name); switch (lval) { case LValErrorUnionVal: + if (result_loc != nullptr) { + IrInstruction *undef = ir_build_const_undefined(irb, scope, node); + ir_build_store_ptr(irb, scope, node, result_loc, undef); + } return err_code; case LValErrorUnionPtr: + if (result_loc != nullptr) { + IrInstruction *undef = ir_build_const_undefined(irb, scope, node); + ir_build_store_ptr(irb, scope, node, result_loc, undef); + } return ir_build_ref(irb, scope, node, err_code, true, false, nullptr); default: return ir_gen_value(irb, scope, node, lval, result_loc, err_code); @@ -15453,12 +15461,38 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC return ir_const_void(ira, &call_instruction->base); } - // This is handled with the result location mechanism. So all we need to do here is - // a LoadPtr on the result location. - IrInstruction *deref = ir_get_deref(ira, &call_instruction->base, result_loc); - if (type_is_invalid(deref->value.type)) - return ira->codegen->invalid_instruction; - return ir_finish_anal(ira, deref); + switch (call_instruction->lval) { + case LValPtr: + zig_unreachable(); + case LValNone: { + IrInstruction *deref = ir_get_deref(ira, &call_instruction->base, result_loc); + if (type_is_invalid(deref->value.type)) + return ira->codegen->invalid_instruction; + return ir_finish_anal(ira, deref); + } + case LValErrorUnionVal: { + assert(result_loc != nullptr); + IrInstruction *only_arg = call_instruction->args[0]->child; + if (type_is_invalid(only_arg->value.type)) + return ira->codegen->invalid_instruction; + if (only_arg->value.type->id == ZigTypeIdErrorSet) { + IrInstruction *undef = ir_const(ira, &call_instruction->base, + ira->codegen->builtin_types.entry_undef); + ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, undef); + return ir_finish_anal(ira, only_arg); + } else if (only_arg->value.type->id == ZigTypeIdErrorUnion) { + zig_panic("TODO"); + } else { + IrInstruction *null = ir_const(ira, &call_instruction->base, + ira->codegen->builtin_types.entry_null); + return ir_finish_anal(ira, null); + } + } + case LValOptional: + zig_panic("TODO"); + case LValErrorUnionPtr: + zig_panic("TODO"); + } } IrInstruction *arg_value = call_instruction->args[0]->child; if (type_is_invalid(arg_value->value.type)) diff --git a/test/cases/error.zig b/test/cases/error.zig index b808d3f63a24..52372e71ca8a 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -3,16 +3,6 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const builtin = @import("builtin"); -test "error binary operator" { - const a = errBinaryOperatorG(true) catch 3; - const b = errBinaryOperatorG(false) catch 3; - assertOrPanic(a == 3); - assertOrPanic(b == 10); -} -fn errBinaryOperatorG(x: bool) anyerror!isize { - return if (x) error.ItBroke else isize(10); -} - test "comptime test error for empty error set" { testComptimeTestErrorEmptySet(1234); comptime testComptimeTestErrorEmptySet(1234); diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig index dd84505c1db8..fc4f39c30b63 100644 --- a/test/stage1/behavior/error.zig +++ b/test/stage1/behavior/error.zig @@ -43,7 +43,15 @@ fn shouldBeNotEqual(a: anyerror, b: anyerror) void { if (a == b) unreachable; } -// test "error binary operator" +test "error binary operator" { + const a = errBinaryOperatorG(true) catch 3; + const b = errBinaryOperatorG(false) catch 3; + assertOrPanic(a == 3); + assertOrPanic(b == 10); +} +fn errBinaryOperatorG(x: bool) anyerror!isize { + return if (x) error.ItBroke else isize(10); +} test "unwrap simple value from error" { const i = unwrapSimpleValueFromErrorDo() catch unreachable; From a697b632e9221334a2b32208f99ed88f59c09ada Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Jan 2019 10:48:41 -0500 Subject: [PATCH 154/190] copy elision: fix comptime test error for empty error set --- src/ir.cpp | 23 ++++++++++++----------- test/cases/error.zig | 11 ----------- test/stage1/behavior/error.zig | 11 ++++++++++- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index fcea6370cd00..ea81ffc28849 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16191,10 +16191,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct if (array_ptr_val == nullptr) return ira->codegen->invalid_instruction; - if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer && - array_ptr_val->special == ConstValSpecialUndef && - array_type->id == ZigTypeIdArray) - { + if (array_ptr_val->special == ConstValSpecialUndef && array_type->id == ZigTypeIdArray) { array_ptr_val->data.x_array.special = ConstArraySpecialNone; array_ptr_val->data.x_array.data.s_none.elements = create_const_vals(array_type->data.array.len); array_ptr_val->special = ConstValSpecialStatic; @@ -21311,20 +21308,26 @@ static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - ZigType *opt_err_set = get_optional_type(ira->codegen, type_entry->data.error_union.err_set_type); + ZigType *err_set_type = type_entry->data.error_union.err_set_type; + ZigType *opt_err_set = get_optional_type(ira->codegen, err_set_type); ZigType *result_type = get_pointer_to_type_extra(ira->codegen, opt_err_set, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, ptr_type->data.pointer.explicit_alignment, 0, 0); + if (!resolve_inferred_error_set(ira->codegen, err_set_type, source_instr->source_node)) + return ira->codegen->invalid_instruction; + if (err_set_type->data.error_set.err_count == 0) { + IrInstruction *null = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_null); + return ir_get_ref(ira, source_instr, null, true, true, nullptr); + } + if (instr_is_comptime(base_ptr)) { ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); - if (err_union_val->data.x_ptr.mut == ConstPtrMutInfer && - err_union_val->special == ConstValSpecialUndef) - { + if (err_union_val->special == ConstValSpecialUndef) { ConstExprValue *vals = create_const_vals(2); ConstExprValue *err_set_val = &vals[0]; ConstExprValue *payload_val = &vals[1]; @@ -21413,9 +21416,7 @@ static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstructio ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (err_union_val == nullptr) return ira->codegen->invalid_instruction; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer && - err_union_val->special == ConstValSpecialUndef) - { + if (err_union_val->special == ConstValSpecialUndef) { ConstExprValue *vals = create_const_vals(2); ConstExprValue *err_set_val = &vals[0]; ConstExprValue *payload_val = &vals[1]; diff --git a/test/cases/error.zig b/test/cases/error.zig index 52372e71ca8a..d0fdb207716b 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -3,17 +3,6 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const builtin = @import("builtin"); -test "comptime test error for empty error set" { - testComptimeTestErrorEmptySet(1234); - comptime testComptimeTestErrorEmptySet(1234); -} - -const EmptyErrorSet = error{}; - -fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { - if (x) |v| assertOrPanic(v == 1234) else |err| @compileError("bad"); -} - test "error union peer type resolution" { testErrorUnionPeerTypeResolution(1); comptime testErrorUnionPeerTypeResolution(1); diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig index fc4f39c30b63..b47fcab5f135 100644 --- a/test/stage1/behavior/error.zig +++ b/test/stage1/behavior/error.zig @@ -129,7 +129,16 @@ fn testExplicitErrorSetCast(set1: Set1) void { assertOrPanic(y == error.A); } -// test "comptime test error for empty error set" { +test "comptime test error for empty error set" { + testComptimeTestErrorEmptySet(1234); + comptime testComptimeTestErrorEmptySet(1234); +} + +const EmptyErrorSet = error{}; + +fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { + if (x) |v| assertOrPanic(v == 1234) else |err| @compileError("bad"); +} test "syntax: optional operator in front of error union operator" { comptime { From ec1516c1ee95a9b72039cd3365b6256090fb224b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Jan 2019 12:20:19 -0500 Subject: [PATCH 155/190] copy elision: fix error union peer type resolution --- src/ir.cpp | 68 +++++++++++++++++++++++----------- test/cases/error.zig | 25 ------------- test/stage1/behavior/error.zig | 31 +++++++++++++++- 3 files changed, 77 insertions(+), 47 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index ea81ffc28849..d31beeb3f907 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12194,8 +12194,8 @@ static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); } -static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *unresolved_result_loc, - ZigType *needed_child_type, bool allow_failure) +static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, AstNode *source_node, + IrInstruction *unresolved_result_loc, ZigType *needed_child_type, bool allow_failure) { if (type_is_invalid(unresolved_result_loc->value.type) || type_is_invalid(needed_child_type)) return ira->codegen->invalid_instruction; @@ -12211,11 +12211,8 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *unr if (have_child_type == needed_child_type) return result_loc; - IrInstruction *source_instr = result_loc; - AstNode *source_node = source_instr->source_node; - // perfect match or non-const to const - ConstCastOnly const_cast_result = types_match_const_cast_only(ira, needed_child_type, have_child_type, + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, have_child_type, needed_child_type, source_node, false); if (const_cast_result.id == ConstCastResultIdInvalid) return ira->codegen->invalid_instruction; @@ -12292,14 +12289,14 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, IrInstruction *unr needed_child_type->id == ZigTypeIdComptimeInt || needed_child_type->id == ZigTypeIdComptimeFloat) { - IrInstruction *cast1 = ir_implicit_cast_result(ira, result_loc, + IrInstruction *cast1 = ir_implicit_cast_result(ira, source_node, result_loc, have_child_type->data.error_union.payload_type, allow_failure); if (cast1 == nullptr) return nullptr; if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; - return ir_implicit_cast_result(ira, cast1, needed_child_type, allow_failure); + return ir_implicit_cast_result(ira, source_node, cast1, needed_child_type, allow_failure); } } @@ -14526,9 +14523,11 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return ira->codegen->invalid_instruction; } - IrInstruction *ptr = ir_implicit_cast_result(ira, uncasted_ptr, uncasted_value->value.type, true); - if (ptr == nullptr) + IrInstruction *ptr = ir_implicit_cast_result(ira, source_instr->source_node, + uncasted_ptr, uncasted_value->value.type, true); + if (ptr == nullptr) { ptr = uncasted_ptr; + } if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; @@ -14659,7 +14658,7 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, } else if (return_type->id == ZigTypeIdErrorSet) { scalar_result_type = return_type; payload_result_type = ira->codegen->builtin_types.entry_void; - convert_to_value = false; + convert_to_value = call_instruction->lval != LValErrorUnionVal && call_instruction->lval != LValErrorUnionPtr; } else if (return_type->id == ZigTypeIdOptional && handle_is_ptr(return_type)) { scalar_result_type = ira->codegen->builtin_types.entry_bool; payload_result_type = return_type->data.maybe.child_type; @@ -14700,26 +14699,34 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, } if (convert_to_value) { - base_result_loc = ir_implicit_cast_result(ira, prev_result_loc, return_type, false); + base_result_loc = ir_implicit_cast_result(ira, call_instruction->base.source_node, + prev_result_loc, return_type, false); if (type_is_invalid(base_result_loc->value.type)) return ira->codegen->invalid_instruction; if (return_type->id == ZigTypeIdErrorUnion) { payload_result_loc = ir_analyze_unwrap_err_payload(ira, &call_instruction->base, base_result_loc, nullptr, false); + if (type_is_invalid(payload_result_loc->value.type)) + return ira->codegen->invalid_instruction; + } else if (return_type->id == ZigTypeIdErrorSet) { + payload_result_loc = nullptr; } else if (return_type->id == ZigTypeIdOptional) { payload_result_loc = ir_analyze_unwrap_optional_payload(ira, &call_instruction->base, base_result_loc, false); + if (type_is_invalid(payload_result_loc->value.type)) + return ira->codegen->invalid_instruction; } else { zig_unreachable(); } } else { - payload_result_loc = ir_implicit_cast_result(ira, prev_result_loc, payload_result_type, need_store_ptr); + payload_result_loc = ir_implicit_cast_result(ira, call_instruction->base.source_node, + prev_result_loc, payload_result_type, need_store_ptr); if (payload_result_loc == nullptr) payload_result_loc = prev_result_loc; base_result_loc = payload_result_loc; + if (type_is_invalid(payload_result_loc->value.type)) + return ira->codegen->invalid_instruction; } - if (type_is_invalid(payload_result_loc->value.type)) - return ira->codegen->invalid_instruction; } // This instruction, in the case of optional and error union, is always the error code / bool value. @@ -14816,6 +14823,21 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, zig_panic("TODO"); } zig_unreachable(); + } else if (return_type->id == ZigTypeIdErrorSet) { + ir_analyze_store_ptr(ira, &call_instruction->base, base_result_loc, new_call_instruction); + switch (call_instruction->lval) { + case LValNone: + return new_call_instruction; + case LValPtr: + return base_result_loc; + case LValErrorUnionVal: + case LValErrorUnionPtr: + zig_unreachable(); + case LValOptional: + // This means incorrectly used optional syntax for error union. + zig_panic("TODO"); + } + zig_unreachable(); } else if (return_type->id == ZigTypeIdOptional) { ir_analyze_set_non_null_bit(ira, &call_instruction->base, base_result_loc, new_call_instruction); switch (call_instruction->lval) { @@ -23100,7 +23122,8 @@ static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, if (type_is_invalid(prev_result_loc->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *new_result_loc = ir_implicit_cast_result(ira, prev_result_loc, elem_type, false); + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->base.source_node, + prev_result_loc, elem_type, false); if (type_is_invalid(new_result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -23121,7 +23144,8 @@ static IrInstruction *ir_analyze_instruction_result_cast(IrAnalyze *ira, IrInstr if (type_is_invalid(prev_result_loc->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *new_result_loc = ir_implicit_cast_result(ira, prev_result_loc, elem_type, false); + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->base.source_node, + prev_result_loc, elem_type, false); if (type_is_invalid(new_result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -23198,8 +23222,8 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; - IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->prev_result_loc->child, - dest_type, true); + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->base.source_node, + instruction->prev_result_loc->child, dest_type, true); if (new_result_loc == nullptr) return nullptr; if (type_is_invalid(new_result_loc->value.type)) @@ -23472,7 +23496,8 @@ static IrInstruction *ir_analyze_instruction_result_bytes_to_slice(IrAnalyze *ir true, false, PtrLenUnknown, 0, 0, 0); ZigType *slice_of_bytes = get_slice_type(ira->codegen, slice_ptr_type); IrInstruction *prev_result_loc = instruction->prev_result_loc->child; - IrInstruction *new_result_loc = ir_implicit_cast_result(ira, prev_result_loc, slice_of_bytes, false); + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->base.source_node, + prev_result_loc, slice_of_bytes, false); if (type_is_invalid(new_result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -23520,7 +23545,8 @@ static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, ZigType *elem_ptr_type = adjust_ptr_child(ira->codegen, bytes_ptr_type, elem_type); ZigType *slice_of_elem = get_slice_type(ira->codegen, elem_ptr_type); - IrInstruction *casted_prev_result_loc = ir_implicit_cast_result(ira, prev_result_loc, slice_of_elem, false); + IrInstruction *casted_prev_result_loc = ir_implicit_cast_result(ira, source_inst->source_node, + prev_result_loc, slice_of_elem, false); if (type_is_invalid(casted_prev_result_loc->value.type)) return ira->codegen->invalid_instruction; diff --git a/test/cases/error.zig b/test/cases/error.zig index d0fdb207716b..ddd2fe072fdb 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -3,31 +3,6 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const builtin = @import("builtin"); -test "error union peer type resolution" { - testErrorUnionPeerTypeResolution(1); - comptime testErrorUnionPeerTypeResolution(1); -} - -fn testErrorUnionPeerTypeResolution(x: i32) void { - const y = switch (x) { - 1 => bar_1(), - 2 => baz_1(), - else => quux_1(), - }; -} - -fn bar_1() anyerror { - return error.A; -} - -fn baz_1() !i32 { - return error.B; -} - -fn quux_1() !i32 { - return error.C; -} - test "error: Zero sized error set returned with value payload crash" { _ = foo3(0); _ = comptime foo3(0); diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig index b47fcab5f135..acf025e4ac4d 100644 --- a/test/stage1/behavior/error.zig +++ b/test/stage1/behavior/error.zig @@ -159,7 +159,36 @@ fn testErrToIntWithOnePossibleValue( } } -// test "error union peer type resolution" { +test "error union peer type resolution" { + testErrorUnionPeerTypeResolution(1); + comptime testErrorUnionPeerTypeResolution(1); +} + +fn testErrorUnionPeerTypeResolution(x: i32) void { + // TODO https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 + const y: anyerror!i32 = switch (x) { + 1 => bar_1(), + 2 => baz_1(), + else => quux_1(), + }; + if (y) |_| { + @panic("expected error"); + } else |e| { + assertOrPanic(e == error.A); + } +} + +fn bar_1() anyerror { + return error.A; +} + +fn baz_1() !i32 { + return error.B; +} + +fn quux_1() !i32 { + return error.C; +} test "error: fn returning empty error set can be passed as fn returning any error" { entry(); From b49201ab4c6a94778c223da6efb6b43ccb765065 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Jan 2019 14:17:13 -0500 Subject: [PATCH 156/190] copy elision: fix test: "error zero sized error set returned with value payload crash" --- src/all_types.hpp | 11 +-- src/analyze.cpp | 79 +++++++++++----------- src/ir.cpp | 118 ++++++++++++++++++++++----------- src/ir.hpp | 4 +- test/cases/error.zig | 10 --- test/stage1/behavior/error.zig | 11 ++- 6 files changed, 136 insertions(+), 97 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index cfa3ebe50ca9..d003e30a5dc3 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -57,9 +57,6 @@ struct IrExecutable { size_t next_debug_id; size_t *backward_branch_count; size_t backward_branch_quota; - bool invalid; - bool is_inline; - bool is_generic_instantiation; ZigFn *fn_entry; Buf *c_import_buf; AstNode *source_node; @@ -79,6 +76,10 @@ struct IrExecutable { IrBasicBlock *coro_suspend_block; IrBasicBlock *coro_final_cleanup_block; ZigVar *coro_allocator_var; + + bool invalid; + bool is_inline; + bool is_generic_instantiation; }; enum OutType { @@ -703,7 +704,7 @@ struct AstNodeUse { AstNode *expr; TldResolution resolution; - IrInstruction *value; + ConstExprValue *value; }; struct AstNodeIfBoolExpr { @@ -1635,7 +1636,7 @@ struct CodeGen { HashMap fn_type_table; HashMap error_table; HashMap generic_table; - HashMap memoized_fn_eval_table; + HashMap memoized_fn_eval_table; HashMap llvm_fn_table; HashMap exported_symbol_names; HashMap external_prototypes; diff --git a/src/analyze.cpp b/src/analyze.cpp index 974666e527d7..a440239d6034 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1295,7 +1295,9 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind return entry; } -static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name) { +static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, + Buf *type_name) +{ size_t backward_branch_count = 0; return ir_eval_const_value(g, scope, node, type_entry, &backward_branch_count, default_backward_branch_quota, @@ -1303,12 +1305,12 @@ static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *nod } ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) { - IrInstruction *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr); - if (result->value.type->id == ZigTypeIdInvalid) + ConstExprValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr); + if (type_is_invalid(result->type)) return g->builtin_types.entry_invalid; - assert(result->value.special != ConstValSpecialRuntime); - return result->value.data.x_type; + assert(result->special != ConstValSpecialRuntime); + return result->data.x_type; } ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { @@ -1359,11 +1361,11 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { - IrInstruction *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr); - if (type_is_invalid(align_result->value.type)) + ConstExprValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr); + if (type_is_invalid(align_result->type)) return false; - uint32_t align_bytes = bigint_as_unsigned(&align_result->value.data.x_bigint); + uint32_t align_bytes = bigint_as_unsigned(&align_result->data.x_bigint); if (align_bytes == 0) { add_node_error(g, node, buf_sprintf("alignment must be >= 1")); return false; @@ -1381,12 +1383,12 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf ** ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0); ZigType *str_type = get_slice_type(g, ptr_type); - IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr); - if (type_is_invalid(instr->value.type)) + ConstExprValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr); + if (type_is_invalid(result_val->type)) return false; - ConstExprValue *ptr_field = &instr->value.data.x_struct.fields[slice_ptr_index]; - ConstExprValue *len_field = &instr->value.data.x_struct.fields[slice_len_index]; + ConstExprValue *ptr_field = &result_val->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *len_field = &result_val->data.x_struct.fields[slice_len_index]; assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray); ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; @@ -2521,20 +2523,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { // In this first pass we resolve explicit tag values. // In a second pass we will fill in the unspecified ones. if (tag_value != nullptr) { - IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); - if (result_inst->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (type_is_invalid(result->type)) { enum_type->data.enumeration.is_invalid = true; continue; } - assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == ZigTypeIdInt || - result_inst->value.type->id == ZigTypeIdComptimeInt); - auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt || + result->type->id == ZigTypeIdComptimeInt); + auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value); if (entry == nullptr) { - bigint_init_bigint(&type_enum_field->value, &result_inst->value.data.x_bigint); + bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint); } else { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + bigint_append_buf(val_buf, &result->data.x_bigint, 10); ErrorMsg *msg = add_node_error(g, tag_value, buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); @@ -2950,19 +2952,19 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { // In a second pass we will fill in the unspecified ones. if (tag_value != nullptr) { ZigType *tag_int_type = tag_type->data.enumeration.tag_int_type; - IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); - if (result_inst->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (result->type->id == ZigTypeIdInvalid) { union_type->data.unionation.is_invalid = true; continue; } - assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == ZigTypeIdInt); - auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt); + auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value); if (entry == nullptr) { - bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint); + bigint_init_bigint(&union_field->enum_field->value, &result->data.x_bigint); } else { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + bigint_append_buf(val_buf, &result->data.x_bigint, 10); ErrorMsg *msg = add_node_error(g, tag_value, buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); @@ -3688,7 +3690,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { linkage = VarLinkageInternal; } - IrInstruction *init_value = nullptr; + ConstExprValue *init_value = nullptr; // TODO more validation for types that can't be used for export/extern variables ZigType *implicit_type = nullptr; @@ -3697,7 +3699,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { } else if (var_decl->expr) { init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type, var_decl->symbol); assert(init_value); - implicit_type = init_value->value.type; + implicit_type = init_value->type; if (implicit_type->id == ZigTypeIdUnreachable) { add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable")); @@ -3715,7 +3717,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant")); implicit_type = g->builtin_types.entry_invalid; } - assert(implicit_type->id == ZigTypeIdInvalid || init_value->value.special != ConstValSpecialRuntime); + assert(implicit_type->id == ZigTypeIdInvalid || init_value->special != ConstValSpecialRuntime); } else if (linkage != VarLinkageExternal) { add_node_error(g, source_node, buf_sprintf("variables must be initialized")); implicit_type = g->builtin_types.entry_invalid; @@ -3724,7 +3726,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { ZigType *type = explicit_type ? explicit_type : implicit_type; assert(type != nullptr); // should have been caught by the parser - ConstExprValue *init_val = init_value ? &init_value->value : create_const_runtime(type); + ConstExprValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(type); tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, is_const, init_val, &tld_var->base, type); @@ -4144,7 +4146,7 @@ void analyze_fn_ir(CodeGen *g, ZigFn *fn_table_entry, AstNode *return_type_node) FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; ZigType *block_return_type = ir_analyze(g, &fn_table_entry->ir_executable, - &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node); + &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node, nullptr); fn_table_entry->src_implicit_return_type = block_return_type; if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) { @@ -4239,18 +4241,17 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode * preview_use_decl(g, src_use_node); } - IrInstruction *use_target_value = src_use_node->data.use.value; - if (use_target_value->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *use_target_value = src_use_node->data.use.value; + if (use_target_value->type->id == ZigTypeIdInvalid) { dst_use_node->owner->any_imports_failed = true; return; } dst_use_node->data.use.resolution = TldResolutionOk; - ConstExprValue *const_val = &use_target_value->value; - assert(const_val->special != ConstValSpecialRuntime); + assert(use_target_value->special != ConstValSpecialRuntime); - ImportTableEntry *target_import = const_val->data.x_import; + ImportTableEntry *target_import = use_target_value->data.x_import; assert(target_import); if (target_import->any_imports_failed) { @@ -4313,10 +4314,10 @@ void preview_use_decl(CodeGen *g, AstNode *node) { } node->data.use.resolution = TldResolutionResolving; - IrInstruction *result = analyze_const_value(g, &node->owner->decls_scope->base, + ConstExprValue *result = analyze_const_value(g, &node->owner->decls_scope->base, node->data.use.expr, g->builtin_types.entry_namespace, nullptr); - if (result->value.type->id == ZigTypeIdInvalid) + if (type_is_invalid(result->type)) node->owner->any_imports_failed = true; node->data.use.value = result; diff --git a/src/ir.cpp b/src/ir.cpp index d31beeb3f907..ffcb7a79463d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -39,6 +39,7 @@ struct IrAnalyze { ZigType *payload_return_type; ZigList src_implicit_return_type_list; IrBasicBlock *const_predecessor_bb; + ConstExprValue *result_value; }; enum ConstCastResultId { @@ -8240,7 +8241,7 @@ ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprVal return val; } -static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { +static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { IrBasicBlock *bb = exec->basic_block_list.at(0); for (size_t i = 0; i < bb->instruction_list.length; i += 1) { IrInstruction *instruction = bb->instruction_list.at(i); @@ -8250,16 +8251,16 @@ static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) if (value->value.special == ConstValSpecialRuntime) { exec_add_error_node(codegen, exec, value->source_node, buf_sprintf("unable to evaluate constant expression")); - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } - return value; + return &value->value; } else if (ir_has_side_effects(instruction)) { exec_add_error_node(codegen, exec, instruction->source_node, buf_sprintf("unable to evaluate constant expression")); - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } } - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) { @@ -10509,13 +10510,13 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un zig_unreachable(); } -IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, +ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutable *parent_exec) { if (expected_type != nullptr && type_is_invalid(expected_type)) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; IrExecutable *ir_executable = allocate(1); ir_executable->source_node = source_node; @@ -10528,7 +10529,7 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node ir_gen(codegen, node, scope, ir_executable); if (ir_executable->invalid) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { fprintf(stderr, "\nSource: "); @@ -10548,9 +10549,12 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node analyzed_executable->backward_branch_count = backward_branch_count; analyzed_executable->backward_branch_quota = backward_branch_quota; analyzed_executable->begin_scope = scope; - ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node); + + ConstExprValue *result_value = create_const_vals(1); + ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node, + result_value); if (type_is_invalid(result_type)) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); @@ -12199,6 +12203,14 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, AstNode *source_no { if (type_is_invalid(unresolved_result_loc->value.type) || type_is_invalid(needed_child_type)) return ira->codegen->invalid_instruction; + + assert(unresolved_result_loc->value.type->id == ZigTypeIdPointer); + if (unresolved_result_loc->value.special == ConstValSpecialStatic && + unresolved_result_loc->value.data.x_ptr.special == ConstPtrSpecialDiscard) + { + return unresolved_result_loc; + } + IrInstruction *result_loc = resolve_possible_alloca_inference(ira, unresolved_result_loc, needed_child_type); if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -14675,8 +14687,17 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, IrInstruction *prev_result_loc = nullptr; if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { prev_result_loc = call_instruction->result_loc->child; + if (prev_result_loc->value.special == ConstValSpecialStatic && + prev_result_loc->value.data.x_ptr.special == ConstPtrSpecialDiscard) + { + need_store_ptr = false; + convert_to_value = false; + prev_result_loc = nullptr; + } } else { need_store_ptr = false; + } + if (prev_result_loc == nullptr) { if (return_type->id == ZigTypeIdErrorUnion || (type_has_bits(return_type) && want_first_arg_sret(ira->codegen, fn_type_id))) { @@ -15002,7 +15023,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } bool cacheable = fn_eval_cacheable(exec_scope, return_type); - IrInstruction *result = nullptr; + ConstExprValue *result = nullptr; if (cacheable) { auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); if (entry) @@ -15018,19 +15039,19 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call if (inferred_err_set_type != nullptr) { inferred_err_set_type->data.error_set.infer_fn = nullptr; - if (result->value.type->id == ZigTypeIdErrorUnion) { - ErrorTableEntry *err = result->value.data.x_err_union.error_set->data.x_err_set; + if (result->type->id == ZigTypeIdErrorUnion) { + ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set; if (err != nullptr) { inferred_err_set_type->data.error_set.err_count = 1; inferred_err_set_type->data.error_set.errors = allocate(1); inferred_err_set_type->data.error_set.errors[0] = err; } - ZigType *fn_inferred_err_set_type = result->value.type->data.error_union.err_set_type; + ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type; inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors; - } else if (result->value.type->id == ZigTypeIdErrorSet) { - inferred_err_set_type->data.error_set.err_count = result->value.type->data.error_set.err_count; - inferred_err_set_type->data.error_set.errors = result->value.type->data.error_set.errors; + } else if (result->type->id == ZigTypeIdErrorSet) { + inferred_err_set_type->data.error_set.err_count = result->type->data.error_set.err_count; + inferred_err_set_type->data.error_set.errors = result->type->data.error_set.errors; } } @@ -15038,14 +15059,14 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ira->codegen->memoized_fn_eval_table.put(exec_scope, result); } - if (type_is_invalid(result->value.type)) + if (type_is_invalid(result->type)) return ira->codegen->invalid_instruction; } // TODO test with optionals and error unions - IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->value.type); + IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->type); // TODO should we use copy_const_val? - new_instruction->value = result->value; + new_instruction->value = *result; if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { IrInstruction *prev_result_loc = call_instruction->result_loc->child; @@ -15243,13 +15264,16 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } if (fn_proto_node->data.fn_proto.align_expr != nullptr) { - IrInstruction *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, + ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen), ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec); + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr); + const_instruction->base.value = *align_result; uint32_t align_bytes = 0; - ir_resolve_align(ira, align_result, &align_bytes); + ir_resolve_align(ira, &const_instruction->base, &align_bytes); impl_fn->align_bytes = align_bytes; inst_fn_type_id.alignment = align_bytes; } @@ -19805,10 +19829,10 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct // Execute the C import block like an inline function ZigType *void_type = ira->codegen->builtin_types.entry_void; - IrInstruction *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, + ConstExprValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, &cimport_scope->buf, block_node, nullptr, nullptr); - if (type_is_invalid(cimport_result->value.type)) + if (type_is_invalid(cimport_result->type)) return ira->codegen->invalid_instruction; find_libc_include_path(ira->codegen); @@ -23080,27 +23104,25 @@ static IrInstruction *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, return ir_const_void(ira, &instruction->base); } -static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrInstructionResultReturn *instruction) { - ZigFn *fn = exec_fn_entry(ira->new_irb.exec); - if (fn == nullptr) { - ir_add_error(ira, &instruction->base, buf_sprintf("return outside function")); - return ira->codegen->invalid_instruction; - } - +static IrInstruction *ir_analyze_result_return(IrAnalyze *ira, IrInstruction *source_inst) { // Here we create a pass2 instruction for getting the return value pointer. // However we create a const value for it and mark it with ConstPtrMutInfer // so that if the return expression is comptime-known, we can emit a memcpy // rather than runtime instructions instructions. + ZigType *result_type = get_pointer_to_type(ira->codegen, ira->payload_return_type, false); - ConstExprValue *pointee = create_const_vals(1); - pointee->special = ConstValSpecialUndef; - pointee->type = ira->payload_return_type; - - ZigType *result_type = get_pointer_to_type(ira->codegen, pointee->type, false); - IrInstruction *result = ir_build_result_return(&ira->new_irb, instruction->base.scope, - instruction->base.source_node); + IrInstruction *result = ir_build_result_return(&ira->new_irb, source_inst->scope, source_inst->source_node); result->value.type = result_type; - if (type_has_bits(pointee->type) && handle_is_ptr(pointee->type)) { + if (ira->result_value != nullptr) { + result->value.special = ConstValSpecialStatic; + result->value.data.x_ptr.special = ConstPtrSpecialRef; + result->value.data.x_ptr.data.ref.pointee = ira->result_value; + result->value.data.x_ptr.mut = ConstPtrMutComptimeVar; + } else if (type_has_bits(ira->payload_return_type) && handle_is_ptr(ira->payload_return_type)) { + ConstExprValue *pointee = create_const_vals(1); + pointee->special = ConstValSpecialUndef; + pointee->type = ira->payload_return_type; + result->value.special = ConstValSpecialStatic; result->value.data.x_ptr.special = ConstPtrSpecialRef; result->value.data.x_ptr.data.ref.pointee = pointee; @@ -23111,6 +23133,16 @@ static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrIns return result; } +static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrInstructionResultReturn *instruction) { + ZigFn *fn = exec_fn_entry(ira->new_irb.exec); + if (fn == nullptr) { + ir_add_error(ira, &instruction->base, buf_sprintf("return outside function")); + return ira->codegen->invalid_instruction; + } + + return ir_analyze_result_return(ira, &instruction->base); +} + static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, IrInstructionResultPtrCast *instruction) { @@ -23916,7 +23948,7 @@ static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *old_ // This function attempts to evaluate IR code while doing type checking and other analysis. // It emits a new IrExecutable which is partially evaluated IR code. ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec, - ZigType *expected_type, AstNode *expected_type_source_node) + ZigType *expected_type, AstNode *expected_type_source_node, ConstExprValue *result_value) { assert(!old_exec->invalid); assert(expected_type == nullptr || !type_is_invalid(expected_type)); @@ -23924,6 +23956,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ IrAnalyze *ira = allocate(1); old_exec->analysis = ira; ira->codegen = codegen; + ira->result_value = result_value; ZigFn *fn_entry = exec_fn_entry(old_exec); bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; @@ -23945,6 +23978,11 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ } } + if (result_value != nullptr) { + result_value->special = ConstValSpecialUndef; + result_value->type = ira->payload_return_type; + } + ira->old_irb.codegen = codegen; ira->old_irb.exec = old_exec; diff --git a/src/ir.hpp b/src/ir.hpp index 7af1d7f52ba8..9d378d194f7f 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -13,13 +13,13 @@ bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutable *ir_executable); bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry); -IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, +ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutable *parent_exec); ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable, - ZigType *expected_type, AstNode *expected_type_source_node); + ZigType *expected_type, AstNode *expected_type_source_node, ConstExprValue *result_value); bool ir_has_side_effects(IrInstruction *instruction); diff --git a/test/cases/error.zig b/test/cases/error.zig index ddd2fe072fdb..12ab848fa9c4 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -3,16 +3,6 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const builtin = @import("builtin"); -test "error: Zero sized error set returned with value payload crash" { - _ = foo3(0); - _ = comptime foo3(0); -} - -const Error = error{}; -fn foo3(b: usize) Error!usize { - return b; -} - test "error: Infer error set from literals" { _ = nullLiteral("n") catch |err| handleErrors(err); _ = floatLiteral("n") catch |err| handleErrors(err); diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig index acf025e4ac4d..7c41031cad60 100644 --- a/test/stage1/behavior/error.zig +++ b/test/stage1/behavior/error.zig @@ -205,5 +205,14 @@ fn foo2(f: fn () anyerror!void) void { fn bar2() (error{}!void) {} -// test "error: Zero sized error set returned with value payload crash" { +test "error: Zero sized error set returned with value payload crash" { + _ = foo3(0); + _ = comptime foo3(0); +} + +const Error = error{}; +fn foo3(b: usize) Error!usize { + return b; +} + // test "error: Infer error set from literals" { From b93ca1718d02a131e52f30aa11b570172885014c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Jan 2019 18:23:44 -0500 Subject: [PATCH 157/190] copy elision: fix test: "error: Infer error set from literals" --- src/analyze.cpp | 6 +- src/ir.cpp | 123 ++++++++++++++++++++++++++++----- src/ir.hpp | 2 +- test/behavior.zig | 1 - test/cases/error.zig | 39 ----------- test/stage1/behavior/error.zig | 35 +++++++++- 6 files changed, 142 insertions(+), 64 deletions(-) delete mode 100644 test/cases/error.zig diff --git a/src/analyze.cpp b/src/analyze.cpp index a440239d6034..9fac8243fada 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4145,11 +4145,11 @@ void analyze_fn_ir(CodeGen *g, ZigFn *fn_table_entry, AstNode *return_type_node) assert(!fn_type->data.fn.is_generic); FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - ZigType *block_return_type = ir_analyze(g, &fn_table_entry->ir_executable, + ConstExprValue *block_result = ir_analyze(g, &fn_table_entry->ir_executable, &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node, nullptr); - fn_table_entry->src_implicit_return_type = block_return_type; + fn_table_entry->src_implicit_return_type = block_result->type; - if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) { + if (type_is_invalid(block_result->type) || fn_table_entry->analyzed_executable.invalid) { assert(g->errors.length > 0); fn_table_entry->anal_state = FnAnalStateInvalid; return; diff --git a/src/ir.cpp b/src/ir.cpp index ffcb7a79463d..a7bf6e199c1b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3434,8 +3434,13 @@ static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, } static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { - if (result_loc) - return result_loc; + if (result_loc) { + if (result_loc->value.special != ConstValSpecialStatic || + result_loc->value.data.x_ptr.special != ConstPtrSpecialDiscard) + { + return result_loc; + } + } return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "", nullptr); } @@ -10550,10 +10555,10 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod analyzed_executable->backward_branch_quota = backward_branch_quota; analyzed_executable->begin_scope = scope; - ConstExprValue *result_value = create_const_vals(1); - ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node, - result_value); - if (type_is_invalid(result_type)) + ConstExprValue *payload_result = create_const_vals(1); + ConstExprValue *analyze_result = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node, + payload_result); + if (type_is_invalid(analyze_result->type)) return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { @@ -10562,7 +10567,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod fprintf(stderr, "}\n"); } - return ir_exec_const_result(codegen, analyzed_executable); + return analyze_result; } static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { @@ -15081,10 +15086,53 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } } - IrInstruction *store_inst = ir_analyze_store_ptr(ira, &call_instruction->base, - prev_result_loc, new_instruction); - if (type_is_invalid(store_inst->value.type)) - return ira->codegen->invalid_instruction; + switch (call_instruction->lval) { + case LValNone: { + IrInstruction *store_inst = ir_analyze_store_ptr(ira, &call_instruction->base, + prev_result_loc, new_instruction); + if (type_is_invalid(store_inst->value.type)) + return ira->codegen->invalid_instruction; + return ir_finish_anal(ira, ir_const_void(ira, &call_instruction->base)); + } + case LValErrorUnionPtr: { + IrInstruction *ref_inst = ir_get_ref(ira, &call_instruction->base, new_instruction, + true, false, nullptr); + if (type_is_invalid(ref_inst->value.type)) + return ira->codegen->invalid_instruction; + if (new_instruction->value.type->id == ZigTypeIdErrorSet) { + IrInstruction *undef = ir_const(ira, &call_instruction->base, + ira->codegen->builtin_types.entry_undef); + ir_analyze_store_ptr(ira, &call_instruction->base, prev_result_loc, undef); + return ir_finish_anal(ira, ref_inst); + } + IrInstruction *payload_ptr = ir_analyze_unwrap_err_payload(ira, + &call_instruction->base, ref_inst, nullptr, false); + if (type_is_invalid(payload_ptr->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *payload = ir_get_deref(ira, &call_instruction->base, payload_ptr); + if (type_is_invalid(payload->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *store_inst = ir_analyze_store_ptr(ira, &call_instruction->base, + prev_result_loc, payload); + if (type_is_invalid(store_inst->value.type)) + return ira->codegen->invalid_instruction; + assert(new_instruction->value.type->id == ZigTypeIdErrorUnion); + ZigType *err_set_type = new_instruction->value.type->data.error_union.err_set_type; + ZigType *opt_err_set_type = get_optional_type(ira->codegen, err_set_type); + IrInstruction *err_code_ptr = ir_analyze_result_error_union_code(ira, ref_inst, + opt_err_set_type); + if (type_is_invalid(err_code_ptr->value.type)) + return ira->codegen->invalid_instruction; + return ir_finish_anal(ira, err_code_ptr); + } + case LValPtr: + zig_panic("TODO"); + case LValOptional: + zig_panic("TODO"); + case LValErrorUnionVal: + zig_panic("TODO"); + } + zig_unreachable(); } switch (call_instruction->lval) { @@ -15095,10 +15143,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call true, false, nullptr); return ir_finish_anal(ira, ref_inst); } - case LValErrorUnionVal: - zig_unreachable(); case LValErrorUnionPtr: zig_unreachable(); + case LValErrorUnionVal: + zig_unreachable(); case LValOptional: zig_unreachable(); } @@ -23947,7 +23995,7 @@ static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *old_ // This function attempts to evaluate IR code while doing type checking and other analysis. // It emits a new IrExecutable which is partially evaluated IR code. -ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec, +ConstExprValue *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec, ZigType *expected_type, AstNode *expected_type_source_node, ConstExprValue *result_value) { assert(!old_exec->invalid); @@ -24014,7 +24062,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ IrInstruction *new_instruction = ir_analyze_instruction(ira, old_instruction); if (new_instruction != nullptr) { if (type_is_invalid(new_instruction->value.type) && ir_should_inline(new_exec, old_instruction->scope)) { - return ira->codegen->builtin_types.entry_invalid; + return &ira->codegen->invalid_instruction->value; } // unreachable instructions do their own control flow. @@ -24025,13 +24073,50 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ ira->instruction_index += 1; } + if (new_exec->invalid) { - return ira->codegen->builtin_types.entry_invalid; + return &ira->codegen->invalid_instruction->value; } else if (ira->src_implicit_return_type_list.length == 0) { - return codegen->builtin_types.entry_unreachable; + return &ira->codegen->unreach_instruction->value; + } + ZigType *implicit_scalar_return_type = ir_resolve_peer_types(ira, expected_type_source_node, expected_type, + ira->src_implicit_return_type_list.items, ira->src_implicit_return_type_list.length); + if (type_is_invalid(implicit_scalar_return_type)) + return &ira->codegen->invalid_instruction->value; + if (result_value == nullptr) { + ConstExprValue *result_val = create_const_vals(1); + result_val->type = implicit_scalar_return_type; + result_val->special = ConstValSpecialRuntime; + return result_val; + } + + ConstExprValue *scalar_result = ir_exec_const_result(codegen, new_exec); + if (type_is_invalid(scalar_result->type)) + return &ira->codegen->invalid_instruction->value; + + if (ira->scalar_return_type == ira->explicit_return_type) { + return scalar_result; + } else if (ira->scalar_return_type->id == ZigTypeIdVoid) { + return result_value; + } else if (ira->scalar_return_type->id == ZigTypeIdBool) { + ConstExprValue *result_val = create_const_vals(1); + result_val->type = ira->explicit_return_type; + result_val->special = ConstValSpecialStatic; + if (scalar_result->data.x_bool) { + result_val->data.x_optional = result_value; + } else { + result_val->data.x_optional = nullptr; + } + return result_val; + } else if (ira->scalar_return_type->id == ZigTypeIdOptional) { + ConstExprValue *result_val = create_const_vals(1); + result_val->type = ira->explicit_return_type; + result_val->special = ConstValSpecialStatic; + result_val->data.x_err_union.error_set = scalar_result; + result_val->data.x_err_union.payload = result_value; + return result_val; } else { - return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, - ira->src_implicit_return_type_list.length); + zig_unreachable(); } } diff --git a/src/ir.hpp b/src/ir.hpp index 9d378d194f7f..89044c2fde66 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -18,7 +18,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutable *parent_exec); -ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable, +ConstExprValue *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable, ZigType *expected_type, AstNode *expected_type_source_node, ConstExprValue *result_value); bool ir_has_side_effects(IrInstruction *instruction); diff --git a/test/behavior.zig b/test/behavior.zig index 8f89b50f5dcd..fca794960110 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -6,7 +6,6 @@ comptime { _ = @import("cases/coroutines.zig"); _ = @import("cases/defer.zig"); _ = @import("cases/enum_with_members.zig"); - _ = @import("cases/error.zig"); _ = @import("cases/eval.zig"); _ = @import("cases/ir_block_deps.zig"); _ = @import("cases/null.zig"); diff --git a/test/cases/error.zig b/test/cases/error.zig deleted file mode 100644 index 12ab848fa9c4..000000000000 --- a/test/cases/error.zig +++ /dev/null @@ -1,39 +0,0 @@ -const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; -const mem = std.mem; -const builtin = @import("builtin"); - -test "error: Infer error set from literals" { - _ = nullLiteral("n") catch |err| handleErrors(err); - _ = floatLiteral("n") catch |err| handleErrors(err); - _ = intLiteral("n") catch |err| handleErrors(err); - _ = comptime nullLiteral("n") catch |err| handleErrors(err); - _ = comptime floatLiteral("n") catch |err| handleErrors(err); - _ = comptime intLiteral("n") catch |err| handleErrors(err); -} - -fn handleErrors(err: var) noreturn { - switch (err) { - error.T => {}, - } - - unreachable; -} - -fn nullLiteral(str: []const u8) !?i64 { - if (str[0] == 'n') return null; - - return error.T; -} - -fn floatLiteral(str: []const u8) !?f64 { - if (str[0] == 'n') return 1.0; - - return error.T; -} - -fn intLiteral(str: []const u8) !?i64 { - if (str[0] == 'n') return 1; - - return error.T; -} diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig index 7c41031cad60..79e935903099 100644 --- a/test/stage1/behavior/error.zig +++ b/test/stage1/behavior/error.zig @@ -215,4 +215,37 @@ fn foo3(b: usize) Error!usize { return b; } -// test "error: Infer error set from literals" { +test "error: Infer error set from literals" { + _ = nullLiteral("n") catch |err| handleErrors(err); + _ = floatLiteral("n") catch |err| handleErrors(err); + _ = intLiteral("n") catch |err| handleErrors(err); + _ = comptime nullLiteral("n") catch |err| handleErrors(err); + _ = comptime floatLiteral("n") catch |err| handleErrors(err); + _ = comptime intLiteral("n") catch |err| handleErrors(err); +} + +fn handleErrors(err: var) noreturn { + switch (err) { + error.T => {}, + } + + unreachable; +} + +fn nullLiteral(str: []const u8) !?i64 { + if (str[0] == 'n') return null; + + return error.T; +} + +fn floatLiteral(str: []const u8) !?f64 { + if (str[0] == 'n') return 1.0; + + return error.T; +} + +fn intLiteral(str: []const u8) !?i64 { + if (str[0] == 'n') return 1; + + return error.T; +} From e383bba2d455f14b3f9f740242c50affec4be235 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Jan 2019 18:30:48 -0500 Subject: [PATCH 158/190] copy elision: move passing tests --- test/behavior.zig | 1 - test/cases/cancel.zig | 92 --------------------------------- test/cases/null.zig | 6 --- test/cases/try.zig | 18 ------- test/stage1/behavior/cancel.zig | 90 ++++++++++++++++++++++++++++++++ test/stage1/behavior/try.zig | 17 ++++++ 6 files changed, 107 insertions(+), 117 deletions(-) delete mode 100644 test/cases/cancel.zig diff --git a/test/behavior.zig b/test/behavior.zig index fca794960110..d36131e86fd8 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,4 @@ comptime { - _ = @import("cases/cancel.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); _ = @import("cases/coroutine_await_struct.zig"); diff --git a/test/cases/cancel.zig b/test/cases/cancel.zig deleted file mode 100644 index 863da4bdb831..000000000000 --- a/test/cases/cancel.zig +++ /dev/null @@ -1,92 +0,0 @@ -const std = @import("std"); - -var defer_f1: bool = false; -var defer_f2: bool = false; -var defer_f3: bool = false; - -test "cancel forwards" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const p = async<&da.allocator> f1() catch unreachable; - cancel p; - std.debug.assertOrPanic(defer_f1); - std.debug.assertOrPanic(defer_f2); - std.debug.assertOrPanic(defer_f3); -} - -async fn f1() void { - defer { - defer_f1 = true; - } - await (async f2() catch unreachable); -} - -async fn f2() void { - defer { - defer_f2 = true; - } - await (async f3() catch unreachable); -} - -async fn f3() void { - defer { - defer_f3 = true; - } - suspend; -} - -var defer_b1: bool = false; -var defer_b2: bool = false; -var defer_b3: bool = false; -var defer_b4: bool = false; - -test "cancel backwards" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const p = async<&da.allocator> b1() catch unreachable; - cancel p; - std.debug.assertOrPanic(defer_b1); - std.debug.assertOrPanic(defer_b2); - std.debug.assertOrPanic(defer_b3); - std.debug.assertOrPanic(defer_b4); -} - -async fn b1() void { - defer { - defer_b1 = true; - } - await (async b2() catch unreachable); -} - -var b4_handle: promise = undefined; - -async fn b2() void { - const b3_handle = async b3() catch unreachable; - resume b4_handle; - cancel b4_handle; - defer { - defer_b2 = true; - } - const value = await b3_handle; - @panic("unreachable"); -} - -async fn b3() i32 { - defer { - defer_b3 = true; - } - await (async b4() catch unreachable); - return 1234; -} - -async fn b4() void { - defer { - defer_b4 = true; - } - suspend { - b4_handle = @handle(); - } - suspend; -} diff --git a/test/cases/null.zig b/test/cases/null.zig index 892a425a10d7..72c376a1d4a2 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -52,9 +52,3 @@ test "optional types" { const StructWithOptionalType = struct { t: ?type, }; - -test "optional pointer to 0 bit type null value at runtime" { - const EmptyStruct = struct {}; - var x: ?*EmptyStruct = null; - assertOrPanic(x == null); -} diff --git a/test/cases/try.zig b/test/cases/try.zig index ed48875eb466..5df58e25caff 100644 --- a/test/cases/try.zig +++ b/test/cases/try.zig @@ -1,23 +1,5 @@ const assertOrPanic = @import("std").debug.assertOrPanic; -test "try on error union" { - tryOnErrorUnionImpl(); - comptime tryOnErrorUnionImpl(); -} - -fn tryOnErrorUnionImpl() void { - const x = if (returnsTen()) |val| val + 1 else |err| switch (err) { - error.ItBroke, error.NoMem => 1, - error.CrappedOut => i32(2), - else => unreachable, - }; - assertOrPanic(x == 11); -} - -fn returnsTen() anyerror!i32 { - return 10; -} - test "try without vars" { const result1 = if (failIfTrue(true)) 1 else |_| i32(2); assertOrPanic(result1 == 2); diff --git a/test/stage1/behavior/cancel.zig b/test/stage1/behavior/cancel.zig index b31ab7d330d2..863da4bdb831 100644 --- a/test/stage1/behavior/cancel.zig +++ b/test/stage1/behavior/cancel.zig @@ -1,2 +1,92 @@ const std = @import("std"); +var defer_f1: bool = false; +var defer_f2: bool = false; +var defer_f3: bool = false; + +test "cancel forwards" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = async<&da.allocator> f1() catch unreachable; + cancel p; + std.debug.assertOrPanic(defer_f1); + std.debug.assertOrPanic(defer_f2); + std.debug.assertOrPanic(defer_f3); +} + +async fn f1() void { + defer { + defer_f1 = true; + } + await (async f2() catch unreachable); +} + +async fn f2() void { + defer { + defer_f2 = true; + } + await (async f3() catch unreachable); +} + +async fn f3() void { + defer { + defer_f3 = true; + } + suspend; +} + +var defer_b1: bool = false; +var defer_b2: bool = false; +var defer_b3: bool = false; +var defer_b4: bool = false; + +test "cancel backwards" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = async<&da.allocator> b1() catch unreachable; + cancel p; + std.debug.assertOrPanic(defer_b1); + std.debug.assertOrPanic(defer_b2); + std.debug.assertOrPanic(defer_b3); + std.debug.assertOrPanic(defer_b4); +} + +async fn b1() void { + defer { + defer_b1 = true; + } + await (async b2() catch unreachable); +} + +var b4_handle: promise = undefined; + +async fn b2() void { + const b3_handle = async b3() catch unreachable; + resume b4_handle; + cancel b4_handle; + defer { + defer_b2 = true; + } + const value = await b3_handle; + @panic("unreachable"); +} + +async fn b3() i32 { + defer { + defer_b3 = true; + } + await (async b4() catch unreachable); + return 1234; +} + +async fn b4() void { + defer { + defer_b4 = true; + } + suspend { + b4_handle = @handle(); + } + suspend; +} diff --git a/test/stage1/behavior/try.zig b/test/stage1/behavior/try.zig index 41c447315af6..66e3d4abeb6c 100644 --- a/test/stage1/behavior/try.zig +++ b/test/stage1/behavior/try.zig @@ -1,2 +1,19 @@ const assertOrPanic = @import("std").debug.assertOrPanic; +test "try on error union" { + tryOnErrorUnionImpl(); + comptime tryOnErrorUnionImpl(); +} + +fn tryOnErrorUnionImpl() void { + const x = if (returnsTen()) |val| val + 1 else |err| switch (err) { + error.ItBroke, error.NoMem => 1, + error.CrappedOut => i32(2), + else => unreachable, + }; + assertOrPanic(x == 11); +} + +fn returnsTen() anyerror!i32 { + return 10; +} From 725b6fa928d53f3551e9b783ea31c238725fd666 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Jan 2019 11:55:36 -0500 Subject: [PATCH 159/190] better implementation of type_has_one_possible_value it wasn't catching pointer to comptime_int: ```zig export fn entry(cond: bool) void { const result1 = if (cond) 1 else 2; } ``` --- src/analyze.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 9fac8243fada..3f7fd69d9478 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5176,6 +5176,7 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { case ZigTypeIdBool: case ZigTypeIdFloat: case ZigTypeIdPromise: + case ZigTypeIdErrorUnion: return OnePossibleValueNo; case ZigTypeIdUndefined: case ZigTypeIdNull: @@ -5183,14 +5184,32 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { case ZigTypeIdUnreachable: return OnePossibleValueYes; case ZigTypeIdArray: + if (type_entry->data.array.len == 0) + return OnePossibleValueYes; + return type_has_one_possible_value(g, type_entry->data.array.child_type); case ZigTypeIdStruct: - case ZigTypeIdUnion: - case ZigTypeIdErrorUnion: - case ZigTypeIdPointer: - case ZigTypeIdEnum: + for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { + TypeStructField *field = &type_entry->data.structure.fields[i]; + switch (type_has_one_possible_value(g, field->type_entry)) { + case OnePossibleValueInvalid: + return OnePossibleValueInvalid; + case OnePossibleValueNo: + return OnePossibleValueNo; + case OnePossibleValueYes: + continue; + } + } + return OnePossibleValueYes; case ZigTypeIdErrorSet: + case ZigTypeIdEnum: case ZigTypeIdInt: return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes; + case ZigTypeIdPointer: + return type_has_one_possible_value(g, type_entry->data.pointer.child_type); + case ZigTypeIdUnion: + if (type_entry->data.unionation.src_field_count > 1) + return OnePossibleValueNo; + return type_has_one_possible_value(g, type_entry->data.unionation.fields[0].type_entry); } zig_unreachable(); } From f164c59fc3d92a8e52fef63e28b9477743cf5eae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Jan 2019 12:03:43 -0500 Subject: [PATCH 160/190] copy elision: move passing tests --- test/behavior.zig | 1 - test/cases/try.zig | 25 ------------------------- test/stage1/behavior/try.zig | 24 ++++++++++++++++++++++++ 3 files changed, 24 insertions(+), 26 deletions(-) delete mode 100644 test/cases/try.zig diff --git a/test/behavior.zig b/test/behavior.zig index d36131e86fd8..7e2e254d904d 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -9,5 +9,4 @@ comptime { _ = @import("cases/ir_block_deps.zig"); _ = @import("cases/null.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); - _ = @import("cases/try.zig"); } diff --git a/test/cases/try.zig b/test/cases/try.zig deleted file mode 100644 index 5df58e25caff..000000000000 --- a/test/cases/try.zig +++ /dev/null @@ -1,25 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -test "try without vars" { - const result1 = if (failIfTrue(true)) 1 else |_| i32(2); - assertOrPanic(result1 == 2); - - const result2 = if (failIfTrue(false)) 1 else |_| i32(2); - assertOrPanic(result2 == 1); -} - -fn failIfTrue(ok: bool) anyerror!void { - if (ok) { - return error.ItBroke; - } else { - return; - } -} - -test "try then not executed with assignment" { - if (failIfTrue(true)) { - unreachable; - } else |err| { - assertOrPanic(err == error.ItBroke); - } -} diff --git a/test/stage1/behavior/try.zig b/test/stage1/behavior/try.zig index 66e3d4abeb6c..fb802d02c626 100644 --- a/test/stage1/behavior/try.zig +++ b/test/stage1/behavior/try.zig @@ -17,3 +17,27 @@ fn tryOnErrorUnionImpl() void { fn returnsTen() anyerror!i32 { return 10; } + +test "try without vars" { + const result1: i32 = if (failIfTrue(true)) 1 else |_| 2; + assertOrPanic(result1 == 2); + + const result2: i32 = if (failIfTrue(false)) 1 else |_| 2; + assertOrPanic(result2 == 1); +} + +fn failIfTrue(ok: bool) anyerror!void { + if (ok) { + return error.ItBroke; + } else { + return; + } +} + +test "try then not executed with assignment" { + if (failIfTrue(true)) { + unreachable; + } else |err| { + assertOrPanic(err == error.ItBroke); + } +} From 0a8c211d447630e3465ec957f2f3119ac6cbc468 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Jan 2019 14:42:06 -0500 Subject: [PATCH 161/190] copy elision: fix some error union return type stuff --- src/analyze.cpp | 4 ++++ src/ir.cpp | 26 +++++++++++++-------- std/fmt/index.zig | 4 ++-- test/behavior.zig | 2 -- test/cases/coroutines.zig | 12 ---------- test/cases/enum_with_members.zig | 27 ---------------------- test/cases/ir_block_deps.zig | 21 ----------------- test/stage1/behavior.zig | 2 +- test/stage1/behavior/enum_with_members.zig | 23 ++++++++++++++++++ test/stage1/behavior/ir_block_deps.zig | 19 +++++++++++++++ 10 files changed, 66 insertions(+), 74 deletions(-) delete mode 100644 test/cases/enum_with_members.zig delete mode 100644 test/cases/ir_block_deps.zig diff --git a/src/analyze.cpp b/src/analyze.cpp index 3f7fd69d9478..26a670ae53f4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4163,6 +4163,10 @@ void analyze_fn_ir(CodeGen *g, ZigFn *fn_table_entry, AstNode *return_type_node) inferred_err_set_type = fn_table_entry->src_implicit_return_type; } else if (fn_table_entry->src_implicit_return_type->id == ZigTypeIdErrorUnion) { inferred_err_set_type = fn_table_entry->src_implicit_return_type->data.error_union.err_set_type; + } else if (fn_table_entry->src_implicit_return_type->id == ZigTypeIdOptional && + fn_table_entry->src_implicit_return_type->data.maybe.child_type->id == ZigTypeIdErrorSet) + { + inferred_err_set_type = fn_table_entry->src_implicit_return_type->data.maybe.child_type; } else { add_node_error(g, return_type_node, buf_sprintf("function with inferred error set must return at least one possible error")); diff --git a/src/ir.cpp b/src/ir.cpp index a7bf6e199c1b..76d698587975 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9618,7 +9618,13 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (prev_type->id == ZigTypeIdErrorSet) { assert(err_set_type != nullptr); - if (cur_type->id == ZigTypeIdErrorSet) { + if (cur_type->id == ZigTypeIdErrorSet || + (cur_type->id == ZigTypeIdOptional && cur_type->data.maybe.child_type->id == ZigTypeIdErrorSet)) + { + if (cur_type->id == ZigTypeIdOptional) { + cur_type = cur_type->data.maybe.child_type; + any_are_null = true; + } if (type_is_global_error_set(err_set_type)) { continue; } @@ -10075,8 +10081,15 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT return slice_type; } } else if (err_set_type != nullptr) { - if (prev_inst->value.type->id == ZigTypeIdErrorSet) { - return err_set_type; + if (prev_inst->value.type->id == ZigTypeIdErrorSet || + (prev_inst->value.type->id == ZigTypeIdOptional && + prev_inst->value.type->data.maybe.child_type->id == ZigTypeIdErrorSet)) + { + if (any_are_null) { + return get_optional_type(ira->codegen, err_set_type); + } else { + return err_set_type; + } } else if (prev_inst->value.type->id == ZigTypeIdErrorUnion) { ZigType *payload_type = prev_inst->value.type->data.error_union.payload_type; if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) @@ -19086,12 +19099,7 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco ensure_field_index(fn_def_val->type, "return_type", 7); fn_def_fields[7].special = ConstValSpecialStatic; fn_def_fields[7].type = ira->codegen->builtin_types.entry_type; - if (fn_entry->src_implicit_return_type != nullptr) - fn_def_fields[7].data.x_type = fn_entry->src_implicit_return_type; - else if (fn_entry->type_entry->data.fn.gen_return_type != nullptr) - fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.gen_return_type; - else - fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; // arg_names: [][] const u8 ensure_field_index(fn_def_val->type, "arg_names", 8); size_t fn_arg_count = fn_entry->variable_list.length; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index cfc4031a8c99..0a5ed7f81f91 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -702,7 +702,7 @@ fn formatIntSigned( const minus_sign: u8 = '-'; try output(context, (*[1]u8)(&minus_sign)[0..]); const new_value = @intCast(uint, -(value + 1)) + 1; - const new_width = if (width == 0) 0 else (width - 1); + const new_width: usize = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); } else if (width == 0) { return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output); @@ -710,7 +710,7 @@ fn formatIntSigned( const plus_sign: u8 = '+'; try output(context, (*[1]u8)(&plus_sign)[0..]); const new_value = @intCast(uint, value); - const new_width = if (width == 0) 0 else (width - 1); + const new_width: usize = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); } } diff --git a/test/behavior.zig b/test/behavior.zig index 7e2e254d904d..f2a3973502a2 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -4,9 +4,7 @@ comptime { _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); _ = @import("cases/defer.zig"); - _ = @import("cases/enum_with_members.zig"); _ = @import("cases/eval.zig"); - _ = @import("cases/ir_block_deps.zig"); _ = @import("cases/null.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); } diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index e648ad3959c6..2622607e1094 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -2,18 +2,6 @@ const std = @import("std"); const builtin = @import("builtin"); const assertOrPanic = std.debug.assertOrPanic; -test "async fn with inferred error set" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - const p = (async<&da.allocator> failing()) catch unreachable; - resume p; - cancel p; -} -async fn failing() !void { - suspend; - return error.Fail; -} - test "error return trace across suspend points - early return" { const p = nonFailing(); resume p; diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig deleted file mode 100644 index 49af1ceae78e..000000000000 --- a/test/cases/enum_with_members.zig +++ /dev/null @@ -1,27 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; -const mem = @import("std").mem; -const fmt = @import("std").fmt; - -const ET = union(enum) { - SINT: i32, - UINT: u32, - - pub fn print(a: *const ET, buf: []u8) anyerror!usize { - return switch (a.*) { - ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), - ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), - }; - } -}; - -test "enum with members" { - const a = ET{ .SINT = -42 }; - const b = ET{ .UINT = 42 }; - var buf: [20]u8 = undefined; - - assertOrPanic((a.print(buf[0..]) catch unreachable) == 3); - assertOrPanic(mem.eql(u8, buf[0..3], "-42")); - - assertOrPanic((b.print(buf[0..]) catch unreachable) == 2); - assertOrPanic(mem.eql(u8, buf[0..2], "42")); -} diff --git a/test/cases/ir_block_deps.zig b/test/cases/ir_block_deps.zig deleted file mode 100644 index bc61e11df7a2..000000000000 --- a/test/cases/ir_block_deps.zig +++ /dev/null @@ -1,21 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -fn foo(id: u64) !i32 { - return switch (id) { - 1 => getErrInt(), - 2 => { - const size = try getErrInt(); - return try getErrInt(); - }, - else => error.ItBroke, - }; -} - -fn getErrInt() anyerror!i32 { - return 0; -} - -test "ir block deps" { - assertOrPanic((foo(1) catch unreachable) == 0); - assertOrPanic((foo(2) catch unreachable) == 0); -} diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index e119b4afc743..80b87fc4cbc7 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -27,6 +27,7 @@ comptime { _ = @import("behavior/cast.zig"); _ = @import("behavior/const_slice_child.zig"); _ = @import("behavior/coroutine_await_struct.zig"); + _ = @import("behavior/coroutines.zig"); _ = @import("behavior/defer.zig"); _ = @import("behavior/enum.zig"); _ = @import("behavior/enum_with_members.zig"); @@ -74,5 +75,4 @@ comptime { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); - _ = @import("behavior/coroutines.zig"); } diff --git a/test/stage1/behavior/enum_with_members.zig b/test/stage1/behavior/enum_with_members.zig index 20f14c40c08e..49af1ceae78e 100644 --- a/test/stage1/behavior/enum_with_members.zig +++ b/test/stage1/behavior/enum_with_members.zig @@ -2,3 +2,26 @@ const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; const fmt = @import("std").fmt; +const ET = union(enum) { + SINT: i32, + UINT: u32, + + pub fn print(a: *const ET, buf: []u8) anyerror!usize { + return switch (a.*) { + ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), + ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), + }; + } +}; + +test "enum with members" { + const a = ET{ .SINT = -42 }; + const b = ET{ .UINT = 42 }; + var buf: [20]u8 = undefined; + + assertOrPanic((a.print(buf[0..]) catch unreachable) == 3); + assertOrPanic(mem.eql(u8, buf[0..3], "-42")); + + assertOrPanic((b.print(buf[0..]) catch unreachable) == 2); + assertOrPanic(mem.eql(u8, buf[0..2], "42")); +} diff --git a/test/stage1/behavior/ir_block_deps.zig b/test/stage1/behavior/ir_block_deps.zig index 41c447315af6..bc61e11df7a2 100644 --- a/test/stage1/behavior/ir_block_deps.zig +++ b/test/stage1/behavior/ir_block_deps.zig @@ -1,2 +1,21 @@ const assertOrPanic = @import("std").debug.assertOrPanic; +fn foo(id: u64) !i32 { + return switch (id) { + 1 => getErrInt(), + 2 => { + const size = try getErrInt(); + return try getErrInt(); + }, + else => error.ItBroke, + }; +} + +fn getErrInt() anyerror!i32 { + return 0; +} + +test "ir block deps" { + assertOrPanic((foo(1) catch unreachable) == 0); + assertOrPanic((foo(2) catch unreachable) == 0); +} From a3b8b5d88a0456a28f596f99e0dbb1b2187a97f2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Jan 2019 17:33:17 -0500 Subject: [PATCH 162/190] copy elision: fix test: "comptime modification of const struct field" --- src/ir.cpp | 21 +++++++++++++-------- test/cases/eval.zig | 15 --------------- test/stage1/behavior/eval.zig | 15 ++++++++++++++- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 76d698587975..1e46bb8e280a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12635,8 +12635,7 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio static IrInstruction *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *instruction) { IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - // TODO determine if we need to use copy_const_val here - result->value = instruction->base.value; + copy_const_val(&result->value, &instruction->base.value, true); return result; } @@ -14579,7 +14578,14 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source if (dest_val == nullptr) return ira->codegen->invalid_instruction; if (dest_val->special != ConstValSpecialRuntime) { - *dest_val = value->value; + // TODO this allows a value stored to have the original value modified and then + // have that affect what should be a copy. We need some kind of advanced copy-on-write + // system to make these two tests pass at the same time: + // * "string literal used as comptime slice is memoized" + // * "comptime modification of const struct field" - except modified to avoid + // ConstPtrMutComptimeVar, thus defeating the logic below. + bool same_global_refs = ptr->value.data.x_ptr.mut != ConstPtrMutComptimeVar; + copy_const_val(dest_val, &value->value, same_global_refs); if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { switch (type_has_one_possible_value(ira->codegen, child_type)) { case OnePossibleValueInvalid: @@ -14848,6 +14854,8 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, if (return_type->id == ZigTypeIdErrorUnion) { IrInstruction *err_code_ptr = ir_analyze_error_union_field_error_set(ira, &call_instruction->base, base_result_loc, false); + if (type_is_invalid(err_code_ptr->value.type)) + return ira->codegen->invalid_instruction; ir_analyze_store_ptr(ira, &call_instruction->base, err_code_ptr, new_call_instruction); switch (call_instruction->lval) { case LValNone: @@ -15081,10 +15089,8 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; } - // TODO test with optionals and error unions IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->type); - // TODO should we use copy_const_val? - new_instruction->value = *result; + copy_const_val(&new_instruction->value, result, true); if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { IrInstruction *prev_result_loc = call_instruction->result_loc->child; @@ -16010,8 +16016,7 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (value->value.special != ConstValSpecialRuntime) { IrInstruction *result = ir_const(ira, &phi_instruction->base, nullptr); - // TODO use copy_const_val? - result->value = value->value; + copy_const_val(&result->value, &value->value, true); return result; } else { return value; diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 9ebd38600363..37a6ea56fc28 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -2,21 +2,6 @@ const std = @import("std"); const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); -pub const Info = struct { - version: u8, -}; - -pub const diamond_info = Info{ .version = 0 }; - -test "comptime modification of const struct field" { - comptime { - var res = diamond_info; - res.version = 1; - assertOrPanic(diamond_info.version == 0); - assertOrPanic(res.version == 1); - } -} - test "@bytesToslice on a packed struct" { const F = packed struct { a: u8, diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig index 0072a47bd35e..b333f4f31bd5 100644 --- a/test/stage1/behavior/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -565,7 +565,20 @@ test "runtime 128 bit integer division" { assertOrPanic(c == 15231399999); } -// comptime modification of const struct field +pub const Info = struct { + version: u8, +}; + +pub const diamond_info = Info{ .version = 0 }; + +test "comptime modification of const struct field" { + comptime { + var res = diamond_info; + res.version = 1; + assertOrPanic(diamond_info.version == 0); + assertOrPanic(res.version == 1); + } +} test "pointer to type" { comptime { From 84931e48e4c694a4ba51146da1f8b3b5a4ce7328 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Jan 2019 17:35:59 -0500 Subject: [PATCH 163/190] copy elision: move passing tests --- test/behavior.zig | 1 - test/cases/const_slice_child.zig | 45 ---------------------- test/stage1/behavior/const_slice_child.zig | 42 ++++++++++++++++++++ 3 files changed, 42 insertions(+), 46 deletions(-) delete mode 100644 test/cases/const_slice_child.zig diff --git a/test/behavior.zig b/test/behavior.zig index f2a3973502a2..81bb301df350 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,6 +1,5 @@ comptime { _ = @import("cases/cast.zig"); - _ = @import("cases/const_slice_child.zig"); _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); _ = @import("cases/defer.zig"); diff --git a/test/cases/const_slice_child.zig b/test/cases/const_slice_child.zig deleted file mode 100644 index 5b9b70a55801..000000000000 --- a/test/cases/const_slice_child.zig +++ /dev/null @@ -1,45 +0,0 @@ -const debug = @import("std").debug; -const assertOrPanic = debug.assertOrPanic; - -var argv: [*]const [*]const u8 = undefined; - -test "const slice child" { - const strs = ([][*]const u8){ - c"one", - c"two", - c"three", - }; - // TODO this should implicitly cast - argv = @ptrCast([*]const [*]const u8, &strs); - bar(strs.len); -} - -fn foo(args: [][]const u8) void { - assertOrPanic(args.len == 3); - assertOrPanic(streql(args[0], "one")); - assertOrPanic(streql(args[1], "two")); - assertOrPanic(streql(args[2], "three")); -} - -fn bar(argc: usize) void { - const args = debug.global_allocator.alloc([]const u8, argc) catch unreachable; - for (args) |_, i| { - const ptr = argv[i]; - args[i] = ptr[0..strlen(ptr)]; - } - foo(args); -} - -fn strlen(ptr: [*]const u8) usize { - var count: usize = 0; - while (ptr[count] != 0) : (count += 1) {} - return count; -} - -fn streql(a: []const u8, b: []const u8) bool { - if (a.len != b.len) return false; - for (a) |item, index| { - if (b[index] != item) return false; - } - return true; -} diff --git a/test/stage1/behavior/const_slice_child.zig b/test/stage1/behavior/const_slice_child.zig index 795268e4065a..5b9b70a55801 100644 --- a/test/stage1/behavior/const_slice_child.zig +++ b/test/stage1/behavior/const_slice_child.zig @@ -1,3 +1,45 @@ const debug = @import("std").debug; const assertOrPanic = debug.assertOrPanic; +var argv: [*]const [*]const u8 = undefined; + +test "const slice child" { + const strs = ([][*]const u8){ + c"one", + c"two", + c"three", + }; + // TODO this should implicitly cast + argv = @ptrCast([*]const [*]const u8, &strs); + bar(strs.len); +} + +fn foo(args: [][]const u8) void { + assertOrPanic(args.len == 3); + assertOrPanic(streql(args[0], "one")); + assertOrPanic(streql(args[1], "two")); + assertOrPanic(streql(args[2], "three")); +} + +fn bar(argc: usize) void { + const args = debug.global_allocator.alloc([]const u8, argc) catch unreachable; + for (args) |_, i| { + const ptr = argv[i]; + args[i] = ptr[0..strlen(ptr)]; + } + foo(args); +} + +fn strlen(ptr: [*]const u8) usize { + var count: usize = 0; + while (ptr[count] != 0) : (count += 1) {} + return count; +} + +fn streql(a: []const u8, b: []const u8) bool { + if (a.len != b.len) return false; + for (a) |item, index| { + if (b[index] != item) return false; + } + return true; +} From 80f7c857cc512ccff103ac543daab01a4b5934e9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 Jan 2019 13:54:40 -0500 Subject: [PATCH 164/190] copy elision: workaround for test case "@bytesToSlice on a packed struct" See https://github.com/ziglang/zig/pull/1682#issuecomment-452813480 --- src/ir.cpp | 5 +++++ test/behavior.zig | 1 - test/cases/eval.zig | 14 -------------- test/stage1/behavior/eval.zig | 11 ++++++++++- 4 files changed, 15 insertions(+), 16 deletions(-) delete mode 100644 test/cases/eval.zig diff --git a/src/ir.cpp b/src/ir.cpp index 1e46bb8e280a..b643668ab9e6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -23623,6 +23623,8 @@ static IrInstruction *ir_analyze_instruction_result_bytes_to_slice(IrAnalyze *ir static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *prev_result_loc, ZigType *elem_type, ZigType *bytes_slice_type) { + Error err; + if (!is_slice(bytes_slice_type)) { ir_add_error(ira, source_inst, buf_sprintf("expected slice, found '%s'", buf_ptr(&bytes_slice_type->name))); @@ -23636,6 +23638,9 @@ static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, return ira->codegen->invalid_instruction; } + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_instruction; + ZigType *elem_ptr_type = adjust_ptr_child(ira->codegen, bytes_ptr_type, elem_type); ZigType *slice_of_elem = get_slice_type(ira->codegen, elem_ptr_type); IrInstruction *casted_prev_result_loc = ir_implicit_cast_result(ira, source_inst->source_node, diff --git a/test/behavior.zig b/test/behavior.zig index 81bb301df350..9c38c8766c25 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -3,7 +3,6 @@ comptime { _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); _ = @import("cases/defer.zig"); - _ = @import("cases/eval.zig"); _ = @import("cases/null.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); } diff --git a/test/cases/eval.zig b/test/cases/eval.zig deleted file mode 100644 index 37a6ea56fc28..000000000000 --- a/test/cases/eval.zig +++ /dev/null @@ -1,14 +0,0 @@ -const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; -const builtin = @import("builtin"); - -test "@bytesToslice on a packed struct" { - const F = packed struct { - a: u8, - }; - - var b = [1]u8{9}; - var f = @bytesToSlice(F, b); - assertOrPanic(f[0].a == 9); -} - diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig index b333f4f31bd5..7341df71993c 100644 --- a/test/stage1/behavior/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -704,7 +704,16 @@ test "@intCast to a u0" { assertOrPanic(y == 0); } -// test "@bytesToslice on a packed struct" +test "@bytesToSlice on a packed struct" { + const F = packed struct { + a: u8, + }; + + var b = [1]u8{9}; + // TODO https://github.com/ziglang/zig/pull/1682#issuecomment-452813480 + var f = @bytesToSlice(F, b[0..]); + assertOrPanic(f[0].a == 9); +} test "comptime pointer cast array and then slice" { const array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; From 5cbc18aa802e37f73e8129c384432b49f0d41d9f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 Jan 2019 17:56:00 -0500 Subject: [PATCH 165/190] copy elision: block results guarantee copy elision --- src/all_types.hpp | 38 +++++++++++++----------- src/ir.cpp | 76 ++++++++++++++++++++++++++++------------------- 2 files changed, 66 insertions(+), 48 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index d003e30a5dc3..d4d47b0be4f6 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1869,6 +1869,22 @@ struct ErrorTableEntry { ConstExprValue *cached_error_name_val; }; +enum LVal { + // The instruction must return the actual value. + LValNone, + // The instruction must return a pointer to the actual value. + LValPtr, + // The instruction must populate the result location, which is + // an error union, and return the optional error code value. + LValErrorUnionVal, + // The instruction must populate the result location, which is + // an error union, and return a pointer to optional error code value. + LValErrorUnionPtr, + // The instruction must populate the result location, which is + // an optional, and return non-null bool value. + LValOptional, +}; + enum ScopeId { ScopeIdDecls, ScopeIdBlock, @@ -1923,10 +1939,12 @@ struct ScopeBlock { ZigList *incoming_values; ZigList *incoming_blocks; - bool safety_off; AstNode *safety_set_node; - bool fast_math_on; AstNode *fast_math_set_node; + + LVal lval; + bool safety_off; + bool fast_math_on; }; // This scope is created from every defer expression. @@ -2063,22 +2081,6 @@ struct IrBasicBlock { IrInstruction *must_be_comptime_source_instr; }; -enum LVal { - // The instruction must return the actual value. - LValNone, - // The instruction must return a pointer to the actual value. - LValPtr, - // The instruction must populate the result location, which is - // an error union, and return the optional error code value. - LValErrorUnionVal, - // The instruction must populate the result location, which is - // an error union, and return a pointer to optional error code value. - LValErrorUnionPtr, - // The instruction must populate the result location, which is - // an optional, and return non-null bool value. - LValOptional, -}; - // These instructions are in transition to having "pass 1" instructions // and "pass 2" instructions. The pass 1 instructions are suffixed with Src // and pass 2 are suffixed with Gen. diff --git a/src/ir.cpp b/src/ir.cpp index b643668ab9e6..b2e318ef18f6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3634,13 +3634,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindError: { assert(expr_node); - if (lval == LValOptional) { - add_node_error(irb->codegen, expr_node, - buf_sprintf("TODO: compiler bug: copy elision not advanced enough to handle nested try")); - return irb->codegen->invalid_instruction; - } - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; IrInstruction *opt_err_code = ir_gen_node(irb, expr_node, scope, LValErrorUnionVal, ensured_result_loc); if (opt_err_code == irb->codegen->invalid_instruction) @@ -3776,7 +3772,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode } if (block_node->data.block.name != nullptr) { - scope_block->result_loc = ensure_result_loc(irb, parent_scope, block_node, result_loc); + scope_block->result_loc = result_loc; + scope_block->lval = lval; scope_block->incoming_blocks = &incoming_blocks; scope_block->incoming_values = &incoming_values; scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); @@ -3816,28 +3813,20 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode } ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); - return ir_gen_result(irb, parent_scope, block_node, lval, scope_block->result_loc); + return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); + } else { + incoming_blocks.append(irb->current_basic_block); + incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node))); } if (block_node->data.block.name != nullptr) { - IrInstruction *continuation_expr_result = ir_build_const_void(irb, parent_scope, block_node); - ir_build_store_ptr(irb, parent_scope, block_node, scope_block->result_loc, continuation_expr_result); - ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime)); ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); - return ir_gen_result(irb, parent_scope, block_node, lval, scope_block->result_loc); + return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else { ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); - if (parent_scope->id == ScopeIdLoop) { - if (reinterpret_cast(parent_scope)->result_loc == result_loc) { - // This block is a loop. We do not need to generate a value for the block - // returning as it is handled by the loop generation code. - return ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)); - } - } - IrInstruction *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)); - return ir_gen_value(irb, parent_scope, block_node, lval, result_loc, void_inst); + return ir_mark_gen(ir_mark_gen(ir_build_const_void(irb, child_scope, block_node))); } } @@ -3972,6 +3961,10 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode { assert(node->type == NodeTypeBinOpExpr); + result_loc = ensure_result_loc(irb, parent_scope, node, result_loc); + if (result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; @@ -4146,7 +4139,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, ir_gen_error_union(irb, scope, node)); case BinOpTypeUnwrapOptional: - return ir_gen_orelse(irb, scope, node, lval, ensure_result_loc(irb, scope, node, result_loc)); + return ir_gen_orelse(irb, scope, node, lval, result_loc); } zig_unreachable(); } @@ -4595,6 +4588,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg5_value; IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + IrInstruction *payload_ptr = (lval == LValOptional) ? ensured_result_loc : ir_build_result_optional_payload(irb, scope, node, ensured_result_loc, arg0_value, false); @@ -4776,6 +4772,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + IrInstruction *new_result_loc = ir_build_result_slice_to_bytes_src(irb, scope, node, arg0_value, ensured_result_loc); @@ -4791,6 +4790,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdToBytes: { IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + IrInstruction *new_result_loc = ir_build_result_bytes_to_slice(irb, scope, node, ensured_result_loc); AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -5065,6 +5067,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + IrInstruction *new_result_loc = ir_build_result_ptr_cast(irb, scope, node, arg0_value, ensured_result_loc); @@ -5701,6 +5706,10 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A { assert(node->type == NodeTypeContainerInitExpr); + result_loc = ensure_result_loc(irb, scope, node, result_loc); + if (result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; ContainerInitKind kind = container_init_expr->kind; @@ -6811,7 +6820,7 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop IrInstruction *result_value; if (node->data.break_expr.expr) { - result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope, LValNone, block_scope->result_loc); + result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope, block_scope->lval, block_scope->result_loc); if (result_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { @@ -6973,7 +6982,10 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { assert(node->type == NodeTypeSliceExpr); - assert(result_loc != nullptr); + + result_loc = ensure_result_loc(irb, scope, node, result_loc); + if (result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; AstNodeSliceExpr *slice_expr = &node->data.slice_expr; AstNode *array_node = slice_expr->array_ref_expr; @@ -7006,6 +7018,10 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode { assert(node->type == NodeTypeUnwrapErrorExpr); + result_loc = ensure_result_loc(irb, parent_scope, node, result_loc); + if (result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + AstNode *op1_node = node->data.unwrap_err_expr.op1; AstNode *op2_node = node->data.unwrap_err_expr.op2; AstNode *var_node = node->data.unwrap_err_expr.symbol; @@ -7788,8 +7804,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc); case NodeTypeContainerInitExpr: - return ir_gen_container_init_expr(irb, scope, node, lval, - ensure_result_loc(irb, scope, node, result_loc)); + return ir_gen_container_init_expr(irb, scope, node, lval, result_loc); case NodeTypeVariableDeclaration: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_var_decl(irb, scope, node)); case NodeTypeWhileExpr: @@ -7811,6 +7826,9 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop } case NodeTypeUnwrapOptional: { IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + AstNode *expr_node = node->data.unwrap_optional.expr; IrInstruction *is_non_null = ir_gen_node(irb, expr_node, scope, LValOptional, ensured_result_loc); if (is_non_null == irb->codegen->invalid_instruction) @@ -7854,11 +7872,9 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeDefer: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_defer(irb, scope, node)); case NodeTypeSliceExpr: - return ir_gen_slice(irb, scope, node, lval, - ensure_result_loc(irb, scope, node, result_loc)); + return ir_gen_slice(irb, scope, node, lval, result_loc); case NodeTypeUnwrapErrorExpr: - return ir_gen_catch(irb, scope, node, lval, - ensure_result_loc(irb, scope, node, result_loc)); + return ir_gen_catch(irb, scope, node, lval, result_loc); case NodeTypeContainerDecl: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_container_decl(irb, scope, node)); case NodeTypeFnProto: From 6495172899975f443fe0bf054e51377bac2f295a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 14 Jan 2019 18:44:52 -0500 Subject: [PATCH 166/190] copy elision: fix nested optional/error unions inside each other --- src/all_types.hpp | 22 +- src/analyze.cpp | 40 ++-- src/codegen.cpp | 68 +++++- src/ir.cpp | 385 +++++++++++++++++++++++----------- src/ir_print.cpp | 20 ++ test/cases/cast.zig | 14 -- test/stage1/behavior/cast.zig | 14 +- 7 files changed, 400 insertions(+), 163 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index d4d47b0be4f6..c510473f49d7 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -94,6 +94,7 @@ enum ConstParentId { ConstParentIdStruct, ConstParentIdErrUnionCode, ConstParentIdErrUnionPayload, + ConstParentIdOptionalPayload, ConstParentIdArray, ConstParentIdUnion, ConstParentIdScalar, @@ -117,6 +118,9 @@ struct ConstParent { struct { ConstExprValue *err_union_val; } p_err_union_payload; + struct { + ConstExprValue *optional_val; + } p_optional_payload; struct { ConstExprValue *union_val; } p_union; @@ -128,13 +132,11 @@ struct ConstParent { struct ConstStructValue { ConstExprValue *fields; - ConstParent parent; }; struct ConstUnionValue { BigInt tag; ConstExprValue *payload; - ConstParent parent; }; enum ConstArraySpecial { @@ -148,7 +150,6 @@ struct ConstArrayValue { union { struct { ConstExprValue *elements; - ConstParent parent; } s_none; Buf *s_buf; } data; @@ -167,6 +168,8 @@ enum ConstPtrSpecial { ConstPtrSpecialBaseErrorUnionCode, // The pointer points to the payload field of an error union ConstPtrSpecialBaseErrorUnionPayload, + // The pointer points to the payload field of an optional + ConstPtrSpecialBaseOptionalPayload, // This means that we did a compile-time pointer reinterpret and we cannot // understand the value of pointee at compile time. However, we will still // emit a binary with a compile time known address. @@ -226,6 +229,9 @@ struct ConstPtrValue { struct { ConstExprValue *err_union_val; } base_err_union_payload; + struct { + ConstExprValue *optional_val; + } base_optional_payload; struct { uint64_t addr; } hard_coded_addr; @@ -238,7 +244,6 @@ struct ConstPtrValue { struct ConstErrValue { ConstExprValue *error_set; ConstExprValue *payload; - ConstParent parent; }; struct ConstBoundFnValue { @@ -293,6 +298,7 @@ struct ConstGlobalRefs { struct ConstExprValue { ZigType *type; ConstValSpecial special; + ConstParent parent; ConstGlobalRefs *global_refs; union { @@ -2236,6 +2242,7 @@ enum IrInstructionId { IrInstructionIdResultErrorUnionCode, IrInstructionIdResultSlicePtr, IrInstructionIdResultReturn, + IrInstructionIdResultChild, IrInstructionIdResultBytesToSlice, IrInstructionIdResultSliceToBytesSrc, IrInstructionIdResultSliceToBytesPlaceholder, @@ -3429,6 +3436,13 @@ struct IrInstructionResultReturn { IrInstruction base; }; +struct IrInstructionResultChild { + IrInstruction base; + + IrInstruction *prev_result_loc; + LVal lval; +}; + struct IrInstructionResultPtrCast { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 26a670ae53f4..4eb33d6584d8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4751,6 +4751,11 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { return true; } +static uint32_t hash_const_val_error_set(ConstExprValue *const_val) { + assert(const_val->data.x_err_set != nullptr); + return const_val->data.x_err_set->value ^ 2630160122; +} + static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { uint32_t hash_val = 0; switch (const_val->data.x_ptr.mut) { @@ -4792,6 +4797,10 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += (uint32_t)3456080131; hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_payload.err_union_val); return hash_val; + case ConstPtrSpecialBaseOptionalPayload: + hash_val += (uint32_t)3163140517; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_optional_payload.optional_val); + return hash_val; case ConstPtrSpecialHardCodedAddr: hash_val += (uint32_t)4048518294; hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); @@ -4904,7 +4913,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return 2709806591; case ZigTypeIdOptional: if (get_codegen_ptr_type(const_val->type) != nullptr) { - return hash_const_val(const_val) * 1992916303; + return hash_const_val_ptr(const_val) * 1992916303; + } else if (const_val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) { + return hash_const_val_error_set(const_val) * 3147031929; } else { if (const_val->data.x_optional) { return hash_const_val(const_val->data.x_optional) * 1992916303; @@ -4916,8 +4927,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { // TODO better hashing algorithm return 3415065496; case ZigTypeIdErrorSet: - assert(const_val->data.x_err_set != nullptr); - return const_val->data.x_err_set->value ^ 2630160122; + return hash_const_val_error_set(const_val); case ZigTypeIdNamespace: return hash_ptr(const_val->data.x_import); case ZigTypeIdBoundFn: @@ -5690,6 +5700,15 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { return false; } return true; + case ConstPtrSpecialBaseOptionalPayload: + if (a->data.x_ptr.data.base_optional_payload.optional_val != + b->data.x_ptr.data.base_optional_payload.optional_val && + a->data.x_ptr.data.base_optional_payload.optional_val->global_refs != + b->data.x_ptr.data.base_optional_payload.optional_val->global_refs) + { + return false; + } + return true; case ConstPtrSpecialHardCodedAddr: if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) return false; @@ -5876,6 +5895,7 @@ static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val case ConstPtrSpecialBaseStruct: case ConstPtrSpecialBaseErrorUnionCode: case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: buf_appendf(buf, "*"); // TODO we need a source node for const_ptr_pointee because it can generate compile errors render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); @@ -6339,19 +6359,7 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { } ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) { - assert(value->type); - ZigType *type_entry = value->type; - if (type_entry->id == ZigTypeIdArray) { - expand_undef_array(g, value); - return &value->data.x_array.data.s_none.parent; - } else if (type_entry->id == ZigTypeIdStruct) { - return &value->data.x_struct.parent; - } else if (type_entry->id == ZigTypeIdUnion) { - return &value->data.x_union.parent; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - return &value->data.x_err_union.parent; - } - return nullptr; + return &value->parent; } static const ZigTypeId all_type_ids[] = { diff --git a/src/codegen.cpp b/src/codegen.cpp index af71e0886abc..1e6807afe0b5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5351,6 +5351,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdResultBytesToSlice: case IrInstructionIdFromBytesLenSrc: case IrInstructionIdToBytesLenSrc: + case IrInstructionIdResultChild: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5554,8 +5555,23 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) { for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { IrInstruction *instruction = current_block->instruction_list.at(instr_i); if (!ir_has_side_effects(instruction)) { - if (instruction->ref_count == 0 || instruction->value.special != ConstValSpecialRuntime) + if (instruction->ref_count == 0) continue; + if (instruction->value.special != ConstValSpecialRuntime) { + // If it's not a pointer, skip + if (get_codegen_ptr_type(instruction->value.type) == nullptr) + continue; + // If the const-ness isn't inferred, skip + if (instruction->value.data.x_ptr.mut != ConstPtrMutInfer) + continue; + + // Follow the const ptr reference, see if the underlying value is runtime + if (const_ptr_pointee(nullptr, g, &instruction->value, nullptr)->special != + ConstValSpecialRuntime) + { + continue; + } + } } instruction->llvm_value = ir_render_instruction(g, executable, instruction); } @@ -5568,6 +5584,7 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *ar static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val); static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val); static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val); +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val); static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { switch (parent->id) { @@ -5582,6 +5599,8 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent return gen_const_ptr_err_union_code_recursive(g, parent->data.p_err_union_code.err_union_val); case ConstParentIdErrUnionPayload: return gen_const_ptr_err_union_payload_recursive(g, parent->data.p_err_union_payload.err_union_val); + case ConstParentIdOptionalPayload: + return gen_const_ptr_optional_payload_recursive(g, parent->data.p_optional_payload.optional_val); case ConstParentIdArray: return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val, parent->data.p_array.elem_index); @@ -5597,7 +5616,7 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index) { expand_undef_array(g, array_const_val); - ConstParent *parent = &array_const_val->data.x_array.data.s_none.parent; + ConstParent *parent = &array_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent); LLVMTypeKind el_type = LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(base_ptr))); @@ -5622,7 +5641,7 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *ar } static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index) { - ConstParent *parent = &struct_const_val->data.x_struct.parent; + ConstParent *parent = &struct_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, struct_const_val, parent); ZigType *u32 = g->builtin_types.entry_u32; @@ -5634,7 +5653,7 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s } static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { - ConstParent *parent = &err_union_const_val->data.x_err_union.parent; + ConstParent *parent = &err_union_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); ZigType *u32 = g->builtin_types.entry_u32; @@ -5646,7 +5665,7 @@ static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExpr } static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { - ConstParent *parent = &err_union_const_val->data.x_err_union.parent; + ConstParent *parent = &err_union_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); ZigType *u32 = g->builtin_types.entry_u32; @@ -5657,8 +5676,20 @@ static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstE return LLVMConstInBoundsGEP(base_ptr, indices, 2); } +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val) { + ConstParent *parent = &optional_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, optional_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, maybe_child_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) { - ConstParent *parent = &union_const_val->data.x_union.parent; + ConstParent *parent = &union_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); ZigType *u32 = g->builtin_types.entry_u32; @@ -5866,6 +5897,25 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con render_const_val_global(g, const_val, ""); return ptr_val; } + case ConstPtrSpecialBaseOptionalPayload: + { + render_const_val_global(g, const_val, name); + ConstExprValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val; + assert(optional_const_val->type->id == ZigTypeIdOptional); + if (optional_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } case ConstPtrSpecialHardCodedAddr: { render_const_val_global(g, const_val, name); @@ -7693,9 +7743,9 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { ConstExprValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i]; this_val->special = ConstValSpecialStatic; this_val->type = struct_type; - this_val->data.x_struct.parent.id = ConstParentIdArray; - this_val->data.x_struct.parent.data.p_array.array_val = test_fn_array; - this_val->data.x_struct.parent.data.p_array.elem_index = i; + this_val->parent.id = ConstParentIdArray; + this_val->parent.data.p_array.array_val = test_fn_array; + this_val->parent.data.p_array.elem_index = i; this_val->data.x_struct.fields = create_const_vals(2); ConstExprValue *name_field = &this_val->data.x_struct.fields[0]; diff --git a/src/ir.cpp b/src/ir.cpp index b2e318ef18f6..28d8554ab2ec 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -210,6 +210,9 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c case ConstPtrSpecialBaseErrorUnionPayload: result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload; break; + case ConstPtrSpecialBaseOptionalPayload: + result = const_val->data.x_ptr.data.base_optional_payload.optional_val->data.x_optional; + break; case ConstPtrSpecialNull: result = const_val; break; @@ -920,6 +923,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionResultReturn *) return IrInstructionIdResultReturn; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultChild *) { + return IrInstructionIdResultChild; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionResultPtrCast *) { return IrInstructionIdResultPtrCast; } @@ -2962,6 +2969,18 @@ static IrInstruction *ir_build_result_return(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } +static IrInstruction *ir_build_result_child(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, LVal lval) +{ + IrInstructionResultChild *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->lval = lval; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_result_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *elem_type, IrInstruction *prev_result_loc) { @@ -3409,36 +3428,78 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime, nullptr); } -static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, - IrInstruction *result_loc) +static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, LVal parent_lval, + IrInstruction *parent_result_loc, LVal child_lval, IrInstruction *child_result_loc) { - assert(result_loc != nullptr); - switch (lval) { + assert(child_result_loc != nullptr); + bool need_copy = (parent_result_loc != nullptr && + parent_result_loc != child_result_loc && child_result_loc->id == IrInstructionIdAllocaSrc); + switch (parent_lval) { case LValNone: - return ir_build_load_ptr(irb, scope, node, result_loc, nullptr); + if (need_copy) { + IrInstruction *result = ir_build_load_ptr(irb, scope, node, child_result_loc, parent_result_loc); + ir_build_infer_comptime(irb, scope, node, parent_result_loc, child_result_loc); + return result; + } else { + return ir_build_load_ptr(irb, scope, node, child_result_loc, nullptr); + } case LValPtr: - return result_loc; - case LValErrorUnionVal: { - IrInstruction *err_ptr = ir_build_error_union_field_error_set(irb, scope, node, result_loc, false); - return ir_build_load_ptr(irb, scope, node, err_ptr, nullptr); - } - case LValErrorUnionPtr: { - return ir_build_error_union_field_error_set(irb, scope, node, result_loc, false); - } + if (need_copy) { + ir_build_ref(irb, scope, node, child_result_loc, true, false, parent_result_loc); + ir_build_infer_comptime(irb, scope, node, parent_result_loc, child_result_loc); + return ir_build_const_void(irb, scope, node); + } else { + return child_result_loc; + } case LValOptional: { - IrInstruction *loaded_ptr = ir_build_load_ptr(irb, scope, node, result_loc, nullptr); + IrInstruction *loaded_ptr = ir_build_load_ptr(irb, scope, node, child_result_loc, nullptr); + if (need_copy) { + IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, child_result_loc, false); + ir_build_load_ptr(irb, scope, node, payload_ptr, parent_result_loc); + ir_build_infer_comptime(irb, scope, node, parent_result_loc, child_result_loc); + } return ir_build_test_nonnull(irb, scope, node, loaded_ptr); } + case LValErrorUnionVal: { + if (need_copy) { + ir_build_unwrap_err_payload(irb, scope, node, child_result_loc, parent_result_loc, false); + ir_build_infer_comptime(irb, scope, node, parent_result_loc, child_result_loc); + } + IrInstruction *err_ptr = ir_build_error_union_field_error_set(irb, scope, node, child_result_loc, false); + return ir_build_load_ptr(irb, scope, node, err_ptr, nullptr); + } + case LValErrorUnionPtr: + if (need_copy) { + ir_build_unwrap_err_payload(irb, scope, node, child_result_loc, parent_result_loc, false); + ir_build_infer_comptime(irb, scope, node, parent_result_loc, child_result_loc); + } + return ir_build_error_union_field_error_set(irb, scope, node, child_result_loc, false); } zig_unreachable(); } -static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *result_loc) { - if (result_loc) { - if (result_loc->value.special != ConstValSpecialStatic || - result_loc->value.data.x_ptr.special != ConstPtrSpecialDiscard) - { - return result_loc; +static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ + if (result_loc != nullptr && + (result_loc->value.special != ConstValSpecialStatic || + result_loc->value.data.x_ptr.special != ConstPtrSpecialDiscard)) + { + switch (lval) { + case LValNone: + return result_loc; + case LValErrorUnionVal: + case LValErrorUnionPtr: + case LValOptional: + if (result_loc->id == IrInstructionIdAllocaSrc) { + break; + } + if (result_loc->id != IrInstructionIdResultChild) { + return ir_build_result_child(irb, scope, node, result_loc, lval); + } + break; + case LValPtr: + zig_unreachable(); } } return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "", nullptr); @@ -3634,7 +3695,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindError: { assert(expr_node); - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, LValErrorUnionVal, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *opt_err_code = ir_gen_node(irb, expr_node, scope, @@ -3665,7 +3726,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, } ir_set_cursor_at_end_and_append_block(irb, continue_block); - return ir_gen_result(irb, scope, node, lval, ensured_result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc, LValErrorUnionVal, + ensured_result_loc); } } zig_unreachable(); @@ -3957,18 +4019,18 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod } static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval, - IrInstruction *result_loc) + IrInstruction *old_result_loc) { assert(node->type == NodeTypeBinOpExpr); - result_loc = ensure_result_loc(irb, parent_scope, node, result_loc); - if (result_loc == irb->codegen->invalid_instruction) + IrInstruction *new_result_loc = ensure_result_loc(irb, parent_scope, node, LValOptional, old_result_loc); + if (new_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; - IrInstruction *is_non_null = ir_gen_node(irb, op1_node, parent_scope, LValOptional, result_loc); + IrInstruction *is_non_null = ir_gen_node(irb, op1_node, parent_scope, LValOptional, new_result_loc); if (is_non_null == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -3981,17 +4043,17 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OrElseNull"); IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OrElseEnd"); - ir_build_cond_br(irb, parent_scope, node, is_non_null, end_block, null_block, is_comptime, result_loc); + ir_build_cond_br(irb, parent_scope, node, is_non_null, end_block, null_block, is_comptime, new_result_loc); ir_set_cursor_at_end_and_append_block(irb, null_block); - IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope, LValNone, result_loc); + IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope, LValNone, new_result_loc); if (null_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; if (!instr_is_unreachable(null_result)) ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_result(irb, parent_scope, node, lval, result_loc); + return ir_gen_result(irb, parent_scope, node, lval, old_result_loc, LValOptional, new_result_loc); } static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -4587,7 +4649,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg5_value == irb->codegen->invalid_instruction) return arg5_value; - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValNone, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -4602,7 +4664,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return cmpxchg; ir_build_set_nonnull_bit(irb, scope, node, ensured_result_loc, cmpxchg, payload_ptr); - return ir_gen_result(irb, scope, node, lval, ensured_result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc, LValNone, ensured_result_loc); } case BuiltinFnIdFence: { @@ -4771,7 +4833,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValNone, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -4785,11 +4847,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo ir_build_from_bytes_len_src(irb, scope, node, ensured_result_loc, new_result_loc); - return ir_gen_result(irb, scope, node, lval, ensured_result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc, LValNone, ensured_result_loc); } case BuiltinFnIdToBytes: { - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValNone, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -4802,7 +4864,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo ir_build_to_bytes_len_src(irb, scope, node, ensured_result_loc, new_result_loc); - return ir_gen_result(irb, scope, node, lval, ensured_result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc, LValNone, ensured_result_loc); } case BuiltinFnIdIntToFloat: { @@ -5066,7 +5128,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValNone, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5080,7 +5142,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo ir_build_infer_comptime(irb, scope, node, ensured_result_loc, new_result_loc); - return ir_gen_result(irb, scope, node, lval, ensured_result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc, LValNone, ensured_result_loc); } case BuiltinFnIdIntToPtr: { @@ -5640,15 +5702,19 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode } static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, - AstNode *expr_node, LVal lval, IrInstruction *result_loc) + AstNode *expr_node, LVal lval, IrInstruction *old_result_loc) { - IrInstruction *opt_err_code = ir_gen_node(irb, expr_node, scope, LValErrorUnionVal, result_loc); + IrInstruction *new_result_loc = ensure_result_loc(irb, scope, source_node, LValErrorUnionVal, old_result_loc); + if (new_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *opt_err_code = ir_gen_node(irb, expr_node, scope, LValErrorUnionVal, new_result_loc); if (opt_err_code == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; ir_build_assert_non_error(irb, scope, source_node, opt_err_code); - return ir_gen_result(irb, scope, source_node, lval, result_loc); + return ir_gen_result(irb, scope, source_node, lval, old_result_loc, LValErrorUnionVal, new_result_loc); } static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5694,7 +5760,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod if (result_loc == nullptr) { return ir_gen_value(irb, scope, expr_node, lval, nullptr, inst); } else { - return ir_gen_result(irb, scope, expr_node, lval, result_loc); + return ir_gen_result(irb, scope, expr_node, lval, result_loc, LValNone, result_loc); } } } @@ -5702,12 +5768,12 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod } static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, - IrInstruction *result_loc) + IrInstruction *old_result_loc) { assert(node->type == NodeTypeContainerInitExpr); - result_loc = ensure_result_loc(irb, scope, node, result_loc); - if (result_loc == irb->codegen->invalid_instruction) + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValNone, old_result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; @@ -5718,7 +5784,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A return container_type; if (kind == ContainerInitKindStruct) { - IrInstruction *new_result_loc = ir_build_result_cast(irb, scope, node, container_type, result_loc); + IrInstruction *new_result_loc = ir_build_result_cast(irb, scope, node, container_type, ensured_result_loc); size_t field_count = container_init_expr->entries.length; IrInstructionContainerInitFieldsField *fields = allocate(field_count); @@ -5740,11 +5806,11 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A fields[i].source_node = entry_node; } ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields, new_result_loc); - return ir_gen_result(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, old_result_loc, LValNone, ensured_result_loc); } else if (kind == ContainerInitKindArray) { size_t elem_count = container_init_expr->entries.length; IrInstruction *array_type = ir_build_infer_array_type(irb, scope, node, container_type, elem_count); - IrInstruction *new_result_loc = ir_build_result_cast(irb, scope, node, array_type, result_loc); + IrInstruction *new_result_loc = ir_build_result_cast(irb, scope, node, array_type, ensured_result_loc); IrInstruction **result_locs = allocate(elem_count); for (size_t i = 0; i < elem_count; i += 1) { @@ -5759,7 +5825,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A result_locs[i] = expr_value; } ir_build_container_init_list(irb, scope, node, array_type, elem_count, result_locs, new_result_loc); - return ir_gen_result(irb, scope, node, lval, result_loc); + return ir_gen_result(irb, scope, node, lval, old_result_loc, LValNone, ensured_result_loc); } else { zig_unreachable(); } @@ -6979,12 +7045,12 @@ static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode } static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, - IrInstruction *result_loc) + IrInstruction *old_result_loc) { assert(node->type == NodeTypeSliceExpr); - result_loc = ensure_result_loc(irb, scope, node, result_loc); - if (result_loc == irb->codegen->invalid_instruction) + IrInstruction *new_result_loc = ensure_result_loc(irb, scope, node, LValNone, old_result_loc); + if (new_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; AstNodeSliceExpr *slice_expr = &node->data.slice_expr; @@ -7009,19 +7075,15 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, end_value = nullptr; } - ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true, result_loc); - return ir_gen_result(irb, scope, node, lval, result_loc); + ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true, new_result_loc); + return ir_gen_result(irb, scope, node, lval, old_result_loc, LValNone, new_result_loc); } static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval, - IrInstruction *result_loc) + IrInstruction *old_result_loc) { assert(node->type == NodeTypeUnwrapErrorExpr); - result_loc = ensure_result_loc(irb, parent_scope, node, result_loc); - if (result_loc == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; - AstNode *op1_node = node->data.unwrap_err_expr.op1; AstNode *op2_node = node->data.unwrap_err_expr.op2; AstNode *var_node = node->data.unwrap_err_expr.symbol; @@ -7033,18 +7095,27 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); return irb->codegen->invalid_instruction; } - return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc); + return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, old_result_loc); } + IrInstruction *new_result_loc; IrInstruction *ptr_opt_err_code; IrInstruction *opt_err_code; if (var_node) { - ptr_opt_err_code = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnionPtr, result_loc); + new_result_loc = ensure_result_loc(irb, parent_scope, node, LValErrorUnionPtr, old_result_loc); + if (new_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + ptr_opt_err_code = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnionPtr, new_result_loc); if (ptr_opt_err_code == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; opt_err_code = ir_build_load_ptr(irb, parent_scope, node, ptr_opt_err_code, nullptr); } else { - opt_err_code = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnionVal, result_loc); + new_result_loc = ensure_result_loc(irb, parent_scope, node, LValErrorUnionVal, old_result_loc); + if (new_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + opt_err_code = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnionVal, new_result_loc); if (opt_err_code == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } @@ -7059,7 +7130,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode IrBasicBlock *err_block = ir_create_basic_block(irb, parent_scope, "CatchError"); IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "CatchEnd"); - ir_build_cond_br(irb, parent_scope, node, is_err, err_block, end_block, is_comptime, result_loc); + ir_build_cond_br(irb, parent_scope, node, is_err, err_block, end_block, is_comptime, new_result_loc); ir_set_cursor_at_end_and_append_block(irb, err_block); Scope *err_scope; @@ -7077,14 +7148,14 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode } else { err_scope = parent_scope; } - IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope, LValNone, result_loc); + IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope, LValNone, new_result_loc); if (err_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; if (!instr_is_unreachable(err_result)) ir_mark_gen(ir_build_br(irb, err_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, end_block); - return ir_gen_result(irb, parent_scope, node, lval, result_loc); + return ir_gen_result(irb, parent_scope, node, lval, old_result_loc, LValErrorUnionVal, new_result_loc); } static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) { @@ -7825,7 +7896,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_ptr(irb, scope, node, lval, result_loc, ptr_inst); } case NodeTypeUnwrapOptional: { - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValOptional, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -7835,7 +7906,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return irb->codegen->invalid_instruction; ir_build_assert_non_null(irb, scope, expr_node, is_non_null); - return ir_gen_result(irb, scope, node, lval, ensured_result_loc); + return ir_gen_result(irb, scope, node, lval, result_loc, LValOptional, ensured_result_loc); } case NodeTypeBoolLiteral: return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_bool_literal(irb, scope, node)); @@ -10669,6 +10740,8 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr ConstExprValue *payload_val = create_const_vals(1); payload_val->type = needed_child_type; payload_val->special = ConstValSpecialUndef; + payload_val->parent.id = ConstParentIdOptionalPayload; + payload_val->parent.data.p_optional_payload.optional_val = optional_val; optional_val->data.x_optional = payload_val; optional_val->special = ConstValSpecialStatic; @@ -10814,19 +10887,13 @@ static void init_if_undef_err_union(IrAnalyze *ira, ConstExprValue *error_union_ err_set_val->type = get_optional_type(ira->codegen, error_union_val->type->data.error_union.err_set_type); err_set_val->special = ConstValSpecialUndef; - ConstParent *err_set_parent = get_const_val_parent(ira->codegen, err_set_val); - if (err_set_parent != nullptr) { - err_set_parent->id = ConstParentIdErrUnionCode; - err_set_parent->data.p_err_union_code.err_union_val = error_union_val; - } + err_set_val->parent.id = ConstParentIdErrUnionCode; + err_set_val->parent.data.p_err_union_code.err_union_val = error_union_val; payload_val->type = error_union_val->type->data.error_union.payload_type; payload_val->special = ConstValSpecialUndef; - ConstParent *payload_parent = get_const_val_parent(ira->codegen, payload_val); - if (payload_parent != nullptr) { - payload_parent->id = ConstParentIdErrUnionPayload; - payload_parent->data.p_err_union_payload.err_union_val = error_union_val; - } + payload_val->parent.id = ConstParentIdErrUnionPayload; + payload_val->parent.data.p_err_union_payload.err_union_val = error_union_val; error_union_val->data.x_err_union.error_set = err_set_val; error_union_val->data.x_err_union.payload = payload_val; @@ -10854,6 +10921,10 @@ static IrInstruction *ir_analyze_result_error_union_payload(IrAnalyze *ira, IrIn init_if_undef_err_union(ira, error_union_val); assert(error_union_val->data.x_err_union.payload != nullptr); + // Make the error set null + error_union_val->data.x_err_union.error_set->special = ConstValSpecialStatic; + error_union_val->data.x_err_union.error_set->data.x_err_set = nullptr; + IrInstruction *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_result_error_union_payload(&ira->new_irb, result_loc->scope, @@ -10868,10 +10939,6 @@ static IrInstruction *ir_analyze_result_error_union_payload(IrAnalyze *ira, IrIn result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; result_val->data.x_ptr.data.ref.pointee = error_union_val->data.x_err_union.payload; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - ptr_val->data.x_ptr.mut = ConstPtrMutComptimeConst; - } - return result; } } @@ -11573,8 +11640,8 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou array_val->type = array_type; array_val->data.x_array.special = ConstArraySpecialNone; array_val->data.x_array.data.s_none.elements = pointee; - array_val->data.x_array.data.s_none.parent.id = ConstParentIdScalar; - array_val->data.x_array.data.s_none.parent.data.p_scalar.scalar_val = pointee; + array_val->parent.id = ConstParentIdScalar; + array_val->parent.data.p_scalar.scalar_val = pointee; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); @@ -14550,6 +14617,73 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, return var_ptr_instruction; } +static void make_const_ptr_runtime(IrAnalyze *ira, IrInstruction *ptr) { + assert(ptr->id != IrInstructionIdConst); + bool is_static = (ptr->value.special != ConstValSpecialRuntime); + ptr->value.special = ConstValSpecialRuntime; + if (!is_static) return; + ConstExprValue *val; + switch (ptr->value.data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialHardCodedAddr: + case ConstPtrSpecialDiscard: + case ConstPtrSpecialFunction: + case ConstPtrSpecialNull: + return; + case ConstPtrSpecialRef: + val = ptr->value.data.x_ptr.data.ref.pointee; + break; + case ConstPtrSpecialBaseArray: + val = ptr->value.data.x_ptr.data.base_array.array_val; + break; + case ConstPtrSpecialBaseStruct: + val = ptr->value.data.x_ptr.data.base_struct.struct_val; + break; + case ConstPtrSpecialBaseErrorUnionCode: + val = ptr->value.data.x_ptr.data.base_err_union_code.err_union_val; + break; + case ConstPtrSpecialBaseErrorUnionPayload: + val = ptr->value.data.x_ptr.data.base_err_union_payload.err_union_val; + break; + case ConstPtrSpecialBaseOptionalPayload: + val = ptr->value.data.x_ptr.data.base_optional_payload.optional_val; + break; + } + for (;;) { + is_static = (val->special != ConstValSpecialRuntime); + val->special = ConstValSpecialRuntime; + if (!is_static) return; + + switch (val->parent.id) { + case ConstParentIdNone: + return; + case ConstParentIdStruct: + val = val->parent.data.p_struct.struct_val; + continue; + case ConstParentIdErrUnionCode: + val = val->parent.data.p_err_union_code.err_union_val; + continue; + case ConstParentIdErrUnionPayload: + val = val->parent.data.p_err_union_payload.err_union_val; + continue; + case ConstParentIdOptionalPayload: + val = val->parent.data.p_optional_payload.optional_val; + continue; + case ConstParentIdArray: + val = val->parent.data.p_array.array_val; + continue; + case ConstParentIdUnion: + val = val->parent.data.p_union.union_val; + continue; + case ConstParentIdScalar: + val = val->parent.data.p_scalar.scalar_val; + continue; + } + zig_unreachable(); + } +} + static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *uncasted_ptr, IrInstruction *uncasted_value) { @@ -14620,10 +14754,8 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source } } if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { - ptr->value.special = ConstValSpecialRuntime; - if (instr_is_comptime(uncasted_ptr) && uncasted_ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { - uncasted_ptr->value.special = ConstValSpecialRuntime; - } + make_const_ptr_runtime(ira, ptr); + make_const_ptr_runtime(ira, uncasted_ptr); } else { ir_add_error(ira, source_instr, buf_sprintf("cannot store runtime value in compile time variable")); @@ -15756,6 +15888,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source case ConstPtrSpecialBaseStruct: case ConstPtrSpecialBaseErrorUnionCode: case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: case ConstPtrSpecialDiscard: case ConstPtrSpecialHardCodedAddr: case ConstPtrSpecialFunction: @@ -16383,6 +16516,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct zig_panic("TODO elem ptr on a const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO elem ptr on a const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -16442,6 +16577,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct zig_panic("TODO elem ptr on a slice backed by const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO elem ptr on a slice backed by const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a slice backed by const optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -18990,7 +19127,6 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco definition_array->special = ConstValSpecialStatic; definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count); definition_array->data.x_array.special = ConstArraySpecialNone; - definition_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; definition_array->data.x_array.data.s_none.elements = create_const_vals(definition_count); init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false); @@ -19021,9 +19157,9 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub; inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = type_info_definition_data_type; - inner_fields[2].data.x_union.parent.id = ConstParentIdStruct; - inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val; - inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1; + inner_fields[2].parent.id = ConstParentIdStruct; + inner_fields[2].parent.data.p_struct.struct_val = definition_val; + inner_fields[2].parent.data.p_struct.field_index = 1; switch (curr_entry->value->id) { case TldIdVar: @@ -19064,8 +19200,8 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco ConstExprValue *fn_def_val = create_const_vals(1); fn_def_val->special = ConstValSpecialStatic; fn_def_val->type = type_info_fn_def_type; - fn_def_val->data.x_struct.parent.id = ConstParentIdUnion; - fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; + fn_def_val->parent.id = ConstParentIdUnion; + fn_def_val->parent.data.p_union.union_val = &inner_fields[2]; ConstExprValue *fn_def_fields = create_const_vals(9); fn_def_val->data.x_struct.fields = fn_def_fields; @@ -19129,7 +19265,6 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr), fn_arg_count); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_name_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fn_def_fields[8], fn_arg_name_array, 0, fn_arg_count, false); @@ -19140,9 +19275,9 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index]; ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name); init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true); - fn_arg_name_val->data.x_struct.parent.id = ConstParentIdArray; - fn_arg_name_val->data.x_struct.parent.data.p_array.array_val = fn_arg_name_array; - fn_arg_name_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + fn_arg_name_val->parent.id = ConstParentIdArray; + fn_arg_name_val->parent.data.p_array.array_val = fn_arg_name_array; + fn_arg_name_val->parent.data.p_array.elem_index = fn_arg_index; } inner_fields[2].data.x_union.payload = fn_def_val; @@ -19435,7 +19570,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE enum_field_array->special = ConstValSpecialStatic; enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count); enum_field_array->data.x_array.special = ConstArraySpecialNone; - enum_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count); init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false); @@ -19445,9 +19579,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; ConstExprValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index]; make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); - enum_field_val->data.x_struct.parent.id = ConstParentIdArray; - enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; - enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; + enum_field_val->parent.id = ConstParentIdArray; + enum_field_val->parent.data.p_array.array_val = enum_field_array; + enum_field_val->parent.data.p_array.elem_index = enum_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); @@ -19474,7 +19608,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE error_array->special = ConstValSpecialStatic; error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count); error_array->data.x_array.special = ConstArraySpecialNone; - error_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; error_array->data.x_array.data.s_none.elements = create_const_vals(error_count); init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false); @@ -19498,9 +19631,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value); error_val->data.x_struct.fields = inner_fields; - error_val->data.x_struct.parent.id = ConstParentIdArray; - error_val->data.x_struct.parent.data.p_array.array_val = error_array; - error_val->data.x_struct.parent.data.p_array.elem_index = error_index; + error_val->parent.id = ConstParentIdArray; + error_val->parent.data.p_array.array_val = error_array; + error_val->parent.data.p_array.elem_index = error_index; } break; @@ -19569,7 +19702,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE union_field_array->special = ConstValSpecialStatic; union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count); union_field_array->data.x_array.special = ConstArraySpecialNone; - union_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count); init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false); @@ -19602,9 +19734,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true); union_field_val->data.x_struct.fields = inner_fields; - union_field_val->data.x_struct.parent.id = ConstParentIdArray; - union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; - union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; + union_field_val->parent.id = ConstParentIdArray; + union_field_val->parent.data.p_array.array_val = union_field_array; + union_field_val->parent.data.p_array.elem_index = union_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); @@ -19644,7 +19776,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE struct_field_array->special = ConstValSpecialStatic; struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count); struct_field_array->data.x_array.special = ConstArraySpecialNone; - struct_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count); init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false); @@ -19678,9 +19809,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true); struct_field_val->data.x_struct.fields = inner_fields; - struct_field_val->data.x_struct.parent.id = ConstParentIdArray; - struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array; - struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; + struct_field_val->parent.id = ConstParentIdArray; + struct_field_val->parent.data.p_array.array_val = struct_field_array; + struct_field_val->parent.data.p_array.elem_index = struct_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 2); @@ -19750,7 +19881,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE fn_arg_array->special = ConstValSpecialStatic; fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count); fn_arg_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false); @@ -19787,9 +19917,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE } fn_arg_val->data.x_struct.fields = inner_fields; - fn_arg_val->data.x_struct.parent.id = ConstParentIdArray; - fn_arg_val->data.x_struct.parent.data.p_array.array_val = fn_arg_array; - fn_arg_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + fn_arg_val->parent.id = ConstParentIdArray; + fn_arg_val->parent.data.p_array.array_val = fn_arg_array; + fn_arg_val->parent.data.p_array.elem_index = fn_arg_index; } break; @@ -19833,8 +19963,8 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, if (payload != nullptr) { assert(payload->type->id == ZigTypeIdStruct); - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = out_val; + payload->parent.id = ConstParentIdUnion; + payload->parent.data.p_union.union_val = out_val; } return result; @@ -20536,6 +20666,8 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio zig_panic("TODO memset on const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO memset on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memset on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -20657,6 +20789,8 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio zig_panic("TODO memcpy on const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -20699,6 +20833,8 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio zig_panic("TODO memcpy on const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: @@ -20882,6 +21018,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_panic("TODO slice const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -20925,6 +21063,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_panic("TODO slice const inner error union code"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -21003,6 +21143,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_panic("TODO"); case ConstPtrSpecialBaseErrorUnionPayload: zig_panic("TODO"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO"); case ConstPtrSpecialHardCodedAddr: init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, parent_ptr->type->data.pointer.child_type, @@ -23220,6 +23362,10 @@ static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrIns return ir_analyze_result_return(ira, &instruction->base); } +static IrInstruction *ir_analyze_instruction_result_child(IrAnalyze *ira, IrInstructionResultChild *instruction) { + return instruction->prev_result_loc->child; +} + static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, IrInstructionResultPtrCast *instruction) { @@ -23988,6 +24134,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction); case IrInstructionIdResultReturn: return ir_analyze_instruction_result_return(ira, (IrInstructionResultReturn *)instruction); + case IrInstructionIdResultChild: + return ir_analyze_instruction_result_child(ira, (IrInstructionResultChild *)instruction); case IrInstructionIdResultPtrCast: return ir_analyze_instruction_result_ptr_cast(ira, (IrInstructionResultPtrCast *)instruction); case IrInstructionIdResultCast: @@ -24310,6 +24458,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdResultErrorUnionPayload: case IrInstructionIdResultErrorUnionCode: case IrInstructionIdResultReturn: + case IrInstructionIdResultChild: case IrInstructionIdResultPtrCast: case IrInstructionIdResultCast: case IrInstructionIdResultSliceToBytesSrc: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 3a0bda4e0a45..01537b8cf7cd 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -70,6 +70,17 @@ static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) ir_print_const_value(irp, &const_instruction->base.value); } +static const char *lval_str(LVal lval) { + switch (lval) { + case LValNone: return "None"; + case LValPtr: return "Ptr"; + case LValErrorUnionVal: return "ErrorUnionVal"; + case LValErrorUnionPtr: return "ErrorUnionPtr"; + case LValOptional: return "Optional"; + } + zig_unreachable(); +} + static const char *ir_bin_op_id_str(IrBinOp op_id) { switch (op_id) { case IrBinOpInvalid: @@ -1354,6 +1365,12 @@ static void ir_print_result_return(IrPrint *irp, IrInstructionResultReturn *inst fprintf(irp->f, "ResultReturn"); } +static void ir_print_result_child(IrPrint *irp, IrInstructionResultChild *instruction) { + fprintf(irp->f, "ResultChild(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",lval=%s)", lval_str(instruction->lval)); +} + static void ir_print_result_bytes_to_slice(IrPrint *irp, IrInstructionResultBytesToSlice *instruction) { fprintf(irp->f, "ResultBytesToSlice("); ir_print_other_instruction(irp, instruction->prev_result_loc); @@ -1947,6 +1964,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdResultReturn: ir_print_result_return(irp, (IrInstructionResultReturn *)instruction); break; + case IrInstructionIdResultChild: + ir_print_result_child(irp, (IrInstructionResultChild *)instruction); + break; case IrInstructionIdResultBytesToSlice: ir_print_result_bytes_to_slice(irp, (IrInstructionResultBytesToSlice *)instruction); break; diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 4d7b5b03050f..1a9eadfb1d45 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -3,20 +3,6 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; -test "implicitly cast from T to anyerror!?T" { - castToOptionalTypeError(1); - comptime castToOptionalTypeError(1); -} - -test "implicitly cast from int to anyerror!?T" { - implicitIntLitToOptional(); - comptime implicitIntLitToOptional(); -} -fn implicitIntLitToOptional() void { - const f: ?i32 = 1; - const g: anyerror!?i32 = 1; -} - test "peer type resolution: ?T and T" { assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index 5fe7a3c3022a..8f36b77ccdee 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -82,7 +82,10 @@ fn testPeerResolveArrayConstSlice(b: bool) void { assertOrPanic(mem.eql(u8, value2, "zz")); } -// test "implicitly cast from T to anyerror!?T" +test "implicitly cast from T to anyerror!?T" { + castToOptionalTypeError(1); + comptime castToOptionalTypeError(1); +} const A = struct { a: i32, @@ -100,7 +103,14 @@ fn castToOptionalTypeError(z: i32) void { assertOrPanic((b catch unreachable).?.a == 1); } -// test "implicitly cast from int to anyerror!?T" +test "implicitly cast from int to anyerror!?T" { + implicitIntLitToOptional(); + comptime implicitIntLitToOptional(); +} +fn implicitIntLitToOptional() void { + const f: ?i32 = 1; + const g: anyerror!?i32 = 1; +} test "return null from fn() anyerror!?&T" { const a = returnNullFromOptionalTypeErrorRef(); From 407bac5f00c2465cc77208149ec3bb3322599407 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 14 Jan 2019 19:56:26 -0500 Subject: [PATCH 167/190] copy elision: fix orelse on constant value --- src/ir.cpp | 30 ++++++++++++++++-------------- test/cases/null.zig | 26 -------------------------- test/stage1/behavior/null.zig | 26 +++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 28d8554ab2ec..e16925e590d1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3695,7 +3695,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindError: { assert(expr_node); - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, LValErrorUnionVal, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, lval, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *opt_err_code = ir_gen_node(irb, expr_node, scope, @@ -4023,7 +4023,7 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode { assert(node->type == NodeTypeBinOpExpr); - IrInstruction *new_result_loc = ensure_result_loc(irb, parent_scope, node, LValOptional, old_result_loc); + IrInstruction *new_result_loc = ensure_result_loc(irb, parent_scope, node, lval, old_result_loc); if (new_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -4833,7 +4833,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValNone, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -4851,7 +4851,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } case BuiltinFnIdToBytes: { - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValNone, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5128,7 +5128,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValNone, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5704,7 +5704,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval, IrInstruction *old_result_loc) { - IrInstruction *new_result_loc = ensure_result_loc(irb, scope, source_node, LValErrorUnionVal, old_result_loc); + IrInstruction *new_result_loc = ensure_result_loc(irb, scope, source_node, lval, old_result_loc); if (new_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5772,7 +5772,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A { assert(node->type == NodeTypeContainerInitExpr); - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValNone, old_result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, old_result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -7049,7 +7049,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, { assert(node->type == NodeTypeSliceExpr); - IrInstruction *new_result_loc = ensure_result_loc(irb, scope, node, LValNone, old_result_loc); + IrInstruction *new_result_loc = ensure_result_loc(irb, scope, node, lval, old_result_loc); if (new_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -7102,7 +7102,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode IrInstruction *ptr_opt_err_code; IrInstruction *opt_err_code; if (var_node) { - new_result_loc = ensure_result_loc(irb, parent_scope, node, LValErrorUnionPtr, old_result_loc); + new_result_loc = ensure_result_loc(irb, parent_scope, node, lval, old_result_loc); if (new_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -7111,7 +7111,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode return irb->codegen->invalid_instruction; opt_err_code = ir_build_load_ptr(irb, parent_scope, node, ptr_opt_err_code, nullptr); } else { - new_result_loc = ensure_result_loc(irb, parent_scope, node, LValErrorUnionVal, old_result_loc); + new_result_loc = ensure_result_loc(irb, parent_scope, node, lval, old_result_loc); if (new_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -7896,7 +7896,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_ptr(irb, scope, node, lval, result_loc, ptr_inst); } case NodeTypeUnwrapOptional: { - IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValOptional, result_loc); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -14747,9 +14747,6 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source break; } } - if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { - ptr->value.data.x_ptr.mut = ConstPtrMutComptimeConst; - } return ir_const_void(ira, source_instr); } } @@ -17914,6 +17911,11 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { if (optional_value_is_null(maybe_val)) { + if (!safety_check_on) { + IrInstruction *undef = ir_const_undef(ira, source_instr); + IrInstruction *casted_undef = ir_implicit_cast(ira, undef, child_type); + return ir_get_ref(ira, source_instr, casted_undef, true, false, nullptr); + } ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); return ira->codegen->invalid_instruction; } diff --git a/test/cases/null.zig b/test/cases/null.zig index 72c376a1d4a2..6c082634c902 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -1,31 +1,5 @@ const assertOrPanic = @import("std").debug.assertOrPanic; -test "optional type" { - const x: ?bool = true; - - if (x) |y| { - if (y) { - // OK - } else { - unreachable; - } - } else { - unreachable; - } - - const next_x: ?i32 = null; - - const z = next_x orelse 1234; - - assertOrPanic(z == 1234); - - const final_x: ?i32 = 13; - - const num = final_x orelse unreachable; - - assertOrPanic(num == 13); -} - test "maybe return" { maybeReturnImpl(); comptime maybeReturnImpl(); diff --git a/test/stage1/behavior/null.zig b/test/stage1/behavior/null.zig index 35f03e719869..05f25cb42f8a 100644 --- a/test/stage1/behavior/null.zig +++ b/test/stage1/behavior/null.zig @@ -1,6 +1,30 @@ const assertOrPanic = @import("std").debug.assertOrPanic; -//test "optional type" { +test "optional type" { + const x: ?bool = true; + + if (x) |y| { + if (y) { + // OK + } else { + unreachable; + } + } else { + unreachable; + } + + const next_x: ?i32 = null; + + const z = next_x orelse 1234; + + assertOrPanic(z == 1234); + + const final_x: ?i32 = 13; + + const num = final_x orelse unreachable; + + assertOrPanic(num == 13); +} test "test maybe object and get a pointer to the inner value" { var maybe_bool: ?bool = true; From 1e19a5a99106dc4d9e8334b017ece20d76d85ab3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 15 Jan 2019 21:41:06 -0500 Subject: [PATCH 168/190] copy elision: fix comptime optional fn call --- src/ir.cpp | 58 +++++++++++++++++++++++++++-------- test/cases/cast.zig | 16 ---------- test/stage1/behavior/cast.zig | 16 +++++++++- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e16925e590d1..885acd65ed07 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -183,6 +183,7 @@ static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, ZigType *child_type_inst, uint32_t align, const char *name_hint, bool is_comptime); static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); +static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -15289,10 +15290,31 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; return ir_finish_anal(ira, err_code_ptr); } + case LValOptional: { + IrInstruction *ref_inst = ir_get_ref(ira, &call_instruction->base, new_instruction, + true, false, nullptr); + if (type_is_invalid(ref_inst->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *payload_ptr = ir_analyze_unwrap_optional_payload(ira, + &call_instruction->base, ref_inst, false); + if (type_is_invalid(payload_ptr->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *payload = ir_get_deref(ira, &call_instruction->base, payload_ptr); + if (type_is_invalid(payload->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *store_inst = ir_analyze_store_ptr(ira, &call_instruction->base, + prev_result_loc, payload); + if (type_is_invalid(store_inst->value.type)) + return ira->codegen->invalid_instruction; + assert(new_instruction->value.type->id == ZigTypeIdOptional); + IrInstruction *is_non_null = ir_analyze_test_non_null(ira, &call_instruction->base, + new_instruction); + if (type_is_invalid(is_non_null->value.type)) + return ira->codegen->invalid_instruction; + return ir_finish_anal(ira, is_non_null); + } case LValPtr: zig_panic("TODO"); - case LValOptional: - zig_panic("TODO"); case LValErrorUnionVal: zig_panic("TODO"); } @@ -15746,8 +15768,14 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC return ir_finish_anal(ira, null); } } - case LValOptional: - zig_panic("TODO"); + case LValOptional: { + assert(result_loc != nullptr); + IrInstruction *only_arg = call_instruction->args[0]->child; + if (type_is_invalid(only_arg->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *true_value = ir_const_bool(ira, &call_instruction->base, true); + return ir_finish_anal(ira, true_value); + } case LValErrorUnionPtr: zig_panic("TODO"); } @@ -17852,11 +17880,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - +static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) { ZigType *type_entry = value->value.type; if (type_entry->id == ZigTypeIdOptional) { @@ -17865,20 +17889,28 @@ static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIns if (!maybe_val) return ira->codegen->invalid_instruction; - return ir_const_bool(ira, &instruction->base, !optional_value_is_null(maybe_val)); + return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val)); } IrInstruction *result = ir_build_test_nonnull(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value); + source_inst->scope, source_inst->source_node, value); result->value.type = ira->codegen->builtin_types.entry_bool; return result; } else if (type_entry->id == ZigTypeIdNull) { - return ir_const_bool(ira, &instruction->base, false); + return ir_const_bool(ira, source_inst, false); } else { - return ir_const_bool(ira, &instruction->base, true); + return ir_const_bool(ira, source_inst, true); } } +static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { + IrInstruction *value = instruction->value->child; + if (type_is_invalid(value->value.type)) + return ira->codegen->invalid_instruction; + + return ir_analyze_test_non_null(ira, &instruction->base, value); +} + static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on) { diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 1a9eadfb1d45..096b728900a9 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -3,22 +3,6 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; -test "peer type resolution: ?T and T" { - assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); - assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); - comptime { - assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); - assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); - } -} -fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { - if (c) { - return if (b) null else usize(0); - } - - return usize(3); -} - test "implicitly cast from [N]T to ?[]const T" { assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); comptime assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index 8f36b77ccdee..2321f2d73fd3 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -125,7 +125,21 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { return null; } -//test "peer type resolution: ?T and T" { +test "peer type resolution: ?T and T" { + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); + comptime { + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); + } +} +fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { + if (c) { + return if (b) null else usize(0); + } + + return usize(3); +} test "peer type resolution: [0]u8 and []const u8" { assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); From 49efa09dcc630528a2a486b9dd7c9a1a23a30381 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 Jan 2019 17:49:33 -0500 Subject: [PATCH 169/190] copy elision: fix returning const optional/error unions --- src/all_types.hpp | 1 + src/codegen.cpp | 4 ---- src/ir.cpp | 44 +++++++++++++++++++++++++---------- test/cases/cast.zig | 9 ------- test/stage1/behavior/cast.zig | 9 ++++++- 5 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index c510473f49d7..45d5cb6dc096 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2508,6 +2508,7 @@ struct IrInstructionReturn { IrInstruction base; IrInstruction *value; + IrInstruction *result_loc; }; // TODO get rid of this instruction, replace with instructions for each op code diff --git a/src/codegen.cpp b/src/codegen.cpp index 1e6807afe0b5..112d63e64861 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2276,10 +2276,6 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns } LLVMValueRef value = ir_llvm_value(g, return_instruction->value); if (handle_is_ptr(return_type)) { - if (return_instruction->value->value.special == ConstValSpecialStatic) { - assert(g->cur_ret_ptr); - gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); - } LLVMBuildRetVoid(g->builder); return nullptr; } else { diff --git a/src/ir.cpp b/src/ir.cpp index 885acd65ed07..0d89657a2de0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1092,13 +1092,17 @@ static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *so return &cond_br_instruction->base; } -static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *return_value) { +static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *return_value, + IrInstruction *result_loc) +{ IrInstructionReturn *return_instruction = ir_build_instruction(irb, scope, source_node); return_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; return_instruction->base.value.special = ConstValSpecialStatic; return_instruction->value = return_value; + return_instruction->result_loc = result_loc; ir_ref_instruction(return_value, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &return_instruction->base; } @@ -3375,13 +3379,13 @@ static bool exec_is_async(IrExecutable *exec) { } static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value, - bool is_generated_code) + bool is_generated_code, IrInstruction *return_result_loc) { ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value)); bool is_async = exec_is_async(irb->exec); if (!is_async) { - IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value); + IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value, return_result_loc); return_inst->is_gen = is_generated_code; return return_inst; } @@ -3618,8 +3622,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, { IrInstruction *return_value; ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + IrInstruction *return_result_loc = nullptr; if (expr_node) { - IrInstruction *return_result_loc = nullptr; LVal expr_lval = LValNone; if (return_type != nullptr) { if (return_type->id == ZigTypeIdErrorUnion) { @@ -3686,11 +3690,11 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block); - return ir_gen_async_return(irb, scope, node, return_value, false); + return ir_gen_async_return(irb, scope, node, return_value, false, return_result_loc); } else { // generate unconditional defers ir_gen_defers_for_block(irb, scope, outer_scope, false); - return ir_gen_async_return(irb, scope, node, return_value, false); + return ir_gen_async_return(irb, scope, node, return_value, false, return_result_loc); } } case ReturnKindError: @@ -3723,7 +3727,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr(irb, scope, node); } - ir_gen_async_return(irb, scope, node, err_val, false); + ir_gen_async_return(irb, scope, node, err_val, false, nullptr); } ir_set_cursor_at_end_and_append_block(irb, continue_block); @@ -8084,7 +8088,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_set_cursor_at_end_and_append_block(irb, alloc_err_block); // we can return undefined here, because the caller passes a pointer to the error struct field // in the error union result, and we populate it in case of allocation failure. - ir_build_return(irb, coro_scope, node, undef); + ir_build_return(irb, coro_scope, node, undef, nullptr); ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block); IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); @@ -8150,7 +8154,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec implicit_return_result = ir_build_const_null(irb, scope, result->source_node); } } - ir_gen_async_return(irb, scope, result->source_node, implicit_return_result, true); + ir_gen_async_return(irb, scope, result->source_node, implicit_return_result, true, nullptr); } if (is_async) { @@ -8169,7 +8173,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_suspend_block); ir_build_coro_end(irb, scope, node); - ir_build_return(irb, scope, node, irb->exec->coro_handle); + ir_build_return(irb, scope, node, irb->exec->coro_handle, nullptr); ir_set_cursor_at_end_and_append_block(irb, invalid_resume_block); ir_build_unreachable(irb, scope, node); @@ -12696,6 +12700,13 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio if (type_is_invalid(value->value.type)) return ir_unreach_error(ira); + IrInstruction *result_loc = nullptr; + if (instruction->result_loc != nullptr) { + result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ir_unreach_error(ira); + } + IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->scalar_return_type); if (type_is_invalid(casted_value->value.type) && ira->explicit_return_type_source_node != nullptr) { ErrorMsg *msg = ira->codegen->errors.last(); @@ -12711,8 +12722,17 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio ir_add_error(ira, casted_value, buf_sprintf("function returns address of local variable")); return ir_unreach_error(ira); } + + if (result_loc != nullptr && instr_is_comptime(result_loc) && + !ir_should_inline(ira->new_irb.exec, instruction->base.scope)) + { + IrInstruction *loaded = ir_get_deref(ira, &instruction->base, result_loc); + result_loc->value.special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, &instruction->base, result_loc, loaded); + } + IrInstruction *result = ir_build_return(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, casted_value); + instruction->base.source_node, casted_value, nullptr); result->value.type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } @@ -23361,7 +23381,7 @@ static IrInstruction *ir_analyze_result_return(IrAnalyze *ira, IrInstruction *so // Here we create a pass2 instruction for getting the return value pointer. // However we create a const value for it and mark it with ConstPtrMutInfer // so that if the return expression is comptime-known, we can emit a memcpy - // rather than runtime instructions instructions. + // rather than runtime instructions. ZigType *result_type = get_pointer_to_type(ira->codegen, ira->payload_return_type, false); IrInstruction *result = ir_build_result_return(&ira->new_irb, source_inst->scope, source_inst->source_node); diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 096b728900a9..dcc2fb1da695 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -3,15 +3,6 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; -test "implicitly cast from [N]T to ?[]const T" { - assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); - comptime assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); -} - -fn castToOptionalSlice() ?[]const u8 { - return "hi"; -} - test "implicitly cast from [0]T to anyerror![]T" { testCastZeroArrayToErrSliceMut(); comptime testCastZeroArrayToErrSliceMut(); diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index 2321f2d73fd3..1cacee7b9862 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -157,7 +157,14 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { return slice[0..1]; } -// test "implicitly cast from [N]T to ?[]const T" { +test "implicitly cast from [N]T to ?[]const T" { + assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); + comptime assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); +} + +fn castToOptionalSlice() ?[]const u8 { + return "hi"; +} // test "implicitly cast from [0]T to anyerror![]T" { From 28bd62426855636497ac8715d32f19366a2df77d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 Jan 2019 20:32:01 -0500 Subject: [PATCH 170/190] copy elision: fix comptime fn call when error union val is expected --- src/ir.cpp | 10 ++++-- test/cases/cast.zig | 57 ----------------------------------- test/stage1/behavior/cast.zig | 57 +++++++++++++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 63 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 0d89657a2de0..36b0123a1ef5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15279,6 +15279,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; return ir_finish_anal(ira, ir_const_void(ira, &call_instruction->base)); } + case LValErrorUnionVal: case LValErrorUnionPtr: { IrInstruction *ref_inst = ir_get_ref(ira, &call_instruction->base, new_instruction, true, false, nullptr); @@ -15308,7 +15309,12 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call opt_err_set_type); if (type_is_invalid(err_code_ptr->value.type)) return ira->codegen->invalid_instruction; - return ir_finish_anal(ira, err_code_ptr); + if (call_instruction->lval == LValErrorUnionPtr) + return ir_finish_anal(ira, err_code_ptr); + IrInstruction *err_code = ir_get_deref(ira, &call_instruction->base, err_code_ptr); + if (type_is_invalid(err_code->value.type)) + return ira->codegen->invalid_instruction; + return ir_finish_anal(ira, err_code); } case LValOptional: { IrInstruction *ref_inst = ir_get_ref(ira, &call_instruction->base, new_instruction, @@ -15335,8 +15341,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } case LValPtr: zig_panic("TODO"); - case LValErrorUnionVal: - zig_panic("TODO"); } zig_unreachable(); } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index dcc2fb1da695..b49433928e88 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -3,63 +3,6 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; -test "implicitly cast from [0]T to anyerror![]T" { - testCastZeroArrayToErrSliceMut(); - comptime testCastZeroArrayToErrSliceMut(); -} - -fn testCastZeroArrayToErrSliceMut() void { - assertOrPanic((gimmeErrOrSlice() catch unreachable).len == 0); -} - -fn gimmeErrOrSlice() anyerror![]u8 { - return []u8{}; -} - -test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { - { - var data = "hi"; - const slice = data[0..]; - assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); - } - comptime { - var data = "hi"; - const slice = data[0..]; - assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); - } -} -fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { - if (a) { - return []u8{}; - } - - return slice[0..1]; -} - -test "peer type resolution: error and [N]T" { - // TODO: implicit error!T to error!U where T can implicitly cast to U - //assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - //comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); - comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); -} - -//fn testPeerErrorAndArray(x: u8) error![]const u8 { -// return switch (x) { -// 0x00 => "OK", -// else => error.BadValue, -// }; -//} -fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { - return switch (x) { - 0x00 => "OK", - 0x01 => "OKK", - else => error.BadValue, - }; -} - test "const slice widen cast" { const bytes align(4) = []u8{ 0x12, diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index 1cacee7b9862..b965534ec81a 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -166,9 +166,40 @@ fn castToOptionalSlice() ?[]const u8 { return "hi"; } -// test "implicitly cast from [0]T to anyerror![]T" { +test "implicitly cast from [0]T to anyerror![]T" { + testCastZeroArrayToErrSliceMut(); + comptime testCastZeroArrayToErrSliceMut(); +} -// test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" +fn testCastZeroArrayToErrSliceMut() void { + assertOrPanic((gimmeErrOrSlice() catch unreachable).len == 0); +} + +fn gimmeErrOrSlice() anyerror![]u8 { + return []u8{}; +} + +test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { + { + var data = "hi"; + const slice = data[0..]; + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + } + comptime { + var data = "hi"; + const slice = data[0..]; + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + } +} +fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { + if (a) { + return []u8{}; + } + + return slice[0..1]; +} test "resolve undefined with integer" { testResolveUndefWithInt(true, 1234); @@ -194,7 +225,27 @@ fn testCastConstArrayRefToConstSlice() void { assertOrPanic(mem.eql(u8, slice, "aoeu")); } -// test "peer type resolution: error and [N]T" { +test "peer type resolution: error and [N]T" { + // TODO: implicit error!T to error!U where T can implicitly cast to U + //assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + //comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); + comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); +} + +//fn testPeerErrorAndArray(x: u8) error![]const u8 { +// return switch (x) { +// 0x00 => "OK", +// else => error.BadValue, +// }; +//} +fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { + return switch (x) { + 0x00 => "OK", + 0x01 => "OKK", + else => error.BadValue, + }; +} test "@floatToInt" { testFloatToInts(); From ae3f6f63dc2eb2a647dcac0def629397c128cd49 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jan 2019 15:05:06 -0500 Subject: [PATCH 171/190] copy elision: move passing tests --- test/behavior.zig | 1 - test/cases/bitreverse.zig | 81 ----------------------------- test/cases/cast.zig | 14 ----- test/stage1/behavior.zig | 1 + test/stage1/behavior/bitreverse.zig | 81 +++++++++++++++++++++++++++++ test/stage1/behavior/cast.zig | 14 ++++- 6 files changed, 95 insertions(+), 97 deletions(-) delete mode 100644 test/cases/bitreverse.zig create mode 100644 test/stage1/behavior/bitreverse.zig diff --git a/test/behavior.zig b/test/behavior.zig index 4c8230a5d510..c9aa8078626b 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,4 @@ comptime { - _ = @import("cases/bitreverse.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); diff --git a/test/cases/bitreverse.zig b/test/cases/bitreverse.zig deleted file mode 100644 index 3721e68a9431..000000000000 --- a/test/cases/bitreverse.zig +++ /dev/null @@ -1,81 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const minInt = std.math.minInt; - -test "@bitreverse" { - comptime testBitReverse(); - testBitReverse(); -} - -fn testBitReverse() void { - // using comptime_ints, unsigned - assert(@bitreverse(u0, 0) == 0); - assert(@bitreverse(u5, 0x12) == 0x9); - assert(@bitreverse(u8, 0x12) == 0x48); - assert(@bitreverse(u16, 0x1234) == 0x2c48); - assert(@bitreverse(u24, 0x123456) == 0x6a2c48); - assert(@bitreverse(u32, 0x12345678) == 0x1e6a2c48); - assert(@bitreverse(u40, 0x123456789a) == 0x591e6a2c48); - assert(@bitreverse(u48, 0x123456789abc) == 0x3d591e6a2c48); - assert(@bitreverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48); - assert(@bitreverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48); - assert(@bitreverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48); - - // using runtime uints, unsigned - var num0: u0 = 0; - assert(@bitreverse(u0, num0) == 0); - var num5: u5 = 0x12; - assert(@bitreverse(u5, num5) == 0x9); - var num8: u8 = 0x12; - assert(@bitreverse(u8, num8) == 0x48); - var num16: u16 = 0x1234; - assert(@bitreverse(u16, num16) == 0x2c48); - var num24: u24 = 0x123456; - assert(@bitreverse(u24, num24) == 0x6a2c48); - var num32: u32 = 0x12345678; - assert(@bitreverse(u32, num32) == 0x1e6a2c48); - var num40: u40 = 0x123456789a; - assert(@bitreverse(u40, num40) == 0x591e6a2c48); - var num48: u48 = 0x123456789abc; - assert(@bitreverse(u48, num48) == 0x3d591e6a2c48); - var num56: u56 = 0x123456789abcde; - assert(@bitreverse(u56, num56) == 0x7b3d591e6a2c48); - var num64: u64 = 0x123456789abcdef1; - assert(@bitreverse(u64, num64) == 0x8f7b3d591e6a2c48); - var num128: u128 = 0x123456789abcdef11121314151617181; - assert(@bitreverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48); - - // using comptime_ints, signed, positive - assert(@bitreverse(i0, 0) == 0); - assert(@bitreverse(i8, @bitCast(i8, u8(0x92))) == @bitCast(i8, u8( 0x49))); - assert(@bitreverse(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16( 0x2c48))); - assert(@bitreverse(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24( 0x6a2c48))); - assert(@bitreverse(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32( 0x1e6a2c48))); - assert(@bitreverse(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40( 0x591e6a2c48))); - assert(@bitreverse(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48( 0x3d591e6a2c48))); - assert(@bitreverse(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56( 0x7b3d591e6a2c48))); - assert(@bitreverse(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64,u64(0x8f7b3d591e6a2c48))); - assert(@bitreverse(i128, @bitCast(i128,u128(0x123456789abcdef11121314151617181))) == @bitCast(i128,u128(0x818e868a828c84888f7b3d591e6a2c48))); - - // using comptime_ints, signed, negative. Compare to runtime ints returned from llvm. - var neg5: i5 = minInt(i5) + 1; - assert(@bitreverse(i5, minInt(i5) + 1) == @bitreverse(i5, neg5)); - var neg8: i8 = -18; - assert(@bitreverse(i8, -18) == @bitreverse(i8, neg8)); - var neg16: i16 = -32694; - assert(@bitreverse(i16, -32694) == @bitreverse(i16, neg16)); - var neg24: i24 = -6773785; - assert(@bitreverse(i24, -6773785) == @bitreverse(i24, neg24)); - var neg32: i32 = -16773785; - assert(@bitreverse(i32, -16773785) == @bitreverse(i32, neg32)); - var neg40: i40 = minInt(i40) + 12345; - assert(@bitreverse(i40, minInt(i40) + 12345) == @bitreverse(i40, neg40)); - var neg48: i48 = minInt(i48) + 12345; - assert(@bitreverse(i48, minInt(i48) + 12345) == @bitreverse(i48, neg48)); - var neg56: i56 = minInt(i56) + 12345; - assert(@bitreverse(i56, minInt(i56) + 12345) == @bitreverse(i56, neg56)); - var neg64: i64 = minInt(i64) + 12345; - assert(@bitreverse(i64, minInt(i64) + 12345) == @bitreverse(i64, neg64)); - var neg128: i128 = minInt(i128) + 12345; - assert(@bitreverse(i128, minInt(i128) + 12345) == @bitreverse(i128, neg128)); -} diff --git a/test/cases/cast.zig b/test/cases/cast.zig index b49433928e88..9a0147fbeffc 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -3,20 +3,6 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; -test "const slice widen cast" { - const bytes align(4) = []u8{ - 0x12, - 0x12, - 0x12, - 0x12, - }; - - const u32_value = @bytesToSlice(u32, bytes[0..])[0]; - assertOrPanic(u32_value == 0x12121212); - - assertOrPanic(@bitCast(u32, bytes) == 0x12121212); -} - test "@bytesToSlice keeps pointer alignment" { var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; const numbers = @bytesToSlice(u32, bytes[0..]); diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 80b87fc4cbc7..d9eb9c710444 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -6,6 +6,7 @@ comptime { _ = @import("behavior/atomics.zig"); _ = @import("behavior/bit_shifting.zig"); _ = @import("behavior/bitcast.zig"); + _ = @import("behavior/bitreverse.zig"); _ = @import("behavior/bool.zig"); _ = @import("behavior/bswap.zig"); _ = @import("behavior/bugs/1076.zig"); diff --git a/test/stage1/behavior/bitreverse.zig b/test/stage1/behavior/bitreverse.zig new file mode 100644 index 000000000000..97787ace84f2 --- /dev/null +++ b/test/stage1/behavior/bitreverse.zig @@ -0,0 +1,81 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const minInt = std.math.minInt; + +test "@bitreverse" { + comptime testBitReverse(); + testBitReverse(); +} + +fn testBitReverse() void { + // using comptime_ints, unsigned + assertOrPanic(@bitreverse(u0, 0) == 0); + assertOrPanic(@bitreverse(u5, 0x12) == 0x9); + assertOrPanic(@bitreverse(u8, 0x12) == 0x48); + assertOrPanic(@bitreverse(u16, 0x1234) == 0x2c48); + assertOrPanic(@bitreverse(u24, 0x123456) == 0x6a2c48); + assertOrPanic(@bitreverse(u32, 0x12345678) == 0x1e6a2c48); + assertOrPanic(@bitreverse(u40, 0x123456789a) == 0x591e6a2c48); + assertOrPanic(@bitreverse(u48, 0x123456789abc) == 0x3d591e6a2c48); + assertOrPanic(@bitreverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48); + assertOrPanic(@bitreverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48); + assertOrPanic(@bitreverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48); + + // using runtime uints, unsigned + var num0: u0 = 0; + assertOrPanic(@bitreverse(u0, num0) == 0); + var num5: u5 = 0x12; + assertOrPanic(@bitreverse(u5, num5) == 0x9); + var num8: u8 = 0x12; + assertOrPanic(@bitreverse(u8, num8) == 0x48); + var num16: u16 = 0x1234; + assertOrPanic(@bitreverse(u16, num16) == 0x2c48); + var num24: u24 = 0x123456; + assertOrPanic(@bitreverse(u24, num24) == 0x6a2c48); + var num32: u32 = 0x12345678; + assertOrPanic(@bitreverse(u32, num32) == 0x1e6a2c48); + var num40: u40 = 0x123456789a; + assertOrPanic(@bitreverse(u40, num40) == 0x591e6a2c48); + var num48: u48 = 0x123456789abc; + assertOrPanic(@bitreverse(u48, num48) == 0x3d591e6a2c48); + var num56: u56 = 0x123456789abcde; + assertOrPanic(@bitreverse(u56, num56) == 0x7b3d591e6a2c48); + var num64: u64 = 0x123456789abcdef1; + assertOrPanic(@bitreverse(u64, num64) == 0x8f7b3d591e6a2c48); + var num128: u128 = 0x123456789abcdef11121314151617181; + assertOrPanic(@bitreverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48); + + // using comptime_ints, signed, positive + assertOrPanic(@bitreverse(i0, 0) == 0); + assertOrPanic(@bitreverse(i8, @bitCast(i8, u8(0x92))) == @bitCast(i8, u8(0x49))); + assertOrPanic(@bitreverse(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x2c48))); + assertOrPanic(@bitreverse(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x6a2c48))); + assertOrPanic(@bitreverse(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x1e6a2c48))); + assertOrPanic(@bitreverse(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x591e6a2c48))); + assertOrPanic(@bitreverse(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0x3d591e6a2c48))); + assertOrPanic(@bitreverse(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0x7b3d591e6a2c48))); + assertOrPanic(@bitreverse(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0x8f7b3d591e6a2c48))); + assertOrPanic(@bitreverse(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == @bitCast(i128, u128(0x818e868a828c84888f7b3d591e6a2c48))); + + // using comptime_ints, signed, negative. Compare to runtime ints returned from llvm. + var neg5: i5 = minInt(i5) + 1; + assertOrPanic(@bitreverse(i5, minInt(i5) + 1) == @bitreverse(i5, neg5)); + var neg8: i8 = -18; + assertOrPanic(@bitreverse(i8, -18) == @bitreverse(i8, neg8)); + var neg16: i16 = -32694; + assertOrPanic(@bitreverse(i16, -32694) == @bitreverse(i16, neg16)); + var neg24: i24 = -6773785; + assertOrPanic(@bitreverse(i24, -6773785) == @bitreverse(i24, neg24)); + var neg32: i32 = -16773785; + assertOrPanic(@bitreverse(i32, -16773785) == @bitreverse(i32, neg32)); + var neg40: i40 = minInt(i40) + 12345; + assertOrPanic(@bitreverse(i40, minInt(i40) + 12345) == @bitreverse(i40, neg40)); + var neg48: i48 = minInt(i48) + 12345; + assertOrPanic(@bitreverse(i48, minInt(i48) + 12345) == @bitreverse(i48, neg48)); + var neg56: i56 = minInt(i56) + 12345; + assertOrPanic(@bitreverse(i56, minInt(i56) + 12345) == @bitreverse(i56, neg56)); + var neg64: i64 = minInt(i64) + 12345; + assertOrPanic(@bitreverse(i64, minInt(i64) + 12345) == @bitreverse(i64, neg64)); + var neg128: i128 = minInt(i128) + 12345; + assertOrPanic(@bitreverse(i128, minInt(i128) + 12345) == @bitreverse(i128, neg128)); +} diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index b965534ec81a..c388dc22c802 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -287,7 +287,19 @@ fn cast128Float(x: u128) f128 { return @bitCast(f128, x); } -// test "const slice widen cast" +test "const slice widen cast" { + const bytes align(4) = []u8{ + 0x12, + 0x12, + 0x12, + 0x12, + }; + + const u32_value = @bytesToSlice(u32, bytes[0..])[0]; + assertOrPanic(u32_value == 0x12121212); + + assertOrPanic(@bitCast(u32, bytes) == 0x12121212); +} test "single-item pointer of array to slice and to unknown length pointer" { testCastPtrOfArrayToSliceAndPtr(); From b0d2680cad448f104937bb47c0ca7f48cac9b118 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jan 2019 16:46:12 -0500 Subject: [PATCH 172/190] copy elision: fix test "@bytesToSlice keeps pointer alignment" --- src/ir.cpp | 11 +++++++++-- test/cases/cast.zig | 6 ------ test/stage1/behavior/cast.zig | 6 +++++- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index b58c6057fccb..49239298665d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12307,6 +12307,11 @@ static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_ali ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); } +// TODO: audit callsites. The purpose of this function is to change the element type of the pointer +// while leaving the other properties the same. But if the original pointer has ABI alignment and +// the original element type has different ABI alignment than the new element type, the resulting +// pointer type will have different alignment, which is probably incorrect. Probably this function +// will have to be removed and the logic in the callsites reworked. static ZigType *adjust_ptr_child(CodeGen *g, ZigType *ptr_type, ZigType *new_child) { assert(ptr_type->id == ZigTypeIdPointer); return get_pointer_to_type_extra(g, @@ -24027,10 +24032,12 @@ static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusZeroBitsKnown))) + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; - ZigType *elem_ptr_type = adjust_ptr_child(ira->codegen, bytes_ptr_type, elem_type); + ZigType *elem_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, + bytes_ptr_type->data.pointer.is_const, bytes_ptr_type->data.pointer.is_volatile, + PtrLenUnknown, get_ptr_align(ira->codegen, bytes_ptr_type), 0, 0); ZigType *slice_of_elem = get_slice_type(ira->codegen, elem_ptr_type); IrInstruction *casted_prev_result_loc = ir_implicit_cast_result(ira, source_inst->source_node, prev_result_loc, slice_of_elem, false); diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 9a0147fbeffc..1b5648bac414 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -3,12 +3,6 @@ const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; -test "@bytesToSlice keeps pointer alignment" { - var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; - const numbers = @bytesToSlice(u32, bytes[0..]); - comptime assertOrPanic(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); -} - test "implicit ptr to *c_void" { var a: u32 = 1; var ptr: *c_void = &a; diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index c388dc22c802..b30c414da2c6 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -364,7 +364,11 @@ test "comptime_int @intToFloat" { } } -// test "@bytesToSlice keeps pointer alignment" { +test "@bytesToSlice keeps pointer alignment" { + var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; + const numbers = @bytesToSlice(u32, bytes[0..]); + comptime assertOrPanic(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); +} test "@intCast i32 to u7" { var x: u128 = maxInt(u128); From cf5e54a7c4a751c213bc57de0ff55658863d6f01 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jan 2019 18:59:35 -0500 Subject: [PATCH 173/190] copy elision: fix test "implicit ptr to *c_void" --- src/all_types.hpp | 1 + src/ir.cpp | 60 ++++++++++++++++++++++++++++------- test/behavior.zig | 1 - test/cases/cast.zig | 15 --------- test/stage1/behavior/cast.zig | 10 +++++- 5 files changed, 59 insertions(+), 28 deletions(-) delete mode 100644 test/cases/cast.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index d1b6f22f3f3c..1229e158c246 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3467,6 +3467,7 @@ struct IrInstructionResultCast { struct IrInstructionAllocaSrc { IrInstruction base; + // TODO: remove this field. use implicit casts instead. IrInstruction *child_type; IrInstruction *align; IrInstruction *is_comptime; diff --git a/src/ir.cpp b/src/ir.cpp index 49239298665d..fdc4bb3650b7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -184,6 +184,7 @@ static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_in uint32_t align, const char *name_hint, bool is_comptime); static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value); +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -5914,20 +5915,34 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); } - IrInstruction *alloca = ir_build_alloca_src(irb, scope, node, type_instruction, align_value, + IrInstruction *alloca = ir_build_alloca_src(irb, scope, node, nullptr, align_value, buf_ptr(variable_declaration->symbol), is_comptime); + IrInstruction *cast_result_loc = nullptr; + if (type_instruction != nullptr) { + cast_result_loc = ir_build_first_arg_result_loc(irb, scope, node, alloca, type_instruction); + } else { + cast_result_loc = alloca; + } + // Temporarily set the name of the IrExecutable to the VariableDeclaration // so that the struct or enum from the init expression inherits the name. Buf *old_exec_name = irb->exec->name; irb->exec->name = variable_declaration->symbol; - IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope, LValNone, alloca); + IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope, LValNone, cast_result_loc); irb->exec->name = old_exec_name; if (init_value == irb->codegen->invalid_instruction) return init_value; - return ir_build_var_decl_src(irb, scope, node, var, type_instruction, align_value, alloca); + if (type_instruction != nullptr) { + IrInstruction **args = allocate(1); + args[0] = ir_gen_result(irb, scope, node, LValNone, alloca, LValNone, cast_result_loc); + ir_build_call(irb, scope, node, nullptr, type_instruction, 1, args, false, + FnInlineAuto, false, nullptr, nullptr, alloca, cast_result_loc, LValNone); + } + + return ir_build_var_decl_src(irb, scope, node, var, nullptr, align_value, alloca); } static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -12298,13 +12313,18 @@ static IrInstruction *resolve_possible_alloca_inference(IrAnalyze *ira, IrInstru } static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { - assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra(g, - ptr_type->data.pointer.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - ptr_type->data.pointer.ptr_len, - new_align, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); + if (ptr_type->id == ZigTypeIdPointer) { + return get_pointer_to_type_extra(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + new_align, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); + } else if (ptr_type->id == ZigTypeIdOptional) { + ZigType *actual_ptr_type = ptr_type->data.pointer.child_type; + return get_optional_type(g, adjust_ptr_align(g, actual_ptr_type, new_align)); + } + zig_unreachable(); } // TODO: audit callsites. The purpose of this function is to change the element type of the pointer @@ -14744,6 +14764,7 @@ static void make_const_ptr_runtime(IrAnalyze *ira, IrInstruction *ptr) { static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *uncasted_ptr, IrInstruction *uncasted_value) { + Error err; if (uncasted_ptr->value.type->id != ZigTypeIdPointer) { ir_add_error(ira, uncasted_ptr, buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&uncasted_ptr->value.type->name))); @@ -14810,6 +14831,18 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { make_const_ptr_runtime(ira, ptr); make_const_ptr_runtime(ira, uncasted_ptr); + if (get_codegen_ptr_type(value->value.type) != nullptr && ptr->id == IrInstructionIdAllocaGen) { + uint32_t child_ptr_alignment; + if ((err = resolve_ptr_align(ira, value->value.type, &child_ptr_alignment))) + return ira->codegen->invalid_instruction; + uint32_t parent_ptr_alignment; + if ((err = resolve_ptr_align(ira, ptr->value.type->data.pointer.child_type, &parent_ptr_alignment))) + return ira->codegen->invalid_instruction; + if (child_ptr_alignment > parent_ptr_alignment) { + ptr->value.type = adjust_ptr_child(ira->codegen, ptr->value.type, + adjust_ptr_align(ira->codegen, ptr->value.type->data.pointer.child_type, child_ptr_alignment)); + } + } } else { ir_add_error(ira, source_instr, buf_sprintf("cannot store runtime value in compile time variable")); @@ -24035,9 +24068,14 @@ static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; + + uint32_t bytes_ptr_align; + if ((err = resolve_ptr_align(ira, bytes_ptr_type, &bytes_ptr_align))) + return ira->codegen->invalid_instruction; + ZigType *elem_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, bytes_ptr_type->data.pointer.is_const, bytes_ptr_type->data.pointer.is_volatile, - PtrLenUnknown, get_ptr_align(ira->codegen, bytes_ptr_type), 0, 0); + PtrLenUnknown, bytes_ptr_align, 0, 0); ZigType *slice_of_elem = get_slice_type(ira->codegen, elem_ptr_type); IrInstruction *casted_prev_result_loc = ir_implicit_cast_result(ira, source_inst->source_node, prev_result_loc, slice_of_elem, false); diff --git a/test/behavior.zig b/test/behavior.zig index c9aa8078626b..73adf9ddb581 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,4 @@ comptime { - _ = @import("cases/cast.zig"); _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); _ = @import("cases/defer.zig"); diff --git a/test/cases/cast.zig b/test/cases/cast.zig deleted file mode 100644 index 1b5648bac414..000000000000 --- a/test/cases/cast.zig +++ /dev/null @@ -1,15 +0,0 @@ -const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; -const mem = std.mem; -const maxInt = std.math.maxInt; - -test "implicit ptr to *c_void" { - var a: u32 = 1; - var ptr: *c_void = &a; - var b: *u32 = @ptrCast(*u32, ptr); - assertOrPanic(b.* == 1); - var ptr2: ?*c_void = &a; - var c: *u32 = @ptrCast(*u32, ptr2.?); - assertOrPanic(c.* == 1); -} - diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index b30c414da2c6..1a8b61454069 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -445,7 +445,15 @@ fn foobar(func: PFN_void) void { std.debug.assertOrPanic(@ptrToInt(func) == maxInt(usize)); } -// test "implicit ptr to *c_void" +test "implicit ptr to *c_void" { + var a: u32 = 1; + var ptr: *c_void = &a; + var b: *u32 = @ptrCast(*u32, ptr); + assertOrPanic(b.* == 1); + var ptr2: ?*c_void = &a; + var c: *u32 = @ptrCast(*u32, ptr2.?); + assertOrPanic(c.* == 1); +} test "@intCast to comptime_int" { assertOrPanic(@intCast(comptime_int, 0) == 0); From 5ed6d276dc5ed885bb35af96dd2d9976e0212a1e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jan 2019 19:05:43 -0500 Subject: [PATCH 174/190] copy elision: move passing tests --- test/behavior.zig | 1 - test/cases/coroutines.zig | 13 +++++++++++++ test/cases/null.zig | 16 ---------------- test/stage1/behavior.zig | 1 + test/{cases => stage1/behavior}/inttoptr.zig | 1 - test/stage1/behavior/null.zig | 16 +++++++++++++++- 6 files changed, 29 insertions(+), 19 deletions(-) rename test/{cases => stage1/behavior}/inttoptr.zig (99%) diff --git a/test/behavior.zig b/test/behavior.zig index 73adf9ddb581..253b1b703fda 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -2,7 +2,6 @@ comptime { _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); _ = @import("cases/defer.zig"); - _ = @import("cases/inttoptr.zig"); _ = @import("cases/null.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); } diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 2622607e1094..1cd31b930ecc 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -2,6 +2,19 @@ const std = @import("std"); const builtin = @import("builtin"); const assertOrPanic = std.debug.assertOrPanic; +test "async fn with inferred error set" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = (async<&da.allocator> failing()) catch unreachable; + resume p; + cancel p; +} + +async fn failing() !void { + suspend; + return error.Fail; +} + test "error return trace across suspend points - early return" { const p = nonFailing(); resume p; diff --git a/test/cases/null.zig b/test/cases/null.zig index 6c082634c902..2f135b3b3e04 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -1,21 +1,5 @@ const assertOrPanic = @import("std").debug.assertOrPanic; -test "maybe return" { - maybeReturnImpl(); - comptime maybeReturnImpl(); -} - -fn maybeReturnImpl() void { - assertOrPanic(foo(1235).?); - if (foo(null) != null) unreachable; - assertOrPanic(!foo(1234).?); -} - -fn foo(x: ?i32) ?bool { - const value = x orelse return null; - return value > 1234; -} - test "optional types" { comptime { const opt_type_struct = StructWithOptionalType{ .t = u8 }; diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index d9eb9c710444..e545a4c4184d 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -42,6 +42,7 @@ comptime { _ = @import("behavior/if.zig"); _ = @import("behavior/import.zig"); _ = @import("behavior/incomplete_struct_param_tld.zig"); + _ = @import("behavior/inttoptr.zig"); _ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/math.zig"); _ = @import("behavior/merge_error_sets.zig"); diff --git a/test/cases/inttoptr.zig b/test/stage1/behavior/inttoptr.zig similarity index 99% rename from test/cases/inttoptr.zig rename to test/stage1/behavior/inttoptr.zig index ba3cc52f0905..bf657fc86a9c 100644 --- a/test/cases/inttoptr.zig +++ b/test/stage1/behavior/inttoptr.zig @@ -24,4 +24,3 @@ fn forceCompilerAnalyzeBranchHardCodedPtrDereference(x: bool) void { return; } } - diff --git a/test/stage1/behavior/null.zig b/test/stage1/behavior/null.zig index 05f25cb42f8a..731f59f6c20b 100644 --- a/test/stage1/behavior/null.zig +++ b/test/stage1/behavior/null.zig @@ -41,7 +41,21 @@ test "rhs maybe unwrap return" { const y = x orelse return; } -// test "maybe return" { +test "maybe return" { + maybeReturnImpl(); + comptime maybeReturnImpl(); +} + +fn maybeReturnImpl() void { + assertOrPanic(foo(1235).?); + if (foo(null) != null) unreachable; + assertOrPanic(!foo(1234).?); +} + +fn foo(x: ?i32) ?bool { + const value = x orelse return null; + return value > 1234; +} test "if var maybe pointer" { assertOrPanic(shouldBeAPlus1(Particle{ From 42b4e6598ca2d55ccca0d5a866bfd277d73fea66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jan 2019 22:12:46 -0500 Subject: [PATCH 175/190] copy elision: fix test "mixing normal and error defers" --- src/ir.cpp | 15 +++++++++++--- test/behavior.zig | 1 - test/cases/defer.zig | 36 ---------------------------------- test/stage1/behavior/defer.zig | 35 +++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 40 deletions(-) delete mode 100644 test/cases/defer.zig diff --git a/src/ir.cpp b/src/ir.cpp index fdc4bb3650b7..b02f78ab2175 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3639,8 +3639,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value; ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; IrInstruction *return_result_loc = nullptr; + LVal expr_lval = LValNone; if (expr_node) { - LVal expr_lval = LValNone; if (return_type != nullptr) { if (return_type->id == ZigTypeIdErrorUnion) { return_result_loc = ir_build_result_return(irb, scope, node); @@ -3670,14 +3670,23 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, size_t defer_counts[2]; ir_count_defers(irb, scope, outer_scope, defer_counts); bool have_err_defers = defer_counts[ReturnKindError] > 0; - if (have_err_defers || irb->codegen->have_err_ret_tracing) { + if (expr_node != nullptr && expr_lval != LValOptional && + (have_err_defers || irb->codegen->have_err_ret_tracing)) + { IrBasicBlock *err_block = ir_create_basic_block(irb, scope, "ErrRetErr"); IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk"); if (!have_err_defers) { ir_gen_defers_for_block(irb, scope, outer_scope, false); } - IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value); + IrInstruction *is_err; + if (expr_lval == LValNone) { + is_err = ir_build_test_err(irb, scope, node, return_value); + } else if (expr_lval == LValErrorUnionVal) { + is_err = ir_build_test_nonnull(irb, scope, node, return_value); + } else { + zig_unreachable(); + } bool should_inline = ir_should_inline(irb->exec, scope); IrInstruction *is_comptime; diff --git a/test/behavior.zig b/test/behavior.zig index 253b1b703fda..3dd130d68362 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,7 +1,6 @@ comptime { _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); - _ = @import("cases/defer.zig"); _ = @import("cases/null.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); } diff --git a/test/cases/defer.zig b/test/cases/defer.zig deleted file mode 100644 index ed111ce248c4..000000000000 --- a/test/cases/defer.zig +++ /dev/null @@ -1,36 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -var result: [3]u8 = undefined; -var index: usize = undefined; - -fn runSomeErrorDefers(x: bool) !bool { - index = 0; - defer { - result[index] = 'a'; - index += 1; - } - errdefer { - result[index] = 'b'; - index += 1; - } - defer { - result[index] = 'c'; - index += 1; - } - return if (x) x else error.FalseNotAllowed; -} - -test "mixing normal and error defers" { - assertOrPanic(runSomeErrorDefers(true) catch unreachable); - assertOrPanic(result[0] == 'c'); - assertOrPanic(result[1] == 'a'); - - const ok = runSomeErrorDefers(false) catch |err| x: { - assertOrPanic(err == error.FalseNotAllowed); - break :x true; - }; - assertOrPanic(ok); - assertOrPanic(result[0] == 'c'); - assertOrPanic(result[1] == 'b'); - assertOrPanic(result[2] == 'a'); -} diff --git a/test/stage1/behavior/defer.zig b/test/stage1/behavior/defer.zig index d44b9e8354b4..1a5125d55cc9 100644 --- a/test/stage1/behavior/defer.zig +++ b/test/stage1/behavior/defer.zig @@ -41,3 +41,38 @@ fn testNestedFnErrDefer() anyerror!void { }; return S.baz(); } + +var result: [3]u8 = undefined; +var index: usize = undefined; + +fn runSomeErrorDefers(x: bool) !bool { + index = 0; + defer { + result[index] = 'a'; + index += 1; + } + errdefer { + result[index] = 'b'; + index += 1; + } + defer { + result[index] = 'c'; + index += 1; + } + return if (x) x else error.FalseNotAllowed; +} + +test "mixing normal and error defers" { + assertOrPanic(runSomeErrorDefers(true) catch unreachable); + assertOrPanic(result[0] == 'c'); + assertOrPanic(result[1] == 'a'); + + const ok = runSomeErrorDefers(false) catch |err| x: { + assertOrPanic(err == error.FalseNotAllowed); + break :x true; + }; + assertOrPanic(ok); + assertOrPanic(result[0] == 'c'); + assertOrPanic(result[1] == 'b'); + assertOrPanic(result[2] == 'a'); +} From 32c3caa62adec7f0f0944453f64c59365222744e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jan 2019 22:34:09 -0500 Subject: [PATCH 176/190] copy elision: fix test "optional types" --- src/ir.cpp | 10 ++++++---- test/behavior.zig | 1 - test/cases/null.zig | 12 ------------ test/stage1/behavior/null.zig | 11 ++++++++++- 4 files changed, 16 insertions(+), 18 deletions(-) delete mode 100644 test/cases/null.zig diff --git a/src/ir.cpp b/src/ir.cpp index b02f78ab2175..48e47756b83c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10790,12 +10790,14 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr { ConstExprValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, result_loc->source_node); assert(optional_val->type->id == ZigTypeIdOptional); + bool same_comptime_repr = types_have_same_zig_comptime_repr(optional_val->type, + optional_val->type->data.maybe.child_type); if (optional_val->special == ConstValSpecialUndef) { switch (type_has_one_possible_value(ira->codegen, needed_child_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; case OnePossibleValueNo: - if (handle_is_ptr(optional_val->type)) { + if (!same_comptime_repr) { ConstExprValue *payload_val = create_const_vals(1); payload_val->type = needed_child_type; payload_val->special = ConstValSpecialUndef; @@ -10834,11 +10836,11 @@ static IrInstruction *ir_analyze_result_optional_payload(IrAnalyze *ira, IrInstr case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; case OnePossibleValueNo: - if (handle_is_ptr(optional_val->type)) { + if (same_comptime_repr) { + result_val->data.x_ptr.data.ref.pointee = optional_val; + } else { assert(optional_val->data.x_optional != nullptr); result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; - } else { - result_val->data.x_ptr.data.ref.pointee = optional_val; } break; case OnePossibleValueYes: diff --git a/test/behavior.zig b/test/behavior.zig index 3dd130d68362..fc4e9eb5382b 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,6 +1,5 @@ comptime { _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); - _ = @import("cases/null.zig"); _ = @import("cases/struct_contains_slice_of_itself.zig"); } diff --git a/test/cases/null.zig b/test/cases/null.zig deleted file mode 100644 index 2f135b3b3e04..000000000000 --- a/test/cases/null.zig +++ /dev/null @@ -1,12 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -test "optional types" { - comptime { - const opt_type_struct = StructWithOptionalType{ .t = u8 }; - assertOrPanic(opt_type_struct.t != null and opt_type_struct.t.? == u8); - } -} - -const StructWithOptionalType = struct { - t: ?type, -}; diff --git a/test/stage1/behavior/null.zig b/test/stage1/behavior/null.zig index 731f59f6c20b..e2f86a05ba44 100644 --- a/test/stage1/behavior/null.zig +++ b/test/stage1/behavior/null.zig @@ -144,7 +144,16 @@ test "null with default unwrap" { assertOrPanic(x == 1); } -// test "optional types" { +test "optional types" { + comptime { + const opt_type_struct = StructWithOptionalType{ .t = u8 }; + assertOrPanic(opt_type_struct.t != null and opt_type_struct.t.? == u8); + } +} + +const StructWithOptionalType = struct { + t: ?type, +}; test "optional pointer to 0 bit type null value at runtime" { const EmptyStruct = struct {}; From 9b02ce7a3359e1576d02c90365644b3960b9f7bb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jan 2019 22:49:08 -0500 Subject: [PATCH 177/190] copy elision: fix test "struct contains slice of itself" --- src/ir.cpp | 4 +- test/behavior.zig | 1 - .../cases/struct_contains_slice_of_itself.zig | 85 ------------------- .../struct_contains_slice_of_itself.zig | 83 ++++++++++++++++++ 4 files changed, 86 insertions(+), 87 deletions(-) delete mode 100644 test/cases/struct_contains_slice_of_itself.zig diff --git a/src/ir.cpp b/src/ir.cpp index 48e47756b83c..31bc0541f3a0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5867,7 +5867,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A if (expr_value == irb->codegen->invalid_instruction) return expr_value; - result_locs[i] = expr_value; + result_locs[i] = elem_result_loc; } ir_build_container_init_list(irb, scope, node, array_type, elem_count, result_locs, new_result_loc); return ir_gen_result(irb, scope, node, lval, old_result_loc, LValNone, ensured_result_loc); @@ -18845,6 +18845,8 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, if (type_is_invalid(elem_result_loc->value.type)) return ira->codegen->invalid_instruction; + assert(elem_result_loc->value.type->id == ZigTypeIdPointer); + if (instr_is_comptime(elem_result_loc) && elem_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { diff --git a/test/behavior.zig b/test/behavior.zig index fc4e9eb5382b..b4cc35afa1e5 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,5 +1,4 @@ comptime { _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); - _ = @import("cases/struct_contains_slice_of_itself.zig"); } diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig deleted file mode 100644 index 746c99a69932..000000000000 --- a/test/cases/struct_contains_slice_of_itself.zig +++ /dev/null @@ -1,85 +0,0 @@ -const assertOrPanic = @import("std").debug.assertOrPanic; - -const Node = struct { - payload: i32, - children: []Node, -}; - -const NodeAligned = struct { - payload: i32, - children: []align(@alignOf(NodeAligned)) NodeAligned, -}; - -test "struct contains slice of itself" { - var other_nodes = []Node{ - Node{ - .payload = 31, - .children = []Node{}, - }, - Node{ - .payload = 32, - .children = []Node{}, - }, - }; - var nodes = []Node{ - Node{ - .payload = 1, - .children = []Node{}, - }, - Node{ - .payload = 2, - .children = []Node{}, - }, - Node{ - .payload = 3, - .children = other_nodes[0..], - }, - }; - const root = Node{ - .payload = 1234, - .children = nodes[0..], - }; - assertOrPanic(root.payload == 1234); - assertOrPanic(root.children[0].payload == 1); - assertOrPanic(root.children[1].payload == 2); - assertOrPanic(root.children[2].payload == 3); - assertOrPanic(root.children[2].children[0].payload == 31); - assertOrPanic(root.children[2].children[1].payload == 32); -} - -test "struct contains aligned slice of itself" { - var other_nodes = []NodeAligned{ - NodeAligned{ - .payload = 31, - .children = []NodeAligned{}, - }, - NodeAligned{ - .payload = 32, - .children = []NodeAligned{}, - }, - }; - var nodes = []NodeAligned{ - NodeAligned{ - .payload = 1, - .children = []NodeAligned{}, - }, - NodeAligned{ - .payload = 2, - .children = []NodeAligned{}, - }, - NodeAligned{ - .payload = 3, - .children = other_nodes[0..], - }, - }; - const root = NodeAligned{ - .payload = 1234, - .children = nodes[0..], - }; - assert(root.payload == 1234); - assert(root.children[0].payload == 1); - assert(root.children[1].payload == 2); - assert(root.children[2].payload == 3); - assert(root.children[2].children[0].payload == 31); - assert(root.children[2].children[1].payload == 32); -} diff --git a/test/stage1/behavior/struct_contains_slice_of_itself.zig b/test/stage1/behavior/struct_contains_slice_of_itself.zig index 41c447315af6..15780a7c4456 100644 --- a/test/stage1/behavior/struct_contains_slice_of_itself.zig +++ b/test/stage1/behavior/struct_contains_slice_of_itself.zig @@ -1,2 +1,85 @@ const assertOrPanic = @import("std").debug.assertOrPanic; +const Node = struct { + payload: i32, + children: []Node, +}; + +const NodeAligned = struct { + payload: i32, + children: []align(@alignOf(NodeAligned)) NodeAligned, +}; + +test "struct contains slice of itself" { + var other_nodes = []Node{ + Node{ + .payload = 31, + .children = []Node{}, + }, + Node{ + .payload = 32, + .children = []Node{}, + }, + }; + var nodes = []Node{ + Node{ + .payload = 1, + .children = []Node{}, + }, + Node{ + .payload = 2, + .children = []Node{}, + }, + Node{ + .payload = 3, + .children = other_nodes[0..], + }, + }; + const root = Node{ + .payload = 1234, + .children = nodes[0..], + }; + assertOrPanic(root.payload == 1234); + assertOrPanic(root.children[0].payload == 1); + assertOrPanic(root.children[1].payload == 2); + assertOrPanic(root.children[2].payload == 3); + assertOrPanic(root.children[2].children[0].payload == 31); + assertOrPanic(root.children[2].children[1].payload == 32); +} + +test "struct contains aligned slice of itself" { + var other_nodes = []NodeAligned{ + NodeAligned{ + .payload = 31, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 32, + .children = []NodeAligned{}, + }, + }; + var nodes = []NodeAligned{ + NodeAligned{ + .payload = 1, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 2, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 3, + .children = other_nodes[0..], + }, + }; + const root = NodeAligned{ + .payload = 1234, + .children = nodes[0..], + }; + assertOrPanic(root.payload == 1234); + assertOrPanic(root.children[0].payload == 1); + assertOrPanic(root.children[1].payload == 2); + assertOrPanic(root.children[2].payload == 3); + assertOrPanic(root.children[2].children[0].payload == 31); + assertOrPanic(root.children[2].children[1].payload == 32); +} From 8e2b07ecb875d786332997cb76030bce0cbd32ff Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Jan 2019 22:59:32 -0500 Subject: [PATCH 178/190] copy elision: fix test "coroutine await struct" --- src/ir.cpp | 8 ++++ test/behavior.zig | 1 - test/cases/coroutine_await_struct.zig | 47 ------------------- .../behavior/coroutine_await_struct.zig | 43 +++++++++++++++++ 4 files changed, 51 insertions(+), 48 deletions(-) delete mode 100644 test/cases/coroutine_await_struct.zig diff --git a/src/ir.cpp b/src/ir.cpp index 31bc0541f3a0..0f0df955766e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -24493,6 +24493,14 @@ ConstExprValue *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl ira->payload_return_type = ira->explicit_return_type->data.maybe.child_type; } } + } else if (is_async) { + if (expected_type->id == ZigTypeIdErrorUnion) { + ira->payload_return_type = expected_type->data.error_union.payload_type; + } else if (expected_type->id == ZigTypeIdOptional) { + ira->payload_return_type = expected_type->data.maybe.child_type; + } else { + ira->payload_return_type = expected_type; + } } if (result_value != nullptr) { diff --git a/test/behavior.zig b/test/behavior.zig index b4cc35afa1e5..269eaefe0680 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,4 +1,3 @@ comptime { - _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/coroutines.zig"); } diff --git a/test/cases/coroutine_await_struct.zig b/test/cases/coroutine_await_struct.zig deleted file mode 100644 index 6ca2a301eca5..000000000000 --- a/test/cases/coroutine_await_struct.zig +++ /dev/null @@ -1,47 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const assertOrPanic = std.debug.assertOrPanic; - -const Foo = struct { - x: i32, -}; - -var await_a_promise: promise = undefined; -var await_final_result = Foo{ .x = 0 }; - -test "coroutine await struct" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - await_seq('a'); - const p = async<&da.allocator> await_amain() catch unreachable; - await_seq('f'); - resume await_a_promise; - await_seq('i'); - assertOrPanic(await_final_result.x == 1234); - assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); -} -async fn await_amain() void { - await_seq('b'); - const p = async await_another() catch unreachable; - await_seq('e'); - await_final_result = await p; - await_seq('h'); -} -async fn await_another() Foo { - await_seq('c'); - suspend { - await_seq('d'); - await_a_promise = @handle(); - } - await_seq('g'); - return Foo{ .x = 1234 }; -} - -var await_points = []u8{0} ** "abcdefghi".len; -var await_seq_index: usize = 0; - -fn await_seq(c: u8) void { - await_points[await_seq_index] = c; - await_seq_index += 1; -} diff --git a/test/stage1/behavior/coroutine_await_struct.zig b/test/stage1/behavior/coroutine_await_struct.zig index c8037714fda6..6ca2a301eca5 100644 --- a/test/stage1/behavior/coroutine_await_struct.zig +++ b/test/stage1/behavior/coroutine_await_struct.zig @@ -2,3 +2,46 @@ const std = @import("std"); const builtin = @import("builtin"); const assertOrPanic = std.debug.assertOrPanic; +const Foo = struct { + x: i32, +}; + +var await_a_promise: promise = undefined; +var await_final_result = Foo{ .x = 0 }; + +test "coroutine await struct" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + await_seq('a'); + const p = async<&da.allocator> await_amain() catch unreachable; + await_seq('f'); + resume await_a_promise; + await_seq('i'); + assertOrPanic(await_final_result.x == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); +} +async fn await_amain() void { + await_seq('b'); + const p = async await_another() catch unreachable; + await_seq('e'); + await_final_result = await p; + await_seq('h'); +} +async fn await_another() Foo { + await_seq('c'); + suspend { + await_seq('d'); + await_a_promise = @handle(); + } + await_seq('g'); + return Foo{ .x = 1234 }; +} + +var await_points = []u8{0} ** "abcdefghi".len; +var await_seq_index: usize = 0; + +fn await_seq(c: u8) void { + await_points[await_seq_index] = c; + await_seq_index += 1; +} From a6cc7f853fce71873f4a7fdb8e505e65d74fd691 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Jan 2019 00:18:30 -0500 Subject: [PATCH 179/190] copy elision: fix test "async fn with inferred error set" --- src/codegen.cpp | 11 +++++++++-- test/cases/coroutines.zig | 13 ------------- test/stage1/behavior/coroutines.zig | 13 ++++++++++++- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 410b337b9bc4..fdef8b41f573 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5087,8 +5087,15 @@ static LLVMValueRef ir_render_result_error_union_code(CodeGen *g, IrExecutable * IrInstructionResultErrorUnionCode *instruction) { LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); - // TODO write 0xaa in debug mode to the undefined payload - return LLVMBuildStructGEP(g->builder, prev_result_loc, err_union_err_index, ""); + ZigType *ptr_to_error_union_type = instruction->prev_result_loc->value.type; + assert(ptr_to_error_union_type->id == ZigTypeIdPointer); + ZigType *error_union_type = ptr_to_error_union_type->data.pointer.child_type; + assert(error_union_type->id == ZigTypeIdErrorUnion); + if (type_has_bits(error_union_type->data.error_union.payload_type)) { + return LLVMBuildStructGEP(g->builder, prev_result_loc, err_union_err_index, ""); + } else { + return prev_result_loc; + } } static LLVMValueRef ir_render_assert_non_error(CodeGen *g, IrExecutable *executable, diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 1cd31b930ecc..2622607e1094 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -2,19 +2,6 @@ const std = @import("std"); const builtin = @import("builtin"); const assertOrPanic = std.debug.assertOrPanic; -test "async fn with inferred error set" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - const p = (async<&da.allocator> failing()) catch unreachable; - resume p; - cancel p; -} - -async fn failing() !void { - suspend; - return error.Fail; -} - test "error return trace across suspend points - early return" { const p = nonFailing(); resume p; diff --git a/test/stage1/behavior/coroutines.zig b/test/stage1/behavior/coroutines.zig index 5603054eeda7..423879f5098b 100644 --- a/test/stage1/behavior/coroutines.zig +++ b/test/stage1/behavior/coroutines.zig @@ -192,7 +192,18 @@ async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { suspend; } -// test "async fn with inferred error set" { +test "async fn with inferred error set" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = (async<&da.allocator> failing()) catch unreachable; + resume p; + cancel p; +} + +async fn failing() !void { + suspend; + return error.Fail; +} // test "error return trace across suspend points - early return" { From 80fc4249c87551dd65d1f40802296f461bfdec49 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Jan 2019 01:19:39 -0500 Subject: [PATCH 180/190] workaround for not handling return value in coroutines All behavior tests are now passing in the copy elision branch. --- src/ir.cpp | 6 +++++ test/behavior.zig | 3 --- test/cases/coroutines.zig | 38 ----------------------------- test/stage1/behavior/coroutines.zig | 35 ++++++++++++++++++++++++-- 4 files changed, 39 insertions(+), 43 deletions(-) delete mode 100644 test/behavior.zig delete mode 100644 test/cases/coroutines.zig diff --git a/src/ir.cpp b/src/ir.cpp index 0f0df955766e..2db5bed4aa35 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -22265,6 +22265,12 @@ static IrInstruction *ir_analyze_instruction_check_statement_is_void(IrAnalyze * if (type_is_invalid(statement_type)) return ira->codegen->invalid_instruction; + // TODO this is a temporary hack because I'm about to rework coroutines anyway + bool is_async = exec_is_async(ira->new_irb.exec); + if (is_async) { + return ir_const_void(ira, &instruction->base); + } + if (statement_type->id != ZigTypeIdVoid && statement_type != ira->codegen->builtin_types.entry_infer) { ir_add_error(ira, &instruction->base, buf_sprintf("expression value is ignored")); } diff --git a/test/behavior.zig b/test/behavior.zig deleted file mode 100644 index 269eaefe0680..000000000000 --- a/test/behavior.zig +++ /dev/null @@ -1,3 +0,0 @@ -comptime { - _ = @import("cases/coroutines.zig"); -} diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig deleted file mode 100644 index 2622607e1094..000000000000 --- a/test/cases/coroutines.zig +++ /dev/null @@ -1,38 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const assertOrPanic = std.debug.assertOrPanic; - -test "error return trace across suspend points - early return" { - const p = nonFailing(); - resume p; - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - const p2 = try async<&da.allocator> printTrace(p); - cancel p2; -} - -test "error return trace across suspend points - async return" { - const p = nonFailing(); - const p2 = try async printTrace(p); - resume p; - cancel p2; -} - -fn nonFailing() (promise->anyerror!void) { - return async suspendThenFail() catch unreachable; -} -async fn suspendThenFail() anyerror!void { - suspend; - return error.Fail; -} -async fn printTrace(p: promise->(anyerror!void)) void { - (await p) catch |e| { - std.debug.assertOrPanic(e == error.Fail); - if (@errorReturnTrace()) |trace| { - assertOrPanic(trace.index == 1); - } else switch (builtin.mode) { - builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"), - builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {}, - } - }; -} diff --git a/test/stage1/behavior/coroutines.zig b/test/stage1/behavior/coroutines.zig index 423879f5098b..a2327c506011 100644 --- a/test/stage1/behavior/coroutines.zig +++ b/test/stage1/behavior/coroutines.zig @@ -205,9 +205,40 @@ async fn failing() !void { return error.Fail; } -// test "error return trace across suspend points - early return" { +test "error return trace across suspend points - early return" { + const p = nonFailing(); + resume p; + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p2 = try async<&da.allocator> printTrace(p); + cancel p2; +} + +test "error return trace across suspend points - async return" { + const p = nonFailing(); + const p2 = try async printTrace(p); + resume p; + cancel p2; +} -// test "error return trace across suspend points - async return" { +fn nonFailing() (promise->anyerror!void) { + return async suspendThenFail() catch unreachable; +} +async fn suspendThenFail() anyerror!void { + suspend; + return error.Fail; +} +async fn printTrace(p: promise->(anyerror!void)) void { + (await p) catch |e| { + std.debug.assertOrPanic(e == error.Fail); + if (@errorReturnTrace()) |trace| { + assertOrPanic(trace.index == 1); + } else switch (builtin.mode) { + builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"), + builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {}, + } + }; +} test "break from suspend" { var buf: [500]u8 = undefined; From 2d3a397160fc05e811b4d7de38c70720fade4ac8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Jan 2019 01:53:52 -0500 Subject: [PATCH 181/190] copy elision: fix address of unwrap optional --- src/ir.cpp | 31 ++++++++++++++++++++----------- test/stage1/behavior/optional.zig | 17 +++++++++++++++++ 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 2db5bed4aa35..e71877f46fdd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3508,6 +3508,7 @@ static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *n { switch (lval) { case LValNone: + case LValPtr: return result_loc; case LValErrorUnionVal: case LValErrorUnionPtr: @@ -3519,8 +3520,6 @@ static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *n return ir_build_result_child(irb, scope, node, result_loc, lval); } break; - case LValPtr: - zig_unreachable(); } } return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "", nullptr); @@ -3534,27 +3533,24 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, if (instr_is_unreachable(value)) return value; if (result_loc != nullptr) { + IrInstruction *store_inst = ir_build_store_ptr(irb, scope, node, result_loc, value); + store_inst->is_gen = value->is_gen; switch (lval) { - case LValNone: { - IrInstruction *store_inst = ir_build_store_ptr(irb, scope, node, result_loc, value); - store_inst->is_gen = value->is_gen; - return value; - } case LValPtr: zig_unreachable(); + case LValNone: + return value; case LValErrorUnionVal: - ir_build_store_ptr(irb, scope, node, result_loc, value); return ir_build_const_null(irb, scope, node); case LValErrorUnionPtr: { - ir_build_store_ptr(irb, scope, node, result_loc, value); IrInstruction *null = ir_build_const_null(irb, scope, node); return ir_build_ref(irb, scope, node, null, true, false, nullptr); } case LValOptional: { - ir_build_store_ptr(irb, scope, node, result_loc, value); return ir_build_const_bool(irb, scope, node, true); } } + zig_unreachable(); } switch (lval) { case LValPtr: @@ -7955,11 +7951,24 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_ptr(irb, scope, node, lval, result_loc, ptr_inst); } case NodeTypeUnwrapOptional: { + AstNode *expr_node = node->data.unwrap_optional.expr; + if (lval == LValPtr) { + IrInstruction *maybe_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, nullptr); + if (maybe_ptr == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, expr_node, maybe_ptr, true); + if (result_loc == nullptr) + return unwrapped_ptr; + ir_build_store_ptr(irb, scope, node, result_loc, unwrapped_ptr); + return result_loc; + } + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, result_loc); if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - AstNode *expr_node = node->data.unwrap_optional.expr; + IrInstruction *is_non_null = ir_gen_node(irb, expr_node, scope, LValOptional, ensured_result_loc); if (is_non_null == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig index 84a75395334c..50a72fed5cb6 100644 --- a/test/stage1/behavior/optional.zig +++ b/test/stage1/behavior/optional.zig @@ -28,3 +28,20 @@ fn testNullPtrsEql() void { assertOrPanic(x == &number); assertOrPanic(&number == x); } + +test "address of unwrap optional" { + const S = struct { + const Foo = struct { + a: i32, + }; + + var global: ?Foo = null; + + pub fn getFoo() anyerror!*Foo { + return &global.?; + } + }; + S.global = S.Foo{ .a = 1234 }; + const foo = S.getFoo() catch unreachable; + assertOrPanic(foo.a == 1234); +} From 5f2f5acfd4ba97af66abe62bb9f829e36af53534 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jan 2019 01:38:30 -0500 Subject: [PATCH 182/190] copy elision: fix nested error union function call in optional unwrap --- src/ir.cpp | 4 ++- test/stage1/behavior/error.zig | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index e71877f46fdd..0ca02c788401 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3513,7 +3513,9 @@ static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *n case LValErrorUnionVal: case LValErrorUnionPtr: case LValOptional: - if (result_loc->id == IrInstructionIdAllocaSrc) { + if (result_loc->id == IrInstructionIdAllocaSrc || + result_loc->id == IrInstructionIdFirstArgResultLoc) + { break; } if (result_loc->id != IrInstructionIdResultChild) { diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig index 79e935903099..6673fcb5c72c 100644 --- a/test/stage1/behavior/error.zig +++ b/test/stage1/behavior/error.zig @@ -1,5 +1,6 @@ const std = @import("std"); const assertOrPanic = std.debug.assertOrPanic; +const assertError = std.debug.assertError; const mem = std.mem; const builtin = @import("builtin"); @@ -249,3 +250,47 @@ fn intLiteral(str: []const u8) !?i64 { return error.T; } + +test "nested error union function call in optional unwrap" { + const S = struct { + const Foo = struct { + a: i32, + }; + + fn errorable() !i32 { + var x: Foo = (try getFoo()) orelse return error.Other; + return x.a; + } + + fn errorable2() !i32 { + var x: Foo = (try getFoo2()) orelse return error.Other; + return x.a; + } + + fn errorable3() !i32 { + var x: Foo = (try getFoo3()) orelse return error.Other; + return x.a; + } + + fn getFoo() anyerror!?Foo { + return Foo{ .a = 1234 }; + } + + fn getFoo2() anyerror!?Foo { + return error.Failure; + } + + fn getFoo3() anyerror!?Foo { + return null; + } + }; + assertOrPanic((try S.errorable()) == 1234); + assertError(S.errorable2(), error.Failure); + assertError(S.errorable3(), error.Other); + comptime { + assertOrPanic((try S.errorable()) == 1234); + // TODO uncomment this + //assertError(S.errorable2(), error.Failure); + assertError(S.errorable3(), error.Other); + } +} From 9312d50b6ee8cfa047a2926e77ea99c5150b8c86 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jan 2019 02:15:09 -0500 Subject: [PATCH 183/190] copy elision: fix widen cast integer payload of... ...error union function call --- src/ir.cpp | 35 ++++++++++++++++++++++++++++++---- test/stage1/behavior/error.zig | 14 ++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 0ca02c788401..6179c7b74e21 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3514,7 +3514,8 @@ static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *n case LValErrorUnionPtr: case LValOptional: if (result_loc->id == IrInstructionIdAllocaSrc || - result_loc->id == IrInstructionIdFirstArgResultLoc) + result_loc->id == IrInstructionIdFirstArgResultLoc || + result_loc->id == IrInstructionIdFieldPtr) { break; } @@ -11943,6 +11944,11 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst (wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdFloat || wanted_type->id == ZigTypeIdComptimeFloat)) { + if (value->value.special == ConstValSpecialUndef) { + IrInstruction *result = ir_const(ira, source_instr, wanted_type); + result->value.special = ConstValSpecialUndef; + return result; + } if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); @@ -14988,6 +14994,8 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, } } + bool need_copy = false; + if (prev_result_loc != nullptr) { if (type_is_invalid(prev_result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -15022,9 +15030,19 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, } } else { payload_result_loc = ir_implicit_cast_result(ira, call_instruction->base.source_node, - prev_result_loc, payload_result_type, need_store_ptr); - if (payload_result_loc == nullptr) - payload_result_loc = prev_result_loc; + prev_result_loc, payload_result_type, true); + if (payload_result_loc == nullptr) { + if (need_store_ptr) { + payload_result_loc = prev_result_loc; + } else { + payload_result_loc = ir_analyze_alloca(ira, &call_instruction->base, payload_result_type, + 0, "", false); + if (type_is_invalid(payload_result_loc->value.type)) + return ira->codegen->invalid_instruction; + payload_result_loc->value.special = ConstValSpecialRuntime; + need_copy = true; + } + } base_result_loc = payload_result_loc; if (type_is_invalid(payload_result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -15042,6 +15060,15 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, if (scalar_result_type->id == ZigTypeIdUnreachable) return ir_finish_anal(ira, new_call_instruction); + if (need_copy) { + IrInstruction *loaded = ir_get_deref(ira, &call_instruction->base, payload_result_loc); + if (type_is_invalid(loaded->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *store_inst = ir_analyze_store_ptr(ira, &call_instruction->base, prev_result_loc, loaded); + if (type_is_invalid(store_inst->value.type)) + return ira->codegen->invalid_instruction; + } + if (need_store_ptr) { ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); switch (call_instruction->lval) { diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig index 6673fcb5c72c..a98d949dd563 100644 --- a/test/stage1/behavior/error.zig +++ b/test/stage1/behavior/error.zig @@ -294,3 +294,17 @@ test "nested error union function call in optional unwrap" { assertError(S.errorable3(), error.Other); } } + +test "widen cast integer payload of error union function call" { + const S = struct { + fn errorable() !u64 { + var x = u64(try number()); + return x; + } + + fn number() anyerror!u32 { + return 1234; + } + }; + assertOrPanic((try S.errorable()) == 1234); +} From 912807541dd23e752b471b53c3a25b3f5db3fc0f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jan 2019 12:51:32 -0500 Subject: [PATCH 184/190] fix return function call to error set from error union function --- src/ir.cpp | 2 +- test/stage1/behavior/error.zig | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 6179c7b74e21..f1c5dc38f45a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15028,7 +15028,7 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, } else { zig_unreachable(); } - } else { + } else if (return_type->id != ZigTypeIdErrorSet) { payload_result_loc = ir_implicit_cast_result(ira, call_instruction->base.source_node, prev_result_loc, payload_result_type, true); if (payload_result_loc == nullptr) { diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig index a98d949dd563..35f8cc5d16c0 100644 --- a/test/stage1/behavior/error.zig +++ b/test/stage1/behavior/error.zig @@ -308,3 +308,18 @@ test "widen cast integer payload of error union function call" { }; assertOrPanic((try S.errorable()) == 1234); } + +test "return function call to error set from error union function" { + const S = struct { + fn errorable() anyerror!i32 { + return fail(); + } + + fn fail() anyerror { + return error.Failure; + } + }; + assertError(S.errorable(), error.Failure); + // TODO uncomment this + //comptime assertError(S.errorable(), error.Failure); +} From 3f569ebccc6da65e960029b68a82949756830b1d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jan 2019 12:59:10 -0500 Subject: [PATCH 185/190] std: workaround for copy elision limitation See https://github.com/ziglang/zig/pull/1682#issuecomment-451261326 --- std/heap.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/std/heap.zig b/std/heap.zig index e0c3d1343a80..b2210a317108 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -243,7 +243,8 @@ pub const ArenaAllocator = struct { const cur_buf = cur_node.data[@sizeOf(BufNode)..]; const addr = @ptrToInt(cur_buf.ptr) + self.end_index; const rem = @rem(addr, alignment); - const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); + // TODO: https://github.com/ziglang/zig/pull/1682#issuecomment-451261326 + const march_forward_bytes: usize = if (rem == 0) 0 else (alignment - rem); const adjusted_index = self.end_index + march_forward_bytes; const new_end_index = adjusted_index + n; if (new_end_index > cur_buf.len) { From 98d87f0f12526444ba89e98b7d16928b04303dd6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jan 2019 13:26:02 -0500 Subject: [PATCH 186/190] copy elision: fix passing an optional integer as a parameter --- src/ir.cpp | 17 ++++++++++++++--- std/debug/index.zig | 3 ++- test/stage1/behavior/optional.zig | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index f1c5dc38f45a..ad2d57d1bdb6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1740,11 +1740,15 @@ static IrInstruction *ir_build_optional_unwrap_val(IrBuilder *irb, Scope *scope, return &instruction->base; } -static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { +static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *value, IrInstruction *result_loc) +{ IrInstructionOptionalWrap *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; + instruction->result_loc = result_loc; ir_ref_instruction(value, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -11071,9 +11075,9 @@ static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *so ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdOptional); + ZigType *payload_type = wanted_type->data.maybe.child_type; if (instr_is_comptime(value)) { - ZigType *payload_type = wanted_type->data.maybe.child_type; IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type); if (type_is_invalid(casted_payload->value.type)) return ira->codegen->invalid_instruction; @@ -11094,7 +11098,14 @@ static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *so return &const_instruction->base; } - IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value); + IrInstruction *result_loc = nullptr; + if (type_has_bits(payload_type) && handle_is_ptr(wanted_type)) { + result_loc = ir_analyze_alloca(ira, source_instr, wanted_type, 0, "", false); + result_loc->value.special = ConstValSpecialRuntime; + } + + IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, + value, result_loc); result->value.type = wanted_type; result->value.data.rh_maybe = RuntimeHintOptionalNonNull; return result; diff --git a/std/debug/index.zig b/std/debug/index.zig index 445f943594cd..5819840414a9 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -1953,7 +1953,8 @@ fn scanAllCompileUnits(di: *DwarfInfo) !void { if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; - const pc_range = x: { + // TODO: https://github.com/ziglang/zig/pull/1682#issuecomment-451261326 + const pc_range: ?PcRange = x: { if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { const pc_end = switch (high_pc_value.*) { diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig index 50a72fed5cb6..e4c7754cb935 100644 --- a/test/stage1/behavior/optional.zig +++ b/test/stage1/behavior/optional.zig @@ -45,3 +45,18 @@ test "address of unwrap optional" { const foo = S.getFoo() catch unreachable; assertOrPanic(foo.a == 1234); } + +test "passing an optional integer as a parameter" { + const S = struct { + fn entry() bool { + var x: i32 = 1234; + return foo(x); + } + + fn foo(x: ?i32) bool { + return x.? == 1234; + } + }; + assertOrPanic(S.entry()); + comptime assertOrPanic(S.entry()); +} From cfa1ef85dfe415f4be01a9fd99cea45b4ff61150 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Jan 2019 14:27:13 -0500 Subject: [PATCH 187/190] copy elision: fix array literal as argument to function also restore default panic handler code --- src/ir.cpp | 62 ++++++++++++++-------------------- std/special/bootstrap.zig | 15 ++++---- std/special/panic.zig | 7 ++-- test/stage1/behavior/array.zig | 40 ++++++++++++++++++++++ 4 files changed, 75 insertions(+), 49 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index ad2d57d1bdb6..c31c4324efa7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10887,7 +10887,9 @@ static IrInstruction *ir_analyze_instruction_result_optional_payload(IrAnalyze * return ir_analyze_result_optional_payload(ira, prev_result_loc, payload_type, instruction->make_non_null); } -static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction *result_loc, uint64_t len) { +static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction *result_loc, uint64_t len, + bool init_array) +{ ZigType *slice_type = result_loc->value.type->data.pointer.child_type; assert(is_slice(slice_type)); ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; @@ -10910,22 +10912,28 @@ static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction slice_ptr_val->type = slice_ptr_type; slice_ptr_val->special = ConstValSpecialUndef; - ConstParent *slice_ptr_parent = get_const_val_parent(ira->codegen, slice_ptr_val); - if (slice_ptr_parent != nullptr) { - slice_ptr_parent->id = ConstParentIdStruct; - slice_ptr_parent->data.p_struct.struct_val = slice_val; - slice_ptr_parent->data.p_struct.field_index = slice_ptr_index; + slice_ptr_val->parent.id = ConstParentIdStruct; + slice_ptr_val->parent.data.p_struct.struct_val = slice_val; + slice_ptr_val->parent.data.p_struct.field_index = slice_ptr_index; + + if (init_array) { + ConstExprValue *array_val = create_const_vals(1); + array_val->type = get_array_type(ira->codegen, slice_ptr_type->data.pointer.child_type, len); + array_val->data.x_array.special = ConstArraySpecialUndef; + + slice_ptr_val->special = ConstValSpecialStatic; + slice_ptr_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + slice_ptr_val->data.x_ptr.special = ConstPtrSpecialBaseArray; + slice_ptr_val->data.x_ptr.data.base_array.array_val = array_val; } slice_len_val->type = ira->codegen->builtin_types.entry_usize; slice_len_val->special = ConstValSpecialStatic; bigint_init_unsigned(&slice_len_val->data.x_bigint, len); - ConstParent *slice_len_parent = get_const_val_parent(ira->codegen, slice_len_val); - if (slice_len_parent != nullptr) { - slice_len_parent->id = ConstParentIdStruct; - slice_len_parent->data.p_struct.struct_val = slice_val; - slice_len_parent->data.p_struct.field_index = slice_len_index; - } + slice_len_val->parent.id = ConstParentIdStruct; + slice_len_val->parent.data.p_struct.struct_val = slice_val; + slice_len_val->parent.data.p_struct.field_index = slice_len_index; + slice_val->special = ConstValSpecialStatic; } assert(slice_val->data.x_struct.fields != nullptr); @@ -12448,7 +12456,7 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, AstNode *source_no array_type->data.array.child_type, source_node, !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) { - return ir_analyze_result_slice_ptr(ira, result_loc, array_type->data.array.len); + return ir_analyze_result_slice_ptr(ira, result_loc, array_type->data.array.len, false); } } @@ -12460,7 +12468,7 @@ static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, AstNode *source_no types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, needed_child_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { - return ir_analyze_result_slice_ptr(ira, result_loc, needed_child_type->data.array.len); + return ir_analyze_result_slice_ptr(ira, result_loc, needed_child_type->data.array.len, true); } } @@ -16639,12 +16647,9 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct ConstExprValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i]; elem_val->special = ConstValSpecialUndef; elem_val->type = array_type->data.array.child_type; - ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); - if (parent != nullptr) { - parent->id = ConstParentIdArray; - parent->data.p_array.array_val = array_ptr_val; - parent->data.p_array.elem_index = i; - } + elem_val->parent.id = ConstParentIdArray; + elem_val->parent.data.p_array.array_val = array_ptr_val; + elem_val->parent.data.p_array.elem_index = i; } } @@ -23819,23 +23824,8 @@ static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira return new_result_loc; } - // Result of this instruction should be the result location for the first argument of the function call. - ZigType *param_type; - if (fn_ref->value.type->id == ZigTypeIdFn) { - ZigType *fn_type = fn_ref->value.type; - param_type = fn_type->data.fn.fn_type_id.param_info[0].type; - } else if (fn_ref->value.type->id == ZigTypeIdBoundFn) { - ZigType *fn_type = fn_ref->value.type->data.bound_fn.fn_type; - param_type = fn_type->data.fn.fn_type_id.param_info[1].type; - } else { - zig_unreachable(); - } - - if (param_type != nullptr && type_is_invalid(param_type)) - return ira->codegen->invalid_instruction; - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); - return ir_analyze_alloca(ira, &instruction->base, param_type, 0, "", is_comptime); + return ir_analyze_alloca(ira, &instruction->base, nullptr, 0, "", is_comptime); } static IrInstruction *ir_analyze_instruction_infer_array_type(IrAnalyze *ira, diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 0349ae1cf930..438774dcf9a8 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -22,8 +22,8 @@ nakedcc fn _start() noreturn { switch (builtin.arch) { builtin.Arch.x86_64 => { argc_ptr = asm ("lea (%%rsp), %[argc]" - : [argc] "=r" (-> [*]usize) - ); + : [argc] "=r" (-> [*]usize) + ); }, builtin.Arch.i386 => { argc_ptr = asm ("lea (%%esp), %[argc]" @@ -104,12 +104,11 @@ inline fn callMain() u8 { builtin.TypeId.ErrorUnion => { root.main() catch |err| { std.debug.warn("error: {}\n", @errorName(err)); - // TODO restore this - //if (builtin.os != builtin.Os.zen) { - // if (@errorReturnTrace()) |trace| { - // std.debug.dumpStackTrace(trace); - // } - //} + if (builtin.os != builtin.Os.zen) { + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace); + } + } return 1; }; return 0; diff --git a/std/special/panic.zig b/std/special/panic.zig index 88a202832b24..bd3ad971e0cb 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -18,11 +18,8 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn std.os.abort(); }, else => { - // TODO put the original panic code back - std.debug.warn("panic: {}\n", msg); - std.os.abort(); - //const first_trace_addr = @ptrToInt(@returnAddress()); - //std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); + const first_trace_addr = @ptrToInt(@returnAddress()); + std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); }, } } diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig index 414f537d01a9..285c584e33c6 100644 --- a/test/stage1/behavior/array.zig +++ b/test/stage1/behavior/array.zig @@ -171,3 +171,43 @@ test "implicit comptime in array type size" { fn plusOne(x: u32) u32 { return x + 1; } + +test "array literal as argument to function" { + const S = struct { + fn entry(two: i32) void { + foo([]i32{ + 1, + 2, + 3, + }); + foo([]i32{ + 1, + two, + 3, + }); + foo2(true, []i32{ + 1, + 2, + 3, + }); + foo2(true, []i32{ + 1, + two, + 3, + }); + } + fn foo(x: []const i32) void { + assertOrPanic(x[0] == 1); + assertOrPanic(x[1] == 2); + assertOrPanic(x[2] == 3); + } + fn foo2(trash: bool, x: []const i32) void { + assertOrPanic(trash); + assertOrPanic(x[0] == 1); + assertOrPanic(x[1] == 2); + assertOrPanic(x[2] == 3); + } + }; + S.entry(2); + comptime S.entry(2); +} From 0d16621de6e6b9550cb0445bf6f7586a0493da18 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 23 Jan 2019 18:54:03 -0500 Subject: [PATCH 188/190] copy elision: fix double nested array to... ...const slice cast in array literal --- src/all_types.hpp | 1 + src/codegen.cpp | 35 +++++++++--- src/ir.cpp | 99 ++++++++++++++++++++++++---------- src/ir_print.cpp | 3 +- test/stage1/behavior/array.zig | 57 ++++++++++++++++++++ 5 files changed, 159 insertions(+), 36 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 1229e158c246..e169b84020c2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3436,6 +3436,7 @@ struct IrInstructionResultSlicePtr { IrInstruction base; IrInstruction *prev_result_loc; + IrInstruction *array_loc; uint64_t len; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index fdef8b41f573..4731804b710a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5066,11 +5066,25 @@ static LLVMValueRef ir_render_result_slice_ptr(CodeGen *g, IrExecutable *executa ZigType *slice_type = instruction->prev_result_loc->value.type->data.pointer.child_type; assert(slice_type->id == ZigTypeIdStruct && slice_type->data.structure.is_slice); size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, (unsigned)len_field_index, ""); LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, instruction->len, false); gen_store_untyped(g, len_val, len_field_ptr, 0, false); + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; - return LLVMBuildStructGEP(g->builder, prev_result_loc, (unsigned)ptr_field_index, ""); + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, (unsigned)ptr_field_index, ""); + if (instruction->array_loc == nullptr) + return ptr_field_ptr; + + LLVMValueRef array_loc = ir_llvm_value(g, instruction->array_loc); + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->type_ref), + LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), + }; + LLVMValueRef casted_array_loc = LLVMBuildInBoundsGEP(g->builder, array_loc, indices, 2, ""); + gen_store_untyped(g, casted_array_loc, ptr_field_ptr, 0, false); + + return ptr_field_ptr; } static LLVMValueRef ir_render_result_error_union_payload(CodeGen *g, IrExecutable *executable, @@ -6588,12 +6602,21 @@ static void do_code_gen(CodeGen *g) { ZigType *ptr_type = instruction->base.value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; - if (type_has_bits(child_type) && instruction->base.value.special == ConstValSpecialRuntime && - child_type != g->builtin_types.entry_infer && instruction->base.ref_count != 0) - { - instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, - get_ptr_align(g, ptr_type)); + if (!type_has_bits(child_type)) + continue; + if (child_type == g->builtin_types.entry_infer) + continue; + if (instruction->base.ref_count == 0) + continue; + if (instruction->base.value.special != ConstValSpecialRuntime) { + if (const_ptr_pointee(nullptr, g, &instruction->base.value, nullptr)->special != + ConstValSpecialRuntime) + { + continue; + } } + instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, + get_ptr_align(g, ptr_type)); } ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); diff --git a/src/ir.cpp b/src/ir.cpp index c31c4324efa7..2e78e3a367a0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2956,13 +2956,15 @@ static IrInstruction *ir_build_result_optional_payload(IrBuilder *irb, Scope *sc } static IrInstruction *ir_build_result_slice_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *prev_result_loc, uint64_t len) + IrInstruction *prev_result_loc, uint64_t len, IrInstruction *array_loc) { IrInstructionResultSlicePtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->prev_result_loc = prev_result_loc; instruction->len = len; + instruction->array_loc = array_loc; ir_ref_instruction(prev_result_loc, irb->current_basic_block); + if (array_loc != nullptr) ir_ref_instruction(array_loc, irb->current_basic_block); return &instruction->base; } @@ -3090,6 +3092,7 @@ static IrInstruction *ir_build_alloca_src(IrBuilder *irb, Scope *scope, AstNode IrInstruction *child_type, IrInstruction *align, const char *name_hint, IrInstruction *is_comptime) { IrInstructionAllocaSrc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base.is_gen = true; instruction->child_type = child_type; instruction->align = align; instruction->name_hint = name_hint; @@ -3541,7 +3544,7 @@ static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, return value; if (result_loc != nullptr) { IrInstruction *store_inst = ir_build_store_ptr(irb, scope, node, result_loc, value); - store_inst->is_gen = value->is_gen; + store_inst->is_gen = value->is_gen || result_loc->is_gen; switch (lval) { case LValPtr: zig_unreachable(); @@ -3580,11 +3583,15 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV switch (lval) { case LValPtr: if (result_loc != nullptr) { - ir_build_store_ptr(irb, scope, node, result_loc, ptr); + IrInstruction *store_inst = ir_build_store_ptr(irb, scope, node, result_loc, ptr); + store_inst->is_gen = result_loc->is_gen || ptr->is_gen; } return ptr; - case LValNone: - return ir_build_load_ptr(irb, scope, node, ptr, result_loc); + case LValNone: { + IrInstruction *load_inst = ir_build_load_ptr(irb, scope, node, ptr, result_loc); + load_inst->is_gen = (result_loc != nullptr && result_loc->is_gen) || ptr->is_gen; + return load_inst; + } case LValErrorUnionPtr: { // ptr points to an error union; // result_loc points to the result payload @@ -5864,8 +5871,8 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A for (size_t i = 0; i < elem_count; i += 1) { AstNode *expr_node = container_init_expr->entries.at(i); IrInstruction *index_value = ir_build_const_usize(irb, scope, expr_node, i); - IrInstruction *elem_result_loc = ir_build_elem_ptr(irb, scope, expr_node, new_result_loc, - index_value, true, PtrLenSingle); + IrInstruction *elem_result_loc = ir_mark_gen(ir_build_elem_ptr(irb, scope, expr_node, new_result_loc, + index_value, true, PtrLenSingle)); IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope, LValNone, elem_result_loc); if (expr_value == irb->codegen->invalid_instruction) return expr_value; @@ -10916,20 +10923,8 @@ static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction slice_ptr_val->parent.data.p_struct.struct_val = slice_val; slice_ptr_val->parent.data.p_struct.field_index = slice_ptr_index; - if (init_array) { - ConstExprValue *array_val = create_const_vals(1); - array_val->type = get_array_type(ira->codegen, slice_ptr_type->data.pointer.child_type, len); - array_val->data.x_array.special = ConstArraySpecialUndef; - - slice_ptr_val->special = ConstValSpecialStatic; - slice_ptr_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; - slice_ptr_val->data.x_ptr.special = ConstPtrSpecialBaseArray; - slice_ptr_val->data.x_ptr.data.base_array.array_val = array_val; - } - slice_len_val->type = ira->codegen->builtin_types.entry_usize; - slice_len_val->special = ConstValSpecialStatic; - bigint_init_unsigned(&slice_len_val->data.x_bigint, len); + slice_len_val->special = ConstValSpecialUndef; slice_len_val->parent.id = ConstParentIdStruct; slice_len_val->parent.data.p_struct.struct_val = slice_val; slice_len_val->parent.data.p_struct.field_index = slice_len_index; @@ -10937,11 +10932,33 @@ static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction slice_val->special = ConstValSpecialStatic; } assert(slice_val->data.x_struct.fields != nullptr); + ConstExprValue *slice_len_val = &slice_val->data.x_struct.fields[slice_len_index]; + if (slice_len_val->special == ConstValSpecialUndef) { + slice_len_val->special = ConstValSpecialStatic; + bigint_init_unsigned(&slice_len_val->data.x_bigint, len); + } + IrInstruction *array_loc = nullptr; + if (init_array) { + ConstExprValue *slice_ptr_val = &slice_val->data.x_struct.fields[slice_ptr_index]; + if (slice_ptr_val->special == ConstValSpecialUndef) { + ZigType *array_type = get_array_type(ira->codegen, slice_ptr_type->data.pointer.child_type, len); + array_loc = ir_analyze_alloca(ira, result_loc, array_type, 0, "", false); + assert(array_loc->value.data.x_ptr.special == ConstPtrSpecialRef); + ConstExprValue *array_val = array_loc->value.data.x_ptr.data.ref.pointee; + array_val->special = ConstValSpecialStatic; + array_val->data.x_array.special = ConstArraySpecialUndef; + + slice_ptr_val->special = ConstValSpecialStatic; + slice_ptr_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + slice_ptr_val->data.x_ptr.special = ConstPtrSpecialBaseArray; + slice_ptr_val->data.x_ptr.data.base_array.array_val = array_val; + } + } IrInstruction *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_result_slice_ptr(&ira->new_irb, result_loc->scope, result_loc->source_node, - result_loc, len); + result_loc, len, array_loc); result->value.type = new_ptr_type; result->value.special = ConstValSpecialStatic; } else { @@ -10955,8 +10972,14 @@ static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction return result; } } + IrInstruction *array_loc = nullptr; + if (init_array) { + ZigType *array_type = get_array_type(ira->codegen, slice_ptr_type->data.pointer.child_type, len); + array_loc = ir_analyze_alloca(ira, result_loc, array_type, 0, "", false); + array_loc->value.special = ConstValSpecialRuntime; + } IrInstruction *result = ir_build_result_slice_ptr(&ira->new_irb, result_loc->scope, result_loc->source_node, - result_loc, len); + result_loc, len, array_loc); result->value.type = new_ptr_type; return result; } @@ -14774,6 +14797,7 @@ static void make_const_ptr_runtime(IrAnalyze *ira, IrInstruction *ptr) { val = ptr->value.data.x_ptr.data.base_optional_payload.optional_val; break; } + for (;;) { is_static = (val->special != ConstValSpecialRuntime); val->special = ConstValSpecialRuntime; @@ -14822,7 +14846,7 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return ir_const_void(ira, source_instr); } - if (uncasted_ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { + if (uncasted_ptr->value.type->data.pointer.is_const && !source_instr->is_gen && !uncasted_ptr->is_gen) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_instruction; } @@ -16658,8 +16682,13 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)) { if (array_type->id == ZigTypeIdPointer) { - IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type); + IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, + elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, safety_check_on, + elem_ptr_instruction->ptr_len); + result->is_gen = array_ptr->is_gen; + result->value.type = return_type; ConstExprValue *out_val = &result->value; + out_val->special = ConstValSpecialStatic; out_val->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; size_t new_index; size_t mem_size; @@ -16720,11 +16749,13 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, false, elem_ptr_instruction->ptr_len); + result->is_gen = array_ptr->is_gen; result->value.type = return_type; return result; } ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index]; IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type); + result->is_gen = array_ptr->is_gen; ConstExprValue *out_val = &result->value; uint64_t slice_len = bigint_as_unsigned(&len_field->data.x_bigint); if (index >= slice_len) { @@ -16782,6 +16813,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } else { result = ir_const(ira, &elem_ptr_instruction->base, return_type); } + result->is_gen = array_ptr->is_gen; ConstExprValue *out_val = &result->value; out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.mut = orig_array_ptr_val->data.x_ptr.mut; @@ -16821,6 +16853,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, safety_check_on, elem_ptr_instruction->ptr_len); + result->is_gen = array_ptr->is_gen; result->value.type = return_type; return result; } @@ -18904,7 +18937,10 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, if (instr_is_comptime(elem_result_loc) && elem_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { - const_ptrs.append(elem_result_loc); + ConstExprValue *elem_val = const_ptr_pointee_unchecked(ira->codegen, &elem_result_loc->value); + if (value_is_comptime(elem_val)) { + const_ptrs.append(elem_result_loc); + } } } @@ -18912,11 +18948,12 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, if (const_ptrs.length == elem_count) { result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; } else { - result_loc->value.special = ConstValSpecialRuntime; + make_const_ptr_runtime(ira, result_loc); for (size_t i = 0; i < const_ptrs.length; i += 1) { IrInstruction *elem_result_loc = const_ptrs.at(i); + assert(elem_result_loc->value.special == ConstValSpecialStatic); IrInstruction *deref = ir_get_deref(ira, elem_result_loc, elem_result_loc); - elem_result_loc->value.special = ConstValSpecialRuntime; + make_const_ptr_runtime(ira, elem_result_loc); ir_analyze_store_ptr(ira, elem_result_loc, elem_result_loc, deref); } } @@ -20633,8 +20670,10 @@ static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInst static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { Error err; - if (ty->id == ZigTypeIdPointer) { - if ((err = type_resolve(ira->codegen, ty->data.pointer.child_type, ResolveStatusAlignmentKnown))) + ZigType *ptr_type = get_src_ptr_type(ty); + assert(ptr_type != nullptr); + if (ptr_type->id == ZigTypeIdPointer) { + if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return err; } @@ -23742,6 +23781,7 @@ static IrInstruction *ir_analyze_instruction_result_cast(IrAnalyze *ira, IrInstr if (type_is_invalid(new_result_loc->value.type)) return ira->codegen->invalid_instruction; + new_result_loc->is_gen = prev_result_loc->is_gen; return new_result_loc; } @@ -23774,6 +23814,7 @@ static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_in if (fn_entry != nullptr) { fn_entry->alloca_list.append(result); } + result->base.is_gen = true; return &result->base; } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 19df27ec3b09..311a8b206bad 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -294,7 +294,8 @@ static void ir_print_container_init_list(IrPrint *irp, IrInstructionContainerIni ir_print_other_instruction(irp, elem_result_loc); } } - fprintf(irp->f, "}"); + fprintf(irp->f, "} result="); + ir_print_other_instruction(irp, instruction->result_loc); } static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerInitFields *instruction) { diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig index 285c584e33c6..118330520986 100644 --- a/test/stage1/behavior/array.zig +++ b/test/stage1/behavior/array.zig @@ -211,3 +211,60 @@ test "array literal as argument to function" { S.entry(2); comptime S.entry(2); } + +test "double nested array to const slice cast in array literal" { + const S = struct { + fn entry(two: i32) void { + const cases = [][]const []const i32{ + [][]const i32{[]i32{1}}, + [][]const i32{[]i32{ 2, 3 }}, + [][]const i32{ + []i32{4}, + []i32{ 5, 6, 7 }, + }, + }; + check(cases); + + const cases2 = [][]const i32{ + []i32{1}, + []i32{ two, 3 }, + }; + assertOrPanic(cases2.len == 2); + assertOrPanic(cases2[0].len == 1); + assertOrPanic(cases2[0][0] == 1); + assertOrPanic(cases2[1].len == 2); + assertOrPanic(cases2[1][0] == 2); + assertOrPanic(cases2[1][1] == 3); + + const cases3 = [][]const []const i32{ + [][]const i32{[]i32{1}}, + [][]const i32{[]i32{ two, 3 }}, + [][]const i32{ + []i32{4}, + []i32{ 5, 6, 7 }, + }, + }; + check(cases3); + } + + fn check(cases: []const []const []const i32) void { + assertOrPanic(cases.len == 3); + assertOrPanic(cases[0].len == 1); + assertOrPanic(cases[0][0].len == 1); + assertOrPanic(cases[0][0][0] == 1); + assertOrPanic(cases[1].len == 1); + assertOrPanic(cases[1][0].len == 2); + assertOrPanic(cases[1][0][0] == 2); + assertOrPanic(cases[1][0][1] == 3); + assertOrPanic(cases[2].len == 2); + assertOrPanic(cases[2][0].len == 1); + assertOrPanic(cases[2][0][0] == 4); + assertOrPanic(cases[2][1].len == 3); + assertOrPanic(cases[2][1][0] == 5); + assertOrPanic(cases[2][1][1] == 6); + assertOrPanic(cases[2][1][2] == 7); + } + }; + S.entry(2); + comptime S.entry(2); +} From 1f7e3c9a4860a66d584bff9e65345eaa73a1241e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Jan 2019 15:48:07 -0500 Subject: [PATCH 189/190] copy elision: fix unwrap function call with optional... ...pointer return value I had to put the panic handler stub back in since some code was being incorrectly comptime eliminated. --- src/analyze.cpp | 8 +++-- src/ir.cpp | 56 ++++++++++++++++++++++--------- std/special/bootstrap.zig | 11 +++--- std/special/panic.zig | 7 ++-- test/stage1/behavior/optional.zig | 19 +++++++++++ 5 files changed, 77 insertions(+), 24 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 1cc08158df73..9f416a486c7b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5908,8 +5908,12 @@ static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val case ConstPtrSpecialBaseErrorUnionPayload: case ConstPtrSpecialBaseOptionalPayload: buf_appendf(buf, "*"); - // TODO we need a source node for const_ptr_pointee because it can generate compile errors - render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); + if (const_val->type->id == ZigTypeIdPointer) { + // TODO we need a source node for const_ptr_pointee because it can generate compile errors + render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); + } else { + buf_appendf(buf, "(TODO print this pointer)"); + } return; case ConstPtrSpecialBaseArray: if (const_val->data.x_ptr.data.base_array.is_cstr) { diff --git a/src/ir.cpp b/src/ir.cpp index 2e78e3a367a0..85df4a1af694 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14553,8 +14553,20 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c result->value.type = get_optional_type(ira->codegen, alloc_fn_error_set_type); return result; } - case LValErrorUnionPtr: - zig_panic("TODO"); + case LValErrorUnionPtr: { + IrInstruction *call_inst_ref = ir_get_ref(ira, &call_instruction->base, new_call_inst, + true, false, nullptr); + IrInstruction *payload_ptr = ir_analyze_unwrap_err_payload(ira, &call_instruction->base, + call_inst_ref, nullptr, false); + IrInstruction *payload_deref = ir_get_deref(ira, &call_instruction->base, payload_ptr); + ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, payload_deref); + + IrInstruction *result = ir_build_error_union_field_error_set(&ira->new_irb, + call_instruction->base.scope, call_instruction->base.source_node, new_call_inst, false); + result->value.type = get_pointer_to_type(ira->codegen, + get_optional_type(ira->codegen, alloc_fn_error_set_type), true); + return result; + } case LValOptional: zig_panic("TODO"); } @@ -14991,8 +15003,8 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, IrInstruction *base_result_loc = nullptr; bool convert_to_value; - ZigType *scalar_result_type; - ZigType *payload_result_type; + ZigType *scalar_result_type; // of the call instruction + ZigType *payload_result_type; // of the call instruction if (return_type->id == ZigTypeIdErrorUnion) { scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); payload_result_type = return_type->data.error_union.payload_type; @@ -15014,6 +15026,9 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, bool need_store_ptr = !convert_to_value && (return_type == payload_result_type) && !handle_is_ptr(return_type); + bool need_unwrap_optional = call_instruction->lval == LValOptional && need_store_ptr && + payload_result_type->id == ZigTypeIdOptional; + IrInstruction *prev_result_loc = nullptr; if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { prev_result_loc = call_instruction->result_loc->child; @@ -15072,6 +15087,9 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, zig_unreachable(); } } else if (return_type->id != ZigTypeIdErrorSet) { + if (need_unwrap_optional) { + payload_result_type = payload_result_type->data.maybe.child_type; + } payload_result_loc = ir_implicit_cast_result(ira, call_instruction->base.source_node, prev_result_loc, payload_result_type, true); if (payload_result_loc == nullptr) { @@ -15113,23 +15131,31 @@ static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, } if (need_store_ptr) { - ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); switch (call_instruction->lval) { case LValNone: + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); return new_call_instruction; case LValPtr: - if (payload_result_loc != nullptr) - return payload_result_loc; - return ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); - case LValErrorUnionVal: - return ir_const(ira, &call_instruction->base, ira->codegen->builtin_types.entry_null); + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); + return payload_result_loc; + case LValErrorUnionVal: { + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); + IrInstruction *err_code_ptr = ir_analyze_error_union_field_error_set(ira, &call_instruction->base, + payload_result_loc, false); + return ir_get_deref(ira, &call_instruction->base, err_code_ptr); + } case LValErrorUnionPtr: { - IrInstruction *null = ir_const(ira, &call_instruction->base, - ira->codegen->builtin_types.entry_null); - return ir_get_ref(ira, &call_instruction->base, null, true, false, nullptr); + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); + return ir_analyze_error_union_field_error_set(ira, &call_instruction->base, + payload_result_loc, false); + } + case LValOptional: { + IrInstruction *ref = ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); + IrInstruction *payload_ptr = ir_analyze_unwrap_optional_payload(ira, &call_instruction->base, ref, false); + IrInstruction *payload = ir_get_deref(ira, &call_instruction->base, payload_ptr); + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, payload); + return ir_analyze_test_non_null(ira, &call_instruction->base, new_call_instruction); } - case LValOptional: - return ir_const_bool(ira, &call_instruction->base, false); } zig_unreachable(); } diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 438774dcf9a8..fb3649735d9c 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -104,11 +104,12 @@ inline fn callMain() u8 { builtin.TypeId.ErrorUnion => { root.main() catch |err| { std.debug.warn("error: {}\n", @errorName(err)); - if (builtin.os != builtin.Os.zen) { - if (@errorReturnTrace()) |trace| { - std.debug.dumpStackTrace(trace); - } - } + // TODO: restore this + //if (builtin.os != builtin.Os.zen) { + // if (@errorReturnTrace()) |trace| { + // std.debug.dumpStackTrace(trace); + // } + //} return 1; }; return 0; diff --git a/std/special/panic.zig b/std/special/panic.zig index bd3ad971e0cb..88a202832b24 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -18,8 +18,11 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn std.os.abort(); }, else => { - const first_trace_addr = @ptrToInt(@returnAddress()); - std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); + // TODO put the original panic code back + std.debug.warn("panic: {}\n", msg); + std.os.abort(); + //const first_trace_addr = @ptrToInt(@returnAddress()); + //std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); }, } } diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig index e4c7754cb935..a3db580e4a22 100644 --- a/test/stage1/behavior/optional.zig +++ b/test/stage1/behavior/optional.zig @@ -60,3 +60,22 @@ test "passing an optional integer as a parameter" { assertOrPanic(S.entry()); comptime assertOrPanic(S.entry()); } + +test "unwrap function call with optional pointer return value" { + const S = struct { + fn entry() void { + assertOrPanic(foo().?.* == 1234); + assertOrPanic(bar() == null); + } + const global: i32 = 1234; + fn foo() ?*const i32 { + return &global; + } + fn bar() ?*i32 { + return null; + } + }; + S.entry(); + // TODO uncomment + //comptime S.entry(); +} From d7a5d09ccfd4e52695a7d30ef873fb3cbf4ba43d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Jan 2019 21:07:21 -0500 Subject: [PATCH 190/190] copy elision: assertOrPanic in behavior tests --- test/stage1/behavior/for.zig | 10 +++--- .../index.zig | 6 ++-- test/stage1/behavior/pointers.zig | 24 ++++++------- test/stage1/behavior/pub_enum/index.zig | 6 ++-- ...ef_var_in_if_after_if_2nd_switch_prong.zig | 10 +++--- test/stage1/behavior/slice.zig | 16 ++++----- .../struct_contains_null_ptr_itself.zig | 4 +-- test/stage1/behavior/truncate.zig | 4 +-- test/stage1/behavior/underscore.zig | 2 +- test/stage1/behavior/var_args.zig | 34 +++++++++---------- 10 files changed, 58 insertions(+), 58 deletions(-) diff --git a/test/stage1/behavior/for.zig b/test/stage1/behavior/for.zig index aecd8b9a0784..b6d1ef24c42f 100644 --- a/test/stage1/behavior/for.zig +++ b/test/stage1/behavior/for.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; test "continue in for loop" { @@ -26,7 +26,7 @@ test "for loop with pointer elem var" { var target: [source.len]u8 = undefined; mem.copy(u8, target[0..], source); mangleString(target[0..]); - assert(mem.eql(u8, target, "bcdefgh")); + assertOrPanic(mem.eql(u8, target, "bcdefgh")); } fn mangleString(s: []u8) void { for (s) |*c| { @@ -68,7 +68,7 @@ test "basic for loop" { buf_index += 1; } - assert(mem.eql(u8, buffer[0..buf_index], expected_result)); + assertOrPanic(mem.eql(u8, buffer[0..buf_index], expected_result)); } test "break from outer for loop" { @@ -85,7 +85,7 @@ fn testBreakOuter() void { break :outer; } } - assert(count == 1); + assertOrPanic(count == 1); } test "continue outer for loop" { @@ -102,5 +102,5 @@ fn testContinueOuter() void { continue :outer; } } - assert(counter == array.len); + assertOrPanic(counter == array.len); } diff --git a/test/stage1/behavior/namespace_depends_on_compile_var/index.zig b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig index ccc49d9367d8..fe3e0cc02093 100644 --- a/test/stage1/behavior/namespace_depends_on_compile_var/index.zig +++ b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig @@ -1,11 +1,11 @@ const builtin = @import("builtin"); -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "namespace depends on compile var" { if (some_namespace.a_bool) { - assert(some_namespace.a_bool); + assertOrPanic(some_namespace.a_bool); } else { - assert(!some_namespace.a_bool); + assertOrPanic(!some_namespace.a_bool); } } const some_namespace = switch (builtin.os) { diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig index 47afb60a2efd..1142d89ab557 100644 --- a/test/stage1/behavior/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "dereference pointer" { comptime testDerefPtr(); @@ -10,33 +10,33 @@ fn testDerefPtr() void { var x: i32 = 1234; var y = &x; y.* += 1; - assert(x == 1235); + assertOrPanic(x == 1235); } test "pointer arithmetic" { var ptr = c"abcd"; - assert(ptr[0] == 'a'); + assertOrPanic(ptr[0] == 'a'); ptr += 1; - assert(ptr[0] == 'b'); + assertOrPanic(ptr[0] == 'b'); ptr += 1; - assert(ptr[0] == 'c'); + assertOrPanic(ptr[0] == 'c'); ptr += 1; - assert(ptr[0] == 'd'); + assertOrPanic(ptr[0] == 'd'); ptr += 1; - assert(ptr[0] == 0); + assertOrPanic(ptr[0] == 0); ptr -= 1; - assert(ptr[0] == 'd'); + assertOrPanic(ptr[0] == 'd'); ptr -= 1; - assert(ptr[0] == 'c'); + assertOrPanic(ptr[0] == 'c'); ptr -= 1; - assert(ptr[0] == 'b'); + assertOrPanic(ptr[0] == 'b'); ptr -= 1; - assert(ptr[0] == 'a'); + assertOrPanic(ptr[0] == 'a'); } test "double pointer parsing" { - comptime assert(PtrOf(PtrOf(i32)) == **i32); + comptime assertOrPanic(PtrOf(PtrOf(i32)) == **i32); } fn PtrOf(comptime T: type) type { diff --git a/test/stage1/behavior/pub_enum/index.zig b/test/stage1/behavior/pub_enum/index.zig index 7fdd07b8a384..181113f6bf19 100644 --- a/test/stage1/behavior/pub_enum/index.zig +++ b/test/stage1/behavior/pub_enum/index.zig @@ -1,13 +1,13 @@ const other = @import("other.zig"); -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "pub enum" { pubEnumTest(other.APubEnum.Two); } fn pubEnumTest(foo: other.APubEnum) void { - assert(foo == other.APubEnum.Two); + assertOrPanic(foo == other.APubEnum.Two); } test "cast with imported symbol" { - assert(other.size_t(42) == 42); + assertOrPanic(other.size_t(42) == 42); } diff --git a/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig index 3c94bb0d49bf..acbe6b245978 100644 --- a/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig +++ b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig @@ -1,14 +1,14 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; var ok: bool = false; test "reference a variable in an if after an if in the 2nd switch prong" { foo(true, Num.Two, false, "aoeu"); - assert(!ok); + assertOrPanic(!ok); foo(false, Num.One, false, "aoeu"); - assert(!ok); + assertOrPanic(!ok); foo(true, Num.One, false, "aoeu"); - assert(ok); + assertOrPanic(ok); } const Num = enum { @@ -32,6 +32,6 @@ fn foo(c: bool, k: Num, c2: bool, b: []const u8) void { } fn a(x: []const u8) void { - assert(mem.eql(u8, x, "aoeu")); + assertOrPanic(mem.eql(u8, x, "aoeu")); ok = true; } diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index b4b43bdd1933..cc29e43485b2 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -1,20 +1,20 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { - assert(@ptrToInt(x.ptr) == 0x1000); - assert(x.len == 0x500); + assertOrPanic(@ptrToInt(x.ptr) == 0x1000); + assertOrPanic(x.len == 0x500); - assert(@ptrToInt(y.ptr) == 0x1100); - assert(y.len == 0x400); + assertOrPanic(@ptrToInt(y.ptr) == 0x1100); + assertOrPanic(y.len == 0x400); } test "slice child property" { var array: [5]i32 = undefined; var slice = array[0..]; - assert(@typeOf(slice).Child == i32); + assertOrPanic(@typeOf(slice).Child == i32); } test "runtime safety lets us slice from len..len" { @@ -23,7 +23,7 @@ test "runtime safety lets us slice from len..len" { 2, 3, }; - assert(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); + assertOrPanic(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); } fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { @@ -36,5 +36,5 @@ test "implicitly cast array of size 0 to slice" { } fn assertLenIsZero(msg: []const u8) void { - assert(msg.len == 0); + assertOrPanic(msg.len == 0); } diff --git a/test/stage1/behavior/struct_contains_null_ptr_itself.zig b/test/stage1/behavior/struct_contains_null_ptr_itself.zig index 21175974b3d8..4cc479f31c4a 100644 --- a/test/stage1/behavior/struct_contains_null_ptr_itself.zig +++ b/test/stage1/behavior/struct_contains_null_ptr_itself.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "struct contains null pointer which contains original struct" { var x: ?*NodeLineComment = null; - assert(x == null); + assertOrPanic(x == null); } pub const Node = struct { diff --git a/test/stage1/behavior/truncate.zig b/test/stage1/behavior/truncate.zig index 02b5085ccd25..b7904bc7fbca 100644 --- a/test/stage1/behavior/truncate.zig +++ b/test/stage1/behavior/truncate.zig @@ -1,8 +1,8 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "truncate u0 to larger integer allowed and has comptime known result" { var x: u0 = 0; const y = @truncate(u8, x); - comptime assert(y == 0); + comptime assertOrPanic(y == 0); } diff --git a/test/stage1/behavior/underscore.zig b/test/stage1/behavior/underscore.zig index da1c97659cf3..7443319336b0 100644 --- a/test/stage1/behavior/underscore.zig +++ b/test/stage1/behavior/underscore.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "ignore lval with underscore" { _ = false; diff --git a/test/stage1/behavior/var_args.zig b/test/stage1/behavior/var_args.zig index 3eb6e304486b..1f782a3bb3a3 100644 --- a/test/stage1/behavior/var_args.zig +++ b/test/stage1/behavior/var_args.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn add(args: ...) i32 { var sum = i32(0); @@ -12,9 +12,9 @@ fn add(args: ...) i32 { } test "add arbitrary args" { - assert(add(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(add(i32(1234)) == 1234); - assert(add() == 0); + assertOrPanic(add(i32(1), i32(2), i32(3), i32(4)) == 10); + assertOrPanic(add(i32(1234)) == 1234); + assertOrPanic(add() == 0); } fn readFirstVarArg(args: ...) void { @@ -26,9 +26,9 @@ test "send void arg to var args" { } test "pass args directly" { - assert(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(addSomeStuff(i32(1234)) == 1234); - assert(addSomeStuff() == 0); + assertOrPanic(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); + assertOrPanic(addSomeStuff(i32(1234)) == 1234); + assertOrPanic(addSomeStuff() == 0); } fn addSomeStuff(args: ...) i32 { @@ -36,24 +36,24 @@ fn addSomeStuff(args: ...) i32 { } test "runtime parameter before var args" { - assert(extraFn(10) == 0); - assert(extraFn(10, false) == 1); - assert(extraFn(10, false, true) == 2); + assertOrPanic(extraFn(10) == 0); + assertOrPanic(extraFn(10, false) == 1); + assertOrPanic(extraFn(10, false, true) == 2); // TODO issue #313 //comptime { - // assert(extraFn(10) == 0); - // assert(extraFn(10, false) == 1); - // assert(extraFn(10, false, true) == 2); + // assertOrPanic(extraFn(10) == 0); + // assertOrPanic(extraFn(10, false) == 1); + // assertOrPanic(extraFn(10, false, true) == 2); //} } fn extraFn(extra: u32, args: ...) usize { if (args.len >= 1) { - assert(args[0] == false); + assertOrPanic(args[0] == false); } if (args.len >= 2) { - assert(args[1] == true); + assertOrPanic(args[1] == true); } return args.len; } @@ -71,8 +71,8 @@ fn foo2(args: ...) bool { } test "array of var args functions" { - assert(foos[0]()); - assert(!foos[1]()); + assertOrPanic(foos[0]()); + assertOrPanic(!foos[1]()); } test "pass zero length array to var args param" {