]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/method/mod.rs
Point at `impl` blocks when they introduce unmet obligations
[rust.git] / compiler / rustc_typeck / src / check / method / mod.rs
1 //! Method lookup: the secret sauce of Rust. See the [rustc dev guide] for more information.
2 //!
3 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html
4
5 mod confirm;
6 mod prelude2021;
7 pub mod probe;
8 mod suggest;
9
10 pub use self::suggest::{SelfSource, TraitInfo};
11 pub use self::CandidateSource::*;
12 pub use self::MethodError::*;
13
14 use crate::check::FnCtxt;
15 use crate::ObligationCause;
16 use rustc_data_structures::sync::Lrc;
17 use rustc_errors::{Applicability, DiagnosticBuilder};
18 use rustc_hir as hir;
19 use rustc_hir::def::{CtorOf, DefKind, Namespace};
20 use rustc_hir::def_id::DefId;
21 use rustc_infer::infer::{self, InferOk};
22 use rustc_middle::ty::subst::Subst;
23 use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
24 use rustc_middle::ty::GenericParamDefKind;
25 use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable, WithConstness};
26 use rustc_span::symbol::Ident;
27 use rustc_span::Span;
28 use rustc_trait_selection::traits;
29 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
30
31 use self::probe::{IsSuggestion, ProbeScope};
32
33 pub fn provide(providers: &mut ty::query::Providers) {
34     suggest::provide(providers);
35     probe::provide(providers);
36 }
37
38 #[derive(Clone, Copy, Debug)]
39 pub struct MethodCallee<'tcx> {
40     /// Impl method ID, for inherent methods, or trait method ID, otherwise.
41     pub def_id: DefId,
42     pub substs: SubstsRef<'tcx>,
43
44     /// Instantiated method signature, i.e., it has been
45     /// substituted, normalized, and has had late-bound
46     /// lifetimes replaced with inference variables.
47     pub sig: ty::FnSig<'tcx>,
48 }
49
50 #[derive(Debug)]
51 pub enum MethodError<'tcx> {
52     // Did not find an applicable method, but we did find various near-misses that may work.
53     NoMatch(NoMatchData<'tcx>),
54
55     // Multiple methods might apply.
56     Ambiguity(Vec<CandidateSource>),
57
58     // Found an applicable method, but it is not visible. The third argument contains a list of
59     // not-in-scope traits which may work.
60     PrivateMatch(DefKind, DefId, Vec<DefId>),
61
62     // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
63     // forgotten to import a trait.
64     IllegalSizedBound(Vec<DefId>, bool, Span),
65
66     // Found a match, but the return type is wrong
67     BadReturnType,
68 }
69
70 // Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
71 // could lead to matches if satisfied, and a list of not-in-scope traits which may work.
72 #[derive(Debug)]
73 pub struct NoMatchData<'tcx> {
74     pub static_candidates: Vec<CandidateSource>,
75     pub unsatisfied_predicates:
76         Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
77     pub out_of_scope_traits: Vec<DefId>,
78     pub lev_candidate: Option<ty::AssocItem>,
79     pub mode: probe::Mode,
80 }
81
82 impl<'tcx> NoMatchData<'tcx> {
83     pub fn new(
84         static_candidates: Vec<CandidateSource>,
85         unsatisfied_predicates: Vec<(
86             ty::Predicate<'tcx>,
87             Option<ty::Predicate<'tcx>>,
88             Option<ObligationCause<'tcx>>,
89         )>,
90         out_of_scope_traits: Vec<DefId>,
91         lev_candidate: Option<ty::AssocItem>,
92         mode: probe::Mode,
93     ) -> Self {
94         NoMatchData {
95             static_candidates,
96             unsatisfied_predicates,
97             out_of_scope_traits,
98             lev_candidate,
99             mode,
100         }
101     }
102 }
103
104 // A pared down enum describing just the places from which a method
105 // candidate can arise. Used for error reporting only.
106 #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
107 pub enum CandidateSource {
108     ImplSource(DefId),
109     TraitSource(DefId /* trait id */),
110 }
111
112 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
113     /// Determines whether the type `self_ty` supports a method name `method_name` or not.
114     #[instrument(level = "debug", skip(self))]
115     pub fn method_exists(
116         &self,
117         method_name: Ident,
118         self_ty: Ty<'tcx>,
119         call_expr_id: hir::HirId,
120         allow_private: bool,
121     ) -> bool {
122         let mode = probe::Mode::MethodCall;
123         match self.probe_for_name(
124             method_name.span,
125             mode,
126             method_name,
127             IsSuggestion(false),
128             self_ty,
129             call_expr_id,
130             ProbeScope::TraitsInScope,
131         ) {
132             Ok(..) => true,
133             Err(NoMatch(..)) => false,
134             Err(Ambiguity(..)) => true,
135             Err(PrivateMatch(..)) => allow_private,
136             Err(IllegalSizedBound(..)) => true,
137             Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"),
138         }
139     }
140
141     /// Adds a suggestion to call the given method to the provided diagnostic.
142     #[instrument(level = "debug", skip(self, err, call_expr))]
143     crate fn suggest_method_call(
144         &self,
145         err: &mut DiagnosticBuilder<'a>,
146         msg: &str,
147         method_name: Ident,
148         self_ty: Ty<'tcx>,
149         call_expr: &hir::Expr<'_>,
150         span: Option<Span>,
151     ) {
152         let params = self
153             .probe_for_name(
154                 method_name.span,
155                 probe::Mode::MethodCall,
156                 method_name,
157                 IsSuggestion(false),
158                 self_ty,
159                 call_expr.hir_id,
160                 ProbeScope::TraitsInScope,
161             )
162             .map(|pick| {
163                 let sig = self.tcx.fn_sig(pick.item.def_id);
164                 sig.inputs().skip_binder().len().saturating_sub(1)
165             })
166             .unwrap_or(0);
167
168         // Account for `foo.bar<T>`;
169         let sugg_span = span.unwrap_or(call_expr.span).shrink_to_hi();
170         let (suggestion, applicability) = (
171             format!("({})", (0..params).map(|_| "_").collect::<Vec<_>>().join(", ")),
172             if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect },
173         );
174
175         err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability);
176     }
177
178     /// Performs method lookup. If lookup is successful, it will return the callee
179     /// and store an appropriate adjustment for the self-expr. In some cases it may
180     /// report an error (e.g., invoking the `drop` method).
181     ///
182     /// # Arguments
183     ///
184     /// Given a method call like `foo.bar::<T1,...Tn>(a, b + 1, ...)`:
185     ///
186     /// * `self`:                  the surrounding `FnCtxt` (!)
187     /// * `self_ty`:               the (unadjusted) type of the self expression (`foo`)
188     /// * `segment`:               the name and generic arguments of the method (`bar::<T1, ...Tn>`)
189     /// * `span`:                  the span for the method call
190     /// * `call_expr`:             the complete method call: (`foo.bar::<T1,...Tn>(...)`)
191     /// * `self_expr`:             the self expression (`foo`)
192     /// * `args`:                  the expressions of the arguments (`a, b + 1, ...`)
193     #[instrument(level = "debug", skip(self, call_expr, self_expr))]
194     pub fn lookup_method(
195         &self,
196         self_ty: Ty<'tcx>,
197         segment: &hir::PathSegment<'_>,
198         span: Span,
199         call_expr: &'tcx hir::Expr<'tcx>,
200         self_expr: &'tcx hir::Expr<'tcx>,
201         args: &'tcx [hir::Expr<'tcx>],
202     ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
203         debug!(
204             "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
205             segment.ident, self_ty, call_expr, self_expr
206         );
207
208         let pick =
209             self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
210
211         self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args);
212
213         for import_id in &pick.import_ids {
214             debug!("used_trait_import: {:?}", import_id);
215             Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports)
216                 .unwrap()
217                 .insert(*import_id);
218         }
219
220         self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None);
221
222         let result =
223             self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment);
224         debug!("result = {:?}", result);
225
226         if let Some(span) = result.illegal_sized_bound {
227             let mut needs_mut = false;
228             if let ty::Ref(region, t_type, mutability) = self_ty.kind() {
229                 let trait_type = self
230                     .tcx
231                     .mk_ref(region, ty::TypeAndMut { ty: t_type, mutbl: mutability.invert() });
232                 // We probe again to see if there might be a borrow mutability discrepancy.
233                 match self.lookup_probe(
234                     span,
235                     segment.ident,
236                     trait_type,
237                     call_expr,
238                     ProbeScope::TraitsInScope,
239                 ) {
240                     Ok(ref new_pick) if *new_pick != pick => {
241                         needs_mut = true;
242                     }
243                     _ => {}
244                 }
245             }
246
247             // We probe again, taking all traits into account (not only those in scope).
248             let candidates = match self.lookup_probe(
249                 span,
250                 segment.ident,
251                 self_ty,
252                 call_expr,
253                 ProbeScope::AllTraits,
254             ) {
255                 // If we find a different result the caller probably forgot to import a trait.
256                 Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
257                 Err(Ambiguity(ref sources)) => sources
258                     .iter()
259                     .filter_map(|source| {
260                         match *source {
261                             // Note: this cannot come from an inherent impl,
262                             // because the first probing succeeded.
263                             ImplSource(def) => self.tcx.trait_id_of_impl(def),
264                             TraitSource(_) => None,
265                         }
266                     })
267                     .collect(),
268                 _ => Vec::new(),
269             };
270
271             return Err(IllegalSizedBound(candidates, needs_mut, span));
272         }
273
274         Ok(result.callee)
275     }
276
277     #[instrument(level = "debug", skip(self, call_expr))]
278     pub fn lookup_probe(
279         &self,
280         span: Span,
281         method_name: Ident,
282         self_ty: Ty<'tcx>,
283         call_expr: &'tcx hir::Expr<'tcx>,
284         scope: ProbeScope,
285     ) -> probe::PickResult<'tcx> {
286         let mode = probe::Mode::MethodCall;
287         let self_ty = self.resolve_vars_if_possible(self_ty);
288         self.probe_for_name(
289             span,
290             mode,
291             method_name,
292             IsSuggestion(false),
293             self_ty,
294             call_expr.hir_id,
295             scope,
296         )
297     }
298
299     pub(super) fn obligation_for_method(
300         &self,
301         span: Span,
302         trait_def_id: DefId,
303         self_ty: Ty<'tcx>,
304         opt_input_types: Option<&[Ty<'tcx>]>,
305     ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
306     {
307         // Construct a trait-reference `self_ty : Trait<input_tys>`
308         let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
309             match param.kind {
310                 GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
311                 GenericParamDefKind::Type { .. } => {
312                     if param.index == 0 {
313                         return self_ty.into();
314                     } else if let Some(input_types) = opt_input_types {
315                         return input_types[param.index as usize - 1].into();
316                     }
317                 }
318             }
319             self.var_for_def(span, param)
320         });
321
322         let trait_ref = ty::TraitRef::new(trait_def_id, substs);
323
324         // Construct an obligation
325         let poly_trait_ref = ty::Binder::dummy(trait_ref);
326         (
327             traits::Obligation::misc(
328                 span,
329                 self.body_id,
330                 self.param_env,
331                 poly_trait_ref.without_const().to_predicate(self.tcx),
332             ),
333             substs,
334         )
335     }
336
337     /// `lookup_method_in_trait` is used for overloaded operators.
338     /// It does a very narrow slice of what the normal probe/confirm path does.
339     /// In particular, it doesn't really do any probing: it simply constructs
340     /// an obligation for a particular trait with the given self type and checks
341     /// whether that trait is implemented.
342     //
343     // FIXME(#18741): it seems likely that we can consolidate some of this
344     // code with the other method-lookup code. In particular, the second half
345     // of this method is basically the same as confirmation.
346     #[instrument(level = "debug", skip(self, span, opt_input_types))]
347     pub(super) fn lookup_method_in_trait(
348         &self,
349         span: Span,
350         m_name: Ident,
351         trait_def_id: DefId,
352         self_ty: Ty<'tcx>,
353         opt_input_types: Option<&[Ty<'tcx>]>,
354     ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
355         debug!(
356             "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})",
357             self_ty, m_name, trait_def_id, opt_input_types
358         );
359
360         let (obligation, substs) =
361             self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
362
363         // Now we want to know if this can be matched
364         if !self.predicate_may_hold(&obligation) {
365             debug!("--> Cannot match obligation");
366             // Cannot be matched, no such method resolution is possible.
367             return None;
368         }
369
370         // Trait must have a method named `m_name` and it should not have
371         // type parameters or early-bound regions.
372         let tcx = self.tcx;
373         let method_item = match self.associated_item(trait_def_id, m_name, Namespace::ValueNS) {
374             Some(method_item) => method_item,
375             None => {
376                 tcx.sess.delay_span_bug(
377                     span,
378                     "operator trait does not have corresponding operator method",
379                 );
380                 return None;
381             }
382         };
383         let def_id = method_item.def_id;
384         let generics = tcx.generics_of(def_id);
385         assert_eq!(generics.params.len(), 0);
386
387         debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
388         let mut obligations = vec![];
389
390         // Instantiate late-bound regions and substitute the trait
391         // parameters into the method type to get the actual method type.
392         //
393         // N.B., instantiate late-bound regions first so that
394         // `instantiate_type_scheme` can normalize associated types that
395         // may reference those regions.
396         let fn_sig = tcx.fn_sig(def_id);
397         let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig).0;
398         let fn_sig = fn_sig.subst(self.tcx, substs);
399
400         let InferOk { value, obligations: o } =
401             self.normalize_associated_types_in_as_infer_ok(span, fn_sig);
402         let fn_sig = {
403             obligations.extend(o);
404             value
405         };
406
407         // Register obligations for the parameters. This will include the
408         // `Self` parameter, which in turn has a bound of the main trait,
409         // so this also effectively registers `obligation` as well.  (We
410         // used to register `obligation` explicitly, but that resulted in
411         // double error messages being reported.)
412         //
413         // Note that as the method comes from a trait, it should not have
414         // any late-bound regions appearing in its bounds.
415         let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
416
417         let InferOk { value, obligations: o } =
418             self.normalize_associated_types_in_as_infer_ok(span, bounds);
419         let bounds = {
420             obligations.extend(o);
421             value
422         };
423
424         assert!(!bounds.has_escaping_bound_vars());
425
426         let cause = traits::ObligationCause::misc(span, self.body_id);
427         obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds));
428
429         // Also add an obligation for the method type being well-formed.
430         let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig));
431         debug!(
432             "lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
433             method_ty, obligation
434         );
435         obligations.push(traits::Obligation::new(
436             cause,
437             self.param_env,
438             ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx),
439         ));
440
441         let callee = MethodCallee { def_id, substs, sig: fn_sig };
442
443         debug!("callee = {:?}", callee);
444
445         Some(InferOk { obligations, value: callee })
446     }
447
448     /// Performs a [full-qualified function call] (formerly "universal function call") lookup. If
449     /// lookup is successful, it will return the type of definition and the [`DefId`] of the found
450     /// function definition.
451     ///
452     /// [full-qualified function call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls
453     ///
454     /// # Arguments
455     ///
456     /// Given a function call like `Foo::bar::<T1,...Tn>(...)`:
457     ///
458     /// * `self`:                  the surrounding `FnCtxt` (!)
459     /// * `span`:                  the span of the call, excluding arguments (`Foo::bar::<T1, ...Tn>`)
460     /// * `method_name`:           the identifier of the function within the container type (`bar`)
461     /// * `self_ty`:               the type to search within (`Foo`)
462     /// * `self_ty_span`           the span for the type being searched within (span of `Foo`)
463     /// * `expr_id`:               the [`hir::HirId`] of the expression composing the entire call
464     #[instrument(level = "debug", skip(self))]
465     pub fn resolve_fully_qualified_call(
466         &self,
467         span: Span,
468         method_name: Ident,
469         self_ty: Ty<'tcx>,
470         self_ty_span: Span,
471         expr_id: hir::HirId,
472     ) -> Result<(DefKind, DefId), MethodError<'tcx>> {
473         debug!(
474             "resolve_fully_qualified_call: method_name={:?} self_ty={:?} expr_id={:?}",
475             method_name, self_ty, expr_id,
476         );
477
478         let tcx = self.tcx;
479
480         // Check if we have an enum variant.
481         if let ty::Adt(adt_def, _) = self_ty.kind() {
482             if adt_def.is_enum() {
483                 let variant_def = adt_def
484                     .variants
485                     .iter()
486                     .find(|vd| tcx.hygienic_eq(method_name, vd.ident, adt_def.did));
487                 if let Some(variant_def) = variant_def {
488                     // Braced variants generate unusable names in value namespace (reserved for
489                     // possible future use), so variants resolved as associated items may refer to
490                     // them as well. It's ok to use the variant's id as a ctor id since an
491                     // error will be reported on any use of such resolution anyway.
492                     let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id);
493                     tcx.check_stability(ctor_def_id, Some(expr_id), span, Some(method_name.span));
494                     return Ok((
495                         DefKind::Ctor(CtorOf::Variant, variant_def.ctor_kind),
496                         ctor_def_id,
497                     ));
498                 }
499             }
500         }
501
502         let pick = self.probe_for_name(
503             span,
504             probe::Mode::Path,
505             method_name,
506             IsSuggestion(false),
507             self_ty,
508             expr_id,
509             ProbeScope::TraitsInScope,
510         )?;
511
512         self.lint_fully_qualified_call_from_2018(
513             span,
514             method_name,
515             self_ty,
516             self_ty_span,
517             expr_id,
518             &pick,
519         );
520
521         debug!("resolve_fully_qualified_call: pick={:?}", pick);
522         {
523             let mut typeck_results = self.typeck_results.borrow_mut();
524             let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap();
525             for import_id in pick.import_ids {
526                 debug!("resolve_fully_qualified_call: used_trait_import: {:?}", import_id);
527                 used_trait_imports.insert(import_id);
528             }
529         }
530
531         let def_kind = pick.item.kind.as_def_kind();
532         debug!(
533             "resolve_fully_qualified_call: def_kind={:?}, def_id={:?}",
534             def_kind, pick.item.def_id
535         );
536         tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
537         Ok((def_kind, pick.item.def_id))
538     }
539
540     /// Finds item with name `item_name` defined in impl/trait `def_id`
541     /// and return it, or `None`, if no such item was defined there.
542     pub fn associated_item(
543         &self,
544         def_id: DefId,
545         item_name: Ident,
546         ns: Namespace,
547     ) -> Option<ty::AssocItem> {
548         self.tcx
549             .associated_items(def_id)
550             .find_by_name_and_namespace(self.tcx, item_name, ns, def_id)
551             .copied()
552     }
553 }