From 4af52eee59ff25a7f636798bdbc3f1bec985828f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 31 Oct 2014 05:40:15 -0400 Subject: [PATCH 1/6] Repair various cases where values of distinct types were being operated upon (e.g., `&int` added to `int`). --- src/libarena/lib.rs | 2 +- src/libcollections/vec.rs | 2 +- src/libcore/option.rs | 4 +- src/libcore/result.rs | 4 +- src/libcoretest/iter.rs | 8 ++-- src/libstd/bitflags.rs | 4 +- src/libstd/time/duration.rs | 2 +- src/libtime/lib.rs | 2 +- src/test/bench/shootout-meteor.rs | 2 +- src/test/run-pass/operator-multidispatch.rs | 41 +++++++++++++++++++++ 10 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 src/test/run-pass/operator-multidispatch.rs diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 415b12c7d0523..3e93430eb6937 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -132,7 +132,7 @@ impl Drop for Arena { #[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 diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index f9f4ae534c16e..b29d7d33782c2 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -919,7 +919,7 @@ impl Vec { /// /// ``` /// 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"] diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 522eb8336376e..b787de4423aac 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -787,8 +787,8 @@ impl> FromIterator> for Option { /// use std::uint; /// /// let v = vec!(1u, 2u); - /// let res: Option> = v.iter().map(|x: &uint| - /// if *x == uint::MAX { None } + /// let res: Option> = v.iter().map(|&x: &uint| + /// if x == uint::MAX { None } /// else { Some(x + 1) } /// ).collect(); /// assert!(res == Some(vec!(2u, 3u))); diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 5b75e98baefe2..2ad5521bb76cd 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -894,8 +894,8 @@ impl> FromIterator> for Result { /// use std::uint; /// /// let v = vec!(1u, 2u); - /// let res: Result, &'static str> = v.iter().map(|x: &uint| - /// if *x == uint::MAX { Err("Overflow!") } + /// let res: Result, &'static str> = v.iter().map(|&x: &uint| + /// if x == uint::MAX { Err("Overflow!") } /// else { Ok(x + 1) } /// ).collect(); /// assert!(res == Ok(vec!(2u, 3u))); diff --git a/src/libcoretest/iter.rs b/src/libcoretest/iter.rs index 5d333d48e96d4..aeab18ca05e30 100644 --- a/src/libcoretest/iter.rs +++ b/src/libcoretest/iter.rs @@ -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] diff --git a/src/libstd/bitflags.rs b/src/libstd/bitflags.rs index 97a1f68606f54..d8023dd3e4e3b 100644 --- a/src/libstd/bitflags.rs +++ b/src/libstd/bitflags.rs @@ -177,13 +177,13 @@ macro_rules! bitflags { /// 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. diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 102f002855762..c3adae8cff839 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -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; diff --git a/src/libtime/lib.rs b/src/libtime/lib.rs index 590d0bfdcabc4..ac3574f5a03ee 100644 --- a/src/libtime/lib.rs +++ b/src/libtime/lib.rs @@ -96,7 +96,7 @@ impl Add for 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; diff --git a/src/test/bench/shootout-meteor.rs b/src/test/bench/shootout-meteor.rs index 6e80c07a1a2aa..47e1969172d2f 100644 --- a/src/test/bench/shootout-meteor.rs +++ b/src/test/bench/shootout-meteor.rs @@ -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. diff --git a/src/test/run-pass/operator-multidispatch.rs b/src/test/run-pass/operator-multidispatch.rs new file mode 100644 index 0000000000000..fc79e4f0edb36 --- /dev/null +++ b/src/test/run-pass/operator-multidispatch.rs @@ -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 or the MIT license +// , 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 for Point { + fn add(&self, other: &Point) -> Point { + Point {x: self.x + (*other).x, y: self.y + (*other).y} + } +} + +impl ops::Add 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}); +} From 33ef78fa8bbe9b8d05ba0da607d4da5e31475a95 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 31 Oct 2014 05:41:25 -0400 Subject: [PATCH 2/6] Add impls of the comparison operators for fixed-length arrays of lengths 0...32 and repair various cases where slices and fixed-length arrays were being compared. --- src/libcollections/slice.rs | 14 ++--- src/libcollections/vec.rs | 6 +-- src/libcore/array.rs | 87 ++++++++++++++++++++++++++++++ src/libcore/lib.rs | 4 ++ src/libstd/collections/hash/set.rs | 2 +- 5 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 src/libcore/array.rs diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index 107dc3e5c28b7..eb4ff345b5133 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -1598,15 +1598,15 @@ mod tests { #[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 @@ mod tests { 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 @@ mod tests { { 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; diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index b29d7d33782c2..40e7c949972b5 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1800,7 +1800,7 @@ mod tests { 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 @@ mod tests { { 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 @@ mod tests { #[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]); } diff --git a/src/libcore/array.rs b/src/libcore/array.rs new file mode 100644 index 0000000000000..999574b4d7d52 --- /dev/null +++ b/src/libcore/array.rs @@ -0,0 +1,87 @@ +// 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 or the MIT license +// , 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. + */ + +#![doc(primitive = "tuple")] +#![stable] + +#[unstable = "this is just a documentation module and should not be part \ + of the public api"] +pub use unit; + +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 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 Eq for [T, ..$N] { } + + #[unstable = "waiting for PartialOrd to stabilize"] + impl PartialOrd for [T, ..$N] { + #[inline] + fn partial_cmp(&self, other: &[T, ..$N]) -> Option { + 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 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 +} + diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 7c5c54c6d8abc..d9a0c398605e1 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -126,6 +126,10 @@ pub mod tuple; 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; diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index b9758e11bc784..688036d22dd6e 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -792,7 +792,7 @@ mod test_set { }; let v = hs.into_iter().collect::>(); - assert!(['a', 'b'] == v.as_slice() || ['b', 'a'] == v.as_slice()); + assert!(['a', 'b'][] == v.as_slice() || ['b', 'a'][] == v.as_slice()); } #[test] From 0b5bc3314fa9bf768163828a8e38bac588936fe9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 31 Oct 2014 05:44:10 -0400 Subject: [PATCH 3/6] Implement new operator dispatch semantics. Key points are: 1. `a + b` maps directly to `Add`, where `A` and `B` are the types of `a` and `b`. 2. Indexing and slicing autoderefs consistently. --- src/librustc/middle/ty.rs | 15 + src/librustc/middle/typeck/check/method.rs | 291 +++++++++-- src/librustc/middle/typeck/check/mod.rs | 486 ++++++++++++------ src/librustc/util/ppaux.rs | 7 + .../borrowck-overloaded-index-autoderef.rs | 91 ++++ src/test/compile-fail/issue-13482-2.rs | 2 +- src/test/compile-fail/issue-15207.rs | 1 - src/test/compile-fail/issue-17033.rs | 2 +- src/test/compile-fail/issue-2149.rs | 1 - src/test/compile-fail/slice-mut-2.rs | 2 +- .../type-params-in-different-spaces-1.rs | 2 +- ...ator.rs => overloaded-index-assoc-list.rs} | 0 .../run-pass/overloaded-index-autoderef.rs | 79 +++ .../run-pass/overloaded-index-in-field.rs | 52 ++ src/test/run-pass/overloaded-index.rs | 17 + 15 files changed, 827 insertions(+), 221 deletions(-) create mode 100644 src/test/compile-fail/borrowck-overloaded-index-autoderef.rs rename src/test/run-pass/{overload-index-operator.rs => overloaded-index-assoc-list.rs} (100%) create mode 100644 src/test/run-pass/overloaded-index-autoderef.rs create mode 100644 src/test/run-pass/overloaded-index-in-field.rs diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index bdb9ef8e71042..23a5289593541 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5566,3 +5566,18 @@ pub fn with_freevars(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() + } +} diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 4334cf7db7a23..abffa857a089a 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -96,6 +96,7 @@ use middle::typeck::{MethodOrigin, MethodParam, MethodTypeParam}; 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::(...)`: - 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>) -> Option { - 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>) + -> Option +{ + 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())); + + let trait_def = ty::lookup_trait_def(fcx.tcx(), trait_def_id); + + 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); - lcx.push_bound_candidates(self_ty, Some(trait_did)); - lcx.push_extension_candidate(trait_did); + assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0); + assert!(trait_def.generics.regions.is_empty()); - // 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 + // Construct a trait-reference `self_ty : Trait` + let substs = subst::Substs::new_trait(input_types, Vec::new(), assoc_types, self_ty); + let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, substs)); + + // Construct an obligation + let obligation = traits::Obligation::misc(span, trait_ref.clone()); + + // Now we want to know if this can be matched + let mut selcx = traits::SelectionContext::new(fcx.infcx(), + &fcx.inh.param_env, + fcx); + if !selcx.evaluate_obligation_intracrate(&obligation) { + debug!("--> Cannot match obligation"); + return None; // Cannot be matched, no such method resolution is possible. + } + + // Trait must have a method named `m_name` and it should not have + // type parameters or early-bound regions. + let tcx = fcx.tcx(); + let (method_num, method_ty) = trait_method(tcx, trait_def_id, m_name).unwrap(); + assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0); + assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0); + + // Substitute the trait parameters into the method type and + // instantiate late-bound regions to get the actual method type. + let ref bare_fn_ty = method_ty.fty; + let fn_sig = bare_fn_ty.sig.subst(tcx, &trait_ref.substs); + let fn_sig = replace_late_bound_regions_with_fresh_var(fcx.infcx(), span, + fn_sig.binder_id, &fn_sig); + let transformed_self_ty = fn_sig.inputs[0]; + let fty = ty::mk_bare_fn(tcx, ty::BareFnTy { + sig: fn_sig, + fn_style: bare_fn_ty.fn_style, + abi: bare_fn_ty.abi.clone(), + }); + + debug!("matched method fty={} obligation={}", + fty.repr(fcx.tcx()), + obligation.repr(fcx.tcx())); + + // Register obligations for the parameters. This will include the + // `Self` parameter, which in turn has a bound of the main trait, + // so this also effectively registers `obligation` as well. (We + // used to register `obligation` explicitly, but that resulted in + // double error messages being reported.) + fcx.add_obligations_for_parameters( + traits::ObligationCause::misc(span), + &trait_ref.substs, + &method_ty.generics); + + // FIXME(#18653) -- Try to resolve obligations, giving us more + // typing information, which can sometimes be needed to avoid + // pathological region inference failures. + vtable::select_new_fcx_obligations(fcx); + + // Insert any adjustments needed (always an autoref of some mutability). + match self_expr { + None => { } + + Some(self_expr) => { + debug!("inserting adjustment if needed (self-id = {}, \ + base adjustment = {}, explicit self = {})", + self_expr.id, autoderefref, method_ty.explicit_self); + + match method_ty.explicit_self { + ty::ByValueExplicitSelfCategory => { + // Trait method is fn(self), no transformation needed. + if !autoderefref.is_identity() { + fcx.write_adjustment( + self_expr.id, + span, + ty::AdjustDerefRef(autoderefref)); + } + } + + ty::ByReferenceExplicitSelfCategory(..) => { + // Trait method is fn(&self) or fn(&mut self), need an + // autoref. Pull the region etc out of the type of first argument. + match ty::get(transformed_self_ty).sty { + ty::ty_rptr(region, ty::mt { mutbl, ty: _ }) => { + let ty::AutoDerefRef { autoderefs, autoref } = autoderefref; + let autoref = autoref.map(|r| box r); + fcx.write_adjustment( + self_expr.id, + span, + ty::AdjustDerefRef(ty::AutoDerefRef { + autoderefs: autoderefs, + autoref: Some(ty::AutoPtr(region, mutbl, autoref)) + })); + } + + _ => { + fcx.tcx().sess.span_bug( + span, + format!( + "trait method is &self but first arg is: {}", + transformed_self_ty.repr(fcx.tcx())).as_slice()); + } + } + } + + _ => { + fcx.tcx().sess.span_bug( + span, + format!( + "unexpected explicit self type in operator method: {}", + method_ty.explicit_self).as_slice()); + } + } + } } + + let callee = MethodCallee { + origin: MethodTypeParam(MethodParam{trait_ref: trait_ref.clone(), + method_num: method_num}), + ty: fty, + substs: trait_ref.substs.clone() + }; + + debug!("callee = {}", callee.repr(fcx.tcx())); + + Some(callee) } pub fn report_error(fcx: &FnCtxt, @@ -1446,9 +1579,8 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } } - fn fixup_derefs_on_method_receiver_if_necessary( - &self, - method_callee: &MethodCallee) { + fn fixup_derefs_on_method_receiver_if_necessary(&self, + method_callee: &MethodCallee) { let sig = match ty::get(method_callee.ty).sty { ty::ty_bare_fn(ref f) => f.sig.clone(), ty::ty_closure(ref f) => f.sig.clone(), @@ -1485,6 +1617,9 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } } + 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 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { Some(_) | None => 0, }; + debug!("fixup_derefs_on_method_receiver_if_necessary: i={} expr={} autoderef_count={}", + i, expr.repr(self.tcx()), autoderef_count); + if autoderef_count > 0 { check::autoderef(self.fcx, expr.span, @@ -1518,24 +1656,59 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { // Don't retry the first one or we might infinite loop! if i != 0 { match expr.node { - ast::ExprIndex(ref base_expr, ref index_expr) => { - check::try_overloaded_index( - self.fcx, - Some(MethodCall::expr(expr.id)), - *expr, + ast::ExprIndex(ref base_expr, _) => { + let mut base_adjustment = + match self.fcx.inh.adjustments.borrow().find(&base_expr.id) { + Some(&ty::AdjustDerefRef(ref adr)) => (*adr).clone(), + None => ty::AutoDerefRef { autoderefs: 0, autoref: None }, + Some(_) => { + self.tcx().sess.span_bug( + base_expr.span, + "unexpected adjustment type"); + } + }; + + // If this is an overloaded index, the + // adjustment will include an extra layer of + // autoref because the method is an &self/&mut + // self method. We have to peel it off to get + // the raw adjustment that `try_index_step` + // expects. This is annoying and horrible. We + // ought to recode this routine so it doesn't + // (ab)use the normal type checking paths. + base_adjustment.autoref = match base_adjustment.autoref { + None => { None } + Some(AutoPtr(_, _, None)) => { None } + Some(AutoPtr(_, _, Some(box r))) => { Some(r) } + Some(_) => { + self.tcx().sess.span_bug( + base_expr.span, + "unexpected adjustment autoref"); + } + }; + + let adjusted_base_ty = + self.fcx.adjust_expr_ty( &**base_expr, - self.fcx.expr_ty(&**base_expr), - index_expr, - PreferMutLvalue); + Some(&ty::AdjustDerefRef(base_adjustment.clone()))); + + check::try_index_step( + self.fcx, + MethodCall::expr(expr.id), + *expr, + &**base_expr, + adjusted_base_ty, + base_adjustment, + PreferMutLvalue); } ast::ExprUnary(ast::UnDeref, ref base_expr) => { check::try_overloaded_deref( - self.fcx, - expr.span, - Some(MethodCall::expr(expr.id)), - Some(&**base_expr), - self.fcx.expr_ty(&**base_expr), - PreferMutLvalue); + self.fcx, + expr.span, + Some(MethodCall::expr(expr.id)), + Some(&**base_expr), + self.fcx.expr_ty(&**base_expr), + PreferMutLvalue); } _ => {} } @@ -1623,15 +1796,25 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { fn replace_late_bound_regions_with_fresh_var(&self, binder_id: ast::NodeId, value: &T) -> T where T : TypeFoldable + Repr { - let (_, value) = replace_late_bound_regions( - self.fcx.tcx(), - binder_id, - value, - |br| self.fcx.infcx().next_region_var(infer::LateBoundRegion(self.span, br))); - value + replace_late_bound_regions_with_fresh_var(self.fcx.infcx(), self.span, binder_id, value) } } +fn replace_late_bound_regions_with_fresh_var(infcx: &infer::InferCtxt, + span: Span, + binder_id: ast::NodeId, + value: &T) + -> T + where T : TypeFoldable + Repr +{ + let (_, value) = replace_late_bound_regions( + infcx.tcx, + binder_id, + value, + |br| infcx.next_region_var(infer::LateBoundRegion(span, br))); + value +} + fn trait_method(tcx: &ty::ctxt, trait_def_id: ast::DefId, method_name: ast::Name) diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 0c7de4b3ac4e0..0e3a77ba9639c 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1630,6 +1630,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + pub fn expr_ty_adjusted(&self, expr: &ast::Expr) -> ty::t { + /*! + * Fetch type of `expr` after applying adjustments that + * have been recorded in the fcx. + */ + + let adjustments = self.inh.adjustments.borrow(); + let adjustment = adjustments.find(&expr.id); + self.adjust_expr_ty(expr, adjustment) + } + + pub fn adjust_expr_ty(&self, + expr: &ast::Expr, + adjustment: Option<&ty::AutoAdjustment>) + -> ty::t + { + /*! + * Apply `adjustment` to the type of `expr` + */ + + let raw_ty = self.expr_ty(expr); + let raw_ty = self.infcx().shallow_resolve(raw_ty); + ty::adjust_ty(self.tcx(), + expr.span, + expr.id, + raw_ty, + adjustment, + |method_call| self.inh.method_map.borrow() + .find(&method_call) + .map(|method| method.ty)) + } + pub fn node_ty(&self, id: ast::NodeId) -> ty::t { match self.inh.node_types.borrow().find(&id) { Some(&t) => t, @@ -2062,6 +2098,10 @@ pub fn autoderef(fcx: &FnCtxt, sp: Span, base_ty: ty::t, for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) { let resolved_t = structurally_resolved_type(fcx, sp, t); + if ty::type_is_error(resolved_t) { + return (resolved_t, autoderefs, None); + } + match should_stop(resolved_t, autoderefs) { Some(x) => return (resolved_t, autoderefs, Some(x)), None => {} @@ -2117,17 +2157,17 @@ fn try_overloaded_call<'a>(fcx: &FnCtxt, None => continue, Some(function_trait) => function_trait, }; - let method_callee = match method::lookup_in_trait( - fcx, - call_expression.span, - Some(&*callee), - method_name, - function_trait, - callee_type, - []) { - None => continue, - Some(method_callee) => method_callee, - }; + let method_callee = + match method::lookup_in_trait(fcx, + call_expression.span, + Some(&*callee), + method_name, + function_trait, + callee_type, + None) { + None => continue, + Some(method_callee) => method_callee, + }; let method_call = MethodCall::expr(call_expression.id); let output_type = check_method_argument_types(fcx, call_expression.span, @@ -2159,13 +2199,14 @@ fn try_overloaded_deref(fcx: &FnCtxt, base_expr: Option<&ast::Expr>, base_ty: ty::t, lvalue_pref: LvaluePreference) - -> Option { + -> Option +{ // 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) -> ty::t { - match method { - &Some(ref method) => method.ty, - &None => ty::mk_err() - } -} +fn make_overloaded_lvalue_return_type(fcx: &FnCtxt, + method_call: Option, + method: Option) + -> Option +{ + /*! + * For the overloaded lvalue expressions (`*x`, `x[3]`), the trait + * returns a type of `&T`, but the actual type we assign to the + * *expression* is `T`. So this function just peels off the return + * type by one layer to yield `T`. It also inserts the + * `method-callee` into the method map. + */ -fn make_return_type(fcx: &FnCtxt, - method_call: Option, - method: Option) - -> Option { 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(fcx: &FnCtxt, + base_expr: &ast::Expr, + base_ty: ty::t, + lvalue_pref: LvaluePreference, + step: |ty::t, ty::AutoDerefRef| -> Option) + -> Option +{ + 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, + method_call: MethodCall, expr: &ast::Expr, base_expr: &ast::Expr, base_ty: ty::t, start_expr: &Option>, end_expr: &Option>, - mutbl: &ast::Mutability) - -> Option { - let method = if mutbl == &ast::MutMutable { + mutbl: ast::Mutability) + -> Option // 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>, + end_expr: &Option>) + -> Option // result type is type of method being called +{ + /*! + * Checks for a `Slice` (or `SliceMut`) impl at the relevant level + * of autoderef. If it finds one, installs method info and returns + * type of method (else None). + */ + + let method = if mutbl == ast::MutMutable { // Try `SliceMut` first, if preferred. match fcx.tcx().lang_items.slice_mut_trait() { Some(trait_did) => { @@ -2235,13 +2378,14 @@ fn try_overloaded_slice(fcx: &FnCtxt, (&None, &None) => "as_mut_slice_", }; - method::lookup_in_trait(fcx, - expr.span, - Some(&*base_expr), - token::intern(method_name), - trait_did, - base_ty, - []) + method::lookup_in_trait_adjusted(fcx, + expr.span, + Some(&*base_expr), + token::intern(method_name), + trait_did, + autoderefref, + base_ty, + None) } _ => None, } @@ -2258,74 +2402,73 @@ fn try_overloaded_slice(fcx: &FnCtxt, (&None, &None) => "as_slice_", }; - method::lookup_in_trait(fcx, - expr.span, - Some(&*base_expr), - token::intern(method_name), - trait_did, - base_ty, - []) + method::lookup_in_trait_adjusted(fcx, + expr.span, + Some(&*base_expr), + token::intern(method_name), + trait_did, + autoderefref, + base_ty, + None) } _ => None, } }; + // 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 + }) +} - // 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)); +fn try_index_step(fcx: &FnCtxt, + method_call: MethodCall, + expr: &ast::Expr, + base_expr: &ast::Expr, + adjusted_ty: ty::t, + adjustment: ty::AutoDerefRef, + lvalue_pref: LvaluePreference) + -> Option<(/*index type*/ ty::t, /*element type*/ ty::t)> +{ + /*! + * To type-check `base_expr[index_expr]`, we progressively autoderef (and otherwise adjust) + * `base_expr`, looking for a type which either supports builtin indexing or overloaded + * indexing. This loop implements one step in that search; the autoderef loop is implemented + * by `autoderef_for_index`. + */ - check_method_argument_types(fcx, - expr.span, - method_type, - expr, - args.as_slice(), - DoDerefArgs, - DontTupleArguments); + 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); - 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 - } + // 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 => None, + + None => { } } -} -fn try_overloaded_index(fcx: &FnCtxt, - method_call: Option, - expr: &ast::Expr, - base_expr: &ast::Expr, - base_ty: ty::t, - index_expr: &P, - lvalue_pref: LvaluePreference) - -> Option { + let input_ty = fcx.infcx().next_ty_var(); + let return_ty = fcx.infcx().next_ty_var(); + // Try `IndexMut` first, if preferred. let method = match (lvalue_pref, fcx.tcx().lang_items.index_mut_trait()) { (PreferMutLvalue, Some(trait_did)) => { - method::lookup_in_trait(fcx, - expr.span, - Some(&*base_expr), - token::intern("index_mut"), - trait_did, - base_ty, - []) + method::lookup_in_trait_adjusted(fcx, + expr.span, + Some(&*base_expr), + token::intern("index_mut"), + trait_did, + adjustment.clone(), + adjusted_ty, + Some(vec![input_ty, return_ty])) } _ => None, }; @@ -2333,29 +2476,25 @@ fn try_overloaded_index(fcx: &FnCtxt, // Otherwise, fall back to `Index`. let method = match (method, fcx.tcx().lang_items.index_trait()) { (None, Some(trait_did)) => { - method::lookup_in_trait(fcx, - expr.span, - Some(&*base_expr), - token::intern("index"), - trait_did, - base_ty, - []) + method::lookup_in_trait_adjusted(fcx, + expr.span, + Some(&*base_expr), + token::intern("index"), + trait_did, + adjustment, + adjusted_ty, + Some(vec![input_ty, return_ty])) } (method, _) => method, }; - // Regardless of whether the lookup succeeds, check the method arguments - // so that we have *some* type for each argument. - let method_type = get_method_ty(&method); - check_method_argument_types(fcx, - expr.span, - method_type, - expr, - &[index_expr], - DoDerefArgs, - DontTupleArguments); - - make_return_type(fcx, method_call, method) + // If some lookup succeeds, write callee into table and extract index/element + // type from the method signature. + // If some lookup succeeded, install method in table + method.map(|method| { + make_overloaded_lvalue_return_type(fcx, Some(method_call), Some(method)); + (input_ty, return_ty) + }) } /// Given the head of a `for` expression, looks up the `next` method in the @@ -2383,7 +2522,7 @@ fn lookup_method_for_for_loop(fcx: &FnCtxt, token::intern("next"), trait_did, expr_type, - []); + None); // Regardless of whether the lookup succeeds, check the method arguments // so that we have *some* type for each argument. @@ -2427,10 +2566,15 @@ fn lookup_method_for_for_loop(fcx: &FnCtxt, if !substs.types.is_empty_in(subst::TypeSpace) => { *substs.types.get(subst::TypeSpace, 0) } + ty::ty_err => { + ty::mk_err() + } _ => { fcx.tcx().sess.span_err(iterator_expr.span, - "`next` method of the `Iterator` \ - trait has an unexpected type"); + format!("`next` method of the `Iterator` \ + trait has an unexpected type `{}`", + fcx.infcx().ty_to_string(return_type)) + .as_slice()); ty::mk_err() } } @@ -2457,7 +2601,7 @@ fn check_method_argument_types<'a>(fcx: &FnCtxt, deref_args, false, tuple_arguments); - ty::FnConverging(method_fn_ty) + ty::FnConverging(ty::mk_err()) } else { match ty::get(method_fn_ty).sty { ty::ty_bare_fn(ref fty) => { @@ -3060,8 +3204,36 @@ fn check_expr_with_unifier(fcx: &FnCtxt, unbound_method: ||) -> ty::t { let method = match trait_did { Some(trait_did) => { - method::lookup_in_trait(fcx, op_ex.span, Some(lhs), opname, - trait_did, lhs_ty, &[]) + // We do eager coercions to make using operators + // more ergonomic: + // + // - If the input is of type &'a T (resp. &'a mut T), + // then reborrow it to &'b T (resp. &'b mut T) where + // 'b <= 'a. This makes things like `x == y`, where + // `x` and `y` are both region pointers, work. We + // could also solve this with variance or different + // traits that don't force left and right to have same + // type. + let (adj_ty, adjustment) = match ty::get(lhs_ty).sty { + ty::ty_rptr(r_in, mt) => { + let r_adj = fcx.infcx().next_region_var(infer::Autoref(lhs.span)); + fcx.mk_subr(infer::Reborrow(lhs.span), r_adj, r_in); + let adjusted_ty = ty::mk_rptr(fcx.tcx(), r_adj, mt); + let autoptr = ty::AutoPtr(r_adj, mt.mutbl, None); + let adjustment = ty::AutoDerefRef { autoderefs: 1, autoref: Some(autoptr) }; + (adjusted_ty, adjustment) + } + _ => { + (lhs_ty, ty::AutoDerefRef { autoderefs: 0, autoref: None }) + } + }; + + debug!("adjusted_ty={} adjustment={}", + adj_ty.repr(fcx.tcx()), + adjustment); + + method::lookup_in_trait_adjusted(fcx, op_ex.span, Some(lhs), opname, + trait_did, adjustment, adj_ty, None) } None => None }; @@ -4338,55 +4510,47 @@ fn check_expr_with_unifier(fcx: &FnCtxt, ast::ExprIndex(ref base, ref idx) => { check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref); check_expr(fcx, &**idx); - let raw_base_t = fcx.expr_ty(&**base); + let base_t = fcx.expr_ty(&**base); let idx_t = fcx.expr_ty(&**idx); - if ty::type_is_error(raw_base_t) { - fcx.write_ty(id, raw_base_t); + if ty::type_is_error(base_t) { + fcx.write_ty(id, base_t); } else if ty::type_is_error(idx_t) { fcx.write_ty(id, idx_t); } else { - let (_, autoderefs, field_ty) = - autoderef(fcx, expr.span, raw_base_t, Some(base.id), - lvalue_pref, |base_t, _| ty::index(base_t)); - match field_ty { - Some(ty) => { - check_expr_has_type(fcx, &**idx, ty::mk_uint()); - fcx.write_ty(id, ty); - fcx.write_autoderef_adjustment(base.id, base.span, autoderefs); + let base_t = structurally_resolved_type(fcx, expr.span, base_t); + + let result = + autoderef_for_index(fcx, &**base, base_t, lvalue_pref, |adj_ty, adj| { + try_index_step(fcx, + MethodCall::expr(expr.id), + expr, + &**base, + adj_ty, + adj, + lvalue_pref) + }); + + match result { + Some((index_ty, element_ty)) => { + check_expr_has_type(fcx, &**idx, index_ty); + fcx.write_ty(id, element_ty); } _ => { - // This is an overloaded method. - let base_t = structurally_resolved_type(fcx, - expr.span, - raw_base_t); - let method_call = MethodCall::expr(expr.id); - match try_overloaded_index(fcx, - Some(method_call), - expr, - &**base, - base_t, - idx, - lvalue_pref) { - Some(mt) => fcx.write_ty(id, mt.ty), - None => { - fcx.type_error_message(expr.span, - |actual| { - format!("cannot \ - index a \ - value of \ - type `{}`", - actual) - }, - base_t, - None); - fcx.write_ty(id, ty::mk_err()) - } - } + check_expr_has_type(fcx, &**idx, ty::mk_err()); + fcx.type_error_message( + expr.span, + |actual| { + format!("cannot index a value of type `{}`", + actual) + }, + base_t, + None); + fcx.write_ty(id, ty::mk_err()) } } } } - ast::ExprSlice(ref base, ref start, ref end, ref mutbl) => { + ast::ExprSlice(ref base, ref start, ref end, mutbl) => { check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref); let raw_base_t = fcx.expr_ty(&**base); @@ -4415,19 +4579,19 @@ fn check_expr_with_unifier(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 { "" diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index fb80b9f30f5df..91e563c75e475 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -32,6 +32,7 @@ use syntax::ast_map; use syntax::codemap::{Span, Pos}; use syntax::parse::token; use syntax::print::pprust; +use syntax::ptr::P; use syntax::{ast, ast_util}; use syntax::owned_slice::OwnedSlice; @@ -561,6 +562,12 @@ impl Repr for Option { } } +impl Repr for P { + fn repr(&self, tcx: &ctxt) -> String { + (*self).repr(tcx) + } +} + impl Repr for Result { fn repr(&self, tcx: &ctxt) -> String { match self { diff --git a/src/test/compile-fail/borrowck-overloaded-index-autoderef.rs b/src/test/compile-fail/borrowck-overloaded-index-autoderef.rs new file mode 100644 index 0000000000000..2253d7512c053 --- /dev/null +++ b/src/test/compile-fail/borrowck-overloaded-index-autoderef.rs @@ -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 or the MIT license +// , 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 for Foo { + fn index<'a>(&'a self, z: &String) -> &'a int { + if z.as_slice() == "x" { + &self.x + } else { + &self.y + } + } +} + +impl IndexMut 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, s: String) { + let _p = &mut f[s]; + let _q = &f[s]; //~ ERROR cannot borrow +} + +fn test2(mut f: Box, s: String) { + let _p = &mut f[s]; + let _q = &mut f[s]; //~ ERROR cannot borrow +} + +struct Bar { + foo: Foo +} + +fn test3(mut f: Box, s: String) { + let _p = &mut f.foo[s]; + let _q = &mut f.foo[s]; //~ ERROR cannot borrow +} + +fn test4(mut f: Box, s: String) { + let _p = &f.foo[s]; + let _q = &f.foo[s]; +} + +fn test5(mut f: Box, s: String) { + let _p = &f.foo[s]; + let _q = &mut f.foo[s]; //~ ERROR cannot borrow +} + +fn test6(mut f: Box, g: Foo, s: String) { + let _p = &f.foo[s]; + f.foo = g; //~ ERROR cannot assign +} + +fn test7(mut f: Box, g: Bar, s: String) { + let _p = &f.foo[s]; + *f = g; //~ ERROR cannot assign +} + +fn test8(mut f: Box, g: Foo, s: String) { + let _p = &mut f.foo[s]; + f.foo = g; //~ ERROR cannot assign +} + +fn test9(mut f: Box, g: Bar, s: String) { + let _p = &mut f.foo[s]; + *f = g; //~ ERROR cannot assign +} + +fn main() { +} + + diff --git a/src/test/compile-fail/issue-13482-2.rs b/src/test/compile-fail/issue-13482-2.rs index 6746b90e32d2e..4ec8c2b1b7ea5 100644 --- a/src/test/compile-fail/issue-13482-2.rs +++ b/src/test/compile-fail/issue-13482-2.rs @@ -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) }; diff --git a/src/test/compile-fail/issue-15207.rs b/src/test/compile-fail/issue-15207.rs index 37fcabef0ed9b..61877775269d1 100644 --- a/src/test/compile-fail/issue-15207.rs +++ b/src/test/compile-fail/issue-15207.rs @@ -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 ; } } diff --git a/src/test/compile-fail/issue-17033.rs b/src/test/compile-fail/issue-17033.rs index 7590546d40a7a..5048a9aa919c6 100644 --- a/src/test/compile-fail/issue-17033.rs +++ b/src/test/compile-fail/issue-17033.rs @@ -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() {} diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs index e64d674b7c862..1150f40db762f 100644 --- a/src/test/compile-fail/issue-2149.rs +++ b/src/test/compile-fail/issue-2149.rs @@ -18,7 +18,6 @@ impl vec_monad for Vec { 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() { diff --git a/src/test/compile-fail/slice-mut-2.rs b/src/test/compile-fail/slice-mut-2.rs index 09019448a6786..6778ed88ff70b 100644 --- a/src/test/compile-fail/slice-mut-2.rs +++ b/src/test/compile-fail/slice-mut-2.rs @@ -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 } diff --git a/src/test/compile-fail/type-params-in-different-spaces-1.rs b/src/test/compile-fail/type-params-in-different-spaces-1.rs index 6e32e6e48354c..c87e85417582d 100644 --- a/src/test/compile-fail/type-params-in-different-spaces-1.rs +++ b/src/test/compile-fail/type-params-in-different-spaces-1.rs @@ -12,7 +12,7 @@ use std::num::Num; trait BrokenAdd: Num { fn broken_add(&self, rhs: T) -> Self { - *self + rhs //~ ERROR mismatched types + *self + rhs //~ ERROR expected `Self`, found `T` } } diff --git a/src/test/run-pass/overload-index-operator.rs b/src/test/run-pass/overloaded-index-assoc-list.rs similarity index 100% rename from src/test/run-pass/overload-index-operator.rs rename to src/test/run-pass/overloaded-index-assoc-list.rs diff --git a/src/test/run-pass/overloaded-index-autoderef.rs b/src/test/run-pass/overloaded-index-autoderef.rs new file mode 100644 index 0000000000000..d51956da89409 --- /dev/null +++ b/src/test/run-pass/overloaded-index-autoderef.rs @@ -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 or the MIT license +// , 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 for Foo { + fn index(&self, z: &int) -> &int { + if *z == 0 { + &self.x + } else { + &self.y + } + } +} + +impl IndexMut 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); +} + diff --git a/src/test/run-pass/overloaded-index-in-field.rs b/src/test/run-pass/overloaded-index-in-field.rs new file mode 100644 index 0000000000000..e8b0408ca0dc0 --- /dev/null +++ b/src/test/run-pass/overloaded-index-in-field.rs @@ -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 or the MIT license +// , 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 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); +} + diff --git a/src/test/run-pass/overloaded-index.rs b/src/test/run-pass/overloaded-index.rs index 1187e06695070..23bebfa35d7f3 100644 --- a/src/test/run-pass/overloaded-index.rs +++ b/src/test/run-pass/overloaded-index.rs @@ -33,6 +33,18 @@ impl IndexMut for Foo { } } +trait Int { + fn get(self) -> int; + fn get_from_ref(&self) -> int; + fn inc(&mut self); +} + +impl Int for int { + fn get(self) -> int { self } + fn get_from_ref(&self) -> int { *self } + fn inc(&mut self) { *self += 1; } +} + fn main() { 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); } From 63718792df1214d0bf7b5a12508cc7301e3be665 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 31 Oct 2014 21:42:35 -0400 Subject: [PATCH 4/6] Update the guide examples and try not to leave user hanging as to what this `&x` sigil is all about. --- src/doc/guide.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/doc/guide.md b/src/doc/guide.md index 8493a30a16e89..9a1a42478d370 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -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::>(); ``` From dfe840245bf0ac42ae2b15176e21cece41c9f7b7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 2 Nov 2014 21:00:23 -0500 Subject: [PATCH 5/6] Remove incorrect doc annotation, mark experimental since we haven't discussed in an API meeting --- src/libcore/array.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libcore/array.rs b/src/libcore/array.rs index 999574b4d7d52..829605ce7cc1b 100644 --- a/src/libcore/array.rs +++ b/src/libcore/array.rs @@ -14,12 +14,8 @@ * to all lengths. */ -#![doc(primitive = "tuple")] #![stable] - -#[unstable = "this is just a documentation module and should not be part \ - of the public api"] -pub use unit; +#![experimental] // not yet reviewed use cmp::*; use option::{Option}; From 81c00e66f564ca67c391a5f24f2ad43189245d75 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 5 Nov 2014 05:36:53 -0500 Subject: [PATCH 6/6] Better debug printouts --- src/librustc/middle/ty.rs | 8 ++++---- .../middle/typeck/infer/region_inference/mod.rs | 12 ++++++++++++ src/librustc/util/ppaux.rs | 14 +++++--------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 23a5289593541..76caa42b85023 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1167,25 +1167,25 @@ impl cmp::PartialEq for InferRegion { impl fmt::Show for TyVid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{ - write!(f, "", self.index) + write!(f, "_#{}t", self.index) } } impl fmt::Show for IntVid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "", self.index) + write!(f, "_#{}i", self.index) } } impl fmt::Show for FloatVid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "", self.index) + write!(f, "_#{}f", self.index) } } impl fmt::Show for RegionVid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "'", self.index) + write!(f, "'_#{}r", self.index) } } diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs index ff1ded726c566..cdc45890c09ea 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs @@ -952,6 +952,11 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { -> Vec { 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 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { }) } + 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={}", diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 91e563c75e475..33e1419753249 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -373,14 +373,10 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { 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) } @@ -858,7 +854,7 @@ impl Repr for ty::Region { } ty::ReInfer(ReVar(ref vid)) => { - format!("ReInfer({})", vid.index) + format!("{}", vid) } ty::ReInfer(ReSkolemized(id, ref bound_region)) => {