Rollup merge of #119766 - oli-obk:split_tait_and_atpit, r=compiler-errors
Split tait and impl trait in assoc items logic And simplify the assoc item logic where applicable. This separation shows that it is easier to reason about impl trait in assoc items compared with TAITs. See https://rust-lang.zulipchat.com/#narrow/stream/315482-t-compiler.2Fetc.2Fopaque-types/topic/impl.20trait.20in.20associated.20type for some discussion. The current plan is to try to stabilize impl trait in associated items before TAIT, as they do not have any issues with their defining scopes (see https://github.com/rust-lang/rust/issues/107645 for why this is not a trivial or uncontroversial topic).
This commit is contained in:
commit
5da220a095
21 changed files with 615 additions and 73 deletions
|
@ -118,6 +118,69 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
|
|||
}
|
||||
TaitInBodyFinder { collector: self }.visit_expr(body);
|
||||
}
|
||||
|
||||
fn visit_opaque_ty(&mut self, alias_ty: &ty::AliasTy<'tcx>) {
|
||||
if !self.seen.insert(alias_ty.def_id.expect_local()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TAITs outside their defining scopes are ignored.
|
||||
let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
|
||||
trace!(?origin);
|
||||
match origin {
|
||||
rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
|
||||
rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
|
||||
if !in_assoc_ty {
|
||||
if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.opaques.push(alias_ty.def_id.expect_local());
|
||||
|
||||
let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
|
||||
// Only check that the parent generics of the TAIT/RPIT are unique.
|
||||
// the args owned by the opaque are going to always be duplicate
|
||||
// lifetime params for RPITs, and empty for TAITs.
|
||||
match self
|
||||
.tcx
|
||||
.uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction)
|
||||
{
|
||||
Ok(()) => {
|
||||
// FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
|
||||
// supported at all, so this is sound to do, but once we want to support them, you'll
|
||||
// start seeing the error below.
|
||||
|
||||
// Collect opaque types nested within the associated type bounds of this opaque type.
|
||||
// We use identity args here, because we already know that the opaque type uses
|
||||
// only generic parameters, and thus substituting would not give us more information.
|
||||
for (pred, span) in self
|
||||
.tcx
|
||||
.explicit_item_bounds(alias_ty.def_id)
|
||||
.instantiate_identity_iter_copied()
|
||||
{
|
||||
trace!(?pred);
|
||||
self.visit_spanned(span, pred);
|
||||
}
|
||||
}
|
||||
Err(NotUniqueParam::NotParam(arg)) => {
|
||||
self.tcx.dcx().emit_err(NotParam {
|
||||
arg,
|
||||
span: self.span(),
|
||||
opaque_span: self.tcx.def_span(alias_ty.def_id),
|
||||
});
|
||||
}
|
||||
Err(NotUniqueParam::DuplicateParam(arg)) => {
|
||||
self.tcx.dcx().emit_err(DuplicateArg {
|
||||
arg,
|
||||
span: self.span(),
|
||||
opaque_span: self.tcx.def_span(alias_ty.def_id),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
|
||||
|
@ -134,67 +197,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
|
|||
t.super_visit_with(self)?;
|
||||
match t.kind() {
|
||||
ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
|
||||
if !self.seen.insert(alias_ty.def_id.expect_local()) {
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
// TAITs outside their defining scopes are ignored.
|
||||
let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
|
||||
trace!(?origin);
|
||||
match origin {
|
||||
rustc_hir::OpaqueTyOrigin::FnReturn(_)
|
||||
| rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
|
||||
rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
|
||||
if !in_assoc_ty {
|
||||
if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.opaques.push(alias_ty.def_id.expect_local());
|
||||
|
||||
let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
|
||||
// Only check that the parent generics of the TAIT/RPIT are unique.
|
||||
// the args owned by the opaque are going to always be duplicate
|
||||
// lifetime params for RPITs, and empty for TAITs.
|
||||
match self.tcx.uses_unique_generic_params(
|
||||
&alias_ty.args[..parent_count],
|
||||
CheckRegions::FromFunction,
|
||||
) {
|
||||
Ok(()) => {
|
||||
// FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
|
||||
// supported at all, so this is sound to do, but once we want to support them, you'll
|
||||
// start seeing the error below.
|
||||
|
||||
// Collect opaque types nested within the associated type bounds of this opaque type.
|
||||
// We use identity args here, because we already know that the opaque type uses
|
||||
// only generic parameters, and thus substituting would not give us more information.
|
||||
for (pred, span) in self
|
||||
.tcx
|
||||
.explicit_item_bounds(alias_ty.def_id)
|
||||
.instantiate_identity_iter_copied()
|
||||
{
|
||||
trace!(?pred);
|
||||
self.visit_spanned(span, pred);
|
||||
}
|
||||
}
|
||||
Err(NotUniqueParam::NotParam(arg)) => {
|
||||
self.tcx.dcx().emit_err(NotParam {
|
||||
arg,
|
||||
span: self.span(),
|
||||
opaque_span: self.tcx.def_span(alias_ty.def_id),
|
||||
});
|
||||
}
|
||||
Err(NotUniqueParam::DuplicateParam(arg)) => {
|
||||
self.tcx.dcx().emit_err(DuplicateArg {
|
||||
arg,
|
||||
span: self.span(),
|
||||
opaque_span: self.tcx.def_span(alias_ty.def_id),
|
||||
});
|
||||
}
|
||||
}
|
||||
self.visit_opaque_ty(alias_ty);
|
||||
}
|
||||
ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => {
|
||||
self.tcx
|
||||
|
@ -272,6 +275,91 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
struct ImplTraitInAssocTypeCollector<'tcx>(OpaqueTypeCollector<'tcx>);
|
||||
|
||||
impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for ImplTraitInAssocTypeCollector<'tcx> {
|
||||
#[instrument(skip(self), ret, level = "trace")]
|
||||
fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> ControlFlow<!> {
|
||||
let old = self.0.span;
|
||||
self.0.span = Some(span);
|
||||
value.visit_with(self);
|
||||
self.0.span = old;
|
||||
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInAssocTypeCollector<'tcx> {
|
||||
#[instrument(skip(self), ret, level = "trace")]
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
|
||||
t.super_visit_with(self)?;
|
||||
match t.kind() {
|
||||
ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
|
||||
self.0.visit_opaque_ty(alias_ty);
|
||||
}
|
||||
ty::Alias(ty::Projection, alias_ty) => {
|
||||
// This avoids having to do normalization of `Self::AssocTy` by only
|
||||
// supporting the case of a method defining opaque types from assoc types
|
||||
// in the same impl block.
|
||||
let parent_trait_ref = self
|
||||
.0
|
||||
.parent_trait_ref()
|
||||
.expect("impl trait in assoc type collector used on non-assoc item");
|
||||
// If the trait ref of the associated item and the impl differs,
|
||||
// then we can't use the impl's identity substitutions below, so
|
||||
// just skip.
|
||||
if alias_ty.trait_ref(self.0.tcx) == parent_trait_ref {
|
||||
let parent = self.0.parent().expect("we should have a parent here");
|
||||
|
||||
for &assoc in self.0.tcx.associated_items(parent).in_definition_order() {
|
||||
trace!(?assoc);
|
||||
if assoc.trait_item_def_id != Some(alias_ty.def_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the type is further specializable, then the type_of
|
||||
// is not actually correct below.
|
||||
if !assoc.defaultness(self.0.tcx).is_final() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let impl_args = alias_ty.args.rebase_onto(
|
||||
self.0.tcx,
|
||||
parent_trait_ref.def_id,
|
||||
ty::GenericArgs::identity_for_item(self.0.tcx, parent),
|
||||
);
|
||||
|
||||
if check_args_compatible(self.0.tcx, assoc, impl_args) {
|
||||
return self
|
||||
.0
|
||||
.tcx
|
||||
.type_of(assoc.def_id)
|
||||
.instantiate(self.0.tcx, impl_args)
|
||||
.visit_with(self);
|
||||
} else {
|
||||
self.0.tcx.dcx().span_delayed_bug(
|
||||
self.0.tcx.def_span(assoc.def_id),
|
||||
"item had incorrect args",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => trace!(kind=?t.kind()),
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_trait_in_assoc_types_defined_by<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item: LocalDefId,
|
||||
) -> &'tcx ty::List<LocalDefId> {
|
||||
let mut collector = ImplTraitInAssocTypeCollector(OpaqueTypeCollector::new(tcx, item));
|
||||
super::sig_types::walk_types(tcx, item, &mut collector);
|
||||
tcx.mk_local_def_ids(&collector.0.opaques)
|
||||
}
|
||||
|
||||
fn opaque_types_defined_by<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item: LocalDefId,
|
||||
|
@ -321,5 +409,6 @@ fn opaque_types_defined_by<'tcx>(
|
|||
}
|
||||
|
||||
pub(super) fn provide(providers: &mut Providers) {
|
||||
*providers = Providers { opaque_types_defined_by, ..*providers };
|
||||
*providers =
|
||||
Providers { opaque_types_defined_by, impl_trait_in_assoc_types_defined_by, ..*providers };
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue