Sema: fix shl_sat with comptime rhs

This commit is contained in:
Andrew Kelley 2022-03-14 23:15:01 -07:00
parent 1adb15098c
commit c64279b15b
7 changed files with 89 additions and 65 deletions

View File

@ -6398,7 +6398,6 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@ -6406,16 +6405,29 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
const operand = sema.resolveInst(extra.rhs);
return sema.intCast(block, dest_ty, dest_ty_src, operand, operand_src, true);
}
fn intCast(
sema: *Sema,
block: *Block,
dest_ty: Type,
dest_ty_src: LazySrcLoc,
operand: Air.Inst.Ref,
operand_src: LazySrcLoc,
runtime_safety: bool,
) CompileError!Air.Inst.Ref {
const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
_ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
if (try sema.isComptimeKnown(block, operand_src, operand)) {
return sema.coerce(block, dest_ty, operand, operand_src);
} else if (dest_is_comptime_int) {
return sema.fail(block, src, "unable to cast runtime value to 'comptime_int'", .{});
return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
}
// TODO insert safety check to make sure the value fits in the dest type
_ = runtime_safety;
if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| {
return sema.addConstant(dest_ty, opv);
@ -7986,6 +7998,7 @@ fn zirShl(
const rhs = sema.resolveInst(extra.rhs);
// TODO coerce rhs if air_tag is not shl_sat
const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, sema.typeOf(rhs));
const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
@ -7999,13 +8012,14 @@ fn zirShl(
}
}
const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
const lhs_ty = sema.typeOf(lhs);
const lhs_ty = sema.typeOf(lhs);
const rhs_ty = sema.typeOf(rhs);
const target = sema.mod.getTarget();
const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
const rhs_val = maybe_rhs_val orelse break :rs rhs_src;
const target = sema.mod.getTarget();
const val = switch (air_tag) {
.shl_exact => val: {
const shifted = try lhs_val.shl(rhs_val, sema.arena);
@ -8038,8 +8052,24 @@ fn zirShl(
// TODO: insert runtime safety check for shl_exact
const new_rhs = if (air_tag == .shl_sat) rhs: {
// Limit the RHS type for saturating shl to be an integer as small as the LHS.
if (rhs_is_comptime_int or
rhs_ty.intInfo(target).bits > lhs_ty.intInfo(target).bits)
{
const max_int = try sema.addConstant(
lhs_ty,
try lhs_ty.maxInt(sema.arena, target),
);
const rhs_limited = try sema.analyzeMinMax(block, rhs_src, rhs, max_int, .min, rhs_src, rhs_src);
break :rhs try sema.intCast(block, lhs_ty, rhs_src, rhs_limited, rhs_src, false);
} else {
break :rhs rhs;
}
} else rhs;
try sema.requireRuntimeBlock(block, runtime_src);
return block.addBinOp(air_tag, lhs, rhs);
return block.addBinOp(air_tag, lhs, new_rhs);
}
fn zirShr(
@ -14537,6 +14567,19 @@ fn zirMinMax(
const rhs = sema.resolveInst(extra.rhs);
try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs));
try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs));
return sema.analyzeMinMax(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
}
fn analyzeMinMax(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
lhs: Air.Inst.Ref,
rhs: Air.Inst.Ref,
air_tag: Air.Inst.Tag,
lhs_src: LazySrcLoc,
rhs_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src);
// TODO @maximum(max_int, undefined) should return max_int

View File

@ -609,7 +609,11 @@ test "negation f64" {
}
test "negation f80" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend != .stage1) {
// This test case exercises @intToFloat f80 in the compiler implementation.
// https://github.com/ziglang/zig/issues/11030
return error.SkipZigTest;
}
if (builtin.os.tag == .freebsd) {
// TODO file issue to track this failure
@ -673,7 +677,10 @@ fn fnWithFloatMode() f32 {
}
test "float literal at compile time not lossy" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend != .stage1) {
// https://github.com/ziglang/zig/issues/11169
return error.SkipZigTest;
}
try expect(16777216.0 + 1.0 == 16777217.0);
try expect(9007199254740992.0 + 1.0 == 9007199254740993.0);

View File

@ -307,13 +307,20 @@ fn acceptsString(foo: []u8) void {
}
test "function pointers" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage1) {
// stage1 has wrong semantics for function pointers
return error.SkipZigTest;
}
const fns = [_]@TypeOf(fn1){
fn1,
fn2,
fn3,
fn4,
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const fns = [_]*const @TypeOf(fn1){
&fn1,
&fn2,
&fn3,
&fn4,
};
for (fns) |f, i| {
try expect(f() == @intCast(u32, i) + 5);
@ -380,7 +387,9 @@ test "ability to give comptime types and non comptime types to same parameter" {
}
test "function with inferred error set but returning no error" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const S = struct {
fn foo() !void {}

View File

@ -163,8 +163,6 @@ test "saturating shift-left" {
}
test "saturating shl uses the LHS type" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const lhs_const: u8 = 1;
var lhs_var: u8 = 1;

View File

@ -45,16 +45,16 @@ test "cast negative integer to pointer" {
}
test "casting to union with a macro" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO Sema.zirUnionInitPtr
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
const l: c_long = 42;
const d: f64 = 2.0;
var casted = h.UNION_CAST(l);
try expectEqual(l, casted.l);
try expect(l == casted.l);
casted = h.UNION_CAST(d);
try expectEqual(d, casted.d);
try expect(d == casted.d);
}
test "nested comma operator" {

View File

@ -230,7 +230,10 @@ test "Type.Vector" {
}
test "Type.AnyFrame" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend != .stage1) {
// https://github.com/ziglang/zig/issues/6025
return error.SkipZigTest;
}
try testTypes(&[_]type{
anyframe,
@ -514,39 +517,3 @@ test "Type.Union from regular enum" {
_ = T;
_ = @typeInfo(T).Union;
}
test "Type.Fn" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// wasm doesn't support align attributes on functions
if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest;
const foo = struct {
fn func(a: usize, b: bool) align(4) callconv(.C) usize {
_ = a;
_ = b;
return 0;
}
}.func;
const Foo = @Type(@typeInfo(@TypeOf(foo)));
const foo_2: Foo = foo;
_ = foo_2;
}
test "Type.BoundFn" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// wasm doesn't support align attributes on functions
if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest;
const TestStruct = packed struct {
pub fn foo(self: *const @This()) align(4) callconv(.Unspecified) void {
_ = self;
}
};
const test_instance: TestStruct = undefined;
try testing.expect(std.meta.eql(
@typeName(@TypeOf(test_instance.foo)),
@typeName(@Type(@typeInfo(@TypeOf(test_instance.foo)))),
));
}

View File

@ -379,12 +379,6 @@ fn testFunction() !void {
try expect(fn_info.Fn.return_type.? == usize);
const fn_aligned_info = @typeInfo(@TypeOf(fooAligned));
try expect(fn_aligned_info.Fn.alignment == 4);
if (builtin.zig_backend != .stage1) return; // no bound fn in stage2
const test_instance: TestPackedStruct = undefined;
const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo));
try expect(bound_fn_info == .BoundFn);
try expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestPackedStruct);
}
extern fn foo(a: usize, b: bool, ...) callconv(.C) usize;
@ -413,7 +407,10 @@ fn testVector() !void {
}
test "type info: anyframe and anyframe->T" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend != .stage1) {
// https://github.com/ziglang/zig/issues/6025
return error.SkipZigTest;
}
try testAnyFrame();
comptime try testAnyFrame();
@ -469,7 +466,10 @@ fn add(a: i32, b: i32) i32 {
}
test "type info for async frames" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend != .stage1) {
// https://github.com/ziglang/zig/issues/6025
return error.SkipZigTest;
}
switch (@typeInfo(@Frame(add))) {
.Frame => |frame| {