Show negative implementation of Sized trait

This commit is contained in:
Guillaume Gomez 2021-02-18 20:46:07 +01:00
parent 9b471a3f5f
commit d20e05b78b
2 changed files with 125 additions and 90 deletions

View file

@ -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> {
@ -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 {

View file

@ -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));