Auto merge of #101679 - compiler-errors:rpitit-default-body, r=nikomatsakis
Support default-body trait functions with return-position `impl Trait` in traits Introduce a new `Trait` candidate kind for the `ImplTraitInTrait` projection candidate, which just projects an RPITIT down to its opaque type form. This is a hack until we lower RPITITs to regular associated types, after which we will need to rework how these default bodies are type-checked, so comments are left in a few places for us to clean up later. Fixes #101665
This commit is contained in:
commit
0938e1680d
14 changed files with 166 additions and 129 deletions
|
@ -522,23 +522,33 @@ fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
|
|||
|
||||
/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
|
||||
/// projections that would result in "inheriting lifetimes".
|
||||
pub(super) fn check_opaque<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
substs: SubstsRef<'tcx>,
|
||||
origin: &hir::OpaqueTyOrigin,
|
||||
) {
|
||||
let span = tcx.def_span(def_id);
|
||||
check_opaque_for_inheriting_lifetimes(tcx, def_id, span);
|
||||
if tcx.type_of(def_id).references_error() {
|
||||
fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
|
||||
let item = tcx.hir().item(id);
|
||||
let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
|
||||
tcx.sess.delay_span_bug(tcx.hir().span(id.hir_id()), "expected opaque item");
|
||||
return;
|
||||
}
|
||||
if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() {
|
||||
return;
|
||||
}
|
||||
check_opaque_meets_bounds(tcx, def_id, substs, span, origin);
|
||||
}
|
||||
};
|
||||
|
||||
// HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
|
||||
// `async-std` (and `pub async fn` in general).
|
||||
// Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it!
|
||||
// See https://github.com/rust-lang/rust/issues/75100
|
||||
if tcx.sess.opts.actually_rustdoc {
|
||||
return;
|
||||
}
|
||||
|
||||
let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id());
|
||||
let span = tcx.def_span(item.def_id.def_id);
|
||||
|
||||
check_opaque_for_inheriting_lifetimes(tcx, item.def_id.def_id, span);
|
||||
if tcx.type_of(item.def_id.def_id).references_error() {
|
||||
return;
|
||||
}
|
||||
if check_opaque_for_cycles(tcx, item.def_id.def_id, substs, span, &origin).is_err() {
|
||||
return;
|
||||
}
|
||||
check_opaque_meets_bounds(tcx, item.def_id.def_id, substs, span, &origin);
|
||||
}
|
||||
/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
|
||||
/// in "inheriting lifetimes".
|
||||
#[instrument(level = "debug", skip(tcx, span))]
|
||||
|
@ -857,17 +867,17 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
|
|||
check_union(tcx, id.def_id.def_id);
|
||||
}
|
||||
DefKind::OpaqueTy => {
|
||||
let item = tcx.hir().item(id);
|
||||
let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
|
||||
return;
|
||||
};
|
||||
// HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
|
||||
// `async-std` (and `pub async fn` in general).
|
||||
// Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it!
|
||||
// See https://github.com/rust-lang/rust/issues/75100
|
||||
if !tcx.sess.opts.actually_rustdoc {
|
||||
let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id());
|
||||
check_opaque(tcx, item.def_id.def_id, substs, &origin);
|
||||
check_opaque(tcx, id);
|
||||
}
|
||||
DefKind::ImplTraitPlaceholder => {
|
||||
let parent = tcx.impl_trait_in_trait_parent(id.def_id.to_def_id());
|
||||
// Only check the validity of this opaque type if the function has a default body
|
||||
if let hir::Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
|
||||
..
|
||||
}) = tcx.hir().get_by_def_id(parent.expect_local())
|
||||
{
|
||||
check_opaque(tcx, id);
|
||||
}
|
||||
}
|
||||
DefKind::TyAlias => {
|
||||
|
|
|
@ -340,10 +340,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
|
|||
..
|
||||
}) => {
|
||||
if in_trait {
|
||||
span_bug!(item.span, "impl-trait in trait has no default")
|
||||
} else {
|
||||
find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
|
||||
assert!(tcx.impl_defaultness(owner).has_value());
|
||||
}
|
||||
find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
|
||||
}
|
||||
ItemKind::Trait(..)
|
||||
| ItemKind::TraitAlias(..)
|
||||
|
|
|
@ -1161,6 +1161,7 @@ impl<'tcx> ProjectionTy<'tcx> {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) {
|
||||
let def_id = tcx.parent(self.item_def_id);
|
||||
assert_eq!(tcx.def_kind(def_id), DefKind::Trait);
|
||||
let trait_generics = tcx.generics_of(def_id);
|
||||
(
|
||||
ty::TraitRef { def_id, substs: self.substs.truncate_to(tcx, trait_generics) },
|
||||
|
|
|
@ -122,8 +122,20 @@ where
|
|||
&mut self,
|
||||
projection: ty::ProjectionTy<'tcx>,
|
||||
) -> ControlFlow<V::BreakTy> {
|
||||
let (trait_ref, assoc_substs) =
|
||||
projection.trait_ref_and_own_substs(self.def_id_visitor.tcx());
|
||||
let tcx = self.def_id_visitor.tcx();
|
||||
let (trait_ref, assoc_substs) = if tcx.def_kind(projection.item_def_id)
|
||||
!= DefKind::ImplTraitPlaceholder
|
||||
{
|
||||
projection.trait_ref_and_own_substs(tcx)
|
||||
} else {
|
||||
// HACK(RPITIT): Remove this when RPITITs are lowered to regular assoc tys
|
||||
let def_id = tcx.impl_trait_in_trait_parent(projection.item_def_id);
|
||||
let trait_generics = tcx.generics_of(def_id);
|
||||
(
|
||||
ty::TraitRef { def_id, substs: projection.substs.truncate_to(tcx, trait_generics) },
|
||||
&projection.substs[trait_generics.count()..],
|
||||
)
|
||||
};
|
||||
self.visit_trait(trait_ref)?;
|
||||
if self.def_id_visitor.shallow() {
|
||||
ControlFlow::CONTINUE
|
||||
|
|
|
@ -72,7 +72,15 @@ enum ProjectionCandidate<'tcx> {
|
|||
/// From an "impl" (or a "pseudo-impl" returned by select)
|
||||
Select(Selection<'tcx>),
|
||||
|
||||
ImplTraitInTrait(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>),
|
||||
ImplTraitInTrait(ImplTraitInTraitCandidate<'tcx>),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
enum ImplTraitInTraitCandidate<'tcx> {
|
||||
// The `impl Trait` from a trait function's default body
|
||||
Trait,
|
||||
// A concrete type provided from a trait's `impl Trait` from an impl
|
||||
Impl(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>),
|
||||
}
|
||||
|
||||
enum ProjectionCandidateSet<'tcx> {
|
||||
|
@ -1319,6 +1327,19 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
|
|||
let tcx = selcx.tcx();
|
||||
if tcx.def_kind(obligation.predicate.item_def_id) == DefKind::ImplTraitPlaceholder {
|
||||
let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.item_def_id);
|
||||
// If we are trying to project an RPITIT with trait's default `Self` parameter,
|
||||
// then we must be within a default trait body.
|
||||
if obligation.predicate.self_ty()
|
||||
== ty::InternalSubsts::identity_for_item(tcx, obligation.predicate.item_def_id)
|
||||
.type_at(0)
|
||||
&& tcx.associated_item(trait_fn_def_id).defaultness(tcx).has_value()
|
||||
{
|
||||
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(
|
||||
ImplTraitInTraitCandidate::Trait,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
let trait_def_id = tcx.parent(trait_fn_def_id);
|
||||
let trait_substs =
|
||||
obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id));
|
||||
|
@ -1330,7 +1351,9 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
|
|||
let _ =
|
||||
selcx.infcx().commit_if_ok(|_| match selcx.select(&obligation.with(trait_predicate)) {
|
||||
Ok(Some(super::ImplSource::UserDefined(data))) => {
|
||||
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
|
||||
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(
|
||||
ImplTraitInTraitCandidate::Impl(data),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
Ok(None) => {
|
||||
|
@ -1792,9 +1815,18 @@ fn confirm_candidate<'cx, 'tcx>(
|
|||
ProjectionCandidate::Select(impl_source) => {
|
||||
confirm_select_candidate(selcx, obligation, impl_source)
|
||||
}
|
||||
ProjectionCandidate::ImplTraitInTrait(data) => {
|
||||
ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Impl(data)) => {
|
||||
confirm_impl_trait_in_trait_candidate(selcx, obligation, data)
|
||||
}
|
||||
// If we're projecting an RPITIT for a default trait body, that's just
|
||||
// the same def-id, but as an opaque type (with regular RPIT semantics).
|
||||
ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Trait) => Progress {
|
||||
term: selcx
|
||||
.tcx()
|
||||
.mk_opaque(obligation.predicate.item_def_id, obligation.predicate.substs)
|
||||
.into(),
|
||||
obligations: vec![],
|
||||
},
|
||||
};
|
||||
|
||||
// When checking for cycle during evaluation, we compare predicates with
|
||||
|
@ -2211,6 +2243,16 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
|
|||
return Progress { term: tcx.ty_error().into(), obligations };
|
||||
}
|
||||
|
||||
// Use the default `impl Trait` for the trait, e.g., for a default trait body
|
||||
if leaf_def.item.container == ty::AssocItemContainer::TraitContainer {
|
||||
return Progress {
|
||||
term: tcx
|
||||
.mk_opaque(obligation.predicate.item_def_id, obligation.predicate.substs)
|
||||
.into(),
|
||||
obligations,
|
||||
};
|
||||
}
|
||||
|
||||
let impl_fn_def_id = leaf_def.item.def_id;
|
||||
let impl_fn_substs = obligation.predicate.substs.rebase_onto(tcx, trait_fn_def_id, data.substs);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue