|
3 | 3 | //! struct implements that trait. |
4 | 4 |
|
5 | 5 | use rustc_data_structures::fx::FxHashSet; |
| 6 | +use rustc_errors::FatalError; |
6 | 7 | use rustc_hir::attrs::{AttributeKind, DocAttribute}; |
7 | | -use rustc_hir::def_id::LOCAL_CRATE; |
| 8 | +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; |
8 | 9 | use rustc_hir::{Attribute, find_attr}; |
9 | | -use rustc_middle::ty; |
| 10 | +use rustc_middle::ty::{self, Ty, TyCtxt}; |
| 11 | +use rustc_span::kw; |
| 12 | +use tracing::debug; |
10 | 13 |
|
11 | 14 | use super::Pass; |
12 | 15 | use crate::clean::*; |
@@ -48,32 +51,33 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> |
48 | 51 | let _prof_timer = tcx.sess.prof.generic_activity("build_extern_trait_impls"); |
49 | 52 | for &cnum in tcx.crates(()) { |
50 | 53 | for &impl_def_id in tcx.trait_impls_in_crate(cnum) { |
51 | | - cx.with_param_env(impl_def_id, |cx| { |
52 | | - let opt_trait_ref = tcx.impl_opt_trait_ref(impl_def_id); |
53 | | - if opt_trait_ref.is_some_and(|trait_ref| { |
54 | | - crate_items.contains(&ItemId::DefId(trait_ref.def_id())) |
55 | | - || Some(trait_ref.def_id()) == tcx.lang_items().deref_trait() |
56 | | - || tcx.is_doc_notable_trait(trait_ref.def_id()) |
57 | | - }) { |
| 54 | + let trait_ref = tcx.impl_trait_ref(impl_def_id); |
| 55 | + debug!("considering extern trait impl {trait_ref:?}"); |
| 56 | + if crate_items.contains(&ItemId::DefId(trait_ref.def_id())) |
| 57 | + || Some(trait_ref.def_id()) == tcx.lang_items().deref_trait() |
| 58 | + || tcx.is_doc_notable_trait(trait_ref.def_id()) |
| 59 | + { |
| 60 | + debug!("-> inlining due to trait"); |
| 61 | + cx.with_param_env(impl_def_id, |cx| { |
58 | 62 | inline::build_impl(cx, impl_def_id, None, &mut new_items_external); |
59 | | - } else { |
60 | | - let self_ty = |
61 | | - tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip(); |
62 | | - let self_ty = clean_middle_ty( |
63 | | - ty::Binder::dummy(self_ty), |
64 | | - cx, |
65 | | - Some(impl_def_id), |
66 | | - None, |
67 | | - ); |
68 | | - if self_ty.is_full_generic() |
69 | | - || self_ty |
70 | | - .def_id(&cx.cache) |
71 | | - .is_some_and(|did| crate_items.contains(&ItemId::DefId(did))) |
72 | | - { |
| 63 | + }); |
| 64 | + } else { |
| 65 | + let self_ty = tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip(); |
| 66 | + debug!(?self_ty); |
| 67 | + let self_ty_head = SelfTyHead::of(ty::Binder::dummy(self_ty), tcx, impl_def_id); |
| 68 | + debug!(?self_ty_head); |
| 69 | + let keep_impl = match self_ty_head { |
| 70 | + SelfTyHead::Generic => true, |
| 71 | + SelfTyHead::Item(def_id) => crate_items.contains(&ItemId::DefId(def_id)), |
| 72 | + SelfTyHead::Primitive | SelfTyHead::Other => false, |
| 73 | + }; |
| 74 | + if keep_impl { |
| 75 | + debug!("-> inlining due to self ty"); |
| 76 | + cx.with_param_env(impl_def_id, |cx| { |
73 | 77 | inline::build_impl(cx, impl_def_id, None, &mut new_items_external); |
74 | | - } |
| 78 | + }); |
75 | 79 | } |
76 | | - }); |
| 80 | + } |
77 | 81 | } |
78 | 82 | } |
79 | 83 | } |
@@ -160,6 +164,113 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> |
160 | 164 | krate |
161 | 165 | } |
162 | 166 |
|
| 167 | +#[derive(Debug)] |
| 168 | +enum SelfTyHead { |
| 169 | + Generic, |
| 170 | + Primitive, |
| 171 | + Item(DefId), |
| 172 | + Other, |
| 173 | +} |
| 174 | + |
| 175 | +impl SelfTyHead { |
| 176 | + /// Compute the "head" (top-level structure) of a type. |
| 177 | + /// |
| 178 | + /// When deciding whether to inline an impl, one of the things we look at is |
| 179 | + /// whether the Self type (the `Foo` in `impl Foo` or `impl Tr for Foo`) is |
| 180 | + /// present in the current crate (usually itself through inlining). However, |
| 181 | + /// constructing a full [`clean::Type`](Type) is expensive and more than we need, |
| 182 | + /// so this function computes just enough information to determine if the type |
| 183 | + /// is in the current crate. |
| 184 | + // FIXME: once -Znormalize-docs works properly / becomes the default, |
| 185 | + // this should invoke normalization where needed (e.g. if the head is an Alias). |
| 186 | + // we'll need to fetch the param_env too. |
| 187 | + fn of<'tcx>(bound_ty: ty::Binder<'tcx, Ty<'tcx>>, tcx: TyCtxt<'tcx>, parent: DefId) -> Self { |
| 188 | + match *bound_ty.skip_binder().kind() { |
| 189 | + ty::Never |
| 190 | + | ty::Bool |
| 191 | + | ty::Char |
| 192 | + | ty::Int(..) |
| 193 | + | ty::Uint(..) |
| 194 | + | ty::Float(..) |
| 195 | + | ty::Str |
| 196 | + | ty::Slice(..) |
| 197 | + | ty::Array(..) |
| 198 | + | ty::RawPtr(..) |
| 199 | + | ty::FnDef(..) |
| 200 | + | ty::FnPtr(..) |
| 201 | + | ty::Tuple(_) => Self::Primitive, |
| 202 | + ty::Pat(ty, _) => Self::of(bound_ty.rebind(ty), tcx, parent), |
| 203 | + ty::Ref(_, ty, _) => match Self::of(bound_ty.rebind(ty), tcx, parent) { |
| 204 | + Self::Generic => Self::Primitive, |
| 205 | + head => head, |
| 206 | + }, |
| 207 | + // FIXME(unsafe_binders): this should probably recurse through the unsafe binder, |
| 208 | + // but clean_middle_ty doesn't handle this correctly yet either |
| 209 | + ty::UnsafeBinder(_) => Self::Other, |
| 210 | + ty::Adt(def, _) => Self::Item(def.did()), |
| 211 | + ty::Foreign(did) => Self::Item(did), |
| 212 | + ty::Dynamic(obj, _) => { |
| 213 | + // HACK: pick the first `did` as the `did` of the trait object. Someone |
| 214 | + // might want to implement "native" support for marker-trait-only |
| 215 | + // trait objects. |
| 216 | + let mut dids = obj.auto_traits(); |
| 217 | + let did = obj |
| 218 | + .principal_def_id() |
| 219 | + .or_else(|| dids.next()) |
| 220 | + .unwrap_or_else(|| panic!("found trait object `{obj:?}` with no traits?")); |
| 221 | + Self::Item(did) |
| 222 | + } |
| 223 | + |
| 224 | + ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Projection { def_id }, .. }) => { |
| 225 | + debug_assert!(!tcx.is_impl_trait_in_trait(def_id)); |
| 226 | + Self::of(bound_ty.rebind(alias_ty.self_ty()), tcx, parent) |
| 227 | + } |
| 228 | + |
| 229 | + ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Inherent { .. }, .. }) => { |
| 230 | + let alias_ty = bound_ty.rebind(alias_ty); |
| 231 | + Self::of(alias_ty.map_bound(|ty| ty.self_ty()), tcx, parent) |
| 232 | + } |
| 233 | + |
| 234 | + ty::Alias(_, ty::AliasTy { kind: ty::Free { def_id }, args, .. }) => { |
| 235 | + if tcx.features().checked_type_aliases() { |
| 236 | + // Free type alias `data` represents the `type X` in `type X = Y`. If we need `Y`, |
| 237 | + // we need to use `type_of`. |
| 238 | + Self::Item(def_id) |
| 239 | + } else { |
| 240 | + let ty = tcx.type_of(def_id).instantiate(tcx, args).skip_norm_wip(); |
| 241 | + Self::of(bound_ty.rebind(ty), tcx, parent) |
| 242 | + } |
| 243 | + } |
| 244 | + |
| 245 | + ty::Param(ref p) => { |
| 246 | + // FIXME: there's a slight behavior difference from clean_middle_ty here |
| 247 | + // since here we represent impl traits as Generic not ImplTrait. |
| 248 | + // probably doesn't matter for collect trait impls since impl trait |
| 249 | + // can't be a self ty |
| 250 | + if p.name == kw::SelfUpper { Self::Other } else { Self::Generic } |
| 251 | + } |
| 252 | + |
| 253 | + ty::Bound(_, ref ty) => match ty.kind { |
| 254 | + ty::BoundTyKind::Param(_) => Self::Generic, |
| 255 | + ty::BoundTyKind::Anon => panic!("unexpected anonymous bound type variable"), |
| 256 | + }, |
| 257 | + |
| 258 | + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { |
| 259 | + panic!("{bound_ty} should not appear as impl self ty") |
| 260 | + } |
| 261 | + |
| 262 | + ty::Closure(..) |
| 263 | + | ty::CoroutineClosure(..) |
| 264 | + | ty::Coroutine(..) |
| 265 | + | ty::Placeholder(..) |
| 266 | + | ty::CoroutineWitness(..) |
| 267 | + | ty::Infer(..) => panic!("unexpected impl self ty {bound_ty}"), |
| 268 | + |
| 269 | + ty::Error(_) => FatalError.raise(), |
| 270 | + } |
| 271 | + } |
| 272 | +} |
| 273 | + |
163 | 274 | struct SyntheticImplCollector<'a, 'tcx> { |
164 | 275 | cx: &'a mut DocContext<'tcx>, |
165 | 276 | impls: Vec<Item>, |
|
0 commit comments