diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index e2d684e12a816..152a7674490c7 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -616,4 +616,9 @@ impl<'b, 'tcx> PredicateEmittingRelation> for NllTypeRelating<'_ } })]); } + + 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()) + } } diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 70e3d7dc9fef0..3487286d58830 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -140,6 +140,9 @@ impl<'a, 'tcx> At<'a, 'tcx> { 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 @@ impl<'a, 'tcx> At<'a, 'tcx> { 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 @@ impl<'a, 'tcx> At<'a, 'tcx> { 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 { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index b573065362601..351d0001c677d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -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_hir::def_id::{DefId, LocalDefId}; 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; @@ -1498,6 +1499,17 @@ impl<'tcx> InferCtxt<'tcx> { } } + 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!( diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 69c090b662e54..82a91538c4dae 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -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>>( + pub fn instantiate_ty_var>( &self, relation: &mut R, target_is_expected: bool, @@ -61,29 +61,56 @@ impl<'tcx> 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 @@ impl<'tcx> 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 `::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 @@ impl<'tcx> 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 @@ impl<'tcx> 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 @@ impl<'tcx> InferCtxt<'tcx> { fn generalize> + Relate>>( &self, span: Span, - structurally_relate_aliases: StructurallyRelateAliases, + initial_state: GeneralizerState, target_vid: impl Into, ambient_variance: ty::Variance, source_term: T, + normalize: &mut dyn FnMut(ty::AliasTy<'tcx>) -> Ty<'tcx>, ) -> RelateResult<'tcx, Generalization> { assert!(!source_term.has_escaping_bound_vars()); let (for_universe, root_vid) = match target_vid.into() { @@ -264,13 +302,13 @@ impl<'tcx> InferCtxt<'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,16 @@ impl<'tcx> TypeVisitor> for MaxUniverse { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +enum GeneralizerState { + /// Treat aliases as potentially normalizable. + Default, + IncompletelyRelateHigherRankedAlias, + /// Only one layer + ShallowStructurallyRelateAliases, + StructurallyRelateAliases, +} + /// The "generalizer" is used when handling inference variables. /// /// The basic strategy for handling a constraint like `?A <: B` is to @@ -335,10 +383,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. @@ -361,9 +405,13 @@ struct Generalizer<'me, 'tcx> { /// This is necessary to correctly handle /// `::Assoc>::Assoc == ?0`. This equality can /// hold by either normalizing the outer or the inner associated type. - in_alias: bool, + 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 +447,51 @@ impl<'tcx> Generalizer<'_, '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, 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::IncompletelyRelateHigherRankedAlias => {} } - let is_nested_alias = mem::replace(&mut self.in_alias, true); + let previous_state = + mem::replace(&mut self.state, GeneralizerState::IncompletelyRelateHigherRankedAlias); 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 +504,14 @@ impl<'tcx> Generalizer<'_, 'tcx> { debug!("generalization failure in alias"); Ok(self.next_ty_var_for_alias()) } - } + GeneralizerState::IncompletelyRelateHigherRankedAlias => return Err(e), + + // Early return. + GeneralizerState::ShallowStructurallyRelateAliases + | GeneralizerState::StructurallyRelateAliases => unreachable!(), + }, }; - self.in_alias = is_nested_alias; + self.state = previous_state; result } } @@ -488,7 +565,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, '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,26 +613,36 @@ impl<'tcx> TypeRelation> for Generalizer<'_, '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. - // - // This is incomplete as it pulls down the universe of the - // original inference variable, even though the alias could - // normalize to a type which does not refer to that type at - // all. I don't expect this to cause unexpected errors in - // practice. - // - // We only need to do so for type and const variables, as - // region variables do not impact normalization, and will get - // correctly constrained by `AliasRelate` later on. - // - // 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::IncompletelyRelateHigherRankedAlias => { + if self.infcx.next_trait_solver() + && !matches!( + self.infcx.typing_mode(), + TypingMode::Coherence + ) + { + // 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. + // + // This is incomplete as it pulls down the universe of the + // original inference variable, even though the alias could + // normalize to a type which does not refer to that type at + // all. I don't expect this to cause unexpected errors in + // practice. + // + // We only need to do so for type and const variables, as + // region variables do not impact normalization, and will get + // correctly constrained by `AliasRelate` later on. + // + // cc trait-system-refactor-initiative#108 + 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 +671,12 @@ impl<'tcx> TypeRelation> for Generalizer<'_, '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) } @@ -679,13 +763,22 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { }) .vid; - // See the comment for type inference variables - // 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::IncompletelyRelateHigherRankedAlias => { + // See the comment for type inference variables + // for more details. + if self.infcx.next_trait_solver() + && !matches!( + self.infcx.typing_mode(), + TypingMode::Coherence + ) + { + variable_table.union(vid, new_var_id); + } + } + GeneralizerState::Default + | GeneralizerState::ShallowStructurallyRelateAliases + | GeneralizerState::StructurallyRelateAliases => {} } Ok(ty::Const::new_var(self.cx(), new_var_id)) } diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index a05e2d40e829f..7e480df7dda63 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -299,4 +299,8 @@ impl<'tcx> PredicateEmittingRelation> for LatticeOp<'_, '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) + } } diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index 96a0375f5fba6..67f9dc69a4a65 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -396,4 +396,13 @@ impl<'tcx> PredicateEmittingRelation> for TypeRelating<'_, 'tcx> } })]); } + + fn try_eagerly_normalize_alias( + &mut self, + _alias: rustc_type_ir::AliasTy< as rustc_type_ir::InferCtxtLike>::Interner>, + ) -> < 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!() + } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 15addd2407857..225fff380ef52 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -900,7 +900,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { 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); diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 0ddcdac817b80..044a9c3ef165f 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -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 @@ macro_rules! declare_hooks { impl Default for Providers { fn default() -> Self { + #[allow(unused)] Providers { - $($name: |_, $($arg,)*| default_hook(stringify!($name), &($($arg,)*))),* + $($name: + |_, $($arg,)*| default_hook(stringify!($name))),* } } } @@ -115,11 +119,32 @@ declare_hooks! { 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") } diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index ce2be24adc586..e469451da993e 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -177,7 +177,11 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { 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, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 05ea217c1de08..f94b0f0c30ed3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -976,7 +976,12 @@ where 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"), ); diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 6841fe1c5124e..fedb6390d9588 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -408,7 +408,7 @@ where /// 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, @@ -1018,7 +1018,8 @@ where 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 @@ where /// 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>( - &self, + pub(super) fn relate_and_get_goals>( + &mut self, param_env: I::ParamEnv, lhs: T, + variance: ty::Variance, rhs: T, ) -> Result>, 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| { + 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 + Copy>( diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs index 5d200c4d340ba..c7699f31a0f95 100644 --- a/compiler/rustc_trait_selection/src/solve.rs +++ b/compiler/rustc_trait_selection/src/solve.rs @@ -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 @@ pub use normalize::{ 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>, @@ -27,6 +35,46 @@ fn evaluate_root_goal_for_proof_tree_raw<'tcx>( ) } +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::, &'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 = Providers { evaluate_root_goal_for_proof_tree_raw, ..*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; } diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 64b87fac77f94..72d54c23733ee 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -40,6 +40,8 @@ where /// 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::Ty; } pub fn super_combine_tys( diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 82ee4f75fcb0a..541b2531fe749 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -15,6 +15,7 @@ pub trait RelateExt: InferCtxtLike { variance: ty::Variance, rhs: T, span: ::Span, + normalize: &mut dyn FnMut(ty::AliasTy) -> ::Ty, ) -> Result< Vec::Predicate>>, TypeError, @@ -32,40 +33,46 @@ pub trait RelateExt: InferCtxtLike { >; } -impl RelateExt for Infcx { - fn relate>( +impl> RelateExt for Infcx { + fn relate>( &self, - param_env: ::ParamEnv, + param_env: I::ParamEnv, lhs: T, variance: ty::Variance, rhs: T, - span: ::Span, - ) -> Result< - Vec::Predicate>>, - TypeError, - > { - let mut relate = - SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env, span); + span: I::Span, + normalize: &mut dyn FnMut(ty::AliasTy) -> I::Ty, + ) -> Result>, TypeError> { + 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>( + fn eq_structurally_relating_aliases>( &self, - param_env: ::ParamEnv, + param_env: I::ParamEnv, lhs: T, rhs: T, - span: ::Span, - ) -> Result< - Vec::Predicate>>, - TypeError, - > { + span: I::Span, + ) -> Result>, TypeError> { + // 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 @@ impl RelateExt for Infcx { /// 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::Ty, goals: Vec>, /// The cache only tracks the `ambient_variance` as it's the /// only field which is mutable and which meaningfully changes @@ -118,12 +127,14 @@ where ambient_variance: ty::Variance, param_env: I::ParamEnv, span: I::Span, + normalize: &'infcx mut dyn FnMut(ty::AliasTy) -> I::Ty, ) -> Self { SolverRelating { infcx, structurally_relate_aliases, span, ambient_variance, + normalize, param_env, goals: vec![], cache: Default::default(), @@ -406,4 +417,8 @@ where } })]); } + + fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTy) -> I::Ty { + (self.normalize)(alias) + } } diff --git a/tests/ui/impl-trait/unsized_coercion.next.stderr b/tests/ui/impl-trait/unsized_coercion.next.stderr deleted file mode 100644 index bea5ddb0aefcc..0000000000000 --- a/tests/ui/impl-trait/unsized_coercion.next.stderr +++ /dev/null @@ -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`. diff --git a/tests/ui/impl-trait/unsized_coercion.rs b/tests/ui/impl-trait/unsized_coercion.rs index 2cbf0d25d7ec6..f77f2198be0ef 100644 --- a/tests/ui/impl-trait/unsized_coercion.rs +++ b/tests/ui/impl-trait/unsized_coercion.rs @@ -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 { if true { let x = hello(); - //[next]~^ ERROR: the size for values of type `dyn Trait` cannot be known at compilation time let y: Box = x; } Box::new(1u32) diff --git a/tests/ui/impl-trait/unsized_coercion3.next.stderr b/tests/ui/impl-trait/unsized_coercion3.next.stderr index a480a69a38641..db758761d7954 100644 --- a/tests/ui/impl-trait/unsized_coercion3.next.stderr +++ b/tests/ui/impl-trait/unsized_coercion3.next.stderr @@ -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 { + | ------------------------ + | | | + | | the expected opaque type + | expected `Box` because of return type +... +LL | Box::new(1u32) + | ^^^^^^^^^^^^^^ types differ + | + = note: expected struct `Box` + found struct `Box` + +error[E0277]: the trait bound `dyn Send: Trait` is not satisfied + --> $DIR/unsized_coercion3.rs:11:1 + | +LL | fn hello() -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/unsized_coercion3.old.stderr b/tests/ui/impl-trait/unsized_coercion3.old.stderr index 52a72b84a8dd6..3bb9f9c209510 100644 --- a/tests/ui/impl-trait/unsized_coercion3.old.stderr +++ b/tests/ui/impl-trait/unsized_coercion3.old.stderr @@ -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 = x; | ^ doesn't have a size known at compile-time diff --git a/tests/ui/impl-trait/unsized_coercion3.rs b/tests/ui/impl-trait/unsized_coercion3.rs index ebfbb2955de55..c1dd5350e229a 100644 --- a/tests/ui/impl-trait/unsized_coercion3.rs +++ b/tests/ui/impl-trait/unsized_coercion3.rs @@ -9,6 +9,7 @@ trait Trait {} impl Trait for u32 {} fn hello() -> Box { + //[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 { //[old]~^ ERROR: the size for values of type `impl Trait + ?Sized` cannot be know } Box::new(1u32) + //[next]~^ ERROR: mismatched types } fn main() {} diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr index afacb3a7d5213..4652bf5e3c586 100644 --- a/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr @@ -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) { diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed index ba46a447802c8..1c45a2c0adb3e 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed @@ -8,6 +8,5 @@ fn main() { //[next]~^^ ERROR expected a `FnMut(& 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(& as Iterator>::Item)` closure, found + //[next]~^^ ERROR expected a `FnMut(& as Iterator>::Item)` closure, found } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr index 7912ed4d7071a..36e49c20c4331 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr @@ -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(& 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`. diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs index 0fd56707763e9..20d6fed3b35b8 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs @@ -8,6 +8,5 @@ fn main() { //[next]~^^ ERROR expected a `FnMut(& 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(& as Iterator>::Item)` closure, found + //[next]~^^ ERROR expected a `FnMut(& as Iterator>::Item)` closure, found } diff --git a/tests/ui/traits/next-solver/generalize/eagely-normalizing-aliases.rs b/tests/ui/traits/next-solver/generalize/eagely-normalizing-aliases.rs new file mode 100644 index 0000000000000..463fe49e55315 --- /dev/null +++ b/tests/ui/traits/next-solver/generalize/eagely-normalizing-aliases.rs @@ -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); +impl View for StableVec {} + +fn assert_view(f: F) -> F { f } + + +fn store(x: StableVec) +where + T: HasAssoc, + StableVec: View, +{ + let _: StableVec = assert_view(x); +} + +fn main() {}