Skip to content

Commit c209d44

Browse files
Ariel Ben-Yehudaarielb1
Ariel Ben-Yehuda
authored andcommitted
refactor autoderef to avoid registering obligations
Refactor `FnCtxt::autoderef` to use an external iterator and to not register any obligation from the main autoderef loop, but rather to register them after (and if) the loop successfully completes. Fixes rust-lang#24819 Fixes rust-lang#25801 Fixes rust-lang#27631 Fixes rust-lang#31258 Fixes rust-lang#31964 Fixes rust-lang#32320 Fixes rust-lang#33515 Fixes rust-lang#33755
1 parent dd6e8d4 commit c209d44

File tree

12 files changed

+388
-356
lines changed

12 files changed

+388
-356
lines changed

src/librustc/traits/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub use self::coherence::overlapping_impls;
3131
pub use self::coherence::OrphanCheckErr;
3232
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
3333
pub use self::project::{MismatchedProjectionTypes, ProjectionMode};
34-
pub use self::project::{normalize, Normalized};
34+
pub use self::project::{normalize, normalize_projection_type, Normalized};
3535
pub use self::object_safety::ObjectSafetyViolation;
3636
pub use self::object_safety::MethodViolationCode;
3737
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};

src/librustc/ty/adjustment.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,9 @@ impl<'a, 'gcx, 'tcx> ty::TyS<'tcx> {
235235
None => {
236236
span_bug!(
237237
expr_span,
238-
"the {}th autoderef failed: {}",
238+
"the {}th autoderef for {} failed: {}",
239239
autoderef,
240+
expr_id,
240241
adjusted_ty);
241242
}
242243
}
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// Copyright 2016 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+
use astconv::AstConv;
12+
13+
use super::FnCtxt;
14+
15+
use rustc::traits;
16+
use rustc::ty::{self, Ty, TraitRef};
17+
use rustc::ty::{ToPredicate, TypeFoldable};
18+
use rustc::ty::{MethodCall, MethodCallee};
19+
use rustc::ty::subst::Substs;
20+
use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
21+
use rustc::hir;
22+
23+
use syntax::codemap::Span;
24+
use syntax::parse::token;
25+
26+
#[derive(Copy, Clone, Debug)]
27+
enum AutoderefKind {
28+
Builtin,
29+
Overloaded
30+
}
31+
32+
pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
33+
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
34+
steps: Vec<(Ty<'tcx>, AutoderefKind)>,
35+
cur_ty: Ty<'tcx>,
36+
obligations: Vec<traits::PredicateObligation<'tcx>>,
37+
at_start: bool,
38+
span: Span
39+
}
40+
41+
impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
42+
type Item = (Ty<'tcx>, usize);
43+
44+
fn next(&mut self) -> Option<Self::Item> {
45+
let tcx = self.fcx.tcx;
46+
47+
debug!("autoderef: steps={:?}, cur_ty={:?}",
48+
self.steps, self.cur_ty);
49+
if self.at_start {
50+
self.at_start = false;
51+
debug!("autoderef stage #0 is {:?}", self.cur_ty);
52+
return Some((self.cur_ty, 0));
53+
}
54+
55+
if self.steps.len() == tcx.sess.recursion_limit.get() {
56+
// We've reached the recursion limit, error gracefully.
57+
span_err!(tcx.sess, self.span, E0055,
58+
"reached the recursion limit while auto-dereferencing {:?}",
59+
self.cur_ty);
60+
return None;
61+
}
62+
63+
if self.cur_ty.is_ty_var() {
64+
return None;
65+
}
66+
67+
// Otherwise, deref if type is derefable:
68+
let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(false, NoPreference) {
69+
(AutoderefKind::Builtin, mt.ty)
70+
} else {
71+
match self.overloaded_deref_ty(self.cur_ty) {
72+
Some(ty) => (AutoderefKind::Overloaded, ty),
73+
_ => return None
74+
}
75+
};
76+
77+
if new_ty.references_error() {
78+
return None;
79+
}
80+
81+
self.steps.push((self.cur_ty, kind));
82+
debug!("autoderef stage #{:?} is {:?} from {:?}", self.steps.len(),
83+
new_ty, (self.cur_ty, kind));
84+
self.cur_ty = new_ty;
85+
86+
Some((self.cur_ty, self.steps.len()))
87+
}
88+
}
89+
90+
impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
91+
fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
92+
debug!("overloaded_deref_ty({:?})", ty);
93+
94+
let tcx = self.fcx.tcx();
95+
96+
// <cur_ty as Deref>
97+
let trait_ref = TraitRef {
98+
def_id: match tcx.lang_items.deref_trait() {
99+
Some(f) => f,
100+
None => return None
101+
},
102+
substs: tcx.mk_substs(Substs::new_trait(vec![], vec![], self.cur_ty))
103+
};
104+
105+
let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
106+
107+
let mut selcx = traits::SelectionContext::new(self.fcx);
108+
let obligation = traits::Obligation::new(cause.clone(), trait_ref.to_predicate());
109+
if !selcx.evaluate_obligation(&obligation) {
110+
debug!("overloaded_deref_ty: cannot match obligation");
111+
return None;
112+
}
113+
114+
let normalized = traits::normalize_projection_type(
115+
&mut selcx,
116+
ty::ProjectionTy {
117+
trait_ref: trait_ref,
118+
item_name: token::intern("Target")
119+
},
120+
cause,
121+
0
122+
);
123+
124+
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
125+
self.obligations.extend(normalized.obligations);
126+
127+
Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
128+
}
129+
130+
pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
131+
self.fcx.structurally_resolved_type(self.span, self.cur_ty)
132+
}
133+
134+
pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
135+
where I: IntoIterator<Item=&'b hir::Expr>
136+
{
137+
let methods : Vec<_> = self.steps.iter().map(|&(ty, kind)| {
138+
if let AutoderefKind::Overloaded = kind {
139+
self.fcx.try_overloaded_deref(self.span, None, ty, pref)
140+
} else {
141+
None
142+
}
143+
}).collect();
144+
145+
debug!("finalize({:?}) - {:?},{:?}", pref, methods, self.obligations);
146+
147+
for expr in exprs {
148+
debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
149+
for (n, method) in methods.iter().enumerate() {
150+
if let &Some(method) = method {
151+
let method_call = MethodCall::autoderef(expr.id, n as u32);
152+
self.fcx.tables.borrow_mut().method_map.insert(method_call, method);
153+
}
154+
}
155+
}
156+
157+
for obligation in self.obligations {
158+
self.fcx.register_predicate(obligation);
159+
}
160+
}
161+
}
162+
163+
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
164+
pub fn autoderef(&'a self,
165+
span: Span,
166+
base_ty: Ty<'tcx>)
167+
-> Autoderef<'a, 'gcx, 'tcx>
168+
{
169+
Autoderef {
170+
fcx: self,
171+
steps: vec![],
172+
cur_ty: self.resolve_type_vars_if_possible(&base_ty),
173+
obligations: vec![],
174+
at_start: true,
175+
span: span
176+
}
177+
}
178+
179+
pub fn try_overloaded_deref(&self,
180+
span: Span,
181+
base_expr: Option<&hir::Expr>,
182+
base_ty: Ty<'tcx>,
183+
lvalue_pref: LvaluePreference)
184+
-> Option<MethodCallee<'tcx>>
185+
{
186+
debug!("try_overloaded_deref({:?},{:?},{:?},{:?})",
187+
span, base_expr, base_ty, lvalue_pref);
188+
// Try DerefMut first, if preferred.
189+
let method = match (lvalue_pref, self.tcx.lang_items.deref_mut_trait()) {
190+
(PreferMutLvalue, Some(trait_did)) => {
191+
self.lookup_method_in_trait(span, base_expr,
192+
token::intern("deref_mut"), trait_did,
193+
base_ty, None)
194+
}
195+
_ => None
196+
};
197+
198+
// Otherwise, fall back to Deref.
199+
let method = match (method, self.tcx.lang_items.deref_trait()) {
200+
(None, Some(trait_did)) => {
201+
self.lookup_method_in_trait(span, base_expr,
202+
token::intern("deref"), trait_did,
203+
base_ty, None)
204+
}
205+
(method, _) => method
206+
};
207+
208+
method
209+
}
210+
}

src/librustc_typeck/check/callee.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use super::{DeferredCallResolution, Expectation, FnCtxt,
12-
TupleArgumentsFlag, UnresolvedTypeAction};
12+
TupleArgumentsFlag};
1313

1414
use CrateCtxt;
1515
use middle::cstore::LOCAL_CRATE;
@@ -72,15 +72,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
7272
{
7373
self.check_expr(callee_expr);
7474
let original_callee_ty = self.expr_ty(callee_expr);
75-
let (callee_ty, _, result) =
76-
self.autoderef(callee_expr.span,
77-
original_callee_ty,
78-
|| Some(callee_expr),
79-
UnresolvedTypeAction::Error,
80-
LvaluePreference::NoPreference,
81-
|adj_ty, idx| {
82-
self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
83-
});
75+
76+
let mut autoderef = self.autoderef(callee_expr.span, original_callee_ty);
77+
let result = autoderef.by_ref().flat_map(|(adj_ty, idx)| {
78+
self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
79+
}).next();
80+
let callee_ty = autoderef.unambiguous_final_ty();
81+
autoderef.finalize(LvaluePreference::NoPreference, Some(callee_expr));
8482

8583
match result {
8684
None => {

src/librustc_typeck/check/coercion.rs

Lines changed: 16 additions & 16 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, UnresolvedTypeAction};
63+
use check::{FnCtxt};
6464

6565
use rustc::hir;
6666
use rustc::infer::{Coercion, InferOk, TypeOrigin, TypeTrace};
@@ -220,7 +220,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
220220
-> CoerceResult<'tcx>
221221
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
222222
where E: Fn() -> I,
223-
I: IntoIterator<Item=&'a hir::Expr> {
223+
I: IntoIterator<Item=&'a hir::Expr>
224+
{
224225

225226
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
226227

@@ -240,18 +241,16 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
240241

241242
let span = self.origin.span();
242243

243-
let lvalue_pref = LvaluePreference::from_mutbl(mt_b.mutbl);
244244
let mut first_error = None;
245245
let mut r_borrow_var = None;
246-
let (_, autoderefs, success) = self.autoderef(span, a, exprs,
247-
UnresolvedTypeAction::Ignore,
248-
lvalue_pref,
249-
|referent_ty, autoderef|
250-
{
251-
if autoderef == 0 {
246+
let mut autoderef = self.autoderef(span, a);
247+
let mut success = None;
248+
249+
for (referent_ty, autoderefs) in autoderef.by_ref() {
250+
if autoderefs == 0 {
252251
// Don't let this pass, otherwise it would cause
253252
// &T to autoref to &&T.
254-
return None;
253+
continue
255254
}
256255

257256
// At this point, we have deref'd `a` to `referent_ty`. So
@@ -326,7 +325,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
326325
// and let regionck figure it out.
327326
let r = if !self.use_lub {
328327
r_b // [2] above
329-
} else if autoderef == 1 {
328+
} else if autoderefs == 1 {
330329
r_a // [3] above
331330
} else {
332331
if r_borrow_var.is_none() { // create var lazilly, at most once
@@ -341,30 +340,31 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
341340
mutbl: mt_b.mutbl // [1] above
342341
});
343342
match self.unify(derefd_ty_a, b) {
344-
Ok(ty) => Some(ty),
343+
Ok(ty) => { success = Some((ty, autoderefs)); break },
345344
Err(err) => {
346345
if first_error.is_none() {
347346
first_error = Some(err);
348347
}
349-
None
350348
}
351349
}
352-
});
350+
}
353351

354352
// Extract type or return an error. We return the first error
355353
// we got, which should be from relating the "base" type
356354
// (e.g., in example above, the failure from relating `Vec<T>`
357355
// to the target type), since that should be the least
358356
// confusing.
359-
let ty = match success {
360-
Some(ty) => ty,
357+
let (ty, autoderefs) = match success {
358+
Some(d) => d,
361359
None => {
362360
let err = first_error.expect("coerce_borrowed_pointer had no error");
363361
debug!("coerce_borrowed_pointer: failed with err = {:?}", err);
364362
return Err(err);
365363
}
366364
};
367365

366+
autoderef.finalize(LvaluePreference::from_mutbl(mt_b.mutbl), exprs());
367+
368368
// Now apply the autoref. We have to extract the region out of
369369
// the final ref type we got.
370370
if ty == a && mt_a.mutbl == hir::MutImmutable && autoderefs == 1 {

0 commit comments

Comments
 (0)