Rollup merge of #100221 - compiler-errors:impossible-trait-items, r=lcnr,notriddle,camelid
Don't document impossible to call default trait items on impls Closes #100176 This only skips documenting _default_ trait items on impls, not ones that are written inside the impl block. This is a conservative approach, since I think we should document all items written in an impl block (I guess unless hidden or whatever), but the existence of this new query I added makes this easy to extend to other rustdoc cases.
This commit is contained in:
commit
d910e5376b
4 changed files with 113 additions and 1 deletions
|
@ -1956,6 +1956,14 @@ rustc_queries! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query is_impossible_method(key: (DefId, DefId)) -> bool {
|
||||||
|
desc { |tcx|
|
||||||
|
"checking if {} is impossible to call within {}",
|
||||||
|
tcx.def_path_str(key.1),
|
||||||
|
tcx.def_path_str(key.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
query method_autoderef_steps(
|
query method_autoderef_steps(
|
||||||
goal: CanonicalTyGoal<'tcx>
|
goal: CanonicalTyGoal<'tcx>
|
||||||
) -> MethodAutoderefStepsResult<'tcx> {
|
) -> MethodAutoderefStepsResult<'tcx> {
|
||||||
|
|
|
@ -34,7 +34,10 @@ use rustc_infer::traits::TraitEngineExt as _;
|
||||||
use rustc_middle::ty::fold::TypeFoldable;
|
use rustc_middle::ty::fold::TypeFoldable;
|
||||||
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
|
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
|
||||||
use rustc_middle::ty::visit::TypeVisitable;
|
use rustc_middle::ty::visit::TypeVisitable;
|
||||||
use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry};
|
use rustc_middle::ty::{
|
||||||
|
self, DefIdTree, GenericParamDefKind, Subst, ToPredicate, Ty, TyCtxt, TypeSuperVisitable,
|
||||||
|
VtblEntry,
|
||||||
|
};
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
@ -503,6 +506,77 @@ fn subst_and_check_impossible_predicates<'tcx>(
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether a trait's method is impossible to call on a given impl.
|
||||||
|
///
|
||||||
|
/// This only considers predicates that reference the impl's generics, and not
|
||||||
|
/// those that reference the method's generics.
|
||||||
|
fn is_impossible_method<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
(impl_def_id, trait_item_def_id): (DefId, DefId),
|
||||||
|
) -> bool {
|
||||||
|
struct ReferencesOnlyParentGenerics<'tcx> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
generics: &'tcx ty::Generics,
|
||||||
|
trait_item_def_id: DefId,
|
||||||
|
}
|
||||||
|
impl<'tcx> ty::TypeVisitor<'tcx> for ReferencesOnlyParentGenerics<'tcx> {
|
||||||
|
type BreakTy = ();
|
||||||
|
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
|
// If this is a parameter from the trait item's own generics, then bail
|
||||||
|
if let ty::Param(param) = t.kind()
|
||||||
|
&& let param_def_id = self.generics.type_param(param, self.tcx).def_id
|
||||||
|
&& self.tcx.parent(param_def_id) == self.trait_item_def_id
|
||||||
|
{
|
||||||
|
return ControlFlow::BREAK;
|
||||||
|
}
|
||||||
|
t.super_visit_with(self)
|
||||||
|
}
|
||||||
|
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
|
if let ty::ReEarlyBound(param) = r.kind()
|
||||||
|
&& let param_def_id = self.generics.region_param(¶m, self.tcx).def_id
|
||||||
|
&& self.tcx.parent(param_def_id) == self.trait_item_def_id
|
||||||
|
{
|
||||||
|
return ControlFlow::BREAK;
|
||||||
|
}
|
||||||
|
r.super_visit_with(self)
|
||||||
|
}
|
||||||
|
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
|
if let ty::ConstKind::Param(param) = ct.kind()
|
||||||
|
&& let param_def_id = self.generics.const_param(¶m, self.tcx).def_id
|
||||||
|
&& self.tcx.parent(param_def_id) == self.trait_item_def_id
|
||||||
|
{
|
||||||
|
return ControlFlow::BREAK;
|
||||||
|
}
|
||||||
|
ct.super_visit_with(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let generics = tcx.generics_of(trait_item_def_id);
|
||||||
|
let predicates = tcx.predicates_of(trait_item_def_id);
|
||||||
|
let impl_trait_ref =
|
||||||
|
tcx.impl_trait_ref(impl_def_id).expect("expected impl to correspond to trait");
|
||||||
|
let param_env = tcx.param_env(impl_def_id);
|
||||||
|
|
||||||
|
let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id };
|
||||||
|
let predicates_for_trait = predicates.predicates.iter().filter_map(|(pred, span)| {
|
||||||
|
if pred.visit_with(&mut visitor).is_continue() {
|
||||||
|
Some(Obligation::new(
|
||||||
|
ObligationCause::dummy_with_span(*span),
|
||||||
|
param_env,
|
||||||
|
ty::EarlyBinder(*pred).subst(tcx, impl_trait_ref.substs),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tcx.infer_ctxt().ignoring_regions().enter(|ref infcx| {
|
||||||
|
let mut fulfill_ctxt = <dyn TraitEngine<'_>>::new(tcx);
|
||||||
|
fulfill_ctxt.register_predicate_obligations(infcx, predicates_for_trait);
|
||||||
|
!fulfill_ctxt.select_all_or_error(infcx).is_empty()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum VtblSegment<'tcx> {
|
enum VtblSegment<'tcx> {
|
||||||
MetadataDSA,
|
MetadataDSA,
|
||||||
|
@ -883,6 +957,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
|
||||||
vtable_entries,
|
vtable_entries,
|
||||||
vtable_trait_upcasting_coercion_new_vptr_slot,
|
vtable_trait_upcasting_coercion_new_vptr_slot,
|
||||||
subst_and_check_impossible_predicates,
|
subst_and_check_impossible_predicates,
|
||||||
|
is_impossible_method,
|
||||||
try_unify_abstract_consts: |tcx, param_env_and| {
|
try_unify_abstract_consts: |tcx, param_env_and| {
|
||||||
let (param_env, (a, b)) = param_env_and.into_parts();
|
let (param_env, (a, b)) = param_env_and.into_parts();
|
||||||
const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env)
|
const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env)
|
||||||
|
|
|
@ -1550,6 +1550,15 @@ fn render_impl(
|
||||||
rendering_params: ImplRenderingParameters,
|
rendering_params: ImplRenderingParameters,
|
||||||
) {
|
) {
|
||||||
for trait_item in &t.items {
|
for trait_item in &t.items {
|
||||||
|
// Skip over any default trait items that are impossible to call
|
||||||
|
// (e.g. if it has a `Self: Sized` bound on an unsized type).
|
||||||
|
if let Some(impl_def_id) = parent.item_id.as_def_id()
|
||||||
|
&& let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
|
||||||
|
&& cx.tcx().is_impossible_method((impl_def_id, trait_item_def_id))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let n = trait_item.name;
|
let n = trait_item.name;
|
||||||
if i.items.iter().any(|m| m.name == n) {
|
if i.items.iter().any(|m| m.name == n) {
|
||||||
continue;
|
continue;
|
||||||
|
|
20
src/test/rustdoc/impossible-default.rs
Normal file
20
src/test/rustdoc/impossible-default.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#![crate_name = "foo"]
|
||||||
|
|
||||||
|
// Check that default trait items that are impossible to satisfy
|
||||||
|
|
||||||
|
pub trait Foo {
|
||||||
|
fn needs_sized(&self)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{}
|
||||||
|
|
||||||
|
fn no_needs_sized(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @!has foo/struct.Bar.html '//*[@id="method.needs_sized"]//h4[@class="code-header"]' \
|
||||||
|
// "fn needs_sized"
|
||||||
|
// @has foo/struct.Bar.html '//*[@id="method.no_needs_sized"]//h4[@class="code-header"]' \
|
||||||
|
// "fn no_needs_sized"
|
||||||
|
pub struct Bar([u8]);
|
||||||
|
|
||||||
|
impl Foo for Bar {}
|
Loading…
Add table
Add a link
Reference in a new issue