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
This commit is contained in:
Andrew Kelley 2016-02-09 18:50:53 -07:00
parent f45c374664
commit d8f6388b63
7 changed files with 387 additions and 379 deletions

View File

@ -13,7 +13,7 @@ pub fn main(args: [][]u8) -> %void {
for (arg, args[1...]) {
if (arg == "-") {
catted_anything = true;
%return cat_stream(stdin);
cat_stream(stdin) %% |err| return err;
} else if (arg[0] == '-') {
return usage(exe);
} else {
@ -24,11 +24,11 @@ pub fn main(args: [][]u8) -> %void {
defer is.close();
catted_anything = true;
%return cat_stream(is);
cat_stream(is) %% |err| return err;
}
}
if (!catted_anything) {
%return cat_stream(stdin)
cat_stream(stdin) %% |err| return err;
}
}

View File

@ -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

View File

@ -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);
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);
if (cond_type->id == TypeTableEntryIdInvalid) {
return cond_type;
}
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;
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);
AstNode **then_node = &node->data.if_var_expr.then_block;
AstNode **else_node = &node->data.if_var_expr.else_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);

View File

@ -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)
{
assert(then_node);
assert(else_node);
TypeTableEntry *then_type = get_expr_type(then_node);
bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable &&
then_type->id != TypeTableEntryIdVoid);
TypeTableEntry *else_type = get_expr_type(else_node);
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");
}
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);
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);
return phi;
}
}
return nullptr;
}
assert(!use_expr_value || then_type->id == TypeTableEntryIdErrorUnion);
bool use_then_value = type_has_bits(then_type);
bool use_else_value = type_has_bits(else_type);
LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block);
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");
}
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);
LLVMPositionBuilderAtEnd(g->builder, endif_block);
if (use_expr_value) {
return LLVMConstNull(g->err_tag_type->type_ref);
} else {
return nullptr;
}
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);
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();
}

View File

@ -16,6 +16,7 @@ pub fn run_tests() -> %void {
%%stderr.print_str(" ");
%%stderr.print_str(test_fn.name);
%%stderr.print_str("...");
%%stderr.flush();
test_fn.func();

View File

@ -225,26 +225,6 @@ pub fn foo_function() -> bool {
)SOURCE");
}
add_simple_case("if statements", R"SOURCE(
import "std.zig";
pub fn main(args: [][]u8) -> %void {
if (1 != 0) {
%%stdout.printf("1 is true\n");
} else {
%%stdout.printf("1 is false\n");
}
if (0 != 0) {
%%stdout.printf("0 is true\n");
} else if (1 - 1 != 0) {
%%stdout.printf("1 - 1 is true\n");
}
if (!(0 != 0)) {
%%stdout.printf("!0 is true\n");
}
}
)SOURCE", "1 is true\n!0 is true\n");
add_simple_case("params", R"SOURCE(
import "std.zig";
@ -259,46 +239,6 @@ pub fn main(args: [][]u8) -> %void {
}
)SOURCE", "pass\n");
add_simple_case("local variables", R"SOURCE(
import "std.zig";
pub fn main(args: [][]u8) -> %void {
const a : i32 = 1;
const b = i32(2);
if (a + b == 3) {
%%stdout.printf("OK\n");
}
}
)SOURCE", "OK\n");
add_simple_case("bool literals", R"SOURCE(
import "std.zig";
pub fn main(args: [][]u8) -> %void {
if (true) { %%stdout.printf("OK 1\n"); }
if (false) { %%stdout.printf("BAD 1\n"); }
if (!true) { %%stdout.printf("BAD 2\n"); }
if (!false) { %%stdout.printf("OK 2\n"); }
}
)SOURCE", "OK 1\nOK 2\n");
add_simple_case("separate block scopes", R"SOURCE(
import "std.zig";
pub fn main(args: [][]u8) -> %void {
if (true) {
const no_conflict : i32 = 5;
if (no_conflict == 5) { %%stdout.printf("OK 1\n"); }
}
const c = {
const no_conflict = i32(10);
no_conflict
};
if (c == 10) { %%stdout.printf("OK 2\n"); }
}
)SOURCE", "OK 1\nOK 2\n");
add_simple_case("void parameters", R"SOURCE(
import "std.zig";
@ -314,48 +254,6 @@ fn void_fun(a : i32, b : void, c : i32) {
}
)SOURCE", "OK\n");
add_simple_case("void struct fields", R"SOURCE(
import "std.zig";
struct Foo {
a : void,
b : i32,
c : void,
}
pub fn main(args: [][]u8) -> %void {
const foo = Foo {
.a = void{},
.b = 1,
.c = void{},
};
if (foo.b != 1) {
%%stdout.printf("BAD\n");
}
if (@sizeof(Foo) != 4) {
%%stdout.printf("BAD\n");
}
%%stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("void arrays", R"SOURCE(
import "std.zig";
pub fn main(args: [][]u8) -> %void {
var array: [4]void = undefined;
array[0] = void{};
array[1] = array[2];
if (@sizeof(@typeof(array)) != 0) {
%%stdout.printf("BAD sizeof\n");
}
if (array.len != 4) {
%%stdout.printf("BAD len\n");
}
%%stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("mutable local variables", R"SOURCE(
import "std.zig";
@ -414,27 +312,6 @@ pub fn main(args: [][]u8) -> %void {
)SOURCE", "Hello, world!\n");
add_simple_case("a + b + c", R"SOURCE(
import "std.zig";
pub fn main(args: [][]u8) -> %void {
if (false || false || false) { %%stdout.printf("BAD 1\n"); }
if (true && true && false) { %%stdout.printf("BAD 2\n"); }
if (1 | 2 | 4 != 7) { %%stdout.printf("BAD 3\n"); }
if (3 ^ 6 ^ 8 != 13) { %%stdout.printf("BAD 4\n"); }
if (7 & 14 & 28 != 4) { %%stdout.printf("BAD 5\n"); }
if (9 << 1 << 2 != 9 << 3) { %%stdout.printf("BAD 6\n"); }
if (90 >> 1 >> 2 != 90 >> 3) { %%stdout.printf("BAD 7\n"); }
if (100 - 1 + 1000 != 1099) { %%stdout.printf("BAD 8\n"); }
if (5 * 4 / 2 % 3 != 1) { %%stdout.printf("BAD 9\n"); }
if (i32(i32(5)) != 5) { %%stdout.printf("BAD 10\n"); }
if (!!false) { %%stdout.printf("BAD 11\n"); }
if (i32(7) != --(i32(7))) { %%stdout.printf("BAD 12\n"); }
%%stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("short circuit", R"SOURCE(
import "std.zig";
@ -729,39 +606,6 @@ pub fn main(args: [][]u8) -> %void {
}
)SOURCE", "loop\nloop\nloop\nloop\n");
add_simple_case("maybe type", R"SOURCE(
import "std.zig";
pub fn main(args: [][]u8) -> %void {
const x : ?bool = true;
if (const y ?= x) {
if (y) {
%%stdout.printf("x is true\n");
} else {
%%stdout.printf("x is false\n");
}
} else {
%%stdout.printf("x is none\n");
}
const next_x : ?i32 = null;
const z = next_x ?? 1234;
if (z != 1234) {
%%stdout.printf("BAD\n");
}
const final_x : ?i32 = 13;
const num = final_x ?? unreachable{};
if (num != 13) {
%%stdout.printf("BAD\n");
}
}
)SOURCE", "x is true\n");
add_simple_case("implicit cast after unreachable", R"SOURCE(
import "std.zig";
pub fn main(args: [][]u8) -> %void {
@ -971,73 +815,6 @@ fn print_ok(val: @typeof(x)) -> @typeof(foo) {
const foo : i32 = 0;
)SOURCE", "OK\n");
add_simple_case("enum type", R"SOURCE(
import "std.zig";
struct Point {
x: u64,
y: u64,
}
enum Foo {
One: i32,
Two: Point,
Three: void,
}
enum Bar {
A,
B,
C,
D,
}
pub fn main(args: [][]u8) -> %void {
const foo1 = Foo.One(13);
const foo2 = Foo.Two(Point { .x = 1234, .y = 5678, });
const bar = Bar.B;
if (bar != Bar.B) {
%%stdout.printf("BAD 1\n");
}
if (@member_count(Foo) != 3) {
%%stdout.printf("BAD 2\n");
}
if (@member_count(Bar) != 4) {
%%stdout.printf("BAD 3\n");
}
if (@sizeof(Foo) != 24) {
%%stdout.printf("BAD 4\n");
}
if (@sizeof(Bar) != 1) {
%%stdout.printf("BAD 5\n");
}
%%stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("array literal", R"SOURCE(
import "std.zig";
pub fn main(args: [][]u8) -> %void {
const HEX_MULT = []u16{4096, 256, 16, 1};
if (HEX_MULT.len != 4) {
%%stdout.printf("BAD\n");
}
if (HEX_MULT[1] != 256) {
%%stdout.printf("BAD\n");
}
%%stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("nested arrays", R"SOURCE(
import "std.zig";
@ -1092,23 +869,6 @@ fn fn3() -> u32 {7}
fn fn4() -> u32 {8}
)SOURCE", "5\n6\n7\n8\n");
add_simple_case("const number literal", R"SOURCE(
import "std.zig";
const ten = 10;
pub fn main(args: [][]u8) -> %void {
const one = 1;
const eleven = ten + one;
if (eleven != 11) {
%%stdout.printf("BAD\n");
}
%%stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("statically initialized struct", R"SOURCE(
import "std.zig";
struct Foo {
@ -1139,21 +899,6 @@ pub fn main(args: [][]u8) -> %void {
}
)SOURCE", "OK\n");
add_simple_case("error values", R"SOURCE(
import "std.zig";
error err1;
error err2;
pub fn main(args: [][]u8) -> %void {
const a = i32(error.err1);
const b = i32(error.err2);
if (a == b) {
%%stdout.printf("BAD\n");
}
%%stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("return with implicit cast from while loop", R"SOURCE(
import "std.zig";
pub fn main(args: [][]u8) -> %void {
@ -1988,6 +1733,13 @@ struct Foo {
}
fn get() -> isize { 1 }
)SOURCE", 1, ".tmp_source.zig:3:9: error: unable to evaluate constant expression");
add_compile_fail_case("unnecessary if statement", R"SOURCE(
fn f() {
if (true) { }
}
)SOURCE", 1, ".tmp_source.zig:3:9: error: condition is always true; unnecessary if statement");
}
//////////////////////////////////////////////////////////////////////////////

View File

@ -5,7 +5,6 @@ fn empty_function() {}
/**
* multi line doc comment
*/
@ -19,6 +18,200 @@ fn comments() {
fn comments_f1(s: []u8) {}
#attribute("test")
fn if_statements() {
should_be_equal(1, 1);
first_eql_third(2, 1, 2);
}
fn should_be_equal(a: i32, b: i32) {
if (a != b) {
unreachable{};
} else {
return;
}
}
fn first_eql_third(a: i32, b: i32, c: i32) {
if (a == b) {
unreachable{};
} else if (b == c) {
unreachable{};
} else if (a == c) {
return;
} else {
unreachable{};
}
}
#attribute("test")
fn local_variables() {
test_loc_vars(2);
}
fn test_loc_vars(b: i32) {
const a: i32 = 1;
if (a + b != 3) unreachable{};
}
#attribute("test")
fn bool_literals() {
should_be_true(true);
should_be_false(false);
}
fn should_be_true(b: bool) {
if (!b) unreachable{};
}
fn should_be_false(b: bool) {
if (b) unreachable{};
}
#attribute("test")
fn separate_block_scopes() {
{
const no_conflict : i32 = 5;
assert(no_conflict == 5);
}
const c = {
const no_conflict = i32(10);
no_conflict
};
assert(c == 10);
}
#attribute("test")
fn void_struct_fields() {
const foo = VoidStructFieldsFoo {
.a = void{},
.b = 1,
.c = void{},
};
assert(foo.b == 1);
assert(@sizeof(VoidStructFieldsFoo) == 4);
}
struct VoidStructFieldsFoo {
a : void,
b : i32,
c : void,
}
#attribute("test")
fn void_arrays() {
var array: [4]void = undefined;
array[0] = void{};
array[1] = array[2];
assert(@sizeof(@typeof(array)) == 0);
assert(array.len == 4);
}
#attribute("test")
fn three_expr_in_a_row() {
assert_false(false || false || false);
assert_false(true && true && false);
assert_false(1 | 2 | 4 != 7);
assert_false(3 ^ 6 ^ 8 != 13);
assert_false(7 & 14 & 28 != 4);
assert_false(9 << 1 << 2 != 9 << 3);
assert_false(90 >> 1 >> 2 != 90 >> 3);
assert_false(100 - 1 + 1000 != 1099);
assert_false(5 * 4 / 2 % 3 != 1);
assert_false(i32(i32(5)) != 5);
assert_false(!!false);
assert_false(i32(7) != --(i32(7)));
}
fn assert_false(b: bool) {
assert(!b);
}
#attribute("test")
fn maybe_type() {
const x : ?bool = true;
if (const y ?= x) {
if (y) {
// OK
} else {
unreachable{};
}
} else {
unreachable{};
}
const next_x : ?i32 = null;
const z = next_x ?? 1234;
assert(z == 1234);
const final_x : ?i32 = 13;
const num = final_x ?? unreachable{};
assert(num == 13);
}
#attribute("test")
fn enum_type() {
const foo1 = EnumTypeFoo.One(13);
const foo2 = EnumTypeFoo.Two(EnumType { .x = 1234, .y = 5678, });
const bar = EnumTypeBar.B;
assert(bar == EnumTypeBar.B);
assert(@member_count(EnumTypeFoo) == 3);
assert(@member_count(EnumTypeBar) == 4);
assert(@sizeof(EnumTypeFoo) == 24);
assert(@sizeof(EnumTypeBar) == 1);
}
struct EnumType {
x: u64,
y: u64,
}
enum EnumTypeFoo {
One: i32,
Two: EnumType,
Three: void,
}
enum EnumTypeBar {
A,
B,
C,
D,
}
#attribute("test")
fn array_literal() {
const HEX_MULT = []u16{4096, 256, 16, 1};
assert(HEX_MULT.len == 4);
assert(HEX_MULT[1] == 256);
}
#attribute("test")
fn const_number_literal() {
const one = 1;
const eleven = ten + one;
assert(eleven == 11);
}
const ten = 10;
#attribute("test")
fn error_values() {
const a = i32(error.err1);
const b = i32(error.err2);
assert(a != b);
}
error err1;
error err2;
#attribute("test")
@ -42,11 +235,14 @@ fn call_struct_field(foo: Foo) -> i32 {
#attribute("test")
fn redefinition_of_error_values_allowed() {
if (error.AnError == error.SecondError) unreachable{}
should_be_not_equal(error.AnError, error.SecondError);
}
error AnError;
error AnError;
error SecondError;
fn should_be_not_equal(a: error, b: error) {
if (a == b) unreachable{}
}
@ -98,14 +294,14 @@ fn continue_in_for_loop() {
fn cast_bool_to_int() {
const t = true;
const f = false;
if (i32(t) != i32(1)) unreachable{}
if (i32(f) != i32(0)) unreachable{}
assert(i32(t) == i32(1));
assert(i32(f) == i32(0));
non_const_cast_bool_to_int(t, f);
}
fn non_const_cast_bool_to_int(t: bool, f: bool) {
if (i32(t) != i32(1)) unreachable{}
if (i32(f) != i32(0)) unreachable{}
assert(i32(t) == i32(1));
assert(i32(f) == i32(0));
}
@ -240,7 +436,7 @@ fn const_expr_eval_on_single_expr_blocks_fn(x: i32, b: bool) -> i32 {
#attribute("test")
fn builtin_const_eval() {
const x : i32 = @const_eval(1 + 2 + 3);
if (x != @const_eval(6)) unreachable{};
assert(x == @const_eval(6));
}
#attribute("test")
@ -279,3 +475,10 @@ struct ArrayDotLenConstExpr {
y: [@const_eval(some_array.len)]u8,
}
const some_array = []u8 {0, 1, 2, 3};
fn assert(b: bool) {
if (!b) unreachable{}
}