Auto merge of #96531 - kckeiks:remove-item-like-visitor-from-rustc-typeck, r=cjgillot
Remove ItemLikeVisitor impls from rustc_typeck Issue #95004 cc `@cjgillot`
This commit is contained in:
commit
f6e5570460
23 changed files with 512 additions and 553 deletions
|
@ -6,7 +6,7 @@ use rustc_session::cstore::ForeignModule;
|
|||
crate fn collect(tcx: TyCtxt<'_>) -> Vec<ForeignModule> {
|
||||
let mut modules = Vec::new();
|
||||
for id in tcx.hir().items() {
|
||||
if !matches!(tcx.hir().def_kind(id.def_id), DefKind::ForeignMod) {
|
||||
if !matches!(tcx.def_kind(id.def_id), DefKind::ForeignMod) {
|
||||
continue;
|
||||
}
|
||||
let item = tcx.hir().item(id);
|
||||
|
|
|
@ -36,7 +36,7 @@ struct Collector<'tcx> {
|
|||
|
||||
impl<'tcx> Collector<'tcx> {
|
||||
fn process_item(&mut self, id: rustc_hir::ItemId) {
|
||||
if !matches!(self.tcx.hir().def_kind(id.def_id), DefKind::ForeignMod) {
|
||||
if !matches!(self.tcx.def_kind(id.def_id), DefKind::ForeignMod) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1813,7 +1813,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
FxHashMap::default();
|
||||
|
||||
for id in tcx.hir().items() {
|
||||
if matches!(tcx.hir().def_kind(id.def_id), DefKind::Impl) {
|
||||
if matches!(tcx.def_kind(id.def_id), DefKind::Impl) {
|
||||
if let Some(trait_ref) = tcx.impl_trait_ref(id.def_id.to_def_id()) {
|
||||
let simplified_self_ty = fast_reject::simplify_type(
|
||||
self.tcx,
|
||||
|
|
|
@ -308,11 +308,6 @@ impl<'hir> Map<'hir> {
|
|||
Some(def_kind)
|
||||
}
|
||||
|
||||
pub fn def_kind(self, local_def_id: LocalDefId) -> DefKind {
|
||||
self.opt_def_kind(local_def_id)
|
||||
.unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", local_def_id))
|
||||
}
|
||||
|
||||
pub fn find_parent_node(self, id: HirId) -> Option<HirId> {
|
||||
if id.local_id == ItemLocalId::from_u32(0) {
|
||||
Some(self.tcx.hir_owner_parent(id.owner))
|
||||
|
|
|
@ -2676,7 +2676,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N
|
|||
// Iterate all local crate items no matter where they are defined.
|
||||
let hir = tcx.hir();
|
||||
for id in hir.items() {
|
||||
if matches!(hir.def_kind(id.def_id), DefKind::Use) {
|
||||
if matches!(tcx.def_kind(id.def_id), DefKind::Use) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -1164,7 +1164,7 @@ struct RootCollector<'a, 'tcx> {
|
|||
|
||||
impl<'v> RootCollector<'_, 'v> {
|
||||
fn process_item(&mut self, id: hir::ItemId) {
|
||||
match self.tcx.hir().def_kind(id.def_id) {
|
||||
match self.tcx.def_kind(id.def_id) {
|
||||
DefKind::Enum | DefKind::Struct | DefKind::Union => {
|
||||
let item = self.tcx.hir().item(id);
|
||||
match item.kind {
|
||||
|
@ -1225,7 +1225,7 @@ impl<'v> RootCollector<'_, 'v> {
|
|||
}
|
||||
|
||||
fn process_impl_item(&mut self, id: hir::ImplItemId) {
|
||||
if matches!(self.tcx.hir().def_kind(id.def_id), DefKind::AssocFn) {
|
||||
if matches!(self.tcx.def_kind(id.def_id), DefKind::AssocFn) {
|
||||
self.push_if_root(id.def_id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -240,9 +240,9 @@ fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems {
|
|||
let crate_items = tcx.hir_crate_items(());
|
||||
|
||||
for id in crate_items.items() {
|
||||
collector.check_for_lang(Target::from_def_kind(tcx.hir().def_kind(id.def_id)), id.hir_id());
|
||||
collector.check_for_lang(Target::from_def_kind(tcx.def_kind(id.def_id)), id.hir_id());
|
||||
|
||||
if matches!(tcx.hir().def_kind(id.def_id), DefKind::Enum) {
|
||||
if matches!(tcx.def_kind(id.def_id), DefKind::Enum) {
|
||||
let item = tcx.hir().item(id);
|
||||
if let hir::ItemKind::Enum(def, ..) = &item.kind {
|
||||
for variant in def.variants {
|
||||
|
|
|
@ -27,6 +27,7 @@ use rustc_trait_selection::traits;
|
|||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
||||
use rustc_ty_utils::representability::{self, Representability};
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
use std::iter;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
|
@ -711,28 +712,35 @@ fn check_opaque_meets_bounds<'tcx>(
|
|||
});
|
||||
}
|
||||
|
||||
pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) {
|
||||
pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
|
||||
debug!(
|
||||
"check_item_type(it.def_id={:?}, it.name={})",
|
||||
it.def_id,
|
||||
tcx.def_path_str(it.def_id.to_def_id())
|
||||
id.def_id,
|
||||
tcx.def_path_str(id.def_id.to_def_id())
|
||||
);
|
||||
let _indenter = indenter();
|
||||
match it.kind {
|
||||
// Consts can play a role in type-checking, so they are included here.
|
||||
hir::ItemKind::Static(..) => {
|
||||
tcx.ensure().typeck(it.def_id);
|
||||
maybe_check_static_with_link_section(tcx, it.def_id, it.span);
|
||||
check_static_inhabited(tcx, it.def_id, it.span);
|
||||
match tcx.def_kind(id.def_id) {
|
||||
DefKind::Static(..) => {
|
||||
tcx.ensure().typeck(id.def_id);
|
||||
maybe_check_static_with_link_section(tcx, id.def_id, tcx.def_span(id.def_id));
|
||||
check_static_inhabited(tcx, id.def_id, tcx.def_span(id.def_id));
|
||||
}
|
||||
hir::ItemKind::Const(..) => {
|
||||
tcx.ensure().typeck(it.def_id);
|
||||
DefKind::Const => {
|
||||
tcx.ensure().typeck(id.def_id);
|
||||
}
|
||||
hir::ItemKind::Enum(ref enum_definition, _) => {
|
||||
check_enum(tcx, it.span, &enum_definition.variants, it.def_id);
|
||||
DefKind::Enum => {
|
||||
let item = tcx.hir().item(id);
|
||||
let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else {
|
||||
return;
|
||||
};
|
||||
check_enum(tcx, item.span, &enum_definition.variants, item.def_id);
|
||||
}
|
||||
hir::ItemKind::Fn(..) => {} // entirely within check_item_body
|
||||
hir::ItemKind::Impl(ref impl_) => {
|
||||
DefKind::Fn => {} // entirely within check_item_body
|
||||
DefKind::Impl => {
|
||||
let it = tcx.hir().item(id);
|
||||
let hir::ItemKind::Impl(ref impl_) = it.kind else {
|
||||
return;
|
||||
};
|
||||
debug!("ItemKind::Impl {} with id {:?}", it.ident, it.def_id);
|
||||
if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.def_id) {
|
||||
check_impl_items_against_trait(
|
||||
|
@ -745,7 +753,11 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) {
|
|||
check_on_unimplemented(tcx, it);
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Trait(_, _, _, _, ref items) => {
|
||||
DefKind::Trait => {
|
||||
let it = tcx.hir().item(id);
|
||||
let hir::ItemKind::Trait(_, _, _, _, ref items) = it.kind else {
|
||||
return;
|
||||
};
|
||||
check_on_unimplemented(tcx, it);
|
||||
|
||||
for item in items.iter() {
|
||||
|
@ -771,28 +783,36 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) {
|
|||
}
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Struct(..) => {
|
||||
check_struct(tcx, it.def_id, it.span);
|
||||
DefKind::Struct => {
|
||||
check_struct(tcx, id.def_id, tcx.def_span(id.def_id));
|
||||
}
|
||||
hir::ItemKind::Union(..) => {
|
||||
check_union(tcx, it.def_id, it.span);
|
||||
DefKind::Union => {
|
||||
check_union(tcx, id.def_id, tcx.def_span(id.def_id));
|
||||
}
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
|
||||
DefKind::OpaqueTy => {
|
||||
let item = tcx.hir().item(id);
|
||||
let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
|
||||
return;
|
||||
};
|
||||
// HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
|
||||
// `async-std` (and `pub async fn` in general).
|
||||
// Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it!
|
||||
// See https://github.com/rust-lang/rust/issues/75100
|
||||
if !tcx.sess.opts.actually_rustdoc {
|
||||
let substs = InternalSubsts::identity_for_item(tcx, it.def_id.to_def_id());
|
||||
check_opaque(tcx, it.def_id, substs, it.span, &origin);
|
||||
let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id());
|
||||
check_opaque(tcx, item.def_id, substs, item.span, &origin);
|
||||
}
|
||||
}
|
||||
hir::ItemKind::TyAlias(..) => {
|
||||
let pty_ty = tcx.type_of(it.def_id);
|
||||
let generics = tcx.generics_of(it.def_id);
|
||||
DefKind::TyAlias => {
|
||||
let pty_ty = tcx.type_of(id.def_id);
|
||||
let generics = tcx.generics_of(id.def_id);
|
||||
check_type_params_are_used(tcx, &generics, pty_ty);
|
||||
}
|
||||
hir::ItemKind::ForeignMod { abi, items } => {
|
||||
DefKind::ForeignMod => {
|
||||
let it = tcx.hir().item(id);
|
||||
let hir::ItemKind::ForeignMod { abi, items } = it.kind else {
|
||||
return;
|
||||
};
|
||||
check_abi(tcx, it.hir_id(), it.span, abi);
|
||||
|
||||
if abi == Abi::RustIntrinsic {
|
||||
|
@ -851,7 +871,7 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) {
|
|||
}
|
||||
}
|
||||
}
|
||||
_ => { /* nothing to do */ }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1451,7 +1471,10 @@ pub(super) fn check_type_params_are_used<'tcx>(
|
|||
}
|
||||
|
||||
pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
|
||||
tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx });
|
||||
let module = tcx.hir_module_items(module_def_id);
|
||||
for id in module.items() {
|
||||
check_item_type(tcx, id);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) use wfcheck::check_item_well_formed;
|
||||
|
|
|
@ -151,7 +151,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
|
||||
sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
|
||||
match def_id.as_local().map(|def_id| hir.def_kind(def_id)) {
|
||||
match def_id.as_local().map(|def_id| self.tcx.def_kind(def_id)) {
|
||||
Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
|
||||
msg = "instantiate this tuple variant";
|
||||
}
|
||||
|
|
|
@ -111,7 +111,6 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc_hir::{HirIdMap, ImplicitSelfKind, Node};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::vec::Idx;
|
||||
|
@ -933,19 +932,6 @@ impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
struct CheckItemTypesVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'tcx> {
|
||||
fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
|
||||
check_item_type(self.tcx, i);
|
||||
}
|
||||
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {}
|
||||
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {}
|
||||
fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {}
|
||||
}
|
||||
|
||||
fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) {
|
||||
tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id));
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
|
|||
}
|
||||
|
||||
for id in tcx.hir().items() {
|
||||
if matches!(tcx.hir().def_kind(id.def_id), DefKind::Use) {
|
||||
if matches!(tcx.def_kind(id.def_id), DefKind::Use) {
|
||||
if tcx.visibility(id.def_id).is_public() {
|
||||
continue;
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
|
|||
let mut crates_to_lint = vec![];
|
||||
|
||||
for id in tcx.hir().items() {
|
||||
if matches!(tcx.hir().def_kind(id.def_id), DefKind::ExternCrate) {
|
||||
if matches!(tcx.def_kind(id.def_id), DefKind::ExternCrate) {
|
||||
let item = tcx.hir().item(id);
|
||||
if let hir::ItemKind::ExternCrate(orig_name) = item.kind {
|
||||
crates_to_lint.push(ExternCrateToLint {
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
|
||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams};
|
||||
use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt};
|
||||
use rustc_span::symbol::sym;
|
||||
|
@ -19,7 +19,9 @@ use rustc_span::Span;
|
|||
/// On-demand query: yields a map containing all types mapped to their inherent impls.
|
||||
pub fn crate_inherent_impls(tcx: TyCtxt<'_>, (): ()) -> CrateInherentImpls {
|
||||
let mut collect = InherentCollect { tcx, impls_map: Default::default() };
|
||||
tcx.hir().visit_all_item_likes(&mut collect);
|
||||
for id in tcx.hir().items() {
|
||||
collect.check_item(id);
|
||||
}
|
||||
collect.impls_map
|
||||
}
|
||||
|
||||
|
@ -46,79 +48,6 @@ struct InherentCollect<'tcx> {
|
|||
impls_map: CrateInherentImpls,
|
||||
}
|
||||
|
||||
impl<'tcx> ItemLikeVisitor<'_> for InherentCollect<'tcx> {
|
||||
fn visit_item(&mut self, item: &hir::Item<'_>) {
|
||||
let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, ref items, .. }) = item.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
let self_ty = self.tcx.type_of(item.def_id);
|
||||
match *self_ty.kind() {
|
||||
ty::Adt(def, _) => {
|
||||
self.check_def_id(item, self_ty, def.did());
|
||||
}
|
||||
ty::Foreign(did) => {
|
||||
self.check_def_id(item, self_ty, did);
|
||||
}
|
||||
ty::Dynamic(data, ..) if data.principal_def_id().is_some() => {
|
||||
self.check_def_id(item, self_ty, data.principal_def_id().unwrap());
|
||||
}
|
||||
ty::Dynamic(..) => {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
ty.span,
|
||||
E0785,
|
||||
"cannot define inherent `impl` for a dyn auto trait"
|
||||
)
|
||||
.span_label(ty.span, "impl requires at least one non-auto trait")
|
||||
.note("define and implement a new trait or type instead")
|
||||
.emit();
|
||||
}
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Str
|
||||
| ty::Array(..)
|
||||
| ty::Slice(_)
|
||||
| ty::RawPtr(_)
|
||||
| ty::Ref(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span),
|
||||
ty::FnPtr(_) | ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => {
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
ty.span,
|
||||
E0118,
|
||||
"no nominal type found for inherent implementation"
|
||||
);
|
||||
|
||||
err.span_label(ty.span, "impl requires a nominal type")
|
||||
.note("either implement a trait on it or create a newtype to wrap it instead");
|
||||
|
||||
err.emit();
|
||||
}
|
||||
ty::FnDef(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(_) => {
|
||||
bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty);
|
||||
}
|
||||
ty::Error(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
|
||||
|
||||
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {}
|
||||
|
||||
fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
|
||||
}
|
||||
|
||||
const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible";
|
||||
const INTO_DEFINING_CRATE: &str =
|
||||
"consider moving this inherent impl into the crate defining the type if possible";
|
||||
|
@ -246,4 +175,74 @@ impl<'tcx> InherentCollect<'tcx> {
|
|||
bug!("unexpected primitive type: {:?}", ty);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item(&mut self, id: hir::ItemId) {
|
||||
if !matches!(self.tcx.def_kind(id.def_id), DefKind::Impl) {
|
||||
return;
|
||||
}
|
||||
|
||||
let item = self.tcx.hir().item(id);
|
||||
let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, ref items, .. }) = item.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
let self_ty = self.tcx.type_of(item.def_id);
|
||||
match *self_ty.kind() {
|
||||
ty::Adt(def, _) => {
|
||||
self.check_def_id(item, self_ty, def.did());
|
||||
}
|
||||
ty::Foreign(did) => {
|
||||
self.check_def_id(item, self_ty, did);
|
||||
}
|
||||
ty::Dynamic(data, ..) if data.principal_def_id().is_some() => {
|
||||
self.check_def_id(item, self_ty, data.principal_def_id().unwrap());
|
||||
}
|
||||
ty::Dynamic(..) => {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
ty.span,
|
||||
E0785,
|
||||
"cannot define inherent `impl` for a dyn auto trait"
|
||||
)
|
||||
.span_label(ty.span, "impl requires at least one non-auto trait")
|
||||
.note("define and implement a new trait or type instead")
|
||||
.emit();
|
||||
}
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Str
|
||||
| ty::Array(..)
|
||||
| ty::Slice(_)
|
||||
| ty::RawPtr(_)
|
||||
| ty::Ref(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span),
|
||||
ty::FnPtr(_) | ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => {
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
ty.span,
|
||||
E0118,
|
||||
"no nominal type found for inherent implementation"
|
||||
);
|
||||
|
||||
err.span_label(ty.span, "impl requires a nominal type")
|
||||
.note("either implement a trait on it or create a newtype to wrap it instead");
|
||||
|
||||
err.emit();
|
||||
}
|
||||
ty::FnDef(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(_) => {
|
||||
bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty);
|
||||
}
|
||||
ty::Error(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_middle::traits::specialization_graph::OverlapMode;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
|
@ -12,7 +12,10 @@ use smallvec::SmallVec;
|
|||
use std::collections::hash_map::Entry;
|
||||
|
||||
pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, (): ()) {
|
||||
tcx.hir().visit_all_item_likes(&mut InherentOverlapChecker { tcx });
|
||||
let mut inherent_overlap_checker = InherentOverlapChecker { tcx };
|
||||
for id in tcx.hir().items() {
|
||||
inherent_overlap_checker.check_item(id);
|
||||
}
|
||||
}
|
||||
|
||||
struct InherentOverlapChecker<'tcx> {
|
||||
|
@ -121,200 +124,184 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
|
|||
|| true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ItemLikeVisitor<'_> for InherentOverlapChecker<'tcx> {
|
||||
fn visit_item(&mut self, item: &hir::Item<'_>) {
|
||||
match item.kind {
|
||||
hir::ItemKind::Enum(..)
|
||||
| hir::ItemKind::Struct(..)
|
||||
| hir::ItemKind::Trait(..)
|
||||
| hir::ItemKind::Union(..) => {
|
||||
let impls = self.tcx.inherent_impls(item.def_id);
|
||||
fn check_item(&mut self, id: hir::ItemId) {
|
||||
let def_kind = self.tcx.def_kind(id.def_id);
|
||||
if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is only one inherent impl block,
|
||||
// there is nothing to overlap check it with
|
||||
if impls.len() <= 1 {
|
||||
return;
|
||||
let impls = self.tcx.inherent_impls(id.def_id);
|
||||
|
||||
// If there is only one inherent impl block,
|
||||
// there is nothing to overlap check it with
|
||||
if impls.len() <= 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
let overlap_mode = OverlapMode::get(self.tcx, id.def_id.to_def_id());
|
||||
|
||||
let impls_items = impls
|
||||
.iter()
|
||||
.map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id)))
|
||||
.collect::<SmallVec<[_; 8]>>();
|
||||
|
||||
// Perform a O(n^2) algorithm for small n,
|
||||
// otherwise switch to an allocating algorithm with
|
||||
// faster asymptotic runtime.
|
||||
const ALLOCATING_ALGO_THRESHOLD: usize = 500;
|
||||
if impls.len() < ALLOCATING_ALGO_THRESHOLD {
|
||||
for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() {
|
||||
for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] {
|
||||
if self.impls_have_common_items(impl_items1, impl_items2) {
|
||||
self.check_for_overlapping_inherent_impls(
|
||||
overlap_mode,
|
||||
impl1_def_id,
|
||||
impl2_def_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Build a set of connected regions of impl blocks.
|
||||
// Two impl blocks are regarded as connected if they share
|
||||
// an item with the same unhygienic identifier.
|
||||
// After we have assembled the connected regions,
|
||||
// run the O(n^2) algorithm on each connected region.
|
||||
// This is advantageous to running the algorithm over the
|
||||
// entire graph when there are many connected regions.
|
||||
|
||||
let overlap_mode = OverlapMode::get(self.tcx, item.def_id.to_def_id());
|
||||
rustc_index::newtype_index! {
|
||||
pub struct RegionId {
|
||||
ENCODABLE = custom
|
||||
}
|
||||
}
|
||||
struct ConnectedRegion {
|
||||
idents: SmallVec<[Symbol; 8]>,
|
||||
impl_blocks: FxHashSet<usize>,
|
||||
}
|
||||
let mut connected_regions: IndexVec<RegionId, _> = Default::default();
|
||||
// Reverse map from the Symbol to the connected region id.
|
||||
let mut connected_region_ids = FxHashMap::default();
|
||||
|
||||
let impls_items = impls
|
||||
.iter()
|
||||
.map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id)))
|
||||
.collect::<SmallVec<[_; 8]>>();
|
||||
|
||||
// Perform a O(n^2) algorithm for small n,
|
||||
// otherwise switch to an allocating algorithm with
|
||||
// faster asymptotic runtime.
|
||||
const ALLOCATING_ALGO_THRESHOLD: usize = 500;
|
||||
if impls.len() < ALLOCATING_ALGO_THRESHOLD {
|
||||
for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() {
|
||||
for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] {
|
||||
if self.impls_have_common_items(impl_items1, impl_items2) {
|
||||
self.check_for_overlapping_inherent_impls(
|
||||
overlap_mode,
|
||||
impl1_def_id,
|
||||
impl2_def_id,
|
||||
);
|
||||
}
|
||||
for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() {
|
||||
if impl_items.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
// First obtain a list of existing connected region ids
|
||||
let mut idents_to_add = SmallVec::<[Symbol; 8]>::new();
|
||||
let mut ids = impl_items
|
||||
.in_definition_order()
|
||||
.filter_map(|item| {
|
||||
let entry = connected_region_ids.entry(item.name);
|
||||
if let Entry::Occupied(e) = &entry {
|
||||
Some(*e.get())
|
||||
} else {
|
||||
idents_to_add.push(item.name);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<SmallVec<[RegionId; 8]>>();
|
||||
// Sort the id list so that the algorithm is deterministic
|
||||
ids.sort_unstable();
|
||||
ids.dedup();
|
||||
let ids = ids;
|
||||
match &ids[..] {
|
||||
// Create a new connected region
|
||||
[] => {
|
||||
let id_to_set = connected_regions.next_index();
|
||||
// Update the connected region ids
|
||||
for ident in &idents_to_add {
|
||||
connected_region_ids.insert(*ident, id_to_set);
|
||||
}
|
||||
connected_regions.insert(
|
||||
id_to_set,
|
||||
ConnectedRegion {
|
||||
idents: idents_to_add,
|
||||
impl_blocks: std::iter::once(i).collect(),
|
||||
},
|
||||
);
|
||||
}
|
||||
// Take the only id inside the list
|
||||
&[id_to_set] => {
|
||||
let region = connected_regions[id_to_set].as_mut().unwrap();
|
||||
region.impl_blocks.insert(i);
|
||||
region.idents.extend_from_slice(&idents_to_add);
|
||||
// Update the connected region ids
|
||||
for ident in &idents_to_add {
|
||||
connected_region_ids.insert(*ident, id_to_set);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Build a set of connected regions of impl blocks.
|
||||
// Two impl blocks are regarded as connected if they share
|
||||
// an item with the same unhygienic identifier.
|
||||
// After we have assembled the connected regions,
|
||||
// run the O(n^2) algorithm on each connected region.
|
||||
// This is advantageous to running the algorithm over the
|
||||
// entire graph when there are many connected regions.
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
pub struct RegionId {
|
||||
ENCODABLE = custom
|
||||
// We have multiple connected regions to merge.
|
||||
// In the worst case this might add impl blocks
|
||||
// one by one and can thus be O(n^2) in the size
|
||||
// of the resulting final connected region, but
|
||||
// this is no issue as the final step to check
|
||||
// for overlaps runs in O(n^2) as well.
|
||||
&[id_to_set, ..] => {
|
||||
let mut region = connected_regions.remove(id_to_set).unwrap();
|
||||
region.impl_blocks.insert(i);
|
||||
region.idents.extend_from_slice(&idents_to_add);
|
||||
// Update the connected region ids
|
||||
for ident in &idents_to_add {
|
||||
connected_region_ids.insert(*ident, id_to_set);
|
||||
}
|
||||
}
|
||||
struct ConnectedRegion {
|
||||
idents: SmallVec<[Symbol; 8]>,
|
||||
impl_blocks: FxHashSet<usize>,
|
||||
}
|
||||
let mut connected_regions: IndexVec<RegionId, _> = Default::default();
|
||||
// Reverse map from the Symbol to the connected region id.
|
||||
let mut connected_region_ids = FxHashMap::default();
|
||||
|
||||
for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() {
|
||||
if impl_items.len() == 0 {
|
||||
continue;
|
||||
// Remove other regions from ids.
|
||||
for &id in ids.iter() {
|
||||
if id == id_to_set {
|
||||
continue;
|
||||
}
|
||||
let r = connected_regions.remove(id).unwrap();
|
||||
for ident in r.idents.iter() {
|
||||
connected_region_ids.insert(*ident, id_to_set);
|
||||
}
|
||||
region.idents.extend_from_slice(&r.idents);
|
||||
region.impl_blocks.extend(r.impl_blocks);
|
||||
}
|
||||
// First obtain a list of existing connected region ids
|
||||
let mut idents_to_add = SmallVec::<[Symbol; 8]>::new();
|
||||
let mut ids = impl_items
|
||||
.in_definition_order()
|
||||
.filter_map(|item| {
|
||||
let entry = connected_region_ids.entry(item.name);
|
||||
if let Entry::Occupied(e) = &entry {
|
||||
Some(*e.get())
|
||||
} else {
|
||||
idents_to_add.push(item.name);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<SmallVec<[RegionId; 8]>>();
|
||||
// Sort the id list so that the algorithm is deterministic
|
||||
ids.sort_unstable();
|
||||
ids.dedup();
|
||||
let ids = ids;
|
||||
match &ids[..] {
|
||||
// Create a new connected region
|
||||
[] => {
|
||||
let id_to_set = connected_regions.next_index();
|
||||
// Update the connected region ids
|
||||
for ident in &idents_to_add {
|
||||
connected_region_ids.insert(*ident, id_to_set);
|
||||
}
|
||||
connected_regions.insert(
|
||||
id_to_set,
|
||||
ConnectedRegion {
|
||||
idents: idents_to_add,
|
||||
impl_blocks: std::iter::once(i).collect(),
|
||||
},
|
||||
);
|
||||
}
|
||||
// Take the only id inside the list
|
||||
&[id_to_set] => {
|
||||
let region = connected_regions[id_to_set].as_mut().unwrap();
|
||||
region.impl_blocks.insert(i);
|
||||
region.idents.extend_from_slice(&idents_to_add);
|
||||
// Update the connected region ids
|
||||
for ident in &idents_to_add {
|
||||
connected_region_ids.insert(*ident, id_to_set);
|
||||
}
|
||||
}
|
||||
// We have multiple connected regions to merge.
|
||||
// In the worst case this might add impl blocks
|
||||
// one by one and can thus be O(n^2) in the size
|
||||
// of the resulting final connected region, but
|
||||
// this is no issue as the final step to check
|
||||
// for overlaps runs in O(n^2) as well.
|
||||
&[id_to_set, ..] => {
|
||||
let mut region = connected_regions.remove(id_to_set).unwrap();
|
||||
region.impl_blocks.insert(i);
|
||||
region.idents.extend_from_slice(&idents_to_add);
|
||||
// Update the connected region ids
|
||||
for ident in &idents_to_add {
|
||||
connected_region_ids.insert(*ident, id_to_set);
|
||||
}
|
||||
|
||||
// Remove other regions from ids.
|
||||
for &id in ids.iter() {
|
||||
if id == id_to_set {
|
||||
continue;
|
||||
}
|
||||
let r = connected_regions.remove(id).unwrap();
|
||||
for ident in r.idents.iter() {
|
||||
connected_region_ids.insert(*ident, id_to_set);
|
||||
}
|
||||
region.idents.extend_from_slice(&r.idents);
|
||||
region.impl_blocks.extend(r.impl_blocks);
|
||||
}
|
||||
|
||||
connected_regions.insert(id_to_set, region);
|
||||
}
|
||||
}
|
||||
connected_regions.insert(id_to_set, region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
"churning through {} components (sum={}, avg={}, var={}, max={})",
|
||||
connected_regions.len(),
|
||||
impls.len(),
|
||||
impls.len() / connected_regions.len(),
|
||||
{
|
||||
let avg = impls.len() / connected_regions.len();
|
||||
let s = connected_regions
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|r| r.impl_blocks.len() as isize - avg as isize)
|
||||
.map(|v| v.abs() as usize)
|
||||
.sum::<usize>();
|
||||
s / connected_regions.len()
|
||||
},
|
||||
connected_regions
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|r| r.impl_blocks.len())
|
||||
.max()
|
||||
.unwrap()
|
||||
);
|
||||
// List of connected regions is built. Now, run the overlap check
|
||||
// for each pair of impl blocks in the same connected region.
|
||||
for region in connected_regions.into_iter().flatten() {
|
||||
let mut impl_blocks =
|
||||
region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>();
|
||||
impl_blocks.sort_unstable();
|
||||
for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() {
|
||||
let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx];
|
||||
for &impl2_items_idx in impl_blocks[(i + 1)..].iter() {
|
||||
let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx];
|
||||
if self.impls_have_common_items(impl_items1, impl_items2) {
|
||||
self.check_for_overlapping_inherent_impls(
|
||||
overlap_mode,
|
||||
impl1_def_id,
|
||||
impl2_def_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
debug!(
|
||||
"churning through {} components (sum={}, avg={}, var={}, max={})",
|
||||
connected_regions.len(),
|
||||
impls.len(),
|
||||
impls.len() / connected_regions.len(),
|
||||
{
|
||||
let avg = impls.len() / connected_regions.len();
|
||||
let s = connected_regions
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|r| r.impl_blocks.len() as isize - avg as isize)
|
||||
.map(|v| v.abs() as usize)
|
||||
.sum::<usize>();
|
||||
s / connected_regions.len()
|
||||
},
|
||||
connected_regions.iter().flatten().map(|r| r.impl_blocks.len()).max().unwrap()
|
||||
);
|
||||
// List of connected regions is built. Now, run the overlap check
|
||||
// for each pair of impl blocks in the same connected region.
|
||||
for region in connected_regions.into_iter().flatten() {
|
||||
let mut impl_blocks =
|
||||
region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>();
|
||||
impl_blocks.sort_unstable();
|
||||
for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() {
|
||||
let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx];
|
||||
for &impl2_items_idx in impl_blocks[(i + 1)..].iter() {
|
||||
let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx];
|
||||
if self.impls_have_common_items(impl_items1, impl_items2) {
|
||||
self.check_for_overlapping_inherent_impls(
|
||||
overlap_mode,
|
||||
impl1_def_id,
|
||||
impl2_def_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
|
||||
|
||||
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {}
|
||||
|
||||
fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
|
||||
}
|
||||
|
|
|
@ -3,101 +3,83 @@
|
|||
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::Unsafety;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
pub fn check(tcx: TyCtxt<'_>) {
|
||||
let mut unsafety = UnsafetyChecker { tcx };
|
||||
tcx.hir().visit_all_item_likes(&mut unsafety);
|
||||
}
|
||||
|
||||
struct UnsafetyChecker<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> UnsafetyChecker<'tcx> {
|
||||
fn check_unsafety_coherence(
|
||||
&mut self,
|
||||
item: &hir::Item<'_>,
|
||||
impl_generics: Option<&hir::Generics<'_>>,
|
||||
unsafety: hir::Unsafety,
|
||||
polarity: hir::ImplPolarity,
|
||||
) {
|
||||
if let Some(trait_ref) = self.tcx.impl_trait_ref(item.def_id) {
|
||||
let trait_def = self.tcx.trait_def(trait_ref.def_id);
|
||||
let unsafe_attr = impl_generics.and_then(|generics| {
|
||||
generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle")
|
||||
});
|
||||
match (trait_def.unsafety, unsafe_attr, unsafety, polarity) {
|
||||
(Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
item.span,
|
||||
E0199,
|
||||
"implementing the trait `{}` is not unsafe",
|
||||
trait_ref.print_only_trait_path()
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
(Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
item.span,
|
||||
E0200,
|
||||
"the trait `{}` requires an `unsafe impl` declaration",
|
||||
trait_ref.print_only_trait_path()
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
(
|
||||
Unsafety::Normal,
|
||||
Some(attr_name),
|
||||
Unsafety::Normal,
|
||||
hir::ImplPolarity::Positive,
|
||||
) => {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
item.span,
|
||||
E0569,
|
||||
"requires an `unsafe impl` declaration due to `#[{}]` attribute",
|
||||
attr_name
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
(_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => {
|
||||
// Reported in AST validation
|
||||
self.tcx.sess.delay_span_bug(item.span, "unsafe negative impl");
|
||||
}
|
||||
(_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_))
|
||||
| (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive)
|
||||
| (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive)
|
||||
| (Unsafety::Normal, None, Unsafety::Normal, _) => {
|
||||
// OK
|
||||
}
|
||||
for id in tcx.hir().items() {
|
||||
if matches!(tcx.def_kind(id.def_id), DefKind::Impl) {
|
||||
let item = tcx.hir().item(id);
|
||||
if let hir::ItemKind::Impl(ref impl_) = item.kind {
|
||||
check_unsafety_coherence(
|
||||
tcx,
|
||||
item,
|
||||
Some(&impl_.generics),
|
||||
impl_.unsafety,
|
||||
impl_.polarity,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ItemLikeVisitor<'_> for UnsafetyChecker<'tcx> {
|
||||
fn visit_item(&mut self, item: &hir::Item<'_>) {
|
||||
if let hir::ItemKind::Impl(ref impl_) = item.kind {
|
||||
self.check_unsafety_coherence(
|
||||
item,
|
||||
Some(&impl_.generics),
|
||||
impl_.unsafety,
|
||||
impl_.polarity,
|
||||
);
|
||||
fn check_unsafety_coherence<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item: &hir::Item<'_>,
|
||||
impl_generics: Option<&hir::Generics<'_>>,
|
||||
unsafety: hir::Unsafety,
|
||||
polarity: hir::ImplPolarity,
|
||||
) {
|
||||
if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) {
|
||||
let trait_def = tcx.trait_def(trait_ref.def_id);
|
||||
let unsafe_attr = impl_generics.and_then(|generics| {
|
||||
generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle")
|
||||
});
|
||||
match (trait_def.unsafety, unsafe_attr, unsafety, polarity) {
|
||||
(Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
item.span,
|
||||
E0199,
|
||||
"implementing the trait `{}` is not unsafe",
|
||||
trait_ref.print_only_trait_path()
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
(Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
item.span,
|
||||
E0200,
|
||||
"the trait `{}` requires an `unsafe impl` declaration",
|
||||
trait_ref.print_only_trait_path()
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
(Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
item.span,
|
||||
E0569,
|
||||
"requires an `unsafe impl` declaration due to `#[{}]` attribute",
|
||||
attr_name
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
(_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => {
|
||||
// Reported in AST validation
|
||||
tcx.sess.delay_span_bug(item.span, "unsafe negative impl");
|
||||
}
|
||||
(_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_))
|
||||
| (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive)
|
||||
| (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive)
|
||||
| (Unsafety::Normal, None, Unsafety::Normal, _) => {
|
||||
// OK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
|
||||
|
||||
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {}
|
||||
|
||||
fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ use min_specialization::check_min_specialization;
|
|||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
|
||||
use rustc_span::Span;
|
||||
|
@ -63,37 +63,25 @@ pub fn impl_wf_check(tcx: TyCtxt<'_>) {
|
|||
|
||||
fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
|
||||
let min_specialization = tcx.features().min_specialization;
|
||||
tcx.hir()
|
||||
.visit_item_likes_in_module(module_def_id, &mut ImplWfCheck { tcx, min_specialization });
|
||||
let module = tcx.hir_module_items(module_def_id);
|
||||
for id in module.items() {
|
||||
if matches!(tcx.def_kind(id.def_id), DefKind::Impl) {
|
||||
let item = tcx.hir().item(id);
|
||||
if let hir::ItemKind::Impl(ref impl_) = item.kind {
|
||||
enforce_impl_params_are_constrained(tcx, item.def_id, impl_.items);
|
||||
enforce_impl_items_are_distinct(tcx, impl_.items);
|
||||
if min_specialization {
|
||||
check_min_specialization(tcx, item.def_id.to_def_id(), item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
*providers = Providers { check_mod_impl_wf, ..*providers };
|
||||
}
|
||||
|
||||
struct ImplWfCheck<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
min_specialization: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> {
|
||||
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
|
||||
if let hir::ItemKind::Impl(ref impl_) = item.kind {
|
||||
enforce_impl_params_are_constrained(self.tcx, item.def_id, impl_.items);
|
||||
enforce_impl_items_are_distinct(self.tcx, impl_.items);
|
||||
if self.min_specialization {
|
||||
check_min_specialization(self.tcx, item.def_id.to_def_id(), item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, _trait_item: &'tcx hir::TraitItem<'tcx>) {}
|
||||
|
||||
fn visit_impl_item(&mut self, _impl_item: &'tcx hir::ImplItem<'tcx>) {}
|
||||
|
||||
fn visit_foreign_item(&mut self, _foreign_item: &'tcx hir::ForeignItem<'tcx>) {}
|
||||
}
|
||||
|
||||
fn enforce_impl_params_are_constrained(
|
||||
tcx: TyCtxt<'_>,
|
||||
impl_def_id: LocalDefId,
|
||||
|
|
|
@ -35,7 +35,7 @@ pub fn infer_predicates<'tcx>(
|
|||
debug!("InferVisitor::visit_item(item={:?})", item_did);
|
||||
|
||||
let mut item_required_predicates = RequiredPredicates::default();
|
||||
match tcx.hir().def_kind(item_did) {
|
||||
match tcx.def_kind(item_did) {
|
||||
DefKind::Union | DefKind::Enum | DefKind::Struct => {
|
||||
let adt_def = tcx.adt_def(item_did.to_def_id());
|
||||
|
||||
|
|
|
@ -1,28 +1,21 @@
|
|||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
pub fn test_inferred_outlives(tcx: TyCtxt<'_>) {
|
||||
tcx.hir().visit_all_item_likes(&mut OutlivesTest { tcx });
|
||||
}
|
||||
|
||||
struct OutlivesTest<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> ItemLikeVisitor<'tcx> for OutlivesTest<'tcx> {
|
||||
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
|
||||
for id in tcx.hir().items() {
|
||||
// For unit testing: check for a special "rustc_outlives"
|
||||
// attribute and report an error with various results if found.
|
||||
if self.tcx.has_attr(item.def_id.to_def_id(), sym::rustc_outlives) {
|
||||
let inferred_outlives_of = self.tcx.inferred_outlives_of(item.def_id);
|
||||
struct_span_err!(self.tcx.sess, item.span, E0640, "{:?}", inferred_outlives_of).emit();
|
||||
if tcx.has_attr(id.def_id.to_def_id(), sym::rustc_outlives) {
|
||||
let inferred_outlives_of = tcx.inferred_outlives_of(id.def_id);
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
tcx.def_span(id.def_id),
|
||||
E0640,
|
||||
"{:?}",
|
||||
inferred_outlives_of
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {}
|
||||
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {}
|
||||
fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
|
||||
|
@ -62,61 +62,71 @@ pub fn add_constraints_from_crate<'a, 'tcx>(
|
|||
constraints: Vec::new(),
|
||||
};
|
||||
|
||||
tcx.hir().visit_all_item_likes(&mut constraint_cx);
|
||||
let crate_items = tcx.hir_crate_items(());
|
||||
|
||||
for id in crate_items.items() {
|
||||
constraint_cx.check_item(id);
|
||||
}
|
||||
|
||||
for id in crate_items.trait_items() {
|
||||
if let DefKind::AssocFn = tcx.def_kind(id.def_id) {
|
||||
constraint_cx.check_node_helper(id.hir_id());
|
||||
}
|
||||
}
|
||||
|
||||
for id in crate_items.impl_items() {
|
||||
if let DefKind::AssocFn = tcx.def_kind(id.def_id) {
|
||||
constraint_cx.check_node_helper(id.hir_id());
|
||||
}
|
||||
}
|
||||
|
||||
for id in crate_items.foreign_items() {
|
||||
if let DefKind::Fn = tcx.def_kind(id.def_id) {
|
||||
constraint_cx.check_node_helper(id.hir_id());
|
||||
}
|
||||
}
|
||||
|
||||
constraint_cx
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
|
||||
fn visit_item(&mut self, item: &hir::Item<'_>) {
|
||||
match item.kind {
|
||||
hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
|
||||
self.visit_node_helper(item.hir_id());
|
||||
impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
||||
fn check_item(&mut self, id: hir::ItemId) {
|
||||
let def_kind = self.tcx().def_kind(id.def_id);
|
||||
match def_kind {
|
||||
DefKind::Struct | DefKind::Union => {
|
||||
let item = self.tcx().hir().item(id);
|
||||
|
||||
if let hir::VariantData::Tuple(..) = *struct_def {
|
||||
self.visit_node_helper(struct_def.ctor_hir_id().unwrap());
|
||||
}
|
||||
}
|
||||
if let hir::ItemKind::Struct(ref struct_def, _)
|
||||
| hir::ItemKind::Union(ref struct_def, _) = item.kind
|
||||
{
|
||||
self.check_node_helper(item.hir_id());
|
||||
|
||||
hir::ItemKind::Enum(ref enum_def, _) => {
|
||||
self.visit_node_helper(item.hir_id());
|
||||
|
||||
for variant in enum_def.variants {
|
||||
if let hir::VariantData::Tuple(..) = variant.data {
|
||||
self.visit_node_helper(variant.data.ctor_hir_id().unwrap());
|
||||
if let hir::VariantData::Tuple(..) = *struct_def {
|
||||
self.check_node_helper(struct_def.ctor_hir_id().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
DefKind::Enum => {
|
||||
let item = self.tcx().hir().item(id);
|
||||
|
||||
hir::ItemKind::Fn(..) => {
|
||||
self.visit_node_helper(item.hir_id());
|
||||
if let hir::ItemKind::Enum(ref enum_def, _) = item.kind {
|
||||
self.check_node_helper(item.hir_id());
|
||||
|
||||
for variant in enum_def.variants {
|
||||
if let hir::VariantData::Tuple(..) = variant.data {
|
||||
self.check_node_helper(variant.data.ctor_hir_id().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DefKind::Fn => {
|
||||
self.check_node_helper(id.hir_id());
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) {
|
||||
if let hir::TraitItemKind::Fn(..) = trait_item.kind {
|
||||
self.visit_node_helper(trait_item.hir_id());
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
|
||||
if let hir::ImplItemKind::Fn(..) = impl_item.kind {
|
||||
self.visit_node_helper(impl_item.hir_id());
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) {
|
||||
if let hir::ForeignItemKind::Fn(..) = foreign_item.kind {
|
||||
self.visit_node_helper(foreign_item.hir_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
||||
fn visit_node_helper(&mut self, id: hir::HirId) {
|
||||
fn check_node_helper(&mut self, id: hir::HirId) {
|
||||
let tcx = self.terms_cx.tcx;
|
||||
let def_id = tcx.hir().local_def_id(id);
|
||||
self.build_constraints_for_item(def_id);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
use rustc_arena::DroplessArena;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::HirIdMap;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use std::fmt;
|
||||
|
@ -79,7 +79,29 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>(
|
|||
//
|
||||
// - https://rustc-dev-guide.rust-lang.org/query.html
|
||||
// - https://rustc-dev-guide.rust-lang.org/variance.html
|
||||
tcx.hir().visit_all_item_likes(&mut terms_cx);
|
||||
let crate_items = tcx.hir_crate_items(());
|
||||
|
||||
for id in crate_items.items() {
|
||||
terms_cx.check_item(id);
|
||||
}
|
||||
|
||||
for id in crate_items.trait_items() {
|
||||
if let DefKind::AssocFn = tcx.def_kind(id.def_id) {
|
||||
terms_cx.add_inferreds_for_item(id.hir_id());
|
||||
}
|
||||
}
|
||||
|
||||
for id in crate_items.impl_items() {
|
||||
if let DefKind::AssocFn = tcx.def_kind(id.def_id) {
|
||||
terms_cx.add_inferreds_for_item(id.hir_id());
|
||||
}
|
||||
}
|
||||
|
||||
for id in crate_items.foreign_items() {
|
||||
if let DefKind::Fn = tcx.def_kind(id.def_id) {
|
||||
terms_cx.add_inferreds_for_item(id.hir_id());
|
||||
}
|
||||
}
|
||||
|
||||
terms_cx
|
||||
}
|
||||
|
@ -124,54 +146,42 @@ impl<'a, 'tcx> TermsContext<'a, 'tcx> {
|
|||
(start..(start + count)).map(|i| &*arena.alloc(InferredTerm(InferredIndex(i)))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for TermsContext<'a, 'tcx> {
|
||||
fn visit_item(&mut self, item: &hir::Item<'_>) {
|
||||
debug!("add_inferreds for item {}", self.tcx.hir().node_to_string(item.hir_id()));
|
||||
fn check_item(&mut self, id: hir::ItemId) {
|
||||
debug!("add_inferreds for item {}", self.tcx.hir().node_to_string(id.hir_id()));
|
||||
|
||||
match item.kind {
|
||||
hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
|
||||
self.add_inferreds_for_item(item.hir_id());
|
||||
let def_kind = self.tcx.def_kind(id.def_id);
|
||||
match def_kind {
|
||||
DefKind::Struct | DefKind::Union => {
|
||||
let item = self.tcx.hir().item(id);
|
||||
|
||||
if let hir::VariantData::Tuple(..) = *struct_def {
|
||||
self.add_inferreds_for_item(struct_def.ctor_hir_id().unwrap());
|
||||
}
|
||||
}
|
||||
if let hir::ItemKind::Struct(ref struct_def, _)
|
||||
| hir::ItemKind::Union(ref struct_def, _) = item.kind
|
||||
{
|
||||
self.add_inferreds_for_item(item.hir_id());
|
||||
|
||||
hir::ItemKind::Enum(ref enum_def, _) => {
|
||||
self.add_inferreds_for_item(item.hir_id());
|
||||
|
||||
for variant in enum_def.variants {
|
||||
if let hir::VariantData::Tuple(..) = variant.data {
|
||||
self.add_inferreds_for_item(variant.data.ctor_hir_id().unwrap());
|
||||
if let hir::VariantData::Tuple(..) = *struct_def {
|
||||
self.add_inferreds_for_item(struct_def.ctor_hir_id().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
DefKind::Enum => {
|
||||
let item = self.tcx.hir().item(id);
|
||||
|
||||
hir::ItemKind::Fn(..) => {
|
||||
self.add_inferreds_for_item(item.hir_id());
|
||||
if let hir::ItemKind::Enum(ref enum_def, _) = item.kind {
|
||||
self.add_inferreds_for_item(item.hir_id());
|
||||
|
||||
for variant in enum_def.variants {
|
||||
if let hir::VariantData::Tuple(..) = variant.data {
|
||||
self.add_inferreds_for_item(variant.data.ctor_hir_id().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DefKind::Fn => {
|
||||
self.add_inferreds_for_item(id.hir_id());
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) {
|
||||
if let hir::TraitItemKind::Fn(..) = trait_item.kind {
|
||||
self.add_inferreds_for_item(trait_item.hir_id());
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
|
||||
if let hir::ImplItemKind::Fn(..) = impl_item.kind {
|
||||
self.add_inferreds_for_item(impl_item.hir_id());
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) {
|
||||
if let hir::ForeignItemKind::Fn(..) = foreign_item.kind {
|
||||
self.add_inferreds_for_item(foreign_item.hir_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,14 @@
|
|||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
pub fn test_variance(tcx: TyCtxt<'_>) {
|
||||
tcx.hir().visit_all_item_likes(&mut VarianceTest { tcx });
|
||||
}
|
||||
|
||||
struct VarianceTest<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> ItemLikeVisitor<'tcx> for VarianceTest<'tcx> {
|
||||
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
|
||||
// For unit testing: check for a special "rustc_variance"
|
||||
// attribute and report an error with various results if found.
|
||||
if self.tcx.has_attr(item.def_id.to_def_id(), sym::rustc_variance) {
|
||||
let variances_of = self.tcx.variances_of(item.def_id);
|
||||
struct_span_err!(self.tcx.sess, item.span, E0208, "{:?}", variances_of).emit();
|
||||
// For unit testing: check for a special "rustc_variance"
|
||||
// attribute and report an error with various results if found.
|
||||
for id in tcx.hir().items() {
|
||||
if tcx.has_attr(id.def_id.to_def_id(), sym::rustc_variance) {
|
||||
let variances_of = tcx.variances_of(id.def_id);
|
||||
struct_span_err!(tcx.sess, tcx.def_span(id.def_id), E0208, "{:?}", variances_of).emit();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {}
|
||||
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {}
|
||||
fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ mod x {
|
|||
mod y {
|
||||
use {Foo, Bar};
|
||||
|
||||
#[rustc_then_this_would_need(typeck)] //~ ERROR no path
|
||||
#[rustc_then_this_would_need(typeck)] //~ ERROR OK
|
||||
pub fn call_bar() {
|
||||
char::bar('a');
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: no path from `x::<impl Foo for char>` to `typeck`
|
||||
error: OK
|
||||
--> $DIR/dep-graph-trait-impl-two-traits.rs:32:5
|
||||
|
|
||||
LL | #[rustc_then_this_would_need(typeck)]
|
||||
|
|
|
@ -51,14 +51,14 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|
|||
let mut map = FxHashMap::<Res, ExistingName>::default();
|
||||
|
||||
for id in cx.tcx.hir().items() {
|
||||
if matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl)
|
||||
if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl)
|
||||
&& let item = cx.tcx.hir().item(id)
|
||||
&& let ItemKind::Impl(Impl {
|
||||
items,
|
||||
of_trait,
|
||||
self_ty,
|
||||
..
|
||||
}) = &item.kind
|
||||
items,
|
||||
of_trait,
|
||||
self_ty,
|
||||
..
|
||||
}) = &item.kind
|
||||
&& let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
|
||||
{
|
||||
if !map.contains_key(res) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue