Skip to content

Commit 71f4658

Browse files
committed
Auto merge of #32048 - bluss:overloaded-assign-op, r=eddyb
Do not trigger unused_assignments for overloaded AssignOps If `v` were a type with some kind of indirection, so that `v += 1` would have an effect even if `v` were not used anymore, the unused_assignments lint would mark a false positive. This exempts overloaded (non-primitive) assign ops from being treated as assignments (they are method calls). The previous compile-fail tests that ensure x += 1 can trigger for primitive types continue to pass. Added a representative test for the "view" indirection. Fixes #31895
2 parents 80922a7 + cfe4efd commit 71f4658

File tree

3 files changed

+78
-6
lines changed

3 files changed

+78
-6
lines changed

src/librustc/middle/liveness.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -1086,11 +1086,17 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
10861086
}
10871087

10881088
hir::ExprAssignOp(_, ref l, ref r) => {
1089-
// see comment on lvalues in
1090-
// propagate_through_lvalue_components()
1091-
let succ = self.write_lvalue(&l, succ, ACC_WRITE|ACC_READ);
1092-
let succ = self.propagate_through_expr(&r, succ);
1093-
self.propagate_through_lvalue_components(&l, succ)
1089+
// an overloaded assign op is like a method call
1090+
if self.ir.tcx.is_method_call(expr.id) {
1091+
let succ = self.propagate_through_expr(&l, succ);
1092+
self.propagate_through_expr(&r, succ)
1093+
} else {
1094+
// see comment on lvalues in
1095+
// propagate_through_lvalue_components()
1096+
let succ = self.write_lvalue(&l, succ, ACC_WRITE|ACC_READ);
1097+
let succ = self.propagate_through_expr(&r, succ);
1098+
self.propagate_through_lvalue_components(&l, succ)
1099+
}
10941100
}
10951101

10961102
// Uninteresting cases: just propagate in rev exec order
@@ -1410,7 +1416,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
14101416
}
14111417

14121418
hir::ExprAssignOp(_, ref l, _) => {
1413-
this.check_lvalue(&l);
1419+
if !this.ir.tcx.is_method_call(expr.id) {
1420+
this.check_lvalue(&l);
1421+
}
14141422

14151423
intravisit::walk_expr(this, expr);
14161424
}

src/test/compile-fail/liveness-unused.rs

+46
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#![deny(unused_assignments)]
1313
#![allow(dead_code, non_camel_case_types, trivial_numeric_casts)]
1414

15+
use std::ops::AddAssign;
16+
1517
fn f1(x: isize) {
1618
//~^ ERROR unused variable: `x`
1719
}
@@ -100,5 +102,49 @@ fn f5c() {
100102
}
101103
}
102104

105+
struct View<'a>(&'a mut [i32]);
106+
107+
impl<'a> AddAssign<i32> for View<'a> {
108+
fn add_assign(&mut self, rhs: i32) {
109+
for lhs in self.0.iter_mut() {
110+
*lhs += rhs;
111+
}
112+
}
113+
}
114+
115+
fn f6() {
116+
let mut array = [1, 2, 3];
117+
let mut v = View(&mut array);
118+
119+
// ensure an error shows up for x even if lhs of an overloaded add assign
120+
121+
let x;
122+
//~^ ERROR variable `x` is assigned to, but never used
123+
124+
*({
125+
x = 0; //~ ERROR value assigned to `x` is never read
126+
&mut v
127+
}) += 1;
128+
}
129+
130+
131+
struct MutRef<'a>(&'a mut i32);
132+
133+
impl<'a> AddAssign<i32> for MutRef<'a> {
134+
fn add_assign(&mut self, rhs: i32) {
135+
*self.0 += rhs;
136+
}
137+
}
138+
139+
fn f7() {
140+
let mut a = 1;
141+
{
142+
// `b` does not trigger unused_variables
143+
let mut b = MutRef(&mut a);
144+
b += 1;
145+
}
146+
drop(a);
147+
}
148+
103149
fn main() {
104150
}

src/test/run-pass/augmented-assignments.rs

+18
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![deny(unused_assignments)]
12+
1113
use std::mem;
1214
use std::ops::{
1315
AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, Index, MulAssign, RemAssign,
@@ -27,6 +29,8 @@ impl Slice {
2729
}
2830
}
2931

32+
struct View<'a>(&'a mut [i32]);
33+
3034
fn main() {
3135
let mut x = Int(1);
3236

@@ -78,6 +82,12 @@ fn main() {
7882
assert_eq!(array[0], 1);
7983
assert_eq!(array[1], 2);
8084
assert_eq!(array[2], 3);
85+
86+
// sized indirection
87+
// check that this does *not* trigger the unused_assignments lint
88+
let mut array = [0, 1, 2];
89+
let mut view = View(&mut array);
90+
view += 1;
8191
}
8292

8393
impl AddAssign for Int {
@@ -159,3 +169,11 @@ impl AddAssign<i32> for Slice {
159169
}
160170
}
161171
}
172+
173+
impl<'a> AddAssign<i32> for View<'a> {
174+
fn add_assign(&mut self, rhs: i32) {
175+
for lhs in self.0.iter_mut() {
176+
*lhs += rhs;
177+
}
178+
}
179+
}

0 commit comments

Comments
 (0)