1use std::{collections::hash_map::Entry, iter, vec};
2
3use flux_common::{
4 bug, dbg, dbg::SpanTrace, index::IndexVec, 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 PanicReason, PanicSpec,
17 global_env::GlobalEnv,
18 pretty::PrettyCx,
19 queries::{QueryResult, try_query},
20 query_bug,
21 rty::{
22 self, AdtDef, AliasReft, BaseTy, BinOp, Binder, Bool, Clause, Constant,
23 CoroutineObligPredicate, EarlyBinder, Expr, FnOutput, FnSig, FnTraitPredicate, GenericArg,
24 GenericArgsExt as _, Int, IntTy, Mutability, Path, PolyFnSig, PtrKind, RefineArgs,
25 RefineArgsExt,
26 Region::ReErased,
27 Sort, SubsetTyCtor, Ty, TyKind, Uint, UintTy, VariantIdx,
28 fold::{TypeFoldable, TypeFolder, TypeSuperFoldable},
29 refining::{Refine, Refiner},
30 },
31};
32use flux_rustc_bridge::{
33 self, ToRustc,
34 mir::{
35 self, AggregateKind, AssertKind, BasicBlock, Body, BodyRoot, BorrowKind, CastKind,
36 ConstOperand, Location, NonDivergingIntrinsic, Operand, Place, Rvalue, START_BLOCK,
37 Statement, StatementKind, Terminator, TerminatorKind, UnOp,
38 },
39 ty::{self, GenericArgsExt as _},
40};
41use itertools::{Itertools, izip};
42use rustc_data_structures::{
43 graph::dominators::Dominators,
44 unord::{UnordMap, UnordSet},
45};
46use rustc_hash::FxHashMap;
47use rustc_hir::{
48 attrs::lang_items::LangItem,
49 def_id::{DefId, LocalDefId},
50};
51use rustc_index::{IndexSlice, bit_set::DenseBitSet};
52use rustc_infer::infer::TyCtxtInferExt;
53use rustc_middle::{
54 mir::{Promoted, SwitchTargets},
55 ty::{TyCtxt, TypeSuperVisitable as _, TypeVisitable as _, TypingMode},
56};
57use rustc_span::{
58 DUMMY_SP, Span, Symbol,
59 sym::{self},
60};
61
62use self::errors::{CheckerError, ResultExt};
63use crate::{
64 checker::mir::RawPtrKind,
65 ghost_statements::{CheckerId, GhostStatement, GhostStatements, Point},
66 primops,
67 queue::WorkQueue,
68 rty::Char,
69 type_env::{BasicBlockEnv, BasicBlockEnvShape, PtrToRefBound, TypeEnv, TypeEnvTrace},
70};
71
72type Result<T = ()> = std::result::Result<T, CheckerError>;
73
74pub(crate) struct Checker<'ck, 'genv, 'tcx, M> {
75 genv: GlobalEnv<'genv, 'tcx>,
76 checker_id: CheckerId,
78 inherited: Inherited<'ck, M>,
79 body: &'ck Body<'tcx>,
80 resume_ty: Option<Ty>,
82 fn_sig: FnSig,
83 markers: IndexVec<BasicBlock, Option<Marker>>,
86 visited: DenseBitSet<BasicBlock>,
87 queue: WorkQueue<'ck>,
88 default_refiner: Refiner<'genv, 'tcx>,
89 promoted: &'ck IndexSlice<Promoted, Ty>,
91}
92
93struct Inherited<'ck, M> {
95 ghost_stmts: &'ck UnordMap<CheckerId, GhostStatements>,
98 mode: &'ck mut M,
99
100 closures: &'ck mut UnordMap<DefId, PolyFnSig>,
104}
105
106#[derive(Debug)]
107struct ResolvedCall {
108 output: Ty,
109 _early_args: Vec<Expr>,
111 _late_args: Vec<Expr>,
113}
114
115impl<'ck, M: Mode> Inherited<'ck, M> {
116 fn new(
117 mode: &'ck mut M,
118 ghost_stmts: &'ck UnordMap<CheckerId, GhostStatements>,
119 closures: &'ck mut UnordMap<DefId, PolyFnSig>,
120 ) -> Self {
121 Self { ghost_stmts, mode, closures }
122 }
123
124 fn reborrow(&mut self) -> Inherited<'_, M> {
125 Inherited { ghost_stmts: self.ghost_stmts, mode: self.mode, closures: self.closures }
126 }
127}
128
129pub(crate) trait Mode: Sized {
130 #[expect(dead_code)]
131 const NAME: &str;
132
133 fn enter_basic_block<'ck, 'genv, 'tcx>(
134 ck: &mut Checker<'ck, 'genv, 'tcx, Self>,
135 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
136 bb: BasicBlock,
137 ) -> TypeEnv<'ck>;
138
139 fn check_goto_join_point<'genv, 'tcx>(
140 ck: &mut Checker<'_, 'genv, 'tcx, Self>,
141 infcx: InferCtxt<'_, 'genv, 'tcx>,
142 env: TypeEnv,
143 terminator_span: Span,
144 target: BasicBlock,
145 ) -> Result<bool>;
146
147 fn clear(ck: &mut Checker<Self>, bb: BasicBlock);
148}
149
150pub(crate) struct ShapeMode {
151 bb_envs: FxHashMap<CheckerId, FxHashMap<BasicBlock, BasicBlockEnvShape>>,
152}
153
154pub(crate) struct RefineMode {
155 bb_envs: FxHashMap<CheckerId, FxHashMap<BasicBlock, BasicBlockEnv>>,
156}
157
158pub(crate) struct ShapeResult(FxHashMap<CheckerId, FxHashMap<BasicBlock, BasicBlockEnvShape>>);
160
161#[derive(Debug)]
163enum Guard {
164 None,
166 Pred(Expr),
168 Match(Place, VariantIdx),
170}
171
172impl<'genv, 'tcx> Checker<'_, 'genv, 'tcx, ShapeMode> {
173 pub(crate) fn run_in_shape_mode<'ck>(
174 genv: GlobalEnv<'genv, 'tcx>,
175 local_id: LocalDefId,
176 ghost_stmts: &'ck UnordMap<CheckerId, GhostStatements>,
177 closures: &'ck mut UnordMap<DefId, PolyFnSig>,
178 opts: InferOpts,
179 poly_sig: &PolyFnSig,
180 ) -> Result<ShapeResult> {
181 let def_id = local_id.to_def_id();
182 dbg::shape_mode_span!(genv.tcx(), local_id).in_scope(|| {
183 let span = genv.tcx().def_span(local_id);
184 let mut mode = ShapeMode { bb_envs: FxHashMap::default() };
185
186 let body = genv.mir(local_id).with_span(span)?;
187
188 let mut root_ctxt = try_query(|| {
190 genv.infcx_root(&body.infcx, opts)
191 .with_dummy_kvars()
192 .identity_for_item(def_id)?
193 .build()
194 })
195 .with_span(span)?;
196
197 let inherited = Inherited::new(&mut mode, ghost_stmts, closures);
198
199 let infcx = root_ctxt.infcx(def_id, &body.infcx);
200 Checker::run(infcx, local_id, inherited, poly_sig.clone())?;
201
202 Ok(ShapeResult(mode.bb_envs))
203 })
204 }
205}
206
207impl<'genv, 'tcx> Checker<'_, 'genv, 'tcx, RefineMode> {
208 pub(crate) fn run_in_refine_mode<'ck>(
209 genv: GlobalEnv<'genv, 'tcx>,
210 local_id: LocalDefId,
211 ghost_stmts: &'ck UnordMap<CheckerId, GhostStatements>,
212 closures: &'ck mut UnordMap<DefId, PolyFnSig>,
213 bb_env_shapes: ShapeResult,
214 opts: InferOpts,
215 poly_sig: &PolyFnSig,
216 ) -> Result<InferCtxtRoot<'genv, 'tcx>> {
217 let def_id = local_id.to_def_id();
218 let span = genv.tcx().def_span(def_id);
219
220 let body = genv.mir(local_id).with_span(span)?;
221 let mut root_ctxt = try_query(|| {
222 genv.infcx_root(&body.infcx, opts)
223 .identity_for_item(def_id)?
224 .build()
225 })
226 .with_span(span)?;
227 let bb_envs = bb_env_shapes.into_bb_envs(&mut root_ctxt, &body.body);
228
229 dbg::refine_mode_span!(genv.tcx(), def_id, bb_envs).in_scope(|| {
230 let mut mode = RefineMode { bb_envs };
232 let inherited = Inherited::new(&mut mode, ghost_stmts, closures);
233 let infcx = root_ctxt.infcx(def_id, &body.infcx);
234 Checker::run(infcx, local_id, inherited, poly_sig.clone())?;
235
236 Ok(root_ctxt)
237 })
238 }
239}
240
241#[derive(Debug)]
245pub enum SubFn {
246 Poly(DefId, EarlyBinder<rty::PolyFnSig>, rty::GenericArgs),
247 Mono(rty::PolyFnSig),
248}
249
250impl SubFn {
251 pub fn as_ref(&self) -> &rty::PolyFnSig {
252 match self {
253 SubFn::Poly(_, sig, _) => sig.skip_binder_ref(),
254 SubFn::Mono(sig) => sig,
255 }
256 }
257}
258
259fn check_fn_subtyping(
272 infcx: &mut InferCtxt,
273 sub_sig: SubFn,
274 super_sig: &rty::PolyFnSig,
275 span: Span,
276) -> InferResult {
277 let mut infcx = infcx.branch();
278 let mut infcx = infcx.at(span);
279 let tcx = infcx.genv.tcx();
280
281 let super_sig = super_sig
282 .replace_bound_vars(
283 |_| rty::ReErased,
284 |sort, _, kind| Expr::fvar(infcx.define_bound_reft_var(sort, kind)),
285 )
286 .deeply_normalize(&mut infcx)?;
287
288 let actuals = super_sig
290 .inputs()
291 .iter()
292 .map(|ty| infcx.unpack(ty))
293 .collect_vec();
294
295 let mut env = TypeEnv::empty();
296 let actuals = unfold_local_ptrs(&mut infcx, &mut env, sub_sig.as_ref(), &actuals)?;
297 let actuals = infer_under_mut_ref_hack(&mut infcx, &actuals[..], sub_sig.as_ref());
298
299 let output = infcx.ensure_resolved_evars(|infcx| {
300 let sub_sig = match sub_sig {
303 SubFn::Poly(def_id, early_sig, sub_args) => {
304 let refine_args = infcx.instantiate_refine_args(def_id, &sub_args)?;
305 early_sig.instantiate(tcx, &sub_args, &refine_args)
306 }
307 SubFn::Mono(sig) => sig,
308 };
309 let sub_sig = sub_sig
311 .replace_bound_vars(
312 |_| rty::ReErased,
313 |sort, mode, _| infcx.fresh_infer_var(sort, mode),
314 )
315 .deeply_normalize(infcx)?;
316
317 for requires in super_sig.requires() {
319 infcx.assume_pred(requires);
320 }
321 infcx.check_pred(
322 Expr::implies(super_sig.no_panic(), sub_sig.no_panic()),
323 ConstrReason::Subtype(SubtypeReason::Input),
324 );
325 for (actual, formal) in iter::zip(actuals, sub_sig.inputs()) {
326 let reason = ConstrReason::Subtype(SubtypeReason::Input);
327 infcx.subtyping_with_env(&mut env, &actual, formal, reason)?;
328 }
329 for requires in sub_sig.requires() {
332 let reason = ConstrReason::Subtype(SubtypeReason::Requires);
333 infcx.check_pred(requires, reason);
334 }
335
336 Ok(sub_sig.output())
337 })?;
338
339 let output = infcx
340 .fully_resolve_evars(&output)
341 .replace_bound_refts_with(|sort, _, kind| {
342 Expr::fvar(infcx.define_bound_reft_var(sort, kind))
343 });
344
345 infcx.ensure_resolved_evars(|infcx| {
347 let super_output = super_sig
348 .output()
349 .replace_bound_refts_with(|sort, mode, _| infcx.fresh_infer_var(sort, mode));
350 let reason = ConstrReason::Subtype(SubtypeReason::Output);
351 infcx.subtyping(&output.ret, &super_output.ret, reason)?;
352
353 env.assume_ensures(infcx, &output.ensures, span);
355 fold_local_ptrs(infcx, &mut env, span)?;
356 env.check_ensures(
357 infcx,
358 &super_output.ensures,
359 ConstrReason::Subtype(SubtypeReason::Ensures),
360 )
361 })
362}
363
364pub(crate) fn trait_impl_subtyping<'genv, 'tcx>(
367 genv: GlobalEnv<'genv, 'tcx>,
368 def_id: LocalDefId,
369 opts: InferOpts,
370 span: Span,
371) -> InferResult<Option<InferCtxtRoot<'genv, 'tcx>>> {
372 let tcx = genv.tcx();
373
374 let Some((impl_trait_ref, trait_method_id)) = find_trait_item(genv, def_id)? else {
376 return Ok(None);
377 };
378 let impl_method_id = def_id.to_def_id();
379 if genv.has_trusted_impl(trait_method_id) || genv.has_trusted_impl(impl_method_id) {
381 return Ok(None);
382 }
383
384 let impl_id = tcx.impl_of_assoc(impl_method_id).unwrap();
385 let impl_method_args = GenericArg::identity_for_item(genv, impl_method_id)?;
386 let trait_method_args = impl_method_args.rebase_onto(&tcx, impl_id, &impl_trait_ref.args);
387 let trait_refine_args = RefineArgs::identity_for_item(genv, trait_method_id)?;
388
389 let rustc_infcx = genv
390 .tcx()
391 .infer_ctxt()
392 .with_next_trait_solver(true)
393 .build(TypingMode::non_body_analysis());
394
395 let mut root_ctxt = genv
396 .infcx_root(&rustc_infcx, opts)
397 .with_const_generics(impl_id)?
398 .with_refinement_generics(trait_method_id, &trait_method_args)?
399 .build()?;
400
401 let mut infcx = root_ctxt.infcx(impl_method_id, &rustc_infcx);
402
403 let trait_fn_sig =
404 genv.fn_sig(trait_method_id)?
405 .instantiate(tcx, &trait_method_args, &trait_refine_args);
406 let impl_sig = genv.fn_sig(impl_method_id)?;
407 let sub_sig = SubFn::Poly(impl_method_id, impl_sig, impl_method_args);
408
409 check_fn_subtyping(&mut infcx, sub_sig, &trait_fn_sig, span)?;
410 Ok(Some(root_ctxt))
411}
412
413fn find_trait_item(
414 genv: GlobalEnv<'_, '_>,
415 def_id: LocalDefId,
416) -> QueryResult<Option<(rty::TraitRef, DefId)>> {
417 let tcx = genv.tcx();
418 let def_id = def_id.to_def_id();
419 if let Some(impl_id) = tcx.trait_impl_of_assoc(def_id) {
420 let impl_trait_ref = genv.impl_trait_ref(impl_id)?.instantiate_identity();
421 let trait_item_id = tcx.associated_item(def_id).trait_item_def_id().unwrap();
422 return Ok(Some((impl_trait_ref, trait_item_id)));
423 }
424 Ok(None)
425}
426
427fn unfold_local_ptrs(
438 infcx: &mut InferCtxt,
439 env: &mut TypeEnv,
440 fn_sig: &PolyFnSig,
441 actuals: &[Ty],
442) -> InferResult<Vec<Ty>> {
443 let fn_sig = fn_sig.skip_binder_ref();
445 let mut tys = vec![];
446 for (actual, input) in izip!(actuals, fn_sig.inputs()) {
447 let actual = if let (
448 TyKind::Indexed(BaseTy::Ref(re, bound, Mutability::Mut), _),
449 TyKind::StrgRef(_, _, _),
450 ) = (actual.kind(), input.kind())
451 {
452 let loc = env.unfold_local_ptr(infcx, bound)?;
453 let path1 = Path::new(loc, rty::List::empty());
454 Ty::ptr(PtrKind::Mut(*re), path1)
455 } else {
456 actual.clone()
457 };
458 tys.push(actual);
459 }
460 Ok(tys)
461}
462
463fn fold_local_ptrs(infcx: &mut InferCtxt, env: &mut TypeEnv, span: Span) -> InferResult {
472 let mut at = infcx.at(span);
473 env.fold_local_ptrs(&mut at)
474}
475
476fn promoted_fn_sig(ty: &Ty) -> PolyFnSig {
477 let safety = rustc_hir::Safety::Safe;
478 let abi = rustc_abi::ExternAbi::Rust;
479 let requires = rty::List::empty();
480 let inputs = rty::List::empty();
481 let output =
482 Binder::bind_with_vars(FnOutput::new(ty.clone(), rty::List::empty()), rty::List::empty());
483 let fn_sig = crate::rty::FnSig::new(safety, abi, requires, inputs, output, Expr::tt(), false);
484 PolyFnSig::bind_with_vars(fn_sig, crate::rty::List::empty())
485}
486
487impl<'ck, 'genv, 'tcx, M: Mode> Checker<'ck, 'genv, 'tcx, M> {
488 fn new(
489 genv: GlobalEnv<'genv, 'tcx>,
490 checker_id: CheckerId,
491 inherited: Inherited<'ck, M>,
492 body: &'ck Body<'tcx>,
493 fn_sig: FnSig,
494 promoted: &'ck IndexSlice<Promoted, Ty>,
495 ) -> QueryResult<Self> {
496 let root_id = checker_id.root_id();
497
498 let resume_ty = if let CheckerId::DefId(def_id) = checker_id
499 && genv.tcx().is_coroutine(def_id.to_def_id())
500 {
501 Some(fn_sig.inputs()[1].clone())
502 } else {
503 None
504 };
505
506 let bb_len = body.basic_blocks.len();
507 Ok(Self {
508 checker_id,
509 genv,
510 inherited,
511 body,
512 resume_ty,
513 visited: DenseBitSet::new_empty(bb_len),
514 fn_sig,
515 markers: IndexVec::from_fn_n(|_| None, bb_len),
516 queue: WorkQueue::empty(bb_len, &body.dominator_order_rank),
517 default_refiner: Refiner::default_for_item(genv, root_id.to_def_id())?,
518 promoted,
519 })
520 }
521
522 fn check_body(
523 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
524 checker_id: CheckerId,
525 inherited: Inherited<'ck, M>,
526 body: &'ck Body<'tcx>,
527 poly_sig: PolyFnSig,
528 promoted: &'ck IndexSlice<Promoted, Ty>,
529 ) -> Result {
530 let span = body.span();
531
532 let fn_sig = poly_sig
533 .replace_bound_vars(
534 |_| rty::ReErased,
535 |sort, _, kind| {
536 let name = infcx.define_bound_reft_var(sort, kind);
537 Expr::fvar(name)
538 },
539 )
540 .deeply_normalize(&mut infcx.at(span))
541 .with_span(span)?;
542 let mut env = TypeEnv::new(infcx, body, &fn_sig);
543
544 let mut ck = Checker::new(infcx.genv, checker_id, inherited, body, fn_sig, promoted)
545 .with_span(span)?;
546 ck.check_ghost_statements_at(infcx, &mut env, Point::FunEntry, span)?;
547
548 ck.check_goto(infcx.branch(), env, body.span(), START_BLOCK)?;
549
550 while let Some(bb) = ck.queue.pop() {
551 let visited = ck.visited.contains(bb);
552
553 if visited {
554 M::clear(&mut ck, bb);
555 }
556
557 let marker = ck.marker_at_dominator(bb);
558 let mut infcx = infcx.move_to(marker, visited);
559 let mut env = M::enter_basic_block(&mut ck, &mut infcx, bb);
560 env.unpack(&mut infcx);
561 ck.check_basic_block(infcx, env, bb)?;
562 }
563 Ok(())
564 }
565
566 fn promoted_tys(
568 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
569 def_id: LocalDefId,
570 body_root: &BodyRoot<'tcx>,
571 ) -> QueryResult<IndexVec<Promoted, Ty>> {
572 let hole_refiner = Refiner::with_holes(infcx.genv, def_id.into())?;
573
574 body_root
575 .promoted
576 .iter()
577 .map(|body| {
578 Ok(body
579 .return_ty()
580 .refine(&hole_refiner)?
581 .replace_holes(|binders, kind| infcx.fresh_infer_var_for_hole(binders, kind)))
582 })
583 .collect()
584 }
585
586 fn run(
587 mut infcx: InferCtxt<'_, 'genv, 'tcx>,
588 def_id: LocalDefId,
589 mut inherited: Inherited<'_, M>,
590 poly_sig: PolyFnSig,
591 ) -> Result {
592 let genv = infcx.genv;
593 let span = genv.tcx().def_span(def_id);
594 let body_root = genv.mir(def_id).with_span(span)?;
595
596 let promoted_tys = Self::promoted_tys(&mut infcx, def_id, &body_root).with_span(span)?;
598
599 for (promoted, ty) in promoted_tys.iter_enumerated() {
601 let body = &body_root.promoted[promoted];
602 let poly_sig = promoted_fn_sig(ty);
603 Checker::check_body(
604 &mut infcx,
605 CheckerId::Promoted(def_id, promoted),
606 inherited.reborrow(),
607 body,
608 poly_sig,
609 &promoted_tys,
610 )?;
611 }
612
613 Checker::check_body(
615 &mut infcx,
616 CheckerId::DefId(def_id),
617 inherited,
618 &body_root.body,
619 poly_sig,
620 &promoted_tys,
621 )
622 }
623
624 fn check_basic_block(
625 &mut self,
626 mut infcx: InferCtxt<'_, 'genv, 'tcx>,
627 mut env: TypeEnv,
628 bb: BasicBlock,
629 ) -> Result {
630 dbg::basic_block_start!(bb, infcx, env);
631
632 self.visited.insert(bb);
633 let data = &self.body.basic_blocks[bb];
634 let mut last_stmt_span = None;
635 let mut location = Location { block: bb, statement_index: 0 };
636 for stmt in &data.statements {
637 let span = stmt.source_info.span;
638 self.check_ghost_statements_at(
639 &mut infcx,
640 &mut env,
641 Point::BeforeLocation(location),
642 span,
643 )?;
644 bug::track_span(span, || {
645 dbg::statement!("start", stmt, &infcx, &env, span, &self);
646 self.check_statement(&mut infcx, &mut env, stmt)?;
647 dbg::statement!("end", stmt, &infcx, &env, span, &self);
648 Ok(())
649 })?;
650 if !stmt.is_nop() {
651 last_stmt_span = Some(span);
652 }
653 location = location.successor_within_block();
654 }
655
656 if let Some(terminator) = &data.terminator {
657 let span = terminator.source_info.span;
658 self.check_ghost_statements_at(
659 &mut infcx,
660 &mut env,
661 Point::BeforeLocation(location),
662 span,
663 )?;
664
665 bug::track_span(span, || {
666 dbg::terminator!("start", terminator, infcx, env);
667
668 let successors = self.check_terminator(
669 &mut infcx,
670 &mut env,
671 terminator,
672 location,
673 last_stmt_span,
674 )?;
675 dbg::terminator!("end", terminator, infcx, env);
676
677 self.markers[bb] = Some(infcx.marker());
678 let term_span = last_stmt_span.unwrap_or(span);
679 self.check_successors(infcx, env, bb, term_span, successors)
680 })?;
681 }
682 Ok(())
683 }
684
685 fn check_assign_ty(
686 &mut self,
687 infcx: &mut InferCtxt,
688 env: &mut TypeEnv,
689 place: &Place,
690 ty: Ty,
691 span: Span,
692 ) -> InferResult {
693 let ty = infcx.hoister(true).hoist(&ty);
694 env.assign(&mut infcx.at(span), place, ty)
695 }
696
697 fn check_statement(
698 &mut self,
699 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
700 env: &mut TypeEnv,
701 stmt: &Statement<'tcx>,
702 ) -> Result {
703 let stmt_span = stmt.source_info.span;
704 match &stmt.kind {
705 StatementKind::Assign(place, rvalue) => {
706 let ty = self.check_rvalue(infcx, env, stmt_span, rvalue)?;
707 self.check_assign_ty(infcx, env, place, ty, stmt_span)
708 .with_span(stmt_span)?;
709 }
710 StatementKind::SetDiscriminant { .. } => {
711 }
714 StatementKind::FakeRead(_) => {
715 }
717 StatementKind::AscribeUserType(_, _) => {
718 }
721 StatementKind::PlaceMention(_) => {
722 }
725 StatementKind::Nop => {}
726 StatementKind::Intrinsic(NonDivergingIntrinsic::Assume(op)) => {
727 let _ = self
731 .check_operand(infcx, env, stmt_span, op)
732 .with_span(stmt_span)?;
733 }
734 }
735 Ok(())
736 }
737
738 fn is_exit_block(&self, bb: BasicBlock) -> bool {
739 let data = &self.body.basic_blocks[bb];
740 let is_no_op = data.statements.iter().all(Statement::is_nop);
741 let is_ret = match &data.terminator {
742 None => false,
743 Some(term) => term.is_return(),
744 };
745 is_no_op && is_ret
746 }
747
748 fn check_terminator(
752 &mut self,
753 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
754 env: &mut TypeEnv,
755 terminator: &Terminator<'tcx>,
756 location: Location,
757 last_stmt_span: Option<Span>,
758 ) -> Result<Vec<(BasicBlock, Guard)>> {
759 let source_info = terminator.source_info;
760 let terminator_span = source_info.span;
761 match &terminator.kind {
762 TerminatorKind::Return => {
763 self.check_ret(infcx, env, last_stmt_span.unwrap_or(terminator_span))?;
764 Ok(vec![])
765 }
766 TerminatorKind::Unreachable => Ok(vec![]),
767 TerminatorKind::CoroutineDrop => Ok(vec![]),
768 TerminatorKind::Goto { target } => Ok(vec![(*target, Guard::None)]),
769 TerminatorKind::Yield { resume, resume_arg, .. } => {
770 if let Some(resume_ty) = self.resume_ty.clone() {
771 self.check_assign_ty(infcx, env, resume_arg, resume_ty, terminator_span)
772 .with_span(terminator_span)?;
773 } else {
774 bug!("yield in non-generator function");
775 }
776 Ok(vec![(*resume, Guard::None)])
777 }
778 TerminatorKind::SwitchInt { discr, targets } => {
779 let discr_ty = self
780 .check_operand(infcx, env, terminator_span, discr)
781 .with_span(terminator_span)?;
782 if discr_ty.is_integral() || discr_ty.is_bool() || discr_ty.is_char() {
783 Ok(Self::check_if(&discr_ty, targets))
784 } else {
785 Ok(self.check_match(infcx, env, &discr_ty, targets, terminator_span))
786 }
787 }
788 TerminatorKind::Call { kind, args, destination, target, .. } => {
789 let actuals = self
790 .check_operands(infcx, env, terminator_span, args)
791 .with_span(terminator_span)?;
792 let ret = match kind {
793 mir::CallKind::FnDef { resolved_id, resolved_args, .. } => {
794 let fn_sig = self.genv.fn_sig(*resolved_id).with_span(terminator_span)?;
795 let generic_args = instantiate_args_for_fun_call(
796 self.genv,
797 self.checker_id.root_id().to_def_id(),
798 *resolved_id,
799 &resolved_args.lowered,
800 )
801 .with_span(terminator_span)?;
802 self.check_call(
803 infcx,
804 env,
805 terminator_span,
806 Some(location),
807 Some(*resolved_id),
808 fn_sig,
809 &generic_args,
810 &actuals,
811 )?
812 .output
813 }
814 mir::CallKind::FnPtr { operand, .. } => {
815 let ty = self
816 .check_operand(infcx, env, terminator_span, operand)
817 .with_span(terminator_span)?;
818 if let TyKind::Indexed(BaseTy::FnPtr(fn_sig), _) = infcx.unpack(&ty).kind()
819 {
820 self.check_call(
821 infcx,
822 env,
823 terminator_span,
824 Some(location),
825 None,
826 EarlyBinder(fn_sig.clone()),
827 &[],
828 &actuals,
829 )?
830 .output
831 } else {
832 bug!("TODO: fnptr call {ty:?}")
833 }
834 }
835 };
836
837 let name = destination.name(&self.body.local_names);
838 let ret = infcx.unpack_at_name(name, &ret);
839 infcx.assume_invariants(&ret);
840
841 env.assign(&mut infcx.at(terminator_span), destination, ret)
842 .with_span(terminator_span)?;
843
844 if let Some(target) = target {
845 Ok(vec![(*target, Guard::None)])
846 } else {
847 Ok(vec![])
848 }
849 }
850 TerminatorKind::Assert { cond, expected, target, msg } => {
851 Ok(vec![(
852 *target,
853 self.check_assert(infcx, env, terminator_span, cond, *expected, msg)
854 .with_span(terminator_span)?,
855 )])
856 }
857 TerminatorKind::Drop { place, target, .. } => {
858 let _ = env.move_place(&mut infcx.at(terminator_span), place);
859 Ok(vec![(*target, Guard::None)])
860 }
861 TerminatorKind::FalseEdge { real_target, .. } => Ok(vec![(*real_target, Guard::None)]),
862 TerminatorKind::FalseUnwind { real_target, .. } => {
863 Ok(vec![(*real_target, Guard::None)])
864 }
865 TerminatorKind::UnwindResume => bug!("TODO: implement checking of cleanup code"),
866 }
867 }
868
869 fn check_ret(
870 &mut self,
871 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
872 env: &mut TypeEnv,
873 span: Span,
874 ) -> Result {
875 let obligations = infcx
876 .at(span)
877 .ensure_resolved_evars(|infcx| {
878 let ret_place_ty = env.lookup_place(infcx, Place::RETURN)?;
879 let output = self
880 .fn_sig
881 .output
882 .replace_bound_refts_with(|sort, mode, _| infcx.fresh_infer_var(sort, mode));
883 let obligations =
884 infcx.subtyping_with_env(env, &ret_place_ty, &output.ret, ConstrReason::Ret)?;
885
886 env.check_ensures(infcx, &output.ensures, ConstrReason::Ret)?;
887
888 Ok(obligations)
889 })
890 .with_span(span)?;
891
892 self.check_coroutine_obligations(infcx, obligations)
893 }
894
895 #[expect(clippy::too_many_arguments)]
896 fn check_call(
897 &mut self,
898 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
899 env: &mut TypeEnv,
900 span: Span,
901 location: Option<Location>,
902 callee_def_id: Option<DefId>,
903 fn_sig: EarlyBinder<PolyFnSig>,
904 generic_args: &[GenericArg],
905 actuals: &[Ty],
906 ) -> Result<ResolvedCall> {
907 let genv = self.genv;
908 let tcx = genv.tcx();
909 let body_def_id = match self.checker_id {
912 CheckerId::DefId(def_id) => Some(def_id.to_def_id()),
913 CheckerId::Promoted(..) => None,
914 };
915 if let Some(callee_def_id) = callee_def_id {
916 dbg::call!(genv, callee_def_id, body_def_id, span);
917 }
918
919 let actuals =
920 unfold_local_ptrs(infcx, env, fn_sig.skip_binder_ref(), actuals).with_span(span)?;
921 let actuals = infer_under_mut_ref_hack(infcx, &actuals, fn_sig.skip_binder_ref());
922 infcx.push_evar_scope();
923
924 let generic_args = infcx.instantiate_generic_args(generic_args);
926
927 let early_refine_args = match callee_def_id {
929 Some(callee_def_id) => {
930 infcx
931 .instantiate_refine_args(callee_def_id, &generic_args)
932 .with_span(span)?
933 }
934 None => rty::List::empty(),
935 };
936
937 let clauses = match callee_def_id {
938 Some(callee_def_id) => {
939 genv.predicates_of(callee_def_id)
940 .with_span(span)?
941 .predicates()
942 .instantiate(tcx, &generic_args, &early_refine_args)
943 }
944 None => crate::rty::List::empty(),
945 };
946
947 let (clauses, fn_clauses) = Clause::split_off_fn_trait_clauses(self.genv, &clauses);
948 infcx
949 .at(span)
950 .check_non_closure_clauses(&clauses, ConstrReason::Call)
951 .with_span(span)?;
952
953 for fn_trait_pred in &fn_clauses {
954 self.check_fn_trait_clause(infcx, fn_trait_pred, span)?;
955 }
956
957 let late_refine_args = vec![];
959 let fn_sig = fn_sig
960 .instantiate(tcx, &generic_args, &early_refine_args)
961 .replace_bound_vars(
962 |_| rty::ReErased,
963 |sort, mode, _| infcx.fresh_infer_var(sort, mode),
964 );
965
966 let fn_sig = fn_sig
967 .deeply_normalize(&mut infcx.at(span))
968 .with_span(span)?;
969
970 let mut at = infcx.at(span);
971
972 if let Some(callee_def_id) = callee_def_id
973 && genv.def_kind(callee_def_id).is_fn_like()
974 {
975 let callee_no_panic = fn_sig.no_panic();
976
977 let callee_inferred_spec = body_def_id
978 .zip(location)
979 .and_then(|(body_def_id, location)| {
980 genv.call_graph().resolved_callee(body_def_id, location)
981 })
982 .map(|key| genv.inferred_no_panic_key(key))
983 .unwrap_or(PanicSpec::MightPanic(PanicReason::NotInCallGraph));
984
985 let inferred_panic_expr = if callee_inferred_spec == PanicSpec::WillNotPanic {
986 Expr::tt()
987 } else {
988 Expr::ff()
989 };
990
991 at.check_pred(
992 Expr::implies(
993 self.fn_sig.no_panic(),
994 Expr::or(callee_no_panic, inferred_panic_expr),
995 ),
996 ConstrReason::NoPanic(callee_def_id, callee_inferred_spec),
997 );
998 }
999
1000 for requires in fn_sig.requires() {
1002 at.check_pred(requires, ConstrReason::Call);
1003 }
1004
1005 for (actual, formal) in iter::zip(actuals, fn_sig.inputs()) {
1007 at.subtyping_with_env(env, &actual, formal, ConstrReason::Call)
1008 .with_span(span)?;
1009 }
1010
1011 infcx.pop_evar_scope().with_span(span)?;
1012 env.fully_resolve_evars(infcx);
1013
1014 let output = infcx
1015 .fully_resolve_evars(&fn_sig.output)
1016 .replace_bound_refts_with(|sort, _, kind| {
1017 Expr::fvar(infcx.define_bound_reft_var(sort, kind))
1018 });
1019
1020 env.assume_ensures(infcx, &output.ensures, span);
1021 fold_local_ptrs(infcx, env, span).with_span(span)?;
1022
1023 Ok(ResolvedCall {
1024 output: output.ret,
1025 _early_args: early_refine_args
1026 .into_iter()
1027 .map(|arg| infcx.fully_resolve_evars(arg))
1028 .collect(),
1029 _late_args: late_refine_args
1030 .into_iter()
1031 .map(|arg| infcx.fully_resolve_evars(&arg))
1032 .collect(),
1033 })
1034 }
1035
1036 fn check_coroutine_obligations(
1037 &mut self,
1038 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1039 obligs: Vec<Binder<CoroutineObligPredicate>>,
1040 ) -> Result {
1041 for oblig in obligs {
1042 let oblig = oblig.skip_binder();
1044
1045 #[expect(clippy::disallowed_methods, reason = "coroutines cannot be extern speced")]
1046 let def_id = oblig.def_id.expect_local();
1047 let span = self.genv.tcx().def_span(def_id);
1048 let body = self.genv.mir(def_id).with_span(span)?;
1049 Checker::run(
1050 infcx.change_item(def_id, &body.infcx),
1051 def_id,
1052 self.inherited.reborrow(),
1053 oblig.to_poly_fn_sig(),
1054 )?;
1055 }
1056 Ok(())
1057 }
1058
1059 fn find_self_ty_fn_sig(
1060 &self,
1061 self_ty: rustc_middle::ty::Ty<'tcx>,
1062 span: Span,
1063 ) -> Result<PolyFnSig> {
1064 let tcx = self.genv.tcx();
1065 let mut def_id = Some(self.checker_id.root_id().to_def_id());
1066 while let Some(did) = def_id {
1067 let generic_predicates = self
1068 .genv
1069 .predicates_of(did)
1070 .with_span(span)?
1071 .instantiate_identity();
1072 let predicates = generic_predicates.predicates;
1073
1074 for poly_fn_trait_pred in Clause::split_off_fn_trait_clauses(self.genv, &predicates).1 {
1075 if poly_fn_trait_pred.skip_binder_ref().self_ty.to_rustc(tcx) == self_ty {
1076 return Ok(poly_fn_trait_pred.map(|fn_trait_pred| fn_trait_pred.fndef_sig()));
1077 }
1078 }
1079 def_id = generic_predicates.parent;
1081 }
1082
1083 span_bug!(
1084 span,
1085 "cannot find self_ty_fn_sig for {:?} with self_ty = {self_ty:?}",
1086 self.checker_id
1087 );
1088 }
1089
1090 fn check_fn_trait_clause(
1091 &mut self,
1092 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1093 poly_fn_trait_pred: &Binder<FnTraitPredicate>,
1094 span: Span,
1095 ) -> Result {
1096 let self_ty = poly_fn_trait_pred
1097 .skip_binder_ref()
1098 .self_ty
1099 .as_bty_skipping_existentials();
1100 let oblig_sig = poly_fn_trait_pred.map_ref(|fn_trait_pred| fn_trait_pred.fndef_sig());
1101 match self_ty {
1102 Some(BaseTy::Closure(def_id, _, _, _)) => {
1103 let Some(poly_sig) = self.inherited.closures.get(def_id).cloned() else {
1104 span_bug!(span, "missing template for closure {def_id:?}");
1105 };
1106 check_fn_subtyping(infcx, SubFn::Mono(poly_sig.clone()), &oblig_sig, span)
1107 .with_span(span)?;
1108 }
1109 Some(BaseTy::FnDef(def_id, args)) => {
1110 let sub_sig = self.genv.fn_sig(*def_id).with_span(span)?;
1114 check_fn_subtyping(
1115 infcx,
1116 SubFn::Poly(*def_id, sub_sig, args.clone()),
1117 &oblig_sig,
1118 span,
1119 )
1120 .with_span(span)?;
1121 }
1122 Some(BaseTy::FnPtr(sub_sig)) => {
1123 check_fn_subtyping(infcx, SubFn::Mono(sub_sig.clone()), &oblig_sig, span)
1124 .with_span(span)?;
1125 }
1126
1127 Some(self_ty @ BaseTy::Param(_)) => {
1129 let tcx = self.genv.tcx();
1131 let self_ty = self_ty.to_rustc(tcx);
1132 let sub_sig = self.find_self_ty_fn_sig(self_ty, span)?;
1133 check_fn_subtyping(infcx, SubFn::Mono(sub_sig), &oblig_sig, span)
1135 .with_span(span)?;
1136 }
1137 _ => {}
1138 }
1139 Ok(())
1140 }
1141
1142 fn check_assert(
1143 &mut self,
1144 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1145 env: &mut TypeEnv,
1146 terminator_span: Span,
1147 cond: &Operand<'tcx>,
1148 expected: bool,
1149 msg: &AssertKind,
1150 ) -> InferResult<Guard> {
1151 let ty = self.check_operand(infcx, env, terminator_span, cond)?;
1152 let TyKind::Indexed(BaseTy::Bool, idx) = ty.kind() else {
1153 tracked_span_bug!("unexpected ty `{ty:?}`");
1154 };
1155 let pred = if expected { idx.clone() } else { idx.not() };
1156
1157 let msg = match msg {
1158 AssertKind::DivisionByZero => "possible division by zero",
1159 AssertKind::BoundsCheck => "possible out-of-bounds access",
1160 AssertKind::RemainderByZero => "possible remainder with a divisor of zero",
1161 AssertKind::Overflow(mir::BinOp::Div) => "possible division with overflow",
1162 AssertKind::Overflow(mir::BinOp::Rem) => "possible remainder with overflow",
1163 AssertKind::Overflow(_) => return Ok(Guard::Pred(pred)),
1164 };
1165 infcx
1166 .at(terminator_span)
1167 .check_pred(&pred, ConstrReason::Assert(msg));
1168 Ok(Guard::Pred(pred))
1169 }
1170
1171 fn check_if(discr_ty: &Ty, targets: &SwitchTargets) -> Vec<(BasicBlock, Guard)> {
1174 let mk = |bits| {
1175 match discr_ty.kind() {
1176 TyKind::Indexed(BaseTy::Bool, idx) => {
1177 if bits == 0 {
1178 idx.not()
1179 } else {
1180 idx.clone()
1181 }
1182 }
1183 TyKind::Indexed(bty @ (BaseTy::Int(_) | BaseTy::Uint(_) | BaseTy::Char), idx) => {
1184 Expr::eq(idx.clone(), Expr::from_bits(bty, bits))
1185 }
1186 _ => tracked_span_bug!("unexpected discr_ty {:?}", discr_ty),
1187 }
1188 };
1189
1190 let mut successors = vec![];
1191
1192 for (bits, bb) in targets.iter() {
1193 successors.push((bb, Guard::Pred(mk(bits))));
1194 }
1195 let otherwise = Expr::and_from_iter(targets.iter().map(|(bits, _)| mk(bits).not()));
1196 successors.push((targets.otherwise(), Guard::Pred(otherwise)));
1197
1198 successors
1199 }
1200
1201 fn check_match(
1202 &mut self,
1203 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1204 env: &mut TypeEnv,
1205 discr_ty: &Ty,
1206 targets: &SwitchTargets,
1207 span: Span,
1208 ) -> Vec<(BasicBlock, Guard)> {
1209 let (adt_def, place) = discr_ty.expect_discr();
1210 let idx = if let Ok(ty) = env.lookup_place(&mut infcx.at(span), place)
1211 && let TyKind::Indexed(_, idx) = ty.kind()
1212 {
1213 Some(idx.clone())
1214 } else {
1215 None
1216 };
1217
1218 let mut successors = vec![];
1219 let mut remaining: FxHashMap<u128, VariantIdx> = adt_def
1220 .discriminants()
1221 .map(|(idx, discr)| (discr, idx))
1222 .collect();
1223 for (bits, bb) in targets.iter() {
1224 let variant_idx = remaining
1225 .remove(&bits)
1226 .expect("value doesn't correspond to any variant");
1227 successors.push((bb, Guard::Match(place.clone(), variant_idx)));
1228 }
1229 let guard = if remaining.len() == 1 {
1230 let (_, variant_idx) = remaining
1232 .into_iter()
1233 .next()
1234 .unwrap_or_else(|| tracked_span_bug!());
1235 Guard::Match(place.clone(), variant_idx)
1236 } else if adt_def.sort_def().is_reflected()
1237 && let Some(idx) = idx
1238 {
1239 let mut cases = vec![];
1241 for (_, variant_idx) in remaining {
1242 let did = adt_def.did();
1243 cases.push(rty::Expr::is_ctor(did, variant_idx, idx.clone()));
1244 }
1245 Guard::Pred(Expr::or_from_iter(cases))
1246 } else {
1247 Guard::None
1248 };
1249 successors.push((targets.otherwise(), guard));
1250
1251 successors
1252 }
1253
1254 fn check_successors(
1255 &mut self,
1256 mut infcx: InferCtxt<'_, 'genv, 'tcx>,
1257 env: TypeEnv,
1258 from: BasicBlock,
1259 terminator_span: Span,
1260 successors: Vec<(BasicBlock, Guard)>,
1261 ) -> Result {
1262 for (target, guard) in successors {
1263 let mut infcx = infcx.branch();
1264 let mut env = env.clone();
1265 match guard {
1266 Guard::None => {}
1267 Guard::Pred(expr) => {
1268 infcx.assume_pred(&expr);
1269 }
1270 Guard::Match(place, variant_idx) => {
1271 env.downcast(&mut infcx.at(terminator_span), &place, variant_idx)
1272 .with_span(terminator_span)?;
1273 }
1274 }
1275 self.check_ghost_statements_at(
1276 &mut infcx,
1277 &mut env,
1278 Point::Edge(from, target),
1279 terminator_span,
1280 )?;
1281 self.check_goto(infcx, env, terminator_span, target)?;
1282 }
1283 Ok(())
1284 }
1285
1286 fn check_goto(
1287 &mut self,
1288 mut infcx: InferCtxt<'_, 'genv, 'tcx>,
1289 mut env: TypeEnv,
1290 span: Span,
1291 target: BasicBlock,
1292 ) -> Result {
1293 if self.is_exit_block(target) {
1294 let mut location = Location { block: target, statement_index: 0 };
1297 for _ in &self.body.basic_blocks[target].statements {
1298 self.check_ghost_statements_at(
1299 &mut infcx,
1300 &mut env,
1301 Point::BeforeLocation(location),
1302 span,
1303 )?;
1304 location = location.successor_within_block();
1305 }
1306 self.check_ghost_statements_at(
1307 &mut infcx,
1308 &mut env,
1309 Point::BeforeLocation(location),
1310 span,
1311 )?;
1312 self.check_ret(&mut infcx, &mut env, span)
1313 } else if let Some(real_target) = self.is_dummy_join(target) {
1314 self.check_goto(infcx, env, span, real_target)
1315 } else if self.body.is_join_point(target) {
1316 if M::check_goto_join_point(self, infcx, env, span, target)? {
1317 self.queue.insert(target);
1318 }
1319 Ok(())
1320 } else {
1321 self.check_basic_block(infcx, env, target)
1322 }
1323 }
1324
1325 fn is_dummy_join(&self, bb: BasicBlock) -> Option<BasicBlock> {
1340 if self.body.is_join_point(bb)
1341 && self.body.basic_blocks[bb]
1342 .statements
1343 .iter()
1344 .all(Statement::is_nop)
1345 && let Some(TerminatorKind::Goto { target: real_target }) = &self.body.basic_blocks[bb]
1346 .terminator
1347 .as_ref()
1348 .map(|terminator| &terminator.kind)
1349 && self.no_ghosts_at(bb, *real_target)
1350 {
1351 Some(*real_target)
1352 } else {
1353 None
1354 }
1355 }
1356
1357 fn no_ghosts_at(&self, bb: BasicBlock, real_target: BasicBlock) -> bool {
1358 let Some(ghosts) = self.inherited.ghost_stmts.get(&self.checker_id) else {
1359 return true;
1360 };
1361
1362 let mut res = ghosts
1363 .statements_at(Point::Edge(bb, real_target))
1364 .next()
1365 .is_none();
1366
1367 let mut location = Location { block: bb, statement_index: 0 };
1368 for _ in &self.body.basic_blocks[bb].statements {
1369 res = res
1370 && ghosts
1371 .statements_at(Point::BeforeLocation(location))
1372 .next()
1373 .is_none();
1374 location = location.successor_within_block();
1375 }
1376 res = res
1377 && ghosts
1378 .statements_at(Point::BeforeLocation(location))
1379 .next()
1380 .is_none();
1381 res
1382 }
1383
1384 fn closure_template(
1385 &mut self,
1386 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1387 env: &mut TypeEnv,
1388 stmt_span: Span,
1389 args: &flux_rustc_bridge::ty::GenericArgs,
1390 operands: &[Operand<'tcx>],
1391 ) -> InferResult<(Vec<Ty>, PolyFnSig)> {
1392 let closure_args = args.as_closure();
1393
1394 let actuals = self.check_operands(infcx, env, stmt_span, operands)?;
1399 let upvar_tys = self
1400 .refine_with_holes(closure_args.upvar_tys())?
1401 .iter()
1402 .map(|ty| {
1403 let ty =
1404 ty.replace_holes(|binders, kind| infcx.fresh_infer_var_for_hole(binders, kind));
1405
1406 let (ty, pred) = ty.unconstr();
1412 infcx.at(stmt_span).check_pred(&pred, ConstrReason::Other);
1413 ty
1414 })
1415 .collect_vec();
1416 for (actual, formal) in iter::zip(&actuals, &upvar_tys) {
1417 infcx
1418 .at(stmt_span)
1419 .subtyping_with_env(env, actual, formal, ConstrReason::Other)?;
1420 }
1421
1422 let ty = closure_args.sig_as_fn_ptr_ty();
1423
1424 if let flux_rustc_bridge::ty::TyKind::FnPtr(poly_sig) = ty.kind() {
1425 let poly_sig = poly_sig.unpack_closure_sig();
1426 let poly_sig = self.refine_with_holes(&poly_sig)?;
1427 let poly_sig = poly_sig.hoist_input_binders();
1428 let poly_sig = poly_sig
1429 .replace_holes(|binders, kind| infcx.fresh_infer_var_for_hole(binders, kind));
1430
1431 Ok((upvar_tys, poly_sig))
1432 } else {
1433 bug!("check_rvalue: closure: expected fn_ptr ty, found {ty:?} in {args:?}");
1434 }
1435 }
1436
1437 fn check_closure_body(
1438 &mut self,
1439 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1440 did: &DefId,
1441 upvar_tys: &[Ty],
1442 args: &flux_rustc_bridge::ty::GenericArgs,
1443 poly_sig: &PolyFnSig,
1444 ) -> Result {
1445 let genv = self.genv;
1446 let tcx = genv.tcx();
1447 #[expect(clippy::disallowed_methods, reason = "closures cannot be extern speced")]
1448 let closure_id = did.expect_local();
1449 let span = tcx.def_span(closure_id);
1450 let body = genv.mir(closure_id).with_span(span)?;
1451 let no_panic = self.genv.no_panic(*did);
1452 let closure_sig = rty::to_closure_sig(tcx, closure_id, upvar_tys, args, poly_sig, no_panic);
1453 Checker::run(
1454 infcx.change_item(closure_id, &body.infcx),
1455 closure_id,
1456 self.inherited.reborrow(),
1457 closure_sig,
1458 )
1459 }
1460
1461 fn check_rvalue_closure(
1462 &mut self,
1463 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1464 env: &mut TypeEnv,
1465 stmt_span: Span,
1466 did: &DefId,
1467 args: &flux_rustc_bridge::ty::GenericArgs,
1468 operands: &[Operand<'tcx>],
1469 ) -> Result<Ty> {
1470 let (upvar_tys, poly_sig) = self
1472 .closure_template(infcx, env, stmt_span, args, operands)
1473 .with_span(stmt_span)?;
1474 self.check_closure_body(infcx, did, &upvar_tys, args, &poly_sig)?;
1476 self.inherited.closures.insert(*did, poly_sig);
1478 let no_panic = self.genv.no_panic(*did);
1480 Ok(Ty::closure(*did, upvar_tys, args, no_panic))
1481 }
1482
1483 fn check_rvalue(
1484 &mut self,
1485 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1486 env: &mut TypeEnv,
1487 stmt_span: Span,
1488 rvalue: &Rvalue<'tcx>,
1489 ) -> Result<Ty> {
1490 let genv = self.genv;
1491 match rvalue {
1492 Rvalue::Use(operand, _retag) => {
1493 self.check_operand(infcx, env, stmt_span, operand)
1494 .with_span(stmt_span)
1495 }
1496 Rvalue::Repeat(operand, c) => {
1497 let ty = self
1498 .check_operand(infcx, env, stmt_span, operand)
1499 .with_span(stmt_span)?;
1500 let arr_ty = ty
1501 .with_holes()
1502 .replace_holes(|binders, kind| infcx.fresh_infer_var_for_hole(binders, kind));
1503 infcx
1504 .at(stmt_span)
1505 .subtyping_with_env(env, &ty, &arr_ty, ConstrReason::Other)
1506 .with_span(stmt_span)?;
1507 Ok(Ty::array(arr_ty, c.clone()))
1508 }
1509 Rvalue::Ref(r, BorrowKind::Mut { .. }, place) => {
1510 env.borrow(&mut infcx.at(stmt_span), *r, Mutability::Mut, place)
1511 .with_span(stmt_span)
1512 }
1513 Rvalue::Ref(r, BorrowKind::Shared | BorrowKind::Fake(..), place) => {
1514 env.borrow(&mut infcx.at(stmt_span), *r, Mutability::Not, place)
1515 .with_span(stmt_span)
1516 }
1517
1518 Rvalue::RawPtr(mir::RawPtrKind::FakeForPtrMetadata, place) => {
1519 env.unfold(infcx, place, stmt_span).with_span(stmt_span)?;
1521 let ty = env
1522 .lookup_place(&mut infcx.at(stmt_span), place)
1523 .with_span(stmt_span)?;
1524 let ty = BaseTy::RawPtrMetadata(ty).to_ty();
1525 Ok(ty)
1526 }
1527 Rvalue::RawPtr(kind, place) => {
1528 let ty = &env.lookup_rust_ty(genv, place).with_span(stmt_span)?;
1530 let ctor = self
1531 .default_refiner
1532 .refine_ty_or_base(ty)
1533 .with_span(stmt_span)?
1534 .expect_base();
1535 raw_ptr_with_size(genv, kind, ctor, infcx, self.checker_id.root_id())
1536 }
1537 Rvalue::Cast(kind, op, to) => {
1538 let from = self
1539 .check_operand(infcx, env, stmt_span, op)
1540 .with_span(stmt_span)?;
1541 self.check_cast(infcx, env, stmt_span, *kind, &from, to)
1542 .with_span(stmt_span)
1543 }
1544 Rvalue::BinaryOp(bin_op, op1, op2) => {
1545 self.check_binary_op(infcx, env, stmt_span, *bin_op, op1, op2)
1546 .with_span(stmt_span)
1547 }
1548
1549 Rvalue::UnaryOp(UnOp::PtrMetadata, Operand::Copy(place))
1550 | Rvalue::UnaryOp(UnOp::PtrMetadata, Operand::Move(place)) => {
1551 self.check_raw_ptr_metadata(infcx, env, stmt_span, place)
1552 }
1553 Rvalue::UnaryOp(un_op, op) => {
1554 self.check_unary_op(infcx, env, stmt_span, *un_op, op)
1555 .with_span(stmt_span)
1556 }
1557 Rvalue::Discriminant(place) => {
1558 let ty = env
1559 .lookup_place(&mut infcx.at(stmt_span), place)
1560 .with_span(stmt_span)?;
1561 let (adt_def, ..) = ty
1563 .as_bty_skipping_existentials()
1564 .unwrap_or_else(|| tracked_span_bug!())
1565 .expect_adt();
1566 Ok(Ty::discr(adt_def.clone(), place.clone()))
1567 }
1568 Rvalue::Aggregate(
1569 AggregateKind::Adt(def_id, variant_idx, args, _, field_idx),
1570 operands,
1571 ) => {
1572 let actuals = self
1573 .check_operands(infcx, env, stmt_span, operands)
1574 .with_span(stmt_span)?;
1575 let sig = genv
1576 .variant_sig(*def_id, *variant_idx)
1577 .with_span(stmt_span)?
1578 .ok_or_query_err(*def_id)
1579 .with_span(stmt_span)?
1580 .to_poly_fn_sig(*field_idx);
1581
1582 let args = instantiate_args_for_constructor(
1583 genv,
1584 self.checker_id.root_id().to_def_id(),
1585 *def_id,
1586 args,
1587 )
1588 .with_span(stmt_span)?;
1589 self.check_call(infcx, env, stmt_span, None, Some(*def_id), sig, &args, &actuals)
1590 .map(|resolved_call| resolved_call.output)
1591 }
1592 Rvalue::Aggregate(AggregateKind::Array(arr_ty), operands) => {
1593 let args = self
1594 .check_operands(infcx, env, stmt_span, operands)
1595 .with_span(stmt_span)?;
1596 let arr_ty = self.refine_with_holes(arr_ty).with_span(stmt_span)?;
1597 self.check_mk_array(infcx, env, stmt_span, &args, arr_ty)
1598 .with_span(stmt_span)
1599 }
1600 Rvalue::Aggregate(AggregateKind::Tuple, args) => {
1601 let tys = self
1602 .check_operands(infcx, env, stmt_span, args)
1603 .with_span(stmt_span)?;
1604 Ok(Ty::tuple(tys))
1605 }
1606 Rvalue::Aggregate(AggregateKind::Closure(did, args), operands) => {
1607 self.check_rvalue_closure(infcx, env, stmt_span, did, args, operands)
1608 }
1609 Rvalue::Aggregate(AggregateKind::Coroutine(did, args), ops) => {
1610 let coroutine_args = args.as_coroutine();
1611 let resume_ty = self
1612 .refine_default(coroutine_args.resume_ty())
1613 .with_span(stmt_span)?;
1614 let upvar_tys = self
1615 .check_operands(infcx, env, stmt_span, ops)
1616 .with_span(stmt_span)?;
1617 Ok(Ty::coroutine(*did, resume_ty, upvar_tys.into(), args.clone()))
1618 }
1619 }
1620 }
1621
1622 fn check_raw_ptr_metadata(
1623 &mut self,
1624 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1625 env: &mut TypeEnv,
1626 stmt_span: Span,
1627 place: &Place,
1628 ) -> Result<Ty> {
1629 let ty = env
1630 .lookup_place(&mut infcx.at(stmt_span), place)
1631 .with_span(stmt_span)?;
1632 let ty = match ty.kind() {
1633 TyKind::Indexed(BaseTy::RawPtrMetadata(ty), _)
1634 | TyKind::Indexed(BaseTy::Ref(_, ty, _), _) => ty,
1635 _ => tracked_span_bug!("check_metadata: bug! unexpected type `{ty:?}`"),
1636 };
1637 match ty.kind() {
1638 TyKind::Indexed(BaseTy::Array(_, len), _) => {
1639 let idx = Expr::from_const(self.genv.tcx(), len);
1640 Ok(Ty::indexed(BaseTy::Uint(UintTy::Usize), idx))
1641 }
1642 TyKind::Indexed(BaseTy::Slice(_), len) => {
1643 Ok(Ty::indexed(BaseTy::Uint(UintTy::Usize), len.clone()))
1644 }
1645 _ => Ok(Ty::unit()),
1646 }
1647 }
1648
1649 fn check_binary_op(
1650 &mut self,
1651 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1652 env: &mut TypeEnv,
1653 stmt_span: Span,
1654 bin_op: mir::BinOp,
1655 op1: &Operand<'tcx>,
1656 op2: &Operand<'tcx>,
1657 ) -> InferResult<Ty> {
1658 let ty1 = self.check_operand(infcx, env, stmt_span, op1)?;
1659 let ty2 = self.check_operand(infcx, env, stmt_span, op2)?;
1660
1661 match (ty1.kind(), ty2.kind()) {
1662 (TyKind::Indexed(bty1, idx1), TyKind::Indexed(bty2, idx2)) => {
1663 let rule =
1664 primops::match_bin_op(bin_op, bty1, idx1, bty2, idx2, infcx.check_overflow);
1665 if let Some(pre) = rule.precondition {
1666 infcx.at(stmt_span).check_pred(pre.pred, pre.reason);
1667 }
1668
1669 Ok(rule.output_type)
1670 }
1671 _ => tracked_span_bug!("incompatible types: `{ty1:?}` `{ty2:?}`"),
1672 }
1673 }
1674
1675 fn check_unary_op(
1676 &mut self,
1677 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1678 env: &mut TypeEnv,
1679 stmt_span: Span,
1680 un_op: mir::UnOp,
1681 op: &Operand<'tcx>,
1682 ) -> InferResult<Ty> {
1683 let ty = self.check_operand(infcx, env, stmt_span, op)?;
1684 match ty.kind() {
1685 TyKind::Indexed(bty, idx) => {
1686 let rule = primops::match_un_op(un_op, bty, idx, infcx.check_overflow);
1687 if let Some(pre) = rule.precondition {
1688 infcx.at(stmt_span).check_pred(pre.pred, pre.reason);
1689 }
1690 Ok(rule.output_type)
1691 }
1692 _ => tracked_span_bug!("invalid type for unary operator `{un_op:?}` `{ty:?}`"),
1693 }
1694 }
1695
1696 fn check_mk_array(
1697 &mut self,
1698 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1699 env: &mut TypeEnv,
1700 stmt_span: Span,
1701 args: &[Ty],
1702 arr_ty: Ty,
1703 ) -> InferResult<Ty> {
1704 let arr_ty = infcx.ensure_resolved_evars(|infcx| {
1705 let arr_ty =
1706 arr_ty.replace_holes(|binders, kind| infcx.fresh_infer_var_for_hole(binders, kind));
1707
1708 let (arr_ty, pred) = arr_ty.unconstr();
1709 let mut at = infcx.at(stmt_span);
1710 at.check_pred(&pred, ConstrReason::Other);
1711 for ty in args {
1712 at.subtyping_with_env(env, ty, &arr_ty, ConstrReason::Other)?;
1713 }
1714 Ok(arr_ty)
1715 })?;
1716 let arr_ty = infcx.fully_resolve_evars(&arr_ty);
1717
1718 Ok(Ty::array(arr_ty, rty::Const::from_usize(self.genv.tcx(), args.len())))
1719 }
1720
1721 fn check_cast(
1722 &self,
1723 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1724 env: &mut TypeEnv,
1725 stmt_span: Span,
1726 kind: CastKind,
1727 from: &Ty,
1728 to: &ty::Ty,
1729 ) -> InferResult<Ty> {
1730 use ty::TyKind as RustTy;
1731 let ty = match kind {
1732 CastKind::PointerExposeProvenance => {
1733 match to.kind() {
1734 RustTy::Int(int_ty) => Ty::int(*int_ty),
1735 RustTy::Uint(uint_ty) => Ty::uint(*uint_ty),
1736 _ => tracked_span_bug!("unsupported PointerExposeProvenance cast"),
1737 }
1738 }
1739 CastKind::IntToInt => {
1740 match (from.kind(), to.kind()) {
1741 (Bool!(idx), RustTy::Int(int_ty)) => bool_int_cast(idx, *int_ty),
1742 (Bool!(idx), RustTy::Uint(uint_ty)) => bool_uint_cast(idx, *uint_ty),
1743 (Int!(int_ty1, idx), RustTy::Int(int_ty2)) => {
1744 int_int_cast(idx, *int_ty1, *int_ty2)
1745 }
1746 (Uint!(uint_ty1, idx), RustTy::Uint(uint_ty2)) => {
1747 uint_uint_cast(idx, *uint_ty1, *uint_ty2)
1748 }
1749 (Uint!(uint_ty, idx), RustTy::Int(int_ty)) => {
1750 uint_int_cast(idx, *uint_ty, *int_ty)
1751 }
1752 (Int!(int_ty, idx), RustTy::Uint(uint_ty)) => {
1753 int_uint_cast(idx, *int_ty, *uint_ty)
1754 }
1755 (TyKind::Discr(adt_def, _), RustTy::Int(int_ty)) => {
1756 Self::discr_to_int_cast(adt_def, BaseTy::Int(*int_ty))
1757 }
1758 (TyKind::Discr(adt_def, _place), RustTy::Uint(uint_ty)) => {
1759 Self::discr_to_int_cast(adt_def, BaseTy::Uint(*uint_ty))
1760 }
1761 (Char!(idx), RustTy::Uint(uint_ty)) => char_uint_cast(idx, *uint_ty),
1762 (Uint!(_, idx), RustTy::Char) => uint_char_cast(idx),
1763 _ => {
1764 tracked_span_bug!("invalid int to int cast {from:?} --> {to:?}")
1765 }
1766 }
1767 }
1768 CastKind::PointerCoercion(mir::PointerCast::Unsize) => {
1769 self.check_unsize_cast(infcx, env, stmt_span, from, to)?
1770 }
1771 CastKind::PointerCoercion(mir::PointerCast::MutToConstPointer) => {
1772 match from.kind() {
1773 TyKind::Indexed(BaseTy::RawPtr(inner_ty, Mutability::Mut), idx) => {
1774 Ty::indexed(BaseTy::RawPtr(inner_ty.clone(), Mutability::Not), idx.clone())
1775 }
1776 _ => self.refine_default(to)?,
1777 }
1778 }
1779 CastKind::PtrToPtr => {
1780 match (from.kind(), to.kind()) {
1786 (
1787 TyKind::Indexed(BaseTy::RawPtr(_, _), idx),
1788 RustTy::RawPtr(to_inner_ty, to_mutbl),
1789 ) => {
1790 let inner_ty = self.refine_default(to_inner_ty)?;
1791 Ty::indexed(BaseTy::RawPtr(inner_ty, *to_mutbl), idx.clone())
1792 }
1793 _ => self.refine_default(to)?,
1794 }
1795 }
1796 CastKind::FloatToInt
1797 | CastKind::IntToFloat
1798 | CastKind::FloatToFloat
1799 | CastKind::PointerCoercion(mir::PointerCast::ClosureFnPointer)
1800 | CastKind::PointerWithExposedProvenance => self.refine_default(to)?,
1801 CastKind::PointerCoercion(mir::PointerCast::ReifyFnPointer(_)) => {
1802 let to = self.refine_default(to)?;
1803 if let TyKind::Indexed(BaseTy::FnDef(def_id, args), _) = from.kind()
1804 && let TyKind::Indexed(BaseTy::FnPtr(super_sig), _) = to.kind()
1805 {
1806 let current_did = infcx.def_id;
1807 let sub_sig =
1808 SubFn::Poly(current_did, infcx.genv.fn_sig(*def_id)?, args.clone());
1809 check_fn_subtyping(infcx, sub_sig, super_sig, stmt_span)?;
1811 to
1812 } else {
1813 tracked_span_bug!("invalid cast from `{from:?}` to `{to:?}`")
1814 }
1815 }
1816 };
1817 Ok(ty)
1818 }
1819
1820 fn discr_to_int_cast(adt_def: &AdtDef, bty: BaseTy) -> Ty {
1821 let vals = adt_def
1823 .discriminants()
1824 .map(|(_, idx)| Expr::eq(Expr::nu(), Expr::from_bits(&bty, idx)))
1825 .collect_vec();
1826 Ty::exists_with_constr(bty, Expr::or_from_iter(vals))
1827 }
1828
1829 fn check_unsize_cast(
1830 &self,
1831 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1832 env: &mut TypeEnv,
1833 span: Span,
1834 src: &Ty,
1835 dst: &ty::Ty,
1836 ) -> InferResult<Ty> {
1837 let src = if let TyKind::Ptr(PtrKind::Mut(re), path) = src.kind() {
1839 env.ptr_to_ref(
1840 &mut infcx.at(span),
1841 ConstrReason::Other,
1842 *re,
1843 path,
1844 PtrToRefBound::Identity,
1845 )?
1846 } else {
1847 src.clone()
1848 };
1849
1850 if let ty::TyKind::Ref(_, deref_ty, _) = dst.kind()
1851 && let ty::TyKind::Dynamic(..) = deref_ty.kind()
1852 {
1853 return Ok(self.refine_default(dst)?);
1854 }
1855
1856 if let TyKind::Indexed(BaseTy::Ref(_, deref_ty, _), _) = src.kind()
1858 && let TyKind::Indexed(BaseTy::Array(arr_ty, arr_len), _) = deref_ty.kind()
1859 && let ty::TyKind::Ref(re, _, mutbl) = dst.kind()
1860 {
1861 let idx = Expr::from_const(self.genv.tcx(), arr_len);
1862 Ok(Ty::mk_ref(*re, Ty::indexed(BaseTy::Slice(arr_ty.clone()), idx), *mutbl))
1863
1864 } else if let TyKind::Indexed(BaseTy::Adt(adt_def, args), _) = src.kind()
1866 && adt_def.is_box()
1867 && let (deref_ty, alloc_ty) = args.box_args()
1868 && let TyKind::Indexed(BaseTy::Array(arr_ty, arr_len), _) = deref_ty.kind()
1869 {
1870 let idx = Expr::from_const(self.genv.tcx(), arr_len);
1871 Ok(Ty::mk_box(
1872 self.genv,
1873 Ty::indexed(BaseTy::Slice(arr_ty.clone()), idx),
1874 alloc_ty.clone(),
1875 )?)
1876 } else {
1877 Err(query_bug!("unsupported unsize cast from `{src:?}` to `{dst:?}`"))?
1878 }
1879 }
1880
1881 fn check_operands(
1882 &mut self,
1883 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1884 env: &mut TypeEnv,
1885 span: Span,
1886 operands: &[Operand<'tcx>],
1887 ) -> InferResult<Vec<Ty>> {
1888 operands
1889 .iter()
1890 .map(|op| self.check_operand(infcx, env, span, op))
1891 .try_collect()
1892 }
1893
1894 fn check_operand(
1895 &mut self,
1896 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
1897 env: &mut TypeEnv,
1898 span: Span,
1899 operand: &Operand<'tcx>,
1900 ) -> InferResult<Ty> {
1901 let ty = match operand {
1902 Operand::Copy(p) => env.lookup_place(&mut infcx.at(span), p)?,
1903 Operand::Move(p) => env.move_place(&mut infcx.at(span), p)?,
1904 Operand::Constant(c) => self.check_constant(infcx, c)?,
1905 };
1906 Ok(infcx.hoister(true).hoist(&ty))
1907 }
1908
1909 fn check_constant(
1910 &mut self,
1911 infcx: &InferCtxt<'_, 'genv, 'tcx>,
1912 constant: &ConstOperand<'tcx>,
1913 ) -> QueryResult<Ty> {
1914 use rustc_middle::mir::Const;
1915 match constant.const_ {
1916 Const::Ty(ty, cst) => self.check_ty_const(constant, cst, ty)?,
1917 Const::Val(val, ty) => self.check_const_val(val, ty)?,
1918 Const::Unevaluated(uneval, ty) => {
1919 self.check_uneval_const(infcx, constant, uneval, ty)?
1920 }
1921 }
1922 .map_or_else(|| self.refine_default(&constant.ty), Ok)
1923 }
1924
1925 fn check_ty_const(
1926 &mut self,
1927 constant: &ConstOperand<'tcx>,
1928 cst: rustc_middle::ty::Const<'tcx>,
1929 ty: rustc_middle::ty::Ty<'tcx>,
1930 ) -> QueryResult<Option<Ty>> {
1931 use rustc_middle::ty::ConstKind;
1932 match cst.kind() {
1933 ConstKind::Param(param) => {
1934 let idx = Expr::const_generic(param);
1935 let ctor = self
1936 .default_refiner
1937 .refine_ty_or_base(&constant.ty)?
1938 .expect_base();
1939 Ok(Some(ctor.replace_bound_reft(&idx).to_ty()))
1940 }
1941 ConstKind::Value(val_tree) => {
1942 let val = self.genv.tcx().valtree_to_const_val(val_tree);
1943 Ok(self.check_const_val(val, ty)?)
1944 }
1945 _ => Ok(None),
1946 }
1947 }
1948
1949 fn check_const_val(
1950 &mut self,
1951 val: rustc_middle::mir::ConstValue,
1952 ty: rustc_middle::ty::Ty<'tcx>,
1953 ) -> QueryResult<Option<Ty>> {
1954 use rustc_middle::{mir::ConstValue, ty};
1955 match val {
1956 ConstValue::Scalar(scalar) => self.check_scalar(scalar, ty),
1957 ConstValue::ZeroSized if ty.is_unit() => Ok(Some(Ty::unit())),
1958 ConstValue::Slice { .. } => {
1959 if let ty::Ref(_, ref_ty, Mutability::Not) = ty.kind()
1960 && ref_ty.is_str()
1961 && let Some(data) = val.try_get_slice_bytes_for_diagnostics(self.genv.tcx())
1962 {
1963 let str = String::from_utf8_lossy(data);
1964 let idx = Expr::constant(Constant::Str(Symbol::intern(&str)));
1965 Ok(Some(Ty::mk_ref(ReErased, Ty::indexed(BaseTy::Str, idx), Mutability::Not)))
1966 } else {
1967 Ok(None)
1968 }
1969 }
1970 _ => Ok(None),
1971 }
1972 }
1973
1974 fn check_uneval_const(
1975 &mut self,
1976 infcx: &InferCtxt<'_, 'genv, 'tcx>,
1977 constant: &ConstOperand<'tcx>,
1978 uneval: rustc_middle::mir::UnevaluatedConst<'tcx>,
1979 ty: rustc_middle::ty::Ty<'tcx>,
1980 ) -> QueryResult<Option<Ty>> {
1981 if let Some(promoted) = uneval.promoted
1983 && let Some(ty) = self.promoted.get(promoted)
1984 {
1985 return Ok(Some(ty.clone()));
1986 }
1987
1988 if !uneval.args.is_empty() {
1992 let tcx = self.genv.tcx();
1993 let param_env = tcx.param_env(self.checker_id.root_id());
1994 let typing_env = infcx.region_infcx.typing_env(param_env);
1995 if let Ok(val) = tcx.const_eval_resolve(typing_env, uneval, constant.span) {
1996 return self.check_const_val(val, ty);
1997 } else {
1998 return Ok(None);
1999 }
2000 }
2001
2002 if let rty::TyOrBase::Base(ctor) = self.default_refiner.refine_ty_or_base(&constant.ty)?
2004 && let rty::ConstantInfo::Interpreted(idx, _) = self.genv.constant_info(uneval.def)?
2005 {
2006 return Ok(Some(ctor.replace_bound_reft(&idx).to_ty()));
2007 }
2008
2009 Ok(None)
2010 }
2011
2012 fn check_scalar(
2013 &mut self,
2014 scalar: rustc_middle::mir::interpret::Scalar,
2015 ty: rustc_middle::ty::Ty<'tcx>,
2016 ) -> QueryResult<Option<Ty>> {
2017 use rustc_middle::mir::interpret::{GlobalAlloc, Scalar};
2018 match scalar {
2019 Scalar::Int(scalar_int) => Ok(self.check_scalar_int(scalar_int, ty)),
2020 Scalar::Ptr(ptr, _) => {
2021 let alloc_id = ptr.provenance.alloc_id();
2022 if let GlobalAlloc::Static(def_id) = self.genv.tcx().global_alloc(alloc_id)
2023 && let rty::StaticInfo::Known(ty) = self.genv.static_info(def_id)?
2024 && !self.genv.tcx().is_mutable_static(def_id)
2025 {
2027 Ok(Some(Ty::mk_ref(ReErased, ty, Mutability::Not)))
2028 } else {
2029 Ok(None)
2030 }
2031 }
2032 }
2033 }
2034
2035 fn check_scalar_int(
2036 &mut self,
2037 scalar: rustc_middle::ty::ScalarInt,
2038 ty: rustc_middle::ty::Ty<'tcx>,
2039 ) -> Option<Ty> {
2040 use flux_rustc_bridge::const_eval::{scalar_to_int, scalar_to_uint};
2041 use rustc_middle::ty;
2042
2043 let tcx = self.genv.tcx();
2044
2045 match ty.kind() {
2046 ty::Int(int_ty) => {
2047 let idx = Expr::constant(Constant::from(scalar_to_int(tcx, scalar, *int_ty)));
2048 Some(Ty::indexed(BaseTy::Int(*int_ty), idx))
2049 }
2050 ty::Uint(uint_ty) => {
2051 let idx = Expr::constant(Constant::from(scalar_to_uint(tcx, scalar, *uint_ty)));
2052 Some(Ty::indexed(BaseTy::Uint(*uint_ty), idx))
2053 }
2054 ty::Float(float_ty) => Some(Ty::float(*float_ty)),
2055 ty::Char => {
2056 let idx = Expr::constant(Constant::Char(scalar.try_into().unwrap()));
2057 Some(Ty::indexed(BaseTy::Char, idx))
2058 }
2059 ty::Bool => {
2060 let idx = Expr::constant(Constant::Bool(scalar.try_to_bool().unwrap()));
2061 Some(Ty::indexed(BaseTy::Bool, idx))
2062 }
2063 _ => None,
2065 }
2066 }
2067
2068 fn check_ghost_statements_at(
2069 &mut self,
2070 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
2071 env: &mut TypeEnv,
2072 point: Point,
2073 span: Span,
2074 ) -> Result {
2075 bug::track_span(span, || {
2076 for stmt in self.ghost_stmts().statements_at(point) {
2077 self.check_ghost_statement(infcx, env, stmt, span)
2078 .with_span(span)?;
2079 }
2080 Ok(())
2081 })
2082 }
2083
2084 fn check_ghost_statement(
2085 &mut self,
2086 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
2087 env: &mut TypeEnv,
2088 stmt: &GhostStatement,
2089 span: Span,
2090 ) -> InferResult {
2091 dbg::statement!("start", stmt, infcx, env, span, &self);
2092 match stmt {
2093 GhostStatement::Fold(place) => {
2094 env.fold(&mut infcx.at(span), place)?;
2095 }
2096 GhostStatement::Unfold(place) => {
2097 env.unfold(infcx, place, span)?;
2098 }
2099 GhostStatement::Unblock(place) => env.unblock(infcx, place),
2100 GhostStatement::PtrToRef(place) => {
2101 env.ptr_to_ref_at_place(&mut infcx.at(span), place)?;
2102 }
2103 }
2104 dbg::statement!("end", stmt, infcx, env, span, &self);
2105 Ok(())
2106 }
2107
2108 #[track_caller]
2109 fn marker_at_dominator(&self, bb: BasicBlock) -> &Marker {
2110 marker_at_dominator(self.body, &self.markers, bb)
2111 }
2112
2113 fn dominators(&self) -> &'ck Dominators<BasicBlock> {
2114 self.body.dominators()
2115 }
2116
2117 fn ghost_stmts(&self) -> &'ck GhostStatements {
2118 &self.inherited.ghost_stmts[&self.checker_id]
2119 }
2120
2121 fn refine_default<T: Refine>(&self, ty: &T) -> QueryResult<T::Output> {
2122 ty.refine(&self.default_refiner)
2123 }
2124
2125 fn refine_with_holes<T: Refine>(&self, ty: &T) -> QueryResult<<T as Refine>::Output> {
2126 ty.refine(&Refiner::with_holes(self.genv, self.checker_id.root_id().to_def_id())?)
2127 }
2128}
2129
2130fn raw_ptr_with_size<'genv, 'tcx>(
2141 genv: GlobalEnv<'genv, 'tcx>,
2142 kind: &RawPtrKind,
2143 ctor: SubsetTyCtor,
2144 infcx: &InferCtxt<'_, 'genv, 'tcx>,
2145 def_id: LocalDefId,
2146) -> Result<Ty> {
2147 let tcx = genv.tcx();
2148 let param_env = tcx.param_env(def_id);
2149 let typing_env = infcx.region_infcx.typing_env(param_env);
2150
2151 let pointee_ty = ctor.to_ty();
2152 let bty = BaseTy::RawPtr(pointee_ty.clone(), kind.to_mutbl_lossy());
2153
2154 let has_sized = pointee_ty.to_rustc(tcx).is_sized(tcx, typing_env);
2155 let nu = Expr::nu();
2156 let base = Expr::field_proj(&nu, rty::FieldProj::RawPtr { field: rty::RawPtrField::Base });
2157 let addr = Expr::field_proj(&nu, rty::FieldProj::RawPtr { field: rty::RawPtrField::Addr });
2158 let size = Expr::field_proj(nu, rty::FieldProj::RawPtr { field: rty::RawPtrField::Size });
2159
2160 if !has_sized {
2163 let pred = Expr::and(Expr::eq(base, &addr), Expr::ne(&addr, Expr::zero()));
2164
2165 let ty = Ty::exists_with_constr(bty, pred);
2166 return Ok(ty);
2167 }
2168
2169 let sized_id = tcx.require_lang_item(LangItem::Sized, DUMMY_SP);
2170 let args = rty::List::from_arr([GenericArg::Base(ctor)]);
2171 let size_of_expr = Expr::alias(
2172 AliasReft {
2173 assoc_id: genv.require_builtin_assoc_reft(sized_id, sym::size_of),
2174 args: args.clone(),
2175 },
2176 rty::List::empty(),
2177 );
2178 let align_of_expr = Expr::alias(
2179 AliasReft { assoc_id: genv.require_builtin_assoc_reft(sized_id, sym::align_of), args },
2180 rty::List::empty(),
2181 );
2182
2183 let pred = Expr::and_from_iter([
2184 Expr::eq(base, &addr),
2185 Expr::ne(&addr, Expr::zero()),
2186 Expr::eq(size, size_of_expr),
2187 Expr::eq(Expr::binary_op(BinOp::Mod(Sort::Int), addr, align_of_expr), Expr::zero()),
2188 ]);
2189
2190 let ty = Ty::exists_with_constr(bty, pred);
2191 Ok(ty)
2192}
2193
2194fn instantiate_args_for_fun_call(
2195 genv: GlobalEnv,
2196 caller_id: DefId,
2197 callee_id: DefId,
2198 args: &ty::GenericArgs,
2199) -> QueryResult<Vec<rty::GenericArg>> {
2200 let params_in_clauses = collect_params_in_clauses(genv, callee_id);
2201 let assumed_parametric_params = genv.assume_parametric_params(callee_id);
2202
2203 let hole_refiner = Refiner::new_for_item(genv, caller_id, |bty| {
2204 let sort = bty.sort();
2205 let bty = bty.shift_in_escaping(1);
2206 let constr = if !sort.is_unit() {
2207 rty::SubsetTy::new(bty, Expr::nu(), Expr::hole(rty::HoleKind::Pred))
2208 } else {
2209 rty::SubsetTy::trivial(bty, Expr::nu())
2210 };
2211 Binder::bind_with_sort(constr, sort)
2212 })?;
2213 let default_refiner = Refiner::default_for_item(genv, caller_id)?;
2214
2215 let callee_generics = genv.generics_of(callee_id)?;
2216 args.iter()
2217 .enumerate()
2218 .map(|(idx, arg)| {
2219 let param = callee_generics.param_at(idx, genv)?;
2220 let is_parametric = !params_in_clauses.contains(&idx)
2221 || assumed_parametric_params.contains(&(idx as u32));
2222 let refiner = if is_parametric { &hole_refiner } else { &default_refiner };
2223 refiner.refine_generic_arg(¶m, arg)
2224 })
2225 .collect()
2226}
2227
2228fn instantiate_args_for_constructor(
2229 genv: GlobalEnv,
2230 caller_id: DefId,
2231 adt_id: DefId,
2232 args: &ty::GenericArgs,
2233) -> QueryResult<Vec<rty::GenericArg>> {
2234 let params_in_clauses = collect_params_in_clauses(genv, adt_id);
2235
2236 let adt_generics = genv.generics_of(adt_id)?;
2237 let hole_refiner = Refiner::with_holes(genv, caller_id)?;
2238 let default_refiner = Refiner::default_for_item(genv, caller_id)?;
2239 args.iter()
2240 .enumerate()
2241 .map(|(idx, arg)| {
2242 let param = adt_generics.param_at(idx, genv)?;
2243 let refiner =
2244 if params_in_clauses.contains(&idx) { &default_refiner } else { &hole_refiner };
2245 refiner.refine_generic_arg(¶m, arg)
2246 })
2247 .collect()
2248}
2249
2250fn collect_params_in_clauses(genv: GlobalEnv, def_id: DefId) -> UnordSet<usize> {
2251 let tcx = genv.tcx();
2252 struct Collector {
2253 params: UnordSet<usize>,
2254 }
2255
2256 impl rustc_middle::ty::TypeVisitor<TyCtxt<'_>> for Collector {
2257 fn visit_ty(&mut self, t: rustc_middle::ty::Ty) {
2258 if let rustc_middle::ty::Param(param_ty) = t.kind() {
2259 self.params.insert(param_ty.index as usize);
2260 }
2261 t.super_visit_with(self);
2262 }
2263 }
2264 let mut vis = Collector { params: UnordSet::new() };
2265
2266 let span = genv.tcx().def_span(def_id);
2267 for (clause, _) in all_predicates_of(tcx, def_id) {
2268 if let Some(trait_pred) = clause.as_trait_clause() {
2269 let trait_id = trait_pred.def_id();
2270 let ignore = [
2271 LangItem::MetaSized,
2272 LangItem::Sized,
2273 LangItem::Tuple,
2274 LangItem::Copy,
2275 LangItem::Destruct,
2276 ];
2277 if ignore
2278 .iter()
2279 .any(|lang_item| tcx.require_lang_item(*lang_item, span) == trait_id)
2280 {
2281 continue;
2282 }
2283
2284 if tcx.fn_trait_kind_from_def_id(trait_id).is_some() {
2285 continue;
2286 }
2287 if tcx.get_diagnostic_item(sym::Hash) == Some(trait_id) {
2288 continue;
2289 }
2290 if tcx.get_diagnostic_item(sym::Eq) == Some(trait_id) {
2291 continue;
2292 }
2293 }
2294 if let Some(proj_pred) = clause.as_projection_clause() {
2295 let assoc_id = proj_pred.item_def_id();
2296 if genv.is_fn_output(assoc_id) {
2297 continue;
2298 }
2299 }
2300 if let Some(outlives_pred) = clause.as_type_outlives_clause() {
2301 if outlives_pred.skip_binder().1 != tcx.lifetimes.re_static {
2304 continue;
2305 }
2306 }
2307 clause.visit_with(&mut vis);
2308 }
2309 vis.params
2310}
2311
2312fn all_predicates_of(
2313 tcx: TyCtxt<'_>,
2314 id: DefId,
2315) -> impl Iterator<Item = &(rustc_middle::ty::Clause<'_>, Span)> {
2316 let mut next_id = Some(id);
2317 iter::from_fn(move || {
2318 next_id.take().map(|id| {
2319 let preds = tcx.clauses_of(id);
2320 next_id = preds.parent;
2321 preds.clauses.iter()
2322 })
2323 })
2324 .flatten()
2325}
2326
2327struct SkipConstr;
2328
2329impl TypeFolder for SkipConstr {
2330 fn fold_ty(&mut self, ty: &rty::Ty) -> rty::Ty {
2331 if let rty::TyKind::Constr(_, inner_ty) = ty.kind() {
2332 inner_ty.fold_with(self)
2333 } else {
2334 ty.super_fold_with(self)
2335 }
2336 }
2337}
2338
2339fn is_indexed_mut_skipping_constr(ty: &Ty) -> bool {
2340 let ty = SkipConstr.fold_ty(ty);
2341 if let rty::Ref!(_, inner_ty, Mutability::Mut) = ty.kind()
2342 && let TyKind::Indexed(..) = inner_ty.kind()
2343 {
2344 true
2345 } else {
2346 false
2347 }
2348}
2349
2350fn infer_under_mut_ref_hack(rcx: &mut InferCtxt, actuals: &[Ty], fn_sig: &PolyFnSig) -> Vec<Ty> {
2355 iter::zip(actuals, fn_sig.skip_binder_ref().inputs())
2356 .map(|(actual, formal)| {
2357 if let rty::Ref!(re, deref_ty, Mutability::Mut) = actual.kind()
2358 && is_indexed_mut_skipping_constr(formal)
2359 {
2360 rty::Ty::mk_ref(*re, rcx.unpack(deref_ty), Mutability::Mut)
2361 } else {
2362 actual.clone()
2363 }
2364 })
2365 .collect()
2366}
2367
2368impl Mode for ShapeMode {
2369 const NAME: &str = "shape";
2370
2371 fn enter_basic_block<'ck, 'genv, 'tcx>(
2372 ck: &mut Checker<'ck, 'genv, 'tcx, ShapeMode>,
2373 _infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
2374 bb: BasicBlock,
2375 ) -> TypeEnv<'ck> {
2376 ck.inherited.mode.bb_envs[&ck.checker_id][&bb].enter(&ck.body.local_decls)
2377 }
2378
2379 fn check_goto_join_point<'genv, 'tcx>(
2380 ck: &mut Checker<'_, 'genv, 'tcx, ShapeMode>,
2381 _: InferCtxt<'_, 'genv, 'tcx>,
2382 env: TypeEnv,
2383 span: Span,
2384 target: BasicBlock,
2385 ) -> Result<bool> {
2386 let bb_envs = &mut ck.inherited.mode.bb_envs;
2387 let target_bb_env = bb_envs.entry(ck.checker_id).or_default().get(&target);
2388 dbg::shape_goto_enter!(target, env, target_bb_env);
2389
2390 let modified = match bb_envs.entry(ck.checker_id).or_default().entry(target) {
2391 Entry::Occupied(mut entry) => entry.get_mut().join(env, span),
2392 Entry::Vacant(entry) => {
2393 let scope = marker_at_dominator(ck.body, &ck.markers, target)
2394 .scope()
2395 .unwrap_or_else(|| tracked_span_bug!());
2396 entry.insert(env.into_infer(scope));
2397 true
2398 }
2399 };
2400
2401 dbg::shape_goto_exit!(target, bb_envs[&ck.checker_id].get(&target));
2402 Ok(modified)
2403 }
2404
2405 fn clear(ck: &mut Checker<ShapeMode>, root: BasicBlock) {
2406 ck.visited.remove(root);
2407 for bb in ck.body.basic_blocks.indices() {
2408 if bb != root && ck.dominators().dominates(root, bb) {
2409 ck.inherited
2410 .mode
2411 .bb_envs
2412 .entry(ck.checker_id)
2413 .or_default()
2414 .remove(&bb);
2415 ck.visited.remove(bb);
2416 }
2417 }
2418 }
2419}
2420
2421impl Mode for RefineMode {
2422 const NAME: &str = "refine";
2423
2424 fn enter_basic_block<'ck, 'genv, 'tcx>(
2425 ck: &mut Checker<'ck, 'genv, 'tcx, RefineMode>,
2426 infcx: &mut InferCtxt<'_, 'genv, 'tcx>,
2427 bb: BasicBlock,
2428 ) -> TypeEnv<'ck> {
2429 ck.inherited.mode.bb_envs[&ck.checker_id][&bb].enter(infcx, &ck.body.local_decls)
2430 }
2431
2432 fn check_goto_join_point(
2433 ck: &mut Checker<RefineMode>,
2434 mut infcx: InferCtxt,
2435 env: TypeEnv,
2436 terminator_span: Span,
2437 target: BasicBlock,
2438 ) -> Result<bool> {
2439 let bb_env = &ck.inherited.mode.bb_envs[&ck.checker_id][&target];
2440 tracked_span_dbg_assert_eq!(
2441 &ck.marker_at_dominator(target)
2442 .scope()
2443 .unwrap_or_else(|| tracked_span_bug!()),
2444 bb_env.scope()
2445 );
2446
2447 dbg::refine_goto!(target, infcx, env, bb_env);
2448
2449 env.check_goto(&mut infcx.at(terminator_span), bb_env, target)
2450 .with_span(terminator_span)?;
2451
2452 Ok(!ck.visited.contains(target))
2453 }
2454
2455 fn clear(_ck: &mut Checker<RefineMode>, _bb: BasicBlock) {
2456 bug!();
2457 }
2458}
2459
2460fn bool_int_cast(b: &Expr, int_ty: IntTy) -> Ty {
2461 let idx = Expr::ite(b, 1, 0);
2462 Ty::indexed(BaseTy::Int(int_ty), idx)
2463}
2464
2465fn uint_char_cast(idx: &Expr) -> Ty {
2468 let idx = Expr::cast(rty::Sort::Int, rty::Sort::Char, idx.clone());
2469 Ty::indexed(BaseTy::Char, idx)
2470}
2471
2472fn char_uint_cast(idx: &Expr, uint_ty: UintTy) -> Ty {
2473 let idx = Expr::cast(rty::Sort::Char, rty::Sort::Int, idx.clone());
2474 if uint_bit_width(uint_ty) >= 32 {
2475 Ty::indexed(BaseTy::Uint(uint_ty), idx)
2477 } else {
2478 guarded_uint_ty(&idx, uint_ty)
2480 }
2481}
2482
2483fn bool_uint_cast(b: &Expr, uint_ty: UintTy) -> Ty {
2484 let idx = Expr::ite(b, 1, 0);
2485 Ty::indexed(BaseTy::Uint(uint_ty), idx)
2486}
2487
2488fn int_int_cast(idx: &Expr, int_ty1: IntTy, int_ty2: IntTy) -> Ty {
2489 if int_bit_width(int_ty1) <= int_bit_width(int_ty2) {
2490 Ty::indexed(BaseTy::Int(int_ty2), idx.clone())
2491 } else {
2492 Ty::int(int_ty2)
2493 }
2494}
2495
2496fn uint_int_cast(idx: &Expr, uint_ty: UintTy, int_ty: IntTy) -> Ty {
2497 if uint_bit_width(uint_ty) < int_bit_width(int_ty) {
2498 Ty::indexed(BaseTy::Int(int_ty), idx.clone())
2499 } else {
2500 Ty::int(int_ty)
2501 }
2502}
2503
2504fn int_uint_cast(idx: &Expr, int_ty: IntTy, uint_ty: UintTy) -> Ty {
2505 let non_neg = Expr::ge(idx.clone(), Expr::zero());
2506
2507 let guard: Expr = if int_bit_width(int_ty) <= uint_bit_width(uint_ty) {
2508 non_neg
2509 } else {
2510 let fits = Expr::le(idx.clone(), Expr::uint_max(uint_ty));
2512 Expr::and(non_neg, fits)
2513 };
2514
2515 let eq = Expr::eq(Expr::nu(), idx.clone());
2516 Ty::exists_with_constr(BaseTy::Uint(uint_ty), Expr::implies(guard, eq))
2517}
2518
2519fn guarded_uint_ty(idx: &Expr, uint_ty: UintTy) -> Ty {
2520 let max_value = Expr::uint_max(uint_ty);
2522 let guard = Expr::le(idx.clone(), max_value);
2523 let eq = Expr::eq(Expr::nu(), idx.clone());
2524 Ty::exists_with_constr(BaseTy::Uint(uint_ty), Expr::implies(guard, eq))
2525}
2526
2527fn uint_uint_cast(idx: &Expr, uint_ty1: UintTy, uint_ty2: UintTy) -> Ty {
2528 if uint_bit_width(uint_ty1) <= uint_bit_width(uint_ty2) {
2529 Ty::indexed(BaseTy::Uint(uint_ty2), idx.clone())
2530 } else {
2531 guarded_uint_ty(idx, uint_ty2)
2532 }
2533}
2534
2535fn uint_bit_width(uint_ty: UintTy) -> u64 {
2536 uint_ty
2537 .bit_width()
2538 .unwrap_or(config::pointer_width().bits())
2539}
2540
2541fn int_bit_width(int_ty: IntTy) -> u64 {
2542 int_ty.bit_width().unwrap_or(config::pointer_width().bits())
2543}
2544
2545impl ShapeResult {
2546 fn into_bb_envs(
2547 self,
2548 infcx: &mut InferCtxtRoot,
2549 body: &Body,
2550 ) -> FxHashMap<CheckerId, FxHashMap<BasicBlock, BasicBlockEnv>> {
2551 self.0
2552 .into_iter()
2553 .map(|(checker_id, shapes)| {
2554 let bb_envs = shapes
2555 .into_iter()
2556 .map(|(bb, shape)| (bb, shape.into_bb_env(infcx, body)))
2557 .collect();
2558 (checker_id, bb_envs)
2559 })
2560 .collect()
2561 }
2562}
2563
2564fn marker_at_dominator<'a>(
2565 body: &Body,
2566 markers: &'a IndexVec<BasicBlock, Option<Marker>>,
2567 bb: BasicBlock,
2568) -> &'a Marker {
2569 let dominator = body
2570 .dominators()
2571 .immediate_dominator(bb)
2572 .unwrap_or_else(|| tracked_span_bug!());
2573 markers[dominator]
2574 .as_ref()
2575 .unwrap_or_else(|| tracked_span_bug!())
2576}
2577
2578pub(crate) mod errors {
2579 use flux_errors::{E0999, ErrorGuaranteed};
2580 use flux_infer::infer::InferErr;
2581 use flux_macros::msg;
2582 use flux_middle::{global_env::GlobalEnv, queries::ErrCtxt};
2583 use rustc_errors::Diagnostic;
2584 use rustc_hir::def_id::LocalDefId;
2585 use rustc_span::Span;
2586
2587 #[derive(Debug)]
2588 pub struct CheckerError {
2589 kind: InferErr,
2590 span: Span,
2591 }
2592
2593 impl CheckerError {
2594 pub fn emit(self, genv: GlobalEnv, fn_def_id: LocalDefId) -> ErrorGuaranteed {
2595 let dcx = genv.sess().dcx().handle();
2596 match self.kind {
2597 InferErr::UnsolvedEvar(_) => {
2598 let mut diag = dcx.struct_span_err(
2599 self.span,
2600 msg!("parameter inference error at function call"),
2601 );
2602 diag.code(E0999);
2603 diag.emit()
2604 }
2605 InferErr::Query(err) => {
2606 let level = rustc_errors::Level::Error;
2607 err.at(ErrCtxt::FnCheck(self.span, fn_def_id))
2608 .into_diag(dcx, level)
2609 .emit()
2610 }
2611 }
2612 }
2613 }
2614
2615 pub trait ResultExt<T> {
2616 fn with_span(self, span: Span) -> Result<T, CheckerError>;
2617 }
2618
2619 impl<T, E> ResultExt<T> for Result<T, E>
2620 where
2621 E: Into<InferErr>,
2622 {
2623 fn with_span(self, span: Span) -> Result<T, CheckerError> {
2624 self.map_err(|err| CheckerError { kind: err.into(), span })
2625 }
2626 }
2627}