1
Fork 0

Use a query rather than recomputing the tail repeatedly

This commit is contained in:
Michael Goulet 2025-03-30 03:40:14 +00:00
parent ccdfd310be
commit 830aeb6102
13 changed files with 234 additions and 90 deletions

View file

@ -937,27 +937,7 @@ fn check_impl_items_against_trait<'tcx>(
let trait_def = tcx.trait_def(trait_ref.def_id);
let infcx = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis());
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let cause = ObligationCause::misc(tcx.def_span(impl_id), impl_id);
let param_env = tcx.param_env(impl_id);
let self_is_guaranteed_unsized = tcx
.struct_tail_raw(
trait_ref.self_ty(),
|ty| {
ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| {
Ty::new_error_with_message(
tcx,
tcx.def_span(impl_id),
"struct tail should be computable",
)
})
},
|| (),
)
.is_guaranteed_unsized_raw();
let self_is_guaranteed_unsize_self = tcx.impl_self_is_guaranteed_unsized(impl_id);
for &impl_item in impl_item_refs {
let ty_impl_item = tcx.associated_item(impl_item);
@ -988,7 +968,7 @@ fn check_impl_items_against_trait<'tcx>(
}
}
if self_is_guaranteed_unsized && tcx.generics_require_sized_self(ty_trait_item.def_id) {
if self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(ty_trait_item.def_id) {
tcx.emit_node_span_lint(
rustc_lint_defs::builtin::DEAD_CODE,
tcx.local_def_id_to_hir_id(ty_impl_item.def_id.expect_local()),
@ -1023,7 +1003,7 @@ fn check_impl_items_against_trait<'tcx>(
if !is_implemented
&& tcx.defaultness(impl_id).is_final()
// unsized types don't need to implement methods that have `Self: Sized` bounds.
&& !(self_is_guaranteed_unsized && tcx.generics_require_sized_self(trait_item_id))
&& !(self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(trait_item_id))
{
missing_items.push(tcx.associated_item(trait_item_id));
}

View file

@ -1026,6 +1026,13 @@ rustc_queries! {
separate_provide_extern
}
/// Given an `impl_def_id`, return true if the self type is guaranteed to be unsized due
/// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct
/// whose tail is one of those types.
query impl_self_is_guaranteed_unsized(impl_def_id: DefId) -> bool {
desc { |tcx| "computing whether `{}` has a guaranteed unsized self type", tcx.def_path_str(impl_def_id) }
}
/// Maps a `DefId` of a type to a list of its inherent impls.
/// Contains implementations of methods that are inherent to a type.
/// Methods in these implementations don't need to be exported.

View file

@ -437,6 +437,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
)
}
fn impl_self_is_guaranteed_unsized(self, impl_def_id: DefId) -> bool {
self.impl_self_is_guaranteed_unsized(impl_def_id)
}
fn has_target_features(self, def_id: DefId) -> bool {
!self.codegen_fn_attrs(def_id).target_features.is_empty()
}

View file

@ -2029,43 +2029,6 @@ impl<'tcx> Ty<'tcx> {
pub fn is_known_rigid(self) -> bool {
self.kind().is_known_rigid()
}
/// Returns true if the type is guaranteed to be one of the three built-in unsized types:
/// `dyn Trait`/`[T]`/`str`. This function is *raw* because it does not compute the struct
/// tail of the type, so you are responsible for doing that yourself.
// NOTE: Keep this in sync with `rustc_type_ir`'s copy.
pub fn is_guaranteed_unsized_raw(self) -> bool {
match self.kind() {
Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true,
Bool
| Char
| Int(_)
| Uint(_)
| Float(_)
| Adt(_, _)
| Foreign(_)
| Array(_, _)
| Pat(_, _)
| RawPtr(_, _)
| Ref(_, _, _)
| FnDef(_, _)
| FnPtr(_, _)
| UnsafeBinder(_)
| Closure(_, _)
| CoroutineClosure(_, _)
| Coroutine(_, _)
| CoroutineWitness(_, _)
| Never
| Tuple(_)
| Alias(_, _)
| Param(_)
| Bound(_, _)
| Placeholder(_)
| Infer(_)
| Error(_)
| Dynamic(_, _, ty::DynStar) => false,
}
}
}
impl<'tcx> rustc_type_ir::inherent::Tys<TyCtxt<'tcx>> for &'tcx ty::List<Ty<'tcx>> {

View file

@ -237,7 +237,7 @@ where
// has a trivially false `Sized` predicate). If it's the latter, we cannot
// delay a bug because we can have trivially false where clauses, so we
// treat it as rigid.
if goal_trait_ref.self_ty().is_guaranteed_unsized_raw() {
if cx.impl_self_is_guaranteed_unsized(impl_def_id) {
ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
} else {

View file

@ -2007,21 +2007,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
"confirm_impl_candidate: no associated type {:?} for {:?}",
assoc_ty.item.name, obligation.predicate
);
let tail = selcx.tcx().struct_tail_raw(
tcx.type_of(impl_def_id).instantiate(tcx, args),
|ty| {
normalize_with_depth_to(
selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
ty,
&mut nested,
)
},
|| {},
);
if tail.is_guaranteed_unsized_raw() {
if tcx.impl_self_is_guaranteed_unsized(impl_def_id) {
// We treat this projection as rigid here, which is represented via
// `Projected::NoProgress`. This will ensure that the projection is
// checked for well-formedness, and it's either satisfied by a trivial

View file

@ -3,6 +3,7 @@ use rustc_hir as hir;
use rustc_hir::LangItem;
use rustc_hir::def::DefKind;
use rustc_index::bit_set::DenseBitSet;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::bug;
use rustc_middle::query::Providers;
use rustc_middle::ty::{
@ -312,6 +313,61 @@ fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> DenseBitSe
unsizing_params
}
fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) -> bool {
debug_assert_eq!(tcx.def_kind(impl_def_id), DefKind::Impl { of_trait: true });
let infcx = tcx.infer_ctxt().ignoring_regions().build(ty::TypingMode::non_body_analysis());
let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
let cause = traits::ObligationCause::dummy();
let param_env = tcx.param_env(impl_def_id);
let tail = tcx.struct_tail_raw(
tcx.type_of(impl_def_id).instantiate_identity(),
|ty| {
ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| {
Ty::new_error_with_message(
tcx,
tcx.def_span(impl_def_id),
"struct tail should be computable",
)
})
},
|| (),
);
match tail.kind() {
ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true,
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Adt(_, _)
| ty::Foreign(_)
| ty::Array(_, _)
| ty::Pat(_, _)
| ty::RawPtr(_, _)
| ty::Ref(_, _, _)
| ty::FnDef(_, _)
| ty::FnPtr(_, _)
| ty::UnsafeBinder(_)
| ty::Closure(_, _)
| ty::CoroutineClosure(_, _)
| ty::Coroutine(_, _)
| ty::CoroutineWitness(_, _)
| ty::Never
| ty::Tuple(_)
| ty::Alias(_, _)
| ty::Param(_)
| ty::Bound(_, _)
| ty::Placeholder(_)
| ty::Infer(_)
| ty::Error(_)
| ty::Dynamic(_, _, ty::DynStar) => false,
}
}
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers {
asyncness,
@ -320,6 +376,7 @@ pub(crate) fn provide(providers: &mut Providers) {
param_env_normalized_for_post_analysis,
defaultness,
unsizing_params_for_adt,
impl_self_is_guaranteed_unsized,
..*providers
};
}

View file

@ -255,6 +255,8 @@ pub trait Interner:
def_id: Self::DefId,
) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>;
fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool;
fn has_target_features(self, def_id: Self::DefId) -> bool;
fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId;