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