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