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