Commit bcb72401d3

Andrew Kelley <andrew@ziglang.org>
2023-02-18 21:04:27
AstGen: add error for discard of unbounded counter
1 parent 22965e6
src/AstGen.zig
@@ -6346,8 +6346,9 @@ fn forExpr(
             const i = @intCast(u32, i_usize);
             const capture_is_ref = token_tags[capture_token] == .asterisk;
             const ident_tok = capture_token + @boolToInt(capture_is_ref);
+            const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_");
 
-            if (mem.eql(u8, tree.tokenSlice(ident_tok), "_") and capture_is_ref) {
+            if (is_discard and capture_is_ref) {
                 return astgen.failTok(capture_token, "pointer modifier invalid on discard", .{});
             }
             // Skip over the comma, and on to the next capture (or the ending pipe character).
@@ -6367,6 +6368,10 @@ fn forExpr(
                 else
                     .none;
 
+                if (end_val == .none and is_discard) {
+                    return astgen.failTok(ident_tok, "discard of unbounded counter", .{});
+                }
+
                 const start_is_zero = nodeIsTriviallyZero(tree, start_node);
                 const range_len = if (end_val == .none or start_is_zero)
                     end_val
test/behavior/for.zig
@@ -276,3 +276,103 @@ test "two counters" {
 
     try expect(sum == 10);
 }
+
+test "1-based counter and ptr to array" {
+    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
+    var ok: usize = 0;
+
+    for (1..6, "hello") |i, b| {
+        if (i == 1) {
+            try expect(b == 'h');
+            ok += 1;
+        }
+        if (i == 2) {
+            try expect(b == 'e');
+            ok += 1;
+        }
+        if (i == 3) {
+            try expect(b == 'l');
+            ok += 1;
+        }
+        if (i == 4) {
+            try expect(b == 'l');
+            ok += 1;
+        }
+        if (i == 5) {
+            try expect(b == 'o');
+            ok += 1;
+        }
+    }
+
+    try expect(ok == 5);
+}
+
+test "slice and two counters, one is offset and one is runtime" {
+    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
+    const slice: []const u8 = "blah";
+    var start: usize = 0;
+
+    for (slice, start..4, 1..5) |a, b, c| {
+        if (a == 'b') {
+            try expect(b == 0);
+            try expect(c == 1);
+        }
+        if (a == 'l') {
+            try expect(b == 1);
+            try expect(c == 2);
+        }
+        if (a == 'a') {
+            try expect(b == 2);
+            try expect(c == 3);
+        }
+        if (a == 'h') {
+            try expect(b == 3);
+            try expect(c == 4);
+        }
+    }
+}
+
+test "two slices, one captured by-ref" {
+    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
+    var buf: [10]u8 = undefined;
+    const slice1: []const u8 = "blah";
+    const slice2: []u8 = buf[0..4];
+
+    for (slice1, slice2) |a, *b| {
+        b.* = a;
+    }
+
+    try expect(slice2[0] == 'b');
+    try expect(slice2[1] == 'l');
+    try expect(slice2[2] == 'a');
+    try expect(slice2[3] == 'h');
+}
+
+test "raw pointer and slice" {
+    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+    var buf: [10]u8 = undefined;
+    const slice: []const u8 = "blah";
+    const ptr: [*]u8 = buf[0..4];
+
+    for (ptr, slice) |*a, b| {
+        a.* = b;
+    }
+
+    try expect(buf[0] == 'b');
+    try expect(buf[1] == 'l');
+    try expect(buf[2] == 'a');
+    try expect(buf[3] == 'h');
+}
test/cases/compile_errors/for_discard_unbounded.zig
@@ -0,0 +1,10 @@
+export fn a() void {
+    for (0..10, 10..) |i, _| {
+        _ = i;
+    }
+}
+// error
+// backend=stage2
+// target=native
+//
+// :2:27: error: discard of unbounded counter
test/cases/compile_errors/for_empty.zig
@@ -0,0 +1,11 @@
+export fn b() void {
+    for () |i| {
+        _ = i;
+    }
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:10: error: expected expression, found ')'
test/cases/compile_errors/for_extra_capture.zig
@@ -0,0 +1,12 @@
+export fn b() void {
+    for (0..10) |i, j| {
+        _ = i; _ = j;
+    }
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:21: error: extra capture in for loop
+// :2:21: note: run 'zig fmt' to upgrade your code automatically
test/cases/compile_errors/for_extra_condition.zig
@@ -0,0 +1,11 @@
+export fn a() void {
+    for (0..10, 10..20) |i| {
+        _ = i;
+    }
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:19: error: for input is not captured