From d0d6af91467ef60f12396f5d40a09eb4de8cb8b7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 2 Oct 2022 04:45:54 +0000 Subject: [PATCH 1/3] Lint for unsatisfied nested opaques --- .../locales/en-US/lint.ftl | 4 + compiler/rustc_lint/src/lib.rs | 3 + .../src/rpit_hidden_inferred_bound.rs | 143 ++++++++++++++++++ compiler/rustc_middle/src/lint.rs | 4 +- 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 compiler/rustc_lint/src/rpit_hidden_inferred_bound.rs diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_error_messages/locales/en-US/lint.ftl index 80b0b1a8904a1..31c9a845e7e9d 100644 --- a/compiler/rustc_error_messages/locales/en-US/lint.ftl +++ b/compiler/rustc_error_messages/locales/en-US/lint.ftl @@ -433,3 +433,7 @@ lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` lint_check_name_warning = {$msg} lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name} + +lint_rpit_hidden_inferred_bound = return-position `{$ty}` does not satisfy its associated type bounds + .specifically = this associated type bound is unsatisfied for `{$proj_ty}` + .suggestion = add this bound diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 4408f68dd63f3..fd7ea5a4bb5a2 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -65,6 +65,7 @@ mod noop_method_call; mod pass_by_value; mod passes; mod redundant_semicolon; +mod rpit_hidden_inferred_bound; mod traits; mod types; mod unused; @@ -95,6 +96,7 @@ use nonstandard_style::*; use noop_method_call::*; use pass_by_value::*; use redundant_semicolon::*; +use rpit_hidden_inferred_bound::*; use traits::*; use types::*; use unused::*; @@ -223,6 +225,7 @@ macro_rules! late_lint_mod_passes { EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums, InvalidAtomicOrdering: InvalidAtomicOrdering, NamedAsmLabels: NamedAsmLabels, + RpitHiddenInferredBound: RpitHiddenInferredBound, ] ); }; diff --git a/compiler/rustc_lint/src/rpit_hidden_inferred_bound.rs b/compiler/rustc_lint/src/rpit_hidden_inferred_bound.rs new file mode 100644 index 0000000000000..fd9872c80a945 --- /dev/null +++ b/compiler/rustc_lint/src/rpit_hidden_inferred_bound.rs @@ -0,0 +1,143 @@ +use hir::def_id::LocalDefId; +use rustc_hir as hir; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_macros::LintDiagnostic; +use rustc_middle::ty::{ + self, fold::BottomUpFolder, Ty, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, +}; +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 `rpit_hidden_inferred_bound` lint detects cases in which nested RPITs + /// in associated type bounds are not written generally enough to satisfy the + /// bounds of the associated type. This functionality was removed in #97346, + /// but then rolled back in #99860 because it was made into a hard error too + /// quickly. + /// + /// 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. + pub RPIT_HIDDEN_INFERRED_BOUND, + Warn, + "detects the use of nested RPITs in associated type bounds that are not general enough" +} + +declare_lint_pass!(RpitHiddenInferredBound => [RPIT_HIDDEN_INFERRED_BOUND]); + +impl<'tcx> LateLintPass<'tcx> for RpitHiddenInferredBound { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: hir::intravisit::FnKind<'tcx>, + _: &'tcx hir::FnDecl<'tcx>, + _: &'tcx hir::Body<'tcx>, + _: rustc_span::Span, + id: hir::HirId, + ) { + if matches!(kind, hir::intravisit::FnKind::Closure) { + return; + } + + let fn_def_id = cx.tcx.hir().local_def_id(id); + let sig: ty::FnSig<'tcx> = + cx.tcx.liberate_late_bound_regions(fn_def_id.to_def_id(), cx.tcx.fn_sig(fn_def_id)); + cx.tcx.infer_ctxt().enter(|ref infcx| { + sig.output().visit_with(&mut VisitOpaqueBounds { infcx, cx, fn_def_id }); + }); + } +} + +struct VisitOpaqueBounds<'a, 'cx, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + cx: &'cx LateContext<'tcx>, + fn_def_id: LocalDefId, +} + +impl<'tcx> TypeVisitor<'tcx> for VisitOpaqueBounds<'_, '_, 'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow { + if let ty::Opaque(def_id, substs) = *ty.kind() + && let Some(hir::Node::Item(item)) = self.cx.tcx.hir().get_if_local(def_id) + && let hir::ItemKind::OpaqueTy(opaque) = &item.kind + && let hir::OpaqueTyOrigin::FnReturn(origin_def_id) = opaque.origin + && origin_def_id == self.fn_def_id + { + for pred_and_span in self.cx.tcx.bound_explicit_item_bounds(def_id).transpose_iter() { + let pred_span = pred_and_span.0.1; + let predicate = self.cx.tcx.liberate_late_bound_regions( + def_id, + pred_and_span.map_bound(|(pred, _)| *pred).subst(self.cx.tcx, substs).kind(), + ); + let ty::PredicateKind::Projection(proj) = predicate else { + continue; + }; + let Some(proj_term) = proj.term.ty() else { continue }; + + let proj_ty = self + .cx + .tcx + .mk_projection(proj.projection_ty.item_def_id, proj.projection_ty.substs); + let proj_replacer = &mut BottomUpFolder { + tcx: self.cx.tcx, + ty_op: |ty| if ty == proj_ty { proj_term } else { ty }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }; + for assoc_pred_and_span in self + .cx + .tcx + .bound_explicit_item_bounds(proj.projection_ty.item_def_id) + .transpose_iter() + { + let assoc_pred_span = assoc_pred_and_span.0.1; + let assoc_pred = assoc_pred_and_span + .map_bound(|(pred, _)| *pred) + .subst(self.cx.tcx, &proj.projection_ty.substs) + .fold_with(proj_replacer); + if !self.infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new( + traits::ObligationCause::dummy(), + self.cx.param_env, + assoc_pred, + )) { + let (suggestion, suggest_span) = + match (proj_term.kind(), assoc_pred.kind().skip_binder()) { + (ty::Opaque(def_id, _), ty::PredicateKind::Trait(trait_pred)) => ( + format!(" + {}", trait_pred.print_modifiers_and_trait_path()), + Some(self.cx.tcx.def_span(def_id).shrink_to_hi()), + ), + _ => (String::new(), None), + }; + self.cx.emit_spanned_lint( + RPIT_HIDDEN_INFERRED_BOUND, + pred_span, + RpitHiddenInferredBoundLint { + ty, + proj_ty: proj_term, + assoc_pred_span, + suggestion, + suggest_span, + }, + ); + } + } + } + } + + ty.super_visit_with(self) + } +} + +#[derive(LintDiagnostic)] +#[diag(lint::rpit_hidden_inferred_bound)] +struct RpitHiddenInferredBoundLint<'tcx> { + ty: Ty<'tcx>, + proj_ty: Ty<'tcx>, + #[label(lint::specifically)] + assoc_pred_span: Span, + #[suggestion_verbose(applicability = "machine-applicable", code = "{suggestion}")] + suggest_span: Option, + suggestion: String, +} diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index f3e4f1faeb05c..1fb27fd87e87e 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -444,7 +444,9 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool { match expn_data.kind { ExpnKind::Inlined | ExpnKind::Root - | ExpnKind::Desugaring(DesugaringKind::ForLoop | DesugaringKind::WhileLoop) => false, + | ExpnKind::Desugaring( + DesugaringKind::ForLoop | DesugaringKind::WhileLoop | DesugaringKind::OpaqueTy, + ) => false, ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external" ExpnKind::Macro(MacroKind::Bang, _) => { // Dummy span for the `def_site` means it's an external macro. From 426424b3200022eba361e171ba84be7c1e7cc837 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 2 Oct 2022 05:45:15 +0000 Subject: [PATCH 2/3] Make it a lint for all opaque types --- .../locales/en-US/lint.ftl | 2 +- compiler/rustc_lint/src/lib.rs | 6 +- .../src/opaque_hidden_inferred_bound.rs | 125 +++++++++++++++ .../src/rpit_hidden_inferred_bound.rs | 143 ------------------ .../ui/impl-trait/nested-return-type2-tait.rs | 1 + .../nested-return-type2-tait.stderr | 17 +++ src/test/ui/impl-trait/nested-return-type2.rs | 1 + .../ui/impl-trait/nested-return-type2.stderr | 17 +++ .../ui/impl-trait/nested-return-type3-tait.rs | 1 + .../nested-return-type3-tait.stderr | 17 +++ .../impl-trait/nested-return-type3-tait2.rs | 1 + .../nested-return-type3-tait2.stderr | 17 +++ .../impl-trait/nested-return-type3-tait3.rs | 1 + .../nested-return-type3-tait3.stderr | 17 +++ src/test/ui/impl-trait/nested-return-type3.rs | 1 + .../ui/impl-trait/nested-return-type3.stderr | 17 +++ 16 files changed, 237 insertions(+), 147 deletions(-) create mode 100644 compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs delete mode 100644 compiler/rustc_lint/src/rpit_hidden_inferred_bound.rs create mode 100644 src/test/ui/impl-trait/nested-return-type2-tait.stderr create mode 100644 src/test/ui/impl-trait/nested-return-type2.stderr create mode 100644 src/test/ui/impl-trait/nested-return-type3-tait.stderr create mode 100644 src/test/ui/impl-trait/nested-return-type3-tait2.stderr create mode 100644 src/test/ui/impl-trait/nested-return-type3-tait3.stderr create mode 100644 src/test/ui/impl-trait/nested-return-type3.stderr diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_error_messages/locales/en-US/lint.ftl index 31c9a845e7e9d..0fd9b0ead167c 100644 --- a/compiler/rustc_error_messages/locales/en-US/lint.ftl +++ b/compiler/rustc_error_messages/locales/en-US/lint.ftl @@ -434,6 +434,6 @@ lint_check_name_warning = {$msg} lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name} -lint_rpit_hidden_inferred_bound = return-position `{$ty}` does not satisfy its associated type bounds +lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its associated type bounds .specifically = this associated type bound is unsatisfied for `{$proj_ty}` .suggestion = add this bound diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index fd7ea5a4bb5a2..9148c42195fbe 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -62,10 +62,10 @@ mod non_ascii_idents; mod non_fmt_panic; mod nonstandard_style; mod noop_method_call; +mod opaque_hidden_inferred_bound; mod pass_by_value; mod passes; mod redundant_semicolon; -mod rpit_hidden_inferred_bound; mod traits; mod types; mod unused; @@ -94,9 +94,9 @@ use non_ascii_idents::*; use non_fmt_panic::NonPanicFmt; use nonstandard_style::*; use noop_method_call::*; +use opaque_hidden_inferred_bound::*; use pass_by_value::*; use redundant_semicolon::*; -use rpit_hidden_inferred_bound::*; use traits::*; use types::*; use unused::*; @@ -225,7 +225,7 @@ macro_rules! late_lint_mod_passes { EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums, InvalidAtomicOrdering: InvalidAtomicOrdering, NamedAsmLabels: NamedAsmLabels, - RpitHiddenInferredBound: RpitHiddenInferredBound, + OpaqueHiddenInferredBound: OpaqueHiddenInferredBound, ] ); }; 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 0000000000000..7f18bf0018f6f --- /dev/null +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -0,0 +1,125 @@ +use rustc_hir as hir; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_macros::LintDiagnostic; +use rustc_middle::ty::{self, fold::BottomUpFolder, 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. This functionality was + /// removed in #97346, but then rolled back in #99860 because it was made + /// into a hard error too quickly. + /// + /// 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. + 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.def_id.def_id.to_def_id(); + cx.tcx.infer_ctxt().enter(|ref infcx| { + // 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`, for all of the bounds on `Assoc`, + // e.g. `type Assoc: OtherTrait`, replace `::Assoc: OtherTrait` + // with `impl Send: OtherTrait`. + for assoc_pred_and_span in cx + .tcx + .bound_explicit_item_bounds(proj.projection_ty.item_def_id) + .transpose_iter() + { + let assoc_pred_span = assoc_pred_and_span.0.1; + let assoc_pred = assoc_pred_and_span + .map_bound(|(pred, _)| *pred) + .subst(cx.tcx, &proj.projection_ty.substs) + .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 (suggestion, suggest_span) = + match (proj_term.kind(), assoc_pred.kind().skip_binder()) { + (ty::Opaque(def_id, _), ty::PredicateKind::Trait(trait_pred)) => ( + format!(" + {}", trait_pred.print_modifiers_and_trait_path()), + Some(cx.tcx.def_span(def_id).shrink_to_hi()), + ), + _ => (String::new(), 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, + suggestion, + suggest_span, + }, + ); + } + } + } + }); + } +} + +#[derive(LintDiagnostic)] +#[diag(lint::opaque_hidden_inferred_bound)] +struct OpaqueHiddenInferredBoundLint<'tcx> { + ty: Ty<'tcx>, + proj_ty: Ty<'tcx>, + #[label(lint::specifically)] + assoc_pred_span: Span, + #[suggestion_verbose(applicability = "machine-applicable", code = "{suggestion}")] + suggest_span: Option, + suggestion: String, +} diff --git a/compiler/rustc_lint/src/rpit_hidden_inferred_bound.rs b/compiler/rustc_lint/src/rpit_hidden_inferred_bound.rs deleted file mode 100644 index fd9872c80a945..0000000000000 --- a/compiler/rustc_lint/src/rpit_hidden_inferred_bound.rs +++ /dev/null @@ -1,143 +0,0 @@ -use hir::def_id::LocalDefId; -use rustc_hir as hir; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_macros::LintDiagnostic; -use rustc_middle::ty::{ - self, fold::BottomUpFolder, Ty, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, -}; -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 `rpit_hidden_inferred_bound` lint detects cases in which nested RPITs - /// in associated type bounds are not written generally enough to satisfy the - /// bounds of the associated type. This functionality was removed in #97346, - /// but then rolled back in #99860 because it was made into a hard error too - /// quickly. - /// - /// 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. - pub RPIT_HIDDEN_INFERRED_BOUND, - Warn, - "detects the use of nested RPITs in associated type bounds that are not general enough" -} - -declare_lint_pass!(RpitHiddenInferredBound => [RPIT_HIDDEN_INFERRED_BOUND]); - -impl<'tcx> LateLintPass<'tcx> for RpitHiddenInferredBound { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: hir::intravisit::FnKind<'tcx>, - _: &'tcx hir::FnDecl<'tcx>, - _: &'tcx hir::Body<'tcx>, - _: rustc_span::Span, - id: hir::HirId, - ) { - if matches!(kind, hir::intravisit::FnKind::Closure) { - return; - } - - let fn_def_id = cx.tcx.hir().local_def_id(id); - let sig: ty::FnSig<'tcx> = - cx.tcx.liberate_late_bound_regions(fn_def_id.to_def_id(), cx.tcx.fn_sig(fn_def_id)); - cx.tcx.infer_ctxt().enter(|ref infcx| { - sig.output().visit_with(&mut VisitOpaqueBounds { infcx, cx, fn_def_id }); - }); - } -} - -struct VisitOpaqueBounds<'a, 'cx, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - cx: &'cx LateContext<'tcx>, - fn_def_id: LocalDefId, -} - -impl<'tcx> TypeVisitor<'tcx> for VisitOpaqueBounds<'_, '_, 'tcx> { - fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow { - if let ty::Opaque(def_id, substs) = *ty.kind() - && let Some(hir::Node::Item(item)) = self.cx.tcx.hir().get_if_local(def_id) - && let hir::ItemKind::OpaqueTy(opaque) = &item.kind - && let hir::OpaqueTyOrigin::FnReturn(origin_def_id) = opaque.origin - && origin_def_id == self.fn_def_id - { - for pred_and_span in self.cx.tcx.bound_explicit_item_bounds(def_id).transpose_iter() { - let pred_span = pred_and_span.0.1; - let predicate = self.cx.tcx.liberate_late_bound_regions( - def_id, - pred_and_span.map_bound(|(pred, _)| *pred).subst(self.cx.tcx, substs).kind(), - ); - let ty::PredicateKind::Projection(proj) = predicate else { - continue; - }; - let Some(proj_term) = proj.term.ty() else { continue }; - - let proj_ty = self - .cx - .tcx - .mk_projection(proj.projection_ty.item_def_id, proj.projection_ty.substs); - let proj_replacer = &mut BottomUpFolder { - tcx: self.cx.tcx, - ty_op: |ty| if ty == proj_ty { proj_term } else { ty }, - lt_op: |lt| lt, - ct_op: |ct| ct, - }; - for assoc_pred_and_span in self - .cx - .tcx - .bound_explicit_item_bounds(proj.projection_ty.item_def_id) - .transpose_iter() - { - let assoc_pred_span = assoc_pred_and_span.0.1; - let assoc_pred = assoc_pred_and_span - .map_bound(|(pred, _)| *pred) - .subst(self.cx.tcx, &proj.projection_ty.substs) - .fold_with(proj_replacer); - if !self.infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new( - traits::ObligationCause::dummy(), - self.cx.param_env, - assoc_pred, - )) { - let (suggestion, suggest_span) = - match (proj_term.kind(), assoc_pred.kind().skip_binder()) { - (ty::Opaque(def_id, _), ty::PredicateKind::Trait(trait_pred)) => ( - format!(" + {}", trait_pred.print_modifiers_and_trait_path()), - Some(self.cx.tcx.def_span(def_id).shrink_to_hi()), - ), - _ => (String::new(), None), - }; - self.cx.emit_spanned_lint( - RPIT_HIDDEN_INFERRED_BOUND, - pred_span, - RpitHiddenInferredBoundLint { - ty, - proj_ty: proj_term, - assoc_pred_span, - suggestion, - suggest_span, - }, - ); - } - } - } - } - - ty.super_visit_with(self) - } -} - -#[derive(LintDiagnostic)] -#[diag(lint::rpit_hidden_inferred_bound)] -struct RpitHiddenInferredBoundLint<'tcx> { - ty: Ty<'tcx>, - proj_ty: Ty<'tcx>, - #[label(lint::specifically)] - assoc_pred_span: Span, - #[suggestion_verbose(applicability = "machine-applicable", code = "{suggestion}")] - suggest_span: Option, - suggestion: String, -} diff --git a/src/test/ui/impl-trait/nested-return-type2-tait.rs b/src/test/ui/impl-trait/nested-return-type2-tait.rs index 42613d5ccd9a4..089018a1cdf01 100644 --- a/src/test/ui/impl-trait/nested-return-type2-tait.rs +++ b/src/test/ui/impl-trait/nested-return-type2-tait.rs @@ -26,6 +26,7 @@ type Sendable = impl Send; // var to make it uphold the `: Duh` bound on `Trait::Assoc`. The opaque // type does not implement `Duh`, but if its hidden type does. fn foo() -> impl Trait { + //~^ WARN opaque type `impl Trait` does not satisfy its associated type bounds || 42 } diff --git a/src/test/ui/impl-trait/nested-return-type2-tait.stderr b/src/test/ui/impl-trait/nested-return-type2-tait.stderr new file mode 100644 index 0000000000000..a8eb69cfcb736 --- /dev/null +++ b/src/test/ui/impl-trait/nested-return-type2-tait.stderr @@ -0,0 +1,17 @@ +warning: opaque type `impl Trait` does not satisfy its associated type bounds + --> $DIR/nested-return-type2-tait.rs:28:24 + | +LL | type Assoc: Duh; + | --- this associated type bound is unsatisfied for `Sendable` +... +LL | fn foo() -> impl Trait { + | ^^^^^^^^^^^^^^^^ + | + = note: `#[warn(opaque_hidden_inferred_bound)]` on by default +help: add this bound + | +LL | type Sendable = impl Send + Duh; + | +++++ + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/nested-return-type2.rs b/src/test/ui/impl-trait/nested-return-type2.rs index 39928d543e15d..cc1f1f4ec44c8 100644 --- a/src/test/ui/impl-trait/nested-return-type2.rs +++ b/src/test/ui/impl-trait/nested-return-type2.rs @@ -23,6 +23,7 @@ impl R> Trait for F { // Lazy TAIT would error out, but we inserted a hack to make it work again, // keeping backwards compatibility. fn foo() -> impl Trait { + //~^ WARN opaque type `impl Trait` does not satisfy its associated type bounds || 42 } diff --git a/src/test/ui/impl-trait/nested-return-type2.stderr b/src/test/ui/impl-trait/nested-return-type2.stderr new file mode 100644 index 0000000000000..3aed05ca13298 --- /dev/null +++ b/src/test/ui/impl-trait/nested-return-type2.stderr @@ -0,0 +1,17 @@ +warning: opaque type `impl Trait` does not satisfy its associated type bounds + --> $DIR/nested-return-type2.rs:25:24 + | +LL | type Assoc: Duh; + | --- this associated type bound is unsatisfied for `impl Send` +... +LL | fn foo() -> impl Trait { + | ^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(opaque_hidden_inferred_bound)]` on by default +help: add this bound + | +LL | fn foo() -> impl Trait { + | +++++ + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/nested-return-type3-tait.rs b/src/test/ui/impl-trait/nested-return-type3-tait.rs index 3936f4dbbb426..3a97e35b4c400 100644 --- a/src/test/ui/impl-trait/nested-return-type3-tait.rs +++ b/src/test/ui/impl-trait/nested-return-type3-tait.rs @@ -17,6 +17,7 @@ impl Trait for F { type Sendable = impl Send; fn foo() -> impl Trait { + //~^ WARN opaque type `impl Trait` does not satisfy its associated type bounds 42 } diff --git a/src/test/ui/impl-trait/nested-return-type3-tait.stderr b/src/test/ui/impl-trait/nested-return-type3-tait.stderr new file mode 100644 index 0000000000000..5f58c8dca4ad4 --- /dev/null +++ b/src/test/ui/impl-trait/nested-return-type3-tait.stderr @@ -0,0 +1,17 @@ +warning: opaque type `impl Trait` does not satisfy its associated type bounds + --> $DIR/nested-return-type3-tait.rs:19:24 + | +LL | type Assoc: Duh; + | --- this associated type bound is unsatisfied for `Sendable` +... +LL | fn foo() -> impl Trait { + | ^^^^^^^^^^^^^^^^ + | + = note: `#[warn(opaque_hidden_inferred_bound)]` on by default +help: add this bound + | +LL | type Sendable = impl Send + Duh; + | +++++ + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/nested-return-type3-tait2.rs b/src/test/ui/impl-trait/nested-return-type3-tait2.rs index 56778ed90dc7b..5b6f78a989687 100644 --- a/src/test/ui/impl-trait/nested-return-type3-tait2.rs +++ b/src/test/ui/impl-trait/nested-return-type3-tait2.rs @@ -16,6 +16,7 @@ impl Trait for F { type Sendable = impl Send; type Traitable = impl Trait; +//~^ WARN opaque type `Traitable` does not satisfy its associated type bounds fn foo() -> Traitable { 42 diff --git a/src/test/ui/impl-trait/nested-return-type3-tait2.stderr b/src/test/ui/impl-trait/nested-return-type3-tait2.stderr new file mode 100644 index 0000000000000..c07f6ead75028 --- /dev/null +++ b/src/test/ui/impl-trait/nested-return-type3-tait2.stderr @@ -0,0 +1,17 @@ +warning: opaque type `Traitable` does not satisfy its associated type bounds + --> $DIR/nested-return-type3-tait2.rs:18:29 + | +LL | type Assoc: Duh; + | --- this associated type bound is unsatisfied for `Sendable` +... +LL | type Traitable = impl Trait; + | ^^^^^^^^^^^^^^^^ + | + = note: `#[warn(opaque_hidden_inferred_bound)]` on by default +help: add this bound + | +LL | type Sendable = impl Send + Duh; + | +++++ + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/nested-return-type3-tait3.rs b/src/test/ui/impl-trait/nested-return-type3-tait3.rs index 04c6c92b1a3c5..394d8f581102f 100644 --- a/src/test/ui/impl-trait/nested-return-type3-tait3.rs +++ b/src/test/ui/impl-trait/nested-return-type3-tait3.rs @@ -15,6 +15,7 @@ impl Trait for F { } type Traitable = impl Trait; +//~^ WARN opaque type `Traitable` does not satisfy its associated type bounds fn foo() -> Traitable { 42 diff --git a/src/test/ui/impl-trait/nested-return-type3-tait3.stderr b/src/test/ui/impl-trait/nested-return-type3-tait3.stderr new file mode 100644 index 0000000000000..d98ad89222fa7 --- /dev/null +++ b/src/test/ui/impl-trait/nested-return-type3-tait3.stderr @@ -0,0 +1,17 @@ +warning: opaque type `Traitable` does not satisfy its associated type bounds + --> $DIR/nested-return-type3-tait3.rs:17:29 + | +LL | type Assoc: Duh; + | --- this associated type bound is unsatisfied for `impl Send` +... +LL | type Traitable = impl Trait; + | ^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(opaque_hidden_inferred_bound)]` on by default +help: add this bound + | +LL | type Traitable = impl Trait; + | +++++ + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/nested-return-type3.rs b/src/test/ui/impl-trait/nested-return-type3.rs index 74b4dae22ebfd..5a764fc4c285a 100644 --- a/src/test/ui/impl-trait/nested-return-type3.rs +++ b/src/test/ui/impl-trait/nested-return-type3.rs @@ -13,6 +13,7 @@ impl Trait for F { } fn foo() -> impl Trait { + //~^ WARN opaque type `impl Trait` does not satisfy its associated type bounds 42 } diff --git a/src/test/ui/impl-trait/nested-return-type3.stderr b/src/test/ui/impl-trait/nested-return-type3.stderr new file mode 100644 index 0000000000000..632de71aa4c88 --- /dev/null +++ b/src/test/ui/impl-trait/nested-return-type3.stderr @@ -0,0 +1,17 @@ +warning: opaque type `impl Trait` does not satisfy its associated type bounds + --> $DIR/nested-return-type3.rs:15:24 + | +LL | type Assoc: Duh; + | --- this associated type bound is unsatisfied for `impl Send` +... +LL | fn foo() -> impl Trait { + | ^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(opaque_hidden_inferred_bound)]` on by default +help: add this bound + | +LL | fn foo() -> impl Trait { + | +++++ + +warning: 1 warning emitted + From 7a8854037b81e04bea5309e5b107bfe09fc841c0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 2 Oct 2022 19:07:31 +0000 Subject: [PATCH 3/3] Add example to opaque_hidden_inferred_bound lint --- .../src/opaque_hidden_inferred_bound.rs | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 7f18bf0018f6f..d8ce20db37ce9 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -11,13 +11,44 @@ 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. This functionality was - /// removed in #97346, but then rolled back in #99860 because it was made - /// into a hard error too quickly. + /// to satisfy the bounds of the associated type. /// - /// 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. + /// ### 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 { + /// 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"