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, ": ");
|
fprintf(ar->f, ": ");
|
||||||
render_node_ungrouped(ar, var_decl->type);
|
render_node_ungrouped(ar, var_decl->type);
|
||||||
}
|
}
|
||||||
fprintf(ar->f, " = ");
|
fprintf(ar->f, " ?= ");
|
||||||
render_node_grouped(ar, var_decl->expr);
|
render_node_grouped(ar, var_decl->expr);
|
||||||
fprintf(ar->f, ") ");
|
fprintf(ar->f, ") ");
|
||||||
render_node_grouped(ar, node->data.if_var_expr.then_block);
|
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);
|
assert(node->type == NodeTypeReturnExpr);
|
||||||
|
|
||||||
FnTableEntry *fn_entry = exec_fn_entry(irb->exec);
|
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;
|
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;
|
AstNode *expr_node = node->data.return_expr.expr;
|
||||||
switch (node->data.return_expr.kind) {
|
switch (node->data.return_expr.kind) {
|
||||||
case ReturnKindUnconditional:
|
case ReturnKindUnconditional:
|
||||||
@ -1747,28 +1756,46 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node)
|
|||||||
IrInstruction *return_value;
|
IrInstruction *return_value;
|
||||||
if (expr_node) {
|
if (expr_node) {
|
||||||
return_value = ir_gen_node(irb, expr_node, scope);
|
return_value = ir_gen_node(irb, expr_node, scope);
|
||||||
|
if (return_value == irb->codegen->invalid_instruction)
|
||||||
|
return irb->codegen->invalid_instruction;
|
||||||
} else {
|
} else {
|
||||||
return_value = ir_build_const_void(irb, scope, node);
|
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);
|
ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
|
||||||
return ir_build_return(irb, scope, node, return_value);
|
return ir_build_return(irb, scope, node, return_value);
|
||||||
}
|
}
|
||||||
case ReturnKindError:
|
case ReturnKindError:
|
||||||
zig_panic("TODO gen IR for %%return");
|
zig_panic("TODO gen IR for %%return");
|
||||||
case ReturnKindMaybe:
|
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();
|
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,
|
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)
|
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:
|
case NodeTypeArrayAccessExpr:
|
||||||
return ir_gen_array_access(irb, scope, node, lval);
|
return ir_gen_array_access(irb, scope, node, lval);
|
||||||
case NodeTypeReturnExpr:
|
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:
|
case NodeTypeFieldAccessExpr:
|
||||||
return ir_gen_field_access(irb, scope, node, lval);
|
return ir_gen_field_access(irb, scope, node, lval);
|
||||||
case NodeTypeThisLiteral:
|
case NodeTypeThisLiteral:
|
||||||
@ -3827,6 +3854,7 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, AstNode *source_n
|
|||||||
return ira->codegen->builtin_types.entry_invalid;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
}
|
}
|
||||||
bool any_are_pure_error = (prev_inst->type_entry->id == TypeTableEntryIdPureError);
|
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) {
|
for (size_t i = 1; i < instruction_count; i += 1) {
|
||||||
IrInstruction *cur_inst = instructions[i];
|
IrInstruction *cur_inst = instructions[i];
|
||||||
TypeTableEntry *cur_type = cur_inst->type_entry;
|
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) {
|
} else if (prev_type->id == TypeTableEntryIdPureError) {
|
||||||
prev_inst = cur_inst;
|
prev_inst = cur_inst;
|
||||||
continue;
|
continue;
|
||||||
|
} else if (prev_type->id == TypeTableEntryIdNullLit) {
|
||||||
|
prev_inst = cur_inst;
|
||||||
|
continue;
|
||||||
} else if (cur_type->id == TypeTableEntryIdPureError) {
|
} else if (cur_type->id == TypeTableEntryIdPureError) {
|
||||||
any_are_pure_error = true;
|
any_are_pure_error = true;
|
||||||
continue;
|
continue;
|
||||||
|
} else if (cur_type->id == TypeTableEntryIdNullLit) {
|
||||||
|
any_are_null = true;
|
||||||
|
continue;
|
||||||
} else if (types_match_const_cast_only(prev_type, cur_type)) {
|
} else if (types_match_const_cast_only(prev_type, cur_type)) {
|
||||||
continue;
|
continue;
|
||||||
} else if (types_match_const_cast_only(cur_type, prev_type)) {
|
} 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;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
}
|
}
|
||||||
} else {
|
} 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_sprintf("incompatible types: '%s' and '%s'",
|
||||||
buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
|
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;
|
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,
|
add_node_error(ira->codegen, source_node,
|
||||||
buf_sprintf("unable to make error union out of number literal"));
|
buf_sprintf("unable to make error union out of number literal"));
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
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 {
|
} else {
|
||||||
return get_error_type(ira->codegen, prev_inst->type_entry);
|
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 {
|
} else {
|
||||||
return prev_inst->type_entry;
|
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,
|
//static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||||
// TypeTableEntry *expected_type, AstNode *node)
|
// 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);
|
// TypeTableEntry *expected_return_type = get_return_type(context);
|
||||||
//
|
//
|
||||||
// switch (node->data.return_expr.kind) {
|
// switch (node->data.return_expr.kind) {
|
||||||
// case ReturnKindUnconditional:
|
|
||||||
// zig_panic("TODO moved to ir.cpp");
|
|
||||||
// case ReturnKindError:
|
// case ReturnKindError:
|
||||||
// {
|
// {
|
||||||
// TypeTableEntry *expected_err_type;
|
// TypeTableEntry *expected_err_type;
|
||||||
@ -9093,34 +9138,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
|||||||
// return g->builtin_types.entry_invalid;
|
// 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();
|
// zig_unreachable();
|
||||||
//}
|
//}
|
||||||
@ -9344,43 +9361,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
|||||||
// return nullptr;
|
// 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();
|
// 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) {
|
static void ir_print_test_null(IrPrint *irp, IrInstructionTestNull *instruction) {
|
||||||
fprintf(irp->f, "*");
|
fprintf(irp->f, "*");
|
||||||
ir_print_other_instruction(irp, instruction->value);
|
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) {
|
static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) {
|
||||||
|
@ -343,6 +343,18 @@ fn shlWithOverflow() {
|
|||||||
assert(result == 0b1011111111111100);
|
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) {
|
fn assert(ok: bool) {
|
||||||
if (!ok)
|
if (!ok)
|
||||||
@unreachable();
|
@unreachable();
|
||||||
@ -377,6 +389,7 @@ fn runAllTests() {
|
|||||||
intTypeBuiltin();
|
intTypeBuiltin();
|
||||||
overflowIntrinsics();
|
overflowIntrinsics();
|
||||||
shlWithOverflow();
|
shlWithOverflow();
|
||||||
|
assignToIfVarPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
export nakedcc fn _start() -> unreachable {
|
export nakedcc fn _start() -> unreachable {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user