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.
This commit is contained in:
parent
f09279395b
commit
f8403aac81
23 changed files with 2397 additions and 2071 deletions
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
603
src/librustc/middle/typeck/check/method/confirm.rs
Normal file
603
src/librustc/middle/typeck/check/method/confirm.rs
Normal 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
|
||||
}
|
126
src/librustc/middle/typeck/check/method/doc.rs
Normal file
126
src/librustc/middle/typeck/check/method/doc.rs
Normal 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.
|
||||
|
||||
*/
|
||||
|
432
src/librustc/middle/typeck/check/method/mod.rs
Normal file
432
src/librustc/middle/typeck/check/method/mod.rs
Normal file
|
@ -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 <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;
|
||||
|
||||
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())
|
||||
}
|
||||
|
1011
src/librustc/middle/typeck/check/method/probe.rs
Normal file
1011
src/librustc/middle/typeck/check/method/probe.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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<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 +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))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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() { }
|
|
@ -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);
|
||||
}
|
||||
|
|
46
src/test/run-pass/method-recursive-blanket-impl.rs
Normal file
46
src/test/run-pass/method-recursive-blanket-impl.rs
Normal 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()
|
||||
}
|
|
@ -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();
|
||||
}
|
Loading…
Add table
Reference in a new issue