Implement new operator dispatch semantics.
Key points are: 1. `a + b` maps directly to `Add<A,B>`, where `A` and `B` are the types of `a` and `b`. 2. Indexing and slicing autoderefs consistently.
This commit is contained in:
parent
33ef78fa8b
commit
0b5bc3314f
15 changed files with 831 additions and 225 deletions
|
@ -5566,3 +5566,18 @@ pub fn with_freevars<T>(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[Freevar]| -> T)
|
||||||
Some(d) => f(d.as_slice())
|
Some(d) => f(d.as_slice())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AutoAdjustment {
|
||||||
|
pub fn is_identity(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
AdjustAddEnv(..) => false,
|
||||||
|
AdjustDerefRef(ref r) => r.is_identity(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AutoDerefRef {
|
||||||
|
pub fn is_identity(&self) -> bool {
|
||||||
|
self.autoderefs == 0 && self.autoref.is_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -96,6 +96,7 @@ use middle::typeck::{MethodOrigin, MethodParam, MethodTypeParam};
|
||||||
use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure, MethodObject, MethodTraitObject};
|
use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure, MethodObject, MethodTraitObject};
|
||||||
use middle::typeck::check::regionmanip::replace_late_bound_regions;
|
use middle::typeck::check::regionmanip::replace_late_bound_regions;
|
||||||
use middle::typeck::TypeAndSubsts;
|
use middle::typeck::TypeAndSubsts;
|
||||||
|
use middle::typeck::check::vtable;
|
||||||
use middle::ty_fold::TypeFoldable;
|
use middle::ty_fold::TypeFoldable;
|
||||||
use util::common::indenter;
|
use util::common::indenter;
|
||||||
use util::ppaux;
|
use util::ppaux;
|
||||||
|
@ -173,46 +174,178 @@ pub fn lookup<'a, 'tcx>(
|
||||||
|
|
||||||
pub fn lookup_in_trait<'a, 'tcx>(
|
pub fn lookup_in_trait<'a, 'tcx>(
|
||||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||||
|
span: Span,
|
||||||
// In a call `a.b::<X, Y, ...>(...)`:
|
self_expr: Option<&'a ast::Expr>,
|
||||||
span: Span, // The expression `a.b(...)`'s span.
|
m_name: ast::Name,
|
||||||
self_expr: Option<&'a ast::Expr>, // The expression `a`, if available.
|
trait_def_id: DefId,
|
||||||
m_name: ast::Name, // The name `b`.
|
self_ty: ty::t,
|
||||||
trait_did: DefId, // The trait to limit the lookup to.
|
opt_input_types: Option<Vec<ty::t>>)
|
||||||
self_ty: ty::t, // The type of `a`.
|
|
||||||
supplied_tps: &'a [ty::t]) // The list of types X, Y, ... .
|
|
||||||
-> Option<MethodCallee>
|
-> Option<MethodCallee>
|
||||||
{
|
{
|
||||||
let mut lcx = LookupContext {
|
lookup_in_trait_adjusted(fcx, span, self_expr, m_name, trait_def_id,
|
||||||
fcx: fcx,
|
ty::AutoDerefRef { autoderefs: 0, autoref: None },
|
||||||
span: span,
|
self_ty, opt_input_types)
|
||||||
self_expr: self_expr,
|
}
|
||||||
m_name: m_name,
|
|
||||||
supplied_tps: supplied_tps,
|
|
||||||
impl_dups: HashSet::new(),
|
|
||||||
inherent_candidates: Vec::new(),
|
|
||||||
extension_candidates: Vec::new(),
|
|
||||||
static_candidates: Vec::new(),
|
|
||||||
deref_args: check::DoDerefArgs,
|
|
||||||
check_traits: CheckTraitsOnly,
|
|
||||||
autoderef_receiver: DontAutoderefReceiver,
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("method lookup_in_trait(self_ty={}, self_expr={}, m_name={}, trait_did={})",
|
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>
|
||||||
|
{
|
||||||
|
debug!("method lookup_in_trait(self_ty={}, self_expr={}, m_name={}, trait_def_id={})",
|
||||||
self_ty.repr(fcx.tcx()),
|
self_ty.repr(fcx.tcx()),
|
||||||
self_expr.repr(fcx.tcx()),
|
self_expr.repr(fcx.tcx()),
|
||||||
m_name.repr(fcx.tcx()),
|
m_name.repr(fcx.tcx()),
|
||||||
trait_did.repr(fcx.tcx()));
|
trait_def_id.repr(fcx.tcx()));
|
||||||
|
|
||||||
lcx.push_bound_candidates(self_ty, Some(trait_did));
|
let trait_def = ty::lookup_trait_def(fcx.tcx(), trait_def_id);
|
||||||
lcx.push_extension_candidate(trait_did);
|
|
||||||
|
|
||||||
// when doing a trait search, ambiguity can't really happen except
|
let expected_number_of_input_types = trait_def.generics.types.len(subst::TypeSpace);
|
||||||
// as part of the trait-lookup in general
|
let input_types = match opt_input_types {
|
||||||
match lcx.search(self_ty) {
|
Some(input_types) => {
|
||||||
Ok(callee) => Some(callee),
|
assert_eq!(expected_number_of_input_types, input_types.len());
|
||||||
Err(_) => None
|
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_intracrate(&obligation) {
|
||||||
|
debug!("--> Cannot match obligation");
|
||||||
|
return None; // Cannot be matched, no such method resolution is possible.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trait must have a method named `m_name` and it should not have
|
||||||
|
// type parameters or early-bound regions.
|
||||||
|
let tcx = fcx.tcx();
|
||||||
|
let (method_num, method_ty) = trait_method(tcx, trait_def_id, m_name).unwrap();
|
||||||
|
assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0);
|
||||||
|
assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0);
|
||||||
|
|
||||||
|
// Substitute the trait parameters into the method type and
|
||||||
|
// instantiate late-bound regions to get the actual method type.
|
||||||
|
let ref bare_fn_ty = method_ty.fty;
|
||||||
|
let fn_sig = bare_fn_ty.sig.subst(tcx, &trait_ref.substs);
|
||||||
|
let fn_sig = replace_late_bound_regions_with_fresh_var(fcx.infcx(), span,
|
||||||
|
fn_sig.binder_id, &fn_sig);
|
||||||
|
let transformed_self_ty = fn_sig.inputs[0];
|
||||||
|
let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {
|
||||||
|
sig: fn_sig,
|
||||||
|
fn_style: bare_fn_ty.fn_style,
|
||||||
|
abi: bare_fn_ty.abi.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
debug!("matched method fty={} obligation={}",
|
||||||
|
fty.repr(fcx.tcx()),
|
||||||
|
obligation.repr(fcx.tcx()));
|
||||||
|
|
||||||
|
// Register obligations for the parameters. This will include the
|
||||||
|
// `Self` parameter, which in turn has a bound of the main trait,
|
||||||
|
// so this also effectively registers `obligation` as well. (We
|
||||||
|
// used to register `obligation` explicitly, but that resulted in
|
||||||
|
// double error messages being reported.)
|
||||||
|
fcx.add_obligations_for_parameters(
|
||||||
|
traits::ObligationCause::misc(span),
|
||||||
|
&trait_ref.substs,
|
||||||
|
&method_ty.generics);
|
||||||
|
|
||||||
|
// FIXME(#18653) -- Try to resolve obligations, giving us more
|
||||||
|
// typing information, which can sometimes be needed to avoid
|
||||||
|
// pathological region inference failures.
|
||||||
|
vtable::select_new_fcx_obligations(fcx);
|
||||||
|
|
||||||
|
// Insert any adjustments needed (always an autoref of some mutability).
|
||||||
|
match self_expr {
|
||||||
|
None => { }
|
||||||
|
|
||||||
|
Some(self_expr) => {
|
||||||
|
debug!("inserting adjustment if needed (self-id = {}, \
|
||||||
|
base adjustment = {}, explicit self = {})",
|
||||||
|
self_expr.id, autoderefref, method_ty.explicit_self);
|
||||||
|
|
||||||
|
match method_ty.explicit_self {
|
||||||
|
ty::ByValueExplicitSelfCategory => {
|
||||||
|
// Trait method is fn(self), no transformation needed.
|
||||||
|
if !autoderefref.is_identity() {
|
||||||
|
fcx.write_adjustment(
|
||||||
|
self_expr.id,
|
||||||
|
span,
|
||||||
|
ty::AdjustDerefRef(autoderefref));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::ByReferenceExplicitSelfCategory(..) => {
|
||||||
|
// Trait method is fn(&self) or fn(&mut self), need an
|
||||||
|
// autoref. Pull the region etc out of the type of first argument.
|
||||||
|
match ty::get(transformed_self_ty).sty {
|
||||||
|
ty::ty_rptr(region, ty::mt { mutbl, ty: _ }) => {
|
||||||
|
let ty::AutoDerefRef { autoderefs, autoref } = autoderefref;
|
||||||
|
let autoref = autoref.map(|r| box r);
|
||||||
|
fcx.write_adjustment(
|
||||||
|
self_expr.id,
|
||||||
|
span,
|
||||||
|
ty::AdjustDerefRef(ty::AutoDerefRef {
|
||||||
|
autoderefs: autoderefs,
|
||||||
|
autoref: Some(ty::AutoPtr(region, mutbl, autoref))
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
fcx.tcx().sess.span_bug(
|
||||||
|
span,
|
||||||
|
format!(
|
||||||
|
"trait method is &self but first arg is: {}",
|
||||||
|
transformed_self_ty.repr(fcx.tcx())).as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
fcx.tcx().sess.span_bug(
|
||||||
|
span,
|
||||||
|
format!(
|
||||||
|
"unexpected explicit self type in operator method: {}",
|
||||||
|
method_ty.explicit_self).as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let callee = MethodCallee {
|
||||||
|
origin: MethodTypeParam(MethodParam{trait_ref: trait_ref.clone(),
|
||||||
|
method_num: method_num}),
|
||||||
|
ty: fty,
|
||||||
|
substs: trait_ref.substs.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("callee = {}", callee.repr(fcx.tcx()));
|
||||||
|
|
||||||
|
Some(callee)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_error(fcx: &FnCtxt,
|
pub fn report_error(fcx: &FnCtxt,
|
||||||
|
@ -1446,9 +1579,8 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixup_derefs_on_method_receiver_if_necessary(
|
fn fixup_derefs_on_method_receiver_if_necessary(&self,
|
||||||
&self,
|
method_callee: &MethodCallee) {
|
||||||
method_callee: &MethodCallee) {
|
|
||||||
let sig = match ty::get(method_callee.ty).sty {
|
let sig = match ty::get(method_callee.ty).sty {
|
||||||
ty::ty_bare_fn(ref f) => f.sig.clone(),
|
ty::ty_bare_fn(ref f) => f.sig.clone(),
|
||||||
ty::ty_closure(ref f) => f.sig.clone(),
|
ty::ty_closure(ref f) => f.sig.clone(),
|
||||||
|
@ -1485,6 +1617,9 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("fixup_derefs_on_method_receiver_if_necessary: exprs={}",
|
||||||
|
exprs.repr(self.tcx()));
|
||||||
|
|
||||||
// Fix up autoderefs and derefs.
|
// Fix up autoderefs and derefs.
|
||||||
for (i, expr) in exprs.iter().rev().enumerate() {
|
for (i, expr) in exprs.iter().rev().enumerate() {
|
||||||
// Count autoderefs.
|
// Count autoderefs.
|
||||||
|
@ -1500,6 +1635,9 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
|
||||||
Some(_) | None => 0,
|
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 {
|
if autoderef_count > 0 {
|
||||||
check::autoderef(self.fcx,
|
check::autoderef(self.fcx,
|
||||||
expr.span,
|
expr.span,
|
||||||
|
@ -1518,24 +1656,59 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
|
||||||
// Don't retry the first one or we might infinite loop!
|
// Don't retry the first one or we might infinite loop!
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
match expr.node {
|
match expr.node {
|
||||||
ast::ExprIndex(ref base_expr, ref index_expr) => {
|
ast::ExprIndex(ref base_expr, _) => {
|
||||||
check::try_overloaded_index(
|
let mut base_adjustment =
|
||||||
self.fcx,
|
match self.fcx.inh.adjustments.borrow().find(&base_expr.id) {
|
||||||
Some(MethodCall::expr(expr.id)),
|
Some(&ty::AdjustDerefRef(ref adr)) => (*adr).clone(),
|
||||||
*expr,
|
None => ty::AutoDerefRef { autoderefs: 0, autoref: None },
|
||||||
|
Some(_) => {
|
||||||
|
self.tcx().sess.span_bug(
|
||||||
|
base_expr.span,
|
||||||
|
"unexpected adjustment type");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If this is an overloaded index, the
|
||||||
|
// adjustment will include an extra layer of
|
||||||
|
// autoref because the method is an &self/&mut
|
||||||
|
// self method. We have to peel it off to get
|
||||||
|
// the raw adjustment that `try_index_step`
|
||||||
|
// expects. This is annoying and horrible. We
|
||||||
|
// ought to recode this routine so it doesn't
|
||||||
|
// (ab)use the normal type checking paths.
|
||||||
|
base_adjustment.autoref = match base_adjustment.autoref {
|
||||||
|
None => { None }
|
||||||
|
Some(AutoPtr(_, _, None)) => { None }
|
||||||
|
Some(AutoPtr(_, _, Some(box r))) => { Some(r) }
|
||||||
|
Some(_) => {
|
||||||
|
self.tcx().sess.span_bug(
|
||||||
|
base_expr.span,
|
||||||
|
"unexpected adjustment autoref");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let adjusted_base_ty =
|
||||||
|
self.fcx.adjust_expr_ty(
|
||||||
&**base_expr,
|
&**base_expr,
|
||||||
self.fcx.expr_ty(&**base_expr),
|
Some(&ty::AdjustDerefRef(base_adjustment.clone())));
|
||||||
index_expr,
|
|
||||||
PreferMutLvalue);
|
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) => {
|
ast::ExprUnary(ast::UnDeref, ref base_expr) => {
|
||||||
check::try_overloaded_deref(
|
check::try_overloaded_deref(
|
||||||
self.fcx,
|
self.fcx,
|
||||||
expr.span,
|
expr.span,
|
||||||
Some(MethodCall::expr(expr.id)),
|
Some(MethodCall::expr(expr.id)),
|
||||||
Some(&**base_expr),
|
Some(&**base_expr),
|
||||||
self.fcx.expr_ty(&**base_expr),
|
self.fcx.expr_ty(&**base_expr),
|
||||||
PreferMutLvalue);
|
PreferMutLvalue);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -1623,15 +1796,25 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
|
||||||
fn replace_late_bound_regions_with_fresh_var<T>(&self, binder_id: ast::NodeId, value: &T) -> T
|
fn replace_late_bound_regions_with_fresh_var<T>(&self, binder_id: ast::NodeId, value: &T) -> T
|
||||||
where T : TypeFoldable + Repr
|
where T : TypeFoldable + Repr
|
||||||
{
|
{
|
||||||
let (_, value) = replace_late_bound_regions(
|
replace_late_bound_regions_with_fresh_var(self.fcx.infcx(), self.span, binder_id, value)
|
||||||
self.fcx.tcx(),
|
|
||||||
binder_id,
|
|
||||||
value,
|
|
||||||
|br| self.fcx.infcx().next_region_var(infer::LateBoundRegion(self.span, br)));
|
|
||||||
value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn replace_late_bound_regions_with_fresh_var<T>(infcx: &infer::InferCtxt,
|
||||||
|
span: Span,
|
||||||
|
binder_id: ast::NodeId,
|
||||||
|
value: &T)
|
||||||
|
-> T
|
||||||
|
where T : TypeFoldable + Repr
|
||||||
|
{
|
||||||
|
let (_, value) = replace_late_bound_regions(
|
||||||
|
infcx.tcx,
|
||||||
|
binder_id,
|
||||||
|
value,
|
||||||
|
|br| infcx.next_region_var(infer::LateBoundRegion(span, br)));
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
fn trait_method(tcx: &ty::ctxt,
|
fn trait_method(tcx: &ty::ctxt,
|
||||||
trait_def_id: ast::DefId,
|
trait_def_id: ast::DefId,
|
||||||
method_name: ast::Name)
|
method_name: ast::Name)
|
||||||
|
|
|
@ -1630,6 +1630,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
adj: ty::AutoAdjustment) {
|
adj: ty::AutoAdjustment) {
|
||||||
debug!("write_adjustment(node_id={}, adj={})", node_id, adj);
|
debug!("write_adjustment(node_id={}, adj={})", node_id, adj);
|
||||||
|
|
||||||
|
if adj.is_identity() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Careful: adjustments can imply trait obligations if we are
|
// Careful: adjustments can imply trait obligations if we are
|
||||||
// casting from a concrete type to an object type. I think
|
// casting from a concrete type to an object type. I think
|
||||||
// it'd probably be nicer to move the logic that creates the
|
// it'd probably be nicer to move the logic that creates the
|
||||||
|
@ -1813,6 +1817,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expr_ty_adjusted(&self, expr: &ast::Expr) -> ty::t {
|
||||||
|
/*!
|
||||||
|
* Fetch type of `expr` after applying adjustments that
|
||||||
|
* have been recorded in the fcx.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let adjustments = self.inh.adjustments.borrow();
|
||||||
|
let adjustment = adjustments.find(&expr.id);
|
||||||
|
self.adjust_expr_ty(expr, adjustment)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn adjust_expr_ty(&self,
|
||||||
|
expr: &ast::Expr,
|
||||||
|
adjustment: Option<&ty::AutoAdjustment>)
|
||||||
|
-> ty::t
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* Apply `adjustment` to the type of `expr`
|
||||||
|
*/
|
||||||
|
|
||||||
|
let raw_ty = self.expr_ty(expr);
|
||||||
|
let raw_ty = self.infcx().shallow_resolve(raw_ty);
|
||||||
|
ty::adjust_ty(self.tcx(),
|
||||||
|
expr.span,
|
||||||
|
expr.id,
|
||||||
|
raw_ty,
|
||||||
|
adjustment,
|
||||||
|
|method_call| self.inh.method_map.borrow()
|
||||||
|
.find(&method_call)
|
||||||
|
.map(|method| method.ty))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn node_ty(&self, id: ast::NodeId) -> ty::t {
|
pub fn node_ty(&self, id: ast::NodeId) -> ty::t {
|
||||||
match self.inh.node_types.borrow().find(&id) {
|
match self.inh.node_types.borrow().find(&id) {
|
||||||
Some(&t) => t,
|
Some(&t) => t,
|
||||||
|
@ -2062,6 +2098,10 @@ pub fn autoderef<T>(fcx: &FnCtxt, sp: Span, base_ty: ty::t,
|
||||||
for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) {
|
for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) {
|
||||||
let resolved_t = structurally_resolved_type(fcx, sp, t);
|
let resolved_t = structurally_resolved_type(fcx, sp, t);
|
||||||
|
|
||||||
|
if ty::type_is_error(resolved_t) {
|
||||||
|
return (resolved_t, autoderefs, None);
|
||||||
|
}
|
||||||
|
|
||||||
match should_stop(resolved_t, autoderefs) {
|
match should_stop(resolved_t, autoderefs) {
|
||||||
Some(x) => return (resolved_t, autoderefs, Some(x)),
|
Some(x) => return (resolved_t, autoderefs, Some(x)),
|
||||||
None => {}
|
None => {}
|
||||||
|
@ -2117,17 +2157,17 @@ fn try_overloaded_call<'a>(fcx: &FnCtxt,
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(function_trait) => function_trait,
|
Some(function_trait) => function_trait,
|
||||||
};
|
};
|
||||||
let method_callee = match method::lookup_in_trait(
|
let method_callee =
|
||||||
fcx,
|
match method::lookup_in_trait(fcx,
|
||||||
call_expression.span,
|
call_expression.span,
|
||||||
Some(&*callee),
|
Some(&*callee),
|
||||||
method_name,
|
method_name,
|
||||||
function_trait,
|
function_trait,
|
||||||
callee_type,
|
callee_type,
|
||||||
[]) {
|
None) {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(method_callee) => method_callee,
|
Some(method_callee) => method_callee,
|
||||||
};
|
};
|
||||||
let method_call = MethodCall::expr(call_expression.id);
|
let method_call = MethodCall::expr(call_expression.id);
|
||||||
let output_type = check_method_argument_types(fcx,
|
let output_type = check_method_argument_types(fcx,
|
||||||
call_expression.span,
|
call_expression.span,
|
||||||
|
@ -2159,13 +2199,14 @@ fn try_overloaded_deref(fcx: &FnCtxt,
|
||||||
base_expr: Option<&ast::Expr>,
|
base_expr: Option<&ast::Expr>,
|
||||||
base_ty: ty::t,
|
base_ty: ty::t,
|
||||||
lvalue_pref: LvaluePreference)
|
lvalue_pref: LvaluePreference)
|
||||||
-> Option<ty::mt> {
|
-> Option<ty::mt>
|
||||||
|
{
|
||||||
// Try DerefMut first, if preferred.
|
// Try DerefMut first, if preferred.
|
||||||
let method = match (lvalue_pref, fcx.tcx().lang_items.deref_mut_trait()) {
|
let method = match (lvalue_pref, fcx.tcx().lang_items.deref_mut_trait()) {
|
||||||
(PreferMutLvalue, Some(trait_did)) => {
|
(PreferMutLvalue, Some(trait_did)) => {
|
||||||
method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x),
|
method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x),
|
||||||
token::intern("deref_mut"), trait_did,
|
token::intern("deref_mut"), trait_did,
|
||||||
base_ty, [])
|
base_ty, None)
|
||||||
}
|
}
|
||||||
_ => None
|
_ => None
|
||||||
};
|
};
|
||||||
|
@ -2175,25 +2216,27 @@ fn try_overloaded_deref(fcx: &FnCtxt,
|
||||||
(None, Some(trait_did)) => {
|
(None, Some(trait_did)) => {
|
||||||
method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x),
|
method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x),
|
||||||
token::intern("deref"), trait_did,
|
token::intern("deref"), trait_did,
|
||||||
base_ty, [])
|
base_ty, None)
|
||||||
}
|
}
|
||||||
(method, _) => method
|
(method, _) => method
|
||||||
};
|
};
|
||||||
|
|
||||||
make_return_type(fcx, method_call, method)
|
make_overloaded_lvalue_return_type(fcx, method_call, method)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_method_ty(method: &Option<MethodCallee>) -> ty::t {
|
fn make_overloaded_lvalue_return_type(fcx: &FnCtxt,
|
||||||
match method {
|
method_call: Option<MethodCall>,
|
||||||
&Some(ref method) => method.ty,
|
method: Option<MethodCallee>)
|
||||||
&None => ty::mk_err()
|
-> Option<ty::mt>
|
||||||
}
|
{
|
||||||
}
|
/*!
|
||||||
|
* For the overloaded lvalue expressions (`*x`, `x[3]`), the trait
|
||||||
|
* returns a type of `&T`, but the actual type we assign to the
|
||||||
|
* *expression* is `T`. So this function just peels off the return
|
||||||
|
* type by one layer to yield `T`. It also inserts the
|
||||||
|
* `method-callee` into the method map.
|
||||||
|
*/
|
||||||
|
|
||||||
fn make_return_type(fcx: &FnCtxt,
|
|
||||||
method_call: Option<MethodCall>,
|
|
||||||
method: Option<MethodCallee>)
|
|
||||||
-> Option<ty::mt> {
|
|
||||||
match method {
|
match method {
|
||||||
Some(method) => {
|
Some(method) => {
|
||||||
let ref_ty = ty::ty_fn_ret(method.ty);
|
let ref_ty = ty::ty_fn_ret(method.ty);
|
||||||
|
@ -2205,26 +2248,126 @@ fn make_return_type(fcx: &FnCtxt,
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
match ref_ty {
|
match ref_ty {
|
||||||
ty::FnConverging(ref_ty) =>
|
ty::FnConverging(ref_ty) => {
|
||||||
ty::deref(ref_ty, true),
|
ty::deref(ref_ty, true)
|
||||||
ty::FnDiverging =>
|
}
|
||||||
None
|
ty::FnDiverging => {
|
||||||
|
fcx.tcx().sess.bug("index/deref traits do not define a `!` return")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn autoderef_for_index<T>(fcx: &FnCtxt,
|
||||||
|
base_expr: &ast::Expr,
|
||||||
|
base_ty: ty::t,
|
||||||
|
lvalue_pref: LvaluePreference,
|
||||||
|
step: |ty::t, ty::AutoDerefRef| -> Option<T>)
|
||||||
|
-> Option<T>
|
||||||
|
{
|
||||||
|
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 };
|
||||||
|
step(adj_ty, autoderefref)
|
||||||
|
});
|
||||||
|
|
||||||
|
if final_mt.is_some() {
|
||||||
|
return final_mt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After we have fully autoderef'd, if the resulting type is [T, ..n], then
|
||||||
|
// do a final unsized coercion to yield [T].
|
||||||
|
match ty::get(ty).sty {
|
||||||
|
ty::ty_vec(element_ty, Some(n)) => {
|
||||||
|
let adjusted_ty = ty::mk_vec(fcx.tcx(), element_ty, None);
|
||||||
|
let autoderefref = ty::AutoDerefRef {
|
||||||
|
autoderefs: autoderefs,
|
||||||
|
autoref: Some(ty::AutoUnsize(ty::UnsizeLength(n)))
|
||||||
|
};
|
||||||
|
step(adjusted_ty, autoderefref)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn try_overloaded_slice(fcx: &FnCtxt,
|
fn try_overloaded_slice(fcx: &FnCtxt,
|
||||||
method_call: Option<MethodCall>,
|
method_call: MethodCall,
|
||||||
expr: &ast::Expr,
|
expr: &ast::Expr,
|
||||||
base_expr: &ast::Expr,
|
base_expr: &ast::Expr,
|
||||||
base_ty: ty::t,
|
base_ty: ty::t,
|
||||||
start_expr: &Option<P<ast::Expr>>,
|
start_expr: &Option<P<ast::Expr>>,
|
||||||
end_expr: &Option<P<ast::Expr>>,
|
end_expr: &Option<P<ast::Expr>>,
|
||||||
mutbl: &ast::Mutability)
|
mutbl: ast::Mutability)
|
||||||
-> Option<ty::mt> {
|
-> Option<ty::t> // return type is result of slice
|
||||||
let method = if mutbl == &ast::MutMutable {
|
{
|
||||||
|
/*!
|
||||||
|
* Autoderefs `base_expr`, looking for a `Slice` impl. If it
|
||||||
|
* finds one, installs the relevant method info and returns the
|
||||||
|
* result type (else None).
|
||||||
|
*/
|
||||||
|
|
||||||
|
let lvalue_pref = match mutbl {
|
||||||
|
ast::MutMutable => PreferMutLvalue,
|
||||||
|
ast::MutImmutable => NoPreference
|
||||||
|
};
|
||||||
|
|
||||||
|
let opt_method_ty =
|
||||||
|
autoderef_for_index(fcx, base_expr, base_ty, lvalue_pref, |adjusted_ty, autoderefref| {
|
||||||
|
try_overloaded_slice_step(fcx, method_call, expr, base_expr,
|
||||||
|
adjusted_ty, autoderefref, mutbl,
|
||||||
|
start_expr, end_expr)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Regardless of whether the lookup succeeds, check the method arguments
|
||||||
|
// so that we have *some* type for each argument.
|
||||||
|
let method_ty_or_err = opt_method_ty.unwrap_or(ty::mk_err());
|
||||||
|
|
||||||
|
let mut args = vec![];
|
||||||
|
start_expr.as_ref().map(|x| args.push(x));
|
||||||
|
end_expr.as_ref().map(|x| args.push(x));
|
||||||
|
|
||||||
|
check_method_argument_types(fcx,
|
||||||
|
expr.span,
|
||||||
|
method_ty_or_err,
|
||||||
|
expr,
|
||||||
|
args.as_slice(),
|
||||||
|
DoDerefArgs,
|
||||||
|
DontTupleArguments);
|
||||||
|
|
||||||
|
opt_method_ty.map(|method_ty| {
|
||||||
|
let result_ty = ty::ty_fn_ret(method_ty);
|
||||||
|
match result_ty {
|
||||||
|
ty::FnConverging(result_ty) => result_ty,
|
||||||
|
ty::FnDiverging => {
|
||||||
|
fcx.tcx().sess.span_bug(expr.span,
|
||||||
|
"slice trait does not define a `!` return")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_overloaded_slice_step(fcx: &FnCtxt,
|
||||||
|
method_call: MethodCall,
|
||||||
|
expr: &ast::Expr,
|
||||||
|
base_expr: &ast::Expr,
|
||||||
|
base_ty: ty::t, // autoderef'd type
|
||||||
|
autoderefref: ty::AutoDerefRef,
|
||||||
|
mutbl: ast::Mutability,
|
||||||
|
start_expr: &Option<P<ast::Expr>>,
|
||||||
|
end_expr: &Option<P<ast::Expr>>)
|
||||||
|
-> Option<ty::t> // result type is type of method being called
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* Checks for a `Slice` (or `SliceMut`) impl at the relevant level
|
||||||
|
* of autoderef. If it finds one, installs method info and returns
|
||||||
|
* type of method (else None).
|
||||||
|
*/
|
||||||
|
|
||||||
|
let method = if mutbl == ast::MutMutable {
|
||||||
// Try `SliceMut` first, if preferred.
|
// Try `SliceMut` first, if preferred.
|
||||||
match fcx.tcx().lang_items.slice_mut_trait() {
|
match fcx.tcx().lang_items.slice_mut_trait() {
|
||||||
Some(trait_did) => {
|
Some(trait_did) => {
|
||||||
|
@ -2235,13 +2378,14 @@ fn try_overloaded_slice(fcx: &FnCtxt,
|
||||||
(&None, &None) => "as_mut_slice_",
|
(&None, &None) => "as_mut_slice_",
|
||||||
};
|
};
|
||||||
|
|
||||||
method::lookup_in_trait(fcx,
|
method::lookup_in_trait_adjusted(fcx,
|
||||||
expr.span,
|
expr.span,
|
||||||
Some(&*base_expr),
|
Some(&*base_expr),
|
||||||
token::intern(method_name),
|
token::intern(method_name),
|
||||||
trait_did,
|
trait_did,
|
||||||
base_ty,
|
autoderefref,
|
||||||
[])
|
base_ty,
|
||||||
|
None)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -2258,74 +2402,73 @@ fn try_overloaded_slice(fcx: &FnCtxt,
|
||||||
(&None, &None) => "as_slice_",
|
(&None, &None) => "as_slice_",
|
||||||
};
|
};
|
||||||
|
|
||||||
method::lookup_in_trait(fcx,
|
method::lookup_in_trait_adjusted(fcx,
|
||||||
expr.span,
|
expr.span,
|
||||||
Some(&*base_expr),
|
Some(&*base_expr),
|
||||||
token::intern(method_name),
|
token::intern(method_name),
|
||||||
trait_did,
|
trait_did,
|
||||||
base_ty,
|
autoderefref,
|
||||||
[])
|
base_ty,
|
||||||
|
None)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If some lookup succeeded, install method in table
|
||||||
// Regardless of whether the lookup succeeds, check the method arguments
|
method.map(|method| {
|
||||||
// so that we have *some* type for each argument.
|
let ty = method.ty;
|
||||||
let method_type = get_method_ty(&method);
|
fcx.inh.method_map.borrow_mut().insert(method_call, method);
|
||||||
|
ty
|
||||||
let mut args = vec![];
|
})
|
||||||
start_expr.as_ref().map(|x| args.push(x));
|
|
||||||
end_expr.as_ref().map(|x| args.push(x));
|
|
||||||
|
|
||||||
check_method_argument_types(fcx,
|
|
||||||
expr.span,
|
|
||||||
method_type,
|
|
||||||
expr,
|
|
||||||
args.as_slice(),
|
|
||||||
DoDerefArgs,
|
|
||||||
DontTupleArguments);
|
|
||||||
|
|
||||||
match method {
|
|
||||||
Some(method) => {
|
|
||||||
let result_ty = ty::ty_fn_ret(method.ty);
|
|
||||||
match method_call {
|
|
||||||
Some(method_call) => {
|
|
||||||
fcx.inh.method_map.borrow_mut().insert(method_call,
|
|
||||||
method);
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
match result_ty {
|
|
||||||
ty::FnConverging(result_ty) =>
|
|
||||||
Some(ty::mt { ty: result_ty, mutbl: ast::MutImmutable }),
|
|
||||||
ty::FnDiverging =>
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_overloaded_index(fcx: &FnCtxt,
|
fn try_index_step(fcx: &FnCtxt,
|
||||||
method_call: Option<MethodCall>,
|
method_call: MethodCall,
|
||||||
expr: &ast::Expr,
|
expr: &ast::Expr,
|
||||||
base_expr: &ast::Expr,
|
base_expr: &ast::Expr,
|
||||||
base_ty: ty::t,
|
adjusted_ty: ty::t,
|
||||||
index_expr: &P<ast::Expr>,
|
adjustment: ty::AutoDerefRef,
|
||||||
lvalue_pref: LvaluePreference)
|
lvalue_pref: LvaluePreference)
|
||||||
-> Option<ty::mt> {
|
-> Option<(/*index type*/ ty::t, /*element type*/ ty::t)>
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* To type-check `base_expr[index_expr]`, we progressively autoderef (and otherwise adjust)
|
||||||
|
* `base_expr`, looking for a type which either supports builtin indexing or overloaded
|
||||||
|
* indexing. This loop implements one step in that search; the autoderef loop is implemented
|
||||||
|
* by `autoderef_for_index`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
debug!("try_index_step(expr={}, base_expr.id={}, adjusted_ty={}, adjustment={})",
|
||||||
|
expr.repr(fcx.tcx()),
|
||||||
|
base_expr.repr(fcx.tcx()),
|
||||||
|
adjusted_ty.repr(fcx.tcx()),
|
||||||
|
adjustment);
|
||||||
|
|
||||||
|
// Try built-in indexing first.
|
||||||
|
match ty::index(adjusted_ty) {
|
||||||
|
Some(ty) => {
|
||||||
|
fcx.write_adjustment(base_expr.id, base_expr.span, ty::AdjustDerefRef(adjustment));
|
||||||
|
return Some((ty::mk_uint(), ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
None => { }
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_ty = fcx.infcx().next_ty_var();
|
||||||
|
let return_ty = fcx.infcx().next_ty_var();
|
||||||
|
|
||||||
// Try `IndexMut` first, if preferred.
|
// Try `IndexMut` first, if preferred.
|
||||||
let method = match (lvalue_pref, fcx.tcx().lang_items.index_mut_trait()) {
|
let method = match (lvalue_pref, fcx.tcx().lang_items.index_mut_trait()) {
|
||||||
(PreferMutLvalue, Some(trait_did)) => {
|
(PreferMutLvalue, Some(trait_did)) => {
|
||||||
method::lookup_in_trait(fcx,
|
method::lookup_in_trait_adjusted(fcx,
|
||||||
expr.span,
|
expr.span,
|
||||||
Some(&*base_expr),
|
Some(&*base_expr),
|
||||||
token::intern("index_mut"),
|
token::intern("index_mut"),
|
||||||
trait_did,
|
trait_did,
|
||||||
base_ty,
|
adjustment.clone(),
|
||||||
[])
|
adjusted_ty,
|
||||||
|
Some(vec![input_ty, return_ty]))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
@ -2333,29 +2476,25 @@ fn try_overloaded_index(fcx: &FnCtxt,
|
||||||
// Otherwise, fall back to `Index`.
|
// Otherwise, fall back to `Index`.
|
||||||
let method = match (method, fcx.tcx().lang_items.index_trait()) {
|
let method = match (method, fcx.tcx().lang_items.index_trait()) {
|
||||||
(None, Some(trait_did)) => {
|
(None, Some(trait_did)) => {
|
||||||
method::lookup_in_trait(fcx,
|
method::lookup_in_trait_adjusted(fcx,
|
||||||
expr.span,
|
expr.span,
|
||||||
Some(&*base_expr),
|
Some(&*base_expr),
|
||||||
token::intern("index"),
|
token::intern("index"),
|
||||||
trait_did,
|
trait_did,
|
||||||
base_ty,
|
adjustment,
|
||||||
[])
|
adjusted_ty,
|
||||||
|
Some(vec![input_ty, return_ty]))
|
||||||
}
|
}
|
||||||
(method, _) => method,
|
(method, _) => method,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Regardless of whether the lookup succeeds, check the method arguments
|
// If some lookup succeeds, write callee into table and extract index/element
|
||||||
// so that we have *some* type for each argument.
|
// type from the method signature.
|
||||||
let method_type = get_method_ty(&method);
|
// If some lookup succeeded, install method in table
|
||||||
check_method_argument_types(fcx,
|
method.map(|method| {
|
||||||
expr.span,
|
make_overloaded_lvalue_return_type(fcx, Some(method_call), Some(method));
|
||||||
method_type,
|
(input_ty, return_ty)
|
||||||
expr,
|
})
|
||||||
&[index_expr],
|
|
||||||
DoDerefArgs,
|
|
||||||
DontTupleArguments);
|
|
||||||
|
|
||||||
make_return_type(fcx, method_call, method)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given the head of a `for` expression, looks up the `next` method in the
|
/// Given the head of a `for` expression, looks up the `next` method in the
|
||||||
|
@ -2383,7 +2522,7 @@ fn lookup_method_for_for_loop(fcx: &FnCtxt,
|
||||||
token::intern("next"),
|
token::intern("next"),
|
||||||
trait_did,
|
trait_did,
|
||||||
expr_type,
|
expr_type,
|
||||||
[]);
|
None);
|
||||||
|
|
||||||
// Regardless of whether the lookup succeeds, check the method arguments
|
// Regardless of whether the lookup succeeds, check the method arguments
|
||||||
// so that we have *some* type for each argument.
|
// so that we have *some* type for each argument.
|
||||||
|
@ -2427,10 +2566,15 @@ fn lookup_method_for_for_loop(fcx: &FnCtxt,
|
||||||
if !substs.types.is_empty_in(subst::TypeSpace) => {
|
if !substs.types.is_empty_in(subst::TypeSpace) => {
|
||||||
*substs.types.get(subst::TypeSpace, 0)
|
*substs.types.get(subst::TypeSpace, 0)
|
||||||
}
|
}
|
||||||
|
ty::ty_err => {
|
||||||
|
ty::mk_err()
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
fcx.tcx().sess.span_err(iterator_expr.span,
|
fcx.tcx().sess.span_err(iterator_expr.span,
|
||||||
"`next` method of the `Iterator` \
|
format!("`next` method of the `Iterator` \
|
||||||
trait has an unexpected type");
|
trait has an unexpected type `{}`",
|
||||||
|
fcx.infcx().ty_to_string(return_type))
|
||||||
|
.as_slice());
|
||||||
ty::mk_err()
|
ty::mk_err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2457,7 +2601,7 @@ fn check_method_argument_types<'a>(fcx: &FnCtxt,
|
||||||
deref_args,
|
deref_args,
|
||||||
false,
|
false,
|
||||||
tuple_arguments);
|
tuple_arguments);
|
||||||
ty::FnConverging(method_fn_ty)
|
ty::FnConverging(ty::mk_err())
|
||||||
} else {
|
} else {
|
||||||
match ty::get(method_fn_ty).sty {
|
match ty::get(method_fn_ty).sty {
|
||||||
ty::ty_bare_fn(ref fty) => {
|
ty::ty_bare_fn(ref fty) => {
|
||||||
|
@ -3060,8 +3204,36 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
|
||||||
unbound_method: ||) -> ty::t {
|
unbound_method: ||) -> ty::t {
|
||||||
let method = match trait_did {
|
let method = match trait_did {
|
||||||
Some(trait_did) => {
|
Some(trait_did) => {
|
||||||
method::lookup_in_trait(fcx, op_ex.span, Some(lhs), opname,
|
// We do eager coercions to make using operators
|
||||||
trait_did, lhs_ty, &[])
|
// more ergonomic:
|
||||||
|
//
|
||||||
|
// - If the input is of type &'a T (resp. &'a mut T),
|
||||||
|
// then reborrow it to &'b T (resp. &'b mut T) where
|
||||||
|
// 'b <= 'a. This makes things like `x == y`, where
|
||||||
|
// `x` and `y` are both region pointers, work. We
|
||||||
|
// could also solve this with variance or different
|
||||||
|
// traits that don't force left and right to have same
|
||||||
|
// type.
|
||||||
|
let (adj_ty, adjustment) = match ty::get(lhs_ty).sty {
|
||||||
|
ty::ty_rptr(r_in, mt) => {
|
||||||
|
let r_adj = fcx.infcx().next_region_var(infer::Autoref(lhs.span));
|
||||||
|
fcx.mk_subr(infer::Reborrow(lhs.span), r_adj, r_in);
|
||||||
|
let adjusted_ty = ty::mk_rptr(fcx.tcx(), r_adj, mt);
|
||||||
|
let autoptr = ty::AutoPtr(r_adj, mt.mutbl, None);
|
||||||
|
let adjustment = ty::AutoDerefRef { autoderefs: 1, autoref: Some(autoptr) };
|
||||||
|
(adjusted_ty, adjustment)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
(lhs_ty, ty::AutoDerefRef { autoderefs: 0, autoref: None })
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("adjusted_ty={} adjustment={}",
|
||||||
|
adj_ty.repr(fcx.tcx()),
|
||||||
|
adjustment);
|
||||||
|
|
||||||
|
method::lookup_in_trait_adjusted(fcx, op_ex.span, Some(lhs), opname,
|
||||||
|
trait_did, adjustment, adj_ty, None)
|
||||||
}
|
}
|
||||||
None => None
|
None => None
|
||||||
};
|
};
|
||||||
|
@ -4338,55 +4510,47 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
|
||||||
ast::ExprIndex(ref base, ref idx) => {
|
ast::ExprIndex(ref base, ref idx) => {
|
||||||
check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref);
|
check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref);
|
||||||
check_expr(fcx, &**idx);
|
check_expr(fcx, &**idx);
|
||||||
let raw_base_t = fcx.expr_ty(&**base);
|
let base_t = fcx.expr_ty(&**base);
|
||||||
let idx_t = fcx.expr_ty(&**idx);
|
let idx_t = fcx.expr_ty(&**idx);
|
||||||
if ty::type_is_error(raw_base_t) {
|
if ty::type_is_error(base_t) {
|
||||||
fcx.write_ty(id, raw_base_t);
|
fcx.write_ty(id, base_t);
|
||||||
} else if ty::type_is_error(idx_t) {
|
} else if ty::type_is_error(idx_t) {
|
||||||
fcx.write_ty(id, idx_t);
|
fcx.write_ty(id, idx_t);
|
||||||
} else {
|
} else {
|
||||||
let (_, autoderefs, field_ty) =
|
let base_t = structurally_resolved_type(fcx, expr.span, base_t);
|
||||||
autoderef(fcx, expr.span, raw_base_t, Some(base.id),
|
|
||||||
lvalue_pref, |base_t, _| ty::index(base_t));
|
let result =
|
||||||
match field_ty {
|
autoderef_for_index(fcx, &**base, base_t, lvalue_pref, |adj_ty, adj| {
|
||||||
Some(ty) => {
|
try_index_step(fcx,
|
||||||
check_expr_has_type(fcx, &**idx, ty::mk_uint());
|
MethodCall::expr(expr.id),
|
||||||
fcx.write_ty(id, ty);
|
expr,
|
||||||
fcx.write_autoderef_adjustment(base.id, base.span, autoderefs);
|
&**base,
|
||||||
|
adj_ty,
|
||||||
|
adj,
|
||||||
|
lvalue_pref)
|
||||||
|
});
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Some((index_ty, element_ty)) => {
|
||||||
|
check_expr_has_type(fcx, &**idx, index_ty);
|
||||||
|
fcx.write_ty(id, element_ty);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// This is an overloaded method.
|
check_expr_has_type(fcx, &**idx, ty::mk_err());
|
||||||
let base_t = structurally_resolved_type(fcx,
|
fcx.type_error_message(
|
||||||
expr.span,
|
expr.span,
|
||||||
raw_base_t);
|
|actual| {
|
||||||
let method_call = MethodCall::expr(expr.id);
|
format!("cannot index a value of type `{}`",
|
||||||
match try_overloaded_index(fcx,
|
actual)
|
||||||
Some(method_call),
|
},
|
||||||
expr,
|
base_t,
|
||||||
&**base,
|
None);
|
||||||
base_t,
|
fcx.write_ty(id, ty::mk_err())
|
||||||
idx,
|
|
||||||
lvalue_pref) {
|
|
||||||
Some(mt) => fcx.write_ty(id, mt.ty),
|
|
||||||
None => {
|
|
||||||
fcx.type_error_message(expr.span,
|
|
||||||
|actual| {
|
|
||||||
format!("cannot \
|
|
||||||
index a \
|
|
||||||
value of \
|
|
||||||
type `{}`",
|
|
||||||
actual)
|
|
||||||
},
|
|
||||||
base_t,
|
|
||||||
None);
|
|
||||||
fcx.write_ty(id, ty::mk_err())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprSlice(ref base, ref start, ref end, ref mutbl) => {
|
ast::ExprSlice(ref base, ref start, ref end, mutbl) => {
|
||||||
check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref);
|
check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref);
|
||||||
let raw_base_t = fcx.expr_ty(&**base);
|
let raw_base_t = fcx.expr_ty(&**base);
|
||||||
|
|
||||||
|
@ -4415,19 +4579,19 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
|
||||||
raw_base_t);
|
raw_base_t);
|
||||||
let method_call = MethodCall::expr(expr.id);
|
let method_call = MethodCall::expr(expr.id);
|
||||||
match try_overloaded_slice(fcx,
|
match try_overloaded_slice(fcx,
|
||||||
Some(method_call),
|
method_call,
|
||||||
expr,
|
expr,
|
||||||
&**base,
|
&**base,
|
||||||
base_t,
|
base_t,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
mutbl) {
|
mutbl) {
|
||||||
Some(mt) => fcx.write_ty(id, mt.ty),
|
Some(ty) => fcx.write_ty(id, ty),
|
||||||
None => {
|
None => {
|
||||||
fcx.type_error_message(expr.span,
|
fcx.type_error_message(expr.span,
|
||||||
|actual| {
|
|actual| {
|
||||||
format!("cannot take a {}slice of a value with type `{}`",
|
format!("cannot take a {}slice of a value with type `{}`",
|
||||||
if mutbl == &ast::MutMutable {
|
if mutbl == ast::MutMutable {
|
||||||
"mutable "
|
"mutable "
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
|
|
|
@ -32,6 +32,7 @@ use syntax::ast_map;
|
||||||
use syntax::codemap::{Span, Pos};
|
use syntax::codemap::{Span, Pos};
|
||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
use syntax::print::pprust;
|
use syntax::print::pprust;
|
||||||
|
use syntax::ptr::P;
|
||||||
use syntax::{ast, ast_util};
|
use syntax::{ast, ast_util};
|
||||||
use syntax::owned_slice::OwnedSlice;
|
use syntax::owned_slice::OwnedSlice;
|
||||||
|
|
||||||
|
@ -561,6 +562,12 @@ impl<T:Repr> Repr for Option<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T:Repr> Repr for P<T> {
|
||||||
|
fn repr(&self, tcx: &ctxt) -> String {
|
||||||
|
(*self).repr(tcx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T:Repr,U:Repr> Repr for Result<T,U> {
|
impl<T:Repr,U:Repr> Repr for Result<T,U> {
|
||||||
fn repr(&self, tcx: &ctxt) -> String {
|
fn repr(&self, tcx: &ctxt) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
|
91
src/test/compile-fail/borrowck-overloaded-index-autoderef.rs
Normal file
91
src/test/compile-fail/borrowck-overloaded-index-autoderef.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// 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 still see borrowck errors of various kinds when using
|
||||||
|
// indexing and autoderef in combination.
|
||||||
|
|
||||||
|
struct Foo {
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<String,int> for Foo {
|
||||||
|
fn index<'a>(&'a self, z: &String) -> &'a int {
|
||||||
|
if z.as_slice() == "x" {
|
||||||
|
&self.x
|
||||||
|
} else {
|
||||||
|
&self.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<String,int> for Foo {
|
||||||
|
fn index_mut<'a>(&'a mut self, z: &String) -> &'a mut int {
|
||||||
|
if z.as_slice() == "x" {
|
||||||
|
&mut self.x
|
||||||
|
} else {
|
||||||
|
&mut self.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test1(mut f: Box<Foo>, s: String) {
|
||||||
|
let _p = &mut f[s];
|
||||||
|
let _q = &f[s]; //~ ERROR cannot borrow
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test2(mut f: Box<Foo>, s: String) {
|
||||||
|
let _p = &mut f[s];
|
||||||
|
let _q = &mut f[s]; //~ ERROR cannot borrow
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bar {
|
||||||
|
foo: Foo
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test3(mut f: Box<Bar>, s: String) {
|
||||||
|
let _p = &mut f.foo[s];
|
||||||
|
let _q = &mut f.foo[s]; //~ ERROR cannot borrow
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test4(mut f: Box<Bar>, s: String) {
|
||||||
|
let _p = &f.foo[s];
|
||||||
|
let _q = &f.foo[s];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test5(mut f: Box<Bar>, s: String) {
|
||||||
|
let _p = &f.foo[s];
|
||||||
|
let _q = &mut f.foo[s]; //~ ERROR cannot borrow
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test6(mut f: Box<Bar>, g: Foo, s: String) {
|
||||||
|
let _p = &f.foo[s];
|
||||||
|
f.foo = g; //~ ERROR cannot assign
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test7(mut f: Box<Bar>, g: Bar, s: String) {
|
||||||
|
let _p = &f.foo[s];
|
||||||
|
*f = g; //~ ERROR cannot assign
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test8(mut f: Box<Bar>, g: Foo, s: String) {
|
||||||
|
let _p = &mut f.foo[s];
|
||||||
|
f.foo = g; //~ ERROR cannot assign
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test9(mut f: Box<Bar>, g: Bar, s: String) {
|
||||||
|
let _p = &mut f.foo[s];
|
||||||
|
*f = g; //~ ERROR cannot assign
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ fn main() {
|
||||||
let x = [1,2];
|
let x = [1,2];
|
||||||
let y = match x {
|
let y = match x {
|
||||||
[] => None,
|
[] => None,
|
||||||
//~^ ERROR types: expected `[_#0i, ..2]`, found `[_#7, ..0]`
|
//~^ ERROR types: expected `[_#0i, ..2]`, found `[_#7t, ..0]`
|
||||||
// (expected array of 2 elements, found array of 0 elements)
|
// (expected array of 2 elements, found array of 0 elements)
|
||||||
[a,_] => Some(a)
|
[a,_] => Some(a)
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
loop {
|
loop {
|
||||||
break.push(1) //~ ERROR the type of this value must be known in this context
|
break.push(1) //~ ERROR the type of this value must be known in this context
|
||||||
//~^ ERROR multiple applicable methods in scope
|
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#![feature(overloaded_calls)]
|
#![feature(overloaded_calls)]
|
||||||
|
|
||||||
fn f<'r>(p: &'r mut fn(p: &mut ())) {
|
fn f<'r>(p: &'r mut fn(p: &mut ())) {
|
||||||
p(()) //~ ERROR mismatched types: expected `&mut ()`, found `()`
|
(*p)(()) //~ ERROR mismatched types: expected `&mut ()`, found `()`
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -18,7 +18,6 @@ impl<A> vec_monad<A> for Vec<A> {
|
||||||
let mut r = panic!();
|
let mut r = panic!();
|
||||||
for elt in self.iter() { r = r + f(*elt); }
|
for elt in self.iter() { r = r + f(*elt); }
|
||||||
//~^ ERROR the type of this value must be known
|
//~^ ERROR the type of this value must be known
|
||||||
//~^^ ERROR not implemented
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -15,5 +15,5 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
let x: &[int] = &[1, 2, 3, 4, 5];
|
let x: &[int] = &[1, 2, 3, 4, 5];
|
||||||
// Can't mutably slice an immutable slice
|
// Can't mutably slice an immutable slice
|
||||||
let y = x[mut 2..4]; //~ ERROR cannot take a mutable slice of a value with type `&[int]`
|
let y = x[mut 2..4]; //~ ERROR cannot borrow
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::num::Num;
|
||||||
|
|
||||||
trait BrokenAdd: Num {
|
trait BrokenAdd: Num {
|
||||||
fn broken_add<T>(&self, rhs: T) -> Self {
|
fn broken_add<T>(&self, rhs: T) -> Self {
|
||||||
*self + rhs //~ ERROR mismatched types
|
*self + rhs //~ ERROR expected `Self`, found `T`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
79
src/test/run-pass/overloaded-index-autoderef.rs
Normal file
79
src/test/run-pass/overloaded-index-autoderef.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
// 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 overloaded indexing combined with autoderef.
|
||||||
|
|
||||||
|
struct Foo {
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<int,int> for Foo {
|
||||||
|
fn index(&self, z: &int) -> &int {
|
||||||
|
if *z == 0 {
|
||||||
|
&self.x
|
||||||
|
} else {
|
||||||
|
&self.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<int,int> for Foo {
|
||||||
|
fn index_mut(&mut self, z: &int) -> &mut int {
|
||||||
|
if *z == 0 {
|
||||||
|
&mut self.x
|
||||||
|
} else {
|
||||||
|
&mut self.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Int {
|
||||||
|
fn get(self) -> int;
|
||||||
|
fn get_from_ref(&self) -> int;
|
||||||
|
fn inc(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Int for int {
|
||||||
|
fn get(self) -> int { self }
|
||||||
|
fn get_from_ref(&self) -> int { *self }
|
||||||
|
fn inc(&mut self) { *self += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut f = box Foo {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(f[1], 2);
|
||||||
|
|
||||||
|
f[0] = 3;
|
||||||
|
|
||||||
|
assert_eq!(f[0], 3);
|
||||||
|
|
||||||
|
// Test explicit IndexMut where `f` must be autoderef:
|
||||||
|
{
|
||||||
|
let p = &mut f[1];
|
||||||
|
*p = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test explicit Index where `f` must be autoderef:
|
||||||
|
{
|
||||||
|
let p = &f[1];
|
||||||
|
assert_eq!(*p, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test calling methods with `&mut self`, `self, and `&self` receivers:
|
||||||
|
f[1].inc();
|
||||||
|
assert_eq!(f[1].get(), 5);
|
||||||
|
assert_eq!(f[1].get_from_ref(), 5);
|
||||||
|
}
|
||||||
|
|
52
src/test/run-pass/overloaded-index-in-field.rs
Normal file
52
src/test/run-pass/overloaded-index-in-field.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// 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 using overloaded indexing when the "map" is stored in a
|
||||||
|
// field. This caused problems at some point.
|
||||||
|
|
||||||
|
struct Foo {
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bar {
|
||||||
|
foo: Foo
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<int,int> for Foo {
|
||||||
|
fn index(&self, z: &int) -> &int {
|
||||||
|
if *z == 0 {
|
||||||
|
&self.x
|
||||||
|
} else {
|
||||||
|
&self.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Int {
|
||||||
|
fn get(self) -> int;
|
||||||
|
fn get_from_ref(&self) -> int;
|
||||||
|
fn inc(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Int for int {
|
||||||
|
fn get(self) -> int { self }
|
||||||
|
fn get_from_ref(&self) -> int { *self }
|
||||||
|
fn inc(&mut self) { *self += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let f = Bar { foo: Foo {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
} };
|
||||||
|
assert_eq!(f.foo[1].get(), 2);
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,18 @@ impl IndexMut<int,int> for Foo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait Int {
|
||||||
|
fn get(self) -> int;
|
||||||
|
fn get_from_ref(&self) -> int;
|
||||||
|
fn inc(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Int for int {
|
||||||
|
fn get(self) -> int { self }
|
||||||
|
fn get_from_ref(&self) -> int { *self }
|
||||||
|
fn inc(&mut self) { *self += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut f = Foo {
|
let mut f = Foo {
|
||||||
x: 1,
|
x: 1,
|
||||||
|
@ -49,5 +61,10 @@ fn main() {
|
||||||
let p = &f[1];
|
let p = &f[1];
|
||||||
assert_eq!(*p, 4);
|
assert_eq!(*p, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test calling methods with `&mut self`, `self, and `&self` receivers:
|
||||||
|
f[1].inc();
|
||||||
|
assert_eq!(f[1].get(), 5);
|
||||||
|
assert_eq!(f[1].get_from_ref(), 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue