1use std::cell::OnceCell;
2use std::ops::ControlFlow;
34use rustc_data_structures::fx::FxHashSet;
5use rustc_data_structures::graph;
6use rustc_data_structures::graph::vec_graph::VecGraph;
7use rustc_data_structures::unord::{UnordMap, UnordSet};
8use rustc_hir::attrs::DivergingFallbackBehavior;
9use rustc_hir::def::{DefKind, Res};
10use rustc_hir::def_id::DefId;
11use rustc_hir::intravisit::{InferKind, Visitor};
12use rustc_hir::{selfas hir, CRATE_HIR_ID, HirId};
13use rustc_lint::builtin::FLOAT_LITERAL_F32_FALLBACK;
14use rustc_middle::ty::{self, FloatVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
15use rustc_session::lint;
16use rustc_span::def_id::LocalDefId;
17use rustc_span::{DUMMY_SP, Span};
18use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
19use tracing::debug;
2021use crate::{FnCtxt, errors};
2223impl<'tcx> FnCtxt<'_, 'tcx> {
24/// Performs type inference fallback, setting [`FnCtxt::diverging_fallback_has_occurred`]
25 /// if the never type fallback has occurred.
26pub(super) fn type_inference_fallback(&self) {
27{
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:27",
"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(27u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("type-inference-fallback start obligations: {0:#?}",
self.fulfillment_cx.borrow_mut().pending_obligations()) as
&dyn Value))])
});
} else { ; }
};debug!(
28"type-inference-fallback start obligations: {:#?}",
29self.fulfillment_cx.borrow_mut().pending_obligations()
30 );
3132// All type checking constraints were added, try to fallback unsolved variables.
33self.select_obligations_where_possible(|_| {});
3435{
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:35",
"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(35u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("type-inference-fallback post selection obligations: {0:#?}",
self.fulfillment_cx.borrow_mut().pending_obligations()) as
&dyn Value))])
});
} else { ; }
};debug!(
36"type-inference-fallback post selection obligations: {:#?}",
37self.fulfillment_cx.borrow_mut().pending_obligations()
38 );
3940let fallback_occurred = self.fallback_types();
4142if fallback_occurred {
43// if fallback occurred, previously stalled goals may make progress again
44self.select_obligations_where_possible(|_| {});
45 }
46 }
4748fn fallback_types(&self) -> bool {
49// Check if we have any unresolved variables. If not, no need for fallback.
50let unresolved_variables = self.unresolved_variables();
5152if unresolved_variables.is_empty() {
53return false;
54 }
5556let (diverging_fallback, diverging_fallback_ty) =
57self.calculate_diverging_fallback(&unresolved_variables);
58let fallback_to_f32 = self.calculate_fallback_to_f32(&unresolved_variables);
5960// We do fallback in two passes, to try to generate
61 // better error messages.
62 // The first time, we do *not* replace opaque types.
63let mut fallback_occurred = false;
64for ty in unresolved_variables {
65{
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:65",
"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(65u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("unsolved_variable = {0:?}",
ty) as &dyn Value))])
});
} else { ; }
};debug!("unsolved_variable = {:?}", ty);
66 fallback_occurred |= self.fallback_if_possible(
67 ty,
68&diverging_fallback,
69 diverging_fallback_ty,
70&fallback_to_f32,
71 );
72 }
7374fallback_occurred75 }
7677/// Tries to apply a fallback to `ty` if it is an unsolved variable.
78 ///
79 /// - Unconstrained ints are replaced with `i32`.
80 ///
81 /// - Unconstrained floats are replaced with `f64`, except when there is a trait predicate
82 /// `f32: From<{float}>`, in which case `f32` is used as the fallback instead.
83 ///
84 /// - Non-numerics may get replaced with `()` or `!`, depending on how they
85 /// were categorized by [`Self::calculate_diverging_fallback`], crate's
86 /// edition, and the setting of `#![rustc_never_type_options(fallback = ...)]`.
87 ///
88 /// Fallback becomes very dubious if we have encountered
89 /// type-checking errors. In that case, fallback to Error.
90 ///
91 /// Sets [`FnCtxt::diverging_fallback_has_occurred`] if never type fallback
92 /// is performed during this call.
93fn fallback_if_possible(
94&self,
95 ty: Ty<'tcx>,
96 diverging_fallback: &UnordSet<Ty<'tcx>>,
97 diverging_fallback_ty: Ty<'tcx>,
98 fallback_to_f32: &UnordSet<FloatVid>,
99 ) -> bool {
100// Careful: we do NOT shallow-resolve `ty`. We know that `ty`
101 // is an unsolved variable, and we determine its fallback
102 // based solely on how it was created, not what other type
103 // variables it may have been unified with since then.
104 //
105 // The reason this matters is that other attempts at fallback
106 // may (in principle) conflict with this fallback, and we wish
107 // to generate a type error in that case. (However, this
108 // actually isn't true right now, because we're only using the
109 // builtin fallback rules. This would be true if we were using
110 // user-supplied fallbacks. But it's still useful to write the
111 // code to detect bugs.)
112 //
113 // (Note though that if we have a general type variable `?T`
114 // that is then unified with an integer type variable `?I`
115 // that ultimately never gets resolved to a special integral
116 // type, `?T` is not considered unsolved, but `?I` is. The
117 // same is true for float variables.)
118let fallback = match ty.kind() {
119_ if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx, e),
120 ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
121 ty::Infer(ty::FloatVar(vid)) if fallback_to_f32.contains(vid) => self.tcx.types.f32,
122 ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
123_ if diverging_fallback.contains(&ty) => {
124self.diverging_fallback_has_occurred.set(true);
125diverging_fallback_ty126 }
127_ => return false,
128 };
129{
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:129",
"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(129u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("fallback_if_possible(ty={0:?}): defaulting to `{1:?}`",
ty, fallback) as &dyn Value))])
});
} else { ; }
};debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
130131let span = ty.ty_vid().map_or(DUMMY_SP, |vid| self.infcx.type_var_origin(vid).span);
132self.demand_eqtype(span, ty, fallback);
133true
134}
135136/// Existing code relies on `f32: From<T>` (usually written as `T: Into<f32>`) resolving `T` to
137 /// `f32` when the type of `T` is inferred from an unsuffixed float literal. Using the default
138 /// fallback of `f64`, this would break when adding `impl From<f16> for f32`, as there are now
139 /// two float type which could be `T`, meaning that the fallback of `f64` would be used and
140 /// compilation error would occur as `f32` does not implement `From<f64>`. To avoid breaking
141 /// existing code, we instead fallback `T` to `f32` when there is a trait predicate
142 /// `f32: From<T>`. This means code like the following will continue to compile:
143 ///
144 /// ```rust
145 /// fn foo<T: Into<f32>>(_: T) {}
146 ///
147 /// foo(1.0);
148 /// ```
149fn calculate_fallback_to_f32(&self, unresolved_variables: &[Ty<'tcx>]) -> UnordSet<FloatVid> {
150// Short-circuit: if no unresolved variable is a float, no f32 fallback can apply,
151 // so we can skip the (potentially very expensive) work in `from_float_for_f32_root_vids`.
152 // Under the new solver, that function walks `visit_proof_tree` for every pending
153 // obligation, which is O(N × proof_tree_size) and can dominate type-checking on crates
154 // with many large pending obligations and no f32 involvement.
155if unresolved_variables.iter().all(|ty| ty.float_vid().is_none()) {
156return UnordSet::new();
157 }
158let roots: UnordSet<ty::FloatVid> = self.from_float_for_f32_root_vids();
159if roots.is_empty() {
160// Most functions have no `f32: From<{float}>` predicates, so short-circuit and return
161 // an empty set when this is the case.
162return UnordSet::new();
163 }
164// Calculate all the unresolved variables that need to fallback to `f32` here. This ensures
165 // we don't need to find root variables in `fallback_if_possible`: see the comment at the
166 // top of that function for details.
167let fallback_to_f32 = unresolved_variables168 .iter()
169 .flat_map(|ty| ty.float_vid())
170 .filter(|vid| roots.contains(&self.root_float_var(*vid)))
171 .inspect(|vid| {
172let origin = self.float_var_origin(*vid);
173// Show the entire literal in the suggestion to make it clearer.
174let mut literal = self.tcx.sess.source_map().span_to_snippet(origin.span).ok();
175// A `.` at the end of the literal is no longer necessary if `f32` is explicitly specified
176if let Some(ref mut literal) = literal177 && literal.ends_with('.')
178 {
179literal.pop();
180 }
181self.tcx.emit_node_span_lint(
182FLOAT_LITERAL_F32_FALLBACK,
183origin.lint_id.unwrap_or(CRATE_HIR_ID),
184origin.span,
185 errors::FloatLiteralF32Fallback {
186 span: literal.as_ref().map(|_| origin.span),
187 literal: literal.unwrap_or_default(),
188 },
189 );
190 })
191 .collect();
192{
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:192",
"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(192u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("calculate_fallback_to_f32: fallback_to_f32={0:?}",
fallback_to_f32) as &dyn Value))])
});
} else { ; }
};debug!("calculate_fallback_to_f32: fallback_to_f32={:?}", fallback_to_f32);
193fallback_to_f32194 }
195196fn calculate_diverging_fallback(
197&self,
198 unresolved_variables: &[Ty<'tcx>],
199 ) -> (UnordSet<Ty<'tcx>>, Ty<'tcx>) {
200{
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:200",
"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(200u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("calculate_diverging_fallback({0:?})",
unresolved_variables) as &dyn Value))])
});
} else { ; }
};debug!("calculate_diverging_fallback({:?})", unresolved_variables);
201202let diverging_fallback_ty = match self.diverging_fallback_behavior {
203 DivergingFallbackBehavior::ToUnit => self.tcx.types.unit,
204 DivergingFallbackBehavior::ToNever => self.tcx.types.never,
205 DivergingFallbackBehavior::NoFallback => {
206// the type doesn't matter, since no fallback will occur
207return (UnordSet::new(), self.tcx.types.unit);
208 }
209 };
210211// Construct a coercion graph where an edge `A -> B` indicates
212 // a type variable is that is coerced
213let coercion_graph = self.create_coercion_graph();
214215// Extract the unsolved type inference variable vids; note that some
216 // unsolved variables are integer/float variables and are excluded.
217let unsolved_vids = unresolved_variables.iter().filter_map(|ty| ty.ty_vid());
218219// Compute the diverging root vids D -- that is, the root vid of
220 // those type variables that (a) are the target of a coercion from
221 // a `!` type and (b) have not yet been solved.
222 //
223 // These variables are the ones that are targets for fallback to
224 // either `!` or `()`.
225let diverging_roots: UnordSet<ty::TyVid> = self226 .diverging_type_vars
227 .borrow()
228 .iter()
229 .map(|&ty_id| self.shallow_resolve(Ty::new_var(self.tcx, ty_id)))
230 .filter_map(|ty| ty.ty_vid())
231 .map(|vid| self.root_var(vid))
232 .collect();
233{
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:233",
"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(233u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("calculate_diverging_fallback: diverging_type_vars={0:?}",
self.diverging_type_vars.borrow()) as &dyn Value))])
});
} else { ; }
};debug!(
234"calculate_diverging_fallback: diverging_type_vars={:?}",
235self.diverging_type_vars.borrow()
236 );
237{
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:237",
"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(237u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("calculate_diverging_fallback: diverging_roots={0:?}",
diverging_roots) as &dyn Value))])
});
} else { ; }
};debug!("calculate_diverging_fallback: diverging_roots={:?}", diverging_roots);
238239// Find all type variables that are reachable from a diverging
240 // type variable. These will typically default to `!`, unless
241 // we find later that they are *also* reachable from some
242 // other type variable outside this set.
243let mut diverging_vids = ::alloc::vec::Vec::new()vec![];
244for unsolved_vid in unsolved_vids {
245let root_vid = self.root_var(unsolved_vid);
246{
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:246",
"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(246u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("calculate_diverging_fallback: unsolved_vid={0:?} root_vid={1:?} diverges={2:?}",
unsolved_vid, root_vid, diverging_roots.contains(&root_vid))
as &dyn Value))])
});
} else { ; }
};debug!(
247"calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}",
248 unsolved_vid,
249 root_vid,
250 diverging_roots.contains(&root_vid),
251 );
252if diverging_roots.contains(&root_vid) {
253 diverging_vids.push(unsolved_vid);
254255{
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:255",
"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(255u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("calculate_diverging_fallback: root_vid={0:?} reaches {1:?}",
root_vid,
graph::depth_first_search(&coercion_graph,
root_vid).collect::<Vec<_>>()) as &dyn Value))])
});
} else { ; }
};debug!(
256"calculate_diverging_fallback: root_vid={:?} reaches {:?}",
257 root_vid,
258 graph::depth_first_search(&coercion_graph, root_vid).collect::<Vec<_>>()
259 );
260 }
261 }
262263{
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:263",
"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(263u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("obligations: {0:#?}",
self.fulfillment_cx.borrow_mut().pending_obligations()) as
&dyn Value))])
});
} else { ; }
};debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations());
264265let mut diverging_fallback = UnordSet::with_capacity(diverging_vids.len());
266let unsafe_infer_vars = OnceCell::new();
267268self.lint_obligations_broken_by_never_type_fallback_change(
269&diverging_vids,
270&coercion_graph,
271 );
272273for &diverging_vid in &diverging_vids {
274let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
275let root_vid = self.root_var(diverging_vid);
276277self.lint_never_type_fallback_flowing_into_unsafe_code(
278&unsafe_infer_vars,
279&coercion_graph,
280 root_vid,
281 );
282283 diverging_fallback.insert(diverging_ty);
284 }
285286 (diverging_fallback, diverging_fallback_ty)
287 }
288289fn lint_never_type_fallback_flowing_into_unsafe_code(
290&self,
291 unsafe_infer_vars: &OnceCell<UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>>,
292 coercion_graph: &VecGraph<ty::TyVid, true>,
293 root_vid: ty::TyVid,
294 ) {
295let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| {
296let unsafe_infer_vars = compute_unsafe_infer_vars(self, self.body_id);
297{
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:297",
"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(297u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fallback"),
::tracing_core::field::FieldSet::new(&["unsafe_infer_vars"],
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&unsafe_infer_vars)
as &dyn Value))])
});
} else { ; }
};debug!(?unsafe_infer_vars);
298unsafe_infer_vars299 });
300301let affected_unsafe_infer_vars =
302 graph::depth_first_search_as_undirected(&coercion_graph, root_vid)
303 .filter_map(|x| unsafe_infer_vars.get(&x).copied())
304 .collect::<Vec<_>>();
305306let sugg = self.try_to_suggest_annotations(&[root_vid], coercion_graph);
307308for (hir_id, span, reason) in affected_unsafe_infer_vars {
309self.tcx.emit_node_span_lint(
310 lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE,
311 hir_id,
312 span,
313match reason {
314 UnsafeUseReason::Call => {
315 errors::NeverTypeFallbackFlowingIntoUnsafe::Call { sugg: sugg.clone() }
316 }
317 UnsafeUseReason::Method => {
318 errors::NeverTypeFallbackFlowingIntoUnsafe::Method { sugg: sugg.clone() }
319 }
320 UnsafeUseReason::Path => {
321 errors::NeverTypeFallbackFlowingIntoUnsafe::Path { sugg: sugg.clone() }
322 }
323 UnsafeUseReason::UnionField => {
324 errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField {
325 sugg: sugg.clone(),
326 }
327 }
328 UnsafeUseReason::Deref => {
329 errors::NeverTypeFallbackFlowingIntoUnsafe::Deref { sugg: sugg.clone() }
330 }
331 },
332 );
333 }
334 }
335336fn lint_obligations_broken_by_never_type_fallback_change(
337&self,
338 diverging_vids: &[ty::TyVid],
339 coercions: &VecGraph<ty::TyVid, true>,
340 ) {
341let DivergingFallbackBehavior::ToUnit = self.diverging_fallback_behavior else { return };
342343// Fallback happens if and only if there are diverging variables
344if diverging_vids.is_empty() {
345return;
346 }
347348// Returns errors which happen if fallback is set to `fallback`
349let remaining_errors_if_fallback_to = |fallback| {
350self.probe(|_| {
351let obligations = self.fulfillment_cx.borrow().pending_obligations();
352let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
353ocx.register_obligations(obligations.iter().cloned());
354355for &diverging_vid in diverging_vids {
356let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
357358 ocx.eq(&ObligationCause::dummy(), self.param_env, diverging_ty, fallback)
359 .expect("expected diverging var to be unconstrained");
360 }
361362ocx.try_evaluate_obligations()
363 })
364 };
365366// If we have no errors with `fallback = ()`, but *do* have errors with `fallback = !`,
367 // then this code will be broken by the never type fallback change.
368let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit);
369if unit_errors.is_empty()
370 && let mut never_errors = remaining_errors_if_fallback_to(self.tcx.types.never)
371 && let [never_error, ..] = never_errors.as_mut_slice()
372 {
373self.adjust_fulfillment_error_for_expr_obligation(never_error);
374let sugg = self.try_to_suggest_annotations(diverging_vids, coercions);
375self.tcx.emit_node_span_lint(
376 lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
377self.tcx.local_def_id_to_hir_id(self.body_id),
378self.tcx.def_span(self.body_id),
379 errors::DependencyOnUnitNeverTypeFallback {
380 obligation_span: never_error.obligation.cause.span,
381 obligation: never_error.obligation.predicate,
382sugg,
383 },
384 )
385 }
386 }
387388/// Returns a graph whose nodes are (unresolved) inference variables and where
389 /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
390fn create_coercion_graph(&self) -> VecGraph<ty::TyVid, true> {
391let pending_obligations = self.fulfillment_cx.borrow_mut().pending_obligations();
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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("create_coercion_graph: pending_obligations={0:?}",
pending_obligations) as &dyn Value))])
});
} else { ; }
};debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations);
393let coercion_edges: Vec<(ty::TyVid, ty::TyVid)> = pending_obligations394 .into_iter()
395 .filter_map(|obligation| {
396// The predicates we are looking for look like `Coerce(?A -> ?B)`.
397 // They will have no bound variables.
398obligation.predicate.kind().no_bound_vars()
399 })
400 .filter_map(|atom| {
401// We consider both subtyping and coercion to imply 'flow' from
402 // some position in the code `a` to a different position `b`.
403 // This is then used to determine which variables interact with
404 // live code, and as such must fall back to `()` to preserve
405 // soundness.
406 //
407 // In practice currently the two ways that this happens is
408 // coercion and subtyping.
409let (a, b) = match atom {
410 ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
411 ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
412 (a, b)
413 }
414_ => return None,
415 };
416417let a_vid = self.root_vid(a)?;
418let b_vid = self.root_vid(b)?;
419Some((a_vid, b_vid))
420 })
421 .collect();
422{
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:422",
"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(422u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("create_coercion_graph: coercion_edges={0:?}",
coercion_edges) as &dyn Value))])
});
} else { ; }
};debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges);
423let num_ty_vars = self.num_ty_vars();
424425VecGraph::new(num_ty_vars, coercion_edges)
426 }
427428/// If `ty` is an unresolved type variable, returns its root vid.
429fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> {
430Some(self.root_var(self.shallow_resolve(ty).ty_vid()?))
431 }
432433/// If `ty` is an unresolved float type variable, returns its root vid.
434pub(crate) fn root_float_vid(&self, ty: Ty<'tcx>) -> Option<ty::FloatVid> {
435Some(self.root_float_var(self.shallow_resolve(ty).float_vid()?))
436 }
437438/// Given a set of diverging vids and coercions, walk the HIR to gather a
439 /// set of suggestions which can be applied to preserve fallback to unit.
440fn try_to_suggest_annotations(
441&self,
442 diverging_vids: &[ty::TyVid],
443 coercions: &VecGraph<ty::TyVid, true>,
444 ) -> errors::SuggestAnnotations {
445let body =
446self.tcx.hir_maybe_body_owned_by(self.body_id).expect("body id must have an owner");
447// For each diverging var, look through the HIR for a place to give it
448 // a type annotation. We do this per var because we only really need one
449 // suggestion to influence a var to be `()`.
450let suggestions = diverging_vids451 .iter()
452 .copied()
453 .filter_map(|vid| {
454let reachable_vids =
455 graph::depth_first_search_as_undirected(coercions, vid).collect();
456AnnotateUnitFallbackVisitor { reachable_vids, fcx: self }
457 .visit_expr(body.value)
458 .break_value()
459 })
460 .collect();
461 errors::SuggestAnnotations { suggestions }
462 }
463}
464465/// Try to walk the HIR to find a place to insert a useful suggestion
466/// to preserve fallback to `()` in 2024.
467struct AnnotateUnitFallbackVisitor<'a, 'tcx> {
468 reachable_vids: FxHashSet<ty::TyVid>,
469 fcx: &'a FnCtxt<'a, 'tcx>,
470}
471impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> {
472// For a given path segment, if it's missing a turbofish, try to suggest adding
473 // one so we can constrain an argument to `()`. To keep the suggestion simple,
474 // we want to simply suggest `_` for all the other args. This (for now) only
475 // works when there are only type variables (and region variables, since we can
476 // elide them)...
477fn suggest_for_segment(
478&self,
479 arg_segment: &'tcx hir::PathSegment<'tcx>,
480 def_id: DefId,
481 id: HirId,
482 ) -> ControlFlow<errors::SuggestAnnotation> {
483if arg_segment.args.is_none()
484 && let Some(all_args) = self.fcx.typeck_results.borrow().node_args_opt(id)
485 && let generics = self.fcx.tcx.generics_of(def_id)
486 && let args = all_args[generics.parent_count..].iter().zip(&generics.own_params)
487// We can't turbofish consts :(
488 && 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))
489 {
490// We filter out APITs, which are not turbofished.
491let non_apit_type_args = args.filter(|(_, param)| {
492#[allow(non_exhaustive_omitted_patterns)] match param.kind {
ty::GenericParamDefKind::Type { synthetic: false, .. } => true,
_ => false,
}matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: false, .. })493 });
494let n_tys = non_apit_type_args.clone().count();
495for (idx, (arg, _)) in non_apit_type_args.enumerate() {
496if let Some(ty) = arg.as_type()
497 && let Some(vid) = self.fcx.root_vid(ty)
498 && self.reachable_vids.contains(&vid)
499 {
500return ControlFlow::Break(errors::SuggestAnnotation::Turbo(
501 arg_segment.ident.span.shrink_to_hi(),
502 n_tys,
503 idx,
504 ));
505 }
506 }
507 }
508 ControlFlow::Continue(())
509 }
510}
511impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> {
512type Result = ControlFlow<errors::SuggestAnnotation>;
513514fn visit_infer(
515&mut self,
516 inf_id: HirId,
517 inf_span: Span,
518 _kind: InferKind<'tcx>,
519 ) -> Self::Result {
520// Try to replace `_` with `()`.
521if let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(inf_id)
522 && let Some(vid) = self.fcx.root_vid(ty)
523 && self.reachable_vids.contains(&vid)
524 && inf_span.can_be_used_for_suggestions()
525 {
526return ControlFlow::Break(errors::SuggestAnnotation::Unit(inf_span));
527 }
528529 ControlFlow::Continue(())
530 }
531532fn visit_qpath(
533&mut self,
534 qpath: &'tcx rustc_hir::QPath<'tcx>,
535 id: HirId,
536 span: Span,
537 ) -> Self::Result {
538let arg_segment = match qpath {
539 hir::QPath::Resolved(_, path) => {
540path.segments.last().expect("paths should have a segment")
541 }
542 hir::QPath::TypeRelative(_, segment) => segment,
543 };
544// Alternatively, try to turbofish `::<_, (), _>`.
545if let Some(def_id) = self.fcx.typeck_results.borrow().qpath_res(qpath, id).opt_def_id()
546 && span.can_be_used_for_suggestions()
547 {
548self.suggest_for_segment(arg_segment, def_id, id)?;
549 }
550 hir::intravisit::walk_qpath(self, qpath, id)
551 }
552553fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
554if let hir::ExprKind::Closure(&hir::Closure { body, .. })
555 | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) = expr.kind
556 {
557self.visit_body(self.fcx.tcx.hir_body(body))?;
558 }
559560// Try to suggest adding an explicit qself `()` to a trait method path.
561 // i.e. changing `Default::default()` to `<() as Default>::default()`.
562if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
563 && let Res::Def(DefKind::AssocFn, def_id) = path.res
564 && self.fcx.tcx.trait_of_assoc(def_id).is_some()
565 && let Some(args) = self.fcx.typeck_results.borrow().node_args_opt(expr.hir_id)
566 && let self_ty = args.type_at(0)
567 && let Some(vid) = self.fcx.root_vid(self_ty)
568 && self.reachable_vids.contains(&vid)
569 && let [.., trait_segment, _method_segment] = path.segments
570 && expr.span.can_be_used_for_suggestions()
571 {
572let span = path.span.shrink_to_lo().to(trait_segment.ident.span);
573return ControlFlow::Break(errors::SuggestAnnotation::Path(span));
574 }
575576// Or else, try suggesting turbofishing the method args.
577if let hir::ExprKind::MethodCall(segment, ..) = expr.kind
578 && let Some(def_id) =
579self.fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id)
580 && expr.span.can_be_used_for_suggestions()
581 {
582self.suggest_for_segment(segment, def_id, expr.hir_id)?;
583 }
584585 hir::intravisit::walk_expr(self, expr)
586 }
587588fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) -> Self::Result {
589// For a local, try suggest annotating the type if it's missing.
590if let hir::LocalSource::Normal = local.source
591 && let None = local.ty
592 && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(local.hir_id)
593 && let Some(vid) = self.fcx.root_vid(ty)
594 && self.reachable_vids.contains(&vid)
595 && local.span.can_be_used_for_suggestions()
596 {
597return ControlFlow::Break(errors::SuggestAnnotation::Local(
598local.pat.span.shrink_to_hi(),
599 ));
600 }
601 hir::intravisit::walk_local(self, local)
602 }
603}
604605#[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)]
606pub(crate) enum UnsafeUseReason {
607 Call,
608 Method,
609 Path,
610 UnionField,
611 Deref,
612}
613614/// Finds all type variables which are passed to an `unsafe` operation.
615///
616/// For example, for this function `f`:
617/// ```ignore (demonstrative)
618/// fn f() {
619/// unsafe {
620/// let x /* ?X */ = core::mem::zeroed();
621/// // ^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason
622///
623/// let y = core::mem::zeroed::<Option<_ /* ?Y */>>();
624/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason
625/// }
626/// }
627/// ```
628///
629/// `compute_unsafe_infer_vars` will return `{ id(?X) -> (hir_id, span, Call) }`
630fn compute_unsafe_infer_vars<'a, 'tcx>(
631 fcx: &'a FnCtxt<'a, 'tcx>,
632 body_id: LocalDefId,
633) -> UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)> {
634let body = fcx.tcx.hir_maybe_body_owned_by(body_id).expect("body id must have an owner");
635let mut res = UnordMap::default();
636637struct UnsafeInferVarsVisitor<'a, 'tcx> {
638 fcx: &'a FnCtxt<'a, 'tcx>,
639 res: &'a mut UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>,
640 }
641642impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_> {
643fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) {
644let typeck_results = self.fcx.typeck_results.borrow();
645646match ex.kind {
647 hir::ExprKind::MethodCall(..) => {
648if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id)
649 && let method_ty =
650self.fcx.tcx.type_of(def_id).instantiate_identity().skip_norm_wip()
651 && let sig = method_ty.fn_sig(self.fcx.tcx)
652 && sig.safety().is_unsafe()
653 {
654let mut collector = InferVarCollector {
655 value: (ex.hir_id, ex.span, UnsafeUseReason::Method),
656 res: self.res,
657 };
658659// Collect generic arguments (incl. `Self`) of the method
660typeck_results661 .node_args(ex.hir_id)
662 .types()
663 .for_each(|t| t.visit_with(&mut collector));
664 }
665 }
666667 hir::ExprKind::Call(func, ..) => {
668let func_ty = typeck_results.expr_ty(func);
669670if func_ty.is_fn()
671 && let sig = func_ty.fn_sig(self.fcx.tcx)
672 && sig.safety().is_unsafe()
673 {
674let mut collector = InferVarCollector {
675 value: (ex.hir_id, ex.span, UnsafeUseReason::Call),
676 res: self.res,
677 };
678679// Try collecting generic arguments of the function.
680 // Note that we do this below for any paths (that don't have to be called),
681 // but there we do it with a different span/reason.
682 // This takes priority.
683typeck_results684 .node_args(func.hir_id)
685 .types()
686 .for_each(|t| t.visit_with(&mut collector));
687688// Also check the return type, for cases like `returns_unsafe_fn_ptr()()`
689sig.output().visit_with(&mut collector);
690 }
691 }
692693// Check paths which refer to functions.
694 // We do this, instead of only checking `Call` to make sure the lint can't be
695 // avoided by storing unsafe function in a variable.
696hir::ExprKind::Path(_) => {
697let ty = typeck_results.expr_ty(ex);
698699// If this path refers to an unsafe function, collect inference variables which may affect it.
700 // `is_fn` excludes closures, but those can't be unsafe.
701if ty.is_fn()
702 && let sig = ty.fn_sig(self.fcx.tcx)
703 && sig.safety().is_unsafe()
704 {
705let mut collector = InferVarCollector {
706 value: (ex.hir_id, ex.span, UnsafeUseReason::Path),
707 res: self.res,
708 };
709710// Collect generic arguments of the function
711typeck_results712 .node_args(ex.hir_id)
713 .types()
714 .for_each(|t| t.visit_with(&mut collector));
715 }
716 }
717718 hir::ExprKind::Unary(hir::UnOp::Deref, pointer) => {
719if let ty::RawPtr(pointee, _) = typeck_results.expr_ty(pointer).kind() {
720pointee.visit_with(&mut InferVarCollector {
721 value: (ex.hir_id, ex.span, UnsafeUseReason::Deref),
722 res: self.res,
723 });
724 }
725 }
726727 hir::ExprKind::Field(base, _) => {
728let base_ty = typeck_results.expr_ty(base);
729730if base_ty.is_union() {
731typeck_results.expr_ty(ex).visit_with(&mut InferVarCollector {
732 value: (ex.hir_id, ex.span, UnsafeUseReason::UnionField),
733 res: self.res,
734 });
735 }
736 }
737738_ => (),
739 };
740741 hir::intravisit::walk_expr(self, ex);
742 }
743 }
744745struct InferVarCollector<'r, V> {
746 value: V,
747 res: &'r mut UnordMap<ty::TyVid, V>,
748 }
749750impl<'tcx, V: Copy> ty::TypeVisitor<TyCtxt<'tcx>> for InferVarCollector<'_, V> {
751fn visit_ty(&mut self, t: Ty<'tcx>) {
752if let Some(vid) = t.ty_vid() {
753_ = self.res.try_insert(vid, self.value);
754 } else {
755t.super_visit_with(self)
756 }
757 }
758 }
759760UnsafeInferVarsVisitor { fcx, res: &mut res }.visit_expr(&body.value);
761762{
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:762",
"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(762u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fallback"),
::tracing_core::field::FieldSet::new(&["message", "res"],
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("collected the following unsafe vars for {0:?}",
body_id) as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&res) as
&dyn Value))])
});
} else { ; }
};debug!(?res, "collected the following unsafe vars for {body_id:?}");
763764res765}