From 7a99d63c764f3d5d92370c90f932b1bf156269f6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 22 Mar 2018 16:56:03 -0400 Subject: ability to use async function pointers closes #817 --- src/ir.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index db9a2b24c3..8f418e2c2f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2141,12 +2141,14 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc } static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, bool is_var_args) + IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, + IrInstruction *async_allocator_type_value, bool is_var_args) { IrInstructionFnProto *instruction = ir_build_instruction(irb, scope, source_node); instruction->param_types = param_types; instruction->align_value = align_value; instruction->return_type = return_type; + instruction->async_allocator_type_value = async_allocator_type_value; instruction->is_var_args = is_var_args; assert(source_node->type == NodeTypeFnProto); @@ -2156,6 +2158,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block); } if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); + if (async_allocator_type_value != nullptr) ir_ref_instruction(async_allocator_type_value, irb->current_basic_block); ir_ref_instruction(return_type, irb->current_basic_block); return &instruction->base; @@ -5989,7 +5992,15 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo return_type = nullptr; } - return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args); + 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); + if (async_allocator_type_value == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + } + + return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, + async_allocator_type_value, is_var_args); } static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -16561,6 +16572,13 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc if (type_is_invalid(fn_type_id.return_type)) return ira->codegen->builtin_types.entry_invalid; + if (fn_type_id.cc == CallingConventionAsync) { + IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->other; + fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value); + if (type_is_invalid(fn_type_id.async_allocator_type)) + return ira->codegen->builtin_types.entry_invalid; + } + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_fn_type(ira->codegen, &fn_type_id); return ira->codegen->builtin_types.entry_type; -- cgit v1.2.3 From b1c07c0ea9e351a43c9bc2fe747fc07c0a19e005 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 12 Mar 2018 23:03:47 -0400 Subject: move error ret tracing codegen to zig ir progress towards #821 --- src/all_types.hpp | 5 ++++ src/codegen.cpp | 39 +++++++++++-------------- src/ir.cpp | 86 +++++++++++++++++++++++++++++++++++++++---------------- src/ir_print.cpp | 7 +++++ 4 files changed, 91 insertions(+), 46 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index c1dec80d7e..b5428c9599 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2034,6 +2034,7 @@ enum IrInstructionId { IrInstructionIdAtomicRmw, IrInstructionIdPromiseResultType, IrInstructionIdAwaitBookkeeping, + IrInstructionIdSaveErrRetAddr, }; struct IrInstruction { @@ -2988,6 +2989,10 @@ struct IrInstructionAwaitBookkeeping { IrInstruction *promise_result_type; }; +struct IrInstructionSaveErrRetAddr { + IrInstruction base; +}; + 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 de8cfa31ed..eddadb94ea 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1607,32 +1607,25 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { return instruction->llvm_value; } +static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *executable, + IrInstructionSaveErrRetAddr *save_err_ret_addr_instruction) +{ + assert(g->have_err_ret_tracing); + + LLVMValueRef return_err_fn = get_return_err_fn(g); + LLVMValueRef args[] = { + g->cur_err_ret_trace_val, + }; + LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); + LLVMSetTailCall(call_instruction, true); + return call_instruction; +} + static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { LLVMValueRef value = ir_llvm_value(g, return_instruction->value); TypeTableEntry *return_type = return_instruction->value->value.type; - if (g->have_err_ret_tracing) { - bool is_err_return = false; - if (return_type->id == TypeTableEntryIdErrorUnion) { - if (return_instruction->value->value.special == ConstValSpecialStatic) { - is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr; - } else if (return_instruction->value->value.special == ConstValSpecialRuntime) { - is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError; - // TODO: emit a branch to check if the return value is an error - } - } else if (return_type->id == TypeTableEntryIdErrorSet) { - is_err_return = true; - } - if (is_err_return) { - LLVMValueRef return_err_fn = get_return_err_fn(g); - LLVMValueRef args[] = { - g->cur_err_ret_trace_val, - }; - LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); - LLVMSetTailCall(call_instruction, true); - } - } if (handle_is_ptr(return_type)) { if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) { assert(g->cur_ret_ptr); @@ -4400,6 +4393,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction); case IrInstructionIdAtomicRmw: return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction); + case IrInstructionIdSaveErrRetAddr: + return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index 8f418e2c2f..a78be66dd6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -713,6 +713,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitBookkeeping return IrInstructionIdAwaitBookkeeping; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) { + return IrInstructionIdSaveErrRetAddr; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2678,6 +2682,11 @@ static IrInstruction *ir_build_await_bookkeeping(IrBuilder *irb, Scope *scope, A return &instruction->base; } +static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionSaveErrRetAddr *instruction = ir_build_instruction(irb, scope, source_node); + 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; @@ -2750,16 +2759,16 @@ static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { return nullptr; } +static bool exec_is_async(IrExecutable *exec) { + FnTableEntry *fn_entry = exec_fn_entry(exec); + return fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; +} + static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value, bool is_generated_code) { - FnTableEntry *fn_entry = exec_fn_entry(irb->exec); - bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; + bool is_async = exec_is_async(irb->exec); if (!is_async) { - //if (irb->codegen->have_err_ret_tracing) { - // IrInstruction *stack_trace_ptr = ir_build_error_return_trace_nonnull(irb, scope, node); - // ir_build_save_err_ret_addr(irb, scope, node, stack_trace_ptr); - //} IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value); return_inst->is_gen = is_generated_code; return return_inst; @@ -2781,21 +2790,33 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode // the above blocks are rendered by ir_gen after the rest of codegen } -//static void ir_gen_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *node, bool is_async) { -// if (!irb->codegen->have_err_ret_tracing) -// return; -// -// if (is_async) { -// IrInstruction *err_ret_addr_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_err_ret_addr_ptr); -// IrInstruction *return_address_ptr = ir_build_return_address(irb, scope, node); -// IrInstruction *return_address_usize = ir_build_ptr_to_int(irb, scope, node, return_address_ptr); -// ir_build_store_ptr(irb, scope, node, err_ret_addr_ptr, return_address_usize); -// return; -// } -// -// IrInstruction *stack_trace_ptr = ir_build_error_return_trace_nonnull(irb, scope, node); -// ir_build_save_err_ret_addr(irb, scope, node, stack_trace_ptr); -//} +static bool exec_have_err_ret_trace(CodeGen *g, IrExecutable *exec) { + if (!g->have_err_ret_tracing) + return false; + FnTableEntry *fn_entry = exec_fn_entry(exec); + if (fn_entry == nullptr) + return false; + if (exec->is_inline) + return false; + return type_can_fail(fn_entry->type_entry->data.fn.fn_type_id.return_type); +} + +static void ir_gen_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *node) { + if (!exec_have_err_ret_trace(irb->codegen, irb->exec)) + return; + + bool is_async = exec_is_async(irb->exec); + + if (is_async) { + //IrInstruction *err_ret_addr_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_err_ret_addr_ptr); + //IrInstruction *return_address_ptr = ir_build_instr_addr(irb, scope, node); + //IrInstruction *return_address_usize = ir_build_ptr_to_int(irb, scope, node, return_address_ptr); + //ir_build_store_ptr(irb, scope, node, err_ret_addr_ptr, return_address_usize); + return; + } + + ir_build_save_err_ret_addr(irb, scope, node); +} static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeReturnExpr); @@ -2856,7 +2877,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, if (have_err_defers) { ir_gen_defers_for_block(irb, scope, outer_scope, true); } - //ir_gen_save_err_ret_addr(irb, scope, node, is_async); + ir_gen_save_err_ret_addr(irb, scope, node); ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ok_block); @@ -2895,6 +2916,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, ir_set_cursor_at_end_and_append_block(irb, return_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr); + ir_gen_save_err_ret_addr(irb, scope, node); ir_gen_async_return(irb, scope, node, err_val, false); ir_set_cursor_at_end_and_append_block(irb, continue_block); @@ -6406,6 +6428,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec return false; 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); } @@ -11464,13 +11487,17 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi return ira->codegen->builtin_types.entry_void; } +static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) { + FnTableEntry *fn_entry = exec_fn_entry(exec); + return fn_entry != nullptr && fn_entry->calls_or_awaits_errorable_fn && g->have_err_ret_tracing; +} + static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, IrInstructionErrorReturnTrace *instruction) { - FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); - if (fn_entry == nullptr || !fn_entry->calls_or_awaits_errorable_fn || !ira->codegen->have_err_ret_tracing) { + if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_maybe = nullptr; return nullable_type; @@ -17775,6 +17802,14 @@ static TypeTableEntry *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, return out_val->type; } +static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) { + IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope, + instruction->base.source_node); + ir_link_new_instruction(result, &instruction->base); + result->value.type = ira->codegen->builtin_types.entry_void; + return result->value.type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -18012,6 +18047,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction); case IrInstructionIdAwaitBookkeeping: return ir_analyze_instruction_await_bookkeeping(ira, (IrInstructionAwaitBookkeeping *)instruction); + case IrInstructionIdSaveErrRetAddr: + return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); } zig_unreachable(); } @@ -18137,6 +18174,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCoroSave: case IrInstructionIdCoroAllocHelper: case IrInstructionIdAwaitBookkeeping: + case IrInstructionIdSaveErrRetAddr: return true; case IrInstructionIdPhi: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 167bd20839..709cb43942 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1161,6 +1161,10 @@ static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeepi fprintf(irp->f, ")"); } +static void ir_print_save_err_ret_addr(IrPrint *irp, IrInstructionSaveErrRetAddr *instruction) { + fprintf(irp->f, "@saveErrRetAddr()"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1532,6 +1536,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdAwaitBookkeeping: ir_print_await_bookkeeping(irp, (IrInstructionAwaitBookkeeping *)instruction); break; + case IrInstructionIdSaveErrRetAddr: + ir_print_save_err_ret_addr(irp, (IrInstructionSaveErrRetAddr *)instruction); + break; } fprintf(irp->f, "\n"); } -- cgit v1.2.3 From 18af2f9a2764cc340571578d58cb2575faeccdc6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 24 Mar 2018 18:21:51 -0400 Subject: fix async fns with inferred error sets closes #856 --- src/all_types.hpp | 12 +++++++++++- src/analyze.cpp | 10 +++++----- src/ast_render.cpp | 11 ++++++++++- src/codegen.cpp | 1 + src/ir.cpp | 45 ++++++++++++++++++++++++++++++++++++++++----- src/ir_print.cpp | 13 +++++++++++-- test/cases/coroutines.zig | 11 +++++++++++ 7 files changed, 89 insertions(+), 14 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index b5428c9599..f5afecfbdd 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1251,7 +1251,10 @@ struct FnTableEntry { ScopeBlock *def_scope; // parent is child_scope Buf symbol_name; TypeTableEntry *type_entry; // function type - TypeTableEntry *implicit_return_type; + // in the case of normal functions this is the implicit return type + // in the case of async functions this is the implicit return type according to the + // zig source code, not according to zig ir + TypeTableEntry *src_implicit_return_type; bool is_test; FnInline fn_inline; FnAnalState anal_state; @@ -2035,6 +2038,7 @@ enum IrInstructionId { IrInstructionIdPromiseResultType, IrInstructionIdAwaitBookkeeping, IrInstructionIdSaveErrRetAddr, + IrInstructionIdAddImplicitReturnType, }; struct IrInstruction { @@ -2993,6 +2997,12 @@ struct IrInstructionSaveErrRetAddr { IrInstruction base; }; +struct IrInstructionAddImplicitReturnType { + IrInstruction base; + + IrInstruction *value; +}; + 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 eb1850c8e1..5f2162b2cc 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3865,7 +3865,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ TypeTableEntry *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->implicit_return_type = block_return_type; + fn_table_entry->src_implicit_return_type = block_return_type; if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) { assert(g->errors.length > 0); @@ -3877,10 +3877,10 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ TypeTableEntry *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type; if (return_err_set_type->data.error_set.infer_fn != nullptr) { TypeTableEntry *inferred_err_set_type; - if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorSet) { - inferred_err_set_type = fn_table_entry->implicit_return_type; - } else if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorUnion) { - inferred_err_set_type = fn_table_entry->implicit_return_type->data.error_union.err_set_type; + if (fn_table_entry->src_implicit_return_type->id == TypeTableEntryIdErrorSet) { + inferred_err_set_type = fn_table_entry->src_implicit_return_type; + } else if (fn_table_entry->src_implicit_return_type->id == TypeTableEntryIdErrorUnion) { + inferred_err_set_type = fn_table_entry->src_implicit_return_type->data.error_union.err_set_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/ast_render.cpp b/src/ast_render.cpp index f88feee856..432489c4d9 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -658,6 +658,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { if (node->data.fn_call_expr.is_builtin) { fprintf(ar->f, "@"); } + if (node->data.fn_call_expr.is_async) { + fprintf(ar->f, "async"); + if (node->data.fn_call_expr.async_allocator != nullptr) { + fprintf(ar->f, "<"); + render_node_extra(ar, node->data.fn_call_expr.async_allocator, true); + fprintf(ar->f, ">"); + } + fprintf(ar->f, " "); + } AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypeAddrOfExpr); render_node_extra(ar, fn_ref_node, grouped); @@ -1023,7 +1032,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypeUnwrapErrorExpr: { render_node_ungrouped(ar, node->data.unwrap_err_expr.op1); - fprintf(ar->f, " %%%% "); + fprintf(ar->f, " catch "); if (node->data.unwrap_err_expr.symbol) { Buf *var_name = node->data.unwrap_err_expr.symbol->data.symbol_expr.symbol; fprintf(ar->f, "|%s| ", buf_ptr(var_name)); diff --git a/src/codegen.cpp b/src/codegen.cpp index eddadb94ea..d675010531 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4245,6 +4245,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdErrorUnion: case IrInstructionIdPromiseResultType: case IrInstructionIdAwaitBookkeeping: + case IrInstructionIdAddImplicitReturnType: zig_unreachable(); case IrInstructionIdReturn: diff --git a/src/ir.cpp b/src/ir.cpp index a78be66dd6..d896153d0b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -34,7 +34,7 @@ struct IrAnalyze { size_t old_bb_index; size_t instruction_index; TypeTableEntry *explicit_return_type; - ZigList implicit_return_type_list; + ZigList src_implicit_return_type_list; IrBasicBlock *const_predecessor_bb; }; @@ -717,6 +717,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr * return IrInstructionIdSaveErrRetAddr; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitReturnType *) { + return IrInstructionIdAddImplicitReturnType; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2687,6 +2691,17 @@ static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, A return &instruction->base; } +static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *value) +{ + IrInstructionAddImplicitReturnType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value = value; + + ir_ref_instruction(value, 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; @@ -2767,6 +2782,8 @@ 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) { + 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); @@ -6399,6 +6416,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_cond_br(irb, scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false); 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. IrInstruction *undef = ir_build_const_undefined(irb, scope, node); ir_build_return(irb, scope, node, undef); @@ -10108,13 +10127,26 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { return result; } +static TypeTableEntry *ir_analyze_instruction_add_implicit_return_type(IrAnalyze *ira, + IrInstructionAddImplicitReturnType *instruction) +{ + IrInstruction *value = instruction->value->other; + if (type_is_invalid(value->value.type)) + return ir_unreach_error(ira); + + ira->src_implicit_return_type_list.append(value); + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->type = ira->codegen->builtin_types.entry_void; + return out_val->type; +} + static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructionReturn *return_instruction) { IrInstruction *value = return_instruction->value->other; if (type_is_invalid(value->value.type)) return ir_unreach_error(ira); - ira->implicit_return_type_list.append(value); IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type); if (casted_value == ira->codegen->invalid_instruction) @@ -18049,6 +18081,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_await_bookkeeping(ira, (IrInstructionAwaitBookkeeping *)instruction); case IrInstructionIdSaveErrRetAddr: return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); + case IrInstructionIdAddImplicitReturnType: + return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstructionAddImplicitReturnType *)instruction); } zig_unreachable(); } @@ -18122,11 +18156,11 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl if (new_exec->invalid) { return ira->codegen->builtin_types.entry_invalid; - } else if (ira->implicit_return_type_list.length == 0) { + } else if (ira->src_implicit_return_type_list.length == 0) { return codegen->builtin_types.entry_unreachable; } else { - return ir_resolve_peer_types(ira, expected_type_source_node, ira->implicit_return_type_list.items, - ira->implicit_return_type_list.length); + return ir_resolve_peer_types(ira, expected_type_source_node, ira->src_implicit_return_type_list.items, + ira->src_implicit_return_type_list.length); } } @@ -18175,6 +18209,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCoroAllocHelper: case IrInstructionIdAwaitBookkeeping: case IrInstructionIdSaveErrRetAddr: + case IrInstructionIdAddImplicitReturnType: return true; case IrInstructionIdPhi: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 709cb43942..5008d3564d 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -201,9 +201,9 @@ static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) { if (call_instruction->is_async) { fprintf(irp->f, "async"); if (call_instruction->async_allocator != nullptr) { - fprintf(irp->f, "("); + fprintf(irp->f, "<"); ir_print_other_instruction(irp, call_instruction->async_allocator); - fprintf(irp->f, ")"); + fprintf(irp->f, ">"); } fprintf(irp->f, " "); } @@ -1165,6 +1165,12 @@ static void ir_print_save_err_ret_addr(IrPrint *irp, IrInstructionSaveErrRetAddr fprintf(irp->f, "@saveErrRetAddr()"); } +static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImplicitReturnType *instruction) { + fprintf(irp->f, "@addImplicitReturnType("); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1539,6 +1545,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdSaveErrRetAddr: ir_print_save_err_ret_addr(irp, (IrInstructionSaveErrRetAddr *)instruction); break; + case IrInstructionIdAddImplicitReturnType: + ir_print_add_implicit_return_type(irp, (IrInstructionAddImplicitReturnType *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index f8ad58f70d..087d561713 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -176,3 +176,14 @@ async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void { *y += 1; suspend; } + +test "async fn with inferred error set" { + const p = (async failing()) catch unreachable; + resume p; + cancel p; +} + +async fn failing() !void { + suspend; + return error.Fail; +} -- cgit v1.2.3 From 897e783763d60449ad1b9514cb5ba86a38f7ae4a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 24 Mar 2018 19:25:53 -0400 Subject: add promise->T syntax parsing closes #857 --- doc/langref.html.in | 4 +++- src/all_types.hpp | 13 ++++++++++++ src/analyze.cpp | 1 + src/ast_render.cpp | 11 ++++++++++ src/codegen.cpp | 1 + src/ir.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++ src/ir_print.cpp | 11 ++++++++++ src/parser.cpp | 14 +++++++++++- src/tokenizer.cpp | 2 ++ src/tokenizer.hpp | 1 + test/cases/coroutines.zig | 1 + 11 files changed, 111 insertions(+), 2 deletions(-) (limited to 'src/ir.cpp') diff --git a/doc/langref.html.in b/doc/langref.html.in index f4aa42eb89..b2e14ac195 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5863,7 +5863,9 @@ StructLiteralField = "." Symbol "=" Expression PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" -PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl +PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType + +PromiseType = "promise" option("->" TypeExpr) ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr diff --git a/src/all_types.hpp b/src/all_types.hpp index f5afecfbdd..64b9b31662 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -409,6 +409,7 @@ enum NodeType { NodeTypeResume, NodeTypeAwaitExpr, NodeTypeSuspend, + NodeTypePromiseType, }; struct AstNodeRoot { @@ -879,6 +880,10 @@ struct AstNodeSuspend { AstNode *promise_symbol; }; +struct AstNodePromiseType { + AstNode *payload_type; // can be NULL +}; + struct AstNode { enum NodeType type; size_t line; @@ -939,6 +944,7 @@ struct AstNode { AstNodeResumeExpr resume_expr; AstNodeAwaitExpr await_expr; AstNodeSuspend suspend; + AstNodePromiseType promise_type; } data; }; @@ -1947,6 +1953,7 @@ enum IrInstructionId { IrInstructionIdSetRuntimeSafety, IrInstructionIdSetFloatMode, IrInstructionIdArrayType, + IrInstructionIdPromiseType, IrInstructionIdSliceType, IrInstructionIdAsm, IrInstructionIdSizeOf, @@ -2365,6 +2372,12 @@ struct IrInstructionArrayType { IrInstruction *child_type; }; +struct IrInstructionPromiseType { + IrInstruction base; + + IrInstruction *payload_type; +}; + struct IrInstructionSliceType { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 5f2162b2cc..0f4728f822 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3254,6 +3254,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeResume: case NodeTypeAwaitExpr: case NodeTypeSuspend: + case NodeTypePromiseType: zig_unreachable(); } } diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 432489c4d9..7b5fc03ea8 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -250,6 +250,8 @@ static const char *node_type_str(NodeType node_type) { return "AwaitExpr"; case NodeTypeSuspend: return "Suspend"; + case NodeTypePromiseType: + return "PromiseType"; } zig_unreachable(); } @@ -781,6 +783,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { render_node_ungrouped(ar, node->data.array_type.child_type); break; } + case NodeTypePromiseType: + { + fprintf(ar->f, "promise"); + if (node->data.promise_type.payload_type != nullptr) { + fprintf(ar->f, "->"); + render_node_grouped(ar, node->data.promise_type.payload_type); + } + break; + } case NodeTypeErrorType: fprintf(ar->f, "error"); break; diff --git a/src/codegen.cpp b/src/codegen.cpp index d675010531..786bd03985 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4205,6 +4205,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdSetRuntimeSafety: case IrInstructionIdSetFloatMode: case IrInstructionIdArrayType: + case IrInstructionIdPromiseType: case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdSwitchTarget: diff --git a/src/ir.cpp b/src/ir.cpp index d896153d0b..95142c88de 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -349,6 +349,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) { return IrInstructionIdArrayType; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseType *) { + return IrInstructionIdPromiseType; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) { return IrInstructionIdSliceType; } @@ -1469,6 +1473,17 @@ static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_promise_type(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *payload_type) +{ + IrInstructionPromiseType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->payload_type = payload_type; + + if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value) { @@ -5074,6 +5089,22 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n } } +static IrInstruction *ir_gen_promise_type(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypePromiseType); + + AstNode *payload_type_node = node->data.promise_type.payload_type; + IrInstruction *payload_type_value = nullptr; + + if (payload_type_node != nullptr) { + payload_type_value = ir_gen_node(irb, payload_type_node, scope); + if (payload_type_value == irb->codegen->invalid_instruction) + return payload_type_value; + + } + + return ir_build_promise_type(irb, scope, node, payload_type_value); +} + static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeUndefinedLiteral); return ir_build_const_undefined(irb, scope, node); @@ -6282,6 +6313,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); case NodeTypeArrayType: return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval); + case NodeTypePromiseType: + return ir_lval_wrap(irb, scope, ir_gen_promise_type(irb, scope, node), lval); case NodeTypeStringLiteral: return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval); case NodeTypeUndefinedLiteral: @@ -14069,6 +14102,24 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, zig_unreachable(); } +static TypeTableEntry *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrInstructionPromiseType *instruction) { + TypeTableEntry *promise_type; + + if (instruction->payload_type == nullptr) { + promise_type = ira->codegen->builtin_types.entry_promise; + } else { + TypeTableEntry *payload_type = ir_resolve_type(ira, instruction->payload_type->other); + if (type_is_invalid(payload_type)) + return ira->codegen->builtin_types.entry_invalid; + + promise_type = get_promise_type(ira->codegen, payload_type); + } + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = promise_type; + return ira->codegen->builtin_types.entry_type; +} + static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructionSizeOf *size_of_instruction) { @@ -17907,6 +17958,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction); case IrInstructionIdArrayType: return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction); + case IrInstructionIdPromiseType: + return ir_analyze_instruction_promise_type(ira, (IrInstructionPromiseType *)instruction); case IrInstructionIdSizeOf: return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); case IrInstructionIdTestNonNull: @@ -18232,6 +18285,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdStructFieldPtr: case IrInstructionIdUnionFieldPtr: case IrInstructionIdArrayType: + case IrInstructionIdPromiseType: case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdTestNonNull: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 5008d3564d..b14d49a4ca 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -404,6 +404,14 @@ static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instructio ir_print_other_instruction(irp, instruction->child_type); } +static void ir_print_promise_type(IrPrint *irp, IrInstructionPromiseType *instruction) { + fprintf(irp->f, "promise"); + if (instruction->payload_type != nullptr) { + fprintf(irp->f, "->"); + ir_print_other_instruction(irp, instruction->payload_type); + } +} + static void ir_print_slice_type(IrPrint *irp, IrInstructionSliceType *instruction) { const char *const_kw = instruction->is_const ? "const " : ""; fprintf(irp->f, "[]%s", const_kw); @@ -1263,6 +1271,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdArrayType: ir_print_array_type(irp, (IrInstructionArrayType *)instruction); break; + case IrInstructionIdPromiseType: + ir_print_promise_type(irp, (IrInstructionPromiseType *)instruction); + break; case IrInstructionIdSliceType: ir_print_slice_type(irp, (IrInstructionSliceType *)instruction); break; diff --git a/src/parser.cpp b/src/parser.cpp index 666b9da3c3..d6faf4c984 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -705,7 +705,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b } /* -PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl +PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" | "suspend" ErrorSetDecl = "error" "{" list(Symbol, ",") "}" */ @@ -774,6 +774,15 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo AstNode *node = ast_create_node(pc, NodeTypeSuspend, token); *token_index += 1; return node; + } else if (token->id == TokenIdKeywordPromise) { + AstNode *node = ast_create_node(pc, NodeTypePromiseType, token); + *token_index += 1; + Token *arrow_tok = &pc->tokens->at(*token_index); + if (arrow_tok->id == TokenIdArrow) { + *token_index += 1; + node->data.promise_type.payload_type = ast_parse_type_expr(pc, token_index, true); + } + return node; } else if (token->id == TokenIdKeywordError) { Token *next_token = &pc->tokens->at(*token_index + 1); if (next_token->id == TokenIdLBrace) { @@ -3081,6 +3090,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.array_type.child_type, visit, context); visit_field(&node->data.array_type.align_expr, visit, context); break; + case NodeTypePromiseType: + visit_field(&node->data.promise_type.payload_type, visit, context); + break; case NodeTypeErrorType: // none break; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index dc17829c0f..365b35cdfd 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -135,6 +135,7 @@ static const struct ZigKeyword zig_keywords[] = { {"null", TokenIdKeywordNull}, {"or", TokenIdKeywordOr}, {"packed", TokenIdKeywordPacked}, + {"promise", TokenIdKeywordPromise}, {"pub", TokenIdKeywordPub}, {"resume", TokenIdKeywordResume}, {"return", TokenIdKeywordReturn}, @@ -1558,6 +1559,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordNull: return "null"; case TokenIdKeywordOr: return "or"; case TokenIdKeywordPacked: return "packed"; + case TokenIdKeywordPromise: return "promise"; case TokenIdKeywordPub: return "pub"; case TokenIdKeywordReturn: return "return"; case TokenIdKeywordSection: return "section"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 2d71427997..b719293704 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -76,6 +76,7 @@ enum TokenId { TokenIdKeywordNull, TokenIdKeywordOr, TokenIdKeywordPacked, + TokenIdKeywordPromise, TokenIdKeywordPub, TokenIdKeywordResume, TokenIdKeywordReturn, diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 087d561713..25a75dca5c 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -5,6 +5,7 @@ var x: i32 = 1; test "create a coroutine and cancel it" { const p = try async simpleAsyncFn(); + comptime assert(@typeOf(p) == promise->void); cancel p; assert(x == 2); } -- cgit v1.2.3 From aa2995ee395b2c1329a61513debcac6225fcb8a8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 24 Mar 2018 22:05:29 -0400 Subject: fix invalid codegen for error return traces across suspend points See #821 Now the code works correctly, but error return traces are missing the frames from coroutines. --- src/all_types.hpp | 10 ++++++- src/analyze.cpp | 7 +++++ src/analyze.hpp | 1 + src/codegen.cpp | 65 +++++++++++++++++++++++++++++++++++----------- src/ir.cpp | 69 +++++++++++++++++++++++++------------------------ test/runtime_safety.zig | 30 +++++++++++++++++++++ 6 files changed, 132 insertions(+), 50 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index 64b9b31662..6951230aa4 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1621,7 +1621,8 @@ struct CodeGen { FnTableEntry *panic_fn; LLVMValueRef cur_ret_ptr; LLVMValueRef cur_fn_val; - LLVMValueRef cur_err_ret_trace_val; + LLVMValueRef cur_err_ret_trace_val_arg; + LLVMValueRef cur_err_ret_trace_val_stack; bool c_want_stdint; bool c_want_stdbool; AstNode *root_export_decl; @@ -1760,6 +1761,7 @@ enum ScopeId { ScopeIdLoop, ScopeIdFnDef, ScopeIdCompTime, + ScopeIdCoroPrelude, }; struct Scope { @@ -1867,6 +1869,12 @@ struct ScopeFnDef { FnTableEntry *fn_entry; }; +// This scope is created to indicate that the code in the scope +// is auto-generated coroutine prelude stuff. +struct ScopeCoroPrelude { + Scope base; +}; + // synchronized with code in define_builtin_compile_vars enum AtomicOrder { AtomicOrderUnordered, diff --git a/src/analyze.cpp b/src/analyze.cpp index 0f4728f822..7ee1de78a2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -170,6 +170,12 @@ Scope *create_comptime_scope(AstNode *node, Scope *parent) { return &scope->base; } +Scope *create_coro_prelude_scope(AstNode *node, Scope *parent) { + ScopeCoroPrelude *scope = allocate(1); + init_scope(&scope->base, ScopeIdCoroPrelude, node, parent); + return &scope->base; +} + ImportTableEntry *get_scope_import(Scope *scope) { while (scope) { if (scope->id == ScopeIdDecls) { @@ -3592,6 +3598,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) { case ScopeIdCImport: case ScopeIdLoop: case ScopeIdCompTime: + case ScopeIdCoroPrelude: scope = scope->parent; continue; case ScopeIdFnDef: diff --git a/src/analyze.hpp b/src/analyze.hpp index 936134030d..aa4557666b 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -107,6 +107,7 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent); ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry); ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import); Scope *create_comptime_scope(AstNode *node, Scope *parent); +Scope *create_coro_prelude_scope(AstNode *node, Scope *parent); void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str); ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str); diff --git a/src/codegen.cpp b/src/codegen.cpp index 786bd03985..25b2ffbf16 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -653,6 +653,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { case ScopeIdDeferExpr: case ScopeIdLoop: case ScopeIdCompTime: + case ScopeIdCoroPrelude: return get_di_scope(g, scope->parent); } zig_unreachable(); @@ -1318,9 +1319,34 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { return fn_val; } -static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) { +static bool is_coro_prelude_scope(Scope *scope) { + while (scope != nullptr) { + if (scope->id == ScopeIdCoroPrelude) { + return true; + } else if (scope->id == ScopeIdFnDef) { + break; + } + scope = scope->parent; + } + return false; +} + +static LLVMValueRef get_cur_err_ret_trace_val(CodeGen *g, Scope *scope) { + if (!g->have_err_ret_tracing) { + return nullptr; + } + if (g->cur_fn->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) { + return is_coro_prelude_scope(scope) ? g->cur_err_ret_trace_val_arg : g->cur_err_ret_trace_val_stack; + } + if (g->cur_err_ret_trace_val_stack != nullptr) { + return g->cur_err_ret_trace_val_stack; + } + return g->cur_err_ret_trace_val_arg; +} + +static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val, Scope *scope) { LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g); - LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val; + LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, scope); if (err_ret_trace_val == nullptr) { TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref); @@ -1614,7 +1640,7 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut LLVMValueRef return_err_fn = get_return_err_fn(g); LLVMValueRef args[] = { - g->cur_err_ret_trace_val, + get_cur_err_ret_trace_val(g, save_err_ret_addr_instruction->base.scope), }; LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); @@ -2725,7 +2751,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr gen_param_index += 1; } if (prefix_arg_err_ret_stack) { - gen_param_values[gen_param_index] = g->cur_err_ret_trace_val; + gen_param_values[gen_param_index] = get_cur_err_ret_trace_val(g, instruction->base.scope); gen_param_index += 1; } if (instruction->is_async) { @@ -3292,11 +3318,12 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable, IrInstructionErrorReturnTrace *instruction) { - if (g->cur_err_ret_trace_val == nullptr) { + LLVMValueRef cur_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope); + if (cur_err_ret_trace_val == nullptr) { TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); return LLVMConstNull(ptr_to_stack_trace_type->type_ref); } - return g->cur_err_ret_trace_val; + return cur_err_ret_trace_val; } static LLVMValueRef ir_render_cancel(CodeGen *g, IrExecutable *executable, IrInstructionCancel *instruction) { @@ -3726,7 +3753,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); LLVMPositionBuilderAtEnd(g->builder, err_block); - gen_safety_crash_for_err(g, err_val); + gen_safety_crash_for_err(g, err_val, instruction->base.scope); LLVMPositionBuilderAtEnd(g->builder, ok_block); } @@ -3918,7 +3945,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec } static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) { - gen_panic(g, ir_llvm_value(g, instruction->msg), g->cur_err_ret_trace_val); + gen_panic(g, ir_llvm_value(g, instruction->msg), get_cur_err_ret_trace_val(g, instruction->base.scope)); return nullptr; } @@ -5279,9 +5306,17 @@ static void do_code_gen(CodeGen *g) { clear_debug_source_node(g); uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); - if (err_ret_trace_arg_index != UINT32_MAX) { - g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index); - } else if (g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn) { + bool have_err_ret_trace_arg = err_ret_trace_arg_index != UINT32_MAX; + if (have_err_ret_trace_arg) { + g->cur_err_ret_trace_val_arg = LLVMGetParam(fn, err_ret_trace_arg_index); + } else { + g->cur_err_ret_trace_val_arg = nullptr; + } + + bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; + bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && + (is_async || !have_err_ret_trace_arg); + if (have_err_ret_trace_stack) { // TODO call graph analysis to find out what this number needs to be for every function static const size_t stack_trace_ptr_count = 30; @@ -5289,13 +5324,13 @@ static void do_code_gen(CodeGen *g) { TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count); LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", get_abi_alignment(g, array_type)); - g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type)); + g->cur_err_ret_trace_val_stack = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type)); size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; - LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, ""); + LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)index_field_index, ""); gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false); size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; - LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, ""); + LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)addresses_field_index, ""); TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry; size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; @@ -5311,7 +5346,7 @@ static void do_code_gen(CodeGen *g) { LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false)); } else { - g->cur_err_ret_trace_val = nullptr; + g->cur_err_ret_trace_val_stack = nullptr; } // allocate temporary stack data diff --git a/src/ir.cpp b/src/ir.cpp index 95142c88de..d86b7f5b21 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6412,60 +6412,61 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec VariableTableEntry *coro_size_var; if (is_async) { // create the coro promise - const_bool_false = ir_build_const_bool(irb, scope, node, false); - VariableTableEntry *promise_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); + Scope *coro_scope = create_coro_prelude_scope(node, scope); + const_bool_false = ir_build_const_bool(irb, coro_scope, node, false); + VariableTableEntry *promise_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; - IrInstruction *promise_init = ir_build_const_promise_init(irb, scope, node, return_type); - ir_build_var_decl(irb, scope, node, promise_var, nullptr, nullptr, promise_init); - IrInstruction *coro_promise_ptr = ir_build_var_ptr(irb, scope, node, promise_var, false, false); + IrInstruction *promise_init = ir_build_const_promise_init(irb, coro_scope, node, return_type); + ir_build_var_decl(irb, coro_scope, node, promise_var, nullptr, nullptr, promise_init); + IrInstruction *coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var, false, false); - VariableTableEntry *await_handle_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); - IrInstruction *null_value = ir_build_const_null(irb, scope, node); - IrInstruction *await_handle_type_val = ir_build_const_type(irb, scope, node, + VariableTableEntry *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_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - ir_build_var_decl(irb, scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); - irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, scope, node, + 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, false, false); - u8_ptr_type = ir_build_const_type(irb, scope, node, + 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, scope, node, u8_ptr_type, coro_promise_ptr); - coro_id = ir_build_coro_id(irb, scope, node, promise_as_u8_ptr); - coro_size_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); - IrInstruction *coro_size = ir_build_coro_size(irb, scope, node); - ir_build_var_decl(irb, scope, node, coro_size_var, nullptr, nullptr, coro_size); - IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, + IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(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); + ir_build_var_decl(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); + IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node, ImplicitAllocatorIdArg); - irb->exec->coro_allocator_var = ir_create_var(irb, node, scope, nullptr, true, true, true, const_bool_false); - ir_build_var_decl(irb, scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); + 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); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); - IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, alloc_field_name); - IrInstruction *alloc_fn = ir_build_load_ptr(irb, scope, node, alloc_fn_ptr); - IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, scope, node, alloc_fn, coro_size); - IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, scope, node, maybe_coro_mem_ptr); - IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, scope, "AllocError"); - IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, scope, "AllocOk"); - ir_build_cond_br(irb, scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false); + 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 *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"); + 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_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. - IrInstruction *undef = ir_build_const_undefined(irb, scope, node); - ir_build_return(irb, scope, node, undef); + IrInstruction *undef = ir_build_const_undefined(irb, coro_scope, node); + 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, scope, node, u8_ptr_type, maybe_coro_mem_ptr); - irb->exec->coro_handle = ir_build_coro_begin(irb, scope, node, coro_id, coro_mem_ptr); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast(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 *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME); - irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, awaiter_handle_field_name); 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, coro_scope, node, coro_promise_ptr, 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); - ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr); + irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, result_ptr_field_name); + ir_build_store_ptr(irb, coro_scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr); irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal"); diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 8b8f612056..1fea6347ab 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -281,4 +281,34 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ f.float = 12.34; \\} ); + + // This case makes sure that the code compiles and runs. There is not actually a special + // runtime safety check having to do specifically with error return traces across suspend points. + cases.addRuntimeSafety("error return trace across suspend points", + \\const std = @import("std"); + \\ + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\ std.os.exit(126); + \\} + \\ + \\pub fn main() void { + \\ const p = nonFailing(); + \\ resume p; + \\ const p2 = async printTrace(p) catch unreachable; + \\ cancel p2; + \\} + \\ + \\fn nonFailing() promise->error!void { + \\ return async failing() catch unreachable; + \\} + \\ + \\async fn failing() error!void { + \\ suspend; + \\ return error.Fail; + \\} + \\ + \\async fn printTrace(p: promise->error!void) void { + \\ (await p) catch unreachable; + \\} + ); } -- cgit v1.2.3 From 6cb99fdac3fb02d7e4e5a363295bae0dd3641a45 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 27 Mar 2018 15:07:45 -0400 Subject: fix crash when compile error in analyzing @panic call --- src/ir.cpp | 6 +++--- test/compile_errors.zig | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index d86b7f5b21..ea001adb93 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16918,18 +16918,18 @@ static TypeTableEntry *ir_analyze_instruction_can_implicit_cast(IrAnalyze *ira, static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) { IrInstruction *msg = instruction->msg->other; if (type_is_invalid(msg->value.type)) - return ira->codegen->builtin_types.entry_invalid; + return ir_unreach_error(ira); if (ir_should_inline(ira->new_irb.exec, instruction->base.scope)) { ir_add_error(ira, &instruction->base, buf_sprintf("encountered @panic at compile-time")); - return ira->codegen->builtin_types.entry_invalid; + return ir_unreach_error(ira); } TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type); IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type); if (type_is_invalid(casted_msg->value.type)) - return ira->codegen->builtin_types.entry_invalid; + return ir_unreach_error(ira); IrInstruction *new_instruction = ir_build_panic(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_msg); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ddf5286335..bed5aa1b63 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("wrong type passed to @panic", + \\export fn entry() void { + \\ var e = error.Foo; + \\ @panic(e); + \\} + , + ".tmp_source.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'"); + + cases.add("@tagName used on union with no associated enum tag", \\const FloatInt = extern union { \\ Float: f32, -- cgit v1.2.3 From 032fccf6151ff201ce4b8c7ab28ca460fed794c0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 28 Mar 2018 23:25:12 -0400 Subject: fix compile time array concatenation for slices closes #866 --- src/ir.cpp | 18 ++++++++++++++++++ test/cases/eval.zig | 12 +++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index ea001adb93..18fd02c297 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11058,6 +11058,24 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * result_type = get_array_type(ira->codegen, child_type, new_len); out_array_val = out_val; + } else if (is_slice(op1_type) || is_slice(op2_type)) { + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, child_type, true); + result_type = get_slice_type(ira->codegen, ptr_type); + out_array_val = create_const_vals(1); + out_array_val->special = ConstValSpecialStatic; + out_array_val->type = get_array_type(ira->codegen, child_type, new_len); + + out_val->data.x_struct.fields = create_const_vals(2); + + out_val->data.x_struct.fields[slice_ptr_index].type = ptr_type; + out_val->data.x_struct.fields[slice_ptr_index].special = ConstValSpecialStatic; + out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.special = ConstPtrSpecialBaseArray; + out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.array_val = out_array_val; + out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.elem_index = 0; + + out_val->data.x_struct.fields[slice_len_index].type = ira->codegen->builtin_types.entry_usize; + out_val->data.x_struct.fields[slice_len_index].special = ConstValSpecialStatic; + bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index].data.x_bigint, new_len); } else { new_len += 1; // null byte diff --git a/test/cases/eval.zig b/test/cases/eval.zig index a5b41275bb..d6f7afe864 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -1,4 +1,5 @@ -const assert = @import("std").debug.assert; +const std = @import("std"); +const assert = std.debug.assert; const builtin = @import("builtin"); test "compile time recursion" { @@ -503,3 +504,12 @@ test "const ptr to comptime mutable data is not memoized" { assert(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; + assert(std.mem.eql(u8, c, "aoeuasdf")); + } +} -- cgit v1.2.3 From 2e5115b0687ee9b7078dbf70da7d7070a7c80399 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 31 Mar 2018 19:04:01 +1300 Subject: Add run compiler command 'zig run file.zig' builds a file and stores the artifacts in the global cache. On successful compilation the binary is executed. 'zig run file.zig -- a b c' does the same, but passes the arguments a, b and c as runtime arguments to the program. Everything after an '--' are treated as runtime arguments. On a posix system, a shebang can be used to run a zig file directly. An example shebang would be '#!/usr/bin/zig run'. You may not be able pass extra compile arguments currently as part of the shebang. Linux for example treats all arguments after the first as a single argument which will result in an 'invalid command'. Currently there is no customisability for the cache path as a compile argument. For a posix system you can use `TMPDIR=. zig run file.zig` to override, in this case using the current directory for the run cache. The input file is always recompiled, even if it has changed. This is intended to be cached but further discussion/thought needs to go into this. Closes #466. --- src/codegen.cpp | 6 ++--- src/ir.cpp | 4 +-- src/main.cpp | 58 ++++++++++++++++++++++++++++++++++++++------ src/os.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/os.hpp | 6 +++-- 5 files changed, 129 insertions(+), 20 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index 0ebdf7fa3d..bdd28b86fd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6286,7 +6286,7 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err)); } Buf *import_code = buf_alloc(); - if ((err = os_fetch_file_path(abs_full_path, import_code))) { + if ((err = os_fetch_file_path(abs_full_path, import_code, false))) { zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err)); } @@ -6374,7 +6374,7 @@ static void gen_root_source(CodeGen *g) { } Buf *source_code = buf_alloc(); - if ((err = os_fetch_file_path(rel_full_path, source_code))) { + if ((err = os_fetch_file_path(rel_full_path, source_code, true))) { zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); } @@ -6439,7 +6439,7 @@ static void gen_global_asm(CodeGen *g) { int err; for (size_t i = 0; i < g->assembly_files.length; i += 1) { Buf *asm_file = g->assembly_files.at(i); - if ((err = os_fetch_file_path(asm_file, &contents))) { + if ((err = os_fetch_file_path(asm_file, &contents, false))) { zig_panic("Unable to read %s: %s", buf_ptr(asm_file), err_str(err)); } buf_append_buf(&g->global_asm, &contents); diff --git a/src/ir.cpp b/src/ir.cpp index 18fd02c297..4fe6769f78 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14709,7 +14709,7 @@ static TypeTableEntry *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructi return ira->codegen->builtin_types.entry_namespace; } - if ((err = os_fetch_file_path(abs_full_path, import_code))) { + if ((err = os_fetch_file_path(abs_full_path, import_code, true))) { if (err == ErrorFileNotFound) { ir_add_error_node(ira, source_node, buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); @@ -15570,7 +15570,7 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr // load from file system into const expr Buf *file_contents = buf_alloc(); int err; - if ((err = os_fetch_file_path(&file_path, file_contents))) { + if ((err = os_fetch_file_path(&file_path, file_contents, false))) { if (err == ErrorFileNotFound) { ir_add_error(ira, instruction->name, buf_sprintf("unable to find '%s'", buf_ptr(&file_path))); return ira->codegen->builtin_types.entry_invalid; diff --git a/src/main.cpp b/src/main.cpp index f2f4cc970e..63b077e833 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ static int usage(const char *arg0) { " build-exe [source] create executable from source or object files\n" " build-lib [source] create library from source or object files\n" " build-obj [source] create object from source or assembly\n" + " run [source] create executable and run immediately\n" " translate-c [source] convert c code to zig code\n" " targets list available compilation targets\n" " test [source] create and run a test build\n" @@ -220,6 +221,7 @@ static Buf *resolve_zig_lib_dir(const char *zig_install_prefix_arg) { enum Cmd { CmdInvalid, CmdBuild, + CmdRun, CmdTest, CmdVersion, CmdZen, @@ -329,6 +331,8 @@ int main(int argc, char **argv) { CliPkg *cur_pkg = allocate(1); BuildMode build_mode = BuildModeDebug; ZigList test_exec_args = {0}; + int comptime_args_end = 0; + int runtime_args_start = argc; if (argc >= 2 && strcmp(argv[1], "build") == 0) { const char *zig_exe_path = arg0; @@ -481,11 +485,15 @@ int main(int argc, char **argv) { return (term.how == TerminationIdClean) ? term.code : -1; } - for (int i = 1; i < argc; i += 1) { + for (int i = 1; i < argc; i += 1, comptime_args_end += 1) { char *arg = argv[i]; if (arg[0] == '-') { - if (strcmp(arg, "--release-fast") == 0) { + if (strcmp(arg, "--") == 0) { + // ignore -- from both compile and runtime arg sets + runtime_args_start = i + 1; + break; + } else if (strcmp(arg, "--release-fast") == 0) { build_mode = BuildModeFastRelease; } else if (strcmp(arg, "--release-safe") == 0) { build_mode = BuildModeSafeRelease; @@ -652,6 +660,9 @@ int main(int argc, char **argv) { } else if (strcmp(arg, "build-lib") == 0) { cmd = CmdBuild; out_type = OutTypeLib; + } else if (strcmp(arg, "run") == 0) { + cmd = CmdRun; + out_type = OutTypeExe; } else if (strcmp(arg, "version") == 0) { cmd = CmdVersion; } else if (strcmp(arg, "zen") == 0) { @@ -670,6 +681,7 @@ int main(int argc, char **argv) { } else { switch (cmd) { case CmdBuild: + case CmdRun: case CmdTranslateC: case CmdTest: if (!in_file) { @@ -724,8 +736,8 @@ int main(int argc, char **argv) { } } - switch (cmd) { + case CmdRun: case CmdBuild: case CmdTranslateC: case CmdTest: @@ -733,7 +745,7 @@ int main(int argc, char **argv) { if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0) { fprintf(stderr, "Expected source file argument or at least one --object or --assembly argument.\n"); return usage(arg0); - } else if ((cmd == CmdTranslateC || cmd == CmdTest) && !in_file) { + } else if ((cmd == CmdTranslateC || cmd == CmdTest || cmd == CmdRun) && !in_file) { fprintf(stderr, "Expected source file argument.\n"); return usage(arg0); } else if (cmd == CmdBuild && out_type == OutTypeObj && objects.length != 0) { @@ -745,6 +757,10 @@ int main(int argc, char **argv) { bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC); + if (cmd == CmdRun) { + out_name = "run"; + } + Buf *in_file_buf = nullptr; Buf *buf_out_name = (cmd == CmdTest) ? buf_create_from_str("test") : @@ -769,9 +785,23 @@ int main(int argc, char **argv) { Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf; Buf *full_cache_dir = buf_alloc(); - os_path_resolve(buf_create_from_str("."), - buf_create_from_str((cache_dir == nullptr) ? default_zig_cache_name : cache_dir), - full_cache_dir); + Buf *run_exec_path = buf_alloc(); + if (cmd == CmdRun) { + if (buf_out_name == nullptr) { + buf_out_name = buf_create_from_str("run"); + } + + Buf *global_cache_dir = buf_alloc(); + os_get_global_cache_directory(global_cache_dir); + os_path_join(global_cache_dir, buf_out_name, run_exec_path); + os_path_resolve(buf_create_from_str("."), global_cache_dir, full_cache_dir); + + out_file = buf_ptr(run_exec_path); + } else { + os_path_resolve(buf_create_from_str("."), + buf_create_from_str((cache_dir == nullptr) ? default_zig_cache_name : cache_dir), + full_cache_dir); + } Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix); @@ -855,7 +885,7 @@ int main(int argc, char **argv) { add_package(g, cur_pkg, g->root_package); - if (cmd == CmdBuild) { + if (cmd == CmdBuild || cmd == CmdRun) { codegen_set_emit_file_type(g, emit_file_type); for (size_t i = 0; i < objects.length; i += 1) { @@ -868,6 +898,18 @@ int main(int argc, char **argv) { codegen_link(g, out_file); if (timing_info) codegen_print_timing_report(g, stdout); + + if (cmd == CmdRun) { + ZigList args = {0}; + for (int i = runtime_args_start; i < argc; ++i) { + args.append(argv[i]); + } + + Termination term; + os_spawn_process(buf_ptr(run_exec_path), args, &term); + return term.code; + } + return EXIT_SUCCESS; } else if (cmd == CmdTranslateC) { codegen_translate_c(g, in_file_buf); diff --git a/src/os.cpp b/src/os.cpp index de1a1f21d2..e0491b21de 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -288,13 +288,39 @@ void os_path_resolve(Buf *ref_path, Buf *target_path, Buf *out_abs_path) { return; } -int os_fetch_file(FILE *f, Buf *out_buf) { +int os_fetch_file(FILE *f, Buf *out_buf, bool skip_shebang) { static const ssize_t buf_size = 0x2000; buf_resize(out_buf, buf_size); ssize_t actual_buf_len = 0; + + bool first_read = true; + for (;;) { size_t amt_read = fread(buf_ptr(out_buf) + actual_buf_len, 1, buf_size, f); actual_buf_len += amt_read; + + if (skip_shebang && first_read && buf_starts_with_str(out_buf, "#!")) { + size_t i = 0; + while (true) { + if (i > buf_len(out_buf)) { + zig_panic("shebang line exceeded %zd characters", buf_size); + } + + size_t current_pos = i; + i += 1; + + if (out_buf->list.at(current_pos) == '\n') { + break; + } + } + + ZigList *list = &out_buf->list; + memmove(list->items, list->items + i, list->length - i); + list->length -= i; + + actual_buf_len -= i; + } + if (amt_read != buf_size) { if (feof(f)) { buf_resize(out_buf, actual_buf_len); @@ -305,6 +331,7 @@ int os_fetch_file(FILE *f, Buf *out_buf) { } buf_resize(out_buf, actual_buf_len + buf_size); + first_read = false; } zig_unreachable(); } @@ -374,8 +401,8 @@ static int os_exec_process_posix(const char *exe, ZigList &args, FILE *stdout_f = fdopen(stdout_pipe[0], "rb"); FILE *stderr_f = fdopen(stderr_pipe[0], "rb"); - os_fetch_file(stdout_f, out_stdout); - os_fetch_file(stderr_f, out_stderr); + os_fetch_file(stdout_f, out_stdout, false); + os_fetch_file(stderr_f, out_stderr, false); fclose(stdout_f); fclose(stderr_f); @@ -588,7 +615,7 @@ int os_copy_file(Buf *src_path, Buf *dest_path) { } } -int os_fetch_file_path(Buf *full_path, Buf *out_contents) { +int os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang) { FILE *f = fopen(buf_ptr(full_path), "rb"); if (!f) { switch (errno) { @@ -607,7 +634,7 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents) { return ErrorFileSystem; } } - int result = os_fetch_file(f, out_contents); + int result = os_fetch_file(f, out_contents, skip_shebang); fclose(f); return result; } @@ -780,6 +807,44 @@ int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path) { #endif } +#if defined(ZIG_OS_POSIX) +int os_get_global_cache_directory(Buf *out_tmp_path) { + const char *tmp_dir = getenv("TMPDIR"); + if (!tmp_dir) { + tmp_dir = P_tmpdir; + } + + Buf *tmp_dir_buf = buf_create_from_str(tmp_dir); + Buf *cache_dirname_buf = buf_create_from_str("zig-cache"); + + buf_resize(out_tmp_path, 0); + os_path_join(tmp_dir_buf, cache_dirname_buf, out_tmp_path); + + buf_deinit(tmp_dir_buf); + buf_deinit(cache_dirname_buf); + return 0; +} +#endif + +#if defined(ZIG_OS_WINDOWS) +int os_get_global_cache_directory(Buf *out_tmp_path) { + char tmp_dir[MAX_PATH + 1]; + if (GetTempPath(MAX_PATH, tmp_dir) == 0) { + zig_panic("GetTempPath failed"); + } + + Buf *tmp_dir_buf = buf_create_from_str(tmp_dir); + Buf *cache_dirname_buf = buf_create_from_str("zig-cache"); + + buf_resize(out_tmp_path, 0); + os_path_join(tmp_dir_buf, cache_dirname_buf, out_tmp_path); + + buf_deinit(tmp_dir_buf); + buf_deinit(cache_dirname_buf); + return 0; +} +#endif + int os_delete_file(Buf *path) { if (remove(buf_ptr(path))) { return ErrorFileSystem; diff --git a/src/os.hpp b/src/os.hpp index 5d29db0d07..b94e98ec3d 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -51,14 +51,16 @@ int os_path_real(Buf *rel_path, Buf *out_abs_path); void os_path_resolve(Buf *ref_path, Buf *target_path, Buf *out_abs_path); bool os_path_is_absolute(Buf *path); +int os_get_global_cache_directory(Buf *out_tmp_path); + int os_make_path(Buf *path); int os_make_dir(Buf *path); void os_write_file(Buf *full_path, Buf *contents); int os_copy_file(Buf *src_path, Buf *dest_path); -int os_fetch_file(FILE *file, Buf *out_contents); -int os_fetch_file_path(Buf *full_path, Buf *out_contents); +int os_fetch_file(FILE *file, Buf *out_contents, bool skip_shebang); +int os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang); int os_get_cwd(Buf *out_cwd); -- cgit v1.2.3