From 878f995405e8fd3c7f2ab80c4cc31fc02c7a4731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 15 Oct 2023 13:40:17 +0200 Subject: [PATCH] Normalize trait ref before orphan check --- compiler/rustc_hir_analysis/messages.ftl | 8 +-- .../src/coherence/orphan.rs | 47 +++++++++++---- compiler/rustc_hir_analysis/src/errors.rs | 6 +- .../src/traits/coherence.rs | 59 +++++++++++++++---- tests/ui/associated-types/issue-38821.rs | 2 + tests/ui/associated-types/issue-38821.stderr | 40 ++++++++++++- .../coherence/auxiliary/parametrized-trait.rs | 2 + .../orphan-check-projection-does-cover.rs | 24 ++++++++ ...eck-projection-doesnt-cover.classic.stderr | 30 ++++++++++ .../orphan-check-projection-doesnt-cover.rs | 41 +++++++++++++ ...eck-weak-alias-doesnt-cover.classic.stderr | 12 ++++ ...-check-weak-alias-doesnt-cover.next.stderr | 12 ++++ .../orphan-check-weak-alias-doesnt-cover.rs | 19 ++++++ .../ui/type-alias-impl-trait/coherence.stderr | 2 +- 14 files changed, 275 insertions(+), 29 deletions(-) create mode 100644 tests/ui/coherence/auxiliary/parametrized-trait.rs create mode 100644 tests/ui/coherence/orphan-check-projection-does-cover.rs create mode 100644 tests/ui/coherence/orphan-check-projection-doesnt-cover.classic.stderr create mode 100644 tests/ui/coherence/orphan-check-projection-doesnt-cover.rs create mode 100644 tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.classic.stderr create mode 100644 tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.next.stderr create mode 100644 tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.rs diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index e8d9918be22d2..cd32767f0e789 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -370,13 +370,13 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de .label = needs at most one field with non-trivial size or alignment, but has {$field_count} .labels = this field has non-zero size or requires alignment -hir_analysis_ty_param_first_local = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) - .label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) +hir_analysis_ty_param_first_local = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`) + .label = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`) .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type .case_note = in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last -hir_analysis_ty_param_some = type parameter `{$param_ty}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param_ty}>`) - .label = type parameter `{$param_ty}` must be used as the type parameter for some local type +hir_analysis_ty_param_some = type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`) + .label = type parameter `{$param}` must be used as the type parameter for some local type .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local .only_note = only traits defined in the current crate can be implemented for a type parameter diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 7eeb7837467fd..a410afbff47cb 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -1,7 +1,7 @@ //! Orphan checker: every impl either implements a trait defined in this //! crate or pertains to a type defined in this crate. -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{DelayDm, ErrorGuaranteed}; use rustc_hir as hir; use rustc_middle::ty::util::CheckRegions; @@ -12,7 +12,7 @@ use rustc_middle::ty::{ }; use rustc_session::lint; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use rustc_trait_selection::traits; use std::ops::ControlFlow; @@ -424,22 +424,49 @@ fn emit_orphan_check_error<'tcx>( }; tcx.sess.emit_err(err_struct) } - traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => { - let mut sp = sp; - for param in generics.params { - if param.name.ident().to_string() == param_ty.to_string() { - sp = param.span; + traits::OrphanCheckErr::UncoveredTy(uncovered_ty, local_type) => { + struct TyParamFinder { + ty_params: FxHashMap, + } + + // FIXME: This only reports the first uncovered type parameter it finds when in fact + // there could be multiple. E.g., in ` as Trait>::Assoc` for ``. + impl<'tcx> TypeVisitor> for TyParamFinder { + type BreakTy = (Symbol, Span); + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + // FIXME: This doesn't respect macro hygiene. + if let ty::Param(param_ty) = ty.kind() + && let Some(&span) = self.ty_params.get(¶m_ty.name) + { + return ControlFlow::Break((param_ty.name, span)); + } + + ty.super_visit_with(self) } } + let mut visitor = TyParamFinder { + ty_params: generics + .params + .iter() + .filter(|param| matches!(param.kind, hir::GenericParamKind::Type { .. })) + .map(|param| (param.name.ident().name, param.span)) + .collect(), + }; + + let ControlFlow::Break((param, span)) = uncovered_ty.visit_with(&mut visitor) else { + bug!("failed to find ty param in {uncovered_ty}"); + }; + match local_type { Some(local_type) => tcx.sess.emit_err(errors::TyParamFirstLocal { - span: sp, + span, note: (), - param_ty, + param, local_type, }), - None => tcx.sess.emit_err(errors::TyParamSome { span: sp, note: (), param_ty }), + None => tcx.sess.emit_err(errors::TyParamSome { span, note: (), param }), } } }) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6a2db1d06276c..9465087655a41 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1198,20 +1198,20 @@ pub struct TyParamFirstLocal<'a> { pub span: Span, #[note(hir_analysis_case_note)] pub note: (), - pub param_ty: Ty<'a>, + pub param: Symbol, pub local_type: Ty<'a>, } #[derive(Diagnostic)] #[diag(hir_analysis_ty_param_some, code = "E0210")] #[note] -pub struct TyParamSome<'a> { +pub struct TyParamSome { #[primary_span] #[label] pub span: Span, #[note(hir_analysis_only_note)] pub note: (), - pub param_ty: Ty<'a>, + pub param: Symbol, } #[derive(Diagnostic)] diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index dcf5fd869290f..c6a65cc278e8c 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -20,7 +20,7 @@ use crate::traits::{ }; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::DefId; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, TraitEngine}; use rustc_middle::traits::query::NoSolution; @@ -620,7 +620,7 @@ pub fn trait_ref_is_local_or_fundamental<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, ) -> bool { - trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental) + trait_ref.def_id.is_local() || tcx.has_attr(trait_ref.def_id, sym::fundamental) } #[derive(Debug)] @@ -637,7 +637,7 @@ pub enum OrphanCheckErr<'tcx> { /// 2. Some local type must appear in `Self`. #[instrument(level = "debug", skip(tcx), ret)] pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> { - // We only except this routine to be invoked on implementations + // We only accept this routine to be invoked on implementations // of a trait, not inherent implementations. let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); debug!(?trait_ref); @@ -648,7 +648,42 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe return Ok(()); } - orphan_check_trait_ref::(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap() + let delay_bug = || { + tcx.sess.delay_span_bug( + tcx.def_span(impl_def_id), + format!( + "orphan check: failed to normalize `{trait_ref}` while checking {impl_def_id:?}" + ), + ) + }; + + let infcx = tcx.infer_ctxt().intercrate(true).build(); + let cause = ObligationCause::dummy(); + let param_env = tcx.param_env(impl_def_id); + + let ocx = ObligationCtxt::new(&infcx); + let trait_ref = ocx.normalize(&cause, param_env, trait_ref); + let trait_ref = infcx.resolve_vars_if_possible(trait_ref); + if !ocx.select_where_possible().is_empty() { + delay_bug(); + } + + orphan_check_trait_ref::(trait_ref, InCrate::Local, |ty| { + Ok(if infcx.next_trait_solver() && let ty::Alias(..) = ty.kind() { + let mut fulfill_cx = >::new(&infcx); + // FIXME(-Ztrait-solver=next): This normalizes unnormalizable projections to + // infer vars (which can't be resolved here yet) (in intercrate mode) and it + // instantiates ty params to infer vars. This prevents us from syntactically + // checking if the result is a projection that contains ty params. + match infcx.at(&cause, param_env).structurally_normalize(ty, &mut *fulfill_cx) { + Ok(ty) => ty, + _ => Ty::new_error(tcx, delay_bug()), + } + } else { + ty + }) + }) + .unwrap() } /// Checks whether a trait-ref is potentially implementable by a crate. @@ -754,7 +789,7 @@ fn orphan_check_trait_ref<'tcx, E: Debug>( Ok(match trait_ref.visit_with(&mut checker) { ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err), - ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => { + ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTy(ty)) => { // Does there exist some local type after the `ParamTy`. checker.search_first_local_ty = true; if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) = @@ -798,11 +833,11 @@ where ControlFlow::Continue(()) } - fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow> { + fn found_uncovered_ty(&mut self, t: Ty<'tcx>) -> ControlFlow> { if self.search_first_local_ty { ControlFlow::Continue(()) } else { - ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t)) + ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTy(t)) } } @@ -816,7 +851,7 @@ where enum OrphanCheckEarlyExit<'tcx, E> { NormalizationFailure(E), - ParamTy(Ty<'tcx>), + UncoveredTy(Ty<'tcx>), LocalTy(Ty<'tcx>), } @@ -851,10 +886,14 @@ where | ty::Never | ty::Tuple(..) | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => { - self.found_non_local_ty(ty) + if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) { + self.found_uncovered_ty(ty) + } else { + ControlFlow::Continue(()) + } } - ty::Param(..) => self.found_param_ty(ty), + ty::Param(..) => self.found_uncovered_ty(ty), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate { InCrate::Local => self.found_non_local_ty(ty), diff --git a/tests/ui/associated-types/issue-38821.rs b/tests/ui/associated-types/issue-38821.rs index 6753860e9ff80..d9537afdf1743 100644 --- a/tests/ui/associated-types/issue-38821.rs +++ b/tests/ui/associated-types/issue-38821.rs @@ -22,6 +22,8 @@ pub trait Column: Expression {} #[derive(Debug, Copy, Clone)] //~^ ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied pub enum ColumnInsertValue where Col: Column, Expr: Expression::Nullable>, diff --git a/tests/ui/associated-types/issue-38821.stderr b/tests/ui/associated-types/issue-38821.stderr index a52a9c138f147..e06a3dd33473f 100644 --- a/tests/ui/associated-types/issue-38821.stderr +++ b/tests/ui/associated-types/issue-38821.stderr @@ -1,3 +1,22 @@ +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression::Nullable>, ::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:17 | @@ -17,6 +36,25 @@ help: consider further restricting the associated type LL | Expr: Expression::Nullable>, ::SqlType: NotNull, | +++++++++++++++++++++++++++++++++++++++ -error: aborting due to previous error +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression::Nullable>, ::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coherence/auxiliary/parametrized-trait.rs b/tests/ui/coherence/auxiliary/parametrized-trait.rs new file mode 100644 index 0000000000000..88a3d5cd52dc0 --- /dev/null +++ b/tests/ui/coherence/auxiliary/parametrized-trait.rs @@ -0,0 +1,2 @@ +pub trait Trait0 {} +pub trait Trait1 {} diff --git a/tests/ui/coherence/orphan-check-projection-does-cover.rs b/tests/ui/coherence/orphan-check-projection-does-cover.rs new file mode 100644 index 0000000000000..7970062ea52bf --- /dev/null +++ b/tests/ui/coherence/orphan-check-projection-does-cover.rs @@ -0,0 +1,24 @@ +// Projections can cover type parameters if they normalize to a (local) type that covers them. +// This ensures that we don't perform an overly strict check on +// projections like in closed PR #100555 which did a syntactic +// check for type parameters in projections without normalizing +// first which would've lead to real-word regressions. + +// check-pass +// revisions: classic next +//[next] compile-flags: -Ztrait-solver=next + +// aux-crate:foreign=parametrized-trait.rs +// edition:2021 + +trait Project { type Output; } + +impl Project for T { + type Output = Local; +} + +struct Local; + +impl foreign::Trait1 for ::Output {} + +fn main() {} diff --git a/tests/ui/coherence/orphan-check-projection-doesnt-cover.classic.stderr b/tests/ui/coherence/orphan-check-projection-doesnt-cover.classic.stderr new file mode 100644 index 0000000000000..e7798b21be0f6 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projection-doesnt-cover.classic.stderr @@ -0,0 +1,30 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projection-doesnt-cover.rs:25:6 + | +LL | impl foreign::Trait0 for ::Output {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projection-doesnt-cover.rs:28:6 + | +LL | impl foreign::Trait0<::Output, Local, T> for Option {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projection-doesnt-cover.rs:40:6 + | +LL | impl foreign::Trait1 for ::Output {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-projection-doesnt-cover.rs b/tests/ui/coherence/orphan-check-projection-doesnt-cover.rs new file mode 100644 index 0000000000000..f036eb7115603 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projection-doesnt-cover.rs @@ -0,0 +1,41 @@ +// Regression test for issue #99554. +// Projections might not cover type parameters. + +// revisions: classic next +//[next] compile-flags: -Ztrait-solver=next + +// FIXME(-Ztrait-solver=next): This currently passes in the next solver but it shouldn't. +//[next] check-pass +//[next] known-bug: unknown + +// compile-flags: --crate-type=lib +// aux-crate:foreign=parametrized-trait.rs +// edition:2021 + +trait Identity { + type Output; +} + +impl Identity for T { + type Output = T; +} + +struct Local; + +impl foreign::Trait0 for ::Output {} +//[classic]~^ ERROR type parameter `T` must be covered by another type + +impl foreign::Trait0<::Output, Local, T> for Option {} +//[classic]~^ ERROR type parameter `T` must be covered by another type + +pub trait Deferred { + type Output; +} + +// A downstream user could implement +// +// impl Deferred for Type { type Output = T; } +// struct Type(T); +// +impl foreign::Trait1 for ::Output {} +//[classic]~^ ERROR type parameter `T` must be covered by another type diff --git a/tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.classic.stderr b/tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.classic.stderr new file mode 100644 index 0000000000000..49d9dfe662957 --- /dev/null +++ b/tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.classic.stderr @@ -0,0 +1,12 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-weak-alias-doesnt-cover.rs:16:6 + | +LL | impl foreign::Trait1 for Identity {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.next.stderr b/tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.next.stderr new file mode 100644 index 0000000000000..49d9dfe662957 --- /dev/null +++ b/tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.next.stderr @@ -0,0 +1,12 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-weak-alias-doesnt-cover.rs:16:6 + | +LL | impl foreign::Trait1 for Identity {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.rs b/tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.rs new file mode 100644 index 0000000000000..02f3aeaad6859 --- /dev/null +++ b/tests/ui/coherence/orphan-check-weak-alias-doesnt-cover.rs @@ -0,0 +1,19 @@ +// Weak aliases might not cover type parameters. + +// revisions: classic next +//[next] compile-flags: -Ztrait-solver=next + +// aux-crate:foreign=parametrized-trait.rs +// edition:2021 + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Identity = T; + +struct Local; + +impl foreign::Trait1 for Identity {} +//~^ ERROR type parameter `T` must be covered by another type + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/coherence.stderr b/tests/ui/type-alias-impl-trait/coherence.stderr index 36bbb985ef028..386e6f79f946f 100644 --- a/tests/ui/type-alias-impl-trait/coherence.stderr +++ b/tests/ui/type-alias-impl-trait/coherence.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------- | | | - | | `AliasOfForeignType<()>` is not defined in the current crate + | | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead