From 0b5bc3314fa9bf768163828a8e38bac588936fe9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 31 Oct 2014 05:44:10 -0400 Subject: [PATCH] Implement new operator dispatch semantics. Key points are: 1. `a + b` maps directly to `Add`, where `A` and `B` are the types of `a` and `b`. 2. Indexing and slicing autoderefs consistently. --- src/librustc/middle/ty.rs | 15 + src/librustc/middle/typeck/check/method.rs | 291 +++++++++-- src/librustc/middle/typeck/check/mod.rs | 486 ++++++++++++------ src/librustc/util/ppaux.rs | 7 + .../borrowck-overloaded-index-autoderef.rs | 91 ++++ src/test/compile-fail/issue-13482-2.rs | 2 +- src/test/compile-fail/issue-15207.rs | 1 - src/test/compile-fail/issue-17033.rs | 2 +- src/test/compile-fail/issue-2149.rs | 1 - src/test/compile-fail/slice-mut-2.rs | 2 +- .../type-params-in-different-spaces-1.rs | 2 +- ...ator.rs => overloaded-index-assoc-list.rs} | 0 .../run-pass/overloaded-index-autoderef.rs | 79 +++ .../run-pass/overloaded-index-in-field.rs | 52 ++ src/test/run-pass/overloaded-index.rs | 17 + 15 files changed, 827 insertions(+), 221 deletions(-) create mode 100644 src/test/compile-fail/borrowck-overloaded-index-autoderef.rs rename src/test/run-pass/{overload-index-operator.rs => overloaded-index-assoc-list.rs} (100%) create mode 100644 src/test/run-pass/overloaded-index-autoderef.rs create mode 100644 src/test/run-pass/overloaded-index-in-field.rs diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index bdb9ef8e710..23a52895935 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5566,3 +5566,18 @@ pub fn with_freevars(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[Freevar]| -> T) Some(d) => f(d.as_slice()) } } + +impl AutoAdjustment { + pub fn is_identity(&self) -> bool { + match *self { + AdjustAddEnv(..) => false, + AdjustDerefRef(ref r) => r.is_identity(), + } + } +} + +impl AutoDerefRef { + pub fn is_identity(&self) -> bool { + self.autoderefs == 0 && self.autoref.is_none() + } +} diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 4334cf7db7a..abffa857a08 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -96,6 +96,7 @@ trait `ToString` imported, and I call `to_string()` on a value of type `T`, use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure, MethodObject, MethodTraitObject}; use middle::typeck::check::regionmanip::replace_late_bound_regions; use middle::typeck::TypeAndSubsts; +use middle::typeck::check::vtable; use middle::ty_fold::TypeFoldable; use util::common::indenter; use util::ppaux; @@ -173,46 +174,178 @@ pub fn lookup<'a, 'tcx>( pub fn lookup_in_trait<'a, 'tcx>( fcx: &'a FnCtxt<'a, 'tcx>, - - // In a call `a.b::(...)`: - span: Span, // The expression `a.b(...)`'s span. - self_expr: Option<&'a ast::Expr>, // The expression `a`, if available. - m_name: ast::Name, // The name `b`. - trait_did: DefId, // The trait to limit the lookup to. - self_ty: ty::t, // The type of `a`. - supplied_tps: &'a [ty::t]) // The list of types X, Y, ... . + span: Span, + self_expr: Option<&'a ast::Expr>, + m_name: ast::Name, + trait_def_id: DefId, + self_ty: ty::t, + opt_input_types: Option>) -> Option { - let mut lcx = LookupContext { - fcx: fcx, - span: span, - self_expr: self_expr, - m_name: m_name, - supplied_tps: supplied_tps, - impl_dups: HashSet::new(), - inherent_candidates: Vec::new(), - extension_candidates: Vec::new(), - static_candidates: Vec::new(), - deref_args: check::DoDerefArgs, - check_traits: CheckTraitsOnly, - autoderef_receiver: DontAutoderefReceiver, - }; + lookup_in_trait_adjusted(fcx, span, self_expr, m_name, trait_def_id, + ty::AutoDerefRef { autoderefs: 0, autoref: None }, + self_ty, opt_input_types) +} - debug!("method lookup_in_trait(self_ty={}, self_expr={}, m_name={}, trait_did={})", +pub fn lookup_in_trait_adjusted<'a, 'tcx>( + fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + self_expr: Option<&'a ast::Expr>, + m_name: ast::Name, + trait_def_id: DefId, + autoderefref: ty::AutoDerefRef, + self_ty: ty::t, + opt_input_types: Option>) + -> Option +{ + debug!("method lookup_in_trait(self_ty={}, self_expr={}, m_name={}, trait_def_id={})", self_ty.repr(fcx.tcx()), self_expr.repr(fcx.tcx()), m_name.repr(fcx.tcx()), - trait_did.repr(fcx.tcx())); + trait_def_id.repr(fcx.tcx())); + + let trait_def = ty::lookup_trait_def(fcx.tcx(), trait_def_id); + + let expected_number_of_input_types = trait_def.generics.types.len(subst::TypeSpace); + let input_types = match opt_input_types { + Some(input_types) => { + assert_eq!(expected_number_of_input_types, input_types.len()); + input_types + } + + None => { + fcx.inh.infcx.next_ty_vars(expected_number_of_input_types) + } + }; + + let number_assoc_types = trait_def.generics.types.len(subst::AssocSpace); + let assoc_types = fcx.inh.infcx.next_ty_vars(number_assoc_types); - lcx.push_bound_candidates(self_ty, Some(trait_did)); - lcx.push_extension_candidate(trait_did); + assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0); + assert!(trait_def.generics.regions.is_empty()); - // when doing a trait search, ambiguity can't really happen except - // as part of the trait-lookup in general - match lcx.search(self_ty) { - Ok(callee) => Some(callee), - Err(_) => None + // Construct a trait-reference `self_ty : Trait` + let substs = subst::Substs::new_trait(input_types, Vec::new(), assoc_types, self_ty); + let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, substs)); + + // Construct an obligation + let obligation = traits::Obligation::misc(span, trait_ref.clone()); + + // Now we want to know if this can be matched + let mut selcx = traits::SelectionContext::new(fcx.infcx(), + &fcx.inh.param_env, + fcx); + if !selcx.evaluate_obligation_intracrate(&obligation) { + debug!("--> Cannot match obligation"); + return None; // Cannot be matched, no such method resolution is possible. + } + + // Trait must have a method named `m_name` and it should not have + // type parameters or early-bound regions. + let tcx = fcx.tcx(); + let (method_num, method_ty) = trait_method(tcx, trait_def_id, m_name).unwrap(); + assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0); + assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0); + + // Substitute the trait parameters into the method type and + // instantiate late-bound regions to get the actual method type. + let ref bare_fn_ty = method_ty.fty; + let fn_sig = bare_fn_ty.sig.subst(tcx, &trait_ref.substs); + let fn_sig = replace_late_bound_regions_with_fresh_var(fcx.infcx(), span, + fn_sig.binder_id, &fn_sig); + let transformed_self_ty = fn_sig.inputs[0]; + let fty = ty::mk_bare_fn(tcx, ty::BareFnTy { + sig: fn_sig, + fn_style: bare_fn_ty.fn_style, + abi: bare_fn_ty.abi.clone(), + }); + + debug!("matched method fty={} obligation={}", + fty.repr(fcx.tcx()), + obligation.repr(fcx.tcx())); + + // Register obligations for the parameters. This will include the + // `Self` parameter, which in turn has a bound of the main trait, + // so this also effectively registers `obligation` as well. (We + // used to register `obligation` explicitly, but that resulted in + // double error messages being reported.) + fcx.add_obligations_for_parameters( + traits::ObligationCause::misc(span), + &trait_ref.substs, + &method_ty.generics); + + // FIXME(#18653) -- Try to resolve obligations, giving us more + // typing information, which can sometimes be needed to avoid + // pathological region inference failures. + vtable::select_new_fcx_obligations(fcx); + + // Insert any adjustments needed (always an autoref of some mutability). + match self_expr { + None => { } + + Some(self_expr) => { + debug!("inserting adjustment if needed (self-id = {}, \ + base adjustment = {}, explicit self = {})", + self_expr.id, autoderefref, method_ty.explicit_self); + + match method_ty.explicit_self { + ty::ByValueExplicitSelfCategory => { + // Trait method is fn(self), no transformation needed. + if !autoderefref.is_identity() { + fcx.write_adjustment( + self_expr.id, + span, + ty::AdjustDerefRef(autoderefref)); + } + } + + ty::ByReferenceExplicitSelfCategory(..) => { + // Trait method is fn(&self) or fn(&mut self), need an + // autoref. Pull the region etc out of the type of first argument. + match ty::get(transformed_self_ty).sty { + ty::ty_rptr(region, ty::mt { mutbl, ty: _ }) => { + let ty::AutoDerefRef { autoderefs, autoref } = autoderefref; + let autoref = autoref.map(|r| box r); + fcx.write_adjustment( + self_expr.id, + span, + ty::AdjustDerefRef(ty::AutoDerefRef { + autoderefs: autoderefs, + autoref: Some(ty::AutoPtr(region, mutbl, autoref)) + })); + } + + _ => { + fcx.tcx().sess.span_bug( + span, + format!( + "trait method is &self but first arg is: {}", + transformed_self_ty.repr(fcx.tcx())).as_slice()); + } + } + } + + _ => { + fcx.tcx().sess.span_bug( + span, + format!( + "unexpected explicit self type in operator method: {}", + method_ty.explicit_self).as_slice()); + } + } + } } + + let callee = MethodCallee { + origin: MethodTypeParam(MethodParam{trait_ref: trait_ref.clone(), + method_num: method_num}), + ty: fty, + substs: trait_ref.substs.clone() + }; + + debug!("callee = {}", callee.repr(fcx.tcx())); + + Some(callee) } pub fn report_error(fcx: &FnCtxt, @@ -1446,9 +1579,8 @@ fn confirm_candidate(&self, rcvr_ty: ty::t, candidate: &Candidate) } } - fn fixup_derefs_on_method_receiver_if_necessary( - &self, - method_callee: &MethodCallee) { + fn fixup_derefs_on_method_receiver_if_necessary(&self, + method_callee: &MethodCallee) { let sig = match ty::get(method_callee.ty).sty { ty::ty_bare_fn(ref f) => f.sig.clone(), ty::ty_closure(ref f) => f.sig.clone(), @@ -1485,6 +1617,9 @@ fn fixup_derefs_on_method_receiver_if_necessary( } } + debug!("fixup_derefs_on_method_receiver_if_necessary: exprs={}", + exprs.repr(self.tcx())); + // Fix up autoderefs and derefs. for (i, expr) in exprs.iter().rev().enumerate() { // Count autoderefs. @@ -1500,6 +1635,9 @@ fn fixup_derefs_on_method_receiver_if_necessary( Some(_) | None => 0, }; + debug!("fixup_derefs_on_method_receiver_if_necessary: i={} expr={} autoderef_count={}", + i, expr.repr(self.tcx()), autoderef_count); + if autoderef_count > 0 { check::autoderef(self.fcx, expr.span, @@ -1518,24 +1656,59 @@ fn fixup_derefs_on_method_receiver_if_necessary( // Don't retry the first one or we might infinite loop! if i != 0 { match expr.node { - ast::ExprIndex(ref base_expr, ref index_expr) => { - check::try_overloaded_index( - self.fcx, - Some(MethodCall::expr(expr.id)), - *expr, + ast::ExprIndex(ref base_expr, _) => { + let mut base_adjustment = + match self.fcx.inh.adjustments.borrow().find(&base_expr.id) { + Some(&ty::AdjustDerefRef(ref adr)) => (*adr).clone(), + None => ty::AutoDerefRef { autoderefs: 0, autoref: None }, + Some(_) => { + self.tcx().sess.span_bug( + base_expr.span, + "unexpected adjustment type"); + } + }; + + // If this is an overloaded index, the + // adjustment will include an extra layer of + // autoref because the method is an &self/&mut + // self method. We have to peel it off to get + // the raw adjustment that `try_index_step` + // expects. This is annoying and horrible. We + // ought to recode this routine so it doesn't + // (ab)use the normal type checking paths. + base_adjustment.autoref = match base_adjustment.autoref { + None => { None } + Some(AutoPtr(_, _, None)) => { None } + Some(AutoPtr(_, _, Some(box r))) => { Some(r) } + Some(_) => { + self.tcx().sess.span_bug( + base_expr.span, + "unexpected adjustment autoref"); + } + }; + + let adjusted_base_ty = + self.fcx.adjust_expr_ty( &**base_expr, - self.fcx.expr_ty(&**base_expr), - index_expr, - PreferMutLvalue); + Some(&ty::AdjustDerefRef(base_adjustment.clone()))); + + check::try_index_step( + self.fcx, + MethodCall::expr(expr.id), + *expr, + &**base_expr, + adjusted_base_ty, + base_adjustment, + PreferMutLvalue); } ast::ExprUnary(ast::UnDeref, ref base_expr) => { check::try_overloaded_deref( - self.fcx, - expr.span, - Some(MethodCall::expr(expr.id)), - Some(&**base_expr), - self.fcx.expr_ty(&**base_expr), - PreferMutLvalue); + self.fcx, + expr.span, + Some(MethodCall::expr(expr.id)), + Some(&**base_expr), + self.fcx.expr_ty(&**base_expr), + PreferMutLvalue); } _ => {} } @@ -1623,15 +1796,25 @@ fn xform_self_ty(&self, method: &Rc, substs: &subst::Substs) -> ty:: fn replace_late_bound_regions_with_fresh_var(&self, binder_id: ast::NodeId, value: &T) -> T where T : TypeFoldable + Repr { - let (_, value) = replace_late_bound_regions( - self.fcx.tcx(), - binder_id, - value, - |br| self.fcx.infcx().next_region_var(infer::LateBoundRegion(self.span, br))); - value + replace_late_bound_regions_with_fresh_var(self.fcx.infcx(), self.span, binder_id, value) } } +fn replace_late_bound_regions_with_fresh_var(infcx: &infer::InferCtxt, + span: Span, + binder_id: ast::NodeId, + value: &T) + -> T + where T : TypeFoldable + Repr +{ + let (_, value) = replace_late_bound_regions( + infcx.tcx, + binder_id, + value, + |br| infcx.next_region_var(infer::LateBoundRegion(span, br))); + value +} + fn trait_method(tcx: &ty::ctxt, trait_def_id: ast::DefId, method_name: ast::Name) diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 0c7de4b3ac4..0e3a77ba963 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1630,6 +1630,10 @@ pub fn write_adjustment(&self, adj: ty::AutoAdjustment) { debug!("write_adjustment(node_id={}, adj={})", node_id, adj); + if adj.is_identity() { + return; + } + // Careful: adjustments can imply trait obligations if we are // casting from a concrete type to an object type. I think // it'd probably be nicer to move the logic that creates the @@ -1813,6 +1817,38 @@ pub fn expr_ty(&self, ex: &ast::Expr) -> ty::t { } } + pub fn expr_ty_adjusted(&self, expr: &ast::Expr) -> ty::t { + /*! + * Fetch type of `expr` after applying adjustments that + * have been recorded in the fcx. + */ + + let adjustments = self.inh.adjustments.borrow(); + let adjustment = adjustments.find(&expr.id); + self.adjust_expr_ty(expr, adjustment) + } + + pub fn adjust_expr_ty(&self, + expr: &ast::Expr, + adjustment: Option<&ty::AutoAdjustment>) + -> ty::t + { + /*! + * Apply `adjustment` to the type of `expr` + */ + + let raw_ty = self.expr_ty(expr); + let raw_ty = self.infcx().shallow_resolve(raw_ty); + ty::adjust_ty(self.tcx(), + expr.span, + expr.id, + raw_ty, + adjustment, + |method_call| self.inh.method_map.borrow() + .find(&method_call) + .map(|method| method.ty)) + } + pub fn node_ty(&self, id: ast::NodeId) -> ty::t { match self.inh.node_types.borrow().find(&id) { Some(&t) => t, @@ -2062,6 +2098,10 @@ pub fn autoderef(fcx: &FnCtxt, sp: Span, base_ty: ty::t, for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) { let resolved_t = structurally_resolved_type(fcx, sp, t); + if ty::type_is_error(resolved_t) { + return (resolved_t, autoderefs, None); + } + match should_stop(resolved_t, autoderefs) { Some(x) => return (resolved_t, autoderefs, Some(x)), None => {} @@ -2117,17 +2157,17 @@ fn try_overloaded_call<'a>(fcx: &FnCtxt, None => continue, Some(function_trait) => function_trait, }; - let method_callee = match method::lookup_in_trait( - fcx, - call_expression.span, - Some(&*callee), - method_name, - function_trait, - callee_type, - []) { - None => continue, - Some(method_callee) => method_callee, - }; + let method_callee = + match method::lookup_in_trait(fcx, + call_expression.span, + Some(&*callee), + method_name, + function_trait, + callee_type, + None) { + None => continue, + Some(method_callee) => method_callee, + }; let method_call = MethodCall::expr(call_expression.id); let output_type = check_method_argument_types(fcx, call_expression.span, @@ -2159,13 +2199,14 @@ fn try_overloaded_deref(fcx: &FnCtxt, base_expr: Option<&ast::Expr>, base_ty: ty::t, lvalue_pref: LvaluePreference) - -> Option { + -> Option +{ // Try DerefMut first, if preferred. let method = match (lvalue_pref, fcx.tcx().lang_items.deref_mut_trait()) { (PreferMutLvalue, Some(trait_did)) => { method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x), token::intern("deref_mut"), trait_did, - base_ty, []) + base_ty, None) } _ => None }; @@ -2175,25 +2216,27 @@ fn try_overloaded_deref(fcx: &FnCtxt, (None, Some(trait_did)) => { method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x), token::intern("deref"), trait_did, - base_ty, []) + base_ty, None) } (method, _) => method }; - make_return_type(fcx, method_call, method) + make_overloaded_lvalue_return_type(fcx, method_call, method) } -fn get_method_ty(method: &Option) -> ty::t { - match method { - &Some(ref method) => method.ty, - &None => ty::mk_err() - } -} +fn make_overloaded_lvalue_return_type(fcx: &FnCtxt, + method_call: Option, + method: Option) + -> Option +{ + /*! + * For the overloaded lvalue expressions (`*x`, `x[3]`), the trait + * returns a type of `&T`, but the actual type we assign to the + * *expression* is `T`. So this function just peels off the return + * type by one layer to yield `T`. It also inserts the + * `method-callee` into the method map. + */ -fn make_return_type(fcx: &FnCtxt, - method_call: Option, - method: Option) - -> Option { match method { Some(method) => { let ref_ty = ty::ty_fn_ret(method.ty); @@ -2205,26 +2248,126 @@ fn make_return_type(fcx: &FnCtxt, None => {} } match ref_ty { - ty::FnConverging(ref_ty) => - ty::deref(ref_ty, true), - ty::FnDiverging => - None + ty::FnConverging(ref_ty) => { + ty::deref(ref_ty, true) + } + ty::FnDiverging => { + fcx.tcx().sess.bug("index/deref traits do not define a `!` return") + } } } None => None, } } +fn autoderef_for_index(fcx: &FnCtxt, + base_expr: &ast::Expr, + base_ty: ty::t, + lvalue_pref: LvaluePreference, + step: |ty::t, ty::AutoDerefRef| -> Option) + -> Option +{ + let (ty, autoderefs, final_mt) = + autoderef(fcx, base_expr.span, base_ty, Some(base_expr.id), lvalue_pref, |adj_ty, idx| { + let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None }; + step(adj_ty, autoderefref) + }); + + if final_mt.is_some() { + return final_mt; + } + + // After we have fully autoderef'd, if the resulting type is [T, ..n], then + // do a final unsized coercion to yield [T]. + match ty::get(ty).sty { + ty::ty_vec(element_ty, Some(n)) => { + let adjusted_ty = ty::mk_vec(fcx.tcx(), element_ty, None); + let autoderefref = ty::AutoDerefRef { + autoderefs: autoderefs, + autoref: Some(ty::AutoUnsize(ty::UnsizeLength(n))) + }; + step(adjusted_ty, autoderefref) + } + _ => { + None + } + } +} + fn try_overloaded_slice(fcx: &FnCtxt, - method_call: Option, + method_call: MethodCall, expr: &ast::Expr, base_expr: &ast::Expr, base_ty: ty::t, start_expr: &Option>, end_expr: &Option>, - mutbl: &ast::Mutability) - -> Option { - let method = if mutbl == &ast::MutMutable { + mutbl: ast::Mutability) + -> Option // return type is result of slice +{ + /*! + * Autoderefs `base_expr`, looking for a `Slice` impl. If it + * finds one, installs the relevant method info and returns the + * result type (else None). + */ + + let lvalue_pref = match mutbl { + ast::MutMutable => PreferMutLvalue, + ast::MutImmutable => NoPreference + }; + + let opt_method_ty = + autoderef_for_index(fcx, base_expr, base_ty, lvalue_pref, |adjusted_ty, autoderefref| { + try_overloaded_slice_step(fcx, method_call, expr, base_expr, + adjusted_ty, autoderefref, mutbl, + start_expr, end_expr) + }); + + // Regardless of whether the lookup succeeds, check the method arguments + // so that we have *some* type for each argument. + let method_ty_or_err = opt_method_ty.unwrap_or(ty::mk_err()); + + let mut args = vec![]; + start_expr.as_ref().map(|x| args.push(x)); + end_expr.as_ref().map(|x| args.push(x)); + + check_method_argument_types(fcx, + expr.span, + method_ty_or_err, + expr, + args.as_slice(), + DoDerefArgs, + DontTupleArguments); + + opt_method_ty.map(|method_ty| { + let result_ty = ty::ty_fn_ret(method_ty); + match result_ty { + ty::FnConverging(result_ty) => result_ty, + ty::FnDiverging => { + fcx.tcx().sess.span_bug(expr.span, + "slice trait does not define a `!` return") + } + } + }) +} + +fn try_overloaded_slice_step(fcx: &FnCtxt, + method_call: MethodCall, + expr: &ast::Expr, + base_expr: &ast::Expr, + base_ty: ty::t, // autoderef'd type + autoderefref: ty::AutoDerefRef, + mutbl: ast::Mutability, + start_expr: &Option>, + end_expr: &Option>) + -> Option // result type is type of method being called +{ + /*! + * Checks for a `Slice` (or `SliceMut`) impl at the relevant level + * of autoderef. If it finds one, installs method info and returns + * type of method (else None). + */ + + let method = if mutbl == ast::MutMutable { // Try `SliceMut` first, if preferred. match fcx.tcx().lang_items.slice_mut_trait() { Some(trait_did) => { @@ -2235,13 +2378,14 @@ fn try_overloaded_slice(fcx: &FnCtxt, (&None, &None) => "as_mut_slice_", }; - method::lookup_in_trait(fcx, - expr.span, - Some(&*base_expr), - token::intern(method_name), - trait_did, - base_ty, - []) + method::lookup_in_trait_adjusted(fcx, + expr.span, + Some(&*base_expr), + token::intern(method_name), + trait_did, + autoderefref, + base_ty, + None) } _ => None, } @@ -2258,74 +2402,73 @@ fn try_overloaded_slice(fcx: &FnCtxt, (&None, &None) => "as_slice_", }; - method::lookup_in_trait(fcx, - expr.span, - Some(&*base_expr), - token::intern(method_name), - trait_did, - base_ty, - []) + method::lookup_in_trait_adjusted(fcx, + expr.span, + Some(&*base_expr), + token::intern(method_name), + trait_did, + autoderefref, + base_ty, + None) } _ => None, } }; + // If some lookup succeeded, install method in table + method.map(|method| { + let ty = method.ty; + fcx.inh.method_map.borrow_mut().insert(method_call, method); + ty + }) +} - // Regardless of whether the lookup succeeds, check the method arguments - // so that we have *some* type for each argument. - let method_type = get_method_ty(&method); - - let mut args = vec![]; - start_expr.as_ref().map(|x| args.push(x)); - end_expr.as_ref().map(|x| args.push(x)); +fn try_index_step(fcx: &FnCtxt, + method_call: MethodCall, + expr: &ast::Expr, + base_expr: &ast::Expr, + adjusted_ty: ty::t, + adjustment: ty::AutoDerefRef, + lvalue_pref: LvaluePreference) + -> Option<(/*index type*/ ty::t, /*element type*/ ty::t)> +{ + /*! + * To type-check `base_expr[index_expr]`, we progressively autoderef (and otherwise adjust) + * `base_expr`, looking for a type which either supports builtin indexing or overloaded + * indexing. This loop implements one step in that search; the autoderef loop is implemented + * by `autoderef_for_index`. + */ - check_method_argument_types(fcx, - expr.span, - method_type, - expr, - args.as_slice(), - DoDerefArgs, - DontTupleArguments); + debug!("try_index_step(expr={}, base_expr.id={}, adjusted_ty={}, adjustment={})", + expr.repr(fcx.tcx()), + base_expr.repr(fcx.tcx()), + adjusted_ty.repr(fcx.tcx()), + adjustment); - match method { - Some(method) => { - let result_ty = ty::ty_fn_ret(method.ty); - match method_call { - Some(method_call) => { - fcx.inh.method_map.borrow_mut().insert(method_call, - method); - } - None => {} - } - match result_ty { - ty::FnConverging(result_ty) => - Some(ty::mt { ty: result_ty, mutbl: ast::MutImmutable }), - ty::FnDiverging => - None - } + // Try built-in indexing first. + match ty::index(adjusted_ty) { + Some(ty) => { + fcx.write_adjustment(base_expr.id, base_expr.span, ty::AdjustDerefRef(adjustment)); + return Some((ty::mk_uint(), ty)); } - None => None, + + None => { } } -} -fn try_overloaded_index(fcx: &FnCtxt, - method_call: Option, - expr: &ast::Expr, - base_expr: &ast::Expr, - base_ty: ty::t, - index_expr: &P, - lvalue_pref: LvaluePreference) - -> Option { + let input_ty = fcx.infcx().next_ty_var(); + let return_ty = fcx.infcx().next_ty_var(); + // Try `IndexMut` first, if preferred. let method = match (lvalue_pref, fcx.tcx().lang_items.index_mut_trait()) { (PreferMutLvalue, Some(trait_did)) => { - method::lookup_in_trait(fcx, - expr.span, - Some(&*base_expr), - token::intern("index_mut"), - trait_did, - base_ty, - []) + method::lookup_in_trait_adjusted(fcx, + expr.span, + Some(&*base_expr), + token::intern("index_mut"), + trait_did, + adjustment.clone(), + adjusted_ty, + Some(vec![input_ty, return_ty])) } _ => None, }; @@ -2333,29 +2476,25 @@ fn try_overloaded_index(fcx: &FnCtxt, // Otherwise, fall back to `Index`. let method = match (method, fcx.tcx().lang_items.index_trait()) { (None, Some(trait_did)) => { - method::lookup_in_trait(fcx, - expr.span, - Some(&*base_expr), - token::intern("index"), - trait_did, - base_ty, - []) + method::lookup_in_trait_adjusted(fcx, + expr.span, + Some(&*base_expr), + token::intern("index"), + trait_did, + adjustment, + adjusted_ty, + Some(vec![input_ty, return_ty])) } (method, _) => method, }; - // Regardless of whether the lookup succeeds, check the method arguments - // so that we have *some* type for each argument. - let method_type = get_method_ty(&method); - check_method_argument_types(fcx, - expr.span, - method_type, - expr, - &[index_expr], - DoDerefArgs, - DontTupleArguments); - - make_return_type(fcx, method_call, method) + // If some lookup succeeds, write callee into table and extract index/element + // type from the method signature. + // If some lookup succeeded, install method in table + method.map(|method| { + make_overloaded_lvalue_return_type(fcx, Some(method_call), Some(method)); + (input_ty, return_ty) + }) } /// Given the head of a `for` expression, looks up the `next` method in the @@ -2383,7 +2522,7 @@ fn lookup_method_for_for_loop(fcx: &FnCtxt, token::intern("next"), trait_did, expr_type, - []); + None); // Regardless of whether the lookup succeeds, check the method arguments // so that we have *some* type for each argument. @@ -2427,10 +2566,15 @@ fn lookup_method_for_for_loop(fcx: &FnCtxt, if !substs.types.is_empty_in(subst::TypeSpace) => { *substs.types.get(subst::TypeSpace, 0) } + ty::ty_err => { + ty::mk_err() + } _ => { fcx.tcx().sess.span_err(iterator_expr.span, - "`next` method of the `Iterator` \ - trait has an unexpected type"); + format!("`next` method of the `Iterator` \ + trait has an unexpected type `{}`", + fcx.infcx().ty_to_string(return_type)) + .as_slice()); ty::mk_err() } } @@ -2457,7 +2601,7 @@ fn check_method_argument_types<'a>(fcx: &FnCtxt, deref_args, false, tuple_arguments); - ty::FnConverging(method_fn_ty) + ty::FnConverging(ty::mk_err()) } else { match ty::get(method_fn_ty).sty { ty::ty_bare_fn(ref fty) => { @@ -3060,8 +3204,36 @@ fn lookup_op_method<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>, unbound_method: ||) -> ty::t { let method = match trait_did { Some(trait_did) => { - method::lookup_in_trait(fcx, op_ex.span, Some(lhs), opname, - trait_did, lhs_ty, &[]) + // We do eager coercions to make using operators + // more ergonomic: + // + // - If the input is of type &'a T (resp. &'a mut T), + // then reborrow it to &'b T (resp. &'b mut T) where + // 'b <= 'a. This makes things like `x == y`, where + // `x` and `y` are both region pointers, work. We + // could also solve this with variance or different + // traits that don't force left and right to have same + // type. + let (adj_ty, adjustment) = match ty::get(lhs_ty).sty { + ty::ty_rptr(r_in, mt) => { + let r_adj = fcx.infcx().next_region_var(infer::Autoref(lhs.span)); + fcx.mk_subr(infer::Reborrow(lhs.span), r_adj, r_in); + let adjusted_ty = ty::mk_rptr(fcx.tcx(), r_adj, mt); + let autoptr = ty::AutoPtr(r_adj, mt.mutbl, None); + let adjustment = ty::AutoDerefRef { autoderefs: 1, autoref: Some(autoptr) }; + (adjusted_ty, adjustment) + } + _ => { + (lhs_ty, ty::AutoDerefRef { autoderefs: 0, autoref: None }) + } + }; + + debug!("adjusted_ty={} adjustment={}", + adj_ty.repr(fcx.tcx()), + adjustment); + + method::lookup_in_trait_adjusted(fcx, op_ex.span, Some(lhs), opname, + trait_did, adjustment, adj_ty, None) } None => None }; @@ -4338,55 +4510,47 @@ fn check_struct_fields_on_error(fcx: &FnCtxt, ast::ExprIndex(ref base, ref idx) => { check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref); check_expr(fcx, &**idx); - let raw_base_t = fcx.expr_ty(&**base); + let base_t = fcx.expr_ty(&**base); let idx_t = fcx.expr_ty(&**idx); - if ty::type_is_error(raw_base_t) { - fcx.write_ty(id, raw_base_t); + if ty::type_is_error(base_t) { + fcx.write_ty(id, base_t); } else if ty::type_is_error(idx_t) { fcx.write_ty(id, idx_t); } else { - let (_, autoderefs, field_ty) = - autoderef(fcx, expr.span, raw_base_t, Some(base.id), - lvalue_pref, |base_t, _| ty::index(base_t)); - match field_ty { - Some(ty) => { - check_expr_has_type(fcx, &**idx, ty::mk_uint()); - fcx.write_ty(id, ty); - fcx.write_autoderef_adjustment(base.id, base.span, autoderefs); + let base_t = structurally_resolved_type(fcx, expr.span, base_t); + + let result = + autoderef_for_index(fcx, &**base, base_t, lvalue_pref, |adj_ty, adj| { + try_index_step(fcx, + MethodCall::expr(expr.id), + expr, + &**base, + adj_ty, + adj, + lvalue_pref) + }); + + match result { + Some((index_ty, element_ty)) => { + check_expr_has_type(fcx, &**idx, index_ty); + fcx.write_ty(id, element_ty); } _ => { - // This is an overloaded method. - let base_t = structurally_resolved_type(fcx, - expr.span, - raw_base_t); - let method_call = MethodCall::expr(expr.id); - match try_overloaded_index(fcx, - Some(method_call), - expr, - &**base, - base_t, - idx, - lvalue_pref) { - Some(mt) => fcx.write_ty(id, mt.ty), - None => { - fcx.type_error_message(expr.span, - |actual| { - format!("cannot \ - index a \ - value of \ - type `{}`", - actual) - }, - base_t, - None); - fcx.write_ty(id, ty::mk_err()) - } - } + check_expr_has_type(fcx, &**idx, ty::mk_err()); + fcx.type_error_message( + expr.span, + |actual| { + format!("cannot index a value of type `{}`", + actual) + }, + base_t, + None); + fcx.write_ty(id, ty::mk_err()) } } } } - ast::ExprSlice(ref base, ref start, ref end, ref mutbl) => { + ast::ExprSlice(ref base, ref start, ref end, mutbl) => { check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref); let raw_base_t = fcx.expr_ty(&**base); @@ -4415,19 +4579,19 @@ fn check_struct_fields_on_error(fcx: &FnCtxt, raw_base_t); let method_call = MethodCall::expr(expr.id); match try_overloaded_slice(fcx, - Some(method_call), + method_call, expr, &**base, base_t, start, end, mutbl) { - Some(mt) => fcx.write_ty(id, mt.ty), + Some(ty) => fcx.write_ty(id, ty), None => { fcx.type_error_message(expr.span, |actual| { format!("cannot take a {}slice of a value with type `{}`", - if mutbl == &ast::MutMutable { + if mutbl == ast::MutMutable { "mutable " } else { "" diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index fb80b9f30f5..91e563c75e4 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -32,6 +32,7 @@ use syntax::codemap::{Span, Pos}; use syntax::parse::token; use syntax::print::pprust; +use syntax::ptr::P; use syntax::{ast, ast_util}; use syntax::owned_slice::OwnedSlice; @@ -561,6 +562,12 @@ fn repr(&self, tcx: &ctxt) -> String { } } +impl Repr for P { + fn repr(&self, tcx: &ctxt) -> String { + (*self).repr(tcx) + } +} + impl Repr for Result { fn repr(&self, tcx: &ctxt) -> String { match self { diff --git a/src/test/compile-fail/borrowck-overloaded-index-autoderef.rs b/src/test/compile-fail/borrowck-overloaded-index-autoderef.rs new file mode 100644 index 00000000000..2253d7512c0 --- /dev/null +++ b/src/test/compile-fail/borrowck-overloaded-index-autoderef.rs @@ -0,0 +1,91 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we still see borrowck errors of various kinds when using +// indexing and autoderef in combination. + +struct Foo { + x: int, + y: int, +} + +impl Index for Foo { + fn index<'a>(&'a self, z: &String) -> &'a int { + if z.as_slice() == "x" { + &self.x + } else { + &self.y + } + } +} + +impl IndexMut for Foo { + fn index_mut<'a>(&'a mut self, z: &String) -> &'a mut int { + if z.as_slice() == "x" { + &mut self.x + } else { + &mut self.y + } + } +} + +fn test1(mut f: Box, s: String) { + let _p = &mut f[s]; + let _q = &f[s]; //~ ERROR cannot borrow +} + +fn test2(mut f: Box, s: String) { + let _p = &mut f[s]; + let _q = &mut f[s]; //~ ERROR cannot borrow +} + +struct Bar { + foo: Foo +} + +fn test3(mut f: Box, s: String) { + let _p = &mut f.foo[s]; + let _q = &mut f.foo[s]; //~ ERROR cannot borrow +} + +fn test4(mut f: Box, s: String) { + let _p = &f.foo[s]; + let _q = &f.foo[s]; +} + +fn test5(mut f: Box, s: String) { + let _p = &f.foo[s]; + let _q = &mut f.foo[s]; //~ ERROR cannot borrow +} + +fn test6(mut f: Box, g: Foo, s: String) { + let _p = &f.foo[s]; + f.foo = g; //~ ERROR cannot assign +} + +fn test7(mut f: Box, g: Bar, s: String) { + let _p = &f.foo[s]; + *f = g; //~ ERROR cannot assign +} + +fn test8(mut f: Box, g: Foo, s: String) { + let _p = &mut f.foo[s]; + f.foo = g; //~ ERROR cannot assign +} + +fn test9(mut f: Box, g: Bar, s: String) { + let _p = &mut f.foo[s]; + *f = g; //~ ERROR cannot assign +} + +fn main() { +} + + diff --git a/src/test/compile-fail/issue-13482-2.rs b/src/test/compile-fail/issue-13482-2.rs index 6746b90e32d..4ec8c2b1b7e 100644 --- a/src/test/compile-fail/issue-13482-2.rs +++ b/src/test/compile-fail/issue-13482-2.rs @@ -14,7 +14,7 @@ fn main() { let x = [1,2]; let y = match x { [] => None, - //~^ ERROR types: expected `[_#0i, ..2]`, found `[_#7, ..0]` + //~^ ERROR types: expected `[_#0i, ..2]`, found `[_#7t, ..0]` // (expected array of 2 elements, found array of 0 elements) [a,_] => Some(a) }; diff --git a/src/test/compile-fail/issue-15207.rs b/src/test/compile-fail/issue-15207.rs index 37fcabef0ed..61877775269 100644 --- a/src/test/compile-fail/issue-15207.rs +++ b/src/test/compile-fail/issue-15207.rs @@ -11,7 +11,6 @@ fn main() { loop { break.push(1) //~ ERROR the type of this value must be known in this context - //~^ ERROR multiple applicable methods in scope ; } } diff --git a/src/test/compile-fail/issue-17033.rs b/src/test/compile-fail/issue-17033.rs index 7590546d40a..5048a9aa919 100644 --- a/src/test/compile-fail/issue-17033.rs +++ b/src/test/compile-fail/issue-17033.rs @@ -11,7 +11,7 @@ #![feature(overloaded_calls)] fn f<'r>(p: &'r mut fn(p: &mut ())) { - p(()) //~ ERROR mismatched types: expected `&mut ()`, found `()` + (*p)(()) //~ ERROR mismatched types: expected `&mut ()`, found `()` } fn main() {} diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs index e64d674b7c8..1150f40db76 100644 --- a/src/test/compile-fail/issue-2149.rs +++ b/src/test/compile-fail/issue-2149.rs @@ -18,7 +18,6 @@ fn bind(&self, f: |A| -> Vec ) { let mut r = panic!(); for elt in self.iter() { r = r + f(*elt); } //~^ ERROR the type of this value must be known - //~^^ ERROR not implemented } } fn main() { diff --git a/src/test/compile-fail/slice-mut-2.rs b/src/test/compile-fail/slice-mut-2.rs index 09019448a67..6778ed88ff7 100644 --- a/src/test/compile-fail/slice-mut-2.rs +++ b/src/test/compile-fail/slice-mut-2.rs @@ -15,5 +15,5 @@ fn main() { let x: &[int] = &[1, 2, 3, 4, 5]; // Can't mutably slice an immutable slice - let y = x[mut 2..4]; //~ ERROR cannot take a mutable slice of a value with type `&[int]` + let y = x[mut 2..4]; //~ ERROR cannot borrow } diff --git a/src/test/compile-fail/type-params-in-different-spaces-1.rs b/src/test/compile-fail/type-params-in-different-spaces-1.rs index 6e32e6e4835..c87e8541758 100644 --- a/src/test/compile-fail/type-params-in-different-spaces-1.rs +++ b/src/test/compile-fail/type-params-in-different-spaces-1.rs @@ -12,7 +12,7 @@ trait BrokenAdd: Num { fn broken_add(&self, rhs: T) -> Self { - *self + rhs //~ ERROR mismatched types + *self + rhs //~ ERROR expected `Self`, found `T` } } diff --git a/src/test/run-pass/overload-index-operator.rs b/src/test/run-pass/overloaded-index-assoc-list.rs similarity index 100% rename from src/test/run-pass/overload-index-operator.rs rename to src/test/run-pass/overloaded-index-assoc-list.rs diff --git a/src/test/run-pass/overloaded-index-autoderef.rs b/src/test/run-pass/overloaded-index-autoderef.rs new file mode 100644 index 00000000000..d51956da894 --- /dev/null +++ b/src/test/run-pass/overloaded-index-autoderef.rs @@ -0,0 +1,79 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test overloaded indexing combined with autoderef. + +struct Foo { + x: int, + y: int, +} + +impl Index for Foo { + fn index(&self, z: &int) -> &int { + if *z == 0 { + &self.x + } else { + &self.y + } + } +} + +impl IndexMut for Foo { + fn index_mut(&mut self, z: &int) -> &mut int { + if *z == 0 { + &mut self.x + } else { + &mut self.y + } + } +} + +trait Int { + fn get(self) -> int; + fn get_from_ref(&self) -> int; + fn inc(&mut self); +} + +impl Int for int { + fn get(self) -> int { self } + fn get_from_ref(&self) -> int { *self } + fn inc(&mut self) { *self += 1; } +} + +fn main() { + let mut f = box Foo { + x: 1, + y: 2, + }; + + assert_eq!(f[1], 2); + + f[0] = 3; + + assert_eq!(f[0], 3); + + // Test explicit IndexMut where `f` must be autoderef: + { + let p = &mut f[1]; + *p = 4; + } + + // Test explicit Index where `f` must be autoderef: + { + let p = &f[1]; + assert_eq!(*p, 4); + } + + // Test calling methods with `&mut self`, `self, and `&self` receivers: + f[1].inc(); + assert_eq!(f[1].get(), 5); + assert_eq!(f[1].get_from_ref(), 5); +} + diff --git a/src/test/run-pass/overloaded-index-in-field.rs b/src/test/run-pass/overloaded-index-in-field.rs new file mode 100644 index 00000000000..e8b0408ca0d --- /dev/null +++ b/src/test/run-pass/overloaded-index-in-field.rs @@ -0,0 +1,52 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test using overloaded indexing when the "map" is stored in a +// field. This caused problems at some point. + +struct Foo { + x: int, + y: int, +} + +struct Bar { + foo: Foo +} + +impl Index for Foo { + fn index(&self, z: &int) -> &int { + if *z == 0 { + &self.x + } else { + &self.y + } + } +} + +trait Int { + fn get(self) -> int; + fn get_from_ref(&self) -> int; + fn inc(&mut self); +} + +impl Int for int { + fn get(self) -> int { self } + fn get_from_ref(&self) -> int { *self } + fn inc(&mut self) { *self += 1; } +} + +fn main() { + let f = Bar { foo: Foo { + x: 1, + y: 2, + } }; + assert_eq!(f.foo[1].get(), 2); +} + diff --git a/src/test/run-pass/overloaded-index.rs b/src/test/run-pass/overloaded-index.rs index 1187e066950..23bebfa35d7 100644 --- a/src/test/run-pass/overloaded-index.rs +++ b/src/test/run-pass/overloaded-index.rs @@ -33,6 +33,18 @@ fn index_mut(&mut self, z: &int) -> &mut int { } } +trait Int { + fn get(self) -> int; + fn get_from_ref(&self) -> int; + fn inc(&mut self); +} + +impl Int for int { + fn get(self) -> int { self } + fn get_from_ref(&self) -> int { *self } + fn inc(&mut self) { *self += 1; } +} + fn main() { let mut f = Foo { x: 1, @@ -49,5 +61,10 @@ fn main() { let p = &f[1]; assert_eq!(*p, 4); } + + // Test calling methods with `&mut self`, `self, and `&self` receivers: + f[1].inc(); + assert_eq!(f[1].get(), 5); + assert_eq!(f[1].get_from_ref(), 5); } -- GitLab