Commit 979910dc38

Veikka Tuominen <git@vexu.eu>
2022-06-28 16:37:41
Sema: validate shift amounts and switch ranges
1 parent 6cadac1
src/Sema.zig
@@ -9027,6 +9027,10 @@ fn validateSwitchRange(
 ) CompileError!void {
     const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;
     const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;
+    if (first_val.compare(.gt, last_val, operand_ty, sema.mod)) {
+        const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), src_node_offset, .first);
+        return sema.fail(block, src, "range start value is greater than the end value", .{});
+    }
     const maybe_prev_src = try range_set.add(first_val, last_val, operand_ty, switch_prong_src);
     return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
 }
@@ -9374,9 +9378,34 @@ fn zirShl(
         if (rhs_val.isUndef()) {
             return sema.addConstUndef(sema.typeOf(lhs));
         }
+        // If rhs is 0, return lhs without doing any calculations.
         if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
             return lhs;
         }
+        if (scalar_ty.zigTypeTag() != .ComptimeInt and air_tag != .shl_sat) {
+            var bits_payload = Value.Payload.U64{
+                .base = .{ .tag = .int_u64 },
+                .data = scalar_ty.intInfo(target).bits,
+            };
+            const bit_value = Value.initPayload(&bits_payload.base);
+            if (rhs_ty.zigTypeTag() == .Vector) {
+                var i: usize = 0;
+                while (i < rhs_ty.vectorLen()) : (i += 1) {
+                    if (rhs_val.indexVectorlike(i).compareHetero(.gte, bit_value, target)) {
+                        return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
+                            rhs_val.indexVectorlike(i).fmtValue(scalar_ty, sema.mod),
+                            i,
+                            scalar_ty.fmt(sema.mod),
+                        });
+                    }
+                }
+            } else if (rhs_val.compareHetero(.gte, bit_value, target)) {
+                return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{
+                    rhs_val.fmtValue(scalar_ty, sema.mod),
+                    scalar_ty.fmt(sema.mod),
+                });
+            }
+        }
     }
 
     const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
@@ -9488,16 +9517,44 @@ fn zirShr(
     const rhs_ty = sema.typeOf(rhs);
     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
     const target = sema.mod.getTarget();
+    const scalar_ty = lhs_ty.scalarType();
 
     const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: {
+        if (rhs_val.isUndef()) {
+            return sema.addConstUndef(lhs_ty);
+        }
+        // If rhs is 0, return lhs without doing any calculations.
+        if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+            return lhs;
+        }
+        if (scalar_ty.zigTypeTag() != .ComptimeInt) {
+            var bits_payload = Value.Payload.U64{
+                .base = .{ .tag = .int_u64 },
+                .data = scalar_ty.intInfo(target).bits,
+            };
+            const bit_value = Value.initPayload(&bits_payload.base);
+            if (rhs_ty.zigTypeTag() == .Vector) {
+                var i: usize = 0;
+                while (i < rhs_ty.vectorLen()) : (i += 1) {
+                    if (rhs_val.indexVectorlike(i).compareHetero(.gte, bit_value, target)) {
+                        return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
+                            rhs_val.indexVectorlike(i).fmtValue(scalar_ty, sema.mod),
+                            i,
+                            scalar_ty.fmt(sema.mod),
+                        });
+                    }
+                }
+            } else if (rhs_val.compareHetero(.gte, bit_value, target)) {
+                return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{
+                    rhs_val.fmtValue(scalar_ty, sema.mod),
+                    scalar_ty.fmt(sema.mod),
+                });
+            }
+        }
         if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
-            if (lhs_val.isUndef() or rhs_val.isUndef()) {
+            if (lhs_val.isUndef()) {
                 return sema.addConstUndef(lhs_ty);
             }
-            // If rhs is 0, return lhs without doing any calculations.
-            if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
-                return sema.addConstant(lhs_ty, lhs_val);
-            }
             if (air_tag == .shr_exact) {
                 // Detect if any ones would be shifted out.
                 const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, target);
@@ -9508,12 +9565,6 @@ fn zirShr(
             const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, target);
             return sema.addConstant(lhs_ty, val);
         } else {
-            // Even if lhs is not comptime known, we can still deduce certain things based
-            // on rhs.
-            // If rhs is 0, return lhs without doing any calculations.
-            if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
-                return lhs;
-            }
             break :rs lhs_src;
         }
     } else rhs_src;
test/cases/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig
@@ -1,16 +0,0 @@
-pub export fn entry() void {
-    var x: i32 = 0;
-    switch (x) {
-        6...1 => {},
-        -1...-5 => {},
-        else => unreachable,
-    }
-}
-
-// error
-// backend=stage1
-// target=native
-// is_test=1
-//
-// tmp.zig:4:9: error: range start value is greater than the end value
-// tmp.zig:5:9: error: range start value is greater than the end value
test/cases/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig → test/cases/compile_errors/shift_on_type_with_non-power-of-two_size.zig
@@ -24,11 +24,10 @@ export fn entry() void {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
-// is_test=1
 //
-// tmp.zig:5:19: error: RHS of shift is too large for LHS type
-// tmp.zig:9:19: error: RHS of shift is too large for LHS type
-// tmp.zig:13:17: error: RHS of shift is too large for LHS type
-// tmp.zig:17:17: error: RHS of shift is too large for LHS type
+// :5:22: error: shift amount '24' is too large for operand type 'u24'
+// :9:22: error: shift amount '24' is too large for operand type 'u24'
+// :13:30: error: shift amount '24' is too large for operand type 'u24'
+// :17:30: error: shift amount '24' is too large for operand type 'u24'
test/cases/compile_errors/switch_ranges_endpoints_are_validated.zig
@@ -0,0 +1,21 @@
+pub export fn entry1() void {
+    var x: i32 = 0;
+    switch (x) {
+        6...1 => {},
+        else => unreachable,
+    }
+}
+pub export fn entr2() void {
+    var x: i32 = 0;
+    switch (x) {
+        -1...-5 => {},
+        else => unreachable,
+    }
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :4:9: error: range start value is greater than the end value
+// :11:9: error: range start value is greater than the end value