diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 19b683b177..0376d5a11d 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -3358,6 +3358,14 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { return self.bitcast(scope, dest_type, inst); } + // undefined to anything + if (inst.value()) |val| { + if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) { + return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); + } + } + assert(inst.ty.zigTypeTag() != .Undefined); + // *[N]T to []T if (inst.ty.isSinglePointer() and dest_type.isSlice() and (!inst.ty.pointerIsConst() or dest_type.pointerIsConst())) @@ -3371,34 +3379,58 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { } } - // comptime_int to fixed-width integer - if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) { - // The representation is already correct; we only need to make sure it fits in the destination type. - const val = inst.value().?; // comptime_int always has comptime known value - if (!val.intFitsInType(dest_type, self.target())) { - return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); + // comptime known number to other number + if (inst.value()) |val| { + const src_zig_tag = inst.ty.zigTypeTag(); + const dst_zig_tag = dest_type.zigTypeTag(); + + if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { + if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { + if (val.floatHasFraction()) { + return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty }); + } + return self.fail(scope, inst.src, "TODO float to int", .{}); + } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { + if (!val.intFitsInType(dest_type, self.target())) { + return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); + } + return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); + } + } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { + if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { + return self.fail(scope, inst.src, "TODO float cast", .{}); + } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { + return self.fail(scope, inst.src, "TODO int to float", .{}); + } } - return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); } // integer widening if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { + assert(inst.value() == null); // handled above + const src_info = inst.ty.intInfo(self.target()); const dst_info = dest_type.intInfo(self.target()); if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) { - if (inst.value()) |val| { - return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); - } else { - return self.fail(scope, inst.src, "TODO implement runtime integer widening ({} to {})", .{ - inst.ty, - dest_type, - }); - } + const b = try self.requireRuntimeBlock(scope, inst.src); + return self.addNewInstArgs(b, inst.src, dest_type, Inst.WidenOrShorten, .{ .operand = inst }); } else { return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type }); } } + // float widening + if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) { + assert(inst.value() == null); // handled above + + const src_bits = inst.ty.floatBits(self.target()); + const dst_bits = dest_type.floatBits(self.target()); + if (dst_bits >= src_bits) { + const b = try self.requireRuntimeBlock(scope, inst.src); + return self.addNewInstArgs(b, inst.src, dest_type, Inst.WidenOrShorten, .{ .operand = inst }); + } + } + return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type }); } diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index d64c1824cf..7db3bcb4aa 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -459,6 +459,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .sub => return self.genSub(inst.castTag(.sub).?), .unreach => return MCValue{ .unreach = {} }, .not => return self.genNot(inst.castTag(.not).?), + .widenorshorten => return self.genWidenOrShorten(isnt.castTag(.widenorshorten).?), + } + } + + fn genWidenOrShorten(self: *Self, inst: *ir.Inst.WidenOrShorten) !MCValue { + // No side effects, so if it's unreferenced, do nothing. + if (inst.base.isUnused()) + return MCValue.dead; + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement widen or shorten for {}", .{self.target.cpu.arch}), } } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 53a73dbf6c..4d82920e52 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -71,6 +71,7 @@ pub const Inst = struct { sub, unreach, not, + widenorshorten, /// There is one-to-one correspondence between tag and type for now, /// but this will not always be the case. For example, binary operations @@ -108,6 +109,7 @@ pub const Inst = struct { .call => Call, .condbr => CondBr, .constant => Constant, + .widenorshorten => WidenOrShorten, }; } @@ -374,6 +376,15 @@ pub const Inst = struct { return null; } }; + + pub const WidenOrShorten = struct { + pub const base_tag = Tag.widenorshorten; + + base: Inst, + args: struct { + operand: *Inst, + }, + }; }; pub const Body = struct { diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 1afa37ec28..3debb1efe4 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -558,6 +558,7 @@ pub const Inst = struct { pub const IntCast = struct { pub const base_tag = Tag.intcast; + pub const builtin_name = "@intCast"; base: Inst, positionals: struct { @@ -569,6 +570,7 @@ pub const Inst = struct { pub const BitCast = struct { pub const base_tag = Tag.bitcast; + pub const builtin_name = "@bitCast"; base: Inst, positionals: struct { @@ -1820,6 +1822,10 @@ const EmitZIR = struct { }; break :blk &new_inst.base; }, + .widenorshorten => blk: { + const old_inst = inst.cast(ir.Inst.WidenOrShorten).?; + break :blk try self.resolveInst(new_body, old_inst.args.operand); + }, }; try instructions.append(new_inst); try inst_table.put(inst, new_inst);