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