`@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
master
Andrew Kelley 2019-02-09 21:10:59 -05:00
parent 31be1ddf09
commit caf672c495
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
4 changed files with 56 additions and 13 deletions

View File

@ -6381,14 +6381,14 @@ fn List(comptime T: type) type {
{#header_close#}
{#header_open|@truncate#}
<pre>{#syntax#}@truncate(comptime T: type, integer) T{#endsyntax#}</pre>
<pre>{#syntax#}@truncate(comptime T: type, integer: var) T{#endsyntax#}</pre>
<p>
This function truncates bits from an integer type, resulting in a smaller
integer type.
</p>
<p>
The following produces a crash in debug mode and undefined behavior in
release mode:
The following produces a crash in {#link|Debug#} mode and {#link|Undefined Behavior#} in
{#link|ReleaseFast#} mode:
</p>
<pre>{#syntax#}const a: u16 = 0xabcd;
const b: u8 = u8(a);{#endsyntax#}</pre>
@ -6402,7 +6402,10 @@ const b: u8 = @truncate(u8, a);
This function always truncates the significant bits of the integer, regardless
of endianness on the target platform.
</p>
<p>
If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#},
then this is semantically equivalent to an {#link|implicit cast|Implicit Casts#}.
</p>
{#header_close#}
{#header_open|@typeId#}

View File

@ -18491,7 +18491,22 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct
return ira->codegen->invalid_instruction;
}
if (src_type->data.integral.bit_count == 0) {
if (dest_type->id == ZigTypeIdComptimeInt) {
return ir_implicit_cast(ira, target, dest_type);
}
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (val == nullptr)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
bigint_truncate(&result->value.data.x_bigint, &val->data.x_bigint,
dest_type->data.integral.bit_count, dest_type->data.integral.is_signed);
return result;
}
if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) {
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
bigint_init_unsigned(&result->value.data.x_bigint, 0);
return result;
@ -18507,13 +18522,6 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct
return ira->codegen->invalid_instruction;
}
if (target->value.special == ConstValSpecialStatic) {
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
bigint_truncate(&result->value.data.x_bigint, &target->value.data.x_bigint,
dest_type->data.integral.bit_count, dest_type->data.integral.is_signed);
return result;
}
IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, dest_type_value, target);
new_instruction->value.type = dest_type;

View File

@ -1,6 +1,15 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.addTest(
"@truncate undefined value",
\\export fn entry() void {
\\ var z = @truncate(u8, u16(undefined));
\\}
,
".tmp_source.zig:2:30: error: use of undefined value",
);
cases.addTest(
"return invalid type from test",
\\test "example" { return 1; }
@ -3335,7 +3344,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.add(
"truncate sign mismatch",
\\fn f() i8 {
\\ const x: u32 = 10;
\\ var x: u32 = 10;
\\ return @truncate(i8, x);
\\}
\\

View File

@ -6,3 +6,26 @@ test "truncate u0 to larger integer allowed and has comptime known result" {
const y = @truncate(u8, x);
comptime expect(y == 0);
}
test "truncate.u0.literal" {
var z = @truncate(u0, 0);
expect(z == 0);
}
test "truncate.u0.const" {
const c0: usize = 0;
var z = @truncate(u0, c0);
expect(z == 0);
}
test "truncate.u0.var" {
var d: u8 = 2;
var z = @truncate(u0, d);
expect(z == 0);
}
test "truncate sign mismatch but comptime known so it works anyway" {
const x: u32 = 10;
var result = @truncate(i8, x);
expect(result == 10);
}