Skip to main content

flux_infer/
infer.rs

1use std::{cell::RefCell, fmt, iter};
2
3use flux_common::{bug, dbg, tracked_span_assert_eq, tracked_span_bug, tracked_span_dbg_assert_eq};
4use flux_config::{self as config, InferOpts, OverflowMode, RawDerefMode};
5use flux_macros::{TypeFoldable, TypeVisitable};
6use flux_middle::{
7    FixpointQueryKind, PanicSpec,
8    def_id::MaybeExternId,
9    global_env::GlobalEnv,
10    metrics::{self, Metric},
11    queries::{QueryErr, QueryResult},
12    query_bug,
13    rty::{
14        self, AliasKind, AliasTy, BaseTy, Binder, BoundReftKind, BoundVariableKinds,
15        CoroutineObligPredicate, Ctor, ESpan, EVid, EarlyBinder, Expr, ExprKind, FieldProj,
16        GenericArg, HoleKind, InferMode, Lambda, List, Loc, Mutability, Name, NameProvenance, Path,
17        PolyVariant, PtrKind, RefineArgs, RefineArgsExt, Region, Sort, Ty, TyCtor, TyKind, Var,
18        canonicalize::{Hoister, HoisterDelegate},
19        fold::TypeFoldable,
20    },
21};
22use itertools::{Itertools, izip};
23use rustc_hir::def_id::{DefId, LocalDefId};
24use rustc_macros::extension;
25use rustc_middle::{
26    mir::BasicBlock,
27    ty::{TyCtxt, Variance},
28};
29use rustc_span::{Span, Symbol};
30use rustc_type_ir::Variance::Invariant;
31
32use crate::{
33    evars::{EVarState, EVarStore},
34    fixpoint_encoding::{
35        Answer, Backend, FixQueryCache, FixpointCtxt, KVarEncoding, KVarGen, lean_task_key,
36        record_lean_task,
37    },
38    lean_encoding::{hyperlink_proof, log_proof},
39    projections::NormalizeExt as _,
40    refine_tree::{Cursor, Marker, RefineTree, Scope},
41};
42
43pub type InferResult<T = ()> = std::result::Result<T, InferErr>;
44
45#[derive(PartialEq, Eq, Clone, Copy, Hash)]
46pub struct Tag {
47    pub reason: ConstrReason,
48    pub src_span: Span,
49    pub dst_span: Option<ESpan>,
50}
51
52impl Tag {
53    pub fn new(reason: ConstrReason, span: Span) -> Self {
54        Self { reason, src_span: span, dst_span: None }
55    }
56
57    pub fn with_dst(self, dst_span: Option<ESpan>) -> Self {
58        Self { dst_span, ..self }
59    }
60}
61
62#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
63pub enum SubtypeReason {
64    Input,
65    Output,
66    Requires,
67    Ensures,
68}
69
70#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
71pub enum ConstrReason {
72    Call,
73    Assign,
74    Ret,
75    Fold,
76    FoldLocal,
77    Predicate,
78    Assert(&'static str),
79    Div,
80    Rem,
81    Goto(BasicBlock),
82    Overflow,
83    Underflow,
84    Subtype(SubtypeReason),
85    NoPanic(DefId, PanicSpec),
86    Other,
87}
88
89pub struct InferCtxtRoot<'genv, 'tcx> {
90    pub genv: GlobalEnv<'genv, 'tcx>,
91    inner: RefCell<InferCtxtInner>,
92    refine_tree: RefineTree,
93    opts: InferOpts,
94}
95
96pub struct InferCtxtRootBuilder<'a, 'genv, 'tcx> {
97    genv: GlobalEnv<'genv, 'tcx>,
98    opts: InferOpts,
99    params: Vec<(Var, Sort)>,
100    infcx: &'a rustc_infer::infer::InferCtxt<'tcx>,
101    dummy_kvars: bool,
102}
103
104#[extension(pub trait GlobalEnvExt<'genv, 'tcx>)]
105impl<'genv, 'tcx> GlobalEnv<'genv, 'tcx> {
106    fn infcx_root<'a>(
107        self,
108        infcx: &'a rustc_infer::infer::InferCtxt<'tcx>,
109        opts: InferOpts,
110    ) -> InferCtxtRootBuilder<'a, 'genv, 'tcx> {
111        InferCtxtRootBuilder { genv: self, infcx, params: vec![], opts, dummy_kvars: false }
112    }
113}
114
115impl<'genv, 'tcx> InferCtxtRootBuilder<'_, 'genv, 'tcx> {
116    pub fn with_dummy_kvars(mut self) -> Self {
117        self.dummy_kvars = true;
118        self
119    }
120
121    pub fn with_const_generics(mut self, def_id: DefId) -> QueryResult<Self> {
122        self.params.extend(
123            self.genv
124                .generics_of(def_id)?
125                .const_params(self.genv)?
126                .into_iter()
127                .map(|(pcst, sort)| (Var::ConstGeneric(pcst), sort)),
128        );
129        Ok(self)
130    }
131
132    pub fn with_refinement_generics(
133        mut self,
134        def_id: DefId,
135        args: &[GenericArg],
136    ) -> QueryResult<Self> {
137        for (index, param) in self
138            .genv
139            .refinement_generics_of(def_id)?
140            .iter_own_params()
141            .enumerate()
142        {
143            let param = param.instantiate(self.genv.tcx(), args, &[]);
144            let sort = param
145                .sort
146                .deeply_normalize_sorts(def_id, self.genv, self.infcx)?;
147
148            let var =
149                Var::EarlyParam(rty::EarlyReftParam { index: index as u32, name: param.name });
150            self.params.push((var, sort));
151        }
152        Ok(self)
153    }
154
155    pub fn identity_for_item(mut self, def_id: DefId) -> QueryResult<Self> {
156        self = self.with_const_generics(def_id)?;
157        let offset = self.params.len();
158        self.genv.refinement_generics_of(def_id)?.fill_item(
159            self.genv,
160            &mut self.params,
161            &mut |param, index| {
162                let index = (index - offset) as u32;
163                let param = param.instantiate_identity();
164                let sort = param
165                    .sort
166                    .deeply_normalize_sorts(def_id, self.genv, self.infcx)?;
167
168                let var = Var::EarlyParam(rty::EarlyReftParam { index, name: param.name });
169                Ok((var, sort))
170            },
171        )?;
172        Ok(self)
173    }
174
175    pub fn build(self) -> QueryResult<InferCtxtRoot<'genv, 'tcx>> {
176        Ok(InferCtxtRoot {
177            genv: self.genv,
178            inner: RefCell::new(InferCtxtInner::new(self.dummy_kvars)),
179            refine_tree: RefineTree::new(self.params),
180            opts: self.opts,
181        })
182    }
183}
184
185impl<'genv, 'tcx> InferCtxtRoot<'genv, 'tcx> {
186    pub fn infcx<'a>(
187        &'a mut self,
188        def_id: DefId,
189        region_infcx: &'a rustc_infer::infer::InferCtxt<'tcx>,
190    ) -> InferCtxt<'a, 'genv, 'tcx> {
191        InferCtxt {
192            genv: self.genv,
193            region_infcx,
194            def_id,
195            cursor: self.refine_tree.cursor_at_root(),
196            inner: &self.inner,
197            check_overflow: self.opts.check_overflow,
198            allow_raw_deref: self.opts.allow_raw_deref,
199        }
200    }
201
202    pub fn fresh_kvar_in_scope(
203        &self,
204        binders: &[BoundVariableKinds],
205        scope: &Scope,
206        encoding: KVarEncoding,
207    ) -> Expr {
208        let inner = &mut *self.inner.borrow_mut();
209        inner.kvars.fresh(binders, scope.iter(), encoding)
210    }
211
212    pub fn execute_lean_query(
213        self,
214        cache: &mut FixQueryCache,
215        def_id: MaybeExternId,
216    ) -> QueryResult {
217        let inner = self.inner.into_inner();
218        let kvars = inner.kvars;
219        let evars = inner.evars;
220        let mut refine_tree = self.refine_tree;
221        refine_tree.replace_evars(&evars).unwrap();
222        refine_tree.simplify(self.genv);
223
224        let solver = match self.opts.solver {
225            flux_config::SmtSolver::Z3 => liquid_fixpoint::SmtSolver::Z3,
226            flux_config::SmtSolver::CVC5 => liquid_fixpoint::SmtSolver::CVC5,
227        };
228        let mut fcx = FixpointCtxt::new(self.genv, def_id, kvars, Backend::Lean);
229        let cstr = refine_tree.to_fixpoint(&mut fcx)?;
230        let (task, _) = fcx.create_task(def_id, cstr, self.opts.scrape_quals, solver)?;
231
232        log_proof(self.genv, def_id)?;
233        // Skip re-generation if task is already cached (same hash → same lean files on disk).
234        let key = lean_task_key(self.genv.tcx(), def_id.resolved_id());
235        let hash = task.hash_with_default();
236        let cached = config::is_cache_enabled() && cache.lookup(&key, hash).is_some();
237        if !cached {
238            fcx.generate_lean_files(def_id, task)?;
239            if config::is_cache_enabled() {
240                record_lean_task(cache, key, hash);
241            }
242        }
243        // After generation, so that the proof file exists the first time around.
244        hyperlink_proof(self.genv, def_id);
245        Ok(())
246    }
247
248    pub fn execute_fixpoint_query(
249        self,
250        cache: &mut FixQueryCache,
251        def_id: MaybeExternId,
252        kind: FixpointQueryKind,
253    ) -> QueryResult<Answer<Tag>> {
254        let inner = self.inner.into_inner();
255        let kvars = inner.kvars;
256        let evars = inner.evars;
257
258        let ext = kind.ext();
259
260        let mut refine_tree = self.refine_tree;
261
262        refine_tree.replace_evars(&evars).unwrap();
263
264        if config::dump_constraint() {
265            dbg::dump_item_info(self.genv.tcx(), def_id.resolved_id(), ext, &refine_tree).unwrap();
266        }
267        refine_tree.simplify(self.genv);
268        if config::dump_constraint() {
269            let simp_ext = format!("simp.{ext}");
270            dbg::dump_item_info(self.genv.tcx(), def_id.resolved_id(), simp_ext, &refine_tree)
271                .unwrap();
272        }
273
274        let backend = match self.opts.solver {
275            flux_config::SmtSolver::Z3 => liquid_fixpoint::SmtSolver::Z3,
276            flux_config::SmtSolver::CVC5 => liquid_fixpoint::SmtSolver::CVC5,
277        };
278
279        let mut fcx = FixpointCtxt::new(self.genv, def_id, kvars, Backend::Fixpoint);
280        let cstr = refine_tree.to_fixpoint(&mut fcx)?;
281
282        // skip checking trivial constraints
283        let count = cstr.concrete_head_count();
284        metrics::incr_metric(Metric::CsTotal, count as u32);
285        if count == 0 {
286            metrics::incr_metric_if(kind.is_body(), Metric::FnTrivial);
287            return Ok(Answer::trivial());
288        }
289
290        let (task, suggestion_ctx) =
291            fcx.create_task(def_id, cstr, self.opts.scrape_quals, backend)?;
292        let result = fcx.run_task(cache, def_id, kind, &task)?;
293        Ok(fcx.result_to_answer(result, suggestion_ctx))
294    }
295
296    pub fn split(self) -> (RefineTree, KVarGen) {
297        (self.refine_tree, self.inner.into_inner().kvars)
298    }
299}
300
301pub struct InferCtxt<'infcx, 'genv, 'tcx> {
302    pub genv: GlobalEnv<'genv, 'tcx>,
303    pub region_infcx: &'infcx rustc_infer::infer::InferCtxt<'tcx>,
304    pub def_id: DefId,
305    pub check_overflow: OverflowMode,
306    pub allow_raw_deref: flux_config::RawDerefMode,
307    cursor: Cursor<'infcx>,
308    inner: &'infcx RefCell<InferCtxtInner>,
309}
310
311struct InferCtxtInner {
312    kvars: KVarGen,
313    evars: EVarStore,
314}
315
316impl InferCtxtInner {
317    fn new(dummy_kvars: bool) -> Self {
318        Self { kvars: KVarGen::new(dummy_kvars), evars: Default::default() }
319    }
320}
321
322impl<'infcx, 'genv, 'tcx> InferCtxt<'infcx, 'genv, 'tcx> {
323    pub fn at(&mut self, span: Span) -> InferCtxtAt<'_, 'infcx, 'genv, 'tcx> {
324        InferCtxtAt { infcx: self, span }
325    }
326
327    pub fn instantiate_refine_args(
328        &mut self,
329        callee_def_id: DefId,
330        args: &[rty::GenericArg],
331    ) -> InferResult<List<Expr>> {
332        Ok(RefineArgs::for_item(self.genv, callee_def_id, |param, _| {
333            let param = param.instantiate(self.genv.tcx(), args, &[]);
334            Ok(self.fresh_infer_var(&param.sort, param.mode))
335        })?)
336    }
337
338    pub fn instantiate_generic_args(&mut self, args: &[GenericArg]) -> Vec<GenericArg> {
339        args.iter()
340            .map(|a| a.replace_holes(|binders, kind| self.fresh_infer_var_for_hole(binders, kind)))
341            .collect_vec()
342    }
343
344    pub fn fresh_infer_var(&self, sort: &Sort, mode: InferMode) -> Expr {
345        match mode {
346            InferMode::KVar => {
347                let fsort = sort.expect_func().expect_mono();
348                let vars = fsort.inputs().iter().cloned().map_into().collect();
349                let kvar = self.fresh_kvar(&[vars], KVarEncoding::Single);
350                Expr::abs(Lambda::bind_with_fsort(kvar, fsort))
351            }
352            InferMode::EVar => self.fresh_evar(),
353        }
354    }
355
356    pub fn fresh_infer_var_for_hole(
357        &mut self,
358        binders: &[BoundVariableKinds],
359        kind: HoleKind,
360    ) -> Expr {
361        match kind {
362            HoleKind::Pred => self.fresh_kvar(binders, KVarEncoding::Conj),
363            HoleKind::Expr(_) => {
364                // We only use expression holes to infer early param arguments for opaque types
365                // at function calls. These should be well-scoped in the current scope, so we ignore
366                // the extra `binders` around the hole.
367                self.fresh_evar()
368            }
369        }
370    }
371
372    /// Generate a fresh kvar in the _given_ [`Scope`] (similar method in [`InferCtxtRoot`]).
373    pub fn fresh_kvar_in_scope(
374        &self,
375        binders: &[BoundVariableKinds],
376        scope: &Scope,
377        encoding: KVarEncoding,
378    ) -> Expr {
379        let inner = &mut *self.inner.borrow_mut();
380        inner.kvars.fresh(binders, scope.iter(), encoding)
381    }
382
383    /// Generate a fresh kvar in the current scope. See [`KVarGen::fresh`].
384    pub fn fresh_kvar(&self, binders: &[BoundVariableKinds], encoding: KVarEncoding) -> Expr {
385        let inner = &mut *self.inner.borrow_mut();
386        inner.kvars.fresh(binders, self.cursor.vars(), encoding)
387    }
388
389    fn fresh_evar(&self) -> Expr {
390        let evars = &mut self.inner.borrow_mut().evars;
391        Expr::evar(evars.fresh(self.cursor.marker()))
392    }
393
394    pub fn unify_exprs(&self, a: &Expr, b: &Expr) {
395        if a.has_evars() {
396            return;
397        }
398        let evars = &mut self.inner.borrow_mut().evars;
399        if let ExprKind::Var(Var::EVar(evid)) = b.kind()
400            && let EVarState::Unsolved(marker) = evars.get(*evid)
401            && !marker.has_free_vars(a)
402        {
403            evars.solve(*evid, a.clone());
404        }
405    }
406
407    fn enter_exists<T, U>(
408        &mut self,
409        t: &Binder<T>,
410        f: impl FnOnce(&mut InferCtxt<'_, 'genv, 'tcx>, T) -> U,
411    ) -> U
412    where
413        T: TypeFoldable,
414    {
415        self.ensure_resolved_evars(|infcx| {
416            let t = t.replace_bound_refts_with(|sort, mode, _| infcx.fresh_infer_var(sort, mode));
417            Ok(f(infcx, t))
418        })
419        .unwrap()
420    }
421
422    /// Used in conjunction with [`InferCtxt::pop_evar_scope`] to ensure evars are solved at the end
423    /// of some scope, for example, to ensure all evars generated during a function call are solved
424    /// after checking argument subtyping. These functions can be used in a stack-like fashion to
425    /// create nested scopes.
426    pub fn push_evar_scope(&mut self) {
427        self.inner.borrow_mut().evars.push_scope();
428    }
429
430    /// Pop a scope and check all evars have been solved. This only check evars generated from the
431    /// last call to [`InferCtxt::push_evar_scope`].
432    pub fn pop_evar_scope(&mut self) -> InferResult {
433        self.inner
434            .borrow_mut()
435            .evars
436            .pop_scope()
437            .map_err(InferErr::UnsolvedEvar)
438    }
439
440    /// Convenience method pairing [`InferCtxt::push_evar_scope`] and [`InferCtxt::pop_evar_scope`].
441    pub fn ensure_resolved_evars<R>(
442        &mut self,
443        f: impl FnOnce(&mut Self) -> InferResult<R>,
444    ) -> InferResult<R> {
445        self.push_evar_scope();
446        let r = f(self)?;
447        self.pop_evar_scope()?;
448        Ok(r)
449    }
450
451    pub fn fully_resolve_evars<T: TypeFoldable>(&self, t: &T) -> T {
452        self.inner.borrow().evars.replace_evars(t).unwrap()
453    }
454
455    pub fn tcx(&self) -> TyCtxt<'tcx> {
456        self.genv.tcx()
457    }
458
459    pub fn cursor(&self) -> &Cursor<'infcx> {
460        &self.cursor
461    }
462
463    pub fn allow_raw_deref(&self) -> bool {
464        matches!(self.allow_raw_deref, RawDerefMode::Ok)
465    }
466}
467
468/// Methods that interact with the underlying [`Cursor`]
469impl<'infcx, 'genv, 'tcx> InferCtxt<'infcx, 'genv, 'tcx> {
470    pub fn change_item<'a>(
471        &'a mut self,
472        def_id: LocalDefId,
473        region_infcx: &'a rustc_infer::infer::InferCtxt<'tcx>,
474    ) -> InferCtxt<'a, 'genv, 'tcx> {
475        InferCtxt {
476            def_id: def_id.to_def_id(),
477            cursor: self.cursor.branch(),
478            region_infcx,
479            ..*self
480        }
481    }
482
483    pub fn move_to(&mut self, marker: &Marker, clear_children: bool) -> InferCtxt<'_, 'genv, 'tcx> {
484        InferCtxt {
485            cursor: self
486                .cursor
487                .move_to(marker, clear_children)
488                .unwrap_or_else(|| tracked_span_bug!()),
489            ..*self
490        }
491    }
492
493    pub fn branch(&mut self) -> InferCtxt<'_, 'genv, 'tcx> {
494        InferCtxt { cursor: self.cursor.branch(), ..*self }
495    }
496
497    fn define_var(&mut self, sort: &Sort, provenance: NameProvenance) -> Name {
498        self.cursor.define_var(sort, provenance)
499    }
500
501    pub fn define_bound_reft_var(&mut self, sort: &Sort, kind: BoundReftKind) -> Name {
502        self.define_var(sort, NameProvenance::UnfoldBoundReft(kind))
503    }
504
505    pub fn define_unknown_var(&mut self, sort: &Sort) -> Name {
506        self.cursor.define_var(sort, NameProvenance::Unknown)
507    }
508
509    pub fn check_pred(&mut self, pred: impl Into<Expr>, tag: Tag) {
510        self.cursor.check_pred(pred, tag);
511    }
512
513    pub fn assume_pred(&mut self, pred: impl Into<Expr>) {
514        self.cursor.assume_pred(pred);
515    }
516
517    pub fn unpack(&mut self, ty: &Ty) -> Ty {
518        self.hoister(false).hoist(ty)
519    }
520
521    pub fn unpack_at_name(&mut self, name: Option<Symbol>, ty: &Ty) -> Ty {
522        let mut hoister = self.hoister(false);
523        hoister.delegate.name = name;
524        hoister.hoist(ty)
525    }
526
527    pub fn marker(&self) -> Marker {
528        self.cursor.marker()
529    }
530
531    pub fn hoister(
532        &mut self,
533        assume_invariants: bool,
534    ) -> Hoister<Unpacker<'_, 'infcx, 'genv, 'tcx>> {
535        Hoister::with_delegate(Unpacker { infcx: self, assume_invariants, name: None })
536            .transparent()
537    }
538
539    pub fn assume_invariants(&mut self, ty: &Ty) {
540        self.cursor
541            .assume_invariants(self.genv, ty, self.check_overflow);
542    }
543
544    fn check_impl(&mut self, pred1: impl Into<Expr>, pred2: impl Into<Expr>, tag: Tag) {
545        self.cursor.check_impl(pred1, pred2, tag);
546    }
547}
548
549pub struct Unpacker<'a, 'infcx, 'genv, 'tcx> {
550    infcx: &'a mut InferCtxt<'infcx, 'genv, 'tcx>,
551    assume_invariants: bool,
552    name: Option<Symbol>,
553}
554
555impl HoisterDelegate for Unpacker<'_, '_, '_, '_> {
556    fn hoist_exists(&mut self, ty_ctor: &TyCtor) -> Ty {
557        let ty = ty_ctor.replace_bound_refts_with(|sort, _, kind| {
558            let kind = if let Some(name) = self.name { BoundReftKind::Named(name) } else { kind };
559            Expr::fvar(self.infcx.define_bound_reft_var(sort, kind))
560        });
561        if self.assume_invariants {
562            self.infcx.assume_invariants(&ty);
563        }
564        ty
565    }
566
567    fn hoist_constr(&mut self, pred: Expr) {
568        self.infcx.assume_pred(pred);
569    }
570}
571
572impl std::fmt::Debug for InferCtxt<'_, '_, '_> {
573    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
574        std::fmt::Debug::fmt(&self.cursor, f)
575    }
576}
577
578#[derive(Debug)]
579pub struct InferCtxtAt<'a, 'infcx, 'genv, 'tcx> {
580    pub infcx: &'a mut InferCtxt<'infcx, 'genv, 'tcx>,
581    pub span: Span,
582}
583
584impl<'genv, 'tcx> InferCtxtAt<'_, '_, 'genv, 'tcx> {
585    fn tag(&self, reason: ConstrReason) -> Tag {
586        Tag::new(reason, self.span)
587    }
588
589    pub fn check_pred(&mut self, pred: impl Into<Expr>, reason: ConstrReason) {
590        let tag = self.tag(reason);
591        self.infcx.check_pred(pred, tag);
592    }
593
594    pub fn check_non_closure_clauses(
595        &mut self,
596        clauses: &[rty::Clause],
597        reason: ConstrReason,
598    ) -> InferResult {
599        for clause in clauses {
600            if let rty::ClauseKind::Projection(projection_pred) = clause.kind_skipping_binder() {
601                let impl_elem = BaseTy::Alias(projection_pred.projection_term.to_alias_ty())
602                    .to_ty()
603                    .deeply_normalize(self)?;
604                let term = projection_pred.term.to_ty().deeply_normalize(self)?;
605
606                // TODO: does this really need to be invariant? https://github.com/flux-rs/flux/pull/478#issuecomment-1654035374
607                self.subtyping(&impl_elem, &term, reason)?;
608                self.subtyping(&term, &impl_elem, reason)?;
609            }
610        }
611        Ok(())
612    }
613
614    /// Relate types via subtyping. This is the same as [`InferCtxtAt::subtyping`] except that we
615    /// also require a [`LocEnv`] to handle pointers and strong references
616    pub fn subtyping_with_env(
617        &mut self,
618        env: &mut impl LocEnv,
619        a: &Ty,
620        b: &Ty,
621        reason: ConstrReason,
622    ) -> InferResult<Vec<Binder<rty::CoroutineObligPredicate>>> {
623        let mut sub = Sub::new(env, reason, self.span);
624        sub.tys(self.infcx, a, b)?;
625        Ok(sub.obligations)
626    }
627
628    /// Relate types via subtyping and returns coroutine obligations. This doesn't handle subtyping
629    /// when strong references are involved.
630    ///
631    /// See comment for [`Sub::obligations`].
632    pub fn subtyping(
633        &mut self,
634        a: &Ty,
635        b: &Ty,
636        reason: ConstrReason,
637    ) -> InferResult<Vec<Binder<rty::CoroutineObligPredicate>>> {
638        let mut env = DummyEnv;
639        let mut sub = Sub::new(&mut env, reason, self.span);
640        sub.tys(self.infcx, a, b)?;
641        Ok(sub.obligations)
642    }
643
644    pub fn subtyping_generic_args(
645        &mut self,
646        variance: Variance,
647        a: &GenericArg,
648        b: &GenericArg,
649        reason: ConstrReason,
650    ) -> InferResult<Vec<Binder<rty::CoroutineObligPredicate>>> {
651        let mut env = DummyEnv;
652        let mut sub = Sub::new(&mut env, reason, self.span);
653        sub.generic_args(self.infcx, variance, a, b)?;
654        Ok(sub.obligations)
655    }
656
657    // FIXME(nilehmann) this is similar to `Checker::check_call`, but since is used from
658    // `place_ty::fold` we cannot use that directly. We should try to unify them, because
659    // there are a couple of things missing here (e.g., checking clauses on the struct definition).
660    pub fn check_constructor(
661        &mut self,
662        variant: EarlyBinder<PolyVariant>,
663        generic_args: &[GenericArg],
664        fields: &[Ty],
665        reason: ConstrReason,
666    ) -> InferResult<Ty> {
667        let ret = self.ensure_resolved_evars(|this| {
668            // Replace holes in generic arguments with fresh inference variables
669            let generic_args = this.instantiate_generic_args(generic_args);
670
671            let variant = variant
672                .instantiate(this.tcx(), &generic_args, &[])
673                .replace_bound_refts_with(|sort, mode, _| this.fresh_infer_var(sort, mode));
674
675            // Check arguments
676            for (actual, formal) in iter::zip(fields, variant.fields()) {
677                this.subtyping(actual, formal, reason)?;
678            }
679
680            // Check requires predicates
681            for require in &variant.requires {
682                this.check_pred(require, ConstrReason::Fold);
683            }
684
685            Ok(variant.ret())
686        })?;
687        Ok(self.fully_resolve_evars(&ret))
688    }
689
690    pub fn ensure_resolved_evars<R>(
691        &mut self,
692        f: impl FnOnce(&mut InferCtxtAt<'_, '_, 'genv, 'tcx>) -> InferResult<R>,
693    ) -> InferResult<R> {
694        self.infcx
695            .ensure_resolved_evars(|infcx| f(&mut infcx.at(self.span)))
696    }
697}
698
699impl<'a, 'genv, 'tcx> std::ops::Deref for InferCtxtAt<'_, 'a, 'genv, 'tcx> {
700    type Target = InferCtxt<'a, 'genv, 'tcx>;
701
702    fn deref(&self) -> &Self::Target {
703        self.infcx
704    }
705}
706
707impl std::ops::DerefMut for InferCtxtAt<'_, '_, '_, '_> {
708    fn deref_mut(&mut self) -> &mut Self::Target {
709        self.infcx
710    }
711}
712
713/// Used for debugging to attach a "trace" to the [`RefineTree`] that can be used to print information
714/// to recover the derivation when relating types via subtyping. The code that attaches the trace is
715/// currently commented out because the output is too verbose.
716#[derive(TypeVisitable, TypeFoldable)]
717pub(crate) enum TypeTrace {
718    Types(Ty, Ty),
719    BaseTys(BaseTy, BaseTy),
720}
721
722#[expect(dead_code, reason = "we use this for debugging some time")]
723impl TypeTrace {
724    fn tys(a: &Ty, b: &Ty) -> Self {
725        Self::Types(a.clone(), b.clone())
726    }
727
728    fn btys(a: &BaseTy, b: &BaseTy) -> Self {
729        Self::BaseTys(a.clone(), b.clone())
730    }
731}
732
733impl fmt::Debug for TypeTrace {
734    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
735        match self {
736            TypeTrace::Types(a, b) => write!(f, "{a:?} - {b:?}"),
737            TypeTrace::BaseTys(a, b) => write!(f, "{a:?} - {b:?}"),
738        }
739    }
740}
741
742pub trait LocEnv {
743    fn ptr_to_ref(
744        &mut self,
745        infcx: &mut InferCtxtAt,
746        reason: ConstrReason,
747        re: Region,
748        path: &Path,
749        bound: Ty,
750    ) -> InferResult<Ty>;
751
752    fn unfold_strg_ref(&mut self, infcx: &mut InferCtxt, path: &Path, ty: &Ty) -> InferResult<Loc>;
753
754    fn get(&self, path: &Path) -> Ty;
755}
756
757struct DummyEnv;
758
759impl LocEnv for DummyEnv {
760    fn ptr_to_ref(
761        &mut self,
762        _: &mut InferCtxtAt,
763        _: ConstrReason,
764        _: Region,
765        _: &Path,
766        _: Ty,
767    ) -> InferResult<Ty> {
768        tracked_span_bug!("call to `ptr_to_ref` on `DummyEnv`")
769    }
770
771    fn unfold_strg_ref(&mut self, _: &mut InferCtxt, _: &Path, _: &Ty) -> InferResult<Loc> {
772        tracked_span_bug!("call to `unfold_str_ref` on `DummyEnv`")
773    }
774
775    fn get(&self, _: &Path) -> Ty {
776        tracked_span_bug!("call to `get` on `DummyEnv`")
777    }
778}
779
780/// Context used to relate two types `a` and `b` via subtyping
781struct Sub<'a, E> {
782    /// The environment to lookup locations pointed to by [`TyKind::Ptr`].
783    env: &'a mut E,
784    reason: ConstrReason,
785    span: Span,
786    /// FIXME(nilehmann) This is used to store coroutine obligations generated during subtyping when
787    /// relating an opaque type. Other obligations related to relating opaque types are resolved
788    /// directly here. The implementation is really messy and we may be missing some obligations.
789    obligations: Vec<Binder<rty::CoroutineObligPredicate>>,
790}
791
792impl<'a, E: LocEnv> Sub<'a, E> {
793    fn new(env: &'a mut E, reason: ConstrReason, span: Span) -> Self {
794        Self { env, reason, span, obligations: vec![] }
795    }
796
797    fn tag(&self) -> Tag {
798        Tag::new(self.reason, self.span)
799    }
800
801    fn tys(&mut self, infcx: &mut InferCtxt, a: &Ty, b: &Ty) -> InferResult {
802        let infcx = &mut infcx.branch();
803        // infcx.cursor.push_trace(TypeTrace::tys(a, b));
804
805        // We *fully* unpack the lhs before continuing to be able to prove goals like this
806        // ∃a. (i32[a], ∃b. {i32[b] | a > b})} <: ∃a,b. ({i32[a] | b < a}, i32[b])
807        // See S4.5 in https://arxiv.org/pdf/2209.13000v1.pdf
808        let a = infcx.unpack(a);
809
810        match (a.kind(), b.kind()) {
811            (TyKind::Exists(..), _) => {
812                bug!("existentials should have been removed by the unpacking above");
813            }
814            (TyKind::Constr(..), _) => {
815                bug!("constraint types should have been removed by the unpacking above");
816            }
817
818            (_, TyKind::Exists(ctor_b)) => {
819                infcx.enter_exists(ctor_b, |infcx, ty_b| self.tys(infcx, &a, &ty_b))
820            }
821            (_, TyKind::Constr(pred_b, ty_b)) => {
822                infcx.check_pred(pred_b, self.tag());
823                self.tys(infcx, &a, ty_b)
824            }
825
826            (TyKind::Ptr(PtrKind::Mut(_), path_a), TyKind::StrgRef(_, path_b, ty_b)) => {
827                // We should technically remove `path1` from `env`, but we are assuming that functions
828                // always give back ownership of the location so `path1` is going to be overwritten
829                // after the call anyways.
830                let ty_a = self.env.get(path_a);
831                infcx.unify_exprs(&path_a.to_expr(), &path_b.to_expr());
832                self.tys(infcx, &ty_a, ty_b)
833            }
834            (TyKind::StrgRef(_, path_a, ty_a), TyKind::StrgRef(_, path_b, ty_b)) => {
835                // We have to unfold strong references prior to a subtyping check. Normally, when
836                // checking a function body, a `StrgRef` is automatically unfolded i.e. `x:&strg T`
837                // is turned into a `x:ptr(l); l: T` where `l` is some fresh location. However, we
838                // need the below to do a similar unfolding during function subtyping where we just
839                // have the super-type signature that needs to be unfolded. We also add the binding
840                // to the environment so that we can:
841                // (1) UPDATE the location after the call, and
842                // (2) CHECK the relevant `ensures` clauses of the super-sig.
843                // Same as the `Ptr` case above we should remove the location from the environment
844                // after unfolding to consume it, but we are assuming functions always give back
845                // ownership.
846                self.env.unfold_strg_ref(infcx, path_a, ty_a)?;
847                let ty_a = self.env.get(path_a);
848                infcx.unify_exprs(&path_a.to_expr(), &path_b.to_expr());
849                self.tys(infcx, &ty_a, ty_b)
850            }
851            (
852                TyKind::Ptr(PtrKind::Mut(re), path),
853                TyKind::Indexed(BaseTy::Ref(_, bound, Mutability::Mut), idx),
854            ) => {
855                // We sometimes generate evars for the index of references so we need to make sure
856                // we solve them.
857                self.idxs_eq(infcx, &Expr::unit(), idx);
858
859                self.env.ptr_to_ref(
860                    &mut infcx.at(self.span),
861                    self.reason,
862                    *re,
863                    path,
864                    bound.clone(),
865                )?;
866                Ok(())
867            }
868
869            (TyKind::Indexed(bty_a, idx_a), TyKind::Indexed(bty_b, idx_b)) => {
870                self.btys(infcx, bty_a, bty_b)?;
871                self.idxs_eq(infcx, idx_a, idx_b);
872                Ok(())
873            }
874            (TyKind::Ptr(pk_a, path_a), TyKind::Ptr(pk_b, path_b)) => {
875                debug_assert_eq!(pk_a, pk_b);
876                debug_assert_eq!(path_a, path_b);
877                Ok(())
878            }
879            (TyKind::Param(param_ty_a), TyKind::Param(param_ty_b)) => {
880                debug_assert_eq!(param_ty_a, param_ty_b);
881                Ok(())
882            }
883            (_, TyKind::Uninit) => Ok(()),
884            (TyKind::Downcast(.., fields_a), TyKind::Downcast(.., fields_b)) => {
885                debug_assert_eq!(fields_a.len(), fields_b.len());
886                for (ty_a, ty_b) in iter::zip(fields_a, fields_b) {
887                    self.tys(infcx, ty_a, ty_b)?;
888                }
889                Ok(())
890            }
891            _ => Err(query_bug!("incompatible types: `{a:?}` - `{b:?}`"))?,
892        }
893    }
894
895    fn btys(&mut self, infcx: &mut InferCtxt, a: &BaseTy, b: &BaseTy) -> InferResult {
896        // infcx.push_trace(TypeTrace::btys(a, b));
897
898        match (a, b) {
899            (BaseTy::Int(int_ty_a), BaseTy::Int(int_ty_b)) => {
900                debug_assert_eq!(int_ty_a, int_ty_b);
901                Ok(())
902            }
903            (BaseTy::Uint(uint_ty_a), BaseTy::Uint(uint_ty_b)) => {
904                debug_assert_eq!(uint_ty_a, uint_ty_b);
905                Ok(())
906            }
907            (BaseTy::Adt(a_adt, a_args), BaseTy::Adt(b_adt, b_args)) => {
908                tracked_span_dbg_assert_eq!(a_adt.did(), b_adt.did());
909                tracked_span_dbg_assert_eq!(a_args.len(), b_args.len());
910                let variances = infcx.genv.variances_of(a_adt.did());
911                for (variance, ty_a, ty_b) in izip!(variances, a_args.iter(), b_args.iter()) {
912                    self.generic_args(infcx, *variance, ty_a, ty_b)?;
913                }
914                Ok(())
915            }
916            (BaseTy::FnDef(a_def_id, a_args), BaseTy::FnDef(b_def_id, b_args)) => {
917                debug_assert_eq!(a_def_id, b_def_id);
918                debug_assert_eq!(a_args.len(), b_args.len());
919                // NOTE: we don't check subtyping here because the RHS is *really*
920                // the function type, the LHS is just generated by rustc.
921                // we could generate a subtyping constraint but those would
922                // just be trivial (but might cause useless cycles in fixpoint).
923                // Nico: (This is probably ok because) We never do function
924                // subtyping between `FnDef` *except* when (the def_id) is
925                // passed as an argument to a function.
926                for (arg_a, arg_b) in iter::zip(a_args, b_args) {
927                    match (arg_a, arg_b) {
928                        (GenericArg::Ty(ty_a), GenericArg::Ty(ty_b)) => {
929                            let bty_a = ty_a.as_bty_skipping_existentials();
930                            let bty_b = ty_b.as_bty_skipping_existentials();
931                            tracked_span_dbg_assert_eq!(bty_a, bty_b);
932                        }
933                        (GenericArg::Base(ctor_a), GenericArg::Base(ctor_b)) => {
934                            let bty_a = ctor_a.as_bty_skipping_binder();
935                            let bty_b = ctor_b.as_bty_skipping_binder();
936                            tracked_span_dbg_assert_eq!(bty_a, bty_b);
937                        }
938                        (_, _) => tracked_span_dbg_assert_eq!(arg_a, arg_b),
939                    }
940                }
941                Ok(())
942            }
943            (BaseTy::Float(float_ty_a), BaseTy::Float(float_ty_b)) => {
944                debug_assert_eq!(float_ty_a, float_ty_b);
945                Ok(())
946            }
947            (BaseTy::Slice(ty_a), BaseTy::Slice(ty_b)) => self.tys(infcx, ty_a, ty_b),
948
949            (BaseTy::RawPtr(ty_a, mut_a), BaseTy::RawPtr(ty_b, mut_b)) => {
950                debug_assert_eq!(mut_a, mut_b);
951                self.tys(infcx, ty_a, ty_b)?;
952                if matches!(mut_a, Mutability::Mut) {
953                    self.tys(infcx, ty_b, ty_a)?;
954                }
955                Ok(())
956            }
957
958            (BaseTy::Ref(_, ty_a, Mutability::Mut), BaseTy::Ref(_, ty_b, Mutability::Mut)) => {
959                if ty_a.is_slice()
960                    && let TyKind::Indexed(_, idx_a) = ty_a.kind()
961                    && let TyKind::Exists(bty_b) = ty_b.kind()
962                {
963                    // For `&mut [T1][e] <: &mut ∃v[T2][v]`, we can hoist out the existential on the right because we know
964                    // the index is immutable. This means we have to prove `&mut [T1][e] <: ∃v. &mut [T2][v]`
965                    // This will in turn require proving `&mut [T1][e1] <: &mut [T2][?v]` for a fresh evar `?v`.
966                    // We know the evar will solve to `e`, so subtyping simplifies to the bellow.
967                    self.tys(infcx, ty_a, ty_b)?;
968                    self.tys(infcx, &bty_b.replace_bound_reft(idx_a), ty_a)
969                } else {
970                    self.tys(infcx, ty_a, ty_b)?;
971                    self.tys(infcx, ty_b, ty_a)
972                }
973            }
974            (BaseTy::Ref(_, ty_a, Mutability::Not), BaseTy::Ref(_, ty_b, Mutability::Not)) => {
975                self.tys(infcx, ty_a, ty_b)
976            }
977            (BaseTy::Tuple(tys_a), BaseTy::Tuple(tys_b)) => {
978                debug_assert_eq!(tys_a.len(), tys_b.len());
979                for (ty_a, ty_b) in iter::zip(tys_a, tys_b) {
980                    self.tys(infcx, ty_a, ty_b)?;
981                }
982                Ok(())
983            }
984            (
985                BaseTy::Alias(AliasTy {
986                    kind: AliasKind::Opaque { def_id: def_id_a },
987                    args: args_a,
988                    refine_args: refine_args_a,
989                }),
990                BaseTy::Alias(AliasTy {
991                    kind: AliasKind::Opaque { def_id: def_id_b },
992                    args: args_b,
993                    refine_args: refine_args_b,
994                }),
995            ) => {
996                debug_assert_eq!(def_id_a, def_id_b);
997
998                // handle type-args
999                for (ty_a, ty_b) in izip!(args_a.iter(), args_b.iter()) {
1000                    self.generic_args(infcx, Invariant, ty_a, ty_b)?;
1001                }
1002
1003                // handle refine-args
1004                debug_assert_eq!(refine_args_a.len(), refine_args_b.len());
1005                iter::zip(refine_args_a.iter(), refine_args_b.iter())
1006                    .for_each(|(expr_a, expr_b)| infcx.unify_exprs(expr_a, expr_b));
1007
1008                Ok(())
1009            }
1010            (
1011                _,
1012                BaseTy::Alias(AliasTy { kind: AliasKind::Opaque { def_id }, args, refine_args }),
1013            ) => {
1014                // only for when concrete type on LHS and impl-with-bounds on RHS
1015                self.handle_opaque_type(infcx, a, *def_id, args, refine_args)
1016            }
1017            (
1018                BaseTy::Alias(alias_ty_a @ AliasTy { kind: AliasKind::Projection { .. }, .. }),
1019                BaseTy::Alias(alias_ty_b @ AliasTy { kind: AliasKind::Projection { .. }, .. }),
1020            ) => {
1021                tracked_span_dbg_assert_eq!(alias_ty_a.erase_regions(), alias_ty_b.erase_regions());
1022                Ok(())
1023            }
1024            (BaseTy::Array(ty_a, len_a), BaseTy::Array(ty_b, len_b)) => {
1025                tracked_span_dbg_assert_eq!(len_a, len_b);
1026                self.tys(infcx, ty_a, ty_b)
1027            }
1028            (BaseTy::Param(param_a), BaseTy::Param(param_b)) => {
1029                debug_assert_eq!(param_a, param_b);
1030                Ok(())
1031            }
1032            (BaseTy::Bool, BaseTy::Bool)
1033            | (BaseTy::Str, BaseTy::Str)
1034            | (BaseTy::Char, BaseTy::Char)
1035            | (BaseTy::RawPtrMetadata(_), BaseTy::RawPtrMetadata(_)) => Ok(()),
1036            (BaseTy::Dynamic(preds_a, _), BaseTy::Dynamic(preds_b, _)) => {
1037                tracked_span_assert_eq!(preds_a.erase_regions(), preds_b.erase_regions());
1038                Ok(())
1039            }
1040            (BaseTy::Closure(did1, tys_a, _, _), BaseTy::Closure(did2, tys_b, _, _))
1041                if did1 == did2 =>
1042            {
1043                debug_assert_eq!(tys_a.len(), tys_b.len());
1044                for (ty_a, ty_b) in iter::zip(tys_a, tys_b) {
1045                    self.tys(infcx, ty_a, ty_b)?;
1046                }
1047                Ok(())
1048            }
1049            (BaseTy::FnPtr(sig_a), BaseTy::FnPtr(sig_b)) => {
1050                tracked_span_assert_eq!(sig_a.erase_regions(), sig_b.erase_regions());
1051                Ok(())
1052            }
1053            (BaseTy::Never, BaseTy::Never) => Ok(()),
1054            (
1055                BaseTy::Coroutine(did1, resume_ty_a, tys_a, _),
1056                BaseTy::Coroutine(did2, resume_ty_b, tys_b, _),
1057            ) if did1 == did2 => {
1058                debug_assert_eq!(tys_a.len(), tys_b.len());
1059                for (ty_a, ty_b) in iter::zip(tys_a, tys_b) {
1060                    self.tys(infcx, ty_a, ty_b)?;
1061                }
1062                // TODO(RJ): Treating resume type as invariant...but I think they should be contravariant(?)
1063                self.tys(infcx, resume_ty_b, resume_ty_a)?;
1064                self.tys(infcx, resume_ty_a, resume_ty_b)?;
1065
1066                Ok(())
1067            }
1068            (BaseTy::Foreign(did_a), BaseTy::Foreign(did_b)) if did_a == did_b => Ok(()),
1069            _ => Err(query_bug!("incompatible base types: `{a:#?}` - `{b:#?}`"))?,
1070        }
1071    }
1072
1073    fn generic_args(
1074        &mut self,
1075        infcx: &mut InferCtxt,
1076        variance: Variance,
1077        a: &GenericArg,
1078        b: &GenericArg,
1079    ) -> InferResult {
1080        let (ty_a, ty_b) = match (a, b) {
1081            (GenericArg::Ty(ty_a), GenericArg::Ty(ty_b)) => (ty_a.clone(), ty_b.clone()),
1082            (GenericArg::Base(ctor_a), GenericArg::Base(ctor_b)) => {
1083                tracked_span_dbg_assert_eq!(
1084                    ctor_a.sort().erase_regions(),
1085                    ctor_b.sort().erase_regions()
1086                );
1087                (ctor_a.to_ty(), ctor_b.to_ty())
1088            }
1089            (GenericArg::Lifetime(_), GenericArg::Lifetime(_)) => return Ok(()),
1090            (GenericArg::Const(cst_a), GenericArg::Const(cst_b)) => {
1091                debug_assert_eq!(cst_a, cst_b);
1092                return Ok(());
1093            }
1094            _ => Err(query_bug!("incompatible generic args: `{a:?}` `{b:?}`"))?,
1095        };
1096        match variance {
1097            Variance::Covariant => self.tys(infcx, &ty_a, &ty_b),
1098            Variance::Invariant => {
1099                self.tys(infcx, &ty_a, &ty_b)?;
1100                self.tys(infcx, &ty_b, &ty_a)
1101            }
1102            Variance::Contravariant => self.tys(infcx, &ty_b, &ty_a),
1103            Variance::Bivariant => Ok(()),
1104        }
1105    }
1106
1107    fn idxs_eq(&mut self, infcx: &mut InferCtxt, a: &Expr, b: &Expr) {
1108        if a == b {
1109            return;
1110        }
1111        match (a.kind(), b.kind()) {
1112            (
1113                ExprKind::Ctor(Ctor::Struct(did_a), flds_a),
1114                ExprKind::Ctor(Ctor::Struct(did_b), flds_b),
1115            ) => {
1116                debug_assert_eq!(did_a, did_b);
1117                for (a, b) in iter::zip(flds_a, flds_b) {
1118                    self.idxs_eq(infcx, a, b);
1119                }
1120            }
1121            (ExprKind::Tuple(flds_a), ExprKind::Tuple(flds_b)) => {
1122                for (a, b) in iter::zip(flds_a, flds_b) {
1123                    self.idxs_eq(infcx, a, b);
1124                }
1125            }
1126            (ExprKind::Ctor(Ctor::RawPtr, flds_a), ExprKind::Ctor(Ctor::RawPtr, flds_b)) => {
1127                for (a, b) in iter::zip(flds_a, flds_b) {
1128                    self.idxs_eq(infcx, a, b);
1129                }
1130            }
1131            (_, ExprKind::Tuple(flds_b)) => {
1132                for (f, b) in flds_b.iter().enumerate() {
1133                    let proj = FieldProj::Tuple { arity: flds_b.len(), field: f as u32 };
1134                    let a = a.proj_and_reduce(proj);
1135                    self.idxs_eq(infcx, &a, b);
1136                }
1137            }
1138            (_, ExprKind::Ctor(Ctor::RawPtr, flds_b)) => {
1139                for (f, b) in flds_b.iter().enumerate() {
1140                    let field = rty::RawPtrField::from_index(f as u32).unwrap();
1141                    let a = a.proj_and_reduce(FieldProj::RawPtr { field });
1142                    self.idxs_eq(infcx, &a, b);
1143                }
1144            }
1145
1146            (_, ExprKind::Ctor(Ctor::Struct(def_id), flds_b)) => {
1147                for (f, b) in flds_b.iter().enumerate() {
1148                    let proj = FieldProj::Adt { def_id: *def_id, field: f as u32 };
1149                    let a = a.proj_and_reduce(proj);
1150                    self.idxs_eq(infcx, &a, b);
1151                }
1152            }
1153
1154            (ExprKind::Tuple(flds_a), _) => {
1155                infcx.unify_exprs(a, b);
1156                for (f, a) in flds_a.iter().enumerate() {
1157                    let proj = FieldProj::Tuple { arity: flds_a.len(), field: f as u32 };
1158                    let b = b.proj_and_reduce(proj);
1159                    self.idxs_eq(infcx, a, &b);
1160                }
1161            }
1162            (ExprKind::Ctor(Ctor::RawPtr, flds_a), _) => {
1163                infcx.unify_exprs(a, b);
1164                for (f, a) in flds_a.iter().enumerate() {
1165                    let field = rty::RawPtrField::from_index(f as u32).unwrap();
1166                    let b = b.proj_and_reduce(FieldProj::RawPtr { field });
1167                    self.idxs_eq(infcx, a, &b);
1168                }
1169            }
1170            (ExprKind::Ctor(Ctor::Struct(def_id), flds_a), _) => {
1171                infcx.unify_exprs(a, b);
1172                for (f, a) in flds_a.iter().enumerate() {
1173                    let proj = FieldProj::Adt { def_id: *def_id, field: f as u32 };
1174                    let b = b.proj_and_reduce(proj);
1175                    self.idxs_eq(infcx, a, &b);
1176                }
1177            }
1178            (ExprKind::Abs(lam_a), ExprKind::Abs(lam_b)) => {
1179                self.abs_eq(infcx, lam_a, lam_b);
1180            }
1181            (_, ExprKind::Abs(lam_b)) => {
1182                self.abs_eq(infcx, &a.eta_expand_abs(lam_b.vars(), lam_b.output()), lam_b);
1183            }
1184            (ExprKind::Abs(lam_a), _) => {
1185                infcx.unify_exprs(a, b);
1186                self.abs_eq(infcx, lam_a, &b.eta_expand_abs(lam_a.vars(), lam_a.output()));
1187            }
1188            (ExprKind::KVar(_), _) | (_, ExprKind::KVar(_)) => {
1189                infcx.check_impl(a, b, self.tag());
1190                infcx.check_impl(b, a, self.tag());
1191            }
1192            _ => {
1193                infcx.unify_exprs(a, b);
1194                let span = b.span();
1195                infcx.check_pred(Expr::binary_op(rty::BinOp::Eq, a, b).at_opt(span), self.tag());
1196            }
1197        }
1198    }
1199
1200    fn abs_eq(&mut self, infcx: &mut InferCtxt, a: &Lambda, b: &Lambda) {
1201        debug_assert_eq!(a.vars().len(), b.vars().len());
1202        let vars = a
1203            .vars()
1204            .iter()
1205            .map(|kind| {
1206                let (sort, _, kind) = kind.expect_refine();
1207                Expr::fvar(infcx.define_bound_reft_var(sort, kind))
1208            })
1209            .collect_vec();
1210        let body_a = a.apply(&vars);
1211        let body_b = b.apply(&vars);
1212        self.idxs_eq(infcx, &body_a, &body_b);
1213    }
1214
1215    fn handle_opaque_type(
1216        &mut self,
1217        infcx: &mut InferCtxt,
1218        bty: &BaseTy,
1219        opaque_def_id: DefId,
1220        opaque_args: &rty::GenericArgs,
1221        opaque_refine_args: &rty::RefineArgs,
1222    ) -> InferResult {
1223        if let BaseTy::Coroutine(def_id, resume_ty, upvar_tys, args) = bty {
1224            let obligs = mk_coroutine_obligations(
1225                infcx.genv,
1226                def_id,
1227                resume_ty,
1228                upvar_tys,
1229                &opaque_def_id,
1230                args.clone(),
1231            )?;
1232            self.obligations.extend(obligs);
1233        } else {
1234            let bounds = infcx.genv.item_bounds(opaque_def_id)?.instantiate(
1235                infcx.tcx(),
1236                opaque_args,
1237                opaque_refine_args,
1238            );
1239            for clause in &bounds {
1240                if !clause.kind().vars().is_empty() {
1241                    Err(query_bug!("handle_opaque_types: clause with bound vars: `{clause:?}`"))?;
1242                }
1243                if let rty::ClauseKind::Projection(pred) = clause.kind_skipping_binder() {
1244                    let alias_ty = pred
1245                        .projection_term
1246                        .with_self_ty(bty.to_subset_ty_ctor())
1247                        .to_alias_ty();
1248                    let ty1 = BaseTy::Alias(alias_ty)
1249                        .to_ty()
1250                        .deeply_normalize(&mut infcx.at(self.span))?;
1251                    let ty2 = pred.term.to_ty();
1252                    self.tys(infcx, &ty1, &ty2)?;
1253                }
1254            }
1255        }
1256        Ok(())
1257    }
1258}
1259
1260fn mk_coroutine_obligations(
1261    genv: GlobalEnv,
1262    generator_did: &DefId,
1263    resume_ty: &Ty,
1264    upvar_tys: &List<Ty>,
1265    opaque_def_id: &DefId,
1266    args: flux_rustc_bridge::ty::GenericArgs,
1267) -> InferResult<Vec<Binder<rty::CoroutineObligPredicate>>> {
1268    let bounds = genv.item_bounds(*opaque_def_id)?.skip_binder();
1269    for bound in &bounds {
1270        if let Some(proj_clause) = bound.as_projection_clause() {
1271            return Ok(vec![proj_clause.map(|proj_clause| {
1272                let output = proj_clause.term;
1273                CoroutineObligPredicate {
1274                    def_id: *generator_did,
1275                    resume_ty: resume_ty.clone(),
1276                    upvar_tys: upvar_tys.clone(),
1277                    output: output.to_ty(),
1278                    args,
1279                }
1280            })]);
1281        }
1282    }
1283    bug!("no projection predicate")
1284}
1285
1286#[derive(Debug)]
1287pub enum InferErr {
1288    UnsolvedEvar(EVid),
1289    Query(QueryErr),
1290}
1291
1292impl From<QueryErr> for InferErr {
1293    fn from(v: QueryErr) -> Self {
1294        Self::Query(v)
1295    }
1296}
1297
1298mod pretty {
1299    use std::fmt;
1300
1301    use flux_middle::pretty::*;
1302
1303    use super::*;
1304
1305    impl Pretty for Tag {
1306        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1307            w!(cx, f, "{:?} at {:?}", ^self.reason, self.src_span)?;
1308            if let Some(dst_span) = self.dst_span {
1309                w!(cx, f, " ({:?})", ^dst_span)?;
1310            }
1311            Ok(())
1312        }
1313    }
1314
1315    impl_debug_with_default_cx!(Tag);
1316}