Skip to content

Commit

Permalink
Normalize trait ref before orphan check
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Oct 25, 2023
1 parent 1bc0463 commit 43e93d6
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 5 deletions.
51 changes: 48 additions & 3 deletions compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)]
Expand All @@ -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);
Expand All @@ -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 = <dyn TraitEngine<'_>>::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:
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/associated-types/issue-38821.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub trait Column: Expression {}

#[derive(Debug, Copy, Clone)]
//~^ ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
pub enum ColumnInsertValue<Col, Expr> where
Col: Column,
Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>,
Expand Down
40 changes: 39 additions & 1 deletion tests/ui/associated-types/issue-38821.stderr
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
--> $DIR/issue-38821.rs:23:10
|
LL | #[derive(Debug, Copy, Clone)]
| ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType`
|
note: required for `<Col as Expression>::SqlType` to implement `IntoNullable`
--> $DIR/issue-38821.rs:9:18
|
LL | impl<T: NotNull> 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<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
| +++++++++++++++++++++++++++++++++++++++

error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
--> $DIR/issue-38821.rs:23:17
|
Expand All @@ -17,6 +36,25 @@ help: consider further restricting the associated type
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
| +++++++++++++++++++++++++++++++++++++++

error: aborting due to previous error
error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied
--> $DIR/issue-38821.rs:23:23
|
LL | #[derive(Debug, Copy, Clone)]
| ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType`
|
note: required for `<Col as Expression>::SqlType` to implement `IntoNullable`
--> $DIR/issue-38821.rs:9:18
|
LL | impl<T: NotNull> 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<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
| +++++++++++++++++++++++++++++++++++++++

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
1 change: 1 addition & 0 deletions tests/ui/coherence/auxiliary/parametrized-trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub trait Trait<T, U, V> {}
Original file line number Diff line number Diff line change
@@ -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<T> foreign::Trait<Local, T, ()> for <T as Identity>::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<T1, ..., Tn> 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<T> foreign::Trait<<T as Identity>::Output, Local, T> for Option<T> {}
| ^ 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<T1, ..., Tn> 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`.
21 changes: 21 additions & 0 deletions tests/ui/coherence/orphan-check-projections-dont-cover.next.stderr
Original file line number Diff line number Diff line change
@@ -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<T> foreign::Trait<Local, T, ()> for <T as Identity>::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<T1, ..., Tn> 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<T> foreign::Trait<<T as Identity>::Output, Local, T> for Option<T> {}
| ^ 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<T1, ..., Tn> 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`.
26 changes: 26 additions & 0 deletions tests/ui/coherence/orphan-check-projections-dont-cover.rs
Original file line number Diff line number Diff line change
@@ -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<T> Identity for T {
type Output = T;
}

struct Local;

impl<T> foreign::Trait<Local, T, ()> for <T as Identity>::Output {}
//~^ ERROR type parameter `T` must be covered by another type

impl<T> foreign::Trait<<T as Identity>::Output, Local, T> for Option<T> {}
//~^ ERROR type parameter `T` must be covered by another type

fn main() {}
Original file line number Diff line number Diff line change
@@ -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<T> foreign::Trait<Local, T, ()> for Identity<T> {}
| ^ 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<T1, ..., Tn> 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`.
Original file line number Diff line number Diff line change
@@ -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<T> foreign::Trait<Local, T, ()> for Identity<T> {}
| ^ 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<T1, ..., Tn> 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`.
19 changes: 19 additions & 0 deletions tests/ui/coherence/orphan-check-weak-aliases-dont-cover.rs
Original file line number Diff line number Diff line change
@@ -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> = T;

struct Local;

impl<T> foreign::Trait<Local, T, ()> for Identity<T> {}
//~^ ERROR type parameter `T` must be covered by another type

fn main() {}
2 changes: 1 addition & 1 deletion tests/ui/type-alias-impl-trait/coherence.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 43e93d6

Please sign in to comment.