2022-10-02 05:45:15 +00:00
|
|
|
use rustc_hir as hir;
|
|
|
|
use rustc_infer::infer::TyCtxtInferExt;
|
2022-10-05 19:46:40 +00:00
|
|
|
use rustc_macros::{LintDiagnostic, Subdiagnostic};
|
2024-05-11 06:03:53 +00:00
|
|
|
use rustc_middle::ty::fold::BottomUpFolder;
|
|
|
|
use rustc_middle::ty::print::{PrintTraitPredicateExt as _, TraitPredPrintModifiersAndPath};
|
|
|
|
use rustc_middle::ty::{self, Ty, TypeFoldable};
|
2024-04-29 03:42:13 +00:00
|
|
|
use rustc_session::{declare_lint, declare_lint_pass};
|
2024-01-25 04:41:38 +00:00
|
|
|
use rustc_span::symbol::kw;
|
|
|
|
use rustc_span::Span;
|
2024-07-11 23:15:00 +00:00
|
|
|
use rustc_trait_selection::traits::{self, ObligationCtxt};
|
2022-10-02 05:45:15 +00:00
|
|
|
|
|
|
|
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
|
2022-10-02 19:07:31 +00:00
|
|
|
/// to satisfy the bounds of the associated type.
|
2022-10-02 05:45:15 +00:00
|
|
|
///
|
2022-10-02 19:07:31 +00:00
|
|
|
/// ### Explanation
|
|
|
|
///
|
|
|
|
/// This functionality was removed in #97346, but then rolled back in #99860
|
|
|
|
/// because it caused regressions.
|
|
|
|
///
|
2024-04-03 23:03:12 +00:00
|
|
|
/// We plan on reintroducing this as a hard error, but in the meantime,
|
2022-10-02 19:07:31 +00:00
|
|
|
/// this lint serves to warn and suggest fixes for any use-cases which rely
|
|
|
|
/// on this behavior.
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
2022-10-24 22:59:32 +00:00
|
|
|
/// ```rust
|
2023-02-27 20:47:06 +00:00
|
|
|
/// #![feature(type_alias_impl_trait)]
|
|
|
|
///
|
2022-10-24 22:59:32 +00:00
|
|
|
/// trait Duh {}
|
|
|
|
///
|
|
|
|
/// impl Duh for i32 {}
|
|
|
|
///
|
2022-10-02 19:07:31 +00:00
|
|
|
/// trait Trait {
|
2022-10-24 22:59:32 +00:00
|
|
|
/// type Assoc: Duh;
|
2022-10-02 19:07:31 +00:00
|
|
|
/// }
|
|
|
|
///
|
2022-10-24 22:59:32 +00:00
|
|
|
/// impl<F: Duh> Trait for F {
|
|
|
|
/// type Assoc = F;
|
2022-10-02 19:07:31 +00:00
|
|
|
/// }
|
|
|
|
///
|
2023-02-27 20:47:06 +00:00
|
|
|
/// type Tait = impl Sized;
|
|
|
|
///
|
|
|
|
/// fn test() -> impl Trait<Assoc = Tait> {
|
2022-10-24 22:59:32 +00:00
|
|
|
/// 42
|
2022-10-02 19:07:31 +00:00
|
|
|
/// }
|
2024-06-11 15:32:38 +00:00
|
|
|
///
|
|
|
|
/// fn main() {}
|
2022-10-02 19:07:31 +00:00
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
|
|
|
/// In this example, `test` declares that the associated type `Assoc` for
|
2023-10-03 11:50:07 +00:00
|
|
|
/// `impl Trait` is `impl Sized`, which does not satisfy the bound `Duh`
|
2022-10-02 19:07:31 +00:00
|
|
|
/// 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
|
2023-10-03 11:50:07 +00:00
|
|
|
/// fixed by changing `Tait = impl Sized` into `Tait = impl Sized + Duh`.
|
2022-10-02 05:45:15 +00:00
|
|
|
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>) {
|
2023-02-27 20:47:06 +00:00
|
|
|
let hir::ItemKind::OpaqueTy(opaque) = &item.kind else {
|
|
|
|
return;
|
|
|
|
};
|
2022-10-27 03:02:18 +00:00
|
|
|
let def_id = item.owner_id.def_id.to_def_id();
|
2022-10-02 05:45:15 +00:00
|
|
|
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.
|
2024-07-06 15:54:22 +00:00
|
|
|
for (pred, pred_span) in cx.tcx.explicit_item_bounds(def_id).iter_identity_copied() {
|
2024-01-30 01:27:06 +00:00
|
|
|
infcx.enter_forall(pred.kind(), |predicate| {
|
|
|
|
let ty::ClauseKind::Projection(proj) = predicate else {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
// Only check types, since those are the only things that may
|
|
|
|
// have opaques in them anyways.
|
2024-05-30 02:23:49 +00:00
|
|
|
let Some(proj_term) = proj.term.as_type() else { return };
|
2022-10-02 05:45:15 +00:00
|
|
|
|
2024-01-30 01:27:06 +00:00
|
|
|
// HACK: `impl Trait<Assoc = impl Trait2>` from an RPIT is "ok"...
|
|
|
|
if let ty::Alias(ty::Opaque, opaque_ty) = *proj_term.kind()
|
|
|
|
&& cx.tcx.parent(opaque_ty.def_id) == def_id
|
|
|
|
&& matches!(
|
|
|
|
opaque.origin,
|
|
|
|
hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2023-02-27 20:47:06 +00:00
|
|
|
|
2024-01-30 01:27:06 +00:00
|
|
|
// HACK: `async fn() -> Self` in traits is "ok"...
|
|
|
|
// This is not really that great, but it's similar to why the `-> Self`
|
|
|
|
// return type is well-formed in traits even when `Self` isn't sized.
|
|
|
|
if let ty::Param(param_ty) = *proj_term.kind()
|
|
|
|
&& param_ty.name == kw::SelfUpper
|
|
|
|
&& matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(_))
|
|
|
|
&& opaque.in_trait
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2024-01-25 04:41:38 +00:00
|
|
|
|
2024-06-21 17:33:08 +00:00
|
|
|
let proj_ty = Ty::new_projection_from_args(
|
2024-05-13 14:00:38 +00:00
|
|
|
cx.tcx,
|
|
|
|
proj.projection_term.def_id,
|
|
|
|
proj.projection_term.args,
|
|
|
|
);
|
2024-01-30 01:27:06 +00:00
|
|
|
// 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,
|
2022-10-02 05:45:15 +00:00
|
|
|
};
|
2024-01-30 01:27:06 +00:00
|
|
|
// 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
|
2024-05-13 14:00:38 +00:00
|
|
|
.explicit_item_bounds(proj.projection_term.def_id)
|
|
|
|
.iter_instantiated_copied(cx.tcx, proj.projection_term.args)
|
2024-01-30 01:27:06 +00:00
|
|
|
{
|
|
|
|
let assoc_pred = assoc_pred.fold_with(proj_replacer);
|
2024-07-11 23:15:00 +00:00
|
|
|
|
|
|
|
let ocx = ObligationCtxt::new(infcx);
|
|
|
|
let assoc_pred =
|
|
|
|
ocx.normalize(&traits::ObligationCause::dummy(), cx.param_env, assoc_pred);
|
|
|
|
if !ocx.select_all_or_error().is_empty() {
|
|
|
|
// Can't normalize for some reason...?
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ocx.register_obligation(traits::Obligation::new(
|
|
|
|
cx.tcx,
|
2024-01-30 01:27:06 +00:00
|
|
|
traits::ObligationCause::dummy(),
|
|
|
|
cx.param_env,
|
|
|
|
assoc_pred,
|
2024-07-11 23:15:00 +00:00
|
|
|
));
|
2024-01-30 01:27:06 +00:00
|
|
|
|
|
|
|
// 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!
|
2024-07-11 23:15:00 +00:00
|
|
|
if !ocx.select_all_or_error().is_empty() {
|
2024-01-30 01:27:06 +00:00
|
|
|
// 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::Alias(ty::Opaque, ty::AliasTy { def_id, .. }),
|
|
|
|
ty::ClauseKind::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_span_lint(
|
|
|
|
OPAQUE_HIDDEN_INFERRED_BOUND,
|
|
|
|
pred_span,
|
|
|
|
OpaqueHiddenInferredBoundLint {
|
|
|
|
ty: Ty::new_opaque(
|
|
|
|
cx.tcx,
|
|
|
|
def_id,
|
|
|
|
ty::GenericArgs::identity_for_item(cx.tcx, def_id),
|
|
|
|
),
|
|
|
|
proj_ty: proj_term,
|
|
|
|
assoc_pred_span,
|
|
|
|
add_bound,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2022-10-02 05:45:15 +00:00
|
|
|
}
|
2024-01-30 01:27:06 +00:00
|
|
|
});
|
2022-10-02 05:45:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(LintDiagnostic)]
|
2022-10-22 09:07:54 +00:00
|
|
|
#[diag(lint_opaque_hidden_inferred_bound)]
|
2022-10-02 05:45:15 +00:00
|
|
|
struct OpaqueHiddenInferredBoundLint<'tcx> {
|
|
|
|
ty: Ty<'tcx>,
|
|
|
|
proj_ty: Ty<'tcx>,
|
2022-10-13 09:13:02 +00:00
|
|
|
#[label(lint_specifically)]
|
2022-10-02 05:45:15 +00:00
|
|
|
assoc_pred_span: Span,
|
2022-10-05 19:55:19 +00:00
|
|
|
#[subdiagnostic]
|
|
|
|
add_bound: Option<AddBound<'tcx>>,
|
2022-10-05 19:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Subdiagnostic)]
|
2022-10-22 13:48:55 +00:00
|
|
|
#[suggestion(
|
2022-10-22 09:07:54 +00:00
|
|
|
lint_opaque_hidden_inferred_bound_sugg,
|
2022-10-22 13:48:55 +00:00
|
|
|
style = "verbose",
|
2022-10-05 19:46:40 +00:00
|
|
|
applicability = "machine-applicable",
|
|
|
|
code = " + {trait_ref}"
|
|
|
|
)]
|
|
|
|
struct AddBound<'tcx> {
|
|
|
|
#[primary_span]
|
|
|
|
suggest_span: Span,
|
|
|
|
#[skip_arg]
|
|
|
|
trait_ref: TraitPredPrintModifiersAndPath<'tcx>,
|
2022-10-02 05:45:15 +00:00
|
|
|
}
|