Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions compiler/rustc_borrowck/src/type_check/relate_tys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,4 +616,9 @@ impl<'b, 'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> 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())
}
}
9 changes: 9 additions & 0 deletions compiler/rustc_infer/src/infer/at.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
13 changes: 12 additions & 1 deletion compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::cell::{Cell, RefCell};
use std::fmt;
use std::{fmt, mem};

pub use at::DefineOpaqueTypes;
use free_regions::RegionRelations;
Expand All @@ -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;
Expand Down Expand Up @@ -1465,6 +1466,16 @@ 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::<_, 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!(
Expand Down
122 changes: 88 additions & 34 deletions compiler/rustc_infer/src/infer/relate/generalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl<'tcx> InferCtxt<'tcx> {
target_vid,
instantiation_variance,
source_ty,
&mut |alias| relation.try_eagerly_normalize_alias(alias),
)?;

// Constrain `b_vid` to the generalized type `generalized_ty`.
Expand Down Expand Up @@ -210,6 +211,7 @@ impl<'tcx> InferCtxt<'tcx> {
target_vid,
ty::Invariant,
source_ct,
&mut |alias| relation.try_eagerly_normalize_alias(alias),
)?;

debug_assert!(!generalized_ct.is_ct_infer());
Expand Down Expand Up @@ -249,6 +251,7 @@ impl<'tcx> InferCtxt<'tcx> {
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() {
Expand All @@ -264,13 +267,16 @@ 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: match structurally_relate_aliases {
StructurallyRelateAliases::No => GeneralizerState::Default,
StructurallyRelateAliases::Yes => GeneralizerState::StructurallyRelateAliases,
},
cache: Default::default(),
normalize,
};

let value_may_be_infer = generalizer.relate(source_term, source_term)?;
Expand Down Expand Up @@ -317,6 +323,16 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> 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
Expand All @@ -335,10 +351,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.
Expand All @@ -361,9 +373,13 @@ struct Generalizer<'me, 'tcx> {
/// 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,
state: GeneralizerState,

cache: SsoHashMap<(Ty<'tcx>, ty::Variance, GeneralizerState), Ty<'tcx>>,

cache: SsoHashMap<(Ty<'tcx>, ty::Variance, bool), 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> {
Expand Down Expand Up @@ -399,27 +415,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<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::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 =
Expand All @@ -432,9 +472,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
}
}
Expand Down Expand Up @@ -488,7 +533,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> 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);
}

Expand Down Expand Up @@ -553,9 +598,15 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, '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::IncompletelyRelateHigherRankedAlias => {
inner.type_variables().equate(vid, new_var_id);
}
GeneralizerState::Default
| GeneralizerState::ShallowStructurallyRelateAliases
| GeneralizerState::StructurallyRelateAliases => {}
}
}

debug!("replacing original vid={:?} with new={:?}", vid, new_var_id);
Expand Down Expand Up @@ -584,15 +635,12 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> 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)
}

Expand Down Expand Up @@ -683,9 +731,15 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
// 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 => {
variable_table.union(vid, new_var_id);
}
GeneralizerState::Default
| GeneralizerState::ShallowStructurallyRelateAliases
| GeneralizerState::StructurallyRelateAliases => {}
}
}
Ok(ty::Const::new_var(self.cx(), new_var_id))
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_infer/src/infer/relate/lattice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,4 +299,8 @@ impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> 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)
}
}
9 changes: 9 additions & 0 deletions compiler/rustc_infer/src/infer/relate/type_relating.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,4 +396,13 @@ impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for TypeRelating<'_, '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!()
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -901,7 +901,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = 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);
Expand Down
30 changes: 23 additions & 7 deletions compiler/rustc_middle/src/hooks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
//! 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::ty::{Ty, TyCtxt};
use crate::{mir, ty};

macro_rules! declare_hooks {
($($(#[$attr:meta])*hook $name:ident($($arg:ident: $K:ty),*) -> $V:ty;)*) => {
Expand All @@ -34,8 +36,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))),*
}
}
}
Expand Down Expand Up @@ -107,11 +111,23 @@ declare_hooks! {
///
/// Creates the MIR for a given `DefId`, including unreachable code.
hook build_mir_inner_impl(def: LocalDefId) -> mir::Body<'tcx>;

/// TODO
hook try_eagerly_normalize_alias(
type_erased_infcx: TypeErasedInfcx<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
span: Span,
alias: ty::AliasTy<'tcx>
) -> Ty<'tcx>;
}

#[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")
}
Loading
Loading