| author | |
| committer | |
| log | 8b05205bb71fca55569a9ff4cab89ec9e09640ba |
| tree | 1bcc329a4a2d79b35f6ab5b6e8e6fcf8bc986f72 |
| parent | e89bfedd8d68a731cb227327a325e16fc7812df9 |
5 files changed, 69 insertions(+), 6 deletions(-)
src/AstGen.zig+4-5| ... | ... | @@ -6394,11 +6394,13 @@ fn forExpr( |
| 6394 | 6394 | } |
| 6395 | 6395 | } |
| 6396 | 6396 | |
| 6397 | if (!any_len_checks) { | |
| 6398 | return astgen.failNode(node, "unbounded for loop", .{}); | |
| 6399 | } | |
| 6400 | ||
| 6397 | 6401 | // We use a dedicated ZIR instruction to assert the lengths to assist with |
| 6398 | 6402 | // nicer error reporting as well as fewer ZIR bytes emitted. |
| 6399 | 6403 | const len: Zir.Inst.Ref = len: { |
| 6400 | if (!any_len_checks) break :len .none; | |
| 6401 | ||
| 6402 | 6404 | const lens_len = @intCast(u32, lens.len); |
| 6403 | 6405 | try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).Struct.fields.len + lens_len); |
| 6404 | 6406 | const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{ |
| ... | ... | @@ -6424,9 +6426,6 @@ fn forExpr( |
| 6424 | 6426 | defer cond_scope.unstack(); |
| 6425 | 6427 | |
| 6426 | 6428 | // Check the condition. |
| 6427 | if (!any_len_checks) { | |
| 6428 | return astgen.failNode(node, "TODO: handle infinite for loop", .{}); | |
| 6429 | } | |
| 6430 | 6429 | const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{ |
| 6431 | 6430 | .lhs = index, |
| 6432 | 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 | 3975 | } |
| 3976 | 3976 | |
| 3977 | 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 | } |
| 3980 | 4004 | |
| 3981 | 4005 | // Now for the runtime checks. |
test/behavior/for.zig+19| ... | ... | @@ -378,3 +378,22 @@ test "raw pointer and slice" { |
| 378 | 378 | try expect(buf[2] == 'a'); |
| 379 | 379 | try expect(buf[3] == 'h'); |
| 380 | 380 | } |
| 381 | ||
| 382 | test "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 | 16 | _ = byte; |
| 17 | 17 | } |
| 18 | 18 | } |
| 19 | export 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 | } | |
| 19 | 26 | |
| 20 | 27 | // error |
| 21 | 28 | // backend=stage2 |
| ... | ... | @@ -28,3 +35,6 @@ export fn c() void { |
| 28 | 35 | // :9:14: note: for loop operand must be an array, slice, tuple, or vector |
| 29 | 36 | // :15:16: error: pointer capture of non pointer type '[10]u8' |
| 30 | 37 | // :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 @@ |
| 1 | export 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 |