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