Commit 8b05205bb7
Changed files (5)
test
behavior
cases
compile_errors
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