Commit 22965e6fcb

Andrew Kelley <andrew@ziglang.org>
2023-02-18 20:26:22
Sema: improve error message for mismatched for loop lengths
1 parent b13745a
Changed files (3)
src
test
cases
compile_errors
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,
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;
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