Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Sized trait display when implemented on type #78181

Merged
merged 2 commits into from
Feb 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
GuillaumeGomez marked this conversation as resolved.
Show resolved Hide resolved
) -> 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);
GuillaumeGomez marked this conversation as resolved.
Show resolved Hide resolved
}

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],
}