Skip to content

Rigidly project missing item due to guaranteed impossible sized predicate #139000

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

Merged
merged 4 commits into from
Apr 10, 2025
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
30 changes: 3 additions & 27 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -937,31 +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 = match 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",
)
})
},
|| (),
)
.kind()
{
ty::Dynamic(_, _, ty::DynKind::Dyn) | ty::Slice(_) | ty::Str => true,
_ => false,
};
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);
Expand Down Expand Up @@ -992,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()),
Expand Down Expand Up @@ -1027,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));
}
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
32 changes: 29 additions & 3 deletions compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ where
// Add GAT where clauses from the trait's definition
// FIXME: We don't need these, since these are the type's own WF obligations.
ecx.add_goals(
GoalSource::Misc,
GoalSource::AliasWellFormed,
cx.own_predicates_of(goal.predicate.def_id())
.iter_instantiated(cx, goal.predicate.alias.args)
.map(|pred| goal.with(cx, pred)),
Expand Down Expand Up @@ -199,7 +199,7 @@ where
// Add GAT where clauses from the trait's definition.
// FIXME: We don't need these, since these are the type's own WF obligations.
ecx.add_goals(
GoalSource::Misc,
GoalSource::AliasWellFormed,
cx.own_predicates_of(goal.predicate.def_id())
.iter_instantiated(cx, goal.predicate.alias.args)
.map(|pred| goal.with(cx, pred)),
Expand Down Expand Up @@ -232,7 +232,33 @@ where
};

if !cx.has_item_definition(target_item_def_id) {
return error_response(ecx, cx.delay_bug("missing item"));
// If the impl is missing an item, it's either because the user forgot to
// provide it, or the user is not *obligated* to provide it (because it
// 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 cx.impl_self_is_guaranteed_unsized(impl_def_id) {
match ecx.typing_mode() {
ty::TypingMode::Coherence => {
return ecx.evaluate_added_goals_and_make_canonical_response(
Certainty::AMBIGUOUS,
);
}
ty::TypingMode::Analysis { .. }
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::PostAnalysis => {
ecx.structurally_instantiate_normalizes_to_term(
goal,
goal.predicate.alias,
);
return ecx
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
}
}
} else {
return error_response(ecx, cx.delay_bug("missing item"));
}
}

let target_container_def_id = cx.parent(target_item_def_id);
Expand Down
90 changes: 45 additions & 45 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -669,30 +669,11 @@ fn project<'cx, 'tcx>(

match candidates {
ProjectionCandidateSet::Single(candidate) => {
Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate)))
confirm_candidate(selcx, obligation, candidate)
}
ProjectionCandidateSet::None => {
let tcx = selcx.tcx();
let term = match tcx.def_kind(obligation.predicate.def_id) {
DefKind::AssocTy => Ty::new_projection_from_args(
tcx,
obligation.predicate.def_id,
obligation.predicate.args,
)
.into(),
DefKind::AssocConst => ty::Const::new_unevaluated(
tcx,
ty::UnevaluatedConst::new(
obligation.predicate.def_id,
obligation.predicate.args,
),
)
.into(),
kind => {
bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id))
}
};

let term = obligation.predicate.to_term(tcx);
Ok(Projected::NoProgress(term))
}
// Error occurred while trying to processing impls.
Expand Down Expand Up @@ -1244,18 +1225,16 @@ fn confirm_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
candidate: ProjectionCandidate<'tcx>,
) -> Progress<'tcx> {
) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
debug!(?obligation, ?candidate, "confirm_candidate");
let mut progress = match candidate {
let mut result = match candidate {
ProjectionCandidate::ParamEnv(poly_projection)
| ProjectionCandidate::Object(poly_projection) => {
confirm_param_env_candidate(selcx, obligation, poly_projection, false)
}

ProjectionCandidate::TraitDef(poly_projection) => {
confirm_param_env_candidate(selcx, obligation, poly_projection, true)
}

| ProjectionCandidate::Object(poly_projection) => Ok(Projected::Progress(
confirm_param_env_candidate(selcx, obligation, poly_projection, false),
)),
ProjectionCandidate::TraitDef(poly_projection) => Ok(Projected::Progress(
confirm_param_env_candidate(selcx, obligation, poly_projection, true),
)),
ProjectionCandidate::Select(impl_source) => {
confirm_select_candidate(selcx, obligation, impl_source)
}
Expand All @@ -1266,23 +1245,26 @@ fn confirm_candidate<'cx, 'tcx>(
// with new region variables, we need to resolve them to existing variables
// when possible for this to work. See `auto-trait-projection-recursion.rs`
// for a case where this matters.
if progress.term.has_infer_regions() {
if let Ok(Projected::Progress(progress)) = &mut result
&& progress.term.has_infer_regions()
{
progress.term = progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx));
}
progress

result
}

fn confirm_select_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
impl_source: Selection<'tcx>,
) -> Progress<'tcx> {
) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
match impl_source {
ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
ImplSource::Builtin(BuiltinImplSource::Misc | BuiltinImplSource::Trivial, data) => {
let tcx = selcx.tcx();
let trait_def_id = obligation.predicate.trait_def_id(tcx);
if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) {
let progress = if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) {
confirm_coroutine_candidate(selcx, obligation, data)
} else if tcx.is_lang_item(trait_def_id, LangItem::Future) {
confirm_future_candidate(selcx, obligation, data)
Expand All @@ -1304,7 +1286,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
confirm_async_fn_kind_helper_candidate(selcx, obligation, data)
} else {
confirm_builtin_candidate(selcx, obligation, data)
}
};
Ok(Projected::Progress(progress))
}
ImplSource::Builtin(BuiltinImplSource::Object { .. }, _)
| ImplSource::Param(..)
Expand Down Expand Up @@ -2000,7 +1983,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
impl_impl_source: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
let tcx = selcx.tcx();

let ImplSourceUserDefinedData { impl_def_id, args, mut nested } = impl_impl_source;
Expand All @@ -2011,19 +1994,33 @@ fn confirm_impl_candidate<'cx, 'tcx>(
let param_env = obligation.param_env;
let assoc_ty = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
Ok(assoc_ty) => assoc_ty,
Err(guar) => return Progress::error(tcx, guar),
Err(guar) => return Ok(Projected::Progress(Progress::error(tcx, guar))),
};

// This means that the impl is missing a definition for the
// associated type. This is either because the associate item
// has impossible-to-satisfy predicates (since those were
// allowed in <https://github.com/rust-lang/rust/pull/135480>),
// or because the impl is literally missing the definition.
if !assoc_ty.item.defaultness(tcx).has_value() {
// This means that the impl is missing a definition for the
// associated type. This error will be reported by the type
// checker method `check_impl_items_against_trait`, so here we
// just return Error.
debug!(
"confirm_impl_candidate: no associated type {:?} for {:?}",
assoc_ty.item.name, obligation.predicate
);
return Progress { term: Ty::new_misc_error(tcx).into(), obligations: nested };
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
// where clause in its env or it results in an error.
return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx)));
} else {
return Ok(Projected::Progress(Progress {
term: Ty::new_misc_error(tcx).into(),
obligations: nested,
}));
}
}

// If we're trying to normalize `<Vec<u32> as X>::A<S>` using
//`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
//
Expand All @@ -2033,6 +2030,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args);
let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node);
let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);

let term: ty::EarlyBinder<'tcx, ty::Term<'tcx>> = if is_const {
let did = assoc_ty.item.def_id;
let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did);
Expand All @@ -2041,7 +2039,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
} else {
tcx.type_of(assoc_ty.item.def_id).map_bound(|ty| ty.into())
};
if !tcx.check_args_compatible(assoc_ty.item.def_id, args) {

let progress = if !tcx.check_args_compatible(assoc_ty.item.def_id, args) {
let err = Ty::new_error_with_message(
tcx,
obligation.cause.span,
Expand All @@ -2051,7 +2050,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
} else {
assoc_ty_own_obligations(selcx, obligation, &mut nested);
Progress { term: term.instantiate(tcx, args), obligations: nested }
}
};
Ok(Projected::Progress(progress))
}

// Get obligations corresponding to the predicates from the where-clause of the
Expand Down
57 changes: 57 additions & 0 deletions compiler/rustc_ty_utils/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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,
Expand All @@ -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
};
}
Loading
Loading