From cdc18b96d6aa38c22b4fa9715c974ef986ad250d Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Fri, 28 Feb 2014 00:02:27 +0200 Subject: [PATCH 1/7] Remove Rc's borrow method to avoid conflicts with RefCell's borrow in Rc>. --- src/doc/tutorial.md | 2 +- src/libarena/lib.rs | 4 +-- src/librustc/middle/astencode.rs | 2 +- src/librustc/middle/borrowck/check_loans.rs | 2 +- .../borrowck/gather_loans/gather_moves.rs | 2 +- .../middle/borrowck/gather_loans/mod.rs | 2 +- src/librustc/middle/const_eval.rs | 2 +- src/librustc/middle/kind.rs | 2 +- src/librustc/middle/liveness.rs | 2 +- src/librustc/middle/subst.rs | 2 +- src/librustc/middle/trans/closure.rs | 14 ++------ src/librustc/middle/trans/consts.rs | 4 +-- src/librustc/middle/ty.rs | 6 ++-- src/librustc/middle/typeck/check/method.rs | 2 +- src/librustc/middle/typeck/check/mod.rs | 4 +-- src/librustc/middle/typeck/check/vtable.rs | 12 +++---- src/librustc/middle/typeck/collect.rs | 4 +-- src/librustdoc/clean.rs | 2 +- src/librustuv/idle.rs | 6 ++-- src/libserialize/serialize.rs | 2 +- src/libstd/hash/mod.rs | 3 +- src/libstd/option.rs | 4 +-- src/libstd/rc.rs | 36 ++++++++----------- src/libsyntax/ext/mtwt.rs | 4 +-- src/libsyntax/print/pprust.rs | 2 +- src/libsyntax/util/interner.rs | 4 +-- src/test/compile-fail/issue-7013.rs | 2 +- src/test/run-pass/self-re-assign.rs | 2 +- 28 files changed, 59 insertions(+), 76 deletions(-) diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index 8f37aecfc34c2..15fd21e9fbc07 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -1687,7 +1687,7 @@ let x = Rc::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); let y = x.clone(); // a new owner let z = x; // this moves `x` into `z`, rather than creating a new owner -assert!(*z.borrow() == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +assert!(*z == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); // the variable is mutable, but not the contents of the box let mut a = Rc::new([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]); diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 1fc88eda37b5f..2ccb8e9c4c250 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -51,11 +51,11 @@ struct Chunk { } impl Chunk { fn capacity(&self) -> uint { - self.data.borrow().borrow().get().capacity() + self.data.deref().borrow().get().capacity() } unsafe fn as_ptr(&self) -> *u8 { - self.data.borrow().borrow().get().as_ptr() + self.data.deref().borrow().get().as_ptr() } } diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 5959f5a9d02dd..06aec8c050e0d 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -1081,7 +1081,7 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, ebml_w.tag(c::tag_table_capture_map, |ebml_w| { ebml_w.id(id); ebml_w.tag(c::tag_table_val, |ebml_w| { - ebml_w.emit_from_vec(cap_vars.borrow().as_slice(), + ebml_w.emit_from_vec(cap_vars.deref().as_slice(), |ebml_w, cap_var| { cap_var.encode(ebml_w); }) diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 53a363b44f88d..5972625caad07 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -716,7 +716,7 @@ impl<'a> CheckLoanCtxt<'a> { span: Span) { let capture_map = self.bccx.capture_map.borrow(); let cap_vars = capture_map.get().get(&closure_id); - for cap_var in cap_vars.borrow().iter() { + for cap_var in cap_vars.deref().iter() { let var_id = ast_util::def_id_of_def(cap_var.def).node; let var_path = @LpVar(var_id); self.check_if_path_is_moved(closure_id, span, diff --git a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs index 6dd7ae31c9d38..cfc4b3de38d8c 100644 --- a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs @@ -49,7 +49,7 @@ pub fn gather_captures(bccx: &BorrowckCtxt, closure_expr: &ast::Expr) { let capture_map = bccx.capture_map.borrow(); let captured_vars = capture_map.get().get(&closure_expr.id); - for captured_var in captured_vars.borrow().iter() { + for captured_var in captured_vars.deref().iter() { match captured_var.mode { moves::CapMove => { let cmt = bccx.cat_captured_var(closure_expr.id, diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index e50d6da378af1..135783f510ff0 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -406,7 +406,7 @@ impl<'a> GatherLoanCtxt<'a> { closure_expr: &ast::Expr) { let capture_map = self.bccx.capture_map.borrow(); let captured_vars = capture_map.get().get(&closure_expr.id); - for captured_var in captured_vars.borrow().iter() { + for captured_var in captured_vars.deref().iter() { match captured_var.mode { moves::CapCopy | moves::CapMove => { continue; } moves::CapRef => { } diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index aaa623bd7b2fd..7c46f05f1c1a6 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -512,7 +512,7 @@ pub fn lit_to_const(lit: &Lit) -> const_val { match lit.node { LitStr(ref s, _) => const_str((*s).clone()), LitBinary(ref data) => { - const_binary(Rc::new(data.borrow().iter().map(|x| *x).collect())) + const_binary(Rc::new(data.deref().iter().map(|x| *x).collect())) } LitChar(n) => const_uint(n as u64), LitInt(n, _) => const_int(n), diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index 90c86cb5e74e2..97aff130dfbfc 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -298,7 +298,7 @@ pub fn check_expr(cx: &mut Context, e: &Expr) { } } }; - let type_param_defs = type_param_defs.borrow(); + let type_param_defs = type_param_defs.deref(); if ts.len() != type_param_defs.len() { // Fail earlier to make debugging easier fail!("internal error: in kind::check_expr, length \ diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 02a947a0ddc7a..48a6b5c92d4e8 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -505,7 +505,7 @@ fn visit_expr(v: &mut LivenessVisitor, expr: &Expr, this: @IrMaps) { let capture_map = this.capture_map.borrow(); let cvs = capture_map.get().get(&expr.id); let mut call_caps = Vec::new(); - for cv in cvs.borrow().iter() { + for cv in cvs.deref().iter() { match moves::moved_variable_node_id_from_def(cv.def) { Some(rv) => { let cv_ln = this.add_live_node(FreeVarNode(cv.span)); diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index b0cf17c5b5467..82458a69ee65a 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -142,7 +142,7 @@ impl Subst for Rc { fn subst_spanned(&self, tcx: ty::ctxt, substs: &ty::substs, span: Option) -> Rc { - Rc::new(self.borrow().subst_spanned(tcx, substs, span)) + Rc::new(self.deref().subst_spanned(tcx, substs, span)) } } diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index f18456a674dd2..bfc9d8d2d6d0d 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -397,21 +397,13 @@ pub fn trans_expr_fn<'a>( // set an inline hint for all closures set_inline_hint(llfn); - let cap_vars = { - let capture_map = ccx.maps.capture_map.borrow(); - capture_map.get().get_copy(&id) - }; + let cap_vars = ccx.maps.capture_map.borrow().get().get_copy(&id); let ClosureResult {llbox, cdata_ty, bcx} = - build_closure(bcx, cap_vars.borrow().as_slice(), sigil); + build_closure(bcx, cap_vars.deref().as_slice(), sigil); trans_closure(ccx, decl, body, llfn, bcx.fcx.param_substs, id, [], ty::ty_fn_ret(fty), - |bcx| { - load_environment(bcx, - cdata_ty, - cap_vars.borrow().as_slice(), - sigil) - }); + |bcx| load_environment(bcx, cdata_ty, cap_vars.deref().as_slice(), sigil)); fill_fn_pair(bcx, dest_addr, llfn, llbox); bcx diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index f1e6fff533985..c9317f18168f2 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -76,9 +76,7 @@ pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: ast::Lit) ast::LitBool(b) => C_bool(b), ast::LitNil => C_nil(), ast::LitStr(ref s, _) => C_str_slice(cx, (*s).clone()), - ast::LitBinary(ref data) => { - C_binary_slice(cx, data.borrow().as_slice()) - } + ast::LitBinary(ref data) => C_binary_slice(cx, data.deref().as_slice()), } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index b2a879b1946e1..7193f6576584b 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1014,13 +1014,13 @@ pub struct Generics { impl Generics { pub fn has_type_params(&self) -> bool { - !self.type_param_defs.borrow().is_empty() + !self.type_param_defs.deref().is_empty() } pub fn type_param_defs<'a>(&'a self) -> &'a [TypeParameterDef] { - self.type_param_defs.borrow().as_slice() + self.type_param_defs.deref().as_slice() } pub fn region_param_defs<'a>(&'a self) -> &'a [RegionParameterDef] { - self.region_param_defs.borrow().as_slice() + self.region_param_defs.deref().as_slice() } } diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index ca36fca687abd..114c385b5a0c4 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -1030,7 +1030,7 @@ impl<'a> LookupContext<'a> { let m_regions = self.fcx.infcx().region_vars_for_defs( self.expr.span, - candidate.method_ty.generics.region_param_defs.borrow().as_slice()); + candidate.method_ty.generics.region_param_defs.deref().as_slice()); for &r in m_regions.iter() { all_regions.push(r); } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 026d2d5d73454..d032f0f841fc7 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -569,7 +569,7 @@ pub fn check_item(ccx: @CrateCtxt, it: &ast::Item) { fn_tpt.generics.type_param_defs(), [], [], - fn_tpt.generics.region_param_defs.borrow().as_slice(), + fn_tpt.generics.region_param_defs.deref().as_slice(), body.id); check_bare_fn(ccx, decl, body, it.id, fn_tpt.ty, param_env); @@ -3732,7 +3732,7 @@ pub fn instantiate_path(fcx: @FnCtxt, nsupplied = num_supplied_regions)); } - fcx.infcx().region_vars_for_defs(span, tpt.generics.region_param_defs.borrow().as_slice()) + fcx.infcx().region_vars_for_defs(span, tpt.generics.region_param_defs.deref().as_slice()) }; let regions = ty::NonerasedRegions(regions); diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 57e85ab55d3bc..70fe3cfde50b8 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -709,15 +709,13 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { debug!("vtable resolution on parameter bounds for method call {}", ex.repr(fcx.tcx())); let type_param_defs = ty::method_call_type_param_defs(cx.tcx, method.origin); - if has_trait_bounds(type_param_defs.borrow().as_slice()) { + if has_trait_bounds(type_param_defs.deref().as_slice()) { let substs = fcx.method_ty_substs(ex.id); let vcx = fcx.vtable_context(); - let vtbls = lookup_vtables(&vcx, - &location_info_for_expr(ex), - type_param_defs.borrow() + let vtbls = lookup_vtables(&vcx, &location_info_for_expr(ex), + type_param_defs.deref() .as_slice(), - &substs, - is_early); + &substs, is_early); if !is_early { insert_vtables(fcx, ex.id, vtbls); } @@ -829,7 +827,7 @@ pub fn resolve_impl(tcx: ty::ctxt, pub fn trans_resolve_method(tcx: ty::ctxt, id: ast::NodeId, substs: &ty::substs) -> Option { let generics = ty::lookup_item_type(tcx, ast_util::local_def(id)).generics; - let type_param_defs = generics.type_param_defs.borrow(); + let type_param_defs = generics.type_param_defs.deref(); if has_trait_bounds(type_param_defs.as_slice()) { let vcx = VtableContext { infcx: &infer::new_infer_ctxt(tcx), diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 234e6e92bac9f..402f06f928cce 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -342,7 +342,7 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) { let mut new_type_param_defs = Vec::new(); let substd_type_param_defs = trait_ty_generics.type_param_defs.subst(tcx, &substs); - new_type_param_defs.push_all(substd_type_param_defs.borrow() + new_type_param_defs.push_all(substd_type_param_defs.deref() .as_slice()); // add in the "self" type parameter @@ -360,7 +360,7 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) { // add in the type parameters from the method let substd_type_param_defs = m.generics.type_param_defs.subst(tcx, &substs); - new_type_param_defs.push_all(substd_type_param_defs.borrow() + new_type_param_defs.push_all(substd_type_param_defs.deref() .as_slice()); debug!("static method {} type_param_defs={} ty={}, substs={}", diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean.rs index d42a611053f91..c27725fd5852a 100644 --- a/src/librustdoc/clean.rs +++ b/src/librustdoc/clean.rs @@ -1192,7 +1192,7 @@ impl ToSource for syntax::codemap::Span { fn lit_to_str(lit: &ast::Lit) -> ~str { match lit.node { ast::LitStr(ref st, _) => st.get().to_owned(), - ast::LitBinary(ref data) => format!("{:?}", data.borrow().as_slice()), + ast::LitBinary(ref data) => format!("{:?}", data.deref().as_slice()), ast::LitChar(c) => ~"'" + std::char::from_u32(c).unwrap().to_str() + "'", ast::LitInt(i, _t) => i.to_str(), ast::LitUint(u, _t) => u.to_str(), diff --git a/src/librustuv/idle.rs b/src/librustuv/idle.rs index aae86b18512bf..a6c0a7b829ae9 100644 --- a/src/librustuv/idle.rs +++ b/src/librustuv/idle.rs @@ -113,7 +113,7 @@ mod test { fn call(&mut self) { let task = match *self { MyCallback(ref rc, n) => { - let mut slot = rc.borrow().borrow_mut(); + let mut slot = rc.deref().borrow_mut(); match *slot.get() { (ref mut task, ref mut val) => { *val = n; @@ -140,7 +140,7 @@ mod test { fn sleep(chan: &Chan) -> uint { let task: ~Task = Local::take(); task.deschedule(1, |task| { - let mut slot = chan.borrow().borrow_mut(); + let mut slot = chan.deref().borrow_mut(); match *slot.get() { (ref mut slot, _) => { assert!(slot.is_none()); @@ -150,7 +150,7 @@ mod test { Ok(()) }); - let slot = chan.borrow().borrow(); + let slot = chan.deref().borrow(); match *slot.get() { (_, n) => n } } diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs index 115bb6cb6f3cf..fd57f47e8819d 100644 --- a/src/libserialize/serialize.rs +++ b/src/libserialize/serialize.rs @@ -388,7 +388,7 @@ impl> Encodable for @T { impl> Encodable for Rc { #[inline] fn encode(&self, s: &mut S) { - self.borrow().encode(s) + self.deref().encode(s) } } diff --git a/src/libstd/hash/mod.rs b/src/libstd/hash/mod.rs index 6c0ae280ed078..dd40f6008735f 100644 --- a/src/libstd/hash/mod.rs +++ b/src/libstd/hash/mod.rs @@ -66,6 +66,7 @@ use container::Container; use io::Writer; use iter::Iterator; +use ops::Deref; use option::{Option, Some, None}; use rc::Rc; use str::{Str, StrSlice}; @@ -246,7 +247,7 @@ impl> Hash for @T { impl> Hash for Rc { #[inline] fn hash(&self, state: &mut S) { - self.borrow().hash(state); + self.deref().hash(state); } } diff --git a/src/libstd/option.rs b/src/libstd/option.rs index 86f8c143a9e99..31605ca961e6a 100644 --- a/src/libstd/option.rs +++ b/src/libstd/option.rs @@ -503,7 +503,7 @@ mod tests { #[unsafe_destructor] impl ::ops::Drop for R { fn drop(&mut self) { - let ii = self.i.borrow(); + let ii = self.i.deref(); ii.set(ii.get() + 1); } } @@ -520,7 +520,7 @@ mod tests { let opt = Some(x); let _y = opt.unwrap(); } - assert_eq!(i.borrow().get(), 1); + assert_eq!(i.deref().get(), 1); } #[test] diff --git a/src/libstd/rc.rs b/src/libstd/rc.rs index 8a7edc728c012..5c4b19b4e4b8c 100644 --- a/src/libstd/rc.rs +++ b/src/libstd/rc.rs @@ -63,12 +63,6 @@ impl Rc { } impl Rc { - /// Borrow the value contained in the reference-counted box - #[inline(always)] - pub fn borrow<'a>(&'a self) -> &'a T { - unsafe { &(*self.ptr).value } - } - /// Downgrade the reference-counted pointer to a weak reference pub fn downgrade(&self) -> Weak { unsafe { @@ -93,7 +87,7 @@ impl Drop for Rc { if self.ptr != 0 as *mut RcBox { (*self.ptr).strong -= 1; if (*self.ptr).strong == 0 { - ptr::read(self.borrow()); // destroy the contained object + ptr::read(self.deref()); // destroy the contained object // remove the implicit "strong weak" pointer now // that we've destroyed the contents. @@ -120,24 +114,24 @@ impl Clone for Rc { impl Eq for Rc { #[inline(always)] - fn eq(&self, other: &Rc) -> bool { *self.borrow() == *other.borrow() } + fn eq(&self, other: &Rc) -> bool { *self.deref() == *other.deref() } #[inline(always)] - fn ne(&self, other: &Rc) -> bool { *self.borrow() != *other.borrow() } + fn ne(&self, other: &Rc) -> bool { *self.deref() != *other.deref() } } impl Ord for Rc { #[inline(always)] - fn lt(&self, other: &Rc) -> bool { *self.borrow() < *other.borrow() } + fn lt(&self, other: &Rc) -> bool { *self.deref() < *other.deref() } #[inline(always)] - fn le(&self, other: &Rc) -> bool { *self.borrow() <= *other.borrow() } + fn le(&self, other: &Rc) -> bool { *self.deref() <= *other.deref() } #[inline(always)] - fn gt(&self, other: &Rc) -> bool { *self.borrow() > *other.borrow() } + fn gt(&self, other: &Rc) -> bool { *self.deref() > *other.deref() } #[inline(always)] - fn ge(&self, other: &Rc) -> bool { *self.borrow() >= *other.borrow() } + fn ge(&self, other: &Rc) -> bool { *self.deref() >= *other.deref() } } /// Weak reference to a reference-counted box @@ -197,30 +191,30 @@ mod tests { fn test_clone() { let x = Rc::new(RefCell::new(5)); let y = x.clone(); - x.borrow().with_mut(|inner| { + x.deref().with_mut(|inner| { *inner = 20; }); - assert_eq!(y.borrow().with(|v| *v), 20); + assert_eq!(y.deref().with(|v| *v), 20); } #[test] fn test_simple() { let x = Rc::new(5); - assert_eq!(*x.borrow(), 5); + assert_eq!(*x.deref(), 5); } #[test] fn test_simple_clone() { let x = Rc::new(5); let y = x.clone(); - assert_eq!(*x.borrow(), 5); - assert_eq!(*y.borrow(), 5); + assert_eq!(*x.deref(), 5); + assert_eq!(*y.deref(), 5); } #[test] fn test_destructor() { let x = Rc::new(~5); - assert_eq!(**x.borrow(), 5); + assert_eq!(**x.deref(), 5); } #[test] @@ -243,7 +237,7 @@ mod tests { // see issue #11532 use gc::Gc; let a = Rc::new(RefCell::new(Gc::new(1))); - assert!(a.borrow().try_borrow_mut().is_some()); + assert!(a.deref().try_borrow_mut().is_some()); } #[test] @@ -254,7 +248,7 @@ mod tests { let a = Rc::new(Cycle { x: RefCell::new(None) }); let b = a.clone().downgrade(); - *a.borrow().x.borrow_mut().get() = Some(b); + *a.deref().x.borrow_mut().get() = Some(b); // hopefully we don't double-free (or leak)... } diff --git a/src/libsyntax/ext/mtwt.rs b/src/libsyntax/ext/mtwt.rs index b0ed215f3e103..b7fad22a7ad94 100644 --- a/src/libsyntax/ext/mtwt.rs +++ b/src/libsyntax/ext/mtwt.rs @@ -103,7 +103,7 @@ pub fn with_sctable(op: |&SCTable| -> T) -> T { } Some(ts) => ts.clone() }; - op(table.borrow()) + op(table.deref()) }) } @@ -158,7 +158,7 @@ fn with_resolve_table_mut(op: |&mut ResolveTable| -> T) -> T { } Some(ts) => ts.clone() }; - op(table.borrow().borrow_mut().get()) + op(table.deref().borrow_mut().get()) }) } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 45ab4c6956a49..6894d6a2b0537 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2291,7 +2291,7 @@ pub fn print_literal(s: &mut State, lit: &ast::Lit) -> io::IoResult<()> { ast::LitBinary(ref arr) => { try!(ibox(s, indent_unit)); try!(word(&mut s.s, "[")); - try!(commasep_cmnt(s, Inconsistent, arr.borrow().as_slice(), + try!(commasep_cmnt(s, Inconsistent, arr.deref().as_slice(), |s, u| word(&mut s.s, format!("{}", *u)), |_| lit.span)); try!(word(&mut s.s, "]")); diff --git a/src/libsyntax/util/interner.rs b/src/libsyntax/util/interner.rs index ba154a8d8923c..969c7cec87cce 100644 --- a/src/libsyntax/util/interner.rs +++ b/src/libsyntax/util/interner.rs @@ -106,13 +106,13 @@ impl TotalOrd for RcStr { impl Str for RcStr { #[inline] fn as_slice<'a>(&'a self) -> &'a str { - let s: &'a str = *self.string.borrow(); + let s: &'a str = *self.string.deref(); s } #[inline] fn into_owned(self) -> ~str { - self.string.borrow().to_owned() + self.string.deref().to_owned() } } diff --git a/src/test/compile-fail/issue-7013.rs b/src/test/compile-fail/issue-7013.rs index 05d7b878d767b..800549b7737c6 100644 --- a/src/test/compile-fail/issue-7013.rs +++ b/src/test/compile-fail/issue-7013.rs @@ -40,7 +40,7 @@ fn main() //~^ ERROR cannot pack type `~B`, which does not fulfill `Send` let v = Rc::new(RefCell::new(a)); let w = v.clone(); - let b = v.borrow(); + let b = v.deref(); let mut b = b.borrow_mut(); b.get().v.set(w.clone()); } diff --git a/src/test/run-pass/self-re-assign.rs b/src/test/run-pass/self-re-assign.rs index 041c5cf4a640e..cba142680dfb9 100644 --- a/src/test/run-pass/self-re-assign.rs +++ b/src/test/run-pass/self-re-assign.rs @@ -20,5 +20,5 @@ pub fn main() { let mut x = Rc::new(3); x = x; - assert!(*x.borrow() == 3); + assert!(*x.deref() == 3); } From 20b4e159edb54cecb8abdedb187ba05a869b3bf0 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Thu, 6 Mar 2014 19:24:11 +0200 Subject: [PATCH 2/7] Implement automatic overloaded dereference. Closes #7141. --- src/libnum/complex.rs | 2 +- src/librustc/middle/astencode.rs | 8 +- src/librustc/middle/borrowck/check_loans.rs | 5 +- .../middle/borrowck/gather_loans/mod.rs | 3 +- src/librustc/middle/borrowck/mod.rs | 12 +- src/librustc/middle/cfg/construct.rs | 4 +- src/librustc/middle/check_const.rs | 4 +- src/librustc/middle/const_eval.rs | 6 +- src/librustc/middle/dataflow.rs | 4 +- src/librustc/middle/dead.rs | 7 +- src/librustc/middle/effect.rs | 5 +- src/librustc/middle/kind.rs | 5 +- src/librustc/middle/lint.rs | 3 +- src/librustc/middle/mem_categorization.rs | 97 +++--- src/librustc/middle/moves.rs | 14 +- src/librustc/middle/privacy.rs | 42 +-- src/librustc/middle/reachable.rs | 15 +- src/librustc/middle/trans/asm.rs | 5 +- src/librustc/middle/trans/callee.rs | 137 ++++---- src/librustc/middle/trans/cleanup.rs | 4 +- src/librustc/middle/trans/common.rs | 23 +- src/librustc/middle/trans/consts.rs | 9 +- src/librustc/middle/trans/expr.rs | 250 ++++++--------- src/librustc/middle/trans/meth.rs | 58 ++-- src/librustc/middle/ty.rs | 121 +++---- src/librustc/middle/typeck/check/method.rs | 303 +++++++++--------- src/librustc/middle/typeck/check/mod.rs | 207 ++++++------ src/librustc/middle/typeck/check/regionck.rs | 118 ++++--- src/librustc/middle/typeck/check/vtable.rs | 141 +++----- src/librustc/middle/typeck/check/writeback.rs | 54 ++-- src/librustc/middle/typeck/mod.rs | 28 +- src/librustc/util/nodemap.rs | 47 ++- src/test/run-pass/overloaded-autoderef.rs | 51 +++ 33 files changed, 871 insertions(+), 921 deletions(-) create mode 100644 src/test/run-pass/overloaded-autoderef.rs diff --git a/src/libnum/complex.rs b/src/libnum/complex.rs index 5ffd23aa34621..b226e773dbb88 100644 --- a/src/libnum/complex.rs +++ b/src/libnum/complex.rs @@ -101,7 +101,7 @@ impl Cmplx { /// Convert a polar representation into a complex number. #[inline] pub fn from_polar(r: &T, theta: &T) -> Cmplx { - Cmplx::new(r * theta.cos(), r * theta.sin()) + Cmplx::new(*r * theta.cos(), *r * theta.sin()) } } diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 06aec8c050e0d..fed35922a90c0 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -21,7 +21,7 @@ use metadata::tydecode; use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter, RegionParameter}; use metadata::tyencode; -use middle::typeck::{MethodCallee, MethodOrigin}; +use middle::typeck::{MethodCall, MethodCallee, MethodOrigin}; use middle::{ty, typeck, moves}; use middle; use util::ppaux::ty_to_str; @@ -1039,7 +1039,8 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, } } - for &method in maps.method_map.borrow().get().find(&id).iter() { + let method_call = MethodCall::expr(id); + for &method in maps.method_map.borrow().get().find(&method_call).iter() { ebml_w.tag(c::tag_table_method_map, |ebml_w| { ebml_w.id(id); ebml_w.tag(c::tag_table_val, |ebml_w| { @@ -1385,7 +1386,8 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext, } c::tag_table_method_map => { let method = val_dsr.read_method_callee(xcx); - dcx.maps.method_map.borrow_mut().get().insert(id, method); + let method_call = MethodCall::expr(id); + dcx.maps.method_map.borrow_mut().get().insert(method_call, method); } c::tag_table_vtable_map => { let vtable_res = diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 5972625caad07..e50d24560701e 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -22,6 +22,7 @@ use mc = middle::mem_categorization; use middle::borrowck::*; use middle::moves; use middle::ty; +use middle::typeck::MethodCall; use std::vec_ng::Vec; use syntax::ast; use syntax::ast_util; @@ -838,11 +839,11 @@ fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>, this.check_call(expr, None, expr.span, args.as_slice()); } ast::ExprIndex(_, rval) | ast::ExprBinary(_, _, rval) - if method_map.get().contains_key(&expr.id) => { + if method_map.get().contains_key(&MethodCall::expr(expr.id)) => { this.check_call(expr, None, expr.span, [rval]); } ast::ExprUnary(_, _) | ast::ExprIndex(_, _) - if method_map.get().contains_key(&expr.id) => { + if method_map.get().contains_key(&MethodCall::expr(expr.id)) => { this.check_call(expr, None, expr.span, []); } ast::ExprInlineAsm(ref ia) => { diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index 135783f510ff0..d2fcee79fc031 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -23,6 +23,7 @@ use middle::moves; use middle::pat_util; use middle::ty::{ty_region}; use middle::ty; +use middle::typeck::MethodCall; use util::common::indenter; use util::ppaux::{Repr}; @@ -242,7 +243,7 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt, ast::ExprIndex(_, arg) | ast::ExprBinary(_, _, arg) - if method_map.get().contains_key(&ex.id) => { + if method_map.get().contains_key(&MethodCall::expr(ex.id)) => { // Arguments in method calls are always passed by ref. // // Currently these do not use adjustments, so we have to diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 654203bf0776b..44a5acc7f1b54 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -556,7 +556,8 @@ impl BorrowckCtxt { move_data::MoveExpr => { let (expr_ty, expr_span) = match self.tcx.map.find(move.id) { Some(ast_map::NodeExpr(expr)) => { - (ty::expr_ty_adjusted(self.tcx, expr), expr.span) + (ty::expr_ty_adjusted(self.tcx, expr, + self.method_map.borrow().get()), expr.span) } r => self.tcx.sess.bug(format!("MoveExpr({:?}) maps to {:?}, not Expr", move.id, r)) @@ -582,7 +583,8 @@ impl BorrowckCtxt { move_data::Captured => { let (expr_ty, expr_span) = match self.tcx.map.find(move.id) { Some(ast_map::NodeExpr(expr)) => { - (ty::expr_ty_adjusted(self.tcx, expr), expr.span) + (ty::expr_ty_adjusted(self.tcx, expr, + self.method_map.borrow().get()), expr.span) } r => self.tcx.sess.bug(format!("Captured({:?}) maps to {:?}, not Expr", move.id, r)) @@ -922,8 +924,8 @@ impl mc::Typer for TcxTyper { Ok(ty::node_id_to_type(self.tcx, id)) } - fn node_method_ty(&mut self, id: ast::NodeId) -> Option { - self.method_map.borrow().get().find(&id).map(|method| method.ty) + fn node_method_ty(&mut self, method_call: typeck::MethodCall) -> Option { + self.method_map.borrow().get().find(&method_call).map(|method| method.ty) } fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> { @@ -932,7 +934,7 @@ impl mc::Typer for TcxTyper { } fn is_method_call(&mut self, id: ast::NodeId) -> bool { - self.method_map.borrow().get().contains_key(&id) + self.method_map.borrow().get().contains_key(&typeck::MethodCall::expr(id)) } fn temporary_scope(&mut self, id: ast::NodeId) -> Option { diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index ec1ddd99cc6bf..0100a82a9d5aa 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -523,7 +523,7 @@ impl CFGBuilder { } fn is_method_call(&self, expr: &ast::Expr) -> bool { - let method_map = self.method_map.borrow(); - method_map.get().contains_key(&expr.id) + let method_call = typeck::MethodCall::expr(expr.id); + self.method_map.borrow().get().contains_key(&method_call) } } diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 680caf101bf72..6841e09f7f2db 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -117,8 +117,8 @@ pub fn check_expr(v: &mut CheckCrateVisitor, } ExprLit(lit) if ast_util::lit_is_str(lit) => {} ExprBinary(..) | ExprUnary(..) => { - let method_map = method_map.borrow(); - if method_map.get().contains_key(&e.id) { + let method_call = typeck::MethodCall::expr(e.id); + if method_map.borrow().get().contains_key(&method_call) { sess.span_err(e.span, "user-defined operators are not \ allowed in constant expressions"); } diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 7c46f05f1c1a6..1f675b7bb4a68 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -16,7 +16,7 @@ use middle::astencode; use middle::ty; use middle::typeck::astconv; use middle; -use util::nodemap::{DefIdMap, NodeMap}; +use util::nodemap::{DefIdMap, FnvHashMap, NodeMap}; use syntax::ast::*; use syntax::parse::token::InternedString; @@ -136,7 +136,7 @@ pub fn lookup_variant_by_id(tcx: ty::ctxt, } let maps = astencode::Maps { root_map: @RefCell::new(HashMap::new()), - method_map: @RefCell::new(NodeMap::new()), + method_map: @RefCell::new(FnvHashMap::new()), vtable_map: @RefCell::new(NodeMap::new()), capture_map: @RefCell::new(NodeMap::new()) }; @@ -186,7 +186,7 @@ pub fn lookup_const_by_id(tcx: ty::ctxt, def_id: ast::DefId) } let maps = astencode::Maps { root_map: @RefCell::new(HashMap::new()), - method_map: @RefCell::new(NodeMap::new()), + method_map: @RefCell::new(FnvHashMap::new()), vtable_map: @RefCell::new(NodeMap::new()), capture_map: @RefCell::new(NodeMap::new()) }; diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index ba3fc34023d18..57b5be4e96069 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -810,8 +810,8 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { } fn is_method_call(&self, expr: &ast::Expr) -> bool { - let method_map = self.dfcx.method_map.borrow(); - method_map.get().contains_key(&expr.id) + let method_call = typeck::MethodCall::expr(expr.id); + self.dfcx.method_map.borrow().get().contains_key(&method_call) } fn reset(&mut self, bits: &mut [uint]) { diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index d226f2561b7bd..310feb88892d7 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -92,9 +92,10 @@ impl MarkSymbolVisitor { } } - fn lookup_and_handle_method(&mut self, id: &ast::NodeId, + fn lookup_and_handle_method(&mut self, id: ast::NodeId, span: codemap::Span) { - match self.method_map.borrow().get().find(id) { + let method_call = typeck::MethodCall::expr(id); + match self.method_map.borrow().get().find(&method_call) { Some(method) => { match method.origin { typeck::MethodStatic(def_id) => { @@ -179,7 +180,7 @@ impl Visitor<()> for MarkSymbolVisitor { fn visit_expr(&mut self, expr: &ast::Expr, _: ()) { match expr.node { ast::ExprMethodCall(..) => { - self.lookup_and_handle_method(&expr.id, expr.span); + self.lookup_and_handle_method(expr.id, expr.span); } _ => () } diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index bac706e5a8660..d3ec7044040cc 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -12,7 +12,7 @@ /// `unsafe`. use middle::ty; -use middle::typeck::MethodMap; +use middle::typeck::{MethodCall, MethodMap}; use util::ppaux; use syntax::ast; @@ -138,7 +138,8 @@ impl Visitor<()> for EffectCheckVisitor { fn visit_expr(&mut self, expr: &ast::Expr, _:()) { match expr.node { ast::ExprMethodCall(_, _, _) => { - let base_type = self.method_map.borrow().get().get(&expr.id).ty; + let method_call = MethodCall::expr(expr.id); + let base_type = self.method_map.borrow().get().get(&method_call).ty; debug!("effect: method call case, base type is {}", ppaux::ty_to_str(self.tcx, base_type)); if type_is_unsafe_function(base_type) { diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index 97aff130dfbfc..7771cb0ab3c14 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -267,7 +267,7 @@ pub fn check_expr(cx: &mut Context, e: &Expr) { // Handle any kind bounds on type parameters { let method_map = cx.method_map.borrow(); - let method = method_map.get().find(&e.id); + let method = method_map.get().find(&typeck::MethodCall::expr(e.id)); let node_type_substs = cx.tcx.node_type_substs.borrow(); let r = match method { Some(method) => Some(&method.substs.tps), @@ -341,7 +341,8 @@ pub fn check_expr(cx: &mut Context, e: &Expr) { match **adjustment { ty::AutoObject(..) => { let source_ty = ty::expr_ty(cx.tcx, e); - let target_ty = ty::expr_ty_adjusted(cx.tcx, e); + let target_ty = ty::expr_ty_adjusted(cx.tcx, e, + cx.method_map.borrow().get()); check_trait_cast(cx, source_ty, target_ty, e.span); } ty::AutoAddEnv(..) | diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index 3e3a94f7f0f66..31d705dc6aa93 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -1491,7 +1491,8 @@ fn check_stability(cx: &Context, e: &ast::Expr) { } } ast::ExprMethodCall(..) => { - match cx.method_map.borrow().get().find(&e.id) { + let method_call = typeck::MethodCall::expr(e.id); + match cx.method_map.borrow().get().find(&method_call) { Some(method) => { match method.origin { typeck::MethodStatic(def_id) => { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index ba93e52bc48ec..c07cd2570a30b 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -63,6 +63,7 @@ #[allow(non_camel_case_types)]; use middle::ty; +use middle::typeck; use util::ppaux::{ty_to_str, region_ptr_to_str, Repr}; use std::vec_ng::Vec; @@ -268,7 +269,7 @@ pub type McResult = Result; pub trait Typer { fn tcx(&self) -> ty::ctxt; fn node_ty(&mut self, id: ast::NodeId) -> McResult; - fn node_method_ty(&mut self, id: ast::NodeId) -> Option; + fn node_method_ty(&mut self, method_call: typeck::MethodCall) -> Option; fn adjustment(&mut self, node_id: ast::NodeId) -> Option<@ty::AutoAdjustment>; fn is_method_call(&mut self, id: ast::NodeId) -> bool; fn temporary_scope(&mut self, rvalue_id: ast::NodeId) -> Option; @@ -365,7 +366,8 @@ impl MemCategorizationContext { fn expr_ty_adjusted(&mut self, expr: &ast::Expr) -> McResult { let unadjusted_ty = if_ok!(self.expr_ty(expr)); let adjustment = self.adjustment(expr.id); - Ok(ty::adjust_ty(self.tcx(), expr.span, unadjusted_ty, adjustment)) + Ok(ty::adjust_ty(self.tcx(), expr.span, expr.id, unadjusted_ty, adjustment, + |method_call| self.typer.node_method_ty(method_call))) } fn node_ty(&mut self, id: ast::NodeId) -> McResult { @@ -435,21 +437,11 @@ impl MemCategorizationContext { let expr_ty = if_ok!(self.expr_ty(expr)); match expr.node { ast::ExprUnary(ast::UnDeref, e_base) => { - let base_cmt = match self.typer.node_method_ty(expr.id) { - Some(method_ty) => { - let ref_ty = ty::ty_fn_ret(method_ty); - self.cat_rvalue_node(expr.id(), expr.span(), ref_ty) - } - None => if_ok!(self.cat_expr(e_base)) - }; + let base_cmt = if_ok!(self.cat_expr(e_base)); Ok(self.cat_deref(expr, base_cmt, 0)) } ast::ExprField(base, f_name, _) => { - // Method calls are now a special syntactic form, - // so `a.b` should always be a field. - assert!(!self.typer.is_method_call(expr.id)); - let base_cmt = if_ok!(self.cat_expr(base)); Ok(self.cat_field(expr, base_cmt, f_name, expr_ty)) } @@ -725,59 +717,64 @@ impl MemCategorizationContext { // `()` (the empty tuple). let opaque_ty = ty::mk_tup(self.tcx(), Vec::new()); - return self.cat_deref_common(node, base_cmt, deref_cnt, opaque_ty); + self.cat_deref_common(node, base_cmt, deref_cnt, opaque_ty) } - pub fn cat_deref(&mut self, - node: &N, - base_cmt: cmt, - deref_cnt: uint) - -> cmt { - let mt = match ty::deref(base_cmt.ty, true) { - Some(mt) => mt, + fn cat_deref(&mut self, + node: &N, + base_cmt: cmt, + deref_cnt: uint) + -> cmt { + let method_call = typeck::MethodCall { + expr_id: node.id(), + autoderef: deref_cnt as u32 + }; + let method_ty = self.typer.node_method_ty(method_call); + + debug!("cat_deref: method_call={:?} method_ty={}", + method_call, method_ty.map(|ty| ty.repr(self.tcx()))); + + let base_cmt = match method_ty { + Some(method_ty) => { + let ref_ty = ty::ty_fn_ret(method_ty); + self.cat_rvalue_node(node.id(), node.span(), ref_ty) + } + None => base_cmt + }; + match ty::deref(base_cmt.ty, true) { + Some(mt) => self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty), None => { self.tcx().sess.span_bug( node.span(), format!("Explicit deref of non-derefable type: {}", base_cmt.ty.repr(self.tcx()))); } - }; - - return self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty); + } } - pub fn cat_deref_common(&mut self, - node: &N, - base_cmt: cmt, - deref_cnt: uint, - deref_ty: ty::t) - -> cmt { - match deref_kind(self.tcx(), base_cmt.ty) { + fn cat_deref_common(&mut self, + node: &N, + base_cmt: cmt, + deref_cnt: uint, + deref_ty: ty::t) + -> cmt { + let (m, cat) = match deref_kind(self.tcx(), base_cmt.ty) { deref_ptr(ptr) => { // for unique ptrs, we inherit mutability from the // owning reference. - let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, - ptr); - - @cmt_ { - id:node.id(), - span:node.span(), - cat:cat_deref(base_cmt, deref_cnt, ptr), - mutbl:m, - ty:deref_ty - } + (MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr), + cat_deref(base_cmt, deref_cnt, ptr)) } - deref_interior(interior) => { - let m = base_cmt.mutbl.inherit(); - @cmt_ { - id:node.id(), - span:node.span(), - cat:cat_interior(base_cmt, interior), - mutbl:m, - ty:deref_ty - } + (base_cmt.mutbl.inherit(), cat_interior(base_cmt, interior)) } + }; + @cmt_ { + id: node.id(), + span: node.span(), + cat: cat, + mutbl: m, + ty: deref_ty } } diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs index 16655474d3a85..b52ec7be63114 100644 --- a/src/librustc/middle/moves.rs +++ b/src/librustc/middle/moves.rs @@ -130,7 +130,7 @@ and so on. use middle::pat_util::{pat_bindings}; use middle::freevars; use middle::ty; -use middle::typeck::MethodMap; +use middle::typeck::{MethodCall, MethodMap}; use util::ppaux; use util::ppaux::Repr; use util::common::indenter; @@ -281,12 +281,10 @@ impl VisitContext { debug!("consume_expr(expr={})", expr.repr(self.tcx)); - let expr_ty = ty::expr_ty_adjusted(self.tcx, expr); + let expr_ty = ty::expr_ty_adjusted(self.tcx, expr, + self.method_map.borrow().get()); if ty::type_moves_by_default(self.tcx, expr_ty) { - { - let mut moves_map = self.move_maps.moves_map.borrow_mut(); - moves_map.get().insert(expr.id); - } + self.move_maps.moves_map.borrow_mut().get().insert(expr.id); self.use_expr(expr, Move); } else { self.use_expr(expr, Read); @@ -608,8 +606,8 @@ impl VisitContext { receiver_expr: @Expr, arg_exprs: &[@Expr]) -> bool { - let method_map = self.method_map.borrow(); - if !method_map.get().contains_key(&expr.id) { + let method_call = MethodCall::expr(expr.id); + if !self.method_map.borrow().get().contains_key(&method_call) { return false; } diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 5cee3e1a20dd9..b8a40f623d947 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -19,7 +19,7 @@ use metadata::csearch; use middle::lint; use middle::resolve; use middle::ty; -use middle::typeck::{MethodMap, MethodOrigin, MethodParam}; +use middle::typeck::{MethodCall, MethodMap, MethodOrigin, MethodParam}; use middle::typeck::{MethodStatic, MethodObject}; use util::nodemap::{NodeMap, NodeSet}; @@ -772,40 +772,26 @@ impl<'a> Visitor<()> for PrivacyVisitor<'a> { fn visit_expr(&mut self, expr: &ast::Expr, _: ()) { match expr.node { ast::ExprField(base, ident, _) => { - // Method calls are now a special syntactic form, - // so `a.b` should always be a field. - let method_map = self.method_map.borrow(); - assert!(!method_map.get().contains_key(&expr.id)); - - // With type_autoderef, make sure we don't - // allow pointers to violate privacy - let t = ty::type_autoderef(ty::expr_ty(self.tcx, base)); - match ty::get(t).sty { + match ty::get(ty::expr_ty_adjusted(self.tcx, base, + self.method_map.borrow().get())).sty { ty::ty_struct(id, _) => { self.check_field(expr.span, id, ident, None); } _ => {} } } - ast::ExprMethodCall(ident, _, ref args) => { - // see above - let t = ty::type_autoderef(ty::expr_ty(self.tcx, - *args.get(0))); - match ty::get(t).sty { - ty::ty_enum(_, _) | ty::ty_struct(_, _) => { - match self.method_map.borrow().get().find(&expr.id) { - None => { - self.tcx.sess.span_bug(expr.span, - "method call not in \ - method map"); - } - Some(method) => { - debug!("(privacy checking) checking impl method"); - self.check_method(expr.span, method.origin, ident); - } - } + ast::ExprMethodCall(ident, _, _) => { + let method_call = MethodCall::expr(expr.id); + match self.method_map.borrow().get().find(&method_call) { + None => { + self.tcx.sess.span_bug(expr.span, + "method call not in \ + method map"); + } + Some(method) => { + debug!("(privacy checking) checking impl method"); + self.check_method(expr.span, method.origin, ident); } - _ => {} } } ast::ExprStruct(_, ref fields, _) => { diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index fac7e7a36e69c..e9d01713e1c27 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -149,24 +149,17 @@ impl Visitor<()> for MarkSymbolVisitor { } } ast::ExprMethodCall(..) => { - match self.method_map.borrow().get().get(&expr.id).origin { + let method_call = typeck::MethodCall::expr(expr.id); + match self.method_map.borrow().get().get(&method_call).origin { typeck::MethodStatic(def_id) => { if is_local(def_id) { if ReachableContext:: def_id_represents_local_inlined_item( self.tcx, def_id) { - { - let mut worklist = self.worklist - .borrow_mut(); - worklist.get().push(def_id.node) - } - } - { - let mut reachable_symbols = - self.reachable_symbols.borrow_mut(); - reachable_symbols.get().insert(def_id.node); + self.worklist.borrow_mut().get().push(def_id.node) } + self.reachable_symbols.borrow_mut().get().insert(def_id.node); } } _ => {} diff --git a/src/librustc/middle/trans/asm.rs b/src/librustc/middle/trans/asm.rs index adcef0278f8b4..1a398a7377100 100644 --- a/src/librustc/middle/trans/asm.rs +++ b/src/librustc/middle/trans/asm.rs @@ -50,10 +50,11 @@ pub fn trans_inline_asm<'a>(bcx: &'a Block<'a>, ia: &ast::InlineAsm) let inputs = ia.inputs.map(|&(ref c, input)| { constraints.push((*c).clone()); + let in_datum = unpack_datum!(bcx, expr::trans(bcx, input)); unpack_result!(bcx, { - callee::trans_arg_expr(bcx, + callee::trans_arg_datum(bcx, expr_ty(bcx, input), - input, + in_datum, cleanup::CustomScope(temp_scope), callee::DontAutorefArg) }) diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 1ede187245e6d..5e2a8792abf86 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -44,6 +44,7 @@ use middle::ty; use middle::subst::Subst; use middle::typeck; use middle::typeck::coherence::make_substs_for_receiver_types; +use middle::typeck::MethodCall; use util::ppaux::Repr; use middle::trans::type_::Type; @@ -120,7 +121,7 @@ fn trans<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> { match def { ast::DefFn(did, _) | ast::DefStaticMethod(did, ast::FromImpl(_), _) => { - fn_callee(bcx, trans_fn_ref(bcx, did, ref_expr.id, false)) + fn_callee(bcx, trans_fn_ref(bcx, did, ExprId(ref_expr.id))) } ast::DefStaticMethod(impl_did, ast::FromTrait(trait_did), @@ -134,10 +135,10 @@ fn trans<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> { assert!(ty::enum_variant_with_id(bcx.tcx(), tid, vid).args.len() > 0u); - fn_callee(bcx, trans_fn_ref(bcx, vid, ref_expr.id, false)) + fn_callee(bcx, trans_fn_ref(bcx, vid, ExprId(ref_expr.id))) } ast::DefStruct(def_id) => { - fn_callee(bcx, trans_fn_ref(bcx, def_id, ref_expr.id, false)) + fn_callee(bcx, trans_fn_ref(bcx, def_id, ExprId(ref_expr.id))) } ast::DefStatic(..) | ast::DefArg(..) | @@ -160,9 +161,7 @@ fn trans<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> { } } -pub fn trans_fn_ref(bcx: &Block, def_id: ast::DefId, - ref_id: ast::NodeId, is_method: bool) - -> ValueRef { +pub fn trans_fn_ref(bcx: &Block, def_id: ast::DefId, node: ExprOrMethodCall) -> ValueRef { /*! * * Translates a reference (with id `ref_id`) to the fn/method @@ -171,15 +170,18 @@ pub fn trans_fn_ref(bcx: &Block, def_id: ast::DefId, let _icx = push_ctxt("trans_fn_ref"); - let type_params = node_id_type_params(bcx, ref_id, is_method); - let vtables = node_vtables(bcx, ref_id); - debug!("trans_fn_ref(def_id={}, ref_id={:?}, type_params={}, vtables={})", - def_id.repr(bcx.tcx()), ref_id, type_params.repr(bcx.tcx()), + let type_params = node_id_type_params(bcx, node); + let vtables = match node { + ExprId(id) => node_vtables(bcx, id), + MethodCall(method_call) if method_call.autoderef == 0 => { + node_vtables(bcx, method_call.expr_id) + } + _ => None + }; + debug!("trans_fn_ref(def_id={}, node={:?}, type_params={}, vtables={})", + def_id.repr(bcx.tcx()), node, type_params.repr(bcx.tcx()), vtables.repr(bcx.tcx())); - trans_fn_ref_with_vtables(bcx, - def_id, - ref_id, - is_method, + trans_fn_ref_with_vtables(bcx, def_id, node, type_params.as_slice(), vtables) } @@ -191,7 +193,7 @@ fn trans_fn_ref_with_vtables_to_callee<'a>(bcx: &'a Block<'a>, vtables: Option) -> Callee<'a> { Callee {bcx: bcx, - data: Fn(trans_fn_ref_with_vtables(bcx, def_id, ref_id, false, + data: Fn(trans_fn_ref_with_vtables(bcx, def_id, ExprId(ref_id), type_params, vtables))} } @@ -241,8 +243,7 @@ fn resolve_default_method_vtables(bcx: &Block, pub fn trans_fn_ref_with_vtables( bcx: &Block, // def_id: ast::DefId, // def id of fn - ref_id: ast::NodeId, // node id of use of fn; may be zero if N/A - is_method: bool, + node: ExprOrMethodCall, // node id of use of fn; may be zero if N/A type_params: &[ty::t], // values for fn's ty params vtables: Option) // vtables for the call -> ValueRef { @@ -254,7 +255,7 @@ pub fn trans_fn_ref_with_vtables( * * - `bcx`: the current block where the reference to the fn occurs * - `def_id`: def id of the fn or method item being referenced - * - `ref_id`: node id of the reference to the fn/method, if applicable. + * - `node`: node id of the reference to the fn/method, if applicable. * This parameter may be zero; but, if so, the resulting value may not * have the right type, so it must be cast before being used. * - `type_params`: values for each of the fn/method's type parameters @@ -265,11 +266,11 @@ pub fn trans_fn_ref_with_vtables( let ccx = bcx.ccx(); let tcx = ccx.tcx; - debug!("trans_fn_ref_with_vtables(bcx={}, def_id={}, ref_id={:?}, \ + debug!("trans_fn_ref_with_vtables(bcx={}, def_id={}, node={:?}, \ type_params={}, vtables={})", bcx.to_str(), def_id.repr(bcx.tcx()), - ref_id, + node, type_params.repr(bcx.tcx()), vtables.repr(bcx.tcx())); @@ -380,19 +381,25 @@ pub fn trans_fn_ref_with_vtables( // Should be either intra-crate or inlined. assert_eq!(def_id.krate, ast::LOCAL_CRATE); + let ref_id = match node { + ExprId(id) if id != 0 => Some(id), + _ => None + }; + let (val, must_cast) = monomorphize::monomorphic_fn(ccx, def_id, &substs, vtables, self_vtables, - Some(ref_id)); + ref_id); let mut val = val; - if must_cast && ref_id != 0 { + if must_cast && node != ExprId(0) { // Monotype of the REFERENCE to the function (type params // are subst'd) - let ref_ty = if is_method { - let t = bcx.ccx().maps.method_map.borrow().get().get(&ref_id).ty; - monomorphize_type(bcx, t) - } else { - node_id_type(bcx, ref_id) + let ref_ty = match node { + ExprId(id) => node_id_type(bcx, id), + MethodCall(method_call) => { + let t = bcx.ccx().maps.method_map.borrow().get().get(&method_call).ty; + monomorphize_type(bcx, t) + } }; val = PointerCast( @@ -472,13 +479,14 @@ pub fn trans_method_call<'a>( -> &'a Block<'a> { let _icx = push_ctxt("trans_method_call"); debug!("trans_method_call(call_ex={})", call_ex.repr(bcx.tcx())); - let method_ty = bcx.ccx().maps.method_map.borrow().get().get(&call_ex.id).ty; + let method_call = MethodCall::expr(call_ex.id); + let method_ty = bcx.ccx().maps.method_map.borrow().get().get(&method_call).ty; trans_call_inner( bcx, Some(common::expr_info(call_ex)), monomorphize_type(bcx, method_ty), |cx, arg_cleanup_scope| { - meth::trans_method_callee(cx, call_ex.id, rcvr, arg_cleanup_scope) + meth::trans_method_callee(cx, method_call, Some(rcvr), arg_cleanup_scope) }, args, Some(dest)).bcx @@ -717,19 +725,16 @@ pub fn trans_call_inner<'a>( assert!(dest.is_some()); let mut llargs = Vec::new(); - bcx = trans_args(bcx, args, callee_ty, &mut llargs, - cleanup::CustomScope(arg_cleanup_scope), false); - fcx.pop_custom_cleanup_scope(arg_cleanup_scope); let arg_tys = match args { ArgExprs(a) => a.iter().map(|x| expr_ty(bcx, *x)).collect(), _ => fail!("expected arg exprs.") }; - bcx = foreign::trans_native_call(bcx, - callee_ty, - llfn, - opt_llretslot.unwrap(), - llargs.as_slice(), - arg_tys); + bcx = trans_args(bcx, args, callee_ty, &mut llargs, + cleanup::CustomScope(arg_cleanup_scope), false); + fcx.pop_custom_cleanup_scope(arg_cleanup_scope); + bcx = foreign::trans_native_call(bcx, callee_ty, + llfn, opt_llretslot.unwrap(), + llargs.as_slice(), arg_tys); } // If the caller doesn't care about the result of this fn call, @@ -754,8 +759,7 @@ pub fn trans_call_inner<'a>( pub enum CallArgs<'a> { ArgExprs(&'a [@ast::Expr]), - // HACK used only by trans_overloaded_op. - ArgAutorefSecond(&'a ast::Expr, Option<&'a ast::Expr>), + ArgOverloadedOp(Datum, Option<(Datum, ast::NodeId)>), ArgVals(&'a [ValueRef]) } @@ -778,40 +782,42 @@ fn trans_args<'a>(cx: &'a Block<'a>, match args { ArgExprs(arg_exprs) => { let num_formal_args = arg_tys.len(); - for (i, arg_expr) in arg_exprs.iter().enumerate() { + for (i, &arg_expr) in arg_exprs.iter().enumerate() { if i == 0 && ignore_self { continue; } let arg_ty = if i >= num_formal_args { assert!(variadic); - expr_ty_adjusted(cx, *arg_expr) + expr_ty_adjusted(cx, arg_expr) } else { *arg_tys.get(i) }; + + let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_expr)); llargs.push(unpack_result!(bcx, { - trans_arg_expr(bcx, arg_ty, *arg_expr, - arg_cleanup_scope, - DontAutorefArg) + trans_arg_datum(bcx, arg_ty, arg_datum, + arg_cleanup_scope, + DontAutorefArg) })); } } - ArgAutorefSecond(arg_expr, arg2) => { + ArgOverloadedOp(lhs, rhs) => { assert!(!variadic); llargs.push(unpack_result!(bcx, { - trans_arg_expr(bcx, *arg_tys.get(0), arg_expr, - arg_cleanup_scope, - DontAutorefArg) + trans_arg_datum(bcx, *arg_tys.get(0), lhs, + arg_cleanup_scope, + DontAutorefArg) })); - match arg2 { - Some(arg2_expr) => { + match rhs { + Some((rhs, rhs_id)) => { assert_eq!(arg_tys.len(), 2); llargs.push(unpack_result!(bcx, { - trans_arg_expr(bcx, *arg_tys.get(1), arg2_expr, - arg_cleanup_scope, - DoAutorefArg) + trans_arg_datum(bcx, *arg_tys.get(1), rhs, + arg_cleanup_scope, + DoAutorefArg(rhs_id)) })); } None => assert_eq!(arg_tys.len(), 1) @@ -827,26 +833,23 @@ fn trans_args<'a>(cx: &'a Block<'a>, pub enum AutorefArg { DontAutorefArg, - DoAutorefArg + DoAutorefArg(ast::NodeId) } -pub fn trans_arg_expr<'a>( +pub fn trans_arg_datum<'a>( bcx: &'a Block<'a>, formal_arg_ty: ty::t, - arg_expr: &ast::Expr, + arg_datum: Datum, arg_cleanup_scope: cleanup::ScopeId, autoref_arg: AutorefArg) -> Result<'a> { - let _icx = push_ctxt("trans_arg_expr"); + let _icx = push_ctxt("trans_arg_datum"); let mut bcx = bcx; let ccx = bcx.ccx(); - debug!("trans_arg_expr(formal_arg_ty=({}), arg_expr={})", - formal_arg_ty.repr(bcx.tcx()), - arg_expr.repr(bcx.tcx())); + debug!("trans_arg_datum({})", + formal_arg_ty.repr(bcx.tcx())); - // translate the arg expr to a datum - let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_expr)); let arg_datum_ty = arg_datum.ty; debug!(" arg datum: {}", arg_datum.to_str(bcx.ccx())); @@ -864,11 +867,11 @@ pub fn trans_arg_expr<'a>( } else { // FIXME(#3548) use the adjustments table match autoref_arg { - DoAutorefArg => { + DoAutorefArg(arg_id) => { // We will pass argument by reference // We want an lvalue, so that we can pass by reference and let arg_datum = unpack_datum!( - bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_expr.id)); + bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_id)); val = arg_datum.val; } DontAutorefArg => { @@ -898,6 +901,6 @@ pub fn trans_arg_expr<'a>( } } - debug!("--- trans_arg_expr passing {}", bcx.val_to_str(val)); - return rslt(bcx, val); + debug!("--- trans_arg_datum passing {}", bcx.val_to_str(val)); + rslt(bcx, val) } diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs index be5eab0e8eca7..d249bd8a894cc 100644 --- a/src/librustc/middle/trans/cleanup.rs +++ b/src/librustc/middle/trans/cleanup.rs @@ -19,7 +19,7 @@ use middle::trans::base; use middle::trans::build; use middle::trans::callee; use middle::trans::common; -use middle::trans::common::{Block, FunctionContext}; +use middle::trans::common::{Block, FunctionContext, ExprId}; use middle::trans::glue; use middle::trans::type_::Type; use middle::ty; @@ -673,7 +673,7 @@ impl<'a> CleanupHelperMethods<'a> for FunctionContext<'a> { // The exception handling personality function. let def_id = common::langcall(pad_bcx, None, "", EhPersonalityLangItem); - let llpersonality = callee::trans_fn_ref(pad_bcx, def_id, 0, false); + let llpersonality = callee::trans_fn_ref(pad_bcx, def_id, ExprId(0)); // The only landing pad clause will be 'cleanup' let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1u); diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 4194f3ff57c06..759e5e872d4be 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -805,22 +805,29 @@ pub fn expr_ty(bcx: &Block, ex: &ast::Expr) -> ty::t { pub fn expr_ty_adjusted(bcx: &Block, ex: &ast::Expr) -> ty::t { let tcx = bcx.tcx(); - let t = ty::expr_ty_adjusted(tcx, ex); + let t = ty::expr_ty_adjusted(tcx, ex, bcx.ccx().maps.method_map.borrow().get()); monomorphize_type(bcx, t) } -pub fn node_id_type_params(bcx: &Block, id: ast::NodeId, is_method: bool) -> Vec { +#[deriving(Eq)] +pub enum ExprOrMethodCall { + ExprId(ast::NodeId), + MethodCall(typeck::MethodCall) +} + +pub fn node_id_type_params(bcx: &Block, node: ExprOrMethodCall) -> Vec { let tcx = bcx.tcx(); - let params = if is_method { - bcx.ccx().maps.method_map.borrow().get().get(&id).substs.tps.clone() - } else { - ty::node_id_to_type_params(tcx, id) + let params = match node { + ExprId(id) => ty::node_id_to_type_params(tcx, id), + MethodCall(method_call) => { + bcx.ccx().maps.method_map.borrow().get().get(&method_call).substs.tps.clone() + } }; if !params.iter().all(|t| !ty::type_needs_infer(*t)) { bcx.sess().bug( - format!("type parameters for node {} include inference types: {}", - id, params.map(|t| bcx.ty_to_str(*t)).connect(","))); + format!("type parameters for node {:?} include inference types: {}", + node, params.map(|t| bcx.ty_to_str(*t)).connect(","))); } match bcx.fcx.param_substs { diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index c9317f18168f2..9d55084c7f4e7 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -190,7 +190,8 @@ pub fn const_expr(cx: @CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef let mut llconst = llconst; let mut inlineable = inlineable; let ety = ty::expr_ty(cx.tcx, e); - let ety_adjusted = ty::expr_ty_adjusted(cx.tcx, e); + let ety_adjusted = ty::expr_ty_adjusted(cx.tcx, e, + cx.maps.method_map.borrow().get()); let adjustment = { let adjustments = cx.tcx.adjustments.borrow(); adjustments.get().find_copy(&e.id) @@ -422,7 +423,8 @@ fn const_expr_unadjusted(cx: @CrateContext, e: &ast::Expr, }, true) } ast::ExprField(base, field, _) => { - let bt = ty::expr_ty_adjusted(cx.tcx, base); + let bt = ty::expr_ty_adjusted(cx.tcx, base, + cx.maps.method_map.borrow().get()); let brepr = adt::represent_type(cx, bt); let (bv, inlineable) = const_expr(cx, base, is_local); expr::with_field_tys(cx.tcx, bt, None, |discr, field_tys| { @@ -432,7 +434,8 @@ fn const_expr_unadjusted(cx: @CrateContext, e: &ast::Expr, } ast::ExprIndex(base, index) => { - let bt = ty::expr_ty_adjusted(cx.tcx, base); + let bt = ty::expr_ty_adjusted(cx.tcx, base, + cx.maps.method_map.borrow().get()); let (bv, inlineable) = const_expr(cx, base, is_local); let iv = match const_eval::eval_const_expr(cx.tcx, index) { const_eval::const_int(i) => i as u64, diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index be0faf2b6445d..056ac62183f90 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -64,6 +64,7 @@ use middle::ty::struct_fields; use middle::ty::{AutoBorrowObj, AutoDerefRef, AutoAddEnv, AutoObject, AutoUnsafe}; use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn}; use middle::ty; +use middle::typeck::MethodCall; use util::common::indenter; use util::ppaux::Repr; use util::nodemap::NodeMap; @@ -211,8 +212,11 @@ fn apply_adjustments<'a>(bcx: &'a Block<'a>, unpack_datum!(bcx, auto_slice_and_ref(bcx, expr, datum)) } Some(AutoBorrowFn(..)) => { - let adjusted_ty = ty::adjust_ty(bcx.tcx(), expr.span, - datum.ty, Some(adjustment)); + let adjusted_ty = ty::adjust_ty(bcx.tcx(), expr.span, expr.id, datum.ty, + Some(adjustment), |method_call| { + bcx.ccx().maps.method_map.borrow().get() + .find(&method_call).map(|method| method.ty) + }); unpack_datum!(bcx, auto_borrow_fn(bcx, adjusted_ty, datum)) } Some(AutoBorrowObj(..)) => { @@ -221,7 +225,8 @@ fn apply_adjustments<'a>(bcx: &'a Block<'a>, }; } AutoObject(..) => { - let adjusted_ty = ty::expr_ty_adjusted(bcx.tcx(), expr); + let adjusted_ty = ty::expr_ty_adjusted(bcx.tcx(), expr, + bcx.ccx().maps.method_map.borrow().get()); let scratch = rvalue_scratch_datum(bcx, adjusted_ty, "__adjust"); bcx = meth::trans_trait_cast( bcx, datum, expr.id, SaveIn(scratch.val)); @@ -231,30 +236,6 @@ fn apply_adjustments<'a>(bcx: &'a Block<'a>, debug!("after adjustments, datum={}", datum.to_str(bcx.ccx())); return DatumBlock {bcx: bcx, datum: datum}; - fn auto_ref<'a>(bcx: &'a Block<'a>, - datum: Datum, - expr: &ast::Expr) - -> DatumBlock<'a, Expr> { - let mut bcx = bcx; - - // Ensure cleanup of `datum` if not already scheduled and obtain - // a "by ref" pointer. - let lv_datum = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "autoref", expr.id)); - - // Compute final type. Note that we are loose with the region and - // mutability, since those things don't matter in trans. - let referent_ty = lv_datum.ty; - let ptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::ReStatic, referent_ty); - - // Get the pointer. - let llref = lv_datum.to_llref(); - - // Construct the resulting datum, using what was the "by ref" - // ValueRef of type `referent_ty` to be the "by value" ValueRef - // of type `&referent_ty`. - DatumBlock(bcx, Datum(llref, ptr_ty, RvalueExpr(Rvalue(ByValue)))) - } - fn auto_borrow_fn<'a>( bcx: &'a Block<'a>, adjusted_ty: ty::t, @@ -462,13 +443,10 @@ fn trans_datum_unadjusted<'a>(bcx: &'a Block<'a>, } ast::ExprLit(lit) => trans_immediate_lit(bcx, expr, (*lit).clone()), ast::ExprBinary(op, lhs, rhs) => { - // if overloaded, would be RvalueDpsExpr - assert!(!bcx.ccx().maps.method_map.borrow().get().contains_key(&expr.id)); - trans_binary(bcx, expr, op, lhs, rhs) } ast::ExprUnary(op, x) => { - trans_unary_datum(bcx, expr, op, x) + trans_unary(bcx, expr, op, x) } ast::ExprAddrOf(_, x) => { trans_addr_of(bcx, expr, x) @@ -789,15 +767,23 @@ fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>, } ast::ExprBinary(_, lhs, rhs) => { // if not overloaded, would be RvalueDatumExpr - trans_overloaded_op(bcx, expr, lhs, Some(&*rhs), Some(dest)).bcx + let lhs = unpack_datum!(bcx, trans(bcx, lhs)); + let rhs_datum = unpack_datum!(bcx, trans(bcx, rhs)); + trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), lhs, + Some((rhs_datum, rhs.id)), Some(dest)).bcx } ast::ExprUnary(_, subexpr) => { // if not overloaded, would be RvalueDatumExpr - trans_overloaded_op(bcx, expr, subexpr, None, Some(dest)).bcx + let arg = unpack_datum!(bcx, trans(bcx, subexpr)); + trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), + arg, None, Some(dest)).bcx } ast::ExprIndex(base, idx) => { // if not overloaded, would be RvalueDatumExpr - trans_overloaded_op(bcx, expr, base, Some(&*idx), Some(dest)).bcx + let base = unpack_datum!(bcx, trans(bcx, base)); + let idx_datum = unpack_datum!(bcx, trans(bcx, idx)); + trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), base, + Some((idx_datum, idx.id)), Some(dest)).bcx } ast::ExprCast(val, _) => { // DPS output mode means this is a trait cast: @@ -848,7 +834,7 @@ fn trans_def_dps_unadjusted<'a>( let variant_info = ty::enum_variant_with_id(ccx.tcx, tid, vid); if variant_info.args.len() > 0u { // N-ary variant. - let llfn = callee::trans_fn_ref(bcx, vid, ref_expr.id, false); + let llfn = callee::trans_fn_ref(bcx, vid, ExprId(ref_expr.id)); Store(bcx, llfn, lldest); return bcx; } else { @@ -888,7 +874,7 @@ fn trans_def_fn_unadjusted<'a>(bcx: &'a Block<'a>, ast::DefFn(did, _) | ast::DefStruct(did) | ast::DefVariant(_, did, _) | ast::DefStaticMethod(did, ast::FromImpl(_), _) => { - callee::trans_fn_ref(bcx, did, ref_expr.id, false) + callee::trans_fn_ref(bcx, did, ExprId(ref_expr.id)) } ast::DefStaticMethod(impl_did, ast::FromTrait(trait_did), _) => { meth::trans_static_method_callee(bcx, impl_did, @@ -1165,25 +1151,22 @@ fn trans_immediate_lit<'a>(bcx: &'a Block<'a>, immediate_rvalue_bcx(bcx, v, ty).to_expr_datumblock() } -fn trans_unary_datum<'a>( - bcx: &'a Block<'a>, - un_expr: &ast::Expr, - op: ast::UnOp, - sub_expr: &ast::Expr) - -> DatumBlock<'a, Expr> { +fn trans_unary<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + op: ast::UnOp, + sub_expr: &ast::Expr) + -> DatumBlock<'a, Expr> { let mut bcx = bcx; let _icx = push_ctxt("trans_unary_datum"); - let overloaded = { - let method_map = bcx.ccx().maps.method_map.borrow(); - method_map.get().contains_key(&un_expr.id) - }; + let method_call = MethodCall::expr(expr.id); + let overloaded = bcx.ccx().maps.method_map.borrow().get().contains_key(&method_call); // if overloaded, would be RvalueDpsExpr assert!(!overloaded || op == ast::UnDeref); - let un_ty = expr_ty(bcx, un_expr); + let un_ty = expr_ty(bcx, expr); - return match op { + match op { ast::UnNot => { let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); let llresult = if ty::type_is_bool(un_ty) { @@ -1218,15 +1201,10 @@ fn trans_unary_datum<'a>( trans_boxed_expr(bcx, un_ty, sub_expr, expr_ty(bcx, sub_expr), heap_exchange) } ast::UnDeref => { - if overloaded { - let r = trans_overloaded_op(bcx, un_expr, sub_expr, None, None); - DatumBlock(r.bcx, Datum(r.val, un_ty, LvalueExpr)) - } else { - let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); - deref_once(bcx, un_expr, datum, 0) - } + let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); + deref_once(bcx, expr, datum, 0) } - }; + } } fn trans_boxed_expr<'a>(bcx: &'a Block<'a>, @@ -1451,41 +1429,43 @@ fn trans_lazy_binop<'a>( return immediate_rvalue_bcx(join, phi, binop_ty).to_expr_datumblock(); } -fn trans_binary<'a>( - bcx: &'a Block<'a>, - binop_expr: &ast::Expr, - op: ast::BinOp, - lhs: &ast::Expr, - rhs: &ast::Expr) - -> DatumBlock<'a, Expr> { +fn trans_binary<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + op: ast::BinOp, + lhs: &ast::Expr, + rhs: &ast::Expr) + -> DatumBlock<'a, Expr> { let _icx = push_ctxt("trans_binary"); let ccx = bcx.ccx(); + // if overloaded, would be RvalueDpsExpr + assert!(!ccx.maps.method_map.borrow().get().contains_key(&MethodCall::expr(expr.id))); + match op { ast::BiAnd => { - trans_lazy_binop(bcx, binop_expr, lazy_and, lhs, rhs) + trans_lazy_binop(bcx, expr, lazy_and, lhs, rhs) } ast::BiOr => { - trans_lazy_binop(bcx, binop_expr, lazy_or, lhs, rhs) + trans_lazy_binop(bcx, expr, lazy_or, lhs, rhs) } _ => { let mut bcx = bcx; let lhs_datum = unpack_datum!(bcx, trans(bcx, lhs)); let rhs_datum = unpack_datum!(bcx, trans(bcx, rhs)); - let binop_ty = expr_ty(bcx, binop_expr); + let binop_ty = expr_ty(bcx, expr); debug!("trans_binary (expr {}): lhs_datum={}", - binop_expr.id, + expr.id, lhs_datum.to_str(ccx)); let lhs_ty = lhs_datum.ty; let lhs = lhs_datum.to_llscalarish(bcx); debug!("trans_binary (expr {}): rhs_datum={}", - binop_expr.id, + expr.id, rhs_datum.to_str(ccx)); let rhs_ty = rhs_datum.ty; let rhs = rhs_datum.to_llscalarish(bcx); - trans_eager_binop(bcx, binop_expr, binop_ty, op, + trans_eager_binop(bcx, expr, binop_ty, op, lhs_ty, lhs, rhs_ty, rhs) } } @@ -1494,21 +1474,22 @@ fn trans_binary<'a>( fn trans_overloaded_op<'a, 'b>( bcx: &'a Block<'a>, expr: &ast::Expr, - rcvr: &'b ast::Expr, - arg: Option<&'b ast::Expr>, + method_call: MethodCall, + lhs: Datum, + rhs: Option<(Datum, ast::NodeId)>, dest: Option) -> Result<'a> { - let method_ty = bcx.ccx().maps.method_map.borrow().get().get(&expr.id).ty; + let method_ty = bcx.ccx().maps.method_map.borrow().get().get(&method_call).ty; callee::trans_call_inner(bcx, Some(expr_info(expr)), monomorphize_type(bcx, method_ty), |bcx, arg_cleanup_scope| { meth::trans_method_callee(bcx, - expr.id, - rcvr, + method_call, + None, arg_cleanup_scope) }, - callee::ArgAutorefSecond(rcvr, arg), + callee::ArgOverloadedOp(lhs, rhs), dest) } @@ -1666,10 +1647,7 @@ fn trans_assign_op<'a>( debug!("trans_assign_op(expr={})", bcx.expr_to_str(expr)); // User-defined operator methods cannot be used with `+=` etc right now - assert!({ - let method_map = bcx.ccx().maps.method_map.borrow(); - !method_map.get().find(&expr.id).is_some() - }); + assert!(!bcx.ccx().maps.method_map.borrow().get().contains_key(&MethodCall::expr(expr.id))); // Evaluate LHS (destination), which should be an lvalue let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign_op")); @@ -1748,6 +1726,30 @@ fn trans_log_level<'a>(bcx: &'a Block<'a>) -> DatumBlock<'a, Expr> { immediate_rvalue_bcx(bcx, Load(bcx, global), ty::mk_u32()).to_expr_datumblock() } +fn auto_ref<'a>(bcx: &'a Block<'a>, + datum: Datum, + expr: &ast::Expr) + -> DatumBlock<'a, Expr> { + let mut bcx = bcx; + + // Ensure cleanup of `datum` if not already scheduled and obtain + // a "by ref" pointer. + let lv_datum = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "autoref", expr.id)); + + // Compute final type. Note that we are loose with the region and + // mutability, since those things don't matter in trans. + let referent_ty = lv_datum.ty; + let ptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::ReStatic, referent_ty); + + // Get the pointer. + let llref = lv_datum.to_llref(); + + // Construct the resulting datum, using what was the "by ref" + // ValueRef of type `referent_ty` to be the "by value" ValueRef + // of type `&referent_ty`. + DatumBlock(bcx, Datum(llref, ptr_ty, RvalueExpr(Rvalue(ByValue)))) +} + fn deref_multiple<'a>(bcx: &'a Block<'a>, expr: &ast::Expr, datum: Datum, @@ -1777,6 +1779,28 @@ fn deref_once<'a>(bcx: &'a Block<'a>, let mut bcx = bcx; + let method_call = MethodCall { + expr_id: expr.id, + autoderef: derefs as u32 + }; + let method_ty = ccx.maps.method_map.borrow().get() + .find(&method_call).map(|method| method.ty); + let datum = match method_ty { + Some(method_ty) => { + let datum = if derefs == 0 { + datum + } else { + // Always perform an AutoPtr when applying an overloaded auto-deref. + unpack_datum!(bcx, auto_ref(bcx, datum, expr)) + }; + let val = unpack_result!(bcx, trans_overloaded_op(bcx, expr, method_call, + datum, None, None)); + let ref_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty)); + Datum(val, ref_ty, RvalueExpr(Rvalue(ByValue))) + } + None => datum + }; + let r = match ty::get(datum.ty).sty { ty::ty_uniq(content_ty) => { deref_owned_pointer(bcx, expr, datum, content_ty) @@ -1805,55 +1829,6 @@ fn deref_once<'a>(bcx: &'a Block<'a>, DatumBlock(bcx, Datum(ptr, content_ty, LvalueExpr)) } - ty::ty_enum(..) | - ty::ty_struct(..) => { - // Subtle efficiency note: In the case where we have a - // newtype struct where the struct itself does not have a - // dtor, but the contents do, we could avoid forcing the - // data into Lvalue and instead return an Rvalue. But it - // doesn't seem worth the trouble. - let datum = unpack_datum!(bcx, ensure_cleanup(bcx, expr, datum)); - - // Unlike the pointer case above, we generate an - // rvalue datum if we are given an rvalue. There are - // two reasons that this makes sense here: - // - // 1. dereferencing a struct does not actually perform a - // pointer load and hence the resulting value is not - // naturally by reference, as would be required by an - // lvalue result. - // - // 2. the struct always owns its contents, and hence and does not - // itself have a dtor (else it would be in lvalue mode). - let repr = adt::represent_type(ccx, datum.ty); - let ty = adt::deref_ty(ccx, repr); - let Datum { val, kind, .. } = datum; - let r = match kind { - LvalueExpr => { - Datum { - val: adt::trans_field_ptr(bcx, repr, val, 0, 0), - ty: ty, - kind: LvalueExpr - } - } - RvalueExpr(Rvalue { mode: ByRef }) => { - Datum { - val: adt::trans_field_ptr(bcx, repr, val, 0, 0), - ty: ty, - kind: RvalueExpr(Rvalue(ByValue)) - } - } - RvalueExpr(Rvalue { mode: ByValue }) => { - Datum { - val: ExtractValue(bcx, val, 0), - ty: ty, - kind: RvalueExpr(Rvalue(ByValue)) - } - } - }; - DatumBlock(bcx, r) - } - _ => { bcx.tcx().sess.span_bug( expr.span, @@ -1867,25 +1842,6 @@ fn deref_once<'a>(bcx: &'a Block<'a>, return r; - fn ensure_cleanup<'a>(mut bcx: &'a Block<'a>, - expr: &ast::Expr, - datum: Datum) - -> DatumBlock<'a, Expr> { - /*! - * If the datum contains data that needs to be dropped, - * convert it to an lvalue, thus ensuring that cleanup - * is scheduled. - */ - - if ty::type_needs_drop(bcx.tcx(), datum.ty) { - let lv_datum = unpack_datum!( - bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); - DatumBlock(bcx, lv_datum.to_expr_datum()) - } else { - DatumBlock(bcx, datum) - } - } - fn deref_owned_pointer<'a>(bcx: &'a Block<'a>, expr: &ast::Expr, datum: Datum, diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index cfc88d163c0b0..b3d69043d33e3 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -29,6 +29,7 @@ use middle::trans::type_::Type; use middle::trans::type_of::*; use middle::ty; use middle::typeck; +use middle::typeck::MethodCall; use util::common::indenter; use util::ppaux::Repr; @@ -93,21 +94,22 @@ pub fn trans_method(ccx: @CrateContext, method: &ast::Method, pub fn trans_method_callee<'a>( bcx: &'a Block<'a>, - expr_id: ast::NodeId, - this: &ast::Expr, + method_call: MethodCall, + self_expr: Option<&ast::Expr>, arg_cleanup_scope: cleanup::ScopeId) -> Callee<'a> { let _icx = push_ctxt("meth::trans_method_callee"); let (origin, method_ty) = match bcx.ccx().maps.method_map - .borrow().get().find(&expr_id) { + .borrow().get().find(&method_call) { Some(method) => { - debug!("trans_method_callee(expr_id={:?}, method={})", - expr_id, method.repr(bcx.tcx())); + debug!("trans_method_callee({:?}, method={})", + method_call, method.repr(bcx.tcx())); (method.origin, method.ty) } None => { - bcx.tcx().sess.span_bug(this.span, "method call expr wasn't in method map") + bcx.tcx().sess.span_bug(bcx.tcx().map.span(method_call.expr_id), + "method call expr wasn't in method map") } }; @@ -115,7 +117,7 @@ pub fn trans_method_callee<'a>( typeck::MethodStatic(did) => { Callee { bcx: bcx, - data: Fn(callee::trans_fn_ref(bcx, did, expr_id, true)) + data: Fn(callee::trans_fn_ref(bcx, did, MethodCall(method_call))) } } typeck::MethodParam(typeck::MethodParam { @@ -131,7 +133,7 @@ pub fn trans_method_callee<'a>( trait_id); let vtbl = find_vtable(bcx.tcx(), substs, p, b); - trans_monomorphized_callee(bcx, expr_id, + trans_monomorphized_callee(bcx, method_call, trait_id, off, vtbl) } // how to get rid of this? @@ -140,10 +142,18 @@ pub fn trans_method_callee<'a>( } typeck::MethodObject(ref mt) => { + let self_expr = match self_expr { + Some(self_expr) => self_expr, + None => { + bcx.tcx().sess.span_bug(bcx.tcx().map.span(method_call.expr_id), + "self expr wasn't provided for trait object \ + callee (trying to call overloaded op?)") + } + }; trans_trait_callee(bcx, monomorphize_type(bcx, method_ty), mt.real_index, - this, + self_expr, arg_cleanup_scope) } } @@ -209,13 +219,10 @@ pub fn trans_static_method_callee(bcx: &Block, let mth_id = method_with_name(ccx, impl_did, mname); let (callee_substs, callee_origins) = combine_impl_and_methods_tps( - bcx, mth_id, expr_id, false, + bcx, mth_id, ExprId(expr_id), rcvr_substs.as_slice(), rcvr_origins); - let llfn = trans_fn_ref_with_vtables(bcx, - mth_id, - expr_id, - false, + let llfn = trans_fn_ref_with_vtables(bcx, mth_id, ExprId(expr_id), callee_substs.as_slice(), Some(callee_origins)); @@ -254,7 +261,7 @@ pub fn method_with_name(ccx: &CrateContext, } fn trans_monomorphized_callee<'a>(bcx: &'a Block<'a>, - expr_id: ast::NodeId, + method_call: MethodCall, trait_id: ast::DefId, n_method: uint, vtbl: typeck::vtable_origin) @@ -270,14 +277,13 @@ fn trans_monomorphized_callee<'a>(bcx: &'a Block<'a>, // those from the impl and those from the method: let (callee_substs, callee_origins) = combine_impl_and_methods_tps( - bcx, mth_id, expr_id, true, + bcx, mth_id, MethodCall(method_call), rcvr_substs.as_slice(), rcvr_origins); // translate the function let llfn = trans_fn_ref_with_vtables(bcx, mth_id, - expr_id, - true, + MethodCall(method_call), callee_substs.as_slice(), Some(callee_origins)); @@ -291,8 +297,7 @@ fn trans_monomorphized_callee<'a>(bcx: &'a Block<'a>, fn combine_impl_and_methods_tps(bcx: &Block, mth_did: ast::DefId, - expr_id: ast::NodeId, - is_method: bool, + node: ExprOrMethodCall, rcvr_substs: &[ty::t], rcvr_origins: typeck::vtable_res) -> (Vec , typeck::vtable_res) { @@ -316,7 +321,7 @@ fn combine_impl_and_methods_tps(bcx: &Block, let ccx = bcx.ccx(); let method = ty::method(ccx.tcx, mth_did); let n_m_tps = method.generics.type_param_defs().len(); - let node_substs = node_id_type_params(bcx, expr_id, is_method); + let node_substs = node_id_type_params(bcx, node); debug!("rcvr_substs={:?}", rcvr_substs.repr(ccx.tcx)); let ty_substs = vec_ng::append(Vec::from_slice(rcvr_substs), @@ -328,7 +333,14 @@ fn combine_impl_and_methods_tps(bcx: &Block, // Now, do the same work for the vtables. The vtables might not // exist, in which case we need to make them. - let r_m_origins = match node_vtables(bcx, expr_id) { + let vtables = match node { + ExprId(id) => node_vtables(bcx, id), + MethodCall(method_call) if method_call.autoderef == 0 => { + node_vtables(bcx, method_call.expr_id) + } + _ => None + }; + let r_m_origins = match vtables { Some(vt) => vt, None => @Vec::from_elem(node_substs.len(), @Vec::new()) }; @@ -555,7 +567,7 @@ fn emit_vtable_methods(bcx: &Block, token::get_ident(ident)); C_null(Type::nil().ptr_to()) } else { - trans_fn_ref_with_vtables(bcx, m_id, 0, false, substs, Some(vtables)) + trans_fn_ref_with_vtables(bcx, m_id, ExprId(0), substs, Some(vtables)) } }) } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 7193f6576584b..33b246539341b 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -23,6 +23,7 @@ use middle::resolve_lifetime; use middle::ty; use middle::subst::Subst; use middle::typeck; +use middle::typeck::{MethodCall, MethodCallee, MethodMap}; use middle::ty_fold; use middle::ty_fold::TypeFolder; use middle; @@ -30,7 +31,7 @@ use util::ppaux::{note_and_explain_region, bound_region_ptr_to_str}; use util::ppaux::{trait_store_to_str, ty_to_str, vstore_to_str}; use util::ppaux::{Repr, UserString}; use util::common::{indenter}; -use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; +use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet, FnvHashMap}; use std::cast; use std::cell::{Cell, RefCell}; @@ -258,10 +259,7 @@ pub struct ctxt_ { diag: @syntax::diagnostic::SpanHandler, // Specifically use a speedy hash algorithm for this hash map, it's used // quite often. - #[cfg(stage0)] - interner: RefCell>, - #[cfg(not(stage0))] - interner: RefCell>, + interner: RefCell>, next_id: Cell, cstore: @metadata::cstore::CStore, sess: session::Session, @@ -1091,19 +1089,11 @@ pub fn mk_ctxt(s: session::Session, region_maps: middle::region::RegionMaps, lang_items: @middle::lang_items::LanguageItems) -> ctxt { - #[cfg(stage0)] - fn hasher() -> HashMap { - HashMap::new() - } - #[cfg(not(stage0))] - fn hasher() -> HashMap { - HashMap::with_hasher(::util::nodemap::FnvHasher) - } @ctxt_ { named_region_map: named_region_map, item_variance_map: RefCell::new(DefIdMap::new()), diag: s.diagnostic(), - interner: RefCell::new(hasher()), + interner: RefCell::new(FnvHashMap::new()), next_id: Cell::new(primitives::LAST_PRIMITIVE_ID), cstore: s.cstore, sess: s, @@ -2710,50 +2700,23 @@ pub fn type_param(ty: t) -> Option { // The parameter `explicit` indicates if this is an *explicit* dereference. // Some types---notably unsafe ptrs---can only be dereferenced explicitly. pub fn deref(t: t, explicit: bool) -> Option { - deref_sty(&get(t).sty, explicit) -} - -pub fn deref_sty(sty: &sty, explicit: bool) -> Option { - match *sty { - ty_box(typ) | ty_uniq(typ) => { - Some(mt { - ty: typ, - mutbl: ast::MutImmutable, - }) - } - - ty_rptr(_, mt) => { - Some(mt) - } - - ty_ptr(mt) if explicit => { - Some(mt) - } - + match get(t).sty { + ty_box(typ) | ty_uniq(typ) => Some(mt { + ty: typ, + mutbl: ast::MutImmutable, + }), + ty_rptr(_, mt) => Some(mt), + ty_ptr(mt) if explicit => Some(mt), _ => None } } -pub fn type_autoderef(t: t) -> t { - let mut t = t; - loop { - match deref(t, false) { - None => return t, - Some(mt) => t = mt.ty - } - } -} - // Returns the type and mutability of t[i] pub fn index(t: t) -> Option { - index_sty(&get(t).sty) -} - -pub fn index_sty(sty: &sty) -> Option { - match *sty { - ty_vec(mt, _) => Some(mt), - ty_str(_) => Some(mt {ty: mk_u8(), mutbl: ast::MutImmutable}), - _ => None + match get(t).sty { + ty_vec(mt, _) => Some(mt), + ty_str(_) => Some(mt {ty: mk_u8(), mutbl: ast::MutImmutable}), + _ => None } } @@ -2964,7 +2927,10 @@ pub fn expr_ty_opt(cx: ctxt, expr: &ast::Expr) -> Option { return node_id_to_type_opt(cx, expr.id); } -pub fn expr_ty_adjusted(cx: ctxt, expr: &ast::Expr) -> t { +pub fn expr_ty_adjusted(cx: ctxt, + expr: &ast::Expr, + method_map: &FnvHashMap) + -> t { /*! * * Returns the type of `expr`, considering any `AutoAdjustment` @@ -2979,11 +2945,10 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: &ast::Expr) -> t { */ let unadjusted_ty = expr_ty(cx, expr); - let adjustment = { - let adjustments = cx.adjustments.borrow(); - adjustments.get().find_copy(&expr.id) - }; - adjust_ty(cx, expr.span, unadjusted_ty, adjustment) + let adjustment = cx.adjustments.borrow().get().find_copy(&expr.id); + adjust_ty(cx, expr.span, expr.id, unadjusted_ty, adjustment, |method_call| { + method_map.find(&method_call).map(|method| method.ty) + }) } pub fn expr_span(cx: ctxt, id: NodeId) -> Span { @@ -3026,14 +2991,14 @@ pub fn local_var_name_str(cx: ctxt, id: NodeId) -> InternedString { pub fn adjust_ty(cx: ctxt, span: Span, + expr_id: ast::NodeId, unadjusted_ty: ty::t, - adjustment: Option<@AutoAdjustment>) + adjustment: Option<@AutoAdjustment>, + method_type: |MethodCall| -> Option) -> ty::t { /*! See `expr_ty_adjusted` */ return match adjustment { - None => unadjusted_ty, - Some(adjustment) => { match *adjustment { AutoAddEnv(r, s) => { @@ -3062,7 +3027,13 @@ pub fn adjust_ty(cx: ctxt, if !ty::type_is_error(adjusted_ty) { for i in range(0, adj.autoderefs) { - match ty::deref(adjusted_ty, true) { + match method_type(MethodCall::autoderef(expr_id, i as u32)) { + Some(method_ty) => { + adjusted_ty = ty_fn_ret(method_ty); + } + None => {} + } + match deref(adjusted_ty, true) { Some(mt) => { adjusted_ty = mt.ty; } None => { cx.sess.span_bug( @@ -3130,6 +3101,7 @@ pub fn adjust_ty(cx: ctxt, } } } + None => unadjusted_ty }; fn borrow_vec(cx: ctxt, span: Span, @@ -3274,7 +3246,7 @@ pub fn resolve_expr(tcx: ctxt, expr: &ast::Expr) -> ast::Def { } pub fn expr_is_lval(tcx: ctxt, - method_map: typeck::MethodMap, + method_map: MethodMap, e: &ast::Expr) -> bool { match expr_kind(tcx, method_map, e) { LvalueExpr => true, @@ -3295,20 +3267,17 @@ pub enum ExprKind { } pub fn expr_kind(tcx: ctxt, - method_map: typeck::MethodMap, + method_map: MethodMap, expr: &ast::Expr) -> ExprKind { - { - let method_map = method_map.borrow(); - if method_map.get().contains_key(&expr.id) { - // Overloaded operations are generally calls, and hence they are - // generated via DPS. However, assign_op (e.g., `x += y`) is an - // exception, as its result is always unit. - return match expr.node { - ast::ExprAssignOp(..) => RvalueStmtExpr, - ast::ExprUnary(ast::UnDeref, _) => LvalueExpr, - _ => RvalueDpsExpr - }; - } + if method_map.borrow().get().contains_key(&MethodCall::expr(expr.id)) { + // Overloaded operations are generally calls, and hence they are + // generated via DPS. However, assign_op (e.g., `x += y`) is an + // exception, as its result is always unit. + return match expr.node { + ast::ExprAssignOp(..) => RvalueStmtExpr, + ast::ExprUnary(ast::UnDeref, _) => LvalueExpr, + _ => RvalueDpsExpr + }; } match expr.node { diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 114c385b5a0c4..89658b32be89a 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -84,9 +84,7 @@ use middle::subst::Subst; use middle::ty::*; use middle::ty; use middle::typeck::astconv::AstConv; -use middle::typeck::check::{FnCtxt, impl_self_ty}; -use middle::typeck::check::{structurally_resolved_type}; -use middle::typeck::check::vtable; +use middle::typeck::check::{FnCtxt, PreferMutLvalue, impl_self_ty}; use middle::typeck::check; use middle::typeck::infer; use middle::typeck::MethodCallee; @@ -106,6 +104,7 @@ use syntax::ast::{DefId, SelfValue, SelfRegion}; use syntax::ast::{SelfUniq, SelfStatic}; use syntax::ast::{MutMutable, MutImmutable}; use syntax::ast; +use syntax::codemap::Span; use syntax::parse::token; #[deriving(Eq)] @@ -120,23 +119,23 @@ pub enum AutoderefReceiverFlag { DontAutoderefReceiver, } -pub fn lookup( +pub fn lookup<'a>( fcx: @FnCtxt, // In a call `a.b::(...)`: expr: &ast::Expr, // The expression `a.b(...)`. - self_expr: &ast::Expr, // The expression `a`. + self_expr: &'a ast::Expr, // The expression `a`. m_name: ast::Name, // The name `b`. self_ty: ty::t, // The type of `a`. - supplied_tps: &[ty::t], // The list of types X, Y, ... . + supplied_tps: &'a [ty::t], // The list of types X, Y, ... . deref_args: check::DerefArgs, // Whether we autopointer first. check_traits: CheckTraitsFlag, // Whether we check traits only. autoderef_receiver: AutoderefReceiverFlag) -> Option { let lcx = LookupContext { fcx: fcx, - expr: expr, - self_expr: self_expr, + span: expr.span, + self_expr: Some(self_expr), m_name: m_name, supplied_tps: supplied_tps, impl_dups: @RefCell::new(HashSet::new()), @@ -147,7 +146,6 @@ pub fn lookup( autoderef_receiver: autoderef_receiver, }; - let self_ty = structurally_resolved_type(fcx, self_expr.span, self_ty); debug!("method lookup(self_ty={}, expr={}, self_expr={})", self_ty.repr(fcx.tcx()), expr.repr(fcx.tcx()), self_expr.repr(fcx.tcx())); @@ -162,25 +160,25 @@ pub fn lookup( debug!("searching extension candidates"); lcx.reset_candidates(); lcx.push_bound_candidates(self_ty, None); - lcx.push_extension_candidates(); + lcx.push_extension_candidates(expr.id); return lcx.search(self_ty); } -pub fn lookup_in_trait( +pub fn lookup_in_trait<'a>( fcx: @FnCtxt, // In a call `a.b::(...)`: - expr: &ast::Expr, // The expression `a.b(...)`. - self_expr: &ast::Expr, // The expression `a`. + 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: &[ty::t], // The list of types X, Y, ... . + supplied_tps: &'a [ty::t], // The list of types X, Y, ... . autoderef_receiver: AutoderefReceiverFlag) -> Option { let lcx = LookupContext { fcx: fcx, - expr: expr, + span: span, self_expr: self_expr, m_name: m_name, supplied_tps: supplied_tps, @@ -192,20 +190,18 @@ pub fn lookup_in_trait( autoderef_receiver: autoderef_receiver, }; - let self_ty = structurally_resolved_type(fcx, self_expr.span, self_ty); - debug!("method lookup_in_trait(self_ty={}, expr={}, self_expr={})", - self_ty.repr(fcx.tcx()), expr.repr(fcx.tcx()), - self_expr.repr(fcx.tcx())); + debug!("method lookup_in_trait(self_ty={}, self_expr={})", + self_ty.repr(fcx.tcx()), self_expr.map(|e| e.repr(fcx.tcx()))); lcx.push_bound_candidates(self_ty, Some(trait_did)); lcx.push_extension_candidate(trait_did); lcx.search(self_ty) } -pub struct LookupContext<'a> { +struct LookupContext<'a> { fcx: @FnCtxt, - expr: &'a ast::Expr, - self_expr: &'a ast::Expr, + span: Span, + self_expr: Option<&'a ast::Expr>, m_name: ast::Name, supplied_tps: &'a [ty::t], impl_dups: @RefCell>, @@ -221,7 +217,7 @@ pub struct LookupContext<'a> { * is of a suitable type. */ #[deriving(Clone)] -pub struct Candidate { +struct Candidate { rcvr_match_condition: RcvrMatchCondition, rcvr_substs: ty::substs, method_ty: @ty::Method, @@ -244,36 +240,35 @@ pub enum RcvrMatchCondition { impl<'a> LookupContext<'a> { fn search(&self, self_ty: ty::t) -> Option { - let mut self_ty = self_ty; - let mut autoderefs = 0; - loop { + let span = self.self_expr.map_or(self.span, |e| e.span); + let self_expr_id = self.self_expr.map(|e| e.id); + let (self_ty, autoderefs, result) = + check::autoderef(self.fcx, span, self_ty, self_expr_id, + PreferMutLvalue, |self_ty, autoderefs| { + debug!("loop: self_ty={} autoderefs={}", self.ty_to_str(self_ty), autoderefs); match self.deref_args { check::DontDerefArgs => { - match self.search_for_autoderefd_method(self_ty, - autoderefs) { - Some(mme) => { return Some(mme); } + match self.search_for_autoderefd_method(self_ty, autoderefs) { + Some(result) => return Some(Some(result)), None => {} } - match self.search_for_autoptrd_method(self_ty, - autoderefs) { - Some(mme) => { return Some(mme); } + match self.search_for_autoptrd_method(self_ty, autoderefs) { + Some(result) => return Some(Some(result)), None => {} } } check::DoDerefArgs => { - match self.search_for_autoptrd_method(self_ty, - autoderefs) { - Some(mme) => { return Some(mme); } + match self.search_for_autoptrd_method(self_ty, autoderefs) { + Some(result) => return Some(Some(result)), None => {} } - match self.search_for_autoderefd_method(self_ty, - autoderefs) { - Some(mme) => { return Some(mme); } + match self.search_for_autoderefd_method(self_ty, autoderefs) { + Some(result) => return Some(Some(result)), None => {} } } @@ -281,30 +276,15 @@ impl<'a> LookupContext<'a> { // Don't autoderef if we aren't supposed to. if self.autoderef_receiver == DontAutoderefReceiver { - break; - } - - // Otherwise, perform autoderef. - match self.deref(self_ty) { - None => { break; } - Some(ty) => { - self_ty = ty; - autoderefs += 1; - } + Some(None) + } else { + None } - } - - self.search_for_autosliced_method(self_ty, autoderefs) - } + }); - fn deref(&self, ty: ty::t) -> Option { - match ty::deref(ty, false) { - None => None, - Some(t) => { - Some(structurally_resolved_type(self.fcx, - self.self_expr.span, - t.ty)) - } + match result { + Some(Some(result)) => Some(result), + _ => self.search_for_autosliced_method(self_ty, autoderefs) } } @@ -326,8 +306,8 @@ impl<'a> LookupContext<'a> { * we'll want to find the inherent impls for `C`. */ - let mut self_ty = self_ty; - loop { + let span = self.self_expr.map_or(self.span, |e| e.span); + check::autoderef(self.fcx, span, self_ty, None, PreferMutLvalue, |self_ty, _| { match get(self_ty).sty { ty_trait(did, ref substs, _, _, _) => { self.push_inherent_candidates_from_object(did, substs); @@ -341,19 +321,18 @@ impl<'a> LookupContext<'a> { _ => { /* No inherent methods in these types */ } } - // n.b.: Generally speaking, we only loop if we hit the - // fallthrough case in the match above. The exception - // would be newtype enums. - self_ty = match self.deref(self_ty) { - None => { return; } - Some(ty) => { ty } + // Don't autoderef if we aren't supposed to. + if self.autoderef_receiver == DontAutoderefReceiver { + Some(()) + } else { + None } - } + }); } fn push_bound_candidates(&self, self_ty: ty::t, restrict_to: Option) { - let mut self_ty = self_ty; - loop { + let span = self.self_expr.map_or(self.span, |e| e.span); + check::autoderef(self.fcx, span, self_ty, None, PreferMutLvalue, |self_ty, _| { match get(self_ty).sty { ty_param(p) => { self.push_inherent_candidates_from_param(self_ty, restrict_to, p); @@ -366,11 +345,13 @@ impl<'a> LookupContext<'a> { _ => { /* No bound methods in these types */ } } - self_ty = match self.deref(self_ty) { - None => { return; } - Some(ty) => { ty } + // Don't autoderef if we aren't supposed to. + if self.autoderef_receiver == DontAutoderefReceiver { + Some(()) + } else { + None } - } + }); } fn push_extension_candidate(&self, trait_did: DefId) { @@ -386,11 +367,11 @@ impl<'a> LookupContext<'a> { } } - fn push_extension_candidates(&self) { + fn push_extension_candidates(&self, expr_id: ast::NodeId) { // If the method being called is associated with a trait, then // find all the impls of that trait. Each of those are // candidates. - let opt_applicable_traits = self.fcx.ccx.trait_map.find(&self.expr.id); + let opt_applicable_traits = self.fcx.ccx.trait_map.find(&expr_id); for applicable_traits in opt_applicable_traits.move_iter() { for trait_did in applicable_traits.iter() { self.push_extension_candidate(*trait_did); @@ -591,8 +572,8 @@ impl<'a> LookupContext<'a> { } fn push_candidates_from_impl(&self, - candidates: &mut Vec , - impl_info: &ty::Impl) { + candidates: &mut Vec, + impl_info: &ty::Impl) { { let mut impl_dups = self.impl_dups.borrow_mut(); if !impl_dups.get().insert(impl_info.did) { @@ -619,12 +600,12 @@ impl<'a> LookupContext<'a> { // determine the `self` of the impl with fresh // variables for each parameter: - let location_info = &vtable::location_info_for_expr(self.self_expr); + let span = self.self_expr.map_or(self.span, |e| e.span); let vcx = self.fcx.vtable_context(); let ty::ty_param_substs_and_ty { substs: impl_substs, ty: impl_ty - } = impl_self_ty(&vcx, location_info, impl_info.did); + } = impl_self_ty(&vcx, span, impl_info.did); candidates.push(Candidate { rcvr_match_condition: RcvrMatchesIfSubtype(impl_ty), @@ -638,28 +619,45 @@ impl<'a> LookupContext<'a> { // Candidate selection (see comment at start of file) fn search_for_autoderefd_method(&self, - self_ty: ty::t, - autoderefs: uint) - -> Option { - let (self_ty, autoadjust) = + self_ty: ty::t, + autoderefs: uint) + -> Option { + let (self_ty, auto_deref_ref) = self.consider_reborrow(self_ty, autoderefs); + + // HACK(eddyb) only overloaded auto-deref calls should be missing + // adjustments, because we imply an AutoPtr adjustment for them. + let adjustment = match auto_deref_ref { + ty::AutoDerefRef { + autoderefs: 0, + autoref: Some(ty::AutoPtr(..)) + } => None, + _ => match self.self_expr { + Some(expr) => Some((expr.id, @ty::AutoDerefRef(auto_deref_ref))), + None => return None + } + }; + match self.search_for_method(self_ty) { None => None, - Some(mme) => { + Some(method) => { debug!("(searching for autoderef'd method) writing \ - adjustment ({}) to {}", - autoderefs, - self.self_expr.id); - self.fcx.write_adjustment(self.self_expr.id, @autoadjust); - Some(mme) + adjustment {:?}", adjustment); + match adjustment { + Some((self_expr_id, adj)) => { + self.fcx.write_adjustment(self_expr_id, adj); + } + None => {} + } + Some(method) } } } fn consider_reborrow(&self, - self_ty: ty::t, - autoderefs: uint) - -> (ty::t, ty::AutoAdjustment) { + self_ty: ty::t, + autoderefs: uint) + -> (ty::t, ty::AutoDerefRef) { /*! * In the event that we are invoking a method with a receiver * of a borrowed type like `&T`, `&mut T`, or `&mut [T]`, @@ -681,44 +679,41 @@ impl<'a> LookupContext<'a> { return match ty::get(self_ty).sty { ty::ty_rptr(_, self_mt) if default_method_hack(self_mt) => { (self_ty, - ty::AutoDerefRef(ty::AutoDerefRef { + ty::AutoDerefRef { autoderefs: autoderefs, - autoref: None})) + autoref: None}) } ty::ty_rptr(_, self_mt) => { let region = - self.infcx().next_region_var( - infer::Autoref(self.expr.span)); + self.infcx().next_region_var(infer::Autoref(self.span)); (ty::mk_rptr(tcx, region, self_mt), - ty::AutoDerefRef(ty::AutoDerefRef { + ty::AutoDerefRef { autoderefs: autoderefs+1, - autoref: Some(ty::AutoPtr(region, self_mt.mutbl))})) + autoref: Some(ty::AutoPtr(region, self_mt.mutbl))}) } ty::ty_vec(self_mt, vstore_slice(_)) => { let region = - self.infcx().next_region_var( - infer::Autoref(self.expr.span)); + self.infcx().next_region_var(infer::Autoref(self.span)); (ty::mk_vec(tcx, self_mt, vstore_slice(region)), - ty::AutoDerefRef(ty::AutoDerefRef { + ty::AutoDerefRef { autoderefs: autoderefs, - autoref: Some(ty::AutoBorrowVec(region, self_mt.mutbl))})) + autoref: Some(ty::AutoBorrowVec(region, self_mt.mutbl))}) } - ty_trait(did, ref substs, ty::RegionTraitStore(_), mutbl, bounds) => { + ty::ty_trait(did, ref substs, ty::RegionTraitStore(_), mutbl, bounds) => { let region = - self.infcx().next_region_var( - infer::Autoref(self.expr.span)); + self.infcx().next_region_var(infer::Autoref(self.span)); (ty::mk_trait(tcx, did, substs.clone(), ty::RegionTraitStore(region), mutbl, bounds), - ty::AutoDerefRef(ty::AutoDerefRef { + ty::AutoDerefRef { autoderefs: autoderefs, - autoref: Some(ty::AutoBorrowObj(region, mutbl))})) + autoref: Some(ty::AutoBorrowObj(region, mutbl))}) } _ => { (self_ty, - ty::AutoDerefRef(ty::AutoDerefRef { + ty::AutoDerefRef { autoderefs: autoderefs, - autoref: None})) + autoref: None}) } }; @@ -848,30 +843,43 @@ impl<'a> LookupContext<'a> { mutbls: &[ast::Mutability], mk_autoref_ty: |ast::Mutability, ty::Region| -> ty::t) -> Option { + // HACK(eddyb) only overloaded auto-deref calls should be missing + // adjustments, because we imply an AutoPtr adjustment for them. + let self_expr_id = match self.self_expr { + Some(expr) => Some(expr.id), + None => match kind(ty::ReEmpty, ast::MutImmutable) { + ty::AutoPtr(..) if autoderefs == 0 => None, + _ => return None + } + }; // This is hokey. We should have mutability inference as a // variable. But for now, try &const, then &, then &mut: let region = - self.infcx().next_region_var( - infer::Autoref(self.expr.span)); + self.infcx().next_region_var(infer::Autoref(self.span)); for mutbl in mutbls.iter() { let autoref_ty = mk_autoref_ty(*mutbl, region); match self.search_for_method(autoref_ty) { None => {} - Some(mme) => { - self.fcx.write_adjustment( - self.self_expr.id, - @ty::AutoDerefRef(ty::AutoDerefRef { - autoderefs: autoderefs, - autoref: Some(kind(region, *mutbl))})); - return Some(mme); + Some(method) => { + match self_expr_id { + Some(self_expr_id) => { + self.fcx.write_adjustment( + self_expr_id, + @ty::AutoDerefRef(ty::AutoDerefRef { + autoderefs: autoderefs, + autoref: Some(kind(region, *mutbl)) + })); + } + None => {} + } + return Some(method); } } } - return None; + None } - fn search_for_method(&self, rcvr_ty: ty::t) - -> Option { + fn search_for_method(&self, rcvr_ty: ty::t) -> Option { debug!("search_for_method(rcvr_ty={})", self.ty_to_str(rcvr_ty)); let _indenter = indenter(); @@ -900,9 +908,8 @@ impl<'a> LookupContext<'a> { } } - fn consider_candidates(&self, - rcvr_ty: ty::t, - candidates: &mut Vec ) + fn consider_candidates(&self, rcvr_ty: ty::t, + candidates: &mut Vec) -> Option { // FIXME(pcwalton): Do we need to clone here? let relevant_candidates: Vec = @@ -918,7 +925,7 @@ impl<'a> LookupContext<'a> { if relevant_candidates.len() > 1 { self.tcx().sess.span_err( - self.expr.span, + self.span, "multiple applicable methods in scope"); for (idx, candidate) in relevant_candidates.iter().enumerate() { self.report_candidate(idx, &candidate.origin); @@ -986,8 +993,7 @@ impl<'a> LookupContext<'a> { let tcx = self.tcx(); - debug!("confirm_candidate(expr={}, rcvr_ty={}, candidate={})", - self.expr.repr(tcx), + debug!("confirm_candidate(rcvr_ty={}, candidate={})", self.ty_to_str(rcvr_ty), candidate.repr(self.tcx())); @@ -1007,12 +1013,12 @@ impl<'a> LookupContext<'a> { self.fcx.infcx().next_ty_vars(num_method_tps) } else if num_method_tps == 0u { tcx.sess.span_err( - self.expr.span, + self.span, "this method does not take type parameters"); self.fcx.infcx().next_ty_vars(num_method_tps) } else if num_supplied_tps != num_method_tps { tcx.sess.span_err( - self.expr.span, + self.span, "incorrect number of type \ parameters given for this method"); self.fcx.infcx().next_ty_vars(num_method_tps) @@ -1025,11 +1031,11 @@ impl<'a> LookupContext<'a> { // FIXME -- permit users to manually specify lifetimes let mut all_regions = match candidate.rcvr_substs.regions { NonerasedRegions(ref v) => v.clone(), - ErasedRegions => tcx.sess.span_bug(self.expr.span, "ErasedRegions") + ErasedRegions => tcx.sess.span_bug(self.span, "ErasedRegions") }; let m_regions = self.fcx.infcx().region_vars_for_defs( - self.expr.span, + self.span, candidate.method_ty.generics.region_param_defs.deref().as_slice()); for &r in m_regions.iter() { all_regions.push(r); @@ -1077,7 +1083,7 @@ impl<'a> LookupContext<'a> { let (_, fn_sig) = replace_late_bound_regions_in_fn_sig( tcx, &fn_sig, |br| self.fcx.infcx().next_region_var( - infer::LateBoundRegion(self.expr.span, br))); + infer::LateBoundRegion(self.span, br))); let transformed_self_ty = *fn_sig.inputs.get(0); let fty = ty::mk_bare_fn(tcx, ty::BareFnTy { sig: fn_sig, @@ -1091,7 +1097,8 @@ impl<'a> LookupContext<'a> { // variables to unify etc). Since we checked beforehand, and // nothing has changed in the meantime, this unification // should never fail. - match self.fcx.mk_subty(false, infer::Misc(self.self_expr.span), + let span = self.self_expr.map_or(self.span, |e| e.span); + match self.fcx.mk_subty(false, infer::Misc(span), rcvr_ty, transformed_self_ty) { result::Ok(_) => {} result::Err(_) => { @@ -1112,8 +1119,7 @@ impl<'a> LookupContext<'a> { &self, trait_def_id: ast::DefId, rcvr_substs: &ty::substs, - method_ty: &ty::Method) -> ty::t - { + method_ty: &ty::Method) -> ty::t { /*! * This is a bit tricky. We have a match against a trait method * being invoked on an object, and we want to generate the @@ -1140,7 +1146,7 @@ impl<'a> LookupContext<'a> { tps: rcvr_substs.tps.clone()}; match method_ty.explicit_self { ast::SelfStatic => { - self.bug(~"static method for object type receiver"); + self.bug("static method for object type receiver"); } ast::SelfValue => { ty::mk_err() // error reported in `enforce_object_limitations()` @@ -1187,14 +1193,14 @@ impl<'a> LookupContext<'a> { match candidate.method_ty.explicit_self { ast::SelfStatic => { // reason (a) above self.tcx().sess.span_err( - self.expr.span, + self.span, "cannot call a method without a receiver \ through an object"); } ast::SelfValue => { // reason (a) above self.tcx().sess.span_err( - self.expr.span, + self.span, "cannot call a method with a by-value receiver \ through an object"); } @@ -1206,7 +1212,7 @@ impl<'a> LookupContext<'a> { let check_for_self_ty = |ty| { if ty::type_has_self(ty) { self.tcx().sess.span_err( - self.expr.span, + self.span, "cannot call a method whose type contains a \ self-type through an object"); true @@ -1228,7 +1234,7 @@ impl<'a> LookupContext<'a> { if candidate.method_ty.generics.has_type_params() { // reason (b) above self.tcx().sess.span_err( - self.expr.span, + self.span, "cannot call a generic method through an object"); } } @@ -1253,7 +1259,7 @@ impl<'a> LookupContext<'a> { } if bad { - self.tcx().sess.span_err(self.expr.span, + self.tcx().sess.span_err(self.span, "explicit call to destructor"); } } @@ -1364,7 +1370,7 @@ impl<'a> LookupContext<'a> { let span = if did.krate == ast::LOCAL_CRATE { self.tcx().map.span(did.node) } else { - self.expr.span + self.span }; self.tcx().sess.span_note( span, @@ -1375,7 +1381,7 @@ impl<'a> LookupContext<'a> { fn report_param_candidate(&self, idx: uint, did: DefId) { self.tcx().sess.span_note( - self.expr.span, + self.span, format!("candidate \\#{} derives from the bound `{}`", idx+1u, ty::item_path_str(self.tcx(), did))); @@ -1383,7 +1389,7 @@ impl<'a> LookupContext<'a> { fn report_trait_candidate(&self, idx: uint, did: DefId) { self.tcx().sess.span_note( - self.expr.span, + self.span, format!("candidate \\#{} derives from the type of the receiver, \ which is the trait `{}`", idx+1u, @@ -1406,8 +1412,9 @@ impl<'a> LookupContext<'a> { ty::item_path_str(self.tcx(), did) } - fn bug(&self, s: ~str) -> ! { - self.tcx().sess.span_bug(self.self_expr.span, s) + fn bug(&self, s: &str) -> ! { + let span = self.self_expr.map_or(self.span, |e| e.span); + self.tcx().sess.span_bug(span, s) } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index d032f0f841fc7..7a51a2cc23177 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -99,19 +99,20 @@ use middle::typeck::check::method::{CheckTraitsAndInherentMethods}; use middle::typeck::check::method::{DontAutoderefReceiver}; use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; use middle::typeck::check::regionmanip::relate_free_regions; -use middle::typeck::check::vtable::{LocationInfo, VtableContext}; +use middle::typeck::check::vtable::VtableContext; use middle::typeck::CrateCtxt; use middle::typeck::infer::{resolve_type, force_tvar}; use middle::typeck::infer; use middle::typeck::rscope::RegionScope; use middle::typeck::{lookup_def_ccx}; use middle::typeck::no_params; -use middle::typeck::{require_same_types, MethodMap, vtable_map}; +use middle::typeck::{require_same_types, vtable_map}; +use middle::typeck::{MethodCall, MethodMap}; use middle::lang_items::TypeIdLangItem; use util::common::{block_query, indenter, loop_query}; use util::ppaux; use util::ppaux::{UserString, Repr}; -use util::nodemap::NodeMap; +use util::nodemap::{FnvHashMap, NodeMap}; use std::cell::{Cell, RefCell}; use collections::HashMap; @@ -266,7 +267,7 @@ impl Inherited { node_types: RefCell::new(NodeMap::new()), node_type_substs: RefCell::new(NodeMap::new()), adjustments: RefCell::new(NodeMap::new()), - method_map: @RefCell::new(NodeMap::new()), + method_map: @RefCell::new(FnvHashMap::new()), vtable_map: @RefCell::new(NodeMap::new()), upvar_borrow_map: RefCell::new(HashMap::new()), } @@ -1108,18 +1109,6 @@ impl FnCtxt { } } - pub fn method_ty(&self, id: ast::NodeId) -> ty::t { - match self.inh.method_map.borrow().get().find(&id) { - Some(method) => method.ty, - None => { - self.tcx().sess.bug( - format!("no method entry for node {}: {} in fcx {}", - id, self.tcx().map.node_to_str(id), - self.tag())); - } - } - } - pub fn node_ty_substs(&self, id: ast::NodeId) -> ty::substs { match self.inh.node_type_substs.borrow().get().find(&id) { Some(ts) => (*ts).clone(), @@ -1133,7 +1122,7 @@ impl FnCtxt { } pub fn method_ty_substs(&self, id: ast::NodeId) -> ty::substs { - match self.inh.method_map.borrow().get().find(&id) { + match self.inh.method_map.borrow().get().find(&MethodCall::expr(id)) { Some(method) => method.substs.clone(), None => { self.tcx().sess.bug( @@ -1252,81 +1241,76 @@ pub enum LvaluePreference { NoPreference } -pub fn do_autoderef(fcx: @FnCtxt, sp: Span, t: ty::t) -> (ty::t, uint) { +pub fn autoderef(fcx: @FnCtxt, sp: Span, t: ty::t, + expr_id: Option, + mut lvalue_pref: LvaluePreference, + should_stop: |ty::t, uint| -> Option) + -> (ty::t, uint, Option) { /*! * - * Autoderefs the type `t` as many times as possible, returning - * a new type and a counter for how many times the type was - * deref'd. If the counter is non-zero, the receiver is responsible - * for inserting an AutoAdjustment record into `tcx.adjustments` + * Autoderefs the type `t` as many times as possible, returning a new type + * and an autoderef count. If the count is not zero, the receiver is + * responsible for inserting an AutoAdjustment record into `tcx.adjustments` * so that trans/borrowck/etc know about this autoderef. */ - let mut t1 = t; - let mut enum_dids = Vec::new(); + let mut t = t; let mut autoderefs = 0; loop { - let sty = structure_of(fcx, sp, t1); + let resolved_t = structurally_resolved_type(fcx, sp, t); // Some extra checks to detect weird cycles and so forth: - match *sty { - ty::ty_box(inner) | ty::ty_uniq(inner) => { - match ty::get(t1).sty { + match ty::get(resolved_t).sty { + ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_rptr(_, _) => { + match ty::get(t).sty { ty::ty_infer(ty::TyVar(v1)) => { - ty::occurs_check(fcx.ccx.tcx, sp, v1, - ty::mk_box(fcx.ccx.tcx, inner)); + ty::occurs_check(fcx.ccx.tcx, sp, v1, resolved_t); } - _ => () - } - } - ty::ty_rptr(_, inner) => { - match ty::get(t1).sty { - ty::ty_infer(ty::TyVar(v1)) => { - ty::occurs_check(fcx.ccx.tcx, sp, v1, - ty::mk_box(fcx.ccx.tcx, inner.ty)); - } - _ => () - } - } - ty::ty_enum(ref did, _) => { - // Watch out for a type like `enum t = @t`. Such a - // type would otherwise infinitely auto-deref. Only - // autoderef loops during typeck (basically, this one - // and the loops in typeck::check::method) need to be - // concerned with this, as an error will be reported - // on the enum definition as well because the enum is - // not instantiable. - if enum_dids.contains(did) { - return (t1, autoderefs); + _ => {} } - enum_dids.push(*did); } _ => { /*ok*/ } } + match should_stop(resolved_t, autoderefs) { + Some(x) => return (resolved_t, autoderefs, Some(x)), + None => {} + } + // Otherwise, deref if type is derefable: - match ty::deref_sty(sty, false) { + let mt = match ty::deref(resolved_t, false) { + Some(mt) => Some(mt), None => { - return (t1, autoderefs); + let method_call = + expr_id.map(|id| MethodCall::autoderef(id, autoderefs as u32)); + try_overloaded_deref(fcx, sp, method_call, None, resolved_t, lvalue_pref) } + }; + match mt { Some(mt) => { + t = mt.ty; + if mt.mutbl == ast::MutImmutable { + lvalue_pref = NoPreference; + } autoderefs += 1; - t1 = mt.ty } + None => return (resolved_t, autoderefs, None) } - }; + } } fn try_overloaded_deref(fcx: @FnCtxt, - expr: &ast::Expr, - base_expr: &ast::Expr, + span: Span, + method_call: Option, + base_expr: Option<&ast::Expr>, base_ty: ty::t, lvalue_pref: LvaluePreference) -> 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, expr, base_expr, token::intern("deref_mut"), - trait_did, base_ty, [], DontAutoderefReceiver) + method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x), + token::intern("deref_mut"), trait_did, + base_ty, [], DontAutoderefReceiver) } _ => None }; @@ -1334,8 +1318,9 @@ fn try_overloaded_deref(fcx: @FnCtxt, // Otherwise, fall back to Deref. let method = match (method, fcx.tcx().lang_items.deref_trait()) { (None, Some(trait_did)) => { - method::lookup_in_trait(fcx, expr, base_expr, token::intern("deref"), - trait_did, base_ty, [], DontAutoderefReceiver) + method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x), + token::intern("deref"), trait_did, + base_ty, [], DontAutoderefReceiver) } (method, _) => method }; @@ -1343,7 +1328,12 @@ fn try_overloaded_deref(fcx: @FnCtxt, match method { Some(method) => { let ref_ty = ty::ty_fn_ret(method.ty); - fcx.inh.method_map.borrow_mut().get().insert(expr.id, method); + match method_call { + Some(method_call) => { + fcx.inh.method_map.borrow_mut().get().insert(method_call, method); + } + None => {} + } ty::deref(ref_ty, true) } None => None @@ -1434,8 +1424,7 @@ fn check_expr_with_lvalue_pref(fcx: @FnCtxt, expr: &ast::Expr, // would return ($0, $1) where $0 and $1 are freshly instantiated type // variables. pub fn impl_self_ty(vcx: &VtableContext, - location_info: &LocationInfo, // (potential) receiver for - // this impl + span: Span, // (potential) receiver for this impl did: ast::DefId) -> ty_param_substs_and_ty { let tcx = vcx.tcx(); @@ -1446,7 +1435,7 @@ pub fn impl_self_ty(vcx: &VtableContext, ity.generics.region_param_defs(), ity.ty); - let rps = vcx.infcx.region_vars_for_defs(location_info.span, rps); + let rps = vcx.infcx.region_vars_for_defs(span, rps); let tps = vcx.infcx.next_ty_vars(n_tps); let substs = substs { @@ -1921,7 +1910,8 @@ fn check_expr_with_unifier(fcx: @FnCtxt, AutoderefReceiver) { Some(method) => { let method_ty = method.ty; - fcx.inh.method_map.borrow_mut().get().insert(expr.id, method); + let method_call = MethodCall::expr(expr.id); + fcx.inh.method_map.borrow_mut().get().insert(method_call, method); method_ty } None => { @@ -2001,15 +1991,17 @@ 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, args[0], opname, trait_did, - self_t, [], autoderef_receiver) + method::lookup_in_trait(fcx, op_ex.span, Some(&*args[0]), opname, + trait_did, self_t, [], autoderef_receiver) } None => None }; match method { Some(method) => { let method_ty = method.ty; - fcx.inh.method_map.borrow_mut().get().insert(op_ex.id, method); + // HACK(eddyb) Fully qualified path to work around a resolve bug. + let method_call = ::middle::typeck::MethodCall::expr(op_ex.id); + fcx.inh.method_map.borrow_mut().get().insert(method_call, method); check_method_argument_types(fcx, op_ex.span, method_ty, op_ex, args, DoDerefArgs) @@ -2293,32 +2285,28 @@ fn check_expr_with_unifier(fcx: @FnCtxt, field: ast::Name, tys: &[ast::P]) { let tcx = fcx.ccx.tcx; - let bot = check_expr_with_lvalue_pref(fcx, base, lvalue_pref); + check_expr_with_lvalue_pref(fcx, base, lvalue_pref); let expr_t = structurally_resolved_type(fcx, expr.span, fcx.expr_ty(base)); - let (base_t, derefs) = do_autoderef(fcx, expr.span, expr_t); - - match *structure_of(fcx, expr.span, base_t) { - ty::ty_struct(base_id, ref substs) => { - // This is just for fields -- the same code handles - // methods in both classes and traits - - // (1) verify that the class id actually has a field called - // field - debug!("class named {}", ppaux::ty_to_str(tcx, base_t)); - let cls_items = ty::lookup_struct_fields(tcx, base_id); - match lookup_field_ty(tcx, base_id, cls_items.as_slice(), - field, &(*substs)) { - Some(field_ty) => { - // (2) look up what field's type is, and return it - fcx.write_ty(expr.id, field_ty); - fcx.write_autoderef_adjustment(base.id, derefs); - return bot; - } - None => () + // FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop. + let (_, autoderefs, field_ty) = + autoderef(fcx, expr.span, expr_t, Some(base.id), lvalue_pref, |base_t, _| { + match ty::get(base_t).sty { + ty::ty_struct(base_id, ref substs) => { + debug!("struct named {}", ppaux::ty_to_str(tcx, base_t)); + let fields = ty::lookup_struct_fields(tcx, base_id); + lookup_field_ty(tcx, base_id, fields.as_slice(), field, &(*substs)) } + _ => None + } + }); + match field_ty { + Some(field_ty) => { + fcx.write_ty(expr.id, field_ty); + fcx.write_autoderef_adjustment(base.id, autoderefs); + return; } - _ => () + None => {} } let tps: Vec = tys.iter().map(|&ty| fcx.to_ty(ty)).collect(); @@ -2738,8 +2726,9 @@ fn check_expr_with_unifier(fcx: @FnCtxt, oprnd_t = structurally_resolved_type(fcx, expr.span, oprnd_t); oprnd_t = match ty::deref(oprnd_t, true) { Some(mt) => mt.ty, - None => match try_overloaded_deref(fcx, expr, oprnd, - oprnd_t, lvalue_pref) { + None => match try_overloaded_deref(fcx, expr.span, + Some(MethodCall::expr(expr.id)), + Some(&*oprnd), oprnd_t, lvalue_pref) { Some(mt) => mt.ty, None => { let is_newtype = match ty::get(oprnd_t).sty { @@ -3175,19 +3164,27 @@ fn check_expr_with_unifier(fcx: @FnCtxt, } else if ty::type_is_error(idx_t) || ty::type_is_bot(idx_t) { fcx.write_ty(id, idx_t); } else { - let (base_t, derefs) = do_autoderef(fcx, expr.span, raw_base_t); - let base_sty = structure_of(fcx, expr.span, base_t); - match ty::index_sty(base_sty) { + let (base_t, 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(mt) => { require_integral(fcx, idx.span, idx_t); fcx.write_ty(id, mt.ty); - fcx.write_autoderef_adjustment(base.id, derefs); + fcx.write_autoderef_adjustment(base.id, autoderefs); } None => { let resolved = structurally_resolved_type(fcx, expr.span, raw_base_t); - let error_message = || { + let ret_ty = lookup_op_method(fcx, + expr, + resolved, + token::intern("index"), + tcx.lang_items.index_trait(), + [base, idx], + AutoderefReceiver, + || { fcx.type_error_message(expr.span, |actual| { format!("cannot index a value \ @@ -3196,15 +3193,7 @@ fn check_expr_with_unifier(fcx: @FnCtxt, }, base_t, None); - }; - let ret_ty = lookup_op_method(fcx, - expr, - resolved, - token::intern("index"), - tcx.lang_items.index_trait(), - [base, idx], - AutoderefReceiver, - error_message); + }); fcx.write_ty(id, ret_ty); } } diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 381ff141bfbb4..169a7cfc90e71 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -129,6 +129,7 @@ use middle::typeck::check::regionmanip::relate_nested_regions; use middle::typeck::infer::resolve_and_force_all_but_regions; use middle::typeck::infer::resolve_type; use middle::typeck::infer; +use middle::typeck::MethodCall; use middle::pat_util; use util::ppaux::{ty_to_str, region_to_str, Repr}; @@ -221,11 +222,17 @@ impl Rcx { } /// Try to resolve the type for the given node. - pub fn resolve_node_type(&mut self, id: ast::NodeId) -> ty::t { + fn resolve_node_type(&mut self, id: ast::NodeId) -> ty::t { let t = self.fcx.node_ty(id); self.resolve_type(t) } + fn resolve_method_type(&mut self, method_call: MethodCall) -> Option { + let method_ty = self.fcx.inh.method_map.borrow().get() + .find(&method_call).map(|method| method.ty); + method_ty.map(|method_ty| self.resolve_type(method_ty)) + } + /// Try to resolve the type for the given node. pub fn resolve_expr_type_adjusted(&mut self, expr: &ast::Expr) -> ty::t { let ty_unadjusted = self.resolve_node_type(expr.id); @@ -233,11 +240,9 @@ impl Rcx { ty_unadjusted } else { let tcx = self.fcx.tcx(); - let adjustment = { - let adjustments = self.fcx.inh.adjustments.borrow(); - adjustments.get().find_copy(&expr.id) - }; - ty::adjust_ty(tcx, expr.span, ty_unadjusted, adjustment) + let adjustment = self.fcx.inh.adjustments.borrow().get().find_copy(&expr.id); + ty::adjust_ty(tcx, expr.span, expr.id, ty_unadjusted, adjustment, + |method_call| self.resolve_method_type(method_call)) } } } @@ -252,10 +257,8 @@ impl<'a> mc::Typer for &'a mut Rcx { if ty::type_is_error(t) {Err(())} else {Ok(t)} } - fn node_method_ty(&mut self, id: ast::NodeId) -> Option { - self.fcx.inh.method_map.borrow().get().find(&id).map(|method| { - self.resolve_type(method.ty) - }) + fn node_method_ty(&mut self, method_call: MethodCall) -> Option { + self.resolve_method_type(method_call) } fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> { @@ -264,7 +267,7 @@ impl<'a> mc::Typer for &'a mut Rcx { } fn is_method_call(&mut self, id: ast::NodeId) -> bool { - self.fcx.inh.method_map.borrow().get().contains_key(&id) + self.fcx.inh.method_map.borrow().get().contains_key(&MethodCall::expr(id)) } fn temporary_scope(&mut self, id: ast::NodeId) -> Option { @@ -383,53 +386,48 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { debug!("regionck::visit_expr(e={}, repeating_scope={:?})", expr.repr(rcx.fcx.tcx()), rcx.repeating_scope); - let has_method_map = rcx.fcx.inh.method_map.get().contains_key(&expr.id); + let method_call = MethodCall::expr(expr.id); + let has_method_map = rcx.fcx.inh.method_map.get().contains_key(&method_call); // Check any autoderefs or autorefs that appear. - { - let adjustments = rcx.fcx.inh.adjustments.borrow(); - let r = adjustments.get().find(&expr.id); - for &adjustment in r.iter() { - debug!("adjustment={:?}", adjustment); - match **adjustment { - ty::AutoDerefRef( - ty::AutoDerefRef {autoderefs: autoderefs, autoref: opt_autoref}) => - { - let expr_ty = rcx.resolve_node_type(expr.id); - constrain_derefs(rcx, expr, autoderefs, expr_ty); - for autoref in opt_autoref.iter() { - link_autoref(rcx, expr, autoderefs, autoref); - - // Require that the resulting region encompasses - // the current node. - // - // FIXME(#6268) remove to support nested method calls - constrain_regions_in_type_of_node( - rcx, expr.id, ty::ReScope(expr.id), - infer::AutoBorrow(expr.span)); - } - } - ty::AutoObject(ast::BorrowedSigil, Some(trait_region), _, _, _, _) => { - // Determine if we are casting `expr` to an trait - // instance. If so, we have to be sure that the type of - // the source obeys the trait's region bound. + for &adjustment in rcx.fcx.inh.adjustments.borrow().get().find(&expr.id).iter() { + debug!("adjustment={:?}", adjustment); + match **adjustment { + ty::AutoDerefRef(ty::AutoDerefRef {autoderefs, autoref: opt_autoref}) => { + let expr_ty = rcx.resolve_node_type(expr.id); + constrain_derefs(rcx, expr, autoderefs, expr_ty); + for autoref in opt_autoref.iter() { + link_autoref(rcx, expr, autoderefs, autoref); + + // Require that the resulting region encompasses + // the current node. // - // Note: there is a subtle point here concerning type - // parameters. It is possible that the type of `source` - // contains type parameters, which in turn may contain - // regions that are not visible to us (only the caller - // knows about them). The kind checker is ultimately - // responsible for guaranteeing region safety in that - // particular case. There is an extensive comment on the - // function check_cast_for_escaping_regions() in kind.rs - // explaining how it goes about doing that. - - let source_ty = rcx.fcx.expr_ty(expr); - constrain_regions_in_type(rcx, trait_region, - infer::RelateObjectBound(expr.span), source_ty); + // FIXME(#6268) remove to support nested method calls + constrain_regions_in_type_of_node( + rcx, expr.id, ty::ReScope(expr.id), + infer::AutoBorrow(expr.span)); } - _ => {} } + ty::AutoObject(ast::BorrowedSigil, Some(trait_region), _, _, _, _) => { + // Determine if we are casting `expr` to an trait + // instance. If so, we have to be sure that the type of + // the source obeys the trait's region bound. + // + // Note: there is a subtle point here concerning type + // parameters. It is possible that the type of `source` + // contains type parameters, which in turn may contain + // regions that are not visible to us (only the caller + // knows about them). The kind checker is ultimately + // responsible for guaranteeing region safety in that + // particular case. There is an extensive comment on the + // function check_cast_for_escaping_regions() in kind.rs + // explaining how it goes about doing that. + + let source_ty = rcx.fcx.expr_ty(expr); + constrain_regions_in_type(rcx, trait_region, + infer::RelateObjectBound(expr.span), source_ty); + } + _ => {} } } @@ -488,7 +486,8 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { ast::ExprUnary(ast::UnDeref, base) => { // For *a, the lifetime of a must enclose the deref - let base_ty = match rcx.fcx.inh.method_map.get().find(&expr.id) { + let method_call = MethodCall::expr(expr.id); + let base_ty = match rcx.fcx.inh.method_map.get().find(&method_call) { Some(method) => { constrain_call(rcx, None, expr, Some(base), [], true); ty::ty_fn_ret(method.ty) @@ -769,7 +768,8 @@ fn constrain_call(rcx: &mut Rcx, implicitly_ref_args); let callee_ty = match fn_expr_id { Some(id) => rcx.resolve_node_type(id), - None => rcx.resolve_type(rcx.fcx.method_ty(call_expr.id)) + None => rcx.resolve_method_type(MethodCall::expr(call_expr.id)) + .expect("call should have been to a method") }; if ty::type_is_error(callee_ty) { // Bail, as function type is unknown @@ -904,11 +904,9 @@ fn constrain_regions_in_type_of_node( // is going to fail anyway, so just stop here and let typeck // report errors later on in the writeback phase. let ty0 = rcx.resolve_node_type(id); - let adjustment = { - let adjustments = rcx.fcx.inh.adjustments.borrow(); - adjustments.get().find_copy(&id) - }; - let ty = ty::adjust_ty(tcx, origin.span(), ty0, adjustment); + let adjustment = rcx.fcx.inh.adjustments.borrow().get().find_copy(&id); + let ty = ty::adjust_ty(tcx, origin.span(), id, ty0, adjustment, + |method_call| rcx.resolve_method_type(method_call)); debug!("constrain_regions_in_type_of_node(\ ty={}, ty0={}, id={}, minimum_lifetime={:?}, adjustment={:?})", ty_to_str(tcx, ty), ty_to_str(tcx, ty0), diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 70fe3cfde50b8..3fee30bc4988c 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -21,6 +21,7 @@ use middle::typeck::infer; use middle::typeck::{vtable_origin, vtable_res, vtable_param_res}; use middle::typeck::{vtable_static, vtable_param, impl_res}; use middle::typeck::{param_numbered, param_self, param_index}; +use middle::typeck::MethodCall; use middle::subst::Subst; use util::common::indenter; use util::ppaux; @@ -62,15 +63,6 @@ use syntax::visit::Visitor; // It may be better to do something more clever, like processing fully // resolved types first. - -/// Location info records the span and ID of the expression or item that is -/// responsible for this vtable instantiation. (This may not be an expression -/// if the vtable instantiation is being performed as part of "deriving".) -pub struct LocationInfo { - span: Span, - id: ast::NodeId -} - /// A vtable context includes an inference context, a crate context, and a /// callback function to call in case of type error. pub struct VtableContext<'a> { @@ -88,14 +80,14 @@ fn has_trait_bounds(type_param_defs: &[ty::TypeParameterDef]) -> bool { } fn lookup_vtables(vcx: &VtableContext, - location_info: &LocationInfo, + span: Span, type_param_defs: &[ty::TypeParameterDef], substs: &ty::substs, is_early: bool) -> vtable_res { - debug!("lookup_vtables(location_info={:?}, \ + debug!("lookup_vtables(span={:?}, \ type_param_defs={}, \ substs={}", - location_info, + span, type_param_defs.repr(vcx.tcx()), substs.repr(vcx.tcx())); @@ -105,18 +97,18 @@ fn lookup_vtables(vcx: &VtableContext, substs.tps.rev_iter() .zip(type_param_defs.rev_iter()) .map(|(ty, def)| - lookup_vtables_for_param(vcx, location_info, Some(substs), - &*def.bounds, *ty, is_early)) + lookup_vtables_for_param(vcx, span, Some(substs), + &*def.bounds, *ty, is_early)) .collect(); result.reverse(); assert_eq!(substs.tps.len(), result.len()); debug!("lookup_vtables result(\ - location_info={:?}, \ + span={:?}, \ type_param_defs={}, \ substs={}, \ result={})", - location_info, + span, type_param_defs.repr(vcx.tcx()), substs.repr(vcx.tcx()), result.repr(vcx.tcx())); @@ -124,7 +116,7 @@ fn lookup_vtables(vcx: &VtableContext, } fn lookup_vtables_for_param(vcx: &VtableContext, - location_info: &LocationInfo, + span: Span, // None for substs means the identity substs: Option<&ty::substs>, type_param_bounds: &ty::ParamBounds, @@ -155,11 +147,10 @@ fn lookup_vtables_for_param(vcx: &VtableContext, debug!("after subst: {}", trait_ref.repr(tcx)); - match lookup_vtable(vcx, location_info, ty, trait_ref, is_early) { + match lookup_vtable(vcx, span, ty, trait_ref, is_early) { Some(vtable) => param_result.push(vtable), None => { - vcx.tcx().sess.span_fatal( - location_info.span, + vcx.tcx().sess.span_fatal(span, format!("failed to find an implementation of \ trait {} for {}", vcx.infcx.trait_ref_to_str(trait_ref), @@ -170,11 +161,11 @@ fn lookup_vtables_for_param(vcx: &VtableContext, }); debug!("lookup_vtables_for_param result(\ - location_info={:?}, \ + span={:?}, \ type_param_bounds={}, \ ty={}, \ result={})", - location_info, + span, type_param_bounds.repr(vcx.tcx()), ty.repr(vcx.tcx()), param_result.repr(vcx.tcx())); @@ -183,10 +174,9 @@ fn lookup_vtables_for_param(vcx: &VtableContext, } fn relate_trait_refs(vcx: &VtableContext, - location_info: &LocationInfo, + span: Span, act_trait_ref: @ty::TraitRef, - exp_trait_ref: @ty::TraitRef) -{ + exp_trait_ref: @ty::TraitRef) { /*! * * Checks that an implementation of `act_trait_ref` is suitable @@ -196,10 +186,9 @@ fn relate_trait_refs(vcx: &VtableContext, match infer::mk_sub_trait_refs(vcx.infcx, false, - infer::RelateTraitRefs(location_info.span), + infer::RelateTraitRefs(span), act_trait_ref, - exp_trait_ref) - { + exp_trait_ref) { result::Ok(()) => {} // Ok. result::Err(ref err) => { // There is an error, but we need to do some work to make @@ -215,8 +204,7 @@ fn relate_trait_refs(vcx: &VtableContext, !ty::trait_ref_contains_error(&r_exp_trait_ref) { let tcx = vcx.tcx(); - tcx.sess.span_err( - location_info.span, + tcx.sess.span_err(span, format!("expected {}, but found {} ({})", ppaux::trait_ref_to_str(tcx, &r_exp_trait_ref), ppaux::trait_ref_to_str(tcx, &r_act_trait_ref), @@ -228,18 +216,17 @@ fn relate_trait_refs(vcx: &VtableContext, // Look up the vtable implementing the trait `trait_ref` at type `t` fn lookup_vtable(vcx: &VtableContext, - location_info: &LocationInfo, + span: Span, ty: ty::t, trait_ref: @ty::TraitRef, is_early: bool) - -> Option -{ + -> Option { debug!("lookup_vtable(ty={}, trait_ref={})", vcx.infcx.ty_to_str(ty), vcx.infcx.trait_ref_to_str(trait_ref)); let _i = indenter(); - let ty = match fixup_ty(vcx, location_info, ty, is_early) { + let ty = match fixup_ty(vcx, span, ty, is_early) { Some(ty) => ty, None => { // fixup_ty can only fail if this is early resolution @@ -261,8 +248,7 @@ fn lookup_vtable(vcx: &VtableContext, .get(n) .trait_bounds .as_slice(); - lookup_vtable_from_bounds(vcx, - location_info, + lookup_vtable_from_bounds(vcx, span, type_param_bounds, param_numbered(n), trait_ref) @@ -270,8 +256,7 @@ fn lookup_vtable(vcx: &VtableContext, ty::ty_self(_) => { let self_param_bound = vcx.param_env.self_param_bound.unwrap(); - lookup_vtable_from_bounds(vcx, - location_info, + lookup_vtable_from_bounds(vcx, span, [self_param_bound], param_self, trait_ref) @@ -285,14 +270,13 @@ fn lookup_vtable(vcx: &VtableContext, // If we aren't a self type or param, or it was, but we didn't find it, // do a search. - return search_for_vtable(vcx, location_info, - ty, trait_ref, is_early) + search_for_vtable(vcx, span, ty, trait_ref, is_early) } // Given a list of bounds on a type, search those bounds to see if any // of them are the vtable we are looking for. fn lookup_vtable_from_bounds(vcx: &VtableContext, - location_info: &LocationInfo, + span: Span, bounds: &[@ty::TraitRef], param: param_index, trait_ref: @ty::TraitRef) @@ -306,10 +290,7 @@ fn lookup_vtable_from_bounds(vcx: &VtableContext, bound_trait_ref.repr(vcx.tcx())); if bound_trait_ref.def_id == trait_ref.def_id { - relate_trait_refs(vcx, - location_info, - bound_trait_ref, - trait_ref); + relate_trait_refs(vcx, span, bound_trait_ref, trait_ref); let vtable = vtable_param(param, n_bound); debug!("found param vtable: {:?}", vtable); @@ -324,7 +305,7 @@ fn lookup_vtable_from_bounds(vcx: &VtableContext, } fn search_for_vtable(vcx: &VtableContext, - location_info: &LocationInfo, + span: Span, ty: ty::t, trait_ref: @ty::TraitRef, is_early: bool) @@ -385,11 +366,10 @@ fn search_for_vtable(vcx: &VtableContext, let ty::ty_param_substs_and_ty { substs: substs, ty: for_ty - } = impl_self_ty(vcx, location_info, im.did); + } = impl_self_ty(vcx, span, im.did); match infer::mk_subty(vcx.infcx, false, - infer::RelateSelfType( - location_info.span), + infer::RelateSelfType(span), ty, for_ty) { result::Err(_) => continue, @@ -418,7 +398,7 @@ fn search_for_vtable(vcx: &VtableContext, vcx.infcx.trait_ref_to_str(of_trait_ref)); let of_trait_ref = of_trait_ref.subst(tcx, &substs); - relate_trait_refs(vcx, location_info, of_trait_ref, trait_ref); + relate_trait_refs(vcx, span, of_trait_ref, trait_ref); // Recall that trait_ref -- the trait type we're casting to -- @@ -430,15 +410,14 @@ fn search_for_vtable(vcx: &VtableContext, // process of looking up bounds might constrain some of them. let im_generics = ty::lookup_item_type(tcx, im.did).generics; - let subres = lookup_vtables(vcx, location_info, + let subres = lookup_vtables(vcx, span, im_generics.type_param_defs(), &substs, is_early); // substs might contain type variables, so we call // fixup_substs to resolve them. - let substs_f = match fixup_substs(vcx, - location_info, + let substs_f = match fixup_substs(vcx, span, trait_ref.def_id, substs, is_early) { @@ -463,7 +442,7 @@ fn search_for_vtable(vcx: &VtableContext, // I am a little confused about this, since it seems to be // very similar to the relate_trait_refs we already do, // but problems crop up if it is removed, so... -sully - connect_trait_tps(vcx, location_info, &substs_f, trait_ref, im.did); + connect_trait_tps(vcx, span, &substs_f, trait_ref, im.did); // Finally, we register that we found a matching impl, and // record the def ID of the impl as well as the resolved list @@ -476,9 +455,7 @@ fn search_for_vtable(vcx: &VtableContext, 1 => return Some(found.get(0).clone()), _ => { if !is_early { - vcx.tcx().sess.span_err( - location_info.span, - "multiple applicable methods in scope"); + vcx.tcx().sess.span_err(span, "multiple applicable methods in scope"); } return Some(found.get(0).clone()); } @@ -487,7 +464,7 @@ fn search_for_vtable(vcx: &VtableContext, fn fixup_substs(vcx: &VtableContext, - location_info: &LocationInfo, + span: Span, id: ast::DefId, substs: ty::substs, is_early: bool) @@ -499,7 +476,7 @@ fn fixup_substs(vcx: &VtableContext, ty::RegionTraitStore(ty::ReStatic), ast::MutImmutable, ty::EmptyBuiltinBounds()); - fixup_ty(vcx, location_info, t, is_early).map(|t_f| { + fixup_ty(vcx, span, t, is_early).map(|t_f| { match ty::get(t_f).sty { ty::ty_trait(_, ref substs_f, _, _, _) => (*substs_f).clone(), _ => fail!("t_f should be a trait") @@ -508,7 +485,7 @@ fn fixup_substs(vcx: &VtableContext, } fn fixup_ty(vcx: &VtableContext, - location_info: &LocationInfo, + span: Span, ty: ty::t, is_early: bool) -> Option { @@ -516,8 +493,7 @@ fn fixup_ty(vcx: &VtableContext, match resolve_type(vcx.infcx, ty, resolve_and_force_all_but_regions) { Ok(new_type) => Some(new_type), Err(e) if !is_early => { - tcx.sess.span_fatal( - location_info.span, + tcx.sess.span_fatal(span, format!("cannot determine a type \ for this bounded type parameter: {}", fixup_err_to_str(e))) @@ -529,7 +505,7 @@ fn fixup_ty(vcx: &VtableContext, } fn connect_trait_tps(vcx: &VtableContext, - location_info: &LocationInfo, + span: Span, impl_substs: &ty::substs, trait_ref: @ty::TraitRef, impl_did: ast::DefId) { @@ -537,12 +513,12 @@ fn connect_trait_tps(vcx: &VtableContext, let impl_trait_ref = match ty::impl_trait_ref(tcx, impl_did) { Some(t) => t, - None => vcx.tcx().sess.span_bug(location_info.span, + None => vcx.tcx().sess.span_bug(span, "connect_trait_tps invoked on a type impl") }; let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); - relate_trait_refs(vcx, location_info, impl_trait_ref, trait_ref); + relate_trait_refs(vcx, span, impl_trait_ref, trait_ref); } fn insert_vtables(fcx: &FnCtxt, expr_id: ast::NodeId, vtables: vtable_res) { @@ -551,19 +527,6 @@ fn insert_vtables(fcx: &FnCtxt, expr_id: ast::NodeId, vtables: vtable_res) { fcx.inh.vtable_map.borrow_mut().get().insert(expr_id, vtables); } -pub fn location_info_for_expr(expr: &ast::Expr) -> LocationInfo { - LocationInfo { - span: expr.span, - id: expr.id - } -} -pub fn location_info_for_item(item: &ast::Item) -> LocationInfo { - LocationInfo { - span: item.span, - id: item.id - } -} - pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { debug!("vtable: early_resolve_expr() ex with id {:?} (early: {}): {}", ex.id, is_early, expr_to_str(ex)); @@ -608,8 +571,6 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { _ => fail!("shouldn't get here"), }; - let location_info = - &location_info_for_expr(ex); let vcx = fcx.vtable_context(); let target_trait_ref = @ty::TraitRef { def_id: target_def_id, @@ -626,7 +587,7 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { }; let vtables = lookup_vtables_for_param(&vcx, - location_info, + ex.span, None, ¶m_bounds, typ, @@ -687,7 +648,7 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { debug!("early_resolve_expr: looking up vtables for type params {}", item_ty.generics.type_param_defs().repr(fcx.tcx())); let vcx = fcx.vtable_context(); - let vtbls = lookup_vtables(&vcx, &location_info_for_expr(ex), + let vtbls = lookup_vtables(&vcx, ex.span, item_ty.generics.type_param_defs(), substs, is_early); if !is_early { @@ -704,7 +665,7 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { ast::ExprAssignOp(_, _, _) | ast::ExprIndex(_, _) | ast::ExprMethodCall(_, _, _) => { - match fcx.inh.method_map.borrow().get().find(&ex.id) { + match fcx.inh.method_map.borrow().get().find(&MethodCall::expr(ex.id)) { Some(method) => { debug!("vtable resolution on parameter bounds for method call {}", ex.repr(fcx.tcx())); @@ -712,7 +673,7 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { if has_trait_bounds(type_param_defs.deref().as_slice()) { let substs = fcx.method_ty_substs(ex.id); let vcx = fcx.vtable_context(); - let vtbls = lookup_vtables(&vcx, &location_info_for_expr(ex), + let vtbls = lookup_vtables(&vcx, ex.span, type_param_defs.deref() .as_slice(), &substs, is_early); @@ -782,13 +743,11 @@ pub fn resolve_impl(tcx: ty::ctxt, let infcx = &infer::new_infer_ctxt(tcx); let vcx = VtableContext { infcx: infcx, param_env: ¶m_env }; - let loc_info = location_info_for_item(impl_item); // First, check that the impl implements any trait bounds // on the trait. let trait_def = ty::lookup_trait_def(tcx, impl_trait_ref.def_id); - let vtbls = lookup_vtables(&vcx, - &loc_info, + let vtbls = lookup_vtables(&vcx, impl_item.span, trait_def.generics.type_param_defs(), &impl_trait_ref.substs, false); @@ -808,7 +767,7 @@ pub fn resolve_impl(tcx: ty::ctxt, // We will need to make one so we can use this information // for compiling default methods that refer to supertraits. let self_vtable_res = - lookup_vtables_for_param(&vcx, &loc_info, None, + lookup_vtables_for_param(&vcx, impl_item.span, None, ¶m_bounds, t, false); @@ -833,13 +792,9 @@ pub fn trans_resolve_method(tcx: ty::ctxt, id: ast::NodeId, infcx: &infer::new_infer_ctxt(tcx), param_env: &ty::construct_parameter_environment(tcx, None, [], [], [], [], id) }; - let loc_info = LocationInfo { - id: id, - span: tcx.map.span(id) - }; Some(lookup_vtables(&vcx, - &loc_info, + tcx.map.span(id), type_param_defs.as_slice(), substs, false)) diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index ced9db069545b..7659842aff411 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -20,7 +20,7 @@ use middle::typeck::check::FnCtxt; use middle::typeck::infer::{force_all, resolve_all, resolve_region}; use middle::typeck::infer::resolve_type; use middle::typeck::infer; -use middle::typeck::MethodCallee; +use middle::typeck::{MethodCall, MethodCallee}; use middle::typeck::{vtable_res, vtable_origin}; use middle::typeck::{vtable_static, vtable_param}; use middle::typeck::write_substs_to_tcx; @@ -63,15 +63,15 @@ fn resolve_type_vars_in_types(fcx: @FnCtxt, sp: Span, tys: &[ty::t]) }).collect() } -fn resolve_method_map_entry(wbcx: &mut WbCtxt, sp: Span, id: ast::NodeId) { +fn resolve_method_map_entry(wbcx: &mut WbCtxt, sp: Span, method_call: MethodCall) { let fcx = wbcx.fcx; let tcx = fcx.ccx.tcx; // Resolve any method map entry - match fcx.inh.method_map.borrow().get().find(&id) { + match fcx.inh.method_map.borrow().get().find(&method_call) { Some(method) => { - debug!("writeback::resolve_method_map_entry(id={:?}, entry={:?})", - id, method.repr(tcx)); + debug!("writeback::resolve_method_map_entry(call={:?}, entry={:?})", + method_call, method.repr(tcx)); let method_ty = match resolve_type_vars_in_type(fcx, sp, method.ty) { Some(t) => t, None => { @@ -95,7 +95,7 @@ fn resolve_method_map_entry(wbcx: &mut WbCtxt, sp: Span, id: ast::NodeId) { self_ty: None } }; - fcx.ccx.method_map.borrow_mut().get().insert(id, new_method); + fcx.ccx.method_map.borrow_mut().get().insert(method_call, new_method); } None => {} } @@ -142,10 +142,7 @@ fn resolve_type_vars_for_node(wbcx: &mut WbCtxt, sp: Span, id: ast::NodeId) let tcx = fcx.ccx.tcx; // Resolve any borrowings for the node with id `id` - let adjustment = { - let adjustments = fcx.inh.adjustments.borrow(); - adjustments.get().find_copy(&id) - }; + let adjustment = fcx.inh.adjustments.borrow().get().find_copy(&id); match adjustment { None => (), @@ -167,30 +164,29 @@ fn resolve_type_vars_for_node(wbcx: &mut WbCtxt, sp: Span, id: ast::NodeId) // FIXME(eddyb) #2190 Allow only statically resolved // bare functions to coerce to a closure to avoid // constructing (slower) indirect call wrappers. - { - let def_map = tcx.def_map.borrow(); - match def_map.get().find(&id) { - Some(&ast::DefFn(..)) | - Some(&ast::DefStaticMethod(..)) | - Some(&ast::DefVariant(..)) | - Some(&ast::DefStruct(_)) => {} - _ => tcx.sess.span_err(sp, - "cannot coerce non-statically resolved bare fn") - } + match tcx.def_map.borrow().get().find(&id) { + Some(&ast::DefFn(..)) | + Some(&ast::DefStaticMethod(..)) | + Some(&ast::DefVariant(..)) | + Some(&ast::DefStruct(_)) => {} + _ => tcx.sess.span_err(sp, + "cannot coerce non-statically resolved bare fn") } let resolved_adj = @ty::AutoAddEnv(r1, s); debug!("Adjustments for node {}: {:?}", - id, - resolved_adj); - let mut adjustments = tcx.adjustments - .borrow_mut(); - adjustments.get().insert(id, resolved_adj); + id, resolved_adj); + tcx.adjustments.borrow_mut().get().insert(id, resolved_adj); } } } ty::AutoDerefRef(adj) => { + for autoderef in range(0, adj.autoderefs) { + let method_call = MethodCall::autoderef(id, autoderef as u32); + resolve_method_map_entry(wbcx, sp, method_call); + } + let fixup_region = |r| { match resolve_region(fcx.infcx(), r, @@ -218,14 +214,12 @@ fn resolve_type_vars_for_node(wbcx: &mut WbCtxt, sp: Span, id: ast::NodeId) autoref: resolved_autoref, }); debug!("Adjustments for node {}: {:?}", id, resolved_adj); - let mut adjustments = tcx.adjustments.borrow_mut(); - adjustments.get().insert(id, resolved_adj); + tcx.adjustments.borrow_mut().get().insert(id, resolved_adj); } ty::AutoObject(..) => { debug!("Adjustments for node {}: {:?}", id, adjustment); - let mut adjustments = tcx.adjustments.borrow_mut(); - adjustments.get().insert(id, adjustment); + tcx.adjustments.borrow_mut().get().insert(id, adjustment); } } } @@ -280,7 +274,7 @@ fn visit_expr(e: &ast::Expr, wbcx: &mut WbCtxt) { } resolve_type_vars_for_node(wbcx, e.span, e.id); - resolve_method_map_entry(wbcx, e.span, e.id); + resolve_method_map_entry(wbcx, e.span, MethodCall::expr(e.id)); resolve_vtable_map_entry(wbcx.fcx, e.span, e.id); match e.node { diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 86f642a228d89..726f89048984c 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -68,7 +68,7 @@ use middle::ty; use util::common::time; use util::ppaux::Repr; use util::ppaux; -use util::nodemap::{DefIdMap, NodeMap}; +use util::nodemap::{DefIdMap, FnvHashMap, NodeMap}; use std::cell::RefCell; use std::rc::Rc; @@ -149,9 +149,31 @@ pub struct MethodCallee { substs: ty::substs } +#[deriving(Clone, Eq, Hash)] +pub struct MethodCall { + expr_id: ast::NodeId, + autoderef: u32 +} + +impl MethodCall { + pub fn expr(id: ast::NodeId) -> MethodCall { + MethodCall { + expr_id: id, + autoderef: 0 + } + } + + pub fn autoderef(expr_id: ast::NodeId, autoderef: u32) -> MethodCall { + MethodCall { + expr_id: expr_id, + autoderef: 1 + autoderef + } + } +} + // maps from an expression id that corresponds to a method call to the details // of the method to be invoked -pub type MethodMap = @RefCell>; +pub type MethodMap = @RefCell>; pub type vtable_param_res = @Vec ; // Resolutions for bounds of all parameters, left to right, for a given path. @@ -442,7 +464,7 @@ pub fn check_crate(tcx: ty::ctxt, let time_passes = tcx.sess.time_passes(); let ccx = @CrateCtxt { trait_map: trait_map, - method_map: @RefCell::new(NodeMap::new()), + method_map: @RefCell::new(FnvHashMap::new()), vtable_map: @RefCell::new(NodeMap::new()), tcx: tcx }; diff --git a/src/librustc/util/nodemap.rs b/src/librustc/util/nodemap.rs index 05b3b10598127..7507fd579ebc1 100644 --- a/src/librustc/util/nodemap.rs +++ b/src/librustc/util/nodemap.rs @@ -16,9 +16,11 @@ use std::io; use syntax::ast; #[cfg(not(stage0))] -pub type NodeMap = HashMap; -#[cfg(not(stage0))] -pub type DefIdMap = HashMap; +pub type FnvHashMap = HashMap; + +pub type NodeMap = FnvHashMap; +pub type DefIdMap = FnvHashMap; + #[cfg(not(stage0))] pub type NodeSet = HashSet; #[cfg(not(stage0))] @@ -26,12 +28,23 @@ pub type DefIdSet = HashSet; // Hacks to get good names #[cfg(not(stage0))] -pub mod NodeMap { +pub mod FnvHashMap { + use std::hash::Hash; use collections::HashMap; - pub fn new() -> super::NodeMap { + pub fn new + Eq, V>() -> super::FnvHashMap { HashMap::with_hasher(super::FnvHasher) } } +pub mod NodeMap { + pub fn new() -> super::NodeMap { + super::FnvHashMap::new() + } +} +pub mod DefIdMap { + pub fn new() -> super::DefIdMap { + super::FnvHashMap::new() + } +} #[cfg(not(stage0))] pub mod NodeSet { use collections::HashSet; @@ -40,13 +53,6 @@ pub mod NodeSet { } } #[cfg(not(stage0))] -pub mod DefIdMap { - use collections::HashMap; - pub fn new() -> super::DefIdMap { - HashMap::with_hasher(super::FnvHasher) - } -} -#[cfg(not(stage0))] pub mod DefIdSet { use collections::HashSet; pub fn new() -> super::DefIdSet { @@ -55,9 +61,8 @@ pub mod DefIdSet { } #[cfg(stage0)] -pub type NodeMap = HashMap; -#[cfg(stage0)] -pub type DefIdMap = HashMap; +pub type FnvHashMap = HashMap; + #[cfg(stage0)] pub type NodeSet = HashSet; #[cfg(stage0)] @@ -65,9 +70,10 @@ pub type DefIdSet = HashSet; // Hacks to get good names #[cfg(stage0)] -pub mod NodeMap { +pub mod FnvHashMap { + use std::hash::Hash; use collections::HashMap; - pub fn new() -> super::NodeMap { + pub fn new() -> super::FnvHashMap { HashMap::new() } } @@ -79,13 +85,6 @@ pub mod NodeSet { } } #[cfg(stage0)] -pub mod DefIdMap { - use collections::HashMap; - pub fn new() -> super::DefIdMap { - HashMap::new() - } -} -#[cfg(stage0)] pub mod DefIdSet { use collections::HashSet; pub fn new() -> super::DefIdSet { diff --git a/src/test/run-pass/overloaded-autoderef.rs b/src/test/run-pass/overloaded-autoderef.rs new file mode 100644 index 0000000000000..6b347b3833e46 --- /dev/null +++ b/src/test/run-pass/overloaded-autoderef.rs @@ -0,0 +1,51 @@ +// 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. + +use std::cell::RefCell; +use std::rc::Rc; + +#[deriving(Eq, Show)] +struct Point { + x: int, + y: int +} + +pub fn main() { + assert_eq!(Rc::new(5u).to_uint(), Some(5)); + assert_eq!((~&~&Rc::new(~~&~5u)).to_uint(), Some(5)); + let point = Rc::new(Point {x: 2, y: 4}); + assert_eq!(point.x, 2); + assert_eq!(point.y, 4); + + let i = Rc::new(RefCell::new(2)); + let i_value = *i.borrow(); + *i.borrow_mut() = 5; + assert_eq!((i_value, *i.borrow()), (2, 5)); + + let s = Rc::new(~"foo"); + assert!(s.equiv(&("foo"))); + assert_eq!(s.as_slice(), "foo"); + + let mut_s = Rc::new(RefCell::new(~"foo")); + mut_s.borrow_mut().push_str("bar"); + // HACK assert_eq! would fail here because it stores the LHS and RHS in two locals. + assert!(mut_s.borrow().as_slice() == "foobar"); + assert!(mut_s.borrow_mut().as_slice() == "foobar"); + + let p = Rc::new(RefCell::new(Point {x: 1, y: 2})); + p.borrow_mut().x = 3; + p.borrow_mut().y += 3; + assert_eq!(*p.borrow(), Point {x: 3, y: 5}); + + let v = Rc::new(RefCell::new(~[1, 2, 3])); + v.borrow_mut()[0] = 3; + v.borrow_mut()[1] += 3; + assert_eq!((v.borrow()[0], v.borrow()[1], v.borrow()[2]), (3, 5, 3)); +} From 26398b4f6d7b9016f1ddb6c23b5090cd98f1fa2e Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Thu, 6 Mar 2014 20:37:24 +0200 Subject: [PATCH 3/7] Introduce a common recursion limit for auto-dereference and monomorphization. --- src/librustc/driver/driver.rs | 3 +- src/librustc/driver/session.rs | 6 +++- src/librustc/middle/trans/monomorphize.rs | 8 ++--- src/librustc/middle/ty.rs | 31 ----------------- src/librustc/middle/typeck/check/mod.rs | 27 +++++---------- src/test/compile-fail/infinite-autoderef.rs | 33 +++++++++++++++++++ .../compile-fail/infinite-instantiation.rs | 2 +- src/test/compile-fail/issue-8727.rs | 2 +- src/test/compile-fail/recursion.rs | 2 +- 9 files changed, 56 insertions(+), 58 deletions(-) create mode 100644 src/test/compile-fail/infinite-autoderef.rs diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 6f2934d9138a0..10b209c998bc1 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -1020,7 +1020,8 @@ pub fn build_session_(sopts: @session::Options, lints: RefCell::new(HashMap::new()), node_id: Cell::new(1), crate_types: @RefCell::new(Vec::new()), - features: front::feature_gate::Features::new() + features: front::feature_gate::Features::new(), + recursion_limit: Cell::new(64), } } diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 4845060dd12a3..b4e1516074e6f 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -194,7 +194,11 @@ pub struct Session_ { Vec<(lint::Lint, codemap::Span, ~str)> >>, node_id: Cell, crate_types: @RefCell >, - features: front::feature_gate::Features + features: front::feature_gate::Features, + + /// The maximum recursion limit for potentially infinitely recursive + /// operations such as auto-dereference and monomorphization. + recursion_limit: Cell, } pub type Session = @Session_; diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index ecfc1aae3d99b..b39bcfb075fab 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -172,11 +172,11 @@ pub fn monomorphic_fn(ccx: @CrateContext, // Random cut-off -- code that needs to instantiate the same function // recursively more than thirty times can probably safely be assumed // to be causing an infinite expansion. - if depth > 30 { - ccx.sess.span_fatal( - ccx.tcx.map.span(fn_id.node), - "overly deep expansion of inlined function"); + if depth > ccx.sess.recursion_limit.get() { + ccx.sess.span_fatal(ccx.tcx.map.span(fn_id.node), + "reached the recursion limit during monomorphization"); } + monomorphizing.get().insert(fn_id, depth + 1); } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 33b246539341b..9d0fb86691698 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3467,37 +3467,6 @@ pub fn param_tys_in_type(ty: t) -> Vec { rslt } -pub fn occurs_check(tcx: ctxt, sp: Span, vid: TyVid, rt: t) { - // Returns a vec of all the type variables occurring in `ty`. It may - // contain duplicates. (Integral type vars aren't counted.) - fn vars_in_type(ty: t) -> Vec { - let mut rslt = Vec::new(); - walk_ty(ty, |ty| { - match get(ty).sty { - ty_infer(TyVar(v)) => rslt.push(v), - _ => () - } - }); - rslt - } - - // Fast path - if !type_needs_infer(rt) { return; } - - // Occurs check! - if vars_in_type(rt).contains(&vid) { - // Maybe this should be span_err -- however, there's an - // assertion later on that the type doesn't contain - // variables, so in this case we have to be sure to die. - tcx.sess.span_fatal - (sp, ~"type inference failed because I \ - could not find a type\n that's both of the form " - + ::util::ppaux::ty_to_str(tcx, mk_var(tcx, vid)) + - " and of the form " + ::util::ppaux::ty_to_str(tcx, rt) + - " - such a type would have to be infinitely large."); - } -} - pub fn ty_sort_str(cx: ctxt, t: t) -> ~str { match get(t).sty { ty_nil | ty_bot | ty_bool | ty_char | ty_int(_) | diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 7a51a2cc23177..e3db7f16064a4 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1241,7 +1241,7 @@ pub enum LvaluePreference { NoPreference } -pub fn autoderef(fcx: @FnCtxt, sp: Span, t: ty::t, +pub fn autoderef(fcx: @FnCtxt, sp: Span, base_ty: ty::t, expr_id: Option, mut lvalue_pref: LvaluePreference, should_stop: |ty::t, uint| -> Option) @@ -1253,24 +1253,10 @@ pub fn autoderef(fcx: @FnCtxt, sp: Span, t: ty::t, * responsible for inserting an AutoAdjustment record into `tcx.adjustments` * so that trans/borrowck/etc know about this autoderef. */ - let mut t = t; - let mut autoderefs = 0; - loop { + let mut t = base_ty; + for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) { let resolved_t = structurally_resolved_type(fcx, sp, t); - // Some extra checks to detect weird cycles and so forth: - match ty::get(resolved_t).sty { - ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_rptr(_, _) => { - match ty::get(t).sty { - ty::ty_infer(ty::TyVar(v1)) => { - ty::occurs_check(fcx.ccx.tcx, sp, v1, resolved_t); - } - _ => {} - } - } - _ => { /*ok*/ } - } - match should_stop(resolved_t, autoderefs) { Some(x) => return (resolved_t, autoderefs, Some(x)), None => {} @@ -1291,11 +1277,16 @@ pub fn autoderef(fcx: @FnCtxt, sp: Span, t: ty::t, if mt.mutbl == ast::MutImmutable { lvalue_pref = NoPreference; } - autoderefs += 1; } None => return (resolved_t, autoderefs, None) } } + + // We've reached the recursion limit, error gracefully. + fcx.tcx().sess.span_err(sp, + format!("reached the recursion limit while auto-dereferencing {}", + base_ty.repr(fcx.tcx()))); + (ty::mk_err(), 0, None) } fn try_overloaded_deref(fcx: @FnCtxt, diff --git a/src/test/compile-fail/infinite-autoderef.rs b/src/test/compile-fail/infinite-autoderef.rs new file mode 100644 index 0000000000000..ddef459453ec4 --- /dev/null +++ b/src/test/compile-fail/infinite-autoderef.rs @@ -0,0 +1,33 @@ +// 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. + +// error-pattern: reached the recursion limit while auto-dereferencing + +use std::ops::Deref; + +struct Foo; + +impl Deref for Foo { + fn deref<'a>(&'a self) -> &'a Foo { + self + } +} + +pub fn main() { + let mut x; + loop { + x = ~x; + x.foo; + x.bar(); + } + + Foo.foo; + Foo.bar(); +} diff --git a/src/test/compile-fail/infinite-instantiation.rs b/src/test/compile-fail/infinite-instantiation.rs index 1a7cc5d3ad5eb..b8fa6285d9955 100644 --- a/src/test/compile-fail/infinite-instantiation.rs +++ b/src/test/compile-fail/infinite-instantiation.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: overly deep expansion +// error-pattern: reached the recursion limit during monomorphization // issue 2258 trait to_opt { diff --git a/src/test/compile-fail/issue-8727.rs b/src/test/compile-fail/issue-8727.rs index 003f1d67cdb8a..fca59ed74ee8d 100644 --- a/src/test/compile-fail/issue-8727.rs +++ b/src/test/compile-fail/issue-8727.rs @@ -15,7 +15,7 @@ struct Data(~Option); fn generic( _ : ~[(Data,T)] ) { - //~^ ERROR overly deep expansion of inlined function + //~^ ERROR reached the recursion limit during monomorphization let rec : ~[(Data,(bool,T))] = ~[]; generic( rec ); } diff --git a/src/test/compile-fail/recursion.rs b/src/test/compile-fail/recursion.rs index 0fe042b924a6e..96676257184ce 100644 --- a/src/test/compile-fail/recursion.rs +++ b/src/test/compile-fail/recursion.rs @@ -20,7 +20,7 @@ impl Dot for Cons { } } fn test (n:int, i:int, first:T, second:T) ->int { - //~^ ERROR: overly deep expansion of inlined function + //~^ ERROR: reached the recursion limit during monomorphization match n { 0 => {first.dot(second)} // Error message should be here. It should be a type error From feedd37653b32a97e2d10c12f2cf1b14c0058c19 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sat, 8 Mar 2014 18:33:39 +0200 Subject: [PATCH 4/7] Apply @nikomatsakis' nits and comments patch. --- src/librustc/middle/trans/callee.rs | 29 +++-- src/librustc/middle/trans/common.rs | 4 + src/librustc/middle/trans/expr.rs | 21 +++- src/librustc/middle/trans/meth.rs | 9 +- src/librustc/middle/ty.rs | 10 +- src/librustc/middle/typeck/check/method.rs | 130 ++++++++++++--------- src/librustc/middle/typeck/check/mod.rs | 27 +++-- src/libsyntax/ast.rs | 2 +- 8 files changed, 149 insertions(+), 83 deletions(-) diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 5e2a8792abf86..ff63b74444c61 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -173,10 +173,13 @@ pub fn trans_fn_ref(bcx: &Block, def_id: ast::DefId, node: ExprOrMethodCall) -> let type_params = node_id_type_params(bcx, node); let vtables = match node { ExprId(id) => node_vtables(bcx, id), - MethodCall(method_call) if method_call.autoderef == 0 => { - node_vtables(bcx, method_call.expr_id) + MethodCall(ref method_call) => { + if method_call.autoderef == 0 { + node_vtables(bcx, method_call.expr_id) + } else { + None + } } - _ => None }; debug!("trans_fn_ref(def_id={}, node={:?}, type_params={}, vtables={})", def_id.repr(bcx.tcx()), node, type_params.repr(bcx.tcx()), @@ -381,15 +384,15 @@ pub fn trans_fn_ref_with_vtables( // Should be either intra-crate or inlined. assert_eq!(def_id.krate, ast::LOCAL_CRATE); - let ref_id = match node { - ExprId(id) if id != 0 => Some(id), - _ => None + let opt_ref_id = match node { + ExprId(id) => if id != 0 { Some(id) } else { None }, + MethodCall(_) => None, }; let (val, must_cast) = monomorphize::monomorphic_fn(ccx, def_id, &substs, vtables, self_vtables, - ref_id); + opt_ref_id); let mut val = val; if must_cast && node != ExprId(0) { // Monotype of the REFERENCE to the function (type params @@ -758,9 +761,19 @@ pub fn trans_call_inner<'a>( } pub enum CallArgs<'a> { + // Supply value of arguments as a list of expressions that must be + // translated. This is used in the common case of `foo(bar, qux)`. ArgExprs(&'a [@ast::Expr]), + + // Supply value of arguments as a list of LLVM value refs; frequently + // used with lang items and so forth, when the argument is an internal + // value. + ArgVals(&'a [ValueRef]), + + // For overloaded operators: `(lhs, Option(rhs, rhs_id))`. `lhs` + // is the left-hand-side and `rhs/rhs_id` is the datum/expr-id of + // the right-hand-side (if any). ArgOverloadedOp(Datum, Option<(Datum, ast::NodeId)>), - ArgVals(&'a [ValueRef]) } fn trans_args<'a>(cx: &'a Block<'a>, diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 759e5e872d4be..e3a996e33d392 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -809,9 +809,13 @@ pub fn expr_ty_adjusted(bcx: &Block, ex: &ast::Expr) -> ty::t { monomorphize_type(bcx, t) } +// Key used to lookup values supplied for type parameters in an expr. #[deriving(Eq)] pub enum ExprOrMethodCall { + // Type parameters for a path like `None::` ExprId(ast::NodeId), + + // Type parameters for a method call like `a.foo::()` MethodCall(typeck::MethodCall) } diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 056ac62183f90..d143d6743052c 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -1160,9 +1160,13 @@ fn trans_unary<'a>(bcx: &'a Block<'a>, let _icx = push_ctxt("trans_unary_datum"); let method_call = MethodCall::expr(expr.id); - let overloaded = bcx.ccx().maps.method_map.borrow().get().contains_key(&method_call); - // if overloaded, would be RvalueDpsExpr - assert!(!overloaded || op == ast::UnDeref); + + // The only overloaded operator that is translated to a datum + // is an overloaded deref, since it is always yields a `&T`. + // Otherwise, we should be in the RvalueDpsExpr path. + assert!( + op == ast::UnDeref || + !bcx.ccx().maps.method_map.borrow().get().contains_key(&method_call)); let un_ty = expr_ty(bcx, expr); @@ -1779,6 +1783,7 @@ fn deref_once<'a>(bcx: &'a Block<'a>, let mut bcx = bcx; + // Check for overloaded deref. let method_call = MethodCall { expr_id: expr.id, autoderef: derefs as u32 @@ -1787,6 +1792,11 @@ fn deref_once<'a>(bcx: &'a Block<'a>, .find(&method_call).map(|method| method.ty); let datum = match method_ty { Some(method_ty) => { + // Overloaded. Evaluate `trans_overloaded_op`, which will + // invoke the user's deref() method, which basically + // converts from the `Shaht` pointer that we have into + // a `&T` pointer. We can then proceed down the normal + // path (below) to dereference that `&T`. let datum = if derefs == 0 { datum } else { @@ -1798,7 +1808,10 @@ fn deref_once<'a>(bcx: &'a Block<'a>, let ref_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty)); Datum(val, ref_ty, RvalueExpr(Rvalue(ByValue))) } - None => datum + None => { + // Not overloaded. We already have a pointer we know how to deref. + datum + } }; let r = match ty::get(datum.ty).sty { diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index b3d69043d33e3..89f3377643142 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -335,10 +335,13 @@ fn combine_impl_and_methods_tps(bcx: &Block, // exist, in which case we need to make them. let vtables = match node { ExprId(id) => node_vtables(bcx, id), - MethodCall(method_call) if method_call.autoderef == 0 => { - node_vtables(bcx, method_call.expr_id) + MethodCall(method_call) => { + if method_call.autoderef == 0 { + node_vtables(bcx, method_call.expr_id) + } else { + None + } } - _ => None }; let r_m_origins = match vtables { Some(vt) => vt, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 9d0fb86691698..c0ad18d9520e3 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -229,7 +229,7 @@ pub struct AutoDerefRef { autoref: Option } -#[deriving(Decodable, Encodable)] +#[deriving(Decodable, Encodable, Eq, Show)] pub enum AutoRef { /// Convert from T to &T AutoPtr(Region, ast::Mutability), @@ -3271,11 +3271,15 @@ pub fn expr_kind(tcx: ctxt, expr: &ast::Expr) -> ExprKind { if method_map.borrow().get().contains_key(&MethodCall::expr(expr.id)) { // Overloaded operations are generally calls, and hence they are - // generated via DPS. However, assign_op (e.g., `x += y`) is an - // exception, as its result is always unit. + // generated via DPS, but there are two exceptions: return match expr.node { + // `a += b` has a unit result. ast::ExprAssignOp(..) => RvalueStmtExpr, + + // the deref method invoked for `*a` always yields an `&T` ast::ExprUnary(ast::UnDeref, _) => LvalueExpr, + + // in the general case, result could be any type, use DPS _ => RvalueDpsExpr }; } diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 89658b32be89a..0d37904445ff6 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -201,7 +201,13 @@ pub fn lookup_in_trait<'a>( struct LookupContext<'a> { fcx: @FnCtxt, span: Span, + + // The receiver to the method call. Only `None` in the case of + // an overloaded autoderef, where the receiver may be an intermediate + // state like "the expression `x` when it has been autoderef'd + // twice already". self_expr: Option<&'a ast::Expr>, + m_name: ast::Name, supplied_tps: &'a [ty::t], impl_dups: @RefCell>, @@ -243,51 +249,69 @@ impl<'a> LookupContext<'a> { let span = self.self_expr.map_or(self.span, |e| e.span); let self_expr_id = self.self_expr.map(|e| e.id); let (self_ty, autoderefs, result) = - check::autoderef(self.fcx, span, self_ty, self_expr_id, - PreferMutLvalue, |self_ty, autoderefs| { - - debug!("loop: self_ty={} autoderefs={}", - self.ty_to_str(self_ty), autoderefs); + check::autoderef( + self.fcx, span, self_ty, self_expr_id, PreferMutLvalue, + |self_ty, autoderefs| self.search_step(self_ty, autoderefs)); - match self.deref_args { - check::DontDerefArgs => { - match self.search_for_autoderefd_method(self_ty, autoderefs) { - Some(result) => return Some(Some(result)), - None => {} - } + match result { + Some(Some(result)) => Some(result), + _ => { + if self.is_overloaded_deref() { + // If we are searching for an overloaded deref, no + // need to try coercing a `~[T]` to an `&[T]` and + // searching for an overloaded deref on *that*. + None + } else { + self.search_for_autosliced_method(self_ty, autoderefs) + } + } + } + } - match self.search_for_autoptrd_method(self_ty, autoderefs) { - Some(result) => return Some(Some(result)), - None => {} - } + fn search_step(&self, + self_ty: ty::t, + autoderefs: uint) + -> Option> { + debug!("search_step: self_ty={} autoderefs={}", + self.ty_to_str(self_ty), autoderefs); + + match self.deref_args { + check::DontDerefArgs => { + match self.search_for_autoderefd_method(self_ty, autoderefs) { + Some(result) => return Some(Some(result)), + None => {} } - check::DoDerefArgs => { - match self.search_for_autoptrd_method(self_ty, autoderefs) { - Some(result) => return Some(Some(result)), - None => {} - } - match self.search_for_autoderefd_method(self_ty, autoderefs) { - Some(result) => return Some(Some(result)), - None => {} - } + match self.search_for_autoptrd_method(self_ty, autoderefs) { + Some(result) => return Some(Some(result)), + None => {} } } + check::DoDerefArgs => { + match self.search_for_autoptrd_method(self_ty, autoderefs) { + Some(result) => return Some(Some(result)), + None => {} + } - // Don't autoderef if we aren't supposed to. - if self.autoderef_receiver == DontAutoderefReceiver { - Some(None) - } else { - None + match self.search_for_autoderefd_method(self_ty, autoderefs) { + Some(result) => return Some(Some(result)), + None => {} + } } - }); + } - match result { - Some(Some(result)) => Some(result), - _ => self.search_for_autosliced_method(self_ty, autoderefs) + // Don't autoderef if we aren't supposed to. + if self.autoderef_receiver == DontAutoderefReceiver { + Some(None) + } else { + None } } + fn is_overloaded_deref(&self) -> bool { + self.self_expr.is_none() + } + // ______________________________________________________________________ // Candidate collection (see comment at start of file) @@ -625,17 +649,13 @@ impl<'a> LookupContext<'a> { let (self_ty, auto_deref_ref) = self.consider_reborrow(self_ty, autoderefs); - // HACK(eddyb) only overloaded auto-deref calls should be missing - // adjustments, because we imply an AutoPtr adjustment for them. - let adjustment = match auto_deref_ref { - ty::AutoDerefRef { - autoderefs: 0, - autoref: Some(ty::AutoPtr(..)) - } => None, - _ => match self.self_expr { - Some(expr) => Some((expr.id, @ty::AutoDerefRef(auto_deref_ref))), - None => return None - } + // Hacky. For overloaded derefs, there may be an adjustment + // added to the expression from the outside context, so we do not store + // an explicit adjustment, but rather we hardwire the single deref + // that occurs in trans and mem_categorization. + let adjustment = match self.self_expr { + Some(expr) => Some((expr.id, @ty::AutoDerefRef(auto_deref_ref))), + None => return None }; match self.search_for_method(self_ty) { @@ -733,9 +753,9 @@ impl<'a> LookupContext<'a> { autoderefs: uint) -> Option { /*! - * * Searches for a candidate by converting things like - * `~[]` to `&[]`. */ + * `~[]` to `&[]`. + */ let tcx = self.tcx(); let sty = ty::get(self_ty).sty.clone(); @@ -843,15 +863,20 @@ impl<'a> LookupContext<'a> { mutbls: &[ast::Mutability], mk_autoref_ty: |ast::Mutability, ty::Region| -> ty::t) -> Option { - // HACK(eddyb) only overloaded auto-deref calls should be missing - // adjustments, because we imply an AutoPtr adjustment for them. + // Hacky. For overloaded derefs, there may be an adjustment + // added to the expression from the outside context, so we do not store + // an explicit adjustment, but rather we hardwire the single deref + // that occurs in trans and mem_categorization. let self_expr_id = match self.self_expr { Some(expr) => Some(expr.id), - None => match kind(ty::ReEmpty, ast::MutImmutable) { - ty::AutoPtr(..) if autoderefs == 0 => None, - _ => return None + None => { + assert_eq!(autoderefs, 0); + assert_eq!(kind(ty::ReEmpty, ast::MutImmutable), + ty::AutoPtr(ty::ReEmpty, ast::MutImmutable)); + None } }; + // This is hokey. We should have mutability inference as a // variable. But for now, try &const, then &, then &mut: let region = @@ -1119,7 +1144,8 @@ impl<'a> LookupContext<'a> { &self, trait_def_id: ast::DefId, rcvr_substs: &ty::substs, - method_ty: &ty::Method) -> ty::t { + method_ty: &ty::Method) + -> ty::t { /*! * This is a bit tricky. We have a match against a trait method * being invoked on an object, and we want to generate the diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index e3db7f16064a4..51efcb7d1c387 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1247,11 +1247,14 @@ pub fn autoderef(fcx: @FnCtxt, sp: Span, base_ty: ty::t, should_stop: |ty::t, uint| -> Option) -> (ty::t, uint, Option) { /*! + * Executes an autoderef loop for the type `t`. At each step, invokes + * `should_stop` to decide whether to terminate the loop. Returns + * the final type and number of derefs that it performed. * - * Autoderefs the type `t` as many times as possible, returning a new type - * and an autoderef count. If the count is not zero, the receiver is - * responsible for inserting an AutoAdjustment record into `tcx.adjustments` - * so that trans/borrowck/etc know about this autoderef. */ + * Note: this method does not modify the adjustments table. The caller is + * responsible for inserting an AutoAdjustment record into the `fcx` + * using one of the suitable methods. + */ let mut t = base_ty; for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) { @@ -2282,15 +2285,15 @@ fn check_expr_with_unifier(fcx: @FnCtxt, // FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop. let (_, autoderefs, field_ty) = autoderef(fcx, expr.span, expr_t, Some(base.id), lvalue_pref, |base_t, _| { - match ty::get(base_t).sty { - ty::ty_struct(base_id, ref substs) => { - debug!("struct named {}", ppaux::ty_to_str(tcx, base_t)); - let fields = ty::lookup_struct_fields(tcx, base_id); - lookup_field_ty(tcx, base_id, fields.as_slice(), field, &(*substs)) + match ty::get(base_t).sty { + ty::ty_struct(base_id, ref substs) => { + debug!("struct named {}", ppaux::ty_to_str(tcx, base_t)); + let fields = ty::lookup_struct_fields(tcx, base_id); + lookup_field_ty(tcx, base_id, fields.as_slice(), field, &(*substs)) + } + _ => None } - _ => None - } - }); + }); match field_ty { Some(field_ty) => { fcx.write_ty(expr.id, field_ty); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index b6b18f6671d31..7fef6da560783 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -354,7 +354,7 @@ pub enum Pat_ { PatVec(Vec<@Pat> , Option<@Pat>, Vec<@Pat> ) } -#[deriving(Clone, Eq, Encodable, Decodable, Hash)] +#[deriving(Clone, Eq, Encodable, Decodable, Hash, Show)] pub enum Mutability { MutMutable, MutImmutable, From 27c62449db9e752bcab39110aaaad63b2773e68b Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Tue, 11 Mar 2014 22:27:55 +0200 Subject: [PATCH 5/7] Region + borrow checker support and tests for overloaded autoderef. --- .../middle/borrowck/gather_loans/mod.rs | 39 +++++- src/librustc/middle/typeck/check/regionck.rs | 79 ++++++++--- ...rrowck-borrow-overloaded-auto-deref-mut.rs | 131 ++++++++++++++++++ .../borrowck-borrow-overloaded-auto-deref.rs | 122 ++++++++++++++++ ...rowck-move-out-of-overloaded-auto-deref.rs | 16 +++ .../run-pass/overloaded-autoderef-count.rs | 82 +++++++++++ .../run-pass/overloaded-autoderef-indexing.rs | 24 ++++ .../run-pass/overloaded-autoderef-order.rs | 71 ++++++++++ 8 files changed, 540 insertions(+), 24 deletions(-) create mode 100644 src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs create mode 100644 src/test/compile-fail/borrowck-borrow-overloaded-auto-deref.rs create mode 100644 src/test/compile-fail/borrowck-move-out-of-overloaded-auto-deref.rs create mode 100644 src/test/run-pass/overloaded-autoderef-count.rs create mode 100644 src/test/run-pass/overloaded-autoderef-indexing.rs create mode 100644 src/test/run-pass/overloaded-autoderef-order.rs diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index d2fcee79fc031..f6168feb2b846 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -326,6 +326,39 @@ impl<'a> GatherLoanCtxt<'a> { assert_eq!(id, popped); } + pub fn guarantee_autoderefs(&mut self, + expr: &ast::Expr, + autoderefs: uint) { + let method_map = self.bccx.method_map.borrow(); + for i in range(0, autoderefs) { + match method_map.get().find(&MethodCall::autoderef(expr.id, i as u32)) { + Some(method) => { + // Treat overloaded autoderefs as if an AutoRef adjustment + // was applied on the base type, as that is always the case. + let mut mc = self.bccx.mc(); + let cmt = match mc.cat_expr_autoderefd(expr, i) { + Ok(v) => v, + Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc") + }; + let self_ty = *ty::ty_fn_args(method.ty).get(0); + let (m, r) = match ty::get(self_ty).sty { + ty::ty_rptr(r, ref m) => (m.mutbl, r), + _ => self.tcx().sess.span_bug(expr.span, + format!("bad overloaded deref type {}", + method.ty.repr(self.tcx()))) + }; + self.guarantee_valid(expr.id, + expr.span, + cmt, + m, + r, + AutoRef); + } + None => {} + } + } + } + pub fn guarantee_adjustments(&mut self, expr: &ast::Expr, adjustment: &ty::AutoAdjustment) { @@ -341,15 +374,17 @@ impl<'a> GatherLoanCtxt<'a> { ty::AutoDerefRef( ty::AutoDerefRef { - autoref: None, .. }) => { + autoref: None, autoderefs }) => { debug!("no autoref"); + self.guarantee_autoderefs(expr, autoderefs); return; } ty::AutoDerefRef( ty::AutoDerefRef { autoref: Some(ref autoref), - autoderefs: autoderefs}) => { + autoderefs}) => { + self.guarantee_autoderefs(expr, autoderefs); let mut mc = self.bccx.mc(); let cmt = match mc.cat_expr_autoderefd(expr, autoderefs) { Ok(v) => v, diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 169a7cfc90e71..aef1b20273a28 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -141,6 +141,19 @@ use syntax::codemap::Span; use syntax::visit; use syntax::visit::Visitor; +// If mem categorization results in an error, it's because the type +// check failed (or will fail, when the error is uncovered and +// reported during writeback). In this case, we just ignore this part +// of the code and don't try to add any more region constraints. +macro_rules! ignore_err( + ($inp: expr) => ( + match $inp { + Ok(v) => v, + Err(()) => return + } + ) +) + pub struct Rcx { fcx: @FnCtxt, errors_reported: uint, @@ -395,7 +408,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { match **adjustment { ty::AutoDerefRef(ty::AutoDerefRef {autoderefs, autoref: opt_autoref}) => { let expr_ty = rcx.resolve_node_type(expr.id); - constrain_derefs(rcx, expr, autoderefs, expr_ty); + constrain_autoderefs(rcx, expr, autoderefs, expr_ty); for autoref in opt_autoref.iter() { link_autoref(rcx, expr, autoderefs, autoref); @@ -494,7 +507,13 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { } None => rcx.resolve_node_type(base.id) }; - constrain_derefs(rcx, expr, 1, base_ty); + match ty::get(base_ty).sty { + ty::ty_rptr(r_ptr, _) => { + mk_subregion_due_to_derefence(rcx, expr.span, + ty::ReScope(expr.id), r_ptr); + } + _ => {} + } visit::walk_expr(rcx, expr, ()); } @@ -819,11 +838,10 @@ fn constrain_call(rcx: &mut Rcx, fn_sig.output); } -fn constrain_derefs(rcx: &mut Rcx, - deref_expr: &ast::Expr, - derefs: uint, - mut derefd_ty: ty::t) -{ +fn constrain_autoderefs(rcx: &mut Rcx, + deref_expr: &ast::Expr, + derefs: uint, + mut derefd_ty: ty::t) { /*! * Invoked on any dereference that occurs, whether explicitly * or through an auto-deref. Checks that if this is a region @@ -832,16 +850,46 @@ fn constrain_derefs(rcx: &mut Rcx, */ let r_deref_expr = ty::ReScope(deref_expr.id); for i in range(0u, derefs) { - debug!("constrain_derefs(deref_expr=?, derefd_ty={}, derefs={:?}/{:?}", + debug!("constrain_autoderefs(deref_expr=?, derefd_ty={}, derefs={:?}/{:?}", rcx.fcx.infcx().ty_to_str(derefd_ty), i, derefs); + let method_call = MethodCall::autoderef(deref_expr.id, i as u32); + derefd_ty = match rcx.fcx.inh.method_map.get().find(&method_call) { + Some(method) => { + // Treat overloaded autoderefs as if an AutoRef adjustment + // was applied on the base type, as that is always the case. + let fn_sig = ty::ty_fn_sig(method.ty); + let self_ty = *fn_sig.inputs.get(0); + let (m, r) = match ty::get(self_ty).sty { + ty::ty_rptr(r, ref m) => (m.mutbl, r), + _ => rcx.tcx().sess.span_bug(deref_expr.span, + format!("bad overloaded deref type {}", + method.ty.repr(rcx.tcx()))) + }; + { + let mut mc = mc::MemCategorizationContext { typer: &mut *rcx }; + let self_cmt = ignore_err!(mc.cat_expr_autoderefd(deref_expr, i)); + link_region(mc.typer, deref_expr.span, r, m, self_cmt); + } + + // Specialized version of constrain_call. + constrain_regions_in_type(rcx, r_deref_expr, + infer::CallRcvr(deref_expr.span), + self_ty); + constrain_regions_in_type(rcx, r_deref_expr, + infer::CallReturn(deref_expr.span), + fn_sig.output); + fn_sig.output + } + None => derefd_ty + }; + match ty::get(derefd_ty).sty { ty::ty_rptr(r_ptr, _) => { mk_subregion_due_to_derefence(rcx, deref_expr.span, r_deref_expr, r_ptr); } - _ => {} } @@ -965,19 +1013,6 @@ fn constrain_regions_in_type( return e == rcx.errors_reported; } -// If mem categorization results in an error, it's because the type -// check failed (or will fail, when the error is uncovered and -// reported during writeback). In this case, we just ignore this part -// of the code and don't try to add any more region constraints. -macro_rules! ignore_err( - ($inp: expr) => ( - match $inp { - Ok(v) => { v } - Err(()) => { return; } - } - ) -) - fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr, mutability: ast::Mutability, base: &ast::Expr) { /*! diff --git a/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs b/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs new file mode 100644 index 0000000000000..9800fd704ac71 --- /dev/null +++ b/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs @@ -0,0 +1,131 @@ +// 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 how overloaded deref interacts with borrows when DerefMut +// is implemented. + +use std::ops::{Deref, DerefMut}; + +struct Own { + value: *mut T +} + +impl Deref for Own { + fn deref<'a>(&'a self) -> &'a T { + unsafe { &*self.value } + } +} + +impl DerefMut for Own { + fn deref_mut<'a>(&'a mut self) -> &'a mut T { + unsafe { &mut *self.value } + } +} + +struct Point { + x: int, + y: int +} + +impl Point { + fn get(&self) -> (int, int) { + (self.x, self.y) + } + + fn set(&mut self, x: int, y: int) { + self.x = x; + self.y = y; + } + + fn x_ref<'a>(&'a self) -> &'a int { + &self.x + } + + fn y_mut<'a>(&'a mut self) -> &'a mut int { + &mut self.y + } +} + +fn deref_imm_field(x: Own) { + let _i = &x.y; +} + +fn deref_mut_field1(x: Own) { + let _i = &mut x.y; //~ ERROR cannot borrow +} + +fn deref_mut_field2(mut x: Own) { + let _i = &mut x.y; +} + +fn deref_extend_field<'a>(x: &'a Own) -> &'a int { + &x.y +} + +fn deref_extend_mut_field1<'a>(x: &'a Own) -> &'a mut int { + &mut x.y //~ ERROR cannot borrow +} + +fn deref_extend_mut_field2<'a>(x: &'a mut Own) -> &'a mut int { + &mut x.y +} + +fn assign_field1<'a>(x: Own) { + x.y = 3; //~ ERROR cannot borrow +} + +fn assign_field2<'a>(x: &'a Own) { + x.y = 3; //~ ERROR cannot assign +} + +fn assign_field3<'a>(x: &'a mut Own) { + x.y = 3; +} + +// FIXME(eddyb) #12825 This shouldn't attempt to call deref_mut. +/* +fn deref_imm_method(x: Own) { + let _i = x.get(); +} +*/ + +fn deref_mut_method1(x: Own) { + x.set(0, 0); //~ ERROR cannot borrow +} + +fn deref_mut_method2(mut x: Own) { + x.set(0, 0); +} + +fn deref_extend_method<'a>(x: &'a Own) -> &'a int { + x.x_ref() +} + +fn deref_extend_mut_method1<'a>(x: &'a Own) -> &'a mut int { + x.y_mut() //~ ERROR cannot borrow +} + +fn deref_extend_mut_method2<'a>(x: &'a mut Own) -> &'a mut int { + x.y_mut() +} + +fn assign_method1<'a>(x: Own) { + *x.y_mut() = 3; //~ ERROR cannot borrow +} + +fn assign_method2<'a>(x: &'a Own) { + *x.y_mut() = 3; //~ ERROR cannot borrow +} + +fn assign_method3<'a>(x: &'a mut Own) { + *x.y_mut() = 3; +} + +pub fn main() {} \ No newline at end of file diff --git a/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref.rs b/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref.rs new file mode 100644 index 0000000000000..de68dd311c657 --- /dev/null +++ b/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref.rs @@ -0,0 +1,122 @@ +// 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 how overloaded deref interacts with borrows when only +// Deref and not DerefMut is implemented. + +use std::ops::Deref; + +struct Rc { + value: *T +} + +impl Deref for Rc { + fn deref<'a>(&'a self) -> &'a T { + unsafe { &*self.value } + } +} + +struct Point { + x: int, + y: int +} + +impl Point { + fn get(&self) -> (int, int) { + (self.x, self.y) + } + + fn set(&mut self, x: int, y: int) { + self.x = x; + self.y = y; + } + + fn x_ref<'a>(&'a self) -> &'a int { + &self.x + } + + fn y_mut<'a>(&'a mut self) -> &'a mut int { + &mut self.y + } +} + +fn deref_imm_field(x: Rc) { + let _i = &x.y; +} + +fn deref_mut_field1(x: Rc) { + let _i = &mut x.y; //~ ERROR cannot borrow +} + +fn deref_mut_field2(mut x: Rc) { + let _i = &mut x.y; //~ ERROR cannot borrow +} + +fn deref_extend_field<'a>(x: &'a Rc) -> &'a int { + &x.y +} + +fn deref_extend_mut_field1<'a>(x: &'a Rc) -> &'a mut int { + &mut x.y //~ ERROR cannot borrow +} + +fn deref_extend_mut_field2<'a>(x: &'a mut Rc) -> &'a mut int { + &mut x.y //~ ERROR cannot borrow +} + +fn assign_field1<'a>(x: Rc) { + x.y = 3; //~ ERROR cannot assign +} + +fn assign_field2<'a>(x: &'a Rc) { + x.y = 3; //~ ERROR cannot assign +} + +fn assign_field3<'a>(x: &'a mut Rc) { + x.y = 3; //~ ERROR cannot assign +} + +fn deref_imm_method(x: Rc) { + let _i = x.get(); +} + +fn deref_mut_method1(x: Rc) { + x.set(0, 0); //~ ERROR cannot borrow +} + +fn deref_mut_method2(mut x: Rc) { + x.set(0, 0); //~ ERROR cannot borrow +} + +fn deref_extend_method<'a>(x: &'a Rc) -> &'a int { + x.x_ref() +} + +fn deref_extend_mut_method1<'a>(x: &'a Rc) -> &'a mut int { + x.y_mut() //~ ERROR cannot borrow +} + +fn deref_extend_mut_method2<'a>(x: &'a mut Rc) -> &'a mut int { + x.y_mut() //~ ERROR cannot borrow +} + +fn assign_method1<'a>(x: Rc) { + *x.y_mut() = 3; //~ ERROR cannot borrow +} + +fn assign_method2<'a>(x: &'a Rc) { + *x.y_mut() = 3; //~ ERROR cannot borrow +} + +fn assign_method3<'a>(x: &'a mut Rc) { + *x.y_mut() = 3; //~ ERROR cannot borrow +} + +pub fn main() {} diff --git a/src/test/compile-fail/borrowck-move-out-of-overloaded-auto-deref.rs b/src/test/compile-fail/borrowck-move-out-of-overloaded-auto-deref.rs new file mode 100644 index 0000000000000..1a96e5ef4b0cc --- /dev/null +++ b/src/test/compile-fail/borrowck-move-out-of-overloaded-auto-deref.rs @@ -0,0 +1,16 @@ +// 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. + +use std::rc::Rc; + +pub fn main() { + let _x = Rc::new(vec!(1, 2)).move_iter(); + //~^ ERROR cannot move out of dereference of `&`-pointer +} diff --git a/src/test/run-pass/overloaded-autoderef-count.rs b/src/test/run-pass/overloaded-autoderef-count.rs new file mode 100644 index 0000000000000..10ee06473c80b --- /dev/null +++ b/src/test/run-pass/overloaded-autoderef-count.rs @@ -0,0 +1,82 @@ +// 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. + +use std::cell::Cell; +use std::ops::{Deref, DerefMut}; +use std::vec_ng::Vec; + +#[deriving(Eq)] +struct DerefCounter { + count_imm: Cell, + count_mut: uint, + value: T +} + +impl DerefCounter { + fn new(value: T) -> DerefCounter { + DerefCounter { + count_imm: Cell::new(0), + count_mut: 0, + value: value + } + } + + fn counts(&self) -> (uint, uint) { + (self.count_imm.get(), self.count_mut) + } +} + +impl Deref for DerefCounter { + fn deref<'a>(&'a self) -> &'a T { + self.count_imm.set(self.count_imm.get() + 1); + &self.value + } +} + +impl DerefMut for DerefCounter { + fn deref_mut<'a>(&'a mut self) -> &'a mut T { + self.count_mut += 1; + &mut self.value + } +} + +#[deriving(Eq, Show)] +struct Point { + x: int, + y: int +} + +impl Point { + fn get(&self) -> (int, int) { + (self.x, self.y) + } +} + +pub fn main() { + let mut p = DerefCounter::new(Point {x: 0, y: 0}); + + let _ = p.x; + assert_eq!(p.counts(), (1, 0)); + + let _ = &p.x; + assert_eq!(p.counts(), (2, 0)); + + let _ = &mut p.y; + assert_eq!(p.counts(), (2, 1)); + + p.x += 3; + assert_eq!(p.counts(), (2, 2)); + + p.get(); + assert_eq!(p.counts(), (2, 3)); + + // Check the final state. + assert_eq!(*p, Point {x: 3, y: 0}); +} diff --git a/src/test/run-pass/overloaded-autoderef-indexing.rs b/src/test/run-pass/overloaded-autoderef-indexing.rs new file mode 100644 index 0000000000000..c8885a3090c00 --- /dev/null +++ b/src/test/run-pass/overloaded-autoderef-indexing.rs @@ -0,0 +1,24 @@ +// 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. + +struct DerefArray<'a, T> { + inner: &'a [T] +} + +impl<'a, T> Deref<&'a [T]> for DerefArray<'a, T> { + fn deref<'b>(&'b self) -> &'b &'a [T] { + &self.inner + } +} + +pub fn main() { + let a = &[1, 2, 3]; + assert_eq!(DerefArray {inner: a}[1], 2); +} diff --git a/src/test/run-pass/overloaded-autoderef-order.rs b/src/test/run-pass/overloaded-autoderef-order.rs new file mode 100644 index 0000000000000..9deeff773642d --- /dev/null +++ b/src/test/run-pass/overloaded-autoderef-order.rs @@ -0,0 +1,71 @@ +// 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. + +use std::rc::Rc; + +struct DerefWrapper { + x: X, + y: Y +} + +impl DerefWrapper { + fn get_x(self) -> X { + self.x + } +} + +impl Deref for DerefWrapper { + fn deref<'a>(&'a self) -> &'a Y { + &self.y + } +} + +mod priv_test { + pub struct DerefWrapperHideX { + priv x: X, + y: Y + } + + impl DerefWrapperHideX { + pub fn new(x: X, y: Y) -> DerefWrapperHideX { + DerefWrapperHideX { + x: x, + y: y + } + } + } + + impl Deref for DerefWrapperHideX { + fn deref<'a>(&'a self) -> &'a Y { + &self.y + } + } +} + +pub fn main() { + let nested = DerefWrapper {x: true, y: DerefWrapper {x: 0, y: 1}}; + + // Use the first field that you can find. + assert_eq!(nested.x, true); + assert_eq!((*nested).x, 0); + + // Same for methods, even though there are multiple + // candidates (at different nesting levels). + assert_eq!(nested.get_x(), true); + assert_eq!((*nested).get_x(), 0); + + // Also go through multiple levels of indirection. + assert_eq!(Rc::new(nested).x, true); + + let nested_priv = priv_test::DerefWrapperHideX::new(true, DerefWrapper {x: 0, y: 1}); + // FIXME(eddyb) #12808 should skip private fields. + // assert_eq!(nested_priv.x, 0); + assert_eq!((*nested_priv).x, 0); +} From a7db0d5d30adbaec2b4ec6e009a0bf9c7850f9be Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Mar 2014 13:31:00 -0400 Subject: [PATCH 6/7] compile-fail: Beef up borrowck test to include some scenarios where we borrow mutably twice in a row --- ...rrowck-borrow-overloaded-auto-deref-mut.rs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs b/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs index 9800fd704ac71..e43ac98aa9802 100644 --- a/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs +++ b/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs @@ -77,6 +77,21 @@ fn deref_extend_mut_field2<'a>(x: &'a mut Own) -> &'a mut int { &mut x.y } +fn deref_extend_mut_field3<'a>(x: &'a mut Own) { + // Hmm, this is unfortunate, because with ~ it would work, + // but it's presently the expected outcome. See `deref_extend_mut_field4` + // for the workaround. + + let _x = &mut x.x; + let _y = &mut x.y; //~ ERROR cannot borrow +} + +fn deref_extend_mut_field4<'a>(x: &'a mut Own) { + let p = &mut **x; + let _x = &mut p.x; + let _y = &mut p.y; +} + fn assign_field1<'a>(x: Own) { x.y = 3; //~ ERROR cannot borrow } @@ -89,6 +104,11 @@ fn assign_field3<'a>(x: &'a mut Own) { x.y = 3; } +fn assign_field4<'a>(x: &'a mut Own) { + let _p: &mut Point = &mut **x; + x.y = 3; //~ ERROR cannot borrow +} + // FIXME(eddyb) #12825 This shouldn't attempt to call deref_mut. /* fn deref_imm_method(x: Own) { @@ -128,4 +148,4 @@ fn assign_method3<'a>(x: &'a mut Own) { *x.y_mut() = 3; } -pub fn main() {} \ No newline at end of file +pub fn main() {} From 01a15d5870169636cec50a1f9d98bc967472a680 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Mar 2014 13:38:17 -0400 Subject: [PATCH 7/7] Tweak comments --- src/librustc/middle/typeck/check/regionck.rs | 23 ++++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index aef1b20273a28..1278bfb2e168f 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -509,8 +509,8 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { }; match ty::get(base_ty).sty { ty::ty_rptr(r_ptr, _) => { - mk_subregion_due_to_derefence(rcx, expr.span, - ty::ReScope(expr.id), r_ptr); + mk_subregion_due_to_dereference(rcx, expr.span, + ty::ReScope(expr.id), r_ptr); } _ => {} } @@ -843,10 +843,9 @@ fn constrain_autoderefs(rcx: &mut Rcx, derefs: uint, mut derefd_ty: ty::t) { /*! - * Invoked on any dereference that occurs, whether explicitly - * or through an auto-deref. Checks that if this is a region - * pointer being derefenced, the lifetime of the pointer includes - * the deref expr. + * Invoked on any auto-dereference that occurs. Checks that if + * this is a region pointer being dereferenced, the lifetime of + * the pointer includes the deref expr. */ let r_deref_expr = ty::ReScope(deref_expr.id); for i in range(0u, derefs) { @@ -887,8 +886,8 @@ fn constrain_autoderefs(rcx: &mut Rcx, match ty::get(derefd_ty).sty { ty::ty_rptr(r_ptr, _) => { - mk_subregion_due_to_derefence(rcx, deref_expr.span, - r_deref_expr, r_ptr); + mk_subregion_due_to_dereference(rcx, deref_expr.span, + r_deref_expr, r_ptr); } _ => {} } @@ -902,10 +901,10 @@ fn constrain_autoderefs(rcx: &mut Rcx, } } -pub fn mk_subregion_due_to_derefence(rcx: &mut Rcx, - deref_span: Span, - minimum_lifetime: ty::Region, - maximum_lifetime: ty::Region) { +pub fn mk_subregion_due_to_dereference(rcx: &mut Rcx, + deref_span: Span, + minimum_lifetime: ty::Region, + maximum_lifetime: ty::Region) { rcx.fcx.mk_subr(true, infer::DerefPointer(deref_span), minimum_lifetime, maximum_lifetime) }