From f8403aac81d5720bb722a85a9120c14ceb763eb9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 5 Nov 2014 22:06:04 -0500 Subject: [PATCH 1/7] Rewrite method resolution to be cleaner, more correct, and to lay groundwork for better performance. Key points: - Separate out determining which method to use from actually selecting a method (this should enable caching, as well as the pcwalton fast-reject strategy). - Merge the impl selection back into method resolution and don't rely on trait matching (this should perform better but also is needed to resolve some kind of conflicts, see e.g. `method-two-traits-distinguished-via-where-clause.rs`) - Purge a lot of out-of-date junk and coercions from method lookups. --- src/librustc/lib.rs | 2 +- src/librustc/middle/traits/mod.rs | 10 + src/librustc/middle/traits/select.rs | 3 +- src/librustc/middle/ty.rs | 60 +- src/librustc/middle/typeck/check/method.rs | 1903 ----------------- .../middle/typeck/check/method/confirm.rs | 603 ++++++ .../middle/typeck/check/method/doc.rs | 126 ++ .../middle/typeck/check/method/mod.rs | 432 ++++ .../middle/typeck/check/method/probe.rs | 1011 +++++++++ src/librustc/middle/typeck/check/mod.rs | 96 +- .../middle/typeck/infer/error_reporting.rs | 4 +- src/librustc/middle/typeck/infer/glb.rs | 13 +- src/librustc/middle/typeck/infer/lub.rs | 9 +- src/librustc/middle/typeck/infer/mod.rs | 60 +- src/librustc/middle/typeck/infer/sub.rs | 5 +- src/libsyntax/ast_map/mod.rs | 2 +- src/test/compile-fail/issue-16683.rs | 2 +- src/test/compile-fail/issue-17758.rs | 2 +- .../method-ambig-one-trait-coerce.rs | 10 +- .../compile-fail/method-commit-to-trait.rs | 33 - .../unboxed-closures-type-mismatch.rs | 2 +- .../run-pass/method-recursive-blanket-impl.rs | 46 + ...o-traits-distinguished-via-where-clause.rs | 34 + 23 files changed, 2397 insertions(+), 2071 deletions(-) delete mode 100644 src/librustc/middle/typeck/check/method.rs create mode 100644 src/librustc/middle/typeck/check/method/confirm.rs create mode 100644 src/librustc/middle/typeck/check/method/doc.rs create mode 100644 src/librustc/middle/typeck/check/method/mod.rs create mode 100644 src/librustc/middle/typeck/check/method/probe.rs delete mode 100644 src/test/compile-fail/method-commit-to-trait.rs create mode 100644 src/test/run-pass/method-recursive-blanket-impl.rs create mode 100644 src/test/run-pass/method-two-traits-distinguished-via-where-clause.rs diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 7a34c5977d6..ce736a344d5 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -29,7 +29,7 @@ This API is completely unstable and subject to change. html_root_url = "http://doc.rust-lang.org/nightly/")] #![feature(default_type_params, globs, if_let, import_shadowing, macro_rules, phase, quote)] -#![feature(slicing_syntax, struct_variant, unsafe_destructor)] +#![feature(slicing_syntax, struct_variant, tuple_indexing, unsafe_destructor)] #![feature(rustc_diagnostic_macros)] extern crate arena; diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 1db8cf6baf3..9e0abb897f7 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -281,6 +281,16 @@ pub fn overlapping_impls(infcx: &InferCtxt, coherence::impl_can_satisfy(infcx, impl2_def_id, impl1_def_id) } +pub fn impl_obligations(tcx: &ty::ctxt, + cause: ObligationCause, + impl_def_id: ast::DefId, + impl_substs: &subst::Substs) + -> subst::VecPerParamSpace +{ + let impl_generics = ty::lookup_item_type(tcx, impl_def_id).generics; + obligations_for_generics(tcx, cause, &impl_generics, impl_substs) +} + pub fn obligations_for_generics(tcx: &ty::ctxt, cause: ObligationCause, generics: &ty::Generics, diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 9ab2f948f9d..7ea58be3480 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1714,7 +1714,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { closure_type.sig.binder_id, &closure_type.sig, |br| self.infcx.next_region_var( - infer::LateBoundRegion(obligation.cause.span, br))); + infer::LateBoundRegion(obligation.cause.span, br, + infer::FnCall))); let arguments_tuple = new_signature.inputs[0]; let trait_ref = Rc::new(ty::TraitRef { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 134dac9d3a7..52b29ff8b2e 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3491,43 +3491,45 @@ pub fn adjust_ty(cx: &ctxt, } } - match adj.autoref { - None => adjusted_ty, - Some(ref autoref) => adjust_for_autoref(cx, span, adjusted_ty, autoref) - } + adjust_ty_for_autoref(cx, span, adjusted_ty, adj.autoref.as_ref()) } } } None => unadjusted_ty }; +} - fn adjust_for_autoref(cx: &ctxt, - span: Span, - ty: ty::t, - autoref: &AutoRef) -> ty::t{ - match *autoref { - AutoPtr(r, m, ref a) => { - let adjusted_ty = match a { - &Some(box ref a) => adjust_for_autoref(cx, span, ty, a), - &None => ty - }; - mk_rptr(cx, r, mt { - ty: adjusted_ty, - mutbl: m - }) - } +pub fn adjust_ty_for_autoref(cx: &ctxt, + span: Span, + ty: ty::t, + autoref: Option<&AutoRef>) + -> ty::t +{ + match autoref { + None => ty, - AutoUnsafe(m, ref a) => { - let adjusted_ty = match a { - &Some(box ref a) => adjust_for_autoref(cx, span, ty, a), - &None => ty - }; - mk_ptr(cx, mt {ty: adjusted_ty, mutbl: m}) - } - - AutoUnsize(ref k) => unsize_ty(cx, ty, k, span), - AutoUnsizeUniq(ref k) => ty::mk_uniq(cx, unsize_ty(cx, ty, k, span)), + Some(&AutoPtr(r, m, ref a)) => { + let adjusted_ty = match a { + &Some(box ref a) => adjust_ty_for_autoref(cx, span, ty, Some(a)), + &None => ty + }; + mk_rptr(cx, r, mt { + ty: adjusted_ty, + mutbl: m + }) } + + Some(&AutoUnsafe(m, ref a)) => { + let adjusted_ty = match a { + &Some(box ref a) => adjust_ty_for_autoref(cx, span, ty, Some(a)), + &None => ty + }; + mk_ptr(cx, mt {ty: adjusted_ty, mutbl: m}) + } + + Some(&AutoUnsize(ref k)) => unsize_ty(cx, ty, k, span), + + Some(&AutoUnsizeUniq(ref k)) => ty::mk_uniq(cx, unsize_ty(cx, ty, k, span)), } } diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs deleted file mode 100644 index 59b8309383c..00000000000 --- a/src/librustc/middle/typeck/check/method.rs +++ /dev/null @@ -1,1903 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - -# Method lookup - -Method lookup can be rather complex due to the interaction of a number -of factors, such as self types, autoderef, trait lookup, etc. The -algorithm is divided into two parts: candidate collection and -candidate selection. - -## Candidate collection - -A `Candidate` is a method item that might plausibly be the method -being invoked. Candidates are grouped into two kinds, inherent and -extension. Inherent candidates are those that are derived from the -type of the receiver itself. So, if you have a receiver of some -nominal type `Foo` (e.g., a struct), any methods defined within an -impl like `impl Foo` are inherent methods. Nothing needs to be -imported to use an inherent method, they are associated with the type -itself (note that inherent impls can only be defined in the same -module as the type itself). - -Inherent candidates are not always derived from impls. If you have a -trait instance, such as a value of type `Box`, then the trait -methods (`to_string()`, in this case) are inherently associated with it. -Another case is type parameters, in which case the methods of their -bounds are inherent. - -Extension candidates are derived from imported traits. If I have the -trait `ToString` imported, and I call `to_string()` on a value of type `T`, -then we will go off to find out whether there is an impl of `ToString` -for `T`. These kinds of method calls are called "extension methods". -They can be defined in any module, not only the one that defined `T`. -Furthermore, you must import the trait to call such a method. - -For better or worse, we currently give weight to inherent methods over -extension methods during candidate selection (below). - -## Candidate selection - -Once we know the set of candidates, we can go off and try to select -which one is actually being called. We do this by taking the type of -the receiver, let's call it R, and checking whether it matches against -the expected receiver type for each of the collected candidates. We -first check for inherent candidates and see whether we get exactly one -match (zero means keep searching, more than one is an error). If so, -we return that as the candidate. Otherwise we search the extension -candidates in the same way. - -If find no matching candidate at all, we proceed to auto-deref the -receiver type and search again. We keep doing that until we cannot -auto-deref any longer. At each step, we also check for candidates -based on "autoptr", which if the current type is `T`, checks for `&mut -T`, `&const T`, and `&T` receivers. Finally, at the very end, we will -also try autoslice, which converts `~[]` to `&[]` (there is no point -at trying autoslice earlier, because no autoderefable type is also -sliceable). - -## Why two phases? - -You might wonder why we first collect the candidates and then select. -Both the inherent candidate collection and the candidate selection -proceed by progressively deref'ing the receiver type, after all. The -answer is that two phases are needed to elegantly deal with explicit -self. After all, if there is an impl for the type `Foo`, it can -define a method with the type `Box`, which means that it expects a -receiver of type `Box`. If we have a receiver of type `Box`, but we -waited to search for that impl until we have deref'd the `Box` away and -obtained the type `Foo`, we would never match this method. - -*/ - -pub use self::CheckTraitsFlag::*; -pub use self::AutoderefReceiverFlag::*; -pub use self::MethodError::*; -pub use self::CandidateSource::*; - -use middle::subst; -use middle::subst::{Subst, SelfSpace}; -use middle::traits; -use middle::ty::*; -use middle::ty; -use middle::typeck::astconv::AstConv; -use middle::typeck::check::{FnCtxt, NoPreference, PreferMutLvalue}; -use middle::typeck::check::{impl_self_ty}; -use middle::typeck::check::vtable::select_new_fcx_obligations; -use middle::typeck::check; -use middle::typeck::infer; -use middle::typeck::{MethodCall, MethodCallee}; -use middle::typeck::{MethodOrigin, MethodParam, MethodTypeParam}; -use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure, MethodObject, MethodTraitObject}; -use middle::typeck::check::regionmanip::replace_late_bound_regions; -use middle::typeck::TypeAndSubsts; -use middle::typeck::check::vtable; -use middle::ty_fold::TypeFoldable; -use util::common::indenter; -use util::ppaux; -use util::ppaux::{Repr, UserString}; - -use std::collections::HashSet; -use std::rc::Rc; -use syntax::ast::{DefId, MutImmutable, MutMutable}; -use syntax::ast; -use syntax::codemap::Span; - -#[deriving(PartialEq)] -pub enum CheckTraitsFlag { - CheckTraitsOnly, - CheckTraitsAndInherentMethods, -} - -#[deriving(PartialEq)] -pub enum AutoderefReceiverFlag { - AutoderefReceiver, - DontAutoderefReceiver, -} - -pub enum MethodError { - // Did not find an applicable method, but we did find various - // static methods that may apply. - NoMatch(Vec), - - // Multiple methods might apply. - Ambiguity(Vec), -} - -pub type MethodResult = Result; - -pub fn lookup<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - - // In a call `a.b::(...)`: - expr: &ast::Expr, // The expression `a.b(...)`. - self_expr: &'a ast::Expr, // The expression `a`. - m_name: ast::Name, // The name `b`. - self_ty: ty::t, // The type of `a`. - supplied_tps: &'a [ty::t], // The list of types X, Y, ... . - deref_args: check::DerefArgs, // Whether we autopointer first. - check_traits: CheckTraitsFlag, // Whether we check traits only. - autoderef_receiver: AutoderefReceiverFlag) - -> MethodResult -{ - let mut lcx = LookupContext { - fcx: fcx, - span: expr.span, - self_expr: Some(self_expr), - m_name: m_name, - supplied_tps: supplied_tps, - impl_dups: HashSet::new(), - inherent_candidates: Vec::new(), - extension_candidates: Vec::new(), - static_candidates: Vec::new(), - deref_args: deref_args, - check_traits: check_traits, - autoderef_receiver: autoderef_receiver, - }; - - debug!("method lookup(self_ty={}, expr={}, self_expr={})", - self_ty.repr(fcx.tcx()), expr.repr(fcx.tcx()), - self_expr.repr(fcx.tcx())); - - debug!("searching inherent candidates"); - lcx.push_inherent_candidates(self_ty); - debug!("searching extension candidates"); - lcx.push_bound_candidates(self_ty, None); - lcx.push_extension_candidates(expr.id); - lcx.search(self_ty) -} - -pub fn lookup_in_trait<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - span: Span, - self_expr: Option<&'a ast::Expr>, - m_name: ast::Name, - trait_def_id: DefId, - self_ty: ty::t, - opt_input_types: Option>) - -> Option -{ - lookup_in_trait_adjusted(fcx, span, self_expr, m_name, trait_def_id, - ty::AutoDerefRef { autoderefs: 0, autoref: None }, - self_ty, opt_input_types) -} - -pub fn lookup_in_trait_adjusted<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - span: Span, - self_expr: Option<&'a ast::Expr>, - m_name: ast::Name, - trait_def_id: DefId, - autoderefref: ty::AutoDerefRef, - self_ty: ty::t, - opt_input_types: Option>) - -> Option -{ - debug!("method lookup_in_trait(self_ty={}, self_expr={}, m_name={}, trait_def_id={})", - self_ty.repr(fcx.tcx()), - self_expr.repr(fcx.tcx()), - m_name.repr(fcx.tcx()), - trait_def_id.repr(fcx.tcx())); - - let trait_def = ty::lookup_trait_def(fcx.tcx(), trait_def_id); - - let expected_number_of_input_types = trait_def.generics.types.len(subst::TypeSpace); - let input_types = match opt_input_types { - Some(input_types) => { - assert_eq!(expected_number_of_input_types, input_types.len()); - input_types - } - - None => { - fcx.inh.infcx.next_ty_vars(expected_number_of_input_types) - } - }; - - let number_assoc_types = trait_def.generics.types.len(subst::AssocSpace); - let assoc_types = fcx.inh.infcx.next_ty_vars(number_assoc_types); - - assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0); - assert!(trait_def.generics.regions.is_empty()); - - // Construct a trait-reference `self_ty : Trait` - let substs = subst::Substs::new_trait(input_types, Vec::new(), assoc_types, self_ty); - let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, substs)); - - // Construct an obligation - let obligation = traits::Obligation::misc(span, trait_ref.clone()); - - // Now we want to know if this can be matched - let mut selcx = traits::SelectionContext::new(fcx.infcx(), - &fcx.inh.param_env, - fcx); - if !selcx.evaluate_obligation(&obligation) { - debug!("--> Cannot match obligation"); - return None; // Cannot be matched, no such method resolution is possible. - } - - // Trait must have a method named `m_name` and it should not have - // type parameters or early-bound regions. - let tcx = fcx.tcx(); - let (method_num, method_ty) = trait_method(tcx, trait_def_id, m_name).unwrap(); - assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0); - assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0); - - // Substitute the trait parameters into the method type and - // instantiate late-bound regions to get the actual method type. - let ref bare_fn_ty = method_ty.fty; - let fn_sig = bare_fn_ty.sig.subst(tcx, &trait_ref.substs); - let fn_sig = replace_late_bound_regions_with_fresh_var(fcx.infcx(), span, - fn_sig.binder_id, &fn_sig); - let transformed_self_ty = fn_sig.inputs[0]; - let fty = ty::mk_bare_fn(tcx, ty::BareFnTy { - sig: fn_sig, - fn_style: bare_fn_ty.fn_style, - abi: bare_fn_ty.abi.clone(), - }); - - debug!("matched method fty={} obligation={}", - fty.repr(fcx.tcx()), - obligation.repr(fcx.tcx())); - - // Register obligations for the parameters. This will include the - // `Self` parameter, which in turn has a bound of the main trait, - // so this also effectively registers `obligation` as well. (We - // used to register `obligation` explicitly, but that resulted in - // double error messages being reported.) - fcx.add_obligations_for_parameters( - traits::ObligationCause::misc(span), - &trait_ref.substs, - &method_ty.generics); - - // FIXME(#18653) -- Try to resolve obligations, giving us more - // typing information, which can sometimes be needed to avoid - // pathological region inference failures. - vtable::select_new_fcx_obligations(fcx); - - // Insert any adjustments needed (always an autoref of some mutability). - match self_expr { - None => { } - - Some(self_expr) => { - debug!("inserting adjustment if needed (self-id = {}, \ - base adjustment = {}, explicit self = {})", - self_expr.id, autoderefref, method_ty.explicit_self); - - match method_ty.explicit_self { - ty::ByValueExplicitSelfCategory => { - // Trait method is fn(self), no transformation needed. - if !autoderefref.is_identity() { - fcx.write_adjustment( - self_expr.id, - span, - ty::AdjustDerefRef(autoderefref)); - } - } - - ty::ByReferenceExplicitSelfCategory(..) => { - // Trait method is fn(&self) or fn(&mut self), need an - // autoref. Pull the region etc out of the type of first argument. - match ty::get(transformed_self_ty).sty { - ty::ty_rptr(region, ty::mt { mutbl, ty: _ }) => { - let ty::AutoDerefRef { autoderefs, autoref } = autoderefref; - let autoref = autoref.map(|r| box r); - fcx.write_adjustment( - self_expr.id, - span, - ty::AdjustDerefRef(ty::AutoDerefRef { - autoderefs: autoderefs, - autoref: Some(ty::AutoPtr(region, mutbl, autoref)) - })); - } - - _ => { - fcx.tcx().sess.span_bug( - span, - format!( - "trait method is &self but first arg is: {}", - transformed_self_ty.repr(fcx.tcx())).as_slice()); - } - } - } - - _ => { - fcx.tcx().sess.span_bug( - span, - format!( - "unexpected explicit self type in operator method: {}", - method_ty.explicit_self).as_slice()); - } - } - } - } - - let callee = MethodCallee { - origin: MethodTypeParam(MethodParam{trait_ref: trait_ref.clone(), - method_num: method_num}), - ty: fty, - substs: trait_ref.substs.clone() - }; - - debug!("callee = {}", callee.repr(fcx.tcx())); - - Some(callee) -} - -pub fn report_error(fcx: &FnCtxt, - span: Span, - rcvr_ty: ty::t, - method_name: ast::Name, - error: MethodError) -{ - match error { - NoMatch(static_sources) => { - let cx = fcx.tcx(); - let method_ustring = method_name.user_string(cx); - - // True if the type is a struct and contains a field with - // the same name as the not-found method - let is_field = match ty::get(rcvr_ty).sty { - ty_struct(did, _) => - ty::lookup_struct_fields(cx, did) - .iter() - .any(|f| f.name.user_string(cx) == method_ustring), - _ => false - }; - - fcx.type_error_message( - span, - |actual| { - format!("type `{}` does not implement any \ - method in scope named `{}`", - actual, - method_ustring) - }, - rcvr_ty, - None); - - // If the method has the name of a field, give a help note - if is_field { - cx.sess.span_note(span, - format!("use `(s.{0})(...)` if you meant to call the \ - function stored in the `{0}` field", method_ustring).as_slice()); - } - - if static_sources.len() > 0 { - fcx.tcx().sess.fileline_note( - span, - "found defined static methods, maybe a `self` is missing?"); - - report_candidates(fcx, span, method_name, static_sources); - } - } - - Ambiguity(sources) => { - span_err!(fcx.sess(), span, E0034, - "multiple applicable methods in scope"); - - report_candidates(fcx, span, method_name, sources); - } - } - - fn report_candidates(fcx: &FnCtxt, - span: Span, - method_name: ast::Name, - mut sources: Vec) { - sources.sort(); - sources.dedup(); - - for (idx, source) in sources.iter().enumerate() { - match *source { - ImplSource(impl_did) => { - // Provide the best span we can. Use the method, if local to crate, else - // the impl, if local to crate (method may be defaulted), else the call site. - let method = impl_method(fcx.tcx(), impl_did, method_name).unwrap(); - let impl_span = fcx.tcx().map.def_id_span(impl_did, span); - let method_span = fcx.tcx().map.def_id_span(method.def_id, impl_span); - - let impl_ty = impl_self_ty(fcx, span, impl_did).ty; - - let insertion = match impl_trait_ref(fcx.tcx(), impl_did) { - None => format!(""), - Some(trait_ref) => format!(" of the trait `{}`", - ty::item_path_str(fcx.tcx(), - trait_ref.def_id)), - }; - - span_note!(fcx.sess(), method_span, - "candidate #{} is defined in an impl{} for the type `{}`", - idx + 1u, - insertion, - impl_ty.user_string(fcx.tcx())); - } - TraitSource(trait_did) => { - let (_, method) = trait_method(fcx.tcx(), trait_did, method_name).unwrap(); - let method_span = fcx.tcx().map.def_id_span(method.def_id, span); - span_note!(fcx.sess(), method_span, - "candidate #{} is defined in the trait `{}`", - idx + 1u, - ty::item_path_str(fcx.tcx(), trait_did)); - } - } - } - } -} - -// Determine the index of a method in the list of all methods belonging -// to a trait and its supertraits. -fn get_method_index(tcx: &ty::ctxt, - trait_ref: &TraitRef, - subtrait: Rc, - n_method: uint) -> uint { - // We need to figure the "real index" of the method in a - // listing of all the methods of an object. We do this by - // iterating down the supertraits of the object's trait until - // we find the trait the method came from, counting up the - // methods from them. - let mut method_count = 0; - ty::each_bound_trait_and_supertraits(tcx, &[subtrait], |bound_ref| { - if bound_ref.def_id == trait_ref.def_id { - false - } else { - let trait_items = ty::trait_items(tcx, bound_ref.def_id); - for trait_item in trait_items.iter() { - match *trait_item { - ty::MethodTraitItem(_) => method_count += 1, - ty::TypeTraitItem(_) => {} - } - } - true - } - }); - method_count + n_method -} - -struct LookupContext<'a, 'tcx: 'a> { - fcx: &'a FnCtxt<'a, 'tcx>, - span: Span, - - // The receiver to the method call. Only `None` in the case of - // an overloaded autoderef, where the receiver may be an intermediate - // state like "the expression `x` when it has been autoderef'd - // twice already". - self_expr: Option<&'a ast::Expr>, - - m_name: ast::Name, - supplied_tps: &'a [ty::t], - impl_dups: HashSet, - inherent_candidates: Vec, - extension_candidates: Vec, - static_candidates: Vec, - deref_args: check::DerefArgs, - check_traits: CheckTraitsFlag, - autoderef_receiver: AutoderefReceiverFlag, -} - -// A method that the user may be trying to invoke. Initially, we -// construct candidates only for inherent methods; for extension -// traits, we use an ExtensionCandidate. -#[deriving(Clone)] -struct Candidate { - xform_self_ty: ty::t, - rcvr_substs: subst::Substs, - method_ty: Rc, - origin: MethodOrigin, -} - -// A variation on a candidate that just stores the data needed -// extension trait matching. Once we pick the trait that matches, -// we'll construct a normal candidate from that. There is no deep -// reason for this, the code just worked out a bit cleaner. -struct ExtensionCandidate { - obligation: traits::Obligation, - xform_self_ty: ty::t, - method_ty: Rc, - method_num: uint, -} - -// A pared down enum describing just the places from which a method -// candidate can arise. Used for error reporting only. -#[deriving(PartialOrd, Ord, PartialEq, Eq)] -pub enum CandidateSource { - ImplSource(ast::DefId), - TraitSource(/* trait id */ ast::DefId), -} - -impl<'a, 'tcx> LookupContext<'a, 'tcx> { - fn search(self, self_ty: ty::t) -> MethodResult { - let span = self.self_expr.map_or(self.span, |e| e.span); - let self_expr_id = self.self_expr.map(|e| e.id); - - let (_, _, result) = - check::autoderef( - self.fcx, span, self_ty, self_expr_id, NoPreference, - |self_ty, autoderefs| self.search_step(self_ty, autoderefs)); - - match result { - Some(Some(Ok(result))) => { - self.fixup_derefs_on_method_receiver_if_necessary(&result); - Ok(result) - } - Some(Some(Err(err))) => { - Err(err) - } - None | Some(None) => { - Err(NoMatch(self.static_candidates)) - } - } - } - - fn search_step(&self, - self_ty: ty::t, - autoderefs: uint) - -> Option> - { - // Oh my, what a return type! - // - // Returning: - // - `None` => autoderef more, keep searching - // - `Some(None)` => stop searching, found nothing - // - `Some(Some(_))` => stop searching, found either callee/error - // - `Some(Some(Ok(_)))` => found a callee - // - `Some(Some(Err(_)))` => found an error (ambiguity, etc) - - debug!("search_step: self_ty={} autoderefs={}", - self.ty_to_string(self_ty), autoderefs); - - match self.deref_args { - check::DontDerefArgs => { - match self.search_for_autoderefd_method(self_ty, autoderefs) { - Some(result) => return Some(Some(result)), - None => {} - } - - match self.search_for_autoptrd_method(self_ty, autoderefs) { - Some(result) => return Some(Some(result)), - None => {} - } - } - check::DoDerefArgs => { - match self.search_for_autoptrd_method(self_ty, autoderefs) { - Some(result) => return Some(Some(result)), - None => {} - } - - match self.search_for_autoderefd_method(self_ty, autoderefs) { - Some(result) => return Some(Some(result)), - None => {} - } - } - } - - // If we are searching for an overloaded deref, no - // need to try coercing a `~[T]` to an `&[T]` and - // searching for an overloaded deref on *that*. - if !self.is_overloaded_deref() { - match self.search_for_autofatptrd_method(self_ty, autoderefs) { - Some(result) => return Some(Some(result)), - None => {} - } - } - - // Don't autoderef if we aren't supposed to. - if self.autoderef_receiver == DontAutoderefReceiver { - Some(None) - } else { - None - } - } - - fn is_overloaded_deref(&self) -> bool { - self.self_expr.is_none() - } - - /////////////////////////////////////////////////////////////////////////// - // Candidate collection (see comment at start of file) - - fn push_inherent_candidates(&mut self, self_ty: ty::t) { - /*! - * Collect all inherent candidates into - * `self.inherent_candidates`. See comment at the start of - * the file. To find the inherent candidates, we repeatedly - * deref the self-ty to find the "base-type". So, for - * example, if the receiver is Box> where `C` is a struct type, - * we'll want to find the inherent impls for `C`. - */ - - let span = self.self_expr.map_or(self.span, |e| e.span); - check::autoderef(self.fcx, span, self_ty, None, NoPreference, |self_ty, _| { - match get(self_ty).sty { - ty_trait(box TyTrait { ref principal, bounds, .. }) => { - self.push_inherent_candidates_from_object(self_ty, &*principal, bounds); - self.push_inherent_impl_candidates_for_type(principal.def_id); - } - ty_enum(did, _) | - ty_struct(did, _) | - ty_unboxed_closure(did, _, _) => { - if self.check_traits == CheckTraitsAndInherentMethods { - self.push_inherent_impl_candidates_for_type(did); - } - } - _ => { /* No inherent methods in these types */ } - } - - // Don't autoderef if we aren't supposed to. - if self.autoderef_receiver == DontAutoderefReceiver { - Some(()) - } else { - None - } - }); - } - - fn push_bound_candidates(&mut self, self_ty: ty::t, restrict_to: Option) { - let span = self.self_expr.map_or(self.span, |e| e.span); - check::autoderef(self.fcx, span, self_ty, None, NoPreference, |self_ty, _| { - match get(self_ty).sty { - ty_param(p) => { - self.push_inherent_candidates_from_param(self_ty, restrict_to, p); - } - _ => { /* No bound methods in these types */ } - } - - // Don't autoderef if we aren't supposed to. - if self.autoderef_receiver == DontAutoderefReceiver { - Some(()) - } else { - None - } - }); - } - - fn push_extension_candidates(&mut self, expr_id: ast::NodeId) { - debug!("push_extension_candidates(expr_id={})", expr_id); - - let mut duplicates = HashSet::new(); - let opt_applicable_traits = self.fcx.ccx.trait_map.get(&expr_id); - for applicable_traits in opt_applicable_traits.into_iter() { - for &trait_did in applicable_traits.iter() { - if duplicates.insert(trait_did) { - self.push_extension_candidate(trait_did); - } - } - } - } - - fn push_extension_candidate(&mut self, trait_def_id: DefId) { - debug!("push_extension_candidates: trait_def_id={}", trait_def_id); - - // Check whether `trait_def_id` defines a method with suitable name: - let trait_items = - ty::trait_items(self.tcx(), trait_def_id); - let matching_index = - trait_items.iter() - .position(|item| item.name() == self.m_name); - let matching_index = match matching_index { - Some(i) => i, - None => { return; } - }; - let method = match (&*trait_items)[matching_index].as_opt_method() { - Some(m) => m, - None => { return; } - }; - - // Check whether `trait_def_id` defines a method with suitable name: - if !self.has_applicable_self(&*method) { - debug!("method has inapplicable self"); - return self.record_static_candidate(TraitSource(trait_def_id)); - } - - // Otherwise, construct the receiver type. - let self_ty = - self.fcx.infcx().next_ty_var(); - let trait_def = - ty::lookup_trait_def(self.tcx(), trait_def_id); - let substs = - self.fcx.infcx().fresh_substs_for_trait(self.span, - &trait_def.generics, - self_ty); - let xform_self_ty = - self.xform_self_ty(&method, &substs); - - // Construct the obligation which must match. - let trait_ref = - Rc::new(ty::TraitRef::new(trait_def_id, substs)); - let obligation = - traits::Obligation::misc(self.span, trait_ref); - - debug!("extension-candidate(xform_self_ty={} obligation={})", - self.infcx().ty_to_string(xform_self_ty), - obligation.repr(self.tcx())); - - self.extension_candidates.push(ExtensionCandidate { - obligation: obligation, - xform_self_ty: xform_self_ty, - method_ty: method, - method_num: matching_index, - }); - } - - fn push_inherent_candidates_from_object(&mut self, - self_ty: ty::t, - principal: &ty::TraitRef, - _bounds: ty::ExistentialBounds) { - debug!("push_inherent_candidates_from_object(self_ty={})", - self_ty.repr(self.tcx())); - - let tcx = self.tcx(); - - // It is illegal to invoke a method on a trait instance that - // refers to the `Self` type. An error will be reported by - // `enforce_object_limitations()` if the method refers to the - // `Self` type anywhere other than the receiver. Here, we use - // a substitution that replaces `Self` with the object type - // itself. Hence, a `&self` method will wind up with an - // argument type like `&Trait`. - let rcvr_substs = principal.substs.with_self_ty(self_ty); - let trait_ref = Rc::new(TraitRef { def_id: principal.def_id, - substs: rcvr_substs.clone() }); - - self.push_inherent_candidates_from_bounds_inner( - &[trait_ref.clone()], - |this, new_trait_ref, m, method_num| { - let vtable_index = - get_method_index(tcx, &*new_trait_ref, - trait_ref.clone(), method_num); - - // FIXME Hacky. By-value `self` methods in objects ought to be - // just a special case of passing ownership of a DST value - // as a parameter. *But* we currently hack them in and tie them to - // the particulars of the `Box` type. So basically for a `fn foo(self,...)` - // method invoked on an object, we don't want the receiver type to be - // `TheTrait`, but rather `Box`. Yuck. - let mut m = m; - match m.explicit_self { - ByValueExplicitSelfCategory => { - let mut n = (*m).clone(); - let self_ty = n.fty.sig.inputs[0]; - n.fty.sig.inputs[0] = ty::mk_uniq(tcx, self_ty); - m = Rc::new(n); - } - _ => { } - } - - let xform_self_ty = - this.xform_self_ty(&m, &new_trait_ref.substs); - - Some(Candidate { - xform_self_ty: xform_self_ty, - rcvr_substs: new_trait_ref.substs.clone(), - method_ty: m, - origin: MethodTraitObject(MethodObject { - trait_ref: new_trait_ref, - object_trait_id: principal.def_id, - method_num: method_num, - real_index: vtable_index - }) - }) - }); - } - - fn push_inherent_candidates_from_param(&mut self, - rcvr_ty: ty::t, - restrict_to: Option, - param_ty: ParamTy) { - debug!("push_inherent_candidates_from_param(param_ty={})", - param_ty); - self.push_inherent_candidates_from_bounds( - rcvr_ty, - param_ty.space, - param_ty.idx, - restrict_to); - } - - fn push_inherent_candidates_from_bounds(&mut self, - _self_ty: ty::t, - space: subst::ParamSpace, - index: uint, - restrict_to: Option) { - let bounds = - self.fcx.inh.param_env.bounds.get(space, index).trait_bounds - .as_slice(); - self.push_inherent_candidates_from_bounds_inner(bounds, - |this, trait_ref, m, method_num| { - match restrict_to { - Some(trait_did) => { - if trait_did != trait_ref.def_id { - return None; - } - } - _ => {} - } - - let xform_self_ty = - this.xform_self_ty(&m, &trait_ref.substs); - - debug!("found match: trait_ref={} substs={} m={}", - trait_ref.repr(this.tcx()), - trait_ref.substs.repr(this.tcx()), - m.repr(this.tcx())); - assert_eq!(m.generics.types.get_slice(subst::TypeSpace).len(), - trait_ref.substs.types.get_slice(subst::TypeSpace).len()); - assert_eq!(m.generics.regions.get_slice(subst::TypeSpace).len(), - trait_ref.substs.regions().get_slice(subst::TypeSpace).len()); - assert_eq!(m.generics.types.get_slice(subst::SelfSpace).len(), - trait_ref.substs.types.get_slice(subst::SelfSpace).len()); - assert_eq!(m.generics.regions.get_slice(subst::SelfSpace).len(), - trait_ref.substs.regions().get_slice(subst::SelfSpace).len()); - - Some(Candidate { - xform_self_ty: xform_self_ty, - rcvr_substs: trait_ref.substs.clone(), - method_ty: m, - origin: MethodTypeParam(MethodParam { - trait_ref: trait_ref, - method_num: method_num, - }) - }) - }) - } - - // Do a search through a list of bounds, using a callback to actually - // create the candidates. - fn push_inherent_candidates_from_bounds_inner( - &mut self, - bounds: &[Rc], - mk_cand: |this: &mut LookupContext, - tr: Rc, - m: Rc, - method_num: uint| - -> Option) - { - let tcx = self.tcx(); - let mut cache = HashSet::new(); - for bound_trait_ref in traits::transitive_bounds(tcx, bounds) { - // Already visited this trait, skip it. - if !cache.insert(bound_trait_ref.def_id) { - continue; - } - - let (pos, method) = match trait_method(tcx, bound_trait_ref.def_id, self.m_name) { - Some(v) => v, - None => { continue; } - }; - - if !self.has_applicable_self(&*method) { - self.record_static_candidate(TraitSource(bound_trait_ref.def_id)); - } else { - match mk_cand(self, - bound_trait_ref, - method, - pos) { - Some(cand) => { - debug!("pushing inherent candidate for param: {}", - cand.repr(self.tcx())); - self.inherent_candidates.push(cand); - } - None => {} - } - } - } - } - - - fn push_inherent_impl_candidates_for_type(&mut self, did: DefId) { - // Read the inherent implementation candidates for this type from the - // metadata if necessary. - ty::populate_implementations_for_type_if_necessary(self.tcx(), did); - - for impl_infos in self.tcx().inherent_impls.borrow().get(&did).iter() { - for impl_did in impl_infos.iter() { - self.push_candidates_from_inherent_impl(*impl_did); - } - } - } - - fn push_candidates_from_inherent_impl(&mut self, - impl_did: DefId) { - if !self.impl_dups.insert(impl_did) { - return; // already visited - } - - let method = match impl_method(self.tcx(), impl_did, self.m_name) { - Some(m) => m, - None => { return; } // No method with correct name on this impl - }; - - debug!("push_candidates_from_inherent_impl: impl_did={} method={}", - impl_did.repr(self.tcx()), - method.repr(self.tcx())); - - if !self.has_applicable_self(&*method) { - // No receiver declared. Not a candidate. - return self.record_static_candidate(ImplSource(impl_did)); - } - - // Determine the `self` of the impl with fresh - // variables for each parameter. - let span = self.self_expr.map_or(self.span, |e| e.span); - let TypeAndSubsts { - substs: impl_substs, - ty: _impl_ty - } = impl_self_ty(self.fcx, span, impl_did); - - // Determine the receiver type that the method itself expects. - let xform_self_ty = - self.xform_self_ty(&method, &impl_substs); - - self.inherent_candidates.push(Candidate { - xform_self_ty: xform_self_ty, - rcvr_substs: impl_substs, - origin: MethodStatic(method.def_id), - method_ty: method, - }); - } - - // ______________________________________________________________________ - // Candidate selection (see comment at start of file) - - fn search_for_autoderefd_method(&self, - self_ty: ty::t, - autoderefs: uint) - -> Option { - // Hacky. For overloaded derefs, there may be an adjustment - // added to the expression from the outside context, so we do not store - // an explicit adjustment, but rather we hardwire the single deref - // that occurs in trans and mem_categorization. - if self.self_expr.is_none() { - return None; - } - - let (self_ty, auto_deref_ref) = self.consider_reborrow(self_ty, autoderefs); - let adjustment = Some((self.self_expr.unwrap().id, ty::AdjustDerefRef(auto_deref_ref))); - - match self.search_for_method(self_ty) { - None => { - None - } - Some(Ok(method)) => { - debug!("(searching for autoderef'd method) writing \ - adjustment {} for {}", adjustment, self.ty_to_string(self_ty)); - match adjustment { - Some((self_expr_id, adj)) => { - self.fcx.write_adjustment(self_expr_id, self.span, adj); - } - None => {} - } - Some(Ok(method)) - } - Some(Err(error)) => { - Some(Err(error)) - } - } - } - - fn consider_reborrow(&self, - self_ty: ty::t, - autoderefs: uint) - -> (ty::t, ty::AutoDerefRef) { - /*! - * In the event that we are invoking a method with a receiver - * of a borrowed type like `&T`, `&mut T`, or `&mut [T]`, - * we will "reborrow" the receiver implicitly. For example, if - * you have a call `r.inc()` and where `r` has type `&mut T`, - * then we treat that like `(&mut *r).inc()`. This avoids - * consuming the original pointer. - * - * You might think that this would be a natural byproduct of - * the auto-deref/auto-ref process. This is true for `Box` - * but not for an `&mut T` receiver. With `Box`, we would - * begin by testing for methods with a self type `Box`, - * then autoderef to `T`, then autoref to `&mut T`. But with - * an `&mut T` receiver the process begins with `&mut T`, only - * without any autoadjustments. - */ - - let tcx = self.tcx(); - return match ty::get(self_ty).sty { - ty::ty_rptr(_, self_mt) if default_method_hack(self_mt) => { - (self_ty, - ty::AutoDerefRef { - autoderefs: autoderefs, - autoref: None}) - } - ty::ty_rptr(_, self_mt) => { - let region = - self.infcx().next_region_var(infer::Autoref(self.span)); - (ty::mk_rptr(tcx, region, self_mt), - ty::AutoDerefRef { - autoderefs: autoderefs + 1, - autoref: Some(ty::AutoPtr(region, self_mt.mutbl, None))}) - } - _ => { - (self_ty, - ty::AutoDerefRef { - autoderefs: autoderefs, - autoref: None}) - } - }; - - fn default_method_hack(self_mt: ty::mt) -> bool { - // FIXME(#6129). Default methods can't deal with autoref. - // - // I am a horrible monster and I pray for death. Currently - // the default method code panics when you try to reborrow - // because it is not handling types correctly. In lieu of - // fixing that, I am introducing this horrible hack. - ndm - self_mt.mutbl == MutImmutable && ty::type_is_self(self_mt.ty) - } - } - - // Takes an [T] - an unwrapped DST pointer (either ~ or &) - // [T] to &[T] or &&[T] (note that we started with a &[T] or ~[T] which has - // been implicitly derefed). - fn auto_slice_vec(&self, ty: ty::t, autoderefs: uint) - -> Option - { - let tcx = self.tcx(); - debug!("auto_slice_vec {}", ppaux::ty_to_string(tcx, ty)); - - // First try to borrow to a slice - let entry = self.search_for_some_kind_of_autorefd_method( - |r, m| AutoPtr(r, m, None), autoderefs, &[MutImmutable, MutMutable], - |m,r| ty::mk_slice(tcx, r, - ty::mt {ty:ty, mutbl:m})); - - if entry.is_some() { - return entry; - } - - // Then try to borrow to a slice *and* borrow a pointer. - self.search_for_some_kind_of_autorefd_method( - |r, m| AutoPtr(r, ast::MutImmutable, Some( box AutoPtr(r, m, None))), - autoderefs, &[MutImmutable, MutMutable], - |m, r| { - let slice_ty = ty::mk_slice(tcx, r, - ty::mt {ty:ty, mutbl:m}); - // NB: we do not try to autoref to a mutable - // pointer. That would be creating a pointer - // to a temporary pointer (the borrowed - // slice), so any update the callee makes to - // it can't be observed. - ty::mk_rptr(tcx, r, ty::mt {ty:slice_ty, mutbl:MutImmutable}) - }) - } - - // [T, ..len] -> [T] or &[T] or &&[T] - fn auto_unsize_vec(&self, ty: ty::t, autoderefs: uint, len: uint) -> Option { - let tcx = self.tcx(); - debug!("auto_unsize_vec {}", ppaux::ty_to_string(tcx, ty)); - - // First try to borrow to an unsized vec. - let entry = self.search_for_some_kind_of_autorefd_method( - |_r, _m| AutoUnsize(ty::UnsizeLength(len)), - autoderefs, &[MutImmutable, MutMutable], - |_m, _r| ty::mk_vec(tcx, ty, None)); - - if entry.is_some() { - return entry; - } - - // Then try to borrow to a slice. - let entry = self.search_for_some_kind_of_autorefd_method( - |r, m| AutoPtr(r, m, Some(box AutoUnsize(ty::UnsizeLength(len)))), - autoderefs, &[MutImmutable, MutMutable], - |m, r| ty::mk_slice(tcx, r, ty::mt {ty:ty, mutbl:m})); - - if entry.is_some() { - return entry; - } - - // Then try to borrow to a slice *and* borrow a pointer. - self.search_for_some_kind_of_autorefd_method( - |r, m| AutoPtr(r, m, - Some(box AutoPtr(r, m, - Some(box AutoUnsize(ty::UnsizeLength(len)))))), - autoderefs, &[MutImmutable, MutMutable], - |m, r| { - let slice_ty = ty::mk_slice(tcx, r, ty::mt {ty:ty, mutbl:m}); - ty::mk_rptr(tcx, r, ty::mt {ty:slice_ty, mutbl:MutImmutable}) - }) - } - - fn auto_slice_str(&self, autoderefs: uint) -> Option { - let tcx = self.tcx(); - debug!("auto_slice_str"); - - let entry = self.search_for_some_kind_of_autorefd_method( - |r, m| AutoPtr(r, m, None), autoderefs, &[MutImmutable], - |_m, r| ty::mk_str_slice(tcx, r, MutImmutable)); - - if entry.is_some() { - return entry; - } - - self.search_for_some_kind_of_autorefd_method( - |r, m| AutoPtr(r, ast::MutImmutable, Some( box AutoPtr(r, m, None))), - autoderefs, &[MutImmutable], - |m, r| { - let slice_ty = ty::mk_str_slice(tcx, r, m); - ty::mk_rptr(tcx, r, ty::mt {ty:slice_ty, mutbl:m}) - }) - } - - // Coerce Box/&Trait instances to &Trait. - fn auto_slice_trait(&self, ty: ty::t, autoderefs: uint) -> Option { - debug!("auto_slice_trait"); - match ty::get(ty).sty { - ty_trait(box ty::TyTrait { ref principal, - bounds: b, - .. }) => { - let trt_did = principal.def_id; - let trt_substs = &principal.substs; - let tcx = self.tcx(); - self.search_for_some_kind_of_autorefd_method( - |r, m| AutoPtr(r, m, None), - autoderefs, &[MutImmutable, MutMutable], - |m, r| { - let principal = ty::TraitRef::new(trt_did, - trt_substs.clone()); - let tr = ty::mk_trait(tcx, principal, b); - ty::mk_rptr(tcx, r, ty::mt{ ty: tr, mutbl: m }) - }) - } - _ => panic!("Expected ty_trait in auto_slice_trait") - } - } - - fn search_for_autofatptrd_method(&self, - self_ty: ty::t, - autoderefs: uint) - -> Option - { - /*! - * Searches for a candidate by converting things like - * `~[]` to `&[]`. - */ - - let tcx = self.tcx(); - debug!("search_for_autofatptrd_method {}", ppaux::ty_to_string(tcx, self_ty)); - - let sty = ty::get(self_ty).sty.clone(); - match sty { - ty_vec(ty, Some(len)) => self.auto_unsize_vec(ty, autoderefs, len), - ty_vec(ty, None) => self.auto_slice_vec(ty, autoderefs), - ty_str => self.auto_slice_str(autoderefs), - ty_trait(..) => self.auto_slice_trait(self_ty, autoderefs), - - ty_closure(..) => { - // This case should probably be handled similarly to - // Trait instances. - None - } - - _ => None - } - } - - fn search_for_autoptrd_method(&self, self_ty: ty::t, autoderefs: uint) - -> Option - { - /*! - * - * Converts any type `T` to `&M T` where `M` is an - * appropriate mutability. - */ - - let tcx = self.tcx(); - match ty::get(self_ty).sty { - ty_bare_fn(..) | ty_uniq(..) | ty_rptr(..) | - ty_infer(IntVar(_)) | - ty_infer(FloatVar(_)) | - ty_param(..) | ty_bool | - ty_char | ty_int(..) | ty_uint(..) | - ty_float(..) | ty_enum(..) | ty_ptr(..) | ty_struct(..) | - ty_unboxed_closure(..) | ty_tup(..) | ty_open(..) | - ty_str | ty_vec(..) | ty_trait(..) | ty_closure(..) => { - self.search_for_some_kind_of_autorefd_method( - |r, m| AutoPtr(r, m, None), autoderefs, &[MutImmutable, MutMutable], - |m,r| ty::mk_rptr(tcx, r, ty::mt {ty:self_ty, mutbl:m})) - } - - ty_err => None, - - ty_infer(TyVar(_)) | - ty_infer(SkolemizedTy(_)) | - ty_infer(SkolemizedIntTy(_)) => { - self.bug(format!("unexpected type: {}", - self.ty_to_string(self_ty)).as_slice()); - } - } - } - - fn search_for_some_kind_of_autorefd_method( - &self, - kind: |Region, ast::Mutability| -> ty::AutoRef, - autoderefs: uint, - mutbls: &[ast::Mutability], - mk_autoref_ty: |ast::Mutability, ty::Region| -> ty::t) - -> Option - { - // Hacky. For overloaded derefs, there may be an adjustment - // added to the expression from the outside context, so we do not store - // an explicit adjustment, but rather we hardwire the single deref - // that occurs in trans and mem_categorization. - let self_expr_id = match self.self_expr { - Some(expr) => Some(expr.id), - None => { - assert_eq!(autoderefs, 0); - assert!(kind(ty::ReEmpty, ast::MutImmutable) == - ty::AutoPtr(ty::ReEmpty, ast::MutImmutable, None)); - None - } - }; - - // This is hokey. We should have mutability inference as a - // variable. But for now, try &, then &mut: - let region = - self.infcx().next_region_var(infer::Autoref(self.span)); - for mutbl in mutbls.iter() { - let autoref_ty = mk_autoref_ty(*mutbl, region); - match self.search_for_method(autoref_ty) { - None => {} - Some(method) => { - match self_expr_id { - Some(self_expr_id) => { - self.fcx.write_adjustment( - self_expr_id, - self.span, - ty::AdjustDerefRef(ty::AutoDerefRef { - autoderefs: autoderefs, - autoref: Some(kind(region, *mutbl)) - })); - } - None => {} - } - return Some(method); - } - } - } - None - } - - fn search_for_method(&self, rcvr_ty: ty::t) -> Option { - debug!("search_for_method(rcvr_ty={})", self.ty_to_string(rcvr_ty)); - let _indenter = indenter(); - - // I am not sure that inherent methods should have higher - // priority, but it is necessary ATM to handle some of the - // existing code. - - debug!("searching inherent candidates"); - match self.consider_candidates(rcvr_ty, self.inherent_candidates.as_slice()) { - None => {} - Some(mme) => { - return Some(mme); - } - } - - debug!("searching extension candidates"); - self.consider_extension_candidates(rcvr_ty) - } - - fn consider_candidates(&self, rcvr_ty: ty::t, - candidates: &[Candidate]) - -> Option { - let relevant_candidates = self.filter_candidates(rcvr_ty, candidates); - - if relevant_candidates.len() == 0 { - return None; - } - - if relevant_candidates.len() > 1 { - let sources = relevant_candidates.iter() - .map(|candidate| candidate.to_source()) - .collect(); - return Some(Err(Ambiguity(sources))); - } - - Some(Ok(self.confirm_candidate(rcvr_ty, &relevant_candidates[0]))) - } - - fn filter_candidates(&self, rcvr_ty: ty::t, candidates: &[Candidate]) -> Vec { - let mut relevant_candidates: Vec = Vec::new(); - - for candidate_a in candidates.iter().filter(|&c| self.is_relevant(rcvr_ty, c)) { - // Skip this one if we already have one like it - if !relevant_candidates.iter().any(|candidate_b| { - debug!("attempting to merge {} and {}", - candidate_a.repr(self.tcx()), - candidate_b.repr(self.tcx())); - match (&candidate_a.origin, &candidate_b.origin) { - (&MethodTypeParam(ref p1), &MethodTypeParam(ref p2)) => { - let same_trait = - p1.trait_ref.def_id == p2.trait_ref.def_id; - let same_method = - p1.method_num == p2.method_num; - // it's ok to compare self-ty with `==` here because - // they are always a TyParam - let same_param = - p1.trait_ref.self_ty() == p2.trait_ref.self_ty(); - same_trait && same_method && same_param - } - _ => false - } - }) { - relevant_candidates.push((*candidate_a).clone()); - } - } - - relevant_candidates - } - - fn consider_extension_candidates(&self, rcvr_ty: ty::t) - -> Option - { - let mut selcx = traits::SelectionContext::new(self.infcx(), - &self.fcx.inh.param_env, - self.fcx); - - let extension_evaluations: Vec<_> = - self.extension_candidates.iter() - .map(|ext| self.probe_extension_candidate(&mut selcx, rcvr_ty, ext)) - .collect(); - - // How many traits can apply? - let applicable_evaluations_count = - extension_evaluations.iter() - .filter(|eval| eval.may_apply()) - .count(); - - // Determine whether there are multiple traits that could apply. - if applicable_evaluations_count > 1 { - let sources = - self.extension_candidates.iter() - .zip(extension_evaluations.iter()) - .filter(|&(_, eval)| eval.may_apply()) - .map(|(ext, _)| ext.to_source()) - .collect(); - return Some(Err(Ambiguity(sources))); - } - - // Determine whether there are no traits that could apply. - if applicable_evaluations_count == 0 { - return None; - } - - // Exactly one trait applies. It itself could *still* be ambiguous thanks - // to coercions. - let applicable_evaluation = extension_evaluations.iter() - .position(|eval| eval.may_apply()) - .unwrap(); - let match_data = match extension_evaluations[applicable_evaluation] { - traits::MethodMatched(data) => data, - traits::MethodAmbiguous(ref impl_def_ids) => { - let sources = impl_def_ids.iter().map(|&d| ImplSource(d)).collect(); - return Some(Err(Ambiguity(sources))); - } - traits::MethodDidNotMatch => { - self.bug("Method did not match and yet may_apply() is true") - } - }; - - let extension = &self.extension_candidates[applicable_evaluation]; - - debug!("picked extension={}", extension.repr(self.tcx())); - - // We have to confirm the method match. This will cause the type variables - // in the obligation to be appropriately unified based on the subtyping/coercion - // between `rcvr_ty` and `extension.xform_self_ty`. - selcx.confirm_method_match(rcvr_ty, extension.xform_self_ty, - &extension.obligation, match_data); - - // Finally, construct the candidate, now that everything is - // known, and confirm *that*. Note that whatever we pick - // (impl, whatever) we can always use the same kind of origin - // (trait-based method dispatch). - let candidate = Candidate { - xform_self_ty: extension.xform_self_ty, - rcvr_substs: extension.obligation.trait_ref.substs.clone(), - method_ty: extension.method_ty.clone(), - origin: MethodTypeParam(MethodParam{trait_ref: extension.obligation.trait_ref.clone(), - method_num: extension.method_num}) - }; - - // Confirming the candidate will do the final work of - // instantiating late-bound variables, unifying things, and - // registering trait obligations (including - // `extension.obligation`, which should be a requirement of - // the `Self` trait). - let callee = self.confirm_candidate(rcvr_ty, &candidate); - - select_new_fcx_obligations(self.fcx); - - Some(Ok(callee)) - } - - fn probe_extension_candidate(&self, - selcx: &mut traits::SelectionContext, - rcvr_ty: ty::t, - candidate: &ExtensionCandidate) - -> traits::MethodMatchResult - { - debug!("probe_extension_candidate(rcvr_ty={}, candidate.obligation={})", - rcvr_ty.repr(self.tcx()), - candidate.obligation.repr(self.tcx())); - - selcx.evaluate_method_obligation(rcvr_ty, candidate.xform_self_ty, &candidate.obligation) - } - - fn confirm_candidate(&self, rcvr_ty: ty::t, candidate: &Candidate) - -> MethodCallee - { - // This method performs two sets of substitutions, one after the other: - // 1. Substitute values for any type/lifetime parameters from the impl and - // method declaration into the method type. This is the function type - // before it is called; it may still include late bound region variables. - // 2. Instantiate any late bound lifetime parameters in the method itself - // with fresh region variables. - - let tcx = self.tcx(); - - debug!("confirm_candidate(rcvr_ty={}, candidate={})", - self.ty_to_string(rcvr_ty), - candidate.repr(self.tcx())); - - let rcvr_substs = candidate.rcvr_substs.clone(); - self.enforce_drop_trait_limitations(candidate); - - // Determine the values for the generic parameters of the method. - // If they were not explicitly supplied, just construct fresh - // variables. - let num_supplied_tps = self.supplied_tps.len(); - let num_method_tps = candidate.method_ty.generics.types.len(subst::FnSpace); - let m_types = { - if num_supplied_tps == 0u { - self.fcx.infcx().next_ty_vars(num_method_tps) - } else if num_method_tps == 0u { - span_err!(tcx.sess, self.span, E0035, - "does not take type parameters"); - self.fcx.infcx().next_ty_vars(num_method_tps) - } else if num_supplied_tps != num_method_tps { - span_err!(tcx.sess, self.span, E0036, - "incorrect number of type parameters given for this method"); - self.fcx.infcx().next_ty_vars(num_method_tps) - } else { - self.supplied_tps.to_vec() - } - }; - - // Create subst for early-bound lifetime parameters, combining - // parameters from the type and those from the method. - // - // FIXME -- permit users to manually specify lifetimes - let m_regions = - self.fcx.infcx().region_vars_for_defs( - self.span, - candidate.method_ty.generics.regions.get_slice(subst::FnSpace)); - - let all_substs = rcvr_substs.with_method(m_types, m_regions); - - let ref bare_fn_ty = candidate.method_ty.fty; - - // Compute the method type with type parameters substituted - debug!("fty={} all_substs={}", - bare_fn_ty.repr(tcx), - all_substs.repr(tcx)); - - let fn_sig = bare_fn_ty.sig.subst(tcx, &all_substs); - - debug!("after subst, fty={}", fn_sig.repr(tcx)); - - // Replace any bound regions that appear in the function - // signature with region variables - let fn_sig = - self.replace_late_bound_regions_with_fresh_var(fn_sig.binder_id, &fn_sig); - let transformed_self_ty = fn_sig.inputs[0]; - let fty = ty::mk_bare_fn(tcx, ty::BareFnTy { - sig: fn_sig, - fn_style: bare_fn_ty.fn_style, - abi: bare_fn_ty.abi.clone(), - }); - debug!("after replacing bound regions, fty={}", self.ty_to_string(fty)); - - // Before, we only checked whether self_ty could be a subtype - // of rcvr_ty; now we actually make it so (this may cause - // variables to unify etc). Since we checked beforehand, and - // nothing has changed in the meantime, this unification - // should never fail. - let span = self.self_expr.map_or(self.span, |e| e.span); - match self.fcx.mk_subty(false, infer::Misc(span), - rcvr_ty, transformed_self_ty) { - Ok(_) => {} - Err(_) => { - self.bug(format!( - "{} was a subtype of {} but now is not?", - self.ty_to_string(rcvr_ty), - self.ty_to_string(transformed_self_ty)).as_slice()); - } - } - - // FIXME(DST). Super hack. For a method on a trait object - // `Trait`, the generic signature requires that - // `Self:Trait`. Since, for an object, we bind `Self` to the - // type `Trait`, this leads to an obligation - // `Trait:Trait`. Until such time we DST is fully implemented, - // that obligation is not necessarily satisfied. (In the - // future, it would be.) - // - // To sidestep this, we overwrite the binding for `Self` with - // `err` (just for trait objects) when we generate the - // obligations. This causes us to generate the obligation - // `err:Trait`, and the error type is considered to implement - // all traits, so we're all good. Hack hack hack. - match candidate.origin { - MethodTraitObject(..) => { - let mut temp_substs = all_substs.clone(); - temp_substs.types.get_mut_slice(SelfSpace)[0] = ty::mk_err(); - self.fcx.add_obligations_for_parameters( - traits::ObligationCause::misc(self.span), - &temp_substs, - &candidate.method_ty.generics); - } - _ => { - self.fcx.add_obligations_for_parameters( - traits::ObligationCause::misc(self.span), - &all_substs, - &candidate.method_ty.generics); - } - } - - MethodCallee { - origin: candidate.origin.clone(), - ty: fty, - substs: all_substs - } - } - - fn fixup_derefs_on_method_receiver_if_necessary(&self, - method_callee: &MethodCallee) { - let sig = match ty::get(method_callee.ty).sty { - ty::ty_bare_fn(ref f) => f.sig.clone(), - ty::ty_closure(ref f) => f.sig.clone(), - _ => return, - }; - - match ty::get(sig.inputs[0]).sty { - ty::ty_rptr(_, ty::mt { - ty: _, - mutbl: ast::MutMutable, - }) => {} - _ => return, - } - - // Gather up expressions we want to munge. - let mut exprs = Vec::new(); - match self.self_expr { - Some(expr) => exprs.push(expr), - None => {} - } - loop { - if exprs.len() == 0 { - break - } - let last = exprs[exprs.len() - 1]; - match last.node { - ast::ExprParen(ref expr) | - ast::ExprField(ref expr, _, _) | - ast::ExprTupField(ref expr, _, _) | - ast::ExprSlice(ref expr, _, _, _) | - ast::ExprIndex(ref expr, _) | - ast::ExprUnary(ast::UnDeref, ref expr) => exprs.push(&**expr), - _ => break, - } - } - - debug!("fixup_derefs_on_method_receiver_if_necessary: exprs={}", - exprs.repr(self.tcx())); - - // Fix up autoderefs and derefs. - for (i, expr) in exprs.iter().rev().enumerate() { - // Count autoderefs. - let autoderef_count = match self.fcx - .inh - .adjustments - .borrow() - .get(&expr.id) { - Some(&ty::AdjustDerefRef(ty::AutoDerefRef { - autoderefs: autoderef_count, - autoref: _ - })) => autoderef_count, - Some(_) | None => 0, - }; - - debug!("fixup_derefs_on_method_receiver_if_necessary: i={} expr={} autoderef_count={}", - i, expr.repr(self.tcx()), autoderef_count); - - if autoderef_count > 0 { - check::autoderef(self.fcx, - expr.span, - self.fcx.expr_ty(*expr), - Some(expr.id), - PreferMutLvalue, - |_, autoderefs| { - if autoderefs == autoderef_count + 1 { - Some(()) - } else { - None - } - }); - } - - // Don't retry the first one or we might infinite loop! - if i != 0 { - match expr.node { - ast::ExprIndex(ref base_expr, _) => { - let mut base_adjustment = - match self.fcx.inh.adjustments.borrow().get(&base_expr.id) { - Some(&ty::AdjustDerefRef(ref adr)) => (*adr).clone(), - None => ty::AutoDerefRef { autoderefs: 0, autoref: None }, - Some(_) => { - self.tcx().sess.span_bug( - base_expr.span, - "unexpected adjustment type"); - } - }; - - // If this is an overloaded index, the - // adjustment will include an extra layer of - // autoref because the method is an &self/&mut - // self method. We have to peel it off to get - // the raw adjustment that `try_index_step` - // expects. This is annoying and horrible. We - // ought to recode this routine so it doesn't - // (ab)use the normal type checking paths. - base_adjustment.autoref = match base_adjustment.autoref { - None => { None } - Some(AutoPtr(_, _, None)) => { None } - Some(AutoPtr(_, _, Some(box r))) => { Some(r) } - Some(_) => { - self.tcx().sess.span_bug( - base_expr.span, - "unexpected adjustment autoref"); - } - }; - - let adjusted_base_ty = - self.fcx.adjust_expr_ty( - &**base_expr, - Some(&ty::AdjustDerefRef(base_adjustment.clone()))); - - check::try_index_step( - self.fcx, - MethodCall::expr(expr.id), - *expr, - &**base_expr, - adjusted_base_ty, - base_adjustment, - PreferMutLvalue); - } - ast::ExprUnary(ast::UnDeref, ref base_expr) => { - // if this is an overloaded deref, then re-evaluate with - // a preference for mut - let method_call = MethodCall::expr(expr.id); - if self.fcx.inh.method_map.borrow().contains_key(&method_call) { - check::try_overloaded_deref( - self.fcx, - expr.span, - Some(method_call), - Some(&**base_expr), - self.fcx.expr_ty(&**base_expr), - PreferMutLvalue); - } - } - _ => {} - } - } - } - } - - fn enforce_drop_trait_limitations(&self, candidate: &Candidate) { - // No code can call the finalize method explicitly. - let bad = match candidate.origin { - MethodStatic(method_id) => { - self.tcx().destructors.borrow().contains(&method_id) - } - MethodStaticUnboxedClosure(_) => { - false - } - MethodTypeParam(MethodParam { ref trait_ref, .. }) | - MethodTraitObject(MethodObject { ref trait_ref, .. }) => { - Some(trait_ref.def_id) == self.tcx().lang_items.drop_trait() - } - }; - - if bad { - span_err!(self.tcx().sess, self.span, E0040, - "explicit call to destructor"); - } - } - - // `rcvr_ty` is the type of the expression. It may be a subtype of a - // candidate method's `self_ty`. - fn is_relevant(&self, rcvr_ty: ty::t, candidate: &Candidate) -> bool { - debug!("is_relevant(rcvr_ty={}, candidate={})", - self.ty_to_string(rcvr_ty), candidate.repr(self.tcx())); - - infer::can_mk_subty(self.infcx(), rcvr_ty, candidate.xform_self_ty).is_ok() - } - - fn infcx(&'a self) -> &'a infer::InferCtxt<'a, 'tcx> { - &self.fcx.inh.infcx - } - - fn tcx(&self) -> &'a ty::ctxt<'tcx> { - self.fcx.tcx() - } - - fn ty_to_string(&self, t: ty::t) -> String { - self.fcx.infcx().ty_to_string(t) - } - - fn bug(&self, s: &str) -> ! { - self.tcx().sess.span_bug(self.span, s) - } - - fn has_applicable_self(&self, method: &ty::Method) -> bool { - // "fast track" -- check for usage of sugar - match method.explicit_self { - StaticExplicitSelfCategory => { - // fallthrough - } - ByValueExplicitSelfCategory | - ByReferenceExplicitSelfCategory(..) | - ByBoxExplicitSelfCategory => { - return true; - } - } - - // FIXME -- check for types that deref to `Self`, - // like `Rc` and so on. - // - // Note also that the current code will break if this type - // includes any of the type parameters defined on the method - // -- but this could be overcome. - return false; - } - - fn record_static_candidate(&mut self, source: CandidateSource) { - self.static_candidates.push(source); - } - - fn xform_self_ty(&self, method: &Rc, substs: &subst::Substs) -> ty::t { - let xform_self_ty = method.fty.sig.inputs[0].subst(self.tcx(), substs); - self.replace_late_bound_regions_with_fresh_var(method.fty.sig.binder_id, &xform_self_ty) - } - - fn replace_late_bound_regions_with_fresh_var(&self, binder_id: ast::NodeId, value: &T) -> T - where T : TypeFoldable + Repr - { - replace_late_bound_regions_with_fresh_var(self.fcx.infcx(), self.span, binder_id, value) - } -} - -fn replace_late_bound_regions_with_fresh_var(infcx: &infer::InferCtxt, - span: Span, - binder_id: ast::NodeId, - value: &T) - -> T - where T : TypeFoldable + Repr -{ - let (_, value) = replace_late_bound_regions( - infcx.tcx, - binder_id, - value, - |br| infcx.next_region_var(infer::LateBoundRegion(span, br))); - value -} - -fn trait_method(tcx: &ty::ctxt, - trait_def_id: ast::DefId, - method_name: ast::Name) - -> Option<(uint, Rc)> -{ - /*! - * Find method with name `method_name` defined in `trait_def_id` and return it, - * along with its index (or `None`, if no such method). - */ - - let trait_items = ty::trait_items(tcx, trait_def_id); - trait_items - .iter() - .enumerate() - .find(|&(_, ref item)| item.name() == method_name) - .and_then(|(idx, item)| item.as_opt_method().map(|m| (idx, m))) -} - -fn impl_method(tcx: &ty::ctxt, - impl_def_id: ast::DefId, - method_name: ast::Name) - -> Option> -{ - let impl_items = tcx.impl_items.borrow(); - let impl_items = impl_items.get(&impl_def_id).unwrap(); - impl_items - .iter() - .map(|&did| ty::impl_or_trait_item(tcx, did.def_id())) - .find(|m| m.name() == method_name) - .and_then(|item| item.as_opt_method()) -} - -impl Repr for Candidate { - fn repr(&self, tcx: &ty::ctxt) -> String { - format!("Candidate(rcvr_ty={}, rcvr_substs={}, method_ty={}, origin={})", - self.xform_self_ty.repr(tcx), - self.rcvr_substs.repr(tcx), - self.method_ty.repr(tcx), - self.origin) - } -} - -impl Repr for ExtensionCandidate { - fn repr(&self, tcx: &ty::ctxt) -> String { - format!("ExtensionCandidate(obligation={}, xform_self_ty={}, method_ty={}, method_num={})", - self.obligation.repr(tcx), - self.xform_self_ty.repr(tcx), - self.method_ty.repr(tcx), - self.method_num) - } -} - -impl Candidate { - fn to_source(&self) -> CandidateSource { - match self.origin { - MethodStatic(def_id) => { - ImplSource(def_id) - } - MethodStaticUnboxedClosure(..) => { - panic!("MethodStaticUnboxedClosure only used in trans") - } - MethodTypeParam(ref param) => { - TraitSource(param.trait_ref.def_id) - } - MethodTraitObject(ref obj) => { - TraitSource(obj.trait_ref.def_id) - } - } - } -} - -impl ExtensionCandidate { - fn to_source(&self) -> CandidateSource { - TraitSource(self.obligation.trait_ref.def_id) - } -} diff --git a/src/librustc/middle/typeck/check/method/confirm.rs b/src/librustc/middle/typeck/check/method/confirm.rs new file mode 100644 index 00000000000..ba64a1e23a7 --- /dev/null +++ b/src/librustc/middle/typeck/check/method/confirm.rs @@ -0,0 +1,603 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::probe; + +use middle::subst; +use middle::subst::Subst; +use middle::traits; +use middle::ty; +use middle::typeck::check; +use middle::typeck::check::{FnCtxt, NoPreference, PreferMutLvalue}; +use middle::typeck::{MethodCall, MethodCallee, MethodObject, MethodOrigin, + MethodParam, MethodStatic, MethodTraitObject, MethodTypeParam}; +use middle::typeck::infer; +use middle::typeck::infer::InferCtxt; +use syntax::ast; +use syntax::codemap::Span; +use std::rc::Rc; +use std::mem; +use util::ppaux::Repr; + +struct ConfirmContext<'a, 'tcx:'a> { + fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + self_expr: &'a ast::Expr, +} + +pub fn confirm(fcx: &FnCtxt, + span: Span, + self_expr: &ast::Expr, + unadjusted_self_ty: ty::t, + pick: probe::Pick, + supplied_method_types: Vec) + -> MethodCallee +{ + debug!("confirm(unadjusted_self_ty={}, pick={}, supplied_method_types={})", + unadjusted_self_ty.repr(fcx.tcx()), + pick.repr(fcx.tcx()), + supplied_method_types.repr(fcx.tcx())); + + let mut confirm_cx = ConfirmContext::new(fcx, span, self_expr); + confirm_cx.confirm(unadjusted_self_ty, pick, supplied_method_types) +} + +impl<'a,'tcx> ConfirmContext<'a,'tcx> { + fn new(fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + self_expr: &'a ast::Expr) + -> ConfirmContext<'a, 'tcx> + { + ConfirmContext { fcx: fcx, span: span, self_expr: self_expr } + } + + fn confirm(&mut self, + unadjusted_self_ty: ty::t, + pick: probe::Pick, + supplied_method_types: Vec) + -> MethodCallee + { + // Adjust the self expression the user provided and obtain the adjusted type. + let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick.adjustment); + + // Make sure nobody calls `drop()` explicitly. + self.enforce_drop_trait_limitations(&pick); + + // Create substitutions for the method's type parameters. + let (rcvr_substs, method_origin) = + self.fresh_receiver_substs(self_ty, &pick); + let (method_types, method_regions) = + self.instantiate_method_substs(&pick, supplied_method_types); + let all_substs = rcvr_substs.with_method(method_types, method_regions); + debug!("all_substs={}", all_substs.repr(self.tcx())); + + // Create the final signature for the method, replacing late-bound regions. + let method_sig = self.instantiate_method_sig(&pick, &all_substs); + let method_self_ty = method_sig.inputs[0]; + + // Unify the (adjusted) self type with what the method expects. + self.unify_receivers(self_ty, method_self_ty); + + // Add any trait/regions obligations specified on the method's type parameters. + self.add_obligations(&pick, &all_substs); + + // Create the final `MethodCallee`. + let fty = ty::mk_bare_fn(self.tcx(), ty::BareFnTy { + sig: method_sig, + fn_style: pick.method_ty.fty.fn_style, + abi: pick.method_ty.fty.abi.clone(), + }); + let callee = MethodCallee { + origin: method_origin, + 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 oveloaded derefs and so on). + self.fixup_derefs_on_method_receiver_if_necessary(&callee); + + callee + } + + /////////////////////////////////////////////////////////////////////////// + // ADJUSTMENTS + + fn adjust_self_ty(&mut self, + unadjusted_self_ty: ty::t, + adjustment: &probe::PickAdjustment) + -> ty::t + { + // Construct the actual adjustment and write it into the table + let auto_deref_ref = self.create_ty_adjustment(adjustment); + + // Commit the autoderefs by calling `autoderef again, but this + // time writing the results into the various tables. + let (autoderefd_ty, n, result) = + check::autoderef( + self.fcx, self.span, unadjusted_self_ty, Some(self.self_expr.id), NoPreference, + |_, n| if n == auto_deref_ref.autoderefs { Some(()) } else { None }); + assert_eq!(n, auto_deref_ref.autoderefs); + assert_eq!(result, Some(())); + + let final_ty = + ty::adjust_ty_for_autoref(self.tcx(), self.span, autoderefd_ty, + auto_deref_ref.autoref.as_ref()); + + // Write out the final adjustment. + self.fcx.write_adjustment(self.self_expr.id, self.span, ty::AdjustDerefRef(auto_deref_ref)); + + final_ty + } + + fn create_ty_adjustment(&mut self, + adjustment: &probe::PickAdjustment) + -> ty::AutoDerefRef + { + match *adjustment { + probe::AutoDeref(num) => { + ty::AutoDerefRef { + autoderefs: num, + autoref: None + } + } + probe::AutoUnsizeLength(autoderefs, len) => { + ty::AutoDerefRef { + autoderefs: autoderefs, + autoref: Some(ty::AutoUnsize(ty::UnsizeLength(len))) + } + } + probe::AutoRef(mutability, ref sub_adjustment) => { + let deref = self.create_ty_adjustment(&**sub_adjustment); + let region = self.infcx().next_region_var(infer::Autoref(self.span)); + wrap_autoref(deref, |base| ty::AutoPtr(region, mutability, base)) + } + } + } + + /////////////////////////////////////////////////////////////////////////// + // + + fn fresh_receiver_substs(&mut self, + self_ty: ty::t, + pick: &probe::Pick) + -> (subst::Substs, MethodOrigin) + { + /*! + * Returns a set of substitutions for the method *receiver* + * where all type and region parameters are instantiated with + * fresh variables. This substitution does not include any + * parameters declared on the method itself. + */ + + match pick.kind { + probe::InherentImplPick(impl_def_id) => { + assert!(ty::impl_trait_ref(self.tcx(), impl_def_id).is_none(), + "impl {} is not an inherent impl", impl_def_id); + let impl_polytype = check::impl_self_ty(self.fcx, self.span, impl_def_id); + + (impl_polytype.substs, MethodStatic(pick.method_ty.def_id)) + } + + probe::ObjectPick(trait_def_id, method_num, real_index) => { + self.extract_trait_ref(self_ty, |this, object_ty, data| { + // The object data has no entry for the Self + // Type. For the purposes of this method call, we + // substitute the object type itself. This + // wouldn't be a sound substitution in all cases, + // since each instance of the object type is a + // different existential and hence could match + // distinct types (e.g., if `Self` appeared as an + // argument type), but those cases have already + // been ruled out when we deemed the trait to be + // "object safe". + let substs = data.principal.substs.clone().with_self_ty(object_ty); + let original_trait_ref = + Rc::new(ty::TraitRef::new(data.principal.def_id, substs)); + let upcast_trait_ref = this.upcast(original_trait_ref.clone(), trait_def_id); + debug!("original_trait_ref={} upcast_trait_ref={} target_trait={}", + original_trait_ref.repr(this.tcx()), + upcast_trait_ref.repr(this.tcx()), + trait_def_id.repr(this.tcx())); + let substs = upcast_trait_ref.substs.clone(); + let origin = MethodTraitObject(MethodObject { + trait_ref: upcast_trait_ref, + object_trait_id: trait_def_id, + method_num: method_num, + real_index: real_index, + }); + (substs, origin) + }) + } + + probe::ExtensionImplPick(impl_def_id, method_num) => { + // The method being invoked is the method as defined on the trait, + // so return the substitutions from the trait. Consider: + // + // impl Trait for Foo { ... } + // + // If we instantiate A, B, and C with $A, $B, and $C + // respectively, then we want to return the type + // parameters from the trait ([$A,$B]), not those from + // the impl ([$A,$B,$C]) not the receiver type ([$C]). + let impl_polytype = check::impl_self_ty(self.fcx, self.span, impl_def_id); + let impl_trait_ref = ty::impl_trait_ref(self.tcx(), impl_def_id) + .unwrap() + .subst(self.tcx(), &impl_polytype.substs); + let origin = MethodTypeParam(MethodParam { trait_ref: impl_trait_ref.clone(), + method_num: method_num }); + (impl_trait_ref.substs.clone(), origin) + } + + probe::TraitPick(trait_def_id, method_num) => { + let trait_def = ty::lookup_trait_def(self.tcx(), trait_def_id); + + // Make a trait reference `$0 : Trait<$1...$n>` + // consisting entirely of type variables. Later on in + // the process we will unify the transformed-self-type + // of the method with the actual type in order to + // unify some of these variables. + let substs = self.infcx().fresh_substs_for_trait(self.span, + &trait_def.generics, + self.infcx().next_ty_var()); + + let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, substs.clone())); + let origin = MethodTypeParam(MethodParam { trait_ref: trait_ref, + method_num: method_num }); + (substs, origin) + } + + probe::WhereClausePick(ref trait_ref, method_num) => { + let origin = MethodTypeParam(MethodParam { trait_ref: (*trait_ref).clone(), + method_num: method_num }); + (trait_ref.substs.clone(), origin) + } + } + } + + fn extract_trait_ref(&mut self, + self_ty: ty::t, + closure: |&mut ConfirmContext<'a,'tcx>, ty::t, &ty::TyTrait| -> R) + -> R + { + // If we specified that this is an object method, then the + // self-type ought to be something that can be dereferenced to + // yield an object-type (e.g., `&Object` or `Box` + // etc). + + let (_, _, result) = + check::autoderef( + self.fcx, self.span, self_ty, None, NoPreference, + |ty, _| { + match ty::get(ty).sty { + ty::ty_trait(ref data) => Some(closure(self, ty, &**data)), + _ => None, + } + }); + + match result { + Some(r) => r, + None => { + self.tcx().sess.span_bug( + self.span, + format!("self-type `{}` for ObjectPick never dereferenced to an object", + self_ty.repr(self.tcx()))[]) + } + } + } + + fn instantiate_method_substs(&mut self, + pick: &probe::Pick, + supplied_method_types: Vec) + -> (Vec, Vec) + { + // Determine the values for the generic parameters of the method. + // If they were not explicitly supplied, just construct fresh + // variables. + let num_supplied_types = supplied_method_types.len(); + let num_method_types = pick.method_ty.generics.types.len(subst::FnSpace); + let method_types = { + if num_supplied_types == 0u { + self.fcx.infcx().next_ty_vars(num_method_types) + } else if num_method_types == 0u { + span_err!(self.tcx().sess, self.span, E0035, + "does not take type parameters"); + self.fcx.infcx().next_ty_vars(num_method_types) + } else if num_supplied_types != num_method_types { + span_err!(self.tcx().sess, self.span, E0036, + "incorrect number of type parameters given for this method"); + Vec::from_elem(num_method_types, ty::mk_err()) + } else { + supplied_method_types + } + }; + + // Create subst for early-bound lifetime parameters, combining + // parameters from the type and those from the method. + // + // FIXME -- permit users to manually specify lifetimes + let method_regions = + self.fcx.infcx().region_vars_for_defs( + self.span, + pick.method_ty.generics.regions.get_slice(subst::FnSpace)); + + (method_types, method_regions) + } + + fn unify_receivers(&mut self, + self_ty: ty::t, + method_self_ty: ty::t) + { + match self.fcx.mk_subty(false, infer::Misc(self.span), self_ty, method_self_ty) { + Ok(_) => {} + Err(_) => { + self.tcx().sess.span_bug( + self.span, + format!( + "{} was a subtype of {} but now is not?", + self_ty.repr(self.tcx()), + method_self_ty.repr(self.tcx()))[]); + } + } + } + + /////////////////////////////////////////////////////////////////////////// + // + + fn instantiate_method_sig(&mut self, + pick: &probe::Pick, + all_substs: &subst::Substs) + -> ty::FnSig + { + let ref bare_fn_ty = pick.method_ty.fty; + let fn_sig = bare_fn_ty.sig.subst(self.tcx(), all_substs); + self.infcx().replace_late_bound_regions_with_fresh_var(fn_sig.binder_id, + self.span, + infer::FnCall, + &fn_sig).0 + } + + fn add_obligations(&mut self, + pick: &probe::Pick, + all_substs: &subst::Substs) { + // FIXME(DST). Super hack. For a method on a trait object + // `Trait`, the generic signature requires that + // `Self:Trait`. Since, for an object, we bind `Self` to the + // type `Trait`, this leads to an obligation + // `Trait:Trait`. Until such time we DST is fully implemented, + // that obligation is not necessarily satisfied. (In the + // future, it would be.) + // + // To sidestep this, we overwrite the binding for `Self` with + // `err` (just for trait objects) when we generate the + // obligations. This causes us to generate the obligation + // `err:Trait`, and the error type is considered to implement + // all traits, so we're all good. Hack hack hack. + match pick.kind { + probe::ObjectPick(..) => { + let mut temp_substs = all_substs.clone(); + temp_substs.types.get_mut_slice(subst::SelfSpace)[0] = ty::mk_err(); + self.fcx.add_obligations_for_parameters( + traits::ObligationCause::misc(self.span), + &temp_substs, + &pick.method_ty.generics); + } + _ => { + self.fcx.add_obligations_for_parameters( + traits::ObligationCause::misc(self.span), + all_substs, + &pick.method_ty.generics); + } + } + } + + /////////////////////////////////////////////////////////////////////////// + // RECONCILIATION + + fn fixup_derefs_on_method_receiver_if_necessary(&self, + method_callee: &MethodCallee) { + /*! + * When we select a method with an `&mut self` receiver, we have to go + * convert any auto-derefs, indices, etc from `Deref` and `Index` into + * `DerefMut` and `IndexMut` respectively. + */ + + let sig = match ty::get(method_callee.ty).sty { + ty::ty_bare_fn(ref f) => f.sig.clone(), + ty::ty_closure(ref f) => f.sig.clone(), + _ => return, + }; + + match ty::get(sig.inputs[0]).sty { + ty::ty_rptr(_, ty::mt { + ty: _, + mutbl: ast::MutMutable, + }) => {} + _ => return, + } + + // Gather up expressions we want to munge. + let mut exprs = Vec::new(); + exprs.push(self.self_expr); + loop { + let last = exprs[exprs.len() - 1]; + match last.node { + ast::ExprParen(ref expr) | + ast::ExprField(ref expr, _, _) | + ast::ExprTupField(ref expr, _, _) | + ast::ExprSlice(ref expr, _, _, _) | + ast::ExprIndex(ref expr, _) | + ast::ExprUnary(ast::UnDeref, ref expr) => exprs.push(&**expr), + _ => break, + } + } + + debug!("fixup_derefs_on_method_receiver_if_necessary: exprs={}", + exprs.repr(self.tcx())); + + // Fix up autoderefs and derefs. + for (i, expr) in exprs.iter().rev().enumerate() { + // Count autoderefs. + let autoderef_count = match self.fcx + .inh + .adjustments + .borrow() + .get(&expr.id) { + Some(&ty::AdjustDerefRef(ty::AutoDerefRef { + autoderefs: autoderef_count, + autoref: _ + })) => autoderef_count, + Some(_) | None => 0, + }; + + debug!("fixup_derefs_on_method_receiver_if_necessary: i={} expr={} autoderef_count={}", + i, expr.repr(self.tcx()), autoderef_count); + + if autoderef_count > 0 { + check::autoderef(self.fcx, + expr.span, + self.fcx.expr_ty(*expr), + Some(expr.id), + PreferMutLvalue, + |_, autoderefs| { + if autoderefs == autoderef_count + 1 { + Some(()) + } else { + None + } + }); + } + + // Don't retry the first one or we might infinite loop! + if i != 0 { + match expr.node { + ast::ExprIndex(ref base_expr, _) => { + let mut base_adjustment = + match self.fcx.inh.adjustments.borrow().get(&base_expr.id) { + Some(&ty::AdjustDerefRef(ref adr)) => (*adr).clone(), + None => ty::AutoDerefRef { autoderefs: 0, autoref: None }, + Some(_) => { + self.tcx().sess.span_bug( + base_expr.span, + "unexpected adjustment type"); + } + }; + + // If this is an overloaded index, the + // adjustment will include an extra layer of + // autoref because the method is an &self/&mut + // self method. We have to peel it off to get + // the raw adjustment that `try_index_step` + // expects. This is annoying and horrible. We + // ought to recode this routine so it doesn't + // (ab)use the normal type checking paths. + base_adjustment.autoref = match base_adjustment.autoref { + None => { None } + Some(ty::AutoPtr(_, _, None)) => { None } + Some(ty::AutoPtr(_, _, Some(box r))) => { Some(r) } + Some(_) => { + self.tcx().sess.span_bug( + base_expr.span, + "unexpected adjustment autoref"); + } + }; + + let adjusted_base_ty = + self.fcx.adjust_expr_ty( + &**base_expr, + Some(&ty::AdjustDerefRef(base_adjustment.clone()))); + + check::try_index_step( + self.fcx, + MethodCall::expr(expr.id), + *expr, + &**base_expr, + adjusted_base_ty, + base_adjustment, + PreferMutLvalue); + } + ast::ExprUnary(ast::UnDeref, ref base_expr) => { + // if this is an overloaded deref, then re-evaluate with + // a preference for mut + let method_call = MethodCall::expr(expr.id); + if self.fcx.inh.method_map.borrow().contains_key(&method_call) { + check::try_overloaded_deref( + self.fcx, + expr.span, + Some(method_call), + Some(&**base_expr), + self.fcx.expr_ty(&**base_expr), + PreferMutLvalue); + } + } + _ => {} + } + } + } + } + + /////////////////////////////////////////////////////////////////////////// + // MISCELLANY + + fn tcx(&self) -> &'a ty::ctxt<'tcx> { + self.fcx.tcx() + } + + fn infcx(&self) -> &'a InferCtxt<'a, 'tcx> { + self.fcx.infcx() + } + + fn enforce_drop_trait_limitations(&self, pick: &probe::Pick) { + // Disallow calls to the method `drop` defined in the `Drop` trait. + match pick.method_ty.container { + ty::TraitContainer(trait_def_id) => { + if Some(trait_def_id) == self.tcx().lang_items.drop_trait() { + span_err!(self.tcx().sess, self.span, E0040, + "explicit call to destructor"); + } + } + ty::ImplContainer(..) => { + // Since `drop` is a trait method, we expect that any + // potential calls to it will wind up in the other + // arm. But just to be sure, check that the method id + // does not appear in the list of destructors. + assert!(!self.tcx().destructors.borrow().contains(&pick.method_ty.def_id)); + } + } + } + + fn upcast(&mut self, + source_trait_ref: Rc, + target_trait_def_id: ast::DefId) + -> Rc + { + for super_trait_ref in traits::supertraits(self.tcx(), source_trait_ref.clone()) { + if super_trait_ref.def_id == target_trait_def_id { + return super_trait_ref; + } + } + + self.tcx().sess.span_bug( + self.span, + format!("cannot upcast `{}` to `{}`", + source_trait_ref.repr(self.tcx()), + target_trait_def_id.repr(self.tcx()))[]); + } +} + +fn wrap_autoref(mut deref: ty::AutoDerefRef, + base_fn: |Option>| -> ty::AutoRef) + -> ty::AutoDerefRef { + let autoref = mem::replace(&mut deref.autoref, None); + let autoref = autoref.map(|r| box r); + deref.autoref = Some(base_fn(autoref)); + deref +} diff --git a/src/librustc/middle/typeck/check/method/doc.rs b/src/librustc/middle/typeck/check/method/doc.rs new file mode 100644 index 00000000000..8c691e02ca9 --- /dev/null +++ b/src/librustc/middle/typeck/check/method/doc.rs @@ -0,0 +1,126 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + +# Method lookup + +Method lookup can be rather complex due to the interaction of a number +of factors, such as self types, autoderef, trait lookup, etc. This +file provides an overview of the process. More detailed notes are in +the code itself, naturally. + +One way to think of method lookup is that we convert an expression of +the form: + + receiver.method(...) + +into a more explicit UFCS form: + + Trait::method(ADJ(receiver), ...) // for a trait call + ReceiverType::method(ADJ(receiver), ...) // for an inherent method call + +Here `ADJ` is some kind of adjustment, which is typically a series of +autoderefs and then possibly an autoref (e.g., `&**receiver`). However +we sometimes do other adjustments and coercions along the way, in +particular unsizing (e.g., converting from `[T, ..n]` to `[T]`). + +## The Two Phases + +Method lookup is divided into two major phases: probing (`probe.rs`) +and confirmation (`confirm.rs`). The probe phase is when we decide +what method to call and how to adjust the receiver. The confirmation +phase "applies" this selection, updating the side-tables, unifying +type variables, and otherwise doing side-effectful things. + +One reason for this division is to be more amenable to caching. The +probe phase produces a "pick" (`probe::Pick`), which is designed to be +cacheable across method-call sites. Therefore, it does not include +inference variables or other information. + +## Probe phase + +The probe phase (`probe.rs`) decides what method is being called and +how to adjust the receiver. + +### Steps + +The first thing that the probe phase does is to create a series of +*steps*. This is done by progressively dereferencing the receiver type +until it cannot be deref'd anymore, as well as applying an optional +"unsize" step. So if the receiver has type `Rc>`, this +might yield: + + Rc> + Box<[T, ..3]> + [T, ..3] + [T] + +### Candidate assembly + +We then search along those steps to create a list of *candidates*. A +`Candidate` is a method item that might plausibly be the method being +invoked. For each candidate, we'll derive a "transformed self type" +that takes into account explicit self. + +Candidates are grouped into two kinds, inherent and extension. + +**Inherent candidates** are those that are derived from the +type of the receiver itself. So, if you have a receiver of some +nominal type `Foo` (e.g., a struct), any methods defined within an +impl like `impl Foo` are inherent methods. Nothing needs to be +imported to use an inherent method, they are associated with the type +itself (note that inherent impls can only be defined in the same +module as the type itself). + +FIXME: Inherent candidates are not always derived from impls. If you +have a trait object, such as a value of type `Box`, then the +trait methods (`to_string()`, in this case) are inherently associated +with it. Another case is type parameters, in which case the methods of +their bounds are inherent. However, this part of the rules is subject +to change: when DST's "impl Trait for Trait" is complete, trait object +dispatch could be subsumed into trait matching, and the type parameter +behavior should be reconsidered in light of where clauses. + +**Extension candidates** are derived from imported traits. If I have +the trait `ToString` imported, and I call `to_string()` on a value of +type `T`, then we will go off to find out whether there is an impl of +`ToString` for `T`. These kinds of method calls are called "extension +methods". They can be defined in any module, not only the one that +defined `T`. Furthermore, you must import the trait to call such a +method. + +So, let's continue our example. Imagine that we were calling a method +`foo` with the receiver `Rc>` and there is a trait `Foo` +that defines it with `&self` for the type `Rc` as well as a method +on the type `Box` that defines `Foo` but with `&mut self`. Then we +might have two candidates: + + &Rc> from the impl of `Foo` for `Rc` where `U=Box + &mut Box<[T, ..3]>> from the inherent impl on `Box` where `U=[T, ..3]` + +### Candidate search + +Finally, to actually pick the method, we will search down the steps, +trying to match the receiver type against the candidate types. At +each step, we also consider an auto-ref and auto-mut-ref to see whether +that makes any of the candidates match. We pick the first step where +we find a match. + +In the case of our example, the first step is `Rc>`, +which does not itself match any candidate. But when we autoref it, we +get the type `&Rc>` which does match. We would then +recursively consider all where-clauses that appear on the impl: if +those match (or we cannot rule out that they do), then this is the +method we would pick. Otherwise, we would continue down the series of +steps. + +*/ + diff --git a/src/librustc/middle/typeck/check/method/mod.rs b/src/librustc/middle/typeck/check/method/mod.rs new file mode 100644 index 00000000000..2cd98137057 --- /dev/null +++ b/src/librustc/middle/typeck/check/method/mod.rs @@ -0,0 +1,432 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! Method lookup: the secret sauce of Rust. See `doc.rs`. */ + +use middle::subst; +use middle::subst::{Subst}; +use middle::traits; +use middle::ty::*; +use middle::ty; +use middle::typeck::astconv::AstConv; +use middle::typeck::check::{FnCtxt}; +use middle::typeck::check::{impl_self_ty}; +use middle::typeck::check::vtable; +use middle::typeck::check::vtable::select_new_fcx_obligations; +use middle::typeck::infer; +use middle::typeck::{MethodCallee}; +use middle::typeck::{MethodParam, MethodTypeParam}; +use util::ppaux::{Repr, UserString}; + +use std::rc::Rc; +use syntax::ast::{DefId}; +use syntax::ast; +use syntax::codemap::Span; + +mod confirm; +mod doc; +mod probe; + +pub enum MethodError { + // Did not find an applicable method, but we did find various + // static methods that may apply. + NoMatch(Vec), + + // Multiple methods might apply. + Ambiguity(Vec), +} + +// A pared down enum describing just the places from which a method +// candidate can arise. Used for error reporting only. +#[deriving(PartialOrd, Ord, PartialEq, Eq)] +pub enum CandidateSource { + ImplSource(ast::DefId), + TraitSource(/* trait id */ ast::DefId), +} + +type MethodIndex = uint; // just for doc purposes + +pub fn exists(fcx: &FnCtxt, + span: Span, + method_name: ast::Name, + self_ty: ty::t, + call_expr_id: ast::NodeId) + -> bool +{ + /*! + * Determines whether the type `self_ty` supports a method name `method_name` or not. + */ + + match probe::probe(fcx, span, method_name, self_ty, call_expr_id) { + Ok(_) => true, + Err(NoMatch(_)) => false, + Err(Ambiguity(_)) => true, + } +} + +pub fn lookup(fcx: &FnCtxt, + span: Span, + method_name: ast::Name, + self_ty: ty::t, + supplied_method_types: Vec, + call_expr_id: ast::NodeId, + self_expr: &ast::Expr) + -> Result +{ + /*! + * Performs method lookup. If lookup is successful, it will return the callee + * and store an appropriate adjustment for the self-expr. In some cases it may + * report an error (e.g., invoking the `drop` method). + * + * # Arguments + * + * Given a method call like `foo.bar::(...)`: + * + * - `fcx`: the surrounding `FnCtxt` (!) + * - `span`: the span for the method call + * - `method_name`: the name of the method being called (`bar`) + * - `self_ty`: the (unadjusted) type of the self expression (`foo`) + * - `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`) + * - `self_expr`: the self expression (`foo`) + */ + + debug!("lookup(method_name={}, self_ty={}, call_expr_id={}, self_expr={})", + method_name.repr(fcx.tcx()), + self_ty.repr(fcx.tcx()), + call_expr_id, + self_expr.repr(fcx.tcx())); + + let pick = try!(probe::probe(fcx, span, method_name, self_ty, call_expr_id)); + Ok(confirm::confirm(fcx, span, self_expr, self_ty, pick, supplied_method_types)) +} + +pub fn lookup_in_trait<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + self_expr: Option<&'a ast::Expr>, + m_name: ast::Name, + trait_def_id: DefId, + self_ty: ty::t, + opt_input_types: Option>) + -> Option +{ + lookup_in_trait_adjusted(fcx, span, self_expr, m_name, trait_def_id, + ty::AutoDerefRef { autoderefs: 0, autoref: None }, + self_ty, opt_input_types) +} + +pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + self_expr: Option<&'a ast::Expr>, + m_name: ast::Name, + trait_def_id: DefId, + autoderefref: ty::AutoDerefRef, + self_ty: ty::t, + opt_input_types: Option>) + -> Option +{ + /*! + * `lookup_in_trait_adjusted` is used for overloaded operators. It + * does a very narrow slice of what the normal probe/confirm path + * does. In particular, it doesn't really do any probing: it + * simply constructs an obligation for a particular trait with the + * given self-type and checks whether that trait is implemented. + * + * FIXME(#18741) -- It seems likely that we can consolidate some of this + * code with the other method-lookup code. In particular, + * autoderef on index is basically identical to autoderef with + * normal probes, except that the test also looks for built-in + * indexing. Also, the second half of this method is basically + * the same as confirmation. + */ + + debug!("lookup_in_trait_adjusted(self_ty={}, self_expr={}, m_name={}, trait_def_id={})", + self_ty.repr(fcx.tcx()), + self_expr.repr(fcx.tcx()), + m_name.repr(fcx.tcx()), + trait_def_id.repr(fcx.tcx())); + + let trait_def = ty::lookup_trait_def(fcx.tcx(), trait_def_id); + + let expected_number_of_input_types = trait_def.generics.types.len(subst::TypeSpace); + let input_types = match opt_input_types { + Some(input_types) => { + assert_eq!(expected_number_of_input_types, input_types.len()); + input_types + } + + None => { + fcx.inh.infcx.next_ty_vars(expected_number_of_input_types) + } + }; + + let number_assoc_types = trait_def.generics.types.len(subst::AssocSpace); + let assoc_types = fcx.inh.infcx.next_ty_vars(number_assoc_types); + + assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0); + assert!(trait_def.generics.regions.is_empty()); + + // Construct a trait-reference `self_ty : Trait` + let substs = subst::Substs::new_trait(input_types, Vec::new(), assoc_types, self_ty); + let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, substs)); + + // Construct an obligation + let obligation = traits::Obligation::misc(span, trait_ref.clone()); + + // Now we want to know if this can be matched + let mut selcx = traits::SelectionContext::new(fcx.infcx(), + &fcx.inh.param_env, + fcx); + if !selcx.evaluate_obligation(&obligation) { + debug!("--> Cannot match obligation"); + return None; // Cannot be matched, no such method resolution is possible. + } + + // Trait must have a method named `m_name` and it should not have + // type parameters or early-bound regions. + let tcx = fcx.tcx(); + let (method_num, method_ty) = trait_method(tcx, trait_def_id, m_name).unwrap(); + assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0); + assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0); + + // Substitute the trait parameters into the method type and + // instantiate late-bound regions to get the actual method type. + let ref bare_fn_ty = method_ty.fty; + let fn_sig = bare_fn_ty.sig.subst(tcx, &trait_ref.substs); + let fn_sig = fcx.infcx().replace_late_bound_regions_with_fresh_var(fn_sig.binder_id, + span, + infer::FnCall, + &fn_sig).0; + let transformed_self_ty = fn_sig.inputs[0]; + let fty = ty::mk_bare_fn(tcx, ty::BareFnTy { + sig: fn_sig, + fn_style: bare_fn_ty.fn_style, + abi: bare_fn_ty.abi.clone(), + }); + + debug!("matched method fty={} obligation={}", + fty.repr(fcx.tcx()), + obligation.repr(fcx.tcx())); + + // Register obligations for the parameters. This will include the + // `Self` parameter, which in turn has a bound of the main trait, + // so this also effectively registers `obligation` as well. (We + // used to register `obligation` explicitly, but that resulted in + // double error messages being reported.) + fcx.add_obligations_for_parameters( + traits::ObligationCause::misc(span), + &trait_ref.substs, + &method_ty.generics); + + // FIXME(#18653) -- Try to resolve obligations, giving us more + // typing information, which can sometimes be needed to avoid + // pathological region inference failures. + vtable::select_new_fcx_obligations(fcx); + + // Insert any adjustments needed (always an autoref of some mutability). + match self_expr { + None => { } + + Some(self_expr) => { + debug!("inserting adjustment if needed (self-id = {}, \ + base adjustment = {}, explicit self = {})", + self_expr.id, autoderefref, method_ty.explicit_self); + + match method_ty.explicit_self { + ty::ByValueExplicitSelfCategory => { + // Trait method is fn(self), no transformation needed. + if !autoderefref.is_identity() { + fcx.write_adjustment( + self_expr.id, + span, + ty::AdjustDerefRef(autoderefref)); + } + } + + ty::ByReferenceExplicitSelfCategory(..) => { + // Trait method is fn(&self) or fn(&mut self), need an + // autoref. Pull the region etc out of the type of first argument. + match ty::get(transformed_self_ty).sty { + ty::ty_rptr(region, ty::mt { mutbl, ty: _ }) => { + let ty::AutoDerefRef { autoderefs, autoref } = autoderefref; + let autoref = autoref.map(|r| box r); + fcx.write_adjustment( + self_expr.id, + span, + ty::AdjustDerefRef(ty::AutoDerefRef { + autoderefs: autoderefs, + autoref: Some(ty::AutoPtr(region, mutbl, autoref)) + })); + } + + _ => { + fcx.tcx().sess.span_bug( + span, + format!( + "trait method is &self but first arg is: {}", + transformed_self_ty.repr(fcx.tcx())).as_slice()); + } + } + } + + _ => { + fcx.tcx().sess.span_bug( + span, + format!( + "unexpected explicit self type in operator method: {}", + method_ty.explicit_self).as_slice()); + } + } + } + } + + let callee = MethodCallee { + origin: MethodTypeParam(MethodParam{trait_ref: trait_ref.clone(), + method_num: method_num}), + ty: fty, + substs: trait_ref.substs.clone() + }; + + debug!("callee = {}", callee.repr(fcx.tcx())); + + Some(callee) +} + +pub fn report_error(fcx: &FnCtxt, + span: Span, + rcvr_ty: ty::t, + method_name: ast::Name, + error: MethodError) +{ + match error { + NoMatch(static_sources) => { + let cx = fcx.tcx(); + let method_ustring = method_name.user_string(cx); + + // True if the type is a struct and contains a field with + // the same name as the not-found method + let is_field = match ty::get(rcvr_ty).sty { + ty_struct(did, _) => + ty::lookup_struct_fields(cx, did) + .iter() + .any(|f| f.name.user_string(cx) == method_ustring), + _ => false + }; + + fcx.type_error_message( + span, + |actual| { + format!("type `{}` does not implement any \ + method in scope named `{}`", + actual, + method_ustring) + }, + rcvr_ty, + None); + + // If the method has the name of a field, give a help note + if is_field { + cx.sess.span_note(span, + format!("use `(s.{0})(...)` if you meant to call the \ + function stored in the `{0}` field", method_ustring).as_slice()); + } + + if static_sources.len() > 0 { + fcx.tcx().sess.fileline_note( + span, + "found defined static methods, maybe a `self` is missing?"); + + report_candidates(fcx, span, method_name, static_sources); + } + } + + Ambiguity(sources) => { + span_err!(fcx.sess(), span, E0034, + "multiple applicable methods in scope"); + + report_candidates(fcx, span, method_name, sources); + } + } + + fn report_candidates(fcx: &FnCtxt, + span: Span, + method_name: ast::Name, + mut sources: Vec) { + sources.sort(); + sources.dedup(); + + for (idx, source) in sources.iter().enumerate() { + match *source { + ImplSource(impl_did) => { + // Provide the best span we can. Use the method, if local to crate, else + // the impl, if local to crate (method may be defaulted), else the call site. + let method = impl_method(fcx.tcx(), impl_did, method_name).unwrap(); + let impl_span = fcx.tcx().map.def_id_span(impl_did, span); + let method_span = fcx.tcx().map.def_id_span(method.def_id, impl_span); + + let impl_ty = impl_self_ty(fcx, span, impl_did).ty; + + let insertion = match impl_trait_ref(fcx.tcx(), impl_did) { + None => format!(""), + Some(trait_ref) => format!(" of the trait `{}`", + ty::item_path_str(fcx.tcx(), + trait_ref.def_id)), + }; + + span_note!(fcx.sess(), method_span, + "candidate #{} is defined in an impl{} for the type `{}`", + idx + 1u, + insertion, + impl_ty.user_string(fcx.tcx())); + } + TraitSource(trait_did) => { + let (_, method) = trait_method(fcx.tcx(), trait_did, method_name).unwrap(); + let method_span = fcx.tcx().map.def_id_span(method.def_id, span); + span_note!(fcx.sess(), method_span, + "candidate #{} is defined in the trait `{}`", + idx + 1u, + ty::item_path_str(fcx.tcx(), trait_did)); + } + } + } + } +} + +fn trait_method(tcx: &ty::ctxt, + trait_def_id: ast::DefId, + method_name: ast::Name) + -> Option<(uint, Rc)> +{ + /*! + * Find method with name `method_name` defined in `trait_def_id` and return it, + * along with its index (or `None`, if no such method). + */ + + let trait_items = ty::trait_items(tcx, trait_def_id); + trait_items + .iter() + .enumerate() + .find(|&(_, ref item)| item.name() == method_name) + .and_then(|(idx, item)| item.as_opt_method().map(|m| (idx, m))) +} + +fn impl_method(tcx: &ty::ctxt, + impl_def_id: ast::DefId, + method_name: ast::Name) + -> Option> +{ + let impl_items = tcx.impl_items.borrow(); + let impl_items = impl_items.get(&impl_def_id).unwrap(); + impl_items + .iter() + .map(|&did| ty::impl_or_trait_item(tcx, did.def_id())) + .find(|m| m.name() == method_name) + .and_then(|item| item.as_opt_method()) +} + diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc/middle/typeck/check/method/probe.rs new file mode 100644 index 00000000000..63f017e8041 --- /dev/null +++ b/src/librustc/middle/typeck/check/method/probe.rs @@ -0,0 +1,1011 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::Ambiguity; +use super::CandidateSource; +use super::ImplSource; +use super::MethodError; +use super::MethodIndex; +use super::NoMatch; +use super::TraitSource; + +use middle::subst; +use middle::subst::Subst; +use middle::traits; +use middle::ty; +use middle::typeck::check; +use middle::typeck::check::{FnCtxt, NoPreference}; +use middle::typeck::{MethodObject}; +use middle::typeck::infer; +use middle::typeck::infer::InferCtxt; +use syntax::ast; +use syntax::codemap::{Span, DUMMY_SP}; +use std::collections::HashSet; +use std::rc::Rc; +use util::ppaux::Repr; + +struct ProbeContext<'a, 'tcx:'a> { + fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + method_name: ast::Name, + steps: Rc>, + inherent_candidates: Vec, + extension_candidates: Vec, + impl_dups: HashSet, + static_candidates: Vec, +} + +struct CandidateStep { + self_ty: ty::t, + adjustment: PickAdjustment +} + +struct Candidate { + xform_self_ty: ty::t, + method_ty: Rc, + kind: CandidateKind, +} + +enum CandidateKind { + InherentImplCandidate(/* Impl */ ast::DefId, subst::Substs), + ObjectCandidate(MethodObject), + ExtensionImplCandidate(/* Impl */ ast::DefId, Rc, subst::Substs, MethodIndex), + UnboxedClosureCandidate(/* Trait */ ast::DefId, MethodIndex), + WhereClauseCandidate(Rc, MethodIndex), +} + +pub struct Pick { + pub method_ty: Rc, + pub adjustment: PickAdjustment, + pub kind: PickKind, +} + +#[deriving(Clone,Show)] +pub enum PickKind { + InherentImplPick(/* Impl */ ast::DefId), + ObjectPick(/* Trait */ ast::DefId, /* method_num */ uint, /* real_index */ uint), + ExtensionImplPick(/* Impl */ ast::DefId, MethodIndex), + TraitPick(/* Trait */ ast::DefId, MethodIndex), + WhereClausePick(/* Trait */ Rc, MethodIndex), +} + +pub type PickResult = Result; + +// This is a kind of "abstracted" version of ty::AutoAdjustment. The +// difference is that it doesn't embed any regions or other +// specifics. The "confirmation" step recreates those details as +// needed. +#[deriving(Clone,Show)] +pub enum PickAdjustment { + // Indicates that the source expression should be autoderef'd N times + // + // A = expr | *expr | **expr + AutoDeref(uint), + + // Indicates that the source expression should be autoderef'd N + // times and then "unsized". This should probably eventually go + // away in favor of just coercing method receivers. + // + // A = unsize(expr | *expr | **expr) + AutoUnsizeLength(/* number of autoderefs */ uint, /* length*/ uint), + + // Indicates that an autoref is applied after some number of other adjustments + // + // A = &A | &mut A + AutoRef(ast::Mutability, Box), +} + +pub fn probe(fcx: &FnCtxt, + span: Span, + method_name: ast::Name, + self_ty: ty::t, + call_expr_id: ast::NodeId) + -> PickResult +{ + debug!("probe(self_ty={}, method_name={}, call_expr_id={})", + self_ty.repr(fcx.tcx()), + method_name, + call_expr_id); + + // FIXME(#18741) -- right now, creating the steps involves evaluating the + // `*` operator, which registers obligations that then escape into + // the global fulfillment context and thus has global + // side-effects. This is a bit of a pain to refactor. So just let + // it ride, although it's really not great, and in fact could I + // think cause spurious errors. Really though this part should + // take place in the `fcx.infcx().probe` below. + let steps = create_steps(fcx, span, self_ty); + + debug!("ProbeContext: steps for self_ty={} are {}", + self_ty.repr(fcx.tcx()), + steps.repr(fcx.tcx())); + + // this creates one big transaction so that all type variables etc + // that we create during the probe process are removed later + let mut steps = Some(steps); // FIXME(#18101) need once closures + fcx.infcx().probe(|| { + let steps = steps.take().unwrap(); + let mut probe_cx = ProbeContext::new(fcx, span, method_name, steps); + probe_cx.assemble_inherent_candidates(); + probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id); + probe_cx.pick() + }) +} + +fn create_steps(fcx: &FnCtxt, span: Span, self_ty: ty::t) -> Vec { + let mut steps = Vec::new(); + + let (fully_dereferenced_ty, dereferences, _) = + check::autoderef( + fcx, span, self_ty, None, NoPreference, + |t, d| { + let adjustment = consider_reborrow(t, d); + steps.push(CandidateStep { self_ty: t, adjustment: adjustment }); + None::<()> // keep iterating until we can't anymore + }); + + match ty::get(fully_dereferenced_ty).sty { + ty::ty_vec(elem_ty, Some(len)) => { + steps.push(CandidateStep { + self_ty: ty::mk_vec(fcx.tcx(), elem_ty, None), + adjustment: AutoUnsizeLength(dereferences, len), + }); + } + _ => { + } + } + + return steps; + + fn consider_reborrow(t: ty::t, d: uint) -> PickAdjustment { + // Insert a `&*` or `&mut *` if this is a reference type: + match ty::get(t).sty { + ty::ty_rptr(_, ref mt) => AutoRef(mt.mutbl, box AutoDeref(d+1)), + _ => AutoDeref(d), + } + } +} + +impl<'a,'tcx> ProbeContext<'a,'tcx> { + fn new(fcx: &'a FnCtxt<'a,'tcx>, + span: Span, + method_name: ast::Name, + steps: Vec) + -> ProbeContext<'a,'tcx> + { + ProbeContext { + fcx: fcx, + span: span, + method_name: method_name, + inherent_candidates: Vec::new(), + extension_candidates: Vec::new(), + impl_dups: HashSet::new(), + steps: Rc::new(steps), + static_candidates: Vec::new(), + } + } + + fn tcx(&self) -> &'a ty::ctxt<'tcx> { + self.fcx.tcx() + } + + fn infcx(&self) -> &'a InferCtxt<'a, 'tcx> { + self.fcx.infcx() + } + + /////////////////////////////////////////////////////////////////////////// + // CANDIDATE ASSEMBLY + + fn assemble_inherent_candidates(&mut self) { + let steps = self.steps.clone(); + for step in steps.iter() { + self.assemble_probe(step.self_ty); + } + } + + fn assemble_probe(&mut self, self_ty: ty::t) { + debug!("assemble_probe: self_ty={}", + self_ty.repr(self.tcx())); + + match ty::get(self_ty).sty { + ty::ty_trait(box ty::TyTrait { ref principal, bounds, .. }) => { + self.assemble_inherent_candidates_from_object(self_ty, &*principal, bounds); + self.assemble_inherent_impl_candidates_for_type(principal.def_id); + } + ty::ty_enum(did, _) | + ty::ty_struct(did, _) | + ty::ty_unboxed_closure(did, _, _) => { + self.assemble_inherent_impl_candidates_for_type(did); + } + ty::ty_param(p) => { + self.assemble_inherent_candidates_from_param(self_ty, p); + } + _ => { + } + } + } + + fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: ast::DefId) { + // Read the inherent implementation candidates for this type from the + // metadata if necessary. + ty::populate_implementations_for_type_if_necessary(self.tcx(), def_id); + + for impl_infos in self.tcx().inherent_impls.borrow().get(&def_id).iter() { + for &impl_did in impl_infos.iter() { + self.assemble_inherent_impl_probe(impl_did); + } + } + } + + fn assemble_inherent_impl_probe(&mut self, impl_did: ast::DefId) { + if !self.impl_dups.insert(impl_did) { + return; // already visited + } + + let method = match impl_method(self.tcx(), impl_did, self.method_name) { + Some(m) => m, + None => { return; } // No method with correct name on this impl + }; + + if !self.has_applicable_self(&*method) { + // No receiver declared. Not a candidate. + return self.record_static_candidate(ImplSource(impl_did)); + } + + let impl_pty = check::impl_self_ty(self.fcx, self.span, impl_did); + let impl_substs = impl_pty.substs; + + // Determine the receiver type that the method itself expects. + let xform_self_ty = + self.xform_self_ty(&method, &impl_substs); + + self.inherent_candidates.push(Candidate { + xform_self_ty: xform_self_ty, + method_ty: method, + kind: InherentImplCandidate(impl_did, impl_substs) + }); + } + + fn assemble_inherent_candidates_from_object(&mut self, + self_ty: ty::t, + principal: &ty::TraitRef, + _bounds: ty::ExistentialBounds) { + debug!("assemble_inherent_candidates_from_object(self_ty={})", + self_ty.repr(self.tcx())); + + let tcx = self.tcx(); + + // It is illegal to invoke a method on a trait instance that + // refers to the `Self` type. An error will be reported by + // `enforce_object_limitations()` if the method refers to the + // `Self` type anywhere other than the receiver. Here, we use + // a substitution that replaces `Self` with the object type + // itself. Hence, a `&self` method will wind up with an + // argument type like `&Trait`. + let rcvr_substs = principal.substs.clone().with_self_ty(self_ty); + let trait_ref = Rc::new(ty::TraitRef { + def_id: principal.def_id, + substs: rcvr_substs.clone() + }); + + self.elaborate_bounds(&[trait_ref.clone()], |this, new_trait_ref, m, method_num| { + let vtable_index = + get_method_index(tcx, &*new_trait_ref, + trait_ref.clone(), method_num); + + // FIXME Hacky. By-value `self` methods in objects ought to be + // just a special case of passing ownership of a DST value + // as a parameter. *But* we currently hack them in and tie them to + // the particulars of the `Box` type. So basically for a `fn foo(self,...)` + // method invoked on an object, we don't want the receiver type to be + // `TheTrait`, but rather `Box`. Yuck. + let mut m = m; + match m.explicit_self { + ty::ByValueExplicitSelfCategory => { + let mut n = (*m).clone(); + let self_ty = n.fty.sig.inputs[0]; + n.fty.sig.inputs[0] = ty::mk_uniq(tcx, self_ty); + m = Rc::new(n); + } + _ => { } + } + + let xform_self_ty = + this.xform_self_ty(&m, &new_trait_ref.substs); + + this.inherent_candidates.push(Candidate { + xform_self_ty: xform_self_ty, + method_ty: m, + kind: ObjectCandidate(MethodObject { + trait_ref: new_trait_ref, + object_trait_id: principal.def_id, + method_num: method_num, + real_index: vtable_index + }) + }); + }); + } + + fn assemble_inherent_candidates_from_param(&mut self, + _rcvr_ty: ty::t, + param_ty: ty::ParamTy) { + // FIXME -- Do we want to commit to this behavior for param bounds? + + let ty::ParamTy { space, idx: index, .. } = param_ty; + let bounds = + self.fcx.inh.param_env.bounds.get(space, index).trait_bounds + .as_slice(); + self.elaborate_bounds(bounds, |this, trait_ref, m, method_num| { + let xform_self_ty = + this.xform_self_ty(&m, &trait_ref.substs); + + debug!("found match: trait_ref={} substs={} m={}", + trait_ref.repr(this.tcx()), + trait_ref.substs.repr(this.tcx()), + m.repr(this.tcx())); + assert_eq!(m.generics.types.get_slice(subst::TypeSpace).len(), + trait_ref.substs.types.get_slice(subst::TypeSpace).len()); + assert_eq!(m.generics.regions.get_slice(subst::TypeSpace).len(), + trait_ref.substs.regions().get_slice(subst::TypeSpace).len()); + assert_eq!(m.generics.types.get_slice(subst::SelfSpace).len(), + trait_ref.substs.types.get_slice(subst::SelfSpace).len()); + assert_eq!(m.generics.regions.get_slice(subst::SelfSpace).len(), + trait_ref.substs.regions().get_slice(subst::SelfSpace).len()); + + // Because this trait derives from a where-clause, it + // should not contain any inference variables or other + // artifacts. This means it is safe to put into the + // `WhereClauseCandidate` and (eventually) into the + // `WhereClausePick`. + assert!(trait_ref.substs.types.iter().all(|&t| !ty::type_needs_infer(t))); + + this.inherent_candidates.push(Candidate { + xform_self_ty: xform_self_ty, + method_ty: m, + kind: WhereClauseCandidate(trait_ref, method_num) + }); + }); + } + + // Do a search through a list of bounds, using a callback to actually + // create the candidates. + fn elaborate_bounds( + &mut self, + bounds: &[Rc], + mk_cand: |this: &mut ProbeContext, + tr: Rc, + m: Rc, + method_num: uint|) + { + let tcx = self.tcx(); + let mut cache = HashSet::new(); + for bound_trait_ref in traits::transitive_bounds(tcx, bounds) { + // Already visited this trait, skip it. + if !cache.insert(bound_trait_ref.def_id) { + continue; + } + + let (pos, method) = match trait_method(tcx, bound_trait_ref.def_id, self.method_name) { + Some(v) => v, + None => { continue; } + }; + + if !self.has_applicable_self(&*method) { + self.record_static_candidate(TraitSource(bound_trait_ref.def_id)); + } else { + mk_cand(self, bound_trait_ref, method, pos); + } + } + } + + fn assemble_extension_candidates_for_traits_in_scope(&mut self, + expr_id: ast::NodeId) + { + let mut duplicates = HashSet::new(); + let opt_applicable_traits = self.fcx.ccx.trait_map.get(&expr_id); + for applicable_traits in opt_applicable_traits.into_iter() { + for &trait_did in applicable_traits.iter() { + if duplicates.insert(trait_did) { + self.assemble_extension_candidates_for_trait(trait_did); + } + } + } + } + + fn assemble_extension_candidates_for_trait(&mut self, + trait_def_id: ast::DefId) { + debug!("assemble_extension_candidates_for_trait: trait_def_id={}", + trait_def_id.repr(self.tcx())); + + // Check whether `trait_def_id` defines a method with suitable name: + let trait_items = + ty::trait_items(self.tcx(), trait_def_id); + let matching_index = + trait_items.iter() + .position(|item| item.name() == self.method_name); + let matching_index = match matching_index { + Some(i) => i, + None => { return; } + }; + let method = match (&*trait_items)[matching_index].as_opt_method() { + Some(m) => m, + None => { return; } + }; + + // Check whether `trait_def_id` defines a method with suitable name: + if !self.has_applicable_self(&*method) { + debug!("method has inapplicable self"); + return self.record_static_candidate(TraitSource(trait_def_id)); + } + + self.assemble_extension_candidates_for_trait_impls(trait_def_id, + method.clone(), + matching_index); + + self.assemble_unboxed_closure_candidates(trait_def_id, + method, + matching_index); + } + + fn assemble_extension_candidates_for_trait_impls(&mut self, + trait_def_id: ast::DefId, + method: Rc, + method_index: uint) + { + ty::populate_implementations_for_trait_if_necessary(self.tcx(), + trait_def_id); + + let trait_impls = self.tcx().trait_impls.borrow(); + let impl_def_ids = match trait_impls.get(&trait_def_id) { + None => { return; } + Some(impls) => impls, + }; + + for &impl_def_id in impl_def_ids.borrow().iter() { + debug!("assemble_extension_candidates_for_trait_impl: trait_def_id={} impl_def_id={}", + trait_def_id.repr(self.tcx()), + impl_def_id.repr(self.tcx())); + + let impl_pty = check::impl_self_ty(self.fcx, self.span, impl_def_id); + let impl_substs = impl_pty.substs; + + debug!("impl_substs={}", impl_substs.repr(self.tcx())); + + let impl_trait_ref = + ty::impl_trait_ref(self.tcx(), impl_def_id) + .unwrap() // we know this is a trait impl + .subst(self.tcx(), &impl_substs); + + debug!("impl_trait_ref={}", impl_trait_ref.repr(self.tcx())); + + // Determine the receiver type that the method itself expects. + let xform_self_ty = + self.xform_self_ty(&method, &impl_trait_ref.substs); + + debug!("xform_self_ty={}", xform_self_ty.repr(self.tcx())); + + self.extension_candidates.push(Candidate { + xform_self_ty: xform_self_ty, + method_ty: method.clone(), + kind: ExtensionImplCandidate(impl_def_id, impl_trait_ref, impl_substs, method_index) + }); + } + } + + fn assemble_unboxed_closure_candidates(&mut self, + trait_def_id: ast::DefId, + method_ty: Rc, + method_index: uint) + { + // Check if this is one of the Fn,FnMut,FnOnce traits. + let tcx = self.tcx(); + let kind = if Some(trait_def_id) == tcx.lang_items.fn_trait() { + ty::FnUnboxedClosureKind + } else if Some(trait_def_id) == tcx.lang_items.fn_mut_trait() { + ty::FnMutUnboxedClosureKind + } else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() { + ty::FnOnceUnboxedClosureKind + } else { + return; + }; + + // Check if there is an unboxed-closure self-type in the list of receivers. + // If so, add "synthetic impls". + let steps = self.steps.clone(); + for step in steps.iter() { + let (closure_def_id, _, _) = match ty::get(step.self_ty).sty { + ty::ty_unboxed_closure(a, b, ref c) => (a, b, c), + _ => continue, + }; + + let unboxed_closures = self.fcx.inh.unboxed_closures.borrow(); + let closure_data = match unboxed_closures.get(&closure_def_id) { + Some(data) => data, + None => { + self.tcx().sess.span_bug( + self.span, + format!("No entry for unboxed closure: {}", + closure_def_id.repr(self.tcx())).as_slice()); + } + }; + + // this closure doesn't implement the right kind of `Fn` trait + if closure_data.kind != kind { + continue; + } + + // create some substitutions for the argument/return type; + // for the purposes of our method lookup, we only take + // receiver type into account, so we can just substitute + // fresh types here to use during substitution and subtyping. + let trait_def = ty::lookup_trait_def(self.tcx(), trait_def_id); + let substs = self.infcx().fresh_substs_for_trait(self.span, + &trait_def.generics, + step.self_ty); + + let xform_self_ty = self.xform_self_ty(&method_ty, &substs); + self.inherent_candidates.push(Candidate { + xform_self_ty: xform_self_ty, + method_ty: method_ty.clone(), + kind: UnboxedClosureCandidate(trait_def_id, method_index) + }); + } + } + + /////////////////////////////////////////////////////////////////////////// + // THE ACTUAL SEARCH + + fn pick(mut self) -> PickResult { + let steps = self.steps.clone(); + + for step in steps.iter() { + match self.pick_step(step) { + Some(r) => { + return r; + } + None => { } + } + } + + Err(NoMatch(self.static_candidates)) + } + + fn pick_step(&mut self, step: &CandidateStep) -> Option { + debug!("pick_step: step={}", step.repr(self.tcx())); + + if ty::type_is_error(step.self_ty) { + return None; + } + + match self.pick_adjusted_method(step) { + Some(result) => return Some(result), + None => {} + } + + match self.pick_autorefd_method(step) { + Some(result) => return Some(result), + None => {} + } + + // FIXME -- Super hack. For DST types, we will convert to + // &&[T] or &&str, as part of a kind of legacy lookup scheme. + match ty::get(step.self_ty).sty { + ty::ty_str | ty::ty_vec(_, None) => self.pick_autorefrefd_method(step), + _ => None + } + } + + fn pick_adjusted_method(&mut self, + step: &CandidateStep) + -> Option + { + self.pick_method(step.self_ty).map(|r| self.adjust(r, step.adjustment.clone())) + } + + fn pick_autorefd_method(&mut self, + step: &CandidateStep) + -> Option + { + let tcx = self.tcx(); + self.search_mutabilities( + |m| AutoRef(m, box step.adjustment.clone()), + |m,r| ty::mk_rptr(tcx, r, ty::mt {ty:step.self_ty, mutbl:m})) + } + + fn pick_autorefrefd_method(&mut self, + step: &CandidateStep) + -> Option + { + let tcx = self.tcx(); + self.search_mutabilities( + |m| AutoRef(m, box AutoRef(m, box step.adjustment.clone())), + |m,r| ty::mk_rptr(tcx, r, ty::mt { ty: ty::mk_rptr(tcx, r, ty::mt { ty:step.self_ty, + mutbl:m}), + mutbl: m })) + } + + fn search_mutabilities(&mut self, + mk_adjustment: |ast::Mutability| -> PickAdjustment, + mk_autoref_ty: |ast::Mutability, ty::Region| -> ty::t) + -> Option + { + let region = self.infcx().next_region_var(infer::Autoref(self.span)); + + // Search through mutabilities in order to find one where pick works: + [ast::MutImmutable, ast::MutMutable] + .iter() + .flat_map(|&m| { + let autoref_ty = mk_autoref_ty(m, region); + self.pick_method(autoref_ty) + .map(|r| self.adjust(r, mk_adjustment(m))) + .into_iter() + }) + .nth(0) + } + + fn adjust(&mut self, result: PickResult, adjustment: PickAdjustment) -> PickResult { + match result { + Err(e) => Err(e), + Ok(mut pick) => { + pick.adjustment = adjustment; + Ok(pick) + } + } + } + + fn pick_method(&mut self, self_ty: ty::t) -> Option { + debug!("pick_method(self_ty={})", self.infcx().ty_to_string(self_ty)); + + debug!("searching inherent candidates"); + match self.consider_candidates(self_ty, self.inherent_candidates[]) { + None => {} + Some(pick) => { + return Some(pick); + } + } + + debug!("searching extension candidates"); + self.consider_candidates(self_ty, self.extension_candidates[]) + } + + fn consider_candidates(&self, self_ty: ty::t, probes: &[Candidate]) -> Option { + let mut applicable_candidates: Vec<_> = + probes.iter() + .filter(|&probe| self.consider_probe(self_ty, probe)) + .collect(); + + debug!("applicable_candidates: {}", applicable_candidates.repr(self.tcx())); + + if applicable_candidates.len() > 1 { + match self.collapse_candidates_to_trait_pick(applicable_candidates[]) { + Some(pick) => { return Some(Ok(pick)); } + None => { } + } + } + + if applicable_candidates.len() > 1 { + let sources = probes.iter().map(|p| p.to_source()).collect(); + return Some(Err(Ambiguity(sources))); + } + + applicable_candidates.pop().map(|probe| { + let pick = probe.to_unadjusted_pick(); + Ok(pick) + }) + } + + fn consider_probe(&self, self_ty: ty::t, probe: &Candidate) -> bool { + debug!("consider_probe: self_ty={} probe={}", + self_ty.repr(self.tcx()), + probe.repr(self.tcx())); + + self.infcx().probe(|| { + match self.make_sub_ty(self_ty, probe.xform_self_ty) { + Ok(()) => { } + Err(_) => { + debug!("--> cannot relate self-types"); + return false; + } + } + + match probe.kind { + InherentImplCandidate(impl_def_id, ref substs) | + ExtensionImplCandidate(impl_def_id, _, ref substs, _) => { + // Check whether the impl imposes obligations we have to worry about. + let obligations = + traits::impl_obligations( + self.tcx(), + traits::ObligationCause::misc(self.span), + impl_def_id, + substs); + + debug!("impl_obligations={}", obligations.repr(self.tcx())); + + let mut selcx = traits::SelectionContext::new(self.infcx(), + &self.fcx.inh.param_env, + self.fcx); + + obligations.all(|o| selcx.evaluate_obligation(o)) + } + + ObjectCandidate(..) | + UnboxedClosureCandidate(..) | + WhereClauseCandidate(..) => { + // These have no additional conditions to check. + true + } + } + }) + } + + fn collapse_candidates_to_trait_pick(&self, probes: &[&Candidate]) -> Option { + /*! + * Sometimes we get in a situation where we have multiple + * probes that are all impls of the same trait, but we don't + * know which impl to use. In this case, since in all cases + * the external interface of the method can be determined from + * the trait, it's ok not to decide. We can basically just + * collapse all of the probes for various impls into one + * where-clause probe. This will result in a pending + * obligation so when more type-info is available we can make + * the final decision. + * + * Example (`src/test/run-pass/method-two-trait-defer-resolution-1.rs`): + * + * ``` + * trait Foo { ... } + * impl Foo for Vec { ... } + * impl Foo for Vec { ... } + * ``` + * + * Now imagine the receiver is `Vec<_>`. It doesn't really + * matter at this time which impl we use, so it's ok to just + * commit to "using the method from the trait Foo". + */ + + // Do all probes correspond to the same trait? + let trait_data = match probes[0].to_trait_data() { + Some(data) => data, + None => return None, + }; + if probes[1..].iter().any(|p| p.to_trait_data() != Some(trait_data)) { + return None; + } + + // If so, just use this trait and call it a day. + let (trait_def_id, method_num) = trait_data; + let method_ty = probes[0].method_ty.clone(); + Some(Pick { + method_ty: method_ty, + adjustment: AutoDeref(0), + kind: TraitPick(trait_def_id, method_num) + }) + } + + /////////////////////////////////////////////////////////////////////////// + // MISCELLANY + + fn make_sub_ty(&self, sub: ty::t, sup: ty::t) -> infer::ures { + self.infcx().sub_types(false, infer::Misc(DUMMY_SP), sub, sup) + } + + fn has_applicable_self(&self, method: &ty::Method) -> bool { + // "fast track" -- check for usage of sugar + match method.explicit_self { + ty::StaticExplicitSelfCategory => { + // fallthrough + } + ty::ByValueExplicitSelfCategory | + ty::ByReferenceExplicitSelfCategory(..) | + ty::ByBoxExplicitSelfCategory => { + return true; + } + } + + // FIXME -- check for types that deref to `Self`, + // like `Rc` and so on. + // + // Note also that the current code will break if this type + // includes any of the type parameters defined on the method + // -- but this could be overcome. + return false; + } + + fn record_static_candidate(&mut self, source: CandidateSource) { + self.static_candidates.push(source); + } + + fn xform_self_ty(&self, method: &Rc, substs: &subst::Substs) -> ty::t { + debug!("xform_self_ty(self_ty={}, substs={})", + method.fty.sig.inputs[0].repr(self.tcx()), + substs.repr(self.tcx())); + + let xform_self_ty = method.fty.sig.inputs[0].subst(self.tcx(), substs); + self.infcx().replace_late_bound_regions_with_fresh_var(method.fty.sig.binder_id, + self.span, + infer::FnCall, + &xform_self_ty).0 + } +} + +fn impl_method(tcx: &ty::ctxt, + impl_def_id: ast::DefId, + method_name: ast::Name) + -> Option> +{ + let impl_items = tcx.impl_items.borrow(); + let impl_items = impl_items.get(&impl_def_id).unwrap(); + impl_items + .iter() + .map(|&did| ty::impl_or_trait_item(tcx, did.def_id())) + .find(|m| m.name() == method_name) + .and_then(|item| item.as_opt_method()) +} + +fn trait_method(tcx: &ty::ctxt, + trait_def_id: ast::DefId, + method_name: ast::Name) + -> Option<(uint, Rc)> +{ + /*! + * Find method with name `method_name` defined in `trait_def_id` and return it, + * along with its index (or `None`, if no such method). + */ + + let trait_items = ty::trait_items(tcx, trait_def_id); + trait_items + .iter() + .enumerate() + .find(|&(_, ref item)| item.name() == method_name) + .and_then(|(idx, item)| item.as_opt_method().map(|m| (idx, m))) +} + +// Determine the index of a method in the list of all methods belonging +// to a trait and its supertraits. +fn get_method_index(tcx: &ty::ctxt, + trait_ref: &ty::TraitRef, + subtrait: Rc, + n_method: uint) -> uint { + // We need to figure the "real index" of the method in a + // listing of all the methods of an object. We do this by + // iterating down the supertraits of the object's trait until + // we find the trait the method came from, counting up the + // methods from them. + let mut method_count = 0; + ty::each_bound_trait_and_supertraits(tcx, &[subtrait], |bound_ref| { + if bound_ref.def_id == trait_ref.def_id { + false + } else { + let trait_items = ty::trait_items(tcx, bound_ref.def_id); + for trait_item in trait_items.iter() { + match *trait_item { + ty::MethodTraitItem(_) => method_count += 1, + ty::TypeTraitItem(_) => {} + } + } + true + } + }); + method_count + n_method +} + +impl Candidate { + fn to_unadjusted_pick(&self) -> Pick { + Pick { + method_ty: self.method_ty.clone(), + adjustment: AutoDeref(0), + kind: match self.kind { + InherentImplCandidate(def_id, _) => { + InherentImplPick(def_id) + } + ObjectCandidate(ref data) => { + ObjectPick(data.trait_ref.def_id, data.method_num, data.real_index) + } + ExtensionImplCandidate(def_id, _, _, index) => { + ExtensionImplPick(def_id, index) + } + UnboxedClosureCandidate(trait_def_id, index) => { + TraitPick(trait_def_id, index) + } + WhereClauseCandidate(ref trait_ref, index) => { + // Only trait derived from where-clauses should + // appear here, so they should not contain any + // inference variables or other artifacts. This + // means they are safe to put into the + // `WhereClausePick`. + assert!(trait_ref.substs.types.iter().all(|&t| !ty::type_needs_infer(t))); + + WhereClausePick((*trait_ref).clone(), index) + } + } + } + } + + fn to_source(&self) -> CandidateSource { + match self.kind { + InherentImplCandidate(def_id, _) => ImplSource(def_id), + ObjectCandidate(ref obj) => TraitSource(obj.trait_ref.def_id), + ExtensionImplCandidate(def_id, _, _, _) => ImplSource(def_id), + UnboxedClosureCandidate(trait_def_id, _) => TraitSource(trait_def_id), + WhereClauseCandidate(ref trait_ref, _) => TraitSource(trait_ref.def_id), + } + } + + fn to_trait_data(&self) -> Option<(ast::DefId,MethodIndex)> { + match self.kind { + InherentImplCandidate(..) | + ObjectCandidate(..) => { + None + } + UnboxedClosureCandidate(trait_def_id, method_num) => { + Some((trait_def_id, method_num)) + } + ExtensionImplCandidate(_, ref trait_ref, _, method_num) | + WhereClauseCandidate(ref trait_ref, method_num) => { + Some((trait_ref.def_id, method_num)) + } + } + } +} + +impl Repr for Candidate { + fn repr(&self, tcx: &ty::ctxt) -> String { + format!("Candidate(xform_self_ty={}, kind={})", + self.xform_self_ty.repr(tcx), + self.kind.repr(tcx)) + } +} + +impl Repr for CandidateKind { + fn repr(&self, tcx: &ty::ctxt) -> String { + match *self { + InherentImplCandidate(ref a, ref b) => + format!("InherentImplCandidate({},{})", a.repr(tcx), b.repr(tcx)), + ObjectCandidate(ref a) => + format!("ObjectCandidate({})", a.repr(tcx)), + ExtensionImplCandidate(ref a, ref b, ref c, ref d) => + format!("ExtensionImplCandidate({},{},{},{})", a.repr(tcx), b.repr(tcx), + c.repr(tcx), d), + UnboxedClosureCandidate(ref a, ref b) => + format!("UnboxedClosureCandidate({},{})", a.repr(tcx), b), + WhereClauseCandidate(ref a, ref b) => + format!("WhereClauseCandidate({},{})", a.repr(tcx), b), + } + } +} + +impl Repr for CandidateStep { + fn repr(&self, tcx: &ty::ctxt) -> String { + format!("CandidateStep({},{})", + self.self_ty.repr(tcx), + self.adjustment) + } +} + +impl Repr for PickAdjustment { + fn repr(&self, _tcx: &ty::ctxt) -> String { + format!("{}", self) + } +} + +impl Repr for PickKind { + fn repr(&self, _tcx: &ty::ctxt) -> String { + format!("{}", self) + } +} + +impl Repr for Pick { + fn repr(&self, tcx: &ty::ctxt) -> String { + format!("Pick(method_ty={}, adjustment={}, kind={})", + self.method_ty.repr(tcx), + self.adjustment, + self.kind) + } +} diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 17c9676a98b..d58b0322433 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -102,8 +102,6 @@ use middle::typeck::astconv::AstConv; use middle::typeck::astconv::{ast_region_to_region, ast_ty_to_ty}; use middle::typeck::astconv; use middle::typeck::check::_match::pat_ctxt; -use middle::typeck::check::method::{AutoderefReceiver}; -use middle::typeck::check::method::{CheckTraitsAndInherentMethods}; use middle::typeck::check::regionmanip::replace_late_bound_regions; use middle::typeck::CrateCtxt; use middle::typeck::infer; @@ -2271,6 +2269,10 @@ fn autoderef_for_index(fcx: &FnCtxt, step: |ty::t, ty::AutoDerefRef| -> Option) -> Option { + // FIXME(#18741) -- this is almost but not quite the same as the + // autoderef that normal method probing does. They could likely be + // consolidated. + let (ty, autoderefs, final_mt) = autoderef(fcx, base_expr.span, base_ty, Some(base_expr.id), lvalue_pref, |adj_ty, idx| { let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None }; @@ -3048,9 +3050,12 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // Replace any bound regions that appear in the function // signature with region variables - let (_, fn_sig) = replace_late_bound_regions(fcx.tcx(), fn_sig.binder_id, fn_sig, |br| { - fcx.infcx().next_region_var(infer::LateBoundRegion(call_expr.span, br)) - }); + let fn_sig = + fcx.infcx().replace_late_bound_regions_with_fresh_var( + fn_sig.binder_id, + call_expr.span, + infer::FnCall, + fn_sig).0; // Call the generic checker. check_argument_types(fcx, @@ -3082,14 +3087,12 @@ fn check_expr_with_unifier(fcx: &FnCtxt, let tps = tps.iter().map(|ast_ty| fcx.to_ty(&**ast_ty)).collect::>(); let fn_ty = match method::lookup(fcx, - expr, - &*rcvr, + method_name.span, method_name.node.name, expr_t, - tps.as_slice(), - DontDerefArgs, - CheckTraitsAndInherentMethods, - AutoderefReceiver) { + tps, + expr.id, + rcvr) { Ok(method) => { let method_ty = method.ty; let method_call = MethodCall::expr(expr.id); @@ -3597,8 +3600,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, expr: &ast::Expr, lvalue_pref: LvaluePreference, base: &ast::Expr, - field: &ast::SpannedIdent, - tys: &[P]) { + field: &ast::SpannedIdent) { let tcx = fcx.ccx.tcx; check_expr_with_lvalue_pref(fcx, base, lvalue_pref); let expr_t = structurally_resolved_type(fcx, expr.span, @@ -3625,42 +3627,29 @@ fn check_expr_with_unifier(fcx: &FnCtxt, None => {} } - let tps: Vec = tys.iter().map(|ty| fcx.to_ty(&**ty)).collect(); - match method::lookup(fcx, - expr, - base, - field.node.name, - expr_t, - tps.as_slice(), - DontDerefArgs, - CheckTraitsAndInherentMethods, - AutoderefReceiver) { - Ok(_) => { - fcx.type_error_message( - field.span, - |actual| { - format!("attempted to take value of method `{}` on type \ - `{}`", token::get_ident(field.node), actual) - }, - expr_t, None); + if method::exists(fcx, field.span, field.node.name, expr_t, expr.id) { + fcx.type_error_message( + field.span, + |actual| { + format!("attempted to take value of method `{}` on type \ + `{}`", token::get_ident(field.node), actual) + }, + expr_t, None); - tcx.sess.span_help(field.span, - "maybe a `()` to call it is missing? \ - If not, try an anonymous function"); - } - - Err(_) => { - fcx.type_error_message( - expr.span, - |actual| { - format!("attempted access of field `{}` on \ - type `{}`, but no field with that \ - name was found", - token::get_ident(field.node), - actual) - }, - expr_t, None); - } + tcx.sess.span_help(field.span, + "maybe a `()` to call it is missing? \ + If not, try an anonymous function"); + } else { + fcx.type_error_message( + expr.span, + |actual| { + format!("attempted access of field `{}` on \ + type `{}`, but no field with that \ + name was found", + token::get_ident(field.node), + actual) + }, + expr_t, None); } fcx.write_error(expr.id); @@ -3671,8 +3660,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, expr: &ast::Expr, lvalue_pref: LvaluePreference, base: &ast::Expr, - idx: codemap::Spanned, - _tys: &[P]) { + idx: codemap::Spanned) { let tcx = fcx.ccx.tcx; check_expr_with_lvalue_pref(fcx, base, lvalue_pref); let expr_t = structurally_resolved_type(fcx, expr.span, @@ -4495,11 +4483,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt, fcx.require_expr_have_sized_type(expr, traits::StructInitializerSized); } - ast::ExprField(ref base, ref field, ref tys) => { - check_field(fcx, expr, lvalue_pref, &**base, field, tys.as_slice()); + ast::ExprField(ref base, ref field, _) => { + check_field(fcx, expr, lvalue_pref, &**base, field); } - ast::ExprTupField(ref base, idx, ref tys) => { - check_tup_field(fcx, expr, lvalue_pref, &**base, idx, tys.as_slice()); + ast::ExprTupField(ref base, idx, _) => { + check_tup_field(fcx, expr, lvalue_pref, &**base, idx); } ast::ExprIndex(ref base, ref idx) => { check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref); diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 77ee389c760..8c55872d962 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -1456,11 +1456,11 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> { infer::AddrOfSlice(_) => " for slice expression".to_string(), infer::Autoref(_) => " for autoref".to_string(), infer::Coercion(_) => " for automatic coercion".to_string(), - infer::LateBoundRegion(_, br) => { + infer::LateBoundRegion(_, br, infer::FnCall) => { format!(" for {}in function call", bound_region_to_string(self.tcx, "lifetime parameter ", true, br)) } - infer::BoundRegionInFnType(_, br) => { + infer::LateBoundRegion(_, br, infer::FnType) => { format!(" for {}in function type", bound_region_to_string(self.tcx, "lifetime parameter ", true, br)) } diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs index 7ed5a5db161..12b81010e98 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/typeck/infer/glb.rs @@ -15,12 +15,13 @@ use middle::ty; use middle::typeck::infer::combine::*; use middle::typeck::infer::lattice::*; use middle::typeck::infer::equate::Equate; +use middle::typeck::infer::FnType; +use middle::typeck::infer::fold_regions_in_sig; use middle::typeck::infer::lub::Lub; +use middle::typeck::infer::region_inference::RegionMark; use middle::typeck::infer::sub::Sub; use middle::typeck::infer::{cres, InferCtxt}; use middle::typeck::infer::{TypeTrace, Subtype}; -use middle::typeck::infer::fold_regions_in_sig; -use middle::typeck::infer::region_inference::RegionMark; use syntax::ast::{Many, Once, MutImmutable, MutMutable}; use syntax::ast::{NormalFn, UnsafeFn, NodeId}; use syntax::ast::{Onceness, FnStyle}; @@ -140,12 +141,12 @@ impl<'f, 'tcx> Combine<'tcx> for Glb<'f, 'tcx> { // Instantiate each bound region with a fresh region variable. let (a_with_fresh, a_map) = - self.fields.infcx.replace_late_bound_regions_with_fresh_regions( - self.trace(), a); + self.fields.infcx.replace_late_bound_regions_with_fresh_var( + a.binder_id, self.trace().span(), FnType, a); let a_vars = var_ids(self, &a_map); let (b_with_fresh, b_map) = - self.fields.infcx.replace_late_bound_regions_with_fresh_regions( - self.trace(), b); + self.fields.infcx.replace_late_bound_regions_with_fresh_var( + b.binder_id, self.trace().span(), FnType, b); let b_vars = var_ids(self, &b_map); // Collect constraints. diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index 59778340564..bd0e88b5812 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/typeck/infer/lub.rs @@ -13,6 +13,7 @@ use middle::ty::RegionVid; use middle::ty; use middle::typeck::infer::combine::*; use middle::typeck::infer::equate::Equate; +use middle::typeck::infer::FnType; use middle::typeck::infer::glb::Glb; use middle::typeck::infer::lattice::*; use middle::typeck::infer::sub::Sub; @@ -126,11 +127,11 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> { // Instantiate each bound region with a fresh region variable. let (a_with_fresh, a_map) = - self.fields.infcx.replace_late_bound_regions_with_fresh_regions( - self.trace(), a); + self.fields.infcx.replace_late_bound_regions_with_fresh_var( + a.binder_id, self.trace().span(), FnType, a); let (b_with_fresh, _) = - self.fields.infcx.replace_late_bound_regions_with_fresh_regions( - self.trace(), b); + self.fields.infcx.replace_late_bound_regions_with_fresh_var( + b.binder_id, self.trace().span(), FnType, b); // Collect constraints. let sig0 = try!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh)); diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 1a64af1270f..ab7c8b7640e 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -41,7 +41,7 @@ use syntax::codemap; use syntax::codemap::Span; use util::common::indent; use util::nodemap::FnvHashMap; -use util::ppaux::{bound_region_to_string, ty_to_string}; +use util::ppaux::{ty_to_string}; use util::ppaux::{trait_ref_to_string, Repr}; use self::coercion::Coerce; @@ -227,6 +227,16 @@ pub enum SubregionOrigin { AutoBorrow(Span), } +/// Times when we replace late-bound regions with variables: +#[deriving(Clone, Show)] +pub enum LateBoundRegionConversionTime { + /// when a fn is called + FnCall, + + /// when two fn types are compared + FnType, +} + /// Reasons to create a region inference variable /// /// See `error_reporting.rs` for more details @@ -256,11 +266,7 @@ pub enum RegionVariableOrigin { // Region variables created for bound regions // in a function or method that is called - LateBoundRegion(Span, ty::BoundRegion), - - // Region variables created for bound regions - // when doing subtyping/lub/glb computations - BoundRegionInFnType(Span, ty::BoundRegion), + LateBoundRegion(Span, ty::BoundRegion, LateBoundRegionConversionTime), UpvarRegion(ty::UpvarId, Span), @@ -959,22 +965,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.report_and_explain_type_error(trace, err); } - pub fn replace_late_bound_regions_with_fresh_regions(&self, - trace: TypeTrace, - fsig: &ty::FnSig) - -> (ty::FnSig, - FnvHashMap) { - let (map, fn_sig) = - replace_late_bound_regions(self.tcx, fsig.binder_id, fsig, |br| { - let rvar = self.next_region_var( - BoundRegionInFnType(trace.origin.span(), br)); - debug!("Bound region {} maps to {}", - bound_region_to_string(self.tcx, "", false, br), - rvar); - rvar - }); - (fn_sig, map) + pub fn replace_late_bound_regions_with_fresh_var(&self, + binder_id: ast::NodeId, + span: Span, + lbrct: LateBoundRegionConversionTime, + value: &T) + -> (T, FnvHashMap) + where T : TypeFoldable + Repr + { + let (map, value) = + replace_late_bound_regions( + self.tcx, + binder_id, + value, + |br| self.next_region_var(LateBoundRegion(span, br, lbrct))); + (value, map) } } @@ -1156,8 +1161,7 @@ impl RegionVariableOrigin { Autoref(a) => a, Coercion(ref a) => a.span(), EarlyBoundRegion(a, _) => a, - LateBoundRegion(a, _) => a, - BoundRegionInFnType(a, _) => a, + LateBoundRegion(a, _, _) => a, BoundRegionInCoherence(_) => codemap::DUMMY_SP, UpvarRegion(_, a) => a } @@ -1182,12 +1186,8 @@ impl Repr for RegionVariableOrigin { EarlyBoundRegion(a, b) => { format!("EarlyBoundRegion({},{})", a.repr(tcx), b.repr(tcx)) } - LateBoundRegion(a, b) => { - format!("LateBoundRegion({},{})", a.repr(tcx), b.repr(tcx)) - } - BoundRegionInFnType(a, b) => { - format!("bound_regionInFnType({},{})", a.repr(tcx), - b.repr(tcx)) + LateBoundRegion(a, b, c) => { + format!("LateBoundRegion({},{},{})", a.repr(tcx), b.repr(tcx), c) } BoundRegionInCoherence(a) => { format!("bound_regionInCoherence({})", a.repr(tcx)) diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index d2f315f2a4b..6f9000f5537 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -16,6 +16,7 @@ use middle::typeck::check::regionmanip::replace_late_bound_regions; use middle::typeck::infer::combine::*; use middle::typeck::infer::{cres, CresCompare}; use middle::typeck::infer::equate::Equate; +use middle::typeck::infer::FnType; use middle::typeck::infer::glb::Glb; use middle::typeck::infer::InferCtxt; use middle::typeck::infer::lub::Lub; @@ -175,8 +176,8 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> { // First, we instantiate each bound region in the subtype with a fresh // region variable. let (a_sig, _) = - self.fields.infcx.replace_late_bound_regions_with_fresh_regions( - self.trace(), a); + self.fields.infcx.replace_late_bound_regions_with_fresh_var( + a.binder_id, self.trace().span(), FnType, a); // Second, we instantiate each bound region in the supertype with a // fresh concrete region. diff --git a/src/libsyntax/ast_map/mod.rs b/src/libsyntax/ast_map/mod.rs index 2a2ad9fd664..b116c84552e 100644 --- a/src/libsyntax/ast_map/mod.rs +++ b/src/libsyntax/ast_map/mod.rs @@ -544,7 +544,7 @@ impl<'ast> Map<'ast> { pub fn def_id_span(&self, def_id: DefId, fallback: Span) -> Span { if def_id.krate == LOCAL_CRATE { - self.span(def_id.node) + self.opt_span(def_id.node).unwrap_or(fallback) } else { fallback } diff --git a/src/test/compile-fail/issue-16683.rs b/src/test/compile-fail/issue-16683.rs index d9dfaac5720..fcbf8a3d351 100644 --- a/src/test/compile-fail/issue-16683.rs +++ b/src/test/compile-fail/issue-16683.rs @@ -11,7 +11,7 @@ trait T<'a> { fn a(&'a self) -> &'a bool; fn b(&self) { - self.a(); //~ ERROR mismatched types: expected `&'a Self`, found `&Self` (lifetime mismatch) + self.a(); //~ ERROR cannot infer } } diff --git a/src/test/compile-fail/issue-17758.rs b/src/test/compile-fail/issue-17758.rs index bb480e5e4dd..a47f5c16a46 100644 --- a/src/test/compile-fail/issue-17758.rs +++ b/src/test/compile-fail/issue-17758.rs @@ -15,7 +15,7 @@ trait Foo<'a> { fn foo(&'a self); fn bar(&self) { self.foo(); - //~^ ERROR mismatched types: expected `&'a Self`, found `&Self` (lifetime mismatch) + //~^ ERROR cannot infer } } diff --git a/src/test/compile-fail/method-ambig-one-trait-coerce.rs b/src/test/compile-fail/method-ambig-one-trait-coerce.rs index e5c3da10df8..5e3206ea516 100644 --- a/src/test/compile-fail/method-ambig-one-trait-coerce.rs +++ b/src/test/compile-fail/method-ambig-one-trait-coerce.rs @@ -27,8 +27,14 @@ impl foo for Box { } fn test1(x: Box) { - // Ambiguous because we could coerce to either impl: - x.foo(); //~ ERROR E0034 + // FIXME(#18737) -- we ought to consider this to be ambiguous, + // since we could coerce to either impl. However, what actually + // happens is that we consider both impls applicable because of + // incorrect subtyping relation. We then decide to call this a + // call to the `foo` trait, leading to the following error + // message. + + x.foo(); //~ ERROR `foo` is not implemented } fn test2(x: Box) { diff --git a/src/test/compile-fail/method-commit-to-trait.rs b/src/test/compile-fail/method-commit-to-trait.rs deleted file mode 100644 index 6e4b5e088c9..00000000000 --- a/src/test/compile-fail/method-commit-to-trait.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Test that we pick `Foo`, and also pick the `impl`, even though in -// this case the vector type `T` is not copyable. This is because -// there is no other reasonable choice. The error you see is thus -// about `T` being non-copyable, not about `Foo` being -// unimplemented. This is better for user too, since it suggests minimal -// diff requird to fix program. - -trait Object { } - -trait Foo { - fn foo(self) -> int; -} - -impl Foo for Vec { - fn foo(self) -> int {1} -} - -fn test1(x: Vec) { - x.foo(); - //~^ ERROR `core::kinds::Copy` is not implemented for the type `T` -} - -fn main() { } diff --git a/src/test/compile-fail/unboxed-closures-type-mismatch.rs b/src/test/compile-fail/unboxed-closures-type-mismatch.rs index 09f13b77386..c60a99ca0df 100644 --- a/src/test/compile-fail/unboxed-closures-type-mismatch.rs +++ b/src/test/compile-fail/unboxed-closures-type-mismatch.rs @@ -14,6 +14,6 @@ use std::ops::FnMut; pub fn main() { let mut f = |&mut: x: int, y: int| -> int { x + y }; - let z = f.call_mut((1u, 2)); //~ ERROR mismatched types + let z = f.call_mut((1u, 2)); //~ ERROR not implemented println!("{}", z); } diff --git a/src/test/run-pass/method-recursive-blanket-impl.rs b/src/test/run-pass/method-recursive-blanket-impl.rs new file mode 100644 index 00000000000..b45faca2de6 --- /dev/null +++ b/src/test/run-pass/method-recursive-blanket-impl.rs @@ -0,0 +1,46 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we don't trigger on the blanket impl for all `&'a T` but +// rather keep autoderefing and trigger on the underlying impl. To +// know not to stop at the blanket, we have to recursively evaluate +// the `T:Foo` bound. + +use std::kinds::Sized; + +// Note: this must be generic for the problem to show up +trait Foo for Sized? { + fn foo(&self); +} + +impl Foo for [u8] { + fn foo(&self) {} +} + +impl<'a, A, T> Foo for &'a T where T: Foo { + fn foo(&self) { + Foo::foo(*self) + } +} + +trait Bar { + fn foo(&self); +} + +struct MyType; + +impl Bar for MyType { + fn foo(&self) {} +} + +fn main() { + let mut m = MyType; + (&mut m).foo() +} diff --git a/src/test/run-pass/method-two-traits-distinguished-via-where-clause.rs b/src/test/run-pass/method-two-traits-distinguished-via-where-clause.rs new file mode 100644 index 00000000000..986236fb6f9 --- /dev/null +++ b/src/test/run-pass/method-two-traits-distinguished-via-where-clause.rs @@ -0,0 +1,34 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we select between traits A and B. To do that, we must +// consider the `Sized` bound. + +trait A { + fn foo(self); +} + +trait B { + fn foo(self); +} + +impl A for *const T { + fn foo(self) {} +} + +impl B for *const [T] { + fn foo(self) {} +} + +fn main() { + let x: [int, ..4] = [1,2,3,4]; + let xptr = x.as_slice() as *const _; + xptr.foo(); +} From e691192042c8505b79e55d7bbb0f3c4ebd2f7992 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 6 Nov 2014 15:51:29 -0500 Subject: [PATCH 2/7] Substitute type/lifetimeInstatiate method type/early-bound lifetime parameters too when creating xform-self-type. Fixes #18208. --- .../middle/typeck/check/method/probe.rs | 26 ++++++++++++++ .../method-early-bound-lifetimes-on-self.rs | 34 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/test/run-pass/method-early-bound-lifetimes-on-self.rs diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc/middle/typeck/check/method/probe.rs index 63f017e8041..c07c6ebfc47 100644 --- a/src/librustc/middle/typeck/check/method/probe.rs +++ b/src/librustc/middle/typeck/check/method/probe.rs @@ -827,6 +827,32 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { method.fty.sig.inputs[0].repr(self.tcx()), substs.repr(self.tcx())); + // It is possible for type parameters or early-bound lifetimes + // to appear in the signature of `self`. The substitutions we + // are given do not include type/lifetime parameters for the + // method yet. So create fresh variables here for those too, + // if there are any. + assert_eq!(substs.types.len(subst::FnSpace), 0); + assert_eq!(substs.regions().len(subst::FnSpace), 0); + let mut substs = substs; + let placeholder; + if + !method.generics.types.is_empty_in(subst::FnSpace) || + !method.generics.regions.is_empty_in(subst::FnSpace) + { + let method_types = + self.infcx().next_ty_vars( + method.generics.types.len(subst::FnSpace)); + + let method_regions = + self.infcx().region_vars_for_defs( + self.span, + method.generics.regions.get_slice(subst::FnSpace)); + + placeholder = (*substs).clone().with_method(method_types, method_regions); + substs = &placeholder; + } + let xform_self_ty = method.fty.sig.inputs[0].subst(self.tcx(), substs); self.infcx().replace_late_bound_regions_with_fresh_var(method.fty.sig.binder_id, self.span, diff --git a/src/test/run-pass/method-early-bound-lifetimes-on-self.rs b/src/test/run-pass/method-early-bound-lifetimes-on-self.rs new file mode 100644 index 00000000000..25ce0d774eb --- /dev/null +++ b/src/test/run-pass/method-early-bound-lifetimes-on-self.rs @@ -0,0 +1,34 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that we successfully handle methods where the `self` type has +// an early-bound lifetime. Issue #18208. + +#![allow(dead_code)] + +struct Cursor<'a>; + +trait CursorNavigator { + fn init_cursor<'a, 'b:'a>(&'a self, cursor: &mut Cursor<'b>) -> bool; +} + +struct SimpleNavigator; + +impl CursorNavigator for SimpleNavigator { + fn init_cursor<'a, 'b: 'a>(&'a self, _cursor: &mut Cursor<'b>) -> bool { + false + } +} + +fn main() { + let mut c = Cursor; + let n = SimpleNavigator; + n.init_cursor(&mut c); +} From 70aecde11d3ed5b3909b3f1076a6ea509436660d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Nov 2014 14:11:22 -0500 Subject: [PATCH 3/7] Correct long line --- src/librustc/middle/typeck/infer/mod.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index ab7c8b7640e..66d2894bb60 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -965,13 +965,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.report_and_explain_type_error(trace, err); } - pub fn replace_late_bound_regions_with_fresh_var(&self, - binder_id: ast::NodeId, - span: Span, - lbrct: LateBoundRegionConversionTime, - value: &T) - -> (T, FnvHashMap) - where T : TypeFoldable + Repr + pub fn replace_late_bound_regions_with_fresh_var( + &self, + binder_id: ast::NodeId, + span: Span, + lbrct: LateBoundRegionConversionTime, + value: &T) + -> (T, FnvHashMap) + where T : TypeFoldable + Repr { let (map, value) = replace_late_bound_regions( From 0ed0a4633b69793ae9ea42fdf617a4df66a746cf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 15 Nov 2014 19:24:42 -0500 Subject: [PATCH 4/7] Correct indentation --- src/librustc/middle/typeck/check/method/probe.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc/middle/typeck/check/method/probe.rs index c07c6ebfc47..d28402b0530 100644 --- a/src/librustc/middle/typeck/check/method/probe.rs +++ b/src/librustc/middle/typeck/check/method/probe.rs @@ -446,8 +446,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { } self.assemble_extension_candidates_for_trait_impls(trait_def_id, - method.clone(), - matching_index); + method.clone(), + matching_index); self.assemble_unboxed_closure_candidates(trait_def_id, method, @@ -455,9 +455,9 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { } fn assemble_extension_candidates_for_trait_impls(&mut self, - trait_def_id: ast::DefId, - method: Rc, - method_index: uint) + trait_def_id: ast::DefId, + method: Rc, + method_index: uint) { ty::populate_implementations_for_trait_if_necessary(self.tcx(), trait_def_id); From d93921b348f37d10850c5f9b077300158a623e35 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 16 Nov 2014 06:33:31 -0500 Subject: [PATCH 5/7] Port a simplified versions of pcwalton's "quick reject" mechanism for quickly throwing out method candidates. Yields a 40%-50% improvement in typechecking time as well as lowering peak memory use from 2.2GB to 1.8GB (due to creating fewer types). Conflicts: src/librustc/driver/config.rs src/librustc/middle/ty.rs src/librustc/middle/typeck/check/method.rs src/librustc/middle/typeck/check/mod.rs src/librustc/middle/typeck/coherence/mod.rs --- src/librustc/lib.rs | 1 + src/librustc/middle/fast_reject.rs | 103 ++++++++++++++++++ .../middle/typeck/check/method/probe.rs | 49 ++++++++- 3 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 src/librustc/middle/fast_reject.rs diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index ce736a344d5..c9a1d47f558 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -87,6 +87,7 @@ pub mod middle { pub mod effect; pub mod entry; pub mod expr_use_visitor; + pub mod fast_reject; pub mod graph; pub mod intrinsicck; pub mod lang_items; diff --git a/src/librustc/middle/fast_reject.rs b/src/librustc/middle/fast_reject.rs new file mode 100644 index 00000000000..915f12cd393 --- /dev/null +++ b/src/librustc/middle/fast_reject.rs @@ -0,0 +1,103 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use middle::ty; +use syntax::ast; + +/** See `simplify_type */ +#[deriving(Clone, PartialEq, Eq, Hash)] +pub enum SimplifiedType { + BoolSimplifiedType, + CharSimplifiedType, + IntSimplifiedType(ast::IntTy), + UintSimplifiedType(ast::UintTy), + FloatSimplifiedType(ast::FloatTy), + EnumSimplifiedType(ast::DefId), + StrSimplifiedType, + VecSimplifiedType, + PtrSimplifiedType, + TupleSimplifiedType(uint), + TraitSimplifiedType(ast::DefId), + StructSimplifiedType(ast::DefId), + UnboxedClosureSimplifiedType(ast::DefId), + FunctionSimplifiedType(uint), + ParameterSimplifiedType, +} + +pub fn simplify_type(tcx: &ty::ctxt, + ty: ty::t, + can_simplify_params: bool) + -> Option +{ + /*! + * Tries to simplify a type by dropping type parameters, deref'ing + * away any reference types, etc. The idea is to get something + * simple that we can use to quickly decide if two types could + * unify during method lookup. + * + * If `can_simplify_params` is false, then we will fail to + * simplify type parameters entirely. This is useful when those + * type parameters would be instantiated with fresh type + * variables, since then we can't say much about whether two types + * would unify. Put another way, `can_simplify_params` should be + * true if type parameters appear free in `ty` and `false` if they + * are to be considered bound. + */ + + match ty::get(ty).sty { + ty::ty_bool => Some(BoolSimplifiedType), + ty::ty_char => Some(CharSimplifiedType), + ty::ty_int(int_type) => Some(IntSimplifiedType(int_type)), + ty::ty_uint(uint_type) => Some(UintSimplifiedType(uint_type)), + ty::ty_float(float_type) => Some(FloatSimplifiedType(float_type)), + ty::ty_enum(def_id, _) => Some(EnumSimplifiedType(def_id)), + ty::ty_str => Some(StrSimplifiedType), + ty::ty_vec(..) => Some(VecSimplifiedType), + ty::ty_ptr(_) => Some(PtrSimplifiedType), + ty::ty_trait(ref trait_info) => { + Some(TraitSimplifiedType(trait_info.principal.def_id)) + } + ty::ty_struct(def_id, _) => { + Some(StructSimplifiedType(def_id)) + } + ty::ty_rptr(_, mt) => { + // since we introduce auto-refs during method lookup, we + // just treat &T and T as equivalent from the point of + // view of possibly unifying + simplify_type(tcx, mt.ty, can_simplify_params) + } + ty::ty_uniq(_) => { + // treat like we would treat `Box` + let def_id = tcx.lang_items.owned_box().unwrap(); + Some(StructSimplifiedType(def_id)) + } + ty::ty_unboxed_closure(def_id, _, _) => { + Some(UnboxedClosureSimplifiedType(def_id)) + } + ty::ty_tup(ref tys) => { + Some(TupleSimplifiedType(tys.len())) + } + ty::ty_closure(ref f) => { + Some(FunctionSimplifiedType(f.sig.inputs.len())) + } + ty::ty_bare_fn(ref f) => { + Some(FunctionSimplifiedType(f.sig.inputs.len())) + } + ty::ty_param(_) => { + if can_simplify_params { + Some(ParameterSimplifiedType) + } else { + None + } + } + ty::ty_open(_) | ty::ty_infer(_) | ty::ty_err => None, + } +} + diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc/middle/typeck/check/method/probe.rs index d28402b0530..bc46b975cdb 100644 --- a/src/librustc/middle/typeck/check/method/probe.rs +++ b/src/librustc/middle/typeck/check/method/probe.rs @@ -16,6 +16,7 @@ use super::MethodIndex; use super::NoMatch; use super::TraitSource; +use middle::fast_reject; use middle::subst; use middle::subst::Subst; use middle::traits; @@ -36,6 +37,7 @@ struct ProbeContext<'a, 'tcx:'a> { span: Span, method_name: ast::Name, steps: Rc>, + opt_simplified_steps: Option>, inherent_candidates: Vec, extension_candidates: Vec, impl_dups: HashSet, @@ -44,7 +46,7 @@ struct ProbeContext<'a, 'tcx:'a> { struct CandidateStep { self_ty: ty::t, - adjustment: PickAdjustment + adjustment: PickAdjustment, } struct Candidate { @@ -123,16 +125,31 @@ pub fn probe(fcx: &FnCtxt, // take place in the `fcx.infcx().probe` below. let steps = create_steps(fcx, span, self_ty); + // Create a list of simplified self types, if we can. + let mut simplified_steps = Vec::new(); + for step in steps.iter() { + match fast_reject::simplify_type(fcx.tcx(), step.self_ty, true) { + None => { break; } + Some(simplified_type) => { simplified_steps.push(simplified_type); } + } + } + let opt_simplified_steps = + if simplified_steps.len() < steps.len() { + None // failed to convert at least one of the steps + } else { + Some(simplified_steps) + }; + debug!("ProbeContext: steps for self_ty={} are {}", self_ty.repr(fcx.tcx()), steps.repr(fcx.tcx())); // this creates one big transaction so that all type variables etc // that we create during the probe process are removed later - let mut steps = Some(steps); // FIXME(#18101) need once closures + let mut dummy = Some((steps, opt_simplified_steps)); // FIXME(#18101) need once closures fcx.infcx().probe(|| { - let steps = steps.take().unwrap(); - let mut probe_cx = ProbeContext::new(fcx, span, method_name, steps); + let (steps, opt_simplified_steps) = dummy.take().unwrap(); + let mut probe_cx = ProbeContext::new(fcx, span, method_name, steps, opt_simplified_steps); probe_cx.assemble_inherent_candidates(); probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id); probe_cx.pick() @@ -177,7 +194,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fn new(fcx: &'a FnCtxt<'a,'tcx>, span: Span, method_name: ast::Name, - steps: Vec) + steps: Vec, + opt_simplified_steps: Option>) -> ProbeContext<'a,'tcx> { ProbeContext { @@ -188,6 +206,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { extension_candidates: Vec::new(), impl_dups: HashSet::new(), steps: Rc::new(steps), + opt_simplified_steps: opt_simplified_steps, static_candidates: Vec::new(), } } @@ -473,6 +492,10 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { trait_def_id.repr(self.tcx()), impl_def_id.repr(self.tcx())); + if !self.impl_can_possibly_match(impl_def_id) { + continue; + } + let impl_pty = check::impl_self_ty(self.fcx, self.span, impl_def_id); let impl_substs = impl_pty.substs; @@ -499,6 +522,22 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { } } + fn impl_can_possibly_match(&self, impl_def_id: ast::DefId) -> bool { + let simplified_steps = match self.opt_simplified_steps { + Some(ref simplified_steps) => simplified_steps, + None => { return true; } + }; + + let impl_type = ty::lookup_item_type(self.tcx(), impl_def_id); + let impl_simplified_type = + match fast_reject::simplify_type(self.tcx(), impl_type.ty, false) { + Some(simplified_type) => simplified_type, + None => { return true; } + }; + + simplified_steps.contains(&impl_simplified_type) + } + fn assemble_unboxed_closure_candidates(&mut self, trait_def_id: ast::DefId, method_ty: Rc, From d7bb01eb52d6b3b40084450d99eb928d74d25d6e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 16 Nov 2014 07:10:37 -0500 Subject: [PATCH 6/7] Use the quick reject mechanism during trait matching as well. Seems to yield an incremental improvement (type-checking rustc drops from ~9s to ~8s). --- src/librustc/middle/traits/select.rs | 36 ++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 7ea58be3480..3dbb7307dd6 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -27,6 +27,7 @@ use super::{VtableBuiltin, VtableImpl, VtableParam, VtableUnboxedClosure}; use super::{VtableImplData, VtableParamData, VtableBuiltinData}; use super::{util}; +use middle::fast_reject; use middle::mem_categorization::Typer; use middle::subst::{Subst, Substs, VecPerParamSpace}; use middle::ty; @@ -1767,12 +1768,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &Obligation) -> Result { + let impl_trait_ref = ty::impl_trait_ref(self.tcx(), + impl_def_id).unwrap(); + + // Before we create the substitutions and everything, first + // consider a "quick reject". This avoids creating more types + // and so forth that we need to. + if self.fast_reject_trait_refs(obligation, &*impl_trait_ref) { + return Err(()); + } + let impl_substs = util::fresh_substs_for_impl(self.infcx, obligation.cause.span, impl_def_id); - let impl_trait_ref = ty::impl_trait_ref(self.tcx(), - impl_def_id).unwrap(); let impl_trait_ref = impl_trait_ref.subst(self.tcx(), &impl_substs); @@ -1782,6 +1791,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + fn fast_reject_trait_refs(&mut self, + obligation: &Obligation, + impl_trait_ref: &ty::TraitRef) + -> bool + { + // We can avoid creating type variables and doing the full + // substitution if we find that any of the input types, when + // simplified, do not match. + + obligation.trait_ref.input_types().iter() + .zip(impl_trait_ref.input_types().iter()) + .any(|(&obligation_ty, &impl_ty)| { + let simplified_obligation_ty = + fast_reject::simplify_type(self.tcx(), obligation_ty, true); + let simplified_impl_ty = + fast_reject::simplify_type(self.tcx(), impl_ty, false); + + simplified_obligation_ty.is_some() && + simplified_impl_ty.is_some() && + simplified_obligation_ty != simplified_impl_ty + }) + } + fn match_trait_refs(&mut self, obligation: &Obligation, trait_ref: Rc) From 99fbd34d7e60faf99afd5065eb55c4fc19af87ec Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Nov 2014 15:25:37 -0500 Subject: [PATCH 7/7] Fix merge conflicts from making enum variants namespaced. --- src/librustc/middle/fast_reject.rs | 2 ++ src/librustc/middle/traits/select.rs | 1 + src/librustc/middle/typeck/check/method/mod.rs | 3 +++ src/librustc/middle/typeck/check/method/probe.rs | 12 ++++++------ src/librustc/middle/typeck/infer/glb.rs | 2 +- src/librustc/middle/typeck/infer/lub.rs | 2 +- src/librustc/middle/typeck/infer/mod.rs | 5 +++-- src/librustc/middle/typeck/infer/sub.rs | 2 +- 8 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/librustc/middle/fast_reject.rs b/src/librustc/middle/fast_reject.rs index 915f12cd393..d2c58b2ceee 100644 --- a/src/librustc/middle/fast_reject.rs +++ b/src/librustc/middle/fast_reject.rs @@ -11,6 +11,8 @@ use middle::ty; use syntax::ast; +use self::SimplifiedType::*; + /** See `simplify_type */ #[deriving(Clone, PartialEq, Eq, Hash)] pub enum SimplifiedType { diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 3dbb7307dd6..46164deca9e 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -33,6 +33,7 @@ use middle::subst::{Subst, Substs, VecPerParamSpace}; use middle::ty; use middle::typeck::check::regionmanip; use middle::typeck::infer; +use middle::typeck::infer::LateBoundRegionConversionTime::*; use middle::typeck::infer::{InferCtxt, TypeSkolemizer}; use middle::ty_fold::TypeFoldable; use std::cell::RefCell; diff --git a/src/librustc/middle/typeck/check/method/mod.rs b/src/librustc/middle/typeck/check/method/mod.rs index 2cd98137057..d4f1d4defa3 100644 --- a/src/librustc/middle/typeck/check/method/mod.rs +++ b/src/librustc/middle/typeck/check/method/mod.rs @@ -30,6 +30,9 @@ use syntax::ast::{DefId}; use syntax::ast; use syntax::codemap::Span; +pub use self::MethodError::*; +pub use self::CandidateSource::*; + mod confirm; mod doc; mod probe; diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc/middle/typeck/check/method/probe.rs index bc46b975cdb..2e5397e768e 100644 --- a/src/librustc/middle/typeck/check/method/probe.rs +++ b/src/librustc/middle/typeck/check/method/probe.rs @@ -8,13 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::Ambiguity; -use super::CandidateSource; -use super::ImplSource; -use super::MethodError; +use super::{MethodError,Ambiguity,NoMatch}; use super::MethodIndex; -use super::NoMatch; -use super::TraitSource; +use super::{CandidateSource,ImplSource,TraitSource}; use middle::fast_reject; use middle::subst; @@ -32,6 +28,10 @@ use std::collections::HashSet; use std::rc::Rc; use util::ppaux::Repr; +use self::CandidateKind::*; +pub use self::PickAdjustment::*; +pub use self::PickKind::*; + struct ProbeContext<'a, 'tcx:'a> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs index 12b81010e98..4ff4857ea06 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/typeck/infer/glb.rs @@ -15,8 +15,8 @@ use middle::ty; use middle::typeck::infer::combine::*; use middle::typeck::infer::lattice::*; use middle::typeck::infer::equate::Equate; -use middle::typeck::infer::FnType; use middle::typeck::infer::fold_regions_in_sig; +use middle::typeck::infer::LateBoundRegionConversionTime::FnType; use middle::typeck::infer::lub::Lub; use middle::typeck::infer::region_inference::RegionMark; use middle::typeck::infer::sub::Sub; diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index bd0e88b5812..aaad3d573be 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/typeck/infer/lub.rs @@ -13,9 +13,9 @@ use middle::ty::RegionVid; use middle::ty; use middle::typeck::infer::combine::*; use middle::typeck::infer::equate::Equate; -use middle::typeck::infer::FnType; use middle::typeck::infer::glb::Glb; use middle::typeck::infer::lattice::*; +use middle::typeck::infer::LateBoundRegionConversionTime::FnType; use middle::typeck::infer::sub::Sub; use middle::typeck::infer::{cres, InferCtxt}; use middle::typeck::infer::fold_regions_in_sig; diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 66d2894bb60..f9dffc04ceb 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -12,10 +12,11 @@ #![allow(non_camel_case_types)] +pub use self::LateBoundRegionConversionTime::*; +pub use self::RegionVariableOrigin::*; +pub use self::SubregionOrigin::*; pub use self::TypeOrigin::*; pub use self::ValuePairs::*; -pub use self::SubregionOrigin::*; -pub use self::RegionVariableOrigin::*; pub use self::fixup_err::*; pub use middle::ty::IntVarValue; pub use self::resolve::resolve_and_force_all_but_regions; diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index 6f9000f5537..2763132024b 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -16,7 +16,7 @@ use middle::typeck::check::regionmanip::replace_late_bound_regions; use middle::typeck::infer::combine::*; use middle::typeck::infer::{cres, CresCompare}; use middle::typeck::infer::equate::Equate; -use middle::typeck::infer::FnType; +use middle::typeck::infer::LateBoundRegionConversionTime::FnType; use middle::typeck::infer::glb::Glb; use middle::typeck::infer::InferCtxt; use middle::typeck::infer::lub::Lub;