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:
bors 2014-11-05 22:31:44 +00:00
commit 63c4f22f2b
31 changed files with 1013 additions and 267 deletions

View File

@ -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>>();
```

View File

@ -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

View File

@ -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;

View File

@ -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
View 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
}

View File

@ -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;

View File

@ -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)));

View File

@ -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)));

View File

@ -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]

View File

@ -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()
}
}

View File

@ -96,6 +96,7 @@ trait `ToString` imported, and I call `to_string()` on a value of type `T`,
use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure, MethodObject, MethodTraitObject};
use middle::typeck::check::regionmanip::replace_late_bound_regions;
use middle::typeck::TypeAndSubsts;
use middle::typeck::check::vtable;
use middle::ty_fold::TypeFoldable;
use util::common::indenter;
use util::ppaux;
@ -173,46 +174,178 @@ pub fn lookup<'a, 'tcx>(
pub fn lookup_in_trait<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>,
// In a call `a.b::<X, Y, ...>(...)`:
span: Span, // The expression `a.b(...)`'s span.
self_expr: Option<&'a ast::Expr>, // The expression `a`, if available.
m_name: ast::Name, // The name `b`.
trait_did: DefId, // The trait to limit the lookup to.
self_ty: ty::t, // The type of `a`.
supplied_tps: &'a [ty::t]) // The list of types X, Y, ... .
span: Span,
self_expr: Option<&'a ast::Expr>,
m_name: ast::Name,
trait_def_id: DefId,
self_ty: ty::t,
opt_input_types: Option<Vec<ty::t>>)
-> Option<MethodCallee>
{
let mut lcx = LookupContext {
fcx: fcx,
span: span,
self_expr: self_expr,
m_name: m_name,
supplied_tps: supplied_tps,
impl_dups: HashSet::new(),
inherent_candidates: Vec::new(),
extension_candidates: Vec::new(),
static_candidates: Vec::new(),
deref_args: check::DoDerefArgs,
check_traits: CheckTraitsOnly,
autoderef_receiver: DontAutoderefReceiver,
};
lookup_in_trait_adjusted(fcx, span, self_expr, m_name, trait_def_id,
ty::AutoDerefRef { autoderefs: 0, autoref: None },
self_ty, opt_input_types)
}
debug!("method lookup_in_trait(self_ty={}, self_expr={}, m_name={}, trait_did={})",
pub fn lookup_in_trait_adjusted<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>,
span: Span,
self_expr: Option<&'a ast::Expr>,
m_name: ast::Name,
trait_def_id: DefId,
autoderefref: ty::AutoDerefRef,
self_ty: ty::t,
opt_input_types: Option<Vec<ty::t>>)
-> Option<MethodCallee>
{
debug!("method lookup_in_trait(self_ty={}, self_expr={}, m_name={}, trait_def_id={})",
self_ty.repr(fcx.tcx()),
self_expr.repr(fcx.tcx()),
m_name.repr(fcx.tcx()),
trait_did.repr(fcx.tcx()));
trait_def_id.repr(fcx.tcx()));
lcx.push_bound_candidates(self_ty, Some(trait_did));
lcx.push_extension_candidate(trait_did);
let trait_def = ty::lookup_trait_def(fcx.tcx(), trait_def_id);
// when doing a trait search, ambiguity can't really happen except
// as part of the trait-lookup in general
match lcx.search(self_ty) {
Ok(callee) => Some(callee),
Err(_) => None
let expected_number_of_input_types = trait_def.generics.types.len(subst::TypeSpace);
let input_types = match opt_input_types {
Some(input_types) => {
assert_eq!(expected_number_of_input_types, input_types.len());
input_types
}
None => {
fcx.inh.infcx.next_ty_vars(expected_number_of_input_types)
}
};
let number_assoc_types = trait_def.generics.types.len(subst::AssocSpace);
let assoc_types = fcx.inh.infcx.next_ty_vars(number_assoc_types);
assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0);
assert!(trait_def.generics.regions.is_empty());
// Construct a trait-reference `self_ty : Trait<input_tys>`
let substs = subst::Substs::new_trait(input_types, Vec::new(), assoc_types, self_ty);
let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, substs));
// Construct an obligation
let obligation = traits::Obligation::misc(span, trait_ref.clone());
// Now we want to know if this can be matched
let mut selcx = traits::SelectionContext::new(fcx.infcx(),
&fcx.inh.param_env,
fcx);
if !selcx.evaluate_obligation_intracrate(&obligation) {
debug!("--> Cannot match obligation");
return None; // Cannot be matched, no such method resolution is possible.
}
// Trait must have a method named `m_name` and it should not have
// type parameters or early-bound regions.
let tcx = fcx.tcx();
let (method_num, method_ty) = trait_method(tcx, trait_def_id, m_name).unwrap();
assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0);
assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0);
// Substitute the trait parameters into the method type and
// instantiate late-bound regions to get the actual method type.
let ref bare_fn_ty = method_ty.fty;
let fn_sig = bare_fn_ty.sig.subst(tcx, &trait_ref.substs);
let fn_sig = replace_late_bound_regions_with_fresh_var(fcx.infcx(), span,
fn_sig.binder_id, &fn_sig);
let transformed_self_ty = fn_sig.inputs[0];
let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {
sig: fn_sig,
fn_style: bare_fn_ty.fn_style,
abi: bare_fn_ty.abi.clone(),
});
debug!("matched method fty={} obligation={}",
fty.repr(fcx.tcx()),
obligation.repr(fcx.tcx()));
// Register obligations for the parameters. This will include the
// `Self` parameter, which in turn has a bound of the main trait,
// so this also effectively registers `obligation` as well. (We
// used to register `obligation` explicitly, but that resulted in
// double error messages being reported.)
fcx.add_obligations_for_parameters(
traits::ObligationCause::misc(span),
&trait_ref.substs,
&method_ty.generics);
// FIXME(#18653) -- Try to resolve obligations, giving us more
// typing information, which can sometimes be needed to avoid
// pathological region inference failures.
vtable::select_new_fcx_obligations(fcx);
// Insert any adjustments needed (always an autoref of some mutability).
match self_expr {
None => { }
Some(self_expr) => {
debug!("inserting adjustment if needed (self-id = {}, \
base adjustment = {}, explicit self = {})",
self_expr.id, autoderefref, method_ty.explicit_self);
match method_ty.explicit_self {
ty::ByValueExplicitSelfCategory => {
// Trait method is fn(self), no transformation needed.
if !autoderefref.is_identity() {
fcx.write_adjustment(
self_expr.id,
span,
ty::AdjustDerefRef(autoderefref));
}
}
ty::ByReferenceExplicitSelfCategory(..) => {
// Trait method is fn(&self) or fn(&mut self), need an
// autoref. Pull the region etc out of the type of first argument.
match ty::get(transformed_self_ty).sty {
ty::ty_rptr(region, ty::mt { mutbl, ty: _ }) => {
let ty::AutoDerefRef { autoderefs, autoref } = autoderefref;
let autoref = autoref.map(|r| box r);
fcx.write_adjustment(
self_expr.id,
span,
ty::AdjustDerefRef(ty::AutoDerefRef {
autoderefs: autoderefs,
autoref: Some(ty::AutoPtr(region, mutbl, autoref))
}));
}
_ => {
fcx.tcx().sess.span_bug(
span,
format!(
"trait method is &self but first arg is: {}",
transformed_self_ty.repr(fcx.tcx())).as_slice());
}
}
}
_ => {
fcx.tcx().sess.span_bug(
span,
format!(
"unexpected explicit self type in operator method: {}",
method_ty.explicit_self).as_slice());
}
}
}
}
let callee = MethodCallee {
origin: MethodTypeParam(MethodParam{trait_ref: trait_ref.clone(),
method_num: method_num}),
ty: fty,
substs: trait_ref.substs.clone()
};
debug!("callee = {}", callee.repr(fcx.tcx()));
Some(callee)
}
pub fn report_error(fcx: &FnCtxt,
@ -1446,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,

View File

@ -1630,6 +1630,10 @@ pub fn write_adjustment(&self,
adj: ty::AutoAdjustment) {
debug!("write_adjustment(node_id={}, adj={})", node_id, adj);
if adj.is_identity() {
return;
}
// Careful: adjustments can imply trait obligations if we are
// casting from a concrete type to an object type. I think
// it'd probably be nicer to move the logic that creates the
@ -1813,6 +1817,38 @@ pub fn expr_ty(&self, ex: &ast::Expr) -> ty::t {
}
}
pub fn expr_ty_adjusted(&self, expr: &ast::Expr) -> ty::t {
/*!
* Fetch type of `expr` after applying adjustments that
* have been recorded in the fcx.
*/
let adjustments = self.inh.adjustments.borrow();
let adjustment = adjustments.find(&expr.id);
self.adjust_expr_ty(expr, adjustment)
}
pub fn adjust_expr_ty(&self,
expr: &ast::Expr,
adjustment: Option<&ty::AutoAdjustment>)
-> ty::t
{
/*!
* Apply `adjustment` to the type of `expr`
*/
let raw_ty = self.expr_ty(expr);
let raw_ty = self.infcx().shallow_resolve(raw_ty);
ty::adjust_ty(self.tcx(),
expr.span,
expr.id,
raw_ty,
adjustment,
|method_call| self.inh.method_map.borrow()
.find(&method_call)
.map(|method| method.ty))
}
pub fn node_ty(&self, id: ast::NodeId) -> ty::t {
match self.inh.node_types.borrow().find(&id) {
Some(&t) => t,
@ -2062,6 +2098,10 @@ pub fn autoderef<T>(fcx: &FnCtxt, sp: Span, base_ty: ty::t,
for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) {
let resolved_t = structurally_resolved_type(fcx, sp, t);
if ty::type_is_error(resolved_t) {
return (resolved_t, autoderefs, None);
}
match should_stop(resolved_t, autoderefs) {
Some(x) => return (resolved_t, autoderefs, Some(x)),
None => {}
@ -2117,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 {
""

View File

@ -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={}",

View File

@ -32,6 +32,7 @@
use syntax::codemap::{Span, Pos};
use syntax::parse::token;
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::{ast, ast_util};
use syntax::owned_slice::OwnedSlice;
@ -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)) => {

View File

@ -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.

View File

@ -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]

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -0,0 +1,91 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we still see borrowck errors of various kinds when using
// indexing and autoderef in combination.
struct Foo {
x: int,
y: int,
}
impl Index<String,int> for Foo {
fn index<'a>(&'a self, z: &String) -> &'a int {
if z.as_slice() == "x" {
&self.x
} else {
&self.y
}
}
}
impl IndexMut<String,int> for Foo {
fn index_mut<'a>(&'a mut self, z: &String) -> &'a mut int {
if z.as_slice() == "x" {
&mut self.x
} else {
&mut self.y
}
}
}
fn test1(mut f: Box<Foo>, s: String) {
let _p = &mut f[s];
let _q = &f[s]; //~ ERROR cannot borrow
}
fn test2(mut f: Box<Foo>, s: String) {
let _p = &mut f[s];
let _q = &mut f[s]; //~ ERROR cannot borrow
}
struct Bar {
foo: Foo
}
fn test3(mut f: Box<Bar>, s: String) {
let _p = &mut f.foo[s];
let _q = &mut f.foo[s]; //~ ERROR cannot borrow
}
fn test4(mut f: Box<Bar>, s: String) {
let _p = &f.foo[s];
let _q = &f.foo[s];
}
fn test5(mut f: Box<Bar>, s: String) {
let _p = &f.foo[s];
let _q = &mut f.foo[s]; //~ ERROR cannot borrow
}
fn test6(mut f: Box<Bar>, g: Foo, s: String) {
let _p = &f.foo[s];
f.foo = g; //~ ERROR cannot assign
}
fn test7(mut f: Box<Bar>, g: Bar, s: String) {
let _p = &f.foo[s];
*f = g; //~ ERROR cannot assign
}
fn test8(mut f: Box<Bar>, g: Foo, s: String) {
let _p = &mut f.foo[s];
f.foo = g; //~ ERROR cannot assign
}
fn test9(mut f: Box<Bar>, g: Bar, s: String) {
let _p = &mut f.foo[s];
*f = g; //~ ERROR cannot assign
}
fn main() {
}

View File

@ -14,7 +14,7 @@ fn main() {
let x = [1,2];
let y = match x {
[] => None,
//~^ ERROR types: expected `[_#0i, ..2]`, found `[_#7, ..0]`
//~^ ERROR types: expected `[_#0i, ..2]`, found `[_#7t, ..0]`
// (expected array of 2 elements, found array of 0 elements)
[a,_] => Some(a)
};

View File

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

View File

@ -11,7 +11,7 @@
#![feature(overloaded_calls)]
fn f<'r>(p: &'r mut fn(p: &mut ())) {
p(()) //~ ERROR mismatched types: expected `&mut ()`, found `()`
(*p)(()) //~ ERROR mismatched types: expected `&mut ()`, found `()`
}
fn main() {}

View File

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

View File

@ -15,5 +15,5 @@
fn main() {
let x: &[int] = &[1, 2, 3, 4, 5];
// Can't mutably slice an immutable slice
let y = x[mut 2..4]; //~ ERROR cannot take a mutable slice of a value with type `&[int]`
let y = x[mut 2..4]; //~ ERROR cannot borrow
}

View File

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

View File

@ -0,0 +1,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});
}

View File

@ -0,0 +1,79 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test overloaded indexing combined with autoderef.
struct Foo {
x: int,
y: int,
}
impl Index<int,int> for Foo {
fn index(&self, z: &int) -> &int {
if *z == 0 {
&self.x
} else {
&self.y
}
}
}
impl IndexMut<int,int> for Foo {
fn index_mut(&mut self, z: &int) -> &mut int {
if *z == 0 {
&mut self.x
} else {
&mut self.y
}
}
}
trait Int {
fn get(self) -> int;
fn get_from_ref(&self) -> int;
fn inc(&mut self);
}
impl Int for int {
fn get(self) -> int { self }
fn get_from_ref(&self) -> int { *self }
fn inc(&mut self) { *self += 1; }
}
fn main() {
let mut f = box Foo {
x: 1,
y: 2,
};
assert_eq!(f[1], 2);
f[0] = 3;
assert_eq!(f[0], 3);
// Test explicit IndexMut where `f` must be autoderef:
{
let p = &mut f[1];
*p = 4;
}
// Test explicit Index where `f` must be autoderef:
{
let p = &f[1];
assert_eq!(*p, 4);
}
// Test calling methods with `&mut self`, `self, and `&self` receivers:
f[1].inc();
assert_eq!(f[1].get(), 5);
assert_eq!(f[1].get_from_ref(), 5);
}

View File

@ -0,0 +1,52 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test using overloaded indexing when the "map" is stored in a
// field. This caused problems at some point.
struct Foo {
x: int,
y: int,
}
struct Bar {
foo: Foo
}
impl Index<int,int> for Foo {
fn index(&self, z: &int) -> &int {
if *z == 0 {
&self.x
} else {
&self.y
}
}
}
trait Int {
fn get(self) -> int;
fn get_from_ref(&self) -> int;
fn inc(&mut self);
}
impl Int for int {
fn get(self) -> int { self }
fn get_from_ref(&self) -> int { *self }
fn inc(&mut self) { *self += 1; }
}
fn main() {
let f = Bar { foo: Foo {
x: 1,
y: 2,
} };
assert_eq!(f.foo[1].get(), 2);
}

View File

@ -33,6 +33,18 @@ fn index_mut(&mut self, z: &int) -> &mut int {
}
}
trait Int {
fn get(self) -> int;
fn get_from_ref(&self) -> int;
fn inc(&mut self);
}
impl Int for int {
fn get(self) -> int { self }
fn get_from_ref(&self) -> int { *self }
fn inc(&mut self) { *self += 1; }
}
fn main() {
let mut f = Foo {
x: 1,
@ -49,5 +61,10 @@ fn main() {
let p = &f[1];
assert_eq!(*p, 4);
}
// Test calling methods with `&mut self`, `self, and `&self` receivers:
f[1].inc();
assert_eq!(f[1].get(), 5);
assert_eq!(f[1].get_from_ref(), 5);
}