From b3b23aada9382e7768c0bd4d8f79319d73558259 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 7 Aug 2022 05:47:32 +0000 Subject: [PATCH] Don't document impossible to call default trait items on impls --- compiler/rustc_middle/src/query/mod.rs | 8 ++ .../rustc_trait_selection/src/traits/mod.rs | 78 ++++++++++++++++++- src/librustdoc/html/render/mod.rs | 9 +++ src/test/rustdoc/impossible-default.rs | 20 +++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/test/rustdoc/impossible-default.rs diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d8483e7e40914..9db193dc0cefa 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1951,6 +1951,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( goal: CanonicalTyGoal<'tcx> ) -> MethodAutoderefStepsResult<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 9c6bb0731f441..06aeafa26c05b 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -30,10 +30,14 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; 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 smallvec::SmallVec; @@ -474,6 +478,77 @@ fn subst_and_check_impossible_predicates<'tcx>( 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 { + // 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 { + 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 { + 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 = >::new(tcx); + fulfill_ctxt.register_predicate_obligations(infcx, predicates_for_trait); + !fulfill_ctxt.select_all_or_error(infcx).is_empty() + }) +} + #[derive(Clone, Debug)] enum VtblSegment<'tcx> { MetadataDSA, @@ -854,6 +929,7 @@ pub fn provide(providers: &mut ty::query::Providers) { vtable_entries, vtable_trait_upcasting_coercion_new_vptr_slot, subst_and_check_impossible_predicates, + is_impossible_method, try_unify_abstract_consts: |tcx, param_env_and| { let (param_env, (a, b)) = param_env_and.into_parts(); const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a262c8f7d1948..ae7d8c108d3ee 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1550,6 +1550,15 @@ fn render_impl( rendering_params: ImplRenderingParameters, ) { 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; if i.items.iter().any(|m| m.name == n) { continue; diff --git a/src/test/rustdoc/impossible-default.rs b/src/test/rustdoc/impossible-default.rs new file mode 100644 index 0000000000000..24d6e3bdac1bd --- /dev/null +++ b/src/test/rustdoc/impossible-default.rs @@ -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 {}