Commit caf672c495

Andrew Kelley <andrew@ziglang.org>
2019-02-10 03:10:59
`@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
Changed files (4)
doc/langref.html.in
@@ -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#}
src/ir.cpp
@@ -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;
test/stage1/behavior/truncate.zig
@@ -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);
+}
test/compile_errors.zig
@@ -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);
         \\}
         \\