From 8b05205bb71fca55569a9ff4cab89ec9e09640ba Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 18 Feb 2023 15:58:27 -0700 Subject: [PATCH] implement error for unbounded for loops --- src/AstGen.zig | 9 ++++--- src/Sema.zig | 26 ++++++++++++++++++++- test/behavior/for.zig | 19 +++++++++++++++ test/cases/compile_errors/for.zig | 10 ++++++++ test/cases/compile_errors/for_unbounded.zig | 11 +++++++++ 5 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 test/cases/compile_errors/for_unbounded.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 866dce02e5..bd1bba168b 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -6394,11 +6394,13 @@ fn forExpr( } } + if (!any_len_checks) { + return astgen.failNode(node, "unbounded for loop", .{}); + } + // We use a dedicated ZIR instruction to assert the lengths to assist with // nicer error reporting as well as fewer ZIR bytes emitted. const len: Zir.Inst.Ref = len: { - if (!any_len_checks) break :len .none; - const lens_len = @intCast(u32, lens.len); try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).Struct.fields.len + lens_len); const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{ @@ -6424,9 +6426,6 @@ fn forExpr( defer cond_scope.unstack(); // Check the condition. - if (!any_len_checks) { - return astgen.failNode(node, "TODO: handle infinite for loop", .{}); - } const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{ .lhs = index, .rhs = len, diff --git a/src/Sema.zig b/src/Sema.zig index 5fa754d6cf..84185f649c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3975,7 +3975,31 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } if (len == .none) { - return sema.fail(block, src, "non-obvious infinite loop", .{}); + const msg = msg: { + const msg = try sema.errMsg(block, src, "unbounded for loop", .{}); + errdefer msg.destroy(gpa); + for (args, 0..) |zir_arg, i_usize| { + const i = @intCast(u32, i_usize); + if (zir_arg == .none) continue; + const object = try sema.resolveInst(zir_arg); + const object_ty = sema.typeOf(object); + // Each arg could be an indexable, or a range, in which case the length + // is passed directly as an integer. + switch (object_ty.zigTypeTag()) { + .Int, .ComptimeInt => continue, + else => {}, + } + const arg_src: LazySrcLoc = .{ .for_input = .{ + .for_node_offset = inst_data.src_node, + .input_index = i, + } }; + try sema.errNote(block, arg_src, msg, "type '{}' has no upper bound", .{ + object_ty.fmt(sema.mod), + }); + } + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); } // Now for the runtime checks. diff --git a/test/behavior/for.zig b/test/behavior/for.zig index 20d43c6ff2..0c5ab392f4 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -378,3 +378,22 @@ test "raw pointer and slice" { try expect(buf[2] == 'a'); try expect(buf[3] == 'h'); } + +test "raw pointer and counter" { + 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 ptr: [*]u8 = &buf; + + for (ptr, 0..4) |*a, b| { + a.* = @intCast(u8, 'A' + b); + } + + try expect(buf[0] == 'A'); + try expect(buf[1] == 'B'); + try expect(buf[2] == 'C'); + try expect(buf[3] == 'D'); +} diff --git a/test/cases/compile_errors/for.zig b/test/cases/compile_errors/for.zig index dff46af085..5bd3aa0c64 100644 --- a/test/cases/compile_errors/for.zig +++ b/test/cases/compile_errors/for.zig @@ -16,6 +16,13 @@ export fn c() void { _ = byte; } } +export fn d() void { + const x: [*]const u8 = "hello"; + const y: [*]const u8 = "world"; + for (x, 0.., y) |x1, x2, x3| { + _ = x1; _ = x2; _ = x3; + } +} // error // backend=stage2 @@ -28,3 +35,6 @@ export fn c() void { // :9:14: note: for loop operand must be an array, slice, tuple, or vector // :15:16: error: pointer capture of non pointer type '[10]u8' // :15:10: note: consider using '&' here +// :22:5: error: unbounded for loop +// :22:10: note: type '[*]const u8' has no upper bound +// :22:18: note: type '[*]const u8' has no upper bound diff --git a/test/cases/compile_errors/for_unbounded.zig b/test/cases/compile_errors/for_unbounded.zig new file mode 100644 index 0000000000..5d05b1061f --- /dev/null +++ b/test/cases/compile_errors/for_unbounded.zig @@ -0,0 +1,11 @@ +export fn b() void { + for (0..) |i| { + _ = i; + } +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: unbounded for loop