Commit 0e2b9ac777

Andrew Kelley <andrew@ziglang.org>
2021-09-22 02:24:55
stage2: fix unsigned integer to signed integer coercion
1 parent be71195
Changed files (4)
src/codegen/llvm.zig
@@ -2032,14 +2032,22 @@ pub const FuncGen = struct {
         if (self.liveness.isUnused(inst))
             return null;
 
+        const target = self.dg.module.getTarget();
         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+        const dest_ty = self.air.typeOfIndex(inst);
+        const dest_info = dest_ty.intInfo(target);
+        const dest_llvm_ty = try self.dg.llvmType(dest_ty);
         const operand = try self.resolveInst(ty_op.operand);
-        const inst_ty = self.air.typeOfIndex(inst);
+        const operand_ty = self.air.typeOf(ty_op.operand);
+        const operand_info = operand_ty.intInfo(target);
 
-        const signed = inst_ty.isSignedInt();
-        // TODO: Should we use intcast here or just a simple bitcast?
-        //       LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes
-        return self.builder.buildIntCast2(operand, try self.dg.llvmType(inst_ty), llvm.Bool.fromBool(signed), "");
+        if (operand_info.bits < dest_info.bits) {
+            switch (operand_info.signedness) {
+                .signed => return self.builder.buildSExt(operand, dest_llvm_ty, ""),
+                .unsigned => return self.builder.buildZExt(operand, dest_llvm_ty, ""),
+            }
+        }
+        return self.builder.buildTrunc(operand, dest_llvm_ty, "");
     }
 
     fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
src/Sema.zig
@@ -9565,7 +9565,7 @@ fn coerce(
                 const src_info = inst_ty.intInfo(target);
                 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
                     // small enough unsigned ints can get casted to large enough signed ints
-                    (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))
+                    (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
                 {
                     try sema.requireRuntimeBlock(block, inst_src);
                     return block.addTyOp(.intcast, dest_type, inst);
test/behavior/widening.zig
@@ -11,3 +11,9 @@ test "integer widening" {
     var f: u128 = e;
     try expect(f == a);
 }
+
+test "implicit unsigned integer to signed integer" {
+    var a: u8 = 250;
+    var b: i16 = a;
+    try expect(b == 250);
+}
test/behavior/widening_stage1.zig
@@ -2,12 +2,6 @@ const std = @import("std");
 const expect = std.testing.expect;
 const mem = std.mem;
 
-test "implicit unsigned integer to signed integer" {
-    var a: u8 = 250;
-    var b: i16 = a;
-    try expect(b == 250);
-}
-
 test "float widening" {
     var a: f16 = 12.34;
     var b: f32 = a;