1
Fork 0

always emit AliasRelate goals when relating aliases

Add `StructurallyRelateAliases` to allow instantiating infer vars with rigid aliases.
Change `instantiate_query_response` to be infallible in the new solver. This requires canonicalization to not hide any information used by the query, so weaken
universe compression. It also modifies `term_is_fully_unconstrained` to allow
region inference variables in a higher universe.
This commit is contained in:
lcnr 2024-02-26 10:17:43 +01:00
parent eeeb9b4d31
commit 1b3164f5c9
21 changed files with 417 additions and 272 deletions

View file

@ -302,7 +302,23 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
let Trace { at, trace, a_is_expected } = self;
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
fields
.equate(a_is_expected)
.equate(StructurallyRelateAliases::No, a_is_expected)
.relate(a, b)
.map(move |_| InferOk { value: (), obligations: fields.obligations })
}
/// Equates `a` and `b` while structurally relating aliases. This should only
/// be used inside of the next generation trait solver when relating rigid aliases.
#[instrument(skip(self), level = "debug")]
pub fn eq_structurally_relating_aliases<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
where
T: Relate<'tcx>,
{
let Trace { at, trace, a_is_expected } = self;
debug_assert!(at.infcx.next_trait_solver());
let mut fields = at.infcx.combine_fields(trace, at.param_env, DefineOpaqueTypes::No);
fields
.equate(StructurallyRelateAliases::Yes, a_is_expected)
.relate(a, b)
.map(move |_| InferOk { value: (), obligations: fields.obligations })
}

View file

@ -54,6 +54,7 @@ use self::region_constraints::{
RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot,
};
pub use self::relate::combine::CombineFields;
pub use self::relate::StructurallyRelateAliases;
use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
pub mod at;

View file

@ -26,6 +26,7 @@ use super::equate::Equate;
use super::glb::Glb;
use super::lub::Lub;
use super::sub::Sub;
use super::StructurallyRelateAliases;
use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace};
use crate::traits::{Obligation, PredicateObligations};
use rustc_middle::infer::canonical::OriginalQueryValues;
@ -116,8 +117,15 @@ impl<'tcx> InferCtxt<'tcx> {
}
(_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => {
relation.register_type_relate_obligation(a, b);
Ok(a)
match relation.structurally_relate_aliases() {
StructurallyRelateAliases::Yes => {
ty::relate::structurally_relate_tys(relation, a, b)
}
StructurallyRelateAliases::No => {
relation.register_type_relate_obligation(a, b);
Ok(a)
}
}
}
// All other cases of inference are errors
@ -240,19 +248,26 @@ impl<'tcx> InferCtxt<'tcx> {
(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
if self.tcx.features().generic_const_exprs || self.next_trait_solver() =>
{
let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) };
match relation.structurally_relate_aliases() {
StructurallyRelateAliases::No => {
let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) };
relation.register_predicates([if self.next_trait_solver() {
ty::PredicateKind::AliasRelate(
a.into(),
b.into(),
ty::AliasRelationDirection::Equate,
)
} else {
ty::PredicateKind::ConstEquate(a, b)
}]);
relation.register_predicates([if self.next_trait_solver() {
ty::PredicateKind::AliasRelate(
a.into(),
b.into(),
ty::AliasRelationDirection::Equate,
)
} else {
ty::PredicateKind::ConstEquate(a, b)
}]);
Ok(b)
Ok(b)
}
StructurallyRelateAliases::Yes => {
ty::relate::structurally_relate_consts(relation, a, b)
}
}
}
_ => ty::relate::structurally_relate_consts(relation, a, b),
}
@ -303,8 +318,12 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
self.infcx.tcx
}
pub fn equate<'a>(&'a mut self, a_is_expected: bool) -> Equate<'a, 'infcx, 'tcx> {
Equate::new(self, a_is_expected)
pub fn equate<'a>(
&'a mut self,
structurally_relate_aliases: StructurallyRelateAliases,
a_is_expected: bool,
) -> Equate<'a, 'infcx, 'tcx> {
Equate::new(self, structurally_relate_aliases, a_is_expected)
}
pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> Sub<'a, 'infcx, 'tcx> {
@ -335,6 +354,11 @@ pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
fn param_env(&self) -> ty::ParamEnv<'tcx>;
/// Whether aliases should be related structurally. This is pretty much
/// always `No` unless you're equating in some specific locations of the
/// new solver. See the comments in these use-cases for more details.
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
/// Register obligations that must hold in order for this relation to hold
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>);

View file

@ -1,4 +1,5 @@
use super::combine::{CombineFields, ObligationEmittingRelation};
use super::StructurallyRelateAliases;
use crate::infer::{DefineOpaqueTypes, SubregionOrigin};
use crate::traits::PredicateObligations;
@ -13,15 +14,17 @@ use rustc_span::Span;
/// Ensures `a` is made equal to `b`. Returns `a` on success.
pub struct Equate<'combine, 'infcx, 'tcx> {
fields: &'combine mut CombineFields<'infcx, 'tcx>,
structurally_relate_aliases: StructurallyRelateAliases,
a_is_expected: bool,
}
impl<'combine, 'infcx, 'tcx> Equate<'combine, 'infcx, 'tcx> {
pub fn new(
fields: &'combine mut CombineFields<'infcx, 'tcx>,
structurally_relate_aliases: StructurallyRelateAliases,
a_is_expected: bool,
) -> Equate<'combine, 'infcx, 'tcx> {
Equate { fields, a_is_expected }
Equate { fields, structurally_relate_aliases, a_is_expected }
}
}
@ -99,7 +102,7 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
) if a_def_id == b_def_id => {
self.fields.infcx.super_combine_tys(self, a, b)?;
infcx.super_combine_tys(self, a, b)?;
}
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
@ -120,7 +123,7 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
);
}
_ => {
self.fields.infcx.super_combine_tys(self, a, b)?;
infcx.super_combine_tys(self, a, b)?;
}
}
@ -180,6 +183,10 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Equate<'_, '_, 'tcx> {
self.fields.trace.span()
}
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
self.structurally_relate_aliases
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.fields.param_env
}

View file

@ -1,5 +1,6 @@
use std::mem;
use super::StructurallyRelateAliases;
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue};
use crate::infer::{InferCtxt, ObligationEmittingRelation, RegionVariableOrigin};
use rustc_data_structures::sso::SsoHashMap;
@ -45,8 +46,14 @@ impl<'tcx> InferCtxt<'tcx> {
// region/type inference variables.
//
// We then relate `generalized_ty <: source_ty`,adding constraints like `'x: '?2` and `?1 <: ?3`.
let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } =
self.generalize(relation.span(), target_vid, instantiation_variance, source_ty)?;
let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = self
.generalize(
relation.span(),
relation.structurally_relate_aliases(),
target_vid,
instantiation_variance,
source_ty,
)?;
// Constrain `b_vid` to the generalized type `generalized_ty`.
if let &ty::Infer(ty::TyVar(generalized_vid)) = generalized_ty.kind() {
@ -178,8 +185,14 @@ impl<'tcx> InferCtxt<'tcx> {
) -> RelateResult<'tcx, ()> {
// FIXME(generic_const_exprs): Occurs check failures for unevaluated
// constants and generic expressions are not yet handled correctly.
let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } =
self.generalize(relation.span(), target_vid, ty::Variance::Invariant, source_ct)?;
let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } = self
.generalize(
relation.span(),
relation.structurally_relate_aliases(),
target_vid,
ty::Variance::Invariant,
source_ct,
)?;
debug_assert!(!generalized_ct.is_ct_infer());
if has_unconstrained_ty_var {
@ -217,6 +230,7 @@ impl<'tcx> InferCtxt<'tcx> {
fn generalize<T: Into<Term<'tcx>> + Relate<'tcx>>(
&self,
span: Span,
structurally_relate_aliases: StructurallyRelateAliases,
target_vid: impl Into<ty::TermVid>,
ambient_variance: ty::Variance,
source_term: T,
@ -237,6 +251,7 @@ impl<'tcx> InferCtxt<'tcx> {
let mut generalizer = Generalizer {
infcx: self,
span,
structurally_relate_aliases,
root_vid,
for_universe,
ambient_variance,
@ -270,6 +285,10 @@ struct Generalizer<'me, 'tcx> {
span: Span,
/// Whether aliases should be related structurally. If not, we have to
/// be careful when generalizing aliases.
structurally_relate_aliases: StructurallyRelateAliases,
/// The vid of the type variable that is in the process of being
/// instantiated. If we find this within the value we are folding,
/// that means we would have created a cyclic value.
@ -314,13 +333,30 @@ impl<'tcx> Generalizer<'_, 'tcx> {
/// to normalize the alias after all.
///
/// We handle this by lazily equating the alias and generalizing
/// it to an inference variable.
/// it to an inference variable. In the new solver, we always
/// generalize to an infer var unless the alias contains escaping
/// bound variables.
///
/// This is incomplete and will hopefully soon get fixed by #119106.
/// Correctly handling aliases with escaping bound variables is
/// difficult and currently incomplete in two opposite ways:
/// - if we get an occurs check failure in the alias, replace it with a new infer var.
/// This causes us to later emit an alias-relate goal and is incomplete in case the
/// alias normalizes to type containing one of the bound variables.
/// - if the alias contains an inference variable not nameable by `for_universe`, we
/// continue generalizing the alias. This ends up pulling down the universe of the
/// inference variable and is incomplete in case the alias would normalize to a type
/// which does not mention that inference variable.
fn generalize_alias_ty(
&mut self,
alias: ty::AliasTy<'tcx>,
) -> Result<Ty<'tcx>, TypeError<'tcx>> {
if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() {
return Ok(self.infcx.next_ty_var_in_universe(
TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span: self.span },
self.for_universe,
));
}
let is_nested_alias = mem::replace(&mut self.in_alias, true);
let result = match self.relate(alias, alias) {
Ok(alias) => Ok(alias.to_ty(self.tcx())),
@ -490,7 +526,10 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
}
}
ty::Alias(_, data) => self.generalize_alias_ty(data),
ty::Alias(_, data) => match self.structurally_relate_aliases {
StructurallyRelateAliases::No => self.generalize_alias_ty(data),
StructurallyRelateAliases::Yes => relate::structurally_relate_tys(self, t, t),
},
_ => relate::structurally_relate_tys(self, t, t),
}?;

View file

@ -6,6 +6,7 @@ use rustc_span::Span;
use super::combine::{CombineFields, ObligationEmittingRelation};
use super::lattice::{self, LatticeDir};
use super::StructurallyRelateAliases;
use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
use crate::traits::{ObligationCause, PredicateObligations};
@ -45,7 +46,9 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
b: T,
) -> RelateResult<'tcx, T> {
match variance {
ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
ty::Invariant => {
self.fields.equate(StructurallyRelateAliases::No, self.a_is_expected).relate(a, b)
}
ty::Covariant => self.relate(a, b),
// FIXME(#41044) -- not correct, need test
ty::Bivariant => Ok(a),
@ -139,6 +142,10 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> {
self.fields.trace.span()
}
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
StructurallyRelateAliases::No
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.fields.param_env
}

View file

@ -2,6 +2,7 @@
use super::combine::{CombineFields, ObligationEmittingRelation};
use super::lattice::{self, LatticeDir};
use super::StructurallyRelateAliases;
use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
use crate::traits::{ObligationCause, PredicateObligations};
@ -45,7 +46,9 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
b: T,
) -> RelateResult<'tcx, T> {
match variance {
ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
ty::Invariant => {
self.fields.equate(StructurallyRelateAliases::No, self.a_is_expected).relate(a, b)
}
ty::Covariant => self.relate(a, b),
// FIXME(#41044) -- not correct, need test
ty::Bivariant => Ok(a),
@ -139,6 +142,10 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> {
self.fields.trace.span()
}
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
StructurallyRelateAliases::No
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.fields.param_env
}

View file

@ -9,3 +9,15 @@ mod higher_ranked;
mod lattice;
mod lub;
mod sub;
/// Whether aliases should be related structurally or not. Used
/// to adjust the behavior of generalization and combine.
///
/// This should always be `No` unless in a few special-cases when
/// instantiating canonical responses and in the new solver. Each
/// such case should have a comment explaining why it is used.
#[derive(Debug, Copy, Clone)]
pub enum StructurallyRelateAliases {
Yes,
No,
}

View file

@ -1,4 +1,5 @@
use super::combine::CombineFields;
use super::StructurallyRelateAliases;
use crate::infer::{DefineOpaqueTypes, ObligationEmittingRelation, SubregionOrigin};
use crate::traits::{Obligation, PredicateObligations};
@ -64,7 +65,9 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
b: T,
) -> RelateResult<'tcx, T> {
match variance {
ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
ty::Invariant => {
self.fields.equate(StructurallyRelateAliases::No, self.a_is_expected).relate(a, b)
}
ty::Covariant => self.relate(a, b),
ty::Bivariant => Ok(a),
ty::Contravariant => self.with_expected_switched(|this| this.relate(b, a)),
@ -204,6 +207,10 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Sub<'_, '_, 'tcx> {
self.fields.trace.span()
}
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
StructurallyRelateAliases::No
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.fields.param_env
}