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.
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() },

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| {
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,22 +27576,22 @@ fn coerceExtra(
return block.addBitCast(dest_ty, inst);
}
const is_undef = inst_ty.zigTypeTag(mod) == .Undefined;
switch (dest_ty.zigTypeTag(mod)) {
.Optional => optional: {
if (maybe_inst_val) |val| {
// undefined sets the optional bit also to undefined.
if (is_undef) {
if (val.toIntern() == .undef) {
return mod.undefRef(dest_ty);
}
// null to ?T
if (inst_ty.zigTypeTag(mod) == .Null) {
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
// but don't do it if the source type is a double pointer
@ -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 },
} })
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
enum_type.values.get(ip)[0]).toValue(), ty),
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 '{}'", .{

View File

@ -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);

View File

@ -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);
}

View File

@ -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 {