From d20e05b78b39b67f2767911bf1c9610160924fba Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 18 Feb 2021 20:46:07 +0100 Subject: [PATCH 1/2] Show negative implementation of Sized trait --- src/librustdoc/clean/auto_trait.rs | 204 ++++++++++++++++------------- src/librustdoc/core.rs | 11 ++ 2 files changed, 125 insertions(+), 90 deletions(-) diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index d43378081ce58..711b0298565d7 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -29,6 +29,107 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { AutoTraitFinder { cx } } + fn generate_for_trait( + &mut self, + ty: Ty<'tcx>, + trait_def_id: DefId, + param_env: ty::ParamEnv<'tcx>, + param_env_def_id: DefId, + f: &auto_trait::AutoTraitFinder<'tcx>, + // If this is set, show only negative trait implementations, not positive ones. + discard_positive_impl: bool, + ) -> Option { + let tcx = self.cx.tcx; + let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, &[]) }; + if !self.cx.generated_synthetics.borrow_mut().insert((ty, trait_def_id)) { + debug!("get_auto_trait_impl_for({:?}): already generated, aborting", trait_ref); + return None; + } + + let result = f.find_auto_trait_generics(ty, param_env, trait_def_id, |infcx, info| { + let region_data = info.region_data; + + let names_map = tcx + .generics_of(param_env_def_id) + .params + .iter() + .filter_map(|param| match param.kind { + ty::GenericParamDefKind::Lifetime => Some(param.name), + _ => None, + }) + .map(|name| (name, Lifetime(name))) + .collect(); + let lifetime_predicates = Self::handle_lifetimes(®ion_data, &names_map); + let new_generics = self.param_env_to_generics( + infcx.tcx, + param_env_def_id, + info.full_user_env, + lifetime_predicates, + info.vid_to_region, + ); + + debug!( + "find_auto_trait_generics(param_env_def_id={:?}, trait_def_id={:?}): \ + finished with {:?}", + param_env_def_id, trait_def_id, new_generics + ); + + new_generics + }); + + let negative_polarity; + let new_generics = match result { + AutoTraitResult::PositiveImpl(new_generics) => { + negative_polarity = false; + if discard_positive_impl { + return None; + } + new_generics + } + AutoTraitResult::NegativeImpl => { + negative_polarity = true; + + // For negative impls, we use the generic params, but *not* the predicates, + // from the original type. Otherwise, the displayed impl appears to be a + // conditional negative impl, when it's really unconditional. + // + // For example, consider the struct Foo(*mut T). Using + // the original predicates in our impl would cause us to generate + // `impl !Send for Foo`, which makes it appear that Foo + // implements Send where T is not copy. + // + // Instead, we generate `impl !Send for Foo`, which better + // expresses the fact that `Foo` never implements `Send`, + // regardless of the choice of `T`. + let params = (tcx.generics_of(param_env_def_id), ty::GenericPredicates::default()) + .clean(self.cx) + .params; + + Generics { params, where_predicates: Vec::new() } + } + AutoTraitResult::ExplicitImpl => return None, + }; + + Some(Item { + source: Span::dummy(), + name: None, + attrs: Default::default(), + visibility: Inherited, + def_id: self.cx.next_def_id(param_env_def_id.krate), + kind: box ImplItem(Impl { + unsafety: hir::Unsafety::Normal, + generics: new_generics, + provided_trait_methods: Default::default(), + trait_: Some(trait_ref.clean(self.cx).get_trait_type().unwrap()), + for_: ty.clean(self.cx), + items: Vec::new(), + negative_polarity, + synthetic: true, + blanket_impl: None, + }), + }) + } + // FIXME(eddyb) figure out a better way to pass information about // parametrization of `ty` than `param_env_def_id`. crate fn get_auto_trait_impls(&mut self, ty: Ty<'tcx>, param_env_def_id: DefId) -> Vec { @@ -38,99 +139,22 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { debug!("get_auto_trait_impls({:?})", ty); let auto_traits: Vec<_> = self.cx.auto_traits.iter().cloned().collect(); - auto_traits + let mut auto_traits: Vec = auto_traits .into_iter() .filter_map(|trait_def_id| { - let trait_ref = - ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, &[]) }; - if !self.cx.generated_synthetics.borrow_mut().insert((ty, trait_def_id)) { - debug!("get_auto_trait_impl_for({:?}): already generated, aborting", trait_ref); - return None; - } - - let result = - f.find_auto_trait_generics(ty, param_env, trait_def_id, |infcx, info| { - let region_data = info.region_data; - - let names_map = tcx - .generics_of(param_env_def_id) - .params - .iter() - .filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime => Some(param.name), - _ => None, - }) - .map(|name| (name, Lifetime(name))) - .collect(); - let lifetime_predicates = Self::handle_lifetimes(®ion_data, &names_map); - let new_generics = self.param_env_to_generics( - infcx.tcx, - param_env_def_id, - info.full_user_env, - lifetime_predicates, - info.vid_to_region, - ); - - debug!( - "find_auto_trait_generics(param_env_def_id={:?}, trait_def_id={:?}): \ - finished with {:?}", - param_env_def_id, trait_def_id, new_generics - ); - - new_generics - }); - - let negative_polarity; - let new_generics = match result { - AutoTraitResult::PositiveImpl(new_generics) => { - negative_polarity = false; - new_generics - } - AutoTraitResult::NegativeImpl => { - negative_polarity = true; - - // For negative impls, we use the generic params, but *not* the predicates, - // from the original type. Otherwise, the displayed impl appears to be a - // conditional negative impl, when it's really unconditional. - // - // For example, consider the struct Foo(*mut T). Using - // the original predicates in our impl would cause us to generate - // `impl !Send for Foo`, which makes it appear that Foo - // implements Send where T is not copy. - // - // Instead, we generate `impl !Send for Foo`, which better - // expresses the fact that `Foo` never implements `Send`, - // regardless of the choice of `T`. - let params = - (tcx.generics_of(param_env_def_id), ty::GenericPredicates::default()) - .clean(self.cx) - .params; - - Generics { params, where_predicates: Vec::new() } - } - AutoTraitResult::ExplicitImpl => return None, - }; - - Some(Item { - source: Span::dummy(), - name: None, - attrs: Default::default(), - visibility: Inherited, - def_id: self.cx.next_def_id(param_env_def_id.krate), - kind: box ImplItem(Impl { - unsafety: hir::Unsafety::Normal, - generics: new_generics, - provided_trait_methods: Default::default(), - trait_: Some(trait_ref.clean(self.cx).get_trait_type().unwrap()), - for_: ty.clean(self.cx), - items: Vec::new(), - negative_polarity, - synthetic: true, - blanket_impl: None, - }), - }) + self.generate_for_trait(ty, trait_def_id, param_env, param_env_def_id, &f, false) }) - .collect() + .collect(); + // We are only interested in case the type *doesn't* implement the Sized trait. + if !ty.is_sized(self.cx.tcx.at(rustc_span::DUMMY_SP), param_env) { + // In case `#![no_core]` is used, `sized_trait` returns nothing. + if let Some(item) = self.cx.tcx.lang_items().sized_trait().and_then(|sized_trait_did| { + self.generate_for_trait(ty, sized_trait_did, param_env, param_env_def_id, &f, true) + }) { + auto_traits.push(item); + } + } + auto_traits } fn get_lifetime(region: Region<'_>, names_map: &FxHashMap) -> Lifetime { diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 4517eda9b333f..1c2d2ad626c5a 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -32,6 +32,7 @@ use std::{ }; use crate::clean; +use crate::clean::inline::build_external_trait; use crate::clean::{AttributesExt, MAX_DEF_IDX}; use crate::config::{Options as RustdocOptions, RenderOptions}; use crate::config::{OutputFormat, RenderInfo}; @@ -530,6 +531,16 @@ crate fn run_global_ctxt( module_trait_cache: RefCell::new(FxHashMap::default()), cache: Cache::default(), }; + + // Small hack to force the Sized trait to be present. + // + // Note that in case of `#![no_core]`, the trait is not available. + if let Some(sized_trait_did) = ctxt.tcx.lang_items().sized_trait() { + let mut sized_trait = build_external_trait(&mut ctxt, sized_trait_did); + sized_trait.is_auto = true; + ctxt.external_traits.borrow_mut().insert(sized_trait_did, sized_trait); + } + debug!("crate: {:?}", tcx.hir().krate()); let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt)); From 46f24c912f55c0a31d4708a145e56b5cf0f5c8f0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 18 Feb 2021 20:46:34 +0100 Subject: [PATCH 2/2] Add tests for !Sized trait display --- src/test/rustdoc/sized_trait.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/test/rustdoc/sized_trait.rs diff --git a/src/test/rustdoc/sized_trait.rs b/src/test/rustdoc/sized_trait.rs new file mode 100644 index 0000000000000..26d12817afca1 --- /dev/null +++ b/src/test/rustdoc/sized_trait.rs @@ -0,0 +1,17 @@ +#![crate_name = "foo"] + +// @has foo/struct.Bar.html +// @!has - '//h3[@id="impl-Sized"]' +pub struct Bar { + a: u16, +} + +// @has foo/struct.Foo.html +// @!has - '//h3[@id="impl-Sized"]' +pub struct Foo(T); + +// @has foo/struct.Unsized.html +// @has - '//h3[@id="impl-Sized"]/code' 'impl !Sized for Unsized' +pub struct Unsized { + data: [u8], +}