Rollup merge of #110454 - oli-obk:limited_impl_trait_in_assoc_type, r=compiler-errors

Require impl Trait in associated types to appear in method signatures

This implements the limited version of TAIT that was proposed in https://github.com/rust-lang/rust/issues/107645#issuecomment-1477899536

Similar to `impl Trait` in return types, `impl Trait` in associated types may only be used within the impl block which it is a part of. To make everything simpler and forward compatible to getting desugared to a plain type alias impl trait in the future, we're requiring that any associated functions or constants that want to register hidden types must be using the associated type in their signature (type of the constant or argument/return type of the associated method. Where bounds mentioning the associated type are ignored).

We have preexisting tests checking that this works transitively across multiple associated types in situations like

```rust
impl Foo for Bar {
    type A = impl Trait;
    type B = impl Iterator<Item = Self::A>;
    fn foo() -> Self::B { ...... }
}
```
This commit is contained in:
Dylan DPC 2023-05-13 11:05:32 +05:30 committed by GitHub
commit 6cb13585d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 560 additions and 68 deletions

View File

@ -305,7 +305,10 @@ fn lower_item_kind(
);
this.arena.alloc(this.ty(span, hir::TyKind::Err(guar)))
}
Some(ty) => this.lower_ty(ty, &ImplTraitContext::TypeAliasesOpaqueTy),
Some(ty) => this.lower_ty(
ty,
&ImplTraitContext::TypeAliasesOpaqueTy { in_assoc_ty: false },
),
},
);
hir::ItemKind::TyAlias(ty, generics)
@ -852,7 +855,10 @@ fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
hir::ImplItemKind::Type(ty)
}
Some(ty) => {
let ty = this.lower_ty(ty, &ImplTraitContext::TypeAliasesOpaqueTy);
let ty = this.lower_ty(
ty,
&ImplTraitContext::TypeAliasesOpaqueTy { in_assoc_ty: true },
);
hir::ImplItemKind::Type(ty)
}
},

View File

@ -247,7 +247,7 @@ enum ImplTraitContext {
in_trait: bool,
},
/// Impl trait in type aliases.
TypeAliasesOpaqueTy,
TypeAliasesOpaqueTy { in_assoc_ty: bool },
/// `impl Trait` is unstably accepted in this position.
FeatureGated(ImplTraitPosition, Symbol),
/// `impl Trait` is not accepted in this position.
@ -1407,14 +1407,15 @@ fn lower_ty_direct(&mut self, t: &Ty, itctx: &ImplTraitContext) -> hir::Ty<'hir>
*in_trait,
itctx,
),
ImplTraitContext::TypeAliasesOpaqueTy => self.lower_opaque_impl_trait(
span,
hir::OpaqueTyOrigin::TyAlias,
*def_node_id,
bounds,
false,
itctx,
),
&ImplTraitContext::TypeAliasesOpaqueTy { in_assoc_ty } => self
.lower_opaque_impl_trait(
span,
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty },
*def_node_id,
bounds,
false,
itctx,
),
ImplTraitContext::Universal => {
let span = t.span;
self.create_def(
@ -1534,13 +1535,16 @@ fn lower_opaque_impl_trait(
// If this came from a TAIT (as opposed to a function that returns an RPIT), we only want
// to capture the lifetimes that appear in the bounds. So visit the bounds to find out
// exactly which ones those are.
let lifetimes_to_remap = if origin == hir::OpaqueTyOrigin::TyAlias {
// in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't keep all the lifetime parameters
Vec::new()
} else {
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a` example,
// we only keep the lifetimes that appear in the `impl Debug` itself:
lifetime_collector::lifetimes_in_bounds(&self.resolver, bounds)
let lifetimes_to_remap = match origin {
hir::OpaqueTyOrigin::TyAlias { .. } => {
// in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't keep all the lifetime parameters
Vec::new()
}
hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..) => {
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a` example,
// we only keep the lifetimes that appear in the `impl Debug` itself:
lifetime_collector::lifetimes_in_bounds(&self.resolver, bounds)
}
};
debug!(?lifetimes_to_remap);

View File

@ -265,7 +265,7 @@ fn infer_opaque_definition_from_instantiation(
// Only check this for TAIT. RPIT already supports `tests/ui/impl-trait/nested-return-type2.rs`
// on stable and we'd break that.
let OpaqueTyOrigin::TyAlias = origin else {
let OpaqueTyOrigin::TyAlias { .. } = origin else {
return definition_ty;
};
let def_id = opaque_type_key.def_id;
@ -360,7 +360,7 @@ fn check_opaque_type_parameter_valid(
// which would error here on all of the `'static` args.
OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return Ok(()),
// Check these
OpaqueTyOrigin::TyAlias => {}
OpaqueTyOrigin::TyAlias { .. } => {}
}
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();

View File

@ -2662,7 +2662,10 @@ pub enum OpaqueTyOrigin {
/// `async fn`
AsyncFn(LocalDefId),
/// type aliases: `type Foo = impl Trait;`
TyAlias,
TyAlias {
/// associated types in impl blocks for traits.
in_assoc_ty: bool,
},
}
/// The various kinds of types recognized by the compiler.

View File

@ -397,7 +397,7 @@ fn check_opaque_meets_bounds<'tcx>(
) {
let defining_use_anchor = match *origin {
hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
hir::OpaqueTyOrigin::TyAlias => def_id,
hir::OpaqueTyOrigin::TyAlias { .. } => tcx.impl_trait_parent(def_id),
};
let param_env = tcx.param_env(defining_use_anchor);
@ -455,10 +455,10 @@ fn check_opaque_meets_bounds<'tcx>(
// They can only be referenced as `<Opaque<T> as Trait<&'static T>>::AssocTy`.
// We don't have to check them here because their well-formedness follows from the WF of
// the projection input types in the defining- and use-sites.
hir::OpaqueTyOrigin::TyAlias
hir::OpaqueTyOrigin::TyAlias { .. }
if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {}
// Can have different predicates to their defining use
hir::OpaqueTyOrigin::TyAlias => {
hir::OpaqueTyOrigin::TyAlias { .. } => {
let wf_tys = ocx.assumed_wf_types(param_env, span, def_id);
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);

View File

@ -6,7 +6,7 @@
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::util::IgnoreRegions;
use rustc_middle::ty::util::CheckRegions;
use rustc_middle::ty::{self, TyCtxt};
use rustc_trait_selection::traits::{self, ObligationCtxt};
@ -81,7 +81,7 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
self_type_did: DefId,
adt_to_impl_substs: SubstsRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_substs, IgnoreRegions::No) else {
let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_substs, CheckRegions::OnlyEarlyBound) else {
return Ok(())
};

View File

@ -6,7 +6,7 @@
use rustc_errors::{Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::util::IgnoreRegions;
use rustc_middle::ty::util::CheckRegions;
use rustc_middle::ty::{
self, AliasKind, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
TypeVisitor,
@ -507,7 +507,7 @@ fn lint_auto_trait_impl<'tcx>(
// Impls which completely cover a given root type are fine as they
// disable auto impls entirely. So only lint if the substs
// are not a permutation of the identity substs.
let Err(arg) = tcx.uses_unique_generic_params(substs, IgnoreRegions::Yes) else {
let Err(arg) = tcx.uses_unique_generic_params(substs, CheckRegions::No) else {
// ok
return;
};

View File

@ -1483,7 +1483,7 @@ fn generator_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<hir::GeneratorK
fn is_type_alias_impl_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
match tcx.hir().get_by_def_id(def_id) {
Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(opaque), .. }) => {
matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias)
matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias { .. })
}
_ => bug!("tried getting opaque_ty_origin for non-opaque: {:?}", def_id),
}

View File

@ -159,7 +159,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
}
Some(fn_def_id.to_def_id())
}
ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => {
ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias { .. },
..
}) => {
let parent_id = tcx.hir().get_parent_item(hir_id);
assert_ne!(parent_id, hir::CRATE_OWNER_ID);
debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id);

View File

@ -721,7 +721,7 @@ pub(super) fn type_param_predicates(
| ItemKind::TyAlias(_, generics)
| ItemKind::OpaqueTy(OpaqueTy {
generics,
origin: hir::OpaqueTyOrigin::TyAlias,
origin: hir::OpaqueTyOrigin::TyAlias { .. },
..
})
| ItemKind::Enum(_, generics)

View File

@ -526,7 +526,8 @@ fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
});
}
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias, ..
origin: hir::OpaqueTyOrigin::TyAlias { .. },
..
}) => {
// Opaque types are visited when we visit the
// `TyKind::OpaqueDef`, so that they have the lifetimes from
@ -707,7 +708,7 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
let opaque_ty = self.tcx.hir().item(item_id);
match &opaque_ty.kind {
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias,
origin: hir::OpaqueTyOrigin::TyAlias { .. },
..
}) => {
intravisit::walk_ty(self, ty);

View File

@ -426,9 +426,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
let substs = InternalSubsts::identity_for_item(tcx, def_id);
tcx.mk_adt(def, substs)
}
ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => {
find_opaque_ty_constraints_for_tait(tcx, def_id)
}
ItemKind::OpaqueTy(OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias { .. },
..
}) => find_opaque_ty_constraints_for_tait(tcx, def_id),
// Opaque types desugared from `impl Trait`.
ItemKind::OpaqueTy(OpaqueTy {
origin:

View File

@ -2,6 +2,7 @@
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
use rustc_errors::{pluralize, Diagnostic, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::print::Printer;
@ -256,6 +257,15 @@ fn foo(&self, x: T) -> T { x }
);
}
}
(ty::Alias(ty::Opaque, alias), _) | (_, ty::Alias(ty::Opaque, alias)) if alias.def_id.is_local() && matches!(tcx.def_kind(body_owner_def_id), DefKind::AssocFn | DefKind::AssocConst) => {
if tcx.is_type_alias_impl_trait(alias.def_id) {
if !tcx.opaque_types_defined_by(body_owner_def_id.expect_local()).contains(&alias.def_id.expect_local()) {
diag.span_note(tcx.def_span(body_owner_def_id), "\
this item must have the opaque type in its signature \
in order to be able to register hidden types");
}
}
}
(ty::FnPtr(_), ty::FnDef(def, _))
if let hir::def::DefKind::Fn = tcx.def_kind(def) => {
diag.note(

View File

@ -149,7 +149,7 @@ pub fn handle_opaque_type(
// no one encounters it in practice.
// It does occur however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`,
// where it is of no concern, so we only check for TAITs.
if let Some(OpaqueTyOrigin::TyAlias) =
if let Some(OpaqueTyOrigin::TyAlias { .. }) =
b_def_id.as_local().and_then(|b_def_id| self.opaque_type_origin(b_def_id))
{
self.tcx.sess.emit_err(OpaqueHiddenTypeDiag {
@ -381,8 +381,12 @@ pub fn opaque_type_origin(&self, def_id: LocalDefId) -> Option<OpaqueTyOrigin> {
// Anonymous `impl Trait`
hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id,
// Named `type Foo = impl Bar;`
hir::OpaqueTyOrigin::TyAlias => {
may_define_opaque_type(self.tcx, parent_def_id, opaque_hir_id)
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
if in_assoc_ty {
self.tcx.opaque_types_defined_by(parent_def_id).contains(&def_id)
} else {
may_define_opaque_type(self.tcx, parent_def_id, opaque_hir_id)
}
}
};
in_definition_scope.then_some(origin)

View File

@ -1641,9 +1641,10 @@ fn encode_info_for_item(&mut self, item: &'tcx hir::Item<'tcx>) {
}
hir::ItemKind::OpaqueTy(ref opaque) => {
self.encode_explicit_item_bounds(def_id);
self.tables
.is_type_alias_impl_trait
.set(def_id.index, matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias));
self.tables.is_type_alias_impl_trait.set(
def_id.index,
matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias { .. }),
);
}
hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
self.tables.impl_defaultness.set_some(def_id.index, *defaultness);

View File

@ -172,6 +172,10 @@ impl EraseType for ty::Binder<'_, ty::FnSig<'_>> {
type Result = [u8; size_of::<ty::Binder<'static, ty::FnSig<'static>>>()];
}
impl EraseType for ty::Binder<'_, &'_ ty::List<Ty<'_>>> {
type Result = [u8; size_of::<ty::Binder<'static, &'static ty::List<Ty<'static>>>>()];
}
impl<T0, T1> EraseType for (&'_ T0, &'_ T1) {
type Result = [u8; size_of::<(&'static (), &'static ())>()];
}

View File

@ -236,6 +236,15 @@
cache_on_disk_if { key.is_local() }
}
query opaque_types_defined_by(
key: LocalDefId
) -> &'tcx [LocalDefId] {
desc {
|tcx| "computing the opaque types defined by `{}`",
tcx.def_path_str(key.to_def_id())
}
}
/// Returns the list of bounds that can be used for
/// `SelectionCandidate::ProjectionCandidate(_)` and
/// `ProjectionTyCandidate::TraitDef`.

View File

@ -2476,6 +2476,18 @@ pub fn impl_trait_in_trait_parent_fn(self, mut def_id: DefId) -> DefId {
}
}
/// Returns the `DefId` of the item within which the `impl Trait` is declared.
/// For type-alias-impl-trait this is the `type` alias.
/// For impl-trait-in-assoc-type this is the assoc type.
/// For return-position-impl-trait this is the function.
pub fn impl_trait_parent(self, mut def_id: LocalDefId) -> LocalDefId {
// Find the surrounding item (type alias or assoc type)
while let DefKind::OpaqueTy = self.def_kind(def_id) {
def_id = self.local_parent(def_id);
}
def_id
}
pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool {
if self.def_kind(def_id) != DefKind::AssocFn {
return false;
@ -2520,7 +2532,7 @@ pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<LocalDefId>
hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => {
Some(parent)
}
hir::OpaqueTyOrigin::TyAlias => None,
hir::OpaqueTyOrigin::TyAlias { .. } => None,
};
}
}

View File

@ -32,7 +32,7 @@ impl<'tcx> TyCtxt<'tcx> {
///
/// This should only be used outside of type inference. For example,
/// it assumes that normalization will succeed.
#[tracing::instrument(level = "debug", skip(self, param_env))]
#[tracing::instrument(level = "debug", skip(self, param_env), ret)]
pub fn normalize_erasing_regions<T>(self, param_env: ty::ParamEnv<'tcx>, value: T) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,

View File

@ -1265,7 +1265,7 @@ pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
/// Extracts the underlying trait reference and own substs from this projection.
/// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`,
/// then this function would return a `T: Iterator` trait reference and `['a]` as the own substs
/// then this function would return a `T: StreamingIterator` trait reference and `['a]` as the own substs
pub fn trait_ref_and_own_substs(
self,
tcx: TyCtxt<'tcx>,

View File

@ -34,9 +34,14 @@ pub struct Discr<'tcx> {
/// Used as an input to [`TyCtxt::uses_unique_generic_params`].
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum IgnoreRegions {
Yes,
pub enum CheckRegions {
No,
/// Only permit early bound regions. This is useful for Adts which
/// can never have late bound regions.
OnlyEarlyBound,
/// Permit both late bound and early bound regions. Use this for functions,
/// which frequently have late bound regions.
Bound,
}
#[derive(Copy, Clone, Debug)]
@ -468,21 +473,28 @@ pub fn destructor_constraints(self, def: ty::AdtDef<'tcx>) -> Vec<ty::subst::Gen
pub fn uses_unique_generic_params(
self,
substs: SubstsRef<'tcx>,
ignore_regions: IgnoreRegions,
ignore_regions: CheckRegions,
) -> Result<(), NotUniqueParam<'tcx>> {
let mut seen = GrowableBitSet::default();
let mut seen_late = FxHashSet::default();
for arg in substs {
match arg.unpack() {
GenericArgKind::Lifetime(lt) => {
if ignore_regions == IgnoreRegions::No {
let ty::ReEarlyBound(p) = lt.kind() else {
return Err(NotUniqueParam::NotParam(lt.into()))
};
GenericArgKind::Lifetime(lt) => match (ignore_regions, lt.kind()) {
(CheckRegions::Bound, ty::ReLateBound(di, reg)) => {
if !seen_late.insert((di, reg)) {
return Err(NotUniqueParam::DuplicateParam(lt.into()));
}
}
(CheckRegions::OnlyEarlyBound | CheckRegions::Bound, ty::ReEarlyBound(p)) => {
if !seen.insert(p.index) {
return Err(NotUniqueParam::DuplicateParam(lt.into()));
}
}
}
(CheckRegions::OnlyEarlyBound | CheckRegions::Bound, _) => {
return Err(NotUniqueParam::NotParam(lt.into()));
}
(CheckRegions::No, _) => {}
},
GenericArgKind::Type(t) => match t.kind() {
ty::Param(p) => {
if !seen.insert(p.index) {

View File

@ -55,3 +55,11 @@ ty_utils_multiple_array_fields_simd_type = monomorphising SIMD type `{$ty}` with
ty_utils_oversized_simd_type = monomorphising SIMD type `{$ty}` of length greater than {$max_lanes}
ty_utils_non_primitive_simd_type = monomorphising SIMD type `{$ty}` with a non-primitive-scalar (integer/float/pointer) element type `{$e_ty}`
ty_utils_impl_trait_duplicate_arg = non-defining opaque type use in defining scope
.label = generic argument `{$arg}` used twice
.note = for this opaque type
ty_utils_impl_trait_not_param = non-defining opaque type use in defining scope
.label = argument `{$arg}` is not a generic parameter
.note = for this opaque type

View File

@ -1,7 +1,7 @@
//! Errors emitted by ty_utils
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_middle::ty::Ty;
use rustc_middle::ty::{GenericArg, Ty};
use rustc_span::Span;
#[derive(Diagnostic)]
@ -100,3 +100,25 @@ pub struct NonPrimitiveSimdType<'tcx> {
pub ty: Ty<'tcx>,
pub e_ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(ty_utils_impl_trait_duplicate_arg)]
pub struct DuplicateArg<'tcx> {
pub arg: GenericArg<'tcx>,
#[primary_span]
#[label]
pub span: Span,
#[note]
pub opaque_span: Span,
}
#[derive(Diagnostic)]
#[diag(ty_utils_impl_trait_not_param)]
pub struct NotParam<'tcx> {
pub arg: GenericArg<'tcx>,
#[primary_span]
#[label]
pub span: Span,
#[note]
pub opaque_span: Span,
}

View File

@ -33,6 +33,7 @@
mod layout;
mod layout_sanity_check;
mod needs_drop;
mod opaque_types;
pub mod representability;
mod structural_match;
mod ty;
@ -47,6 +48,7 @@ pub fn provide(providers: &mut Providers) {
implied_bounds::provide(providers);
layout::provide(providers);
needs_drop::provide(providers);
opaque_types::provide(providers);
representability::provide(providers);
ty::provide(providers);
instance::provide(providers);

View File

@ -0,0 +1,197 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::{def::DefKind, def_id::LocalDefId};
use rustc_middle::ty::util::{CheckRegions, NotUniqueParam};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_span::Span;
use rustc_type_ir::AliasKind;
use std::ops::ControlFlow;
use crate::errors::{DuplicateArg, NotParam};
struct OpaqueTypeCollector<'tcx> {
tcx: TyCtxt<'tcx>,
opaques: Vec<LocalDefId>,
/// The `DefId` of the item which we are collecting opaque types for.
item: LocalDefId,
/// Avoid infinite recursion due to recursive declarations.
seen: FxHashSet<LocalDefId>,
}
impl<'tcx> OpaqueTypeCollector<'tcx> {
fn collect(
tcx: TyCtxt<'tcx>,
item: LocalDefId,
val: ty::Binder<'tcx, impl TypeVisitable<TyCtxt<'tcx>>>,
) -> Vec<LocalDefId> {
let mut collector = Self { tcx, opaques: Vec::new(), item, seen: Default::default() };
val.skip_binder().visit_with(&mut collector);
collector.opaques
}
fn span(&self) -> Span {
self.tcx.def_span(self.item)
}
fn parent(&self) -> Option<LocalDefId> {
match self.tcx.def_kind(self.item) {
DefKind::Fn => None,
DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
Some(self.tcx.local_parent(self.item))
}
other => span_bug!(
self.tcx.def_span(self.item),
"unhandled item with opaque types: {other:?}"
),
}
}
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
type BreakTy = ErrorGuaranteed;
#[instrument(skip(self), ret, level = "trace")]
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<ErrorGuaranteed> {
match t.kind() {
ty::Alias(AliasKind::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
if !self.seen.insert(alias_ty.def_id.expect_local()) {
return ControlFlow::Continue(());
}
match self.tcx.uses_unique_generic_params(alias_ty.substs, CheckRegions::Bound) {
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.
self.opaques.push(alias_ty.def_id.expect_local());
// Collect opaque types nested within the associated type bounds of this opaque type.
for (pred, _span) in self
.tcx
.explicit_item_bounds(alias_ty.def_id)
.subst_iter_copied(self.tcx, alias_ty.substs)
{
trace!(?pred);
pred.visit_with(self)?;
}
ControlFlow::Continue(())
}
Err(NotUniqueParam::NotParam(arg)) => {
let err = self.tcx.sess.emit_err(NotParam {
arg,
span: self.span(),
opaque_span: self.tcx.def_span(alias_ty.def_id),
});
ControlFlow::Break(err)
}
Err(NotUniqueParam::DuplicateParam(arg)) => {
let err = self.tcx.sess.emit_err(DuplicateArg {
arg,
span: self.span(),
opaque_span: self.tcx.def_span(alias_ty.def_id),
});
ControlFlow::Break(err)
}
}
}
ty::Alias(AliasKind::Projection, alias_ty) => {
if let Some(parent) = self.parent() {
trace!(?alias_ty);
let (trait_ref, own_substs) = alias_ty.trait_ref_and_own_substs(self.tcx);
trace!(?trait_ref, ?own_substs);
// 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.
if trait_ref.self_ty() == self.tcx.type_of(parent).subst_identity() {
for assoc in self.tcx.associated_items(parent).in_definition_order() {
trace!(?assoc);
if assoc.trait_item_def_id == Some(alias_ty.def_id) {
// We reconstruct the generic args of the associated type within the impl
// from the impl's generics and the generic args passed to the type via the
// projection.
let substs = ty::InternalSubsts::identity_for_item(
self.tcx,
parent.to_def_id(),
);
trace!(?substs);
let substs: Vec<_> =
substs.iter().chain(own_substs.iter().copied()).collect();
trace!(?substs);
// Find opaque types in this associated type.
return self
.tcx
.type_of(assoc.def_id)
.subst(self.tcx, &substs)
.visit_with(self);
}
}
}
}
t.super_visit_with(self)
}
_ => t.super_visit_with(self),
}
}
}
fn opaque_types_defined_by<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx [LocalDefId] {
let kind = tcx.def_kind(item);
trace!(?kind);
// FIXME(type_alias_impl_trait): This is definitely still wrong except for RPIT and impl trait in assoc types.
match kind {
// We're also doing this for `AssocTy` for the wf checks in `check_opaque_meets_bounds`
DefKind::Fn | DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
let defined_opaques = match kind {
DefKind::Fn => {
OpaqueTypeCollector::collect(tcx, item, tcx.fn_sig(item).subst_identity())
}
DefKind::AssocFn => {
OpaqueTypeCollector::collect(tcx, item, tcx.fn_sig(item).subst_identity())
}
DefKind::AssocTy | DefKind::AssocConst => OpaqueTypeCollector::collect(
tcx,
item,
ty::Binder::dummy(tcx.type_of(item).subst_identity()),
),
_ => unreachable!(),
};
tcx.arena.alloc_from_iter(defined_opaques)
}
DefKind::Mod
| DefKind::Struct
| DefKind::Union
| DefKind::Enum
| DefKind::Variant
| DefKind::Trait
| DefKind::TyAlias
| DefKind::ForeignTy
| DefKind::TraitAlias
| DefKind::TyParam
| DefKind::Const
| DefKind::ConstParam
| DefKind::Static(_)
| DefKind::Ctor(_, _)
| DefKind::Macro(_)
| DefKind::ExternCrate
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::OpaqueTy
| DefKind::ImplTraitPlaceholder
| DefKind::Field
| DefKind::LifetimeParam
| DefKind::GlobalAsm
| DefKind::Impl { .. }
| DefKind::Closure
| DefKind::Generator => &[],
}
}
pub(super) fn provide(providers: &mut ty::query::Providers) {
*providers = ty::query::Providers { opaque_types_defined_by, ..*providers };
}

View File

@ -455,7 +455,8 @@ fn visit_item_inner(
| hir::ItemKind::Union(..)
| hir::ItemKind::TyAlias(..)
| hir::ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias, ..
origin: hir::OpaqueTyOrigin::TyAlias { .. },
..
})
| hir::ItemKind::Static(..)
| hir::ItemKind::Trait(..)

View File

@ -19,4 +19,5 @@ impl<'a> A<'a> for C {
type B<'b> = impl Clone;
fn a(&'a self) -> Self::B<'a> {} //~ ERROR: non-defining opaque type use in defining scope
//~^ ERROR: mismatched types
}

View File

@ -1,16 +1,34 @@
error: non-defining opaque type use in defining scope
--> $DIR/issue-88595.rs:21:35
--> $DIR/issue-88595.rs:21:5
|
LL | fn a(&'a self) -> Self::B<'a> {}
| ^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ generic argument `'a` used twice
|
note: lifetime used multiple times
--> $DIR/issue-88595.rs:18:6
note: for this opaque type
--> $DIR/issue-88595.rs:19:18
|
LL | impl<'a> A<'a> for C {
| ^^
LL | type B<'b> = impl Clone;
| ^^
| ^^^^^^^^^^
error: aborting due to previous error
error[E0308]: mismatched types
--> $DIR/issue-88595.rs:21:23
|
LL | type B<'b> = impl Clone;
| ---------- the expected opaque type
LL |
LL | fn a(&'a self) -> Self::B<'a> {}
| - ^^^^^^^^^^^ expected opaque type, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
|
= note: expected opaque type `<C as A<'a>>::B<'a>`
found unit type `()`
note: this item must have the opaque type in its signature in order to be able to register hidden types
--> $DIR/issue-88595.rs:21:5
|
LL | fn a(&'a self) -> Self::B<'a> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -0,0 +1,27 @@
#![feature(impl_trait_in_assoc_type)]
mod compare_ty {
trait Trait {
type Ty: IntoIterator<Item = ()>;
}
impl Trait for () {
type Ty = Option<impl Sized>;
//~^ ERROR: unconstrained opaque type
//~| ERROR: type mismatch resolving `<Option<<() as Trait>::Ty::{opaque#0}> as IntoIterator>::Item == ()`
}
}
mod compare_method {
trait Trait {
type Ty;
fn method() -> Self::Ty;
}
impl Trait for () {
type Ty = impl Sized;
//~^ ERROR: unconstrained opaque type
fn method() -> () {}
//~^ ERROR: method `method` has an incompatible type for trait
}
}
fn main() {}

View File

@ -0,0 +1,59 @@
error[E0271]: type mismatch resolving `<Option<<() as Trait>::Ty::{opaque#0}> as IntoIterator>::Item == ()`
--> $DIR/in-assoc-type-unconstrained.rs:8:19
|
LL | type Ty = Option<impl Sized>;
| ^^^^^^^^^^^^^^^^^^ expected `()`, found opaque type
|
= note: expected unit type `()`
found opaque type `<() as compare_ty::Trait>::Ty::{opaque#0}`
note: required by a bound in `compare_ty::Trait::Ty`
--> $DIR/in-assoc-type-unconstrained.rs:5:31
|
LL | type Ty: IntoIterator<Item = ()>;
| ^^^^^^^^^ required by this bound in `Trait::Ty`
error: unconstrained opaque type
--> $DIR/in-assoc-type-unconstrained.rs:8:26
|
LL | type Ty = Option<impl Sized>;
| ^^^^^^^^^^
|
= note: `Ty` must be used in combination with a concrete type within the same impl
error[E0053]: method `method` has an incompatible type for trait
--> $DIR/in-assoc-type-unconstrained.rs:22:24
|
LL | type Ty = impl Sized;
| ---------- the expected opaque type
LL |
LL | fn method() -> () {}
| ^^
| |
| expected opaque type, found `()`
| help: change the output type to match the trait: `<() as compare_method::Trait>::Ty`
|
note: type in trait
--> $DIR/in-assoc-type-unconstrained.rs:17:24
|
LL | fn method() -> Self::Ty;
| ^^^^^^^^
= note: expected signature `fn() -> <() as compare_method::Trait>::Ty`
found signature `fn()`
note: this item must have the opaque type in its signature in order to be able to register hidden types
--> $DIR/in-assoc-type-unconstrained.rs:22:9
|
LL | fn method() -> () {}
| ^^^^^^^^^^^^^^^^^
error: unconstrained opaque type
--> $DIR/in-assoc-type-unconstrained.rs:20:19
|
LL | type Ty = impl Sized;
| ^^^^^^^^^^
|
= note: `Ty` must be used in combination with a concrete type within the same impl
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0053, E0271.
For more information about an error, try `rustc --explain E0053`.

View File

@ -0,0 +1,21 @@
#![feature(impl_trait_in_assoc_type)]
trait Foo<T> {
type Bar;
fn foo(&self) -> <Self as Foo<()>>::Bar
where
Self: Foo<()>;
}
impl Foo<()> for () {
type Bar = impl std::fmt::Debug;
fn foo(&self) -> Self::Bar {}
}
impl Foo<i32> for () {
type Bar = u32;
fn foo(&self) -> <Self as Foo<()>>::Bar {}
//~^ ERROR: mismatched types
}
fn main() {}

View File

@ -0,0 +1,22 @@
error[E0308]: mismatched types
--> $DIR/in-assoc-type.rs:17:22
|
LL | type Bar = impl std::fmt::Debug;
| -------------------- the expected opaque type
...
LL | fn foo(&self) -> <Self as Foo<()>>::Bar {}
| --- ^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
|
= note: expected opaque type `<() as Foo<()>>::Bar`
found unit type `()`
note: this item must have the opaque type in its signature in order to be able to register hidden types
--> $DIR/in-assoc-type.rs:17:5
|
LL | fn foo(&self) -> <Self as Foo<()>>::Bar {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.

View File

@ -43,6 +43,11 @@ LL | fn eq(&self, _other: &(Bar, i32)) -> bool {
|
= note: expected signature `fn(&b::Bar, &(b::Foo, i32)) -> _`
found signature `fn(&b::Bar, &(b::Bar, i32)) -> _`
note: this item must have the opaque type in its signature in order to be able to register hidden types
--> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:24:9
|
LL | fn eq(&self, _other: &(Bar, i32)) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 4 previous errors

View File

@ -5,15 +5,16 @@
trait Trait {
type Opaque1;
type Opaque2;
fn constrain(self);
fn constrain(self) -> (Self::Opaque1, Self::Opaque2);
}
impl<'a> Trait for &'a () {
type Opaque1 = impl Sized;
type Opaque2 = impl Sized + 'a;
fn constrain(self) {
let _: Self::Opaque1 = ();
let _: Self::Opaque2 = self;
fn constrain(self) -> (Self::Opaque1, Self::Opaque2) {
let a: Self::Opaque1 = ();
let b: Self::Opaque2 = self;
(a, b)
}
}

View File

@ -0,0 +1,16 @@
#![feature(impl_trait_in_assoc_type)]
trait Foo {
type Foo;
fn bar();
}
impl Foo for () {
type Foo = impl std::fmt::Debug;
fn bar() {
let x: Self::Foo = ();
//~^ ERROR: mismatched types
}
}
fn main() {}

View File

@ -0,0 +1,22 @@
error[E0308]: mismatched types
--> $DIR/invalid_impl_trait_in_assoc_ty.rs:11:28
|
LL | type Foo = impl std::fmt::Debug;
| -------------------- the expected opaque type
LL | fn bar() {
LL | let x: Self::Foo = ();
| --------- ^^ expected opaque type, found `()`
| |
| expected due to this
|
= note: expected opaque type `<() as Foo>::Foo`
found unit type `()`
note: this item must have the opaque type in its signature in order to be able to register hidden types
--> $DIR/invalid_impl_trait_in_assoc_ty.rs:10:5
|
LL | fn bar() {
| ^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.