1use std::cell::OnceCell;
2use std::ops::ControlFlow;
3
4use rustc_data_structures::fx::FxHashSet;
5use rustc_data_structures::graph::vec_graph::VecGraph;
6use rustc_data_structures::graph::{self};
7use rustc_data_structures::unord::{UnordMap, UnordSet};
8use rustc_hir as hir;
9use rustc_hir::HirId;
10use rustc_hir::def::{DefKind, Res};
11use rustc_hir::def_id::DefId;
12use rustc_hir::intravisit::{InferKind, Visitor};
13use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
14use rustc_session::lint;
15use rustc_span::def_id::LocalDefId;
16use rustc_span::{DUMMY_SP, Span};
17use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
18use tracing::debug;
19
20use crate::{FnCtxt, errors};
21
22#[derive(Copy, Clone)]
23pub(crate) enum DivergingFallbackBehavior {
24 ToUnit,
26 ToNever,
29 NoFallback,
31}
32
33impl<'tcx> FnCtxt<'_, 'tcx> {
34 pub(super) fn type_inference_fallback(&self) {
37 debug!(
38 "type-inference-fallback start obligations: {:#?}",
39 self.fulfillment_cx.borrow_mut().pending_obligations()
40 );
41
42 self.select_obligations_where_possible(|_| {});
44
45 debug!(
46 "type-inference-fallback post selection obligations: {:#?}",
47 self.fulfillment_cx.borrow_mut().pending_obligations()
48 );
49
50 let fallback_occurred = self.fallback_types();
51
52 if fallback_occurred {
53 self.select_obligations_where_possible(|_| {});
55 }
56 }
57
58 fn fallback_types(&self) -> bool {
59 let unresolved_variables = self.unresolved_variables();
61
62 if unresolved_variables.is_empty() {
63 return false;
64 }
65
66 let (diverging_fallback, diverging_fallback_ty) =
67 self.calculate_diverging_fallback(&unresolved_variables);
68
69 let mut fallback_occurred = false;
73 for ty in unresolved_variables {
74 debug!("unsolved_variable = {:?}", ty);
75 fallback_occurred |=
76 self.fallback_if_possible(ty, &diverging_fallback, diverging_fallback_ty);
77 }
78
79 fallback_occurred
80 }
81
82 fn fallback_if_possible(
98 &self,
99 ty: Ty<'tcx>,
100 diverging_fallback: &UnordSet<Ty<'tcx>>,
101 diverging_fallback_ty: Ty<'tcx>,
102 ) -> bool {
103 let fallback = match ty.kind() {
122 _ if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx, e),
123 ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
124 ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
125 _ if diverging_fallback.contains(&ty) => {
126 self.diverging_fallback_has_occurred.set(true);
127 diverging_fallback_ty
128 }
129 _ => return false,
130 };
131 debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
132
133 let span = ty.ty_vid().map_or(DUMMY_SP, |vid| self.infcx.type_var_origin(vid).span);
134 self.demand_eqtype(span, ty, fallback);
135 true
136 }
137
138 fn calculate_diverging_fallback(
139 &self,
140 unresolved_variables: &[Ty<'tcx>],
141 ) -> (UnordSet<Ty<'tcx>>, Ty<'tcx>) {
142 debug!("calculate_diverging_fallback({:?})", unresolved_variables);
143
144 let diverging_fallback_ty = match self.diverging_fallback_behavior {
145 DivergingFallbackBehavior::ToUnit => self.tcx.types.unit,
146 DivergingFallbackBehavior::ToNever => self.tcx.types.never,
147 DivergingFallbackBehavior::NoFallback => {
148 return (UnordSet::new(), self.tcx.types.unit);
150 }
151 };
152
153 let coercion_graph = self.create_coercion_graph();
156
157 let unsolved_vids = unresolved_variables.iter().filter_map(|ty| ty.ty_vid());
160
161 let diverging_roots: UnordSet<ty::TyVid> = self
168 .diverging_type_vars
169 .borrow()
170 .items()
171 .map(|&ty| self.shallow_resolve(ty))
172 .filter_map(|ty| ty.ty_vid())
173 .map(|vid| self.root_var(vid))
174 .collect();
175 debug!(
176 "calculate_diverging_fallback: diverging_type_vars={:?}",
177 self.diverging_type_vars.borrow()
178 );
179 debug!("calculate_diverging_fallback: diverging_roots={:?}", diverging_roots);
180
181 let mut diverging_vids = vec![];
186 for unsolved_vid in unsolved_vids {
187 let root_vid = self.root_var(unsolved_vid);
188 debug!(
189 "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}",
190 unsolved_vid,
191 root_vid,
192 diverging_roots.contains(&root_vid),
193 );
194 if diverging_roots.contains(&root_vid) {
195 diverging_vids.push(unsolved_vid);
196
197 debug!(
198 "calculate_diverging_fallback: root_vid={:?} reaches {:?}",
199 root_vid,
200 graph::depth_first_search(&coercion_graph, root_vid).collect::<Vec<_>>()
201 );
202 }
203 }
204
205 debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations());
206
207 let mut diverging_fallback = UnordSet::with_capacity(diverging_vids.len());
208 let unsafe_infer_vars = OnceCell::new();
209
210 self.lint_obligations_broken_by_never_type_fallback_change(
211 &diverging_vids,
212 &coercion_graph,
213 );
214
215 for &diverging_vid in &diverging_vids {
216 let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
217 let root_vid = self.root_var(diverging_vid);
218
219 self.lint_never_type_fallback_flowing_into_unsafe_code(
220 &unsafe_infer_vars,
221 &coercion_graph,
222 root_vid,
223 );
224
225 diverging_fallback.insert(diverging_ty);
226 }
227
228 (diverging_fallback, diverging_fallback_ty)
229 }
230
231 fn lint_never_type_fallback_flowing_into_unsafe_code(
232 &self,
233 unsafe_infer_vars: &OnceCell<UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>>,
234 coercion_graph: &VecGraph<ty::TyVid, true>,
235 root_vid: ty::TyVid,
236 ) {
237 let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| {
238 let unsafe_infer_vars = compute_unsafe_infer_vars(self, self.body_id);
239 debug!(?unsafe_infer_vars);
240 unsafe_infer_vars
241 });
242
243 let affected_unsafe_infer_vars =
244 graph::depth_first_search_as_undirected(&coercion_graph, root_vid)
245 .filter_map(|x| unsafe_infer_vars.get(&x).copied())
246 .collect::<Vec<_>>();
247
248 let sugg = self.try_to_suggest_annotations(&[root_vid], coercion_graph);
249
250 for (hir_id, span, reason) in affected_unsafe_infer_vars {
251 self.tcx.emit_node_span_lint(
252 lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE,
253 hir_id,
254 span,
255 match reason {
256 UnsafeUseReason::Call => {
257 errors::NeverTypeFallbackFlowingIntoUnsafe::Call { sugg: sugg.clone() }
258 }
259 UnsafeUseReason::Method => {
260 errors::NeverTypeFallbackFlowingIntoUnsafe::Method { sugg: sugg.clone() }
261 }
262 UnsafeUseReason::Path => {
263 errors::NeverTypeFallbackFlowingIntoUnsafe::Path { sugg: sugg.clone() }
264 }
265 UnsafeUseReason::UnionField => {
266 errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField {
267 sugg: sugg.clone(),
268 }
269 }
270 UnsafeUseReason::Deref => {
271 errors::NeverTypeFallbackFlowingIntoUnsafe::Deref { sugg: sugg.clone() }
272 }
273 },
274 );
275 }
276 }
277
278 fn lint_obligations_broken_by_never_type_fallback_change(
279 &self,
280 diverging_vids: &[ty::TyVid],
281 coercions: &VecGraph<ty::TyVid, true>,
282 ) {
283 let DivergingFallbackBehavior::ToUnit = self.diverging_fallback_behavior else { return };
284
285 if diverging_vids.is_empty() {
287 return;
288 }
289
290 let remaining_errors_if_fallback_to = |fallback| {
292 self.probe(|_| {
293 let obligations = self.fulfillment_cx.borrow().pending_obligations();
294 let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
295 ocx.register_obligations(obligations.iter().cloned());
296
297 for &diverging_vid in diverging_vids {
298 let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
299
300 ocx.eq(&ObligationCause::dummy(), self.param_env, diverging_ty, fallback)
301 .expect("expected diverging var to be unconstrained");
302 }
303
304 ocx.try_evaluate_obligations()
305 })
306 };
307
308 let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit);
311 if unit_errors.is_empty()
312 && let mut never_errors = remaining_errors_if_fallback_to(self.tcx.types.never)
313 && let [never_error, ..] = never_errors.as_mut_slice()
314 {
315 self.adjust_fulfillment_error_for_expr_obligation(never_error);
316 let sugg = self.try_to_suggest_annotations(diverging_vids, coercions);
317 self.tcx.emit_node_span_lint(
318 lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
319 self.tcx.local_def_id_to_hir_id(self.body_id),
320 self.tcx.def_span(self.body_id),
321 errors::DependencyOnUnitNeverTypeFallback {
322 obligation_span: never_error.obligation.cause.span,
323 obligation: never_error.obligation.predicate,
324 sugg,
325 },
326 )
327 }
328 }
329
330 fn create_coercion_graph(&self) -> VecGraph<ty::TyVid, true> {
333 let pending_obligations = self.fulfillment_cx.borrow_mut().pending_obligations();
334 debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations);
335 let coercion_edges: Vec<(ty::TyVid, ty::TyVid)> = pending_obligations
336 .into_iter()
337 .filter_map(|obligation| {
338 obligation.predicate.kind().no_bound_vars()
341 })
342 .filter_map(|atom| {
343 let (a, b) = match atom {
352 ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
353 ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
354 (a, b)
355 }
356 _ => return None,
357 };
358
359 let a_vid = self.root_vid(a)?;
360 let b_vid = self.root_vid(b)?;
361 Some((a_vid, b_vid))
362 })
363 .collect();
364 debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges);
365 let num_ty_vars = self.num_ty_vars();
366
367 VecGraph::new(num_ty_vars, coercion_edges)
368 }
369
370 fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> {
372 Some(self.root_var(self.shallow_resolve(ty).ty_vid()?))
373 }
374
375 fn try_to_suggest_annotations(
378 &self,
379 diverging_vids: &[ty::TyVid],
380 coercions: &VecGraph<ty::TyVid, true>,
381 ) -> errors::SuggestAnnotations {
382 let body =
383 self.tcx.hir_maybe_body_owned_by(self.body_id).expect("body id must have an owner");
384 let suggestions = diverging_vids
388 .iter()
389 .copied()
390 .filter_map(|vid| {
391 let reachable_vids =
392 graph::depth_first_search_as_undirected(coercions, vid).collect();
393 AnnotateUnitFallbackVisitor { reachable_vids, fcx: self }
394 .visit_expr(body.value)
395 .break_value()
396 })
397 .collect();
398 errors::SuggestAnnotations { suggestions }
399 }
400}
401
402struct AnnotateUnitFallbackVisitor<'a, 'tcx> {
405 reachable_vids: FxHashSet<ty::TyVid>,
406 fcx: &'a FnCtxt<'a, 'tcx>,
407}
408impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> {
409 fn suggest_for_segment(
415 &self,
416 arg_segment: &'tcx hir::PathSegment<'tcx>,
417 def_id: DefId,
418 id: HirId,
419 ) -> ControlFlow<errors::SuggestAnnotation> {
420 if arg_segment.args.is_none()
421 && let Some(all_args) = self.fcx.typeck_results.borrow().node_args_opt(id)
422 && let generics = self.fcx.tcx.generics_of(def_id)
423 && let args = all_args[generics.parent_count..].iter().zip(&generics.own_params)
424 && args.clone().all(|(_, param)| matches!(param.kind, ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Lifetime))
426 {
427 let non_apit_type_args = args.filter(|(_, param)| {
429 matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: false, .. })
430 });
431 let n_tys = non_apit_type_args.clone().count();
432 for (idx, (arg, _)) in non_apit_type_args.enumerate() {
433 if let Some(ty) = arg.as_type()
434 && let Some(vid) = self.fcx.root_vid(ty)
435 && self.reachable_vids.contains(&vid)
436 {
437 return ControlFlow::Break(errors::SuggestAnnotation::Turbo(
438 arg_segment.ident.span.shrink_to_hi(),
439 n_tys,
440 idx,
441 ));
442 }
443 }
444 }
445 ControlFlow::Continue(())
446 }
447}
448impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> {
449 type Result = ControlFlow<errors::SuggestAnnotation>;
450
451 fn visit_infer(
452 &mut self,
453 inf_id: HirId,
454 inf_span: Span,
455 _kind: InferKind<'tcx>,
456 ) -> Self::Result {
457 if let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(inf_id)
459 && let Some(vid) = self.fcx.root_vid(ty)
460 && self.reachable_vids.contains(&vid)
461 && inf_span.can_be_used_for_suggestions()
462 {
463 return ControlFlow::Break(errors::SuggestAnnotation::Unit(inf_span));
464 }
465
466 ControlFlow::Continue(())
467 }
468
469 fn visit_qpath(
470 &mut self,
471 qpath: &'tcx rustc_hir::QPath<'tcx>,
472 id: HirId,
473 span: Span,
474 ) -> Self::Result {
475 let arg_segment = match qpath {
476 hir::QPath::Resolved(_, path) => {
477 path.segments.last().expect("paths should have a segment")
478 }
479 hir::QPath::TypeRelative(_, segment) => segment,
480 };
481 if let Some(def_id) = self.fcx.typeck_results.borrow().qpath_res(qpath, id).opt_def_id()
483 && span.can_be_used_for_suggestions()
484 {
485 self.suggest_for_segment(arg_segment, def_id, id)?;
486 }
487 hir::intravisit::walk_qpath(self, qpath, id)
488 }
489
490 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
491 if let hir::ExprKind::Closure(&hir::Closure { body, .. })
492 | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) = expr.kind
493 {
494 self.visit_body(self.fcx.tcx.hir_body(body))?;
495 }
496
497 if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
500 && let Res::Def(DefKind::AssocFn, def_id) = path.res
501 && self.fcx.tcx.trait_of_assoc(def_id).is_some()
502 && let Some(args) = self.fcx.typeck_results.borrow().node_args_opt(expr.hir_id)
503 && let self_ty = args.type_at(0)
504 && let Some(vid) = self.fcx.root_vid(self_ty)
505 && self.reachable_vids.contains(&vid)
506 && let [.., trait_segment, _method_segment] = path.segments
507 && expr.span.can_be_used_for_suggestions()
508 {
509 let span = path.span.shrink_to_lo().to(trait_segment.ident.span);
510 return ControlFlow::Break(errors::SuggestAnnotation::Path(span));
511 }
512
513 if let hir::ExprKind::MethodCall(segment, ..) = expr.kind
515 && let Some(def_id) =
516 self.fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id)
517 && expr.span.can_be_used_for_suggestions()
518 {
519 self.suggest_for_segment(segment, def_id, expr.hir_id)?;
520 }
521
522 hir::intravisit::walk_expr(self, expr)
523 }
524
525 fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) -> Self::Result {
526 if let hir::LocalSource::Normal = local.source
528 && let None = local.ty
529 && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(local.hir_id)
530 && let Some(vid) = self.fcx.root_vid(ty)
531 && self.reachable_vids.contains(&vid)
532 && local.span.can_be_used_for_suggestions()
533 {
534 return ControlFlow::Break(errors::SuggestAnnotation::Local(
535 local.pat.span.shrink_to_hi(),
536 ));
537 }
538 hir::intravisit::walk_local(self, local)
539 }
540}
541
542#[derive(Debug, Copy, Clone)]
543pub(crate) enum UnsafeUseReason {
544 Call,
545 Method,
546 Path,
547 UnionField,
548 Deref,
549}
550
551fn compute_unsafe_infer_vars<'a, 'tcx>(
568 fcx: &'a FnCtxt<'a, 'tcx>,
569 body_id: LocalDefId,
570) -> UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)> {
571 let body = fcx.tcx.hir_maybe_body_owned_by(body_id).expect("body id must have an owner");
572 let mut res = UnordMap::default();
573
574 struct UnsafeInferVarsVisitor<'a, 'tcx> {
575 fcx: &'a FnCtxt<'a, 'tcx>,
576 res: &'a mut UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>,
577 }
578
579 impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_> {
580 fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) {
581 let typeck_results = self.fcx.typeck_results.borrow();
582
583 match ex.kind {
584 hir::ExprKind::MethodCall(..) => {
585 if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id)
586 && let method_ty = self.fcx.tcx.type_of(def_id).instantiate_identity()
587 && let sig = method_ty.fn_sig(self.fcx.tcx)
588 && sig.safety().is_unsafe()
589 {
590 let mut collector = InferVarCollector {
591 value: (ex.hir_id, ex.span, UnsafeUseReason::Method),
592 res: self.res,
593 };
594
595 typeck_results
597 .node_args(ex.hir_id)
598 .types()
599 .for_each(|t| t.visit_with(&mut collector));
600 }
601 }
602
603 hir::ExprKind::Call(func, ..) => {
604 let func_ty = typeck_results.expr_ty(func);
605
606 if func_ty.is_fn()
607 && let sig = func_ty.fn_sig(self.fcx.tcx)
608 && sig.safety().is_unsafe()
609 {
610 let mut collector = InferVarCollector {
611 value: (ex.hir_id, ex.span, UnsafeUseReason::Call),
612 res: self.res,
613 };
614
615 typeck_results
620 .node_args(func.hir_id)
621 .types()
622 .for_each(|t| t.visit_with(&mut collector));
623
624 sig.output().visit_with(&mut collector);
626 }
627 }
628
629 hir::ExprKind::Path(_) => {
633 let ty = typeck_results.expr_ty(ex);
634
635 if ty.is_fn()
638 && let sig = ty.fn_sig(self.fcx.tcx)
639 && sig.safety().is_unsafe()
640 {
641 let mut collector = InferVarCollector {
642 value: (ex.hir_id, ex.span, UnsafeUseReason::Path),
643 res: self.res,
644 };
645
646 typeck_results
648 .node_args(ex.hir_id)
649 .types()
650 .for_each(|t| t.visit_with(&mut collector));
651 }
652 }
653
654 hir::ExprKind::Unary(hir::UnOp::Deref, pointer) => {
655 if let ty::RawPtr(pointee, _) = typeck_results.expr_ty(pointer).kind() {
656 pointee.visit_with(&mut InferVarCollector {
657 value: (ex.hir_id, ex.span, UnsafeUseReason::Deref),
658 res: self.res,
659 });
660 }
661 }
662
663 hir::ExprKind::Field(base, _) => {
664 let base_ty = typeck_results.expr_ty(base);
665
666 if base_ty.is_union() {
667 typeck_results.expr_ty(ex).visit_with(&mut InferVarCollector {
668 value: (ex.hir_id, ex.span, UnsafeUseReason::UnionField),
669 res: self.res,
670 });
671 }
672 }
673
674 _ => (),
675 };
676
677 hir::intravisit::walk_expr(self, ex);
678 }
679 }
680
681 struct InferVarCollector<'r, V> {
682 value: V,
683 res: &'r mut UnordMap<ty::TyVid, V>,
684 }
685
686 impl<'tcx, V: Copy> ty::TypeVisitor<TyCtxt<'tcx>> for InferVarCollector<'_, V> {
687 fn visit_ty(&mut self, t: Ty<'tcx>) {
688 if let Some(vid) = t.ty_vid() {
689 _ = self.res.try_insert(vid, self.value);
690 } else {
691 t.super_visit_with(self)
692 }
693 }
694 }
695
696 UnsafeInferVarsVisitor { fcx, res: &mut res }.visit_expr(&body.value);
697
698 debug!(?res, "collected the following unsafe vars for {body_id:?}");
699
700 res
701}