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