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