| ... | @@ -1226,7 +1226,7 @@ fn awaitExpr( | ... | @@ -1226,7 +1226,7 @@ fn awaitExpr( |
| 1226 | try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}), | 1226 | try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}), |
| 1227 | }); | 1227 | }); |
| 1228 | } | 1228 | } |
| 1229 | const operand = try expr(gz, scope, .{ .rl = .none }, rhs_node); | 1229 | const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node); |
| 1230 | const result = if (gz.nosuspend_node != 0) | 1230 | const result = if (gz.nosuspend_node != 0) |
| 1231 | try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{ | 1231 | try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{ |
| 1232 | .node = gz.nodeIndexToRelative(node), | 1232 | .node = gz.nodeIndexToRelative(node), |
| ... | @@ -1248,7 +1248,7 @@ fn resumeExpr( | ... | @@ -1248,7 +1248,7 @@ fn resumeExpr( |
| 1248 | const tree = astgen.tree; | 1248 | const tree = astgen.tree; |
| 1249 | const node_datas = tree.nodes.items(.data); | 1249 | const node_datas = tree.nodes.items(.data); |
| 1250 | const rhs_node = node_datas[node].lhs; | 1250 | const rhs_node = node_datas[node].lhs; |
| 1251 | const operand = try expr(gz, scope, .{ .rl = .none }, rhs_node); | 1251 | const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node); |
| 1252 | const result = try gz.addUnNode(.@"resume", operand, node); | 1252 | const result = try gz.addUnNode(.@"resume", operand, node); |
| 1253 | return rvalue(gz, ri, result, node); | 1253 | return rvalue(gz, ri, result, node); |
| 1254 | } | 1254 | } |
| ... | @@ -2941,11 +2941,19 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v | ... | @@ -2941,11 +2941,19 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v |
| 2941 | const s = scope.cast(Scope.LocalPtr).?; | 2941 | const s = scope.cast(Scope.LocalPtr).?; |
| 2942 | if (s.used == 0 and s.discarded == 0) { | 2942 | if (s.used == 0 and s.discarded == 0) { |
| 2943 | try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)}); | 2943 | try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)}); |
| 2944 | } else if (s.used != 0 and s.discarded != 0) { | 2944 | } else { |
| 2945 | try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{ | 2945 | if (s.used != 0 and s.discarded != 0) { |
| 2946 | try gz.astgen.errNoteTok(s.used, "used here", .{}), | 2946 | try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{ |
| 2947 | }); | 2947 | try astgen.errNoteTok(s.used, "used here", .{}), |
| | 2948 | }); |
| | 2949 | } |
| | 2950 | if (s.id_cat == .@"local variable" and !s.used_as_lvalue) { |
| | 2951 | try astgen.appendErrorTokNotes(s.token_src, "local variable is never mutated", .{}, &.{ |
| | 2952 | try astgen.errNoteTok(s.token_src, "consider using 'const'", .{}), |
| | 2953 | }); |
| | 2954 | } |
| 2948 | } | 2955 | } |
| | 2956 | |
| 2949 | scope = s.parent; | 2957 | scope = s.parent; |
| 2950 | }, | 2958 | }, |
| 2951 | .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, | 2959 | .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, |
| ... | @@ -7579,7 +7587,10 @@ fn localVarRef( | ... | @@ -7579,7 +7587,10 @@ fn localVarRef( |
| 7579 | ); | 7587 | ); |
| 7580 | | 7588 | |
| 7581 | switch (ri.rl) { | 7589 | switch (ri.rl) { |
| 7582 | .ref, .ref_coerced_ty => return ptr_inst, | 7590 | .ref, .ref_coerced_ty => { |
| | 7591 | local_ptr.used_as_lvalue = true; |
| | 7592 | return ptr_inst; |
| | 7593 | }, |
| 7583 | else => { | 7594 | else => { |
| 7584 | const loaded = try gz.addUnNode(.load, ptr_inst, ident); | 7595 | const loaded = try gz.addUnNode(.load, ptr_inst, ident); |
| 7585 | return rvalueNoCoercePreRef(gz, ri, loaded, ident); | 7596 | return rvalueNoCoercePreRef(gz, ri, loaded, ident); |
| ... | @@ -10948,6 +10959,9 @@ const Scope = struct { | ... | @@ -10948,6 +10959,9 @@ const Scope = struct { |
| 10948 | /// Track the identifier where it is discarded, like this `_ = foo;`. | 10959 | /// Track the identifier where it is discarded, like this `_ = foo;`. |
| 10949 | /// 0 means never discarded. | 10960 | /// 0 means never discarded. |
| 10950 | discarded: Ast.TokenIndex = 0, | 10961 | discarded: Ast.TokenIndex = 0, |
| | 10962 | /// Whether this value is used as an lvalue after inititialization. |
| | 10963 | /// If not, we know it can be `const`, so will emit a compile error if it is `var`. |
| | 10964 | used_as_lvalue: bool = false, |
| 10951 | /// String table index. | 10965 | /// String table index. |
| 10952 | name: u32, | 10966 | name: u32, |
| 10953 | id_cat: IdCat, | 10967 | id_cat: IdCat, |