IR: implement maybe return expression
This commit is contained in:
parent
3f3630d7e3
commit
647d13168a
@ -759,7 +759,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
fprintf(ar->f, ": ");
|
||||
render_node_ungrouped(ar, var_decl->type);
|
||||
}
|
||||
fprintf(ar->f, " = ");
|
||||
fprintf(ar->f, " ?= ");
|
||||
render_node_grouped(ar, var_decl->expr);
|
||||
fprintf(ar->f, ") ");
|
||||
render_node_grouped(ar, node->data.if_var_expr.then_block);
|
||||
|
146
src/ir.cpp
146
src/ir.cpp
@ -1731,7 +1731,13 @@ static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node) {
|
||||
static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
|
||||
assert(basic_block);
|
||||
|
||||
irb->current_basic_block = basic_block;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
|
||||
assert(node->type == NodeTypeReturnExpr);
|
||||
|
||||
FnTableEntry *fn_entry = exec_fn_entry(irb->exec);
|
||||
@ -1740,6 +1746,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node)
|
||||
return irb->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
Scope *outer_scope = fn_entry->child_scope;
|
||||
bool is_inline = ir_should_inline(irb);
|
||||
|
||||
AstNode *expr_node = node->data.return_expr.expr;
|
||||
switch (node->data.return_expr.kind) {
|
||||
case ReturnKindUnconditional:
|
||||
@ -1747,28 +1756,46 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node)
|
||||
IrInstruction *return_value;
|
||||
if (expr_node) {
|
||||
return_value = ir_gen_node(irb, expr_node, scope);
|
||||
if (return_value == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
} else {
|
||||
return_value = ir_build_const_void(irb, scope, node);
|
||||
}
|
||||
|
||||
Scope *outer_scope = fn_entry->child_scope;
|
||||
// TODO conditionally gen maybe defers and error defers
|
||||
ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
|
||||
return ir_build_return(irb, scope, node, return_value);
|
||||
}
|
||||
case ReturnKindError:
|
||||
zig_panic("TODO gen IR for %%return");
|
||||
case ReturnKindMaybe:
|
||||
zig_panic("TODO gen IR for ?return");
|
||||
{
|
||||
assert(expr_node);
|
||||
IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
|
||||
if (maybe_val_ptr == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
IrInstruction *is_nonnull_val = ir_build_test_null(irb, scope, node, maybe_val_ptr);
|
||||
|
||||
IrBasicBlock *return_block = ir_build_basic_block(irb, scope, "MaybeRetReturn");
|
||||
IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "MaybeRetContinue");
|
||||
ir_build_cond_br(irb, scope, node, is_nonnull_val, continue_block, return_block, is_inline);
|
||||
|
||||
ir_set_cursor_at_end(irb, return_block);
|
||||
ir_gen_defers_for_block(irb, scope, outer_scope, false, true);
|
||||
IrInstruction *null = ir_build_const_null(irb, scope, node);
|
||||
ir_build_return(irb, scope, node, null);
|
||||
|
||||
ir_set_cursor_at_end(irb, continue_block);
|
||||
IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_val_ptr, false);
|
||||
if (lval != LValPurposeNone)
|
||||
return unwrapped_ptr;
|
||||
else
|
||||
return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
|
||||
assert(basic_block);
|
||||
|
||||
irb->current_basic_block = basic_block;
|
||||
}
|
||||
|
||||
static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
|
||||
Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, bool is_inline)
|
||||
{
|
||||
@ -3606,7 +3633,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
|
||||
case NodeTypeArrayAccessExpr:
|
||||
return ir_gen_array_access(irb, scope, node, lval);
|
||||
case NodeTypeReturnExpr:
|
||||
return ir_lval_wrap(irb, scope, ir_gen_return(irb, scope, node), lval);
|
||||
return ir_gen_return(irb, scope, node, lval);
|
||||
case NodeTypeFieldAccessExpr:
|
||||
return ir_gen_field_access(irb, scope, node, lval);
|
||||
case NodeTypeThisLiteral:
|
||||
@ -3827,6 +3854,7 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, AstNode *source_n
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
bool any_are_pure_error = (prev_inst->type_entry->id == TypeTableEntryIdPureError);
|
||||
bool any_are_null = (prev_inst->type_entry->id == TypeTableEntryIdNullLit);
|
||||
for (size_t i = 1; i < instruction_count; i += 1) {
|
||||
IrInstruction *cur_inst = instructions[i];
|
||||
TypeTableEntry *cur_type = cur_inst->type_entry;
|
||||
@ -3836,9 +3864,15 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, AstNode *source_n
|
||||
} else if (prev_type->id == TypeTableEntryIdPureError) {
|
||||
prev_inst = cur_inst;
|
||||
continue;
|
||||
} else if (prev_type->id == TypeTableEntryIdNullLit) {
|
||||
prev_inst = cur_inst;
|
||||
continue;
|
||||
} else if (cur_type->id == TypeTableEntryIdPureError) {
|
||||
any_are_pure_error = true;
|
||||
continue;
|
||||
} else if (cur_type->id == TypeTableEntryIdNullLit) {
|
||||
any_are_null = true;
|
||||
continue;
|
||||
} else if (types_match_const_cast_only(prev_type, cur_type)) {
|
||||
continue;
|
||||
} else if (types_match_const_cast_only(cur_type, prev_type)) {
|
||||
@ -3889,9 +3923,13 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, AstNode *source_n
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
} else {
|
||||
add_node_error(ira->codegen, source_node,
|
||||
ErrorMsg *msg = add_node_error(ira->codegen, source_node,
|
||||
buf_sprintf("incompatible types: '%s' and '%s'",
|
||||
buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
|
||||
add_error_note(ira->codegen, msg, prev_inst->source_node,
|
||||
buf_sprintf("type '%s' here", buf_ptr(&prev_type->name)));
|
||||
add_error_note(ira->codegen, msg, cur_inst->source_node,
|
||||
buf_sprintf("type '%s' here", buf_ptr(&cur_type->name)));
|
||||
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
@ -3903,9 +3941,23 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, AstNode *source_n
|
||||
add_node_error(ira->codegen, source_node,
|
||||
buf_sprintf("unable to make error union out of number literal"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
} else if (prev_inst->type_entry->id == TypeTableEntryIdNullLit) {
|
||||
add_node_error(ira->codegen, source_node,
|
||||
buf_sprintf("unable to make error union out of null literal"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
} else {
|
||||
return get_error_type(ira->codegen, prev_inst->type_entry);
|
||||
}
|
||||
} else if (any_are_null && prev_inst->type_entry->id != TypeTableEntryIdNullLit) {
|
||||
if (prev_inst->type_entry->id == TypeTableEntryIdNumLitInt ||
|
||||
prev_inst->type_entry->id == TypeTableEntryIdNumLitFloat)
|
||||
{
|
||||
add_node_error(ira->codegen, source_node,
|
||||
buf_sprintf("unable to make maybe out of number literal"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
} else {
|
||||
return get_maybe_type(ira->codegen, prev_inst->type_entry);
|
||||
}
|
||||
} else {
|
||||
return prev_inst->type_entry;
|
||||
}
|
||||
@ -9053,16 +9105,9 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
//static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
// TypeTableEntry *expected_type, AstNode *node)
|
||||
//{
|
||||
// if (!node->data.return_expr.expr) {
|
||||
// node->data.return_expr.expr = create_ast_void_node(g, import, node);
|
||||
// normalize_parent_ptrs(node);
|
||||
// }
|
||||
//
|
||||
// TypeTableEntry *expected_return_type = get_return_type(context);
|
||||
//
|
||||
// switch (node->data.return_expr.kind) {
|
||||
// case ReturnKindUnconditional:
|
||||
// zig_panic("TODO moved to ir.cpp");
|
||||
// case ReturnKindError:
|
||||
// {
|
||||
// TypeTableEntry *expected_err_type;
|
||||
@ -9093,34 +9138,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
// return g->builtin_types.entry_invalid;
|
||||
// }
|
||||
// }
|
||||
// case ReturnKindMaybe:
|
||||
// {
|
||||
// TypeTableEntry *expected_maybe_type;
|
||||
// if (expected_type) {
|
||||
// expected_maybe_type = get_maybe_type(g, expected_type);
|
||||
// } else {
|
||||
// expected_maybe_type = nullptr;
|
||||
// }
|
||||
// TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_maybe_type,
|
||||
// node->data.return_expr.expr);
|
||||
// if (resolved_type->id == TypeTableEntryIdInvalid) {
|
||||
// return resolved_type;
|
||||
// } else if (resolved_type->id == TypeTableEntryIdMaybe) {
|
||||
// if (expected_return_type->id != TypeTableEntryIdMaybe) {
|
||||
// ErrorMsg *msg = add_node_error(g, node,
|
||||
// buf_sprintf("?return statement in function with return type '%s'",
|
||||
// buf_ptr(&expected_return_type->name)));
|
||||
// AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type;
|
||||
// add_error_note(g, msg, return_type_node, buf_sprintf("function return type here"));
|
||||
// }
|
||||
//
|
||||
// return resolved_type->data.maybe.child_type;
|
||||
// } else {
|
||||
// add_node_error(g, node->data.return_expr.expr,
|
||||
// buf_sprintf("expected maybe type, found '%s'", buf_ptr(&resolved_type->name)));
|
||||
// return g->builtin_types.entry_invalid;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// zig_unreachable();
|
||||
//}
|
||||
@ -9344,43 +9361,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
// return nullptr;
|
||||
// }
|
||||
// }
|
||||
// case ReturnKindMaybe:
|
||||
// {
|
||||
// assert(value_type->id == TypeTableEntryIdMaybe);
|
||||
// TypeTableEntry *child_type = value_type->data.maybe.child_type;
|
||||
//
|
||||
// LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetReturn");
|
||||
// LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetContinue");
|
||||
//
|
||||
// LLVMValueRef maybe_val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
|
||||
// LLVMValueRef is_non_null = LLVMBuildLoad(g->builder, maybe_val_ptr, "");
|
||||
//
|
||||
// LLVMValueRef zero = LLVMConstNull(LLVMInt1Type());
|
||||
// LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, is_non_null, zero, "");
|
||||
// LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block);
|
||||
//
|
||||
// LLVMPositionBuilderAtEnd(g->builder, return_block);
|
||||
// TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
|
||||
// assert(return_type->id == TypeTableEntryIdMaybe);
|
||||
// if (handle_is_ptr(return_type)) {
|
||||
// assert(g->cur_ret_ptr);
|
||||
//
|
||||
// LLVMValueRef maybe_bit_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 1, "");
|
||||
// LLVMBuildStore(g->builder, zero, maybe_bit_ptr);
|
||||
// LLVMBuildRetVoid(g->builder);
|
||||
// } else {
|
||||
// LLVMValueRef ret_zero_value = LLVMConstNull(return_type->type_ref);
|
||||
// gen_return(g, node, ret_zero_value, ReturnKnowledgeKnownNull);
|
||||
// }
|
||||
//
|
||||
// LLVMPositionBuilderAtEnd(g->builder, continue_block);
|
||||
// if (type_has_bits(child_type)) {
|
||||
// LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 0, "");
|
||||
// return get_handle_value(g, val_ptr, child_type);
|
||||
// } else {
|
||||
// return nullptr;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// zig_unreachable();
|
||||
//}
|
||||
|
@ -585,7 +585,7 @@ static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) {
|
||||
static void ir_print_test_null(IrPrint *irp, IrInstructionTestNull *instruction) {
|
||||
fprintf(irp->f, "*");
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
fprintf(irp->f, " == null");
|
||||
fprintf(irp->f, " != null");
|
||||
}
|
||||
|
||||
static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) {
|
||||
|
@ -343,6 +343,18 @@ fn shlWithOverflow() {
|
||||
assert(result == 0b1011111111111100);
|
||||
}
|
||||
|
||||
fn assignToIfVarPtr() {
|
||||
|
||||
var maybe_bool: ?bool = true;
|
||||
|
||||
if (const *b ?= maybe_bool) {
|
||||
*b = false;
|
||||
}
|
||||
|
||||
assert(??maybe_bool == false);
|
||||
}
|
||||
|
||||
|
||||
fn assert(ok: bool) {
|
||||
if (!ok)
|
||||
@unreachable();
|
||||
@ -377,6 +389,7 @@ fn runAllTests() {
|
||||
intTypeBuiltin();
|
||||
overflowIntrinsics();
|
||||
shlWithOverflow();
|
||||
assignToIfVarPtr();
|
||||
}
|
||||
|
||||
export nakedcc fn _start() -> unreachable {
|
||||
|
Loading…
x
Reference in New Issue
Block a user