From 0e2b9ac7770df07212d4d1cbfb15c3aaed0bef18 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Sep 2021 17:24:55 -0700 Subject: [PATCH] stage2: fix unsigned integer to signed integer coercion --- src/Sema.zig | 2 +- src/codegen/llvm.zig | 18 +++++++++++++----- test/behavior/widening.zig | 6 ++++++ test/behavior/widening_stage1.zig | 6 ------ 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index f6bea69129..91d12b7b31 100644 --- a/src/Sema.zig +++ b/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); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 29efa27685..3a977bc582 100644 --- a/src/codegen/llvm.zig +++ b/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 { diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig index daa592e64c..efcbab9883 100644 --- a/test/behavior/widening.zig +++ b/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); +} diff --git a/test/behavior/widening_stage1.zig b/test/behavior/widening_stage1.zig index 5b5bc67e45..0cec3988cb 100644 --- a/test/behavior/widening_stage1.zig +++ b/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;