]>
Commit | Line | Data |
---|---|---|
0731742a | 1 | //! Method lookup: the secret sauce of Rust. See the [rustc guide] for more information. |
0531ce1d | 2 | //! |
a1dfa0c6 | 3 | //! [rustc guide]: https://rust-lang.github.io/rustc-guide/method-lookup.html |
1a4d82fc | 4 | |
0731742a XL |
5 | mod confirm; |
6 | pub mod probe; | |
7 | mod suggest; | |
8 | ||
9 | pub use self::MethodError::*; | |
10 | pub use self::CandidateSource::*; | |
11 | pub use self::suggest::{SelfSource, TraitInfo}; | |
12 | ||
9fa01778 XL |
13 | use crate::check::FnCtxt; |
14 | use crate::namespace::Namespace; | |
0731742a | 15 | use errors::{Applicability, DiagnosticBuilder}; |
0731742a XL |
16 | use rustc_data_structures::sync::Lrc; |
17 | use rustc::hir; | |
532ac7d7 | 18 | use rustc::hir::def::{CtorOf, Def}; |
0731742a | 19 | use rustc::hir::def_id::DefId; |
54a0048b | 20 | use rustc::traits; |
532ac7d7 | 21 | use rustc::ty::subst::{InternalSubsts, SubstsRef}; |
ea8adc8c | 22 | use rustc::ty::{self, Ty, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable}; |
94b46f34 | 23 | use rustc::ty::GenericParamDefKind; |
cc61c64b XL |
24 | use rustc::ty::subst::Subst; |
25 | use rustc::infer::{self, InferOk}; | |
1a4d82fc | 26 | use syntax::ast; |
3157f602 | 27 | use syntax_pos::Span; |
1a4d82fc | 28 | |
0731742a | 29 | use crate::{check_type_alias_enum_variants_enabled}; |
3b2f2976 | 30 | use self::probe::{IsSuggestion, ProbeScope}; |
32a655c1 | 31 | |
9fa01778 | 32 | pub fn provide(providers: &mut ty::query::Providers<'_>) { |
83c7162d | 33 | suggest::provide(providers); |
0731742a | 34 | probe::provide(providers); |
83c7162d XL |
35 | } |
36 | ||
7cac9316 XL |
37 | #[derive(Clone, Copy, Debug)] |
38 | pub struct MethodCallee<'tcx> { | |
39 | /// Impl method ID, for inherent methods, or trait method ID, otherwise. | |
40 | pub def_id: DefId, | |
532ac7d7 | 41 | pub substs: SubstsRef<'tcx>, |
7cac9316 | 42 | |
0731742a | 43 | /// Instantiated method signature, i.e., it has been |
7cac9316 XL |
44 | /// substituted, normalized, and has had late-bound |
45 | /// lifetimes replaced with inference variables. | |
46 | pub sig: ty::FnSig<'tcx>, | |
47 | } | |
48 | ||
62682a34 SL |
49 | pub enum MethodError<'tcx> { |
50 | // Did not find an applicable method, but we did find various near-misses that may work. | |
51 | NoMatch(NoMatchData<'tcx>), | |
1a4d82fc JJ |
52 | |
53 | // Multiple methods might apply. | |
54 | Ambiguity(Vec<CandidateSource>), | |
85aaf69f | 55 | |
3b2f2976 XL |
56 | // Found an applicable method, but it is not visible. The second argument contains a list of |
57 | // not-in-scope traits which may work. | |
58 | PrivateMatch(Def, Vec<DefId>), | |
59 | ||
60 | // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have | |
61 | // forgotten to import a trait. | |
62 | IllegalSizedBound(Vec<DefId>), | |
ea8adc8c XL |
63 | |
64 | // Found a match, but the return type is wrong | |
65 | BadReturnType, | |
1a4d82fc JJ |
66 | } |
67 | ||
62682a34 SL |
68 | // Contains a list of static methods that may apply, a list of unsatisfied trait predicates which |
69 | // could lead to matches if satisfied, and a list of not-in-scope traits which may work. | |
70 | pub struct NoMatchData<'tcx> { | |
71 | pub static_candidates: Vec<CandidateSource>, | |
72 | pub unsatisfied_predicates: Vec<TraitRef<'tcx>>, | |
e9174d1e | 73 | pub out_of_scope_traits: Vec<DefId>, |
ea8adc8c | 74 | pub lev_candidate: Option<ty::AssociatedItem>, |
c30ab7b3 | 75 | pub mode: probe::Mode, |
62682a34 SL |
76 | } |
77 | ||
78 | impl<'tcx> NoMatchData<'tcx> { | |
79 | pub fn new(static_candidates: Vec<CandidateSource>, | |
80 | unsatisfied_predicates: Vec<TraitRef<'tcx>>, | |
e9174d1e | 81 | out_of_scope_traits: Vec<DefId>, |
ea8adc8c | 82 | lev_candidate: Option<ty::AssociatedItem>, |
c30ab7b3 SL |
83 | mode: probe::Mode) |
84 | -> Self { | |
62682a34 | 85 | NoMatchData { |
3b2f2976 XL |
86 | static_candidates, |
87 | unsatisfied_predicates, | |
88 | out_of_scope_traits, | |
ea8adc8c | 89 | lev_candidate, |
3b2f2976 | 90 | mode, |
62682a34 SL |
91 | } |
92 | } | |
93 | } | |
94 | ||
1a4d82fc JJ |
95 | // A pared down enum describing just the places from which a method |
96 | // candidate can arise. Used for error reporting only. | |
62682a34 | 97 | #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] |
1a4d82fc | 98 | pub enum CandidateSource { |
e9174d1e | 99 | ImplSource(DefId), |
0731742a | 100 | TraitSource(DefId /* trait id */), |
1a4d82fc JJ |
101 | } |
102 | ||
a7813a04 XL |
103 | impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { |
104 | /// Determines whether the type `self_ty` supports a method name `method_name` or not. | |
105 | pub fn method_exists(&self, | |
94b46f34 | 106 | method_name: ast::Ident, |
ea8adc8c | 107 | self_ty: Ty<'tcx>, |
532ac7d7 | 108 | call_expr_id: hir::HirId, |
a7813a04 | 109 | allow_private: bool) |
c30ab7b3 | 110 | -> bool { |
a7813a04 | 111 | let mode = probe::Mode::MethodCall; |
8faf50e0 | 112 | match self.probe_for_name(method_name.span, mode, method_name, |
94b46f34 XL |
113 | IsSuggestion(false), self_ty, call_expr_id, |
114 | ProbeScope::TraitsInScope) { | |
a7813a04 XL |
115 | Ok(..) => true, |
116 | Err(NoMatch(..)) => false, | |
117 | Err(Ambiguity(..)) => true, | |
a7813a04 | 118 | Err(PrivateMatch(..)) => allow_private, |
3b2f2976 | 119 | Err(IllegalSizedBound(..)) => true, |
ea8adc8c XL |
120 | Err(BadReturnType) => { |
121 | bug!("no return type expectations but got BadReturnType") | |
122 | } | |
123 | ||
a7813a04 | 124 | } |
1a4d82fc | 125 | } |
1a4d82fc | 126 | |
9fa01778 | 127 | /// Adds a suggestion to call the given method to the provided diagnostic. |
0731742a XL |
128 | crate fn suggest_method_call( |
129 | &self, | |
130 | err: &mut DiagnosticBuilder<'a>, | |
131 | msg: &str, | |
132 | method_name: ast::Ident, | |
133 | self_ty: Ty<'tcx>, | |
532ac7d7 | 134 | call_expr_id: hir::HirId, |
0731742a XL |
135 | ) { |
136 | let has_params = self | |
137 | .probe_for_name( | |
138 | method_name.span, | |
139 | probe::Mode::MethodCall, | |
140 | method_name, | |
141 | IsSuggestion(false), | |
142 | self_ty, | |
143 | call_expr_id, | |
144 | ProbeScope::TraitsInScope, | |
145 | ) | |
146 | .and_then(|pick| { | |
147 | let sig = self.tcx.fn_sig(pick.item.def_id); | |
148 | Ok(sig.inputs().skip_binder().len() > 1) | |
149 | }); | |
150 | ||
151 | let (suggestion, applicability) = if has_params.unwrap_or_default() { | |
152 | ( | |
153 | format!("{}(...)", method_name), | |
154 | Applicability::HasPlaceholders, | |
155 | ) | |
156 | } else { | |
157 | (format!("{}()", method_name), Applicability::MaybeIncorrect) | |
158 | }; | |
159 | ||
9fa01778 | 160 | err.span_suggestion(method_name.span, msg, suggestion, applicability); |
0731742a XL |
161 | } |
162 | ||
a7813a04 XL |
163 | /// Performs method lookup. If lookup is successful, it will return the callee |
164 | /// and store an appropriate adjustment for the self-expr. In some cases it may | |
165 | /// report an error (e.g., invoking the `drop` method). | |
166 | /// | |
167 | /// # Arguments | |
168 | /// | |
169 | /// Given a method call like `foo.bar::<T1,...Tn>(...)`: | |
170 | /// | |
171 | /// * `fcx`: the surrounding `FnCtxt` (!) | |
172 | /// * `span`: the span for the method call | |
173 | /// * `method_name`: the name of the method being called (`bar`) | |
174 | /// * `self_ty`: the (unadjusted) type of the self expression (`foo`) | |
175 | /// * `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`) | |
176 | /// * `self_expr`: the self expression (`foo`) | |
177 | pub fn lookup_method(&self, | |
ea8adc8c | 178 | self_ty: Ty<'tcx>, |
041b39d2 XL |
179 | segment: &hir::PathSegment, |
180 | span: Span, | |
a7813a04 XL |
181 | call_expr: &'gcx hir::Expr, |
182 | self_expr: &'gcx hir::Expr) | |
7cac9316 | 183 | -> Result<MethodCallee<'tcx>, MethodError<'tcx>> { |
a7813a04 | 184 | debug!("lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", |
8faf50e0 | 185 | segment.ident, |
a7813a04 XL |
186 | self_ty, |
187 | call_expr, | |
188 | self_expr); | |
189 | ||
3b2f2976 XL |
190 | let pick = self.lookup_probe( |
191 | span, | |
8faf50e0 | 192 | segment.ident, |
3b2f2976 XL |
193 | self_ty, |
194 | call_expr, | |
195 | ProbeScope::TraitsInScope | |
196 | )?; | |
a7813a04 XL |
197 | |
198 | if let Some(import_id) = pick.import_id { | |
532ac7d7 | 199 | let import_def_id = self.tcx.hir().local_def_id_from_hir_id(import_id); |
8bb4bdeb | 200 | debug!("used_trait_import: {:?}", import_def_id); |
0531ce1d | 201 | Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports) |
0731742a | 202 | .unwrap().insert(import_def_id); |
1a4d82fc JJ |
203 | } |
204 | ||
532ac7d7 | 205 | self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span); |
476ff2be | 206 | |
564c78a2 XL |
207 | let result = self.confirm_method( |
208 | span, | |
209 | self_expr, | |
210 | call_expr, | |
211 | self_ty, | |
212 | pick.clone(), | |
213 | segment, | |
214 | ); | |
3b2f2976 XL |
215 | |
216 | if result.illegal_sized_bound { | |
217 | // We probe again, taking all traits into account (not only those in scope). | |
218 | let candidates = | |
219 | match self.lookup_probe(span, | |
8faf50e0 | 220 | segment.ident, |
3b2f2976 XL |
221 | self_ty, |
222 | call_expr, | |
223 | ProbeScope::AllTraits) { | |
224 | ||
225 | // If we find a different result the caller probably forgot to import a trait. | |
226 | Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()], | |
227 | Err(Ambiguity(ref sources)) => { | |
228 | sources.iter() | |
229 | .filter_map(|source| { | |
230 | match *source { | |
231 | // Note: this cannot come from an inherent impl, | |
232 | // because the first probing succeeded. | |
233 | ImplSource(def) => self.tcx.trait_id_of_impl(def), | |
234 | TraitSource(_) => None, | |
235 | } | |
236 | }) | |
237 | .collect() | |
238 | } | |
239 | _ => Vec::new(), | |
240 | }; | |
241 | ||
242 | return Err(IllegalSizedBound(candidates)); | |
243 | } | |
244 | ||
245 | Ok(result.callee) | |
246 | } | |
247 | ||
248 | fn lookup_probe(&self, | |
249 | span: Span, | |
8faf50e0 | 250 | method_name: ast::Ident, |
ea8adc8c | 251 | self_ty: Ty<'tcx>, |
3b2f2976 XL |
252 | call_expr: &'gcx hir::Expr, |
253 | scope: ProbeScope) | |
254 | -> probe::PickResult<'tcx> { | |
255 | let mode = probe::Mode::MethodCall; | |
256 | let self_ty = self.resolve_type_vars_if_possible(&self_ty); | |
257 | self.probe_for_name(span, mode, method_name, IsSuggestion(false), | |
532ac7d7 | 258 | self_ty, call_expr.hir_id, scope) |
c1a9b12d | 259 | } |
1a4d82fc | 260 | |
7cac9316 | 261 | /// `lookup_method_in_trait` is used for overloaded operators. |
a7813a04 XL |
262 | /// It does a very narrow slice of what the normal probe/confirm path does. |
263 | /// In particular, it doesn't really do any probing: it simply constructs | |
9fa01778 | 264 | /// an obligation for a particular trait with the given self type and checks |
a7813a04 | 265 | /// whether that trait is implemented. |
9fa01778 XL |
266 | // |
267 | // FIXME(#18741): it seems likely that we can consolidate some of this | |
268 | // code with the other method-lookup code. In particular, the second half | |
269 | // of this method is basically the same as confirmation. | |
7cac9316 XL |
270 | pub fn lookup_method_in_trait(&self, |
271 | span: Span, | |
8faf50e0 | 272 | m_name: ast::Ident, |
7cac9316 | 273 | trait_def_id: DefId, |
ea8adc8c XL |
274 | self_ty: Ty<'tcx>, |
275 | opt_input_types: Option<&[Ty<'tcx>]>) | |
7cac9316 XL |
276 | -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { |
277 | debug!("lookup_in_trait_adjusted(self_ty={:?}, \ | |
a7813a04 XL |
278 | m_name={}, trait_def_id={:?})", |
279 | self_ty, | |
a7813a04 XL |
280 | m_name, |
281 | trait_def_id); | |
282 | ||
a7813a04 | 283 | // Construct a trait-reference `self_ty : Trait<input_tys>` |
532ac7d7 | 284 | let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { |
94b46f34 | 285 | match param.kind { |
532ac7d7 XL |
286 | GenericParamDefKind::Lifetime | GenericParamDefKind::Const => {} |
287 | GenericParamDefKind::Type { .. } => { | |
94b46f34 XL |
288 | if param.index == 0 { |
289 | return self_ty.into(); | |
290 | } else if let Some(ref input_types) = opt_input_types { | |
291 | return input_types[param.index as usize - 1].into(); | |
292 | } | |
293 | } | |
a7813a04 | 294 | } |
94b46f34 | 295 | self.var_for_def(span, param) |
9e0c209e | 296 | }); |
1a4d82fc | 297 | |
9e0c209e | 298 | let trait_ref = ty::TraitRef::new(trait_def_id, substs); |
1a4d82fc | 299 | |
a7813a04 XL |
300 | // Construct an obligation |
301 | let poly_trait_ref = trait_ref.to_poly_trait_ref(); | |
c30ab7b3 | 302 | let obligation = |
7cac9316 XL |
303 | traits::Obligation::misc(span, |
304 | self.body_id, | |
305 | self.param_env, | |
306 | poly_trait_ref.to_predicate()); | |
1a4d82fc | 307 | |
a7813a04 | 308 | // Now we want to know if this can be matched |
83c7162d | 309 | if !self.predicate_may_hold(&obligation) { |
a7813a04 XL |
310 | debug!("--> Cannot match obligation"); |
311 | return None; // Cannot be matched, no such method resolution is possible. | |
312 | } | |
313 | ||
314 | // Trait must have a method named `m_name` and it should not have | |
315 | // type parameters or early-bound regions. | |
316 | let tcx = self.tcx; | |
a1dfa0c6 XL |
317 | let method_item = match self.associated_item(trait_def_id, m_name, Namespace::Value) { |
318 | Some(method_item) => method_item, | |
319 | None => { | |
320 | tcx.sess.delay_span_bug(span, | |
321 | "operator trait does not have corresponding operator method"); | |
322 | return None; | |
323 | } | |
324 | }; | |
476ff2be | 325 | let def_id = method_item.def_id; |
7cac9316 | 326 | let generics = tcx.generics_of(def_id); |
94b46f34 | 327 | assert_eq!(generics.params.len(), 0); |
a7813a04 | 328 | |
476ff2be | 329 | debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); |
cc61c64b | 330 | let mut obligations = vec![]; |
a7813a04 XL |
331 | |
332 | // Instantiate late-bound regions and substitute the trait | |
333 | // parameters into the method type to get the actual method type. | |
334 | // | |
0731742a | 335 | // N.B., instantiate late-bound regions first so that |
a7813a04 XL |
336 | // `instantiate_type_scheme` can normalize associated types that |
337 | // may reference those regions. | |
041b39d2 | 338 | let fn_sig = tcx.fn_sig(def_id); |
a1dfa0c6 XL |
339 | let fn_sig = self.replace_bound_vars_with_fresh_vars( |
340 | span, | |
341 | infer::FnCall, | |
342 | &fn_sig | |
343 | ).0; | |
cc61c64b XL |
344 | let fn_sig = fn_sig.subst(self.tcx, substs); |
345 | let fn_sig = match self.normalize_associated_types_in_as_infer_ok(span, &fn_sig) { | |
346 | InferOk { value, obligations: o } => { | |
347 | obligations.extend(o); | |
348 | value | |
349 | } | |
350 | }; | |
a7813a04 | 351 | |
0731742a | 352 | // Register obligations for the parameters. This will include the |
a7813a04 XL |
353 | // `Self` parameter, which in turn has a bound of the main trait, |
354 | // so this also effectively registers `obligation` as well. (We | |
355 | // used to register `obligation` explicitly, but that resulted in | |
356 | // double error messages being reported.) | |
357 | // | |
358 | // Note that as the method comes from a trait, it should not have | |
359 | // any late-bound regions appearing in its bounds. | |
7cac9316 | 360 | let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); |
cc61c64b XL |
361 | let bounds = match self.normalize_associated_types_in_as_infer_ok(span, &bounds) { |
362 | InferOk { value, obligations: o } => { | |
363 | obligations.extend(o); | |
364 | value | |
365 | } | |
366 | }; | |
a1dfa0c6 | 367 | assert!(!bounds.has_escaping_bound_vars()); |
a7813a04 | 368 | |
cc61c64b | 369 | let cause = traits::ObligationCause::misc(span, self.body_id); |
7cac9316 XL |
370 | obligations.extend(traits::predicates_for_generics(cause.clone(), |
371 | self.param_env, | |
372 | &bounds)); | |
a7813a04 | 373 | |
cc61c64b | 374 | // Also add an obligation for the method type being well-formed. |
83c7162d | 375 | let method_ty = tcx.mk_fn_ptr(ty::Binder::bind(fn_sig)); |
7cac9316 XL |
376 | debug!("lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}", |
377 | method_ty, | |
378 | obligation); | |
379 | obligations.push(traits::Obligation::new(cause, | |
380 | self.param_env, | |
381 | ty::Predicate::WellFormed(method_ty))); | |
1a4d82fc | 382 | |
7cac9316 | 383 | let callee = MethodCallee { |
3b2f2976 | 384 | def_id, |
c30ab7b3 | 385 | substs: trait_ref.substs, |
7cac9316 | 386 | sig: fn_sig, |
a7813a04 | 387 | }; |
1a4d82fc | 388 | |
a7813a04 | 389 | debug!("callee = {:?}", callee); |
1a4d82fc | 390 | |
cc61c64b XL |
391 | Some(InferOk { |
392 | obligations, | |
393 | value: callee | |
394 | }) | |
a7813a04 | 395 | } |
1a4d82fc | 396 | |
0731742a XL |
397 | pub fn resolve_ufcs( |
398 | &self, | |
399 | span: Span, | |
400 | method_name: ast::Ident, | |
401 | self_ty: Ty<'tcx>, | |
532ac7d7 | 402 | expr_id: hir::HirId |
0731742a XL |
403 | ) -> Result<Def, MethodError<'tcx>> { |
404 | debug!( | |
405 | "resolve_ufcs: method_name={:?} self_ty={:?} expr_id={:?}", | |
406 | method_name, self_ty, expr_id, | |
407 | ); | |
408 | ||
409 | let tcx = self.tcx; | |
410 | ||
9fa01778 XL |
411 | // Check if we have an enum variant. |
412 | if let ty::Adt(adt_def, _) = self_ty.sty { | |
413 | if adt_def.is_enum() { | |
414 | let variant_def = adt_def.variants.iter().find(|vd| { | |
415 | tcx.hygienic_eq(method_name, vd.ident, adt_def.did) | |
416 | }); | |
417 | if let Some(variant_def) = variant_def { | |
418 | check_type_alias_enum_variants_enabled(tcx, span); | |
419 | ||
532ac7d7 XL |
420 | // Braced variants generate unusable names in value namespace (reserved for |
421 | // possible future use), so variants resolved as associated items may refer to | |
422 | // them as well. It's ok to use the variant's id as a ctor id since an | |
423 | // error will be reported on any use of such resolution anyway. | |
424 | let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id); | |
425 | let def = Def::Ctor(ctor_def_id, CtorOf::Variant, variant_def.ctor_kind); | |
9fa01778 XL |
426 | tcx.check_stability(def.def_id(), Some(expr_id), span); |
427 | return Ok(def); | |
0731742a | 428 | } |
0731742a | 429 | } |
9fa01778 | 430 | } |
476ff2be | 431 | |
9fa01778 XL |
432 | let pick = self.probe_for_name(span, probe::Mode::Path, method_name, IsSuggestion(false), |
433 | self_ty, expr_id, ProbeScope::TraitsInScope)?; | |
434 | debug!("resolve_ufcs: pick={:?}", pick); | |
435 | if let Some(import_id) = pick.import_id { | |
532ac7d7 | 436 | let import_def_id = tcx.hir().local_def_id_from_hir_id(import_id); |
9fa01778 XL |
437 | debug!("resolve_ufcs: used_trait_import: {:?}", import_def_id); |
438 | Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports) | |
439 | .unwrap().insert(import_def_id); | |
0731742a | 440 | } |
9fa01778 XL |
441 | |
442 | let def = pick.item.def(); | |
443 | debug!("resolve_ufcs: def={:?}", def); | |
444 | tcx.check_stability(def.def_id(), Some(expr_id), span); | |
445 | Ok(def) | |
c1a9b12d | 446 | } |
c34b1796 | 447 | |
9fa01778 | 448 | /// Finds item with name `item_name` defined in impl/trait `def_id` |
9e0c209e | 449 | /// and return it, or `None`, if no such item was defined there. |
8faf50e0 | 450 | pub fn associated_item(&self, def_id: DefId, item_name: ast::Ident, ns: Namespace) |
476ff2be | 451 | -> Option<ty::AssociatedItem> { |
8faf50e0 XL |
452 | self.tcx.associated_items(def_id).find(|item| { |
453 | Namespace::from(item.kind) == ns && | |
454 | self.tcx.hygienic_eq(item_name, item.ident, def_id) | |
455 | }) | |
a7813a04 | 456 | } |
1a4d82fc | 457 | } |