flux_refineck/
checker.rs

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