Auto merge of #144554 - cjgillot:no-hir-eff-vis, r=petrochenkov

Use less HIR to compute effective visibility.

r? `@ghost`
This commit is contained in:
bors
2025-08-02 09:23:36 +00:00

View File

@@ -26,9 +26,9 @@ use rustc_errors::{MultiSpan, listify};
use rustc_hir as hir;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId};
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
use rustc_hir::intravisit::{self, InferKind, Visitor};
use rustc_hir::{AmbigArg, ForeignItemId, ItemId, PatKind, find_attr};
use rustc_hir::{AmbigArg, ForeignItemId, ItemId, OwnerId, PatKind, find_attr};
use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
use rustc_middle::query::Providers;
use rustc_middle::ty::print::PrintTraitRefExt as _;
@@ -599,7 +599,7 @@ impl<'tcx> EmbargoVisitor<'tcx> {
DefKind::Struct | DefKind::Union => {
// While structs and unions have type privacy, their fields do not.
let struct_def = self.tcx.adt_def(def_id);
for field in struct_def.non_enum_variant().fields.iter() {
for field in &struct_def.non_enum_variant().fields {
let def_id = field.did.expect_local();
let field_vis = self.tcx.local_visibility(def_id);
if field_vis.is_accessible_from(module, self.tcx) {
@@ -637,45 +637,49 @@ impl<'tcx> EmbargoVisitor<'tcx> {
}
}
impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
impl<'tcx> EmbargoVisitor<'tcx> {
fn check_def_id(&mut self, owner_id: OwnerId) {
// Update levels of nested things and mark all items
// in interfaces of reachable items as reachable.
let item_ev = self.get(item.owner_id.def_id);
match item.kind {
let item_ev = self.get(owner_id.def_id);
match self.tcx.def_kind(owner_id) {
// The interface is empty, and no nested items.
hir::ItemKind::Use(..)
| hir::ItemKind::ExternCrate(..)
| hir::ItemKind::GlobalAsm { .. } => {}
// The interface is empty, and all nested items are processed by `visit_item`.
hir::ItemKind::Mod(..) => {}
hir::ItemKind::Macro(_, macro_def, _) => {
DefKind::Use | DefKind::ExternCrate | DefKind::GlobalAsm => {}
// The interface is empty, and all nested items are processed by `check_def_id`.
DefKind::Mod => {}
DefKind::Macro { .. } => {
if let Some(item_ev) = item_ev {
self.update_reachability_from_macro(item.owner_id.def_id, macro_def, item_ev);
let (_, macro_def, _) =
self.tcx.hir_expect_item(owner_id.def_id).expect_macro();
self.update_reachability_from_macro(owner_id.def_id, macro_def, item_ev);
}
}
hir::ItemKind::Const(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Fn { .. }
| hir::ItemKind::TyAlias(..) => {
DefKind::ForeignTy
| DefKind::Const
| DefKind::Static { .. }
| DefKind::Fn
| DefKind::TyAlias => {
if let Some(item_ev) = item_ev {
self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty();
self.reach(owner_id.def_id, item_ev).generics().predicates().ty();
}
}
hir::ItemKind::Trait(.., trait_item_refs) => {
DefKind::Trait => {
if let Some(item_ev) = item_ev {
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
self.reach(owner_id.def_id, item_ev).generics().predicates();
for trait_item_ref in trait_item_refs {
self.update(trait_item_ref.owner_id.def_id, item_ev, Level::Reachable);
for assoc_item in self.tcx.associated_items(owner_id).in_definition_order() {
if assoc_item.is_impl_trait_in_trait() {
continue;
}
let def_id = assoc_item.def_id.expect_local();
self.update(def_id, item_ev, Level::Reachable);
let tcx = self.tcx;
let mut reach = self.reach(trait_item_ref.owner_id.def_id, item_ev);
let mut reach = self.reach(def_id, item_ev);
reach.generics().predicates();
if let DefKind::AssocTy = tcx.def_kind(trait_item_ref.owner_id)
&& !tcx.defaultness(trait_item_ref.owner_id).has_value()
{
if assoc_item.is_type() && !assoc_item.defaultness(tcx).has_value() {
// No type to visit.
} else {
reach.ty();
@@ -683,12 +687,12 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
}
}
}
hir::ItemKind::TraitAlias(..) => {
DefKind::TraitAlias => {
if let Some(item_ev) = item_ev {
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
self.reach(owner_id.def_id, item_ev).generics().predicates();
}
}
hir::ItemKind::Impl(impl_) => {
DefKind::Impl { of_trait } => {
// Type inference is very smart sometimes. It can make an impl reachable even some
// components of its type or trait are unreachable. E.g. methods of
// `impl ReachableTrait<UnreachableTy> for ReachableTy<UnreachableTy> { ... }`
@@ -700,19 +704,23 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
// without knowing both "shallow" version of its self type and "shallow" version of
// its trait if it exists (which require reaching the `DefId`s in them).
let item_ev = EffectiveVisibility::of_impl::<true>(
item.owner_id.def_id,
owner_id.def_id,
self.tcx,
&self.effective_visibilities,
);
self.update_eff_vis(item.owner_id.def_id, item_ev, None, Level::Direct);
self.update_eff_vis(owner_id.def_id, item_ev, None, Level::Direct);
self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty().trait_ref();
self.reach(owner_id.def_id, item_ev).generics().predicates().ty().trait_ref();
for impl_item_ref in impl_.items {
let def_id = impl_item_ref.owner_id.def_id;
for assoc_item in self.tcx.associated_items(owner_id).in_definition_order() {
if assoc_item.is_impl_trait_in_trait() {
continue;
}
let def_id = assoc_item.def_id.expect_local();
let max_vis =
impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id));
if of_trait { None } else { Some(self.tcx.local_visibility(def_id)) };
self.update_eff_vis(def_id, item_ev, max_vis, Level::Direct);
if let Some(impl_item_ev) = self.get(def_id) {
@@ -720,65 +728,76 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
}
}
}
hir::ItemKind::Enum(_, _, ref def) => {
DefKind::Enum => {
if let Some(item_ev) = item_ev {
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
self.reach(owner_id.def_id, item_ev).generics().predicates();
}
for variant in def.variants {
let def = self.tcx.adt_def(owner_id);
for variant in def.variants() {
if let Some(item_ev) = item_ev {
self.update(variant.def_id, item_ev, Level::Reachable);
self.update(variant.def_id.expect_local(), item_ev, Level::Reachable);
}
if let Some(variant_ev) = self.get(variant.def_id) {
if let Some(ctor_def_id) = variant.data.ctor_def_id() {
self.update(ctor_def_id, variant_ev, Level::Reachable);
if let Some(variant_ev) = self.get(variant.def_id.expect_local()) {
if let Some(ctor_def_id) = variant.ctor_def_id() {
self.update(ctor_def_id.expect_local(), variant_ev, Level::Reachable);
}
for field in variant.data.fields() {
self.update(field.def_id, variant_ev, Level::Reachable);
self.reach(field.def_id, variant_ev).ty();
for field in &variant.fields {
let field = field.did.expect_local();
self.update(field, variant_ev, Level::Reachable);
self.reach(field, variant_ev).ty();
}
// Corner case: if the variant is reachable, but its
// enum is not, make the enum reachable as well.
self.reach(item.owner_id.def_id, variant_ev).ty();
self.reach(owner_id.def_id, variant_ev).ty();
}
if let Some(ctor_def_id) = variant.data.ctor_def_id() {
if let Some(ctor_ev) = self.get(ctor_def_id) {
self.reach(item.owner_id.def_id, ctor_ev).ty();
if let Some(ctor_def_id) = variant.ctor_def_id() {
if let Some(ctor_ev) = self.get(ctor_def_id.expect_local()) {
self.reach(owner_id.def_id, ctor_ev).ty();
}
}
}
}
hir::ItemKind::ForeignMod { items, .. } => {
for foreign_item in items {
if let Some(foreign_item_ev) = self.get(foreign_item.owner_id.def_id) {
self.reach(foreign_item.owner_id.def_id, foreign_item_ev)
.generics()
.predicates()
.ty();
}
}
}
hir::ItemKind::Struct(_, _, ref struct_def)
| hir::ItemKind::Union(_, _, ref struct_def) => {
DefKind::Struct | DefKind::Union => {
let def = self.tcx.adt_def(owner_id).non_enum_variant();
if let Some(item_ev) = item_ev {
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
for field in struct_def.fields() {
self.update(field.def_id, item_ev, Level::Reachable);
if let Some(field_ev) = self.get(field.def_id) {
self.reach(field.def_id, field_ev).ty();
self.reach(owner_id.def_id, item_ev).generics().predicates();
for field in &def.fields {
let field = field.did.expect_local();
self.update(field, item_ev, Level::Reachable);
if let Some(field_ev) = self.get(field) {
self.reach(field, field_ev).ty();
}
}
}
if let Some(ctor_def_id) = struct_def.ctor_def_id() {
if let Some(ctor_def_id) = def.ctor_def_id() {
if let Some(item_ev) = item_ev {
self.update(ctor_def_id, item_ev, Level::Reachable);
self.update(ctor_def_id.expect_local(), item_ev, Level::Reachable);
}
if let Some(ctor_ev) = self.get(ctor_def_id) {
self.reach(item.owner_id.def_id, ctor_ev).ty();
if let Some(ctor_ev) = self.get(ctor_def_id.expect_local()) {
self.reach(owner_id.def_id, ctor_ev).ty();
}
}
}
// Contents are checked directly.
DefKind::ForeignMod => {}
DefKind::Field
| DefKind::Variant
| DefKind::AssocFn
| DefKind::AssocTy
| DefKind::AssocConst
| DefKind::TyParam
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::OpaqueTy
| DefKind::Closure
| DefKind::SyntheticCoroutineBody
| DefKind::ConstParam
| DefKind::LifetimeParam
| DefKind::Ctor(..) => {
bug!("should be checked while checking parent")
}
}
}
}
@@ -839,7 +858,7 @@ pub struct TestReachabilityVisitor<'a, 'tcx> {
}
impl<'a, 'tcx> TestReachabilityVisitor<'a, 'tcx> {
fn effective_visibility_diagnostic(&mut self, def_id: LocalDefId) {
fn effective_visibility_diagnostic(&self, def_id: LocalDefId) {
if self.tcx.has_attr(def_id, sym::rustc_effective_visibility) {
let mut error_msg = String::new();
let span = self.tcx.def_span(def_id.to_def_id());
@@ -859,43 +878,35 @@ impl<'a, 'tcx> TestReachabilityVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for TestReachabilityVisitor<'a, 'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
self.effective_visibility_diagnostic(item.owner_id.def_id);
impl<'a, 'tcx> TestReachabilityVisitor<'a, 'tcx> {
fn check_def_id(&self, owner_id: OwnerId) {
self.effective_visibility_diagnostic(owner_id.def_id);
match item.kind {
hir::ItemKind::Enum(_, _, ref def) => {
for variant in def.variants.iter() {
self.effective_visibility_diagnostic(variant.def_id);
if let Some(ctor_def_id) = variant.data.ctor_def_id() {
self.effective_visibility_diagnostic(ctor_def_id);
match self.tcx.def_kind(owner_id) {
DefKind::Enum => {
let def = self.tcx.adt_def(owner_id.def_id);
for variant in def.variants() {
self.effective_visibility_diagnostic(variant.def_id.expect_local());
if let Some(ctor_def_id) = variant.ctor_def_id() {
self.effective_visibility_diagnostic(ctor_def_id.expect_local());
}
for field in variant.data.fields() {
self.effective_visibility_diagnostic(field.def_id);
for field in &variant.fields {
self.effective_visibility_diagnostic(field.did.expect_local());
}
}
}
hir::ItemKind::Struct(_, _, ref def) | hir::ItemKind::Union(_, _, ref def) => {
DefKind::Struct | DefKind::Union => {
let def = self.tcx.adt_def(owner_id.def_id).non_enum_variant();
if let Some(ctor_def_id) = def.ctor_def_id() {
self.effective_visibility_diagnostic(ctor_def_id);
self.effective_visibility_diagnostic(ctor_def_id.expect_local());
}
for field in def.fields() {
self.effective_visibility_diagnostic(field.def_id);
for field in &def.fields {
self.effective_visibility_diagnostic(field.did.expect_local());
}
}
_ => {}
}
}
fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem<'tcx>) {
self.effective_visibility_diagnostic(item.owner_id.def_id);
}
fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem<'tcx>) {
self.effective_visibility_diagnostic(item.owner_id.def_id);
}
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
self.effective_visibility_diagnostic(item.owner_id.def_id);
}
}
//////////////////////////////////////////////////////////////////////////////////////
@@ -1838,8 +1849,14 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
visitor.changed = false;
}
let crate_items = tcx.hir_crate_items(());
loop {
tcx.hir_visit_all_item_likes_in_crate(&mut visitor);
for id in crate_items.free_items() {
visitor.check_def_id(id.owner_id);
}
for id in crate_items.foreign_items() {
visitor.check_def_id(id.owner_id);
}
if visitor.changed {
visitor.changed = false;
} else {
@@ -1848,10 +1865,11 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
}
visitor.effective_visibilities.check_invariants(tcx);
let mut check_visitor =
let check_visitor =
TestReachabilityVisitor { tcx, effective_visibilities: &visitor.effective_visibilities };
check_visitor.effective_visibility_diagnostic(CRATE_DEF_ID);
tcx.hir_visit_all_item_likes_in_crate(&mut check_visitor);
for id in crate_items.owners() {
check_visitor.check_def_id(id);
}
tcx.arena.alloc(visitor.effective_visibilities)
}