Commit 4dd958d585

Andrew Kelley <andrew@ziglang.org>
2023-02-18 22:10:56
improve error message for byref capture of byval array
1 parent 601db39
Changed files (4)
lib
std
crypto
src
test
cases
compile_errors
lib/std/crypto/aes/soft.zig
@@ -420,7 +420,7 @@ const powx = init: {
     var array: [16]u8 = undefined;
 
     var value = 1;
-    for (array) |*power| {
+    for (&array) |*power| {
         power.* = value;
         value = mul(value, 2);
     }
src/Module.zig
@@ -2469,6 +2469,48 @@ pub const SrcLoc = struct {
                 const src_node = for_full.ast.inputs[for_input.input_index];
                 return nodeToSpan(tree, src_node);
             },
+            .for_capture_from_input => |node_off| {
+                const tree = try src_loc.file_scope.getTree(gpa);
+                const token_tags = tree.tokens.items(.tag);
+                const input_node = src_loc.declRelativeToNodeIndex(node_off);
+                // We have to actually linear scan the whole AST to find the for loop
+                // that contains this input.
+                const node_tags = tree.nodes.items(.tag);
+                for (node_tags, 0..) |node_tag, node_usize| {
+                    const node = @intCast(Ast.Node.Index, node_usize);
+                    switch (node_tag) {
+                        .for_simple, .@"for" => {
+                            const for_full = tree.fullFor(node).?;
+                            for (for_full.ast.inputs, 0..) |input, input_index| {
+                                if (input_node == input) {
+                                    var count = input_index;
+                                    var tok = for_full.payload_token;
+                                    while (true) {
+                                        switch (token_tags[tok]) {
+                                            .comma => {
+                                                count -= 1;
+                                                tok += 1;
+                                            },
+                                            .identifier => {
+                                                if (count == 0)
+                                                    return tokensToSpan(tree, tok, tok + 1, tok);
+                                                tok += 1;
+                                            },
+                                            .asterisk => {
+                                                if (count == 0)
+                                                    return tokensToSpan(tree, tok, tok + 2, tok);
+                                                tok += 1;
+                                            },
+                                            else => unreachable,
+                                        }
+                                    }
+                                }
+                            }
+                        },
+                        else => continue,
+                    }
+                } else unreachable;
+            },
             .node_offset_bin_lhs => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
                 const node = src_loc.declRelativeToNodeIndex(node_off);
@@ -3129,6 +3171,12 @@ pub const LazySrcLoc = union(enum) {
         /// Picks one of the inputs from the condition.
         input_index: u32,
     },
+    /// The source location points to one of the captures of a for loop, found
+    /// by taking this AST node index offset from the containing
+    /// Decl AST node, which points to one of the input nodes of a for loop.
+    /// Next, navigate to the corresponding capture.
+    /// The Decl is determined contextually.
+    for_capture_from_input: i32,
 
     pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease;
 
@@ -3216,6 +3264,7 @@ pub const LazySrcLoc = union(enum) {
             .node_offset_store_ptr,
             .node_offset_store_operand,
             .for_input,
+            .for_capture_from_input,
             => .{
                 .file_scope = decl.getFileScope(),
                 .parent_decl_node = decl.src_node,
src/Sema.zig
@@ -9716,6 +9716,21 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const array_ptr = try sema.resolveInst(extra.lhs);
     const elem_index = try sema.resolveInst(extra.rhs);
+    const indexable_ty = sema.typeOf(array_ptr);
+    if (indexable_ty.zigTypeTag() != .Pointer) {
+        const capture_src: LazySrcLoc = .{ .for_capture_from_input = inst_data.src_node };
+        const msg = msg: {
+            const msg = try sema.errMsg(block, capture_src, "pointer capture of non pointer type '{}'", .{
+                indexable_ty.fmt(sema.mod),
+            });
+            errdefer msg.destroy(sema.gpa);
+            if (indexable_ty.zigTypeTag() == .Array) {
+                try sema.errNote(block, src, msg, "consider using '&' here", .{});
+            }
+            break :msg msg;
+        };
+        return sema.failWithOwnedErrorMsg(msg);
+    }
     return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false);
 }
 
@@ -24195,12 +24210,7 @@ fn elemPtrOneLayerOnly(
                 },
             }
         },
-        else => {
-            // TODO add note pointing at corresponding for loop input and suggest using '&'
-            return sema.fail(block, indexable_src, "pointer capture of non pointer type '{}'", .{
-                indexable_ty.fmt(sema.mod),
-            });
-        },
+        else => unreachable,
     }
 }
 
test/cases/compile_errors/for.zig
@@ -10,6 +10,12 @@ export fn b() void {
         _ = i; _ = j;
     }
 }
+export fn c() void {
+    var buf: [10]u8 = undefined;
+    for (buf) |*byte| {
+        _ = byte;
+    }
+}
 
 // error
 // backend=stage2
@@ -20,3 +26,5 @@ export fn b() void {
 // :2:19: note: length 11 here
 // :9:14: error: type 'bool' does not support indexing
 // :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