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