Skip to content

Commit d6b7ca0

Browse files
committed
Made ref pattern bindings correctly pick Deref or DerefMut
Added LvaluePreference::from_mutbl Closes #15609
1 parent c21fd9a commit d6b7ca0

File tree

7 files changed

+192
-25
lines changed

7 files changed

+192
-25
lines changed

src/librustc/middle/pat_util.rs

+19-7
Original file line numberDiff line numberDiff line change
@@ -135,22 +135,34 @@ pub fn pat_contains_bindings(dm: &DefMap, pat: &ast::Pat) -> bool {
135135
contains_bindings
136136
}
137137

138-
/// Checks if the pattern contains any `ref` or `ref mut` bindings.
139-
pub fn pat_contains_ref_binding(dm: &DefMap, pat: &ast::Pat) -> bool {
140-
let mut result = false;
138+
/// Checks if the pattern contains any `ref` or `ref mut` bindings,
139+
/// and if yes wether its containing mutable ones or just immutables ones.
140+
pub fn pat_contains_ref_binding(dm: &DefMap, pat: &ast::Pat) -> Option<ast::Mutability> {
141+
let mut result = None;
141142
pat_bindings(dm, pat, |mode, _, _, _| {
142143
match mode {
143-
ast::BindingMode::BindByRef(_) => { result = true; }
144+
ast::BindingMode::BindByRef(m) => {
145+
// Pick Mutable as maximum
146+
match result {
147+
None | Some(ast::MutImmutable) => result = Some(m),
148+
_ => (),
149+
}
150+
}
144151
ast::BindingMode::BindByValue(_) => { }
145152
}
146153
});
147154
result
148155
}
149156

150157
/// Checks if the patterns for this arm contain any `ref` or `ref mut`
151-
/// bindings.
152-
pub fn arm_contains_ref_binding(dm: &DefMap, arm: &ast::Arm) -> bool {
153-
arm.pats.iter().any(|pat| pat_contains_ref_binding(dm, pat))
158+
/// bindings, and if yes wether its containing mutable ones or just immutables ones.
159+
pub fn arm_contains_ref_binding(dm: &DefMap, arm: &ast::Arm) -> Option<ast::Mutability> {
160+
arm.pats.iter()
161+
.filter_map(|pat| pat_contains_ref_binding(dm, pat))
162+
.max_by(|m| match *m {
163+
ast::MutMutable => 1,
164+
ast::MutImmutable => 0,
165+
})
154166
}
155167

156168
/// Checks if the pattern contains any patterns that bind something to

src/librustc/middle/ty.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2846,11 +2846,11 @@ impl<'tcx> ctxt<'tcx> {
28462846
self.ty_param_defs.borrow().get(&node_id).unwrap().clone()
28472847
}
28482848

2849-
pub fn pat_contains_ref_binding(&self, pat: &ast::Pat) -> bool {
2849+
pub fn pat_contains_ref_binding(&self, pat: &ast::Pat) -> Option<ast::Mutability> {
28502850
pat_util::pat_contains_ref_binding(&self.def_map, pat)
28512851
}
28522852

2853-
pub fn arm_contains_ref_binding(&self, arm: &ast::Arm) -> bool {
2853+
pub fn arm_contains_ref_binding(&self, arm: &ast::Arm) -> Option<ast::Mutability> {
28542854
pat_util::arm_contains_ref_binding(&self.def_map, arm)
28552855
}
28562856
}

src/librustc_typeck/check/_match.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use middle::subst::Substs;
1818
use middle::ty::{self, Ty};
1919
use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
2020
use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
21+
use check::{check_expr_with_lvalue_pref, LvaluePreference};
2122
use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
2223
use require_same_types;
2324
use util::nodemap::FnvHashMap;
@@ -438,10 +439,15 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
438439
// Not entirely obvious: if matches may create ref bindings, we
439440
// want to use the *precise* type of the discriminant, *not* some
440441
// supertype, as the "discriminant type" (issue #23116).
441-
let contains_ref_bindings = arms.iter().any(|a| tcx.arm_contains_ref_binding(a));
442+
let contains_ref_bindings = arms.iter()
443+
.filter_map(|a| tcx.arm_contains_ref_binding(a))
444+
.max_by(|m| match *m {
445+
ast::MutMutable => 1,
446+
ast::MutImmutable => 0,
447+
});
442448
let discrim_ty;
443-
if contains_ref_bindings {
444-
check_expr(fcx, discrim);
449+
if let Some(m) = contains_ref_bindings {
450+
check_expr_with_lvalue_pref(fcx, discrim, LvaluePreference::from_mutbl(m));
445451
discrim_ty = fcx.expr_ty(discrim);
446452
} else {
447453
// ...but otherwise we want to use any supertype of the

src/librustc_typeck/check/coercion.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
//! sort of a minor point so I've opted to leave it for later---after all
6161
//! we may want to adjust precisely when coercions occur.
6262
63-
use check::{autoderef, FnCtxt, NoPreference, PreferMutLvalue, UnresolvedTypeAction};
63+
use check::{autoderef, FnCtxt, LvaluePreference, UnresolvedTypeAction};
6464

6565
use middle::infer::{self, Coercion};
6666
use middle::traits::{self, ObligationCause};
@@ -188,10 +188,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
188188
let r_borrow = self.tcx().mk_region(r_borrow);
189189
let autoref = Some(ty::AutoPtr(r_borrow, mutbl_b));
190190

191-
let lvalue_pref = match mutbl_b {
192-
ast::MutMutable => PreferMutLvalue,
193-
ast::MutImmutable => NoPreference
194-
};
191+
let lvalue_pref = LvaluePreference::from_mutbl(mutbl_b);
195192
let mut first_error = None;
196193
let (_, autoderefs, success) = autoderef(self.fcx,
197194
expr_a.span,

src/librustc_typeck/check/mod.rs

+14-8
Original file line numberDiff line numberDiff line change
@@ -1908,6 +1908,15 @@ pub enum LvaluePreference {
19081908
NoPreference
19091909
}
19101910

1911+
impl LvaluePreference {
1912+
pub fn from_mutbl(m: ast::Mutability) -> Self {
1913+
match m {
1914+
ast::MutMutable => PreferMutLvalue,
1915+
ast::MutImmutable => NoPreference,
1916+
}
1917+
}
1918+
}
1919+
19111920
/// Whether `autoderef` requires types to resolve.
19121921
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
19131922
pub enum UnresolvedTypeAction {
@@ -3224,10 +3233,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
32243233
_ => NoExpectation
32253234
}
32263235
});
3227-
let lvalue_pref = match mutbl {
3228-
ast::MutMutable => PreferMutLvalue,
3229-
ast::MutImmutable => NoPreference
3230-
};
3236+
let lvalue_pref = LvaluePreference::from_mutbl(mutbl);
32313237
check_expr_with_expectation_and_lvalue_pref(fcx,
32323238
&**oprnd,
32333239
hint,
@@ -3925,9 +3931,7 @@ pub fn check_decl_initializer<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
39253931
let ref_bindings = fcx.tcx().pat_contains_ref_binding(&local.pat);
39263932

39273933
let local_ty = fcx.local_ty(init.span, local.id);
3928-
if !ref_bindings {
3929-
check_expr_coercable_to_type(fcx, init, local_ty)
3930-
} else {
3934+
if let Some(m) = ref_bindings {
39313935
// Somewhat subtle: if we have a `ref` binding in the pattern,
39323936
// we want to avoid introducing coercions for the RHS. This is
39333937
// both because it helps preserve sanity and, in the case of
@@ -3936,9 +3940,11 @@ pub fn check_decl_initializer<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
39363940
// referent for the reference that results is *equal to* the
39373941
// type of the lvalue it is referencing, and not some
39383942
// supertype thereof.
3939-
check_expr(fcx, init);
3943+
check_expr_with_lvalue_pref(fcx, init, LvaluePreference::from_mutbl(m));
39403944
let init_ty = fcx.expr_ty(init);
39413945
demand::eqtype(fcx, init.span, init_ty, local_ty);
3946+
} else {
3947+
check_expr_coercable_to_type(fcx, init, local_ty)
39423948
};
39433949
}
39443950

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we choose Deref or DerefMut appropriately based on mutability of ref bindings (#15609).
12+
13+
use std::ops::{Deref, DerefMut};
14+
15+
struct DerefOk<T>(T);
16+
struct DerefMutOk<T>(T);
17+
18+
impl<T> Deref for DerefOk<T> {
19+
type Target = T;
20+
fn deref(&self) -> &Self::Target {
21+
&self.0
22+
}
23+
}
24+
25+
impl<T> DerefMut for DerefOk<T> {
26+
fn deref_mut(&mut self) -> &mut Self::Target {
27+
panic!()
28+
}
29+
}
30+
31+
impl<T> Deref for DerefMutOk<T> {
32+
type Target = T;
33+
fn deref(&self) -> &Self::Target {
34+
panic!()
35+
}
36+
}
37+
38+
impl<T> DerefMut for DerefMutOk<T> {
39+
fn deref_mut(&mut self) -> &mut Self::Target {
40+
&mut self.0
41+
}
42+
}
43+
44+
fn main() {
45+
// Check that mutable ref binding in match picks DerefMut
46+
let mut b = DerefMutOk(0);
47+
match *b {
48+
ref mut n => n,
49+
};
50+
51+
// Check that mutable ref binding in let picks DerefMut
52+
let mut y = DerefMutOk(1);
53+
let ref mut z = *y;
54+
55+
// Check that immutable ref binding in match picks Deref
56+
let mut b = DerefOk(2);
57+
match *b {
58+
ref n => n,
59+
};
60+
61+
// Check that immutable ref binding in let picks Deref
62+
let mut y = DerefOk(3);
63+
let ref z = *y;
64+
65+
// Check that mixed mutable/immutable ref binding in match picks DerefMut
66+
let mut b = DerefMutOk((0, 9));
67+
match *b {
68+
(ref mut n, ref m) => (n, m),
69+
};
70+
71+
let mut b = DerefMutOk((0, 9));
72+
match *b {
73+
(ref n, ref mut m) => (n, m),
74+
};
75+
76+
// Check that mixed mutable/immutable ref binding in let picks DerefMut
77+
let mut y = DerefMutOk((1, 8));
78+
let (ref mut z, ref a) = *y;
79+
80+
let mut y = DerefMutOk((1, 8));
81+
let (ref z, ref mut a) = *y;
82+
83+
// Check that multiple immutable ref bindings in match picks Deref
84+
let mut b = DerefOk((2, 7));
85+
match *b {
86+
(ref n, ref m) => (n, m),
87+
};
88+
89+
// Check that multiple immutable ref bindings in let picks Deref
90+
let mut y = DerefOk((3, 6));
91+
let (ref z, ref a) = *y;
92+
93+
// Check that multiple mutable ref bindings in match picks DerefMut
94+
let mut b = DerefMutOk((4, 5));
95+
match *b {
96+
(ref mut n, ref mut m) => (n, m),
97+
};
98+
99+
// Check that multiple mutable ref bindings in let picks DerefMut
100+
let mut y = DerefMutOk((5, 4));
101+
let (ref mut z, ref mut a) = *y;
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we choose Deref or DerefMut appropriately based on mutability of ref bindings (#15609).
12+
13+
fn main() {
14+
use std::cell::RefCell;
15+
16+
struct S {
17+
node: E,
18+
}
19+
20+
enum E {
21+
Foo(u32),
22+
Bar,
23+
}
24+
25+
// Check match
26+
let x = RefCell::new(S { node: E::Foo(0) });
27+
28+
let mut b = x.borrow_mut();
29+
match b.node {
30+
E::Foo(ref mut n) => *n += 1,
31+
_ => (),
32+
}
33+
34+
// Check let
35+
let x = RefCell::new(0);
36+
let mut y = x.borrow_mut();
37+
let ref mut z = *y;
38+
39+
fn foo(a: &mut RefCell<Option<String>>) {
40+
if let Some(ref mut s) = *a.borrow_mut() {
41+
s.push('a')
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)