summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_infer/src/infer/relate/combine.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
commit9918693037dce8aa4bb6f08741b6812923486c18 (patch)
tree21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /compiler/rustc_infer/src/infer/relate/combine.rs
parentReleasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff)
downloadrustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz
rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_infer/src/infer/relate/combine.rs')
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs613
1 files changed, 613 insertions, 0 deletions
diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
new file mode 100644
index 000000000..ee911c432
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/relate/combine.rs
@@ -0,0 +1,613 @@
+//! There are four type combiners: [Equate], [Sub], [Lub], and [Glb].
+//! Each implements the trait [TypeRelation] and contains methods for
+//! combining two instances of various things and yielding a new instance.
+//! These combiner methods always yield a `Result<T>`. To relate two
+//! types, you can use `infcx.at(cause, param_env)` which then allows
+//! you to use the relevant methods of [At](crate::infer::at::At).
+//!
+//! Combiners mostly do their specific behavior and then hand off the
+//! bulk of the work to [InferCtxt::super_combine_tys] and
+//! [InferCtxt::super_combine_consts].
+//!
+//! Combining two types may have side-effects on the inference contexts
+//! which can be undone by using snapshots. You probably want to use
+//! either [InferCtxt::commit_if_ok] or [InferCtxt::probe].
+//!
+//! On success, the LUB/GLB operations return the appropriate bound. The
+//! return value of `Equate` or `Sub` shouldn't really be used.
+//!
+//! ## Contravariance
+//!
+//! We explicitly track which argument is expected using
+//! [TypeRelation::a_is_expected], so when dealing with contravariance
+//! this should be correctly updated.
+
+use super::equate::Equate;
+use super::generalize::{self, CombineDelegate, Generalization};
+use super::glb::Glb;
+use super::lub::Lub;
+use super::sub::Sub;
+use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace};
+use crate::traits::{Obligation, PredicateObligations};
+use rustc_middle::infer::canonical::OriginalQueryValues;
+use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue};
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::relate::{RelateResult, TypeRelation};
+use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{AliasRelationDirection, TyVar};
+use rustc_middle::ty::{IntType, UintType};
+use rustc_span::DUMMY_SP;
+
+#[derive(Clone)]
+pub struct CombineFields<'infcx, 'tcx> {
+ pub infcx: &'infcx InferCtxt<'tcx>,
+ pub trace: TypeTrace<'tcx>,
+ pub cause: Option<ty::relate::Cause>,
+ pub param_env: ty::ParamEnv<'tcx>,
+ pub obligations: PredicateObligations<'tcx>,
+ pub define_opaque_types: DefineOpaqueTypes,
+}
+
+impl<'tcx> InferCtxt<'tcx> {
+ pub fn super_combine_tys<R>(
+ &self,
+ relation: &mut R,
+ a: Ty<'tcx>,
+ b: Ty<'tcx>,
+ ) -> RelateResult<'tcx, Ty<'tcx>>
+ where
+ R: ObligationEmittingRelation<'tcx>,
+ {
+ let a_is_expected = relation.a_is_expected();
+ debug_assert!(!a.has_escaping_bound_vars());
+ debug_assert!(!b.has_escaping_bound_vars());
+
+ match (a.kind(), b.kind()) {
+ // Relate integral variables to other types
+ (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => {
+ self.inner
+ .borrow_mut()
+ .int_unification_table()
+ .unify_var_var(a_id, b_id)
+ .map_err(|e| int_unification_error(a_is_expected, e))?;
+ Ok(a)
+ }
+ (&ty::Infer(ty::IntVar(v_id)), &ty::Int(v)) => {
+ self.unify_integral_variable(a_is_expected, v_id, IntType(v))
+ }
+ (&ty::Int(v), &ty::Infer(ty::IntVar(v_id))) => {
+ self.unify_integral_variable(!a_is_expected, v_id, IntType(v))
+ }
+ (&ty::Infer(ty::IntVar(v_id)), &ty::Uint(v)) => {
+ self.unify_integral_variable(a_is_expected, v_id, UintType(v))
+ }
+ (&ty::Uint(v), &ty::Infer(ty::IntVar(v_id))) => {
+ self.unify_integral_variable(!a_is_expected, v_id, UintType(v))
+ }
+
+ // Relate floating-point variables to other types
+ (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => {
+ self.inner
+ .borrow_mut()
+ .float_unification_table()
+ .unify_var_var(a_id, b_id)
+ .map_err(|e| float_unification_error(a_is_expected, e))?;
+ Ok(a)
+ }
+ (&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => {
+ self.unify_float_variable(a_is_expected, v_id, v)
+ }
+ (&ty::Float(v), &ty::Infer(ty::FloatVar(v_id))) => {
+ self.unify_float_variable(!a_is_expected, v_id, v)
+ }
+
+ // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm.
+ (ty::Alias(..), ty::Infer(ty::TyVar(_))) | (ty::Infer(ty::TyVar(_)), ty::Alias(..))
+ if self.next_trait_solver() =>
+ {
+ bug!(
+ "We do not expect to encounter `TyVar` this late in combine \
+ -- they should have been handled earlier"
+ )
+ }
+ (_, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)))
+ | (ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), _)
+ if self.next_trait_solver() =>
+ {
+ bug!("We do not expect to encounter `Fresh` variables in the new solver")
+ }
+
+ (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => {
+ relation.register_type_relate_obligation(a, b);
+ Ok(a)
+ }
+
+ // All other cases of inference are errors
+ (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
+ Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b)))
+ }
+
+ // During coherence, opaque types should be treated as *possibly*
+ // equal to any other type (except for possibly itself). This is an
+ // extremely heavy hammer, but can be relaxed in a fowards-compatible
+ // way later.
+ (&ty::Alias(ty::Opaque, _), _) | (_, &ty::Alias(ty::Opaque, _)) if self.intercrate => {
+ relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]);
+ Ok(a)
+ }
+
+ _ => ty::relate::structurally_relate_tys(relation, a, b),
+ }
+ }
+
+ pub fn super_combine_consts<R>(
+ &self,
+ relation: &mut R,
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>>
+ where
+ R: ObligationEmittingRelation<'tcx>,
+ {
+ debug!("{}.consts({:?}, {:?})", relation.tag(), a, b);
+ debug_assert!(!a.has_escaping_bound_vars());
+ debug_assert!(!b.has_escaping_bound_vars());
+ if a == b {
+ return Ok(a);
+ }
+
+ let a = self.shallow_resolve(a);
+ let b = self.shallow_resolve(b);
+
+ // We should never have to relate the `ty` field on `Const` as it is checked elsewhere that consts have the
+ // correct type for the generic param they are an argument for. However there have been a number of cases
+ // historically where asserting that the types are equal has found bugs in the compiler so this is valuable
+ // to check even if it is a bit nasty impl wise :(
+ //
+ // This probe is probably not strictly necessary but it seems better to be safe and not accidentally find
+ // ourselves with a check to find bugs being required for code to compile because it made inference progress.
+ let compatible_types = self.probe(|_| {
+ if a.ty() == b.ty() {
+ return Ok(());
+ }
+
+ // We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the
+ // two const param's types are able to be equal has to go through a canonical query with the actual logic
+ // in `rustc_trait_selection`.
+ let canonical = self.canonicalize_query(
+ relation.param_env().and((a.ty(), b.ty())),
+ &mut OriginalQueryValues::default(),
+ );
+ self.tcx.check_tys_might_be_eq(canonical).map_err(|_| {
+ self.tcx.sess.span_delayed_bug(
+ DUMMY_SP,
+ format!("cannot relate consts of different types (a={a:?}, b={b:?})",),
+ )
+ })
+ });
+
+ // If the consts have differing types, just bail with a const error with
+ // the expected const's type. Specifically, we don't want const infer vars
+ // to do any type shapeshifting before and after resolution.
+ if let Err(guar) = compatible_types {
+ // HACK: equating both sides with `[const error]` eagerly prevents us
+ // from leaving unconstrained inference vars during things like impl
+ // matching in the solver.
+ let a_error = ty::Const::new_error(self.tcx, guar, a.ty());
+ if let ty::ConstKind::Infer(InferConst::Var(vid)) = a.kind() {
+ return self.unify_const_variable(vid, a_error, relation.param_env());
+ }
+ let b_error = ty::Const::new_error(self.tcx, guar, b.ty());
+ if let ty::ConstKind::Infer(InferConst::Var(vid)) = b.kind() {
+ return self.unify_const_variable(vid, b_error, relation.param_env());
+ }
+
+ return Ok(if relation.a_is_expected() { a_error } else { b_error });
+ }
+
+ match (a.kind(), b.kind()) {
+ (
+ ty::ConstKind::Infer(InferConst::Var(a_vid)),
+ ty::ConstKind::Infer(InferConst::Var(b_vid)),
+ ) => {
+ self.inner.borrow_mut().const_unification_table().union(a_vid, b_vid);
+ return Ok(a);
+ }
+
+ (
+ ty::ConstKind::Infer(InferConst::EffectVar(a_vid)),
+ ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
+ ) => {
+ self.inner
+ .borrow_mut()
+ .effect_unification_table()
+ .unify_var_var(a_vid, b_vid)
+ .map_err(|a| effect_unification_error(self.tcx, relation.a_is_expected(), a))?;
+ return Ok(a);
+ }
+
+ // All other cases of inference with other variables are errors.
+ (
+ ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
+ ty::ConstKind::Infer(_),
+ )
+ | (
+ ty::ConstKind::Infer(_),
+ ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
+ ) => {
+ bug!(
+ "tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}"
+ )
+ }
+
+ (ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
+ return self.unify_const_variable(vid, b, relation.param_env());
+ }
+
+ (_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
+ return self.unify_const_variable(vid, a, relation.param_env());
+ }
+
+ (ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
+ return self.unify_effect_variable(
+ relation.a_is_expected(),
+ vid,
+ EffectVarValue::Const(b),
+ );
+ }
+
+ (_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
+ return self.unify_effect_variable(
+ !relation.a_is_expected(),
+ vid,
+ EffectVarValue::Const(a),
+ );
+ }
+
+ (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
+ if self.tcx.features().generic_const_exprs || self.next_trait_solver() =>
+ {
+ let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) };
+
+ relation.register_predicates([ty::Binder::dummy(if self.next_trait_solver() {
+ ty::PredicateKind::AliasRelate(
+ a.into(),
+ b.into(),
+ ty::AliasRelationDirection::Equate,
+ )
+ } else {
+ ty::PredicateKind::ConstEquate(a, b)
+ })]);
+
+ return Ok(b);
+ }
+ _ => {}
+ }
+
+ ty::relate::structurally_relate_consts(relation, a, b)
+ }
+
+ /// Unifies the const variable `target_vid` with the given constant.
+ ///
+ /// This also tests if the given const `ct` contains an inference variable which was previously
+ /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct`
+ /// would result in an infinite type as we continuously replace an inference variable
+ /// in `ct` with `ct` itself.
+ ///
+ /// This is especially important as unevaluated consts use their parents generics.
+ /// They therefore often contain unused args, making these errors far more likely.
+ ///
+ /// A good example of this is the following:
+ ///
+ /// ```compile_fail,E0308
+ /// #![feature(generic_const_exprs)]
+ ///
+ /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
+ /// todo!()
+ /// }
+ ///
+ /// fn main() {
+ /// let mut arr = Default::default();
+ /// arr = bind(arr);
+ /// }
+ /// ```
+ ///
+ /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics
+ /// of `fn bind` (meaning that its args contain `N`).
+ ///
+ /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`.
+ /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`.
+ ///
+ /// As `3 + 4` contains `N` in its args, this must not succeed.
+ ///
+ /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant.
+ #[instrument(level = "debug", skip(self))]
+ fn unify_const_variable(
+ &self,
+ target_vid: ty::ConstVid,
+ ct: ty::Const<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
+ let span =
+ self.inner.borrow_mut().const_unification_table().probe_value(target_vid).origin.span;
+ // FIXME(generic_const_exprs): Occurs check failures for unevaluated
+ // constants and generic expressions are not yet handled correctly.
+ let Generalization { value_may_be_infer: value, needs_wf: _ } = generalize::generalize(
+ self,
+ &mut CombineDelegate { infcx: self, span, param_env },
+ ct,
+ target_vid,
+ ty::Variance::Invariant,
+ )?;
+
+ self.inner.borrow_mut().const_unification_table().union_value(
+ target_vid,
+ ConstVarValue {
+ origin: ConstVariableOrigin {
+ kind: ConstVariableOriginKind::ConstInference,
+ span: DUMMY_SP,
+ },
+ val: ConstVariableValue::Known { value },
+ },
+ );
+ Ok(value)
+ }
+
+ fn unify_integral_variable(
+ &self,
+ vid_is_expected: bool,
+ vid: ty::IntVid,
+ val: ty::IntVarValue,
+ ) -> RelateResult<'tcx, Ty<'tcx>> {
+ self.inner
+ .borrow_mut()
+ .int_unification_table()
+ .unify_var_value(vid, Some(val))
+ .map_err(|e| int_unification_error(vid_is_expected, e))?;
+ match val {
+ IntType(v) => Ok(Ty::new_int(self.tcx, v)),
+ UintType(v) => Ok(Ty::new_uint(self.tcx, v)),
+ }
+ }
+
+ fn unify_float_variable(
+ &self,
+ vid_is_expected: bool,
+ vid: ty::FloatVid,
+ val: ty::FloatTy,
+ ) -> RelateResult<'tcx, Ty<'tcx>> {
+ self.inner
+ .borrow_mut()
+ .float_unification_table()
+ .unify_var_value(vid, Some(ty::FloatVarValue(val)))
+ .map_err(|e| float_unification_error(vid_is_expected, e))?;
+ Ok(Ty::new_float(self.tcx, val))
+ }
+
+ fn unify_effect_variable(
+ &self,
+ vid_is_expected: bool,
+ vid: ty::EffectVid,
+ val: EffectVarValue<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
+ self.inner
+ .borrow_mut()
+ .effect_unification_table()
+ .unify_var_value(vid, Some(val))
+ .map_err(|e| effect_unification_error(self.tcx, vid_is_expected, e))?;
+ Ok(val.as_const(self.tcx))
+ }
+}
+
+impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
+ pub fn tcx(&self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
+ }
+
+ pub fn equate<'a>(&'a mut self, a_is_expected: bool) -> Equate<'a, 'infcx, 'tcx> {
+ Equate::new(self, a_is_expected)
+ }
+
+ pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> Sub<'a, 'infcx, 'tcx> {
+ Sub::new(self, a_is_expected)
+ }
+
+ pub fn lub<'a>(&'a mut self, a_is_expected: bool) -> Lub<'a, 'infcx, 'tcx> {
+ Lub::new(self, a_is_expected)
+ }
+
+ pub fn glb<'a>(&'a mut self, a_is_expected: bool) -> Glb<'a, 'infcx, 'tcx> {
+ Glb::new(self, a_is_expected)
+ }
+
+ /// Here, `dir` is either `EqTo`, `SubtypeOf`, or `SupertypeOf`.
+ /// The idea is that we should ensure that the type `a_ty` is equal
+ /// to, a subtype of, or a supertype of (respectively) the type
+ /// to which `b_vid` is bound.
+ ///
+ /// Since `b_vid` has not yet been instantiated with a type, we
+ /// will first instantiate `b_vid` with a *generalized* version
+ /// of `a_ty`. Generalization introduces other inference
+ /// variables wherever subtyping could occur.
+ #[instrument(skip(self), level = "debug")]
+ pub fn instantiate(
+ &mut self,
+ a_ty: Ty<'tcx>,
+ ambient_variance: ty::Variance,
+ b_vid: ty::TyVid,
+ a_is_expected: bool,
+ ) -> RelateResult<'tcx, ()> {
+ // Get the actual variable that b_vid has been inferred to
+ debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown());
+
+ // Generalize type of `a_ty` appropriately depending on the
+ // direction. As an example, assume:
+ //
+ // - `a_ty == &'x ?1`, where `'x` is some free region and `?1` is an
+ // inference variable,
+ // - and `dir` == `SubtypeOf`.
+ //
+ // Then the generalized form `b_ty` would be `&'?2 ?3`, where
+ // `'?2` and `?3` are fresh region/type inference
+ // variables. (Down below, we will relate `a_ty <: b_ty`,
+ // adding constraints like `'x: '?2` and `?1 <: ?3`.)
+ let Generalization { value_may_be_infer: b_ty, needs_wf } = generalize::generalize(
+ self.infcx,
+ &mut CombineDelegate {
+ infcx: self.infcx,
+ param_env: self.param_env,
+ span: self.trace.span(),
+ },
+ a_ty,
+ b_vid,
+ ambient_variance,
+ )?;
+
+ // Constrain `b_vid` to the generalized type `b_ty`.
+ if let &ty::Infer(TyVar(b_ty_vid)) = b_ty.kind() {
+ self.infcx.inner.borrow_mut().type_variables().equate(b_vid, b_ty_vid);
+ } else {
+ self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
+ }
+
+ if needs_wf {
+ self.obligations.push(Obligation::new(
+ self.tcx(),
+ self.trace.cause.clone(),
+ self.param_env,
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ b_ty.into(),
+ ))),
+ ));
+ }
+
+ // Finally, relate `b_ty` to `a_ty`, as described in previous comment.
+ //
+ // FIXME(#16847): This code is non-ideal because all these subtype
+ // 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 b_ty.is_ty_var() {
+ // 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
+ // the alias can be normalized to something which does not
+ // mention `?0`.
+ if self.infcx.next_trait_solver() {
+ let (lhs, rhs, direction) = match ambient_variance {
+ ty::Variance::Invariant => {
+ (a_ty.into(), b_ty.into(), AliasRelationDirection::Equate)
+ }
+ ty::Variance::Covariant => {
+ (a_ty.into(), b_ty.into(), AliasRelationDirection::Subtype)
+ }
+ ty::Variance::Contravariant => {
+ (b_ty.into(), a_ty.into(), AliasRelationDirection::Subtype)
+ }
+ ty::Variance::Bivariant => unreachable!("bivariant generalization"),
+ };
+ self.obligations.push(Obligation::new(
+ self.tcx(),
+ self.trace.cause.clone(),
+ self.param_env,
+ ty::PredicateKind::AliasRelate(lhs, rhs, direction),
+ ));
+ } else {
+ match a_ty.kind() {
+ &ty::Alias(ty::AliasKind::Projection, data) => {
+ // FIXME: This does not handle subtyping correctly, we could
+ // instead create a new inference variable for `a_ty`, emitting
+ // `Projection(a_ty, a_infer)` and `a_infer <: b_ty`.
+ self.obligations.push(Obligation::new(
+ self.tcx(),
+ self.trace.cause.clone(),
+ self.param_env,
+ ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() },
+ ))
+ }
+ // The old solver only accepts projection predicates for associated types.
+ ty::Alias(
+ ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque,
+ _,
+ ) => return Err(TypeError::CyclicTy(a_ty)),
+ _ => bug!("generalizated `{a_ty:?} to infer, not an alias"),
+ }
+ }
+ } else {
+ match ambient_variance {
+ ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
+ ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
+ ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
+ ty::Contravariant,
+ ty::VarianceDiagInfo::default(),
+ a_ty,
+ b_ty,
+ ),
+ ty::Variance::Bivariant => unreachable!("bivariant generalization"),
+ }?;
+ }
+
+ Ok(())
+ }
+
+ pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
+ self.obligations.extend(obligations);
+ }
+
+ pub fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ToPredicate<'tcx>>) {
+ self.obligations.extend(obligations.into_iter().map(|to_pred| {
+ Obligation::new(self.infcx.tcx, self.trace.cause.clone(), self.param_env, to_pred)
+ }))
+ }
+}
+
+pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
+ fn param_env(&self) -> ty::ParamEnv<'tcx>;
+
+ /// Register obligations that must hold in order for this relation to hold
+ fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>);
+
+ /// Register predicates that must hold in order for this relation to hold. Uses
+ /// a default obligation cause, [`ObligationEmittingRelation::register_obligations`] should
+ /// be used if control over the obligation causes is required.
+ fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ToPredicate<'tcx>>);
+
+ /// Register an obligation that both types must be related to each other according to
+ /// the [`ty::AliasRelationDirection`] given by [`ObligationEmittingRelation::alias_relate_direction`]
+ fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
+ self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate(
+ a.into(),
+ b.into(),
+ self.alias_relate_direction(),
+ ))]);
+ }
+
+ /// Relation direction emitted for `AliasRelate` predicates, corresponding to the direction
+ /// of the relation.
+ fn alias_relate_direction(&self) -> ty::AliasRelationDirection;
+}
+
+fn int_unification_error<'tcx>(
+ a_is_expected: bool,
+ v: (ty::IntVarValue, ty::IntVarValue),
+) -> TypeError<'tcx> {
+ let (a, b) = v;
+ TypeError::IntMismatch(ExpectedFound::new(a_is_expected, a, b))
+}
+
+fn float_unification_error<'tcx>(
+ a_is_expected: bool,
+ v: (ty::FloatVarValue, ty::FloatVarValue),
+) -> TypeError<'tcx> {
+ let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
+ TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b))
+}
+
+fn effect_unification_error<'tcx>(
+ _tcx: TyCtxt<'tcx>,
+ _a_is_expected: bool,
+ (_a, _b): (EffectVarValue<'tcx>, EffectVarValue<'tcx>),
+) -> TypeError<'tcx> {
+ bug!("unexpected effect unification error")
+}