add error for break outside loop

also fix break in nested loops
master
Andrew Kelley 2015-12-24 15:30:32 -07:00
parent 5ceaae288c
commit 44ca5e19dc
4 changed files with 35 additions and 10 deletions

View File

@ -830,6 +830,11 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
context->parent = parent;
context->variable_table.init(8);
if (parent) {
context->break_allowed = parent->break_allowed || parent->next_child_break_allowed;
parent->next_child_break_allowed = false;
}
if (node && node->type == NodeTypeFnDef) {
AstNode *fn_proto_node = node->data.fn_def.fn_proto;
context->fn_entry = fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry;
@ -1359,13 +1364,20 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import,
TypeTableEntry *expected_type, AstNode *node)
{
analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.while_expr.condition);
context->next_child_break_allowed = true;
analyze_expression(g, import, context, g->builtin_types.entry_void, node->data.while_expr.body);
return g->builtin_types.entry_void;
}
static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
if (!context->break_allowed) {
add_node_error(g, node,
buf_sprintf("'break' expression not in loop"));
}
return g->builtin_types.entry_unreachable;
}

View File

@ -196,7 +196,7 @@ struct CodeGen {
FnTableEntry *cur_fn;
LLVMBasicBlockRef cur_basic_block;
BlockContext *cur_block_context;
LLVMBasicBlockRef cur_break_block;
ZigList<LLVMBasicBlockRef> break_block_stack;
bool c_stdint_used;
AstNode *root_export_decl;
int version_major;
@ -226,6 +226,8 @@ struct BlockContext {
HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
ZigList<CastNode *> cast_expr_alloca_list;
ZigList<StructValExprNode *> struct_val_expr_alloca_list;
bool break_allowed;
bool next_child_break_allowed;
LLVMZigDIScope *di_scope;
};

View File

@ -1024,10 +1024,12 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
LLVMBuildCondBr(g->builder, cond_val, body_block, end_block);
LLVMPositionBuilderAtEnd(g->builder, body_block);
g->cur_break_block = end_block;
g->break_block_stack.append(end_block);
gen_expr(g, node->data.while_expr.body);
g->cur_break_block = nullptr;
LLVMBuildBr(g->builder, cond_block);
g->break_block_stack.pop();
if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
LLVMBuildBr(g->builder, cond_block);
}
LLVMPositionBuilderAtEnd(g->builder, end_block);
return nullptr;
@ -1035,10 +1037,10 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
static LLVMValueRef gen_break(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeBreak);
assert(g->cur_break_block);
LLVMBasicBlockRef dest_block = g->break_block_stack.last();
add_debug_source_node(g, node);
return LLVMBuildBr(g->builder, g->cur_break_block);
return LLVMBuildBr(g->builder, dest_block);
}
static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {

View File

@ -664,11 +664,14 @@ use "std.zig";
export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
var i : i32 = 0;
while true {
if i >= 4 {
break;
while true {
if i >= 4 {
break;
}
print_str("loop\n");
i += 1;
}
print_str("loop\n");
i += 1;
break;
}
return 0;
}
@ -949,6 +952,12 @@ fn f() {
};
}
)SOURCE", 1, ".tmp_source.zig:11:9: error: no member named 'foo' in 'A'");
add_compile_fail_case("invalid break expression", R"SOURCE(
fn f() {
break;
}
)SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression not in loop");
}
static void print_compiler_invocation(TestCase *test_case) {