authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-22 17:36:18-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-10-22 17:36:18-04:00
loge6ac082437b87fccf6a7e592f813d3403427e512
tree4c61a3a5d19afd57cf1b20010179ccbf538347c2
parent78199a684f50f37f652dabcc88b02e8b998ab5fa
parent475fc2934b9bb4c0c10ca213c4322cc902c4b303
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6744 from LemonBoy/intcast-vec

stage1: Implement `@intCast` between vectors

7 files changed, 233 insertions(+), 44 deletions(-)

doc/docgen.zig+3-3
...@@ -1251,7 +1251,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any...@@ -1251,7 +1251,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1251 },1251 },
1252 }1252 }
1253 if (mem.indexOf(u8, result.stderr, error_match) == null) {1253 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1254 print("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });1254 print("{}\nExpected to find '{}' in stderr\n", .{ result.stderr, error_match });
1255 return parseError(tokenizer, code.source_token, "example did not have expected compile error", .{});1255 return parseError(tokenizer, code.source_token, "example did not have expected compile error", .{});
1256 }1256 }
1257 const escaped_stderr = try escapeHtml(allocator, result.stderr);1257 const escaped_stderr = try escapeHtml(allocator, result.stderr);
...@@ -1306,7 +1306,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any...@@ -1306,7 +1306,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1306 },1306 },
1307 }1307 }
1308 if (mem.indexOf(u8, result.stderr, error_match) == null) {1308 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1309 print("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });1309 print("{}\nExpected to find '{}' in stderr\n", .{ result.stderr, error_match });
1310 return parseError(tokenizer, code.source_token, "example did not have expected runtime safety error message", .{});1310 return parseError(tokenizer, code.source_token, "example did not have expected runtime safety error message", .{});
1311 }1311 }
1312 const escaped_stderr = try escapeHtml(allocator, result.stderr);1312 const escaped_stderr = try escapeHtml(allocator, result.stderr);
...@@ -1385,7 +1385,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any...@@ -1385,7 +1385,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1385 },1385 },
1386 }1386 }
1387 if (mem.indexOf(u8, result.stderr, error_match) == null) {1387 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1388 print("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });1388 print("{}\nExpected to find '{}' in stderr\n", .{ result.stderr, error_match });
1389 return parseError(tokenizer, code.source_token, "example did not have expected compile error message", .{});1389 return parseError(tokenizer, code.source_token, "example did not have expected compile error message", .{});
1390 }1390 }
1391 const escaped_stderr = try escapeHtml(allocator, result.stderr);1391 const escaped_stderr = try escapeHtml(allocator, result.stderr);
doc/langref.html.in+2-2
...@@ -8713,7 +8713,7 @@ fn foo(x: []const u8) u8 {...@@ -8713,7 +8713,7 @@ fn foo(x: []const u8) u8 {
8713 {#header_close#}8713 {#header_close#}
8714 {#header_open|Cast Negative Number to Unsigned Integer#}8714 {#header_open|Cast Negative Number to Unsigned Integer#}
8715 <p>At compile-time:</p>8715 <p>At compile-time:</p>
8716 {#code_begin|test_err|cannot cast negative value -1 to unsigned integer type 'u32'#}8716 {#code_begin|test_err|attempt to cast negative value to unsigned integer#}
8717comptime {8717comptime {
8718 const value: i32 = -1;8718 const value: i32 = -1;
8719 const unsigned = @intCast(u32, value);8719 const unsigned = @intCast(u32, value);
...@@ -8735,7 +8735,7 @@ pub fn main() void {...@@ -8735,7 +8735,7 @@ pub fn main() void {
8735 {#header_close#}8735 {#header_close#}
8736 {#header_open|Cast Truncates Data#}8736 {#header_open|Cast Truncates Data#}
8737 <p>At compile-time:</p>8737 <p>At compile-time:</p>
8738 {#code_begin|test_err|integer value 300 cannot be coerced to type 'u8'#}8738 {#code_begin|test_err|cast from 'u16' to 'u8' truncates bits#}
8739comptime {8739comptime {
8740 const spartan_count: u16 = 300;8740 const spartan_count: u16 = 300;
8741 const byte = @intCast(u8, spartan_count);8741 const byte = @intCast(u8, spartan_count);
src/stage1/codegen.cpp+29-15
...@@ -1433,6 +1433,9 @@ static void add_sentinel_check(CodeGen *g, LLVMValueRef sentinel_elem_ptr, ZigVa...@@ -1433,6 +1433,9 @@ static void add_sentinel_check(CodeGen *g, LLVMValueRef sentinel_elem_ptr, ZigVa
1433static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) {1433static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) {
1434 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type));1434 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type));
1435 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, "");1435 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, "");
1436 if (int_type->id == ZigTypeIdVector) {
1437 ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit);
1438 }
1436 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk");1439 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk");
1437 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail");1440 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail");
1438 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);1441 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
...@@ -1450,29 +1453,37 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z...@@ -1450,29 +1453,37 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
1450 assert(actual_type->id == wanted_type->id);1453 assert(actual_type->id == wanted_type->id);
1451 assert(expr_val != nullptr);1454 assert(expr_val != nullptr);
14521455
1456 ZigType *scalar_actual_type = (actual_type->id == ZigTypeIdVector) ?
1457 actual_type->data.vector.elem_type : actual_type;
1458 ZigType *scalar_wanted_type = (wanted_type->id == ZigTypeIdVector) ?
1459 wanted_type->data.vector.elem_type : wanted_type;
1460
1453 uint64_t actual_bits;1461 uint64_t actual_bits;
1454 uint64_t wanted_bits;1462 uint64_t wanted_bits;
1455 if (actual_type->id == ZigTypeIdFloat) {1463 if (scalar_actual_type->id == ZigTypeIdFloat) {
1456 actual_bits = actual_type->data.floating.bit_count;1464 actual_bits = scalar_actual_type->data.floating.bit_count;
1457 wanted_bits = wanted_type->data.floating.bit_count;1465 wanted_bits = scalar_wanted_type->data.floating.bit_count;
1458 } else if (actual_type->id == ZigTypeIdInt) {1466 } else if (scalar_actual_type->id == ZigTypeIdInt) {
1459 actual_bits = actual_type->data.integral.bit_count;1467 actual_bits = scalar_actual_type->data.integral.bit_count;
1460 wanted_bits = wanted_type->data.integral.bit_count;1468 wanted_bits = scalar_wanted_type->data.integral.bit_count;
1461 } else {1469 } else {
1462 zig_unreachable();1470 zig_unreachable();
1463 }1471 }
14641472
1465 if (actual_type->id == ZigTypeIdInt && want_runtime_safety && (1473 if (scalar_actual_type->id == ZigTypeIdInt && want_runtime_safety && (
1466 // negative to unsigned1474 // negative to unsigned
1467 (!wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed) ||1475 (!scalar_wanted_type->data.integral.is_signed && scalar_actual_type->data.integral.is_signed) ||
1468 // unsigned would become negative1476 // unsigned would become negative
1469 (wanted_type->data.integral.is_signed && !actual_type->data.integral.is_signed && actual_bits == wanted_bits)))1477 (scalar_wanted_type->data.integral.is_signed && !scalar_actual_type->data.integral.is_signed && actual_bits == wanted_bits)))
1470 {1478 {
1471 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, actual_type));1479 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, actual_type));
1472 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, "");1480 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, "");
14731481
1474 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SignCastOk");1482 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SignCastOk");
1475 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SignCastFail");1483 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SignCastFail");
1484 if (actual_type->id == ZigTypeIdVector) {
1485 ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit);
1486 }
1476 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);1487 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
14771488
1478 LLVMPositionBuilderAtEnd(g->builder, fail_block);1489 LLVMPositionBuilderAtEnd(g->builder, fail_block);
...@@ -1484,10 +1495,10 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z...@@ -1484,10 +1495,10 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
1484 if (actual_bits == wanted_bits) {1495 if (actual_bits == wanted_bits) {
1485 return expr_val;1496 return expr_val;
1486 } else if (actual_bits < wanted_bits) {1497 } else if (actual_bits < wanted_bits) {
1487 if (actual_type->id == ZigTypeIdFloat) {1498 if (scalar_actual_type->id == ZigTypeIdFloat) {
1488 return LLVMBuildFPExt(g->builder, expr_val, get_llvm_type(g, wanted_type), "");1499 return LLVMBuildFPExt(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
1489 } else if (actual_type->id == ZigTypeIdInt) {1500 } else if (scalar_actual_type->id == ZigTypeIdInt) {
1490 if (actual_type->data.integral.is_signed) {1501 if (scalar_actual_type->data.integral.is_signed) {
1491 return LLVMBuildSExt(g->builder, expr_val, get_llvm_type(g, wanted_type), "");1502 return LLVMBuildSExt(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
1492 } else {1503 } else {
1493 return LLVMBuildZExt(g->builder, expr_val, get_llvm_type(g, wanted_type), "");1504 return LLVMBuildZExt(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
...@@ -1496,9 +1507,9 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z...@@ -1496,9 +1507,9 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
1496 zig_unreachable();1507 zig_unreachable();
1497 }1508 }
1498 } else if (actual_bits > wanted_bits) {1509 } else if (actual_bits > wanted_bits) {
1499 if (actual_type->id == ZigTypeIdFloat) {1510 if (scalar_actual_type->id == ZigTypeIdFloat) {
1500 return LLVMBuildFPTrunc(g->builder, expr_val, get_llvm_type(g, wanted_type), "");1511 return LLVMBuildFPTrunc(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
1501 } else if (actual_type->id == ZigTypeIdInt) {1512 } else if (scalar_actual_type->id == ZigTypeIdInt) {
1502 if (wanted_bits == 0) {1513 if (wanted_bits == 0) {
1503 if (!want_runtime_safety)1514 if (!want_runtime_safety)
1504 return nullptr;1515 return nullptr;
...@@ -1510,12 +1521,15 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z...@@ -1510,12 +1521,15 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
1510 return trunc_val;1521 return trunc_val;
1511 }1522 }
1512 LLVMValueRef orig_val;1523 LLVMValueRef orig_val;
1513 if (wanted_type->data.integral.is_signed) {1524 if (scalar_wanted_type->data.integral.is_signed) {
1514 orig_val = LLVMBuildSExt(g->builder, trunc_val, get_llvm_type(g, actual_type), "");1525 orig_val = LLVMBuildSExt(g->builder, trunc_val, get_llvm_type(g, actual_type), "");
1515 } else {1526 } else {
1516 orig_val = LLVMBuildZExt(g->builder, trunc_val, get_llvm_type(g, actual_type), "");1527 orig_val = LLVMBuildZExt(g->builder, trunc_val, get_llvm_type(g, actual_type), "");
1517 }1528 }
1518 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, orig_val, "");1529 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, orig_val, "");
1530 if (actual_type->id == ZigTypeIdVector) {
1531 ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit);
1532 }
1519 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk");1533 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk");
1520 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail");1534 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail");
1521 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);1535 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
src/stage1/ir.cpp+129-21
...@@ -86,6 +86,8 @@ enum ConstCastResultId {...@@ -86,6 +86,8 @@ enum ConstCastResultId {
86 ConstCastResultIdCV,86 ConstCastResultIdCV,
87 ConstCastResultIdPtrSentinel,87 ConstCastResultIdPtrSentinel,
88 ConstCastResultIdIntShorten,88 ConstCastResultIdIntShorten,
89 ConstCastResultIdVectorLength,
90 ConstCastResultIdVectorChild,
89};91};
9092
91struct ConstCastOnly;93struct ConstCastOnly;
...@@ -912,6 +914,7 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte...@@ -912,6 +914,7 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte
912 if (is_opt_err_set(expected) && is_opt_err_set(actual))914 if (is_opt_err_set(expected) && is_opt_err_set(actual))
913 return true;915 return true;
914916
917 // XXX: Vectors and arrays are interchangeable at comptime
915 if (expected->id != actual->id)918 if (expected->id != actual->id)
916 return false;919 return false;
917920
...@@ -945,9 +948,11 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte...@@ -945,9 +948,11 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte
945 case ZigTypeIdErrorUnion:948 case ZigTypeIdErrorUnion:
946 case ZigTypeIdEnum:949 case ZigTypeIdEnum:
947 case ZigTypeIdUnion:950 case ZigTypeIdUnion:
948 case ZigTypeIdVector:
949 case ZigTypeIdFnFrame:951 case ZigTypeIdFnFrame:
950 return false;952 return false;
953 case ZigTypeIdVector:
954 return expected->data.vector.len == actual->data.vector.len &&
955 types_have_same_zig_comptime_repr(codegen, expected->data.vector.elem_type, actual->data.vector.elem_type);
951 case ZigTypeIdArray:956 case ZigTypeIdArray:
952 return expected->data.array.len == actual->data.array.len &&957 return expected->data.array.len == actual->data.array.len &&
953 expected->data.array.child_type == actual->data.array.child_type &&958 expected->data.array.child_type == actual->data.array.child_type &&
...@@ -12172,6 +12177,24 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted...@@ -12172,6 +12177,24 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
12172 return result;12177 return result;
12173 }12178 }
1217412179
12180 if (wanted_type->id == ZigTypeIdVector && actual_type->id == ZigTypeIdVector) {
12181 if (actual_type->data.vector.len != wanted_type->data.vector.len) {
12182 result.id = ConstCastResultIdVectorLength;
12183 return result;
12184 }
12185
12186 ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.vector.elem_type,
12187 actual_type->data.vector.elem_type, source_node, false);
12188 if (child.id == ConstCastResultIdInvalid)
12189 return child;
12190 if (child.id != ConstCastResultIdOk) {
12191 result.id = ConstCastResultIdVectorChild;
12192 return result;
12193 }
12194
12195 return result;
12196 }
12197
12175 result.id = ConstCastResultIdType;12198 result.id = ConstCastResultIdType;
12176 result.data.type_mismatch = heap::c_allocator.allocate_nonzero<ConstCastTypeMismatch>(1);12199 result.data.type_mismatch = heap::c_allocator.allocate_nonzero<ConstCastTypeMismatch>(1);
12177 result.data.type_mismatch->wanted_type = wanted_type;12200 result.data.type_mismatch->wanted_type = wanted_type;
...@@ -14288,37 +14311,62 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,...@@ -14288,37 +14311,62 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,
14288 return ira->codegen->invalid_inst_gen;14311 return ira->codegen->invalid_inst_gen;
14289}14312}
1429014313
14314static bool value_numeric_fits_in_type(ZigValue *value, ZigType *type_entry);
14315
14291static IrInstGen *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInst* source_instr,14316static IrInstGen *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInst* source_instr,
14292 IrInstGen *target, ZigType *wanted_type)14317 IrInstGen *target, ZigType *wanted_type)
14293{14318{
14294 assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdFloat);14319 ZigType *wanted_scalar_type = (target->value->type->id == ZigTypeIdVector) ?
14320 wanted_type->data.vector.elem_type : wanted_type;
14321
14322 assert(wanted_scalar_type->id == ZigTypeIdInt || wanted_scalar_type->id == ZigTypeIdFloat);
1429514323
14296 if (instr_is_comptime(target)) {14324 if (instr_is_comptime(target)) {
14297 ZigValue *val = ir_resolve_const(ira, target, UndefBad);14325 ZigValue *val = ir_resolve_const(ira, target, UndefBad);
14298 if (!val)14326 if (!val)
14299 return ira->codegen->invalid_inst_gen;14327 return ira->codegen->invalid_inst_gen;
14300 if (wanted_type->id == ZigTypeIdInt) {14328
14301 if (bigint_cmp_zero(&val->data.x_bigint) == CmpLT && !wanted_type->data.integral.is_signed) {14329 if (wanted_scalar_type->id == ZigTypeIdInt) {
14330 if (!wanted_scalar_type->data.integral.is_signed && value_cmp_numeric_val_any(val, CmpLT, nullptr)) {
14302 ir_add_error(ira, source_instr,14331 ir_add_error(ira, source_instr,
14303 buf_sprintf("attempt to cast negative value to unsigned integer"));14332 buf_sprintf("attempt to cast negative value to unsigned integer"));
14304 return ira->codegen->invalid_inst_gen;14333 return ira->codegen->invalid_inst_gen;
14305 }14334 }
14306 if (!bigint_fits_in_bits(&val->data.x_bigint, wanted_type->data.integral.bit_count,14335 if (!value_numeric_fits_in_type(val, wanted_scalar_type)) {
14307 wanted_type->data.integral.is_signed))
14308 {
14309 ir_add_error(ira, source_instr,14336 ir_add_error(ira, source_instr,
14310 buf_sprintf("cast from '%s' to '%s' truncates bits",14337 buf_sprintf("cast from '%s' to '%s' truncates bits",
14311 buf_ptr(&target->value->type->name), buf_ptr(&wanted_type->name)));14338 buf_ptr(&target->value->type->name), buf_ptr(&wanted_scalar_type->name)));
14312 return ira->codegen->invalid_inst_gen;14339 return ira->codegen->invalid_inst_gen;
14313 }14340 }
14314 }14341 }
14342
14315 IrInstGen *result = ir_const(ira, source_instr, wanted_type);14343 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
14316 result->value->type = wanted_type;14344 result->value->type = wanted_type;
14317 if (wanted_type->id == ZigTypeIdInt) {14345
14318 bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint);14346 if (wanted_type->id == ZigTypeIdVector) {
14347 result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(wanted_type->data.vector.len);
14348
14349 for (size_t i = 0; i < wanted_type->data.vector.len; i++) {
14350 ZigValue *scalar_dest_value = &result->value->data.x_array.data.s_none.elements[i];
14351 ZigValue *scalar_src_value = &val->data.x_array.data.s_none.elements[i];
14352
14353 scalar_dest_value->type = wanted_scalar_type;
14354 scalar_dest_value->special = ConstValSpecialStatic;
14355
14356 if (wanted_scalar_type->id == ZigTypeIdInt) {
14357 bigint_init_bigint(&scalar_dest_value->data.x_bigint, &scalar_src_value->data.x_bigint);
14358 } else {
14359 float_init_float(scalar_dest_value, scalar_src_value);
14360 }
14361 }
14319 } else {14362 } else {
14320 float_init_float(result->value, val);14363 if (wanted_type->id == ZigTypeIdInt) {
14364 bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint);
14365 } else {
14366 float_init_float(result->value, val);
14367 }
14321 }14368 }
14369
14322 return result;14370 return result;
14323 }14371 }
1432414372
...@@ -14761,6 +14809,8 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa...@@ -14761,6 +14809,8 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
14761 actual_signed, actual_type->data.integral.bit_count));14809 actual_signed, actual_type->data.integral.bit_count));
14762 break;14810 break;
14763 }14811 }
14812 case ConstCastResultIdVectorLength: // TODO
14813 case ConstCastResultIdVectorChild: // TODO
14764 case ConstCastResultIdFnAlign: // TODO14814 case ConstCastResultIdFnAlign: // TODO
14765 case ConstCastResultIdFnVarArgs: // TODO14815 case ConstCastResultIdFnVarArgs: // TODO
14766 case ConstCastResultIdFnReturnType: // TODO14816 case ConstCastResultIdFnReturnType: // TODO
...@@ -15444,12 +15494,35 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,...@@ -15444,12 +15494,35 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
15444 }15494 }
1544515495
15446 // @Vector(N,T1) to @Vector(N,T2)15496 // @Vector(N,T1) to @Vector(N,T2)
15447 if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector) {15497 if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector &&
15448 if (actual_type->data.vector.len == wanted_type->data.vector.len &&15498 actual_type->data.vector.len == wanted_type->data.vector.len)
15449 types_match_const_cast_only(ira, wanted_type->data.vector.elem_type,15499 {
15450 actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)15500 ZigType *scalar_actual_type = actual_type->data.vector.elem_type;
15501 ZigType *scalar_wanted_type = wanted_type->data.vector.elem_type;
15502
15503 // widening conversion
15504 if (scalar_wanted_type->id == ZigTypeIdInt &&
15505 scalar_actual_type->id == ZigTypeIdInt &&
15506 scalar_wanted_type->data.integral.is_signed == scalar_actual_type->data.integral.is_signed &&
15507 scalar_wanted_type->data.integral.bit_count >= scalar_actual_type->data.integral.bit_count)
15508 {
15509 return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
15510 }
15511
15512 // small enough unsigned ints can get casted to large enough signed ints
15513 if (scalar_wanted_type->id == ZigTypeIdInt && scalar_wanted_type->data.integral.is_signed &&
15514 scalar_actual_type->id == ZigTypeIdInt && !scalar_actual_type->data.integral.is_signed &&
15515 scalar_wanted_type->data.integral.bit_count > scalar_actual_type->data.integral.bit_count)
15516 {
15517 return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
15518 }
15519
15520 // float widening conversion
15521 if (scalar_wanted_type->id == ZigTypeIdFloat &&
15522 scalar_actual_type->id == ZigTypeIdFloat &&
15523 scalar_wanted_type->data.floating.bit_count >= scalar_actual_type->data.floating.bit_count)
15451 {15524 {
15452 return ir_analyze_bit_cast(ira, source_instr, value, wanted_type);15525 return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
15453 }15526 }
15454 }15527 }
1545515528
...@@ -17710,6 +17783,33 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {...@@ -17710,6 +17783,33 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
17710 zig_unreachable();17783 zig_unreachable();
17711}17784}
1771217785
17786// Returns true if integer `value` can be converted to `type_entry` without
17787// losing data.
17788// If `value` is a vector the function returns true if this is valid for every
17789// element.
17790static bool value_numeric_fits_in_type(ZigValue *value, ZigType *type_entry) {
17791 assert(value->special == ConstValSpecialStatic);
17792 assert(type_entry->id == ZigTypeIdInt);
17793
17794 switch (value->type->id) {
17795 case ZigTypeIdComptimeInt:
17796 case ZigTypeIdInt: {
17797 return bigint_fits_in_bits(&value->data.x_bigint, type_entry->data.integral.bit_count,
17798 type_entry->data.integral.is_signed);
17799 }
17800 case ZigTypeIdVector: {
17801 for (size_t i = 0; i < value->type->data.vector.len; i++) {
17802 ZigValue *scalar_value = &value->data.x_array.data.s_none.elements[i];
17803 const bool result = bigint_fits_in_bits(&scalar_value->data.x_bigint,
17804 type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
17805 if (!result) return false;
17806 }
17807 return true;
17808 }
17809 default: zig_unreachable();
17810 }
17811}
17812
17713static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) {17813static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) {
17714 assert(left->special == ConstValSpecialStatic);17814 assert(left->special == ConstValSpecialStatic);
17715 assert(right == nullptr || right->special == ConstValSpecialStatic);17815 assert(right == nullptr || right->special == ConstValSpecialStatic);
...@@ -27132,8 +27232,12 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa...@@ -27132,8 +27232,12 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa
27132 if (type_is_invalid(dest_type))27232 if (type_is_invalid(dest_type))
27133 return ira->codegen->invalid_inst_gen;27233 return ira->codegen->invalid_inst_gen;
2713427234
27135 if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) {27235 ZigType *scalar_dest_type = (dest_type->id == ZigTypeIdVector) ?
27136 ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));27236 dest_type->data.vector.elem_type : dest_type;
27237
27238 if (scalar_dest_type->id != ZigTypeIdInt && scalar_dest_type->id != ZigTypeIdComptimeInt) {
27239 ir_add_error(ira, &instruction->dest_type->base,
27240 buf_sprintf("expected integer type, found '%s'", buf_ptr(&scalar_dest_type->name)));
27137 return ira->codegen->invalid_inst_gen;27241 return ira->codegen->invalid_inst_gen;
27138 }27242 }
2713927243
...@@ -27141,13 +27245,16 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa...@@ -27141,13 +27245,16 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa
27141 if (type_is_invalid(target->value->type))27245 if (type_is_invalid(target->value->type))
27142 return ira->codegen->invalid_inst_gen;27246 return ira->codegen->invalid_inst_gen;
2714327247
27144 if (target->value->type->id != ZigTypeIdInt && target->value->type->id != ZigTypeIdComptimeInt) {27248 ZigType *scalar_target_type = (target->value->type->id == ZigTypeIdVector) ?
27249 target->value->type->data.vector.elem_type : target->value->type;
27250
27251 if (scalar_target_type->id != ZigTypeIdInt && scalar_target_type->id != ZigTypeIdComptimeInt) {
27145 ir_add_error(ira, &instruction->target->base, buf_sprintf("expected integer type, found '%s'",27252 ir_add_error(ira, &instruction->target->base, buf_sprintf("expected integer type, found '%s'",
27146 buf_ptr(&target->value->type->name)));27253 buf_ptr(&scalar_target_type->name)));
27147 return ira->codegen->invalid_inst_gen;27254 return ira->codegen->invalid_inst_gen;
27148 }27255 }
2714927256
27150 if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeInt) {27257 if (scalar_dest_type->id == ZigTypeIdComptimeInt) {
27151 ZigValue *val = ir_resolve_const(ira, target, UndefBad);27258 ZigValue *val = ir_resolve_const(ira, target, UndefBad);
27152 if (val == nullptr)27259 if (val == nullptr)
27153 return ira->codegen->invalid_inst_gen;27260 return ira->codegen->invalid_inst_gen;
...@@ -27200,6 +27307,7 @@ static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFlo...@@ -27200,6 +27307,7 @@ static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFlo
27200 if (val == nullptr)27307 if (val == nullptr)
27201 return ira->codegen->invalid_inst_gen;27308 return ira->codegen->invalid_inst_gen;
2720227309
27310 // XXX: This will trigger an assertion failure if dest_type is comptime_float
27203 return ir_analyze_widen_or_shorten(ira, &instruction->target->base, target, dest_type);27311 return ir_analyze_widen_or_shorten(ira, &instruction->target->base, target, dest_type);
27204 }27312 }
2720527313
test/compile_errors.zig+2-3
...@@ -2944,7 +2944,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -2944,7 +2944,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2944 "tmp.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void",2944 "tmp.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void",
2945 "tmp.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'",2945 "tmp.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'",
2946 });2946 });
2947
2948 cases.add("cast negative value to unsigned integer",2947 cases.add("cast negative value to unsigned integer",
2949 \\comptime {2948 \\comptime {
2950 \\ const value: i32 = -1;2949 \\ const value: i32 = -1;
...@@ -2955,7 +2954,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -2955,7 +2954,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2955 \\ const unsigned: u32 = value;2954 \\ const unsigned: u32 = value;
2956 \\}2955 \\}
2957 , &[_][]const u8{2956 , &[_][]const u8{
2958 "tmp.zig:3:36: error: cannot cast negative value -1 to unsigned integer type 'u32'",2957 "tmp.zig:3:22: error: attempt to cast negative value to unsigned integer",
2959 "tmp.zig:7:27: error: cannot cast negative value -1 to unsigned integer type 'u32'",2958 "tmp.zig:7:27: error: cannot cast negative value -1 to unsigned integer type 'u32'",
2960 });2959 });
29612960
...@@ -2977,7 +2976,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -2977,7 +2976,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2977 \\ var unsigned: u64 = signed;2976 \\ var unsigned: u64 = signed;
2978 \\}2977 \\}
2979 , &[_][]const u8{2978 , &[_][]const u8{
2980 "tmp.zig:3:31: error: integer value 300 cannot be coerced to type 'u8'",2979 "tmp.zig:3:18: error: cast from 'u16' to 'u8' truncates bits",
2981 "tmp.zig:7:22: error: integer value 300 cannot be coerced to type 'u8'",2980 "tmp.zig:7:22: error: integer value 300 cannot be coerced to type 'u8'",
2982 "tmp.zig:11:20: error: expected type 'u8', found 'u16'",2981 "tmp.zig:11:20: error: expected type 'u8', found 'u16'",
2983 "tmp.zig:11:20: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values",2982 "tmp.zig:11:20: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values",
test/runtime_safety.zig+30
...@@ -70,6 +70,36 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -70,6 +70,36 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
70 );70 );
71 }71 }
7272
73 cases.addRuntimeSafety("truncating vector cast",
74 \\const std = @import("std");
75 \\const V = @import("std").meta.Vector;
76 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
77 \\ if (std.mem.eql(u8, message, "integer cast truncated bits")) {
78 \\ std.process.exit(126); // good
79 \\ }
80 \\ std.process.exit(0); // test failed
81 \\}
82 \\pub fn main() void {
83 \\ var x = @splat(4, @as(u32, 0xdeadbeef));
84 \\ var y = @intCast(V(4, u16), x);
85 \\}
86 );
87
88 cases.addRuntimeSafety("unsigned-signed vector cast",
89 \\const std = @import("std");
90 \\const V = @import("std").meta.Vector;
91 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
92 \\ if (std.mem.eql(u8, message, "attempt to cast negative value to unsigned integer")) {
93 \\ std.process.exit(126); // good
94 \\ }
95 \\ std.process.exit(0); // test failed
96 \\}
97 \\pub fn main() void {
98 \\ var x = @splat(4, @as(u32, 0x80000000));
99 \\ var y = @intCast(V(4, i32), x);
100 \\}
101 );
102
73 cases.addRuntimeSafety("shift left by huge amount",103 cases.addRuntimeSafety("shift left by huge amount",
74 \\const std = @import("std");104 \\const std = @import("std");
75 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {105 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
test/stage1/behavior/cast.zig+38
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
3const mem = std.mem;3const mem = std.mem;
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const Vector = std.meta.Vector;
56
6test "int to ptr cast" {7test "int to ptr cast" {
7 const x = @as(usize, 13);8 const x = @as(usize, 13);
...@@ -364,6 +365,43 @@ test "@floatCast comptime_int and comptime_float" {...@@ -364,6 +365,43 @@ test "@floatCast comptime_int and comptime_float" {
364 }365 }
365}366}
366367
368test "vector casts" {
369 const S = struct {
370 fn doTheTest() void {
371 // Upcast (implicit, equivalent to @intCast)
372 var up0: Vector(2, u8) = [_]u8{ 0x55, 0xaa };
373 var up1 = @as(Vector(2, u16), up0);
374 var up2 = @as(Vector(2, u32), up0);
375 var up3 = @as(Vector(2, u64), up0);
376 // Downcast (safety-checked)
377 var down0 = up3;
378 var down1 = @intCast(Vector(2, u32), down0);
379 var down2 = @intCast(Vector(2, u16), down0);
380 var down3 = @intCast(Vector(2, u8), down0);
381
382 expect(mem.eql(u16, &@as([2]u16, up1), &[2]u16{ 0x55, 0xaa }));
383 expect(mem.eql(u32, &@as([2]u32, up2), &[2]u32{ 0x55, 0xaa }));
384 expect(mem.eql(u64, &@as([2]u64, up3), &[2]u64{ 0x55, 0xaa }));
385
386 expect(mem.eql(u32, &@as([2]u32, down1), &[2]u32{ 0x55, 0xaa }));
387 expect(mem.eql(u16, &@as([2]u16, down2), &[2]u16{ 0x55, 0xaa }));
388 expect(mem.eql(u8, &@as([2]u8, down3), &[2]u8{ 0x55, 0xaa }));
389 }
390
391 fn doTheTestFloat() void {
392 var vec = @splat(2, @as(f32, 1234.0));
393 var wider: Vector(2, f64) = vec;
394 expect(wider[0] == 1234.0);
395 expect(wider[1] == 1234.0);
396 }
397 };
398
399 S.doTheTest();
400 comptime S.doTheTest();
401 S.doTheTestFloat();
402 comptime S.doTheTestFloat();
403}
404
367test "comptime_int @intToFloat" {405test "comptime_int @intToFloat" {
368 {406 {
369 const result = @intToFloat(f16, 1234);407 const result = @intToFloat(f16, 1234);