Skip to content

Commit caf672c

Browse files
committed
@truncate: comptime 0 when target type is 0 bits
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 #1568
1 parent 31be1dd commit caf672c

File tree

4 files changed

+56
-13
lines changed

4 files changed

+56
-13
lines changed

doc/langref.html.in

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6381,14 +6381,14 @@ fn List(comptime T: type) type {
63816381
{#header_close#}
63826382

63836383
{#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>
63856385
<p>
63866386
This function truncates bits from an integer type, resulting in a smaller
63876387
integer type.
63886388
</p>
63896389
<p>
6390-
The following produces a crash in debug mode and undefined behavior in
6391-
release mode:
6390+
The following produces a crash in {#link|Debug#} mode and {#link|Undefined Behavior#} in
6391+
{#link|ReleaseFast#} mode:
63926392
</p>
63936393
<pre>{#syntax#}const a: u16 = 0xabcd;
63946394
const b: u8 = u8(a);{#endsyntax#}</pre>
@@ -6402,7 +6402,10 @@ const b: u8 = @truncate(u8, a);
64026402
This function always truncates the significant bits of the integer, regardless
64036403
of endianness on the target platform.
64046404
</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>
64066409
{#header_close#}
64076410

64086411
{#header_open|@typeId#}

src/ir.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18491,7 +18491,22 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct
1849118491
return ira->codegen->invalid_instruction;
1849218492
}
1849318493

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) {
1849518510
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
1849618511
bigint_init_unsigned(&result->value.data.x_bigint, 0);
1849718512
return result;
@@ -18507,13 +18522,6 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct
1850718522
return ira->codegen->invalid_instruction;
1850818523
}
1850918524

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-
1851718525
IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope,
1851818526
instruction->base.source_node, dest_type_value, target);
1851918527
new_instruction->value.type = dest_type;

test/compile_errors.zig

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
const tests = @import("tests.zig");
22

33
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+
413
cases.addTest(
514
"return invalid type from test",
615
\\test "example" { return 1; }
@@ -3335,7 +3344,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
33353344
cases.add(
33363345
"truncate sign mismatch",
33373346
\\fn f() i8 {
3338-
\\ const x: u32 = 10;
3347+
\\ var x: u32 = 10;
33393348
\\ return @truncate(i8, x);
33403349
\\}
33413350
\\

test/stage1/behavior/truncate.zig

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,26 @@ test "truncate u0 to larger integer allowed and has comptime known result" {
66
const y = @truncate(u8, x);
77
comptime expect(y == 0);
88
}
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+
}

0 commit comments

Comments
 (0)