auto merge of #18694 : nikomatsakis/rust/issue-18208-method-dispatch-2, r=nrc

This is a pretty major refactoring of the method dispatch infrastructure. It is intended to avoid gross inefficiencies and enable caching and other optimizations (e.g. #17995), though it itself doesn't seem to execute particularly faster yet. It also solves some cases where we were failing to resolve methods that we theoretically should have succeeded with.

Fixes #18674.

cc #18208
This commit is contained in:
bors 2014-11-17 20:37:19 +00:00
commit 336349c932
25 changed files with 2644 additions and 2075 deletions

View file

@ -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;
@ -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;

View file

@ -0,0 +1,105 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use middle::ty;
use syntax::ast;
use self::SimplifiedType::*;
/** 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<SimplifiedType>
{
/*!
* 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,
}
}

View file

@ -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<Obligation>
{
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,

View file

@ -27,11 +27,13 @@ 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;
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;
@ -1714,7 +1716,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 {
@ -1766,12 +1769,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &Obligation)
-> Result<Substs, ()>
{
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);
@ -1781,6 +1792,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<ty::TraitRef>)

View file

@ -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)),
}
}

File diff suppressed because it is too large Load diff

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use 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<ty::t>)
-> 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<ty::t>)
-> 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<A,B,C> Trait<A,B> for Foo<C> { ... }
//
// 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<R>(&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<Object>`
// 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<ty::t>)
-> (Vec<ty::t>, Vec<ty::Region>)
{
// 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<ty::TraitRef>,
target_trait_def_id: ast::DefId)
-> Rc<ty::TraitRef>
{
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<Box<ty::AutoRef>>| -> 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
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# 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<Box<[T, ..3]>>`, this
might yield:
Rc<Box<[T, ..3]>>
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<ToString>`, 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<Box<[T, ..3]>>` and there is a trait `Foo`
that defines it with `&self` for the type `Rc<U>` as well as a method
on the type `Box` that defines `Foo` but with `&mut self`. Then we
might have two candidates:
&Rc<Box<[T, ..3]>> from the impl of `Foo` for `Rc<U>` where `U=Box<T, ..3]>
&mut Box<[T, ..3]>> from the inherent impl on `Box<U>` 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<Box<[T, ..3]>>`,
which does not itself match any candidate. But when we autoref it, we
get the type `&Rc<Box<[T, ..3]>>` 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.
*/

View file

@ -0,0 +1,435 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! 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;
pub use self::MethodError::*;
pub use self::CandidateSource::*;
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<CandidateSource>),
// Multiple methods might apply.
Ambiguity(Vec<CandidateSource>),
}
// 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<ty::t>,
call_expr_id: ast::NodeId,
self_expr: &ast::Expr)
-> Result<MethodCallee, MethodError>
{
/*!
* 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::<T1,...Tn>(...)`:
*
* - `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<Vec<ty::t>>)
-> Option<MethodCallee>
{
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<Vec<ty::t>>)
-> Option<MethodCallee>
{
/*!
* `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<input_tys>`
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<CandidateSource>) {
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<ty::Method>)>
{
/*!
* 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<Rc<ty::Method>>
{
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())
}

File diff suppressed because it is too large Load diff

View file

@ -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<T>(fcx: &FnCtxt,
step: |ty::t, ty::AutoDerefRef| -> Option<T>)
-> Option<T>
{
// 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::<Vec<_>>();
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<ast::Ty>]) {
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<ty::t> = 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<uint>,
_tys: &[P<ast::Ty>]) {
idx: codemap::Spanned<uint>) {
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);

View file

@ -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))
}

View file

@ -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::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;
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.

View file

@ -15,6 +15,7 @@ use middle::typeck::infer::combine::*;
use middle::typeck::infer::equate::Equate;
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;
@ -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));

View file

@ -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;
@ -41,7 +42,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 +228,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 +267,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 +966,22 @@ 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<ty::BoundRegion,
ty::Region>) {
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<T>(
&self,
binder_id: ast::NodeId,
span: Span,
lbrct: LateBoundRegionConversionTime,
value: &T)
-> (T, FnvHashMap<ty::BoundRegion,ty::Region>)
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 +1163,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 +1188,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))

View file

@ -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::LateBoundRegionConversionTime::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.

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -27,8 +27,14 @@ impl foo for Box<Object+Send> {
}
fn test1(x: Box<Object+Send+Sync>) {
// 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<Object+Send>) {

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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<T:Copy> Foo for Vec<T> {
fn foo(self) -> int {1}
}
fn test1<T>(x: Vec<T>) {
x.foo();
//~^ ERROR `core::kinds::Copy` is not implemented for the type `T`
}
fn main() { }

View file

@ -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);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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<A> for Sized? {
fn foo(&self);
}
impl Foo<u8> for [u8] {
fn foo(&self) {}
}
impl<'a, A, T> Foo<A> for &'a T where T: Foo<A> {
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()
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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<T: Sized> A for *const T {
fn foo(self) {}
}
impl<T> 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();
}