authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-15 18:18:38+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-15 18:24:32+00:00
log827a96b1efbd1d143752ac15c58b7e2152af133c
treeff96ee7be8594ee0a8f3691319cd702bf5fdb3e3
parent64f1c27332839f5dba9463aa2be3644da0cd5008
signaturelock-open Commit is signed but in an unrecognized format.

compiler: fix missing "local variable is never mutated" error

This regressed back in https://github.com/ziglang/zig/pull/25154. I didn't get around to fixing it until now, so a few instances of the warning snuck into the repo over the past few months, which were fixed in the previous commit. The regression has not appeared in a tagged release though, so this is not a breaking change in 0.16.0. Resolves: https://codeberg.org/ziglang/zig/issues/31049

2 files changed, 89 insertions(+), 32 deletions(-)

lib/std/zig/AstGen.zig+75-32
...@@ -308,6 +308,9 @@ const ResultInfo = struct {...@@ -308,6 +308,9 @@ const ResultInfo = struct {
308 /// The expression must generate a pointer rather than a value, and the pointer will be coerced308 /// The expression must generate a pointer rather than a value, and the pointer will be coerced
309 /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type.309 /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type.
310 ref_coerced_ty: Zir.Inst.Ref,310 ref_coerced_ty: Zir.Inst.Ref,
311 /// Like `ref`, but the pointer will never be stored to, so local variables should not be
312 /// marked as possibly being mutated.
313 ref_const,
311 /// The expression must store its result into this typed pointer. The result instruction314 /// The expression must store its result into this typed pointer. The result instruction
312 /// from the expression must be ignored.315 /// from the expression must be ignored.
313 ptr: PtrResultLoc,316 ptr: PtrResultLoc,
...@@ -339,7 +342,7 @@ const ResultInfo = struct {...@@ -339,7 +342,7 @@ const ResultInfo = struct {
339 /// If the location does not have a known result type, returns `null`.342 /// If the location does not have a known result type, returns `null`.
340 fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref {343 fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref {
341 return switch (rl) {344 return switch (rl) {
342 .discard, .none, .ref, .inferred_ptr, .destructure => null,345 .discard, .none, .ref, .ref_const, .inferred_ptr, .destructure => null,
343 .ty, .coerced_ty => |ty_ref| ty_ref,346 .ty, .coerced_ty => |ty_ref| ty_ref,
344 .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node),347 .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node),
345 .ptr => |ptr| {348 .ptr => |ptr| {
...@@ -937,7 +940,11 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -937,7 +940,11 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
937 const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node);940 const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node);
938 _ = try gz.addUnNode(.validate_deref, lhs, node);941 _ = try gz.addUnNode(.validate_deref, lhs, node);
939 switch (ri.rl) {942 switch (ri.rl) {
940 .ref, .ref_coerced_ty => return lhs,943 .ref,
944 .ref_coerced_ty,
945 .ref_const,
946 => return lhs,
947
941 else => {948 else => {
942 const result = try gz.addUnNode(.load, lhs, node);949 const result = try gz.addUnNode(.load, lhs, node);
943 return rvalue(gz, ri, result, node);950 return rvalue(gz, ri, result, node);
...@@ -973,6 +980,14 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -973,6 +980,14 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
973980
974 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node);981 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node);
975 },982 },
983 .ref_const => {
984 const lhs = try expr(gz, scope, .{ .rl = .ref_const }, tree.nodeData(node).node_and_token[0]);
985
986 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
987 try emitDbgStmt(gz, cursor);
988
989 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node);
990 },
976 else => {991 else => {
977 const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node_and_token[0]);992 const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node_and_token[0]);
978993
...@@ -998,7 +1013,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -998,7 +1013,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
998 .field_name_start = str_index,1013 .field_name_start = str_index,
999 });1014 });
1000 switch (ri.rl) {1015 switch (ri.rl) {
1001 .discard, .none, .ref, .inferred_ptr, .destructure => unreachable, // no result type1016 .discard, .none, .ref, .ref_const, .inferred_ptr, .destructure => unreachable, // no result type
1002 .ty, .coerced_ty => return res, // `decl_literal` does the coercion for us1017 .ty, .coerced_ty => return res, // `decl_literal` does the coercion for us
1003 .ref_coerced_ty, .ptr => return rvalue(gz, ri, res, node),1018 .ref_coerced_ty, .ptr => return rvalue(gz, ri, res, node),
1004 }1019 }
...@@ -1030,7 +1045,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -1030,7 +1045,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
1030 return switchExpr(gz, scope, ri.br(), node, switch_full, .@"catch");1045 return switchExpr(gz, scope, ri.br(), node, switch_full, .@"catch");
1031 }1046 }
1032 switch (ri.rl) {1047 switch (ri.rl) {
1033 .ref, .ref_coerced_ty => return orelseCatchExpr(1048 .ref, .ref_const, .ref_coerced_ty => return orelseCatchExpr(
1034 gz,1049 gz,
1035 scope,1050 scope,
1036 ri,1051 ri,
...@@ -1053,7 +1068,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -1053,7 +1068,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
1053 }1068 }
1054 },1069 },
1055 .@"orelse" => switch (ri.rl) {1070 .@"orelse" => switch (ri.rl) {
1056 .ref, .ref_coerced_ty => return orelseCatchExpr(1071 .ref, .ref_const, .ref_coerced_ty => return orelseCatchExpr(
1057 gz,1072 gz,
1058 scope,1073 scope,
1059 ri,1074 ri,
...@@ -1511,7 +1526,7 @@ fn arrayInitExpr(...@@ -1511,7 +1526,7 @@ fn arrayInitExpr(
1511 }1526 }
1512 return .void_value;1527 return .void_value;
1513 },1528 },
1514 .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true),1529 .ref, .ref_const => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true),
1515 else => {1530 else => {
1516 const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false);1531 const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false);
1517 return rvalue(gz, ri, array_inst, node);1532 return rvalue(gz, ri, array_inst, node);
...@@ -1527,7 +1542,7 @@ fn arrayInitExpr(...@@ -1527,7 +1542,7 @@ fn arrayInitExpr(
1527 }1542 }
1528 return Zir.Inst.Ref.void_value;1543 return Zir.Inst.Ref.void_value;
1529 },1544 },
1530 .ref => {1545 .ref, .ref_const => {
1531 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);1546 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
1532 return gz.addUnTok(.ref, result, tree.firstToken(node));1547 return gz.addUnTok(.ref, result, tree.firstToken(node));
1533 },1548 },
...@@ -1701,7 +1716,7 @@ fn structInitExpr(...@@ -1701,7 +1716,7 @@ fn structInitExpr(
1701 const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node);1716 const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node);
1702 return rvalue(gz, ri, val, node);1717 return rvalue(gz, ri, val, node);
1703 },1718 },
1704 .none, .ref, .inferred_ptr => {1719 .none, .ref, .ref_const, .inferred_ptr => {
1705 return rvalue(gz, ri, .empty_tuple, node);1720 return rvalue(gz, ri, .empty_tuple, node);
1706 },1721 },
1707 .destructure => |destructure| {1722 .destructure => |destructure| {
...@@ -1817,7 +1832,7 @@ fn structInitExpr(...@@ -1817,7 +1832,7 @@ fn structInitExpr(
1817 const ty_inst = try typeExpr(gz, scope, type_expr);1832 const ty_inst = try typeExpr(gz, scope, type_expr);
1818 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);1833 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
1819 switch (ri.rl) {1834 switch (ri.rl) {
1820 .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true),1835 .ref, .ref_const => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true),
1821 else => {1836 else => {
1822 const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false);1837 const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false);
1823 return rvalue(gz, ri, struct_inst, node);1838 return rvalue(gz, ri, struct_inst, node);
...@@ -1834,7 +1849,7 @@ fn structInitExpr(...@@ -1834,7 +1849,7 @@ fn structInitExpr(
1834 }1849 }
1835 return .void_value;1850 return .void_value;
1836 },1851 },
1837 .ref => {1852 .ref, .ref_const => {
1838 const result = try structInitExprAnon(gz, scope, node, struct_init);1853 const result = try structInitExprAnon(gz, scope, node, struct_init);
1839 return gz.addUnTok(.ref, result, tree.firstToken(node));1854 return gz.addUnTok(.ref, result, tree.firstToken(node));
1840 },1855 },
...@@ -5832,6 +5847,7 @@ fn tryExpr(...@@ -5832,6 +5847,7 @@ fn tryExpr(
58325847
5833 const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) {5848 const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) {
5834 .ref, .ref_coerced_ty => .{ .ref, .try_ptr },5849 .ref, .ref_coerced_ty => .{ .ref, .try_ptr },
5850 .ref_const => .{ .ref_const, .try_ptr },
5835 else => .{ .none, .@"try" },5851 else => .{ .none, .@"try" },
5836 };5852 };
5837 const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr };5853 const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr };
...@@ -5856,7 +5872,7 @@ fn tryExpr(...@@ -5856,7 +5872,7 @@ fn tryExpr(
5856 defer else_scope.unstack();5872 defer else_scope.unstack();
58575873
5858 const err_tag = switch (ri.rl) {5874 const err_tag = switch (ri.rl) {
5859 .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr,5875 .ref, .ref_const, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr,
5860 else => Zir.Inst.Tag.err_union_code,5876 else => Zir.Inst.Tag.err_union_code,
5861 };5877 };
5862 const err_code = try else_scope.addUnNode(err_tag, operand, node);5878 const err_code = try else_scope.addUnNode(err_tag, operand, node);
...@@ -5867,7 +5883,7 @@ fn tryExpr(...@@ -5867,7 +5883,7 @@ fn tryExpr(
5867 try else_scope.setTryBody(try_inst, operand);5883 try else_scope.setTryBody(try_inst, operand);
5868 const result = try_inst.toRef();5884 const result = try_inst.toRef();
5869 switch (ri.rl) {5885 switch (ri.rl) {
5870 .ref, .ref_coerced_ty => return result,5886 .ref, .ref_const, .ref_coerced_ty => return result,
5871 else => return rvalue(parent_gz, ri, result, node),5887 else => return rvalue(parent_gz, ri, result, node),
5872 }5888 }
5873}5889}
...@@ -5909,6 +5925,7 @@ fn orelseCatchExpr(...@@ -5909,6 +5925,7 @@ fn orelseCatchExpr(
59095925
5910 const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) {5926 const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) {
5911 .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none },5927 .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none },
5928 .ref_const => .{ .rl = .ref_const, .ctx = if (do_err_trace) .error_handling_expr else .none },
5912 else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none },5929 else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none },
5913 };5930 };
5914 // This could be a pointer or value depending on the `operand_ri` parameter.5931 // This could be a pointer or value depending on the `operand_ri` parameter.
...@@ -5930,7 +5947,7 @@ fn orelseCatchExpr(...@@ -5930,7 +5947,7 @@ fn orelseCatchExpr(
5930 // This could be a pointer or value depending on `unwrap_op`.5947 // This could be a pointer or value depending on `unwrap_op`.
5931 const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);5948 const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
5932 const then_result = switch (ri.rl) {5949 const then_result = switch (ri.rl) {
5933 .ref, .ref_coerced_ty => unwrapped_payload,5950 .ref, .ref_const, .ref_coerced_ty => unwrapped_payload,
5934 else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node),5951 else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node),
5935 };5952 };
5936 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node);5953 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node);
...@@ -6026,8 +6043,9 @@ fn fieldAccess(...@@ -6026,8 +6043,9 @@ fn fieldAccess(
6026) InnerError!Zir.Inst.Ref {6043) InnerError!Zir.Inst.Ref {
6027 switch (ri.rl) {6044 switch (ri.rl) {
6028 .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node),6045 .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node),
6046 .ref_const => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref_const }, node),
6029 else => {6047 else => {
6030 const access = try addFieldAccess(.field_ptr_load, gz, scope, .{ .rl = .ref }, node);6048 const access = try addFieldAccess(.field_ptr_load, gz, scope, .{ .rl = .ref_const }, node);
6031 return rvalue(gz, ri, access, node);6049 return rvalue(gz, ri, access, node);
6032 },6050 },
6033 }6051 }
...@@ -6075,9 +6093,20 @@ fn arrayAccess(...@@ -6075,9 +6093,20 @@ fn arrayAccess(
60756093
6076 return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });6094 return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
6077 },6095 },
6096 .ref_const => {
6097 const lhs_node, const rhs_node = tree.nodeData(node).node_and_node;
6098 const lhs = try expr(gz, scope, .{ .rl = .ref_const }, lhs_node);
6099
6100 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
6101
6102 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, rhs_node);
6103 try emitDbgStmt(gz, cursor);
6104
6105 return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
6106 },
6078 else => {6107 else => {
6079 const lhs_node, const rhs_node = tree.nodeData(node).node_and_node;6108 const lhs_node, const rhs_node = tree.nodeData(node).node_and_node;
6080 const lhs = try expr(gz, scope, .{ .rl = .ref }, lhs_node);6109 const lhs = try expr(gz, scope, .{ .rl = .ref_const }, lhs_node);
60816110
6082 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);6111 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
60836112
...@@ -7092,11 +7121,15 @@ fn switchExpr(...@@ -7092,11 +7121,15 @@ fn switchExpr(
70927121
7093 const catch_or_if_node = if (needs_non_err_handling) node else undefined;7122 const catch_or_if_node = if (needs_non_err_handling) node else undefined;
7094 const do_err_trace = needs_non_err_handling and astgen.fn_block != null;7123 const do_err_trace = needs_non_err_handling and astgen.fn_block != null;
7095 const non_err_is_ref: bool = switch (non_err) {7124 const non_err_is_ref: enum { no, yes, yes_const } = switch (non_err) {
7096 .none, .peer_break_target => undefined,7125 .none, .peer_break_target => undefined,
7097 .@"catch" => ri.rl == .ref or ri.rl == .ref_coerced_ty,7126 .@"catch" => switch (ri.rl) {
7098 .@"if" => |if_full| if_full.payload_token != null and7127 .ref, .ref_coerced_ty => .yes,
7099 tree.tokenTag(if_full.payload_token.?) == .asterisk,7128 .ref_const => .yes_const,
7129 else => .no,
7130 },
7131 .@"if" => |if_full| if (if_full.payload_token != null and
7132 tree.tokenTag(if_full.payload_token.?) == .asterisk) .yes else .no,
7100 };7133 };
71017134
7102 if (switch_full.label_token) |label_token| {7135 if (switch_full.label_token) |label_token| {
...@@ -7285,8 +7318,12 @@ fn switchExpr(...@@ -7285,8 +7318,12 @@ fn switchExpr(
7285 block_scope.instructions_top = GenZir.unstacked_top;7318 block_scope.instructions_top = GenZir.unstacked_top;
72867319
7287 const operand_ri: ResultInfo = .{7320 const operand_ri: ResultInfo = .{
7288 .rl = if (any_payload_is_ref or7321 .rl = loc: {
7289 (needs_non_err_handling and non_err_is_ref)) .ref else .none,7322 if (any_payload_is_ref) break :loc .ref;
7323 if (needs_non_err_handling and non_err_is_ref == .yes) break :loc .ref;
7324 if (needs_non_err_handling and non_err_is_ref == .yes_const) break :loc .ref_const;
7325 break :loc .none;
7326 },
7290 .ctx = if (do_err_trace) .error_handling_expr else .none,7327 .ctx = if (do_err_trace) .error_handling_expr else .none,
7291 };7328 };
72927329
...@@ -7454,10 +7491,10 @@ fn switchExpr(...@@ -7454,10 +7491,10 @@ fn switchExpr(
7454 .@"catch" => {7491 .@"catch" => {
7455 // We always effectively capture the error union payload; we use7492 // We always effectively capture the error union payload; we use
7456 // it to `break` from the entire `switch_block_err_union`.7493 // it to `break` from the entire `switch_block_err_union`.
7457 non_err_capture = if (non_err_is_ref) .by_ref else .by_val;7494 non_err_capture = if (non_err_is_ref != .no) .by_ref else .by_val;
74587495
7459 const then_result = switch (ri.rl) {7496 const then_result = switch (ri.rl) {
7460 .ref, .ref_coerced_ty => non_err_payload_inst.toRef(),7497 .ref, .ref_const, .ref_coerced_ty => non_err_payload_inst.toRef(),
7461 else => try rvalue(7498 else => try rvalue(
7462 &scratch_scope,7499 &scratch_scope,
7463 block_scope.break_result_info,7500 block_scope.break_result_info,
...@@ -7478,13 +7515,13 @@ fn switchExpr(...@@ -7478,13 +7515,13 @@ fn switchExpr(
7478 const then_node = if_full.ast.then_expr;7515 const then_node = if_full.ast.then_expr;
7479 const then_sub_scope: *Scope = scope: {7516 const then_sub_scope: *Scope = scope: {
7480 if (if_full.payload_token) |payload_token| {7517 if (if_full.payload_token) |payload_token| {
7481 const ident_token = payload_token + @intFromBool(non_err_is_ref);7518 const ident_token = payload_token + @intFromBool(non_err_is_ref != .no);
7482 const ident_name = try astgen.identAsString(ident_token);7519 const ident_name = try astgen.identAsString(ident_token);
7483 const ident_name_str = tree.tokenSlice(ident_token);7520 const ident_name_str = tree.tokenSlice(ident_token);
7484 if (mem.eql(u8, "_", ident_name_str)) {7521 if (mem.eql(u8, "_", ident_name_str)) {
7485 break :scope &scratch_scope.base;7522 break :scope &scratch_scope.base;
7486 }7523 }
7487 non_err_capture = if (non_err_is_ref) .by_ref else .by_val;7524 non_err_capture = if (non_err_is_ref != .no) .by_ref else .by_val;
7488 try astgen.detectLocalShadowing(&scratch_scope.base, ident_name, ident_token, ident_name_str, .capture);7525 try astgen.detectLocalShadowing(&scratch_scope.base, ident_name, ident_token, ident_name_str, .capture);
7489 payload_val_scope = .{7526 payload_val_scope = .{
7490 .parent = &scratch_scope.base,7527 .parent = &scratch_scope.base,
...@@ -7522,7 +7559,7 @@ fn switchExpr(...@@ -7522,7 +7559,7 @@ fn switchExpr(
7522 non_err_info = .{7559 non_err_info = .{
7523 .body_len = @intCast(body_len),7560 .body_len = @intCast(body_len),
7524 .capture = non_err_capture,7561 .capture = non_err_capture,
7525 .operand_is_ref = non_err_is_ref,7562 .operand_is_ref = non_err_is_ref != .no,
7526 };7563 };
7527 }7564 }
75287565
...@@ -8245,7 +8282,7 @@ fn localVarRef(...@@ -8245,7 +8282,7 @@ fn localVarRef(
8245 }8282 }
82468283
8247 switch (ri.rl) {8284 switch (ri.rl) {
8248 .ref, .ref_coerced_ty => {8285 .ref, .ref_const, .ref_coerced_ty => {
8249 const ptr_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(8286 const ptr_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
8250 gz,8287 gz,
8251 ident,8288 ident,
...@@ -8254,7 +8291,7 @@ fn localVarRef(...@@ -8254,7 +8291,7 @@ fn localVarRef(
8254 .{ .token = local_ptr.token_src },8291 .{ .token = local_ptr.token_src },
8255 name_str_index,8292 name_str_index,
8256 ) else local_ptr.ptr;8293 ) else local_ptr.ptr;
8257 local_ptr.used_as_lvalue = true;8294 if (ri.rl != .ref_const) local_ptr.used_as_lvalue = true;
8258 return ptr_inst;8295 return ptr_inst;
8259 },8296 },
8260 else => {8297 else => {
...@@ -8303,7 +8340,7 @@ fn localVarRef(...@@ -8303,7 +8340,7 @@ fn localVarRef(
83038340
8304 if (found_namespaces_out > 0 and found_needs_tunnel) {8341 if (found_namespaces_out > 0 and found_needs_tunnel) {
8305 switch (ri.rl) {8342 switch (ri.rl) {
8306 .ref, .ref_coerced_ty => return tunnelThroughClosure(8343 .ref, .ref_const, .ref_coerced_ty => return tunnelThroughClosure(
8307 gz,8344 gz,
8308 ident,8345 ident,
8309 found_namespaces_out,8346 found_namespaces_out,
...@@ -8326,7 +8363,7 @@ fn localVarRef(...@@ -8326,7 +8363,7 @@ fn localVarRef(
8326 }8363 }
83278364
8328 switch (ri.rl) {8365 switch (ri.rl) {
8329 .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token),8366 .ref, .ref_const, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
8330 else => {8367 else => {
8331 const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);8368 const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
8332 return rvalueNoCoercePreRef(gz, ri, result, ident);8369 return rvalueNoCoercePreRef(gz, ri, result, ident);
...@@ -9108,9 +9145,15 @@ fn builtinCall(...@@ -9108,9 +9145,15 @@ fn builtinCall(
9108 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),9145 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
9109 });9146 });
9110 },9147 },
9148 .ref_const => {
9149 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
9150 .lhs = try expr(gz, scope, .{ .rl = .ref_const }, params[0]),
9151 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
9152 });
9153 },
9111 else => {9154 else => {
9112 const result = try gz.addPlNode(.field_ptr_named_load, node, Zir.Inst.FieldNamed{9155 const result = try gz.addPlNode(.field_ptr_named_load, node, Zir.Inst.FieldNamed{
9113 .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),9156 .lhs = try expr(gz, scope, .{ .rl = .ref_const }, params[0]),
9114 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),9157 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
9115 });9158 });
9116 return rvalue(gz, ri, result, node);9159 return rvalue(gz, ri, result, node);
...@@ -10530,7 +10573,7 @@ fn rvalueInner(...@@ -10530,7 +10573,7 @@ fn rvalueInner(
10530 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);10573 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
10531 return .void_value;10574 return .void_value;
10532 },10575 },
10533 .ref, .ref_coerced_ty => {10576 .ref, .ref_const, .ref_coerced_ty => {
10534 const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: {10577 const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: {
10535 const ptr_ty = ri.rl.ref_coerced_ty;10578 const ptr_ty = ri.rl.ref_coerced_ty;
10536 break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{10579 break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{
test/cases/compile_errors/var_never_mutated.zig+14
...@@ -18,6 +18,16 @@ fn entry2() void {...@@ -18,6 +18,16 @@ fn entry2() void {
1818
19fn foo(_: u32) void {}19fn foo(_: u32) void {}
2020
21fn entry3() void {
22 var a: [1]u8 = .{0};
23 _ = a[0];
24}
25
26fn entry4() void {
27 var s: struct { a: u8 } = .{ .a = 0 };
28 _ = s.a;
29}
30
21// error31// error
22//32//
23// :2:9: error: local variable is never mutated33// :2:9: error: local variable is never mutated
...@@ -26,3 +36,7 @@ fn foo(_: u32) void {}...@@ -26,3 +36,7 @@ fn foo(_: u32) void {}
26// :9:9: note: consider using 'const'36// :9:9: note: consider using 'const'
27// :15:9: error: local variable is never mutated37// :15:9: error: local variable is never mutated
28// :15:9: note: consider using 'const'38// :15:9: note: consider using 'const'
39// :22:9: error: local variable is never mutated
40// :22:9: note: consider using 'const'
41// :27:9: error: local variable is never mutated
42// :27:9: note: consider using 'const'