From 22965e6fcbafbcba207a6da8eb493af2cf7ef924 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 18 Feb 2023 12:26:22 -0700 Subject: [PATCH] Sema: improve error message for mismatched for loop lengths --- src/Module.zig | 16 ++++++++++++++++ src/Sema.zig | 27 +++++++++++++++++++++++---- test/cases/compile_errors/for.zig | 13 +++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 test/cases/compile_errors/for.zig diff --git a/src/Module.zig b/src/Module.zig index 4feb04abdd..377ccd2441 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2462,6 +2462,13 @@ pub const SrcLoc = struct { }; return nodeToSpan(tree, src_node); }, + .for_input => |for_input| { + const tree = try src_loc.file_scope.getTree(gpa); + const node = src_loc.declRelativeToNodeIndex(for_input.for_node_offset); + const for_full = tree.fullFor(node).?; + const src_node = for_full.ast.inputs[for_input.input_index]; + return nodeToSpan(tree, src_node); + }, .node_offset_bin_lhs => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); const node = src_loc.declRelativeToNodeIndex(node_off); @@ -3114,6 +3121,14 @@ pub const LazySrcLoc = union(enum) { /// The source location points to the RHS of an assignment. /// The Decl is determined contextually. node_offset_store_operand: i32, + /// The source location points to a for loop input. + /// The Decl is determined contextually. + for_input: struct { + /// Points to the for loop AST node. + for_node_offset: i32, + /// Picks one of the inputs from the condition. + input_index: u32, + }, pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease; @@ -3200,6 +3215,7 @@ pub const LazySrcLoc = union(enum) { .node_offset_init_ty, .node_offset_store_ptr, .node_offset_store_operand, + .for_input, => .{ .file_scope = decl.getFileScope(), .parent_decl_node = decl.src_node, diff --git a/src/Sema.zig b/src/Sema.zig index 980aee720b..5a185a709c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3910,14 +3910,15 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. var len: Air.Inst.Ref = .none; var len_val: ?Value = null; - var len_idx: usize = undefined; + var len_idx: u32 = undefined; var any_runtime = false; const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, args.len); defer gpa.free(runtime_arg_lens); // First pass to look for comptime values. - for (args, 0..) |zir_arg, i| { + for (args, 0..) |zir_arg, i_usize| { + const i = @intCast(u32, i_usize); runtime_arg_lens[i] = .none; if (zir_arg == .none) continue; const object = try sema.resolveInst(zir_arg); @@ -3941,8 +3942,26 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| { if (len_val) |v| { if (!(try sema.valuesEqual(arg_val, v, Type.usize))) { - // TODO error notes for each arg stating the differing values - return sema.fail(block, src, "non-matching for loop lengths", .{}); + const msg = msg: { + const msg = try sema.errMsg(block, src, "non-matching for loop lengths", .{}); + errdefer msg.destroy(gpa); + const a_src: LazySrcLoc = .{ .for_input = .{ + .for_node_offset = inst_data.src_node, + .input_index = len_idx, + } }; + const b_src: LazySrcLoc = .{ .for_input = .{ + .for_node_offset = inst_data.src_node, + .input_index = i, + } }; + try sema.errNote(block, a_src, msg, "length {} here", .{ + v.fmtValue(Type.usize, sema.mod), + }); + try sema.errNote(block, b_src, msg, "length {} here", .{ + arg_val.fmtValue(Type.usize, sema.mod), + }); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); } } else { len = arg_len; diff --git a/test/cases/compile_errors/for.zig b/test/cases/compile_errors/for.zig new file mode 100644 index 0000000000..999782c991 --- /dev/null +++ b/test/cases/compile_errors/for.zig @@ -0,0 +1,13 @@ +export fn a() void { + for (0..10, 10..21) |i, j| { + _ = i; _ = j; + } +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: non-matching for loop lengths +// :2:11: note: length 10 here +// :2:19: note: length 11 here