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