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