diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:13:23 +0000 |
commit | 20431706a863f92cb37dc512fef6e48d192aaf2c (patch) | |
tree | 2867f13f5fd5437ba628c67d7f87309ccadcd286 /compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs | |
parent | Releasing progress-linux version 1.65.0+dfsg1-2~progress7.99u1. (diff) | |
download | rustc-20431706a863f92cb37dc512fef6e48d192aaf2c.tar.xz rustc-20431706a863f92cb37dc512fef6e48d192aaf2c.zip |
Merging upstream version 1.66.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs new file mode 100644 index 000000000..00bf287ba --- /dev/null +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -0,0 +1,163 @@ +use rustc_hir as hir; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_macros::{LintDiagnostic, Subdiagnostic}; +use rustc_middle::ty::{ + self, fold::BottomUpFolder, print::TraitPredPrintModifiersAndPath, Ty, TypeFoldable, +}; +use rustc_span::Span; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; + +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `opaque_hidden_inferred_bound` lint detects cases in which nested + /// `impl Trait` in associated type bounds are not written generally enough + /// to satisfy the bounds of the associated type. + /// + /// ### Explanation + /// + /// This functionality was removed in #97346, but then rolled back in #99860 + /// because it caused regressions. + /// + /// We plan on reintroducing this as a hard error, but in the mean time, + /// this lint serves to warn and suggest fixes for any use-cases which rely + /// on this behavior. + /// + /// ### Example + /// + /// ``` + /// trait Trait { + /// type Assoc: Send; + /// } + /// + /// struct Struct; + /// + /// impl Trait for Struct { + /// type Assoc = i32; + /// } + /// + /// fn test() -> impl Trait<Assoc = impl Sized> { + /// Struct + /// } + /// ``` + /// + /// {{produces}} + /// + /// In this example, `test` declares that the associated type `Assoc` for + /// `impl Trait` is `impl Sized`, which does not satisfy the `Send` bound + /// on the associated type. + /// + /// Although the hidden type, `i32` does satisfy this bound, we do not + /// consider the return type to be well-formed with this lint. It can be + /// fixed by changing `impl Sized` into `impl Sized + Send`. + pub OPAQUE_HIDDEN_INFERRED_BOUND, + Warn, + "detects the use of nested `impl Trait` types in associated type bounds that are not general enough" +} + +declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]); + +impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + let hir::ItemKind::OpaqueTy(_) = &item.kind else { return; }; + let def_id = item.owner_id.def_id.to_def_id(); + let infcx = &cx.tcx.infer_ctxt().build(); + // For every projection predicate in the opaque type's explicit bounds, + // check that the type that we're assigning actually satisfies the bounds + // of the associated type. + for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) { + // Liberate bound regions in the predicate since we + // don't actually care about lifetimes in this check. + let predicate = cx.tcx.liberate_late_bound_regions(def_id, pred.kind()); + let ty::PredicateKind::Projection(proj) = predicate else { + continue; + }; + // Only check types, since those are the only things that may + // have opaques in them anyways. + let Some(proj_term) = proj.term.ty() else { continue }; + + let proj_ty = + cx.tcx.mk_projection(proj.projection_ty.item_def_id, proj.projection_ty.substs); + // For every instance of the projection type in the bounds, + // replace them with the term we're assigning to the associated + // type in our opaque type. + let proj_replacer = &mut BottomUpFolder { + tcx: cx.tcx, + ty_op: |ty| if ty == proj_ty { proj_term } else { ty }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }; + // For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`, + // e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait` + // with `impl Send: OtherTrait`. + for (assoc_pred, assoc_pred_span) in cx + .tcx + .bound_explicit_item_bounds(proj.projection_ty.item_def_id) + .subst_iter_copied(cx.tcx, &proj.projection_ty.substs) + { + let assoc_pred = assoc_pred.fold_with(proj_replacer); + let Ok(assoc_pred) = traits::fully_normalize(infcx, traits::ObligationCause::dummy(), cx.param_env, assoc_pred) else { + continue; + }; + // If that predicate doesn't hold modulo regions (but passed during type-check), + // then we must've taken advantage of the hack in `project_and_unify_types` where + // we replace opaques with inference vars. Emit a warning! + if !infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new( + traits::ObligationCause::dummy(), + cx.param_env, + assoc_pred, + )) { + // If it's a trait bound and an opaque that doesn't satisfy it, + // then we can emit a suggestion to add the bound. + let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) { + (ty::Opaque(def_id, _), ty::PredicateKind::Trait(trait_pred)) => { + Some(AddBound { + suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(), + trait_ref: trait_pred.print_modifiers_and_trait_path(), + }) + } + _ => None, + }; + cx.emit_spanned_lint( + OPAQUE_HIDDEN_INFERRED_BOUND, + pred_span, + OpaqueHiddenInferredBoundLint { + ty: cx.tcx.mk_opaque( + def_id, + ty::InternalSubsts::identity_for_item(cx.tcx, def_id), + ), + proj_ty: proj_term, + assoc_pred_span, + add_bound, + }, + ); + } + } + } + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_opaque_hidden_inferred_bound)] +struct OpaqueHiddenInferredBoundLint<'tcx> { + ty: Ty<'tcx>, + proj_ty: Ty<'tcx>, + #[label(specifically)] + assoc_pred_span: Span, + #[subdiagnostic] + add_bound: Option<AddBound<'tcx>>, +} + +#[derive(Subdiagnostic)] +#[suggestion_verbose( + lint_opaque_hidden_inferred_bound_sugg, + applicability = "machine-applicable", + code = " + {trait_ref}" +)] +struct AddBound<'tcx> { + #[primary_span] + suggest_span: Span, + #[skip_arg] + trait_ref: TraitPredPrintModifiersAndPath<'tcx>, +} |