| author | |
| committer | |
| log | caf672c49586f1af5e3d41ae200aded991b8b0f7 |
| tree | 76b8e9dd955342c6a7ccda3ca62eade39edb4c5f |
| parent | 31be1ddf09fafae270d9946a3f09aa25816dd153 |
| signature | Commit is signed but in an unrecognized format. |
also if the dest type is a comptime_int, then treat it
as an implicit cast.
also compile error for attempting to truncate undefined
closes #15684 files changed, 56 insertions(+), 13 deletions(-)
doc/langref.html.in+7-4| ... | @@ -6381,14 +6381,14 @@ fn List(comptime T: type) type { | ... | @@ -6381,14 +6381,14 @@ fn List(comptime T: type) type { |
| 6381 | {#header_close#} | 6381 | {#header_close#} |
| 6382 | 6382 | ||
| 6383 | {#header_open|@truncate#} | 6383 | {#header_open|@truncate#} |
| 6384 | <pre>{#syntax#}@truncate(comptime T: type, integer) T{#endsyntax#}</pre> | 6384 | <pre>{#syntax#}@truncate(comptime T: type, integer: var) T{#endsyntax#}</pre> |
| 6385 | <p> | 6385 | <p> |
| 6386 | This function truncates bits from an integer type, resulting in a smaller | 6386 | This function truncates bits from an integer type, resulting in a smaller |
| 6387 | integer type. | 6387 | integer type. |
| 6388 | </p> | 6388 | </p> |
| 6389 | <p> | 6389 | <p> |
| 6390 | The following produces a crash in debug mode and undefined behavior in | 6390 | The following produces a crash in {#link|Debug#} mode and {#link|Undefined Behavior#} in |
| 6391 | release mode: | 6391 | {#link|ReleaseFast#} mode: |
| 6392 | </p> | 6392 | </p> |
| 6393 | <pre>{#syntax#}const a: u16 = 0xabcd; | 6393 | <pre>{#syntax#}const a: u16 = 0xabcd; |
| 6394 | const b: u8 = u8(a);{#endsyntax#}</pre> | 6394 | const b: u8 = u8(a);{#endsyntax#}</pre> |
| ... | @@ -6402,7 +6402,10 @@ const b: u8 = @truncate(u8, a); | ... | @@ -6402,7 +6402,10 @@ const b: u8 = @truncate(u8, a); |
| 6402 | This function always truncates the significant bits of the integer, regardless | 6402 | This function always truncates the significant bits of the integer, regardless |
| 6403 | of endianness on the target platform. | 6403 | of endianness on the target platform. |
| 6404 | </p> | 6404 | </p> |
| 6405 | 6405 | <p> | |
| 6406 | If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#}, | ||
| 6407 | then this is semantically equivalent to an {#link|implicit cast|Implicit Casts#}. | ||
| 6408 | </p> | ||
| 6406 | {#header_close#} | 6409 | {#header_close#} |
| 6407 | 6410 | ||
| 6408 | {#header_open|@typeId#} | 6411 | {#header_open|@typeId#} |
src/ir.cpp+16-8| ... | @@ -18491,7 +18491,22 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct | ... | @@ -18491,7 +18491,22 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct |
| 18491 | return ira->codegen->invalid_instruction; | 18491 | return ira->codegen->invalid_instruction; |
| 18492 | } | 18492 | } |
| 18493 | 18493 | ||
| 18494 | if (src_type->data.integral.bit_count == 0) { | 18494 | if (dest_type->id == ZigTypeIdComptimeInt) { |
| 18495 | return ir_implicit_cast(ira, target, dest_type); | ||
| 18496 | } | ||
| 18497 | |||
| 18498 | if (instr_is_comptime(target)) { | ||
| 18499 | ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); | ||
| 18500 | if (val == nullptr) | ||
| 18501 | return ira->codegen->invalid_instruction; | ||
| 18502 | |||
| 18503 | IrInstruction *result = ir_const(ira, &instruction->base, dest_type); | ||
| 18504 | bigint_truncate(&result->value.data.x_bigint, &val->data.x_bigint, | ||
| 18505 | dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); | ||
| 18506 | return result; | ||
| 18507 | } | ||
| 18508 | |||
| 18509 | if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) { | ||
| 18495 | IrInstruction *result = ir_const(ira, &instruction->base, dest_type); | 18510 | IrInstruction *result = ir_const(ira, &instruction->base, dest_type); |
| 18496 | bigint_init_unsigned(&result->value.data.x_bigint, 0); | 18511 | bigint_init_unsigned(&result->value.data.x_bigint, 0); |
| 18497 | return result; | 18512 | return result; |
| ... | @@ -18507,13 +18522,6 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct | ... | @@ -18507,13 +18522,6 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct |
| 18507 | return ira->codegen->invalid_instruction; | 18522 | return ira->codegen->invalid_instruction; |
| 18508 | } | 18523 | } |
| 18509 | 18524 | ||
| 18510 | if (target->value.special == ConstValSpecialStatic) { | ||
| 18511 | IrInstruction *result = ir_const(ira, &instruction->base, dest_type); | ||
| 18512 | bigint_truncate(&result->value.data.x_bigint, &target->value.data.x_bigint, | ||
| 18513 | dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); | ||
| 18514 | return result; | ||
| 18515 | } | ||
| 18516 | |||
| 18517 | IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope, | 18525 | IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope, |
| 18518 | instruction->base.source_node, dest_type_value, target); | 18526 | instruction->base.source_node, dest_type_value, target); |
| 18519 | new_instruction->value.type = dest_type; | 18527 | new_instruction->value.type = dest_type; |
test/compile_errors.zig+10-1| ... | @@ -1,6 +1,15 @@ | ... | @@ -1,6 +1,15 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4 | cases.addTest( | ||
| 5 | "@truncate undefined value", | ||
| 6 | \\export fn entry() void { | ||
| 7 | \\ var z = @truncate(u8, u16(undefined)); | ||
| 8 | \\} | ||
| 9 | , | ||
| 10 | ".tmp_source.zig:2:30: error: use of undefined value", | ||
| 11 | ); | ||
| 12 | |||
| 4 | cases.addTest( | 13 | cases.addTest( |
| 5 | "return invalid type from test", | 14 | "return invalid type from test", |
| 6 | \\test "example" { return 1; } | 15 | \\test "example" { return 1; } |
| ... | @@ -3335,7 +3344,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -3335,7 +3344,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3335 | cases.add( | 3344 | cases.add( |
| 3336 | "truncate sign mismatch", | 3345 | "truncate sign mismatch", |
| 3337 | \\fn f() i8 { | 3346 | \\fn f() i8 { |
| 3338 | \\ const x: u32 = 10; | 3347 | \\ var x: u32 = 10; |
| 3339 | \\ return @truncate(i8, x); | 3348 | \\ return @truncate(i8, x); |
| 3340 | \\} | 3349 | \\} |
| 3341 | \\ | 3350 | \\ |
test/stage1/behavior/truncate.zig+23| ... | @@ -6,3 +6,26 @@ test "truncate u0 to larger integer allowed and has comptime known result" { | ... | @@ -6,3 +6,26 @@ test "truncate u0 to larger integer allowed and has comptime known result" { |
| 6 | const y = @truncate(u8, x); | 6 | const y = @truncate(u8, x); |
| 7 | comptime expect(y == 0); | 7 | comptime expect(y == 0); |
| 8 | } | 8 | } |
| 9 | |||
| 10 | test "truncate.u0.literal" { | ||
| 11 | var z = @truncate(u0, 0); | ||
| 12 | expect(z == 0); | ||
| 13 | } | ||
| 14 | |||
| 15 | test "truncate.u0.const" { | ||
| 16 | const c0: usize = 0; | ||
| 17 | var z = @truncate(u0, c0); | ||
| 18 | expect(z == 0); | ||
| 19 | } | ||
| 20 | |||
| 21 | test "truncate.u0.var" { | ||
| 22 | var d: u8 = 2; | ||
| 23 | var z = @truncate(u0, d); | ||
| 24 | expect(z == 0); | ||
| 25 | } | ||
| 26 | |||
| 27 | test "truncate sign mismatch but comptime known so it works anyway" { | ||
| 28 | const x: u32 = 10; | ||
| 29 | var result = @truncate(i8, x); | ||
| 30 | expect(result == 10); | ||
| 31 | } |