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