Rollup merge of #33852 - arielb1:autoderef-iterator, r=eddyb
refactor autoderef to avoid prematurely 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 #24819 Fixes #25801 Fixes #27631 Fixes #31258 Fixes #31964 Fixes #32320 Fixes #33515 Fixes #33755 r? @eddyb
This commit is contained in:
commit
edd7d422b7
@ -163,6 +163,11 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
// If the number of errors increases, that's also a sign (line
|
||||
// `tained_by_errors`) to avoid reporting certain kinds of errors.
|
||||
err_count_on_creation: usize,
|
||||
|
||||
// This flag is used for debugging, and is set to true if there are
|
||||
// any obligations set during the current snapshot. In that case, the
|
||||
// snapshot can't be rolled back.
|
||||
pub obligations_in_snapshot: Cell<bool>,
|
||||
}
|
||||
|
||||
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
|
||||
@ -476,7 +481,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
|
||||
normalize: false,
|
||||
projection_mode: ProjectionMode::AnyFinal,
|
||||
tainted_by_errors_flag: Cell::new(false),
|
||||
err_count_on_creation: self.sess.err_count()
|
||||
err_count_on_creation: self.sess.err_count(),
|
||||
obligations_in_snapshot: Cell::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -515,7 +521,8 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
|
||||
normalize: normalize,
|
||||
projection_mode: projection_mode,
|
||||
tainted_by_errors_flag: Cell::new(false),
|
||||
err_count_on_creation: tcx.sess.err_count()
|
||||
err_count_on_creation: tcx.sess.err_count(),
|
||||
obligations_in_snapshot: Cell::new(false),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -542,6 +549,7 @@ pub struct CombinedSnapshot {
|
||||
int_snapshot: unify::Snapshot<ty::IntVid>,
|
||||
float_snapshot: unify::Snapshot<ty::FloatVid>,
|
||||
region_vars_snapshot: RegionSnapshot,
|
||||
obligations_in_snapshot: bool,
|
||||
}
|
||||
|
||||
/// Helper trait for shortening the lifetimes inside a
|
||||
@ -809,11 +817,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
fn start_snapshot(&self) -> CombinedSnapshot {
|
||||
let obligations_in_snapshot = self.obligations_in_snapshot.get();
|
||||
self.obligations_in_snapshot.set(false);
|
||||
|
||||
CombinedSnapshot {
|
||||
type_snapshot: self.type_variables.borrow_mut().snapshot(),
|
||||
int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
|
||||
float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
|
||||
region_vars_snapshot: self.region_vars.start_snapshot(),
|
||||
obligations_in_snapshot: obligations_in_snapshot,
|
||||
}
|
||||
}
|
||||
|
||||
@ -822,7 +834,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
let CombinedSnapshot { type_snapshot,
|
||||
int_snapshot,
|
||||
float_snapshot,
|
||||
region_vars_snapshot } = snapshot;
|
||||
region_vars_snapshot,
|
||||
obligations_in_snapshot } = snapshot;
|
||||
|
||||
assert!(!self.obligations_in_snapshot.get());
|
||||
self.obligations_in_snapshot.set(obligations_in_snapshot);
|
||||
|
||||
self.type_variables
|
||||
.borrow_mut()
|
||||
@ -842,7 +858,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
let CombinedSnapshot { type_snapshot,
|
||||
int_snapshot,
|
||||
float_snapshot,
|
||||
region_vars_snapshot } = snapshot;
|
||||
region_vars_snapshot,
|
||||
obligations_in_snapshot } = snapshot;
|
||||
|
||||
self.obligations_in_snapshot.set(obligations_in_snapshot);
|
||||
|
||||
self.type_variables
|
||||
.borrow_mut()
|
||||
@ -904,12 +923,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
let CombinedSnapshot { type_snapshot,
|
||||
int_snapshot,
|
||||
float_snapshot,
|
||||
region_vars_snapshot } = self.start_snapshot();
|
||||
region_vars_snapshot,
|
||||
obligations_in_snapshot } = self.start_snapshot();
|
||||
|
||||
let r = self.commit_if_ok(|_| f());
|
||||
|
||||
debug!("commit_regions_if_ok: rolling back everything but regions");
|
||||
|
||||
assert!(!self.obligations_in_snapshot.get());
|
||||
self.obligations_in_snapshot.set(obligations_in_snapshot);
|
||||
|
||||
// Roll back any non-region bindings - they should be resolved
|
||||
// inside `f`, with, e.g. `resolve_type_vars_if_possible`.
|
||||
self.type_variables
|
||||
|
@ -171,6 +171,8 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
|
||||
// debug output much nicer to read and so on.
|
||||
let obligation = infcx.resolve_type_vars_if_possible(&obligation);
|
||||
|
||||
infcx.obligations_in_snapshot.set(true);
|
||||
|
||||
if infcx.tcx.fulfilled_predicates.borrow().check_duplicate(&obligation.predicate)
|
||||
{
|
||||
return
|
||||
|
@ -31,7 +31,7 @@ pub use self::coherence::overlapping_impls;
|
||||
pub use self::coherence::OrphanCheckErr;
|
||||
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
|
||||
pub use self::project::{MismatchedProjectionTypes, ProjectionMode};
|
||||
pub use self::project::{normalize, Normalized};
|
||||
pub use self::project::{normalize, normalize_projection_type, Normalized};
|
||||
pub use self::object_safety::ObjectSafetyViolation;
|
||||
pub use self::object_safety::MethodViolationCode;
|
||||
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
|
||||
|
@ -187,51 +187,49 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||
source_trait_ref: ty::TraitRef<'tcx>,
|
||||
target_impl: DefId)
|
||||
-> Result<&'tcx Substs<'tcx>, ()> {
|
||||
infcx.commit_if_ok(|_| {
|
||||
let selcx = &mut SelectionContext::new(&infcx);
|
||||
let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
|
||||
let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
|
||||
target_impl,
|
||||
&target_substs);
|
||||
let selcx = &mut SelectionContext::new(&infcx);
|
||||
let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
|
||||
let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
|
||||
target_impl,
|
||||
&target_substs);
|
||||
|
||||
// do the impls unify? If not, no specialization.
|
||||
if let Err(_) = infcx.eq_trait_refs(true,
|
||||
TypeOrigin::Misc(DUMMY_SP),
|
||||
source_trait_ref,
|
||||
target_trait_ref) {
|
||||
debug!("fulfill_implication: {:?} does not unify with {:?}",
|
||||
source_trait_ref,
|
||||
target_trait_ref);
|
||||
return Err(());
|
||||
}
|
||||
// do the impls unify? If not, no specialization.
|
||||
if let Err(_) = infcx.eq_trait_refs(true,
|
||||
TypeOrigin::Misc(DUMMY_SP),
|
||||
source_trait_ref,
|
||||
target_trait_ref) {
|
||||
debug!("fulfill_implication: {:?} does not unify with {:?}",
|
||||
source_trait_ref,
|
||||
target_trait_ref);
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// attempt to prove all of the predicates for impl2 given those for impl1
|
||||
// (which are packed up in penv)
|
||||
// attempt to prove all of the predicates for impl2 given those for impl1
|
||||
// (which are packed up in penv)
|
||||
|
||||
let mut fulfill_cx = FulfillmentContext::new();
|
||||
for oblig in obligations.into_iter() {
|
||||
fulfill_cx.register_predicate_obligation(&infcx, oblig);
|
||||
}
|
||||
let mut fulfill_cx = FulfillmentContext::new();
|
||||
for oblig in obligations.into_iter() {
|
||||
fulfill_cx.register_predicate_obligation(&infcx, oblig);
|
||||
}
|
||||
|
||||
if let Err(errors) = infcx.drain_fulfillment_cx(&mut fulfill_cx, &()) {
|
||||
// no dice!
|
||||
debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
|
||||
{:?}",
|
||||
source_trait_ref,
|
||||
target_trait_ref,
|
||||
errors,
|
||||
infcx.parameter_environment.caller_bounds);
|
||||
Err(())
|
||||
} else {
|
||||
debug!("fulfill_implication: an impl for {:?} specializes {:?}",
|
||||
source_trait_ref,
|
||||
target_trait_ref);
|
||||
if let Err(errors) = infcx.drain_fulfillment_cx(&mut fulfill_cx, &()) {
|
||||
// no dice!
|
||||
debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
|
||||
{:?}",
|
||||
source_trait_ref,
|
||||
target_trait_ref,
|
||||
errors,
|
||||
infcx.parameter_environment.caller_bounds);
|
||||
Err(())
|
||||
} else {
|
||||
debug!("fulfill_implication: an impl for {:?} specializes {:?}",
|
||||
source_trait_ref,
|
||||
target_trait_ref);
|
||||
|
||||
// Now resolve the *substitution* we built for the target earlier, replacing
|
||||
// the inference variables inside with whatever we got from fulfillment.
|
||||
Ok(infcx.resolve_type_vars_if_possible(&target_substs))
|
||||
}
|
||||
})
|
||||
// Now resolve the *substitution* we built for the target earlier, replacing
|
||||
// the inference variables inside with whatever we got from fulfillment.
|
||||
Ok(infcx.resolve_type_vars_if_possible(&target_substs))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SpecializesCache {
|
||||
|
@ -235,8 +235,9 @@ impl<'a, 'gcx, 'tcx> ty::TyS<'tcx> {
|
||||
None => {
|
||||
span_bug!(
|
||||
expr_span,
|
||||
"the {}th autoderef failed: {}",
|
||||
"the {}th autoderef for {} failed: {}",
|
||||
autoderef,
|
||||
expr_id,
|
||||
adjusted_ty);
|
||||
}
|
||||
}
|
||||
|
210
src/librustc_typeck/check/autoderef.rs
Normal file
210
src/librustc_typeck/check/autoderef.rs
Normal file
@ -0,0 +1,210 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use astconv::AstConv;
|
||||
|
||||
use super::FnCtxt;
|
||||
|
||||
use rustc::traits;
|
||||
use rustc::ty::{self, Ty, TraitRef};
|
||||
use rustc::ty::{ToPredicate, TypeFoldable};
|
||||
use rustc::ty::{MethodCall, MethodCallee};
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
|
||||
use rustc::hir;
|
||||
|
||||
use syntax::codemap::Span;
|
||||
use syntax::parse::token;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum AutoderefKind {
|
||||
Builtin,
|
||||
Overloaded
|
||||
}
|
||||
|
||||
pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
|
||||
steps: Vec<(Ty<'tcx>, AutoderefKind)>,
|
||||
cur_ty: Ty<'tcx>,
|
||||
obligations: Vec<traits::PredicateObligation<'tcx>>,
|
||||
at_start: bool,
|
||||
span: Span
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
|
||||
type Item = (Ty<'tcx>, usize);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let tcx = self.fcx.tcx;
|
||||
|
||||
debug!("autoderef: steps={:?}, cur_ty={:?}",
|
||||
self.steps, self.cur_ty);
|
||||
if self.at_start {
|
||||
self.at_start = false;
|
||||
debug!("autoderef stage #0 is {:?}", self.cur_ty);
|
||||
return Some((self.cur_ty, 0));
|
||||
}
|
||||
|
||||
if self.steps.len() == tcx.sess.recursion_limit.get() {
|
||||
// We've reached the recursion limit, error gracefully.
|
||||
span_err!(tcx.sess, self.span, E0055,
|
||||
"reached the recursion limit while auto-dereferencing {:?}",
|
||||
self.cur_ty);
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.cur_ty.is_ty_var() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Otherwise, deref if type is derefable:
|
||||
let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(false, NoPreference) {
|
||||
(AutoderefKind::Builtin, mt.ty)
|
||||
} else {
|
||||
match self.overloaded_deref_ty(self.cur_ty) {
|
||||
Some(ty) => (AutoderefKind::Overloaded, ty),
|
||||
_ => return None
|
||||
}
|
||||
};
|
||||
|
||||
if new_ty.references_error() {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.steps.push((self.cur_ty, kind));
|
||||
debug!("autoderef stage #{:?} is {:?} from {:?}", self.steps.len(),
|
||||
new_ty, (self.cur_ty, kind));
|
||||
self.cur_ty = new_ty;
|
||||
|
||||
Some((self.cur_ty, self.steps.len()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
||||
fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
debug!("overloaded_deref_ty({:?})", ty);
|
||||
|
||||
let tcx = self.fcx.tcx();
|
||||
|
||||
// <cur_ty as Deref>
|
||||
let trait_ref = TraitRef {
|
||||
def_id: match tcx.lang_items.deref_trait() {
|
||||
Some(f) => f,
|
||||
None => return None
|
||||
},
|
||||
substs: tcx.mk_substs(Substs::new_trait(vec![], vec![], self.cur_ty))
|
||||
};
|
||||
|
||||
let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
|
||||
|
||||
let mut selcx = traits::SelectionContext::new(self.fcx);
|
||||
let obligation = traits::Obligation::new(cause.clone(), trait_ref.to_predicate());
|
||||
if !selcx.evaluate_obligation(&obligation) {
|
||||
debug!("overloaded_deref_ty: cannot match obligation");
|
||||
return None;
|
||||
}
|
||||
|
||||
let normalized = traits::normalize_projection_type(
|
||||
&mut selcx,
|
||||
ty::ProjectionTy {
|
||||
trait_ref: trait_ref,
|
||||
item_name: token::intern("Target")
|
||||
},
|
||||
cause,
|
||||
0
|
||||
);
|
||||
|
||||
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
|
||||
self.obligations.extend(normalized.obligations);
|
||||
|
||||
Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
|
||||
}
|
||||
|
||||
pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
|
||||
self.fcx.structurally_resolved_type(self.span, self.cur_ty)
|
||||
}
|
||||
|
||||
pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
|
||||
where I: IntoIterator<Item=&'b hir::Expr>
|
||||
{
|
||||
let methods : Vec<_> = self.steps.iter().map(|&(ty, kind)| {
|
||||
if let AutoderefKind::Overloaded = kind {
|
||||
self.fcx.try_overloaded_deref(self.span, None, ty, pref)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect();
|
||||
|
||||
debug!("finalize({:?}) - {:?},{:?}", pref, methods, self.obligations);
|
||||
|
||||
for expr in exprs {
|
||||
debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
|
||||
for (n, method) in methods.iter().enumerate() {
|
||||
if let &Some(method) = method {
|
||||
let method_call = MethodCall::autoderef(expr.id, n as u32);
|
||||
self.fcx.tables.borrow_mut().method_map.insert(method_call, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for obligation in self.obligations {
|
||||
self.fcx.register_predicate(obligation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn autoderef(&'a self,
|
||||
span: Span,
|
||||
base_ty: Ty<'tcx>)
|
||||
-> Autoderef<'a, 'gcx, 'tcx>
|
||||
{
|
||||
Autoderef {
|
||||
fcx: self,
|
||||
steps: vec![],
|
||||
cur_ty: self.resolve_type_vars_if_possible(&base_ty),
|
||||
obligations: vec![],
|
||||
at_start: true,
|
||||
span: span
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_overloaded_deref(&self,
|
||||
span: Span,
|
||||
base_expr: Option<&hir::Expr>,
|
||||
base_ty: Ty<'tcx>,
|
||||
lvalue_pref: LvaluePreference)
|
||||
-> Option<MethodCallee<'tcx>>
|
||||
{
|
||||
debug!("try_overloaded_deref({:?},{:?},{:?},{:?})",
|
||||
span, base_expr, base_ty, lvalue_pref);
|
||||
// Try DerefMut first, if preferred.
|
||||
let method = match (lvalue_pref, self.tcx.lang_items.deref_mut_trait()) {
|
||||
(PreferMutLvalue, Some(trait_did)) => {
|
||||
self.lookup_method_in_trait(span, base_expr,
|
||||
token::intern("deref_mut"), trait_did,
|
||||
base_ty, None)
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
|
||||
// Otherwise, fall back to Deref.
|
||||
let method = match (method, self.tcx.lang_items.deref_trait()) {
|
||||
(None, Some(trait_did)) => {
|
||||
self.lookup_method_in_trait(span, base_expr,
|
||||
token::intern("deref"), trait_did,
|
||||
base_ty, None)
|
||||
}
|
||||
(method, _) => method
|
||||
};
|
||||
|
||||
method
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
// except according to those terms.
|
||||
|
||||
use super::{DeferredCallResolution, Expectation, FnCtxt,
|
||||
TupleArgumentsFlag, UnresolvedTypeAction};
|
||||
TupleArgumentsFlag};
|
||||
|
||||
use CrateCtxt;
|
||||
use middle::cstore::LOCAL_CRATE;
|
||||
@ -72,15 +72,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
{
|
||||
self.check_expr(callee_expr);
|
||||
let original_callee_ty = self.expr_ty(callee_expr);
|
||||
let (callee_ty, _, result) =
|
||||
self.autoderef(callee_expr.span,
|
||||
original_callee_ty,
|
||||
|| Some(callee_expr),
|
||||
UnresolvedTypeAction::Error,
|
||||
LvaluePreference::NoPreference,
|
||||
|adj_ty, idx| {
|
||||
self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
|
||||
});
|
||||
|
||||
let mut autoderef = self.autoderef(callee_expr.span, original_callee_ty);
|
||||
let result = autoderef.by_ref().flat_map(|(adj_ty, idx)| {
|
||||
self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
|
||||
}).next();
|
||||
let callee_ty = autoderef.unambiguous_final_ty();
|
||||
autoderef.finalize(LvaluePreference::NoPreference, Some(callee_expr));
|
||||
|
||||
match result {
|
||||
None => {
|
||||
|
@ -60,7 +60,7 @@
|
||||
//! sort of a minor point so I've opted to leave it for later---after all
|
||||
//! we may want to adjust precisely when coercions occur.
|
||||
|
||||
use check::{FnCtxt, UnresolvedTypeAction};
|
||||
use check::{FnCtxt};
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::infer::{Coercion, InferOk, TypeOrigin, TypeTrace};
|
||||
@ -220,7 +220,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||
-> CoerceResult<'tcx>
|
||||
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
|
||||
where E: Fn() -> I,
|
||||
I: IntoIterator<Item=&'a hir::Expr> {
|
||||
I: IntoIterator<Item=&'a hir::Expr>
|
||||
{
|
||||
|
||||
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
|
||||
|
||||
@ -240,18 +241,16 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||
|
||||
let span = self.origin.span();
|
||||
|
||||
let lvalue_pref = LvaluePreference::from_mutbl(mt_b.mutbl);
|
||||
let mut first_error = None;
|
||||
let mut r_borrow_var = None;
|
||||
let (_, autoderefs, success) = self.autoderef(span, a, exprs,
|
||||
UnresolvedTypeAction::Ignore,
|
||||
lvalue_pref,
|
||||
|referent_ty, autoderef|
|
||||
{
|
||||
if autoderef == 0 {
|
||||
let mut autoderef = self.autoderef(span, a);
|
||||
let mut success = None;
|
||||
|
||||
for (referent_ty, autoderefs) in autoderef.by_ref() {
|
||||
if autoderefs == 0 {
|
||||
// Don't let this pass, otherwise it would cause
|
||||
// &T to autoref to &&T.
|
||||
return None;
|
||||
continue
|
||||
}
|
||||
|
||||
// At this point, we have deref'd `a` to `referent_ty`. So
|
||||
@ -326,7 +325,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||
// and let regionck figure it out.
|
||||
let r = if !self.use_lub {
|
||||
r_b // [2] above
|
||||
} else if autoderef == 1 {
|
||||
} else if autoderefs == 1 {
|
||||
r_a // [3] above
|
||||
} else {
|
||||
if r_borrow_var.is_none() { // create var lazilly, at most once
|
||||
@ -341,23 +340,22 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||
mutbl: mt_b.mutbl // [1] above
|
||||
});
|
||||
match self.unify(derefd_ty_a, b) {
|
||||
Ok(ty) => Some(ty),
|
||||
Ok(ty) => { success = Some((ty, autoderefs)); break },
|
||||
Err(err) => {
|
||||
if first_error.is_none() {
|
||||
first_error = Some(err);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Extract type or return an error. We return the first error
|
||||
// we got, which should be from relating the "base" type
|
||||
// (e.g., in example above, the failure from relating `Vec<T>`
|
||||
// to the target type), since that should be the least
|
||||
// confusing.
|
||||
let ty = match success {
|
||||
Some(ty) => ty,
|
||||
let (ty, autoderefs) = match success {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
let err = first_error.expect("coerce_borrowed_pointer had no error");
|
||||
debug!("coerce_borrowed_pointer: failed with err = {:?}", err);
|
||||
@ -365,6 +363,10 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
// This commits the obligations to the fulfillcx. After this succeeds,
|
||||
// this snapshot can't be rolled back.
|
||||
autoderef.finalize(LvaluePreference::from_mutbl(mt_b.mutbl), exprs());
|
||||
|
||||
// Now apply the autoref. We have to extract the region out of
|
||||
// the final ref type we got.
|
||||
if ty == a && mt_a.mutbl == hir::MutImmutable && autoderefs == 1 {
|
||||
|
@ -279,78 +279,63 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
// type.
|
||||
|
||||
// Compute skolemized form of impl and trait method tys.
|
||||
let impl_fty = tcx.mk_fn_ptr(impl_m.fty);
|
||||
let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
|
||||
let trait_fty = tcx.mk_fn_ptr(trait_m.fty);
|
||||
let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
|
||||
let tcx = infcx.tcx;
|
||||
let origin = TypeOrigin::MethodCompatCheck(impl_m_span);
|
||||
|
||||
let err = infcx.commit_if_ok(|snapshot| {
|
||||
let tcx = infcx.tcx;
|
||||
let origin = TypeOrigin::MethodCompatCheck(impl_m_span);
|
||||
let (impl_sig, _) =
|
||||
infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
|
||||
infer::HigherRankedType,
|
||||
&impl_m.fty.sig);
|
||||
let impl_sig =
|
||||
impl_sig.subst(tcx, impl_to_skol_substs);
|
||||
let impl_sig =
|
||||
assoc::normalize_associated_types_in(&infcx,
|
||||
&mut fulfillment_cx,
|
||||
impl_m_span,
|
||||
impl_m_body_id,
|
||||
&impl_sig);
|
||||
let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
||||
unsafety: impl_m.fty.unsafety,
|
||||
abi: impl_m.fty.abi,
|
||||
sig: ty::Binder(impl_sig)
|
||||
}));
|
||||
debug!("compare_impl_method: impl_fty={:?}", impl_fty);
|
||||
|
||||
let (impl_sig, _) =
|
||||
infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
|
||||
infer::HigherRankedType,
|
||||
&impl_m.fty.sig);
|
||||
let impl_sig =
|
||||
impl_sig.subst(tcx, impl_to_skol_substs);
|
||||
let impl_sig =
|
||||
assoc::normalize_associated_types_in(&infcx,
|
||||
&mut fulfillment_cx,
|
||||
impl_m_span,
|
||||
impl_m_body_id,
|
||||
&impl_sig);
|
||||
let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
||||
unsafety: impl_m.fty.unsafety,
|
||||
abi: impl_m.fty.abi,
|
||||
sig: ty::Binder(impl_sig)
|
||||
}));
|
||||
debug!("compare_impl_method: impl_fty={:?}",
|
||||
impl_fty);
|
||||
let trait_sig = tcx.liberate_late_bound_regions(
|
||||
infcx.parameter_environment.free_id_outlive,
|
||||
&trait_m.fty.sig);
|
||||
let trait_sig =
|
||||
trait_sig.subst(tcx, &trait_to_skol_substs);
|
||||
let trait_sig =
|
||||
assoc::normalize_associated_types_in(&infcx,
|
||||
&mut fulfillment_cx,
|
||||
impl_m_span,
|
||||
impl_m_body_id,
|
||||
&trait_sig);
|
||||
let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
||||
unsafety: trait_m.fty.unsafety,
|
||||
abi: trait_m.fty.abi,
|
||||
sig: ty::Binder(trait_sig)
|
||||
}));
|
||||
|
||||
let (trait_sig, skol_map) =
|
||||
infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
|
||||
let trait_sig =
|
||||
trait_sig.subst(tcx, &trait_to_skol_substs);
|
||||
let trait_sig =
|
||||
assoc::normalize_associated_types_in(&infcx,
|
||||
&mut fulfillment_cx,
|
||||
impl_m_span,
|
||||
impl_m_body_id,
|
||||
&trait_sig);
|
||||
let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
||||
unsafety: trait_m.fty.unsafety,
|
||||
abi: trait_m.fty.abi,
|
||||
sig: ty::Binder(trait_sig)
|
||||
}));
|
||||
debug!("compare_impl_method: trait_fty={:?}", trait_fty);
|
||||
|
||||
debug!("compare_impl_method: trait_fty={:?}",
|
||||
if let Err(terr) = infcx.sub_types(false, origin, impl_fty, trait_fty) {
|
||||
debug!("sub_types failed: impl ty {:?}, trait ty {:?}",
|
||||
impl_fty,
|
||||
trait_fty);
|
||||
|
||||
infcx.sub_types(false, origin, impl_fty, trait_fty)?;
|
||||
|
||||
infcx.leak_check(false, &skol_map, snapshot)
|
||||
});
|
||||
|
||||
match err {
|
||||
Ok(()) => { }
|
||||
Err(terr) => {
|
||||
debug!("checking trait method for compatibility: impl ty {:?}, trait ty {:?}",
|
||||
impl_fty,
|
||||
trait_fty);
|
||||
span_err!(tcx.sess, impl_m_span, E0053,
|
||||
"method `{}` has an incompatible type for trait: {}",
|
||||
trait_m.name,
|
||||
terr);
|
||||
return;
|
||||
}
|
||||
span_err!(tcx.sess, impl_m_span, E0053,
|
||||
"method `{}` has an incompatible type for trait: {}",
|
||||
trait_m.name,
|
||||
terr);
|
||||
return
|
||||
}
|
||||
|
||||
// Check that all obligations are satisfied by the implementation's
|
||||
// version.
|
||||
match fulfillment_cx.select_all_or_error(&infcx) {
|
||||
Err(ref errors) => { infcx.report_fulfillment_errors(errors) }
|
||||
Ok(_) => {}
|
||||
if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) {
|
||||
infcx.report_fulfillment_errors(errors);
|
||||
return
|
||||
}
|
||||
|
||||
// Finally, resolve all regions. This catches wily misuses of
|
||||
|
@ -11,11 +11,10 @@
|
||||
use super::probe;
|
||||
|
||||
use check::{FnCtxt, callee};
|
||||
use check::UnresolvedTypeAction;
|
||||
use hir::def_id::DefId;
|
||||
use rustc::ty::subst::{self};
|
||||
use rustc::traits;
|
||||
use rustc::ty::{self, NoPreference, PreferMutLvalue, Ty};
|
||||
use rustc::ty::{self, LvaluePreference, NoPreference, PreferMutLvalue, Ty};
|
||||
use rustc::ty::adjustment::{AdjustDerefRef, AutoDerefRef, AutoPtr};
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::infer::{self, InferOk, TypeOrigin};
|
||||
@ -133,10 +132,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
ty: fty,
|
||||
substs: all_substs
|
||||
};
|
||||
// If this is an `&mut self` method, bias the receiver
|
||||
// expression towards mutability (this will switch
|
||||
// e.g. `Deref` to `DerefMut` in overloaded derefs and so on).
|
||||
self.fixup_derefs_on_method_receiver_if_necessary(&callee);
|
||||
|
||||
if let Some(hir::MutMutable) = pick.autoref {
|
||||
self.convert_lvalue_derefs_to_mutable();
|
||||
}
|
||||
|
||||
callee
|
||||
}
|
||||
@ -164,22 +163,14 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
// Commit the autoderefs by calling `autoderef again, but this
|
||||
// Commit the autoderefs by calling `autoderef` again, but this
|
||||
// time writing the results into the various tables.
|
||||
let (autoderefd_ty, n, result) = self.autoderef(self.span,
|
||||
unadjusted_self_ty,
|
||||
|| Some(self.self_expr),
|
||||
UnresolvedTypeAction::Error,
|
||||
NoPreference,
|
||||
|_, n| {
|
||||
if n == pick.autoderefs {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let mut autoderef = self.autoderef(self.span, unadjusted_self_ty);
|
||||
let (autoderefd_ty, n) = autoderef.nth(pick.autoderefs).unwrap();
|
||||
assert_eq!(n, pick.autoderefs);
|
||||
assert_eq!(result, Some(()));
|
||||
|
||||
autoderef.unambiguous_final_ty();
|
||||
autoderef.finalize(LvaluePreference::NoPreference, Some(self.self_expr));
|
||||
|
||||
// Write out the final adjustment.
|
||||
self.write_adjustment(self.self_expr.id, AdjustDerefRef(AutoDerefRef {
|
||||
@ -293,27 +284,21 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
// yield an object-type (e.g., `&Object` or `Box<Object>`
|
||||
// etc).
|
||||
|
||||
let (_, _, result) = self.fcx.autoderef(self.span,
|
||||
self_ty,
|
||||
|| None,
|
||||
UnresolvedTypeAction::Error,
|
||||
NoPreference,
|
||||
|ty, _| {
|
||||
match ty.sty {
|
||||
ty::TyTrait(ref data) => Some(closure(self, ty, &data)),
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
|
||||
match result {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
// FIXME: this feels, like, super dubious
|
||||
self.fcx.autoderef(self.span, self_ty)
|
||||
.filter_map(|(ty, _)| {
|
||||
match ty.sty {
|
||||
ty::TyTrait(ref data) => Some(closure(self, ty, &data)),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.next()
|
||||
.unwrap_or_else(|| {
|
||||
span_bug!(
|
||||
self.span,
|
||||
"self-type `{}` for ObjectPick never dereferenced to an object",
|
||||
self_ty)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn instantiate_method_substs(&mut self,
|
||||
@ -463,24 +448,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// RECONCILIATION
|
||||
|
||||
/// When we select a method with an `&mut self` receiver, we have to go convert any
|
||||
/// When we select a method with a mutable autoref, we have to go convert any
|
||||
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
|
||||
/// respectively.
|
||||
fn fixup_derefs_on_method_receiver_if_necessary(&self,
|
||||
method_callee: &ty::MethodCallee) {
|
||||
let sig = match method_callee.ty.sty {
|
||||
ty::TyFnDef(_, _, ref f) => f.sig.clone(),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
match sig.0.inputs[0].sty {
|
||||
ty::TyRef(_, ty::TypeAndMut {
|
||||
ty: _,
|
||||
mutbl: hir::MutMutable,
|
||||
}) => {}
|
||||
_ => return,
|
||||
}
|
||||
|
||||
fn convert_lvalue_derefs_to_mutable(&self) {
|
||||
// Gather up expressions we want to munge.
|
||||
let mut exprs = Vec::new();
|
||||
exprs.push(self.self_expr);
|
||||
@ -495,8 +466,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
debug!("fixup_derefs_on_method_receiver_if_necessary: exprs={:?}",
|
||||
exprs);
|
||||
debug!("convert_lvalue_derefs_to_mutable: exprs={:?}", exprs);
|
||||
|
||||
// Fix up autoderefs and derefs.
|
||||
for (i, &expr) in exprs.iter().rev().enumerate() {
|
||||
@ -509,23 +479,17 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
Some(_) | None => 0,
|
||||
};
|
||||
|
||||
debug!("fixup_derefs_on_method_receiver_if_necessary: i={} expr={:?} \
|
||||
autoderef_count={}",
|
||||
debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?} \
|
||||
autoderef_count={}",
|
||||
i, expr, autoderef_count);
|
||||
|
||||
if autoderef_count > 0 {
|
||||
self.autoderef(expr.span,
|
||||
self.expr_ty(expr),
|
||||
|| Some(expr),
|
||||
UnresolvedTypeAction::Error,
|
||||
PreferMutLvalue,
|
||||
|_, autoderefs| {
|
||||
if autoderefs == autoderef_count + 1 {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let mut autoderef = self.autoderef(expr.span, self.expr_ty(expr));
|
||||
autoderef.nth(autoderef_count).unwrap_or_else(|| {
|
||||
span_bug!(expr.span, "expr was deref-able {} times but now isn't?",
|
||||
autoderef_count);
|
||||
});
|
||||
autoderef.finalize(PreferMutLvalue, Some(expr));
|
||||
}
|
||||
|
||||
// Don't retry the first one or we might infinite loop!
|
||||
|
@ -13,13 +13,13 @@ use super::NoMatchData;
|
||||
use super::{CandidateSource, ImplSource, TraitSource};
|
||||
use super::suggest;
|
||||
|
||||
use check::{FnCtxt, UnresolvedTypeAction};
|
||||
use check::{FnCtxt};
|
||||
use hir::def_id::DefId;
|
||||
use hir::def::Def;
|
||||
use rustc::ty::subst;
|
||||
use rustc::ty::subst::Subst;
|
||||
use rustc::traits;
|
||||
use rustc::ty::{self, NoPreference, Ty, ToPolyTraitRef, TraitRef, TypeFoldable};
|
||||
use rustc::ty::{self, Ty, ToPolyTraitRef, TraitRef, TypeFoldable};
|
||||
use rustc::infer::{InferOk, TypeOrigin};
|
||||
use syntax::ast;
|
||||
use syntax::codemap::{Span, DUMMY_SP};
|
||||
@ -208,25 +208,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
fn create_steps(&self,
|
||||
span: Span,
|
||||
self_ty: Ty<'tcx>)
|
||||
-> Option<Vec<CandidateStep<'tcx>>> {
|
||||
let mut steps = Vec::new();
|
||||
-> Option<Vec<CandidateStep<'tcx>>>
|
||||
{
|
||||
// FIXME: we don't need to create the entire steps in one pass
|
||||
|
||||
let (final_ty, dereferences, _) = self.autoderef(span,
|
||||
self_ty,
|
||||
|| None,
|
||||
UnresolvedTypeAction::Error,
|
||||
NoPreference,
|
||||
|t, d| {
|
||||
steps.push(CandidateStep {
|
||||
self_ty: t,
|
||||
autoderefs: d,
|
||||
unsize: false
|
||||
});
|
||||
None::<()> // keep iterating until we can't anymore
|
||||
});
|
||||
let mut autoderef = self.autoderef(span, self_ty);
|
||||
let mut steps: Vec<_> = autoderef.by_ref().map(|(ty, d)| CandidateStep {
|
||||
self_ty: ty,
|
||||
autoderefs: d,
|
||||
unsize: false
|
||||
}).collect();
|
||||
|
||||
let final_ty = autoderef.unambiguous_final_ty();
|
||||
match final_ty.sty {
|
||||
ty::TyArray(elem_ty, _) => {
|
||||
let dereferences = steps.len() - 1;
|
||||
|
||||
steps.push(CandidateStep {
|
||||
self_ty: self.tcx.mk_slice(elem_ty),
|
||||
autoderefs: dereferences,
|
||||
@ -237,6 +234,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
debug!("create_steps: steps={:?}", steps);
|
||||
|
||||
Some(steps)
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
use CrateCtxt;
|
||||
|
||||
use check::{self, FnCtxt, UnresolvedTypeAction};
|
||||
use check::{FnCtxt};
|
||||
use rustc::hir::map as hir_map;
|
||||
use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable};
|
||||
use middle::cstore;
|
||||
@ -21,7 +21,6 @@ use hir::def::Def;
|
||||
use hir::def_id::DefId;
|
||||
use middle::lang_items::FnOnceTraitLangItem;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::LvaluePreference;
|
||||
use rustc::traits::{Obligation, SelectionContext};
|
||||
use util::nodemap::{FnvHashSet};
|
||||
|
||||
@ -48,42 +47,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true,
|
||||
// If it's not a simple function, look for things which implement FnOnce
|
||||
_ => {
|
||||
if let Ok(fn_once_trait_did) =
|
||||
tcx.lang_items.require(FnOnceTraitLangItem) {
|
||||
let (_, _, opt_is_fn) = self.autoderef(span,
|
||||
ty,
|
||||
|| None,
|
||||
UnresolvedTypeAction::Ignore,
|
||||
LvaluePreference::NoPreference,
|
||||
|ty, _| {
|
||||
self.probe(|_| {
|
||||
let fn_once_substs =
|
||||
Substs::new_trait(vec![self.next_ty_var()], vec![], ty);
|
||||
let trait_ref =
|
||||
ty::TraitRef::new(fn_once_trait_did,
|
||||
tcx.mk_substs(fn_once_substs));
|
||||
let poly_trait_ref = trait_ref.to_poly_trait_ref();
|
||||
let obligation = Obligation::misc(span,
|
||||
self.body_id,
|
||||
poly_trait_ref
|
||||
.to_predicate());
|
||||
let mut selcx = SelectionContext::new(self);
|
||||
let fn_once = match tcx.lang_items.require(FnOnceTraitLangItem) {
|
||||
Ok(fn_once) => fn_once,
|
||||
Err(..) => return false
|
||||
};
|
||||
|
||||
if selcx.evaluate_obligation(&obligation) {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
opt_is_fn.is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
self.autoderef(span, ty).any(|(ty, _)| self.probe(|_| {
|
||||
let fn_once_substs =
|
||||
Substs::new_trait(vec![self.next_ty_var()], vec![], ty);
|
||||
let trait_ref =
|
||||
ty::TraitRef::new(fn_once,
|
||||
tcx.mk_substs(fn_once_substs));
|
||||
let poly_trait_ref = trait_ref.to_poly_trait_ref();
|
||||
let obligation = Obligation::misc(span,
|
||||
self.body_id,
|
||||
poly_trait_ref
|
||||
.to_predicate());
|
||||
SelectionContext::new(self).evaluate_obligation(&obligation)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_method_error(&self,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
@ -384,15 +369,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
return is_local(self.resolve_type_vars_with_obligations(rcvr_ty));
|
||||
}
|
||||
|
||||
self.autoderef(span, rcvr_ty, || None,
|
||||
check::UnresolvedTypeAction::Ignore, ty::NoPreference,
|
||||
|ty, _| {
|
||||
if is_local(ty) {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).2.is_some()
|
||||
self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,7 @@ use rustc_back::slice;
|
||||
use rustc_const_eval::eval_repeat_count;
|
||||
|
||||
mod assoc;
|
||||
mod autoderef;
|
||||
pub mod dropck;
|
||||
pub mod _match;
|
||||
pub mod writeback;
|
||||
@ -1412,17 +1413,6 @@ impl<'a, 'gcx, 'tcx> RegionScope for FnCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether `autoderef` requires types to resolve.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum UnresolvedTypeAction {
|
||||
/// Produce an error and return `TyError` whenever a type cannot
|
||||
/// be resolved (i.e. it is `TyInfer`).
|
||||
Error,
|
||||
/// Go on without emitting any errors, and return the unresolved
|
||||
/// type. Useful for probing, e.g. in coercions.
|
||||
Ignore
|
||||
}
|
||||
|
||||
/// Controls whether the arguments are tupled. This is used for the call
|
||||
/// operator.
|
||||
///
|
||||
@ -2228,120 +2218,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes an autoderef loop for the type `t`. At each step, invokes `should_stop`
|
||||
/// to decide whether to terminate the loop. Returns the final type and number of
|
||||
/// derefs that it performed.
|
||||
///
|
||||
/// Note: this method does not modify the adjustments table. The caller is responsible for
|
||||
/// inserting an AutoAdjustment record into the `self` using one of the suitable methods.
|
||||
pub fn autoderef<'b, E, I, T, F>(&self,
|
||||
sp: Span,
|
||||
base_ty: Ty<'tcx>,
|
||||
maybe_exprs: E,
|
||||
unresolved_type_action: UnresolvedTypeAction,
|
||||
mut lvalue_pref: LvaluePreference,
|
||||
mut should_stop: F)
|
||||
-> (Ty<'tcx>, usize, Option<T>)
|
||||
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
|
||||
where E: Fn() -> I,
|
||||
I: IntoIterator<Item=&'b hir::Expr>,
|
||||
F: FnMut(Ty<'tcx>, usize) -> Option<T>,
|
||||
{
|
||||
debug!("autoderef(base_ty={:?}, lvalue_pref={:?})",
|
||||
base_ty, lvalue_pref);
|
||||
|
||||
let mut t = base_ty;
|
||||
for autoderefs in 0..self.tcx.sess.recursion_limit.get() {
|
||||
let resolved_t = match unresolved_type_action {
|
||||
UnresolvedTypeAction::Error => {
|
||||
self.structurally_resolved_type(sp, t)
|
||||
}
|
||||
UnresolvedTypeAction::Ignore => {
|
||||
// We can continue even when the type cannot be resolved
|
||||
// (i.e. it is an inference variable) because `Ty::builtin_deref`
|
||||
// and `try_overloaded_deref` both simply return `None`
|
||||
// in such a case without producing spurious errors.
|
||||
self.resolve_type_vars_if_possible(&t)
|
||||
}
|
||||
};
|
||||
if resolved_t.references_error() {
|
||||
return (resolved_t, autoderefs, None);
|
||||
}
|
||||
|
||||
match should_stop(resolved_t, autoderefs) {
|
||||
Some(x) => return (resolved_t, autoderefs, Some(x)),
|
||||
None => {}
|
||||
}
|
||||
|
||||
// Otherwise, deref if type is derefable:
|
||||
|
||||
// Super subtle: it might seem as though we should
|
||||
// pass `opt_expr` to `try_overloaded_deref`, so that
|
||||
// the (implicit) autoref of using an overloaded deref
|
||||
// would get added to the adjustment table. However we
|
||||
// do not do that, because it's kind of a
|
||||
// "meta-adjustment" -- instead, we just leave it
|
||||
// unrecorded and know that there "will be" an
|
||||
// autoref. regionck and other bits of the code base,
|
||||
// when they encounter an overloaded autoderef, have
|
||||
// to do some reconstructive surgery. This is a pretty
|
||||
// complex mess that is begging for a proper MIR.
|
||||
let mt = if let Some(mt) = resolved_t.builtin_deref(false, lvalue_pref) {
|
||||
mt
|
||||
} else if let Some(method) = self.try_overloaded_deref(sp, None,
|
||||
resolved_t, lvalue_pref) {
|
||||
for expr in maybe_exprs() {
|
||||
let method_call = MethodCall::autoderef(expr.id, autoderefs as u32);
|
||||
self.tables.borrow_mut().method_map.insert(method_call, method);
|
||||
}
|
||||
self.make_overloaded_lvalue_return_type(method)
|
||||
} else {
|
||||
return (resolved_t, autoderefs, None);
|
||||
};
|
||||
|
||||
t = mt.ty;
|
||||
if mt.mutbl == hir::MutImmutable {
|
||||
lvalue_pref = NoPreference;
|
||||
}
|
||||
}
|
||||
|
||||
// We've reached the recursion limit, error gracefully.
|
||||
span_err!(self.tcx.sess, sp, E0055,
|
||||
"reached the recursion limit while auto-dereferencing {:?}",
|
||||
base_ty);
|
||||
(self.tcx.types.err, 0, None)
|
||||
}
|
||||
|
||||
fn try_overloaded_deref(&self,
|
||||
span: Span,
|
||||
base_expr: Option<&hir::Expr>,
|
||||
base_ty: Ty<'tcx>,
|
||||
lvalue_pref: LvaluePreference)
|
||||
-> Option<MethodCallee<'tcx>>
|
||||
{
|
||||
// Try DerefMut first, if preferred.
|
||||
let method = match (lvalue_pref, self.tcx.lang_items.deref_mut_trait()) {
|
||||
(PreferMutLvalue, Some(trait_did)) => {
|
||||
self.lookup_method_in_trait(span, base_expr,
|
||||
token::intern("deref_mut"), trait_did,
|
||||
base_ty, None)
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
|
||||
// Otherwise, fall back to Deref.
|
||||
let method = match (method, self.tcx.lang_items.deref_trait()) {
|
||||
(None, Some(trait_did)) => {
|
||||
self.lookup_method_in_trait(span, base_expr,
|
||||
token::intern("deref"), trait_did,
|
||||
base_ty, None)
|
||||
}
|
||||
(method, _) => method
|
||||
};
|
||||
|
||||
method
|
||||
}
|
||||
|
||||
/// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait
|
||||
/// returns a type of `&T`, but the actual type we assign to the
|
||||
/// *expression* is `T`. So this function just peels off the return
|
||||
@ -2371,29 +2247,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// autoderef that normal method probing does. They could likely be
|
||||
// consolidated.
|
||||
|
||||
let (ty, autoderefs, final_mt) = self.autoderef(base_expr.span,
|
||||
base_ty,
|
||||
|| Some(base_expr),
|
||||
UnresolvedTypeAction::Error,
|
||||
lvalue_pref,
|
||||
|adj_ty, idx| {
|
||||
self.try_index_step(MethodCall::expr(expr.id), expr, base_expr,
|
||||
adj_ty, idx, false, lvalue_pref, idx_ty)
|
||||
});
|
||||
let mut autoderef = self.autoderef(base_expr.span, base_ty);
|
||||
|
||||
if final_mt.is_some() {
|
||||
return final_mt;
|
||||
}
|
||||
while let Some((adj_ty, autoderefs)) = autoderef.next() {
|
||||
if let Some(final_mt) = self.try_index_step(
|
||||
MethodCall::expr(expr.id),
|
||||
expr, base_expr, adj_ty, autoderefs,
|
||||
false, lvalue_pref, idx_ty)
|
||||
{
|
||||
autoderef.finalize(lvalue_pref, Some(base_expr));
|
||||
return Some(final_mt);
|
||||
}
|
||||
|
||||
// After we have fully autoderef'd, if the resulting type is [T; n], then
|
||||
// do a final unsized coercion to yield [T].
|
||||
if let ty::TyArray(element_ty, _) = ty.sty {
|
||||
let adjusted_ty = self.tcx.mk_slice(element_ty);
|
||||
self.try_index_step(MethodCall::expr(expr.id), expr, base_expr,
|
||||
adjusted_ty, autoderefs, true, lvalue_pref, idx_ty)
|
||||
} else {
|
||||
None
|
||||
if let ty::TyArray(element_ty, _) = adj_ty.sty {
|
||||
autoderef.finalize(lvalue_pref, Some(base_expr));
|
||||
let adjusted_ty = self.tcx.mk_slice(element_ty);
|
||||
return self.try_index_step(
|
||||
MethodCall::expr(expr.id), expr, base_expr,
|
||||
adjusted_ty, autoderefs, true, lvalue_pref, idx_ty);
|
||||
}
|
||||
}
|
||||
autoderef.unambiguous_final_ty();
|
||||
None
|
||||
}
|
||||
|
||||
/// To type-check `base_expr[index_expr]`, we progressively autoderef
|
||||
@ -3034,32 +2909,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
let expr_t = self.structurally_resolved_type(expr.span,
|
||||
self.expr_ty(base));
|
||||
let mut private_candidate = None;
|
||||
let (_, autoderefs, field_ty) = self.autoderef(expr.span,
|
||||
expr_t,
|
||||
|| Some(base),
|
||||
UnresolvedTypeAction::Error,
|
||||
lvalue_pref,
|
||||
|base_t, _| {
|
||||
if let ty::TyStruct(base_def, substs) = base_t.sty {
|
||||
debug!("struct named {:?}", base_t);
|
||||
if let Some(field) = base_def.struct_variant().find_field_named(field.node) {
|
||||
let field_ty = self.field_ty(expr.span, field, substs);
|
||||
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
||||
return Some(field_ty);
|
||||
}
|
||||
private_candidate = Some((base_def.did, field_ty));
|
||||
let mut autoderef = self.autoderef(expr.span, expr_t);
|
||||
while let Some((base_t, autoderefs)) = autoderef.next() {
|
||||
if let ty::TyStruct(base_def, substs) = base_t.sty {
|
||||
debug!("struct named {:?}", base_t);
|
||||
if let Some(field) = base_def.struct_variant().find_field_named(field.node) {
|
||||
let field_ty = self.field_ty(expr.span, field, substs);
|
||||
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
||||
autoderef.finalize(lvalue_pref, Some(base));
|
||||
self.write_ty(expr.id, field_ty);
|
||||
self.write_autoderef_adjustment(base.id, autoderefs);
|
||||
return;
|
||||
}
|
||||
private_candidate = Some((base_def.did, field_ty));
|
||||
}
|
||||
None
|
||||
});
|
||||
match field_ty {
|
||||
Some(field_ty) => {
|
||||
self.write_ty(expr.id, field_ty);
|
||||
self.write_autoderef_adjustment(base.id, autoderefs);
|
||||
return;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
autoderef.unambiguous_final_ty();
|
||||
|
||||
if let Some((did, field_ty)) = private_candidate {
|
||||
let struct_path = self.tcx().item_path_str(did);
|
||||
@ -3132,42 +2998,39 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
self.expr_ty(base));
|
||||
let mut private_candidate = None;
|
||||
let mut tuple_like = false;
|
||||
let (_, autoderefs, field_ty) = self.autoderef(expr.span,
|
||||
expr_t,
|
||||
|| Some(base),
|
||||
UnresolvedTypeAction::Error,
|
||||
lvalue_pref,
|
||||
|base_t, _| {
|
||||
let (base_def, substs) = match base_t.sty {
|
||||
ty::TyStruct(base_def, substs) => (base_def, substs),
|
||||
ty::TyTuple(ref v) => {
|
||||
tuple_like = true;
|
||||
return if idx.node < v.len() { Some(v[idx.node]) } else { None }
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let mut autoderef = self.autoderef(expr.span, expr_t);
|
||||
while let Some((base_t, autoderefs)) = autoderef.next() {
|
||||
let field = match base_t.sty {
|
||||
ty::TyStruct(base_def, substs) => {
|
||||
tuple_like = base_def.struct_variant().is_tuple_struct();
|
||||
if !tuple_like { continue }
|
||||
|
||||
tuple_like = base_def.struct_variant().is_tuple_struct();
|
||||
if !tuple_like { return None }
|
||||
|
||||
debug!("tuple struct named {:?}", base_t);
|
||||
if let Some(field) = base_def.struct_variant().fields.get(idx.node) {
|
||||
let field_ty = self.field_ty(expr.span, field, substs);
|
||||
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
||||
return Some(field_ty);
|
||||
}
|
||||
private_candidate = Some((base_def.did, field_ty));
|
||||
debug!("tuple struct named {:?}", base_t);
|
||||
base_def.struct_variant().fields.get(idx.node).and_then(|field| {
|
||||
let field_ty = self.field_ty(expr.span, field, substs);
|
||||
private_candidate = Some((base_def.did, field_ty));
|
||||
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
||||
Some(field_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
None
|
||||
});
|
||||
match field_ty {
|
||||
Some(field_ty) => {
|
||||
ty::TyTuple(ref v) => {
|
||||
tuple_like = true;
|
||||
v.get(idx.node).cloned()
|
||||
}
|
||||
_ => continue
|
||||
};
|
||||
|
||||
if let Some(field_ty) = field {
|
||||
autoderef.finalize(lvalue_pref, Some(base));
|
||||
self.write_ty(expr.id, field_ty);
|
||||
self.write_autoderef_adjustment(base.id, autoderefs);
|
||||
return;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
autoderef.unambiguous_final_ty();
|
||||
|
||||
if let Some((did, field_ty)) = private_candidate {
|
||||
let struct_path = self.tcx().item_path_str(did);
|
||||
|
@ -19,5 +19,4 @@ fn main() {
|
||||
let foo = Foo;
|
||||
let ref_foo = &&Foo;
|
||||
ref_foo.foo(); //~ ERROR E0055
|
||||
//~^ ERROR E0275
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ fn assign_field1<'a>(x: Own<Point>) {
|
||||
}
|
||||
|
||||
fn assign_field2<'a>(x: &'a Own<Point>) {
|
||||
x.y = 3; //~ ERROR cannot assign
|
||||
x.y = 3; //~ ERROR cannot borrow
|
||||
}
|
||||
|
||||
fn assign_field3<'a>(x: &'a mut Own<Point>) {
|
||||
|
21
src/test/compile-fail/issue-24819.rs
Normal file
21
src/test/compile-fail/issue-24819.rs
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn main() {
|
||||
let mut v = Vec::new();
|
||||
foo(&mut v);
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected struct `std::collections::HashSet`, found struct `std::vec::Vec`
|
||||
}
|
||||
|
||||
fn foo(h: &mut HashSet<u32>) {
|
||||
}
|
@ -34,7 +34,8 @@ impl<'a, 't> Foo<'a, 't> for &'a isize {
|
||||
}
|
||||
|
||||
fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
|
||||
//~^ ERROR method `wrong_bound1` has an incompatible type for trait
|
||||
//~^ ERROR method not compatible with trait
|
||||
//~^^ ERROR method not compatible with trait
|
||||
//
|
||||
// Note: This is a terrible error message. It is caused
|
||||
// because, in the trait, 'b is early bound, and in the impl,
|
||||
|
@ -23,7 +23,7 @@ impl<'a> get_ctxt for has_ctxt<'a> {
|
||||
|
||||
// Here an error occurs because we used `&self` but
|
||||
// the definition used `&`:
|
||||
fn get_ctxt(&self) -> &'a ctxt { //~ ERROR method `get_ctxt` has an incompatible type
|
||||
fn get_ctxt(&self) -> &'a ctxt { //~ ERROR method not compatible with trait
|
||||
self.c
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user