flux_refineck/
checker.rs

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