flux_refineck/
checker.rs

1use std::{collections::hash_map::Entry, iter, vec};
2
3use flux_common::{
4    bug, dbg, dbg::SpanTrace, index::IndexVec, iter::IterExt, span_bug, tracked_span_bug,
5    tracked_span_dbg_assert_eq,
6};
7use flux_config::{self as config, InferOpts};
8use flux_infer::{
9    infer::{
10        ConstrReason, GlobalEnvExt as _, InferCtxt, InferCtxtRoot, InferResult, SubtypeReason,
11    },
12    projections::NormalizeExt as _,
13    refine_tree::{Marker, RefineCtxtTrace},
14};
15use flux_middle::{
16    global_env::GlobalEnv,
17    queries::{QueryResult, try_query},
18    query_bug,
19    rty::{
20        self, AdtDef, BaseTy, Binder, Bool, Clause, Constant, CoroutineObligPredicate, EarlyBinder,
21        Expr, FnOutput, FnSig, FnTraitPredicate, GenericArg, GenericArgsExt as _, Int, IntTy,
22        Mutability, Path, PolyFnSig, PtrKind, RefineArgs, RefineArgsExt,
23        Region::ReErased,
24        Ty, TyKind, Uint, UintTy, VariantIdx,
25        fold::{TypeFoldable, TypeFolder, TypeSuperFoldable},
26        refining::{Refine, Refiner},
27    },
28};
29use flux_rustc_bridge::{
30    self, ToRustc,
31    mir::{
32        self, AggregateKind, AssertKind, BasicBlock, Body, BodyRoot, BorrowKind, CastKind,
33        ConstOperand, Location, NonDivergingIntrinsic, Operand, Place, Rvalue, START_BLOCK,
34        Statement, StatementKind, Terminator, TerminatorKind, UnOp,
35    },
36    ty::{self, GenericArgsExt as _},
37};
38use itertools::{Itertools, izip};
39use rustc_data_structures::{graph::dominators::Dominators, unord::UnordMap};
40use rustc_hash::{FxHashMap, FxHashSet};
41use rustc_hir::{
42    LangItem,
43    def_id::{DefId, LocalDefId},
44};
45use rustc_index::{IndexSlice, bit_set::DenseBitSet};
46use rustc_infer::infer::TyCtxtInferExt;
47use rustc_middle::{
48    mir::{Promoted, SwitchTargets},
49    ty::{TyCtxt, TypeSuperVisitable as _, TypeVisitable as _, TypingMode},
50};
51use rustc_span::{
52    Span, Symbol,
53    sym::{self},
54};
55
56use self::errors::{CheckerError, ResultExt};
57use crate::{
58    ghost_statements::{CheckerId, GhostStatement, GhostStatements, Point},
59    primops,
60    queue::WorkQueue,
61    rty::Char,
62    type_env::{BasicBlockEnv, BasicBlockEnvShape, PtrToRefBound, TypeEnv, TypeEnvTrace},
63};
64
65type Result<T = ()> = std::result::Result<T, CheckerError>;
66
67pub(crate) struct Checker<'ck, 'genv, 'tcx, M> {
68    genv: GlobalEnv<'genv, 'tcx>,
69    /// [`CheckerId`] of the function-like item being checked.
70    checker_id: CheckerId,
71    inherited: Inherited<'ck, M>,
72    body: &'ck Body<'tcx>,
73    /// The type used for the `resume` argument if we are checking a generator.
74    resume_ty: Option<Ty>,
75    output: Binder<FnOutput>,
76    /// A marker to the node in the refinement tree at the end of the basic block after applying
77    /// the effects of the terminator.
78    markers: IndexVec<BasicBlock, Option<Marker>>,
79    visited: DenseBitSet<BasicBlock>,
80    queue: WorkQueue<'ck>,
81    default_refiner: Refiner<'genv, 'tcx>,
82    /// The templates for the promoted bodies of the current function
83    promoted: &'ck IndexSlice<Promoted, Ty>,
84}
85
86/// Fields shared by the top-level function and its nested closure/generators
87struct Inherited<'ck, M> {
88    /// [`Expr`]s used to instantiate the early bound refinement parameters of the top-level function
89    /// signature
90    ghost_stmts: &'ck UnordMap<CheckerId, GhostStatements>,
91    mode: &'ck mut M,
92
93    /// This map has the "templates" generated for the closures constructed (in [`Checker::check_rvalue_closure`]).
94    /// The [`PolyFnSig`] can have free variables (inside the scope of kvars), so we need to be
95    /// careful and only use it in the correct scope.
96    closures: &'ck mut UnordMap<DefId, PolyFnSig>,
97}
98
99#[derive(Debug)]
100struct ResolvedCall {
101    output: Ty,
102    /// The refine arguments given to the call
103    _early_args: Vec<Expr>,
104    /// The refine arguments given to the call
105    _late_args: Vec<Expr>,
106}
107
108impl<'ck, M: Mode> Inherited<'ck, M> {
109    fn new(
110        mode: &'ck mut M,
111        ghost_stmts: &'ck UnordMap<CheckerId, GhostStatements>,
112        closures: &'ck mut UnordMap<DefId, PolyFnSig>,
113    ) -> Self {
114        Self { ghost_stmts, mode, closures }
115    }
116
117    fn reborrow(&mut self) -> Inherited<'_, M> {
118        Inherited { ghost_stmts: self.ghost_stmts, mode: self.mode, closures: self.closures }
119    }
120}
121
122pub(crate) trait Mode: Sized {
123    const NAME: &str;
124
125    fn enter_basic_block<'ck, 'genv, 'tcx>(
126        ck: &mut Checker<'ck, 'genv, 'tcx, Self>,
127        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
128        bb: BasicBlock,
129    ) -> TypeEnv<'ck>;
130
131    fn check_goto_join_point<'genv, 'tcx>(
132        ck: &mut Checker<'_, 'genv, 'tcx, Self>,
133        infcx: InferCtxt<'_, 'genv, 'tcx>,
134        env: TypeEnv,
135        terminator_span: Span,
136        target: BasicBlock,
137    ) -> Result<bool>;
138
139    fn clear(ck: &mut Checker<Self>, bb: BasicBlock);
140}
141
142pub(crate) struct ShapeMode {
143    bb_envs: FxHashMap<CheckerId, FxHashMap<BasicBlock, BasicBlockEnvShape>>,
144}
145
146pub(crate) struct RefineMode {
147    bb_envs: FxHashMap<CheckerId, FxHashMap<BasicBlock, BasicBlockEnv>>,
148}
149
150/// The result of running the shape phase.
151pub(crate) struct ShapeResult(FxHashMap<CheckerId, FxHashMap<BasicBlock, BasicBlockEnvShape>>);
152
153/// A `Guard` describes extra "control" information that holds at the start of a successor basic block
154#[derive(Debug)]
155enum Guard {
156    /// No extra information holds, e.g., for a plain goto.
157    None,
158    /// A predicate that can be assumed, e.g., in the branches of an if-then-else.
159    Pred(Expr),
160    /// The corresponding place was found to be of a particular variant.
161    Match(Place, VariantIdx),
162}
163
164impl<'genv, 'tcx> Checker<'_, 'genv, 'tcx, ShapeMode> {
165    pub(crate) fn run_in_shape_mode<'ck>(
166        genv: GlobalEnv<'genv, 'tcx>,
167        local_id: LocalDefId,
168        ghost_stmts: &'ck UnordMap<CheckerId, GhostStatements>,
169        closures: &'ck mut UnordMap<DefId, PolyFnSig>,
170        opts: InferOpts,
171    ) -> Result<ShapeResult> {
172        let def_id = local_id.to_def_id();
173        dbg::shape_mode_span!(genv.tcx(), local_id).in_scope(|| {
174            let span = genv.tcx().def_span(local_id);
175            let mut mode = ShapeMode { bb_envs: FxHashMap::default() };
176
177            let body = genv.mir(local_id).with_span(span)?;
178
179            // In shape mode we don't care about kvars
180            let mut root_ctxt = try_query(|| {
181                genv.infcx_root(&body.infcx, opts)
182                    .with_dummy_kvars()
183                    .identity_for_item(def_id)?
184                    .build()
185            })
186            .with_span(span)?;
187
188            let inherited = Inherited::new(&mut mode, ghost_stmts, closures);
189
190            let infcx = root_ctxt.infcx(def_id, &body.infcx);
191            let poly_sig = genv
192                .fn_sig(local_id)
193                .with_span(span)?
194                .instantiate_identity();
195            Checker::run(infcx, local_id, inherited, poly_sig)?;
196
197            Ok(ShapeResult(mode.bb_envs))
198        })
199    }
200}
201
202impl<'genv, 'tcx> Checker<'_, 'genv, 'tcx, RefineMode> {
203    pub(crate) fn run_in_refine_mode<'ck>(
204        genv: GlobalEnv<'genv, 'tcx>,
205        local_id: LocalDefId,
206        ghost_stmts: &'ck UnordMap<CheckerId, GhostStatements>,
207        closures: &'ck mut UnordMap<DefId, PolyFnSig>,
208        bb_env_shapes: ShapeResult,
209        opts: InferOpts,
210    ) -> Result<InferCtxtRoot<'genv, 'tcx>> {
211        let def_id = local_id.to_def_id();
212        let span = genv.tcx().def_span(def_id);
213
214        let body = genv.mir(local_id).with_span(span)?;
215        let mut root_ctxt = try_query(|| {
216            genv.infcx_root(&body.infcx, opts)
217                .identity_for_item(def_id)?
218                .build()
219        })
220        .with_span(span)?;
221        let bb_envs = bb_env_shapes.into_bb_envs(&mut root_ctxt);
222
223        dbg::refine_mode_span!(genv.tcx(), def_id, bb_envs).in_scope(|| {
224            // Check the body of the function def_id against its signature
225            let mut mode = RefineMode { bb_envs };
226            let inherited = Inherited::new(&mut mode, ghost_stmts, closures);
227            let infcx = root_ctxt.infcx(def_id, &body.infcx);
228            let poly_sig = genv.fn_sig(def_id).with_span(span)?;
229            let poly_sig = poly_sig.instantiate_identity();
230            Checker::run(infcx, local_id, inherited, poly_sig)?;
231
232            Ok(root_ctxt)
233        })
234    }
235}
236
237/// `SubFn` lets us reuse _most_ of the same code for `check_fn_subtyping` for both the case where
238/// we have an early-bound function signature (e.g., for a trait method???) and versions without,
239/// e.g. a plain closure against its FnTraitPredicate obligation.
240#[derive(Debug)]
241pub enum SubFn {
242    Poly(DefId, EarlyBinder<rty::PolyFnSig>, rty::GenericArgs),
243    Mono(rty::PolyFnSig),
244}
245
246impl SubFn {
247    pub fn as_ref(&self) -> &rty::PolyFnSig {
248        match self {
249            SubFn::Poly(_, sig, _) => sig.skip_binder_ref(),
250            SubFn::Mono(sig) => sig,
251        }
252    }
253}
254
255/// The function `check_fn_subtyping` does a function subtyping check between
256/// the sub-type (T_f) corresponding to the type of `def_id` @ `args` and the
257/// super-type (T_g) corresponding to the `oblig_sig`. This subtyping is handled
258/// as akin to the code
259///
260///   T_f := (S1,...,Sn) -> S
261///   T_g := (T1,...,Tn) -> T
262///   T_f <: T_g
263///
264///  fn g(x1:T1,...,xn:Tn) -> T {
265///      f(x1,...,xn)
266///  }
267fn check_fn_subtyping(
268    infcx: &mut InferCtxt,
269    sub_sig: SubFn,
270    super_sig: &rty::PolyFnSig,
271    span: Span,
272) -> InferResult {
273    let mut infcx = infcx.branch();
274    let mut infcx = infcx.at(span);
275    let tcx = infcx.genv.tcx();
276
277    let super_sig = super_sig
278        .replace_bound_vars(|_| rty::ReErased, |sort, _| Expr::fvar(infcx.define_var(sort)))
279        .deeply_normalize(&mut infcx)?;
280
281    // 1. Unpack `T_g` input types
282    let actuals = super_sig
283        .inputs()
284        .iter()
285        .map(|ty| infcx.unpack(ty))
286        .collect_vec();
287
288    let mut env = TypeEnv::empty();
289    let actuals = unfold_local_ptrs(&mut infcx, &mut env, sub_sig.as_ref(), &actuals)?;
290    let actuals = infer_under_mut_ref_hack(&mut infcx, &actuals[..], sub_sig.as_ref());
291
292    let output = infcx.ensure_resolved_evars(|infcx| {
293        // 2. Fresh names for `T_f` refine-params / Instantiate fn_def_sig and normalize it
294        // in subtyping_mono, skip next two steps...
295        let sub_sig = match sub_sig {
296            SubFn::Poly(def_id, early_sig, sub_args) => {
297                let refine_args = infcx.instantiate_refine_args(def_id, &sub_args)?;
298                early_sig.instantiate(tcx, &sub_args, &refine_args)
299            }
300            SubFn::Mono(sig) => sig,
301        };
302        // ... jump right here.
303        let sub_sig = sub_sig
304            .replace_bound_vars(|_| rty::ReErased, |sort, mode| infcx.fresh_infer_var(sort, mode))
305            .deeply_normalize(infcx)?;
306
307        // 3. INPUT subtyping (g-input <: f-input)
308        for requires in super_sig.requires() {
309            infcx.assume_pred(requires);
310        }
311        for (actual, formal) in iter::zip(actuals, sub_sig.inputs()) {
312            let reason = ConstrReason::Subtype(SubtypeReason::Input);
313            infcx.subtyping_with_env(&mut env, &actual, formal, reason)?;
314        }
315        // we check the requires AFTER the actual-formal subtyping as the above may unfold stuff in
316        // the actuals
317        for requires in sub_sig.requires() {
318            let reason = ConstrReason::Subtype(SubtypeReason::Requires);
319            infcx.check_pred(requires, reason);
320        }
321
322        Ok(sub_sig.output())
323    })?;
324
325    let output = infcx
326        .fully_resolve_evars(&output)
327        .replace_bound_refts_with(|sort, _, _| Expr::fvar(infcx.define_var(sort)));
328
329    // 4. OUTPUT subtyping (f_out <: g_out)
330    infcx.ensure_resolved_evars(|infcx| {
331        let super_output = super_sig
332            .output()
333            .replace_bound_refts_with(|sort, mode, _| infcx.fresh_infer_var(sort, mode));
334        let reason = ConstrReason::Subtype(SubtypeReason::Output);
335        infcx.subtyping(&output.ret, &super_output.ret, reason)?;
336
337        // 6. Update state with Output "ensures" and check super ensures
338        env.assume_ensures(infcx, &output.ensures, span);
339        fold_local_ptrs(infcx, &mut env, span)?;
340        env.check_ensures(
341            infcx,
342            &super_output.ensures,
343            ConstrReason::Subtype(SubtypeReason::Ensures),
344        )
345    })
346}
347
348/// Trait subtyping check, which makes sure that the type for an impl method (def_id)
349/// is a subtype of the corresponding trait method.
350pub(crate) fn trait_impl_subtyping<'genv, 'tcx>(
351    genv: GlobalEnv<'genv, 'tcx>,
352    def_id: LocalDefId,
353    opts: InferOpts,
354    span: Span,
355) -> InferResult<Option<InferCtxtRoot<'genv, 'tcx>>> {
356    let tcx = genv.tcx();
357
358    // Skip the check if this is not an impl method
359    let Some((impl_trait_ref, trait_method_id)) = find_trait_item(genv, def_id)? else {
360        return Ok(None);
361    };
362    let impl_method_id = def_id.to_def_id();
363    // Skip the check if either the trait-method or the impl-method are marked as `trusted_impl`
364    if genv.has_trusted_impl(trait_method_id) || genv.has_trusted_impl(impl_method_id) {
365        return Ok(None);
366    }
367
368    let impl_id = tcx.impl_of_assoc(impl_method_id).unwrap();
369    let impl_method_args = GenericArg::identity_for_item(genv, impl_method_id)?;
370    let trait_method_args = impl_method_args.rebase_onto(&tcx, impl_id, &impl_trait_ref.args);
371    let trait_refine_args = RefineArgs::identity_for_item(genv, trait_method_id)?;
372
373    let rustc_infcx = genv
374        .tcx()
375        .infer_ctxt()
376        .with_next_trait_solver(true)
377        .build(TypingMode::non_body_analysis());
378
379    let mut root_ctxt = genv
380        .infcx_root(&rustc_infcx, opts)
381        .with_const_generics(impl_id)?
382        .with_refinement_generics(trait_method_id, &trait_method_args)?
383        .build()?;
384
385    let mut infcx = root_ctxt.infcx(impl_method_id, &rustc_infcx);
386
387    let trait_fn_sig =
388        genv.fn_sig(trait_method_id)?
389            .instantiate(tcx, &trait_method_args, &trait_refine_args);
390    let impl_sig = genv.fn_sig(impl_method_id)?;
391    let sub_sig = SubFn::Poly(impl_method_id, impl_sig, impl_method_args);
392
393    check_fn_subtyping(&mut infcx, sub_sig, &trait_fn_sig, span)?;
394    Ok(Some(root_ctxt))
395}
396
397fn find_trait_item(
398    genv: GlobalEnv<'_, '_>,
399    def_id: LocalDefId,
400) -> QueryResult<Option<(rty::TraitRef, DefId)>> {
401    let tcx = genv.tcx();
402    let def_id = def_id.to_def_id();
403    if let Some(impl_id) = tcx.impl_of_assoc(def_id)
404        && let Some(impl_trait_ref) = genv.impl_trait_ref(impl_id)?
405    {
406        let impl_trait_ref = impl_trait_ref.instantiate_identity();
407        let trait_item_id = tcx.associated_item(def_id).trait_item_def_id().unwrap();
408        return Ok(Some((impl_trait_ref, trait_item_id)));
409    }
410    Ok(None)
411}
412
413/// Temporarily (around a function call) convert an `&mut` to an `&strg` to allow for the call to be
414/// checked. This is done by unfolding the `&mut` into a local pointer at the call-site and then
415/// folding the pointer back into the `&mut` upon return.
416/// See also [`fold_local_ptrs`].
417///
418/// ```text
419///             unpack(T) = T'
420/// ---------------------------------------[local-unfold]
421/// Γ ; &mut T => Γ, l:[<: T] T' ; ptr(l)
422/// ```
423fn unfold_local_ptrs(
424    infcx: &mut InferCtxt,
425    env: &mut TypeEnv,
426    fn_sig: &PolyFnSig,
427    actuals: &[Ty],
428) -> InferResult<Vec<Ty>> {
429    // We *only* need to know whether each input is a &strg or not
430    let fn_sig = fn_sig.skip_binder_ref();
431    let mut tys = vec![];
432    for (actual, input) in izip!(actuals, fn_sig.inputs()) {
433        let actual = if let (
434            TyKind::Indexed(BaseTy::Ref(re, bound, Mutability::Mut), _),
435            TyKind::StrgRef(_, _, _),
436        ) = (actual.kind(), input.kind())
437        {
438            let loc = env.unfold_local_ptr(infcx, bound)?;
439            let path1 = Path::new(loc, rty::List::empty());
440            Ty::ptr(PtrKind::Mut(*re), path1)
441        } else {
442            actual.clone()
443        };
444        tys.push(actual);
445    }
446    Ok(tys)
447}
448
449/// Fold local pointers implements roughly a rule like the following (for all local pointers)
450/// that converts the local pointers created via [`unfold_local_ptrs`] back into `&mut`.
451///
452/// ```text
453///       T1 <: T2
454/// --------------------- [local-fold]
455/// Γ, l:[<: T2] T1 => Γ
456/// ```
457fn fold_local_ptrs(infcx: &mut InferCtxt, env: &mut TypeEnv, span: Span) -> InferResult {
458    let mut at = infcx.at(span);
459    env.fold_local_ptrs(&mut at)
460}
461
462fn promoted_fn_sig(ty: &Ty) -> PolyFnSig {
463    let safety = rustc_hir::Safety::Safe;
464    let abi = rustc_abi::ExternAbi::Rust;
465    let requires = rty::List::empty();
466    let inputs = rty::List::empty();
467    let output =
468        Binder::bind_with_vars(FnOutput::new(ty.clone(), rty::List::empty()), rty::List::empty());
469    let fn_sig = crate::rty::FnSig::new(safety, abi, requires, inputs, output);
470    PolyFnSig::bind_with_vars(fn_sig, crate::rty::List::empty())
471}
472
473impl<'ck, 'genv, 'tcx, M: Mode> Checker<'ck, 'genv, 'tcx, M> {
474    fn new(
475        genv: GlobalEnv<'genv, 'tcx>,
476        checker_id: CheckerId,
477        inherited: Inherited<'ck, M>,
478        body: &'ck Body<'tcx>,
479        fn_sig: FnSig,
480        promoted: &'ck IndexSlice<Promoted, Ty>,
481    ) -> QueryResult<Self> {
482        let root_id = checker_id.root_id();
483
484        let resume_ty = if let CheckerId::DefId(def_id) = checker_id
485            && genv.tcx().is_coroutine(def_id.to_def_id())
486        {
487            Some(fn_sig.inputs()[1].clone())
488        } else {
489            None
490        };
491
492        let bb_len = body.basic_blocks.len();
493        Ok(Self {
494            checker_id,
495            genv,
496            inherited,
497            body,
498            resume_ty,
499            visited: DenseBitSet::new_empty(bb_len),
500            output: fn_sig.output().clone(),
501            markers: IndexVec::from_fn_n(|_| None, bb_len),
502            queue: WorkQueue::empty(bb_len, &body.dominator_order_rank),
503            default_refiner: Refiner::default_for_item(genv, root_id.to_def_id())?,
504            promoted,
505        })
506    }
507
508    fn check_body(
509        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
510        checker_id: CheckerId,
511        inherited: Inherited<'ck, M>,
512        body: &'ck Body<'tcx>,
513        poly_sig: PolyFnSig,
514        promoted: &'ck IndexSlice<Promoted, Ty>,
515    ) -> Result {
516        let span = body.span();
517
518        let fn_sig = poly_sig
519            .replace_bound_vars(|_| rty::ReErased, |sort, _| Expr::fvar(infcx.define_var(sort)))
520            .deeply_normalize(&mut infcx.at(span))
521            .with_span(span)?;
522
523        let mut env = TypeEnv::new(infcx, body, &fn_sig);
524
525        let mut ck = Checker::new(infcx.genv, checker_id, inherited, body, fn_sig, promoted)
526            .with_span(span)?;
527        ck.check_ghost_statements_at(infcx, &mut env, Point::FunEntry, span)?;
528
529        ck.check_goto(infcx.branch(), env, body.span(), START_BLOCK)?;
530
531        while let Some(bb) = ck.queue.pop() {
532            let visited = ck.visited.contains(bb);
533
534            if visited {
535                M::clear(&mut ck, bb);
536            }
537
538            let marker = ck.marker_at_dominator(bb);
539            let mut infcx = infcx.move_to(marker, visited);
540            let mut env = M::enter_basic_block(&mut ck, &mut infcx, bb);
541            env.unpack(&mut infcx);
542            ck.check_basic_block(infcx, env, bb)?;
543        }
544        Ok(())
545    }
546
547    /// Assign a template with fresh kvars to each promoted constant in `body_root`.
548    fn promoted_tys(
549        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
550        def_id: LocalDefId,
551        body_root: &BodyRoot<'tcx>,
552    ) -> QueryResult<IndexVec<Promoted, Ty>> {
553        let hole_refiner = Refiner::with_holes(infcx.genv, def_id.into())?;
554
555        body_root
556            .promoted
557            .iter()
558            .map(|body| {
559                Ok(body
560                    .return_ty()
561                    .refine(&hole_refiner)?
562                    .replace_holes(|binders, kind| infcx.fresh_infer_var_for_hole(binders, kind)))
563            })
564            .collect()
565    }
566
567    fn run(
568        mut infcx: InferCtxt<'_, 'genv, 'tcx>,
569        def_id: LocalDefId,
570        mut inherited: Inherited<'_, M>,
571        poly_sig: PolyFnSig,
572    ) -> Result {
573        let genv = infcx.genv;
574        let span = genv.tcx().def_span(def_id);
575        let body_root = genv.mir(def_id).with_span(span)?;
576
577        // 1. Generate templates for promoted consts
578        let promoted_tys = Self::promoted_tys(&mut infcx, def_id, &body_root).with_span(span)?;
579
580        // 2. Check the body of all promoted
581        for (promoted, ty) in promoted_tys.iter_enumerated() {
582            let body = &body_root.promoted[promoted];
583            let poly_sig = promoted_fn_sig(ty);
584            Checker::check_body(
585                &mut infcx,
586                CheckerId::Promoted(def_id, promoted),
587                inherited.reborrow(),
588                body,
589                poly_sig,
590                &promoted_tys,
591            )?;
592        }
593
594        // 3. Check the main body
595        Checker::check_body(
596            &mut infcx,
597            CheckerId::DefId(def_id),
598            inherited,
599            &body_root.body,
600            poly_sig,
601            &promoted_tys,
602        )
603    }
604
605    fn check_basic_block(
606        &mut self,
607        mut infcx: InferCtxt<'_, 'genv, 'tcx>,
608        mut env: TypeEnv,
609        bb: BasicBlock,
610    ) -> Result {
611        dbg::basic_block_start!(bb, infcx, env);
612
613        self.visited.insert(bb);
614        let data = &self.body.basic_blocks[bb];
615        let mut last_stmt_span = None;
616        let mut location = Location { block: bb, statement_index: 0 };
617        for stmt in &data.statements {
618            let span = stmt.source_info.span;
619            self.check_ghost_statements_at(
620                &mut infcx,
621                &mut env,
622                Point::BeforeLocation(location),
623                span,
624            )?;
625            bug::track_span(span, || {
626                dbg::statement!("start", stmt, &infcx, &env, span, &self);
627                self.check_statement(&mut infcx, &mut env, stmt)?;
628                dbg::statement!("end", stmt, &infcx, &env, span, &self);
629                Ok(())
630            })?;
631            if !stmt.is_nop() {
632                last_stmt_span = Some(span);
633            }
634            location = location.successor_within_block();
635        }
636
637        if let Some(terminator) = &data.terminator {
638            let span = terminator.source_info.span;
639            self.check_ghost_statements_at(
640                &mut infcx,
641                &mut env,
642                Point::BeforeLocation(location),
643                span,
644            )?;
645
646            bug::track_span(span, || {
647                dbg::terminator!("start", terminator, infcx, env);
648
649                let successors =
650                    self.check_terminator(&mut infcx, &mut env, terminator, last_stmt_span)?;
651                dbg::terminator!("end", terminator, infcx, env);
652
653                self.markers[bb] = Some(infcx.marker());
654                let term_span = last_stmt_span.unwrap_or(span);
655                self.check_successors(infcx, env, bb, term_span, successors)
656            })?;
657        }
658        Ok(())
659    }
660
661    fn check_assign_ty(
662        &mut self,
663        infcx: &mut InferCtxt,
664        env: &mut TypeEnv,
665        place: &Place,
666        ty: Ty,
667        span: Span,
668    ) -> InferResult {
669        let ty = infcx.hoister(true).hoist(&ty);
670        env.assign(&mut infcx.at(span), place, ty)
671    }
672
673    fn check_statement(
674        &mut self,
675        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
676        env: &mut TypeEnv,
677        stmt: &Statement<'tcx>,
678    ) -> Result {
679        let stmt_span = stmt.source_info.span;
680        match &stmt.kind {
681            StatementKind::Assign(place, rvalue) => {
682                let ty = self.check_rvalue(infcx, env, stmt_span, rvalue)?;
683                self.check_assign_ty(infcx, env, place, ty, stmt_span)
684                    .with_span(stmt_span)?;
685            }
686            StatementKind::SetDiscriminant { .. } => {
687                // TODO(nilehmann) double check here that the place is unfolded to
688                // the correct variant. This should be guaranteed by rustc
689            }
690            StatementKind::FakeRead(_) => {
691                // TODO(nilehmann) fake reads should be folding points
692            }
693            StatementKind::AscribeUserType(_, _) => {
694                // User ascriptions affect nll, but no refinement type checking.
695                // Maybe we can use this to associate refinement type to locals.
696            }
697            StatementKind::PlaceMention(_) => {
698                // Place mentions are a no-op used to detect uses of unsafe that would
699                // otherwise be optimized away.
700            }
701            StatementKind::Nop => {}
702            StatementKind::Intrinsic(NonDivergingIntrinsic::Assume(op)) => {
703                // Currently, we only have the `assume` intrinsic, which if we're to trust rustc should be a NOP.
704                // TODO: There may be a use-case to actually "assume" the bool index associated with the operand,
705                // i.e. to strengthen the `rcx` / `env` with the assumption that the bool-index is in fact `true`...
706                let _ = self
707                    .check_operand(infcx, env, stmt_span, op)
708                    .with_span(stmt_span)?;
709            }
710        }
711        Ok(())
712    }
713
714    fn is_exit_block(&self, bb: BasicBlock) -> bool {
715        let data = &self.body.basic_blocks[bb];
716        let is_no_op = data.statements.iter().all(Statement::is_nop);
717        let is_ret = match &data.terminator {
718            None => false,
719            Some(term) => term.is_return(),
720        };
721        is_no_op && is_ret
722    }
723
724    /// For `check_terminator`, the output `Vec<BasicBlock, Guard>` denotes,
725    /// - `BasicBlock` "successors" of the current terminator, and
726    /// - `Guard` are extra control information from, e.g. the `SwitchInt` (or `Assert`) you can assume when checking the corresponding successor.
727    fn check_terminator(
728        &mut self,
729        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
730        env: &mut TypeEnv,
731        terminator: &Terminator<'tcx>,
732        last_stmt_span: Option<Span>,
733    ) -> Result<Vec<(BasicBlock, Guard)>> {
734        let source_info = terminator.source_info;
735        let terminator_span = source_info.span;
736        match &terminator.kind {
737            TerminatorKind::Return => {
738                self.check_ret(infcx, env, last_stmt_span.unwrap_or(terminator_span))?;
739                Ok(vec![])
740            }
741            TerminatorKind::Unreachable => Ok(vec![]),
742            TerminatorKind::CoroutineDrop => Ok(vec![]),
743            TerminatorKind::Goto { target } => Ok(vec![(*target, Guard::None)]),
744            TerminatorKind::Yield { resume, resume_arg, .. } => {
745                if let Some(resume_ty) = self.resume_ty.clone() {
746                    self.check_assign_ty(infcx, env, resume_arg, resume_ty, terminator_span)
747                        .with_span(terminator_span)?;
748                } else {
749                    bug!("yield in non-generator function");
750                }
751                Ok(vec![(*resume, Guard::None)])
752            }
753            TerminatorKind::SwitchInt { discr, targets } => {
754                let discr_ty = self
755                    .check_operand(infcx, env, terminator_span, discr)
756                    .with_span(terminator_span)?;
757                if discr_ty.is_integral() || discr_ty.is_bool() || discr_ty.is_char() {
758                    Ok(Self::check_if(&discr_ty, targets))
759                } else {
760                    Ok(Self::check_match(infcx, env, &discr_ty, targets, terminator_span))
761                }
762            }
763            TerminatorKind::Call { kind, args, destination, target, .. } => {
764                let actuals = self
765                    .check_operands(infcx, env, terminator_span, args)
766                    .with_span(terminator_span)?;
767                let ret = match kind {
768                    mir::CallKind::FnDef { resolved_id, resolved_args, .. } => {
769                        let fn_sig = self.genv.fn_sig(*resolved_id).with_span(terminator_span)?;
770                        let generic_args = instantiate_args_for_fun_call(
771                            self.genv,
772                            self.checker_id.root_id().to_def_id(),
773                            *resolved_id,
774                            &resolved_args.lowered,
775                        )
776                        .with_span(terminator_span)?;
777                        self.check_call(
778                            infcx,
779                            env,
780                            terminator_span,
781                            Some(*resolved_id),
782                            fn_sig,
783                            &generic_args,
784                            &actuals,
785                        )?
786                        .output
787                    }
788                    mir::CallKind::FnPtr { operand, .. } => {
789                        let ty = self
790                            .check_operand(infcx, env, terminator_span, operand)
791                            .with_span(terminator_span)?;
792                        if let TyKind::Indexed(BaseTy::FnPtr(fn_sig), _) = infcx.unpack(&ty).kind()
793                        {
794                            self.check_call(
795                                infcx,
796                                env,
797                                terminator_span,
798                                None,
799                                EarlyBinder(fn_sig.clone()),
800                                &[],
801                                &actuals,
802                            )?
803                            .output
804                        } else {
805                            bug!("TODO: fnptr call {ty:?}")
806                        }
807                    }
808                };
809
810                let ret = infcx.unpack(&ret);
811                infcx.assume_invariants(&ret);
812
813                env.assign(&mut infcx.at(terminator_span), destination, ret)
814                    .with_span(terminator_span)?;
815
816                if let Some(target) = target {
817                    Ok(vec![(*target, Guard::None)])
818                } else {
819                    Ok(vec![])
820                }
821            }
822            TerminatorKind::Assert { cond, expected, target, msg } => {
823                Ok(vec![(
824                    *target,
825                    self.check_assert(infcx, env, terminator_span, cond, *expected, msg)
826                        .with_span(terminator_span)?,
827                )])
828            }
829            TerminatorKind::Drop { place, target, .. } => {
830                let _ = env.move_place(&mut infcx.at(terminator_span), place);
831                Ok(vec![(*target, Guard::None)])
832            }
833            TerminatorKind::FalseEdge { real_target, .. } => Ok(vec![(*real_target, Guard::None)]),
834            TerminatorKind::FalseUnwind { real_target, .. } => {
835                Ok(vec![(*real_target, Guard::None)])
836            }
837            TerminatorKind::UnwindResume => bug!("TODO: implement checking of cleanup code"),
838        }
839    }
840
841    fn check_ret(
842        &mut self,
843        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
844        env: &mut TypeEnv,
845        span: Span,
846    ) -> Result {
847        let obligations = infcx
848            .at(span)
849            .ensure_resolved_evars(|infcx| {
850                let ret_place_ty = env.lookup_place(infcx, Place::RETURN)?;
851                let output = self
852                    .output
853                    .replace_bound_refts_with(|sort, mode, _| infcx.fresh_infer_var(sort, mode));
854                let obligations = infcx.subtyping(&ret_place_ty, &output.ret, ConstrReason::Ret)?;
855
856                env.check_ensures(infcx, &output.ensures, ConstrReason::Ret)?;
857
858                Ok(obligations)
859            })
860            .with_span(span)?;
861
862        self.check_coroutine_obligations(infcx, obligations)
863    }
864
865    #[expect(clippy::too_many_arguments)]
866    fn check_call(
867        &mut self,
868        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
869        env: &mut TypeEnv,
870        span: Span,
871        callee_def_id: Option<DefId>,
872        fn_sig: EarlyBinder<PolyFnSig>,
873        generic_args: &[GenericArg],
874        actuals: &[Ty],
875    ) -> Result<ResolvedCall> {
876        let genv = self.genv;
877        let tcx = genv.tcx();
878
879        if M::NAME == "refine" {
880            let no_panic = genv.no_panic(self.checker_id.root_id());
881
882            if no_panic
883                && let Some(callee_def_id) = callee_def_id
884                && genv.def_kind(callee_def_id).is_fn_like()
885            {
886                let callee_no_panic = genv.no_panic(callee_def_id);
887                if !callee_no_panic {
888                    let callee_name = tcx.def_path_str(callee_def_id);
889                    genv.sess()
890                        .emit_err(errors::PanicError { span, callee: callee_name });
891                }
892            }
893        }
894
895        let actuals =
896            unfold_local_ptrs(infcx, env, fn_sig.skip_binder_ref(), actuals).with_span(span)?;
897        let actuals = infer_under_mut_ref_hack(infcx, &actuals, fn_sig.skip_binder_ref());
898        infcx.push_evar_scope();
899
900        // Replace holes in generic arguments with fresh inference variables
901        let generic_args = infcx.instantiate_generic_args(generic_args);
902
903        // Generate fresh inference variables for refinement arguments
904        let early_refine_args = match callee_def_id {
905            Some(callee_def_id) => {
906                infcx
907                    .instantiate_refine_args(callee_def_id, &generic_args)
908                    .with_span(span)?
909            }
910            None => rty::List::empty(),
911        };
912
913        let clauses = match callee_def_id {
914            Some(callee_def_id) => {
915                genv.predicates_of(callee_def_id)
916                    .with_span(span)?
917                    .predicates()
918                    .instantiate(tcx, &generic_args, &early_refine_args)
919            }
920            None => crate::rty::List::empty(),
921        };
922
923        let (clauses, fn_clauses) = Clause::split_off_fn_trait_clauses(self.genv, &clauses);
924        infcx
925            .at(span)
926            .check_non_closure_clauses(&clauses, ConstrReason::Call)
927            .with_span(span)?;
928
929        for fn_trait_pred in fn_clauses {
930            self.check_fn_trait_clause(infcx, &fn_trait_pred, span)?;
931        }
932
933        // Instantiate function signature and normalize it
934        let late_refine_args = vec![];
935        let fn_sig = fn_sig
936            .instantiate(tcx, &generic_args, &early_refine_args)
937            .replace_bound_vars(|_| rty::ReErased, |sort, mode| infcx.fresh_infer_var(sort, mode));
938
939        let fn_sig = fn_sig
940            .deeply_normalize(&mut infcx.at(span))
941            .with_span(span)?;
942
943        let mut at = infcx.at(span);
944
945        // Check requires predicates
946        for requires in fn_sig.requires() {
947            at.check_pred(requires, ConstrReason::Call);
948        }
949
950        // Check arguments
951        for (actual, formal) in iter::zip(actuals, fn_sig.inputs()) {
952            at.subtyping_with_env(env, &actual, formal, ConstrReason::Call)
953                .with_span(span)?;
954        }
955
956        infcx.pop_evar_scope().with_span(span)?;
957        env.fully_resolve_evars(infcx);
958
959        let output = infcx
960            .fully_resolve_evars(&fn_sig.output)
961            .replace_bound_refts_with(|sort, _, _| Expr::fvar(infcx.define_var(sort)));
962
963        env.assume_ensures(infcx, &output.ensures, span);
964        fold_local_ptrs(infcx, env, span).with_span(span)?;
965
966        Ok(ResolvedCall {
967            output: output.ret,
968            _early_args: early_refine_args
969                .into_iter()
970                .map(|arg| infcx.fully_resolve_evars(arg))
971                .collect(),
972            _late_args: late_refine_args
973                .into_iter()
974                .map(|arg| infcx.fully_resolve_evars(&arg))
975                .collect(),
976        })
977    }
978
979    fn check_coroutine_obligations(
980        &mut self,
981        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
982        obligs: Vec<Binder<CoroutineObligPredicate>>,
983    ) -> Result {
984        for oblig in obligs {
985            // FIXME(nilehmann) we shouldn't be skipping this binder
986            let oblig = oblig.skip_binder();
987
988            #[expect(clippy::disallowed_methods, reason = "coroutines cannot be extern speced")]
989            let def_id = oblig.def_id.expect_local();
990            let span = self.genv.tcx().def_span(def_id);
991            let body = self.genv.mir(def_id).with_span(span)?;
992            Checker::run(
993                infcx.change_item(def_id, &body.infcx),
994                def_id,
995                self.inherited.reborrow(),
996                oblig.to_poly_fn_sig(),
997            )?;
998        }
999        Ok(())
1000    }
1001
1002    fn find_self_ty_fn_sig(
1003        &self,
1004        self_ty: rustc_middle::ty::Ty<'tcx>,
1005        span: Span,
1006    ) -> Result<PolyFnSig> {
1007        let tcx = self.genv.tcx();
1008        let mut def_id = Some(self.checker_id.root_id().to_def_id());
1009        while let Some(did) = def_id {
1010            let generic_predicates = self
1011                .genv
1012                .predicates_of(did)
1013                .with_span(span)?
1014                .instantiate_identity();
1015            let predicates = generic_predicates.predicates;
1016
1017            for poly_fn_trait_pred in Clause::split_off_fn_trait_clauses(self.genv, &predicates).1 {
1018                if poly_fn_trait_pred.skip_binder_ref().self_ty.to_rustc(tcx) == self_ty {
1019                    return Ok(poly_fn_trait_pred.map(|fn_trait_pred| fn_trait_pred.fndef_sig()));
1020                }
1021            }
1022            // Continue to the parent if we didn't find a match
1023            def_id = generic_predicates.parent;
1024        }
1025
1026        span_bug!(
1027            span,
1028            "cannot find self_ty_fn_sig for {:?} with self_ty = {self_ty:?}",
1029            self.checker_id
1030        );
1031    }
1032
1033    fn check_fn_trait_clause(
1034        &mut self,
1035        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1036        poly_fn_trait_pred: &Binder<FnTraitPredicate>,
1037        span: Span,
1038    ) -> Result {
1039        let self_ty = poly_fn_trait_pred
1040            .skip_binder_ref()
1041            .self_ty
1042            .as_bty_skipping_existentials();
1043        let oblig_sig = poly_fn_trait_pred.map_ref(|fn_trait_pred| fn_trait_pred.fndef_sig());
1044        match self_ty {
1045            Some(BaseTy::Closure(def_id, _, _)) => {
1046                let Some(poly_sig) = self.inherited.closures.get(def_id).cloned() else {
1047                    span_bug!(span, "missing template for closure {def_id:?}");
1048                };
1049                check_fn_subtyping(infcx, SubFn::Mono(poly_sig.clone()), &oblig_sig, span)
1050                    .with_span(span)?;
1051            }
1052            Some(BaseTy::FnDef(def_id, args)) => {
1053                // Generates "function subtyping" obligations between the (super-type) `oblig_sig` in the `fn_trait_pred`
1054                // and the (sub-type) corresponding to the signature of `def_id + args`.
1055                // See `tests/neg/surface/fndef00.rs`
1056                let sub_sig = self.genv.fn_sig(def_id).with_span(span)?;
1057                check_fn_subtyping(
1058                    infcx,
1059                    SubFn::Poly(*def_id, sub_sig, args.clone()),
1060                    &oblig_sig,
1061                    span,
1062                )
1063                .with_span(span)?;
1064            }
1065            Some(BaseTy::FnPtr(sub_sig)) => {
1066                check_fn_subtyping(infcx, SubFn::Mono(sub_sig.clone()), &oblig_sig, span)
1067                    .with_span(span)?;
1068            }
1069
1070            // Some(self_ty) => {
1071            Some(self_ty @ BaseTy::Param(_)) => {
1072                // Step 1. Find matching clause and turn it into a FnSig
1073                let tcx = self.genv.tcx();
1074                let self_ty = self_ty.to_rustc(tcx);
1075                let sub_sig = self.find_self_ty_fn_sig(self_ty, span)?;
1076                // Step 2. Issue the subtyping
1077                check_fn_subtyping(infcx, SubFn::Mono(sub_sig), &oblig_sig, span)
1078                    .with_span(span)?;
1079            }
1080            _ => {}
1081        }
1082        Ok(())
1083    }
1084
1085    fn check_assert(
1086        &mut self,
1087        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1088        env: &mut TypeEnv,
1089        terminator_span: Span,
1090        cond: &Operand<'tcx>,
1091        expected: bool,
1092        msg: &AssertKind,
1093    ) -> InferResult<Guard> {
1094        let ty = self.check_operand(infcx, env, terminator_span, cond)?;
1095        let TyKind::Indexed(BaseTy::Bool, idx) = ty.kind() else {
1096            tracked_span_bug!("unexpected ty `{ty:?}`");
1097        };
1098        let pred = if expected { idx.clone() } else { idx.not() };
1099
1100        let msg = match msg {
1101            AssertKind::DivisionByZero => "possible division by zero",
1102            AssertKind::BoundsCheck => "possible out-of-bounds access",
1103            AssertKind::RemainderByZero => "possible remainder with a divisor of zero",
1104            AssertKind::Overflow(mir::BinOp::Div) => "possible division with overflow",
1105            AssertKind::Overflow(mir::BinOp::Rem) => "possible reminder with overflow",
1106            AssertKind::Overflow(_) => return Ok(Guard::Pred(pred)),
1107        };
1108        infcx
1109            .at(terminator_span)
1110            .check_pred(&pred, ConstrReason::Assert(msg));
1111        Ok(Guard::Pred(pred))
1112    }
1113
1114    /// Checks conditional branching as in a `match` statement. [`SwitchTargets`](https://doc.rust-lang.org/nightly/nightly-rustc/stable_mir/mir/struct.SwitchTargets.html) contains a list of branches - the exact bit value which is being compared and the block to jump to. Using the conditionals, each branch can be checked using the new control flow information.
1115    /// See <https://github.com/flux-rs/flux/pull/840#discussion_r1786543174>
1116    fn check_if(discr_ty: &Ty, targets: &SwitchTargets) -> Vec<(BasicBlock, Guard)> {
1117        let mk = |bits| {
1118            match discr_ty.kind() {
1119                TyKind::Indexed(BaseTy::Bool, idx) => {
1120                    if bits == 0 {
1121                        idx.not()
1122                    } else {
1123                        idx.clone()
1124                    }
1125                }
1126                TyKind::Indexed(bty @ (BaseTy::Int(_) | BaseTy::Uint(_) | BaseTy::Char), idx) => {
1127                    Expr::eq(idx.clone(), Expr::from_bits(bty, bits))
1128                }
1129                _ => tracked_span_bug!("unexpected discr_ty {:?}", discr_ty),
1130            }
1131        };
1132
1133        let mut successors = vec![];
1134
1135        for (bits, bb) in targets.iter() {
1136            successors.push((bb, Guard::Pred(mk(bits))));
1137        }
1138        let otherwise = Expr::and_from_iter(targets.iter().map(|(bits, _)| mk(bits).not()));
1139        successors.push((targets.otherwise(), Guard::Pred(otherwise)));
1140
1141        successors
1142    }
1143
1144    fn check_match(
1145        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1146        env: &mut TypeEnv,
1147        discr_ty: &Ty,
1148        targets: &SwitchTargets,
1149        span: Span,
1150    ) -> Vec<(BasicBlock, Guard)> {
1151        let (adt_def, place) = discr_ty.expect_discr();
1152        let idx = if let Ok(ty) = env.lookup_place(&mut infcx.at(span), place)
1153            && let TyKind::Indexed(_, idx) = ty.kind()
1154        {
1155            Some(idx.clone())
1156        } else {
1157            None
1158        };
1159
1160        let mut successors = vec![];
1161        let mut remaining: FxHashMap<u128, VariantIdx> = adt_def
1162            .discriminants()
1163            .map(|(idx, discr)| (discr, idx))
1164            .collect();
1165        for (bits, bb) in targets.iter() {
1166            let variant_idx = remaining
1167                .remove(&bits)
1168                .expect("value doesn't correspond to any variant");
1169            successors.push((bb, Guard::Match(place.clone(), variant_idx)));
1170        }
1171        let guard = if remaining.len() == 1 {
1172            // If there's only one variant left, we know for sure that this is the one, so can force an unfold
1173            let (_, variant_idx) = remaining
1174                .into_iter()
1175                .next()
1176                .unwrap_or_else(|| tracked_span_bug!());
1177            Guard::Match(place.clone(), variant_idx)
1178        } else if adt_def.sort_def().is_reflected()
1179            && let Some(idx) = idx
1180        {
1181            // If there's more than one variant left, we can only assume the `is_ctor` holds for one of them
1182            let mut cases = vec![];
1183            for (_, variant_idx) in remaining {
1184                let did = adt_def.did();
1185                cases.push(rty::Expr::is_ctor(did, variant_idx, idx.clone()));
1186            }
1187            Guard::Pred(Expr::or_from_iter(cases))
1188        } else {
1189            Guard::None
1190        };
1191        successors.push((targets.otherwise(), guard));
1192
1193        successors
1194    }
1195
1196    fn check_successors(
1197        &mut self,
1198        mut infcx: InferCtxt<'_, 'genv, 'tcx>,
1199        env: TypeEnv,
1200        from: BasicBlock,
1201        terminator_span: Span,
1202        successors: Vec<(BasicBlock, Guard)>,
1203    ) -> Result {
1204        for (target, guard) in successors {
1205            let mut infcx = infcx.branch();
1206            let mut env = env.clone();
1207            match guard {
1208                Guard::None => {}
1209                Guard::Pred(expr) => {
1210                    infcx.assume_pred(&expr);
1211                }
1212                Guard::Match(place, variant_idx) => {
1213                    env.downcast(&mut infcx.at(terminator_span), &place, variant_idx)
1214                        .with_span(terminator_span)?;
1215                }
1216            }
1217            self.check_ghost_statements_at(
1218                &mut infcx,
1219                &mut env,
1220                Point::Edge(from, target),
1221                terminator_span,
1222            )?;
1223            self.check_goto(infcx, env, terminator_span, target)?;
1224        }
1225        Ok(())
1226    }
1227
1228    fn check_goto(
1229        &mut self,
1230        mut infcx: InferCtxt<'_, 'genv, 'tcx>,
1231        mut env: TypeEnv,
1232        span: Span,
1233        target: BasicBlock,
1234    ) -> Result {
1235        if self.is_exit_block(target) {
1236            let location = self.body.terminator_loc(target);
1237            self.check_ghost_statements_at(
1238                &mut infcx,
1239                &mut env,
1240                Point::BeforeLocation(location),
1241                span,
1242            )?;
1243            self.check_ret(&mut infcx, &mut env, span)
1244        } else if self.body.is_join_point(target) {
1245            if M::check_goto_join_point(self, infcx, env, span, target)? {
1246                self.queue.insert(target);
1247            }
1248            Ok(())
1249        } else {
1250            self.check_basic_block(infcx, env, target)
1251        }
1252    }
1253
1254    fn closure_template(
1255        &mut self,
1256        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1257        env: &mut TypeEnv,
1258        stmt_span: Span,
1259        args: &flux_rustc_bridge::ty::GenericArgs,
1260        operands: &[Operand<'tcx>],
1261    ) -> InferResult<(Vec<Ty>, PolyFnSig)> {
1262        let upvar_tys = self
1263            .check_operands(infcx, env, stmt_span, operands)?
1264            .into_iter()
1265            .map(|ty| {
1266                if let TyKind::Ptr(PtrKind::Mut(re), path) = ty.kind() {
1267                    env.ptr_to_ref(
1268                        &mut infcx.at(stmt_span),
1269                        ConstrReason::Other,
1270                        *re,
1271                        path,
1272                        PtrToRefBound::Infer,
1273                    )
1274                } else {
1275                    Ok(ty.clone())
1276                }
1277            })
1278            .try_collect_vec()?;
1279
1280        let closure_args = args.as_closure();
1281        let ty = closure_args.sig_as_fn_ptr_ty();
1282
1283        if let flux_rustc_bridge::ty::TyKind::FnPtr(poly_sig) = ty.kind() {
1284            let poly_sig = poly_sig.unpack_closure_sig();
1285            let poly_sig = self.refine_with_holes(&poly_sig)?;
1286            let poly_sig = poly_sig.hoist_input_binders();
1287            let poly_sig = poly_sig
1288                .replace_holes(|binders, kind| infcx.fresh_infer_var_for_hole(binders, kind));
1289
1290            Ok((upvar_tys, poly_sig))
1291        } else {
1292            bug!("check_rvalue: closure: expected fn_ptr ty, found {ty:?} in {args:?}");
1293        }
1294    }
1295
1296    fn check_closure_body(
1297        &mut self,
1298        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1299        did: &DefId,
1300        upvar_tys: &[Ty],
1301        args: &flux_rustc_bridge::ty::GenericArgs,
1302        poly_sig: &PolyFnSig,
1303    ) -> Result {
1304        let genv = self.genv;
1305        let tcx = genv.tcx();
1306        #[expect(clippy::disallowed_methods, reason = "closures cannot be extern speced")]
1307        let closure_id = did.expect_local();
1308        let span = tcx.def_span(closure_id);
1309        let body = genv.mir(closure_id).with_span(span)?;
1310        let closure_sig = rty::to_closure_sig(tcx, closure_id, upvar_tys, args, poly_sig);
1311        Checker::run(
1312            infcx.change_item(closure_id, &body.infcx),
1313            closure_id,
1314            self.inherited.reborrow(),
1315            closure_sig,
1316        )
1317    }
1318
1319    fn check_rvalue_closure(
1320        &mut self,
1321        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1322        env: &mut TypeEnv,
1323        stmt_span: Span,
1324        did: &DefId,
1325        args: &flux_rustc_bridge::ty::GenericArgs,
1326        operands: &[Operand<'tcx>],
1327    ) -> Result<Ty> {
1328        // (1) Create the closure template
1329        let (upvar_tys, poly_sig) = self
1330            .closure_template(infcx, env, stmt_span, args, operands)
1331            .with_span(stmt_span)?;
1332        // (2) Check the closure body against the template
1333        self.check_closure_body(infcx, did, &upvar_tys, args, &poly_sig)?;
1334        // (3) "Save" the closure type in the `closures` map
1335        self.inherited.closures.insert(*did, poly_sig);
1336        // (4) Return the closure type
1337        Ok(Ty::closure(*did, upvar_tys, args))
1338    }
1339
1340    fn check_rvalue(
1341        &mut self,
1342        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1343        env: &mut TypeEnv,
1344        stmt_span: Span,
1345        rvalue: &Rvalue<'tcx>,
1346    ) -> Result<Ty> {
1347        let genv = self.genv;
1348        match rvalue {
1349            Rvalue::Use(operand) => {
1350                self.check_operand(infcx, env, stmt_span, operand)
1351                    .with_span(stmt_span)
1352            }
1353            Rvalue::Repeat(operand, c) => {
1354                let ty = self
1355                    .check_operand(infcx, env, stmt_span, operand)
1356                    .with_span(stmt_span)?;
1357                Ok(Ty::array(ty, c.clone()))
1358            }
1359            Rvalue::Ref(r, BorrowKind::Mut { .. }, place) => {
1360                env.borrow(&mut infcx.at(stmt_span), *r, Mutability::Mut, place)
1361                    .with_span(stmt_span)
1362            }
1363            Rvalue::Ref(r, BorrowKind::Shared | BorrowKind::Fake(..), place) => {
1364                env.borrow(&mut infcx.at(stmt_span), *r, Mutability::Not, place)
1365                    .with_span(stmt_span)
1366            }
1367
1368            Rvalue::RawPtr(mir::RawPtrKind::FakeForPtrMetadata, place) => {
1369                // see tests/tests/neg/surface/slice02.rs for what happens without unfolding here.
1370                env.unfold(infcx, place, stmt_span).with_span(stmt_span)?;
1371                let ty = env
1372                    .lookup_place(&mut infcx.at(stmt_span), place)
1373                    .with_span(stmt_span)?;
1374                let ty = BaseTy::RawPtrMetadata(ty).to_ty();
1375                Ok(ty)
1376            }
1377            Rvalue::RawPtr(kind, place) => {
1378                // ignore any refinements on the type stored at place
1379                let ty = &env.lookup_rust_ty(genv, place).with_span(stmt_span)?;
1380                let ty = self.refine_default(ty).with_span(stmt_span)?;
1381                let ty = BaseTy::RawPtr(ty, kind.to_mutbl_lossy()).to_ty();
1382                Ok(ty)
1383            }
1384            Rvalue::Cast(kind, op, to) => {
1385                let from = self
1386                    .check_operand(infcx, env, stmt_span, op)
1387                    .with_span(stmt_span)?;
1388                self.check_cast(infcx, env, stmt_span, *kind, &from, to)
1389                    .with_span(stmt_span)
1390            }
1391            Rvalue::BinaryOp(bin_op, op1, op2) => {
1392                self.check_binary_op(infcx, env, stmt_span, *bin_op, op1, op2)
1393                    .with_span(stmt_span)
1394            }
1395            Rvalue::NullaryOp(null_op, ty) => Ok(self.check_nullary_op(*null_op, ty)),
1396            Rvalue::UnaryOp(UnOp::PtrMetadata, Operand::Copy(place))
1397            | Rvalue::UnaryOp(UnOp::PtrMetadata, Operand::Move(place)) => {
1398                self.check_raw_ptr_metadata(infcx, env, stmt_span, place)
1399            }
1400            Rvalue::UnaryOp(un_op, op) => {
1401                self.check_unary_op(infcx, env, stmt_span, *un_op, op)
1402                    .with_span(stmt_span)
1403            }
1404            Rvalue::Discriminant(place) => {
1405                let ty = env
1406                    .lookup_place(&mut infcx.at(stmt_span), place)
1407                    .with_span(stmt_span)?;
1408                // HACK(nilehmann, mut-ref-unfolding) place should be unfolded here.
1409                let (adt_def, ..) = ty
1410                    .as_bty_skipping_existentials()
1411                    .unwrap_or_else(|| tracked_span_bug!())
1412                    .expect_adt();
1413                Ok(Ty::discr(adt_def.clone(), place.clone()))
1414            }
1415            Rvalue::Aggregate(
1416                AggregateKind::Adt(def_id, variant_idx, args, _, field_idx),
1417                operands,
1418            ) => {
1419                let actuals = self
1420                    .check_operands(infcx, env, stmt_span, operands)
1421                    .with_span(stmt_span)?;
1422                let sig = genv
1423                    .variant_sig(*def_id, *variant_idx)
1424                    .with_span(stmt_span)?
1425                    .ok_or_query_err(*def_id)
1426                    .with_span(stmt_span)?
1427                    .to_poly_fn_sig(*field_idx);
1428
1429                let args = instantiate_args_for_constructor(
1430                    genv,
1431                    self.checker_id.root_id().to_def_id(),
1432                    *def_id,
1433                    args,
1434                )
1435                .with_span(stmt_span)?;
1436                self.check_call(infcx, env, stmt_span, Some(*def_id), sig, &args, &actuals)
1437                    .map(|resolved_call| resolved_call.output)
1438            }
1439            Rvalue::Aggregate(AggregateKind::Array(arr_ty), operands) => {
1440                let args = self
1441                    .check_operands(infcx, env, stmt_span, operands)
1442                    .with_span(stmt_span)?;
1443                let arr_ty = self.refine_with_holes(arr_ty).with_span(stmt_span)?;
1444                self.check_mk_array(infcx, env, stmt_span, &args, arr_ty)
1445                    .with_span(stmt_span)
1446            }
1447            Rvalue::Aggregate(AggregateKind::Tuple, args) => {
1448                let tys = self
1449                    .check_operands(infcx, env, stmt_span, args)
1450                    .with_span(stmt_span)?;
1451                Ok(Ty::tuple(tys))
1452            }
1453            Rvalue::Aggregate(AggregateKind::Closure(did, args), operands) => {
1454                self.check_rvalue_closure(infcx, env, stmt_span, did, args, operands)
1455            }
1456            Rvalue::Aggregate(AggregateKind::Coroutine(did, args), ops) => {
1457                let args = args.as_coroutine();
1458                let resume_ty = self.refine_default(args.resume_ty()).with_span(stmt_span)?;
1459                let upvar_tys = self
1460                    .check_operands(infcx, env, stmt_span, ops)
1461                    .with_span(stmt_span)?;
1462                Ok(Ty::coroutine(*did, resume_ty, upvar_tys.into()))
1463            }
1464            Rvalue::ShallowInitBox(operand, _) => {
1465                self.check_operand(infcx, env, stmt_span, operand)
1466                    .with_span(stmt_span)?;
1467                Ty::mk_box_with_default_alloc(self.genv, Ty::uninit()).with_span(stmt_span)
1468            }
1469        }
1470    }
1471
1472    fn check_raw_ptr_metadata(
1473        &mut self,
1474        infcx: &mut InferCtxt,
1475        env: &mut TypeEnv,
1476        stmt_span: Span,
1477        place: &Place,
1478    ) -> Result<Ty> {
1479        let ty = env
1480            .lookup_place(&mut infcx.at(stmt_span), place)
1481            .with_span(stmt_span)?;
1482        let ty = match ty.kind() {
1483            TyKind::Indexed(BaseTy::RawPtrMetadata(ty), _)
1484            | TyKind::Indexed(BaseTy::Ref(_, ty, _), _) => ty,
1485            _ => tracked_span_bug!("check_metadata: bug! unexpected type `{ty:?}`"),
1486        };
1487        match ty.kind() {
1488            TyKind::Indexed(BaseTy::Array(_, len), _) => {
1489                let idx = Expr::from_const(self.genv.tcx(), len);
1490                Ok(Ty::indexed(BaseTy::Uint(UintTy::Usize), idx))
1491            }
1492            TyKind::Indexed(BaseTy::Slice(_), len) => {
1493                Ok(Ty::indexed(BaseTy::Uint(UintTy::Usize), len.clone()))
1494            }
1495            _ => Ok(Ty::unit()),
1496        }
1497    }
1498
1499    fn check_binary_op(
1500        &mut self,
1501        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1502        env: &mut TypeEnv,
1503        stmt_span: Span,
1504        bin_op: mir::BinOp,
1505        op1: &Operand<'tcx>,
1506        op2: &Operand<'tcx>,
1507    ) -> InferResult<Ty> {
1508        let ty1 = self.check_operand(infcx, env, stmt_span, op1)?;
1509        let ty2 = self.check_operand(infcx, env, stmt_span, op2)?;
1510
1511        match (ty1.kind(), ty2.kind()) {
1512            (TyKind::Indexed(bty1, idx1), TyKind::Indexed(bty2, idx2)) => {
1513                let rule =
1514                    primops::match_bin_op(bin_op, bty1, idx1, bty2, idx2, infcx.check_overflow);
1515                if let Some(pre) = rule.precondition {
1516                    infcx.at(stmt_span).check_pred(pre.pred, pre.reason);
1517                }
1518
1519                Ok(rule.output_type)
1520            }
1521            _ => tracked_span_bug!("incompatible types: `{ty1:?}` `{ty2:?}`"),
1522        }
1523    }
1524
1525    fn check_nullary_op(&self, null_op: mir::NullOp, _ty: &ty::Ty) -> Ty {
1526        match null_op {
1527            mir::NullOp::SizeOf | mir::NullOp::AlignOf => {
1528                // We could try to get the layout of type to index this with the actual value, but
1529                // this enough for now. Revisit if we ever need the precision.
1530                Ty::uint(UintTy::Usize)
1531            }
1532        }
1533    }
1534
1535    fn check_unary_op(
1536        &mut self,
1537        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1538        env: &mut TypeEnv,
1539        stmt_span: Span,
1540        un_op: mir::UnOp,
1541        op: &Operand<'tcx>,
1542    ) -> InferResult<Ty> {
1543        let ty = self.check_operand(infcx, env, stmt_span, op)?;
1544        match ty.kind() {
1545            TyKind::Indexed(bty, idx) => {
1546                let rule = primops::match_un_op(un_op, bty, idx, infcx.check_overflow);
1547                if let Some(pre) = rule.precondition {
1548                    infcx.at(stmt_span).check_pred(pre.pred, pre.reason);
1549                }
1550                Ok(rule.output_type)
1551            }
1552            _ => tracked_span_bug!("invalid type for unary operator `{un_op:?}` `{ty:?}`"),
1553        }
1554    }
1555
1556    fn check_mk_array(
1557        &mut self,
1558        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1559        env: &mut TypeEnv,
1560        stmt_span: Span,
1561        args: &[Ty],
1562        arr_ty: Ty,
1563    ) -> InferResult<Ty> {
1564        let arr_ty = infcx.ensure_resolved_evars(|infcx| {
1565            let arr_ty =
1566                arr_ty.replace_holes(|binders, kind| infcx.fresh_infer_var_for_hole(binders, kind));
1567
1568            let (arr_ty, pred) = arr_ty.unconstr();
1569            let mut at = infcx.at(stmt_span);
1570            at.check_pred(&pred, ConstrReason::Other);
1571            for ty in args {
1572                at.subtyping_with_env(env, ty, &arr_ty, ConstrReason::Other)?;
1573            }
1574            Ok(arr_ty)
1575        })?;
1576        let arr_ty = infcx.fully_resolve_evars(&arr_ty);
1577
1578        Ok(Ty::array(arr_ty, rty::Const::from_usize(self.genv.tcx(), args.len())))
1579    }
1580
1581    fn check_cast(
1582        &self,
1583        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1584        env: &mut TypeEnv,
1585        stmt_span: Span,
1586        kind: CastKind,
1587        from: &Ty,
1588        to: &ty::Ty,
1589    ) -> InferResult<Ty> {
1590        use ty::TyKind as RustTy;
1591        let ty = match kind {
1592            CastKind::PointerExposeProvenance => {
1593                match to.kind() {
1594                    RustTy::Int(int_ty) => Ty::int(*int_ty),
1595                    RustTy::Uint(uint_ty) => Ty::uint(*uint_ty),
1596                    _ => tracked_span_bug!("unsupported PointerExposeProvenance cast"),
1597                }
1598            }
1599            CastKind::IntToInt => {
1600                match (from.kind(), to.kind()) {
1601                    (Bool!(idx), RustTy::Int(int_ty)) => bool_int_cast(idx, *int_ty),
1602                    (Bool!(idx), RustTy::Uint(uint_ty)) => bool_uint_cast(idx, *uint_ty),
1603                    (Int!(int_ty1, idx), RustTy::Int(int_ty2)) => {
1604                        int_int_cast(idx, *int_ty1, *int_ty2)
1605                    }
1606                    (Uint!(uint_ty1, idx), RustTy::Uint(uint_ty2)) => {
1607                        uint_uint_cast(idx, *uint_ty1, *uint_ty2)
1608                    }
1609                    (Uint!(uint_ty, idx), RustTy::Int(int_ty)) => {
1610                        uint_int_cast(idx, *uint_ty, *int_ty)
1611                    }
1612                    (Int!(_, _), RustTy::Uint(uint_ty)) => Ty::uint(*uint_ty),
1613                    (TyKind::Discr(adt_def, _), RustTy::Int(int_ty)) => {
1614                        Self::discr_to_int_cast(adt_def, BaseTy::Int(*int_ty))
1615                    }
1616                    (TyKind::Discr(adt_def, _place), RustTy::Uint(uint_ty)) => {
1617                        Self::discr_to_int_cast(adt_def, BaseTy::Uint(*uint_ty))
1618                    }
1619                    (Char!(idx), RustTy::Uint(uint_ty)) => char_uint_cast(idx, *uint_ty),
1620                    (Uint!(_, idx), RustTy::Char) => uint_char_cast(idx),
1621                    _ => {
1622                        tracked_span_bug!("invalid int to int cast {from:?} --> {to:?}")
1623                    }
1624                }
1625            }
1626            CastKind::PointerCoercion(mir::PointerCast::Unsize) => {
1627                self.check_unsize_cast(infcx, env, stmt_span, from, to)?
1628            }
1629            CastKind::FloatToInt
1630            | CastKind::IntToFloat
1631            | CastKind::PtrToPtr
1632            | CastKind::PointerCoercion(mir::PointerCast::MutToConstPointer)
1633            | CastKind::PointerCoercion(mir::PointerCast::ClosureFnPointer)
1634            | CastKind::PointerWithExposedProvenance => self.refine_default(to)?,
1635            CastKind::PointerCoercion(mir::PointerCast::ReifyFnPointer) => {
1636                let to = self.refine_default(to)?;
1637                if let TyKind::Indexed(rty::BaseTy::FnDef(def_id, args), _) = from.kind()
1638                    && let TyKind::Indexed(BaseTy::FnPtr(super_sig), _) = to.kind()
1639                {
1640                    let current_did = infcx.def_id;
1641                    let sub_sig =
1642                        SubFn::Poly(current_did, infcx.genv.fn_sig(*def_id)?, args.clone());
1643                    // TODO:CLOSURE:2 TODO(RJ) dicey maneuver? assumes that sig_b is unrefined?
1644                    check_fn_subtyping(infcx, sub_sig, super_sig, stmt_span)?;
1645                    to
1646                } else {
1647                    tracked_span_bug!("invalid cast from `{from:?}` to `{to:?}`")
1648                }
1649            }
1650        };
1651        Ok(ty)
1652    }
1653
1654    fn discr_to_int_cast(adt_def: &AdtDef, bty: BaseTy) -> Ty {
1655        // TODO: This could be a giant disjunction, maybe better (if less precise) to use the interval?
1656        let vals = adt_def
1657            .discriminants()
1658            .map(|(_, idx)| Expr::eq(Expr::nu(), Expr::from_bits(&bty, idx)))
1659            .collect_vec();
1660        Ty::exists_with_constr(bty, Expr::or_from_iter(vals))
1661    }
1662
1663    fn check_unsize_cast(
1664        &self,
1665        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1666        env: &mut TypeEnv,
1667        span: Span,
1668        src: &Ty,
1669        dst: &ty::Ty,
1670    ) -> InferResult<Ty> {
1671        // Convert `ptr` to `&mut`
1672        let src = if let TyKind::Ptr(PtrKind::Mut(re), path) = src.kind() {
1673            env.ptr_to_ref(
1674                &mut infcx.at(span),
1675                ConstrReason::Other,
1676                *re,
1677                path,
1678                PtrToRefBound::Identity,
1679            )?
1680        } else {
1681            src.clone()
1682        };
1683
1684        if let ty::TyKind::Ref(_, deref_ty, _) = dst.kind()
1685            && let ty::TyKind::Dynamic(..) = deref_ty.kind()
1686        {
1687            return Ok(self.refine_default(dst)?);
1688        }
1689
1690        // `&mut [T; n] -> &mut [T]` or `&[T; n] -> &[T]`
1691        if let TyKind::Indexed(BaseTy::Ref(_, deref_ty, _), _) = src.kind()
1692            && let TyKind::Indexed(BaseTy::Array(arr_ty, arr_len), _) = deref_ty.kind()
1693            && let ty::TyKind::Ref(re, _, mutbl) = dst.kind()
1694        {
1695            let idx = Expr::from_const(self.genv.tcx(), arr_len);
1696            Ok(Ty::mk_ref(*re, Ty::indexed(BaseTy::Slice(arr_ty.clone()), idx), *mutbl))
1697
1698        // `Box<[T; n]> -> Box<[T]>`
1699        } else if let TyKind::Indexed(BaseTy::Adt(adt_def, args), _) = src.kind()
1700            && adt_def.is_box()
1701            && let (deref_ty, alloc_ty) = args.box_args()
1702            && let TyKind::Indexed(BaseTy::Array(arr_ty, arr_len), _) = deref_ty.kind()
1703        {
1704            let idx = Expr::from_const(self.genv.tcx(), arr_len);
1705            Ok(Ty::mk_box(
1706                self.genv,
1707                Ty::indexed(BaseTy::Slice(arr_ty.clone()), idx),
1708                alloc_ty.clone(),
1709            )?)
1710        } else {
1711            Err(query_bug!("unsupported unsize cast from `{src:?}` to `{dst:?}`"))?
1712        }
1713    }
1714
1715    fn check_operands(
1716        &mut self,
1717        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1718        env: &mut TypeEnv,
1719        span: Span,
1720        operands: &[Operand<'tcx>],
1721    ) -> InferResult<Vec<Ty>> {
1722        operands
1723            .iter()
1724            .map(|op| self.check_operand(infcx, env, span, op))
1725            .try_collect()
1726    }
1727
1728    fn check_operand(
1729        &mut self,
1730        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1731        env: &mut TypeEnv,
1732        span: Span,
1733        operand: &Operand<'tcx>,
1734    ) -> InferResult<Ty> {
1735        let ty = match operand {
1736            Operand::Copy(p) => env.lookup_place(&mut infcx.at(span), p)?,
1737            Operand::Move(p) => env.move_place(&mut infcx.at(span), p)?,
1738            Operand::Constant(c) => self.check_constant(infcx, c)?,
1739        };
1740        Ok(infcx.hoister(true).hoist(&ty))
1741    }
1742
1743    fn check_constant(
1744        &mut self,
1745        infcx: &InferCtxt<'_, 'genv, 'tcx>,
1746        constant: &ConstOperand<'tcx>,
1747    ) -> QueryResult<Ty> {
1748        use rustc_middle::mir::Const;
1749        match constant.const_ {
1750            Const::Ty(ty, cst) => self.check_ty_const(constant, cst, ty)?,
1751            Const::Val(val, ty) => self.check_const_val(val, ty),
1752            Const::Unevaluated(uneval, ty) => {
1753                self.check_uneval_const(infcx, constant, uneval, ty)?
1754            }
1755        }
1756        .map_or_else(|| self.refine_default(&constant.ty), Ok)
1757    }
1758
1759    fn check_ty_const(
1760        &mut self,
1761        constant: &ConstOperand<'tcx>,
1762        cst: rustc_middle::ty::Const<'tcx>,
1763        ty: rustc_middle::ty::Ty<'tcx>,
1764    ) -> QueryResult<Option<Ty>> {
1765        use rustc_middle::ty::ConstKind;
1766        match cst.kind() {
1767            ConstKind::Param(param) => {
1768                let idx = Expr::const_generic(param);
1769                let ctor = self
1770                    .default_refiner
1771                    .refine_ty_or_base(&constant.ty)?
1772                    .expect_base();
1773                Ok(Some(ctor.replace_bound_reft(&idx).to_ty()))
1774            }
1775            ConstKind::Value(val_tree) => {
1776                let val = self.genv.tcx().valtree_to_const_val(val_tree);
1777                Ok(self.check_const_val(val, ty))
1778            }
1779            _ => Ok(None),
1780        }
1781    }
1782
1783    fn check_const_val(
1784        &mut self,
1785        val: rustc_middle::mir::ConstValue,
1786        ty: rustc_middle::ty::Ty<'tcx>,
1787    ) -> Option<Ty> {
1788        use rustc_middle::{mir::ConstValue, ty};
1789        match val {
1790            ConstValue::Scalar(scalar) => self.check_scalar(scalar, ty),
1791            ConstValue::ZeroSized if ty.is_unit() => Some(Ty::unit()),
1792            ConstValue::Slice { .. } => {
1793                if let ty::Ref(_, ref_ty, Mutability::Not) = ty.kind()
1794                    && ref_ty.is_str()
1795                    && let Some(data) = val.try_get_slice_bytes_for_diagnostics(self.genv.tcx())
1796                {
1797                    let str = String::from_utf8_lossy(data);
1798                    let idx = Expr::constant(Constant::Str(Symbol::intern(&str)));
1799                    Some(Ty::mk_ref(ReErased, Ty::indexed(BaseTy::Str, idx), Mutability::Not))
1800                } else {
1801                    None
1802                }
1803            }
1804            _ => None,
1805        }
1806    }
1807
1808    fn check_uneval_const(
1809        &mut self,
1810        infcx: &InferCtxt<'_, 'genv, 'tcx>,
1811        constant: &ConstOperand<'tcx>,
1812        uneval: rustc_middle::mir::UnevaluatedConst<'tcx>,
1813        ty: rustc_middle::ty::Ty<'tcx>,
1814    ) -> QueryResult<Option<Ty>> {
1815        // 1. Use template for promoted constants, if applicable
1816        if let Some(promoted) = uneval.promoted
1817            && let Some(ty) = self.promoted.get(promoted)
1818        {
1819            return Ok(Some(ty.clone()));
1820        }
1821
1822        // 2. `Genv::constant_info` cannot handle constants with generics, so, we evaluate
1823        //    them here. These mostly come from inline consts, e.g., `const { 1 + 1 }`, because
1824        //    the generic_const_items feature is unstable.
1825        if !uneval.args.is_empty() {
1826            let tcx = self.genv.tcx();
1827            let param_env = tcx.param_env(self.checker_id.root_id());
1828            let typing_env = infcx.region_infcx.typing_env(param_env);
1829            if let Ok(val) = tcx.const_eval_resolve(typing_env, uneval, constant.span) {
1830                return Ok(self.check_const_val(val, ty));
1831            } else {
1832                return Ok(None);
1833            }
1834        }
1835
1836        // 3. Try to see if we have `consant_info` for it.
1837        if let rty::TyOrBase::Base(ctor) = self.default_refiner.refine_ty_or_base(&constant.ty)?
1838            && let rty::ConstantInfo::Interpreted(idx, _) = self.genv.constant_info(uneval.def)?
1839        {
1840            return Ok(Some(ctor.replace_bound_reft(&idx).to_ty()));
1841        }
1842
1843        Ok(None)
1844    }
1845
1846    fn check_scalar(
1847        &mut self,
1848        scalar: rustc_middle::mir::interpret::Scalar,
1849        ty: rustc_middle::ty::Ty<'tcx>,
1850    ) -> Option<Ty> {
1851        use rustc_middle::mir::interpret::Scalar;
1852        match scalar {
1853            Scalar::Int(scalar_int) => self.check_scalar_int(scalar_int, ty),
1854            Scalar::Ptr(..) => None,
1855        }
1856    }
1857
1858    fn check_scalar_int(
1859        &mut self,
1860        scalar: rustc_middle::ty::ScalarInt,
1861        ty: rustc_middle::ty::Ty<'tcx>,
1862    ) -> Option<Ty> {
1863        use flux_rustc_bridge::const_eval::{scalar_to_int, scalar_to_uint};
1864        use rustc_middle::ty;
1865
1866        let tcx = self.genv.tcx();
1867
1868        match ty.kind() {
1869            ty::Int(int_ty) => {
1870                let idx = Expr::constant(Constant::from(scalar_to_int(tcx, scalar, *int_ty)));
1871                Some(Ty::indexed(BaseTy::Int(*int_ty), idx))
1872            }
1873            ty::Uint(uint_ty) => {
1874                let idx = Expr::constant(Constant::from(scalar_to_uint(tcx, scalar, *uint_ty)));
1875                Some(Ty::indexed(BaseTy::Uint(*uint_ty), idx))
1876            }
1877            ty::Float(float_ty) => Some(Ty::float(*float_ty)),
1878            ty::Char => {
1879                let idx = Expr::constant(Constant::Char(scalar.try_into().unwrap()));
1880                Some(Ty::indexed(BaseTy::Char, idx))
1881            }
1882            ty::Bool => {
1883                let idx = Expr::constant(Constant::Bool(scalar.try_to_bool().unwrap()));
1884                Some(Ty::indexed(BaseTy::Bool, idx))
1885            }
1886            // ty::Tuple(tys) if tys.is_empty() => Constant::Unit,
1887            _ => None,
1888        }
1889    }
1890
1891    fn check_ghost_statements_at(
1892        &mut self,
1893        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1894        env: &mut TypeEnv,
1895        point: Point,
1896        span: Span,
1897    ) -> Result {
1898        bug::track_span(span, || {
1899            for stmt in self.ghost_stmts().statements_at(point) {
1900                self.check_ghost_statement(infcx, env, stmt, span)
1901                    .with_span(span)?;
1902            }
1903            Ok(())
1904        })
1905    }
1906
1907    fn check_ghost_statement(
1908        &mut self,
1909        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1910        env: &mut TypeEnv,
1911        stmt: &GhostStatement,
1912        span: Span,
1913    ) -> InferResult {
1914        dbg::statement!("start", stmt, infcx, env, span, &self);
1915        match stmt {
1916            GhostStatement::Fold(place) => {
1917                env.fold(&mut infcx.at(span), place)?;
1918            }
1919            GhostStatement::Unfold(place) => {
1920                env.unfold(infcx, place, span)?;
1921            }
1922            GhostStatement::Unblock(place) => env.unblock(infcx, place),
1923            GhostStatement::PtrToRef(place) => {
1924                env.ptr_to_ref_at_place(&mut infcx.at(span), place)?;
1925            }
1926        }
1927        dbg::statement!("end", stmt, infcx, env, span, &self);
1928        Ok(())
1929    }
1930
1931    #[track_caller]
1932    fn marker_at_dominator(&self, bb: BasicBlock) -> &Marker {
1933        marker_at_dominator(self.body, &self.markers, bb)
1934    }
1935
1936    fn dominators(&self) -> &'ck Dominators<BasicBlock> {
1937        self.body.dominators()
1938    }
1939
1940    fn ghost_stmts(&self) -> &'ck GhostStatements {
1941        &self.inherited.ghost_stmts[&self.checker_id]
1942    }
1943
1944    fn refine_default<T: Refine>(&self, ty: &T) -> QueryResult<T::Output> {
1945        ty.refine(&self.default_refiner)
1946    }
1947
1948    fn refine_with_holes<T: Refine>(&self, ty: &T) -> QueryResult<<T as Refine>::Output> {
1949        ty.refine(&Refiner::with_holes(self.genv, self.checker_id.root_id().to_def_id())?)
1950    }
1951}
1952
1953fn instantiate_args_for_fun_call(
1954    genv: GlobalEnv,
1955    caller_id: DefId,
1956    callee_id: DefId,
1957    args: &ty::GenericArgs,
1958) -> QueryResult<Vec<rty::GenericArg>> {
1959    let params_in_clauses = collect_params_in_clauses(genv, callee_id);
1960
1961    let hole_refiner = Refiner::new_for_item(genv, caller_id, |bty| {
1962        let sort = bty.sort();
1963        let bty = bty.shift_in_escaping(1);
1964        let constr = if !sort.is_unit() {
1965            rty::SubsetTy::new(bty, Expr::nu(), Expr::hole(rty::HoleKind::Pred))
1966        } else {
1967            rty::SubsetTy::trivial(bty, Expr::nu())
1968        };
1969        Binder::bind_with_sort(constr, sort)
1970    })?;
1971    let default_refiner = Refiner::default_for_item(genv, caller_id)?;
1972
1973    let callee_generics = genv.generics_of(callee_id)?;
1974    args.iter()
1975        .enumerate()
1976        .map(|(idx, arg)| {
1977            let param = callee_generics.param_at(idx, genv)?;
1978            let refiner =
1979                if params_in_clauses.contains(&idx) { &default_refiner } else { &hole_refiner };
1980            refiner.refine_generic_arg(&param, arg)
1981        })
1982        .collect()
1983}
1984
1985fn instantiate_args_for_constructor(
1986    genv: GlobalEnv,
1987    caller_id: DefId,
1988    adt_id: DefId,
1989    args: &ty::GenericArgs,
1990) -> QueryResult<Vec<rty::GenericArg>> {
1991    let params_in_clauses = collect_params_in_clauses(genv, adt_id);
1992
1993    let adt_generics = genv.generics_of(adt_id)?;
1994    let hole_refiner = Refiner::with_holes(genv, caller_id)?;
1995    let default_refiner = Refiner::default_for_item(genv, caller_id)?;
1996    args.iter()
1997        .enumerate()
1998        .map(|(idx, arg)| {
1999            let param = adt_generics.param_at(idx, genv)?;
2000            let refiner =
2001                if params_in_clauses.contains(&idx) { &default_refiner } else { &hole_refiner };
2002            refiner.refine_generic_arg(&param, arg)
2003        })
2004        .collect()
2005}
2006
2007fn collect_params_in_clauses(genv: GlobalEnv, def_id: DefId) -> FxHashSet<usize> {
2008    let tcx = genv.tcx();
2009    struct Collector {
2010        params: FxHashSet<usize>,
2011    }
2012
2013    impl rustc_middle::ty::TypeVisitor<TyCtxt<'_>> for Collector {
2014        fn visit_ty(&mut self, t: rustc_middle::ty::Ty) {
2015            if let rustc_middle::ty::Param(param_ty) = t.kind() {
2016                self.params.insert(param_ty.index as usize);
2017            }
2018            t.super_visit_with(self);
2019        }
2020    }
2021    let mut vis = Collector { params: Default::default() };
2022
2023    let span = genv.tcx().def_span(def_id);
2024    for (clause, _) in all_predicates_of(tcx, def_id) {
2025        if let Some(trait_pred) = clause.as_trait_clause() {
2026            let trait_id = trait_pred.def_id();
2027            let ignore = [
2028                LangItem::MetaSized,
2029                LangItem::Sized,
2030                LangItem::Tuple,
2031                LangItem::Copy,
2032                LangItem::Destruct,
2033            ];
2034            if ignore
2035                .iter()
2036                .any(|lang_item| tcx.require_lang_item(*lang_item, span) == trait_id)
2037            {
2038                continue;
2039            }
2040
2041            if tcx.fn_trait_kind_from_def_id(trait_id).is_some() {
2042                continue;
2043            }
2044            if tcx.get_diagnostic_item(sym::Hash) == Some(trait_id) {
2045                continue;
2046            }
2047            if tcx.get_diagnostic_item(sym::Eq) == Some(trait_id) {
2048                continue;
2049            }
2050        }
2051        if let Some(proj_pred) = clause.as_projection_clause() {
2052            let assoc_id = proj_pred.item_def_id();
2053            if genv.is_fn_output(assoc_id) {
2054                continue;
2055            }
2056        }
2057        if let Some(outlives_pred) = clause.as_type_outlives_clause() {
2058            // We skip outlives bounds if they are not 'static. A 'static bound means the type
2059            // implements `Any` which makes it unsound to instantiate the argument with refinements.
2060            if outlives_pred.skip_binder().1 != tcx.lifetimes.re_static {
2061                continue;
2062            }
2063        }
2064        clause.visit_with(&mut vis);
2065    }
2066    vis.params
2067}
2068
2069fn all_predicates_of(
2070    tcx: TyCtxt<'_>,
2071    id: DefId,
2072) -> impl Iterator<Item = &(rustc_middle::ty::Clause<'_>, Span)> {
2073    let mut next_id = Some(id);
2074    iter::from_fn(move || {
2075        next_id.take().map(|id| {
2076            let preds = tcx.predicates_of(id);
2077            next_id = preds.parent;
2078            preds.predicates.iter()
2079        })
2080    })
2081    .flatten()
2082}
2083
2084struct SkipConstr;
2085
2086impl TypeFolder for SkipConstr {
2087    fn fold_ty(&mut self, ty: &rty::Ty) -> rty::Ty {
2088        if let rty::TyKind::Constr(_, inner_ty) = ty.kind() {
2089            inner_ty.fold_with(self)
2090        } else {
2091            ty.super_fold_with(self)
2092        }
2093    }
2094}
2095
2096fn is_indexed_mut_skipping_constr(ty: &Ty) -> bool {
2097    let ty = SkipConstr.fold_ty(ty);
2098    if let rty::Ref!(_, inner_ty, Mutability::Mut) = ty.kind()
2099        && let TyKind::Indexed(..) = inner_ty.kind()
2100    {
2101        true
2102    } else {
2103        false
2104    }
2105}
2106
2107/// HACK(nilehmann) This let us infer parameters under mutable references for the simple case
2108/// where the formal argument is of the form `&mut B[@n]`, e.g., the type of the first argument
2109/// to `RVec::get_mut` is `&mut RVec<T>[@n]`. We should remove this after we implement opening of
2110/// mutable references.
2111fn infer_under_mut_ref_hack(rcx: &mut InferCtxt, actuals: &[Ty], fn_sig: &PolyFnSig) -> Vec<Ty> {
2112    iter::zip(actuals, fn_sig.skip_binder_ref().inputs())
2113        .map(|(actual, formal)| {
2114            if let rty::Ref!(re, deref_ty, Mutability::Mut) = actual.kind()
2115                && is_indexed_mut_skipping_constr(formal)
2116            {
2117                rty::Ty::mk_ref(*re, rcx.unpack(deref_ty), Mutability::Mut)
2118            } else {
2119                actual.clone()
2120            }
2121        })
2122        .collect()
2123}
2124
2125impl Mode for ShapeMode {
2126    const NAME: &str = "shape";
2127
2128    fn enter_basic_block<'ck, 'genv, 'tcx>(
2129        ck: &mut Checker<'ck, 'genv, 'tcx, ShapeMode>,
2130        _infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
2131        bb: BasicBlock,
2132    ) -> TypeEnv<'ck> {
2133        ck.inherited.mode.bb_envs[&ck.checker_id][&bb].enter(&ck.body.local_decls)
2134    }
2135
2136    fn check_goto_join_point<'genv, 'tcx>(
2137        ck: &mut Checker<'_, 'genv, 'tcx, ShapeMode>,
2138        _: InferCtxt<'_, 'genv, 'tcx>,
2139        env: TypeEnv,
2140        span: Span,
2141        target: BasicBlock,
2142    ) -> Result<bool> {
2143        let bb_envs = &mut ck.inherited.mode.bb_envs;
2144        let target_bb_env = bb_envs.entry(ck.checker_id).or_default().get(&target);
2145        dbg::shape_goto_enter!(target, env, target_bb_env);
2146
2147        let modified = match bb_envs.entry(ck.checker_id).or_default().entry(target) {
2148            Entry::Occupied(mut entry) => entry.get_mut().join(env, span),
2149            Entry::Vacant(entry) => {
2150                let scope = marker_at_dominator(ck.body, &ck.markers, target)
2151                    .scope()
2152                    .unwrap_or_else(|| tracked_span_bug!());
2153                entry.insert(env.into_infer(scope));
2154                true
2155            }
2156        };
2157
2158        dbg::shape_goto_exit!(target, bb_envs[&ck.checker_id].get(&target));
2159        Ok(modified)
2160    }
2161
2162    fn clear(ck: &mut Checker<ShapeMode>, root: BasicBlock) {
2163        ck.visited.remove(root);
2164        for bb in ck.body.basic_blocks.indices() {
2165            if bb != root && ck.dominators().dominates(root, bb) {
2166                ck.inherited
2167                    .mode
2168                    .bb_envs
2169                    .entry(ck.checker_id)
2170                    .or_default()
2171                    .remove(&bb);
2172                ck.visited.remove(bb);
2173            }
2174        }
2175    }
2176}
2177
2178impl Mode for RefineMode {
2179    const NAME: &str = "refine";
2180
2181    fn enter_basic_block<'ck, 'genv, 'tcx>(
2182        ck: &mut Checker<'ck, 'genv, 'tcx, RefineMode>,
2183        infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
2184        bb: BasicBlock,
2185    ) -> TypeEnv<'ck> {
2186        ck.inherited.mode.bb_envs[&ck.checker_id][&bb].enter(infcx, &ck.body.local_decls)
2187    }
2188
2189    fn check_goto_join_point(
2190        ck: &mut Checker<RefineMode>,
2191        mut infcx: InferCtxt,
2192        env: TypeEnv,
2193        terminator_span: Span,
2194        target: BasicBlock,
2195    ) -> Result<bool> {
2196        let bb_env = &ck.inherited.mode.bb_envs[&ck.checker_id][&target];
2197        tracked_span_dbg_assert_eq!(
2198            &ck.marker_at_dominator(target)
2199                .scope()
2200                .unwrap_or_else(|| tracked_span_bug!()),
2201            bb_env.scope()
2202        );
2203
2204        dbg::refine_goto!(target, infcx, env, bb_env);
2205
2206        env.check_goto(&mut infcx.at(terminator_span), bb_env, target)
2207            .with_span(terminator_span)?;
2208
2209        Ok(!ck.visited.contains(target))
2210    }
2211
2212    fn clear(_ck: &mut Checker<RefineMode>, _bb: BasicBlock) {
2213        bug!();
2214    }
2215}
2216
2217fn bool_int_cast(b: &Expr, int_ty: IntTy) -> Ty {
2218    let idx = Expr::ite(b, 1, 0);
2219    Ty::indexed(BaseTy::Int(int_ty), idx)
2220}
2221
2222/// Unlike [`char_uint_cast`] rust only allows `u8` to `char` casts, which are
2223/// non-lossy, so we can use indexed type directly.
2224fn uint_char_cast(idx: &Expr) -> Ty {
2225    let idx = Expr::cast(rty::Sort::Int, rty::Sort::Char, idx.clone());
2226    Ty::indexed(BaseTy::Char, idx)
2227}
2228
2229fn char_uint_cast(idx: &Expr, uint_ty: UintTy) -> Ty {
2230    let idx = Expr::cast(rty::Sort::Char, rty::Sort::Int, idx.clone());
2231    if uint_bit_width(uint_ty) >= 32 {
2232        // non-lossy cast: uint[cast(idx)]
2233        Ty::indexed(BaseTy::Uint(uint_ty), idx)
2234    } else {
2235        // lossy-cast: uint{v: cast(idx) <= max_value => v == cast(idx) }
2236        guarded_uint_ty(&idx, uint_ty)
2237    }
2238}
2239
2240fn bool_uint_cast(b: &Expr, uint_ty: UintTy) -> Ty {
2241    let idx = Expr::ite(b, 1, 0);
2242    Ty::indexed(BaseTy::Uint(uint_ty), idx)
2243}
2244
2245fn int_int_cast(idx: &Expr, int_ty1: IntTy, int_ty2: IntTy) -> Ty {
2246    if int_bit_width(int_ty1) <= int_bit_width(int_ty2) {
2247        Ty::indexed(BaseTy::Int(int_ty2), idx.clone())
2248    } else {
2249        Ty::int(int_ty2)
2250    }
2251}
2252
2253fn uint_int_cast(idx: &Expr, uint_ty: UintTy, int_ty: IntTy) -> Ty {
2254    if uint_bit_width(uint_ty) < int_bit_width(int_ty) {
2255        Ty::indexed(BaseTy::Int(int_ty), idx.clone())
2256    } else {
2257        Ty::int(int_ty)
2258    }
2259}
2260
2261fn guarded_uint_ty(idx: &Expr, uint_ty: UintTy) -> Ty {
2262    // uint_ty2{v: idx <= max_value => v == idx }
2263    let max_value = Expr::uint_max(uint_ty);
2264    let guard = Expr::le(idx.clone(), max_value);
2265    let eq = Expr::eq(Expr::nu(), idx.clone());
2266    Ty::exists_with_constr(BaseTy::Uint(uint_ty), Expr::implies(guard, eq))
2267}
2268
2269fn uint_uint_cast(idx: &Expr, uint_ty1: UintTy, uint_ty2: UintTy) -> Ty {
2270    if uint_bit_width(uint_ty1) <= uint_bit_width(uint_ty2) {
2271        Ty::indexed(BaseTy::Uint(uint_ty2), idx.clone())
2272    } else {
2273        guarded_uint_ty(idx, uint_ty2)
2274    }
2275}
2276
2277fn uint_bit_width(uint_ty: UintTy) -> u64 {
2278    uint_ty
2279        .bit_width()
2280        .unwrap_or(config::pointer_width().bits())
2281}
2282
2283fn int_bit_width(int_ty: IntTy) -> u64 {
2284    int_ty.bit_width().unwrap_or(config::pointer_width().bits())
2285}
2286
2287impl ShapeResult {
2288    fn into_bb_envs(
2289        self,
2290        infcx: &mut InferCtxtRoot,
2291    ) -> FxHashMap<CheckerId, FxHashMap<BasicBlock, BasicBlockEnv>> {
2292        self.0
2293            .into_iter()
2294            .map(|(checker_id, shapes)| {
2295                let bb_envs = shapes
2296                    .into_iter()
2297                    .map(|(bb, shape)| (bb, shape.into_bb_env(infcx)))
2298                    .collect();
2299                (checker_id, bb_envs)
2300            })
2301            .collect()
2302    }
2303}
2304
2305fn marker_at_dominator<'a>(
2306    body: &Body,
2307    markers: &'a IndexVec<BasicBlock, Option<Marker>>,
2308    bb: BasicBlock,
2309) -> &'a Marker {
2310    let dominator = body
2311        .dominators()
2312        .immediate_dominator(bb)
2313        .unwrap_or_else(|| tracked_span_bug!());
2314    markers[dominator]
2315        .as_ref()
2316        .unwrap_or_else(|| tracked_span_bug!())
2317}
2318
2319pub(crate) mod errors {
2320    use flux_errors::{E0999, ErrorGuaranteed};
2321    use flux_infer::infer::InferErr;
2322    use flux_macros::Diagnostic;
2323    use flux_middle::{global_env::GlobalEnv, queries::ErrCtxt};
2324    use rustc_errors::Diagnostic;
2325    use rustc_hir::def_id::LocalDefId;
2326    use rustc_span::Span;
2327
2328    use crate::fluent_generated as fluent;
2329
2330    #[derive(Diagnostic)]
2331    #[diag(refineck_panic_error, code = E0999)]
2332    pub(super) struct PanicError {
2333        #[primary_span]
2334        pub(super) span: Span,
2335        pub(super) callee: String,
2336    }
2337
2338    #[derive(Debug)]
2339    pub struct CheckerError {
2340        kind: InferErr,
2341        span: Span,
2342    }
2343
2344    impl CheckerError {
2345        pub fn emit(self, genv: GlobalEnv, fn_def_id: LocalDefId) -> ErrorGuaranteed {
2346            let dcx = genv.sess().dcx().handle();
2347            match self.kind {
2348                InferErr::UnsolvedEvar(_) => {
2349                    let mut diag =
2350                        dcx.struct_span_err(self.span, fluent::refineck_param_inference_error);
2351                    diag.code(E0999);
2352                    diag.emit()
2353                }
2354                InferErr::Query(err) => {
2355                    let level = rustc_errors::Level::Error;
2356                    err.at(ErrCtxt::FnCheck(self.span, fn_def_id))
2357                        .into_diag(dcx, level)
2358                        .emit()
2359                }
2360            }
2361        }
2362    }
2363
2364    pub trait ResultExt<T> {
2365        fn with_span(self, span: Span) -> Result<T, CheckerError>;
2366    }
2367
2368    impl<T, E> ResultExt<T> for Result<T, E>
2369    where
2370        E: Into<InferErr>,
2371    {
2372        fn with_span(self, span: Span) -> Result<T, CheckerError> {
2373            self.map_err(|err| CheckerError { kind: err.into(), span })
2374        }
2375    }
2376}