auto merge of #18486 : nikomatsakis/rust/operator-dispatch, r=pcwalton
This branch cleans up overloaded operator resolution so that it is strictly based on the traits in `ops`, rather than going through the normal method lookup mechanism. It also adds full support for autoderef to overloaded index (whereas before autoderef only worked for non-overloaded index) as well as for the slicing operators. This is a [breaking-change]: in the past, we were accepting combinations of operands that were not intended to be accepted. For example, it was possible to compare a fixed-length array and a slice, or apply the `!` operator to a `&int`. See the first two commits in this pull-request for examples. One downside of this change is that comparing fixed-length arrays doesn't always work as smoothly as it did before. Before this, comparisons sometimes worked due to various coercions to slices. I've added impls for `Eq`, `Ord`, etc for fixed-lengths arrays up to and including length 32, but if the array is longer than that you'll need to either newtype the array or convert to slices. Note that this plays better with deriving in any case than the previous scheme. Fixes #4920. Fixes #16821. Fixes #15757. cc @alexcrichton cc @aturon
This commit is contained in:
commit
63c4f22f2b
@ -4601,20 +4601,24 @@ returns `true` or `false`. The new iterator `filter()` produces
|
||||
only the elements that that closure returns `true` for:
|
||||
|
||||
```{rust}
|
||||
for i in range(1i, 100i).filter(|x| x % 2 == 0) {
|
||||
for i in range(1i, 100i).filter(|&x| x % 2 == 0) {
|
||||
println!("{}", i);
|
||||
}
|
||||
```
|
||||
|
||||
This will print all of the even numbers between one and a hundred.
|
||||
(Note that because `filter` doesn't consume the elements that are
|
||||
being iterated over, it is passed a reference to each element, and
|
||||
thus the filter predicate uses the `&x` pattern to extract the integer
|
||||
itself.)
|
||||
|
||||
You can chain all three things together: start with an iterator, adapt it
|
||||
a few times, and then consume the result. Check it out:
|
||||
|
||||
```{rust}
|
||||
range(1i, 1000i)
|
||||
.filter(|x| x % 2 == 0)
|
||||
.filter(|x| x % 3 == 0)
|
||||
.filter(|&x| x % 2 == 0)
|
||||
.filter(|&x| x % 3 == 0)
|
||||
.take(5)
|
||||
.collect::<Vec<int>>();
|
||||
```
|
||||
|
@ -132,7 +132,7 @@ fn drop(&mut self) {
|
||||
|
||||
#[inline]
|
||||
fn round_up(base: uint, align: uint) -> uint {
|
||||
(base.checked_add(&(align - 1))).unwrap() & !(&(align - 1))
|
||||
(base.checked_add(&(align - 1))).unwrap() & !(align - 1)
|
||||
}
|
||||
|
||||
// Walk down a chunk, running the destructors for any objects stored
|
||||
|
@ -1598,15 +1598,15 @@ fn test_permute_fail() {
|
||||
#[test]
|
||||
fn test_total_ord() {
|
||||
let c: &[int] = &[1, 2, 3];
|
||||
[1, 2, 3, 4].cmp(& c) == Greater;
|
||||
[1, 2, 3, 4][].cmp(& c) == Greater;
|
||||
let c: &[int] = &[1, 2, 3, 4];
|
||||
[1, 2, 3].cmp(& c) == Less;
|
||||
[1, 2, 3][].cmp(& c) == Less;
|
||||
let c: &[int] = &[1, 2, 3, 6];
|
||||
[1, 2, 3, 4].cmp(& c) == Equal;
|
||||
[1, 2, 3, 4][].cmp(& c) == Equal;
|
||||
let c: &[int] = &[1, 2, 3, 4, 5, 6];
|
||||
[1, 2, 3, 4, 5, 5, 5, 5].cmp(& c) == Less;
|
||||
[1, 2, 3, 4, 5, 5, 5, 5][].cmp(& c) == Less;
|
||||
let c: &[int] = &[1, 2, 3, 4];
|
||||
[2, 2].cmp(& c) == Greater;
|
||||
[2, 2][].cmp(& c) == Greater;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1980,7 +1980,7 @@ fn test_mut_split_at() {
|
||||
let (left, right) = values.split_at_mut(2);
|
||||
{
|
||||
let left: &[_] = left;
|
||||
assert!(left[0..left.len()] == [1, 2]);
|
||||
assert!(left[0..left.len()] == [1, 2][]);
|
||||
}
|
||||
for p in left.iter_mut() {
|
||||
*p += 1;
|
||||
@ -1988,7 +1988,7 @@ fn test_mut_split_at() {
|
||||
|
||||
{
|
||||
let right: &[_] = right;
|
||||
assert!(right[0..right.len()] == [3, 4, 5]);
|
||||
assert!(right[0..right.len()] == [3, 4, 5][]);
|
||||
}
|
||||
for p in right.iter_mut() {
|
||||
*p += 2;
|
||||
|
@ -919,7 +919,7 @@ pub fn remove(&mut self, index: uint) -> Option<T> {
|
||||
///
|
||||
/// ```
|
||||
/// let mut vec = vec![1i, 2, 3, 4];
|
||||
/// vec.retain(|x| x%2 == 0);
|
||||
/// vec.retain(|&x| x%2 == 0);
|
||||
/// assert_eq!(vec, vec![2, 4]);
|
||||
/// ```
|
||||
#[unstable = "the closure argument may become an unboxed closure"]
|
||||
@ -1800,7 +1800,7 @@ fn test_split_at_mut() {
|
||||
let (left, right) = values.split_at_mut(2);
|
||||
{
|
||||
let left: &[_] = left;
|
||||
assert!(left[0..left.len()] == [1, 2]);
|
||||
assert!(left[0..left.len()] == [1, 2][]);
|
||||
}
|
||||
for p in left.iter_mut() {
|
||||
*p += 1;
|
||||
@ -1808,7 +1808,7 @@ fn test_split_at_mut() {
|
||||
|
||||
{
|
||||
let right: &[_] = right;
|
||||
assert!(right[0..right.len()] == [3, 4, 5]);
|
||||
assert!(right[0..right.len()] == [3, 4, 5][]);
|
||||
}
|
||||
for p in right.iter_mut() {
|
||||
*p += 2;
|
||||
@ -1863,7 +1863,7 @@ fn test_grow_fn() {
|
||||
#[test]
|
||||
fn test_retain() {
|
||||
let mut vec = vec![1u, 2, 3, 4];
|
||||
vec.retain(|x| x%2 == 0);
|
||||
vec.retain(|&x| x % 2 == 0);
|
||||
assert!(vec == vec![2u, 4]);
|
||||
}
|
||||
|
||||
|
83
src/libcore/array.rs
Normal file
83
src/libcore/array.rs
Normal file
@ -0,0 +1,83 @@
|
||||
// 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.
|
||||
|
||||
/*!
|
||||
* Implementations of things like `Eq` for fixed-length arrays
|
||||
* up to a certain length. Eventually we should able to generalize
|
||||
* to all lengths.
|
||||
*/
|
||||
|
||||
#![stable]
|
||||
#![experimental] // not yet reviewed
|
||||
|
||||
use cmp::*;
|
||||
use option::{Option};
|
||||
|
||||
// macro for implementing n-ary tuple functions and operations
|
||||
macro_rules! array_impls {
|
||||
($($N:expr)+) => {
|
||||
$(
|
||||
#[unstable = "waiting for PartialEq to stabilize"]
|
||||
impl<T:PartialEq> PartialEq for [T, ..$N] {
|
||||
#[inline]
|
||||
fn eq(&self, other: &[T, ..$N]) -> bool {
|
||||
self[] == other[]
|
||||
}
|
||||
#[inline]
|
||||
fn ne(&self, other: &[T, ..$N]) -> bool {
|
||||
self[] != other[]
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable = "waiting for Eq to stabilize"]
|
||||
impl<T:Eq> Eq for [T, ..$N] { }
|
||||
|
||||
#[unstable = "waiting for PartialOrd to stabilize"]
|
||||
impl<T:PartialOrd> PartialOrd for [T, ..$N] {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &[T, ..$N]) -> Option<Ordering> {
|
||||
PartialOrd::partial_cmp(&self[], &other[])
|
||||
}
|
||||
#[inline]
|
||||
fn lt(&self, other: &[T, ..$N]) -> bool {
|
||||
PartialOrd::lt(&self[], &other[])
|
||||
}
|
||||
#[inline]
|
||||
fn le(&self, other: &[T, ..$N]) -> bool {
|
||||
PartialOrd::le(&self[], &other[])
|
||||
}
|
||||
#[inline]
|
||||
fn ge(&self, other: &[T, ..$N]) -> bool {
|
||||
PartialOrd::ge(&self[], &other[])
|
||||
}
|
||||
#[inline]
|
||||
fn gt(&self, other: &[T, ..$N]) -> bool {
|
||||
PartialOrd::gt(&self[], &other[])
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable = "waiting for Ord to stabilize"]
|
||||
impl<T:Ord> Ord for [T, ..$N] {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &[T, ..$N]) -> Ordering {
|
||||
Ord::cmp(&self[], &other[])
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
array_impls! {
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
10 11 12 13 14 15 16 17 18 19
|
||||
20 21 22 23 24 25 26 27 28 29
|
||||
30 31 32
|
||||
}
|
||||
|
@ -126,6 +126,10 @@
|
||||
pub mod unit;
|
||||
pub mod fmt;
|
||||
|
||||
// note: does not need to be public
|
||||
#[cfg(not(stage0))]
|
||||
mod array;
|
||||
|
||||
#[doc(hidden)]
|
||||
mod core {
|
||||
pub use panicking;
|
||||
|
@ -787,8 +787,8 @@ impl<A, V: FromIterator<A>> FromIterator<Option<A>> for Option<V> {
|
||||
/// use std::uint;
|
||||
///
|
||||
/// let v = vec!(1u, 2u);
|
||||
/// let res: Option<Vec<uint>> = v.iter().map(|x: &uint|
|
||||
/// if *x == uint::MAX { None }
|
||||
/// let res: Option<Vec<uint>> = v.iter().map(|&x: &uint|
|
||||
/// if x == uint::MAX { None }
|
||||
/// else { Some(x + 1) }
|
||||
/// ).collect();
|
||||
/// assert!(res == Some(vec!(2u, 3u)));
|
||||
|
@ -894,8 +894,8 @@ impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> {
|
||||
/// use std::uint;
|
||||
///
|
||||
/// let v = vec!(1u, 2u);
|
||||
/// let res: Result<Vec<uint>, &'static str> = v.iter().map(|x: &uint|
|
||||
/// if *x == uint::MAX { Err("Overflow!") }
|
||||
/// let res: Result<Vec<uint>, &'static str> = v.iter().map(|&x: &uint|
|
||||
/// if x == uint::MAX { Err("Overflow!") }
|
||||
/// else { Ok(x + 1) }
|
||||
/// ).collect();
|
||||
/// assert!(res == Ok(vec!(2u, 3u)));
|
||||
|
@ -356,7 +356,7 @@ fn test_iterator_size_hint() {
|
||||
assert_eq!(vi.zip(v2.iter()).size_hint(), (3, Some(3)));
|
||||
assert_eq!(vi.scan(0i, |_,_| Some(0i)).size_hint(), (0, Some(10)));
|
||||
assert_eq!(vi.filter(|_| false).size_hint(), (0, Some(10)));
|
||||
assert_eq!(vi.map(|i| i+1).size_hint(), (10, Some(10)));
|
||||
assert_eq!(vi.map(|&i| i+1).size_hint(), (10, Some(10)));
|
||||
assert_eq!(vi.filter_map(|_| Some(0i)).size_hint(), (0, Some(10)));
|
||||
}
|
||||
|
||||
@ -388,9 +388,9 @@ fn test_any() {
|
||||
#[test]
|
||||
fn test_find() {
|
||||
let v: &[int] = &[1i, 3, 9, 27, 103, 14, 11];
|
||||
assert_eq!(*v.iter().find(|x| *x & 1 == 0).unwrap(), 14);
|
||||
assert_eq!(*v.iter().find(|x| *x % 3 == 0).unwrap(), 3);
|
||||
assert!(v.iter().find(|x| *x % 12 == 0).is_none());
|
||||
assert_eq!(*v.iter().find(|&&x| x & 1 == 0).unwrap(), 14);
|
||||
assert_eq!(*v.iter().find(|&&x| x % 3 == 0).unwrap(), 3);
|
||||
assert!(v.iter().find(|&&x| x % 12 == 0).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1167,25 +1167,25 @@ fn ne(&self, other: &InferRegion) -> bool {
|
||||
|
||||
impl fmt::Show for TyVid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
|
||||
write!(f, "<generic #{}>", self.index)
|
||||
write!(f, "_#{}t", self.index)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for IntVid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "<generic integer #{}>", self.index)
|
||||
write!(f, "_#{}i", self.index)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for FloatVid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "<generic float #{}>", self.index)
|
||||
write!(f, "_#{}f", self.index)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for RegionVid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "'<generic lifetime #{}>", self.index)
|
||||
write!(f, "'_#{}r", self.index)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,8 +1579,7 @@ fn confirm_candidate(&self, rcvr_ty: ty::t, candidate: &Candidate)
|
||||
}
|
||||
}
|
||||
|
||||
fn fixup_derefs_on_method_receiver_if_necessary(
|
||||
&self,
|
||||
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(),
|
||||
@ -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,14 +1656,49 @@ 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(
|
||||
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,
|
||||
Some(&ty::AdjustDerefRef(base_adjustment.clone())));
|
||||
|
||||
check::try_index_step(
|
||||
self.fcx,
|
||||
Some(MethodCall::expr(expr.id)),
|
||||
MethodCall::expr(expr.id),
|
||||
*expr,
|
||||
&**base_expr,
|
||||
self.fcx.expr_ty(&**base_expr),
|
||||
index_expr,
|
||||
adjusted_base_ty,
|
||||
base_adjustment,
|
||||
PreferMutLvalue);
|
||||
}
|
||||
ast::ExprUnary(ast::UnDeref, ref base_expr) => {
|
||||
@ -1622,15 +1795,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
|
||||
{
|
||||
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(
|
||||
self.fcx.tcx(),
|
||||
infcx.tcx,
|
||||
binder_id,
|
||||
value,
|
||||
|br| self.fcx.infcx().next_region_var(infer::LateBoundRegion(self.span, br)));
|
||||
|br| infcx.next_region_var(infer::LateBoundRegion(span, br)));
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
fn trait_method(tcx: &ty::ctxt,
|
||||
trait_def_id: ast::DefId,
|
||||
|
@ -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,14 +2157,14 @@ fn try_overloaded_call<'a>(fcx: &FnCtxt,
|
||||
None => continue,
|
||||
Some(function_trait) => function_trait,
|
||||
};
|
||||
let method_callee = match method::lookup_in_trait(
|
||||
fcx,
|
||||
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,
|
||||
};
|
||||
@ -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_return_type(fcx: &FnCtxt,
|
||||
fn make_overloaded_lvalue_return_type(fcx: &FnCtxt,
|
||||
method_call: Option<MethodCall>,
|
||||
method: Option<MethodCallee>)
|
||||
-> Option<ty::mt> {
|
||||
-> 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.
|
||||
*/
|
||||
|
||||
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,
|
||||
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,
|
||||
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>,
|
||||
fn try_index_step(fcx: &FnCtxt,
|
||||
method_call: MethodCall,
|
||||
expr: &ast::Expr,
|
||||
base_expr: &ast::Expr,
|
||||
base_ty: ty::t,
|
||||
index_expr: &P<ast::Expr>,
|
||||
adjusted_ty: ty::t,
|
||||
adjustment: ty::AutoDerefRef,
|
||||
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.
|
||||
let method = match (lvalue_pref, fcx.tcx().lang_items.index_mut_trait()) {
|
||||
(PreferMutLvalue, Some(trait_did)) => {
|
||||
method::lookup_in_trait(fcx,
|
||||
method::lookup_in_trait_adjusted(fcx,
|
||||
expr.span,
|
||||
Some(&*base_expr),
|
||||
token::intern("index_mut"),
|
||||
trait_did,
|
||||
base_ty,
|
||||
[])
|
||||
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,
|
||||
method::lookup_in_trait_adjusted(fcx,
|
||||
expr.span,
|
||||
Some(&*base_expr),
|
||||
token::intern("index"),
|
||||
trait_did,
|
||||
base_ty,
|
||||
[])
|
||||
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,43 +4510,37 @@ 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);
|
||||
}
|
||||
_ => {
|
||||
// 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),
|
||||
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,
|
||||
base_t,
|
||||
idx,
|
||||
lvalue_pref) {
|
||||
Some(mt) => fcx.write_ty(id, mt.ty),
|
||||
None => {
|
||||
fcx.type_error_message(expr.span,
|
||||
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);
|
||||
}
|
||||
_ => {
|
||||
check_expr_has_type(fcx, &**idx, ty::mk_err());
|
||||
fcx.type_error_message(
|
||||
expr.span,
|
||||
|actual| {
|
||||
format!("cannot \
|
||||
index a \
|
||||
value of \
|
||||
type `{}`",
|
||||
format!("cannot index a value of type `{}`",
|
||||
actual)
|
||||
},
|
||||
base_t,
|
||||
@ -4384,9 +4550,7 @@ fn check_struct_fields_on_error(fcx: &FnCtxt,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
""
|
||||
|
@ -952,6 +952,11 @@ fn infer_variable_values(&self,
|
||||
-> Vec<VarValue>
|
||||
{
|
||||
let mut var_data = self.construct_var_data();
|
||||
|
||||
// Dorky hack to cause `dump_constraints` to only get called
|
||||
// if debug mode is enabled:
|
||||
debug!("----() End constraint listing {}---", self.dump_constraints());
|
||||
|
||||
self.expansion(var_data.as_mut_slice());
|
||||
self.contraction(var_data.as_mut_slice());
|
||||
let values =
|
||||
@ -974,6 +979,13 @@ fn construct_var_data(&self) -> Vec<VarData> {
|
||||
})
|
||||
}
|
||||
|
||||
fn dump_constraints(&self) {
|
||||
debug!("----() Start constraint listing ()----");
|
||||
for (idx, (constraint, _)) in self.constraints.borrow().iter().enumerate() {
|
||||
debug!("Constraint {} => {}", idx, constraint.repr(self.tcx));
|
||||
}
|
||||
}
|
||||
|
||||
fn expansion(&self, var_data: &mut [VarData]) {
|
||||
self.iterate_until_fixed_point("Expansion", |constraint| {
|
||||
debug!("expansion: constraint={} origin={}",
|
||||
|
@ -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;
|
||||
|
||||
@ -372,14 +373,10 @@ fn push_sig_to_string(cx: &ctxt,
|
||||
fn infer_ty_to_string(cx: &ctxt, ty: ty::InferTy) -> String {
|
||||
let print_var_ids = cx.sess.verbose();
|
||||
match ty {
|
||||
ty::TyVar(ty::TyVid { index: vid }) if print_var_ids =>
|
||||
format!("_#{}", vid),
|
||||
ty::IntVar(ty::IntVid { index: vid }) if print_var_ids =>
|
||||
format!("_#{}i", vid),
|
||||
ty::FloatVar(ty::FloatVid { index: vid }) if print_var_ids =>
|
||||
format!("_#{}f", vid),
|
||||
ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_) =>
|
||||
"_".to_string(),
|
||||
ty::TyVar(ref vid) if print_var_ids => vid.repr(cx),
|
||||
ty::IntVar(ref vid) if print_var_ids => vid.repr(cx),
|
||||
ty::FloatVar(ref vid) if print_var_ids => vid.repr(cx),
|
||||
ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_) => format!("_"),
|
||||
ty::SkolemizedTy(v) => format!("SkolemizedTy({})", v),
|
||||
ty::SkolemizedIntTy(v) => format!("SkolemizedIntTy({})", v)
|
||||
}
|
||||
@ -561,6 +558,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 {
|
||||
@ -851,7 +854,7 @@ fn repr(&self, tcx: &ctxt) -> String {
|
||||
}
|
||||
|
||||
ty::ReInfer(ReVar(ref vid)) => {
|
||||
format!("ReInfer({})", vid.index)
|
||||
format!("{}", vid)
|
||||
}
|
||||
|
||||
ty::ReInfer(ReSkolemized(id, ref bound_region)) => {
|
||||
|
@ -177,13 +177,13 @@ pub fn is_all(&self) -> bool {
|
||||
/// Returns `true` if there are flags common to both `self` and `other`.
|
||||
#[inline]
|
||||
pub fn intersects(&self, other: $BitFlags) -> bool {
|
||||
!(self & other).is_empty()
|
||||
!(*self & other).is_empty()
|
||||
}
|
||||
|
||||
/// Returns `true` all of the flags in `other` are contained within `self`.
|
||||
#[inline]
|
||||
pub fn contains(&self, other: $BitFlags) -> bool {
|
||||
(self & other) == other
|
||||
(*self & other) == other
|
||||
}
|
||||
|
||||
/// Inserts the specified flags in-place.
|
||||
|
@ -792,7 +792,7 @@ fn test_move_iter() {
|
||||
};
|
||||
|
||||
let v = hs.into_iter().collect::<Vec<char>>();
|
||||
assert!(['a', 'b'] == v.as_slice() || ['b', 'a'] == v.as_slice());
|
||||
assert!(['a', 'b'][] == v.as_slice() || ['b', 'a'][] == v.as_slice());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -319,7 +319,7 @@ impl fmt::Show for Duration {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// technically speaking, negative duration is not valid ISO 8601,
|
||||
// but we need to print it anyway.
|
||||
let (abs, sign) = if self.secs < 0 { (-self, "-") } else { (*self, "") };
|
||||
let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") };
|
||||
|
||||
let days = abs.secs / SECS_PER_DAY;
|
||||
let secs = abs.secs - days * SECS_PER_DAY;
|
||||
|
@ -96,7 +96,7 @@ fn add(&self, other: &Duration) -> Timespec {
|
||||
let d_sec = other.num_seconds();
|
||||
// It is safe to unwrap the nanoseconds, because there cannot be
|
||||
// more than one second left, which fits in i64 and in i32.
|
||||
let d_nsec = (other - Duration::seconds(d_sec))
|
||||
let d_nsec = (*other - Duration::seconds(d_sec))
|
||||
.num_nanoseconds().unwrap() as i32;
|
||||
let mut sec = self.sec + d_sec;
|
||||
let mut nsec = self.nsec + d_nsec;
|
||||
|
@ -290,7 +290,7 @@ fn search(
|
||||
let masks_at = &masks[i];
|
||||
|
||||
// for every unused piece
|
||||
for id in range(0u, 10).filter(|id| board & (1 << (id + 50)) == 0) {
|
||||
for id in range(0u, 10).filter(|&id| board & (1 << (id + 50)) == 0) {
|
||||
// for each mask that fits on the board
|
||||
for m in masks_at[id].iter().filter(|&m| board & *m == 0) {
|
||||
// This check is too costly.
|
||||
|
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`
|
||||
}
|
||||
}
|
||||
|
||||
|
41
src/test/run-pass/operator-multidispatch.rs
Normal file
41
src/test/run-pass/operator-multidispatch.rs
Normal file
@ -0,0 +1,41 @@
|
||||
// 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 can overload the `+` operator for points so that two
|
||||
// points can be added, and a point can be added to an integer.
|
||||
|
||||
use std::ops;
|
||||
|
||||
#[deriving(Show,PartialEq,Eq)]
|
||||
struct Point {
|
||||
x: int,
|
||||
y: int
|
||||
}
|
||||
|
||||
impl ops::Add<Point,Point> for Point {
|
||||
fn add(&self, other: &Point) -> Point {
|
||||
Point {x: self.x + (*other).x, y: self.y + (*other).y}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add<int,Point> for Point {
|
||||
fn add(&self, &other: &int) -> Point {
|
||||
Point {x: self.x + other,
|
||||
y: self.y + other}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let mut p = Point {x: 10, y: 20};
|
||||
p = p + Point {x: 101, y: 102};
|
||||
assert_eq!(p, Point {x: 111, y: 122});
|
||||
p = p + 1;
|
||||
assert_eq!(p, Point {x: 112, y: 123});
|
||||
}
|
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