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