Commit e2ff486de5

Jacob Young <jacobly0@users.noreply.github.com>
2023-08-27 23:14:31
Sema: cleanup `coerceExtra`
* remove unreachable code * remove already handled cases * avoid `InternPool.getCoerced` * add some undef checks * error when converting undef int to float Closes #16987
1 parent 49075d2
Changed files (5)
src/Module.zig
@@ -6360,7 +6360,7 @@ pub fn errorSetFromUnsortedNames(
 
 /// Supports only pointers, not pointer-like optionals.
 pub fn ptrIntValue(mod: *Module, ty: Type, x: u64) Allocator.Error!Value {
-    assert(ty.zigTypeTag(mod) == .Pointer);
+    assert(ty.zigTypeTag(mod) == .Pointer and !ty.isSlice(mod));
     const i = try intern(mod, .{ .ptr = .{
         .ty = ty.toIntern(),
         .addr = .{ .int = (try mod.intValue_u64(Type.usize, x)).toIntern() },
src/Sema.zig
@@ -17282,12 +17282,13 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             for (enum_field_vals, 0..) |*field_val, i| {
                 const enum_type = ip.indexToKey(ty.toIntern()).enum_type;
                 const value_val = if (enum_type.values.len > 0)
-                    try mod.intern_pool.getCoerced(gpa, enum_type.values.get(ip)[i], .comptime_int_type)
+                    try mod.intern_pool.getCoercedInts(
+                        mod.gpa,
+                        mod.intern_pool.indexToKey(enum_type.values.get(ip)[i]).int,
+                        .comptime_int_type,
+                    )
                 else
-                    try mod.intern(.{ .int = .{
-                        .ty = .comptime_int_type,
-                        .storage = .{ .u64 = @intCast(i) },
-                    } });
+                    (try mod.intValue(Type.comptime_int, i)).toIntern();
                 // TODO: write something like getCoercedInts to avoid needing to dupe
                 const name = try sema.arena.dupe(u8, ip.stringToSlice(enum_type.names.get(ip)[i]));
                 const name_val = v: {
@@ -21259,7 +21260,7 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
     try sema.checkFloatType(block, operand_src, operand_scalar_ty);
 
     if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
-        const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty);
+        const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, .truncate);
         return Air.internedToRef(result_val.toIntern());
     } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
         return sema.failWithNeededComptime(block, operand_src, .{
@@ -27575,21 +27576,21 @@ fn coerceExtra(
         return block.addBitCast(dest_ty, inst);
     }
 
-    const is_undef = inst_ty.zigTypeTag(mod) == .Undefined;
-
     switch (dest_ty.zigTypeTag(mod)) {
         .Optional => optional: {
-            // undefined sets the optional bit also to undefined.
-            if (is_undef) {
-                return mod.undefRef(dest_ty);
-            }
+            if (maybe_inst_val) |val| {
+                // undefined sets the optional bit also to undefined.
+                if (val.toIntern() == .undef) {
+                    return mod.undefRef(dest_ty);
+                }
 
-            // null to ?T
-            if (inst_ty.zigTypeTag(mod) == .Null) {
-                return Air.internedToRef((try mod.intern(.{ .opt = .{
-                    .ty = dest_ty.toIntern(),
-                    .val = .none,
-                } })));
+                // null to ?T
+                if (val.toIntern() == .null_value) {
+                    return Air.internedToRef((try mod.intern(.{ .opt = .{
+                        .ty = dest_ty.toIntern(),
+                        .val = .none,
+                    } })));
+                }
             }
 
             // cast from ?*T and ?[*]T to ?*anyopaque
@@ -27681,7 +27682,9 @@ fn coerceExtra(
 
                 if (dest_info.sentinel != .none) {
                     if (array_ty.sentinel(mod)) |inst_sent| {
-                        if (dest_info.sentinel != (try mod.getCoerced(inst_sent, dst_elem_type)).toIntern()) {
+                        if (Air.internedToRef(dest_info.sentinel) !=
+                            try sema.coerceInMemory(inst_sent, dst_elem_type))
+                        {
                             in_memory_result = .{ .ptr_sentinel = .{
                                 .actual = inst_sent,
                                 .wanted = dest_info.sentinel.toValue(),
@@ -27758,9 +27761,10 @@ fn coerceExtra(
             switch (dest_info.flags.size) {
                 // coercion to C pointer
                 .C => switch (inst_ty.zigTypeTag(mod)) {
-                    .Null => {
-                        return Air.internedToRef((try mod.getCoerced(Value.null, dest_ty)).toIntern());
-                    },
+                    .Null => return Air.internedToRef(try mod.intern(.{ .ptr = .{
+                        .ty = dest_ty.toIntern(),
+                        .addr = .{ .int = .zero_usize },
+                    } })),
                     .ComptimeInt => {
                         const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
                             error.NotCoercible => break :pointer,
@@ -27865,10 +27869,19 @@ fn coerceExtra(
                         // we use a dummy pointer value with the required alignment.
                         return Air.internedToRef((try mod.intern(.{ .ptr = .{
                             .ty = dest_ty.toIntern(),
-                            .addr = .{ .int = (if (dest_info.flags.alignment != .none)
-                                try mod.intValue(Type.usize, dest_info.flags.alignment.toByteUnitsOptional().?)
+                            .addr = .{ .int = if (dest_info.flags.alignment != .none)
+                                (try mod.intValue(
+                                    Type.usize,
+                                    dest_info.flags.alignment.toByteUnitsOptional().?,
+                                )).toIntern()
                             else
-                                try mod.getCoerced(try dest_info.child.toType().lazyAbiAlignment(mod), Type.usize)).toIntern() },
+                                try mod.intern_pool.getCoercedInts(
+                                    mod.gpa,
+                                    mod.intern_pool.indexToKey(
+                                        (try dest_info.child.toType().lazyAbiAlignment(mod)).toIntern(),
+                                    ).int,
+                                    .usize_type,
+                                ) },
                             .len = (try mod.intValue(Type.usize, 0)).toIntern(),
                         } })));
                     }
@@ -27904,8 +27917,8 @@ fn coerceExtra(
                     }
 
                     if (dest_info.sentinel == .none or inst_info.sentinel == .none or
-                        dest_info.sentinel !=
-                        try mod.intern_pool.getCoerced(sema.gpa, inst_info.sentinel, dest_info.child))
+                        Air.internedToRef(dest_info.sentinel) !=
+                        try sema.coerceInMemory(inst_info.sentinel.toValue(), dest_info.child.toType()))
                         break :p;
 
                     const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
@@ -27915,10 +27928,7 @@ fn coerceExtra(
         },
         .Int, .ComptimeInt => switch (inst_ty.zigTypeTag(mod)) {
             .Float, .ComptimeFloat => float: {
-                if (is_undef) {
-                    return mod.undefRef(dest_ty);
-                }
-                const val = (try sema.resolveMaybeUndefVal(inst)) orelse {
+                const val = maybe_inst_val orelse {
                     if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
                         if (!opts.report_err) return error.NotCoercible;
                         return sema.failWithNeededComptime(block, inst_src, .{
@@ -27927,29 +27937,23 @@ fn coerceExtra(
                     }
                     break :float;
                 };
-
-                if (val.floatHasFraction(mod)) {
-                    return sema.fail(
-                        block,
-                        inst_src,
-                        "fractional component prevents float value '{}' from coercion to type '{}'",
-                        .{ val.fmtValue(inst_ty, mod), dest_ty.fmt(mod) },
-                    );
-                }
-                const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty);
+                const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty, .exact);
                 return Air.internedToRef(result_val.toIntern());
             },
             .Int, .ComptimeInt => {
-                if (is_undef) {
-                    return mod.undefRef(dest_ty);
-                }
-                if (try sema.resolveMaybeUndefVal(inst)) |val| {
+                if (maybe_inst_val) |val| {
                     // comptime-known integer to other number
                     if (!(try sema.intFitsInType(val, dest_ty, null))) {
                         if (!opts.report_err) return error.NotCoercible;
                         return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) });
                     }
-                    return Air.internedToRef((try mod.getCoerced(val, dest_ty)).toIntern());
+                    return switch (mod.intern_pool.indexToKey(val.toIntern())) {
+                        .undef => try mod.undefRef(dest_ty),
+                        .int => |int| Air.internedToRef(
+                            try mod.intern_pool.getCoercedInts(mod.gpa, int, dest_ty.toIntern()),
+                        ),
+                        else => unreachable,
+                    };
                 }
                 if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
                     if (!opts.report_err) return error.NotCoercible;
@@ -27970,9 +27974,6 @@ fn coerceExtra(
                     return block.addTyOp(.intcast, dest_ty, inst);
                 }
             },
-            .Undefined => {
-                return mod.undefRef(dest_ty);
-            },
             else => {},
         },
         .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) {
@@ -27982,10 +27983,7 @@ fn coerceExtra(
                 return Air.internedToRef(result_val.toIntern());
             },
             .Float => {
-                if (is_undef) {
-                    return mod.undefRef(dest_ty);
-                }
-                if (try sema.resolveMaybeUndefVal(inst)) |val| {
+                if (maybe_inst_val) |val| {
                     const result_val = try val.floatCast(dest_ty, mod);
                     if (!val.eql(try result_val.floatCast(inst_ty, mod), inst_ty, mod)) {
                         return sema.fail(
@@ -28012,10 +28010,7 @@ fn coerceExtra(
                 }
             },
             .Int, .ComptimeInt => int: {
-                if (is_undef) {
-                    return mod.undefRef(dest_ty);
-                }
-                const val = (try sema.resolveMaybeUndefVal(inst)) orelse {
+                const val = maybe_inst_val orelse {
                     if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {
                         if (!opts.report_err) return error.NotCoercible;
                         return sema.failWithNeededComptime(block, inst_src, .{
@@ -28037,9 +28032,6 @@ fn coerceExtra(
                 //}
                 return Air.internedToRef(result_val.toIntern());
             },
-            .Undefined => {
-                return mod.undefRef(dest_ty);
-            },
             else => {},
         },
         .Enum => switch (inst_ty.zigTypeTag(mod)) {
@@ -28070,9 +28062,6 @@ fn coerceExtra(
                     return sema.unionToTag(block, dest_ty, inst, inst_src);
                 }
             },
-            .Undefined => {
-                return mod.undefRef(dest_ty);
-            },
             else => {},
         },
         .ErrorUnion => switch (inst_ty.zigTypeTag(mod)) {
@@ -28107,9 +28096,6 @@ fn coerceExtra(
                 // E to E!T
                 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
             },
-            .Undefined => {
-                return mod.undefRef(dest_ty);
-            },
             else => eu: {
                 // T to E!T
                 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) {
@@ -28125,9 +28111,6 @@ fn coerceExtra(
                     return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src);
                 }
             },
-            .Undefined => {
-                return mod.undefRef(dest_ty);
-            },
             else => {},
         },
         .Array => switch (inst_ty.zigTypeTag(mod)) {
@@ -28140,9 +28123,6 @@ fn coerceExtra(
                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
                 }
             },
-            .Undefined => {
-                return mod.undefRef(dest_ty);
-            },
             else => {},
         },
         .Vector => switch (inst_ty.zigTypeTag(mod)) {
@@ -28152,9 +28132,6 @@ fn coerceExtra(
                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
                 }
             },
-            .Undefined => {
-                return mod.undefRef(dest_ty);
-            },
             else => {},
         },
         .Struct => blk: {
@@ -28174,9 +28151,7 @@ fn coerceExtra(
     // undefined to anything. We do this after the big switch above so that
     // special logic has a chance to run first, such as `*[N]T` to `[]T` which
     // should initialize the length field of the slice.
-    if (is_undef) {
-        return mod.undefRef(dest_ty);
-    }
+    if (maybe_inst_val) |val| if (val.toIntern() == .undef) return mod.undefRef(dest_ty);
 
     if (!opts.report_err) return error.NotCoercible;
 
@@ -34043,10 +34018,10 @@ fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value {
     switch (mod.intern_pool.indexToKey(val.toIntern())) {
         .int => |int| switch (int.storage) {
             .u64, .i64, .big_int => return val,
-            .lazy_align, .lazy_size => return (try mod.intern(.{ .int = .{
-                .ty = int.ty,
-                .storage = .{ .u64 = (try val.getUnsignedIntAdvanced(mod, sema)).? },
-            } })).toValue(),
+            .lazy_align, .lazy_size => return mod.intValue(
+                int.ty.toType(),
+                (try val.getUnsignedIntAdvanced(mod, sema)).?,
+            ),
         },
         .ptr => |ptr| {
             const resolved_len = switch (ptr.len) {
@@ -36217,20 +36192,21 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
                     .auto, .explicit => {
                         if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null;
 
-                        switch (enum_type.names.len) {
-                            0 => {
-                                const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
-                                return only.toValue();
-                            },
-                            1 => return try mod.getCoerced((if (enum_type.values.len == 0)
-                                try mod.intern(.{ .int = .{
-                                    .ty = enum_type.tag_ty,
-                                    .storage = .{ .u64 = 0 },
-                                } })
-                            else
-                                enum_type.values.get(ip)[0]).toValue(), ty),
+                        return switch (enum_type.names.len) {
+                            0 => try mod.intern(.{ .empty_enum_value = ty.toIntern() }),
+                            1 => try mod.intern(.{ .enum_tag = .{
+                                .ty = ty.toIntern(),
+                                .int = if (enum_type.values.len == 0)
+                                    (try mod.intValue(enum_type.tag_ty.toType(), 0)).toIntern()
+                                else
+                                    try mod.intern_pool.getCoercedInts(
+                                        mod.gpa,
+                                        mod.intern_pool.indexToKey(enum_type.values.get(ip)[0]).int,
+                                        enum_type.tag_ty,
+                                    ),
+                            } }),
                             else => return null,
-                        }
+                        }.toValue();
                     },
                 },
 
@@ -37069,6 +37045,8 @@ fn intSubWithOverflowScalar(
     };
 }
 
+const IntFromFloatMode = enum { exact, truncate };
+
 fn intFromFloat(
     sema: *Sema,
     block: *Block,
@@ -37076,6 +37054,7 @@ fn intFromFloat(
     val: Value,
     float_ty: Type,
     int_ty: Type,
+    mode: IntFromFloatMode,
 ) CompileError!Value {
     const mod = sema.mod;
     if (float_ty.zigTypeTag(mod) == .Vector) {
@@ -37084,14 +37063,14 @@ fn intFromFloat(
         const scalar_ty = int_ty.scalarType(mod);
         for (result_data, 0..) |*scalar, i| {
             const elem_val = try val.elemValue(sema.mod, i);
-            scalar.* = try (try sema.intFromFloatScalar(block, src, elem_val, elem_ty, int_ty.scalarType(mod))).intern(scalar_ty, mod);
+            scalar.* = try (try sema.intFromFloatScalar(block, src, elem_val, elem_ty, int_ty.scalarType(mod), mode)).intern(scalar_ty, mod);
         }
         return (try mod.intern(.{ .aggregate = .{
             .ty = int_ty.toIntern(),
             .storage = .{ .elems = result_data },
         } })).toValue();
     }
-    return sema.intFromFloatScalar(block, src, val, float_ty, int_ty);
+    return sema.intFromFloatScalar(block, src, val, float_ty, int_ty, mode);
 }
 
 // float is expected to be finite and non-NaN
@@ -37126,9 +37105,19 @@ fn intFromFloatScalar(
     val: Value,
     float_ty: Type,
     int_ty: Type,
+    mode: IntFromFloatMode,
 ) CompileError!Value {
     const mod = sema.mod;
 
+    if (val.isUndef(mod)) return sema.failWithUseOfUndef(block, src);
+
+    if (mode == .exact and val.floatHasFraction(mod)) return sema.fail(
+        block,
+        src,
+        "fractional component prevents float value '{}' from coercion to type '{}'",
+        .{ val.fmtValue(float_ty, mod), int_ty.fmt(mod) },
+    );
+
     const float = val.toFloat(f128, mod);
     if (std.math.isNan(float)) {
         return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{
src/value.zig
@@ -1866,7 +1866,7 @@ pub const Value = struct {
 
     pub fn floatFromIntScalar(val: Value, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
         return switch (mod.intern_pool.indexToKey(val.toIntern())) {
-            .undef => (try mod.intern(.{ .undef = float_ty.toIntern() })).toValue(),
+            .undef => try mod.undefValue(float_ty),
             .int => |int| switch (int.storage) {
                 .big_int => |big_int| {
                     const float = bigIntToFloat(big_int.limbs, big_int.positive);
test/behavior/cast.zig
@@ -1155,6 +1155,8 @@ fn foobar(func: PFN_void) !void {
 }
 
 test "cast function with an opaque parameter" {
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+
     const Container = struct {
         const Ctx = opaque {};
         ctx: *Ctx,
@@ -2494,3 +2496,14 @@ test "@intFromBool on vector" {
     try S.doTheTest();
     try comptime S.doTheTest();
 }
+
+test "numeric coercions with undefined" {
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
+
+    const from: i32 = undefined;
+    var to: f32 = from;
+    to = @floatFromInt(from);
+    to = 42.0;
+    try expectEqual(@as(f32, 42.0), to);
+}
test/behavior/undefined.zig
@@ -1,6 +1,7 @@
 const std = @import("std");
 const builtin = @import("builtin");
 const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
 const mem = std.mem;
 
 fn initStaticArray() [10]i32 {