Skip to main content

rustc_type_ir/relate/
combine.rs

1use std::iter;
2
3use tracing::debug;
4
5use super::{
6    ExpectedFound, RelateResult, StructurallyRelateAliases, TypeRelation,
7    structurally_relate_consts, structurally_relate_tys,
8};
9use crate::error::TypeError;
10use crate::inherent::*;
11use crate::relate::VarianceDiagInfo;
12use crate::solve::Goal;
13use crate::visit::TypeVisitableExt as _;
14use crate::{self as ty, InferCtxtLike, Interner, TypingMode, Upcast};
15
16pub trait PredicateEmittingRelation<Infcx, I = <Infcx as InferCtxtLike>::Interner>:
17    TypeRelation<I>
18where
19    Infcx: InferCtxtLike<Interner = I>,
20    I: Interner,
21{
22    fn span(&self) -> I::Span;
23
24    fn param_env(&self) -> I::ParamEnv;
25
26    /// Whether aliases should be related structurally. This is pretty much
27    /// always `No` unless you're equating in some specific locations of the
28    /// new solver. See the comments in these use-cases for more details.
29    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
30
31    /// Register obligations that must hold in order for this relation to hold
32    fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>);
33
34    /// Register predicates that must hold in order for this relation to hold.
35    /// This uses the default `param_env` of the obligation.
36    fn register_predicates(
37        &mut self,
38        obligations: impl IntoIterator<Item: Upcast<I, I::Predicate>>,
39    );
40
41    fn ambient_variance(&self) -> ty::Variance;
42}
43
44pub fn super_combine_tys<Infcx, I, R>(
45    infcx: &Infcx,
46    relation: &mut R,
47    a: I::Ty,
48    b: I::Ty,
49) -> RelateResult<I, I::Ty>
50where
51    Infcx: InferCtxtLike<Interner = I>,
52    I: Interner,
53    R: PredicateEmittingRelation<Infcx>,
54{
55    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/relate/combine.rs:55",
                        "rustc_type_ir::relate::combine", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/relate/combine.rs"),
                        ::tracing_core::__macro_support::Option::Some(55u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_type_ir::relate::combine"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("super_combine_tys::<{0}>({1:?}, {2:?})",
                                                    std::any::type_name::<R>(), a, b) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("super_combine_tys::<{}>({:?}, {:?})", std::any::type_name::<R>(), a, b);
56    if true {
    if !!a.has_escaping_bound_vars() {
        ::core::panicking::panic("assertion failed: !a.has_escaping_bound_vars()")
    };
};debug_assert!(!a.has_escaping_bound_vars());
57    if true {
    if !!b.has_escaping_bound_vars() {
        ::core::panicking::panic("assertion failed: !b.has_escaping_bound_vars()")
    };
};debug_assert!(!b.has_escaping_bound_vars());
58
59    match (a.kind(), b.kind()) {
60        (ty::Error(e), _) | (_, ty::Error(e)) => {
61            infcx.set_tainted_by_errors(e);
62            return Ok(Ty::new_error(infcx.cx(), e));
63        }
64
65        // Relate integral variables to other types
66        (ty::Infer(ty::IntVar(a_id)), ty::Infer(ty::IntVar(b_id))) => {
67            infcx.equate_int_vids_raw(a_id, b_id);
68            Ok(a)
69        }
70        (ty::Infer(ty::IntVar(v_id)), ty::Int(v)) => {
71            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::IntType(v));
72            Ok(b)
73        }
74        (ty::Int(v), ty::Infer(ty::IntVar(v_id))) => {
75            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::IntType(v));
76            Ok(a)
77        }
78        (ty::Infer(ty::IntVar(v_id)), ty::Uint(v)) => {
79            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::UintType(v));
80            Ok(b)
81        }
82        (ty::Uint(v), ty::Infer(ty::IntVar(v_id))) => {
83            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::UintType(v));
84            Ok(a)
85        }
86
87        // Relate floating-point variables to other types
88        (ty::Infer(ty::FloatVar(a_id)), ty::Infer(ty::FloatVar(b_id))) => {
89            infcx.equate_float_vids_raw(a_id, b_id);
90            Ok(a)
91        }
92        (ty::Infer(ty::FloatVar(v_id)), ty::Float(v)) => {
93            infcx.instantiate_float_var_raw(v_id, ty::FloatVarValue::Known(v));
94            Ok(b)
95        }
96        (ty::Float(v), ty::Infer(ty::FloatVar(v_id))) => {
97            infcx.instantiate_float_var_raw(v_id, ty::FloatVarValue::Known(v));
98            Ok(a)
99        }
100
101        // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm.
102        (ty::Alias(..), ty::Infer(ty::TyVar(_))) | (ty::Infer(ty::TyVar(_)), ty::Alias(..))
103            if infcx.next_trait_solver() =>
104        {
105            {
    ::core::panicking::panic_fmt(format_args!("We do not expect to encounter `TyVar` this late in combine -- they should have been handled earlier"));
}panic!(
106                "We do not expect to encounter `TyVar` this late in combine \
107                    -- they should have been handled earlier"
108            )
109        }
110        (_, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)))
111        | (ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), _)
112            if infcx.next_trait_solver() =>
113        {
114            {
    ::core::panicking::panic_fmt(format_args!("We do not expect to encounter `Fresh` variables in the new solver"));
}panic!("We do not expect to encounter `Fresh` variables in the new solver")
115        }
116
117        (ty::Alias(ty::IsRigid::No, alias), _) | (_, ty::Alias(ty::IsRigid::No, alias))
118            if infcx.next_trait_solver()
119                && let StructurallyRelateAliases::No = relation.structurally_relate_aliases() =>
120        {
121            // If both sides are aliases, arbitrarily do the LHS first
122            let terms_are_inverted = !#[allow(non_exhaustive_omitted_patterns)] match a.kind() {
    ty::Alias(ty::IsRigid::No, _) => true,
    _ => false,
}matches!(a.kind(), ty::Alias(ty::IsRigid::No, _));
123            let other = if terms_are_inverted { a } else { b };
124            match (relation.ambient_variance(), terms_are_inverted) {
125                (ty::Invariant, _) => relation.register_predicates([ty::ProjectionPredicate {
126                    projection_term: alias.into(),
127                    term: other.into(),
128                }]),
129                (ty::Covariant, false) | (ty::Contravariant, true) => {
130                    // Generate a new var to represent `alias <: other`
131                    // with `alias == ?A && ?A <: other`
132                    let new_var = infcx.next_ty_infer();
133                    relation.register_predicates([
134                        ty::PredicateKind::Clause(ty::ClauseKind::Projection(
135                            ty::ProjectionPredicate {
136                                projection_term: alias.into(),
137                                term: new_var.into(),
138                            },
139                        )),
140                        ty::PredicateKind::Subtype(ty::SubtypePredicate {
141                            a_is_expected: !terms_are_inverted,
142                            a: new_var,
143                            b: other,
144                        }),
145                    ]);
146                }
147                (ty::Contravariant, false) | (ty::Covariant, true) => {
148                    // a :> b is b <: a
149                    let new_var = infcx.next_ty_infer();
150                    relation.register_predicates([
151                        ty::PredicateKind::Clause(ty::ClauseKind::Projection(
152                            ty::ProjectionPredicate {
153                                projection_term: alias.into(),
154                                term: new_var.into(),
155                            },
156                        )),
157                        ty::PredicateKind::Subtype(ty::SubtypePredicate {
158                            a_is_expected: terms_are_inverted,
159                            a: other,
160                            b: new_var,
161                        }),
162                    ]);
163                }
164                (ty::Bivariant, _) => {
165                    {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("cannot handle bivariant aliases in register_projection_with_variance")));
}unreachable!(
166                        "cannot handle bivariant aliases in register_projection_with_variance"
167                    )
168                }
169            }
170            Ok(a)
171        }
172
173        // All other cases of inference are errors
174        (ty::Infer(_), _) | (_, ty::Infer(_)) => Err(TypeError::Sorts(ExpectedFound::new(a, b))),
175
176        (ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }), _)
177        | (_, ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }))
178            if !infcx.next_trait_solver() =>
179        {
180            match infcx.typing_mode_raw().assert_not_erased() {
181                // During coherence, opaque types should be treated as *possibly*
182                // equal to any other type. This is an
183                // extremely heavy hammer, but can be relaxed in a forwards-compatible
184                // way later.
185                TypingMode::Coherence => {
186                    relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]);
187                    Ok(a)
188                }
189                TypingMode::Typeck { .. }
190                | TypingMode::PostTypeckUntilBorrowck { .. }
191                | TypingMode::PostBorrowck { .. }
192                | TypingMode::PostAnalysis
193                | TypingMode::Codegen => structurally_relate_tys(relation, a, b),
194            }
195        }
196
197        _ => structurally_relate_tys(relation, a, b),
198    }
199}
200
201pub fn super_combine_consts<Infcx, I, R>(
202    infcx: &Infcx,
203    relation: &mut R,
204    a: I::Const,
205    b: I::Const,
206) -> RelateResult<I, I::Const>
207where
208    Infcx: InferCtxtLike<Interner = I>,
209    I: Interner,
210    R: PredicateEmittingRelation<Infcx>,
211{
212    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_type_ir/src/relate/combine.rs:212",
                        "rustc_type_ir::relate::combine", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_type_ir/src/relate/combine.rs"),
                        ::tracing_core::__macro_support::Option::Some(212u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_type_ir::relate::combine"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("super_combine_consts::<{0}>({1:?}, {2:?})",
                                                    std::any::type_name::<R>(), a, b) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("super_combine_consts::<{}>({:?}, {:?})", std::any::type_name::<R>(), a, b);
213    if true {
    if !!a.has_escaping_bound_vars() {
        ::core::panicking::panic("assertion failed: !a.has_escaping_bound_vars()")
    };
};debug_assert!(!a.has_escaping_bound_vars());
214    if true {
    if !!b.has_escaping_bound_vars() {
        ::core::panicking::panic("assertion failed: !b.has_escaping_bound_vars()")
    };
};debug_assert!(!b.has_escaping_bound_vars());
215
216    if a == b {
217        return Ok(a);
218    }
219
220    let a = infcx.shallow_resolve_const(a);
221    let b = infcx.shallow_resolve_const(b);
222
223    match (a.kind(), b.kind()) {
224        (
225            ty::ConstKind::Infer(ty::InferConst::Var(a_vid)),
226            ty::ConstKind::Infer(ty::InferConst::Var(b_vid)),
227        ) => {
228            infcx.equate_const_vids_raw(a_vid, b_vid);
229            Ok(a)
230        }
231
232        // All other cases of inference with other variables are errors.
233        (ty::ConstKind::Infer(ty::InferConst::Var(_)), ty::ConstKind::Infer(_))
234        | (ty::ConstKind::Infer(_), ty::ConstKind::Infer(ty::InferConst::Var(_))) => {
235            {
    ::core::panicking::panic_fmt(format_args!("tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {0:?} and {1:?}",
            a, b));
}panic!(
236                "tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}"
237            )
238        }
239
240        (ty::ConstKind::Infer(ty::InferConst::Var(vid)), _) => {
241            infcx.instantiate_const_var_raw(relation, true, vid, b)?;
242            Ok(b)
243        }
244
245        (_, ty::ConstKind::Infer(ty::InferConst::Var(vid))) => {
246            infcx.instantiate_const_var_raw(relation, false, vid, a)?;
247            Ok(a)
248        }
249
250        (ty::ConstKind::Alias(ty::IsRigid::No, alias), _)
251        | (_, ty::ConstKind::Alias(ty::IsRigid::No, alias))
252            if (infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver())
253                && let StructurallyRelateAliases::No = relation.structurally_relate_aliases() =>
254        {
255            if infcx.next_trait_solver() {
256                let other = if #[allow(non_exhaustive_omitted_patterns)] match a.kind() {
    ty::ConstKind::Alias(..) => true,
    _ => false,
}matches!(a.kind(), ty::ConstKind::Alias(..)) { b } else { a };
257                relation.register_predicates([ty::ProjectionPredicate {
258                    projection_term: alias.into(),
259                    term: other.into(),
260                }])
261            } else {
262                relation.register_predicates([ty::PredicateKind::ConstEquate(a, b)]);
263            }
264
265            Ok(b)
266        }
267
268        _ => structurally_relate_consts(relation, a, b),
269    }
270}
271
272pub fn combine_ty_args<Infcx, I, R>(
273    infcx: &Infcx,
274    relation: &mut R,
275    a_ty: I::Ty,
276    b_ty: I::Ty,
277    variances: I::VariancesOf,
278    a_args: I::GenericArgs,
279    b_args: I::GenericArgs,
280    mk: impl FnOnce(I::GenericArgs) -> I::Ty,
281) -> RelateResult<I, I::Ty>
282where
283    Infcx: InferCtxtLike<Interner = I>,
284    I: Interner,
285    R: PredicateEmittingRelation<Infcx>,
286{
287    let cx = infcx.cx();
288    let mut has_unconstrained_bivariant_arg = false;
289    let args = iter::zip(a_args.iter(), b_args.iter()).enumerate().map(|(i, (a, b))| {
290        let variance = variances.get(i).unwrap();
291        let variance_info = match variance {
292            ty::Invariant => {
293                VarianceDiagInfo::Invariant { ty: a_ty, param_index: i.try_into().unwrap() }
294            }
295            ty::Covariant | ty::Contravariant => VarianceDiagInfo::default(),
296            ty::Bivariant => {
297                let has_non_region_infer = |arg: I::GenericArg| {
298                    arg.has_non_region_infer()
299                        && infcx.resolve_vars_if_possible(arg).has_non_region_infer()
300                };
301                if has_non_region_infer(a) || has_non_region_infer(b) {
302                    has_unconstrained_bivariant_arg = true;
303                }
304                VarianceDiagInfo::default()
305            }
306        };
307        relation.relate_with_variance(variance, variance_info, a, b)
308    });
309    let args = cx.mk_args_from_iter(args)?;
310
311    // In general, we do not check whether all types which occur during
312    // type checking are well-formed. We only check wf of user-provided types
313    // and when actually using a type, e.g. for method calls.
314    //
315    // This means that when subtyping, we may end up with unconstrained
316    // inference variables if a generalized type has bivariant parameters.
317    // A parameter may only be bivariant if it is constrained by a projection
318    // bound in a where-clause. As an example, imagine a type:
319    //
320    //     struct Foo<A, B> where A: Iterator<Item = B> {
321    //         data: A
322    //     }
323    //
324    // here, `A` will be covariant, but `B` is unconstrained. However, whatever it is,
325    // for `Foo` to be WF, it must be equal to `A::Item`.
326    //
327    // If we have an input `Foo<?A, ?B>`, then after generalization we will wind
328    // up with a type like `Foo<?C, ?D>`. When we enforce `Foo<?A, ?B> <: Foo<?C, ?D>`,
329    // we will wind up with the requirement that `?A <: ?C`, but no particular
330    // relationship between `?B` and `?D` (after all, these types may be completely
331    // different). If we do nothing else, this may mean that `?D` goes unconstrained
332    // (as in #41677). To avoid this we emit a `WellFormed` when relating types with
333    // bivariant arguments.
334    if has_unconstrained_bivariant_arg {
335        relation.register_predicates([
336            ty::ClauseKind::WellFormed(a_ty.into()),
337            ty::ClauseKind::WellFormed(b_ty.into()),
338        ]);
339    }
340
341    if a_args == args { Ok(a_ty) } else { Ok(mk(args)) }
342}