Auto merge of #151746 - jdonszelmann:eagerly-normalize-in-generalize, r=lcnr

Eagerly normalize in generalize

*[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/rust/pull/151746)*

r? @lcnr 

cc: https://github.com/rust-lang/trait-system-refactor-initiative/issues/262
This commit is contained in:
bors
2026-03-20 18:22:43 +00:00
24 changed files with 455 additions and 131 deletions
@@ -616,4 +616,9 @@ fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
}
})]);
}
fn try_eagerly_normalize_alias(&mut self, _alias: ty::AliasTy<'tcx>) -> Ty<'tcx> {
// Past hir typeck, so we don't have to worry about type inference anymore.
self.type_checker.infcx.next_ty_var(self.span())
}
}
+9
View File
@@ -140,6 +140,9 @@ pub fn sup<T>(
ty::Contravariant,
actual,
self.cause.span,
&mut |alias| {
self.infcx.try_eagerly_normalize_alias(self.param_env, self.cause.span, alias)
},
)
.map(|goals| self.goals_to_obligations(goals))
} else {
@@ -173,6 +176,9 @@ pub fn sub<T>(
ty::Covariant,
actual,
self.cause.span,
&mut |alias| {
self.infcx.try_eagerly_normalize_alias(self.param_env, self.cause.span, alias)
},
)
.map(|goals| self.goals_to_obligations(goals))
} else {
@@ -225,6 +231,9 @@ pub fn eq_trace<T>(
ty::Invariant,
actual,
self.cause.span,
&mut |alias| {
self.infcx.try_eagerly_normalize_alias(self.param_env, self.cause.span, alias)
},
)
.map(|goals| self.goals_to_obligations(goals))
} else {
+13 -1
View File
@@ -1,5 +1,5 @@
use std::cell::{Cell, RefCell};
use std::fmt;
use std::{fmt, mem};
pub use at::DefineOpaqueTypes;
use free_regions::RegionRelations;
@@ -21,6 +21,7 @@
use rustc_macros::extension;
pub use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::bug;
use rustc_middle::hooks::TypeErasedInfcx;
use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::select;
@@ -1528,6 +1529,17 @@ pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar) -> boo
}
}
pub fn try_eagerly_normalize_alias<'a>(
&'a self,
param_env: ty::ParamEnv<'tcx>,
span: Span,
alias: ty::AliasTy<'tcx>,
) -> Ty<'tcx> {
let erased =
unsafe { mem::transmute::<&'a InferCtxt<'tcx>, TypeErasedInfcx<'a, 'tcx>>(self) };
self.tcx.try_eagerly_normalize_alias(erased, param_env, span, alias)
}
/// Attach a callback to be invoked on each root obligation evaluated in the new trait solver.
pub fn attach_obligation_inspector(&self, inspector: ObligationInspector<'tcx>) {
debug_assert!(
@@ -51,7 +51,7 @@ impl<'tcx> InferCtxt<'tcx> {
/// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all
/// other usecases (i.e. setting the value of a type var).
#[instrument(level = "debug", skip(self, relation))]
pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
pub fn instantiate_ty_var<R: PredicateEmittingRelation<Self>>(
&self,
relation: &mut R,
target_is_expected: bool,
@@ -61,29 +61,56 @@ pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
) -> RelateResult<'tcx, ()> {
debug_assert!(self.inner.borrow_mut().type_variables().probe(target_vid).is_unknown());
// Generalize `source_ty` depending on the current variance. As an example, assume
// `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference
// variable.
//
// Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh
// 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 } = self.generalize(
relation.span(),
relation.structurally_relate_aliases(),
target_vid,
instantiation_variance,
source_ty,
)?;
let generalized_ty = if self.next_trait_solver()
&& matches!(relation.structurally_relate_aliases(), StructurallyRelateAliases::No)
&& let ty::Alias(_, alias) = source_ty.kind()
{
let normalized_alias = relation.try_eagerly_normalize_alias(*alias);
// Constrain `b_vid` to the generalized type `generalized_ty`.
if let &ty::Infer(ty::TyVar(generalized_vid)) = generalized_ty.kind() {
self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid);
if normalized_alias.is_ty_var() {
normalized_alias
} else {
let Generalization { value_may_be_infer: generalized_ty } = self.generalize(
relation.span(),
GeneralizerState::ShallowStructurallyRelateAliases,
target_vid,
instantiation_variance,
normalized_alias,
&mut |alias| relation.try_eagerly_normalize_alias(alias),
)?;
// The only way to get a tyvar back is if the outermost type is an alias.
// However, here, though we know it *is* an alias, we initialize the generalizer
// with `ShallowStructurallyRelateAliases` so we treat the outermost alias as rigid,
// ensuring this is never a tyvar.
assert!(!generalized_ty.is_ty_var());
generalized_ty
}
} else {
self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty);
}
// Generalize `source_ty` depending on the current variance. As an example, assume
// `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference
// variable.
//
// Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh
// 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 } = self.generalize(
relation.span(),
match relation.structurally_relate_aliases() {
StructurallyRelateAliases::No => GeneralizerState::Default,
StructurallyRelateAliases::Yes => GeneralizerState::StructurallyRelateAliases,
},
target_vid,
instantiation_variance,
source_ty,
&mut |alias| relation.try_eagerly_normalize_alias(alias),
)?;
generalized_ty
};
// Finally, relate `generalized_ty` to `source_ty`, as described in previous comment.
//
@@ -91,7 +118,10 @@ pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
// relations wind up attributed to the same spans. We need
// to associate causes/spans with each of the relations in
// the stack to get this right.
if generalized_ty.is_ty_var() {
if let &ty::Infer(ty::TyVar(generalized_vid)) = generalized_ty.kind() {
// Constrain `b_vid` to the generalized type variable.
self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid);
// This happens for cases like `<?0 as Trait>::Assoc == ?0`.
// We can't instantiate `?0` here as that would result in a
// cyclic type. We instead delay the unification in case
@@ -132,6 +162,9 @@ pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
}
}
} else {
// Constrain `b_vid` to the generalized type `generalized_ty`.
self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty);
// NOTE: The `instantiation_variance` is not the same variance as
// used by the relation. When instantiating `b`, `target_is_expected`
// is flipped and the `instantiation_variance` is also flipped. To
@@ -206,10 +239,14 @@ pub(crate) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'tcx>
// constants and generic expressions are not yet handled correctly.
let Generalization { value_may_be_infer: generalized_ct } = self.generalize(
relation.span(),
relation.structurally_relate_aliases(),
match relation.structurally_relate_aliases() {
StructurallyRelateAliases::No => GeneralizerState::Default,
StructurallyRelateAliases::Yes => GeneralizerState::StructurallyRelateAliases,
},
target_vid,
ty::Invariant,
source_ct,
&mut |alias| relation.try_eagerly_normalize_alias(alias),
)?;
debug_assert!(!generalized_ct.is_ct_infer());
@@ -245,10 +282,11 @@ pub(crate) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'tcx>
fn generalize<T: Into<Term<'tcx>> + Relate<TyCtxt<'tcx>>>(
&self,
span: Span,
structurally_relate_aliases: StructurallyRelateAliases,
initial_state: GeneralizerState,
target_vid: impl Into<TermVid>,
ambient_variance: ty::Variance,
source_term: T,
normalize: &mut dyn FnMut(ty::AliasTy<'tcx>) -> Ty<'tcx>,
) -> RelateResult<'tcx, Generalization<T>> {
assert!(!source_term.has_escaping_bound_vars());
let (for_universe, root_vid) = match target_vid.into() {
@@ -264,13 +302,13 @@ fn generalize<T: Into<Term<'tcx>> + Relate<TyCtxt<'tcx>>>(
let mut generalizer = Generalizer {
infcx: self,
span,
structurally_relate_aliases,
root_vid,
for_universe,
root_term: source_term.into(),
ambient_variance,
in_alias: false,
state: initial_state,
cache: Default::default(),
normalize,
};
let value_may_be_infer = generalizer.relate(source_term, source_term)?;
@@ -317,6 +355,54 @@ fn visit_region(&mut self, r: ty::Region<'tcx>) {
}
}
/// This state determines how generalization treats aliases.
///
/// Based on which state we're in, we treat them either as rigid or normalizable,
/// which might change depending on what types the generalization visitor encounters.
/// See `handle_alias_ty` for the logic of how we change states.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum GeneralizerState {
/// Treat aliases as potentially normalizable.
///
/// This is the default state that generalization starts in, unless we're
/// treating aliases as rigid. It also means we're not currently inside an
/// alias, since then we change the state to `IncompletelyRelateAliasArgs`.
Default,
/// We enter this state when we're generalizing the arguments of a
/// potentially normalizeable alias.
///
/// The behavior here is different between the old and the new solver:
///
/// In the old solver, the difference between this and `Default` is needed to
/// correctly handle `<T as Bar<<?0 as Foo>::Assoc>::Assoc == ?0`. That
/// equality can hold by either normalizing the outer or the inner
/// associated type. In the old solver, we always structurally relate
/// aliases. If we we encounter an occurs check failure, we propagate the
/// failure to the outermost alias, for which we then emit a `Projection`
/// goal instead.
///
/// In the new solver, we rarely get into this state.
/// When we encounter aliases we instead attempt to normalize them, and treat
/// them as rigid using `ShallowStructurallyRelate`. Only when an alias has
/// escaping bound variables do we continue with similar logic to the old
/// solver, except now we also explicitly relate the type and consts in the
/// arguments of aliases while in this mode.
///
/// FIXME: Because we relate the type and consts in the arguments of aliases
/// while in this mode, this is incomplete.
IncompletelyRelateAliasArgs,
/// During generalization, when we encounter aliases, we will first attempt
/// to normalize them when we're using the next trait solver. We can now
/// treat the normalized alias as rigid, but only for "one layer", hence
/// shallow. New aliases encountered inside the arguments of the outer alias
/// should once again be related as normal.
ShallowStructurallyRelateAliases,
/// Treat aliases as rigid when relating them.
///
/// This corresponds to `relation.structurally_relate_aliases()`.
StructurallyRelateAliases,
}
/// The "generalizer" is used when handling inference variables.
///
/// The basic strategy for handling a constraint like `?A <: B` is to
@@ -335,10 +421,6 @@ 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.
@@ -356,14 +438,17 @@ struct Generalizer<'me, 'tcx> {
/// some other type. What will be the variance at this point?
ambient_variance: ty::Variance,
/// This is set once we're generalizing the arguments of an alias.
/// This field keeps track of how we treat aliases during generalization.
///
/// This is necessary to correctly handle
/// `<T as Bar<<?0 as Foo>::Assoc>::Assoc == ?0`. This equality can
/// hold by either normalizing the outer or the inner associated type.
in_alias: bool,
/// Refer to [`GeneralizerState`]'s docs for more information about the
/// all the possible values this can have, and when we use which.
state: GeneralizerState,
cache: SsoHashMap<(Ty<'tcx>, ty::Variance, bool), Ty<'tcx>>,
cache: SsoHashMap<(Ty<'tcx>, ty::Variance, GeneralizerState), Ty<'tcx>>,
/// Normalize an alias in the trait solver.
/// If normalization fails, a fresh infer var is returned.
normalize: &'me mut dyn FnMut(ty::AliasTy<'tcx>) -> Ty<'tcx>,
}
impl<'tcx> Generalizer<'_, 'tcx> {
@@ -399,27 +484,51 @@ fn next_ty_var_for_alias(&self) -> Ty<'tcx> {
/// 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(
fn handle_alias_ty(
&mut self,
alias_ty: Ty<'tcx>,
alias: ty::AliasTy<'tcx>,
) -> Result<Ty<'tcx>, TypeError<'tcx>> {
// We do not eagerly replace aliases with inference variables if they have
// escaping bound vars, see the method comment for details. However, when we
// are inside of an alias with escaping bound vars replacing nested aliases
// with inference variables can cause incorrect ambiguity.
//
// cc trait-system-refactor-initiative#110
if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() && !self.in_alias {
return Ok(self.next_ty_var_for_alias());
match self.state {
GeneralizerState::ShallowStructurallyRelateAliases => {
// We can switch back to default, we've treated one layer as rigid by doing this operation.
self.state = GeneralizerState::Default;
let res = relate::structurally_relate_tys(self, alias_ty, alias_ty);
self.state = GeneralizerState::ShallowStructurallyRelateAliases;
return res;
}
GeneralizerState::StructurallyRelateAliases => {
return relate::structurally_relate_tys(self, alias_ty, alias_ty);
}
GeneralizerState::Default
if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() =>
{
// We do not eagerly replace aliases with inference variables if they have
// escaping bound vars, see the method comment for details. However, when we
// are inside of an alias with escaping bound vars replacing nested aliases
// with inference variables can cause incorrect ambiguity.
//
// cc trait-system-refactor-initiative#110
let normalized_alias = (self.normalize)(alias);
self.state = GeneralizerState::ShallowStructurallyRelateAliases;
// recursively generalize, treat the outer alias as rigid to avoid infinite recursion
let res = self.relate(normalized_alias, normalized_alias);
// only one way to get here
self.state = GeneralizerState::Default;
return res;
}
GeneralizerState::Default | GeneralizerState::IncompletelyRelateAliasArgs => {}
}
let is_nested_alias = mem::replace(&mut self.in_alias, true);
let previous_state =
mem::replace(&mut self.state, GeneralizerState::IncompletelyRelateAliasArgs);
let result = match self.relate(alias, alias) {
Ok(alias) => Ok(alias.to_ty(self.cx())),
Err(e) => {
if is_nested_alias {
return Err(e);
} else {
Err(e) => match previous_state {
GeneralizerState::Default => {
let mut visitor = MaxUniverse::new();
alias.visit_with(&mut visitor);
let infer_replacement_is_complete =
@@ -432,9 +541,14 @@ fn generalize_alias_ty(
debug!("generalization failure in alias");
Ok(self.next_ty_var_for_alias())
}
}
GeneralizerState::IncompletelyRelateAliasArgs => return Err(e),
// Early return.
GeneralizerState::ShallowStructurallyRelateAliases
| GeneralizerState::StructurallyRelateAliases => unreachable!(),
},
};
self.in_alias = is_nested_alias;
self.state = previous_state;
result
}
}
@@ -488,7 +602,7 @@ fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
if let Some(&result) = self.cache.get(&(t, self.ambient_variance, self.in_alias)) {
if let Some(&result) = self.cache.get(&(t, self.ambient_variance, self.state)) {
return Ok(result);
}
@@ -536,6 +650,7 @@ fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
// of each other. This is currently only used for diagnostics.
// To see why, see the docs in the `type_variables` module.
inner.type_variables().sub_unify(vid, new_var_id);
// If we're in the new solver and create a new inference
// variable inside of an alias we eagerly constrain that
// inference variable to prevent unexpected ambiguity errors.
@@ -553,9 +668,15 @@ fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
// cc trait-system-refactor-initiative#108
if self.infcx.next_trait_solver()
&& !matches!(self.infcx.typing_mode(), TypingMode::Coherence)
&& self.in_alias
{
inner.type_variables().equate(vid, new_var_id);
match self.state {
GeneralizerState::IncompletelyRelateAliasArgs => {
inner.type_variables().equate(vid, new_var_id);
}
GeneralizerState::Default
| GeneralizerState::ShallowStructurallyRelateAliases
| GeneralizerState::StructurallyRelateAliases => {}
}
}
debug!("replacing original vid={:?} with new={:?}", vid, new_var_id);
@@ -584,15 +705,12 @@ fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
}
}
ty::Alias(_, data) => match self.structurally_relate_aliases {
StructurallyRelateAliases::No => self.generalize_alias_ty(data),
StructurallyRelateAliases::Yes => relate::structurally_relate_tys(self, t, t),
},
ty::Alias(_, data) => self.handle_alias_ty(t, data),
_ => relate::structurally_relate_tys(self, t, t),
}?;
self.cache.insert((t, self.ambient_variance, self.in_alias), g);
self.cache.insert((t, self.ambient_variance, self.state), g);
Ok(g)
}
@@ -683,9 +801,15 @@ fn consts(
// for more details.
if self.infcx.next_trait_solver()
&& !matches!(self.infcx.typing_mode(), TypingMode::Coherence)
&& self.in_alias
{
variable_table.union(vid, new_var_id);
match self.state {
GeneralizerState::IncompletelyRelateAliasArgs => {
variable_table.union(vid, new_var_id);
}
GeneralizerState::Default
| GeneralizerState::ShallowStructurallyRelateAliases
| GeneralizerState::StructurallyRelateAliases => {}
}
}
Ok(ty::Const::new_var(self.cx(), new_var_id))
}
@@ -299,4 +299,8 @@ fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
ty::AliasRelationDirection::Equate,
))]);
}
fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTy<'tcx>) -> Ty<'tcx> {
self.infcx.try_eagerly_normalize_alias(self.param_env(), self.span(), alias)
}
}
@@ -396,4 +396,13 @@ fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
}
})]);
}
fn try_eagerly_normalize_alias(
&mut self,
_alias: rustc_type_ir::AliasTy<<InferCtxt<'tcx> as rustc_type_ir::InferCtxtLike>::Interner>,
) -> <<InferCtxt<'tcx> as rustc_type_ir::InferCtxtLike>::Interner as rustc_type_ir::Interner>::Ty
{
// We only try to eagerly normalize aliases if we're using the new solver.
unreachable!()
}
}
+1 -1
View File
@@ -904,7 +904,7 @@ pub fn write_interface<'tcx>(tcx: TyCtxt<'tcx>) {
rustc_hir_typeck::provide(&mut providers.queries);
ty::provide(&mut providers.queries);
traits::provide(&mut providers.queries);
solve::provide(&mut providers.queries);
solve::provide(providers);
rustc_passes::provide(&mut providers.queries);
rustc_traits::provide(&mut providers.queries);
rustc_ty_utils::provide(&mut providers.queries);
+32 -7
View File
@@ -3,14 +3,16 @@
//! similar to queries, but queries come with a lot of machinery for caching and incremental
//! compilation, whereas hooks are just plain function pointers without any of the query magic.
use std::marker::PhantomData;
use rustc_hir::def_id::{DefId, DefPathHash};
use rustc_session::StableCrateId;
use rustc_span::def_id::{CrateNum, LocalDefId};
use rustc_span::{ExpnHash, ExpnId};
use rustc_span::{ExpnHash, ExpnId, Span};
use crate::mir;
use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex};
use crate::ty::{Ty, TyCtxt};
use crate::{mir, ty};
macro_rules! declare_hooks {
($($(#[$attr:meta])*hook $name:ident($($arg:ident: $K:ty),*) -> $V:ty;)*) => {
@@ -35,8 +37,10 @@ pub struct Providers {
impl Default for Providers {
fn default() -> Self {
#[allow(unused)]
Providers {
$($name: |_, $($arg,)*| default_hook(stringify!($name), &($($arg,)*))),*
$($name:
|_, $($arg,)*| default_hook(stringify!($name))),*
}
}
}
@@ -113,11 +117,32 @@ fn clone(&self) -> Self { *self }
encoder: &mut CacheEncoder<'_, 'tcx>,
query_result_index: &mut EncodedDepNodeIndex
) -> ();
/// Tries to normalize an alias, ignoring any errors.
///
/// Generalization with the new trait solver calls into this,
/// when generalizing outside of the trait solver in `hir_typeck`.
hook try_eagerly_normalize_alias(
type_erased_infcx: TypeErasedInfcx<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
span: Span,
alias: ty::AliasTy<'tcx>
) -> Ty<'tcx>;
}
/// The `try_eagerly_normalize_alias` hook passes an `Infcx` from where it's called (in `rustc_infer`)
/// to where it's provided (in `rustc_trait_selection`).
/// Both of those crates have that type available, but `rustc_middle` does not.
/// Instead we pass this type-erased `Infcx` and transmute on both sides.
///
/// Has to be `repr(transparent)` so we can transmute a `&'a Infcx<'tcx>` to this struct.
#[repr(transparent)]
pub struct TypeErasedInfcx<'a, 'tcx> {
_infcx: *const (),
phantom: PhantomData<&'a mut &'tcx ()>,
}
#[cold]
fn default_hook(name: &str, args: &dyn std::fmt::Debug) -> ! {
bug!(
"`tcx.{name}{args:?}` cannot be called as `{name}` was never assigned to a provider function"
)
fn default_hook(name: &str) -> ! {
bug!("`tcx.{name}` cannot be called as `{name}` was never assigned to a provider function")
}
@@ -177,7 +177,11 @@ fn canonicalize_param_env(
cache: Default::default(),
};
let param_env = param_env.fold_with(&mut env_canonicalizer);
debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
debug_assert!(
env_canonicalizer.sub_root_lookup_table.is_empty(),
"{:?}",
env_canonicalizer.sub_root_lookup_table
);
(
param_env,
env_canonicalizer.variables,
@@ -987,7 +987,12 @@ fn try_eagerly_replace_alias(
let replacement = self.ecx.instantiate_binder_with_infer(*replacement);
self.nested.extend(
self.ecx
.eq_and_get_goals(self.param_env, alias_term, replacement.projection_term)
.relate_and_get_goals(
self.param_env,
alias_term,
ty::Invariant,
replacement.projection_term,
)
.expect("expected to be able to unify goal projection with dyn's projection"),
);
@@ -408,7 +408,7 @@ pub(super) fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsage
/// Recursively evaluates `goal`, returning whether any inference vars have
/// been constrained and the certainty of the result.
fn evaluate_goal(
pub(super) fn evaluate_goal(
&mut self,
source: GoalSource,
goal: Goal<I, I::Predicate>,
@@ -1018,7 +1018,8 @@ pub(super) fn relate<T: Relate<I>>(
variance: ty::Variance,
rhs: T,
) -> Result<(), NoSolution> {
let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?;
let goals = self.relate_and_get_goals(param_env, lhs, variance, rhs)?;
for &goal in goals.iter() {
let source = match goal.predicate.kind().skip_binder() {
ty::PredicateKind::Subtype { .. } | ty::PredicateKind::AliasRelate(..) => {
@@ -1039,13 +1040,37 @@ pub(super) fn relate<T: Relate<I>>(
/// If possible, try using `eq` instead which automatically handles nested
/// goals correctly.
#[instrument(level = "trace", skip(self, param_env), ret)]
pub(super) fn eq_and_get_goals<T: Relate<I>>(
&self,
pub(super) fn relate_and_get_goals<T: Relate<I>>(
&mut self,
param_env: I::ParamEnv,
lhs: T,
variance: ty::Variance,
rhs: T,
) -> Result<Vec<Goal<I, I::Predicate>>, NoSolution> {
Ok(self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs, self.origin_span)?)
let cx = self.cx();
let delegate = self.delegate;
let origin_span = self.origin_span;
let mut normalize = |alias: ty::AliasTy<I>| {
let inference_var = self.next_ty_infer();
let goal = Goal::new(
cx,
param_env,
ty::PredicateKind::AliasRelate(
alias.to_ty(cx).into(),
inference_var.into(),
ty::AliasRelationDirection::Equate,
),
);
// Ignore the result. If we can't eagerly normalize, returning the inference variable is enough.
let _ = self.evaluate_goal(GoalSource::TypeRelating, goal, None);
self.resolve_vars_if_possible(inference_var)
};
Ok(delegate.relate(param_env, lhs, variance, rhs, origin_span, &mut normalize)?)
}
pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<I> + Copy>(
+52 -4
View File
@@ -1,3 +1,8 @@
use std::mem;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::{Obligation, ObligationCause};
use rustc_middle::hooks::TypeErasedInfcx;
pub use rustc_next_trait_solver::solve::*;
mod delegate;
@@ -13,10 +18,13 @@
deeply_normalize, deeply_normalize_with_skipped_universes,
deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals,
};
use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::util::Providers;
use rustc_span::Span;
pub use select::InferCtxtSelectExt;
use crate::traits::ObligationCtxt;
fn evaluate_root_goal_for_proof_tree_raw<'tcx>(
tcx: TyCtxt<'tcx>,
canonical_input: CanonicalInput<TyCtxt<'tcx>>,
@@ -27,6 +35,46 @@ fn evaluate_root_goal_for_proof_tree_raw<'tcx>(
)
}
pub fn provide(providers: &mut Providers) {
*providers = Providers { evaluate_root_goal_for_proof_tree_raw, ..*providers };
fn try_eagerly_normalize_alias<'a, 'tcx>(
tcx: TyCtxt<'tcx>,
type_erased_infcx: TypeErasedInfcx<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
span: Span,
alias: ty::AliasTy<'tcx>,
) -> Ty<'tcx> {
let infcx = unsafe {
mem::transmute::<TypeErasedInfcx<'a, 'tcx>, &'a InferCtxt<'tcx>>(type_erased_infcx)
};
let ocx = ObligationCtxt::new(infcx);
let infer_term = infcx.next_ty_var(span);
// Dummy because we ignore the error anyway.
// We do provide a span, because this span is used when registering opaque types.
// For example, if we don't provide a span here, some diagnostics talking about TAIT will refer to a dummy span.
let cause = ObligationCause::dummy_with_span(span);
let obligation = Obligation::new(
tcx,
cause,
param_env,
ty::PredicateKind::AliasRelate(
alias.to_ty(tcx).into(),
infer_term.into(),
ty::AliasRelationDirection::Equate,
),
);
ocx.register_obligation(obligation);
// We only use this to constrain inference variables.
// We don't care if it errors.
let _ = ocx.try_evaluate_obligations();
infcx.resolve_vars_if_possible(infer_term)
}
pub fn provide(providers: &mut Providers) {
providers.hooks.try_eagerly_normalize_alias = try_eagerly_normalize_alias;
providers.queries.evaluate_root_goal_for_proof_tree_raw = evaluate_root_goal_for_proof_tree_raw;
}
@@ -40,6 +40,8 @@ fn register_predicates(
/// Register `AliasRelate` obligation(s) that both types must be related to each other.
fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty);
fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTy<I>) -> I::Ty;
}
pub fn super_combine_tys<Infcx, I, R>(
@@ -15,6 +15,7 @@ fn relate<T: Relate<Self::Interner>>(
variance: ty::Variance,
rhs: T,
span: <Self::Interner as Interner>::Span,
normalize: &mut dyn FnMut(ty::AliasTy<Self::Interner>) -> <Self::Interner as Interner>::Ty,
) -> Result<
Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
TypeError<Self::Interner>,
@@ -32,40 +33,46 @@ fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
>;
}
impl<Infcx: InferCtxtLike> RelateExt for Infcx {
fn relate<T: Relate<Self::Interner>>(
impl<I: Interner, Infcx: InferCtxtLike<Interner = I>> RelateExt for Infcx {
fn relate<T: Relate<I>>(
&self,
param_env: <Self::Interner as Interner>::ParamEnv,
param_env: I::ParamEnv,
lhs: T,
variance: ty::Variance,
rhs: T,
span: <Self::Interner as Interner>::Span,
) -> Result<
Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
TypeError<Self::Interner>,
> {
let mut relate =
SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env, span);
span: I::Span,
normalize: &mut dyn FnMut(ty::AliasTy<I>) -> I::Ty,
) -> Result<Vec<Goal<I, I::Predicate>>, TypeError<I>> {
let mut relate = SolverRelating::new(
self,
StructurallyRelateAliases::No,
variance,
param_env,
span,
normalize,
);
relate.relate(lhs, rhs)?;
Ok(relate.goals)
}
fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
fn eq_structurally_relating_aliases<T: Relate<I>>(
&self,
param_env: <Self::Interner as Interner>::ParamEnv,
param_env: I::ParamEnv,
lhs: T,
rhs: T,
span: <Self::Interner as Interner>::Span,
) -> Result<
Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
TypeError<Self::Interner>,
> {
span: I::Span,
) -> Result<Vec<Goal<I, I::Predicate>>, TypeError<I>> {
// Structurally relating, we treat aliases as rigid,
// so we shouldn't ever try to normalize them.
let mut normalize_unreachable = |_alias| unreachable!();
let mut relate = SolverRelating::new(
self,
StructurallyRelateAliases::Yes,
ty::Invariant,
param_env,
span,
&mut normalize_unreachable,
);
relate.relate(lhs, rhs)?;
Ok(relate.goals)
@@ -75,12 +82,14 @@ fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
/// Enforce that `a` is equal to or a subtype of `b`.
pub struct SolverRelating<'infcx, Infcx, I: Interner> {
infcx: &'infcx Infcx,
// Immutable fields.
structurally_relate_aliases: StructurallyRelateAliases,
param_env: I::ParamEnv,
span: I::Span,
// Mutable fields.
ambient_variance: ty::Variance,
normalize: &'infcx mut dyn FnMut(ty::AliasTy<I>) -> I::Ty,
goals: Vec<Goal<I, I::Predicate>>,
/// The cache only tracks the `ambient_variance` as it's the
/// only field which is mutable and which meaningfully changes
@@ -118,12 +127,14 @@ pub fn new(
ambient_variance: ty::Variance,
param_env: I::ParamEnv,
span: I::Span,
normalize: &'infcx mut dyn FnMut(ty::AliasTy<I>) -> I::Ty,
) -> Self {
SolverRelating {
infcx,
structurally_relate_aliases,
span,
ambient_variance,
normalize,
param_env,
goals: vec![],
cache: Default::default(),
@@ -406,4 +417,8 @@ fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty) {
}
})]);
}
fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTy<I>) -> I::Ty {
(self.normalize)(alias)
}
}
@@ -1,11 +0,0 @@
error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
--> $DIR/unsized_coercion.rs:14:17
|
LL | let x = hello();
| ^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn Trait`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.
+1 -2
View File
@@ -3,7 +3,7 @@
//@ revisions: next old
//@[next] compile-flags: -Znext-solver
//@[old] check-pass
//@ check-pass
trait Trait {}
@@ -12,7 +12,6 @@ impl Trait for u32 {}
fn hello() -> Box<impl Trait> {
if true {
let x = hello();
//[next]~^ ERROR: the size for values of type `dyn Trait` cannot be known at compilation time
let y: Box<dyn Trait> = x;
}
Box::new(1u32)
@@ -1,5 +1,5 @@
error[E0277]: the trait bound `dyn Send: Trait` is not satisfied
--> $DIR/unsized_coercion3.rs:13:17
--> $DIR/unsized_coercion3.rs:14:17
|
LL | let x = hello();
| ^^^^^^^ the trait `Trait` is not implemented for `dyn Send`
@@ -9,7 +9,37 @@ help: the trait `Trait` is implemented for `u32`
|
LL | impl Trait for u32 {}
| ^^^^^^^^^^^^^^^^^^
note: required by a bound in `Box`
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
error: aborting due to 1 previous error
error[E0308]: mismatched types
--> $DIR/unsized_coercion3.rs:19:5
|
LL | fn hello() -> Box<impl Trait + ?Sized> {
| ------------------------
| | |
| | the expected opaque type
| expected `Box<impl Trait + ?Sized>` because of return type
...
LL | Box::new(1u32)
| ^^^^^^^^^^^^^^ types differ
|
= note: expected struct `Box<impl Trait + ?Sized>`
found struct `Box<u32>`
For more information about this error, try `rustc --explain E0277`.
error[E0277]: the trait bound `dyn Send: Trait` is not satisfied
--> $DIR/unsized_coercion3.rs:11:1
|
LL | fn hello() -> Box<impl Trait + ?Sized> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `dyn Send`
|
help: the trait `Trait` is implemented for `u32`
--> $DIR/unsized_coercion3.rs:9:1
|
LL | impl Trait for u32 {}
| ^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
@@ -1,5 +1,5 @@
error[E0277]: the size for values of type `impl Trait + ?Sized` cannot be known at compilation time
--> $DIR/unsized_coercion3.rs:15:32
--> $DIR/unsized_coercion3.rs:16:32
|
LL | let y: Box<dyn Send> = x;
| ^ doesn't have a size known at compile-time
+2
View File
@@ -9,6 +9,7 @@ trait Trait {}
impl Trait for u32 {}
fn hello() -> Box<impl Trait + ?Sized> {
//[next]~^ ERROR: the trait bound `dyn Send: Trait` is not satisfied
if true {
let x = hello();
//[next]~^ ERROR: the trait bound `dyn Send: Trait` is not satisfied
@@ -16,6 +17,7 @@ fn hello() -> Box<impl Trait + ?Sized> {
//[old]~^ ERROR: the size for values of type `impl Trait + ?Sized` cannot be know
}
Box::new(1u32)
//[next]~^ ERROR: mismatched types
}
fn main() {}
@@ -9,7 +9,7 @@ note: candidate #1 is defined in the trait `Trait1`
|
LL | fn method(&self) {
| ^^^^^^^^^^^^^^^^
note: candidate #2 is defined in the trait `Trait2`
note: candidate #2 is defined in an impl of the trait `Trait2` for the type `T`
--> $DIR/rigid-alias-bound-is-not-inherent.rs:27:5
|
LL | fn method(&self) {
@@ -8,6 +8,5 @@ fn main() {
//[next]~^^ ERROR expected a `FnMut(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item)` closure, found
let _ = (-10..=10).find(|x: &i32| x.signum() == 0);
//[current]~^ ERROR type mismatch in closure arguments
//[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}`
//[next]~| ERROR expected a `FnMut(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item)` closure, found
//[next]~^^ ERROR expected a `FnMut(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item)` closure, found
}
@@ -12,12 +12,6 @@ LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0);
note: required by a bound in `find`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
error[E0271]: expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}`
--> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:24
|
LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0);
| ^^^^ expected `&&i32`, found integer
error[E0277]: expected a `FnMut(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}`
--> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:29
|
@@ -32,7 +26,6 @@ LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0);
note: required by a bound in `find`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
error: aborting due to 3 previous errors
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.
For more information about this error, try `rustc --explain E0277`.
@@ -8,6 +8,5 @@ fn main() {
//[next]~^^ ERROR expected a `FnMut(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item)` closure, found
let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0);
//[current]~^ ERROR type mismatch in closure arguments
//[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}`
//[next]~| ERROR expected a `FnMut(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item)` closure, found
//[next]~^^ ERROR expected a `FnMut(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item)` closure, found
}
@@ -0,0 +1,26 @@
//@ revisions: old next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)
//@ check-pass
// Regression test for trait-system-refactor-initiative#262
trait View {}
trait HasAssoc {
type Assoc;
}
struct StableVec<T>(T);
impl<T> View for StableVec<T> {}
fn assert_view<F: View>(f: F) -> F { f }
fn store<T>(x: StableVec<T::Assoc>)
where
T: HasAssoc,
StableVec<T>: View,
{
let _: StableVec<T::Assoc> = assert_view(x);
}
fn main() {}