authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-06 18:02:42-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-06 18:02:42-07:00
logc75d40680ff8a143b9298968a3ca3053dea2e460
tree1d54def2a7dd431805db19debe07eb983bedd5db
parent5f0bfcac24036e1fff0b2beda643a60dad465213

while detects simple constant condition


6 files changed, 196 insertions(+), 41 deletions(-)

example/guess_number/main.zig+2-2
......@@ -27,6 +27,8 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
2727 print_u64(answer);
2828 print_str("\n");
2929
30 return 0;
31
3032 /*
3133 while (true) {
3234 const line = readline("\nGuess a number between 1 and 100: ");
......@@ -45,6 +47,4 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
4547 }
4648 }
4749 */
48
49 return 0;
5050}
src/analyze.cpp+137-12
......@@ -12,6 +12,8 @@
1212
1313static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
1414 TypeTableEntry *expected_type, AstNode *node);
15static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
16 AstNode *node, AstNodeNumberLiteral *out_number_literal);
1517
1618static AstNode *first_executing_node(AstNode *node) {
1719 switch (node->type) {
......@@ -284,6 +286,98 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry
284286 }
285287}
286288
289static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
290 AstNode *node, AstNodeNumberLiteral *out_number_literal)
291{
292 AstNodeNumberLiteral op1_lit;
293 AstNodeNumberLiteral op2_lit;
294 TypeTableEntry *op1_type = eval_const_expr(g, context, node->data.bin_op_expr.op1, &op1_lit);
295 TypeTableEntry *op2_type = eval_const_expr(g, context, node->data.bin_op_expr.op1, &op2_lit);
296
297 if (op1_type->id == TypeTableEntryIdInvalid ||
298 op2_type->id == TypeTableEntryIdInvalid)
299 {
300 return g->builtin_types.entry_invalid;
301 }
302
303 // TODO complete more of this function instead of returning invalid
304 // returning invalid makes the "unable to evaluate constant expression" error
305
306 switch (node->data.bin_op_expr.bin_op) {
307 case BinOpTypeCmpNotEq:
308 {
309 if (is_num_lit_unsigned(op1_lit.kind) &&
310 is_num_lit_unsigned(op2_lit.kind))
311 {
312 out_number_literal->kind = NumLitU8;
313 out_number_literal->overflow = false;
314 out_number_literal->data.x_uint = (op1_lit.data.x_uint != op2_lit.data.x_uint);
315 return node->codegen_node->expr_node.type_entry;
316 } else {
317 return g->builtin_types.entry_invalid;
318 }
319 }
320 case BinOpTypeCmpLessThan:
321 {
322 if (is_num_lit_unsigned(op1_lit.kind) &&
323 is_num_lit_unsigned(op2_lit.kind))
324 {
325 out_number_literal->kind = NumLitU8;
326 out_number_literal->overflow = false;
327 out_number_literal->data.x_uint = (op1_lit.data.x_uint < op2_lit.data.x_uint);
328 return node->codegen_node->expr_node.type_entry;
329 } else {
330 return g->builtin_types.entry_invalid;
331 }
332 }
333 case BinOpTypeMod:
334 {
335 if (is_num_lit_unsigned(op1_lit.kind) &&
336 is_num_lit_unsigned(op2_lit.kind))
337 {
338 out_number_literal->kind = NumLitU64;
339 out_number_literal->overflow = false;
340 out_number_literal->data.x_uint = (op1_lit.data.x_uint % op2_lit.data.x_uint);
341 return node->codegen_node->expr_node.type_entry;
342 } else {
343 return g->builtin_types.entry_invalid;
344 }
345 }
346 case BinOpTypeBoolOr:
347 case BinOpTypeBoolAnd:
348 case BinOpTypeCmpEq:
349 case BinOpTypeCmpGreaterThan:
350 case BinOpTypeCmpLessOrEq:
351 case BinOpTypeCmpGreaterOrEq:
352 case BinOpTypeBinOr:
353 case BinOpTypeBinXor:
354 case BinOpTypeBinAnd:
355 case BinOpTypeBitShiftLeft:
356 case BinOpTypeBitShiftRight:
357 case BinOpTypeAdd:
358 case BinOpTypeSub:
359 case BinOpTypeMult:
360 case BinOpTypeDiv:
361 return g->builtin_types.entry_invalid;
362 case BinOpTypeInvalid:
363 case BinOpTypeAssign:
364 case BinOpTypeAssignTimes:
365 case BinOpTypeAssignDiv:
366 case BinOpTypeAssignMod:
367 case BinOpTypeAssignPlus:
368 case BinOpTypeAssignMinus:
369 case BinOpTypeAssignBitShiftLeft:
370 case BinOpTypeAssignBitShiftRight:
371 case BinOpTypeAssignBitAnd:
372 case BinOpTypeAssignBitXor:
373 case BinOpTypeAssignBitOr:
374 case BinOpTypeAssignBoolAnd:
375 case BinOpTypeAssignBoolOr:
376 zig_unreachable();
377 }
378 zig_unreachable();
379}
380
287381static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
288382 AstNode *node, AstNodeNumberLiteral *out_number_literal)
289383{
......@@ -291,9 +385,11 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
291385 case NodeTypeNumberLiteral:
292386 *out_number_literal = node->data.number_literal;
293387 return node->codegen_node->expr_node.type_entry;
388 case NodeTypeBoolLiteral:
389 out_number_literal->data.x_uint = node->data.bool_literal ? 1 : 0;
390 return node->codegen_node->expr_node.type_entry;
294391 case NodeTypeBinOpExpr:
295 zig_panic("TODO eval_const_expr bin op expr");
296 break;
392 return eval_const_expr_bin_op(g, context, node, out_number_literal);
297393 case NodeTypeCompilerFnType:
298394 {
299395 Buf *name = &node->data.compiler_fn_type.name;
......@@ -1133,8 +1229,12 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
11331229 context->variable_table.init(8);
11341230
11351231 if (parent) {
1136 context->break_allowed = parent->break_allowed || parent->next_child_break_allowed;
1137 parent->next_child_break_allowed = false;
1232 if (parent->next_child_parent_loop_node) {
1233 context->parent_loop_node = parent->next_child_parent_loop_node;
1234 parent->next_child_parent_loop_node = nullptr;
1235 } else {
1236 context->parent_loop_node = parent->parent_loop_node;
1237 }
11381238 }
11391239
11401240 if (node && node->type == NodeTypeFnDef) {
......@@ -1690,20 +1790,45 @@ static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *imp
16901790static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
16911791 TypeTableEntry *expected_type, AstNode *node)
16921792{
1693 analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.while_expr.condition);
1793 AstNode *condition_node = node->data.while_expr.condition;
1794 AstNode *while_body_node = node->data.while_expr.body;
1795 TypeTableEntry *condition_type = analyze_expression(g, import, context,
1796 g->builtin_types.entry_bool, condition_node);
1797
1798 context->next_child_parent_loop_node = node;
1799 analyze_expression(g, import, context, g->builtin_types.entry_void, while_body_node);
1800
1801
1802 TypeTableEntry *expr_return_type = g->builtin_types.entry_void;
16941803
1695 context->next_child_break_allowed = true;
1696 analyze_expression(g, import, context, g->builtin_types.entry_void, node->data.while_expr.body);
1804 if (condition_type->id == TypeTableEntryIdInvalid) {
1805 expr_return_type = g->builtin_types.entry_invalid;
1806 } else {
1807 // if the condition is a simple constant expression and there are no break statements
1808 // then the return type is unreachable
1809 AstNodeNumberLiteral number_literal;
1810 TypeTableEntry *resolved_type = eval_const_expr(g, context, condition_node, &number_literal);
1811 if (resolved_type->id != TypeTableEntryIdInvalid) {
1812 assert(resolved_type->id == TypeTableEntryIdBool);
1813 bool constant_cond_value = number_literal.data.x_uint;
1814 if (constant_cond_value && !node->codegen_node->data.while_node.contains_break) {
1815 expr_return_type = g->builtin_types.entry_unreachable;
1816 }
1817 }
1818 }
16971819
1698 return g->builtin_types.entry_void;
1820 return expr_return_type;
16991821}
17001822
17011823static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
17021824 TypeTableEntry *expected_type, AstNode *node)
17031825{
1704 if (!context->break_allowed) {
1826 AstNode *loop_node = context->parent_loop_node;
1827 if (loop_node) {
1828 loop_node->codegen_node->data.while_node.contains_break = true;
1829 } else {
17051830 add_node_error(g, node,
1706 buf_sprintf("'break' expression not in loop"));
1831 buf_sprintf("'break' expression outside loop"));
17071832 }
17081833 return g->builtin_types.entry_unreachable;
17091834}
......@@ -1711,9 +1836,9 @@ static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import,
17111836static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
17121837 TypeTableEntry *expected_type, AstNode *node)
17131838{
1714 if (!context->break_allowed) {
1839 if (!context->parent_loop_node) {
17151840 add_node_error(g, node,
1716 buf_sprintf("'continue' expression not in loop"));
1841 buf_sprintf("'continue' expression outside loop"));
17171842 }
17181843 return g->builtin_types.entry_unreachable;
17191844}
src/analyze.hpp+7-2
......@@ -244,8 +244,8 @@ struct BlockContext {
244244 HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
245245 ZigList<CastNode *> cast_expr_alloca_list;
246246 ZigList<StructValExprNode *> struct_val_expr_alloca_list;
247 bool break_allowed;
248 bool next_child_break_allowed;
247 AstNode *parent_loop_node;
248 AstNode *next_child_parent_loop_node;
249249 LLVMZigDIScope *di_scope;
250250};
251251
......@@ -340,6 +340,10 @@ struct ImportNode {
340340 ImportTableEntry *import;
341341};
342342
343struct WhileNode {
344 bool contains_break;
345};
346
343347struct CodeGenNode {
344348 union {
345349 TypeNode type_node; // for NodeTypeType
......@@ -358,6 +362,7 @@ struct CodeGenNode {
358362 IfVarNode if_var_node; // for NodeTypeStructValueExpr
359363 ParamDeclNode param_decl_node; // for NodeTypeParamDecl
360364 ImportNode import_node; // for NodeTypeUse
365 WhileNode while_node; // for NodeTypeWhileExpr
361366 } data;
362367 ExprNode expr_node; // for all the expression nodes
363368};
src/codegen.cpp+42-19
......@@ -1157,29 +1157,52 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
11571157 assert(node->data.while_expr.condition);
11581158 assert(node->data.while_expr.body);
11591159
1160 LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileCond");
1161 LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
1162 LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
1160 if (get_expr_type(node)->id == TypeTableEntryIdUnreachable) {
1161 // generate a forever loop. guarantees no break statements
11631162
1164 add_debug_source_node(g, node);
1165 LLVMBuildBr(g->builder, cond_block);
1166
1167 LLVMPositionBuilderAtEnd(g->builder, cond_block);
1168 LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition);
1169 add_debug_source_node(g, node->data.while_expr.condition);
1170 LLVMBuildCondBr(g->builder, cond_val, body_block, end_block);
1171
1172 LLVMPositionBuilderAtEnd(g->builder, body_block);
1173 g->break_block_stack.append(end_block);
1174 g->continue_block_stack.append(cond_block);
1175 gen_expr(g, node->data.while_expr.body);
1176 g->break_block_stack.pop();
1177 g->continue_block_stack.pop();
1178 if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
1163 LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
1164
1165 add_debug_source_node(g, node);
1166 LLVMBuildBr(g->builder, body_block);
1167
1168 LLVMPositionBuilderAtEnd(g->builder, body_block);
1169 g->continue_block_stack.append(body_block);
1170 gen_expr(g, node->data.while_expr.body);
1171 g->continue_block_stack.pop();
1172
1173 if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
1174 add_debug_source_node(g, node);
1175 LLVMBuildBr(g->builder, body_block);
1176 }
1177 } else {
1178 // generate a normal while loop
1179
1180 LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileCond");
1181 LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
1182 LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
1183
1184 add_debug_source_node(g, node);
11791185 LLVMBuildBr(g->builder, cond_block);
1186
1187 LLVMPositionBuilderAtEnd(g->builder, cond_block);
1188 LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition);
1189 add_debug_source_node(g, node->data.while_expr.condition);
1190 LLVMBuildCondBr(g->builder, cond_val, body_block, end_block);
1191
1192 LLVMPositionBuilderAtEnd(g->builder, body_block);
1193 g->break_block_stack.append(end_block);
1194 g->continue_block_stack.append(cond_block);
1195 gen_expr(g, node->data.while_expr.body);
1196 g->break_block_stack.pop();
1197 g->continue_block_stack.pop();
1198 if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
1199 add_debug_source_node(g, node);
1200 LLVMBuildBr(g->builder, cond_block);
1201 }
1202
1203 LLVMPositionBuilderAtEnd(g->builder, end_block);
11801204 }
11811205
1182 LLVMPositionBuilderAtEnd(g->builder, end_block);
11831206 return nullptr;
11841207}
11851208
std/rand.zig-3
......@@ -67,9 +67,6 @@ pub struct Rand {
6767 return start + (rand_val % range);
6868 }
6969 }
70 // TODO detect simple constant in while loop and no breaks and turn it into unreachable
71 // type. then we can remove this unreachable.
72 unreachable;
7370 }
7471
7572 fn generate_numbers(r: &Rand) {
test/run_tests.cpp+8-3
......@@ -683,7 +683,12 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
683683 print_str("loop\n");
684684 i += 1;
685685 }
686 return 0;
686 return f();
687}
688fn f() -> i32 {
689 while (true) {
690 return 0;
691 }
687692}
688693 )SOURCE", "loop\nloop\nloop\nloop\n");
689694
......@@ -1168,13 +1173,13 @@ fn f() {
11681173fn f() {
11691174 break;
11701175}
1171 )SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression not in loop");
1176 )SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression outside loop");
11721177
11731178 add_compile_fail_case("invalid continue expression", R"SOURCE(
11741179fn f() {
11751180 continue;
11761181}
1177 )SOURCE", 1, ".tmp_source.zig:3:5: error: 'continue' expression not in loop");
1182 )SOURCE", 1, ".tmp_source.zig:3:5: error: 'continue' expression outside loop");
11781183
11791184 add_compile_fail_case("invalid maybe type", R"SOURCE(
11801185fn f() {