about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2024-01-23 21:53:56 +0100
committerGitHub <noreply@github.com>2024-01-23 21:53:56 +0100
commit5da220a095f9e4ab46b7ea19ddfb7bbc7dd63e70 (patch)
treeb799186627f93b59c1de739b0472e85146ba70a9
parent023c3eb44b2e44e0ab057e815154e31ebe907d91 (diff)
parent5e5d1350b600ad8922a7c4bee44611bea597d082 (diff)
downloadrust-5da220a095f9e4ab46b7ea19ddfb7bbc7dd63e70.tar.gz
rust-5da220a095f9e4ab46b7ea19ddfb7bbc7dd63e70.zip
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).
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs78
-rw-r--r--compiler/rustc_middle/src/query/mod.rs9
-rw-r--r--compiler/rustc_ty_utils/src/opaque_types.rs213
-rw-r--r--tests/ui/impl-trait/rpit/early_bound.rs13
-rw-r--r--tests/ui/impl-trait/rpit/early_bound.stderr26
-rw-r--r--tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs8
-rw-r--r--tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr15
-rw-r--r--tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs38
-rw-r--r--tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr41
-rw-r--r--tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs22
-rw-r--r--tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr17
-rw-r--r--tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs38
-rw-r--r--tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs17
-rw-r--r--tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr13
-rw-r--r--tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs21
-rw-r--r--tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr22
-rw-r--r--tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr23
-rw-r--r--tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs26
-rw-r--r--tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs20
-rw-r--r--tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr22
21 files changed, 615 insertions, 73 deletions
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index bfa9dc42422..3674a760cbf 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -530,9 +530,13 @@ pub(super) fn type_of_opaque(
         Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id) {
             Node::Item(item) => match item.kind {
                 ItemKind::OpaqueTy(OpaqueTy {
-                    origin: hir::OpaqueTyOrigin::TyAlias { .. },
+                    origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false },
                     ..
                 }) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id),
+                ItemKind::OpaqueTy(OpaqueTy {
+                    origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true },
+                    ..
+                }) => opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id),
                 // Opaque types desugared from `impl Trait`.
                 ItemKind::OpaqueTy(&OpaqueTy {
                     origin:
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
index 85093bc12b3..79cb384c5bd 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
@@ -23,6 +23,60 @@ pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed>
     res
 }
 
+/// Checks "defining uses" of opaque `impl Trait` in associated types.
+/// These can only be defined by associated items of the same trait.
+#[instrument(skip(tcx), level = "debug")]
+pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
+    tcx: TyCtxt<'_>,
+    def_id: LocalDefId,
+) -> Ty<'_> {
+    let mut parent_def_id = def_id;
+    while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
+        // Account for `type Alias = impl Trait<Foo = impl Trait>;` (#116031)
+        parent_def_id = tcx.local_parent(parent_def_id);
+    }
+    let impl_def_id = tcx.local_parent(parent_def_id);
+    match tcx.def_kind(impl_def_id) {
+        DefKind::Impl { .. } => {}
+        other => bug!("invalid impl trait in assoc type parent: {other:?}"),
+    }
+
+    let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
+
+    for &assoc_id in tcx.associated_item_def_ids(impl_def_id) {
+        let assoc = tcx.associated_item(assoc_id);
+        match assoc.kind {
+            ty::AssocKind::Const | ty::AssocKind::Fn => {
+                locator.check(assoc_id.expect_local(), ImplTraitSource::AssocTy)
+            }
+            // Associated types don't have bodies, so they can't constrain hidden types
+            ty::AssocKind::Type => {}
+        }
+    }
+
+    if let Some(hidden) = locator.found {
+        // Only check against typeck if we didn't already error
+        if !hidden.ty.references_error() {
+            for concrete_type in locator.typeck_types {
+                if concrete_type.ty != tcx.erase_regions(hidden.ty)
+                    && !(concrete_type, hidden).references_error()
+                {
+                    hidden.report_mismatch(&concrete_type, def_id, tcx).emit();
+                }
+            }
+        }
+
+        hidden.ty
+    } else {
+        let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
+            span: tcx.def_span(def_id),
+            name: tcx.item_name(parent_def_id.to_def_id()),
+            what: "impl",
+        });
+        Ty::new_error(tcx, reported)
+    }
+}
+
 /// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions
 /// laid for "higher-order pattern unification".
 /// This ensures that inference is tractable.
@@ -128,9 +182,15 @@ struct TaitConstraintLocator<'tcx> {
     typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
 }
 
+#[derive(Debug)]
+enum ImplTraitSource {
+    AssocTy,
+    TyAlias,
+}
+
 impl TaitConstraintLocator<'_> {
     #[instrument(skip(self), level = "debug")]
-    fn check(&mut self, item_def_id: LocalDefId) {
+    fn check(&mut self, item_def_id: LocalDefId, source: ImplTraitSource) {
         // Don't try to check items that cannot possibly constrain the type.
         if !self.tcx.has_typeck_results(item_def_id) {
             debug!("no constraint: no typeck results");
@@ -182,7 +242,13 @@ impl TaitConstraintLocator<'_> {
                 continue;
             }
             constrained = true;
-            if !self.tcx.opaque_types_defined_by(item_def_id).contains(&self.def_id) {
+            let opaque_types_defined_by = match source {
+                ImplTraitSource::AssocTy => {
+                    self.tcx.impl_trait_in_assoc_types_defined_by(item_def_id)
+                }
+                ImplTraitSource::TyAlias => self.tcx.opaque_types_defined_by(item_def_id),
+            };
+            if !opaque_types_defined_by.contains(&self.def_id) {
                 self.tcx.dcx().emit_err(TaitForwardCompat {
                     span: hidden_type.span,
                     item_span: self
@@ -240,7 +306,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
     }
     fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
         if let hir::ExprKind::Closure(closure) = ex.kind {
-            self.check(closure.def_id);
+            self.check(closure.def_id, ImplTraitSource::TyAlias);
         }
         intravisit::walk_expr(self, ex);
     }
@@ -248,7 +314,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
         trace!(?it.owner_id);
         // The opaque type itself or its children are not within its reveal scope.
         if it.owner_id.def_id != self.def_id {
-            self.check(it.owner_id.def_id);
+            self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
             intravisit::walk_item(self, it);
         }
     }
@@ -256,13 +322,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
         trace!(?it.owner_id);
         // The opaque type itself or its children are not within its reveal scope.
         if it.owner_id.def_id != self.def_id {
-            self.check(it.owner_id.def_id);
+            self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
             intravisit::walk_impl_item(self, it);
         }
     }
     fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
         trace!(?it.owner_id);
-        self.check(it.owner_id.def_id);
+        self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
         intravisit::walk_trait_item(self, it);
     }
     fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index a9dc7f5d11a..23daefd5a65 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -343,6 +343,15 @@ rustc_queries! {
         }
     }
 
+    query impl_trait_in_assoc_types_defined_by(
+        key: LocalDefId
+    ) -> &'tcx ty::List<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`.
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index 0bb833c66fa..ef67317a601 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -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 };
 }
diff --git a/tests/ui/impl-trait/rpit/early_bound.rs b/tests/ui/impl-trait/rpit/early_bound.rs
new file mode 100644
index 00000000000..03bd64d4d76
--- /dev/null
+++ b/tests/ui/impl-trait/rpit/early_bound.rs
@@ -0,0 +1,13 @@
+use std::convert::identity;
+
+fn test<'a: 'a>(n: bool) -> impl Sized + 'a {
+    //~^ ERROR concrete type differs from previous defining opaque type use
+    let true = n else { loop {} };
+    let _ = || {
+        let _ = identity::<&'a ()>(test(false));
+        //~^ ERROR hidden type for `impl Sized + 'a` captures lifetime that does not appear in bounds
+    };
+    loop {}
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/rpit/early_bound.stderr b/tests/ui/impl-trait/rpit/early_bound.stderr
new file mode 100644
index 00000000000..815368f250e
--- /dev/null
+++ b/tests/ui/impl-trait/rpit/early_bound.stderr
@@ -0,0 +1,26 @@
+error[E0700]: hidden type for `impl Sized + 'a` captures lifetime that does not appear in bounds
+  --> $DIR/early_bound.rs:7:17
+   |
+LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a {
+   |         --                  --------------- opaque type defined here
+   |         |
+   |         hidden type `&'a ()` captures the lifetime `'a` as defined here
+...
+LL |         let _ = identity::<&'a ()>(test(false));
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: concrete type differs from previous defining opaque type use
+  --> $DIR/early_bound.rs:3:29
+   |
+LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a {
+   |                             ^^^^^^^^^^^^^^^ expected `&()`, got `()`
+   |
+note: previous use here
+  --> $DIR/early_bound.rs:7:36
+   |
+LL |         let _ = identity::<&'a ()>(test(false));
+   |                                    ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs
index e440dce5e51..4c881dd1330 100644
--- a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs
+++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs
@@ -1,9 +1,8 @@
-//! This test shows that we can even follow projections
-//! into associated types of the same impl if they are
-//! indirectly mentioned in a struct field.
+//! This test shows that we do not treat opaque types
+//! as defined by a method if the opaque type is
+//! only indirectly mentioned in a struct field.
 
 #![feature(impl_trait_in_assoc_type)]
-// check-pass
 
 struct Bar;
 
@@ -16,6 +15,7 @@ impl Trait for Bar {
     type Assoc = impl std::fmt::Debug;
     fn foo() -> Foo {
         Foo { field: () }
+        //~^ ERROR: item constrains opaque type that is not in its signature
     }
 }
 
diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr
new file mode 100644
index 00000000000..5c53dfa3a75
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr
@@ -0,0 +1,15 @@
+error: item constrains opaque type that is not in its signature
+  --> $DIR/hidden_behind_struct_field2.rs:17:22
+   |
+LL |         Foo { field: () }
+   |                      ^^
+   |
+   = note: this item must mention the opaque type in its signature in order to be able to register hidden types
+note: this item must mention the opaque type in its signature in order to be able to register hidden types
+  --> $DIR/hidden_behind_struct_field2.rs:16:8
+   |
+LL |     fn foo() -> Foo {
+   |        ^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs
new file mode 100644
index 00000000000..a788563ab77
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs
@@ -0,0 +1,38 @@
+//! Check that we cannot instantiate a hidden type in the body
+//! of an assoc fn or const unless mentioned in the signature.
+
+#![feature(impl_trait_in_assoc_type)]
+
+trait Trait: Sized {
+    type Assoc;
+    fn foo();
+    fn bar() -> Self::Assoc;
+}
+
+impl Trait for () {
+    type Assoc = impl std::fmt::Debug;
+    fn foo() {
+        let x: Self::Assoc = 42; //~ ERROR: mismatched types
+    }
+    fn bar() -> Self::Assoc {
+        ""
+    }
+}
+
+trait Trait2: Sized {
+    type Assoc;
+    const FOO: ();
+    fn bar() -> Self::Assoc;
+}
+
+impl Trait2 for () {
+    type Assoc = impl std::fmt::Debug;
+    const FOO: () = {
+        let x: Self::Assoc = 42; //~ ERROR: mismatched types
+    };
+    fn bar() -> Self::Assoc {
+        ""
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr
new file mode 100644
index 00000000000..1d7a97c5367
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr
@@ -0,0 +1,41 @@
+error[E0308]: mismatched types
+  --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:15:30
+   |
+LL |     type Assoc = impl std::fmt::Debug;
+   |                  -------------------- the expected opaque type
+LL |     fn foo() {
+LL |         let x: Self::Assoc = 42;
+   |                -----------   ^^ expected opaque type, found integer
+   |                |
+   |                expected due to this
+   |
+   = note: expected opaque type `<() as Trait>::Assoc`
+                     found type `{integer}`
+note: this item must have the opaque type in its signature in order to be able to register hidden types
+  --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:14:8
+   |
+LL |     fn foo() {
+   |        ^^^
+
+error[E0308]: mismatched types
+  --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:31:30
+   |
+LL |     type Assoc = impl std::fmt::Debug;
+   |                  -------------------- the expected opaque type
+LL |     const FOO: () = {
+LL |         let x: Self::Assoc = 42;
+   |                -----------   ^^ expected opaque type, found integer
+   |                |
+   |                expected due to this
+   |
+   = note: expected opaque type `<() as Trait2>::Assoc`
+                     found type `{integer}`
+note: this item must have the opaque type in its signature in order to be able to register hidden types
+  --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:30:11
+   |
+LL |     const FOO: () = {
+   |           ^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs
new file mode 100644
index 00000000000..77cdca198da
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs
@@ -0,0 +1,22 @@
+//! Check that we cannot instantiate a hidden type from another assoc type.
+
+#![feature(impl_trait_in_assoc_type)]
+
+trait Trait: Sized {
+    type Assoc;
+    type Foo;
+    fn foo() -> Self::Assoc;
+}
+
+impl Trait for () {
+    type Assoc = impl std::fmt::Debug;
+    type Foo = [(); {
+        let x: Self::Assoc = 42; //~ ERROR: mismatched types
+        3
+    }];
+    fn foo() -> Self::Assoc {
+        ""
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr
new file mode 100644
index 00000000000..708c3f28d2d
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr
@@ -0,0 +1,17 @@
+error[E0308]: mismatched types
+  --> $DIR/impl_trait_in_trait_defined_outside_trait2.rs:14:30
+   |
+LL |     type Assoc = impl std::fmt::Debug;
+   |                  -------------------- the expected opaque type
+LL |     type Foo = [(); {
+LL |         let x: Self::Assoc = 42;
+   |                -----------   ^^ expected opaque type, found integer
+   |                |
+   |                expected due to this
+   |
+   = note: expected opaque type `<() as Trait>::Assoc`
+                     found type `{integer}`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs
new file mode 100644
index 00000000000..dfcf7336533
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs
@@ -0,0 +1,38 @@
+//! Check that non-defining assoc items can use the opaque type
+//! opaquely.
+
+// check-pass
+
+#![feature(impl_trait_in_assoc_type)]
+
+trait Trait: Sized {
+    type Assoc;
+    fn foo();
+    fn bar() -> Self::Assoc;
+}
+
+impl Trait for () {
+    type Assoc = impl std::fmt::Debug;
+    fn foo() {
+        let x: Self::Assoc = Self::bar();
+    }
+    fn bar() -> Self::Assoc {
+        ""
+    }
+}
+
+trait Trait2: Sized {
+    type Assoc;
+    const FOO: ();
+    const BAR: Self::Assoc;
+}
+
+impl Trait2 for () {
+    type Assoc = impl Copy;
+    const FOO: () = {
+        let x: Self::Assoc = Self::BAR;
+    };
+    const BAR: Self::Assoc = "";
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs
new file mode 100644
index 00000000000..baeba1d3de6
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs
@@ -0,0 +1,17 @@
+#![feature(impl_trait_in_assoc_type)]
+
+trait Foo {
+    type Assoc<'a, 'b>;
+    fn bar<'a: 'a, 'b: 'b>(_: &'a ()) -> Self::Assoc<'a, 'b>;
+}
+
+impl Foo for () {
+    type Assoc<'a, 'b> = impl Sized;
+    fn bar<'a: 'a, 'b: 'b>(x: &'a ()) -> Self::Assoc<'a, 'b> {
+        let closure = |x: &'a ()| -> Self::Assoc<'b, 'a> { x };
+        //~^ ERROR `<() as Foo>::Assoc<'b, 'a>` captures lifetime that does not appear in bounds
+        x
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr
new file mode 100644
index 00000000000..a7d3e7f0be4
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr
@@ -0,0 +1,13 @@
+error[E0700]: hidden type for `<() as Foo>::Assoc<'b, 'a>` captures lifetime that does not appear in bounds
+  --> $DIR/in-assoc-ty-early-bound.rs:11:60
+   |
+LL |     type Assoc<'a, 'b> = impl Sized;
+   |                          ---------- opaque type defined here
+LL |     fn bar<'a: 'a, 'b: 'b>(x: &'a ()) -> Self::Assoc<'a, 'b> {
+   |            -- hidden type `&'a ()` captures the lifetime `'a` as defined here
+LL |         let closure = |x: &'a ()| -> Self::Assoc<'b, 'a> { x };
+   |                                                            ^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs
new file mode 100644
index 00000000000..7452000b65d
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs
@@ -0,0 +1,21 @@
+#![feature(impl_trait_in_assoc_type)]
+
+trait Foo {
+    type Assoc<'a>;
+    fn bar<'a: 'a>();
+}
+
+impl Foo for () {
+    type Assoc<'a> = impl Sized; //~ ERROR unconstrained opaque type
+    fn bar<'a: 'a>()
+    where
+        Self::Assoc<'a>:,
+    {
+        let _ = |x: &'a ()| {
+            let _: Self::Assoc<'a> = x;
+            //~^ ERROR `<() as Foo>::Assoc<'a>` captures lifetime that does not appear in bound
+        };
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr
new file mode 100644
index 00000000000..1274a8b60de
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr
@@ -0,0 +1,22 @@
+error[E0700]: hidden type for `<() as Foo>::Assoc<'a>` captures lifetime that does not appear in bounds
+  --> $DIR/in-assoc-ty-early-bound2.rs:15:20
+   |
+LL |     type Assoc<'a> = impl Sized;
+   |                      ---------- opaque type defined here
+LL |     fn bar<'a: 'a>()
+   |            -- hidden type `&'a ()` captures the lifetime `'a` as defined here
+...
+LL |             let _: Self::Assoc<'a> = x;
+   |                    ^^^^^^^^^^^^^^^
+
+error: unconstrained opaque type
+  --> $DIR/in-assoc-ty-early-bound2.rs:9:22
+   |
+LL |     type Assoc<'a> = impl Sized;
+   |                      ^^^^^^^^^^
+   |
+   = note: `Assoc` must be used in combination with a concrete type within the same impl
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr
new file mode 100644
index 00000000000..4acc47eaef2
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr
@@ -0,0 +1,23 @@
+error[E0308]: mismatched types
+  --> $DIR/itiat-allow-nested-closures.rs:21:22
+   |
+LL |     type Assoc = impl Sized;
+   |                  ---------- the found opaque type
+...
+LL |         let _: i32 = closure();
+   |                ---   ^^^^^^^^^ expected `i32`, found opaque type
+   |                |
+   |                expected due to this
+
+error[E0308]: mismatched types
+  --> $DIR/itiat-allow-nested-closures.rs:22:9
+   |
+LL |     fn bar() -> Self::Assoc {
+   |                 ----------- expected `()` because of return type
+...
+LL |         1i32
+   |         ^^^^ expected `()`, found `i32`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs
new file mode 100644
index 00000000000..55994d6a325
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs
@@ -0,0 +1,26 @@
+#![feature(impl_trait_in_assoc_type)]
+
+// revisions: ok bad
+// [ok] check-pass
+
+trait Foo {
+    type Assoc;
+    fn bar() -> Self::Assoc;
+}
+
+impl Foo for () {
+    type Assoc = impl Sized;
+    fn bar() -> Self::Assoc {
+        let closure = || -> Self::Assoc {
+            #[cfg(ok)]
+            let x: Self::Assoc = 42_i32;
+            #[cfg(bad)]
+            let x: Self::Assoc = ();
+            x
+        };
+        let _: i32 = closure(); //[bad]~ ERROR mismatched types
+        1i32 //[bad]~ ERROR mismatched types
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs
new file mode 100644
index 00000000000..8c9d780c111
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs
@@ -0,0 +1,20 @@
+#![feature(impl_trait_in_assoc_type)]
+
+trait Foo {
+    type Assoc;
+    fn bar() -> Self::Assoc;
+}
+
+impl Foo for () {
+    type Assoc = impl Sized;
+    fn bar() -> Self::Assoc {
+        fn foo() -> <() as Foo>::Assoc {
+            let x: <() as Foo>::Assoc = 42_i32; //~ ERROR mismatched types
+            x
+        };
+        let _: i32 = foo();
+        1i32
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr
new file mode 100644
index 00000000000..c177201431a
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr
@@ -0,0 +1,22 @@
+error[E0308]: mismatched types
+  --> $DIR/itiat-forbid-nested-items.rs:12:41
+   |
+LL |     type Assoc = impl Sized;
+   |                  ---------- the expected opaque type
+...
+LL |             let x: <() as Foo>::Assoc = 42_i32;
+   |                    ------------------   ^^^^^^ expected opaque type, found `i32`
+   |                    |
+   |                    expected due to this
+   |
+   = note: expected opaque type `<() as Foo>::Assoc`
+                     found type `i32`
+note: this item must have the opaque type in its signature in order to be able to register hidden types
+  --> $DIR/itiat-forbid-nested-items.rs:11:12
+   |
+LL |         fn foo() -> <() as Foo>::Assoc {
+   |            ^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.