From bcb72401d3cf01c190a346af9c9d8eec4a334b45 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 18 Feb 2023 13:04:27 -0700 Subject: [PATCH] AstGen: add error for discard of unbounded counter --- src/AstGen.zig | 7 +- test/behavior/for.zig | 100 ++++++++++++++++++ .../compile_errors/for_discard_unbounded.zig | 10 ++ test/cases/compile_errors/for_empty.zig | 11 ++ .../compile_errors/for_extra_capture.zig | 12 +++ .../compile_errors/for_extra_condition.zig | 11 ++ 6 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/for_discard_unbounded.zig create mode 100644 test/cases/compile_errors/for_empty.zig create mode 100644 test/cases/compile_errors/for_extra_capture.zig create mode 100644 test/cases/compile_errors/for_extra_condition.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index b4fda8e274..866dce02e5 100644 --- a/src/AstGen.zig +++ b/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 diff --git a/test/behavior/for.zig b/test/behavior/for.zig index 67ea5df808..e3c4a8dcc0 100644 --- a/test/behavior/for.zig +++ b/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'); +} diff --git a/test/cases/compile_errors/for_discard_unbounded.zig b/test/cases/compile_errors/for_discard_unbounded.zig new file mode 100644 index 0000000000..93434d0c21 --- /dev/null +++ b/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 diff --git a/test/cases/compile_errors/for_empty.zig b/test/cases/compile_errors/for_empty.zig new file mode 100644 index 0000000000..a4cb5b3c4e --- /dev/null +++ b/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 ')' diff --git a/test/cases/compile_errors/for_extra_capture.zig b/test/cases/compile_errors/for_extra_capture.zig new file mode 100644 index 0000000000..a137b57d51 --- /dev/null +++ b/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 diff --git a/test/cases/compile_errors/for_extra_condition.zig b/test/cases/compile_errors/for_extra_condition.zig new file mode 100644 index 0000000000..11c013acee --- /dev/null +++ b/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