aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2016-02-09 18:50:53 -0700
committerAndrew Kelley <superjoe30@gmail.com>2016-02-09 18:53:28 -0700
commitd8f6388b63b3c4abe235a5fe0a49e9b18f6fb2f5 (patch)
tree4ed6e6ba5a173ca66f5137af175d170966838e54 /src
parentf45c374664b38dcbf8c65c938863ed10d0dda25a (diff)
downloadzig-d8f6388b63b3c4abe235a5fe0a49e9b18f6fb2f5.tar.gz
zig-d8f6388b63b3c4abe235a5fe0a49e9b18f6fb2f5.zip
if statements can be const expr evaluated
also introduce error for unnecessary if statement but if the condition depends on a compile variable, then the if statement is OK
Diffstat (limited to 'src')
-rw-r--r--src/all_types.hpp25
-rw-r--r--src/analyze.cpp136
-rw-r--r--src/codegen.cpp115
3 files changed, 164 insertions, 112 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp
index e831facfc8..599307cea9 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1049,6 +1049,19 @@ struct BuiltinFnEntry {
LLVMValueRef fn_val;
};
+enum CIntType {
+ CIntTypeShort,
+ CIntTypeUShort,
+ CIntTypeInt,
+ CIntTypeUInt,
+ CIntTypeLong,
+ CIntTypeULong,
+ CIntTypeLongLong,
+ CIntTypeULongLong,
+
+ CIntTypeCount,
+};
+
struct CodeGen {
LLVMModuleRef module;
ZigList<ErrorMsg*> errors;
@@ -1072,7 +1085,7 @@ struct CodeGen {
struct {
TypeTableEntry *entry_bool;
TypeTableEntry *entry_int[2][4]; // [signed,unsigned][8,16,32,64]
- TypeTableEntry *entry_c_int[8];
+ TypeTableEntry *entry_c_int[CIntTypeCount];
TypeTableEntry *entry_c_long_double;
TypeTableEntry *entry_u8;
TypeTableEntry *entry_u16;
@@ -1196,16 +1209,6 @@ struct BlockContext {
Buf *c_import_buf;
};
-enum CIntType {
- CIntTypeShort,
- CIntTypeUShort,
- CIntTypeInt,
- CIntTypeUInt,
- CIntTypeLong,
- CIntTypeULong,
- CIntTypeLongLong,
- CIntTypeULongLong,
-};
#endif
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 700a3e7a85..02b1c3010e 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2159,6 +2159,7 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry
&get_resolved_expr(val_field_node->data.struct_val_field.expr)->const_val;
if (field_val->ok) {
const_val->data.x_struct.fields[field_index] = field_val;
+ const_val->depends_on_compile_var = const_val->depends_on_compile_var || field_val->depends_on_compile_var;
} else {
const_val->ok = false;
}
@@ -2197,6 +2198,8 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry
ConstExprValue *elem_const_val = &get_resolved_expr(*elem_node)->const_val;
if (elem_const_val->ok) {
const_val->data.x_array.fields[i] = elem_const_val;
+ const_val->depends_on_compile_var = const_val->depends_on_compile_var ||
+ elem_const_val->depends_on_compile_var;
} else {
const_val->ok = false;
}
@@ -2431,9 +2434,12 @@ static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node,
return g->builtin_types.entry_pure_error;
}
-static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value) {
+static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value,
+ bool depends_on_compile_var)
+{
Expr *expr = get_resolved_expr(node);
expr->const_val.ok = true;
+ expr->const_val.depends_on_compile_var = depends_on_compile_var;
expr->const_val.data.x_bool = value;
return g->builtin_types.entry_bool;
}
@@ -2817,7 +2823,8 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
zig_unreachable();
}
- return resolve_expr_const_val_as_bool(g, node, answer);
+ bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
+ return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var);
}
static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -2844,7 +2851,8 @@ static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *i
}
bool answer = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op_type, op2_val->data.x_bool);
- return resolve_expr_const_val_as_bool(g, node, answer);
+ bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
+ return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var);
}
static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -3001,6 +3009,8 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
}
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
const_val->ok = true;
+ const_val->depends_on_compile_var = op1_val->depends_on_compile_var ||
+ op2_val->depends_on_compile_var;
ConstExprValue *all_fields = allocate<ConstExprValue>(2);
ConstExprValue *ptr_field = &all_fields[0];
@@ -3444,51 +3454,111 @@ static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *impor
return g->builtin_types.entry_unreachable;
}
-static TypeTableEntry *analyze_if_then_else(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *then_block, AstNode *else_node, AstNode *parent_node)
+static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+ TypeTableEntry *expected_type, AstNode *node,
+ AstNode **then_node, AstNode **else_node, bool cond_is_const, bool cond_bool_val)
{
- TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, then_block);
-
- TypeTableEntry *else_type;
- if (else_node) {
- else_type = analyze_expression(g, import, context, expected_type, else_node);
- } else {
- else_type = resolve_type_compatibility(g, import, context, parent_node, expected_type,
- g->builtin_types.entry_void);
+ if (!*else_node) {
+ *else_node = create_ast_void_node(g, import, node);
+ normalize_parent_ptrs(node);
}
+ TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, *then_node);
+ TypeTableEntry *else_type = analyze_expression(g, import, context, expected_type, *else_node);
+ if (then_type->id == TypeTableEntryIdInvalid || else_type->id == TypeTableEntryIdInvalid) {
+ return g->builtin_types.entry_invalid;
+ }
+
+ TypeTableEntry *result_type;
if (expected_type) {
- return (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
+ result_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
} else {
- AstNode *op_nodes[] = {then_block, else_node};
+ AstNode *op_nodes[] = {*then_node, *else_node};
TypeTableEntry *op_types[] = {then_type, else_type};
- return resolve_peer_type_compatibility(g, import, context, parent_node, op_nodes, op_types, 2);
+ result_type = resolve_peer_type_compatibility(g, import, context, node, op_nodes, op_types, 2);
+ }
+
+ if (!cond_is_const) {
+ return result_type;
}
+
+ ConstExprValue *other_const_val;
+ if (cond_bool_val) {
+ other_const_val = &get_resolved_expr(*then_node)->const_val;
+ } else {
+ other_const_val = &get_resolved_expr(*else_node)->const_val;
+ }
+ if (!other_const_val->ok) {
+ return result_type;
+ }
+
+ ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+ *const_val = *other_const_val;
+ return result_type;
}
static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
- analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_bool_expr.condition);
+ AstNode **cond = &node->data.if_bool_expr.condition;
+ TypeTableEntry *cond_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, *cond);
+
+ if (cond_type->id == TypeTableEntryIdInvalid) {
+ return cond_type;
+ }
- return analyze_if_then_else(g, import, context, expected_type,
- node->data.if_bool_expr.then_block,
- node->data.if_bool_expr.else_node,
- node);
+ ConstExprValue *cond_val = &get_resolved_expr(*cond)->const_val;
+ if (cond_val->ok && !cond_val->depends_on_compile_var) {
+ const char *str_val = cond_val->data.x_bool ? "true" : "false";
+ add_node_error(g, first_executing_node(*cond),
+ buf_sprintf("condition is always %s; unnecessary if statement", str_val));
+ }
+
+ bool cond_is_const = cond_val->ok;
+ bool cond_bool_val = cond_val->data.x_bool;
+
+ AstNode **then_node = &node->data.if_bool_expr.then_block;
+ AstNode **else_node = &node->data.if_bool_expr.else_node;
+
+ return analyze_if(g, import, context, expected_type, node,
+ then_node, else_node, cond_is_const, cond_bool_val);
}
-static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
TypeTableEntry *expected_type, AstNode *node)
{
assert(node->type == NodeTypeIfVarExpr);
- BlockContext *child_context = new_block_context(node, context);
+ BlockContext *child_context = new_block_context(node, parent_context);
analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true);
+ VariableTableEntry *var = node->data.if_var_expr.var_decl.variable;
+ if (var->type->id == TypeTableEntryIdInvalid) {
+ return g->builtin_types.entry_invalid;
+ }
+ AstNode *var_expr_node = node->data.if_var_expr.var_decl.expr;
+ ConstExprValue *var_const_val = &get_resolved_expr(var_expr_node)->const_val;
+ bool cond_is_const = var_const_val->ok;
+ bool cond_bool_val = cond_is_const ? (var_const_val->data.x_maybe != nullptr) : false;
+
+
+ AstNode **then_node = &node->data.if_var_expr.then_block;
+ AstNode **else_node = &node->data.if_var_expr.else_node;
- return analyze_if_then_else(g, import, child_context, expected_type,
- node->data.if_var_expr.then_block, node->data.if_var_expr.else_node, node);
+ return analyze_if(g, import, child_context, expected_type,
+ node, then_node, else_node, cond_is_const, cond_bool_val);
+}
+
+static bool int_type_depends_on_compile_var(CodeGen *g, TypeTableEntry *int_type) {
+ assert(int_type->id == TypeTableEntryIdInt);
+
+ for (int i = 0; i < CIntTypeCount; i += 1) {
+ if (int_type == g->builtin_types.entry_c_int[i]) {
+ return true;
+ }
+ }
+ return false;
}
static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -3504,6 +3574,7 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
} else if (type_entry->id == TypeTableEntryIdInt) {
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
const_val->ok = true;
+ const_val->depends_on_compile_var = int_type_depends_on_compile_var(g, type_entry);
if (is_max) {
if (type_entry->data.integral.is_signed) {
int64_t val;
@@ -3558,7 +3629,7 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
zig_panic("TODO analyze_min_max_value float");
return type_entry;
} else if (type_entry->id == TypeTableEntryIdBool) {
- return resolve_expr_const_val_as_bool(g, node, is_max);
+ return resolve_expr_const_val_as_bool(g, node, is_max, false);
} else {
add_node_error(g, node,
buf_sprintf(err_format, buf_ptr(&type_entry->name)));
@@ -3573,6 +3644,8 @@ static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *ex
if (!other_val->ok) {
return;
}
+ const_val->depends_on_compile_var = other_val->depends_on_compile_var;
+
assert(other_val != const_val);
switch (node->data.fn_call_expr.cast_op) {
case CastOpNoCast:
@@ -4132,11 +4205,11 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
const_val->depends_on_compile_var = true;
if (buf_eql_str(&var_name, "is_big_endian")) {
- return resolve_expr_const_val_as_bool(g, node, g->is_big_endian);
+ return resolve_expr_const_val_as_bool(g, node, g->is_big_endian, true);
} else if (buf_eql_str(&var_name, "is_release")) {
- return resolve_expr_const_val_as_bool(g, node, g->is_release_build);
+ return resolve_expr_const_val_as_bool(g, node, g->is_release_build, true);
} else if (buf_eql_str(&var_name, "is_test")) {
- return resolve_expr_const_val_as_bool(g, node, g->is_test_build);
+ return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true);
} else {
add_node_error(g, *str_node,
buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name)));
@@ -4353,7 +4426,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
}
bool answer = !target_const_val->data.x_bool;
- return resolve_expr_const_val_as_bool(g, node, answer);
+ return resolve_expr_const_val_as_bool(g, node, answer, target_const_val->depends_on_compile_var);
}
case PrefixOpBinNot:
{
@@ -4390,6 +4463,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
}
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
const_val->ok = true;
+ const_val->depends_on_compile_var = target_const_val->depends_on_compile_var;
bignum_negate(&const_val->data.x_bignum, &target_const_val->data.x_bignum);
return expr_type;
} else {
@@ -4880,7 +4954,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,
node->data.char_literal.value);
break;
case NodeTypeBoolLiteral:
- return_type = resolve_expr_const_val_as_bool(g, node, node->data.bool_literal.value);
+ return_type = resolve_expr_const_val_as_bool(g, node, node->data.bool_literal.value, false);
break;
case NodeTypeNullLiteral:
return_type = analyze_null_literal_expr(g, import, context, expected_type, node);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index fb4c92dc4b..01ef7f2fef 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1737,81 +1737,60 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
zig_unreachable();
}
-static LLVMValueRef gen_defer(CodeGen *g, AstNode *node) {
- assert(node->type == NodeTypeDefer);
-
-
- return nullptr;
-}
-
static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMValueRef cond_value,
AstNode *then_node, AstNode *else_node)
{
- TypeTableEntry *then_type = get_expr_type(then_node);
- bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable &&
- then_type->id != TypeTableEntryIdVoid);
-
- if (else_node) {
- LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
- LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
-
- LLVMBasicBlockRef endif_block;
- bool then_endif_reachable = get_expr_type(then_node)->id != TypeTableEntryIdUnreachable;
- bool else_endif_reachable = get_expr_type(else_node)->id != TypeTableEntryIdUnreachable;
- if (then_endif_reachable || else_endif_reachable) {
- endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
- }
+ assert(then_node);
+ assert(else_node);
- LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
-
- LLVMPositionBuilderAtEnd(g->builder, then_block);
- LLVMValueRef then_expr_result = gen_expr(g, then_node);
- if (then_endif_reachable) {
- LLVMBuildBr(g->builder, endif_block);
- }
- LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
-
- LLVMPositionBuilderAtEnd(g->builder, else_block);
- LLVMValueRef else_expr_result = gen_expr(g, else_node);
- if (else_endif_reachable) {
- LLVMBuildBr(g->builder, endif_block);
- }
- LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
+ TypeTableEntry *then_type = get_expr_type(then_node);
+ TypeTableEntry *else_type = get_expr_type(else_node);
- if (then_endif_reachable || else_endif_reachable) {
- LLVMPositionBuilderAtEnd(g->builder, endif_block);
- if (use_expr_value) {
- LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
- LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
- LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
- LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+ bool use_then_value = type_has_bits(then_type);
+ bool use_else_value = type_has_bits(else_type);
- return phi;
- }
- }
+ LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
+ LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
- return nullptr;
+ LLVMBasicBlockRef endif_block;
+ bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable;
+ bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable;
+ if (then_endif_reachable || else_endif_reachable) {
+ endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
}
- assert(!use_expr_value || then_type->id == TypeTableEntryIdErrorUnion);
-
- LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
- LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
-
- LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block);
+ LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
LLVMPositionBuilderAtEnd(g->builder, then_block);
- gen_expr(g, then_node);
- if (get_expr_type(then_node)->id != TypeTableEntryIdUnreachable)
+ LLVMValueRef then_expr_result = gen_expr(g, then_node);
+ if (then_endif_reachable) {
LLVMBuildBr(g->builder, endif_block);
+ }
+ LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
- LLVMPositionBuilderAtEnd(g->builder, endif_block);
-
- if (use_expr_value) {
- return LLVMConstNull(g->err_tag_type->type_ref);
- } else {
- return nullptr;
+ LLVMPositionBuilderAtEnd(g->builder, else_block);
+ LLVMValueRef else_expr_result = gen_expr(g, else_node);
+ if (else_endif_reachable) {
+ LLVMBuildBr(g->builder, endif_block);
}
+ LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
+
+ if (then_endif_reachable || else_endif_reachable) {
+ LLVMPositionBuilderAtEnd(g->builder, endif_block);
+ if (use_then_value && use_else_value) {
+ LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
+ LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
+ LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
+ LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+ return phi;
+ } else if (use_then_value) {
+ return then_expr_result;
+ } else if (use_else_value) {
+ return else_expr_result;
+ }
+ }
+
+ return nullptr;
}
static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
@@ -1866,14 +1845,6 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
return return_value;
}
-//static int block_exit_path_count(BlockContext *block_context) {
-// int sum = 0;
-// for (int i = 0; i < BlockExitPathCount; i += 1) {
-// sum += block_context->block_exit_paths[i] ? 1 : 0;
-// }
-// return sum;
-//}
-
static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
assert(block_node->type == NodeTypeBlock);
@@ -2553,7 +2524,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
case NodeTypeReturnExpr:
return gen_return_expr(g, node);
case NodeTypeDefer:
- return gen_defer(g, node);
+ // nothing to do
+ return nullptr;
case NodeTypeVariableDeclaration:
return gen_var_decl_expr(g, node);
case NodeTypePrefixOpExpr:
@@ -3191,6 +3163,7 @@ static const CIntTypeInfo c_int_type_infos[] = {
static int get_c_type_size_in_bits(CodeGen *g, CIntType id) {
// TODO other architectures besides x86_64
+ // other operating systems besides linux
switch (id) {
case CIntTypeShort:
case CIntTypeUShort:
@@ -3203,6 +3176,8 @@ static int get_c_type_size_in_bits(CodeGen *g, CIntType id) {
case CIntTypeLongLong:
case CIntTypeULongLong:
return 64;
+ case CIntTypeCount:
+ zig_unreachable();
}
zig_unreachable();
}