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