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