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
This commit is contained in:
Jacob Young 2023-08-27 17:14:31 -04:00 committed by Andrew Kelley
parent 49075d2055
commit e2ff486de5
5 changed files with 102 additions and 99 deletions

View File

@ -6360,7 +6360,7 @@ pub fn errorSetFromUnsortedNames(
/// Supports only pointers, not pointer-like optionals. /// Supports only pointers, not pointer-like optionals.
pub fn ptrIntValue(mod: *Module, ty: Type, x: u64) Allocator.Error!Value { 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 = .{ const i = try intern(mod, .{ .ptr = .{
.ty = ty.toIntern(), .ty = ty.toIntern(),
.addr = .{ .int = (try mod.intValue_u64(Type.usize, x)).toIntern() }, .addr = .{ .int = (try mod.intValue_u64(Type.usize, x)).toIntern() },

View File

@ -17282,12 +17282,13 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
for (enum_field_vals, 0..) |*field_val, i| { for (enum_field_vals, 0..) |*field_val, i| {
const enum_type = ip.indexToKey(ty.toIntern()).enum_type; const enum_type = ip.indexToKey(ty.toIntern()).enum_type;
const value_val = if (enum_type.values.len > 0) 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 else
try mod.intern(.{ .int = .{ (try mod.intValue(Type.comptime_int, i)).toIntern();
.ty = .comptime_int_type,
.storage = .{ .u64 = @intCast(i) },
} });
// TODO: write something like getCoercedInts to avoid needing to dupe // 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 = try sema.arena.dupe(u8, ip.stringToSlice(enum_type.names.get(ip)[i]));
const name_val = v: { 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); try sema.checkFloatType(block, operand_src, operand_scalar_ty);
if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { 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()); return Air.internedToRef(result_val.toIntern());
} else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) { } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
return sema.failWithNeededComptime(block, operand_src, .{ return sema.failWithNeededComptime(block, operand_src, .{
@ -27575,21 +27576,21 @@ fn coerceExtra(
return block.addBitCast(dest_ty, inst); return block.addBitCast(dest_ty, inst);
} }
const is_undef = inst_ty.zigTypeTag(mod) == .Undefined;
switch (dest_ty.zigTypeTag(mod)) { switch (dest_ty.zigTypeTag(mod)) {
.Optional => optional: { .Optional => optional: {
// undefined sets the optional bit also to undefined. if (maybe_inst_val) |val| {
if (is_undef) { // undefined sets the optional bit also to undefined.
return mod.undefRef(dest_ty); if (val.toIntern() == .undef) {
} return mod.undefRef(dest_ty);
}
// null to ?T // null to ?T
if (inst_ty.zigTypeTag(mod) == .Null) { if (val.toIntern() == .null_value) {
return Air.internedToRef((try mod.intern(.{ .opt = .{ return Air.internedToRef((try mod.intern(.{ .opt = .{
.ty = dest_ty.toIntern(), .ty = dest_ty.toIntern(),
.val = .none, .val = .none,
} }))); } })));
}
} }
// cast from ?*T and ?[*]T to ?*anyopaque // cast from ?*T and ?[*]T to ?*anyopaque
@ -27681,7 +27682,9 @@ fn coerceExtra(
if (dest_info.sentinel != .none) { if (dest_info.sentinel != .none) {
if (array_ty.sentinel(mod)) |inst_sent| { 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 = .{ in_memory_result = .{ .ptr_sentinel = .{
.actual = inst_sent, .actual = inst_sent,
.wanted = dest_info.sentinel.toValue(), .wanted = dest_info.sentinel.toValue(),
@ -27758,9 +27761,10 @@ fn coerceExtra(
switch (dest_info.flags.size) { switch (dest_info.flags.size) {
// coercion to C pointer // coercion to C pointer
.C => switch (inst_ty.zigTypeTag(mod)) { .C => switch (inst_ty.zigTypeTag(mod)) {
.Null => { .Null => return Air.internedToRef(try mod.intern(.{ .ptr = .{
return Air.internedToRef((try mod.getCoerced(Value.null, dest_ty)).toIntern()); .ty = dest_ty.toIntern(),
}, .addr = .{ .int = .zero_usize },
} })),
.ComptimeInt => { .ComptimeInt => {
const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
error.NotCoercible => break :pointer, error.NotCoercible => break :pointer,
@ -27865,10 +27869,19 @@ fn coerceExtra(
// we use a dummy pointer value with the required alignment. // we use a dummy pointer value with the required alignment.
return Air.internedToRef((try mod.intern(.{ .ptr = .{ return Air.internedToRef((try mod.intern(.{ .ptr = .{
.ty = dest_ty.toIntern(), .ty = dest_ty.toIntern(),
.addr = .{ .int = (if (dest_info.flags.alignment != .none) .addr = .{ .int = if (dest_info.flags.alignment != .none)
try mod.intValue(Type.usize, dest_info.flags.alignment.toByteUnitsOptional().?) (try mod.intValue(
Type.usize,
dest_info.flags.alignment.toByteUnitsOptional().?,
)).toIntern()
else 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(), .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 if (dest_info.sentinel == .none or inst_info.sentinel == .none or
dest_info.sentinel != Air.internedToRef(dest_info.sentinel) !=
try mod.intern_pool.getCoerced(sema.gpa, inst_info.sentinel, dest_info.child)) try sema.coerceInMemory(inst_info.sentinel.toValue(), dest_info.child.toType()))
break :p; break :p;
const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 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)) { .Int, .ComptimeInt => switch (inst_ty.zigTypeTag(mod)) {
.Float, .ComptimeFloat => float: { .Float, .ComptimeFloat => float: {
if (is_undef) { const val = maybe_inst_val orelse {
return mod.undefRef(dest_ty);
}
const val = (try sema.resolveMaybeUndefVal(inst)) orelse {
if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
if (!opts.report_err) return error.NotCoercible; if (!opts.report_err) return error.NotCoercible;
return sema.failWithNeededComptime(block, inst_src, .{ return sema.failWithNeededComptime(block, inst_src, .{
@ -27927,29 +27937,23 @@ fn coerceExtra(
} }
break :float; break :float;
}; };
const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty, .exact);
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);
return Air.internedToRef(result_val.toIntern()); return Air.internedToRef(result_val.toIntern());
}, },
.Int, .ComptimeInt => { .Int, .ComptimeInt => {
if (is_undef) { if (maybe_inst_val) |val| {
return mod.undefRef(dest_ty);
}
if (try sema.resolveMaybeUndefVal(inst)) |val| {
// comptime-known integer to other number // comptime-known integer to other number
if (!(try sema.intFitsInType(val, dest_ty, null))) { if (!(try sema.intFitsInType(val, dest_ty, null))) {
if (!opts.report_err) return error.NotCoercible; 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 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 (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
if (!opts.report_err) return error.NotCoercible; if (!opts.report_err) return error.NotCoercible;
@ -27970,9 +27974,6 @@ fn coerceExtra(
return block.addTyOp(.intcast, dest_ty, inst); return block.addTyOp(.intcast, dest_ty, inst);
} }
}, },
.Undefined => {
return mod.undefRef(dest_ty);
},
else => {}, else => {},
}, },
.Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) { .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) {
@ -27982,10 +27983,7 @@ fn coerceExtra(
return Air.internedToRef(result_val.toIntern()); return Air.internedToRef(result_val.toIntern());
}, },
.Float => { .Float => {
if (is_undef) { if (maybe_inst_val) |val| {
return mod.undefRef(dest_ty);
}
if (try sema.resolveMaybeUndefVal(inst)) |val| {
const result_val = try val.floatCast(dest_ty, mod); const result_val = try val.floatCast(dest_ty, mod);
if (!val.eql(try result_val.floatCast(inst_ty, mod), inst_ty, mod)) { if (!val.eql(try result_val.floatCast(inst_ty, mod), inst_ty, mod)) {
return sema.fail( return sema.fail(
@ -28012,10 +28010,7 @@ fn coerceExtra(
} }
}, },
.Int, .ComptimeInt => int: { .Int, .ComptimeInt => int: {
if (is_undef) { const val = maybe_inst_val orelse {
return mod.undefRef(dest_ty);
}
const val = (try sema.resolveMaybeUndefVal(inst)) orelse {
if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {
if (!opts.report_err) return error.NotCoercible; if (!opts.report_err) return error.NotCoercible;
return sema.failWithNeededComptime(block, inst_src, .{ return sema.failWithNeededComptime(block, inst_src, .{
@ -28037,9 +28032,6 @@ fn coerceExtra(
//} //}
return Air.internedToRef(result_val.toIntern()); return Air.internedToRef(result_val.toIntern());
}, },
.Undefined => {
return mod.undefRef(dest_ty);
},
else => {}, else => {},
}, },
.Enum => switch (inst_ty.zigTypeTag(mod)) { .Enum => switch (inst_ty.zigTypeTag(mod)) {
@ -28070,9 +28062,6 @@ fn coerceExtra(
return sema.unionToTag(block, dest_ty, inst, inst_src); return sema.unionToTag(block, dest_ty, inst, inst_src);
} }
}, },
.Undefined => {
return mod.undefRef(dest_ty);
},
else => {}, else => {},
}, },
.ErrorUnion => switch (inst_ty.zigTypeTag(mod)) { .ErrorUnion => switch (inst_ty.zigTypeTag(mod)) {
@ -28107,9 +28096,6 @@ fn coerceExtra(
// E to E!T // E to E!T
return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src); return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
}, },
.Undefined => {
return mod.undefRef(dest_ty);
},
else => eu: { else => eu: {
// T to E!T // T to E!T
return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) { 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); return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src);
} }
}, },
.Undefined => {
return mod.undefRef(dest_ty);
},
else => {}, else => {},
}, },
.Array => switch (inst_ty.zigTypeTag(mod)) { .Array => switch (inst_ty.zigTypeTag(mod)) {
@ -28140,9 +28123,6 @@ fn coerceExtra(
return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
} }
}, },
.Undefined => {
return mod.undefRef(dest_ty);
},
else => {}, else => {},
}, },
.Vector => switch (inst_ty.zigTypeTag(mod)) { .Vector => switch (inst_ty.zigTypeTag(mod)) {
@ -28152,9 +28132,6 @@ fn coerceExtra(
return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
} }
}, },
.Undefined => {
return mod.undefRef(dest_ty);
},
else => {}, else => {},
}, },
.Struct => blk: { .Struct => blk: {
@ -28174,9 +28151,7 @@ fn coerceExtra(
// undefined to anything. We do this after the big switch above so that // 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 // special logic has a chance to run first, such as `*[N]T` to `[]T` which
// should initialize the length field of the slice. // should initialize the length field of the slice.
if (is_undef) { if (maybe_inst_val) |val| if (val.toIntern() == .undef) return mod.undefRef(dest_ty);
return mod.undefRef(dest_ty);
}
if (!opts.report_err) return error.NotCoercible; 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())) { switch (mod.intern_pool.indexToKey(val.toIntern())) {
.int => |int| switch (int.storage) { .int => |int| switch (int.storage) {
.u64, .i64, .big_int => return val, .u64, .i64, .big_int => return val,
.lazy_align, .lazy_size => return (try mod.intern(.{ .int = .{ .lazy_align, .lazy_size => return mod.intValue(
.ty = int.ty, int.ty.toType(),
.storage = .{ .u64 = (try val.getUnsignedIntAdvanced(mod, sema)).? }, (try val.getUnsignedIntAdvanced(mod, sema)).?,
} })).toValue(), ),
}, },
.ptr => |ptr| { .ptr => |ptr| {
const resolved_len = switch (ptr.len) { const resolved_len = switch (ptr.len) {
@ -36217,20 +36192,21 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
.auto, .explicit => { .auto, .explicit => {
if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null; if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null;
switch (enum_type.names.len) { return switch (enum_type.names.len) {
0 => { 0 => try mod.intern(.{ .empty_enum_value = ty.toIntern() }),
const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); 1 => try mod.intern(.{ .enum_tag = .{
return only.toValue(); .ty = ty.toIntern(),
}, .int = if (enum_type.values.len == 0)
1 => return try mod.getCoerced((if (enum_type.values.len == 0) (try mod.intValue(enum_type.tag_ty.toType(), 0)).toIntern()
try mod.intern(.{ .int = .{ else
.ty = enum_type.tag_ty, try mod.intern_pool.getCoercedInts(
.storage = .{ .u64 = 0 }, mod.gpa,
} }) mod.intern_pool.indexToKey(enum_type.values.get(ip)[0]).int,
else enum_type.tag_ty,
enum_type.values.get(ip)[0]).toValue(), ty), ),
} }),
else => return null, else => return null,
} }.toValue();
}, },
}, },
@ -37069,6 +37045,8 @@ fn intSubWithOverflowScalar(
}; };
} }
const IntFromFloatMode = enum { exact, truncate };
fn intFromFloat( fn intFromFloat(
sema: *Sema, sema: *Sema,
block: *Block, block: *Block,
@ -37076,6 +37054,7 @@ fn intFromFloat(
val: Value, val: Value,
float_ty: Type, float_ty: Type,
int_ty: Type, int_ty: Type,
mode: IntFromFloatMode,
) CompileError!Value { ) CompileError!Value {
const mod = sema.mod; const mod = sema.mod;
if (float_ty.zigTypeTag(mod) == .Vector) { if (float_ty.zigTypeTag(mod) == .Vector) {
@ -37084,14 +37063,14 @@ fn intFromFloat(
const scalar_ty = int_ty.scalarType(mod); const scalar_ty = int_ty.scalarType(mod);
for (result_data, 0..) |*scalar, i| { for (result_data, 0..) |*scalar, i| {
const elem_val = try val.elemValue(sema.mod, 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 = .{ return (try mod.intern(.{ .aggregate = .{
.ty = int_ty.toIntern(), .ty = int_ty.toIntern(),
.storage = .{ .elems = result_data }, .storage = .{ .elems = result_data },
} })).toValue(); } })).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 // float is expected to be finite and non-NaN
@ -37126,9 +37105,19 @@ fn intFromFloatScalar(
val: Value, val: Value,
float_ty: Type, float_ty: Type,
int_ty: Type, int_ty: Type,
mode: IntFromFloatMode,
) CompileError!Value { ) CompileError!Value {
const mod = sema.mod; 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); const float = val.toFloat(f128, mod);
if (std.math.isNan(float)) { if (std.math.isNan(float)) {
return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{ return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{

View File

@ -1866,7 +1866,7 @@ pub const Value = struct {
pub fn floatFromIntScalar(val: Value, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value { pub fn floatFromIntScalar(val: Value, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
return switch (mod.intern_pool.indexToKey(val.toIntern())) { 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) { .int => |int| switch (int.storage) {
.big_int => |big_int| { .big_int => |big_int| {
const float = bigIntToFloat(big_int.limbs, big_int.positive); const float = bigIntToFloat(big_int.limbs, big_int.positive);

View File

@ -1155,6 +1155,8 @@ fn foobar(func: PFN_void) !void {
} }
test "cast function with an opaque parameter" { test "cast function with an opaque parameter" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
const Container = struct { const Container = struct {
const Ctx = opaque {}; const Ctx = opaque {};
ctx: *Ctx, ctx: *Ctx,
@ -2494,3 +2496,14 @@ test "@intFromBool on vector" {
try S.doTheTest(); try S.doTheTest();
try comptime 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);
}

View File

@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const expect = std.testing.expect; const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const mem = std.mem; const mem = std.mem;
fn initStaticArray() [10]i32 { fn initStaticArray() [10]i32 {