From 43e93d6e615c7a30a1d953d6fbedcf64e5c4b71f 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 --- .../src/traits/coherence.rs | 51 +++++++++++++++++-- tests/ui/associated-types/issue-38821.rs | 2 + tests/ui/associated-types/issue-38821.stderr | 40 ++++++++++++++- .../coherence/auxiliary/parametrized-trait.rs | 1 + ...heck-projections-dont-cover.classic.stderr | 21 ++++++++ ...n-check-projections-dont-cover.next.stderr | 21 ++++++++ .../orphan-check-projections-dont-cover.rs | 26 ++++++++++ ...eck-weak-aliases-dont-cover.classic.stderr | 12 +++++ ...-check-weak-aliases-dont-cover.next.stderr | 12 +++++ .../orphan-check-weak-aliases-dont-cover.rs | 19 +++++++ .../ui/type-alias-impl-trait/coherence.stderr | 2 +- 11 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 tests/ui/coherence/auxiliary/parametrized-trait.rs create mode 100644 tests/ui/coherence/orphan-check-projections-dont-cover.classic.stderr create mode 100644 tests/ui/coherence/orphan-check-projections-dont-cover.next.stderr create mode 100644 tests/ui/coherence/orphan-check-projections-dont-cover.rs create mode 100644 tests/ui/coherence/orphan-check-weak-aliases-dont-cover.classic.stderr create mode 100644 tests/ui/coherence/orphan-check-weak-aliases-dont-cover.next.stderr create mode 100644 tests/ui/coherence/orphan-check-weak-aliases-dont-cover.rs diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index acab4498a0985..630690194386c 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -22,7 +22,7 @@ use crate::traits::{ }; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; -use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, TraitEngine}; use rustc_middle::traits::query::NoSolution; @@ -547,7 +547,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)] @@ -564,7 +564,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); @@ -575,9 +575,54 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe return Ok(()); } + let trait_ref = normalize_trait_ref(tcx, trait_ref, impl_def_id); + orphan_check_trait_ref::(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap() } +fn normalize_trait_ref<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + impl_def_id: DefId, +) -> ty::TraitRef<'tcx> { + 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().build(); + let cause = ObligationCause::dummy(); + let param_env = tcx.param_env(impl_def_id); + + if infcx.next_trait_solver() { + let mut fulfill_cx = >::new(&infcx); + let args = trait_ref.args.iter().map(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Alias(..) = ty.kind() + { + match infcx.at(&cause, param_env).structurally_normalize(ty, &mut *fulfill_cx) { + Ok(ty) => ty, + _ => Ty::new_error(tcx, delay_bug()), + }.into() + } else { + arg + } + }); + ty::TraitRef::new(tcx, trait_ref.def_id, args) + } else { + let ocx = ObligationCtxt::new(&infcx); + let trait_ref = ocx.normalize(&cause, param_env, trait_ref); + if !ocx.select_all_or_error().is_empty() { + delay_bug(); + } + trait_ref + } +} + /// Checks whether a trait-ref is potentially implementable by a crate. /// /// The current rule is that a trait-ref orphan checks in a crate C: 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..390f8a9f9d39d --- /dev/null +++ b/tests/ui/coherence/auxiliary/parametrized-trait.rs @@ -0,0 +1 @@ +pub trait Trait {} diff --git a/tests/ui/coherence/orphan-check-projections-dont-cover.classic.stderr b/tests/ui/coherence/orphan-check-projections-dont-cover.classic.stderr new file mode 100644 index 0000000000000..0cc7555e9c5b0 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-dont-cover.classic.stderr @@ -0,0 +1,21 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projections-dont-cover.rs:20:6 + | +LL | impl foreign::Trait 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-projections-dont-cover.rs:23:6 + | +LL | impl foreign::Trait<::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: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-projections-dont-cover.next.stderr b/tests/ui/coherence/orphan-check-projections-dont-cover.next.stderr new file mode 100644 index 0000000000000..0cc7555e9c5b0 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-dont-cover.next.stderr @@ -0,0 +1,21 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-projections-dont-cover.rs:20:6 + | +LL | impl foreign::Trait 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-projections-dont-cover.rs:23:6 + | +LL | impl foreign::Trait<::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: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-projections-dont-cover.rs b/tests/ui/coherence/orphan-check-projections-dont-cover.rs new file mode 100644 index 0000000000000..c57104cbd9485 --- /dev/null +++ b/tests/ui/coherence/orphan-check-projections-dont-cover.rs @@ -0,0 +1,26 @@ +// Regression test for issue #99554. +// Don't consider projections to cover type parameters. + +// revisions: classic next +//[next] -Ztrait-solver=next + +// aux-crate:foreign=parametrized-trait.rs +// edition:2021 + +trait Identity { + type Output; +} + +impl Identity for T { + type Output = T; +} + +struct Local; + +impl foreign::Trait for ::Output {} +//~^ ERROR type parameter `T` must be covered by another type + +impl foreign::Trait<::Output, Local, T> for Option {} +//~^ ERROR type parameter `T` must be covered by another type + +fn main() {} diff --git a/tests/ui/coherence/orphan-check-weak-aliases-dont-cover.classic.stderr b/tests/ui/coherence/orphan-check-weak-aliases-dont-cover.classic.stderr new file mode 100644 index 0000000000000..011c57a47a56d --- /dev/null +++ b/tests/ui/coherence/orphan-check-weak-aliases-dont-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-aliases-dont-cover.rs:16:6 + | +LL | impl foreign::Trait 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-aliases-dont-cover.next.stderr b/tests/ui/coherence/orphan-check-weak-aliases-dont-cover.next.stderr new file mode 100644 index 0000000000000..011c57a47a56d --- /dev/null +++ b/tests/ui/coherence/orphan-check-weak-aliases-dont-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-aliases-dont-cover.rs:16:6 + | +LL | impl foreign::Trait 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-aliases-dont-cover.rs b/tests/ui/coherence/orphan-check-weak-aliases-dont-cover.rs new file mode 100644 index 0000000000000..1aadd05efdd51 --- /dev/null +++ b/tests/ui/coherence/orphan-check-weak-aliases-dont-cover.rs @@ -0,0 +1,19 @@ +// Don't consider weak aliases to cover type parameters. + +// revisions: classic next +//[next] -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::Trait 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