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:
Niko Matsakis 2014-10-31 05:44:10 -04:00
parent 33ef78fa8b
commit 0b5bc3314f
15 changed files with 831 additions and 225 deletions

View File

@ -5566,3 +5566,18 @@ pub fn with_freevars<T>(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[Freevar]| -> T)
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()
}
}

View File

@ -96,6 +96,7 @@ trait `ToString` imported, and I call `to_string()` on a value of type `T`,
use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure, MethodObject, MethodTraitObject};
use middle::typeck::check::regionmanip::replace_late_bound_regions;
use middle::typeck::TypeAndSubsts;
use middle::typeck::check::vtable;
use middle::ty_fold::TypeFoldable;
use util::common::indenter;
use util::ppaux;
@ -173,46 +174,178 @@ pub fn lookup<'a, 'tcx>(
pub fn lookup_in_trait<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>,
// In a call `a.b::<X, Y, ...>(...)`:
span: Span, // The expression `a.b(...)`'s span.
self_expr: Option<&'a ast::Expr>, // The expression `a`, if available.
m_name: ast::Name, // The name `b`.
trait_did: DefId, // The trait to limit the lookup to.
self_ty: ty::t, // The type of `a`.
supplied_tps: &'a [ty::t]) // The list of types X, Y, ... .
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>
{
let mut lcx = LookupContext {
fcx: fcx,
span: span,
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,
};
lookup_in_trait_adjusted(fcx, span, self_expr, m_name, trait_def_id,
ty::AutoDerefRef { autoderefs: 0, autoref: None },
self_ty, opt_input_types)
}
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_expr.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));
lcx.push_extension_candidate(trait_did);
let trait_def = ty::lookup_trait_def(fcx.tcx(), trait_def_id);
// when doing a trait search, ambiguity can't really happen except
// as part of the trait-lookup in general
match lcx.search(self_ty) {
Ok(callee) => Some(callee),
Err(_) => None
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_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,
@ -1446,9 +1579,8 @@ fn confirm_candidate(&self, rcvr_ty: ty::t, candidate: &Candidate)
}
}
fn fixup_derefs_on_method_receiver_if_necessary(
&self,
method_callee: &MethodCallee) {
fn fixup_derefs_on_method_receiver_if_necessary(&self,
method_callee: &MethodCallee) {
let sig = match ty::get(method_callee.ty).sty {
ty::ty_bare_fn(ref f) => f.sig.clone(),
ty::ty_closure(ref f) => f.sig.clone(),
@ -1485,6 +1617,9 @@ fn fixup_derefs_on_method_receiver_if_necessary(
}
}
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.
@ -1500,6 +1635,9 @@ fn fixup_derefs_on_method_receiver_if_necessary(
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,
@ -1518,24 +1656,59 @@ fn fixup_derefs_on_method_receiver_if_necessary(
// Don't retry the first one or we might infinite loop!
if i != 0 {
match expr.node {
ast::ExprIndex(ref base_expr, ref index_expr) => {
check::try_overloaded_index(
self.fcx,
Some(MethodCall::expr(expr.id)),
*expr,
ast::ExprIndex(ref base_expr, _) => {
let mut base_adjustment =
match self.fcx.inh.adjustments.borrow().find(&base_expr.id) {
Some(&ty::AdjustDerefRef(ref adr)) => (*adr).clone(),
None => ty::AutoDerefRef { autoderefs: 0, autoref: None },
Some(_) => {
self.tcx().sess.span_bug(
base_expr.span,
"unexpected adjustment type");
}
};
// If this is an overloaded index, the
// adjustment will include an extra layer of
// autoref because the method is an &self/&mut
// self method. We have to peel it off to get
// the raw adjustment that `try_index_step`
// expects. This is annoying and horrible. We
// ought to recode this routine so it doesn't
// (ab)use the normal type checking paths.
base_adjustment.autoref = match base_adjustment.autoref {
None => { None }
Some(AutoPtr(_, _, None)) => { None }
Some(AutoPtr(_, _, Some(box r))) => { Some(r) }
Some(_) => {
self.tcx().sess.span_bug(
base_expr.span,
"unexpected adjustment autoref");
}
};
let adjusted_base_ty =
self.fcx.adjust_expr_ty(
&**base_expr,
self.fcx.expr_ty(&**base_expr),
index_expr,
PreferMutLvalue);
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) => {
check::try_overloaded_deref(
self.fcx,
expr.span,
Some(MethodCall::expr(expr.id)),
Some(&**base_expr),
self.fcx.expr_ty(&**base_expr),
PreferMutLvalue);
self.fcx,
expr.span,
Some(MethodCall::expr(expr.id)),
Some(&**base_expr),
self.fcx.expr_ty(&**base_expr),
PreferMutLvalue);
}
_ => {}
}
@ -1623,15 +1796,25 @@ fn xform_self_ty(&self, method: &Rc<ty::Method>, substs: &subst::Substs) -> ty::
fn replace_late_bound_regions_with_fresh_var<T>(&self, binder_id: ast::NodeId, value: &T) -> T
where T : TypeFoldable + Repr
{
let (_, value) = replace_late_bound_regions(
self.fcx.tcx(),
binder_id,
value,
|br| self.fcx.infcx().next_region_var(infer::LateBoundRegion(self.span, br)));
value
replace_late_bound_regions_with_fresh_var(self.fcx.infcx(), self.span, binder_id, 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,
trait_def_id: ast::DefId,
method_name: ast::Name)

View File

@ -1630,6 +1630,10 @@ pub fn write_adjustment(&self,
adj: ty::AutoAdjustment) {
debug!("write_adjustment(node_id={}, adj={})", node_id, adj);
if adj.is_identity() {
return;
}
// Careful: adjustments can imply trait obligations if we are
// casting from a concrete type to an object type. I think
// it'd probably be nicer to move the logic that creates the
@ -1813,6 +1817,38 @@ pub fn expr_ty(&self, ex: &ast::Expr) -> ty::t {
}
}
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 {
match self.inh.node_types.borrow().find(&id) {
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()) {
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) {
Some(x) => return (resolved_t, autoderefs, Some(x)),
None => {}
@ -2117,17 +2157,17 @@ fn try_overloaded_call<'a>(fcx: &FnCtxt,
None => continue,
Some(function_trait) => function_trait,
};
let method_callee = match method::lookup_in_trait(
fcx,
call_expression.span,
Some(&*callee),
method_name,
function_trait,
callee_type,
[]) {
None => continue,
Some(method_callee) => method_callee,
};
let method_callee =
match method::lookup_in_trait(fcx,
call_expression.span,
Some(&*callee),
method_name,
function_trait,
callee_type,
None) {
None => continue,
Some(method_callee) => method_callee,
};
let method_call = MethodCall::expr(call_expression.id);
let output_type = check_method_argument_types(fcx,
call_expression.span,
@ -2159,13 +2199,14 @@ fn try_overloaded_deref(fcx: &FnCtxt,
base_expr: Option<&ast::Expr>,
base_ty: ty::t,
lvalue_pref: LvaluePreference)
-> Option<ty::mt> {
-> Option<ty::mt>
{
// Try DerefMut first, if preferred.
let method = match (lvalue_pref, fcx.tcx().lang_items.deref_mut_trait()) {
(PreferMutLvalue, Some(trait_did)) => {
method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x),
token::intern("deref_mut"), trait_did,
base_ty, [])
base_ty, None)
}
_ => None
};
@ -2175,25 +2216,27 @@ fn try_overloaded_deref(fcx: &FnCtxt,
(None, Some(trait_did)) => {
method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x),
token::intern("deref"), trait_did,
base_ty, [])
base_ty, None)
}
(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 {
match method {
&Some(ref method) => method.ty,
&None => ty::mk_err()
}
}
fn make_overloaded_lvalue_return_type(fcx: &FnCtxt,
method_call: Option<MethodCall>,
method: Option<MethodCallee>)
-> 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 {
Some(method) => {
let ref_ty = ty::ty_fn_ret(method.ty);
@ -2205,26 +2248,126 @@ fn make_return_type(fcx: &FnCtxt,
None => {}
}
match ref_ty {
ty::FnConverging(ref_ty) =>
ty::deref(ref_ty, true),
ty::FnDiverging =>
None
ty::FnConverging(ref_ty) => {
ty::deref(ref_ty, true)
}
ty::FnDiverging => {
fcx.tcx().sess.bug("index/deref traits do not define a `!` return")
}
}
}
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,
method_call: Option<MethodCall>,
method_call: MethodCall,
expr: &ast::Expr,
base_expr: &ast::Expr,
base_ty: ty::t,
start_expr: &Option<P<ast::Expr>>,
end_expr: &Option<P<ast::Expr>>,
mutbl: &ast::Mutability)
-> Option<ty::mt> {
let method = if mutbl == &ast::MutMutable {
mutbl: ast::Mutability)
-> Option<ty::t> // return type is result of slice
{
/*!
* 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.
match fcx.tcx().lang_items.slice_mut_trait() {
Some(trait_did) => {
@ -2235,13 +2378,14 @@ fn try_overloaded_slice(fcx: &FnCtxt,
(&None, &None) => "as_mut_slice_",
};
method::lookup_in_trait(fcx,
expr.span,
Some(&*base_expr),
token::intern(method_name),
trait_did,
base_ty,
[])
method::lookup_in_trait_adjusted(fcx,
expr.span,
Some(&*base_expr),
token::intern(method_name),
trait_did,
autoderefref,
base_ty,
None)
}
_ => None,
}
@ -2258,74 +2402,73 @@ fn try_overloaded_slice(fcx: &FnCtxt,
(&None, &None) => "as_slice_",
};
method::lookup_in_trait(fcx,
expr.span,
Some(&*base_expr),
token::intern(method_name),
trait_did,
base_ty,
[])
method::lookup_in_trait_adjusted(fcx,
expr.span,
Some(&*base_expr),
token::intern(method_name),
trait_did,
autoderefref,
base_ty,
None)
}
_ => None,
}
};
// Regardless of whether the lookup succeeds, check the method arguments
// so that we have *some* type for each argument.
let method_type = get_method_ty(&method);
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,
}
// If some lookup succeeded, install method in table
method.map(|method| {
let ty = method.ty;
fcx.inh.method_map.borrow_mut().insert(method_call, method);
ty
})
}
fn try_overloaded_index(fcx: &FnCtxt,
method_call: Option<MethodCall>,
expr: &ast::Expr,
base_expr: &ast::Expr,
base_ty: ty::t,
index_expr: &P<ast::Expr>,
lvalue_pref: LvaluePreference)
-> Option<ty::mt> {
fn try_index_step(fcx: &FnCtxt,
method_call: MethodCall,
expr: &ast::Expr,
base_expr: &ast::Expr,
adjusted_ty: ty::t,
adjustment: ty::AutoDerefRef,
lvalue_pref: LvaluePreference)
-> 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.
let method = match (lvalue_pref, fcx.tcx().lang_items.index_mut_trait()) {
(PreferMutLvalue, Some(trait_did)) => {
method::lookup_in_trait(fcx,
expr.span,
Some(&*base_expr),
token::intern("index_mut"),
trait_did,
base_ty,
[])
method::lookup_in_trait_adjusted(fcx,
expr.span,
Some(&*base_expr),
token::intern("index_mut"),
trait_did,
adjustment.clone(),
adjusted_ty,
Some(vec![input_ty, return_ty]))
}
_ => None,
};
@ -2333,29 +2476,25 @@ fn try_overloaded_index(fcx: &FnCtxt,
// Otherwise, fall back to `Index`.
let method = match (method, fcx.tcx().lang_items.index_trait()) {
(None, Some(trait_did)) => {
method::lookup_in_trait(fcx,
expr.span,
Some(&*base_expr),
token::intern("index"),
trait_did,
base_ty,
[])
method::lookup_in_trait_adjusted(fcx,
expr.span,
Some(&*base_expr),
token::intern("index"),
trait_did,
adjustment,
adjusted_ty,
Some(vec![input_ty, return_ty]))
}
(method, _) => method,
};
// Regardless of whether the lookup succeeds, check the method arguments
// so that we have *some* type for each argument.
let method_type = get_method_ty(&method);
check_method_argument_types(fcx,
expr.span,
method_type,
expr,
&[index_expr],
DoDerefArgs,
DontTupleArguments);
make_return_type(fcx, method_call, method)
// If some lookup succeeds, write callee into table and extract index/element
// type from the method signature.
// If some lookup succeeded, install method in table
method.map(|method| {
make_overloaded_lvalue_return_type(fcx, Some(method_call), Some(method));
(input_ty, return_ty)
})
}
/// 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"),
trait_did,
expr_type,
[]);
None);
// Regardless of whether the lookup succeeds, check the method arguments
// 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) => {
*substs.types.get(subst::TypeSpace, 0)
}
ty::ty_err => {
ty::mk_err()
}
_ => {
fcx.tcx().sess.span_err(iterator_expr.span,
"`next` method of the `Iterator` \
trait has an unexpected type");
format!("`next` method of the `Iterator` \
trait has an unexpected type `{}`",
fcx.infcx().ty_to_string(return_type))
.as_slice());
ty::mk_err()
}
}
@ -2457,7 +2601,7 @@ fn check_method_argument_types<'a>(fcx: &FnCtxt,
deref_args,
false,
tuple_arguments);
ty::FnConverging(method_fn_ty)
ty::FnConverging(ty::mk_err())
} else {
match ty::get(method_fn_ty).sty {
ty::ty_bare_fn(ref fty) => {
@ -3060,8 +3204,36 @@ fn lookup_op_method<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
unbound_method: ||) -> ty::t {
let method = match trait_did {
Some(trait_did) => {
method::lookup_in_trait(fcx, op_ex.span, Some(lhs), opname,
trait_did, lhs_ty, &[])
// We do eager coercions to make using operators
// 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
};
@ -4338,55 +4510,47 @@ fn check_struct_fields_on_error(fcx: &FnCtxt,
ast::ExprIndex(ref base, ref idx) => {
check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref);
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);
if ty::type_is_error(raw_base_t) {
fcx.write_ty(id, raw_base_t);
if ty::type_is_error(base_t) {
fcx.write_ty(id, base_t);
} else if ty::type_is_error(idx_t) {
fcx.write_ty(id, idx_t);
} else {
let (_, autoderefs, field_ty) =
autoderef(fcx, expr.span, raw_base_t, Some(base.id),
lvalue_pref, |base_t, _| ty::index(base_t));
match field_ty {
Some(ty) => {
check_expr_has_type(fcx, &**idx, ty::mk_uint());
fcx.write_ty(id, ty);
fcx.write_autoderef_adjustment(base.id, base.span, autoderefs);
let base_t = structurally_resolved_type(fcx, expr.span, base_t);
let result =
autoderef_for_index(fcx, &**base, base_t, lvalue_pref, |adj_ty, adj| {
try_index_step(fcx,
MethodCall::expr(expr.id),
expr,
&**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.
let base_t = structurally_resolved_type(fcx,
expr.span,
raw_base_t);
let method_call = MethodCall::expr(expr.id);
match try_overloaded_index(fcx,
Some(method_call),
expr,
&**base,
base_t,
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())
}
}
check_expr_has_type(fcx, &**idx, ty::mk_err());
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);
let raw_base_t = fcx.expr_ty(&**base);
@ -4415,19 +4579,19 @@ fn check_struct_fields_on_error(fcx: &FnCtxt,
raw_base_t);
let method_call = MethodCall::expr(expr.id);
match try_overloaded_slice(fcx,
Some(method_call),
method_call,
expr,
&**base,
base_t,
start,
end,
mutbl) {
Some(mt) => fcx.write_ty(id, mt.ty),
Some(ty) => fcx.write_ty(id, ty),
None => {
fcx.type_error_message(expr.span,
|actual| {
format!("cannot take a {}slice of a value with type `{}`",
if mutbl == &ast::MutMutable {
if mutbl == ast::MutMutable {
"mutable "
} else {
""

View File

@ -32,6 +32,7 @@
use syntax::codemap::{Span, Pos};
use syntax::parse::token;
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::{ast, ast_util};
use syntax::owned_slice::OwnedSlice;
@ -561,6 +562,12 @@ fn repr(&self, tcx: &ctxt) -> String {
}
}
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> {
fn repr(&self, tcx: &ctxt) -> String {
match self {

View 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() {
}

View File

@ -14,7 +14,7 @@ fn main() {
let x = [1,2];
let y = match x {
[] => 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)
[a,_] => Some(a)
};

View File

@ -11,7 +11,6 @@
fn main() {
loop {
break.push(1) //~ ERROR the type of this value must be known in this context
//~^ ERROR multiple applicable methods in scope
;
}
}

View File

@ -11,7 +11,7 @@
#![feature(overloaded_calls)]
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() {}

View File

@ -18,7 +18,6 @@ fn bind<B>(&self, f: |A| -> Vec<B> ) {
let mut r = panic!();
for elt in self.iter() { r = r + f(*elt); }
//~^ ERROR the type of this value must be known
//~^^ ERROR not implemented
}
}
fn main() {

View File

@ -15,5 +15,5 @@
fn main() {
let x: &[int] = &[1, 2, 3, 4, 5];
// 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
}

View File

@ -12,7 +12,7 @@
trait BrokenAdd: Num {
fn broken_add<T>(&self, rhs: T) -> Self {
*self + rhs //~ ERROR mismatched types
*self + rhs //~ ERROR expected `Self`, found `T`
}
}

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

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

View File

@ -33,6 +33,18 @@ fn index_mut(&mut self, z: &int) -> &mut int {
}
}
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 = Foo {
x: 1,
@ -49,5 +61,10 @@ fn main() {
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);
}