authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-06-28 17:37:41+03:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-30 09:57:38+02:00
log979910dc38cd15e506218d5175e5a91f56244055
treef4fb68effdca09af5eb08af4695fab4c779c8854
parent6cadac18b8fb1775815fbb3f16f503c5de0e89d0

Sema: validate shift amounts and switch ranges


5 files changed, 116 insertions(+), 61 deletions(-)

src/Sema.zig+62-11
...@@ -9027,6 +9027,10 @@ fn validateSwitchRange(...@@ -9027,6 +9027,10 @@ fn validateSwitchRange(
9027) CompileError!void {9027) CompileError!void {
9028 const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;9028 const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;
9029 const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;9029 const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;
9030 if (first_val.compare(.gt, last_val, operand_ty, sema.mod)) {
9031 const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), src_node_offset, .first);
9032 return sema.fail(block, src, "range start value is greater than the end value", .{});
9033 }
9030 const maybe_prev_src = try range_set.add(first_val, last_val, operand_ty, switch_prong_src);9034 const maybe_prev_src = try range_set.add(first_val, last_val, operand_ty, switch_prong_src);
9031 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);9035 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
9032}9036}
...@@ -9374,9 +9378,34 @@ fn zirShl(...@@ -9374,9 +9378,34 @@ fn zirShl(
9374 if (rhs_val.isUndef()) {9378 if (rhs_val.isUndef()) {
9375 return sema.addConstUndef(sema.typeOf(lhs));9379 return sema.addConstUndef(sema.typeOf(lhs));
9376 }9380 }
9381 // If rhs is 0, return lhs without doing any calculations.
9377 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {9382 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
9378 return lhs;9383 return lhs;
9379 }9384 }
9385 if (scalar_ty.zigTypeTag() != .ComptimeInt and air_tag != .shl_sat) {
9386 var bits_payload = Value.Payload.U64{
9387 .base = .{ .tag = .int_u64 },
9388 .data = scalar_ty.intInfo(target).bits,
9389 };
9390 const bit_value = Value.initPayload(&bits_payload.base);
9391 if (rhs_ty.zigTypeTag() == .Vector) {
9392 var i: usize = 0;
9393 while (i < rhs_ty.vectorLen()) : (i += 1) {
9394 if (rhs_val.indexVectorlike(i).compareHetero(.gte, bit_value, target)) {
9395 return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
9396 rhs_val.indexVectorlike(i).fmtValue(scalar_ty, sema.mod),
9397 i,
9398 scalar_ty.fmt(sema.mod),
9399 });
9400 }
9401 }
9402 } else if (rhs_val.compareHetero(.gte, bit_value, target)) {
9403 return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{
9404 rhs_val.fmtValue(scalar_ty, sema.mod),
9405 scalar_ty.fmt(sema.mod),
9406 });
9407 }
9408 }
9380 }9409 }
93819410
9382 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {9411 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
...@@ -9488,16 +9517,44 @@ fn zirShr(...@@ -9488,16 +9517,44 @@ fn zirShr(
9488 const rhs_ty = sema.typeOf(rhs);9517 const rhs_ty = sema.typeOf(rhs);
9489 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);9518 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
9490 const target = sema.mod.getTarget();9519 const target = sema.mod.getTarget();
9520 const scalar_ty = lhs_ty.scalarType();
94919521
9492 const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: {9522 const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: {
9523 if (rhs_val.isUndef()) {
9524 return sema.addConstUndef(lhs_ty);
9525 }
9526 // If rhs is 0, return lhs without doing any calculations.
9527 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
9528 return lhs;
9529 }
9530 if (scalar_ty.zigTypeTag() != .ComptimeInt) {
9531 var bits_payload = Value.Payload.U64{
9532 .base = .{ .tag = .int_u64 },
9533 .data = scalar_ty.intInfo(target).bits,
9534 };
9535 const bit_value = Value.initPayload(&bits_payload.base);
9536 if (rhs_ty.zigTypeTag() == .Vector) {
9537 var i: usize = 0;
9538 while (i < rhs_ty.vectorLen()) : (i += 1) {
9539 if (rhs_val.indexVectorlike(i).compareHetero(.gte, bit_value, target)) {
9540 return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
9541 rhs_val.indexVectorlike(i).fmtValue(scalar_ty, sema.mod),
9542 i,
9543 scalar_ty.fmt(sema.mod),
9544 });
9545 }
9546 }
9547 } else if (rhs_val.compareHetero(.gte, bit_value, target)) {
9548 return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{
9549 rhs_val.fmtValue(scalar_ty, sema.mod),
9550 scalar_ty.fmt(sema.mod),
9551 });
9552 }
9553 }
9493 if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {9554 if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
9494 if (lhs_val.isUndef() or rhs_val.isUndef()) {9555 if (lhs_val.isUndef()) {
9495 return sema.addConstUndef(lhs_ty);9556 return sema.addConstUndef(lhs_ty);
9496 }9557 }
9497 // If rhs is 0, return lhs without doing any calculations.
9498 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
9499 return sema.addConstant(lhs_ty, lhs_val);
9500 }
9501 if (air_tag == .shr_exact) {9558 if (air_tag == .shr_exact) {
9502 // Detect if any ones would be shifted out.9559 // Detect if any ones would be shifted out.
9503 const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, target);9560 const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, target);
...@@ -9508,12 +9565,6 @@ fn zirShr(...@@ -9508,12 +9565,6 @@ fn zirShr(
9508 const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, target);9565 const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, target);
9509 return sema.addConstant(lhs_ty, val);9566 return sema.addConstant(lhs_ty, val);
9510 } else {9567 } else {
9511 // Even if lhs is not comptime known, we can still deduce certain things based
9512 // on rhs.
9513 // If rhs is 0, return lhs without doing any calculations.
9514 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
9515 return lhs;
9516 }
9517 break :rs lhs_src;9568 break :rs lhs_src;
9518 }9569 }
9519 } else rhs_src;9570 } else rhs_src;
test/cases/compile_errors/shift_on_type_with_non-power-of-two_size.zig created+33
...@@ -0,0 +1,33 @@
1export fn entry() void {
2 const S = struct {
3 fn a() void {
4 var x: u24 = 42;
5 _ = x >> 24;
6 }
7 fn b() void {
8 var x: u24 = 42;
9 _ = x << 24;
10 }
11 fn c() void {
12 var x: u24 = 42;
13 _ = @shlExact(x, 24);
14 }
15 fn d() void {
16 var x: u24 = 42;
17 _ = @shrExact(x, 24);
18 }
19 };
20 S.a();
21 S.b();
22 S.c();
23 S.d();
24}
25
26// error
27// backend=stage2
28// target=native
29//
30// :5:22: error: shift amount '24' is too large for operand type 'u24'
31// :9:22: error: shift amount '24' is too large for operand type 'u24'
32// :13:30: error: shift amount '24' is too large for operand type 'u24'
33// :17:30: error: shift amount '24' is too large for operand type 'u24'
test/cases/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig deleted-34
...@@ -1,34 +0,0 @@
1export fn entry() void {
2 const S = struct {
3 fn a() void {
4 var x: u24 = 42;
5 _ = x >> 24;
6 }
7 fn b() void {
8 var x: u24 = 42;
9 _ = x << 24;
10 }
11 fn c() void {
12 var x: u24 = 42;
13 _ = @shlExact(x, 24);
14 }
15 fn d() void {
16 var x: u24 = 42;
17 _ = @shrExact(x, 24);
18 }
19 };
20 S.a();
21 S.b();
22 S.c();
23 S.d();
24}
25
26// error
27// backend=stage1
28// target=native
29// is_test=1
30//
31// tmp.zig:5:19: error: RHS of shift is too large for LHS type
32// tmp.zig:9:19: error: RHS of shift is too large for LHS type
33// tmp.zig:13:17: error: RHS of shift is too large for LHS type
34// tmp.zig:17:17: error: RHS of shift is too large for LHS type
test/cases/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig deleted-16
...@@ -1,16 +0,0 @@
1pub export fn entry() void {
2 var x: i32 = 0;
3 switch (x) {
4 6...1 => {},
5 -1...-5 => {},
6 else => unreachable,
7 }
8}
9
10// error
11// backend=stage1
12// target=native
13// is_test=1
14//
15// tmp.zig:4:9: error: range start value is greater than the end value
16// tmp.zig:5:9: error: range start value is greater than the end value
test/cases/compile_errors/switch_ranges_endpoints_are_validated.zig created+21
...@@ -0,0 +1,21 @@
1pub export fn entry1() void {
2 var x: i32 = 0;
3 switch (x) {
4 6...1 => {},
5 else => unreachable,
6 }
7}
8pub export fn entr2() void {
9 var x: i32 = 0;
10 switch (x) {
11 -1...-5 => {},
12 else => unreachable,
13 }
14}
15
16// error
17// backend=stage2
18// target=native
19//
20// :4:9: error: range start value is greater than the end value
21// :11:9: error: range start value is greater than the end value