fix async fns with inferred error sets

closes #856
This commit is contained in:
Andrew Kelley 2018-03-24 18:21:51 -04:00
parent b1c07c0ea9
commit 18af2f9a27
7 changed files with 89 additions and 14 deletions

View File

@ -1251,7 +1251,10 @@ struct FnTableEntry {
ScopeBlock *def_scope; // parent is child_scope ScopeBlock *def_scope; // parent is child_scope
Buf symbol_name; Buf symbol_name;
TypeTableEntry *type_entry; // function type 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; bool is_test;
FnInline fn_inline; FnInline fn_inline;
FnAnalState anal_state; FnAnalState anal_state;
@ -2035,6 +2038,7 @@ enum IrInstructionId {
IrInstructionIdPromiseResultType, IrInstructionIdPromiseResultType,
IrInstructionIdAwaitBookkeeping, IrInstructionIdAwaitBookkeeping,
IrInstructionIdSaveErrRetAddr, IrInstructionIdSaveErrRetAddr,
IrInstructionIdAddImplicitReturnType,
}; };
struct IrInstruction { struct IrInstruction {
@ -2993,6 +2997,12 @@ struct IrInstructionSaveErrRetAddr {
IrInstruction base; IrInstruction base;
}; };
struct IrInstructionAddImplicitReturnType {
IrInstruction base;
IrInstruction *value;
};
static const size_t slice_ptr_index = 0; static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1; static const size_t slice_len_index = 1;

View File

@ -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, 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->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) { if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) {
assert(g->errors.length > 0); 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; 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) { if (return_err_set_type->data.error_set.infer_fn != nullptr) {
TypeTableEntry *inferred_err_set_type; TypeTableEntry *inferred_err_set_type;
if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorSet) { if (fn_table_entry->src_implicit_return_type->id == TypeTableEntryIdErrorSet) {
inferred_err_set_type = fn_table_entry->implicit_return_type; inferred_err_set_type = fn_table_entry->src_implicit_return_type;
} else if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorUnion) { } else if (fn_table_entry->src_implicit_return_type->id == TypeTableEntryIdErrorUnion) {
inferred_err_set_type = fn_table_entry->implicit_return_type->data.error_union.err_set_type; inferred_err_set_type = fn_table_entry->src_implicit_return_type->data.error_union.err_set_type;
} else { } else {
add_node_error(g, return_type_node, add_node_error(g, return_type_node,
buf_sprintf("function with inferred error set must return at least one possible error")); buf_sprintf("function with inferred error set must return at least one possible error"));

View File

@ -658,6 +658,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
if (node->data.fn_call_expr.is_builtin) { if (node->data.fn_call_expr.is_builtin) {
fprintf(ar->f, "@"); 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; AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypeAddrOfExpr); bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypeAddrOfExpr);
render_node_extra(ar, fn_ref_node, grouped); 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: case NodeTypeUnwrapErrorExpr:
{ {
render_node_ungrouped(ar, node->data.unwrap_err_expr.op1); render_node_ungrouped(ar, node->data.unwrap_err_expr.op1);
fprintf(ar->f, " %%%% "); fprintf(ar->f, " catch ");
if (node->data.unwrap_err_expr.symbol) { if (node->data.unwrap_err_expr.symbol) {
Buf *var_name = node->data.unwrap_err_expr.symbol->data.symbol_expr.symbol; Buf *var_name = node->data.unwrap_err_expr.symbol->data.symbol_expr.symbol;
fprintf(ar->f, "|%s| ", buf_ptr(var_name)); fprintf(ar->f, "|%s| ", buf_ptr(var_name));

View File

@ -4245,6 +4245,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdErrorUnion: case IrInstructionIdErrorUnion:
case IrInstructionIdPromiseResultType: case IrInstructionIdPromiseResultType:
case IrInstructionIdAwaitBookkeeping: case IrInstructionIdAwaitBookkeeping:
case IrInstructionIdAddImplicitReturnType:
zig_unreachable(); zig_unreachable();
case IrInstructionIdReturn: case IrInstructionIdReturn:

View File

@ -34,7 +34,7 @@ struct IrAnalyze {
size_t old_bb_index; size_t old_bb_index;
size_t instruction_index; size_t instruction_index;
TypeTableEntry *explicit_return_type; TypeTableEntry *explicit_return_type;
ZigList<IrInstruction *> implicit_return_type_list; ZigList<IrInstruction *> src_implicit_return_type_list;
IrBasicBlock *const_predecessor_bb; IrBasicBlock *const_predecessor_bb;
}; };
@ -717,6 +717,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *
return IrInstructionIdSaveErrRetAddr; return IrInstructionIdSaveErrRetAddr;
} }
static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitReturnType *) {
return IrInstructionIdAddImplicitReturnType;
}
template<typename T> template<typename T>
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate<T>(1); T *special_instruction = allocate<T>(1);
@ -2687,6 +2691,17 @@ static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, A
return &instruction->base; 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<IrInstructionAddImplicitReturnType>(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) { static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0; results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 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, static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value,
bool is_generated_code) 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); bool is_async = exec_is_async(irb->exec);
if (!is_async) { 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);
@ -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_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); 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); IrInstruction *undef = ir_build_const_undefined(irb, scope, node);
ir_build_return(irb, scope, node, undef); ir_build_return(irb, scope, node, undef);
@ -10108,13 +10127,26 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
return result; 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, static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira,
IrInstructionReturn *return_instruction) IrInstructionReturn *return_instruction)
{ {
IrInstruction *value = return_instruction->value->other; IrInstruction *value = return_instruction->value->other;
if (type_is_invalid(value->value.type)) if (type_is_invalid(value->value.type))
return ir_unreach_error(ira); return ir_unreach_error(ira);
ira->implicit_return_type_list.append(value);
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type); IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type);
if (casted_value == ira->codegen->invalid_instruction) 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); return ir_analyze_instruction_await_bookkeeping(ira, (IrInstructionAwaitBookkeeping *)instruction);
case IrInstructionIdSaveErrRetAddr: case IrInstructionIdSaveErrRetAddr:
return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); 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(); zig_unreachable();
} }
@ -18122,11 +18156,11 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
if (new_exec->invalid) { if (new_exec->invalid) {
return ira->codegen->builtin_types.entry_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; return codegen->builtin_types.entry_unreachable;
} else { } else {
return ir_resolve_peer_types(ira, expected_type_source_node, ira->implicit_return_type_list.items, return ir_resolve_peer_types(ira, expected_type_source_node, ira->src_implicit_return_type_list.items,
ira->implicit_return_type_list.length); ira->src_implicit_return_type_list.length);
} }
} }
@ -18175,6 +18209,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdCoroAllocHelper: case IrInstructionIdCoroAllocHelper:
case IrInstructionIdAwaitBookkeeping: case IrInstructionIdAwaitBookkeeping:
case IrInstructionIdSaveErrRetAddr: case IrInstructionIdSaveErrRetAddr:
case IrInstructionIdAddImplicitReturnType:
return true; return true;
case IrInstructionIdPhi: case IrInstructionIdPhi:

View File

@ -201,9 +201,9 @@ static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) {
if (call_instruction->is_async) { if (call_instruction->is_async) {
fprintf(irp->f, "async"); fprintf(irp->f, "async");
if (call_instruction->async_allocator != nullptr) { if (call_instruction->async_allocator != nullptr) {
fprintf(irp->f, "("); fprintf(irp->f, "<");
ir_print_other_instruction(irp, call_instruction->async_allocator); ir_print_other_instruction(irp, call_instruction->async_allocator);
fprintf(irp->f, ")"); 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()"); 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) { static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction); ir_print_prefix(irp, instruction);
switch (instruction->id) { switch (instruction->id) {
@ -1539,6 +1545,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdSaveErrRetAddr: case IrInstructionIdSaveErrRetAddr:
ir_print_save_err_ret_addr(irp, (IrInstructionSaveErrRetAddr *)instruction); ir_print_save_err_ret_addr(irp, (IrInstructionSaveErrRetAddr *)instruction);
break; break;
case IrInstructionIdAddImplicitReturnType:
ir_print_add_implicit_return_type(irp, (IrInstructionAddImplicitReturnType *)instruction);
break;
} }
fprintf(irp->f, "\n"); fprintf(irp->f, "\n");
} }

View File

@ -176,3 +176,14 @@ async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void {
*y += 1; *y += 1;
suspend; suspend;
} }
test "async fn with inferred error set" {
const p = (async<std.debug.global_allocator> failing()) catch unreachable;
resume p;
cancel p;
}
async fn failing() !void {
suspend;
return error.Fail;
}