Include relation direction in AliasEq predicate

This commit is contained in:
Michael Goulet 2023-03-21 21:50:16 +00:00
parent 439292bc79
commit 5dc3fd7c05
16 changed files with 173 additions and 34 deletions

View file

@ -842,7 +842,7 @@ pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) };
self.register_predicates([ty::Binder::dummy(if self.tcx().trait_solver_next() { self.register_predicates([ty::Binder::dummy(if self.tcx().trait_solver_next() {
ty::PredicateKind::AliasEq(a.into(), b.into()) ty::PredicateKind::AliasEq(a.into(), b.into(), ty::AliasRelationDirection::Equate)
} else { } else {
ty::PredicateKind::ConstEquate(a, b) ty::PredicateKind::ConstEquate(a, b)
})]); })]);
@ -852,13 +852,15 @@ pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
/// ///
/// If they aren't equal then the relation doesn't hold. /// If they aren't equal then the relation doesn't hold.
fn register_type_equate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { fn register_type_equate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) };
self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasEq( self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasEq(
a.into(), a.into(),
b.into(), b.into(),
self.alias_relate_direction(),
))]); ))]);
} }
/// Relation direction emitted for `AliasEq` predicates
fn alias_relate_direction(&self) -> ty::AliasRelationDirection;
} }
fn int_unification_error<'tcx>( fn int_unification_error<'tcx>(

View file

@ -210,4 +210,8 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Equate<'_, '_, 'tcx> {
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
self.fields.register_obligations(obligations); self.fields.register_obligations(obligations);
} }
fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
ty::AliasRelationDirection::Equate
}
} }

View file

@ -155,4 +155,9 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> {
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
self.fields.register_obligations(obligations); self.fields.register_obligations(obligations);
} }
fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
// FIXME(deferred_projection_equality): This isn't right, I think?
ty::AliasRelationDirection::Equate
}
} }

View file

@ -155,4 +155,9 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> {
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
self.fields.register_obligations(obligations) self.fields.register_obligations(obligations)
} }
fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
// FIXME(deferred_projection_equality): This isn't right, I think?
ty::AliasRelationDirection::Equate
}
} }

View file

@ -777,6 +777,16 @@ where
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
self.delegate.register_obligations(obligations); self.delegate.register_obligations(obligations);
} }
fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
match self.ambient_variance {
ty::Variance::Covariant => ty::AliasRelationDirection::Subtype,
ty::Variance::Contravariant => ty::AliasRelationDirection::Supertype,
ty::Variance::Invariant => ty::AliasRelationDirection::Equate,
// FIXME(deferred_projection_equality): Implement this when we trigger it
ty::Variance::Bivariant => unreachable!(),
}
}
} }
/// When we encounter a binder like `for<..> fn(..)`, we actually have /// When we encounter a binder like `for<..> fn(..)`, we actually have

View file

@ -236,4 +236,8 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Sub<'_, '_, 'tcx> {
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
self.fields.register_obligations(obligations); self.fields.register_obligations(obligations);
} }
fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
ty::AliasRelationDirection::Subtype
}
} }

View file

@ -288,7 +288,7 @@ impl FlagComputation {
self.add_ty(ty); self.add_ty(ty);
} }
ty::PredicateKind::Ambiguous => {} ty::PredicateKind::Ambiguous => {}
ty::PredicateKind::AliasEq(t1, t2) => { ty::PredicateKind::AliasEq(t1, t2, _) => {
self.add_term(t1); self.add_term(t1);
self.add_term(t2); self.add_term(t2);
} }

View file

@ -640,7 +640,25 @@ pub enum PredicateKind<'tcx> {
/// This predicate requires two terms to be equal to eachother. /// This predicate requires two terms to be equal to eachother.
/// ///
/// Only used for new solver /// Only used for new solver
AliasEq(Term<'tcx>, Term<'tcx>), AliasEq(Term<'tcx>, Term<'tcx>, AliasRelationDirection),
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, Debug)]
pub enum AliasRelationDirection {
Equate,
Subtype,
Supertype,
}
impl AliasRelationDirection {
pub fn invert(self) -> Self {
match self {
AliasRelationDirection::Equate => AliasRelationDirection::Equate,
AliasRelationDirection::Subtype => AliasRelationDirection::Supertype,
AliasRelationDirection::Supertype => AliasRelationDirection::Subtype,
}
}
} }
/// The crate outlives map is computed during typeck and contains the /// The crate outlives map is computed during typeck and contains the
@ -976,11 +994,11 @@ impl<'tcx> Term<'tcx> {
} }
} }
/// This function returns `None` for `AliasKind::Opaque`. /// This function returns the inner `AliasTy` if this term is a projection.
/// ///
/// FIXME: rename `AliasTy` to `AliasTerm` and make sure we correctly /// FIXME: rename `AliasTy` to `AliasTerm` and make sure we correctly
/// deal with constants. /// deal with constants.
pub fn to_alias_term_no_opaque(&self, tcx: TyCtxt<'tcx>) -> Option<AliasTy<'tcx>> { pub fn to_projection_term(&self, tcx: TyCtxt<'tcx>) -> Option<AliasTy<'tcx>> {
match self.unpack() { match self.unpack() {
TermKind::Ty(ty) => match ty.kind() { TermKind::Ty(ty) => match ty.kind() {
ty::Alias(kind, alias_ty) => match kind { ty::Alias(kind, alias_ty) => match kind {

View file

@ -2847,7 +2847,8 @@ define_print_and_forward_display! {
p!("the type `", print(ty), "` is found in the environment") p!("the type `", print(ty), "` is found in the environment")
} }
ty::PredicateKind::Ambiguous => p!("ambiguous"), ty::PredicateKind::Ambiguous => p!("ambiguous"),
ty::PredicateKind::AliasEq(t1, t2) => p!(print(t1), " == ", print(t2)), // TODO
ty::PredicateKind::AliasEq(t1, t2, _) => p!(print(t1), " == ", print(t2)),
} }
} }

View file

@ -177,7 +177,8 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> {
write!(f, "TypeWellFormedFromEnv({:?})", ty) write!(f, "TypeWellFormedFromEnv({:?})", ty)
} }
ty::PredicateKind::Ambiguous => write!(f, "Ambiguous"), ty::PredicateKind::Ambiguous => write!(f, "Ambiguous"),
ty::PredicateKind::AliasEq(t1, t2) => write!(f, "AliasEq({t1:?}, {t2:?})"), // TODO
ty::PredicateKind::AliasEq(t1, t2, _) => write!(f, "AliasEq({t1:?}, {t2:?})"),
} }
} }
} }
@ -250,6 +251,7 @@ TrivialTypeTraversalAndLiftImpls! {
crate::ty::AssocItem, crate::ty::AssocItem,
crate::ty::AssocKind, crate::ty::AssocKind,
crate::ty::AliasKind, crate::ty::AliasKind,
crate::ty::AliasRelationDirection,
crate::ty::Placeholder<crate::ty::BoundRegionKind>, crate::ty::Placeholder<crate::ty::BoundRegionKind>,
crate::ty::Placeholder<crate::ty::BoundTyKind>, crate::ty::Placeholder<crate::ty::BoundTyKind>,
crate::ty::ClosureKind, crate::ty::ClosureKind,

View file

@ -180,7 +180,7 @@ where
| ty::PredicateKind::ConstEquate(_, _) | ty::PredicateKind::ConstEquate(_, _)
| ty::PredicateKind::TypeWellFormedFromEnv(_) | ty::PredicateKind::TypeWellFormedFromEnv(_)
| ty::PredicateKind::Ambiguous | ty::PredicateKind::Ambiguous
| ty::PredicateKind::AliasEq(_, _) => bug!("unexpected predicate: {:?}", predicate), | ty::PredicateKind::AliasEq(..) => bug!("unexpected predicate: {:?}", predicate),
} }
} }

View file

@ -223,8 +223,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
ty::PredicateKind::TypeWellFormedFromEnv(..) => { ty::PredicateKind::TypeWellFormedFromEnv(..) => {
bug!("TypeWellFormedFromEnv is only used for Chalk") bug!("TypeWellFormedFromEnv is only used for Chalk")
} }
ty::PredicateKind::AliasEq(lhs, rhs) => { ty::PredicateKind::AliasEq(lhs, rhs, direction) => {
self.compute_alias_eq_goal(Goal { param_env, predicate: (lhs, rhs) }) self.compute_alias_eq_goal(Goal { param_env, predicate: (lhs, rhs, direction) })
} }
} }
} else { } else {

View file

@ -73,7 +73,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
MismatchedProjectionTypes { err: TypeError::Mismatch }, MismatchedProjectionTypes { err: TypeError::Mismatch },
) )
} }
ty::PredicateKind::AliasEq(_, _) => { ty::PredicateKind::AliasEq(_, _, _) => {
FulfillmentErrorCode::CodeProjectionError( FulfillmentErrorCode::CodeProjectionError(
MismatchedProjectionTypes { err: TypeError::Mismatch }, MismatchedProjectionTypes { err: TypeError::Mismatch },
) )

View file

@ -158,13 +158,35 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
#[instrument(level = "debug", skip(self), ret)] #[instrument(level = "debug", skip(self), ret)]
fn compute_alias_eq_goal( fn compute_alias_eq_goal(
&mut self, &mut self,
goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>)>, goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
) -> QueryResult<'tcx> { ) -> QueryResult<'tcx> {
let tcx = self.tcx(); let tcx = self.tcx();
let evaluate_normalizes_to = |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other| { let evaluate_normalizes_to = |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other, direction| {
debug!("evaluate_normalizes_to(alias={:?}, other={:?})", alias, other); debug!("evaluate_normalizes_to(alias={:?}, other={:?})", alias, other);
let r = ecx.probe(|ecx| { let result = ecx.probe(|ecx| {
let other = match direction {
// This is purely an optimization.
ty::AliasRelationDirection::Equate => other,
ty::AliasRelationDirection::Subtype | ty::AliasRelationDirection::Supertype => {
let fresh = ecx.next_term_infer_of_kind(other);
let (sub, sup) = if direction == ty::AliasRelationDirection::Subtype {
(fresh, other)
} else {
(other, fresh)
};
ecx.add_goals(
ecx.infcx
.at(&ObligationCause::dummy(), goal.param_env)
.sub(DefineOpaqueTypes::No, sub, sup)?
.into_obligations()
.into_iter()
.map(|o| o.into()),
);
fresh
}
};
ecx.add_goal(goal.with( ecx.add_goal(goal.with(
tcx, tcx,
ty::Binder::dummy(ty::ProjectionPredicate { ty::Binder::dummy(ty::ProjectionPredicate {
@ -174,37 +196,64 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
)); ));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}); });
debug!("evaluate_normalizes_to(..) -> {:?}", r); debug!("evaluate_normalizes_to({alias}, {other}, {direction:?}) -> {result:?}");
r result
}; };
if goal.predicate.0.is_infer() || goal.predicate.1.is_infer() { let (lhs, rhs, direction) = goal.predicate;
if lhs.is_infer() || rhs.is_infer() {
bug!( bug!(
"`AliasEq` goal with an infer var on lhs or rhs which should have been instantiated" "`AliasEq` goal with an infer var on lhs or rhs which should have been instantiated"
); );
} }
match ( match (lhs.to_projection_term(tcx), rhs.to_projection_term(tcx)) {
goal.predicate.0.to_alias_term_no_opaque(tcx),
goal.predicate.1.to_alias_term_no_opaque(tcx),
) {
(None, None) => bug!("`AliasEq` goal without an alias on either lhs or rhs"), (None, None) => bug!("`AliasEq` goal without an alias on either lhs or rhs"),
(Some(alias), None) => evaluate_normalizes_to(self, alias, goal.predicate.1),
(None, Some(alias)) => evaluate_normalizes_to(self, alias, goal.predicate.0), // RHS is not a projection, only way this is true is if LHS normalizes-to RHS
(Some(alias_lhs), None) => evaluate_normalizes_to(self, alias_lhs, rhs, direction),
// LHS is not a projection, only way this is true is if RHS normalizes-to LHS
(None, Some(alias_rhs)) => {
evaluate_normalizes_to(self, alias_rhs, lhs, direction.invert())
}
(Some(alias_lhs), Some(alias_rhs)) => { (Some(alias_lhs), Some(alias_rhs)) => {
debug!("compute_alias_eq_goal: both sides are aliases"); debug!("compute_alias_eq_goal: both sides are aliases");
let mut candidates = Vec::with_capacity(3); let candidates = vec![
// LHS normalizes-to RHS
evaluate_normalizes_to(self, alias_lhs, rhs, direction),
// RHS normalizes-to RHS
evaluate_normalizes_to(self, alias_rhs, lhs, direction.invert()),
// Relate via substs
self.probe(|ecx| {
debug!("compute_alias_eq_goal: alias defids are equal, equating substs");
// Evaluate all 3 potential candidates for the alias' being equal ecx.add_goals(
candidates.push(evaluate_normalizes_to(self, alias_lhs, goal.predicate.1)); match direction {
candidates.push(evaluate_normalizes_to(self, alias_rhs, goal.predicate.0)); ty::AliasRelationDirection::Equate => ecx
candidates.push(self.probe(|ecx| { .infcx
debug!("compute_alias_eq_goal: alias defids are equal, equating substs"); .at(&ObligationCause::dummy(), goal.param_env)
ecx.eq(goal.param_env, alias_lhs, alias_rhs)?; .eq(DefineOpaqueTypes::No, alias_lhs, alias_rhs),
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) ty::AliasRelationDirection::Subtype => ecx
})); .infcx
.at(&ObligationCause::dummy(), goal.param_env)
.sub(DefineOpaqueTypes::No, alias_lhs, alias_rhs),
ty::AliasRelationDirection::Supertype => ecx
.infcx
.at(&ObligationCause::dummy(), goal.param_env)
.sup(DefineOpaqueTypes::No, alias_lhs, alias_rhs),
}?
.into_obligations()
.into_iter()
.map(|o| o.into()),
);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}),
];
debug!(?candidates); debug!(?candidates);
self.try_merge_responses(candidates.into_iter()) self.try_merge_responses(candidates.into_iter())

View file

@ -92,6 +92,11 @@ impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> {
} }
impl<'tcx> ObligationEmittingRelation<'tcx> for CollectAllMismatches<'_, 'tcx> { impl<'tcx> ObligationEmittingRelation<'tcx> for CollectAllMismatches<'_, 'tcx> {
fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
// FIXME(deferred_projection_equality): We really should get rid of this relation.
ty::AliasRelationDirection::Equate
}
fn register_obligations(&mut self, _obligations: PredicateObligations<'tcx>) { fn register_obligations(&mut self, _obligations: PredicateObligations<'tcx>) {
// FIXME(deferred_projection_equality) // FIXME(deferred_projection_equality)
} }

View file

@ -0,0 +1,34 @@
// compile-flags: -Ztrait-solver=next
// check-pass
trait Trait {
type Assoc: Sized;
}
impl Trait for &'static str {
type Assoc = &'static str;
}
// Wrapper is just here to get around stupid `Sized` obligations in mir typeck
struct Wrapper<T: ?Sized>(std::marker::PhantomData<T>);
fn mk<T: Trait>(x: T) -> Wrapper<<T as Trait>::Assoc> { todo!() }
trait IsStaticStr {}
impl IsStaticStr for (&'static str,) {}
fn define<T: IsStaticStr>(_: T) {}
fn foo<'a, T: Trait>() {
let y = Default::default();
// `<?0 as Trait>::Assoc <: &'a str`
// In the old solver, this would *equate* the LHS and RHS.
let _: Wrapper<&'a str> = mk(y);
// ... then later on, we constrain `?0 = &'static str`
// but that should not mean that `'a = 'static`, because
// we should use *sub* above.
define((y,));
}
fn main() {}