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