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