Commit baabc6013e
Changed files (2)
src
translate_c
src/translate_c/ast.zig
@@ -1625,13 +1625,18 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
});
const main_token = try c.addToken(.equal, "=");
if (payload.value.tag() == .identifier) {
- // Render as `_ = @TypeOf(foo);` to avoid tripping "pointless discard" error.
+ // Render as `_ = &foo;` to avoid tripping "pointless discard" and "local variable never mutated" errors.
+ var addr_of_pl: Payload.UnOp = .{
+ .base = .{ .tag = .address_of },
+ .data = payload.value,
+ };
+ const addr_of: Node = .{ .ptr_otherwise = &addr_of_pl.base };
return c.addNode(.{
.tag = .assign,
.main_token = main_token,
.data = .{
.lhs = lhs,
- .rhs = try renderBuiltinCall(c, "@TypeOf", &.{payload.value}),
+ .rhs = try renderNode(c, addr_of),
},
});
} else {
src/AstGen.zig
@@ -1226,7 +1226,7 @@ fn awaitExpr(
try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}),
});
}
- const operand = try expr(gz, scope, .{ .rl = .none }, rhs_node);
+ const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
const result = if (gz.nosuspend_node != 0)
try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
@@ -1248,7 +1248,7 @@ fn resumeExpr(
const tree = astgen.tree;
const node_datas = tree.nodes.items(.data);
const rhs_node = node_datas[node].lhs;
- const operand = try expr(gz, scope, .{ .rl = .none }, rhs_node);
+ const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
const result = try gz.addUnNode(.@"resume", operand, node);
return rvalue(gz, ri, result, node);
}
@@ -2941,11 +2941,19 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v
const s = scope.cast(Scope.LocalPtr).?;
if (s.used == 0 and s.discarded == 0) {
try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
- } else if (s.used != 0 and s.discarded != 0) {
- try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
- try gz.astgen.errNoteTok(s.used, "used here", .{}),
- });
+ } else {
+ if (s.used != 0 and s.discarded != 0) {
+ try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
+ try astgen.errNoteTok(s.used, "used here", .{}),
+ });
+ }
+ if (s.id_cat == .@"local variable" and !s.used_as_lvalue) {
+ try astgen.appendErrorTokNotes(s.token_src, "local variable is never mutated", .{}, &.{
+ try astgen.errNoteTok(s.token_src, "consider using 'const'", .{}),
+ });
+ }
}
+
scope = s.parent;
},
.defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
@@ -7579,7 +7587,10 @@ fn localVarRef(
);
switch (ri.rl) {
- .ref, .ref_coerced_ty => return ptr_inst,
+ .ref, .ref_coerced_ty => {
+ local_ptr.used_as_lvalue = true;
+ return ptr_inst;
+ },
else => {
const loaded = try gz.addUnNode(.load, ptr_inst, ident);
return rvalueNoCoercePreRef(gz, ri, loaded, ident);
@@ -10948,6 +10959,9 @@ const Scope = struct {
/// Track the identifier where it is discarded, like this `_ = foo;`.
/// 0 means never discarded.
discarded: Ast.TokenIndex = 0,
+ /// Whether this value is used as an lvalue after inititialization.
+ /// If not, we know it can be `const`, so will emit a compile error if it is `var`.
+ used_as_lvalue: bool = false,
/// String table index.
name: u32,
id_cat: IdCat,