2 use rustc_infer::infer::TyCtxtInferExt;
3 use rustc_macros::{LintDiagnostic, Subdiagnostic};
4 use rustc_middle::ty::{
5 self, fold::BottomUpFolder, print::TraitPredPrintModifiersAndPath, Ty, TypeFoldable,
8 use rustc_trait_selection::traits;
9 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
11 use crate::{LateContext, LateLintPass, LintContext};
14 /// The `opaque_hidden_inferred_bound` lint detects cases in which nested
15 /// `impl Trait` in associated type bounds are not written generally enough
16 /// to satisfy the bounds of the associated type.
20 /// This functionality was removed in #97346, but then rolled back in #99860
21 /// because it caused regressions.
23 /// We plan on reintroducing this as a hard error, but in the mean time,
24 /// this lint serves to warn and suggest fixes for any use-cases which rely
36 /// impl Trait for Struct {
40 /// fn test() -> impl Trait<Assoc = impl Sized> {
47 /// In this example, `test` declares that the associated type `Assoc` for
48 /// `impl Trait` is `impl Sized`, which does not satisfy the `Send` bound
49 /// on the associated type.
51 /// Although the hidden type, `i32` does satisfy this bound, we do not
52 /// consider the return type to be well-formed with this lint. It can be
53 /// fixed by changing `impl Sized` into `impl Sized + Send`.
54 pub OPAQUE_HIDDEN_INFERRED_BOUND,
56 "detects the use of nested `impl Trait` types in associated type bounds that are not general enough"
59 declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]);
61 impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
62 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
63 let hir::ItemKind::OpaqueTy(_) = &item.kind else { return; };
64 let def_id = item.def_id.def_id.to_def_id();
65 let infcx = &cx.tcx.infer_ctxt().build();
66 // For every projection predicate in the opaque type's explicit bounds,
67 // check that the type that we're assigning actually satisfies the bounds
68 // of the associated type.
69 for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) {
70 // Liberate bound regions in the predicate since we
71 // don't actually care about lifetimes in this check.
72 let predicate = cx.tcx.liberate_late_bound_regions(def_id, pred.kind());
73 let ty::PredicateKind::Projection(proj) = predicate else {
76 // Only check types, since those are the only things that may
77 // have opaques in them anyways.
78 let Some(proj_term) = proj.term.ty() else { continue };
81 cx.tcx.mk_projection(proj.projection_ty.item_def_id, proj.projection_ty.substs);
82 // For every instance of the projection type in the bounds,
83 // replace them with the term we're assigning to the associated
84 // type in our opaque type.
85 let proj_replacer = &mut BottomUpFolder {
87 ty_op: |ty| if ty == proj_ty { proj_term } else { ty },
91 // For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`,
92 // e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait`
93 // with `impl Send: OtherTrait`.
94 for (assoc_pred, assoc_pred_span) in cx
96 .bound_explicit_item_bounds(proj.projection_ty.item_def_id)
97 .subst_iter_copied(cx.tcx, &proj.projection_ty.substs)
99 let assoc_pred = assoc_pred.fold_with(proj_replacer);
100 let Ok(assoc_pred) = traits::fully_normalize(infcx, traits::ObligationCause::dummy(), cx.param_env, assoc_pred) else {
103 // If that predicate doesn't hold modulo regions (but passed during type-check),
104 // then we must've taken advantage of the hack in `project_and_unify_types` where
105 // we replace opaques with inference vars. Emit a warning!
106 if !infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new(
107 traits::ObligationCause::dummy(),
111 // If it's a trait bound and an opaque that doesn't satisfy it,
112 // then we can emit a suggestion to add the bound.
113 let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
114 (ty::Opaque(def_id, _), ty::PredicateKind::Trait(trait_pred)) => {
116 suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(),
117 trait_ref: trait_pred.print_modifiers_and_trait_path(),
122 cx.emit_spanned_lint(
123 OPAQUE_HIDDEN_INFERRED_BOUND,
125 OpaqueHiddenInferredBoundLint {
126 ty: cx.tcx.mk_opaque(
128 ty::InternalSubsts::identity_for_item(cx.tcx, def_id),
141 #[derive(LintDiagnostic)]
142 #[diag(lint_opaque_hidden_inferred_bound)]
143 struct OpaqueHiddenInferredBoundLint<'tcx> {
146 #[label(specifically)]
147 assoc_pred_span: Span,
149 add_bound: Option<AddBound<'tcx>>,
152 #[derive(Subdiagnostic)]
153 #[suggestion_verbose(
154 lint_opaque_hidden_inferred_bound_sugg,
155 applicability = "machine-applicable",
156 code = " + {trait_ref}"
158 struct AddBound<'tcx> {
162 trait_ref: TraitPredPrintModifiersAndPath<'tcx>,