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
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
""
|
||||
|
@ -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 {
|
||||
|
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 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)
|
||||
};
|
||||
|
@ -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
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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`
|
||||
}
|
||||
}
|
||||
|
||||
|
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 @@ 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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user