while detects simple constant condition

This commit is contained in:
Andrew Kelley 2016-01-06 18:02:42 -07:00
parent 5f0bfcac24
commit c75d40680f
6 changed files with 194 additions and 39 deletions

View File

@ -27,6 +27,8 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
print_u64(answer); print_u64(answer);
print_str("\n"); print_str("\n");
return 0;
/* /*
while (true) { while (true) {
const line = readline("\nGuess a number between 1 and 100: "); const line = readline("\nGuess a number between 1 and 100: ");
@ -45,6 +47,4 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
} }
} }
*/ */
return 0;
} }

View File

@ -12,6 +12,8 @@
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node); TypeTableEntry *expected_type, AstNode *node);
static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
AstNode *node, AstNodeNumberLiteral *out_number_literal);
static AstNode *first_executing_node(AstNode *node) { static AstNode *first_executing_node(AstNode *node) {
switch (node->type) { switch (node->type) {
@ -284,6 +286,98 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry
} }
} }
static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
AstNode *node, AstNodeNumberLiteral *out_number_literal)
{
AstNodeNumberLiteral op1_lit;
AstNodeNumberLiteral op2_lit;
TypeTableEntry *op1_type = eval_const_expr(g, context, node->data.bin_op_expr.op1, &op1_lit);
TypeTableEntry *op2_type = eval_const_expr(g, context, node->data.bin_op_expr.op1, &op2_lit);
if (op1_type->id == TypeTableEntryIdInvalid ||
op2_type->id == TypeTableEntryIdInvalid)
{
return g->builtin_types.entry_invalid;
}
// TODO complete more of this function instead of returning invalid
// returning invalid makes the "unable to evaluate constant expression" error
switch (node->data.bin_op_expr.bin_op) {
case BinOpTypeCmpNotEq:
{
if (is_num_lit_unsigned(op1_lit.kind) &&
is_num_lit_unsigned(op2_lit.kind))
{
out_number_literal->kind = NumLitU8;
out_number_literal->overflow = false;
out_number_literal->data.x_uint = (op1_lit.data.x_uint != op2_lit.data.x_uint);
return node->codegen_node->expr_node.type_entry;
} else {
return g->builtin_types.entry_invalid;
}
}
case BinOpTypeCmpLessThan:
{
if (is_num_lit_unsigned(op1_lit.kind) &&
is_num_lit_unsigned(op2_lit.kind))
{
out_number_literal->kind = NumLitU8;
out_number_literal->overflow = false;
out_number_literal->data.x_uint = (op1_lit.data.x_uint < op2_lit.data.x_uint);
return node->codegen_node->expr_node.type_entry;
} else {
return g->builtin_types.entry_invalid;
}
}
case BinOpTypeMod:
{
if (is_num_lit_unsigned(op1_lit.kind) &&
is_num_lit_unsigned(op2_lit.kind))
{
out_number_literal->kind = NumLitU64;
out_number_literal->overflow = false;
out_number_literal->data.x_uint = (op1_lit.data.x_uint % op2_lit.data.x_uint);
return node->codegen_node->expr_node.type_entry;
} else {
return g->builtin_types.entry_invalid;
}
}
case BinOpTypeBoolOr:
case BinOpTypeBoolAnd:
case BinOpTypeCmpEq:
case BinOpTypeCmpGreaterThan:
case BinOpTypeCmpLessOrEq:
case BinOpTypeCmpGreaterOrEq:
case BinOpTypeBinOr:
case BinOpTypeBinXor:
case BinOpTypeBinAnd:
case BinOpTypeBitShiftLeft:
case BinOpTypeBitShiftRight:
case BinOpTypeAdd:
case BinOpTypeSub:
case BinOpTypeMult:
case BinOpTypeDiv:
return g->builtin_types.entry_invalid;
case BinOpTypeInvalid:
case BinOpTypeAssign:
case BinOpTypeAssignTimes:
case BinOpTypeAssignDiv:
case BinOpTypeAssignMod:
case BinOpTypeAssignPlus:
case BinOpTypeAssignMinus:
case BinOpTypeAssignBitShiftLeft:
case BinOpTypeAssignBitShiftRight:
case BinOpTypeAssignBitAnd:
case BinOpTypeAssignBitXor:
case BinOpTypeAssignBitOr:
case BinOpTypeAssignBoolAnd:
case BinOpTypeAssignBoolOr:
zig_unreachable();
}
zig_unreachable();
}
static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context, static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
AstNode *node, AstNodeNumberLiteral *out_number_literal) AstNode *node, AstNodeNumberLiteral *out_number_literal)
{ {
@ -291,9 +385,11 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
case NodeTypeNumberLiteral: case NodeTypeNumberLiteral:
*out_number_literal = node->data.number_literal; *out_number_literal = node->data.number_literal;
return node->codegen_node->expr_node.type_entry; return node->codegen_node->expr_node.type_entry;
case NodeTypeBoolLiteral:
out_number_literal->data.x_uint = node->data.bool_literal ? 1 : 0;
return node->codegen_node->expr_node.type_entry;
case NodeTypeBinOpExpr: case NodeTypeBinOpExpr:
zig_panic("TODO eval_const_expr bin op expr"); return eval_const_expr_bin_op(g, context, node, out_number_literal);
break;
case NodeTypeCompilerFnType: case NodeTypeCompilerFnType:
{ {
Buf *name = &node->data.compiler_fn_type.name; Buf *name = &node->data.compiler_fn_type.name;
@ -1133,8 +1229,12 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
context->variable_table.init(8); context->variable_table.init(8);
if (parent) { if (parent) {
context->break_allowed = parent->break_allowed || parent->next_child_break_allowed; if (parent->next_child_parent_loop_node) {
parent->next_child_break_allowed = false; context->parent_loop_node = parent->next_child_parent_loop_node;
parent->next_child_parent_loop_node = nullptr;
} else {
context->parent_loop_node = parent->parent_loop_node;
}
} }
if (node && node->type == NodeTypeFnDef) { if (node && node->type == NodeTypeFnDef) {
@ -1690,20 +1790,45 @@ static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *imp
static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node) TypeTableEntry *expected_type, AstNode *node)
{ {
analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.while_expr.condition); AstNode *condition_node = node->data.while_expr.condition;
AstNode *while_body_node = node->data.while_expr.body;
TypeTableEntry *condition_type = analyze_expression(g, import, context,
g->builtin_types.entry_bool, condition_node);
context->next_child_break_allowed = true; context->next_child_parent_loop_node = node;
analyze_expression(g, import, context, g->builtin_types.entry_void, node->data.while_expr.body); analyze_expression(g, import, context, g->builtin_types.entry_void, while_body_node);
return g->builtin_types.entry_void;
TypeTableEntry *expr_return_type = g->builtin_types.entry_void;
if (condition_type->id == TypeTableEntryIdInvalid) {
expr_return_type = g->builtin_types.entry_invalid;
} else {
// if the condition is a simple constant expression and there are no break statements
// then the return type is unreachable
AstNodeNumberLiteral number_literal;
TypeTableEntry *resolved_type = eval_const_expr(g, context, condition_node, &number_literal);
if (resolved_type->id != TypeTableEntryIdInvalid) {
assert(resolved_type->id == TypeTableEntryIdBool);
bool constant_cond_value = number_literal.data.x_uint;
if (constant_cond_value && !node->codegen_node->data.while_node.contains_break) {
expr_return_type = g->builtin_types.entry_unreachable;
}
}
}
return expr_return_type;
} }
static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node) TypeTableEntry *expected_type, AstNode *node)
{ {
if (!context->break_allowed) { AstNode *loop_node = context->parent_loop_node;
if (loop_node) {
loop_node->codegen_node->data.while_node.contains_break = true;
} else {
add_node_error(g, node, add_node_error(g, node,
buf_sprintf("'break' expression not in loop")); buf_sprintf("'break' expression outside loop"));
} }
return g->builtin_types.entry_unreachable; return g->builtin_types.entry_unreachable;
} }
@ -1711,9 +1836,9 @@ static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import,
static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node) TypeTableEntry *expected_type, AstNode *node)
{ {
if (!context->break_allowed) { if (!context->parent_loop_node) {
add_node_error(g, node, add_node_error(g, node,
buf_sprintf("'continue' expression not in loop")); buf_sprintf("'continue' expression outside loop"));
} }
return g->builtin_types.entry_unreachable; return g->builtin_types.entry_unreachable;
} }

View File

@ -244,8 +244,8 @@ struct BlockContext {
HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table; HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
ZigList<CastNode *> cast_expr_alloca_list; ZigList<CastNode *> cast_expr_alloca_list;
ZigList<StructValExprNode *> struct_val_expr_alloca_list; ZigList<StructValExprNode *> struct_val_expr_alloca_list;
bool break_allowed; AstNode *parent_loop_node;
bool next_child_break_allowed; AstNode *next_child_parent_loop_node;
LLVMZigDIScope *di_scope; LLVMZigDIScope *di_scope;
}; };
@ -340,6 +340,10 @@ struct ImportNode {
ImportTableEntry *import; ImportTableEntry *import;
}; };
struct WhileNode {
bool contains_break;
};
struct CodeGenNode { struct CodeGenNode {
union { union {
TypeNode type_node; // for NodeTypeType TypeNode type_node; // for NodeTypeType
@ -358,6 +362,7 @@ struct CodeGenNode {
IfVarNode if_var_node; // for NodeTypeStructValueExpr IfVarNode if_var_node; // for NodeTypeStructValueExpr
ParamDeclNode param_decl_node; // for NodeTypeParamDecl ParamDeclNode param_decl_node; // for NodeTypeParamDecl
ImportNode import_node; // for NodeTypeUse ImportNode import_node; // for NodeTypeUse
WhileNode while_node; // for NodeTypeWhileExpr
} data; } data;
ExprNode expr_node; // for all the expression nodes ExprNode expr_node; // for all the expression nodes
}; };

View File

@ -1157,29 +1157,52 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
assert(node->data.while_expr.condition); assert(node->data.while_expr.condition);
assert(node->data.while_expr.body); assert(node->data.while_expr.body);
LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileCond"); if (get_expr_type(node)->id == TypeTableEntryIdUnreachable) {
LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody"); // generate a forever loop. guarantees no break statements
LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
add_debug_source_node(g, node); LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
LLVMBuildBr(g->builder, cond_block);
LLVMPositionBuilderAtEnd(g->builder, cond_block); add_debug_source_node(g, node);
LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition); LLVMBuildBr(g->builder, body_block);
add_debug_source_node(g, node->data.while_expr.condition);
LLVMBuildCondBr(g->builder, cond_val, body_block, end_block);
LLVMPositionBuilderAtEnd(g->builder, body_block); LLVMPositionBuilderAtEnd(g->builder, body_block);
g->break_block_stack.append(end_block); g->continue_block_stack.append(body_block);
g->continue_block_stack.append(cond_block); gen_expr(g, node->data.while_expr.body);
gen_expr(g, node->data.while_expr.body); g->continue_block_stack.pop();
g->break_block_stack.pop();
g->continue_block_stack.pop(); if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) { add_debug_source_node(g, node);
LLVMBuildBr(g->builder, body_block);
}
} else {
// generate a normal while loop
LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileCond");
LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, cond_block); LLVMBuildBr(g->builder, cond_block);
LLVMPositionBuilderAtEnd(g->builder, cond_block);
LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition);
add_debug_source_node(g, node->data.while_expr.condition);
LLVMBuildCondBr(g->builder, cond_val, body_block, end_block);
LLVMPositionBuilderAtEnd(g->builder, body_block);
g->break_block_stack.append(end_block);
g->continue_block_stack.append(cond_block);
gen_expr(g, node->data.while_expr.body);
g->break_block_stack.pop();
g->continue_block_stack.pop();
if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, cond_block);
}
LLVMPositionBuilderAtEnd(g->builder, end_block);
} }
LLVMPositionBuilderAtEnd(g->builder, end_block);
return nullptr; return nullptr;
} }

View File

@ -67,9 +67,6 @@ pub struct Rand {
return start + (rand_val % range); return start + (rand_val % range);
} }
} }
// TODO detect simple constant in while loop and no breaks and turn it into unreachable
// type. then we can remove this unreachable.
unreachable;
} }
fn generate_numbers(r: &Rand) { fn generate_numbers(r: &Rand) {

View File

@ -683,7 +683,12 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
print_str("loop\n"); print_str("loop\n");
i += 1; i += 1;
} }
return 0; return f();
}
fn f() -> i32 {
while (true) {
return 0;
}
} }
)SOURCE", "loop\nloop\nloop\nloop\n"); )SOURCE", "loop\nloop\nloop\nloop\n");
@ -1168,13 +1173,13 @@ fn f() {
fn f() { fn f() {
break; break;
} }
)SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression not in loop"); )SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression outside loop");
add_compile_fail_case("invalid continue expression", R"SOURCE( add_compile_fail_case("invalid continue expression", R"SOURCE(
fn f() { fn f() {
continue; continue;
} }
)SOURCE", 1, ".tmp_source.zig:3:5: error: 'continue' expression not in loop"); )SOURCE", 1, ".tmp_source.zig:3:5: error: 'continue' expression outside loop");
add_compile_fail_case("invalid maybe type", R"SOURCE( add_compile_fail_case("invalid maybe type", R"SOURCE(
fn f() { fn f() {