1use rustc_hir::{selfas hir, AmbigArg};
2use rustc_infer::infer::TyCtxtInferExt;
3use rustc_macros::{Diagnostic, Subdiagnostic};
4use rustc_middle::ty::print::{PrintTraitPredicateExtas _, TraitPredPrintModifiersAndPath};
5use rustc_middle::ty::{self, BottomUpFolder, Ty, TypeFoldable};
6use rustc_session::{declare_lint, declare_lint_pass};
7use rustc_span::{Span, kw};
8use rustc_trait_selection::traits::{self, ObligationCtxt};
910use crate::{LateContext, LateLintPass, LintContext};
1112#[doc =
r" The `opaque_hidden_inferred_bound` lint detects cases in which nested"]
#[doc =
r" `impl Trait` in associated type bounds are not written generally enough"]
#[doc = r" to satisfy the bounds of the associated type."]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" This functionality was removed in #97346, but then rolled back in #99860"]
#[doc = r" because it caused regressions."]
#[doc = r""]
#[doc =
r" We plan on reintroducing this as a hard error, but in the meantime,"]
#[doc =
r" this lint serves to warn and suggest fixes for any use-cases which rely"]
#[doc = r" on this behavior."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" #![feature(type_alias_impl_trait)]"]
#[doc = r""]
#[doc = r" trait Duh {}"]
#[doc = r""]
#[doc = r" impl Duh for i32 {}"]
#[doc = r""]
#[doc = r" trait Trait {"]
#[doc = r" type Assoc: Duh;"]
#[doc = r" }"]
#[doc = r""]
#[doc = r" impl<F: Duh> Trait for F {"]
#[doc = r" type Assoc = F;"]
#[doc = r" }"]
#[doc = r""]
#[doc = r" type Tait = impl Sized;"]
#[doc = r""]
#[doc = r" #[define_opaque(Tait)]"]
#[doc = r" fn test() -> impl Trait<Assoc = Tait> {"]
#[doc = r" 42"]
#[doc = r" }"]
#[doc = r""]
#[doc = r" fn main() {}"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc =
r" In this example, `test` declares that the associated type `Assoc` for"]
#[doc =
r" `impl Trait` is `impl Sized`, which does not satisfy the bound `Duh`"]
#[doc = r" on the associated type."]
#[doc = r""]
#[doc =
r" Although the hidden type, `i32` does satisfy this bound, we do not"]
#[doc =
r" consider the return type to be well-formed with this lint. It can be"]
#[doc =
r" fixed by changing `Tait = impl Sized` into `Tait = impl Sized + Duh`."]
pub static OPAQUE_HIDDEN_INFERRED_BOUND: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "OPAQUE_HIDDEN_INFERRED_BOUND",
default_level: ::rustc_lint_defs::Warn,
desc: "detects the use of nested `impl Trait` types in associated type bounds that are not general enough",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
13/// The `opaque_hidden_inferred_bound` lint detects cases in which nested
14 /// `impl Trait` in associated type bounds are not written generally enough
15 /// to satisfy the bounds of the associated type.
16 ///
17 /// ### Explanation
18 ///
19 /// This functionality was removed in #97346, but then rolled back in #99860
20 /// because it caused regressions.
21 ///
22 /// We plan on reintroducing this as a hard error, but in the meantime,
23 /// this lint serves to warn and suggest fixes for any use-cases which rely
24 /// on this behavior.
25 ///
26 /// ### Example
27 ///
28 /// ```rust
29 /// #![feature(type_alias_impl_trait)]
30 ///
31 /// trait Duh {}
32 ///
33 /// impl Duh for i32 {}
34 ///
35 /// trait Trait {
36 /// type Assoc: Duh;
37 /// }
38 ///
39 /// impl<F: Duh> Trait for F {
40 /// type Assoc = F;
41 /// }
42 ///
43 /// type Tait = impl Sized;
44 ///
45 /// #[define_opaque(Tait)]
46 /// fn test() -> impl Trait<Assoc = Tait> {
47 /// 42
48 /// }
49 ///
50 /// fn main() {}
51 /// ```
52 ///
53 /// {{produces}}
54 ///
55 /// In this example, `test` declares that the associated type `Assoc` for
56 /// `impl Trait` is `impl Sized`, which does not satisfy the bound `Duh`
57 /// on the associated type.
58 ///
59 /// Although the hidden type, `i32` does satisfy this bound, we do not
60 /// consider the return type to be well-formed with this lint. It can be
61 /// fixed by changing `Tait = impl Sized` into `Tait = impl Sized + Duh`.
62pub OPAQUE_HIDDEN_INFERRED_BOUND,
63 Warn,
64"detects the use of nested `impl Trait` types in associated type bounds that are not general enough"
65}6667pub struct OpaqueHiddenInferredBound;
#[automatically_derived]
impl ::core::marker::Copy for OpaqueHiddenInferredBound { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for OpaqueHiddenInferredBound { }
#[automatically_derived]
impl ::core::clone::Clone for OpaqueHiddenInferredBound {
#[inline]
fn clone(&self) -> OpaqueHiddenInferredBound { *self }
}
impl ::rustc_lint_defs::LintPass for OpaqueHiddenInferredBound {
fn name(&self) -> &'static str { "OpaqueHiddenInferredBound" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[OPAQUE_HIDDEN_INFERRED_BOUND]))
}
}
impl OpaqueHiddenInferredBound {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[OPAQUE_HIDDEN_INFERRED_BOUND]))
}
}declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]);
6869impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
70fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
71let hir::TyKind::OpaqueDef(opaque) = &ty.kind else {
72return;
73 };
7475// If this is an RPITIT from a trait method with no body, then skip.
76 // That's because although we may have an opaque type on the function,
77 // it won't have a hidden type, so proving predicates about it is
78 // not really meaningful.
79if let hir::OpaqueTyOrigin::FnReturn { parent: method_def_id, .. } = opaque.origin
80 && let hir::Node::TraitItem(trait_item) = cx.tcx.hir_node_by_def_id(method_def_id)
81 && !trait_item.defaultness.has_value()
82 {
83return;
84 }
8586let def_id = opaque.def_id.to_def_id();
87let infcx = &cx.tcx.infer_ctxt().build(cx.typing_mode());
88// For every projection predicate in the opaque type's explicit bounds,
89 // check that the type that we're assigning actually satisfies the bounds
90 // of the associated type.
91for (pred, pred_span) in cx.tcx.explicit_item_bounds(def_id).iter_identity_copied() {
92 infcx.enter_forall(pred.kind(), |predicate| {
93let ty::ClauseKind::Projection(proj) = predicate else {
94return;
95 };
96// Only check types, since those are the only things that may
97 // have opaques in them anyways.
98let Some(proj_term) = proj.term.as_type() else { return };
99100// HACK: `impl Trait<Assoc = impl Trait2>` from an RPIT is "ok"...
101if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: opaque_def_id }, .. }) =
102*proj_term.kind()
103 && cx.tcx.parent(opaque_def_id) == def_id
104 && #[allow(non_exhaustive_omitted_patterns)] match opaque.origin {
hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. }
=> true,
_ => false,
}matches!(
105 opaque.origin,
106 hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. }
107 )108 {
109return;
110 }
111112// HACK: `async fn() -> Self` in traits is "ok"...
113 // This is not really that great, but it's similar to why the `-> Self`
114 // return type is well-formed in traits even when `Self` isn't sized.
115if let ty::Param(param_ty) = *proj_term.kind()
116 && param_ty.name == kw::SelfUpper
117 && #[allow(non_exhaustive_omitted_patterns)] match opaque.origin {
hir::OpaqueTyOrigin::AsyncFn {
in_trait_or_impl: Some(hir::RpitContext::Trait), .. } => true,
_ => false,
}matches!(
118 opaque.origin,
119 hir::OpaqueTyOrigin::AsyncFn {
120 in_trait_or_impl: Some(hir::RpitContext::Trait),
121 ..
122 }
123 )124 {
125return;
126 }
127128let proj_ty = Ty::new_projection_from_args(
129 cx.tcx,
130 proj.projection_term.def_id,
131 proj.projection_term.args,
132 );
133// For every instance of the projection type in the bounds,
134 // replace them with the term we're assigning to the associated
135 // type in our opaque type.
136let proj_replacer = &mut BottomUpFolder {
137 tcx: cx.tcx,
138 ty_op: |ty| if ty == proj_ty { proj_term } else { ty },
139 lt_op: |lt| lt,
140 ct_op: |ct| ct,
141 };
142// For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`,
143 // e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait`
144 // with `impl Send: OtherTrait`.
145for (assoc_pred, assoc_pred_span) in cx
146 .tcx
147 .explicit_item_bounds(proj.projection_term.def_id)
148 .iter_instantiated_copied(cx.tcx, proj.projection_term.args)
149 {
150let assoc_pred = assoc_pred.fold_with(proj_replacer);
151152let ocx = ObligationCtxt::new(infcx);
153let assoc_pred =
154 ocx.normalize(&traits::ObligationCause::dummy(), cx.param_env, assoc_pred);
155if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() {
156// Can't normalize for some reason...?
157continue;
158 }
159160 ocx.register_obligation(traits::Obligation::new(
161 cx.tcx,
162 traits::ObligationCause::dummy(),
163 cx.param_env,
164 assoc_pred,
165 ));
166167// If that predicate doesn't hold modulo regions (but passed during type-check),
168 // then we must've taken advantage of the hack in `project_and_unify_types` where
169 // we replace opaques with inference vars. Emit a warning!
170if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() {
171// If it's a trait bound and an opaque that doesn't satisfy it,
172 // then we can emit a suggestion to add the bound.
173let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
174 (
175 ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }),
176 ty::ClauseKind::Trait(trait_pred),
177 ) => Some(AddBound {
178 suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(),
179 trait_ref: trait_pred.print_modifiers_and_trait_path(),
180 }),
181_ => None,
182 };
183184 cx.emit_span_lint(
185 OPAQUE_HIDDEN_INFERRED_BOUND,
186 pred_span,
187 OpaqueHiddenInferredBoundLint {
188 ty: Ty::new_opaque(
189 cx.tcx,
190 def_id,
191 ty::GenericArgs::identity_for_item(cx.tcx, def_id),
192 ),
193 proj_ty: proj_term,
194 assoc_pred_span,
195 add_bound,
196 },
197 );
198 }
199 }
200 });
201 }
202 }
203}
204205#[derive(const _: () =
{
impl<'_sess, 'tcx, G> rustc_errors::Diagnostic<'_sess, G> for
OpaqueHiddenInferredBoundLint<'tcx> where
G: rustc_errors::EmissionGuarantee {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
match self {
OpaqueHiddenInferredBoundLint {
ty: __binding_0,
proj_ty: __binding_1,
assoc_pred_span: __binding_2,
add_bound: __binding_3 } => {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("opaque type `{$ty}` does not satisfy its associated type bounds")));
;
diag.arg("ty", __binding_0);
diag.arg("proj_ty", __binding_1);
diag.span_label(__binding_2,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("this associated type bound is unsatisfied for `{$proj_ty}`")));
if let Some(__binding_3) = __binding_3 {
diag.subdiagnostic(__binding_3);
}
diag
}
}
}
}
};Diagnostic)]
206#[diag("opaque type `{$ty}` does not satisfy its associated type bounds")]
207struct OpaqueHiddenInferredBoundLint<'tcx> {
208 ty: Ty<'tcx>,
209 proj_ty: Ty<'tcx>,
210#[label("this associated type bound is unsatisfied for `{$proj_ty}`")]
211assoc_pred_span: Span,
212#[subdiagnostic]
213add_bound: Option<AddBound<'tcx>>,
214}
215216#[derive(const _: () =
{
impl<'tcx> rustc_errors::Subdiagnostic for AddBound<'tcx> {
fn add_to_diag<__G>(self, diag: &mut rustc_errors::Diag<'_, __G>)
where __G: rustc_errors::EmissionGuarantee {
match self {
AddBound { suggest_span: __binding_0, trait_ref: __binding_1
} => {
let __code_118 =
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" + {0}", __binding_1))
})].into_iter();
let mut sub_args = rustc_errors::DiagArgMap::default();
let __message =
rustc_errors::format_diag_message(&rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("add this bound")),
&sub_args);
diag.span_suggestions_with_style(__binding_0, __message,
__code_118, rustc_errors::Applicability::MachineApplicable,
rustc_errors::SuggestionStyle::ShowAlways);
}
}
}
}
};Subdiagnostic)]
217#[suggestion(
218"add this bound",
219 style = "verbose",
220 applicability = "machine-applicable",
221 code = " + {trait_ref}"
222)]
223struct AddBound<'tcx> {
224#[primary_span]
225suggest_span: Span,
226#[skip_arg]
227trait_ref: TraitPredPrintModifiersAndPath<'tcx>,
228}