]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
c34b1796 | 11 | //! Method lookup: the secret sauce of Rust. See `README.md`. |
1a4d82fc JJ |
12 | |
13 | use astconv::AstConv; | |
c34b1796 | 14 | use check::FnCtxt; |
54a0048b SL |
15 | use hir::def::Def; |
16 | use hir::def_id::DefId; | |
17 | use rustc::ty::subst; | |
18 | use rustc::traits; | |
19 | use rustc::ty::{self, TyCtxt, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable}; | |
20 | use rustc::ty::adjustment::{AdjustDerefRef, AutoDerefRef, AutoPtr}; | |
21 | use rustc::infer; | |
1a4d82fc | 22 | |
1a4d82fc JJ |
23 | use syntax::ast; |
24 | use syntax::codemap::Span; | |
25 | ||
54a0048b | 26 | use rustc::hir; |
e9174d1e | 27 | |
1a4d82fc JJ |
28 | pub use self::MethodError::*; |
29 | pub use self::CandidateSource::*; | |
30 | ||
85aaf69f SL |
31 | pub use self::suggest::{report_error, AllTraitsVec}; |
32 | ||
1a4d82fc | 33 | mod confirm; |
1a4d82fc | 34 | mod probe; |
85aaf69f | 35 | mod suggest; |
1a4d82fc | 36 | |
62682a34 SL |
37 | pub enum MethodError<'tcx> { |
38 | // Did not find an applicable method, but we did find various near-misses that may work. | |
39 | NoMatch(NoMatchData<'tcx>), | |
1a4d82fc JJ |
40 | |
41 | // Multiple methods might apply. | |
42 | Ambiguity(Vec<CandidateSource>), | |
85aaf69f SL |
43 | |
44 | // Using a `Fn`/`FnMut`/etc method on a raw closure type before we have inferred its kind. | |
e9174d1e | 45 | ClosureAmbiguity(/* DefId of fn trait */ DefId), |
54a0048b SL |
46 | |
47 | // Found an applicable method, but it is not visible. | |
48 | PrivateMatch(Def), | |
1a4d82fc JJ |
49 | } |
50 | ||
62682a34 SL |
51 | // Contains a list of static methods that may apply, a list of unsatisfied trait predicates which |
52 | // could lead to matches if satisfied, and a list of not-in-scope traits which may work. | |
53 | pub struct NoMatchData<'tcx> { | |
54 | pub static_candidates: Vec<CandidateSource>, | |
55 | pub unsatisfied_predicates: Vec<TraitRef<'tcx>>, | |
e9174d1e | 56 | pub out_of_scope_traits: Vec<DefId>, |
62682a34 SL |
57 | pub mode: probe::Mode |
58 | } | |
59 | ||
60 | impl<'tcx> NoMatchData<'tcx> { | |
61 | pub fn new(static_candidates: Vec<CandidateSource>, | |
62 | unsatisfied_predicates: Vec<TraitRef<'tcx>>, | |
e9174d1e | 63 | out_of_scope_traits: Vec<DefId>, |
62682a34 SL |
64 | mode: probe::Mode) -> Self { |
65 | NoMatchData { | |
66 | static_candidates: static_candidates, | |
67 | unsatisfied_predicates: unsatisfied_predicates, | |
68 | out_of_scope_traits: out_of_scope_traits, | |
69 | mode: mode | |
70 | } | |
71 | } | |
72 | } | |
73 | ||
1a4d82fc JJ |
74 | // A pared down enum describing just the places from which a method |
75 | // candidate can arise. Used for error reporting only. | |
62682a34 | 76 | #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] |
1a4d82fc | 77 | pub enum CandidateSource { |
e9174d1e SL |
78 | ImplSource(DefId), |
79 | TraitSource(/* trait id */ DefId), | |
1a4d82fc JJ |
80 | } |
81 | ||
1a4d82fc JJ |
82 | /// Determines whether the type `self_ty` supports a method name `method_name` or not. |
83 | pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, | |
84 | span: Span, | |
85 | method_name: ast::Name, | |
d9579d0f | 86 | self_ty: ty::Ty<'tcx>, |
1a4d82fc JJ |
87 | call_expr_id: ast::NodeId) |
88 | -> bool | |
89 | { | |
c34b1796 AL |
90 | let mode = probe::Mode::MethodCall; |
91 | match probe::probe(fcx, span, mode, method_name, self_ty, call_expr_id) { | |
85aaf69f SL |
92 | Ok(..) => true, |
93 | Err(NoMatch(..)) => false, | |
94 | Err(Ambiguity(..)) => true, | |
95 | Err(ClosureAmbiguity(..)) => true, | |
54a0048b | 96 | Err(PrivateMatch(..)) => true, |
1a4d82fc JJ |
97 | } |
98 | } | |
99 | ||
100 | /// Performs method lookup. If lookup is successful, it will return the callee and store an | |
101 | /// appropriate adjustment for the self-expr. In some cases it may report an error (e.g., invoking | |
102 | /// the `drop` method). | |
103 | /// | |
104 | /// # Arguments | |
105 | /// | |
106 | /// Given a method call like `foo.bar::<T1,...Tn>(...)`: | |
107 | /// | |
108 | /// * `fcx`: the surrounding `FnCtxt` (!) | |
109 | /// * `span`: the span for the method call | |
110 | /// * `method_name`: the name of the method being called (`bar`) | |
111 | /// * `self_ty`: the (unadjusted) type of the self expression (`foo`) | |
112 | /// * `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`) | |
113 | /// * `self_expr`: the self expression (`foo`) | |
114 | pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, | |
115 | span: Span, | |
116 | method_name: ast::Name, | |
d9579d0f AL |
117 | self_ty: ty::Ty<'tcx>, |
118 | supplied_method_types: Vec<ty::Ty<'tcx>>, | |
e9174d1e SL |
119 | call_expr: &'tcx hir::Expr, |
120 | self_expr: &'tcx hir::Expr) | |
62682a34 | 121 | -> Result<ty::MethodCallee<'tcx>, MethodError<'tcx>> |
1a4d82fc | 122 | { |
62682a34 SL |
123 | debug!("lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", |
124 | method_name, | |
125 | self_ty, | |
126 | call_expr, | |
127 | self_expr); | |
1a4d82fc | 128 | |
c34b1796 | 129 | let mode = probe::Mode::MethodCall; |
1a4d82fc | 130 | let self_ty = fcx.infcx().resolve_type_vars_if_possible(&self_ty); |
54a0048b | 131 | let pick = probe::probe(fcx, span, mode, method_name, self_ty, call_expr.id)?; |
1a4d82fc JJ |
132 | Ok(confirm::confirm(fcx, span, self_expr, call_expr, self_ty, pick, supplied_method_types)) |
133 | } | |
134 | ||
135 | pub fn lookup_in_trait<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, | |
136 | span: Span, | |
e9174d1e | 137 | self_expr: Option<&hir::Expr>, |
1a4d82fc JJ |
138 | m_name: ast::Name, |
139 | trait_def_id: DefId, | |
d9579d0f AL |
140 | self_ty: ty::Ty<'tcx>, |
141 | opt_input_types: Option<Vec<ty::Ty<'tcx>>>) | |
142 | -> Option<ty::MethodCallee<'tcx>> | |
1a4d82fc JJ |
143 | { |
144 | lookup_in_trait_adjusted(fcx, span, self_expr, m_name, trait_def_id, | |
9346a6ac | 145 | 0, false, self_ty, opt_input_types) |
1a4d82fc JJ |
146 | } |
147 | ||
148 | /// `lookup_in_trait_adjusted` is used for overloaded operators. It does a very narrow slice of | |
149 | /// what the normal probe/confirm path does. In particular, it doesn't really do any probing: it | |
150 | /// simply constructs an obligation for a particular trait with the given self-type and checks | |
151 | /// whether that trait is implemented. | |
152 | /// | |
153 | /// FIXME(#18741) -- It seems likely that we can consolidate some of this code with the other | |
154 | /// method-lookup code. In particular, autoderef on index is basically identical to autoderef with | |
155 | /// normal probes, except that the test also looks for built-in indexing. Also, the second half of | |
156 | /// this method is basically the same as confirmation. | |
157 | pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, | |
158 | span: Span, | |
e9174d1e | 159 | self_expr: Option<&hir::Expr>, |
1a4d82fc JJ |
160 | m_name: ast::Name, |
161 | trait_def_id: DefId, | |
9346a6ac AL |
162 | autoderefs: usize, |
163 | unsize: bool, | |
d9579d0f AL |
164 | self_ty: ty::Ty<'tcx>, |
165 | opt_input_types: Option<Vec<ty::Ty<'tcx>>>) | |
166 | -> Option<ty::MethodCallee<'tcx>> | |
1a4d82fc | 167 | { |
62682a34 SL |
168 | debug!("lookup_in_trait_adjusted(self_ty={:?}, self_expr={:?}, m_name={}, trait_def_id={:?})", |
169 | self_ty, | |
170 | self_expr, | |
171 | m_name, | |
172 | trait_def_id); | |
1a4d82fc | 173 | |
c1a9b12d SL |
174 | let trait_def = fcx.tcx().lookup_trait_def(trait_def_id); |
175 | ||
176 | let type_parameter_defs = trait_def.generics.types.get_slice(subst::TypeSpace); | |
177 | let expected_number_of_input_types = type_parameter_defs.len(); | |
178 | ||
179 | assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0); | |
180 | assert!(trait_def.generics.regions.is_empty()); | |
1a4d82fc | 181 | |
c1a9b12d SL |
182 | // Construct a trait-reference `self_ty : Trait<input_tys>` |
183 | let mut substs = subst::Substs::new_trait(Vec::new(), Vec::new(), self_ty); | |
184 | ||
185 | match opt_input_types { | |
1a4d82fc JJ |
186 | Some(input_types) => { |
187 | assert_eq!(expected_number_of_input_types, input_types.len()); | |
c1a9b12d | 188 | substs.types.replace(subst::ParamSpace::TypeSpace, input_types); |
1a4d82fc JJ |
189 | } |
190 | ||
191 | None => { | |
c1a9b12d SL |
192 | fcx.inh.infcx.type_vars_for_defs( |
193 | span, | |
194 | subst::ParamSpace::TypeSpace, | |
195 | &mut substs, | |
196 | type_parameter_defs); | |
1a4d82fc | 197 | } |
c1a9b12d | 198 | } |
1a4d82fc | 199 | |
d9579d0f | 200 | let trait_ref = ty::TraitRef::new(trait_def_id, fcx.tcx().mk_substs(substs)); |
1a4d82fc JJ |
201 | |
202 | // Construct an obligation | |
203 | let poly_trait_ref = trait_ref.to_poly_trait_ref(); | |
204 | let obligation = traits::Obligation::misc(span, | |
205 | fcx.body_id, | |
c1a9b12d | 206 | poly_trait_ref.to_predicate()); |
1a4d82fc JJ |
207 | |
208 | // Now we want to know if this can be matched | |
c1a9b12d | 209 | let mut selcx = traits::SelectionContext::new(fcx.infcx()); |
1a4d82fc JJ |
210 | if !selcx.evaluate_obligation(&obligation) { |
211 | debug!("--> Cannot match obligation"); | |
212 | return None; // Cannot be matched, no such method resolution is possible. | |
213 | } | |
214 | ||
215 | // Trait must have a method named `m_name` and it should not have | |
216 | // type parameters or early-bound regions. | |
217 | let tcx = fcx.tcx(); | |
c1a9b12d SL |
218 | let method_item = trait_item(tcx, trait_def_id, m_name).unwrap(); |
219 | let method_ty = method_item.as_opt_method().unwrap(); | |
1a4d82fc JJ |
220 | assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0); |
221 | assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0); | |
222 | ||
c1a9b12d SL |
223 | debug!("lookup_in_trait_adjusted: method_item={:?} method_ty={:?}", |
224 | method_item, method_ty); | |
1a4d82fc JJ |
225 | |
226 | // Instantiate late-bound regions and substitute the trait | |
227 | // parameters into the method type to get the actual method type. | |
228 | // | |
229 | // NB: Instantiate late-bound regions first so that | |
230 | // `instantiate_type_scheme` can normalize associated types that | |
231 | // may reference those regions. | |
232 | let fn_sig = fcx.infcx().replace_late_bound_regions_with_fresh_var(span, | |
233 | infer::FnCall, | |
234 | &method_ty.fty.sig).0; | |
235 | let fn_sig = fcx.instantiate_type_scheme(span, trait_ref.substs, &fn_sig); | |
236 | let transformed_self_ty = fn_sig.inputs[0]; | |
54a0048b SL |
237 | let def_id = method_item.def_id(); |
238 | let fty = tcx.mk_fn_def(def_id, trait_ref.substs, ty::BareFnTy { | |
1a4d82fc JJ |
239 | sig: ty::Binder(fn_sig), |
240 | unsafety: method_ty.fty.unsafety, | |
241 | abi: method_ty.fty.abi.clone(), | |
54a0048b | 242 | }); |
1a4d82fc | 243 | |
62682a34 SL |
244 | debug!("lookup_in_trait_adjusted: matched method fty={:?} obligation={:?}", |
245 | fty, | |
246 | obligation); | |
1a4d82fc JJ |
247 | |
248 | // Register obligations for the parameters. This will include the | |
249 | // `Self` parameter, which in turn has a bound of the main trait, | |
250 | // so this also effectively registers `obligation` as well. (We | |
251 | // used to register `obligation` explicitly, but that resulted in | |
252 | // double error messages being reported.) | |
253 | // | |
254 | // Note that as the method comes from a trait, it should not have | |
255 | // any late-bound regions appearing in its bounds. | |
85aaf69f | 256 | let method_bounds = fcx.instantiate_bounds(span, trait_ref.substs, &method_ty.predicates); |
1a4d82fc JJ |
257 | assert!(!method_bounds.has_escaping_regions()); |
258 | fcx.add_obligations_for_parameters( | |
259 | traits::ObligationCause::misc(span, fcx.body_id), | |
260 | &method_bounds); | |
261 | ||
b039eaaf SL |
262 | // Also register an obligation for the method type being well-formed. |
263 | fcx.register_wf_obligation(fty, span, traits::MiscObligation); | |
264 | ||
1a4d82fc JJ |
265 | // FIXME(#18653) -- Try to resolve obligations, giving us more |
266 | // typing information, which can sometimes be needed to avoid | |
267 | // pathological region inference failures. | |
7453a54e | 268 | fcx.select_obligations_where_possible(); |
1a4d82fc JJ |
269 | |
270 | // Insert any adjustments needed (always an autoref of some mutability). | |
271 | match self_expr { | |
272 | None => { } | |
273 | ||
274 | Some(self_expr) => { | |
275 | debug!("lookup_in_trait_adjusted: inserting adjustment if needed \ | |
9346a6ac AL |
276 | (self-id={}, autoderefs={}, unsize={}, explicit_self={:?})", |
277 | self_expr.id, autoderefs, unsize, | |
278 | method_ty.explicit_self); | |
1a4d82fc JJ |
279 | |
280 | match method_ty.explicit_self { | |
9cc50fc6 | 281 | ty::ExplicitSelfCategory::ByValue => { |
1a4d82fc | 282 | // Trait method is fn(self), no transformation needed. |
9346a6ac AL |
283 | assert!(!unsize); |
284 | fcx.write_autoderef_adjustment(self_expr.id, autoderefs); | |
1a4d82fc JJ |
285 | } |
286 | ||
9cc50fc6 | 287 | ty::ExplicitSelfCategory::ByReference(..) => { |
1a4d82fc JJ |
288 | // Trait method is fn(&self) or fn(&mut self), need an |
289 | // autoref. Pull the region etc out of the type of first argument. | |
290 | match transformed_self_ty.sty { | |
c1a9b12d | 291 | ty::TyRef(region, ty::TypeAndMut { mutbl, ty: _ }) => { |
9346a6ac | 292 | fcx.write_adjustment(self_expr.id, |
e9174d1e | 293 | AdjustDerefRef(AutoDerefRef { |
1a4d82fc | 294 | autoderefs: autoderefs, |
e9174d1e | 295 | autoref: Some(AutoPtr(region, mutbl)), |
9346a6ac AL |
296 | unsize: if unsize { |
297 | Some(transformed_self_ty) | |
298 | } else { | |
299 | None | |
300 | } | |
1a4d82fc JJ |
301 | })); |
302 | } | |
303 | ||
304 | _ => { | |
54a0048b | 305 | span_bug!( |
1a4d82fc | 306 | span, |
54a0048b SL |
307 | "trait method is &self but first arg is: {}", |
308 | transformed_self_ty); | |
1a4d82fc JJ |
309 | } |
310 | } | |
311 | } | |
312 | ||
313 | _ => { | |
54a0048b | 314 | span_bug!( |
1a4d82fc | 315 | span, |
54a0048b SL |
316 | "unexpected explicit self type in operator method: {:?}", |
317 | method_ty.explicit_self); | |
1a4d82fc JJ |
318 | } |
319 | } | |
320 | } | |
321 | } | |
322 | ||
d9579d0f | 323 | let callee = ty::MethodCallee { |
54a0048b | 324 | def_id: def_id, |
1a4d82fc | 325 | ty: fty, |
c1a9b12d | 326 | substs: trait_ref.substs |
1a4d82fc JJ |
327 | }; |
328 | ||
62682a34 | 329 | debug!("callee = {:?}", callee); |
1a4d82fc JJ |
330 | |
331 | Some(callee) | |
332 | } | |
333 | ||
c34b1796 AL |
334 | pub fn resolve_ufcs<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, |
335 | span: Span, | |
336 | method_name: ast::Name, | |
d9579d0f | 337 | self_ty: ty::Ty<'tcx>, |
c34b1796 | 338 | expr_id: ast::NodeId) |
54a0048b | 339 | -> Result<Def, MethodError<'tcx>> |
c34b1796 AL |
340 | { |
341 | let mode = probe::Mode::Path; | |
54a0048b SL |
342 | let pick = probe::probe(fcx, span, mode, method_name, self_ty, expr_id)?; |
343 | let def = pick.item.def(); | |
344 | ||
c1a9b12d | 345 | if let probe::InherentImplPick = pick.kind { |
54a0048b SL |
346 | if !pick.item.vis().is_accessible_from(fcx.body_id, &fcx.tcx().map) { |
347 | let msg = format!("{} `{}` is private", def.kind_name(), &method_name.as_str()); | |
348 | fcx.tcx().sess.span_err(span, &msg); | |
c34b1796 | 349 | } |
c1a9b12d | 350 | } |
54a0048b | 351 | Ok(def) |
c34b1796 AL |
352 | } |
353 | ||
c1a9b12d SL |
354 | /// Find item with name `item_name` defined in `trait_def_id` |
355 | /// and return it, or `None`, if no such item. | |
54a0048b | 356 | fn trait_item<'tcx>(tcx: &TyCtxt<'tcx>, |
e9174d1e | 357 | trait_def_id: DefId, |
d9579d0f | 358 | item_name: ast::Name) |
c1a9b12d | 359 | -> Option<ty::ImplOrTraitItem<'tcx>> |
1a4d82fc | 360 | { |
c1a9b12d SL |
361 | let trait_items = tcx.trait_items(trait_def_id); |
362 | trait_items.iter() | |
363 | .find(|item| item.name() == item_name) | |
364 | .cloned() | |
1a4d82fc JJ |
365 | } |
366 | ||
54a0048b | 367 | fn impl_item<'tcx>(tcx: &TyCtxt<'tcx>, |
e9174d1e | 368 | impl_def_id: DefId, |
d9579d0f AL |
369 | item_name: ast::Name) |
370 | -> Option<ty::ImplOrTraitItem<'tcx>> | |
1a4d82fc JJ |
371 | { |
372 | let impl_items = tcx.impl_items.borrow(); | |
373 | let impl_items = impl_items.get(&impl_def_id).unwrap(); | |
374 | impl_items | |
375 | .iter() | |
c1a9b12d | 376 | .map(|&did| tcx.impl_or_trait_item(did.def_id())) |
d9579d0f | 377 | .find(|m| m.name() == item_name) |
1a4d82fc | 378 | } |