Skip to content

Commit ae076e1

Browse files
committed
Implement deref coercions (rust-lang/rfcs#241).
1 parent b48c4c8 commit ae076e1

File tree

8 files changed

+261
-60
lines changed

8 files changed

+261
-60
lines changed

src/librustc_typeck/check/callee.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use super::LvaluePreference;
2121
use super::method;
2222
use super::structurally_resolved_type;
2323
use super::TupleArgumentsFlag;
24+
use super::UnresolvedTypeAction;
2425
use super::write_call;
2526

2627
use middle::infer;
@@ -77,6 +78,7 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
7778
callee_expr.span,
7879
original_callee_ty,
7980
Some(callee_expr),
81+
UnresolvedTypeAction::Error,
8082
LvaluePreference::NoPreference,
8183
|adj_ty, idx| {
8284
let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None };

src/librustc_typeck/check/coercion.rs

Lines changed: 60 additions & 20 deletions
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::FnCtxt;
63+
use check::{autoderef, FnCtxt, NoPreference, PreferMutLvalue, UnresolvedTypeAction};
6464

6565
use middle::infer::{self, cres, Coercion, TypeTrace};
6666
use middle::infer::combine::Combine;
@@ -98,7 +98,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
9898
f(self.fcx.infcx().shallow_resolve(a))
9999
}
100100

101-
fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
101+
fn coerce(&self,
102+
expr_a: &ast::Expr,
103+
a: Ty<'tcx>,
104+
b: Ty<'tcx>)
105+
-> CoerceResult<'tcx> {
102106
debug!("Coerce.tys({} => {})",
103107
a.repr(self.tcx()),
104108
b.repr(self.tcx()));
@@ -124,7 +128,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
124128

125129
ty::ty_rptr(_, mt_b) => {
126130
return self.unpack_actual_value(a, |a| {
127-
self.coerce_borrowed_pointer(a, b, mt_b.mutbl)
131+
self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl)
128132
});
129133
}
130134

@@ -147,8 +151,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
147151
})
148152
}
149153

150-
// ~T -> &T or &mut T -> &T (including where T = [U] or str)
154+
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
155+
/// To match `A` with `B`, autoderef will be performed,
156+
/// calling `deref`/`deref_mut` where necessary.
151157
fn coerce_borrowed_pointer(&self,
158+
expr_a: &ast::Expr,
152159
a: Ty<'tcx>,
153160
b: Ty<'tcx>,
154161
mutbl_b: ast::Mutability)
@@ -163,29 +170,62 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
163170
// to type check, we will construct the type that `&M*expr` would
164171
// yield.
165172

166-
let coercion = Coercion(self.trace.clone());
167-
let r_borrow = self.fcx.infcx().next_region_var(coercion);
168-
169-
let inner_ty = match a.sty {
173+
match a.sty {
170174
ty::ty_rptr(_, mt_a) => {
171175
if !can_coerce_mutbls(mt_a.mutbl, mutbl_b) {
172176
return Err(ty::terr_mutability);
173177
}
174-
mt_a.ty
175178
}
176179
_ => return self.subtype(a, b)
177-
};
180+
}
178181

179-
let a_borrowed = ty::mk_rptr(self.tcx(),
180-
self.tcx().mk_region(r_borrow),
181-
mt {ty: inner_ty, mutbl: mutbl_b});
182-
try!(self.subtype(a_borrowed, b));
183-
if let Err(original_err) = self.subtype(a_borrowed, b) {
182+
let coercion = Coercion(self.trace.clone());
183+
let r_borrow = self.fcx.infcx().next_region_var(coercion);
184+
let autoref = Some(AutoPtr(r_borrow, mutbl_b, None));
184185

185-
Ok(Some(AdjustDerefRef(AutoDerefRef {
186-
autoderefs: 1,
187-
autoref: Some(AutoPtr(r_borrow, mutbl_b, None))
188-
})))
186+
let r_borrow = self.tcx().mk_region(r_borrow);
187+
let lvalue_pref = match mutbl_b {
188+
ast::MutMutable => PreferMutLvalue,
189+
ast::MutImmutable => NoPreference
190+
};
191+
let mut first_error = None;
192+
let (_, autoderefs, success) = autoderef(self.fcx,
193+
expr_a.span,
194+
a,
195+
Some(expr_a),
196+
UnresolvedTypeAction::Ignore,
197+
lvalue_pref,
198+
|inner_ty, autoderef| {
199+
if autoderef == 0 {
200+
// Don't let this pass, otherwise it would cause
201+
// &T to autoref to &&T.
202+
return None;
203+
}
204+
let ty = ty::mk_rptr(self.tcx(), r_borrow,
205+
mt {ty: inner_ty, mutbl: mutbl_b});
206+
if let Err(err) = self.fcx.infcx().try(|_| self.subtype(ty, b)) {
207+
if first_error.is_none() {
208+
first_error = Some(err);
209+
}
210+
None
211+
} else {
212+
Some(())
213+
}
214+
});
215+
216+
match success {
217+
Some(_) => {
218+
Ok(Some(AdjustDerefRef(AutoDerefRef {
219+
autoderefs: autoderefs,
220+
autoref: autoref
221+
})))
222+
}
223+
None => {
224+
// Return original error as if overloaded deref was never
225+
// attempted, to avoid irrelevant/confusing error messages.
226+
Err(first_error.expect("coerce_borrowed_pointer failed with no error?"))
227+
}
228+
}
189229
}
190230

191231

@@ -426,7 +466,7 @@ pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
426466
Coerce {
427467
fcx: fcx,
428468
trace: infer::TypeTrace::types(origin, false, a, b)
429-
}.coerce(a, b)
469+
}.coerce(expr, a, b)
430470
})
431471
}));
432472
if let Some(adjustment) = adjustment {

src/librustc_typeck/check/method/confirm.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use super::probe;
1212

1313
use check::{self, FnCtxt, NoPreference, PreferMutLvalue, callee, demand};
14+
use check::UnresolvedTypeAction;
1415
use middle::mem_categorization::Typer;
1516
use middle::subst::{self};
1617
use middle::traits;
@@ -141,10 +142,19 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
141142

142143
// Commit the autoderefs by calling `autoderef again, but this
143144
// time writing the results into the various tables.
144-
let (autoderefd_ty, n, result) =
145-
check::autoderef(
146-
self.fcx, self.span, unadjusted_self_ty, Some(self.self_expr), NoPreference,
147-
|_, n| if n == auto_deref_ref.autoderefs { Some(()) } else { None });
145+
let (autoderefd_ty, n, result) = check::autoderef(self.fcx,
146+
self.span,
147+
unadjusted_self_ty,
148+
Some(self.self_expr),
149+
UnresolvedTypeAction::Error,
150+
NoPreference,
151+
|_, n| {
152+
if n == auto_deref_ref.autoderefs {
153+
Some(())
154+
} else {
155+
None
156+
}
157+
});
148158
assert_eq!(n, auto_deref_ref.autoderefs);
149159
assert_eq!(result, Some(()));
150160

@@ -302,15 +312,18 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
302312
// yield an object-type (e.g., `&Object` or `Box<Object>`
303313
// etc).
304314

305-
let (_, _, result) =
306-
check::autoderef(
307-
self.fcx, self.span, self_ty, None, NoPreference,
308-
|ty, _| {
309-
match ty.sty {
310-
ty::ty_trait(ref data) => Some(closure(self, ty, &**data)),
311-
_ => None,
312-
}
313-
});
315+
let (_, _, result) = check::autoderef(self.fcx,
316+
self.span,
317+
self_ty,
318+
None,
319+
UnresolvedTypeAction::Error,
320+
NoPreference,
321+
|ty, _| {
322+
match ty.sty {
323+
ty::ty_trait(ref data) => Some(closure(self, ty, &**data)),
324+
_ => None,
325+
}
326+
});
314327

315328
match result {
316329
Some(r) => r,
@@ -517,6 +530,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
517530
expr.span,
518531
self.fcx.expr_ty(expr),
519532
Some(expr),
533+
UnresolvedTypeAction::Error,
520534
PreferMutLvalue,
521535
|_, autoderefs| {
522536
if autoderefs == autoderef_count + 1 {

src/librustc_typeck/check/method/probe.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use super::{CandidateSource,ImplSource,TraitSource};
1414
use super::suggest;
1515

1616
use check;
17-
use check::{FnCtxt, NoPreference};
17+
use check::{FnCtxt, NoPreference, UnresolvedTypeAction};
1818
use middle::fast_reject;
1919
use middle::subst;
2020
use middle::subst::Subst;
@@ -169,16 +169,19 @@ fn create_steps<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
169169
-> Option<Vec<CandidateStep<'tcx>>> {
170170
let mut steps = Vec::new();
171171

172-
let (fully_dereferenced_ty, dereferences, _) =
173-
check::autoderef(
174-
fcx, span, self_ty, None, NoPreference,
175-
|t, d| {
176-
let adjustment = AutoDeref(d);
177-
steps.push(CandidateStep { self_ty: t, adjustment: adjustment });
178-
None::<()> // keep iterating until we can't anymore
179-
});
180-
181-
match fully_dereferenced_ty.sty {
172+
let (final_ty, dereferences, _) = check::autoderef(fcx,
173+
span,
174+
self_ty,
175+
None,
176+
UnresolvedTypeAction::Error,
177+
NoPreference,
178+
|t, d| {
179+
let adjustment = AutoDeref(d);
180+
steps.push(CandidateStep { self_ty: t, adjustment: adjustment });
181+
None::<()> // keep iterating until we can't anymore
182+
});
183+
184+
match final_ty.sty {
182185
ty::ty_vec(elem_ty, Some(len)) => {
183186
steps.push(CandidateStep {
184187
self_ty: ty::mk_vec(fcx.tcx(), elem_ty, None),

src/librustc_typeck/check/mod.rs

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,6 +1865,17 @@ pub enum LvaluePreference {
18651865
NoPreference
18661866
}
18671867

1868+
/// Whether `autoderef` requires types to resolve.
1869+
#[derive(Copy, Show, PartialEq, Eq)]
1870+
pub enum UnresolvedTypeAction {
1871+
/// Produce an error and return `ty_err` whenever a type cannot
1872+
/// be resolved (i.e. it is `ty_infer`).
1873+
Error,
1874+
/// Go on without emitting any errors, and return the unresolved
1875+
/// type. Useful for probing, e.g. in coercions.
1876+
Ignore
1877+
}
1878+
18681879
/// Executes an autoderef loop for the type `t`. At each step, invokes `should_stop` to decide
18691880
/// whether to terminate the loop. Returns the final type and number of derefs that it performed.
18701881
///
@@ -1874,6 +1885,7 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
18741885
sp: Span,
18751886
base_ty: Ty<'tcx>,
18761887
opt_expr: Option<&ast::Expr>,
1888+
unresolved_type_action: UnresolvedTypeAction,
18771889
mut lvalue_pref: LvaluePreference,
18781890
mut should_stop: F)
18791891
-> (Ty<'tcx>, uint, Option<T>)
@@ -1886,11 +1898,22 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
18861898

18871899
let mut t = base_ty;
18881900
for autoderefs in 0..fcx.tcx().sess.recursion_limit.get() {
1889-
let resolved_t = structurally_resolved_type(fcx, sp, t);
1890-
1891-
if ty::type_is_error(resolved_t) {
1892-
return (resolved_t, autoderefs, None);
1893-
}
1901+
let resolved_t = match unresolved_type_action {
1902+
UnresolvedTypeAction::Error => {
1903+
let resolved_t = structurally_resolved_type(fcx, sp, t);
1904+
if ty::type_is_error(resolved_t) {
1905+
return (resolved_t, autoderefs, None);
1906+
}
1907+
resolved_t
1908+
}
1909+
UnresolvedTypeAction::Ignore => {
1910+
// We can continue even when the type cannot be resolved
1911+
// (i.e. it is an inference variable) because `ty::deref`
1912+
// and `try_overloaded_deref` both simply return `None`
1913+
// in such a case without producing spurious errors.
1914+
fcx.resolve_type_vars_if_possible(t)
1915+
}
1916+
};
18941917

18951918
match should_stop(resolved_t, autoderefs) {
18961919
Some(x) => return (resolved_t, autoderefs, Some(x)),
@@ -2011,8 +2034,13 @@ fn autoderef_for_index<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
20112034
// autoderef that normal method probing does. They could likely be
20122035
// consolidated.
20132036

2014-
let (ty, autoderefs, final_mt) =
2015-
autoderef(fcx, base_expr.span, base_ty, Some(base_expr), lvalue_pref, |adj_ty, idx| {
2037+
let (ty, autoderefs, final_mt) = autoderef(fcx,
2038+
base_expr.span,
2039+
base_ty,
2040+
Some(base_expr),
2041+
UnresolvedTypeAction::Error,
2042+
lvalue_pref,
2043+
|adj_ty, idx| {
20162044
let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None };
20172045
step(adj_ty, autoderefref)
20182046
});
@@ -3053,8 +3081,13 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
30533081
let expr_t = structurally_resolved_type(fcx, expr.span,
30543082
fcx.expr_ty(base));
30553083
// FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop.
3056-
let (_, autoderefs, field_ty) =
3057-
autoderef(fcx, expr.span, expr_t, Some(base), lvalue_pref, |base_t, _| {
3084+
let (_, autoderefs, field_ty) = autoderef(fcx,
3085+
expr.span,
3086+
expr_t,
3087+
Some(base),
3088+
UnresolvedTypeAction::Error,
3089+
lvalue_pref,
3090+
|base_t, _| {
30583091
match base_t.sty {
30593092
ty::ty_struct(base_id, substs) => {
30603093
debug!("struct named {}", ppaux::ty_to_string(tcx, base_t));
@@ -3146,8 +3179,13 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
31463179
fcx.expr_ty(base));
31473180
let mut tuple_like = false;
31483181
// FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop.
3149-
let (_, autoderefs, field_ty) =
3150-
autoderef(fcx, expr.span, expr_t, Some(base), lvalue_pref, |base_t, _| {
3182+
let (_, autoderefs, field_ty) = autoderef(fcx,
3183+
expr.span,
3184+
expr_t,
3185+
Some(base),
3186+
UnresolvedTypeAction::Error,
3187+
lvalue_pref,
3188+
|base_t, _| {
31513189
match base_t.sty {
31523190
ty::ty_struct(base_id, substs) => {
31533191
tuple_like = ty::is_tuple_struct(tcx, base_id);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2014 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+
fn borrow_mut<T>(x: &mut T) -> &mut T { x }
12+
fn borrow<T>(x: &T) -> &T { x }
13+
14+
fn borrow_mut2<T>(_: &mut T, _: &mut T) {}
15+
fn borrow2<T>(_: &mut T, _: &T) {}
16+
17+
fn double_mut_borrow<T>(x: &mut Box<T>) {
18+
let y = borrow_mut(x);
19+
let z = borrow_mut(x);
20+
//~^ ERROR cannot borrow `*x` as mutable more than once at a time
21+
}
22+
23+
fn double_imm_borrow(x: &mut Box<i32>) {
24+
let y = borrow(x);
25+
let z = borrow(x);
26+
**x += 1;
27+
//~^ ERROR cannot assign to `**x` because it is borrowed
28+
}
29+
30+
fn double_mut_borrow2<T>(x: &mut Box<T>) {
31+
borrow_mut2(x, x);
32+
//~^ ERROR cannot borrow `*x` as mutable more than once at a time
33+
}
34+
35+
fn double_borrow2<T>(x: &mut Box<T>) {
36+
borrow2(x, x);
37+
//~^ ERROR cannot borrow `*x` as immutable because it is also borrowed as mutable
38+
}
39+
40+
pub fn main() {}

0 commit comments

Comments
 (0)