Commit c64279b15b

Andrew Kelley <andrew@ziglang.org>
2022-03-15 07:15:01
Sema: fix shl_sat with comptime rhs
1 parent 1adb150
src/Sema.zig
@@ -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
test/behavior/floatop.zig
@@ -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);
test/behavior/fn.zig
@@ -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;
+    }
+
+    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 = [_]@TypeOf(fn1){
-        fn1,
-        fn2,
-        fn3,
-        fn4,
+    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 {}
test/behavior/saturating_arithmetic.zig
@@ -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;
 
test/behavior/translate_c_macros.zig
@@ -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" {
test/behavior/type.zig
@@ -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)))),
-    ));
-}
test/behavior/type_info.zig
@@ -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| {