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