Normalize consistently for specializations

This commit is contained in:
Matthew Jasper 2023-05-05 15:50:17 +01:00
parent bd928a0b5e
commit fafe9e71d5
4 changed files with 104 additions and 17 deletions

View File

@ -99,14 +99,13 @@ pub fn translate_substs<'tcx>(
return source_substs; return source_substs;
} }
fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else( fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl)
|()| { .unwrap_or_else(|()| {
bug!( bug!(
"When translating substitutions from {source_impl:?} to {target_impl:?}, \ "When translating substitutions from {source_impl:?} to {target_impl:?}, \
the expected specialization failed to hold" the expected specialization failed to hold"
) )
}, })
)
} }
specialization_graph::Node::Trait(..) => source_trait_ref.substs, specialization_graph::Node::Trait(..) => source_trait_ref.substs,
}; };
@ -153,20 +152,9 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
// Create an infcx, taking the predicates of impl1 as assumptions: // Create an infcx, taking the predicates of impl1 as assumptions:
let infcx = tcx.infer_ctxt().build(); let infcx = tcx.infer_ctxt().build();
let impl1_trait_ref =
match traits::fully_normalize(&infcx, ObligationCause::dummy(), penv, impl1_trait_ref) {
Ok(impl1_trait_ref) => impl1_trait_ref,
Err(_errors) => {
tcx.sess.delay_span_bug(
tcx.def_span(impl1_def_id),
format!("failed to fully normalize {impl1_trait_ref}"),
);
impl1_trait_ref
}
};
// Attempt to prove that impl2 applies, given all of the above. // Attempt to prove that impl2 applies, given all of the above.
fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() fulfill_implication(&infcx, penv, impl1_trait_ref, impl1_def_id, impl2_def_id).is_ok()
} }
/// Attempt to fulfill all obligations of `target_impl` after unification with /// Attempt to fulfill all obligations of `target_impl` after unification with
@ -178,6 +166,7 @@ fn fulfill_implication<'tcx>(
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
source_trait_ref: ty::TraitRef<'tcx>, source_trait_ref: ty::TraitRef<'tcx>,
source_impl: DefId,
target_impl: DefId, target_impl: DefId,
) -> Result<SubstsRef<'tcx>, ()> { ) -> Result<SubstsRef<'tcx>, ()> {
debug!( debug!(
@ -185,6 +174,22 @@ fn fulfill_implication<'tcx>(
param_env, source_trait_ref, target_impl param_env, source_trait_ref, target_impl
); );
let source_trait_ref = match traits::fully_normalize(
&infcx,
ObligationCause::dummy(),
param_env,
source_trait_ref,
) {
Ok(source_trait_ref) => source_trait_ref,
Err(_errors) => {
infcx.tcx.sess.delay_span_bug(
infcx.tcx.def_span(source_impl),
format!("failed to fully normalize {source_trait_ref}"),
);
source_trait_ref
}
};
let source_trait = ImplSubject::Trait(source_trait_ref); let source_trait = ImplSubject::Trait(source_trait_ref);
let selcx = &mut SelectionContext::new(&infcx); let selcx = &mut SelectionContext::new(&infcx);
@ -194,7 +199,7 @@ fn fulfill_implication<'tcx>(
// do the impls unify? If not, no specialization. // do the impls unify? If not, no specialization.
let Ok(InferOk { obligations: more_obligations, .. }) = let Ok(InferOk { obligations: more_obligations, .. }) =
infcx.at(&ObligationCause::dummy(), param_env, ).eq(DefineOpaqueTypes::No,source_trait, target_trait) infcx.at(&ObligationCause::dummy(), param_env).eq(DefineOpaqueTypes::No, source_trait, target_trait)
else { else {
debug!( debug!(
"fulfill_implication: {:?} does not unify with {:?}", "fulfill_implication: {:?} does not unify with {:?}",

View File

@ -0,0 +1,37 @@
// Another regression test for #109815.
// check-pass
#![feature(min_specialization)]
#![feature(rustc_attrs)]
#[rustc_specialization_trait]
trait X {}
trait Z {
type Assoc: X;
}
struct A<T>(T);
impl X for () {}
impl<T: X> Z for A<T> {
type Assoc = ();
}
trait MyFrom<T> {
fn from(other: T) -> Self;
}
impl<T> MyFrom<()> for T {
default fn from(other: ()) -> T {
panic!();
}
}
impl<T: X> MyFrom<<A<T> as Z>::Assoc> for T {
fn from(other: ()) -> T {
panic!();
}
}
fn main() {}

View File

@ -0,0 +1,33 @@
// A regression test for #109815.
#![feature(min_specialization)]
#![feature(rustc_attrs)]
#[rustc_specialization_trait]
trait X {}
trait Y: X {}
trait Z {
type Assoc: Y;
}
struct A<T>(T);
impl<T: X> Z for A<T> {}
//~^ ERROR not all trait items implemented
trait MyFrom<T> {
fn from(other: T) -> Self;
}
impl<T> MyFrom<T> for T {
default fn from(other: T) -> T {
other
}
}
impl<T: X> MyFrom<<A<T> as Z>::Assoc> for T {
fn from(other: <A<T> as Z>::Assoc) -> T {
other
}
}
fn main() {}

View File

@ -0,0 +1,12 @@
error[E0046]: not all trait items implemented, missing: `Assoc`
--> $DIR/specialize_on_type_error.rs:14:1
|
LL | type Assoc: Y;
| ------------- `Assoc` from trait
...
LL | impl<T: X> Z for A<T> {}
| ^^^^^^^^^^^^^^^^^^^^^ missing `Assoc` in implementation
error: aborting due to previous error
For more information about this error, try `rustc --explain E0046`.