authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-18 15:58:27-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-18 19:20:19-07:00
log8b05205bb71fca55569a9ff4cab89ec9e09640ba
tree1bcc329a4a2d79b35f6ab5b6e8e6fcf8bc986f72
parente89bfedd8d68a731cb227327a325e16fc7812df9

implement error for unbounded for loops


5 files changed, 69 insertions(+), 6 deletions(-)

src/AstGen.zig+4-5
...@@ -6394,11 +6394,13 @@ fn forExpr(...@@ -6394,11 +6394,13 @@ fn forExpr(
6394 }6394 }
6395 }6395 }
63966396
6397 if (!any_len_checks) {
6398 return astgen.failNode(node, "unbounded for loop", .{});
6399 }
6400
6397 // We use a dedicated ZIR instruction to assert the lengths to assist with6401 // We use a dedicated ZIR instruction to assert the lengths to assist with
6398 // nicer error reporting as well as fewer ZIR bytes emitted.6402 // nicer error reporting as well as fewer ZIR bytes emitted.
6399 const len: Zir.Inst.Ref = len: {6403 const len: Zir.Inst.Ref = len: {
6400 if (!any_len_checks) break :len .none;
6401
6402 const lens_len = @intCast(u32, lens.len);6404 const lens_len = @intCast(u32, lens.len);
6403 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).Struct.fields.len + lens_len);6405 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).Struct.fields.len + lens_len);
6404 const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{6406 const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{
...@@ -6424,9 +6426,6 @@ fn forExpr(...@@ -6424,9 +6426,6 @@ fn forExpr(
6424 defer cond_scope.unstack();6426 defer cond_scope.unstack();
64256427
6426 // Check the condition.6428 // Check the condition.
6427 if (!any_len_checks) {
6428 return astgen.failNode(node, "TODO: handle infinite for loop", .{});
6429 }
6430 const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{6429 const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{
6431 .lhs = index,6430 .lhs = index,
6432 .rhs = len,6431 .rhs = len,
src/Sema.zig+25-1
...@@ -3975,7 +3975,31 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -3975,7 +3975,31 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
3975 }3975 }
39763976
3977 if (len == .none) {3977 if (len == .none) {
3978 return sema.fail(block, src, "non-obvious infinite loop", .{});3978 const msg = msg: {
3979 const msg = try sema.errMsg(block, src, "unbounded for loop", .{});
3980 errdefer msg.destroy(gpa);
3981 for (args, 0..) |zir_arg, i_usize| {
3982 const i = @intCast(u32, i_usize);
3983 if (zir_arg == .none) continue;
3984 const object = try sema.resolveInst(zir_arg);
3985 const object_ty = sema.typeOf(object);
3986 // Each arg could be an indexable, or a range, in which case the length
3987 // is passed directly as an integer.
3988 switch (object_ty.zigTypeTag()) {
3989 .Int, .ComptimeInt => continue,
3990 else => {},
3991 }
3992 const arg_src: LazySrcLoc = .{ .for_input = .{
3993 .for_node_offset = inst_data.src_node,
3994 .input_index = i,
3995 } };
3996 try sema.errNote(block, arg_src, msg, "type '{}' has no upper bound", .{
3997 object_ty.fmt(sema.mod),
3998 });
3999 }
4000 break :msg msg;
4001 };
4002 return sema.failWithOwnedErrorMsg(msg);
3979 }4003 }
39804004
3981 // Now for the runtime checks.4005 // Now for the runtime checks.
test/behavior/for.zig+19
...@@ -378,3 +378,22 @@ test "raw pointer and slice" {...@@ -378,3 +378,22 @@ test "raw pointer and slice" {
378 try expect(buf[2] == 'a');378 try expect(buf[2] == 'a');
379 try expect(buf[3] == 'h');379 try expect(buf[3] == 'h');
380}380}
381
382test "raw pointer and counter" {
383 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
384 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
385 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
386 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
387
388 var buf: [10]u8 = undefined;
389 const ptr: [*]u8 = &buf;
390
391 for (ptr, 0..4) |*a, b| {
392 a.* = @intCast(u8, 'A' + b);
393 }
394
395 try expect(buf[0] == 'A');
396 try expect(buf[1] == 'B');
397 try expect(buf[2] == 'C');
398 try expect(buf[3] == 'D');
399}
test/cases/compile_errors/for.zig+10
...@@ -16,6 +16,13 @@ export fn c() void {...@@ -16,6 +16,13 @@ export fn c() void {
16 _ = byte;16 _ = byte;
17 }17 }
18}18}
19export fn d() void {
20 const x: [*]const u8 = "hello";
21 const y: [*]const u8 = "world";
22 for (x, 0.., y) |x1, x2, x3| {
23 _ = x1; _ = x2; _ = x3;
24 }
25}
1926
20// error27// error
21// backend=stage228// backend=stage2
...@@ -28,3 +35,6 @@ export fn c() void {...@@ -28,3 +35,6 @@ export fn c() void {
28// :9:14: note: for loop operand must be an array, slice, tuple, or vector35// :9:14: note: for loop operand must be an array, slice, tuple, or vector
29// :15:16: error: pointer capture of non pointer type '[10]u8'36// :15:16: error: pointer capture of non pointer type '[10]u8'
30// :15:10: note: consider using '&' here37// :15:10: note: consider using '&' here
38// :22:5: error: unbounded for loop
39// :22:10: note: type '[*]const u8' has no upper bound
40// :22:18: note: type '[*]const u8' has no upper bound
test/cases/compile_errors/for_unbounded.zig created+11
...@@ -0,0 +1,11 @@
1export fn b() void {
2 for (0..) |i| {
3 _ = i;
4 }
5}
6
7// error
8// backend=stage2
9// target=native
10//
11// :2:5: error: unbounded for loop