implement error for unbounded for loops

This commit is contained in:
Andrew Kelley 2023-02-18 15:58:27 -07:00
parent e89bfedd8d
commit 8b05205bb7
5 changed files with 69 additions and 6 deletions

View File

@ -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,

View File

@ -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.

View File

@ -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');
}

View File

@ -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

View File

@ -0,0 +1,11 @@
export fn b() void {
for (0..) |i| {
_ = i;
}
}
// error
// backend=stage2
// target=native
//
// :2:5: error: unbounded for loop