| author | |
| committer | |
| log | 0b1e8690dadb5d81b4d63832e27a117f57b4be81 |
| tree | 20cc3fb63d08286bd78a1b77b529ff509c5e969a |
| parent | 3bf0b8eada9ec97d4413fed5be29f5e2af30dce1 |
Fixes #16311
The actual cause of #16311 is the `start_is_zero` special case:
```zig
const range_len = if (end_val == .none or start_is_zero)
end_val
else
try parent_gz.addPlNode(.sub, input, Zir.Inst.Bin{
.lhs = end_val,
.rhs = start_val,
});
```
It only happens if the range start is 0. In that case we would not perform any type checking.
Only in the other cases coincidentally `.sub` performs type checking in Sema, but the errors are still rather poor:
```
$ zig test x.zig
x.zig:9:15: error: invalid operands to binary expression: 'Pointer' and 'Pointer'
for ("abc".."def") |val| {
~~~~~^~~~~~~
```
Note how it's the same as if I use `-`:
```
x.zig:9:11: error: invalid operands to binary expression: 'Pointer' and 'Pointer'
"abc" - "def";
~~~~~~^~~~~~~
```
Now after this PR, the errors are much clearer for both range start and end:
```
x.zig:9:10: error: expected type 'usize', found '*const [3:0]u8'
for ("abc".."def") |val| {
^~~~~
```
This is why I decided to use `.ty` instead of `.coerced_ty` for both range start and end rather than
just perform type checking in that `end_val == .none or start_is_zero` case.2 files changed, 39 insertions(+), 4 deletions(-)
src/AstGen.zig+4-4| ... | @@ -6500,11 +6500,11 @@ fn forExpr( | ... | @@ -6500,11 +6500,11 @@ fn forExpr( |
| 6500 | return astgen.failTok(ident_tok, "cannot capture reference to range", .{}); | 6500 | return astgen.failTok(ident_tok, "cannot capture reference to range", .{}); |
| 6501 | } | 6501 | } |
| 6502 | const start_node = node_data[input].lhs; | 6502 | const start_node = node_data[input].lhs; |
| 6503 | const start_val = try expr(parent_gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, start_node); | 6503 | const start_val = try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, start_node); |
| 6504 | 6504 | ||
| 6505 | const end_node = node_data[input].rhs; | 6505 | const end_node = node_data[input].rhs; |
| 6506 | const end_val = if (end_node != 0) | 6506 | const end_val = if (end_node != 0) |
| 6507 | try expr(parent_gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_data[input].rhs) | 6507 | try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_data[input].rhs) |
| 6508 | else | 6508 | else |
| 6509 | .none; | 6509 | .none; |
| 6510 | 6510 | ||
| ... | @@ -10339,8 +10339,8 @@ fn nodeUsesAnonNameStrategy(tree: *const Ast, node: Ast.Node.Index) bool { | ... | @@ -10339,8 +10339,8 @@ fn nodeUsesAnonNameStrategy(tree: *const Ast, node: Ast.Node.Index) bool { |
| 10339 | 10339 | ||
| 10340 | /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of | 10340 | /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of |
| 10341 | /// result locations must call this function on their result. | 10341 | /// result locations must call this function on their result. |
| 10342 | /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer. | 10342 | /// As an example, if `ri.rl` is `.ptr`, it will write the result to the pointer. |
| 10343 | /// If the `ResultLoc` is `ty`, it will coerce the result to the type. | 10343 | /// If `ri.rl` is `.ty`, it will coerce the result to the type. |
| 10344 | /// Assumes nothing stacked on `gz`. | 10344 | /// Assumes nothing stacked on `gz`. |
| 10345 | fn rvalue( | 10345 | fn rvalue( |
| 10346 | gz: *GenZir, | 10346 | gz: *GenZir, |
test/cases/compile_errors/for_invalid_ranges.zig created+35| ... | @@ -0,0 +1,35 @@ | ||
| 1 | export fn a() void { | ||
| 2 | for (0.."hello") |i| { | ||
| 3 | _ = i; | ||
| 4 | } | ||
| 5 | } | ||
| 6 | export fn b() void { | ||
| 7 | for (-1..-5) |i| { | ||
| 8 | _ = i; | ||
| 9 | } | ||
| 10 | } | ||
| 11 | export fn c() void { | ||
| 12 | for ("hello"..0) |i| { | ||
| 13 | _ = i; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | export fn d() void { | ||
| 17 | for (0..&.{ 'a', 'b', 'c' }) |i| { | ||
| 18 | _ = i; | ||
| 19 | } | ||
| 20 | } | ||
| 21 | export fn e() void { | ||
| 22 | for (@as(u8, 1)..0) |i| { | ||
| 23 | _ = i; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | // error | ||
| 28 | // backend=stage2 | ||
| 29 | // target=native | ||
| 30 | // | ||
| 31 | // :2:13: error: expected type 'usize', found '*const [5:0]u8' | ||
| 32 | // :7:10: error: type 'usize' cannot represent integer value '-1' | ||
| 33 | // :12:10: error: expected type 'usize', found '*const [5:0]u8' | ||
| 34 | // :17:13: error: expected type 'usize', found '*const struct{comptime comptime_int = 97, comptime comptime_int = 98, comptime comptime_int = 99}' | ||
| 35 | // :22:20: error: overflow of integer type 'usize' with value '-1' | ||