1
Fork 0

Remove suspicious auto trait lint

This commit is contained in:
Santiago Pastorino 2024-02-19 17:39:25 -03:00
parent eee9d2a773
commit 086463b227
No known key found for this signature in database
GPG key ID: 8131A24E0C79EFAF
23 changed files with 78 additions and 527 deletions

View file

@ -1,20 +1,12 @@
//! Orphan checker: every impl either implements a trait defined in this
//! crate or pertains to a type defined in this crate.
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{DelayDm, ErrorGuaranteed};
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_middle::ty::util::CheckRegions;
use rustc_middle::ty::GenericArgs;
use rustc_middle::ty::{
self, AliasKind, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
TypeVisitor,
};
use rustc_session::lint;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_trait_selection::traits;
use std::ops::ControlFlow;
use crate::errors;
@ -26,12 +18,7 @@ pub(crate) fn orphan_check_impl(
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
trait_ref.error_reported()?;
let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id);
if tcx.trait_is_auto(trait_ref.def_id) {
lint_auto_trait_impl(tcx, trait_ref, impl_def_id);
}
ret
do_orphan_check_impl(tcx, trait_ref, impl_def_id)
}
fn do_orphan_check_impl<'tcx>(
@ -445,146 +432,3 @@ fn emit_orphan_check_error<'tcx>(
}
})
}
/// Lint impls of auto traits if they are likely to have
/// unsound or surprising effects on auto impls.
fn lint_auto_trait_impl<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
impl_def_id: LocalDefId,
) {
if trait_ref.args.len() != 1 {
tcx.dcx().span_delayed_bug(
tcx.def_span(impl_def_id),
"auto traits cannot have generic parameters",
);
return;
}
let self_ty = trait_ref.self_ty();
let (self_type_did, args) = match self_ty.kind() {
ty::Adt(def, args) => (def.did(), args),
_ => {
// FIXME: should also lint for stuff like `&i32` but
// considering that auto traits are unstable, that
// isn't too important for now as this only affects
// crates using `nightly`, and std.
return;
}
};
// Impls which completely cover a given root type are fine as they
// disable auto impls entirely. So only lint if the args
// are not a permutation of the identity args.
let Err(arg) = tcx.uses_unique_generic_params(args, CheckRegions::No) else {
// ok
return;
};
// Ideally:
//
// - compute the requirements for the auto impl candidate
// - check whether these are implied by the non covering impls
// - if not, emit the lint
//
// What we do here is a bit simpler:
//
// - badly check if an auto impl candidate definitely does not apply
// for the given simplified type
// - if so, do not lint
if fast_reject_auto_impl(tcx, trait_ref.def_id, self_ty) {
// ok
return;
}
tcx.node_span_lint(
lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS,
tcx.local_def_id_to_hir_id(impl_def_id),
tcx.def_span(impl_def_id),
DelayDm(|| {
format!(
"cross-crate traits with a default impl, like `{}`, \
should not be specialized",
tcx.def_path_str(trait_ref.def_id),
)
}),
|lint| {
let item_span = tcx.def_span(self_type_did);
let self_descr = tcx.def_descr(self_type_did);
match arg {
ty::util::NotUniqueParam::DuplicateParam(arg) => {
lint.note(format!("`{arg}` is mentioned multiple times"));
}
ty::util::NotUniqueParam::NotParam(arg) => {
lint.note(format!("`{arg}` is not a generic parameter"));
}
}
lint.span_note(
item_span,
format!(
"try using the same sequence of generic parameters as the {self_descr} definition",
),
);
},
);
}
fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool {
struct DisableAutoTraitVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
self_ty_root: Ty<'tcx>,
seen: FxHashSet<DefId>,
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for DisableAutoTraitVisitor<'tcx> {
type BreakTy = ();
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
let tcx = self.tcx;
if ty != self.self_ty_root {
for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, ty) {
match tcx.impl_polarity(impl_def_id) {
ImplPolarity::Negative => return ControlFlow::Break(()),
ImplPolarity::Reservation => {}
// FIXME(@lcnr): That's probably not good enough, idk
//
// We might just want to take the rustdoc code and somehow avoid
// explicit impls for `Self`.
ImplPolarity::Positive => return ControlFlow::Continue(()),
}
}
}
match ty.kind() {
ty::Adt(def, args) if def.is_phantom_data() => args.visit_with(self),
ty::Adt(def, args) => {
// @lcnr: This is the only place where cycles can happen. We avoid this
// by only visiting each `DefId` once.
//
// This will be is incorrect in subtle cases, but I don't care :)
if self.seen.insert(def.did()) {
for ty in def.all_fields().map(|field| field.ty(tcx, args)) {
ty.visit_with(self)?;
}
}
ControlFlow::Continue(())
}
_ => ty.super_visit_with(self),
}
}
}
let self_ty_root = match self_ty.kind() {
ty::Adt(def, _) => Ty::new_adt(tcx, *def, GenericArgs::identity_for_item(tcx, def.did())),
_ => unimplemented!("unexpected self ty {:?}", self_ty),
};
self_ty_root
.visit_with(&mut DisableAutoTraitVisitor {
tcx,
self_ty_root,
trait_def_id,
seen: FxHashSet::default(),
})
.is_break()
}