Skip to content
Open
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
14 changes: 13 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 @@ -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!(
Expand Down
257 changes: 175 additions & 82 deletions compiler/rustc_infer/src/infer/relate/generalize.rs

Large diffs are not rendered by default.

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 @@ -900,7 +900,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
39 changes: 32 additions & 7 deletions compiler/rustc_middle/src/hooks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;)*) => {
Expand All @@ -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))),*
}
}
}
Expand Down Expand Up @@ -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")
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,11 @@ impl<'a, D: SolverDelegate<Interner = I>, 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
);

Expand Down
35 changes: 30 additions & 5 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<I, I::Predicate>,
Expand Down Expand Up @@ -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(..) => {
Expand All @@ -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<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>(
Expand Down
54 changes: 51 additions & 3 deletions compiler/rustc_trait_selection/src/solve.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<TyCtxt<'tcx>>,
Expand All @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to encapsulate these transmute in inhernet functions on InferCtxt and add a safety comment there?

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 = 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;
}
2 changes: 2 additions & 0 deletions compiler/rustc_type_ir/src/relate/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>) -> I::Ty;
}

pub fn super_combine_tys<Infcx, I, R>(
Expand Down
Loading
Loading