rustc_hir_typeck/
fallback.rs

1use std::cell::OnceCell;
2use std::ops::ControlFlow;
3
4use rustc_data_structures::fx::FxHashSet;
5use rustc_data_structures::graph::iterate::DepthFirstSearch;
6use rustc_data_structures::graph::vec_graph::VecGraph;
7use rustc_data_structures::graph::{self};
8use rustc_data_structures::unord::{UnordBag, UnordMap, UnordSet};
9use rustc_hir as hir;
10use rustc_hir::HirId;
11use rustc_hir::def::{DefKind, Res};
12use rustc_hir::def_id::DefId;
13use rustc_hir::intravisit::{InferKind, Visitor};
14use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
15use rustc_session::lint;
16use rustc_span::def_id::LocalDefId;
17use rustc_span::{DUMMY_SP, Span};
18use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
19use tracing::debug;
20
21use crate::{FnCtxt, errors};
22
23#[derive(Copy, Clone)]
24pub(crate) enum DivergingFallbackBehavior {
25    /// Always fallback to `()` (aka "always spontaneous decay")
26    ToUnit,
27    /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken.
28    ContextDependent,
29    /// Always fallback to `!` (which should be equivalent to never falling back + not making
30    /// never-to-any coercions unless necessary)
31    ToNever,
32    /// Don't fallback at all
33    NoFallback,
34}
35
36impl<'tcx> FnCtxt<'_, 'tcx> {
37    /// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
38    /// if fallback has occurred.
39    pub(super) fn type_inference_fallback(&self) {
40        debug!(
41            "type-inference-fallback start obligations: {:#?}",
42            self.fulfillment_cx.borrow_mut().pending_obligations()
43        );
44
45        // All type checking constraints were added, try to fallback unsolved variables.
46        self.select_obligations_where_possible(|_| {});
47
48        debug!(
49            "type-inference-fallback post selection obligations: {:#?}",
50            self.fulfillment_cx.borrow_mut().pending_obligations()
51        );
52
53        let fallback_occurred = self.fallback_types();
54
55        if !fallback_occurred {
56            return;
57        }
58
59        // We now see if we can make progress. This might cause us to
60        // unify inference variables for opaque types, since we may
61        // have unified some other type variables during the first
62        // phase of fallback. This means that we only replace
63        // inference variables with their underlying opaque types as a
64        // last resort.
65        //
66        // In code like this:
67        //
68        // ```rust
69        // type MyType = impl Copy;
70        // fn produce() -> MyType { true }
71        // fn bad_produce() -> MyType { panic!() }
72        // ```
73        //
74        // we want to unify the opaque inference variable in `bad_produce`
75        // with the diverging fallback for `panic!` (e.g. `()` or `!`).
76        // This will produce a nice error message about conflicting concrete
77        // types for `MyType`.
78        //
79        // If we had tried to fallback the opaque inference variable to `MyType`,
80        // we will generate a confusing type-check error that does not explicitly
81        // refer to opaque types.
82        self.select_obligations_where_possible(|_| {});
83    }
84
85    fn fallback_types(&self) -> bool {
86        // Check if we have any unresolved variables. If not, no need for fallback.
87        let unresolved_variables = self.unresolved_variables();
88
89        if unresolved_variables.is_empty() {
90            return false;
91        }
92
93        let diverging_fallback = self
94            .calculate_diverging_fallback(&unresolved_variables, self.diverging_fallback_behavior);
95
96        // We do fallback in two passes, to try to generate
97        // better error messages.
98        // The first time, we do *not* replace opaque types.
99        let mut fallback_occurred = false;
100        for ty in unresolved_variables {
101            debug!("unsolved_variable = {:?}", ty);
102            fallback_occurred |= self.fallback_if_possible(ty, &diverging_fallback);
103        }
104
105        fallback_occurred
106    }
107
108    // Tries to apply a fallback to `ty` if it is an unsolved variable.
109    //
110    // - Unconstrained ints are replaced with `i32`.
111    //
112    // - Unconstrained floats are replaced with `f64`.
113    //
114    // - Non-numerics may get replaced with `()` or `!`, depending on
115    //   how they were categorized by `calculate_diverging_fallback`
116    //   (and the setting of `#![feature(never_type_fallback)]`).
117    //
118    // Fallback becomes very dubious if we have encountered
119    // type-checking errors. In that case, fallback to Error.
120    //
121    // Sets `FnCtxt::fallback_has_occurred` if fallback is performed
122    // during this call.
123    fn fallback_if_possible(
124        &self,
125        ty: Ty<'tcx>,
126        diverging_fallback: &UnordMap<Ty<'tcx>, Ty<'tcx>>,
127    ) -> bool {
128        // Careful: we do NOT shallow-resolve `ty`. We know that `ty`
129        // is an unsolved variable, and we determine its fallback
130        // based solely on how it was created, not what other type
131        // variables it may have been unified with since then.
132        //
133        // The reason this matters is that other attempts at fallback
134        // may (in principle) conflict with this fallback, and we wish
135        // to generate a type error in that case. (However, this
136        // actually isn't true right now, because we're only using the
137        // builtin fallback rules. This would be true if we were using
138        // user-supplied fallbacks. But it's still useful to write the
139        // code to detect bugs.)
140        //
141        // (Note though that if we have a general type variable `?T`
142        // that is then unified with an integer type variable `?I`
143        // that ultimately never gets resolved to a special integral
144        // type, `?T` is not considered unsolved, but `?I` is. The
145        // same is true for float variables.)
146        let fallback = match ty.kind() {
147            _ if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx, e),
148            ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
149            ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
150            _ => match diverging_fallback.get(&ty) {
151                Some(&fallback_ty) => fallback_ty,
152                None => return false,
153            },
154        };
155        debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
156
157        let span = ty.ty_vid().map_or(DUMMY_SP, |vid| self.infcx.type_var_origin(vid).span);
158        self.demand_eqtype(span, ty, fallback);
159        self.fallback_has_occurred.set(true);
160        true
161    }
162
163    /// The "diverging fallback" system is rather complicated. This is
164    /// a result of our need to balance 'do the right thing' with
165    /// backwards compatibility.
166    ///
167    /// "Diverging" type variables are variables created when we
168    /// coerce a `!` type into an unbound type variable `?X`. If they
169    /// never wind up being constrained, the "right and natural" thing
170    /// is that `?X` should "fallback" to `!`. This means that e.g. an
171    /// expression like `Some(return)` will ultimately wind up with a
172    /// type like `Option<!>` (presuming it is not assigned or
173    /// constrained to have some other type).
174    ///
175    /// However, the fallback used to be `()` (before the `!` type was
176    /// added). Moreover, there are cases where the `!` type 'leaks
177    /// out' from dead code into type variables that affect live
178    /// code. The most common case is something like this:
179    ///
180    /// ```rust
181    /// # fn foo() -> i32 { 4 }
182    /// match foo() {
183    ///     22 => Default::default(), // call this type `?D`
184    ///     _ => return, // return has type `!`
185    /// } // call the type of this match `?M`
186    /// ```
187    ///
188    /// Here, coercing the type `!` into `?M` will create a diverging
189    /// type variable `?X` where `?X <: ?M`. We also have that `?D <:
190    /// ?M`. If `?M` winds up unconstrained, then `?X` will
191    /// fallback. If it falls back to `!`, then all the type variables
192    /// will wind up equal to `!` -- this includes the type `?D`
193    /// (since `!` doesn't implement `Default`, we wind up a "trait
194    /// not implemented" error in code like this). But since the
195    /// original fallback was `()`, this code used to compile with `?D
196    /// = ()`. This is somewhat surprising, since `Default::default()`
197    /// on its own would give an error because the types are
198    /// insufficiently constrained.
199    ///
200    /// Our solution to this dilemma is to modify diverging variables
201    /// so that they can *either* fallback to `!` (the default) or to
202    /// `()` (the backwards compatibility case). We decide which
203    /// fallback to use based on whether there is a coercion pattern
204    /// like this:
205    ///
206    /// ```ignore (not-rust)
207    /// ?Diverging -> ?V
208    /// ?NonDiverging -> ?V
209    /// ?V != ?NonDiverging
210    /// ```
211    ///
212    /// Here `?Diverging` represents some diverging type variable and
213    /// `?NonDiverging` represents some non-diverging type
214    /// variable. `?V` can be any type variable (diverging or not), so
215    /// long as it is not equal to `?NonDiverging`.
216    ///
217    /// Intuitively, what we are looking for is a case where a
218    /// "non-diverging" type variable (like `?M` in our example above)
219    /// is coerced *into* some variable `?V` that would otherwise
220    /// fallback to `!`. In that case, we make `?V` fallback to `!`,
221    /// along with anything that would flow into `?V`.
222    ///
223    /// The algorithm we use:
224    /// * Identify all variables that are coerced *into* by a
225    ///   diverging variable. Do this by iterating over each
226    ///   diverging, unsolved variable and finding all variables
227    ///   reachable from there. Call that set `D`.
228    /// * Walk over all unsolved, non-diverging variables, and find
229    ///   any variable that has an edge into `D`.
230    fn calculate_diverging_fallback(
231        &self,
232        unresolved_variables: &[Ty<'tcx>],
233        behavior: DivergingFallbackBehavior,
234    ) -> UnordMap<Ty<'tcx>, Ty<'tcx>> {
235        debug!("calculate_diverging_fallback({:?})", unresolved_variables);
236
237        // Construct a coercion graph where an edge `A -> B` indicates
238        // a type variable is that is coerced
239        let coercion_graph = self.create_coercion_graph();
240
241        // Extract the unsolved type inference variable vids; note that some
242        // unsolved variables are integer/float variables and are excluded.
243        let unsolved_vids = unresolved_variables.iter().filter_map(|ty| ty.ty_vid());
244
245        // Compute the diverging root vids D -- that is, the root vid of
246        // those type variables that (a) are the target of a coercion from
247        // a `!` type and (b) have not yet been solved.
248        //
249        // These variables are the ones that are targets for fallback to
250        // either `!` or `()`.
251        let diverging_roots: UnordSet<ty::TyVid> = self
252            .diverging_type_vars
253            .borrow()
254            .items()
255            .map(|&ty| self.shallow_resolve(ty))
256            .filter_map(|ty| ty.ty_vid())
257            .map(|vid| self.root_var(vid))
258            .collect();
259        debug!(
260            "calculate_diverging_fallback: diverging_type_vars={:?}",
261            self.diverging_type_vars.borrow()
262        );
263        debug!("calculate_diverging_fallback: diverging_roots={:?}", diverging_roots);
264
265        // Find all type variables that are reachable from a diverging
266        // type variable. These will typically default to `!`, unless
267        // we find later that they are *also* reachable from some
268        // other type variable outside this set.
269        let mut roots_reachable_from_diverging = DepthFirstSearch::new(&coercion_graph);
270        let mut diverging_vids = vec![];
271        let mut non_diverging_vids = vec![];
272        for unsolved_vid in unsolved_vids {
273            let root_vid = self.root_var(unsolved_vid);
274            debug!(
275                "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}",
276                unsolved_vid,
277                root_vid,
278                diverging_roots.contains(&root_vid),
279            );
280            if diverging_roots.contains(&root_vid) {
281                diverging_vids.push(unsolved_vid);
282                roots_reachable_from_diverging.push_start_node(root_vid);
283
284                debug!(
285                    "calculate_diverging_fallback: root_vid={:?} reaches {:?}",
286                    root_vid,
287                    graph::depth_first_search(&coercion_graph, root_vid).collect::<Vec<_>>()
288                );
289
290                // drain the iterator to visit all nodes reachable from this node
291                roots_reachable_from_diverging.complete_search();
292            } else {
293                non_diverging_vids.push(unsolved_vid);
294            }
295        }
296
297        debug!(
298            "calculate_diverging_fallback: roots_reachable_from_diverging={:?}",
299            roots_reachable_from_diverging,
300        );
301
302        // Find all type variables N0 that are not reachable from a
303        // diverging variable, and then compute the set reachable from
304        // N0, which we call N. These are the *non-diverging* type
305        // variables. (Note that this set consists of "root variables".)
306        let mut roots_reachable_from_non_diverging = DepthFirstSearch::new(&coercion_graph);
307        for &non_diverging_vid in &non_diverging_vids {
308            let root_vid = self.root_var(non_diverging_vid);
309            if roots_reachable_from_diverging.visited(root_vid) {
310                continue;
311            }
312            roots_reachable_from_non_diverging.push_start_node(root_vid);
313            roots_reachable_from_non_diverging.complete_search();
314        }
315        debug!(
316            "calculate_diverging_fallback: roots_reachable_from_non_diverging={:?}",
317            roots_reachable_from_non_diverging,
318        );
319
320        debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations());
321
322        // For each diverging variable, figure out whether it can
323        // reach a member of N. If so, it falls back to `()`. Else
324        // `!`.
325        let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len());
326        let unsafe_infer_vars = OnceCell::new();
327
328        self.lint_obligations_broken_by_never_type_fallback_change(
329            behavior,
330            &diverging_vids,
331            &coercion_graph,
332        );
333
334        for &diverging_vid in &diverging_vids {
335            let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
336            let root_vid = self.root_var(diverging_vid);
337            let can_reach_non_diverging = graph::depth_first_search(&coercion_graph, root_vid)
338                .any(|n| roots_reachable_from_non_diverging.visited(n));
339
340            let infer_var_infos: UnordBag<_> = self
341                .infer_var_info
342                .borrow()
343                .items()
344                .filter(|&(vid, _)| self.infcx.root_var(*vid) == root_vid)
345                .map(|(_, info)| *info)
346                .collect();
347
348            let found_infer_var_info = ty::InferVarInfo {
349                self_in_trait: infer_var_infos.items().any(|info| info.self_in_trait),
350                output: infer_var_infos.items().any(|info| info.output),
351            };
352
353            let mut fallback_to = |ty| {
354                self.lint_never_type_fallback_flowing_into_unsafe_code(
355                    &unsafe_infer_vars,
356                    &coercion_graph,
357                    root_vid,
358                );
359
360                diverging_fallback.insert(diverging_ty, ty);
361            };
362
363            match behavior {
364                DivergingFallbackBehavior::ToUnit => {
365                    debug!("fallback to () - legacy: {:?}", diverging_vid);
366                    fallback_to(self.tcx.types.unit);
367                }
368                DivergingFallbackBehavior::ContextDependent => {
369                    if found_infer_var_info.self_in_trait && found_infer_var_info.output {
370                        // This case falls back to () to ensure that the code pattern in
371                        // tests/ui/never_type/fallback-closure-ret.rs continues to
372                        // compile when never_type_fallback is enabled.
373                        //
374                        // This rule is not readily explainable from first principles,
375                        // but is rather intended as a patchwork fix to ensure code
376                        // which compiles before the stabilization of never type
377                        // fallback continues to work.
378                        //
379                        // Typically this pattern is encountered in a function taking a
380                        // closure as a parameter, where the return type of that closure
381                        // (checked by `relationship.output`) is expected to implement
382                        // some trait (checked by `relationship.self_in_trait`). This
383                        // can come up in non-closure cases too, so we do not limit this
384                        // rule to specifically `FnOnce`.
385                        //
386                        // When the closure's body is something like `panic!()`, the
387                        // return type would normally be inferred to `!`. However, it
388                        // needs to fall back to `()` in order to still compile, as the
389                        // trait is specifically implemented for `()` but not `!`.
390                        //
391                        // For details on the requirements for these relationships to be
392                        // set, see the relationship finding module in
393                        // compiler/rustc_trait_selection/src/traits/relationships.rs.
394                        debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
395                        fallback_to(self.tcx.types.unit);
396                    } else if can_reach_non_diverging {
397                        debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
398                        fallback_to(self.tcx.types.unit);
399                    } else {
400                        debug!("fallback to ! - all diverging: {:?}", diverging_vid);
401                        fallback_to(self.tcx.types.never);
402                    }
403                }
404                DivergingFallbackBehavior::ToNever => {
405                    debug!(
406                        "fallback to ! - `rustc_never_type_mode = \"fallback_to_never\")`: {:?}",
407                        diverging_vid
408                    );
409                    fallback_to(self.tcx.types.never);
410                }
411                DivergingFallbackBehavior::NoFallback => {
412                    debug!(
413                        "no fallback - `rustc_never_type_mode = \"no_fallback\"`: {:?}",
414                        diverging_vid
415                    );
416                }
417            }
418        }
419
420        diverging_fallback
421    }
422
423    fn lint_never_type_fallback_flowing_into_unsafe_code(
424        &self,
425        unsafe_infer_vars: &OnceCell<UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>>,
426        coercion_graph: &VecGraph<ty::TyVid, true>,
427        root_vid: ty::TyVid,
428    ) {
429        let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| {
430            let unsafe_infer_vars = compute_unsafe_infer_vars(self, self.body_id);
431            debug!(?unsafe_infer_vars);
432            unsafe_infer_vars
433        });
434
435        let affected_unsafe_infer_vars =
436            graph::depth_first_search_as_undirected(&coercion_graph, root_vid)
437                .filter_map(|x| unsafe_infer_vars.get(&x).copied())
438                .collect::<Vec<_>>();
439
440        let sugg = self.try_to_suggest_annotations(&[root_vid], coercion_graph);
441
442        for (hir_id, span, reason) in affected_unsafe_infer_vars {
443            self.tcx.emit_node_span_lint(
444                lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE,
445                hir_id,
446                span,
447                match reason {
448                    UnsafeUseReason::Call => {
449                        errors::NeverTypeFallbackFlowingIntoUnsafe::Call { sugg: sugg.clone() }
450                    }
451                    UnsafeUseReason::Method => {
452                        errors::NeverTypeFallbackFlowingIntoUnsafe::Method { sugg: sugg.clone() }
453                    }
454                    UnsafeUseReason::Path => {
455                        errors::NeverTypeFallbackFlowingIntoUnsafe::Path { sugg: sugg.clone() }
456                    }
457                    UnsafeUseReason::UnionField => {
458                        errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField {
459                            sugg: sugg.clone(),
460                        }
461                    }
462                    UnsafeUseReason::Deref => {
463                        errors::NeverTypeFallbackFlowingIntoUnsafe::Deref { sugg: sugg.clone() }
464                    }
465                },
466            );
467        }
468    }
469
470    fn lint_obligations_broken_by_never_type_fallback_change(
471        &self,
472        behavior: DivergingFallbackBehavior,
473        diverging_vids: &[ty::TyVid],
474        coercions: &VecGraph<ty::TyVid, true>,
475    ) {
476        let DivergingFallbackBehavior::ToUnit = behavior else { return };
477
478        // Fallback happens if and only if there are diverging variables
479        if diverging_vids.is_empty() {
480            return;
481        }
482
483        // Returns errors which happen if fallback is set to `fallback`
484        let remaining_errors_if_fallback_to = |fallback| {
485            self.probe(|_| {
486                let obligations = self.fulfillment_cx.borrow().pending_obligations();
487                let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
488                ocx.register_obligations(obligations.iter().cloned());
489
490                for &diverging_vid in diverging_vids {
491                    let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
492
493                    ocx.eq(&ObligationCause::dummy(), self.param_env, diverging_ty, fallback)
494                        .expect("expected diverging var to be unconstrained");
495                }
496
497                ocx.select_where_possible()
498            })
499        };
500
501        // If we have no errors with `fallback = ()`, but *do* have errors with `fallback = !`,
502        // then this code will be broken by the never type fallback change.
503        let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit);
504        if unit_errors.is_empty()
505            && let mut never_errors = remaining_errors_if_fallback_to(self.tcx.types.never)
506            && let [ref mut never_error, ..] = never_errors.as_mut_slice()
507        {
508            self.adjust_fulfillment_error_for_expr_obligation(never_error);
509            let sugg = self.try_to_suggest_annotations(diverging_vids, coercions);
510            self.tcx.emit_node_span_lint(
511                lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
512                self.tcx.local_def_id_to_hir_id(self.body_id),
513                self.tcx.def_span(self.body_id),
514                errors::DependencyOnUnitNeverTypeFallback {
515                    obligation_span: never_error.obligation.cause.span,
516                    obligation: never_error.obligation.predicate,
517                    sugg,
518                },
519            )
520        }
521    }
522
523    /// Returns a graph whose nodes are (unresolved) inference variables and where
524    /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
525    fn create_coercion_graph(&self) -> VecGraph<ty::TyVid, true> {
526        let pending_obligations = self.fulfillment_cx.borrow_mut().pending_obligations();
527        debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations);
528        let coercion_edges: Vec<(ty::TyVid, ty::TyVid)> = pending_obligations
529            .into_iter()
530            .filter_map(|obligation| {
531                // The predicates we are looking for look like `Coerce(?A -> ?B)`.
532                // They will have no bound variables.
533                obligation.predicate.kind().no_bound_vars()
534            })
535            .filter_map(|atom| {
536                // We consider both subtyping and coercion to imply 'flow' from
537                // some position in the code `a` to a different position `b`.
538                // This is then used to determine which variables interact with
539                // live code, and as such must fall back to `()` to preserve
540                // soundness.
541                //
542                // In practice currently the two ways that this happens is
543                // coercion and subtyping.
544                let (a, b) = match atom {
545                    ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
546                    ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
547                        (a, b)
548                    }
549                    _ => return None,
550                };
551
552                let a_vid = self.root_vid(a)?;
553                let b_vid = self.root_vid(b)?;
554                Some((a_vid, b_vid))
555            })
556            .collect();
557        debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges);
558        let num_ty_vars = self.num_ty_vars();
559
560        VecGraph::new(num_ty_vars, coercion_edges)
561    }
562
563    /// If `ty` is an unresolved type variable, returns its root vid.
564    fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> {
565        Some(self.root_var(self.shallow_resolve(ty).ty_vid()?))
566    }
567
568    /// Given a set of diverging vids and coercions, walk the HIR to gather a
569    /// set of suggestions which can be applied to preserve fallback to unit.
570    fn try_to_suggest_annotations(
571        &self,
572        diverging_vids: &[ty::TyVid],
573        coercions: &VecGraph<ty::TyVid, true>,
574    ) -> errors::SuggestAnnotations {
575        let body =
576            self.tcx.hir().maybe_body_owned_by(self.body_id).expect("body id must have an owner");
577        // For each diverging var, look through the HIR for a place to give it
578        // a type annotation. We do this per var because we only really need one
579        // suggestion to influence a var to be `()`.
580        let suggestions = diverging_vids
581            .iter()
582            .copied()
583            .filter_map(|vid| {
584                let reachable_vids =
585                    graph::depth_first_search_as_undirected(coercions, vid).collect();
586                AnnotateUnitFallbackVisitor { reachable_vids, fcx: self }
587                    .visit_expr(body.value)
588                    .break_value()
589            })
590            .collect();
591        errors::SuggestAnnotations { suggestions }
592    }
593}
594
595/// Try to walk the HIR to find a place to insert a useful suggestion
596/// to preserve fallback to `()` in 2024.
597struct AnnotateUnitFallbackVisitor<'a, 'tcx> {
598    reachable_vids: FxHashSet<ty::TyVid>,
599    fcx: &'a FnCtxt<'a, 'tcx>,
600}
601impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> {
602    // For a given path segment, if it's missing a turbofish, try to suggest adding
603    // one so we can constrain an argument to `()`. To keep the suggestion simple,
604    // we want to simply suggest `_` for all the other args. This (for now) only
605    // works when there are only type variables (and region variables, since we can
606    // elide them)...
607    fn suggest_for_segment(
608        &self,
609        arg_segment: &'tcx hir::PathSegment<'tcx>,
610        def_id: DefId,
611        id: HirId,
612    ) -> ControlFlow<errors::SuggestAnnotation> {
613        if arg_segment.args.is_none()
614            && let Some(all_args) = self.fcx.typeck_results.borrow().node_args_opt(id)
615            && let generics = self.fcx.tcx.generics_of(def_id)
616            && let args = all_args[generics.parent_count..].iter().zip(&generics.own_params)
617            // We can't turbofish consts :(
618            && args.clone().all(|(_, param)| matches!(param.kind, ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Lifetime))
619        {
620            // We filter out APITs, which are not turbofished.
621            let non_apit_type_args = args.filter(|(_, param)| {
622                matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: false, .. })
623            });
624            let n_tys = non_apit_type_args.clone().count();
625            for (idx, (arg, _)) in non_apit_type_args.enumerate() {
626                if let Some(ty) = arg.as_type()
627                    && let Some(vid) = self.fcx.root_vid(ty)
628                    && self.reachable_vids.contains(&vid)
629                {
630                    return ControlFlow::Break(errors::SuggestAnnotation::Turbo(
631                        arg_segment.ident.span.shrink_to_hi(),
632                        n_tys,
633                        idx,
634                    ));
635                }
636            }
637        }
638        ControlFlow::Continue(())
639    }
640}
641impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> {
642    type Result = ControlFlow<errors::SuggestAnnotation>;
643
644    fn visit_infer(
645        &mut self,
646        inf_id: HirId,
647        inf_span: Span,
648        _kind: InferKind<'tcx>,
649    ) -> Self::Result {
650        // Try to replace `_` with `()`.
651        if let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(inf_id)
652            && let Some(vid) = self.fcx.root_vid(ty)
653            && self.reachable_vids.contains(&vid)
654            && inf_span.can_be_used_for_suggestions()
655        {
656            return ControlFlow::Break(errors::SuggestAnnotation::Unit(inf_span));
657        }
658
659        ControlFlow::Continue(())
660    }
661
662    fn visit_qpath(
663        &mut self,
664        qpath: &'tcx rustc_hir::QPath<'tcx>,
665        id: HirId,
666        span: Span,
667    ) -> Self::Result {
668        let arg_segment = match qpath {
669            hir::QPath::Resolved(_, path) => {
670                path.segments.last().expect("paths should have a segment")
671            }
672            hir::QPath::TypeRelative(_, segment) => segment,
673            hir::QPath::LangItem(..) => {
674                return hir::intravisit::walk_qpath(self, qpath, id);
675            }
676        };
677        // Alternatively, try to turbofish `::<_, (), _>`.
678        if let Some(def_id) = self.fcx.typeck_results.borrow().qpath_res(qpath, id).opt_def_id()
679            && span.can_be_used_for_suggestions()
680        {
681            self.suggest_for_segment(arg_segment, def_id, id)?;
682        }
683        hir::intravisit::walk_qpath(self, qpath, id)
684    }
685
686    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
687        if let hir::ExprKind::Closure(&hir::Closure { body, .. })
688        | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) = expr.kind
689        {
690            self.visit_body(self.fcx.tcx.hir().body(body))?;
691        }
692
693        // Try to suggest adding an explicit qself `()` to a trait method path.
694        // i.e. changing `Default::default()` to `<() as Default>::default()`.
695        if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
696            && let Res::Def(DefKind::AssocFn, def_id) = path.res
697            && self.fcx.tcx.trait_of_item(def_id).is_some()
698            && let Some(args) = self.fcx.typeck_results.borrow().node_args_opt(expr.hir_id)
699            && let self_ty = args.type_at(0)
700            && let Some(vid) = self.fcx.root_vid(self_ty)
701            && self.reachable_vids.contains(&vid)
702            && let [.., trait_segment, _method_segment] = path.segments
703            && expr.span.can_be_used_for_suggestions()
704        {
705            let span = path.span.shrink_to_lo().to(trait_segment.ident.span);
706            return ControlFlow::Break(errors::SuggestAnnotation::Path(span));
707        }
708
709        // Or else, try suggesting turbofishing the method args.
710        if let hir::ExprKind::MethodCall(segment, ..) = expr.kind
711            && let Some(def_id) =
712                self.fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id)
713            && expr.span.can_be_used_for_suggestions()
714        {
715            self.suggest_for_segment(segment, def_id, expr.hir_id)?;
716        }
717
718        hir::intravisit::walk_expr(self, expr)
719    }
720
721    fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) -> Self::Result {
722        // For a local, try suggest annotating the type if it's missing.
723        if let hir::LocalSource::Normal = local.source
724            && let None = local.ty
725            && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(local.hir_id)
726            && let Some(vid) = self.fcx.root_vid(ty)
727            && self.reachable_vids.contains(&vid)
728            && local.span.can_be_used_for_suggestions()
729        {
730            return ControlFlow::Break(errors::SuggestAnnotation::Local(
731                local.pat.span.shrink_to_hi(),
732            ));
733        }
734        hir::intravisit::walk_local(self, local)
735    }
736}
737
738#[derive(Debug, Copy, Clone)]
739pub(crate) enum UnsafeUseReason {
740    Call,
741    Method,
742    Path,
743    UnionField,
744    Deref,
745}
746
747/// Finds all type variables which are passed to an `unsafe` operation.
748///
749/// For example, for this function `f`:
750/// ```ignore (demonstrative)
751/// fn f() {
752///     unsafe {
753///         let x /* ?X */ = core::mem::zeroed();
754///         //               ^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason
755///
756///         let y = core::mem::zeroed::<Option<_ /* ?Y */>>();
757///         //      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason
758///     }
759/// }
760/// ```
761///
762/// `compute_unsafe_infer_vars` will return `{ id(?X) -> (hir_id, span, Call) }`
763fn compute_unsafe_infer_vars<'a, 'tcx>(
764    fcx: &'a FnCtxt<'a, 'tcx>,
765    body_id: LocalDefId,
766) -> UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)> {
767    let body = fcx.tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner");
768    let mut res = UnordMap::default();
769
770    struct UnsafeInferVarsVisitor<'a, 'tcx> {
771        fcx: &'a FnCtxt<'a, 'tcx>,
772        res: &'a mut UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>,
773    }
774
775    impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_> {
776        fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) {
777            let typeck_results = self.fcx.typeck_results.borrow();
778
779            match ex.kind {
780                hir::ExprKind::MethodCall(..) => {
781                    if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id)
782                        && let method_ty = self.fcx.tcx.type_of(def_id).instantiate_identity()
783                        && let sig = method_ty.fn_sig(self.fcx.tcx)
784                        && sig.safety().is_unsafe()
785                    {
786                        let mut collector = InferVarCollector {
787                            value: (ex.hir_id, ex.span, UnsafeUseReason::Method),
788                            res: self.res,
789                        };
790
791                        // Collect generic arguments (incl. `Self`) of the method
792                        typeck_results
793                            .node_args(ex.hir_id)
794                            .types()
795                            .for_each(|t| t.visit_with(&mut collector));
796                    }
797                }
798
799                hir::ExprKind::Call(func, ..) => {
800                    let func_ty = typeck_results.expr_ty(func);
801
802                    if func_ty.is_fn()
803                        && let sig = func_ty.fn_sig(self.fcx.tcx)
804                        && sig.safety().is_unsafe()
805                    {
806                        let mut collector = InferVarCollector {
807                            value: (ex.hir_id, ex.span, UnsafeUseReason::Call),
808                            res: self.res,
809                        };
810
811                        // Try collecting generic arguments of the function.
812                        // Note that we do this below for any paths (that don't have to be called),
813                        // but there we do it with a different span/reason.
814                        // This takes priority.
815                        typeck_results
816                            .node_args(func.hir_id)
817                            .types()
818                            .for_each(|t| t.visit_with(&mut collector));
819
820                        // Also check the return type, for cases like `returns_unsafe_fn_ptr()()`
821                        sig.output().visit_with(&mut collector);
822                    }
823                }
824
825                // Check paths which refer to functions.
826                // We do this, instead of only checking `Call` to make sure the lint can't be
827                // avoided by storing unsafe function in a variable.
828                hir::ExprKind::Path(_) => {
829                    let ty = typeck_results.expr_ty(ex);
830
831                    // If this path refers to an unsafe function, collect inference variables which may affect it.
832                    // `is_fn` excludes closures, but those can't be unsafe.
833                    if ty.is_fn()
834                        && let sig = ty.fn_sig(self.fcx.tcx)
835                        && sig.safety().is_unsafe()
836                    {
837                        let mut collector = InferVarCollector {
838                            value: (ex.hir_id, ex.span, UnsafeUseReason::Path),
839                            res: self.res,
840                        };
841
842                        // Collect generic arguments of the function
843                        typeck_results
844                            .node_args(ex.hir_id)
845                            .types()
846                            .for_each(|t| t.visit_with(&mut collector));
847                    }
848                }
849
850                hir::ExprKind::Unary(hir::UnOp::Deref, pointer) => {
851                    if let ty::RawPtr(pointee, _) = typeck_results.expr_ty(pointer).kind() {
852                        pointee.visit_with(&mut InferVarCollector {
853                            value: (ex.hir_id, ex.span, UnsafeUseReason::Deref),
854                            res: self.res,
855                        });
856                    }
857                }
858
859                hir::ExprKind::Field(base, _) => {
860                    let base_ty = typeck_results.expr_ty(base);
861
862                    if base_ty.is_union() {
863                        typeck_results.expr_ty(ex).visit_with(&mut InferVarCollector {
864                            value: (ex.hir_id, ex.span, UnsafeUseReason::UnionField),
865                            res: self.res,
866                        });
867                    }
868                }
869
870                _ => (),
871            };
872
873            hir::intravisit::walk_expr(self, ex);
874        }
875    }
876
877    struct InferVarCollector<'r, V> {
878        value: V,
879        res: &'r mut UnordMap<ty::TyVid, V>,
880    }
881
882    impl<'tcx, V: Copy> ty::TypeVisitor<TyCtxt<'tcx>> for InferVarCollector<'_, V> {
883        fn visit_ty(&mut self, t: Ty<'tcx>) {
884            if let Some(vid) = t.ty_vid() {
885                _ = self.res.try_insert(vid, self.value);
886            } else {
887                t.super_visit_with(self)
888            }
889        }
890    }
891
892    UnsafeInferVarsVisitor { fcx, res: &mut res }.visit_expr(&body.value);
893
894    debug!(?res, "collected the following unsafe vars for {body_id:?}");
895
896    res
897}