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