authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-05-09 08:44:41-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-05-09 08:44:41-07:00
log56908dcb9dd7bbfae7c22b6312752eb576a227c2
tree5ee9c222bcc562062db953c707dd6985791de964
parent9e905ab3646ebb41f834d04707fd0fc3857d7834

add debug safety for shortening casts

closes #150

7 files changed, 139 insertions(+), 2 deletions(-)

src/all_types.hpp+1
...@@ -1129,6 +1129,7 @@ enum BuiltinFnId {...@@ -1129,6 +1129,7 @@ enum BuiltinFnId {
1129 BuiltinFnIdCmpExchange,1129 BuiltinFnIdCmpExchange,
1130 BuiltinFnIdFence,1130 BuiltinFnIdFence,
1131 BuiltinFnIdDivExact,1131 BuiltinFnIdDivExact,
1132 BuiltinFnIdTruncate,
1132};1133};
11331134
1134struct BuiltinFnEntry {1135struct BuiltinFnEntry {
src/analyze.cpp+40
...@@ -4686,6 +4686,44 @@ static TypeTableEntry *analyze_div_exact(CodeGen *g, ImportTableEntry *import,...@@ -4686,6 +4686,44 @@ static TypeTableEntry *analyze_div_exact(CodeGen *g, ImportTableEntry *import,
4686 }4686 }
4687}4687}
46884688
4689static TypeTableEntry *analyze_truncate(CodeGen *g, ImportTableEntry *import,
4690 BlockContext *context, AstNode *node)
4691{
4692 assert(node->type == NodeTypeFnCallExpr);
4693
4694 AstNode **op1 = &node->data.fn_call_expr.params.at(0);
4695 AstNode **op2 = &node->data.fn_call_expr.params.at(1);
4696
4697 TypeTableEntry *dest_type = analyze_type_expr(g, import, context, *op1);
4698 TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, *op2);
4699
4700 if (dest_type->id == TypeTableEntryIdInvalid || src_type->id == TypeTableEntryIdInvalid) {
4701 return g->builtin_types.entry_invalid;
4702 } else if (dest_type->id != TypeTableEntryIdInt) {
4703 add_node_error(g, *op1,
4704 buf_sprintf("expected integer type, got '%s'", buf_ptr(&dest_type->name)));
4705 return g->builtin_types.entry_invalid;
4706 } else if (src_type->id != TypeTableEntryIdInt) {
4707 add_node_error(g, *op2,
4708 buf_sprintf("expected integer type, got '%s'", buf_ptr(&src_type->name)));
4709 return g->builtin_types.entry_invalid;
4710 } else if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) {
4711 const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned";
4712 add_node_error(g, *op2,
4713 buf_sprintf("expected %s integer type, got '%s'", sign_str, buf_ptr(&src_type->name)));
4714 return g->builtin_types.entry_invalid;
4715 } else if (src_type->data.integral.bit_count <= dest_type->data.integral.bit_count) {
4716 add_node_error(g, *op2,
4717 buf_sprintf("type '%s' has same or fewer bits than destination type '%s'",
4718 buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
4719 return g->builtin_types.entry_invalid;
4720 }
4721
4722 // TODO const expr eval
4723
4724 return dest_type;
4725}
4726
4689static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,4727static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
4690 TypeTableEntry *expected_type, AstNode *node)4728 TypeTableEntry *expected_type, AstNode *node)
4691{4729{
...@@ -5028,6 +5066,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry...@@ -5028,6 +5066,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
5028 return analyze_fence(g, import, context, node);5066 return analyze_fence(g, import, context, node);
5029 case BuiltinFnIdDivExact:5067 case BuiltinFnIdDivExact:
5030 return analyze_div_exact(g, import, context, node);5068 return analyze_div_exact(g, import, context, node);
5069 case BuiltinFnIdTruncate:
5070 return analyze_truncate(g, import, context, node);
5031 }5071 }
5032 zig_unreachable();5072 zig_unreachable();
5033}5073}
src/codegen.cpp+53-1
...@@ -473,6 +473,18 @@ static LLVMValueRef gen_div_exact(CodeGen *g, AstNode *node) {...@@ -473,6 +473,18 @@ static LLVMValueRef gen_div_exact(CodeGen *g, AstNode *node) {
473 return gen_div(g, node, op1_val, op2_val, get_expr_type(op1_node), true);473 return gen_div(g, node, op1_val, op2_val, get_expr_type(op1_node), true);
474}474}
475475
476static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) {
477 assert(node->type == NodeTypeFnCallExpr);
478
479 TypeTableEntry *dest_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
480 AstNode *src_node = node->data.fn_call_expr.params.at(1);
481
482 LLVMValueRef src_val = gen_expr(g, src_node);
483
484 set_debug_source_node(g, node);
485 return LLVMBuildTrunc(g->builder, src_val, dest_type->type_ref, "");
486}
487
476static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {488static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {
477 assert(node->type == NodeTypeFnCallExpr);489 assert(node->type == NodeTypeFnCallExpr);
478490
...@@ -661,6 +673,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {...@@ -661,6 +673,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
661 return gen_fence(g, node);673 return gen_fence(g, node);
662 case BuiltinFnIdDivExact:674 case BuiltinFnIdDivExact:
663 return gen_div_exact(g, node);675 return gen_div_exact(g, node);
676 case BuiltinFnIdTruncate:
677 return gen_truncate(g, node);
664 }678 }
665 zig_unreachable();679 zig_unreachable();
666}680}
...@@ -729,6 +743,24 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT...@@ -729,6 +743,24 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT
729 zig_unreachable();743 zig_unreachable();
730 }744 }
731745
746 if (actual_bits >= wanted_bits && actual_type->id == TypeTableEntryIdInt &&
747 !wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed &&
748 want_debug_safety(g, source_node))
749 {
750 set_debug_source_node(g, source_node);
751 LLVMValueRef zero = LLVMConstNull(actual_type->type_ref);
752 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, "");
753
754 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SignCastOk");
755 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SignCastFail");
756 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
757
758 LLVMPositionBuilderAtEnd(g->builder, fail_block);
759 gen_debug_safety_crash(g);
760
761 LLVMPositionBuilderAtEnd(g->builder, ok_block);
762 }
763
732 if (actual_bits == wanted_bits) {764 if (actual_bits == wanted_bits) {
733 return expr_val;765 return expr_val;
734 } else if (actual_bits < wanted_bits) {766 } else if (actual_bits < wanted_bits) {
...@@ -752,7 +784,26 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT...@@ -752,7 +784,26 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT
752 return LLVMBuildFPTrunc(g->builder, expr_val, wanted_type->type_ref, "");784 return LLVMBuildFPTrunc(g->builder, expr_val, wanted_type->type_ref, "");
753 } else if (actual_type->id == TypeTableEntryIdInt) {785 } else if (actual_type->id == TypeTableEntryIdInt) {
754 set_debug_source_node(g, source_node);786 set_debug_source_node(g, source_node);
755 return LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, "");787 LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, "");
788 if (!want_debug_safety(g, source_node)) {
789 return trunc_val;
790 }
791 LLVMValueRef orig_val;
792 if (actual_type->data.integral.is_signed) {
793 orig_val = LLVMBuildSExt(g->builder, trunc_val, actual_type->type_ref, "");
794 } else {
795 orig_val = LLVMBuildZExt(g->builder, trunc_val, actual_type->type_ref, "");
796 }
797 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, orig_val, "");
798 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "CastShortenOk");
799 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "CastShortenFail");
800 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
801
802 LLVMPositionBuilderAtEnd(g->builder, fail_block);
803 gen_debug_safety_crash(g);
804
805 LLVMPositionBuilderAtEnd(g->builder, ok_block);
806 return trunc_val;
756 } else {807 } else {
757 zig_unreachable();808 zig_unreachable();
758 }809 }
...@@ -4568,6 +4619,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -4568,6 +4619,7 @@ static void define_builtin_fns(CodeGen *g) {
4568 create_builtin_fn_with_arg_count(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);4619 create_builtin_fn_with_arg_count(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);
4569 create_builtin_fn_with_arg_count(g, BuiltinFnIdFence, "fence", 1);4620 create_builtin_fn_with_arg_count(g, BuiltinFnIdFence, "fence", 1);
4570 create_builtin_fn_with_arg_count(g, BuiltinFnIdDivExact, "div_exact", 2);4621 create_builtin_fn_with_arg_count(g, BuiltinFnIdDivExact, "div_exact", 2);
4622 create_builtin_fn_with_arg_count(g, BuiltinFnIdTruncate, "truncate", 2);
4571}4623}
45724624
4573static void init(CodeGen *g, Buf *source_path) {4625static void init(CodeGen *g, Buf *source_path) {
src/eval.cpp+1
...@@ -827,6 +827,7 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_...@@ -827,6 +827,7 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_
827 case BuiltinFnIdErrName:827 case BuiltinFnIdErrName:
828 case BuiltinFnIdEmbedFile:828 case BuiltinFnIdEmbedFile:
829 case BuiltinFnIdCmpExchange:829 case BuiltinFnIdCmpExchange:
830 case BuiltinFnIdTruncate:
830 zig_panic("TODO");831 zig_panic("TODO");
831 case BuiltinFnIdBreakpoint:832 case BuiltinFnIdBreakpoint:
832 case BuiltinFnIdInvalid:833 case BuiltinFnIdInvalid:
std/rand.zig+1-1
...@@ -15,7 +15,7 @@ pub struct Rand {...@@ -15,7 +15,7 @@ pub struct Rand {
15 var i : isize = 1;15 var i : isize = 1;
16 var prev_value: u64w = seed;16 var prev_value: u64w = seed;
17 while (i < ARRAY_SIZE; i += 1) {17 while (i < ARRAY_SIZE; i += 1) {
18 r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + u64w(i));18 r.array[i] = @truncate(u32, (prev_value ^ (prev_value << 30)) * 0x6c078965 + u64w(i));
19 prev_value = r.array[i];19 prev_value = r.array[i];
20 }20 }
21 return r;21 return r;
test/run_tests.cpp+34
...@@ -1368,6 +1368,20 @@ fn add(x: i8w, y: i32) {...@@ -1368,6 +1368,20 @@ fn add(x: i8w, y: i32) {
1368}1368}
1369 )SOURCE", 1, ".tmp_source.zig:3:17: error: incompatible types: 'i8w' and 'i32'");1369 )SOURCE", 1, ".tmp_source.zig:3:17: error: incompatible types: 'i8w' and 'i32'");
13701370
1371 add_compile_fail_case("truncate sign mismatch", R"SOURCE(
1372fn f() {
1373 const x: u32 = 10;
1374 @truncate(i8, x);
1375}
1376 )SOURCE", 1, ".tmp_source.zig:4:19: error: expected signed integer type, got 'u32'");
1377
1378 add_compile_fail_case("truncate same bit count", R"SOURCE(
1379fn f() {
1380 const x: i8 = 10;
1381 @truncate(i8, x);
1382}
1383 )SOURCE", 1, ".tmp_source.zig:4:19: error: type 'i8' has same or fewer bits than destination type 'i8'");
1384
1371}1385}
13721386
1373//////////////////////////////////////////////////////////////////////////////1387//////////////////////////////////////////////////////////////////////////////
...@@ -1476,6 +1490,26 @@ fn widen_slice(slice: []u8) -> []i32 {...@@ -1476,6 +1490,26 @@ fn widen_slice(slice: []u8) -> []i32 {
1476}1490}
1477 )SOURCE");1491 )SOURCE");
14781492
1493 add_debug_safety_case("value does not fit in shortening cast", R"SOURCE(
1494pub fn main(args: [][]u8) -> %void {
1495 shorten_cast(200);
1496}
1497#static_eval_enable(false)
1498fn shorten_cast(x: i32) -> i8 {
1499 i8(x)
1500}
1501 )SOURCE");
1502
1503 add_debug_safety_case("signed integer not fitting in cast to unsigned integer", R"SOURCE(
1504pub fn main(args: [][]u8) -> %void {
1505 unsigned_cast(-10);
1506}
1507#static_eval_enable(false)
1508fn unsigned_cast(x: i32) -> u32 {
1509 u32(x)
1510}
1511 )SOURCE");
1512
1479}1513}
14801514
1481//////////////////////////////////////////////////////////////////////////////1515//////////////////////////////////////////////////////////////////////////////
test/self_hosted.zig+9
...@@ -1631,3 +1631,12 @@ struct SillyStruct {...@@ -1631,3 +1631,12 @@ struct SillyStruct {
1631const here_is_a_null_literal = SillyStruct {1631const here_is_a_null_literal = SillyStruct {
1632 .context = null,1632 .context = null,
1633};1633};
1634
1635#attribute("test")
1636fn truncate() {
1637 assert(test_truncate(0x10fd) == 0xfd);
1638}
1639#static_eval_enable(false)
1640fn test_truncate(x: u32) -> u8 {
1641 @truncate(u8, x)
1642}