| author | |
| committer | |
| log | 56908dcb9dd7bbfae7c22b6312752eb576a227c2 |
| tree | 5ee9c222bcc562062db953c707dd6985791de964 |
| parent | 9e905ab3646ebb41f834d04707fd0fc3857d7834 |
closes #1507 files changed, 139 insertions(+), 2 deletions(-)
src/all_types.hpp+1| ... | ... | @@ -1129,6 +1129,7 @@ enum BuiltinFnId { |
| 1129 | 1129 | BuiltinFnIdCmpExchange, |
| 1130 | 1130 | BuiltinFnIdFence, |
| 1131 | 1131 | BuiltinFnIdDivExact, |
| 1132 | BuiltinFnIdTruncate, | |
| 1132 | 1133 | }; |
| 1133 | 1134 | |
| 1134 | 1135 | struct BuiltinFnEntry { |
src/analyze.cpp+40| ... | ... | @@ -4686,6 +4686,44 @@ static TypeTableEntry *analyze_div_exact(CodeGen *g, ImportTableEntry *import, |
| 4686 | 4686 | } |
| 4687 | 4687 | } |
| 4688 | 4688 | |
| 4689 | static 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 | ||
| 4689 | 4727 | static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 4690 | 4728 | TypeTableEntry *expected_type, AstNode *node) |
| 4691 | 4729 | { |
| ... | ... | @@ -5028,6 +5066,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 5028 | 5066 | return analyze_fence(g, import, context, node); |
| 5029 | 5067 | case BuiltinFnIdDivExact: |
| 5030 | 5068 | return analyze_div_exact(g, import, context, node); |
| 5069 | case BuiltinFnIdTruncate: | |
| 5070 | return analyze_truncate(g, import, context, node); | |
| 5031 | 5071 | } |
| 5032 | 5072 | zig_unreachable(); |
| 5033 | 5073 | } |
src/codegen.cpp+53-1| ... | ... | @@ -473,6 +473,18 @@ static LLVMValueRef gen_div_exact(CodeGen *g, AstNode *node) { |
| 473 | 473 | return gen_div(g, node, op1_val, op2_val, get_expr_type(op1_node), true); |
| 474 | 474 | } |
| 475 | 475 | |
| 476 | static 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 | ||
| 476 | 488 | static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) { |
| 477 | 489 | assert(node->type == NodeTypeFnCallExpr); |
| 478 | 490 | |
| ... | ... | @@ -661,6 +673,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { |
| 661 | 673 | return gen_fence(g, node); |
| 662 | 674 | case BuiltinFnIdDivExact: |
| 663 | 675 | return gen_div_exact(g, node); |
| 676 | case BuiltinFnIdTruncate: | |
| 677 | return gen_truncate(g, node); | |
| 664 | 678 | } |
| 665 | 679 | zig_unreachable(); |
| 666 | 680 | } |
| ... | ... | @@ -729,6 +743,24 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT |
| 729 | 743 | zig_unreachable(); |
| 730 | 744 | } |
| 731 | 745 | |
| 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 | 764 | if (actual_bits == wanted_bits) { |
| 733 | 765 | return expr_val; |
| 734 | 766 | } else if (actual_bits < wanted_bits) { |
| ... | ... | @@ -752,7 +784,26 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT |
| 752 | 784 | return LLVMBuildFPTrunc(g->builder, expr_val, wanted_type->type_ref, ""); |
| 753 | 785 | } else if (actual_type->id == TypeTableEntryIdInt) { |
| 754 | 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 | 807 | } else { |
| 757 | 808 | zig_unreachable(); |
| 758 | 809 | } |
| ... | ... | @@ -4568,6 +4619,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 4568 | 4619 | create_builtin_fn_with_arg_count(g, BuiltinFnIdCmpExchange, "cmpxchg", 5); |
| 4569 | 4620 | create_builtin_fn_with_arg_count(g, BuiltinFnIdFence, "fence", 1); |
| 4570 | 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 | } |
| 4572 | 4624 | |
| 4573 | 4625 | static 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 | 827 | case BuiltinFnIdErrName: |
| 828 | 828 | case BuiltinFnIdEmbedFile: |
| 829 | 829 | case BuiltinFnIdCmpExchange: |
| 830 | case BuiltinFnIdTruncate: | |
| 830 | 831 | zig_panic("TODO"); |
| 831 | 832 | case BuiltinFnIdBreakpoint: |
| 832 | 833 | case BuiltinFnIdInvalid: |
std/rand.zig+1-1| ... | ... | @@ -15,7 +15,7 @@ pub struct Rand { |
| 15 | 15 | var i : isize = 1; |
| 16 | 16 | var prev_value: u64w = seed; |
| 17 | 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 | 19 | prev_value = r.array[i]; |
| 20 | 20 | } |
| 21 | 21 | return r; |
test/run_tests.cpp+34| ... | ... | @@ -1368,6 +1368,20 @@ fn add(x: i8w, y: i32) { |
| 1368 | 1368 | } |
| 1369 | 1369 | )SOURCE", 1, ".tmp_source.zig:3:17: error: incompatible types: 'i8w' and 'i32'"); |
| 1370 | 1370 | |
| 1371 | add_compile_fail_case("truncate sign mismatch", R"SOURCE( | |
| 1372 | fn 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( | |
| 1379 | fn 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 | } |
| 1372 | 1386 | |
| 1373 | 1387 | ////////////////////////////////////////////////////////////////////////////// |
| ... | ... | @@ -1476,6 +1490,26 @@ fn widen_slice(slice: []u8) -> []i32 { |
| 1476 | 1490 | } |
| 1477 | 1491 | )SOURCE"); |
| 1478 | 1492 | |
| 1493 | add_debug_safety_case("value does not fit in shortening cast", R"SOURCE( | |
| 1494 | pub fn main(args: [][]u8) -> %void { | |
| 1495 | shorten_cast(200); | |
| 1496 | } | |
| 1497 | #static_eval_enable(false) | |
| 1498 | fn 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( | |
| 1504 | pub fn main(args: [][]u8) -> %void { | |
| 1505 | unsigned_cast(-10); | |
| 1506 | } | |
| 1507 | #static_eval_enable(false) | |
| 1508 | fn unsigned_cast(x: i32) -> u32 { | |
| 1509 | u32(x) | |
| 1510 | } | |
| 1511 | )SOURCE"); | |
| 1512 | ||
| 1479 | 1513 | } |
| 1480 | 1514 | |
| 1481 | 1515 | ////////////////////////////////////////////////////////////////////////////// |
test/self_hosted.zig+9| ... | ... | @@ -1631,3 +1631,12 @@ struct SillyStruct { |
| 1631 | 1631 | const here_is_a_null_literal = SillyStruct { |
| 1632 | 1632 | .context = null, |
| 1633 | 1633 | }; |
| 1634 | ||
| 1635 | #attribute("test") | |
| 1636 | fn truncate() { | |
| 1637 | assert(test_truncate(0x10fd) == 0xfd); | |
| 1638 | } | |
| 1639 | #static_eval_enable(false) | |
| 1640 | fn test_truncate(x: u32) -> u8 { | |
| 1641 | @truncate(u8, x) | |
| 1642 | } |