| author | |
| committer | |
| log | e6ac082437b87fccf6a7e592f813d3403427e512 |
| tree | 4c61a3a5d19afd57cf1b20010179ccbf538347c2 |
| parent | 78199a684f50f37f652dabcc88b02e8b998ab5fa |
| parent | 475fc2934b9bb4c0c10ca213c4322cc902c4b303 |
| signature |
stage1: Implement `@intCast` between vectors7 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 | 1251 | }, |
| 1252 | 1252 | } |
| 1253 | 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 | 1255 | return parseError(tokenizer, code.source_token, "example did not have expected compile error", .{}); |
| 1256 | 1256 | } |
| 1257 | 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 | 1306 | }, |
| 1307 | 1307 | } |
| 1308 | 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 | 1310 | return parseError(tokenizer, code.source_token, "example did not have expected runtime safety error message", .{}); |
| 1311 | 1311 | } |
| 1312 | 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 | 1385 | }, |
| 1386 | 1386 | } |
| 1387 | 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 | 1389 | return parseError(tokenizer, code.source_token, "example did not have expected compile error message", .{}); |
| 1390 | 1390 | } |
| 1391 | 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 | 8713 | {#header_close#} |
| 8714 | 8714 | {#header_open|Cast Negative Number to Unsigned Integer#} |
| 8715 | 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#} | |
| 8717 | 8717 | comptime { |
| 8718 | 8718 | const value: i32 = -1; |
| 8719 | 8719 | const unsigned = @intCast(u32, value); |
| ... | ... | @@ -8735,7 +8735,7 @@ pub fn main() void { |
| 8735 | 8735 | {#header_close#} |
| 8736 | 8736 | {#header_open|Cast Truncates Data#} |
| 8737 | 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#} | |
| 8739 | 8739 | comptime { |
| 8740 | 8740 | const spartan_count: u16 = 300; |
| 8741 | 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 | 1433 | static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { |
| 1434 | 1434 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type)); |
| 1435 | 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 | 1439 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk"); |
| 1437 | 1440 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail"); |
| 1438 | 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 | 1453 | assert(actual_type->id == wanted_type->id); |
| 1451 | 1454 | assert(expr_val != nullptr); |
| 1452 | 1455 | |
| 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 | 1461 | uint64_t actual_bits; |
| 1454 | 1462 | uint64_t wanted_bits; |
| 1455 | if (actual_type->id == ZigTypeIdFloat) { | |
| 1456 | actual_bits = actual_type->data.floating.bit_count; | |
| 1457 | wanted_bits = wanted_type->data.floating.bit_count; | |
| 1458 | } else if (actual_type->id == ZigTypeIdInt) { | |
| 1459 | actual_bits = actual_type->data.integral.bit_count; | |
| 1460 | wanted_bits = wanted_type->data.integral.bit_count; | |
| 1463 | if (scalar_actual_type->id == ZigTypeIdFloat) { | |
| 1464 | actual_bits = scalar_actual_type->data.floating.bit_count; | |
| 1465 | wanted_bits = scalar_wanted_type->data.floating.bit_count; | |
| 1466 | } else if (scalar_actual_type->id == ZigTypeIdInt) { | |
| 1467 | actual_bits = scalar_actual_type->data.integral.bit_count; | |
| 1468 | wanted_bits = scalar_wanted_type->data.integral.bit_count; | |
| 1461 | 1469 | } else { |
| 1462 | 1470 | zig_unreachable(); |
| 1463 | 1471 | } |
| 1464 | 1472 | |
| 1465 | if (actual_type->id == ZigTypeIdInt && want_runtime_safety && ( | |
| 1473 | if (scalar_actual_type->id == ZigTypeIdInt && want_runtime_safety && ( | |
| 1466 | 1474 | // 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 | 1476 | // 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 | 1479 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, actual_type)); |
| 1472 | 1480 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, ""); |
| 1473 | 1481 | |
| 1474 | 1482 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SignCastOk"); |
| 1475 | 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 | 1487 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 1477 | 1488 | |
| 1478 | 1489 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | ... | @@ -1484,10 +1495,10 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z |
| 1484 | 1495 | if (actual_bits == wanted_bits) { |
| 1485 | 1496 | return expr_val; |
| 1486 | 1497 | } else if (actual_bits < wanted_bits) { |
| 1487 | if (actual_type->id == ZigTypeIdFloat) { | |
| 1498 | if (scalar_actual_type->id == ZigTypeIdFloat) { | |
| 1488 | 1499 | return LLVMBuildFPExt(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); |
| 1489 | } else if (actual_type->id == ZigTypeIdInt) { | |
| 1490 | if (actual_type->data.integral.is_signed) { | |
| 1500 | } else if (scalar_actual_type->id == ZigTypeIdInt) { | |
| 1501 | if (scalar_actual_type->data.integral.is_signed) { | |
| 1491 | 1502 | return LLVMBuildSExt(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); |
| 1492 | 1503 | } else { |
| 1493 | 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 | 1507 | zig_unreachable(); |
| 1497 | 1508 | } |
| 1498 | 1509 | } else if (actual_bits > wanted_bits) { |
| 1499 | if (actual_type->id == ZigTypeIdFloat) { | |
| 1510 | if (scalar_actual_type->id == ZigTypeIdFloat) { | |
| 1500 | 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 | 1513 | if (wanted_bits == 0) { |
| 1503 | 1514 | if (!want_runtime_safety) |
| 1504 | 1515 | return nullptr; |
| ... | ... | @@ -1510,12 +1521,15 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z |
| 1510 | 1521 | return trunc_val; |
| 1511 | 1522 | } |
| 1512 | 1523 | LLVMValueRef orig_val; |
| 1513 | if (wanted_type->data.integral.is_signed) { | |
| 1524 | if (scalar_wanted_type->data.integral.is_signed) { | |
| 1514 | 1525 | orig_val = LLVMBuildSExt(g->builder, trunc_val, get_llvm_type(g, actual_type), ""); |
| 1515 | 1526 | } else { |
| 1516 | 1527 | orig_val = LLVMBuildZExt(g->builder, trunc_val, get_llvm_type(g, actual_type), ""); |
| 1517 | 1528 | } |
| 1518 | 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 | 1533 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk"); |
| 1520 | 1534 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail"); |
| 1521 | 1535 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
src/stage1/ir.cpp+129-21| ... | ... | @@ -86,6 +86,8 @@ enum ConstCastResultId { |
| 86 | 86 | ConstCastResultIdCV, |
| 87 | 87 | ConstCastResultIdPtrSentinel, |
| 88 | 88 | ConstCastResultIdIntShorten, |
| 89 | ConstCastResultIdVectorLength, | |
| 90 | ConstCastResultIdVectorChild, | |
| 89 | 91 | }; |
| 90 | 92 | |
| 91 | 93 | struct ConstCastOnly; |
| ... | ... | @@ -912,6 +914,7 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte |
| 912 | 914 | if (is_opt_err_set(expected) && is_opt_err_set(actual)) |
| 913 | 915 | return true; |
| 914 | 916 | |
| 917 | // XXX: Vectors and arrays are interchangeable at comptime | |
| 915 | 918 | if (expected->id != actual->id) |
| 916 | 919 | return false; |
| 917 | 920 | |
| ... | ... | @@ -945,9 +948,11 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte |
| 945 | 948 | case ZigTypeIdErrorUnion: |
| 946 | 949 | case ZigTypeIdEnum: |
| 947 | 950 | case ZigTypeIdUnion: |
| 948 | case ZigTypeIdVector: | |
| 949 | 951 | case ZigTypeIdFnFrame: |
| 950 | 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 | 956 | case ZigTypeIdArray: |
| 952 | 957 | return expected->data.array.len == actual->data.array.len && |
| 953 | 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 | 12177 | return result; |
| 12173 | 12178 | } |
| 12174 | 12179 | |
| 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 | 12198 | result.id = ConstCastResultIdType; |
| 12176 | 12199 | result.data.type_mismatch = heap::c_allocator.allocate_nonzero<ConstCastTypeMismatch>(1); |
| 12177 | 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 | 14311 | return ira->codegen->invalid_inst_gen; |
| 14289 | 14312 | } |
| 14290 | 14313 | |
| 14314 | static bool value_numeric_fits_in_type(ZigValue *value, ZigType *type_entry); | |
| 14315 | ||
| 14291 | 14316 | static IrInstGen *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInst* source_instr, |
| 14292 | 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); | |
| 14295 | 14323 | |
| 14296 | 14324 | if (instr_is_comptime(target)) { |
| 14297 | 14325 | ZigValue *val = ir_resolve_const(ira, target, UndefBad); |
| 14298 | 14326 | if (!val) |
| 14299 | 14327 | return ira->codegen->invalid_inst_gen; |
| 14300 | if (wanted_type->id == ZigTypeIdInt) { | |
| 14301 | if (bigint_cmp_zero(&val->data.x_bigint) == CmpLT && !wanted_type->data.integral.is_signed) { | |
| 14328 | ||
| 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 | 14331 | ir_add_error(ira, source_instr, |
| 14303 | 14332 | buf_sprintf("attempt to cast negative value to unsigned integer")); |
| 14304 | 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, | |
| 14307 | wanted_type->data.integral.is_signed)) | |
| 14308 | { | |
| 14335 | if (!value_numeric_fits_in_type(val, wanted_scalar_type)) { | |
| 14309 | 14336 | ir_add_error(ira, source_instr, |
| 14310 | 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 | 14339 | return ira->codegen->invalid_inst_gen; |
| 14313 | 14340 | } |
| 14314 | 14341 | } |
| 14342 | ||
| 14315 | 14343 | IrInstGen *result = ir_const(ira, source_instr, wanted_type); |
| 14316 | 14344 | result->value->type = wanted_type; |
| 14317 | if (wanted_type->id == ZigTypeIdInt) { | |
| 14318 | bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); | |
| 14345 | ||
| 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 | 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 | 14370 | return result; |
| 14323 | 14371 | } |
| 14324 | 14372 | |
| ... | ... | @@ -14761,6 +14809,8 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa |
| 14761 | 14809 | actual_signed, actual_type->data.integral.bit_count)); |
| 14762 | 14810 | break; |
| 14763 | 14811 | } |
| 14812 | case ConstCastResultIdVectorLength: // TODO | |
| 14813 | case ConstCastResultIdVectorChild: // TODO | |
| 14764 | 14814 | case ConstCastResultIdFnAlign: // TODO |
| 14765 | 14815 | case ConstCastResultIdFnVarArgs: // TODO |
| 14766 | 14816 | case ConstCastResultIdFnReturnType: // TODO |
| ... | ... | @@ -15444,12 +15494,35 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, |
| 15444 | 15494 | } |
| 15445 | 15495 | |
| 15446 | 15496 | // @Vector(N,T1) to @Vector(N,T2) |
| 15447 | if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector) { | |
| 15448 | if (actual_type->data.vector.len == wanted_type->data.vector.len && | |
| 15449 | types_match_const_cast_only(ira, wanted_type->data.vector.elem_type, | |
| 15450 | actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) | |
| 15497 | if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector && | |
| 15498 | actual_type->data.vector.len == wanted_type->data.vector.len) | |
| 15499 | { | |
| 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 | } |
| 15455 | 15528 | |
| ... | ... | @@ -17710,6 +17783,33 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { |
| 17710 | 17783 | zig_unreachable(); |
| 17711 | 17784 | } |
| 17712 | 17785 | |
| 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. | |
| 17790 | static 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 | ||
| 17713 | 17813 | static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) { |
| 17714 | 17814 | assert(left->special == ConstValSpecialStatic); |
| 17715 | 17815 | assert(right == nullptr || right->special == ConstValSpecialStatic); |
| ... | ... | @@ -27132,8 +27232,12 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa |
| 27132 | 27232 | if (type_is_invalid(dest_type)) |
| 27133 | 27233 | return ira->codegen->invalid_inst_gen; |
| 27134 | 27234 | |
| 27135 | if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) { | |
| 27136 | ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); | |
| 27235 | ZigType *scalar_dest_type = (dest_type->id == ZigTypeIdVector) ? | |
| 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 | 27241 | return ira->codegen->invalid_inst_gen; |
| 27138 | 27242 | } |
| 27139 | 27243 | |
| ... | ... | @@ -27141,13 +27245,16 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa |
| 27141 | 27245 | if (type_is_invalid(target->value->type)) |
| 27142 | 27246 | return ira->codegen->invalid_inst_gen; |
| 27143 | 27247 | |
| 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 | 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 | 27254 | return ira->codegen->invalid_inst_gen; |
| 27148 | 27255 | } |
| 27149 | 27256 | |
| 27150 | if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeInt) { | |
| 27257 | if (scalar_dest_type->id == ZigTypeIdComptimeInt) { | |
| 27151 | 27258 | ZigValue *val = ir_resolve_const(ira, target, UndefBad); |
| 27152 | 27259 | if (val == nullptr) |
| 27153 | 27260 | return ira->codegen->invalid_inst_gen; |
| ... | ... | @@ -27200,6 +27307,7 @@ static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFlo |
| 27200 | 27307 | if (val == nullptr) |
| 27201 | 27308 | return ira->codegen->invalid_inst_gen; |
| 27202 | 27309 | |
| 27310 | // XXX: This will trigger an assertion failure if dest_type is comptime_float | |
| 27203 | 27311 | return ir_analyze_widen_or_shorten(ira, &instruction->target->base, target, dest_type); |
| 27204 | 27312 | } |
| 27205 | 27313 |
test/compile_errors.zig+2-3| ... | ... | @@ -2944,7 +2944,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2944 | 2944 | "tmp.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void", |
| 2945 | 2945 | "tmp.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'", |
| 2946 | 2946 | }); |
| 2947 | ||
| 2948 | 2947 | cases.add("cast negative value to unsigned integer", |
| 2949 | 2948 | \\comptime { |
| 2950 | 2949 | \\ const value: i32 = -1; |
| ... | ... | @@ -2955,7 +2954,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2955 | 2954 | \\ const unsigned: u32 = value; |
| 2956 | 2955 | \\} |
| 2957 | 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 | 2958 | "tmp.zig:7:27: error: cannot cast negative value -1 to unsigned integer type 'u32'", |
| 2960 | 2959 | }); |
| 2961 | 2960 | |
| ... | ... | @@ -2977,7 +2976,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2977 | 2976 | \\ var unsigned: u64 = signed; |
| 2978 | 2977 | \\} |
| 2979 | 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 | 2980 | "tmp.zig:7:22: error: integer value 300 cannot be coerced to type 'u8'", |
| 2982 | 2981 | "tmp.zig:11:20: error: expected type 'u8', found 'u16'", |
| 2983 | 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 | 70 | ); |
| 71 | 71 | } |
| 72 | 72 | |
| 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 | 103 | cases.addRuntimeSafety("shift left by huge amount", |
| 74 | 104 | \\const std = @import("std"); |
| 75 | 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 | 2 | const expect = std.testing.expect; |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const maxInt = std.math.maxInt; |
| 5 | const Vector = std.meta.Vector; | |
| 5 | 6 | |
| 6 | 7 | test "int to ptr cast" { |
| 7 | 8 | const x = @as(usize, 13); |
| ... | ... | @@ -364,6 +365,43 @@ test "@floatCast comptime_int and comptime_float" { |
| 364 | 365 | } |
| 365 | 366 | } |
| 366 | 367 | |
| 368 | test "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 | ||
| 367 | 405 | test "comptime_int @intToFloat" { |
| 368 | 406 | { |
| 369 | 407 | const result = @intToFloat(f16, 1234); |