Commit 8b05205bb7

Andrew Kelley <andrew@ziglang.org>
2023-02-18 23:58:27
implement error for unbounded for loops
1 parent e89bfed
Changed files (5)
src
test
behavior
cases
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,
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.
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');
+}
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
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