Skip to content

Commit

Permalink
Auto merge of #78181 - GuillaumeGomez:sized-trait, r=jyn514
Browse files Browse the repository at this point in the history
Add Sized trait display when implemented on type

Fixes #24183.

I'm not too happy about the hack I had to add in here, however, it seems like the `Sized` trait is **very** special.

cc `@jyn514`
r? `@ollie27`
  • Loading branch information
bors committed Feb 20, 2021
2 parents 9b471a3 + 46f24c9 commit b75baad
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 90 deletions.
204 changes: 114 additions & 90 deletions src/librustdoc/clean/auto_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item> {
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(&region_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<T: Copy>(*mut T). Using
// the original predicates in our impl would cause us to generate
// `impl !Send for Foo<T: Copy>`, which makes it appear that Foo
// implements Send where T is not copy.
//
// Instead, we generate `impl !Send for Foo<T>`, which better
// expresses the fact that `Foo<T>` 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<Item> {
Expand All @@ -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<Item> = 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(&region_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<T: Copy>(*mut T). Using
// the original predicates in our impl would cause us to generate
// `impl !Send for Foo<T: Copy>`, which makes it appear that Foo
// implements Send where T is not copy.
//
// Instead, we generate `impl !Send for Foo<T>`, which better
// expresses the fact that `Foo<T>` 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<Symbol, Lifetime>) -> Lifetime {
Expand Down
11 changes: 11 additions & 0 deletions src/librustdoc/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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));
Expand Down
17 changes: 17 additions & 0 deletions src/test/rustdoc/sized_trait.rs
Original file line number Diff line number Diff line change
@@ -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: ?Sized>(T);

// @has foo/struct.Unsized.html
// @has - '//h3[@id="impl-Sized"]/code' 'impl !Sized for Unsized'
pub struct Unsized {
data: [u8],
}

0 comments on commit b75baad

Please sign in to comment.