| author | |
| committer | |
| log | 2f465761bb960ef945f62186ba0b38a95684af51 |
| tree | d0bd076ba334b0b93d17c4e031dd95575894c942 |
| parent | 245d98d32dd29e80de9732f415a4731748008acf |
Explicit and implicit integer casts on vector types are now supported
and follow the same rules as their scalar counterparts.
Implicit float casts are accidentally supported, `@floatCast` is still
not vector-aware.5 files changed, 228 insertions(+), 39 deletions(-)
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 |
| 1433 | static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { | 1433 | static 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); |
| 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 | 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 | } |
| 1464 | 1472 | ||
| 1465 | if (actual_type->id == ZigTypeIdInt && want_runtime_safety && ( | 1473 | if (scalar_actual_type->id == ZigTypeIdInt && want_runtime_safety && ( |
| 1466 | // negative to unsigned | 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 | // unsigned would become negative | 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 | 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, ""); |
| 1473 | 1481 | ||
| 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); |
| 1477 | 1488 | ||
| 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 | }; |
| 90 | 92 | ||
| 91 | struct ConstCastOnly; | 93 | struct ConstCastOnly; |
| ... | @@ -914,6 +916,7 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte | ... | @@ -914,6 +916,7 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte |
| 914 | if (is_opt_err_set(expected) && is_opt_err_set(actual)) | 916 | if (is_opt_err_set(expected) && is_opt_err_set(actual)) |
| 915 | return true; | 917 | return true; |
| 916 | 918 | ||
| 919 | // XXX: Vectors and arrays are interchangeable at comptime | ||
| 917 | if (expected->id != actual->id) | 920 | if (expected->id != actual->id) |
| 918 | return false; | 921 | return false; |
| 919 | 922 | ||
| ... | @@ -947,9 +950,11 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte | ... | @@ -947,9 +950,11 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte |
| 947 | case ZigTypeIdErrorUnion: | 950 | case ZigTypeIdErrorUnion: |
| 948 | case ZigTypeIdEnum: | 951 | case ZigTypeIdEnum: |
| 949 | case ZigTypeIdUnion: | 952 | case ZigTypeIdUnion: |
| 950 | case ZigTypeIdVector: | ||
| 951 | case ZigTypeIdFnFrame: | 953 | case ZigTypeIdFnFrame: |
| 952 | return false; | 954 | return false; |
| 955 | case ZigTypeIdVector: | ||
| 956 | return expected->data.vector.len == actual->data.vector.len && | ||
| 957 | types_have_same_zig_comptime_repr(codegen, expected->data.vector.elem_type, actual->data.vector.elem_type); | ||
| 953 | case ZigTypeIdArray: | 958 | case ZigTypeIdArray: |
| 954 | return expected->data.array.len == actual->data.array.len && | 959 | return expected->data.array.len == actual->data.array.len && |
| 955 | expected->data.array.child_type == actual->data.array.child_type && | 960 | expected->data.array.child_type == actual->data.array.child_type && |
| ... | @@ -12190,6 +12195,24 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted | ... | @@ -12190,6 +12195,24 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted |
| 12190 | return result; | 12195 | return result; |
| 12191 | } | 12196 | } |
| 12192 | 12197 | ||
| 12198 | if (wanted_type->id == ZigTypeIdVector && actual_type->id == ZigTypeIdVector) { | ||
| 12199 | if (actual_type->data.vector.len != wanted_type->data.vector.len) { | ||
| 12200 | result.id = ConstCastResultIdVectorLength; | ||
| 12201 | return result; | ||
| 12202 | } | ||
| 12203 | |||
| 12204 | ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.vector.elem_type, | ||
| 12205 | actual_type->data.vector.elem_type, source_node, false); | ||
| 12206 | if (child.id == ConstCastResultIdInvalid) | ||
| 12207 | return child; | ||
| 12208 | if (child.id != ConstCastResultIdOk) { | ||
| 12209 | result.id = ConstCastResultIdVectorChild; | ||
| 12210 | return result; | ||
| 12211 | } | ||
| 12212 | |||
| 12213 | return result; | ||
| 12214 | } | ||
| 12215 | |||
| 12193 | result.id = ConstCastResultIdType; | 12216 | result.id = ConstCastResultIdType; |
| 12194 | result.data.type_mismatch = heap::c_allocator.allocate_nonzero<ConstCastTypeMismatch>(1); | 12217 | result.data.type_mismatch = heap::c_allocator.allocate_nonzero<ConstCastTypeMismatch>(1); |
| 12195 | result.data.type_mismatch->wanted_type = wanted_type; | 12218 | result.data.type_mismatch->wanted_type = wanted_type; |
| ... | @@ -14306,37 +14329,62 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr, | ... | @@ -14306,37 +14329,62 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr, |
| 14306 | return ira->codegen->invalid_inst_gen; | 14329 | return ira->codegen->invalid_inst_gen; |
| 14307 | } | 14330 | } |
| 14308 | 14331 | ||
| 14332 | static bool value_numeric_fits_in_type(ZigValue *value, ZigType *type_entry); | ||
| 14333 | |||
| 14309 | static IrInstGen *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInst* source_instr, | 14334 | static IrInstGen *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInst* source_instr, |
| 14310 | IrInstGen *target, ZigType *wanted_type) | 14335 | IrInstGen *target, ZigType *wanted_type) |
| 14311 | { | 14336 | { |
| 14312 | assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdFloat); | 14337 | ZigType *wanted_scalar_type = (target->value->type->id == ZigTypeIdVector) ? |
| 14338 | wanted_type->data.vector.elem_type : wanted_type; | ||
| 14339 | |||
| 14340 | assert(wanted_scalar_type->id == ZigTypeIdInt || wanted_scalar_type->id == ZigTypeIdFloat); | ||
| 14313 | 14341 | ||
| 14314 | if (instr_is_comptime(target)) { | 14342 | if (instr_is_comptime(target)) { |
| 14315 | ZigValue *val = ir_resolve_const(ira, target, UndefBad); | 14343 | ZigValue *val = ir_resolve_const(ira, target, UndefBad); |
| 14316 | if (!val) | 14344 | if (!val) |
| 14317 | return ira->codegen->invalid_inst_gen; | 14345 | return ira->codegen->invalid_inst_gen; |
| 14318 | if (wanted_type->id == ZigTypeIdInt) { | 14346 | |
| 14319 | if (bigint_cmp_zero(&val->data.x_bigint) == CmpLT && !wanted_type->data.integral.is_signed) { | 14347 | if (wanted_scalar_type->id == ZigTypeIdInt) { |
| 14348 | if (!wanted_scalar_type->data.integral.is_signed && value_cmp_numeric_val_any(val, CmpLT, nullptr)) { | ||
| 14320 | ir_add_error(ira, source_instr, | 14349 | ir_add_error(ira, source_instr, |
| 14321 | buf_sprintf("attempt to cast negative value to unsigned integer")); | 14350 | buf_sprintf("attempt to cast negative value to unsigned integer")); |
| 14322 | return ira->codegen->invalid_inst_gen; | 14351 | return ira->codegen->invalid_inst_gen; |
| 14323 | } | 14352 | } |
| 14324 | if (!bigint_fits_in_bits(&val->data.x_bigint, wanted_type->data.integral.bit_count, | 14353 | if (!value_numeric_fits_in_type(val, wanted_scalar_type)) { |
| 14325 | wanted_type->data.integral.is_signed)) | ||
| 14326 | { | ||
| 14327 | ir_add_error(ira, source_instr, | 14354 | ir_add_error(ira, source_instr, |
| 14328 | buf_sprintf("cast from '%s' to '%s' truncates bits", | 14355 | buf_sprintf("cast from '%s' to '%s' truncates bits", |
| 14329 | buf_ptr(&target->value->type->name), buf_ptr(&wanted_type->name))); | 14356 | buf_ptr(&target->value->type->name), buf_ptr(&wanted_scalar_type->name))); |
| 14330 | return ira->codegen->invalid_inst_gen; | 14357 | return ira->codegen->invalid_inst_gen; |
| 14331 | } | 14358 | } |
| 14332 | } | 14359 | } |
| 14360 | |||
| 14333 | IrInstGen *result = ir_const(ira, source_instr, wanted_type); | 14361 | IrInstGen *result = ir_const(ira, source_instr, wanted_type); |
| 14334 | result->value->type = wanted_type; | 14362 | result->value->type = wanted_type; |
| 14335 | if (wanted_type->id == ZigTypeIdInt) { | 14363 | |
| 14336 | bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); | 14364 | if (wanted_type->id == ZigTypeIdVector) { |
| 14365 | result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(wanted_type->data.vector.len); | ||
| 14366 | |||
| 14367 | for (size_t i = 0; i < wanted_type->data.vector.len; i++) { | ||
| 14368 | ZigValue *scalar_dest_value = &result->value->data.x_array.data.s_none.elements[i]; | ||
| 14369 | ZigValue *scalar_src_value = &val->data.x_array.data.s_none.elements[i]; | ||
| 14370 | |||
| 14371 | scalar_dest_value->type = wanted_scalar_type; | ||
| 14372 | scalar_dest_value->special = ConstValSpecialStatic; | ||
| 14373 | |||
| 14374 | if (wanted_scalar_type->id == ZigTypeIdInt) { | ||
| 14375 | bigint_init_bigint(&scalar_dest_value->data.x_bigint, &scalar_src_value->data.x_bigint); | ||
| 14376 | } else { | ||
| 14377 | float_init_float(scalar_dest_value, scalar_src_value); | ||
| 14378 | } | ||
| 14379 | } | ||
| 14337 | } else { | 14380 | } else { |
| 14338 | float_init_float(result->value, val); | 14381 | if (wanted_type->id == ZigTypeIdInt) { |
| 14382 | bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); | ||
| 14383 | } else { | ||
| 14384 | float_init_float(result->value, val); | ||
| 14385 | } | ||
| 14339 | } | 14386 | } |
| 14387 | |||
| 14340 | return result; | 14388 | return result; |
| 14341 | } | 14389 | } |
| 14342 | 14390 | ||
| ... | @@ -14779,6 +14827,8 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa | ... | @@ -14779,6 +14827,8 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa |
| 14779 | actual_signed, actual_type->data.integral.bit_count)); | 14827 | actual_signed, actual_type->data.integral.bit_count)); |
| 14780 | break; | 14828 | break; |
| 14781 | } | 14829 | } |
| 14830 | case ConstCastResultIdVectorLength: // TODO | ||
| 14831 | case ConstCastResultIdVectorChild: // TODO | ||
| 14782 | case ConstCastResultIdFnAlign: // TODO | 14832 | case ConstCastResultIdFnAlign: // TODO |
| 14783 | case ConstCastResultIdFnVarArgs: // TODO | 14833 | case ConstCastResultIdFnVarArgs: // TODO |
| 14784 | case ConstCastResultIdFnReturnType: // TODO | 14834 | case ConstCastResultIdFnReturnType: // TODO |
| ... | @@ -15462,12 +15512,35 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, | ... | @@ -15462,12 +15512,35 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, |
| 15462 | } | 15512 | } |
| 15463 | 15513 | ||
| 15464 | // @Vector(N,T1) to @Vector(N,T2) | 15514 | // @Vector(N,T1) to @Vector(N,T2) |
| 15465 | if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector) { | 15515 | if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector && |
| 15466 | if (actual_type->data.vector.len == wanted_type->data.vector.len && | 15516 | actual_type->data.vector.len == wanted_type->data.vector.len) |
| 15467 | types_match_const_cast_only(ira, wanted_type->data.vector.elem_type, | 15517 | { |
| 15468 | actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) | 15518 | ZigType *scalar_actual_type = actual_type->data.vector.elem_type; |
| 15519 | ZigType *scalar_wanted_type = wanted_type->data.vector.elem_type; | ||
| 15520 | |||
| 15521 | // widening conversion | ||
| 15522 | if (scalar_wanted_type->id == ZigTypeIdInt && | ||
| 15523 | scalar_actual_type->id == ZigTypeIdInt && | ||
| 15524 | scalar_wanted_type->data.integral.is_signed == scalar_actual_type->data.integral.is_signed && | ||
| 15525 | scalar_wanted_type->data.integral.bit_count >= scalar_actual_type->data.integral.bit_count) | ||
| 15526 | { | ||
| 15527 | return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); | ||
| 15528 | } | ||
| 15529 | |||
| 15530 | // small enough unsigned ints can get casted to large enough signed ints | ||
| 15531 | if (scalar_wanted_type->id == ZigTypeIdInt && scalar_wanted_type->data.integral.is_signed && | ||
| 15532 | scalar_actual_type->id == ZigTypeIdInt && !scalar_actual_type->data.integral.is_signed && | ||
| 15533 | scalar_wanted_type->data.integral.bit_count > scalar_actual_type->data.integral.bit_count) | ||
| 15534 | { | ||
| 15535 | return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); | ||
| 15536 | } | ||
| 15537 | |||
| 15538 | // float widening conversion | ||
| 15539 | if (scalar_wanted_type->id == ZigTypeIdFloat && | ||
| 15540 | scalar_actual_type->id == ZigTypeIdFloat && | ||
| 15541 | scalar_wanted_type->data.floating.bit_count >= scalar_actual_type->data.floating.bit_count) | ||
| 15469 | { | 15542 | { |
| 15470 | return ir_analyze_bit_cast(ira, source_instr, value, wanted_type); | 15543 | return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); |
| 15471 | } | 15544 | } |
| 15472 | } | 15545 | } |
| 15473 | 15546 | ||
| ... | @@ -17728,6 +17801,33 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { | ... | @@ -17728,6 +17801,33 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { |
| 17728 | zig_unreachable(); | 17801 | zig_unreachable(); |
| 17729 | } | 17802 | } |
| 17730 | 17803 | ||
| 17804 | // Returns true if integer `value` can be converted to `type_entry` without | ||
| 17805 | // losing data. | ||
| 17806 | // If `value` is a vector the function returns true if this is valid for every | ||
| 17807 | // element. | ||
| 17808 | static bool value_numeric_fits_in_type(ZigValue *value, ZigType *type_entry) { | ||
| 17809 | assert(value->special == ConstValSpecialStatic); | ||
| 17810 | assert(type_entry->id == ZigTypeIdInt); | ||
| 17811 | |||
| 17812 | switch (value->type->id) { | ||
| 17813 | case ZigTypeIdComptimeInt: | ||
| 17814 | case ZigTypeIdInt: { | ||
| 17815 | return bigint_fits_in_bits(&value->data.x_bigint, type_entry->data.integral.bit_count, | ||
| 17816 | type_entry->data.integral.is_signed); | ||
| 17817 | } | ||
| 17818 | case ZigTypeIdVector: { | ||
| 17819 | for (size_t i = 0; i < value->type->data.vector.len; i++) { | ||
| 17820 | ZigValue *scalar_value = &value->data.x_array.data.s_none.elements[i]; | ||
| 17821 | const bool result = bigint_fits_in_bits(&scalar_value->data.x_bigint, | ||
| 17822 | type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); | ||
| 17823 | if (!result) return false; | ||
| 17824 | } | ||
| 17825 | return true; | ||
| 17826 | } | ||
| 17827 | default: zig_unreachable(); | ||
| 17828 | } | ||
| 17829 | } | ||
| 17830 | |||
| 17731 | static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) { | 17831 | static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) { |
| 17732 | assert(left->special == ConstValSpecialStatic); | 17832 | assert(left->special == ConstValSpecialStatic); |
| 17733 | assert(right == nullptr || right->special == ConstValSpecialStatic); | 17833 | assert(right == nullptr || right->special == ConstValSpecialStatic); |
| ... | @@ -27154,8 +27254,12 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa | ... | @@ -27154,8 +27254,12 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa |
| 27154 | if (type_is_invalid(dest_type)) | 27254 | if (type_is_invalid(dest_type)) |
| 27155 | return ira->codegen->invalid_inst_gen; | 27255 | return ira->codegen->invalid_inst_gen; |
| 27156 | 27256 | ||
| 27157 | if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) { | 27257 | ZigType *scalar_dest_type = (dest_type->id == ZigTypeIdVector) ? |
| 27158 | ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); | 27258 | dest_type->data.vector.elem_type : dest_type; |
| 27259 | |||
| 27260 | if (scalar_dest_type->id != ZigTypeIdInt && scalar_dest_type->id != ZigTypeIdComptimeInt) { | ||
| 27261 | ir_add_error(ira, &instruction->dest_type->base, | ||
| 27262 | buf_sprintf("expected integer type, found '%s'", buf_ptr(&scalar_dest_type->name))); | ||
| 27159 | return ira->codegen->invalid_inst_gen; | 27263 | return ira->codegen->invalid_inst_gen; |
| 27160 | } | 27264 | } |
| 27161 | 27265 | ||
| ... | @@ -27163,13 +27267,16 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa | ... | @@ -27163,13 +27267,16 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa |
| 27163 | if (type_is_invalid(target->value->type)) | 27267 | if (type_is_invalid(target->value->type)) |
| 27164 | return ira->codegen->invalid_inst_gen; | 27268 | return ira->codegen->invalid_inst_gen; |
| 27165 | 27269 | ||
| 27166 | if (target->value->type->id != ZigTypeIdInt && target->value->type->id != ZigTypeIdComptimeInt) { | 27270 | ZigType *scalar_target_type = (target->value->type->id == ZigTypeIdVector) ? |
| 27271 | target->value->type->data.vector.elem_type : target->value->type; | ||
| 27272 | |||
| 27273 | if (scalar_target_type->id != ZigTypeIdInt && scalar_target_type->id != ZigTypeIdComptimeInt) { | ||
| 27167 | ir_add_error(ira, &instruction->target->base, buf_sprintf("expected integer type, found '%s'", | 27274 | ir_add_error(ira, &instruction->target->base, buf_sprintf("expected integer type, found '%s'", |
| 27168 | buf_ptr(&target->value->type->name))); | 27275 | buf_ptr(&scalar_target_type->name))); |
| 27169 | return ira->codegen->invalid_inst_gen; | 27276 | return ira->codegen->invalid_inst_gen; |
| 27170 | } | 27277 | } |
| 27171 | 27278 | ||
| 27172 | if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeInt) { | 27279 | if (scalar_dest_type->id == ZigTypeIdComptimeInt) { |
| 27173 | ZigValue *val = ir_resolve_const(ira, target, UndefBad); | 27280 | ZigValue *val = ir_resolve_const(ira, target, UndefBad); |
| 27174 | if (val == nullptr) | 27281 | if (val == nullptr) |
| 27175 | return ira->codegen->invalid_inst_gen; | 27282 | return ira->codegen->invalid_inst_gen; |
| ... | @@ -27222,6 +27329,7 @@ static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFlo | ... | @@ -27222,6 +27329,7 @@ static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFlo |
| 27222 | if (val == nullptr) | 27329 | if (val == nullptr) |
| 27223 | return ira->codegen->invalid_inst_gen; | 27330 | return ira->codegen->invalid_inst_gen; |
| 27224 | 27331 | ||
| 27332 | // XXX: This will trigger an assertion failure if dest_type is comptime_float | ||
| 27225 | return ir_analyze_widen_or_shorten(ira, &instruction->target->base, target, dest_type); | 27333 | return ir_analyze_widen_or_shorten(ira, &instruction->target->base, target, dest_type); |
| 27226 | } | 27334 | } |
| 27227 | 27335 |
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 | }); |
| 2961 | 2960 | ||
| ... | @@ -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 | } |
| 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 | 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"); |
| 2 | const expect = std.testing.expect; | 2 | const expect = std.testing.expect; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
| 5 | const Vector = std.meta.Vector; | ||
| 5 | 6 | ||
| 6 | test "int to ptr cast" { | 7 | test "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 | } |
| 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 | test "comptime_int @intToFloat" { | 405 | test "comptime_int @intToFloat" { |
| 368 | { | 406 | { |
| 369 | const result = @intToFloat(f16, 1234); | 407 | const result = @intToFloat(f16, 1234); |