`@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_close#}
{#header_open|@truncate#} {#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> <p>
This function truncates bits from an integer type, resulting in a smaller This function truncates bits from an integer type, resulting in a smaller
integer type. integer type.
</p> </p>
<p> <p>
The following produces a crash in debug mode and undefined behavior in The following produces a crash in {#link|Debug#} mode and {#link|Undefined Behavior#} in
release mode: {#link|ReleaseFast#} mode:
</p> </p>
<pre>{#syntax#}const a: u16 = 0xabcd; <pre>{#syntax#}const a: u16 = 0xabcd;
const b: u8 = u8(a);{#endsyntax#}</pre> 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 This function always truncates the significant bits of the integer, regardless
of endianness on the target platform. of endianness on the target platform.
</p> </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_close#}
{#header_open|@typeId#} {#header_open|@typeId#}

View File

@ -18491,7 +18491,22 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct
return ira->codegen->invalid_instruction; 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); IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
bigint_init_unsigned(&result->value.data.x_bigint, 0); bigint_init_unsigned(&result->value.data.x_bigint, 0);
return result; return result;
@ -18507,13 +18522,6 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct
return ira->codegen->invalid_instruction; 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, IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, dest_type_value, target); instruction->base.source_node, dest_type_value, target);
new_instruction->value.type = dest_type; new_instruction->value.type = dest_type;

View File

@ -1,6 +1,15 @@
const tests = @import("tests.zig"); const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void { 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( cases.addTest(
"return invalid type from test", "return invalid type from test",
\\test "example" { return 1; } \\test "example" { return 1; }
@ -3335,7 +3344,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.add( cases.add(
"truncate sign mismatch", "truncate sign mismatch",
\\fn f() i8 { \\fn f() i8 {
\\ const x: u32 = 10; \\ var x: u32 = 10;
\\ return @truncate(i8, x); \\ 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); const y = @truncate(u8, x);
comptime expect(y == 0); 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);
}