Skip to main content

rustc_hir_typeck/
fallback.rs

1use std::ops::ControlFlow;
2
3use rustc_data_structures::fx::FxHashSet;
4use rustc_data_structures::graph;
5use rustc_data_structures::graph::vec_graph::VecGraph;
6use rustc_data_structures::unord::{UnordMap, UnordSet};
7use rustc_hir::def::{DefKind, Res};
8use rustc_hir::def_id::DefId;
9use rustc_hir::intravisit::{InferKind, Visitor};
10use rustc_hir::{self as hir, CRATE_HIR_ID, HirId};
11use rustc_lint_defs::builtin::{
12    FLOAT_LITERAL_F32_FALLBACK, NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE,
13};
14use rustc_middle::ty::{self, FloatVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
15use rustc_span::def_id::LocalDefId;
16use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
17use rustc_trait_selection::traits::TraitEngine;
18use tracing::debug;
19
20use crate::{FnCtxt, diagnostics};
21
22impl<'tcx> FnCtxt<'_, 'tcx> {
23    /// Performs type inference fallback, setting [`FnCtxt::diverging_fallback_has_occurred`]
24    /// if the never type fallback has occurred.
25    pub(super) fn type_inference_fallback(&self) {
26        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs:26",
                        "rustc_hir_typeck::fallback", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs"),
                        ::tracing_core::__macro_support::Option::Some(26u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fallback"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("type-inference-fallback start obligations: {0:#?}",
                                                    self.fulfillment_cx.borrow_mut().pending_obligations()) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
27            "type-inference-fallback start obligations: {:#?}",
28            self.fulfillment_cx.borrow_mut().pending_obligations()
29        );
30
31        // All type checking constraints were added, try to fallback unsolved variables.
32        self.select_obligations_where_possible(|_| {});
33
34        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs:34",
                        "rustc_hir_typeck::fallback", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs"),
                        ::tracing_core::__macro_support::Option::Some(34u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fallback"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("type-inference-fallback post selection obligations: {0:#?}",
                                                    self.fulfillment_cx.borrow_mut().pending_obligations()) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
35            "type-inference-fallback post selection obligations: {:#?}",
36            self.fulfillment_cx.borrow_mut().pending_obligations()
37        );
38
39        let fallback_occurred = self.fallback_types();
40
41        if fallback_occurred {
42            // if fallback occurred, previously stalled goals may make progress again
43            self.select_obligations_where_possible(|_| {});
44        }
45    }
46
47    /// Tries to apply a fallback to all unresolved variables.
48    ///
49    /// - Unconstrained ints are replaced with `i32`.
50    ///
51    /// - Unconstrained floats are replaced with `f64`, except when there is a trait predicate
52    ///   `f32: From<{float}>`, in which case `f32` is used as the fallback instead and a
53    ///   [`FLOAT_LITERAL_F32_FALLBACK`] FCW is emitted.
54    ///
55    /// - Non-numerics may get replaced with `()` or `!`, depending on how they
56    ///   were categorized by [`Self::calculate_diverging_fallback`], crate's
57    ///   edition, and the setting of `#![rustc_never_type_options(fallback = ...)]`.
58    ///
59    /// Fallback becomes very dubious if we have encountered type-checking errors.
60    /// In that case, all variables fallback to Error.
61    ///
62    /// Sets [`FnCtxt::diverging_fallback_has_occurred`] if never type fallback
63    /// is performed during this call.
64    ///
65    /// Returns `true` if *any* kind of fallback has occurred during this call.
66    fn fallback_types(&self) -> bool {
67        let (unresolved_ty, unresolved_int, unresolved_float) = self.unresolved_root_variables();
68
69        // Check if we have any unresolved variables. If not, no need for fallback.
70        if unresolved_ty.is_empty() && unresolved_int.is_empty() && unresolved_float.is_empty() {
71            return false;
72        }
73
74        // If tainted by errors, fallback all unresolved variables to error type,
75        // in order to prevent unnecessary diagnostics.
76        if let Some(guar) = self.tainted_by_errors() {
77            self.fallback_types_to_error(guar, unresolved_ty, unresolved_int, unresolved_float);
78            return true;
79        }
80
81        let diverging_fallback = self.calculate_diverging_fallback();
82        let fallback_to_f32 = self.calculate_fallback_to_f32(&unresolved_float);
83
84        // All unresolved int and float variables always use fallback,
85        // whereas unresolved type variables might not use fallback.
86        let mut fallback_occurred = !unresolved_int.is_empty() || !unresolved_float.is_empty();
87
88        for &vid in unresolved_ty.iter().filter(|&vid| diverging_fallback.contains(vid)) {
89            let span = self.type_var_origin(vid).span;
90            self.demand_eqtype(span, Ty::new_var(self.tcx, vid), self.tcx.types.never);
91
92            self.diverging_fallback_has_occurred.set(true);
93            fallback_occurred = true;
94        }
95
96        for vid in unresolved_int {
97            self.demand_eqtype(DUMMY_SP, Ty::new_int_var(self.tcx, vid), self.tcx.types.i32);
98        }
99
100        for vid in unresolved_float {
101            let fallback = if fallback_to_f32.contains(&vid) {
102                self.tcx.types.f32
103            } else {
104                self.tcx.types.f64
105            };
106            let span = self.float_var_origin(vid).span;
107            self.demand_eqtype(span, Ty::new_float_var(self.tcx, vid), fallback);
108        }
109
110        fallback_occurred
111    }
112
113    fn fallback_types_to_error(
114        &self,
115        guar: ErrorGuaranteed,
116        unresolved_ty: Vec<ty::TyVid>,
117        unresolved_int: Vec<ty::IntVid>,
118        unresolved_float: Vec<ty::FloatVid>,
119    ) {
120        let vars = unresolved_ty
121            .into_iter()
122            .map(|vid| Ty::new_var(self.tcx, vid))
123            .chain(unresolved_int.into_iter().map(|vid| Ty::new_int_var(self.tcx, vid)))
124            .chain(unresolved_float.into_iter().map(|vid| Ty::new_float_var(self.tcx, vid)));
125
126        let error = Ty::new_error(self.tcx, guar);
127
128        for var in vars {
129            self.demand_eqtype(DUMMY_SP, var, error);
130        }
131    }
132
133    /// Existing code relies on `f32: From<T>` (usually written as `T: Into<f32>`) resolving `T` to
134    /// `f32` when the type of `T` is inferred from an unsuffixed float literal. Using the default
135    /// fallback of `f64`, this would break when adding `impl From<f16> for f32`, as there are now
136    /// two float type which could be `T`, meaning that the fallback of `f64` would be used and
137    /// compilation error would occur as `f32` does not implement `From<f64>`. To avoid breaking
138    /// existing code, we instead fallback `T` to `f32` when there is a trait predicate
139    /// `f32: From<T>`. This means code like the following will continue to compile:
140    ///
141    /// ```rust
142    /// fn foo<T: Into<f32>>(_: T) {}
143    ///
144    /// foo(1.0);
145    /// ```
146    fn calculate_fallback_to_f32(
147        &self,
148        unresolved_root_variables: &[ty::FloatVid],
149    ) -> UnordSet<FloatVid> {
150        // Short-circuit: if no unresolved variable is a float, no f32 fallback can apply,
151        // so we can skip the (potentially very expensive) work in `from_float_for_f32_root_vids`.
152        // Under the new solver, that function walks `visit_proof_tree` for every pending
153        // obligation, which is O(N × proof_tree_size) and can dominate type-checking on crates
154        // with many large pending obligations and no f32 involvement.
155        if unresolved_root_variables.is_empty() {
156            return UnordSet::new();
157        }
158
159        let roots: UnordSet<ty::FloatVid> = self.from_float_for_f32_root_vids();
160        if roots.is_empty() {
161            // Most functions have no `f32: From<{float}>` predicates, so short-circuit and return
162            // an empty set when this is the case.
163            return UnordSet::new();
164        }
165        // Calculate all the unresolved variables that need to fallback to `f32` here. This ensures
166        // we don't need to find root variables in `fallback_if_possible`: see the comment at the
167        // top of that function for details.
168        let fallback_to_f32 = unresolved_root_variables
169            .iter()
170            .copied()
171            .filter(|&vid| roots.contains(&vid))
172            .inspect(|&vid| {
173                let origin = self.float_var_origin(vid);
174                // Show the entire literal in the suggestion to make it clearer.
175                let mut literal = self.tcx.sess.source_map().span_to_snippet(origin.span).ok();
176                // A `.` at the end of the literal is no longer necessary if `f32` is explicitly specified
177                if let Some(ref mut literal) = literal
178                    && literal.ends_with('.')
179                {
180                    literal.pop();
181                }
182                self.tcx.emit_node_span_lint(
183                    FLOAT_LITERAL_F32_FALLBACK,
184                    origin.lint_id.unwrap_or(CRATE_HIR_ID),
185                    origin.span,
186                    diagnostics::FloatLiteralF32Fallback {
187                        span: literal.as_ref().map(|_| origin.span),
188                        literal: literal.unwrap_or_default(),
189                    },
190                );
191            })
192            .collect();
193        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs:193",
                        "rustc_hir_typeck::fallback", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs"),
                        ::tracing_core::__macro_support::Option::Some(193u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fallback"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("calculate_fallback_to_f32: fallback_to_f32={0:?}",
                                                    fallback_to_f32) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("calculate_fallback_to_f32: fallback_to_f32={:?}", fallback_to_f32);
194        fallback_to_f32
195    }
196
197    fn calculate_diverging_fallback(&self) -> UnordSet<ty::TyVid> {
198        // Compute the diverging root vids D -- that is, the root vid of
199        // those type variables that (a) are the target of a coercion from
200        // a `!` type and (b) have not yet been solved.
201        //
202        // These variables are the ones that are targets for fallback to `!`.
203        let diverging_root_vids: Vec<ty::TyVid> = self
204            .diverging_type_vars
205            .borrow()
206            .iter()
207            .filter_map(|&vid| self.infcx.shallow_resolve_ty_var_or_get_root(vid).err())
208            .collect();
209
210        {
211            if !diverging_root_vids.is_empty() {
212                // Construct a coercion graph where an edge `A -> B` indicates
213                // a type variable is that is coerced
214                let coercion_graph = self.create_coercion_graph();
215
216                let unsafe_infer_vars = compute_unsafe_infer_vars(self, self.body_def_id);
217
218                for &root_vid in &diverging_root_vids {
219                    self.lint_never_type_fallback_flowing_into_unsafe_code(
220                        &unsafe_infer_vars,
221                        &coercion_graph,
222                        root_vid,
223                    );
224                }
225            }
226        }
227
228        diverging_root_vids.into_iter().collect::<UnordSet<_>>()
229    }
230
231    fn lint_never_type_fallback_flowing_into_unsafe_code(
232        &self,
233        unsafe_infer_vars: &UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>,
234        coercion_graph: &VecGraph<ty::TyVid, true>,
235        root_vid: ty::TyVid,
236    ) {
237        let affected_unsafe_infer_vars =
238            graph::depth_first_search_as_undirected(&coercion_graph, root_vid)
239                .filter_map(|x| unsafe_infer_vars.get(&x).copied())
240                .collect::<Vec<_>>();
241
242        let sugg = self.try_to_suggest_annotations(&[root_vid], coercion_graph);
243
244        for (hir_id, span, reason) in affected_unsafe_infer_vars {
245            self.tcx.emit_node_span_lint(
246                NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE,
247                hir_id,
248                span,
249                match reason {
250                    UnsafeUseReason::Call => {
251                        diagnostics::NeverTypeFallbackFlowingIntoUnsafe::Call { sugg: sugg.clone() }
252                    }
253                    UnsafeUseReason::Method => {
254                        diagnostics::NeverTypeFallbackFlowingIntoUnsafe::Method {
255                            sugg: sugg.clone(),
256                        }
257                    }
258                    UnsafeUseReason::Path => {
259                        diagnostics::NeverTypeFallbackFlowingIntoUnsafe::Path { sugg: sugg.clone() }
260                    }
261                    UnsafeUseReason::UnionField => {
262                        diagnostics::NeverTypeFallbackFlowingIntoUnsafe::UnionField {
263                            sugg: sugg.clone(),
264                        }
265                    }
266                    UnsafeUseReason::Deref => {
267                        diagnostics::NeverTypeFallbackFlowingIntoUnsafe::Deref {
268                            sugg: sugg.clone(),
269                        }
270                    }
271                },
272            );
273        }
274    }
275
276    /// Returns a graph whose nodes are (unresolved) inference variables and where
277    /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
278    fn create_coercion_graph(&self) -> VecGraph<ty::TyVid, true> {
279        let pending_obligations = self.fulfillment_cx.borrow_mut().pending_obligations();
280        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs:280",
                        "rustc_hir_typeck::fallback", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs"),
                        ::tracing_core::__macro_support::Option::Some(280u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fallback"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("create_coercion_graph: pending_obligations={0:?}",
                                                    pending_obligations) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations);
281        let coercion_edges: Vec<(ty::TyVid, ty::TyVid)> = pending_obligations
282            .into_iter()
283            .filter_map(|obligation| {
284                // The predicates we are looking for look like `Coerce(?A -> ?B)`.
285                // They will have no bound variables.
286                obligation.predicate.kind().no_bound_vars()
287            })
288            .filter_map(|atom| {
289                // We consider both subtyping and coercion to imply 'flow' from
290                // some position in the code `a` to a different position `b`.
291                // This is then used to determine which variables interact with
292                // live code, and as such must fall back to `()` to preserve
293                // soundness.
294                //
295                // In practice currently the two ways that this happens is
296                // coercion and subtyping.
297                let (a, b) = match atom {
298                    ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
299                    ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
300                        (a, b)
301                    }
302                    _ => return None,
303                };
304
305                let a_vid = self.root_vid(a)?;
306                let b_vid = self.root_vid(b)?;
307                Some((a_vid, b_vid))
308            })
309            .collect();
310        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs:310",
                        "rustc_hir_typeck::fallback", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs"),
                        ::tracing_core::__macro_support::Option::Some(310u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fallback"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("create_coercion_graph: coercion_edges={0:?}",
                                                    coercion_edges) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges);
311        let num_ty_vars = self.num_ty_vars();
312
313        VecGraph::new(num_ty_vars, coercion_edges)
314    }
315
316    /// If `ty` is an unresolved type variable, returns its root vid.
317    fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> {
318        Some(self.root_var(self.shallow_resolve(ty).ty_vid()?))
319    }
320
321    /// If `ty` is an unresolved float type variable, returns its root vid.
322    pub(crate) fn root_float_vid(&self, ty: Ty<'tcx>) -> Option<ty::FloatVid> {
323        Some(self.root_float_var(self.shallow_resolve(ty).float_vid()?))
324    }
325
326    /// Given a set of diverging vids and coercions, walk the HIR to gather a
327    /// set of suggestions which can be applied to preserve fallback to unit.
328    fn try_to_suggest_annotations(
329        &self,
330        diverging_vids: &[ty::TyVid],
331        coercions: &VecGraph<ty::TyVid, true>,
332    ) -> diagnostics::SuggestAnnotations {
333        let body = self.tcx.hir_body_owned_by(self.body_def_id);
334        // For each diverging var, look through the HIR for a place to give it
335        // a type annotation. We do this per var because we only really need one
336        // suggestion to influence a var to be `()`.
337        let suggestions = diverging_vids
338            .iter()
339            .copied()
340            .filter_map(|vid| {
341                let reachable_vids =
342                    graph::depth_first_search_as_undirected(coercions, vid).collect();
343                AnnotateUnitFallbackVisitor { reachable_vids, fcx: self }
344                    .visit_expr(body.value)
345                    .break_value()
346            })
347            .collect();
348        diagnostics::SuggestAnnotations { suggestions }
349    }
350}
351
352/// Try to walk the HIR to find a place to insert a useful suggestion
353/// to preserve fallback to `()` in 2024.
354struct AnnotateUnitFallbackVisitor<'a, 'tcx> {
355    reachable_vids: FxHashSet<ty::TyVid>,
356    fcx: &'a FnCtxt<'a, 'tcx>,
357}
358impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> {
359    // For a given path segment, if it's missing a turbofish, try to suggest adding
360    // one so we can constrain an argument to `()`. To keep the suggestion simple,
361    // we want to simply suggest `_` for all the other args. This (for now) only
362    // works when there are only type variables (and region variables, since we can
363    // elide them)...
364    fn suggest_for_segment(
365        &self,
366        arg_segment: &'tcx hir::PathSegment<'tcx>,
367        def_id: DefId,
368        id: HirId,
369    ) -> ControlFlow<diagnostics::SuggestAnnotation> {
370        if arg_segment.args.is_none()
371            && let Some(all_args) = self.fcx.typeck_results.borrow().node_args_opt(id)
372            && let generics = self.fcx.tcx.generics_of(def_id)
373            && let args = all_args[generics.parent_count..].iter().zip(&generics.own_params)
374            // We can't turbofish consts :(
375            && args.clone().all(|(_, param)| #[allow(non_exhaustive_omitted_patterns)] match param.kind {
    ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Lifetime
        => true,
    _ => false,
}matches!(param.kind, ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Lifetime))
376        {
377            // We filter out APITs, which are not turbofished.
378            let non_apit_type_args = args.filter(|(_, param)| {
379                #[allow(non_exhaustive_omitted_patterns)] match param.kind {
    ty::GenericParamDefKind::Type { synthetic: false, .. } => true,
    _ => false,
}matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: false, .. })
380            });
381            let n_tys = non_apit_type_args.clone().count();
382            for (idx, (arg, _)) in non_apit_type_args.enumerate() {
383                if let Some(ty) = arg.as_type()
384                    && let Some(vid) = self.fcx.root_vid(ty)
385                    && self.reachable_vids.contains(&vid)
386                {
387                    return ControlFlow::Break(diagnostics::SuggestAnnotation::Turbo(
388                        arg_segment.ident.span.shrink_to_hi(),
389                        n_tys,
390                        idx,
391                    ));
392                }
393            }
394        }
395        ControlFlow::Continue(())
396    }
397}
398impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> {
399    type Result = ControlFlow<diagnostics::SuggestAnnotation>;
400
401    fn visit_infer(
402        &mut self,
403        inf_id: HirId,
404        inf_span: Span,
405        _kind: InferKind<'tcx>,
406    ) -> Self::Result {
407        // Try to replace `_` with `()`.
408        if let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(inf_id)
409            && let Some(vid) = self.fcx.root_vid(ty)
410            && self.reachable_vids.contains(&vid)
411            && inf_span.can_be_used_for_suggestions()
412        {
413            return ControlFlow::Break(diagnostics::SuggestAnnotation::Unit(inf_span));
414        }
415
416        ControlFlow::Continue(())
417    }
418
419    fn visit_qpath(
420        &mut self,
421        qpath: &'tcx rustc_hir::QPath<'tcx>,
422        id: HirId,
423        span: Span,
424    ) -> Self::Result {
425        let arg_segment = match qpath {
426            hir::QPath::Resolved(_, path) => {
427                path.segments.last().expect("paths should have a segment")
428            }
429            hir::QPath::TypeRelative(_, segment) => segment,
430        };
431        // Alternatively, try to turbofish `::<_, (), _>`.
432        if let Some(def_id) = self.fcx.typeck_results.borrow().qpath_res(qpath, id).opt_def_id()
433            && span.can_be_used_for_suggestions()
434        {
435            self.suggest_for_segment(arg_segment, def_id, id)?;
436        }
437        hir::intravisit::walk_qpath(self, qpath, id)
438    }
439
440    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
441        if let hir::ExprKind::Closure(&hir::Closure { body, .. })
442        | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) = expr.kind
443        {
444            self.visit_body(self.fcx.tcx.hir_body(body))?;
445        }
446
447        // Try to suggest adding an explicit qself `()` to a trait method path.
448        // i.e. changing `Default::default()` to `<() as Default>::default()`.
449        if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
450            && let Res::Def(DefKind::AssocFn, def_id) = path.res
451            && self.fcx.tcx.trait_of_assoc(def_id).is_some()
452            && let Some(args) = self.fcx.typeck_results.borrow().node_args_opt(expr.hir_id)
453            && let self_ty = args.type_at(0)
454            && let Some(vid) = self.fcx.root_vid(self_ty)
455            && self.reachable_vids.contains(&vid)
456            && let [.., trait_segment, _method_segment] = path.segments
457            && expr.span.can_be_used_for_suggestions()
458        {
459            let span = path.span.shrink_to_lo().to(trait_segment.ident.span);
460            return ControlFlow::Break(diagnostics::SuggestAnnotation::Path(span));
461        }
462
463        // Or else, try suggesting turbofishing the method args.
464        if let hir::ExprKind::MethodCall(segment, ..) = expr.kind
465            && let Some(def_id) =
466                self.fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id)
467            && expr.span.can_be_used_for_suggestions()
468        {
469            self.suggest_for_segment(segment, def_id, expr.hir_id)?;
470        }
471
472        hir::intravisit::walk_expr(self, expr)
473    }
474
475    fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) -> Self::Result {
476        // For a local, try suggest annotating the type if it's missing.
477        if let hir::LocalSource::Normal = local.source
478            && let None = local.ty
479            && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(local.hir_id)
480            && let Some(vid) = self.fcx.root_vid(ty)
481            && self.reachable_vids.contains(&vid)
482            && local.span.can_be_used_for_suggestions()
483        {
484            return ControlFlow::Break(diagnostics::SuggestAnnotation::Local(
485                local.pat.span.shrink_to_hi(),
486            ));
487        }
488        hir::intravisit::walk_local(self, local)
489    }
490}
491
492#[derive(#[automatically_derived]
impl ::core::fmt::Debug for UnsafeUseReason {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                UnsafeUseReason::Call => "Call",
                UnsafeUseReason::Method => "Method",
                UnsafeUseReason::Path => "Path",
                UnsafeUseReason::UnionField => "UnionField",
                UnsafeUseReason::Deref => "Deref",
            })
    }
}Debug, #[automatically_derived]
impl ::core::marker::Copy for UnsafeUseReason { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for UnsafeUseReason { }
#[automatically_derived]
impl ::core::clone::Clone for UnsafeUseReason {
    #[inline]
    fn clone(&self) -> UnsafeUseReason { *self }
}Clone)]
493pub(crate) enum UnsafeUseReason {
494    Call,
495    Method,
496    Path,
497    UnionField,
498    Deref,
499}
500
501/// Finds all type variables which are passed to an `unsafe` operation.
502///
503/// For example, for this function `f`:
504/// ```ignore (demonstrative)
505/// fn f() {
506///     unsafe {
507///         let x /* ?X */ = core::mem::zeroed();
508///         //               ^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason
509///
510///         let y = core::mem::zeroed::<Option<_ /* ?Y */>>();
511///         //      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason
512///     }
513/// }
514/// ```
515///
516/// `compute_unsafe_infer_vars` will return `{ id(?X) -> (hir_id, span, Call) }`
517fn compute_unsafe_infer_vars<'a, 'tcx>(
518    fcx: &'a FnCtxt<'a, 'tcx>,
519    body_def_id: LocalDefId,
520) -> UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)> {
521    let body = fcx.tcx.hir_body_owned_by(body_def_id);
522    let mut res = UnordMap::default();
523
524    struct UnsafeInferVarsVisitor<'a, 'tcx> {
525        fcx: &'a FnCtxt<'a, 'tcx>,
526        res: &'a mut UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>,
527    }
528
529    impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_> {
530        fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) {
531            let typeck_results = self.fcx.typeck_results.borrow();
532
533            match ex.kind {
534                hir::ExprKind::MethodCall(..) => {
535                    if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id)
536                        && let method_ty =
537                            self.fcx.tcx.type_of(def_id).instantiate_identity().skip_norm_wip()
538                        && let sig = method_ty.fn_sig(self.fcx.tcx)
539                        && sig.safety().is_unsafe()
540                    {
541                        let mut collector = InferVarCollector {
542                            value: (ex.hir_id, ex.span, UnsafeUseReason::Method),
543                            res: self.res,
544                        };
545
546                        // Collect generic arguments (incl. `Self`) of the method
547                        typeck_results
548                            .node_args(ex.hir_id)
549                            .types()
550                            .for_each(|t| t.visit_with(&mut collector));
551                    }
552                }
553
554                hir::ExprKind::Call(func, ..) => {
555                    let func_ty = typeck_results.expr_ty(func);
556
557                    if func_ty.is_fn()
558                        && let sig = func_ty.fn_sig(self.fcx.tcx)
559                        && sig.safety().is_unsafe()
560                    {
561                        let mut collector = InferVarCollector {
562                            value: (ex.hir_id, ex.span, UnsafeUseReason::Call),
563                            res: self.res,
564                        };
565
566                        // Try collecting generic arguments of the function.
567                        // Note that we do this below for any paths (that don't have to be called),
568                        // but there we do it with a different span/reason.
569                        // This takes priority.
570                        typeck_results
571                            .node_args(func.hir_id)
572                            .types()
573                            .for_each(|t| t.visit_with(&mut collector));
574
575                        // Also check the return type, for cases like `returns_unsafe_fn_ptr()()`
576                        sig.output().visit_with(&mut collector);
577                    }
578                }
579
580                // Check paths which refer to functions.
581                // We do this, instead of only checking `Call` to make sure the lint can't be
582                // avoided by storing unsafe function in a variable.
583                hir::ExprKind::Path(_) => {
584                    let ty = typeck_results.expr_ty(ex);
585
586                    // If this path refers to an unsafe function, collect inference variables which may affect it.
587                    // `is_fn` excludes closures, but those can't be unsafe.
588                    if ty.is_fn()
589                        && let sig = ty.fn_sig(self.fcx.tcx)
590                        && sig.safety().is_unsafe()
591                    {
592                        let mut collector = InferVarCollector {
593                            value: (ex.hir_id, ex.span, UnsafeUseReason::Path),
594                            res: self.res,
595                        };
596
597                        // Collect generic arguments of the function
598                        typeck_results
599                            .node_args(ex.hir_id)
600                            .types()
601                            .for_each(|t| t.visit_with(&mut collector));
602                    }
603                }
604
605                hir::ExprKind::Unary(hir::UnOp::Deref, pointer) => {
606                    if let ty::RawPtr(pointee, _) = typeck_results.expr_ty(pointer).kind() {
607                        pointee.visit_with(&mut InferVarCollector {
608                            value: (ex.hir_id, ex.span, UnsafeUseReason::Deref),
609                            res: self.res,
610                        });
611                    }
612                }
613
614                hir::ExprKind::Field(base, _) => {
615                    let base_ty = typeck_results.expr_ty(base);
616
617                    if base_ty.is_union() {
618                        typeck_results.expr_ty(ex).visit_with(&mut InferVarCollector {
619                            value: (ex.hir_id, ex.span, UnsafeUseReason::UnionField),
620                            res: self.res,
621                        });
622                    }
623                }
624
625                _ => (),
626            };
627
628            hir::intravisit::walk_expr(self, ex);
629        }
630    }
631
632    struct InferVarCollector<'r, V> {
633        value: V,
634        res: &'r mut UnordMap<ty::TyVid, V>,
635    }
636
637    impl<'tcx, V: Copy> ty::TypeVisitor<TyCtxt<'tcx>> for InferVarCollector<'_, V> {
638        fn visit_ty(&mut self, t: Ty<'tcx>) {
639            if let Some(vid) = t.ty_vid() {
640                _ = self.res.try_insert(vid, self.value);
641            } else {
642                t.super_visit_with(self)
643            }
644        }
645    }
646
647    UnsafeInferVarsVisitor { fcx, res: &mut res }.visit_expr(&body.value);
648
649    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs:649",
                        "rustc_hir_typeck::fallback", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_hir_typeck/src/fallback.rs"),
                        ::tracing_core::__macro_support::Option::Some(649u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fallback"),
                        ::tracing_core::field::FieldSet::new(&["message",
                                        {
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("res")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("res");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("collected the following unsafe vars for {0:?}",
                                                    body_def_id) as &dyn ::tracing::field::Value)),
                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&res)
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(?res, "collected the following unsafe vars for {body_def_id:?}");
650
651    res
652}