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 {...@@ -27,6 +27,8 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
27 print_u64(answer);27 print_u64(answer);
28 print_str("\n");28 print_str("\n");
2929
30 return 0;
31
30 /*32 /*
31 while (true) {33 while (true) {
32 const line = readline("\nGuess a number between 1 and 100: ");34 const line = readline("\nGuess a number between 1 and 100: ");
...@@ -45,6 +47,4 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {...@@ -45,6 +47,4 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
45 }47 }
46 }48 }
47 */49 */
48
49 return 0;
50}50}
src/analyze.cpp+137-12
...@@ -12,6 +12,8 @@...@@ -12,6 +12,8 @@
1212
13static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,13static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
14 TypeTableEntry *expected_type, AstNode *node);14 TypeTableEntry *expected_type, AstNode *node);
15static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
16 AstNode *node, AstNodeNumberLiteral *out_number_literal);
1517
16static AstNode *first_executing_node(AstNode *node) {18static AstNode *first_executing_node(AstNode *node) {
17 switch (node->type) {19 switch (node->type) {
...@@ -284,6 +286,98 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry...@@ -284,6 +286,98 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry
284 }286 }
285}287}
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
287static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,381static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
288 AstNode *node, AstNodeNumberLiteral *out_number_literal)382 AstNode *node, AstNodeNumberLiteral *out_number_literal)
289{383{
...@@ -291,9 +385,11 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,...@@ -291,9 +385,11 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
291 case NodeTypeNumberLiteral:385 case NodeTypeNumberLiteral:
292 *out_number_literal = node->data.number_literal;386 *out_number_literal = node->data.number_literal;
293 return node->codegen_node->expr_node.type_entry;387 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;
294 case NodeTypeBinOpExpr:391 case NodeTypeBinOpExpr:
295 zig_panic("TODO eval_const_expr bin op expr");392 return eval_const_expr_bin_op(g, context, node, out_number_literal);
296 break;
297 case NodeTypeCompilerFnType:393 case NodeTypeCompilerFnType:
298 {394 {
299 Buf *name = &node->data.compiler_fn_type.name;395 Buf *name = &node->data.compiler_fn_type.name;
...@@ -1133,8 +1229,12 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {...@@ -1133,8 +1229,12 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
1133 context->variable_table.init(8);1229 context->variable_table.init(8);
11341230
1135 if (parent) {1231 if (parent) {
1136 context->break_allowed = parent->break_allowed || parent->next_child_break_allowed;1232 if (parent->next_child_parent_loop_node) {
1137 parent->next_child_break_allowed = false;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 }
1138 }1238 }
11391239
1140 if (node && node->type == NodeTypeFnDef) {1240 if (node && node->type == NodeTypeFnDef) {
...@@ -1690,20 +1790,45 @@ static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *imp...@@ -1690,20 +1790,45 @@ static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *imp
1690static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,1790static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
1691 TypeTableEntry *expected_type, AstNode *node)1791 TypeTableEntry *expected_type, AstNode *node)
1692{1792{
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;1804 if (condition_type->id == TypeTableEntryIdInvalid) {
1696 analyze_expression(g, import, context, g->builtin_types.entry_void, node->data.while_expr.body);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;
1699}1821}
17001822
1701static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,1823static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
1702 TypeTableEntry *expected_type, AstNode *node)1824 TypeTableEntry *expected_type, AstNode *node)
1703{1825{
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 {
1705 add_node_error(g, node,1830 add_node_error(g, node,
1706 buf_sprintf("'break' expression not in loop"));1831 buf_sprintf("'break' expression outside loop"));
1707 }1832 }
1708 return g->builtin_types.entry_unreachable;1833 return g->builtin_types.entry_unreachable;
1709}1834}
...@@ -1711,9 +1836,9 @@ static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import,...@@ -1711,9 +1836,9 @@ static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import,
1711static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,1836static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
1712 TypeTableEntry *expected_type, AstNode *node)1837 TypeTableEntry *expected_type, AstNode *node)
1713{1838{
1714 if (!context->break_allowed) {1839 if (!context->parent_loop_node) {
1715 add_node_error(g, node,1840 add_node_error(g, node,
1716 buf_sprintf("'continue' expression not in loop"));1841 buf_sprintf("'continue' expression outside loop"));
1717 }1842 }
1718 return g->builtin_types.entry_unreachable;1843 return g->builtin_types.entry_unreachable;
1719}1844}
src/analyze.hpp+7-2
...@@ -244,8 +244,8 @@ struct BlockContext {...@@ -244,8 +244,8 @@ struct BlockContext {
244 HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;244 HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
245 ZigList<CastNode *> cast_expr_alloca_list;245 ZigList<CastNode *> cast_expr_alloca_list;
246 ZigList<StructValExprNode *> struct_val_expr_alloca_list;246 ZigList<StructValExprNode *> struct_val_expr_alloca_list;
247 bool break_allowed;247 AstNode *parent_loop_node;
248 bool next_child_break_allowed;248 AstNode *next_child_parent_loop_node;
249 LLVMZigDIScope *di_scope;249 LLVMZigDIScope *di_scope;
250};250};
251251
...@@ -340,6 +340,10 @@ struct ImportNode {...@@ -340,6 +340,10 @@ struct ImportNode {
340 ImportTableEntry *import;340 ImportTableEntry *import;
341};341};
342342
343struct WhileNode {
344 bool contains_break;
345};
346
343struct CodeGenNode {347struct CodeGenNode {
344 union {348 union {
345 TypeNode type_node; // for NodeTypeType349 TypeNode type_node; // for NodeTypeType
...@@ -358,6 +362,7 @@ struct CodeGenNode {...@@ -358,6 +362,7 @@ struct CodeGenNode {
358 IfVarNode if_var_node; // for NodeTypeStructValueExpr362 IfVarNode if_var_node; // for NodeTypeStructValueExpr
359 ParamDeclNode param_decl_node; // for NodeTypeParamDecl363 ParamDeclNode param_decl_node; // for NodeTypeParamDecl
360 ImportNode import_node; // for NodeTypeUse364 ImportNode import_node; // for NodeTypeUse
365 WhileNode while_node; // for NodeTypeWhileExpr
361 } data;366 } data;
362 ExprNode expr_node; // for all the expression nodes367 ExprNode expr_node; // for all the expression nodes
363};368};
src/codegen.cpp+42-19
...@@ -1157,29 +1157,52 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {...@@ -1157,29 +1157,52 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
1157 assert(node->data.while_expr.condition);1157 assert(node->data.while_expr.condition);
1158 assert(node->data.while_expr.body);1158 assert(node->data.while_expr.body);
11591159
1160 LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileCond");1160 if (get_expr_type(node)->id == TypeTableEntryIdUnreachable) {
1161 LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");1161 // generate a forever loop. guarantees no break statements
1162 LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
11631162
1164 add_debug_source_node(g, node);1163 LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
1165 LLVMBuildBr(g->builder, cond_block);1164
11661165 add_debug_source_node(g, node);
1167 LLVMPositionBuilderAtEnd(g->builder, cond_block);1166 LLVMBuildBr(g->builder, body_block);
1168 LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition);1167
1169 add_debug_source_node(g, node->data.while_expr.condition);1168 LLVMPositionBuilderAtEnd(g->builder, body_block);
1170 LLVMBuildCondBr(g->builder, cond_val, body_block, end_block);1169 g->continue_block_stack.append(body_block);
11711170 gen_expr(g, node->data.while_expr.body);
1172 LLVMPositionBuilderAtEnd(g->builder, body_block);1171 g->continue_block_stack.pop();
1173 g->break_block_stack.append(end_block);1172
1174 g->continue_block_stack.append(cond_block);1173 if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
1175 gen_expr(g, node->data.while_expr.body);1174 add_debug_source_node(g, node);
1176 g->break_block_stack.pop();1175 LLVMBuildBr(g->builder, body_block);
1177 g->continue_block_stack.pop();1176 }
1178 if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {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);
1179 LLVMBuildBr(g->builder, cond_block);1185 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);
1180 }1204 }
11811205
1182 LLVMPositionBuilderAtEnd(g->builder, end_block);
1183 return nullptr;1206 return nullptr;
1184}1207}
11851208
std/rand.zig-3
...@@ -67,9 +67,6 @@ pub struct Rand {...@@ -67,9 +67,6 @@ pub struct Rand {
67 return start + (rand_val % range);67 return start + (rand_val % range);
68 }68 }
69 }69 }
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;
73 }70 }
7471
75 fn generate_numbers(r: &Rand) {72 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 {...@@ -683,7 +683,12 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
683 print_str("loop\n");683 print_str("loop\n");
684 i += 1;684 i += 1;
685 }685 }
686 return 0;686 return f();
687}
688fn f() -> i32 {
689 while (true) {
690 return 0;
691 }
687}692}
688 )SOURCE", "loop\nloop\nloop\nloop\n");693 )SOURCE", "loop\nloop\nloop\nloop\n");
689694
...@@ -1168,13 +1173,13 @@ fn f() {...@@ -1168,13 +1173,13 @@ fn f() {
1168fn f() {1173fn f() {
1169 break;1174 break;
1170}1175}
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
1173 add_compile_fail_case("invalid continue expression", R"SOURCE(1178 add_compile_fail_case("invalid continue expression", R"SOURCE(
1174fn f() {1179fn f() {
1175 continue;1180 continue;
1176}1181}
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
1179 add_compile_fail_case("invalid maybe type", R"SOURCE(1184 add_compile_fail_case("invalid maybe type", R"SOURCE(
1180fn f() {1185fn f() {