1use std::{cell::RefCell, fmt, iter};
2
3use flux_common::{bug, dbg, tracked_span_assert_eq, tracked_span_bug, tracked_span_dbg_assert_eq};
4use flux_config::{self as config, InferOpts, OverflowMode, RawDerefMode};
5use flux_macros::{TypeFoldable, TypeVisitable};
6use flux_middle::{
7 FixpointQueryKind, PanicSpec,
8 def_id::MaybeExternId,
9 global_env::GlobalEnv,
10 metrics::{self, Metric},
11 queries::{QueryErr, QueryResult},
12 query_bug,
13 rty::{
14 self, AliasKind, AliasTy, BaseTy, Binder, BoundReftKind, BoundVariableKinds,
15 CoroutineObligPredicate, Ctor, ESpan, EVid, EarlyBinder, Expr, ExprKind, FieldProj,
16 GenericArg, HoleKind, InferMode, Lambda, List, Loc, Mutability, Name, NameProvenance, Path,
17 PolyVariant, PtrKind, RefineArgs, RefineArgsExt, Region, Sort, Ty, TyCtor, TyKind, Var,
18 canonicalize::{Hoister, HoisterDelegate},
19 fold::TypeFoldable,
20 },
21};
22use itertools::{Itertools, izip};
23use rustc_hir::def_id::{DefId, LocalDefId};
24use rustc_macros::extension;
25use rustc_middle::{
26 mir::BasicBlock,
27 ty::{TyCtxt, Variance},
28};
29use rustc_span::{Span, Symbol};
30use rustc_type_ir::Variance::Invariant;
31
32use crate::{
33 evars::{EVarState, EVarStore},
34 fixpoint_encoding::{
35 Answer, Backend, FixQueryCache, FixpointCtxt, KVarEncoding, KVarGen, lean_task_key,
36 },
37 lean_encoding::log_proof,
38 projections::NormalizeExt as _,
39 refine_tree::{Cursor, Marker, RefineTree, Scope},
40};
41
42pub type InferResult<T = ()> = std::result::Result<T, InferErr>;
43
44#[derive(PartialEq, Eq, Clone, Copy, Hash)]
45pub struct Tag {
46 pub reason: ConstrReason,
47 pub src_span: Span,
48 pub dst_span: Option<ESpan>,
49}
50
51impl Tag {
52 pub fn new(reason: ConstrReason, span: Span) -> Self {
53 Self { reason, src_span: span, dst_span: None }
54 }
55
56 pub fn with_dst(self, dst_span: Option<ESpan>) -> Self {
57 Self { dst_span, ..self }
58 }
59}
60
61#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
62pub enum SubtypeReason {
63 Input,
64 Output,
65 Requires,
66 Ensures,
67}
68
69#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
70pub enum ConstrReason {
71 Call,
72 Assign,
73 Ret,
74 Fold,
75 FoldLocal,
76 Predicate,
77 Assert(&'static str),
78 Div,
79 Rem,
80 Goto(BasicBlock),
81 Overflow,
82 Underflow,
83 Subtype(SubtypeReason),
84 NoPanic(DefId, PanicSpec),
85 Other,
86}
87
88pub struct InferCtxtRoot<'genv, 'tcx> {
89 pub genv: GlobalEnv<'genv, 'tcx>,
90 inner: RefCell<InferCtxtInner>,
91 refine_tree: RefineTree,
92 opts: InferOpts,
93}
94
95pub struct InferCtxtRootBuilder<'a, 'genv, 'tcx> {
96 genv: GlobalEnv<'genv, 'tcx>,
97 opts: InferOpts,
98 params: Vec<(Var, Sort)>,
99 infcx: &'a rustc_infer::infer::InferCtxt<'tcx>,
100 dummy_kvars: bool,
101}
102
103#[extension(pub trait GlobalEnvExt<'genv, 'tcx>)]
104impl<'genv, 'tcx> GlobalEnv<'genv, 'tcx> {
105 fn infcx_root<'a>(
106 self,
107 infcx: &'a rustc_infer::infer::InferCtxt<'tcx>,
108 opts: InferOpts,
109 ) -> InferCtxtRootBuilder<'a, 'genv, 'tcx> {
110 InferCtxtRootBuilder { genv: self, infcx, params: vec![], opts, dummy_kvars: false }
111 }
112}
113
114impl<'genv, 'tcx> InferCtxtRootBuilder<'_, 'genv, 'tcx> {
115 pub fn with_dummy_kvars(mut self) -> Self {
116 self.dummy_kvars = true;
117 self
118 }
119
120 pub fn with_const_generics(mut self, def_id: DefId) -> QueryResult<Self> {
121 self.params.extend(
122 self.genv
123 .generics_of(def_id)?
124 .const_params(self.genv)?
125 .into_iter()
126 .map(|(pcst, sort)| (Var::ConstGeneric(pcst), sort)),
127 );
128 Ok(self)
129 }
130
131 pub fn with_refinement_generics(
132 mut self,
133 def_id: DefId,
134 args: &[GenericArg],
135 ) -> QueryResult<Self> {
136 for (index, param) in self
137 .genv
138 .refinement_generics_of(def_id)?
139 .iter_own_params()
140 .enumerate()
141 {
142 let param = param.instantiate(self.genv.tcx(), args, &[]);
143 let sort = param
144 .sort
145 .deeply_normalize_sorts(def_id, self.genv, self.infcx)?;
146
147 let var =
148 Var::EarlyParam(rty::EarlyReftParam { index: index as u32, name: param.name });
149 self.params.push((var, sort));
150 }
151 Ok(self)
152 }
153
154 pub fn identity_for_item(mut self, def_id: DefId) -> QueryResult<Self> {
155 self = self.with_const_generics(def_id)?;
156 let offset = self.params.len();
157 self.genv.refinement_generics_of(def_id)?.fill_item(
158 self.genv,
159 &mut self.params,
160 &mut |param, index| {
161 let index = (index - offset) as u32;
162 let param = param.instantiate_identity();
163 let sort = param
164 .sort
165 .deeply_normalize_sorts(def_id, self.genv, self.infcx)?;
166
167 let var = Var::EarlyParam(rty::EarlyReftParam { index, name: param.name });
168 Ok((var, sort))
169 },
170 )?;
171 Ok(self)
172 }
173
174 pub fn build(self) -> QueryResult<InferCtxtRoot<'genv, 'tcx>> {
175 Ok(InferCtxtRoot {
176 genv: self.genv,
177 inner: RefCell::new(InferCtxtInner::new(self.dummy_kvars)),
178 refine_tree: RefineTree::new(self.params),
179 opts: self.opts,
180 })
181 }
182}
183
184impl<'genv, 'tcx> InferCtxtRoot<'genv, 'tcx> {
185 pub fn infcx<'a>(
186 &'a mut self,
187 def_id: DefId,
188 region_infcx: &'a rustc_infer::infer::InferCtxt<'tcx>,
189 ) -> InferCtxt<'a, 'genv, 'tcx> {
190 InferCtxt {
191 genv: self.genv,
192 region_infcx,
193 def_id,
194 cursor: self.refine_tree.cursor_at_root(),
195 inner: &self.inner,
196 check_overflow: self.opts.check_overflow,
197 allow_raw_deref: self.opts.allow_raw_deref,
198 }
199 }
200
201 pub fn fresh_kvar_in_scope(
202 &self,
203 binders: &[BoundVariableKinds],
204 scope: &Scope,
205 encoding: KVarEncoding,
206 ) -> Expr {
207 let inner = &mut *self.inner.borrow_mut();
208 inner.kvars.fresh(binders, scope.iter(), encoding)
209 }
210
211 pub fn execute_lean_query(
212 self,
213 cache: &mut FixQueryCache,
214 def_id: MaybeExternId,
215 ) -> QueryResult {
216 let inner = self.inner.into_inner();
217 let kvars = inner.kvars;
218 let evars = inner.evars;
219 let mut refine_tree = self.refine_tree;
220 refine_tree.replace_evars(&evars).unwrap();
221 refine_tree.simplify(self.genv);
222
223 let solver = match self.opts.solver {
224 flux_config::SmtSolver::Z3 => liquid_fixpoint::SmtSolver::Z3,
225 flux_config::SmtSolver::CVC5 => liquid_fixpoint::SmtSolver::CVC5,
226 };
227 let mut fcx = FixpointCtxt::new(self.genv, def_id, kvars, Backend::Lean);
228 let cstr = refine_tree.to_fixpoint(&mut fcx)?;
229 let (task, _) = fcx.create_task(def_id, cstr, self.opts.scrape_quals, solver)?;
230
231 log_proof(self.genv, def_id)?;
232 if config::is_cache_enabled() {
234 let key = lean_task_key(self.genv.tcx(), def_id.resolved_id());
235 let hash = task.hash_with_default();
236 if cache.lookup(&key, hash).is_some() {
237 return Ok(());
238 }
239 }
240
241 fcx.generate_lean_files(def_id, task)
242 }
243
244 pub fn execute_fixpoint_query(
245 self,
246 cache: &mut FixQueryCache,
247 def_id: MaybeExternId,
248 kind: FixpointQueryKind,
249 ) -> QueryResult<Answer<Tag>> {
250 let inner = self.inner.into_inner();
251 let kvars = inner.kvars;
252 let evars = inner.evars;
253
254 let ext = kind.ext();
255
256 let mut refine_tree = self.refine_tree;
257
258 refine_tree.replace_evars(&evars).unwrap();
259
260 if config::dump_constraint() {
261 dbg::dump_item_info(self.genv.tcx(), def_id.resolved_id(), ext, &refine_tree).unwrap();
262 }
263 refine_tree.simplify(self.genv);
264 if config::dump_constraint() {
265 let simp_ext = format!("simp.{ext}");
266 dbg::dump_item_info(self.genv.tcx(), def_id.resolved_id(), simp_ext, &refine_tree)
267 .unwrap();
268 }
269
270 let backend = match self.opts.solver {
271 flux_config::SmtSolver::Z3 => liquid_fixpoint::SmtSolver::Z3,
272 flux_config::SmtSolver::CVC5 => liquid_fixpoint::SmtSolver::CVC5,
273 };
274
275 let mut fcx = FixpointCtxt::new(self.genv, def_id, kvars, Backend::Fixpoint);
276 let cstr = refine_tree.to_fixpoint(&mut fcx)?;
277
278 let count = cstr.concrete_head_count();
280 metrics::incr_metric(Metric::CsTotal, count as u32);
281 if count == 0 {
282 metrics::incr_metric_if(kind.is_body(), Metric::FnTrivial);
283 return Ok(Answer::trivial());
284 }
285
286 let (task, suggestion_ctx) =
287 fcx.create_task(def_id, cstr, self.opts.scrape_quals, backend)?;
288 let result = fcx.run_task(cache, def_id, kind, &task)?;
289 Ok(fcx.result_to_answer(result, suggestion_ctx))
290 }
291
292 pub fn split(self) -> (RefineTree, KVarGen) {
293 (self.refine_tree, self.inner.into_inner().kvars)
294 }
295}
296
297pub struct InferCtxt<'infcx, 'genv, 'tcx> {
298 pub genv: GlobalEnv<'genv, 'tcx>,
299 pub region_infcx: &'infcx rustc_infer::infer::InferCtxt<'tcx>,
300 pub def_id: DefId,
301 pub check_overflow: OverflowMode,
302 pub allow_raw_deref: flux_config::RawDerefMode,
303 cursor: Cursor<'infcx>,
304 inner: &'infcx RefCell<InferCtxtInner>,
305}
306
307struct InferCtxtInner {
308 kvars: KVarGen,
309 evars: EVarStore,
310}
311
312impl InferCtxtInner {
313 fn new(dummy_kvars: bool) -> Self {
314 Self { kvars: KVarGen::new(dummy_kvars), evars: Default::default() }
315 }
316}
317
318impl<'infcx, 'genv, 'tcx> InferCtxt<'infcx, 'genv, 'tcx> {
319 pub fn at(&mut self, span: Span) -> InferCtxtAt<'_, 'infcx, 'genv, 'tcx> {
320 InferCtxtAt { infcx: self, span }
321 }
322
323 pub fn instantiate_refine_args(
324 &mut self,
325 callee_def_id: DefId,
326 args: &[rty::GenericArg],
327 ) -> InferResult<List<Expr>> {
328 Ok(RefineArgs::for_item(self.genv, callee_def_id, |param, _| {
329 let param = param.instantiate(self.genv.tcx(), args, &[]);
330 Ok(self.fresh_infer_var(¶m.sort, param.mode))
331 })?)
332 }
333
334 pub fn instantiate_generic_args(&mut self, args: &[GenericArg]) -> Vec<GenericArg> {
335 args.iter()
336 .map(|a| a.replace_holes(|binders, kind| self.fresh_infer_var_for_hole(binders, kind)))
337 .collect_vec()
338 }
339
340 pub fn fresh_infer_var(&self, sort: &Sort, mode: InferMode) -> Expr {
341 match mode {
342 InferMode::KVar => {
343 let fsort = sort.expect_func().expect_mono();
344 let vars = fsort.inputs().iter().cloned().map_into().collect();
345 let kvar = self.fresh_kvar(&[vars], KVarEncoding::Single);
346 Expr::abs(Lambda::bind_with_fsort(kvar, fsort))
347 }
348 InferMode::EVar => self.fresh_evar(),
349 }
350 }
351
352 pub fn fresh_infer_var_for_hole(
353 &mut self,
354 binders: &[BoundVariableKinds],
355 kind: HoleKind,
356 ) -> Expr {
357 match kind {
358 HoleKind::Pred => self.fresh_kvar(binders, KVarEncoding::Conj),
359 HoleKind::Expr(_) => {
360 self.fresh_evar()
364 }
365 }
366 }
367
368 pub fn fresh_kvar_in_scope(
370 &self,
371 binders: &[BoundVariableKinds],
372 scope: &Scope,
373 encoding: KVarEncoding,
374 ) -> Expr {
375 let inner = &mut *self.inner.borrow_mut();
376 inner.kvars.fresh(binders, scope.iter(), encoding)
377 }
378
379 pub fn fresh_kvar(&self, binders: &[BoundVariableKinds], encoding: KVarEncoding) -> Expr {
381 let inner = &mut *self.inner.borrow_mut();
382 inner.kvars.fresh(binders, self.cursor.vars(), encoding)
383 }
384
385 fn fresh_evar(&self) -> Expr {
386 let evars = &mut self.inner.borrow_mut().evars;
387 Expr::evar(evars.fresh(self.cursor.marker()))
388 }
389
390 pub fn unify_exprs(&self, a: &Expr, b: &Expr) {
391 if a.has_evars() {
392 return;
393 }
394 let evars = &mut self.inner.borrow_mut().evars;
395 if let ExprKind::Var(Var::EVar(evid)) = b.kind()
396 && let EVarState::Unsolved(marker) = evars.get(*evid)
397 && !marker.has_free_vars(a)
398 {
399 evars.solve(*evid, a.clone());
400 }
401 }
402
403 fn enter_exists<T, U>(
404 &mut self,
405 t: &Binder<T>,
406 f: impl FnOnce(&mut InferCtxt<'_, 'genv, 'tcx>, T) -> U,
407 ) -> U
408 where
409 T: TypeFoldable,
410 {
411 self.ensure_resolved_evars(|infcx| {
412 let t = t.replace_bound_refts_with(|sort, mode, _| infcx.fresh_infer_var(sort, mode));
413 Ok(f(infcx, t))
414 })
415 .unwrap()
416 }
417
418 pub fn push_evar_scope(&mut self) {
423 self.inner.borrow_mut().evars.push_scope();
424 }
425
426 pub fn pop_evar_scope(&mut self) -> InferResult {
429 self.inner
430 .borrow_mut()
431 .evars
432 .pop_scope()
433 .map_err(InferErr::UnsolvedEvar)
434 }
435
436 pub fn ensure_resolved_evars<R>(
438 &mut self,
439 f: impl FnOnce(&mut Self) -> InferResult<R>,
440 ) -> InferResult<R> {
441 self.push_evar_scope();
442 let r = f(self)?;
443 self.pop_evar_scope()?;
444 Ok(r)
445 }
446
447 pub fn fully_resolve_evars<T: TypeFoldable>(&self, t: &T) -> T {
448 self.inner.borrow().evars.replace_evars(t).unwrap()
449 }
450
451 pub fn tcx(&self) -> TyCtxt<'tcx> {
452 self.genv.tcx()
453 }
454
455 pub fn cursor(&self) -> &Cursor<'infcx> {
456 &self.cursor
457 }
458
459 pub fn allow_raw_deref(&self) -> bool {
460 matches!(self.allow_raw_deref, RawDerefMode::Ok)
461 }
462}
463
464impl<'infcx, 'genv, 'tcx> InferCtxt<'infcx, 'genv, 'tcx> {
466 pub fn change_item<'a>(
467 &'a mut self,
468 def_id: LocalDefId,
469 region_infcx: &'a rustc_infer::infer::InferCtxt<'tcx>,
470 ) -> InferCtxt<'a, 'genv, 'tcx> {
471 InferCtxt {
472 def_id: def_id.to_def_id(),
473 cursor: self.cursor.branch(),
474 region_infcx,
475 ..*self
476 }
477 }
478
479 pub fn move_to(&mut self, marker: &Marker, clear_children: bool) -> InferCtxt<'_, 'genv, 'tcx> {
480 InferCtxt {
481 cursor: self
482 .cursor
483 .move_to(marker, clear_children)
484 .unwrap_or_else(|| tracked_span_bug!()),
485 ..*self
486 }
487 }
488
489 pub fn branch(&mut self) -> InferCtxt<'_, 'genv, 'tcx> {
490 InferCtxt { cursor: self.cursor.branch(), ..*self }
491 }
492
493 fn define_var(&mut self, sort: &Sort, provenance: NameProvenance) -> Name {
494 self.cursor.define_var(sort, provenance)
495 }
496
497 pub fn define_bound_reft_var(&mut self, sort: &Sort, kind: BoundReftKind) -> Name {
498 self.define_var(sort, NameProvenance::UnfoldBoundReft(kind))
499 }
500
501 pub fn define_unknown_var(&mut self, sort: &Sort) -> Name {
502 self.cursor.define_var(sort, NameProvenance::Unknown)
503 }
504
505 pub fn check_pred(&mut self, pred: impl Into<Expr>, tag: Tag) {
506 self.cursor.check_pred(pred, tag);
507 }
508
509 pub fn assume_pred(&mut self, pred: impl Into<Expr>) {
510 self.cursor.assume_pred(pred);
511 }
512
513 pub fn unpack(&mut self, ty: &Ty) -> Ty {
514 self.hoister(false).hoist(ty)
515 }
516
517 pub fn unpack_at_name(&mut self, name: Option<Symbol>, ty: &Ty) -> Ty {
518 let mut hoister = self.hoister(false);
519 hoister.delegate.name = name;
520 hoister.hoist(ty)
521 }
522
523 pub fn marker(&self) -> Marker {
524 self.cursor.marker()
525 }
526
527 pub fn hoister(
528 &mut self,
529 assume_invariants: bool,
530 ) -> Hoister<Unpacker<'_, 'infcx, 'genv, 'tcx>> {
531 Hoister::with_delegate(Unpacker { infcx: self, assume_invariants, name: None })
532 .transparent()
533 }
534
535 pub fn assume_invariants(&mut self, ty: &Ty) {
536 self.cursor
537 .assume_invariants(self.genv.tcx(), ty, self.check_overflow);
538 }
539
540 fn check_impl(&mut self, pred1: impl Into<Expr>, pred2: impl Into<Expr>, tag: Tag) {
541 self.cursor.check_impl(pred1, pred2, tag);
542 }
543}
544
545pub struct Unpacker<'a, 'infcx, 'genv, 'tcx> {
546 infcx: &'a mut InferCtxt<'infcx, 'genv, 'tcx>,
547 assume_invariants: bool,
548 name: Option<Symbol>,
549}
550
551impl HoisterDelegate for Unpacker<'_, '_, '_, '_> {
552 fn hoist_exists(&mut self, ty_ctor: &TyCtor) -> Ty {
553 let ty = ty_ctor.replace_bound_refts_with(|sort, _, kind| {
554 let kind = if let Some(name) = self.name { BoundReftKind::Named(name) } else { kind };
555 Expr::fvar(self.infcx.define_bound_reft_var(sort, kind))
556 });
557 if self.assume_invariants {
558 self.infcx.assume_invariants(&ty);
559 }
560 ty
561 }
562
563 fn hoist_constr(&mut self, pred: Expr) {
564 self.infcx.assume_pred(pred);
565 }
566}
567
568impl std::fmt::Debug for InferCtxt<'_, '_, '_> {
569 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
570 std::fmt::Debug::fmt(&self.cursor, f)
571 }
572}
573
574#[derive(Debug)]
575pub struct InferCtxtAt<'a, 'infcx, 'genv, 'tcx> {
576 pub infcx: &'a mut InferCtxt<'infcx, 'genv, 'tcx>,
577 pub span: Span,
578}
579
580impl<'genv, 'tcx> InferCtxtAt<'_, '_, 'genv, 'tcx> {
581 fn tag(&self, reason: ConstrReason) -> Tag {
582 Tag::new(reason, self.span)
583 }
584
585 pub fn check_pred(&mut self, pred: impl Into<Expr>, reason: ConstrReason) {
586 let tag = self.tag(reason);
587 self.infcx.check_pred(pred, tag);
588 }
589
590 pub fn check_non_closure_clauses(
591 &mut self,
592 clauses: &[rty::Clause],
593 reason: ConstrReason,
594 ) -> InferResult {
595 for clause in clauses {
596 if let rty::ClauseKind::Projection(projection_pred) = clause.kind_skipping_binder() {
597 let impl_elem = BaseTy::projection(projection_pred.projection_ty)
598 .to_ty()
599 .deeply_normalize(self)?;
600 let term = projection_pred.term.to_ty().deeply_normalize(self)?;
601
602 self.subtyping(&impl_elem, &term, reason)?;
604 self.subtyping(&term, &impl_elem, reason)?;
605 }
606 }
607 Ok(())
608 }
609
610 pub fn subtyping_with_env(
613 &mut self,
614 env: &mut impl LocEnv,
615 a: &Ty,
616 b: &Ty,
617 reason: ConstrReason,
618 ) -> InferResult<Vec<Binder<rty::CoroutineObligPredicate>>> {
619 let mut sub = Sub::new(env, reason, self.span);
620 sub.tys(self.infcx, a, b)?;
621 Ok(sub.obligations)
622 }
623
624 pub fn subtyping(
629 &mut self,
630 a: &Ty,
631 b: &Ty,
632 reason: ConstrReason,
633 ) -> InferResult<Vec<Binder<rty::CoroutineObligPredicate>>> {
634 let mut env = DummyEnv;
635 let mut sub = Sub::new(&mut env, reason, self.span);
636 sub.tys(self.infcx, a, b)?;
637 Ok(sub.obligations)
638 }
639
640 pub fn subtyping_generic_args(
641 &mut self,
642 variance: Variance,
643 a: &GenericArg,
644 b: &GenericArg,
645 reason: ConstrReason,
646 ) -> InferResult<Vec<Binder<rty::CoroutineObligPredicate>>> {
647 let mut env = DummyEnv;
648 let mut sub = Sub::new(&mut env, reason, self.span);
649 sub.generic_args(self.infcx, variance, a, b)?;
650 Ok(sub.obligations)
651 }
652
653 pub fn check_constructor(
657 &mut self,
658 variant: EarlyBinder<PolyVariant>,
659 generic_args: &[GenericArg],
660 fields: &[Ty],
661 reason: ConstrReason,
662 ) -> InferResult<Ty> {
663 let ret = self.ensure_resolved_evars(|this| {
664 let generic_args = this.instantiate_generic_args(generic_args);
666
667 let variant = variant
668 .instantiate(this.tcx(), &generic_args, &[])
669 .replace_bound_refts_with(|sort, mode, _| this.fresh_infer_var(sort, mode));
670
671 for (actual, formal) in iter::zip(fields, variant.fields()) {
673 this.subtyping(actual, formal, reason)?;
674 }
675
676 for require in &variant.requires {
678 this.check_pred(require, ConstrReason::Fold);
679 }
680
681 Ok(variant.ret())
682 })?;
683 Ok(self.fully_resolve_evars(&ret))
684 }
685
686 pub fn ensure_resolved_evars<R>(
687 &mut self,
688 f: impl FnOnce(&mut InferCtxtAt<'_, '_, 'genv, 'tcx>) -> InferResult<R>,
689 ) -> InferResult<R> {
690 self.infcx
691 .ensure_resolved_evars(|infcx| f(&mut infcx.at(self.span)))
692 }
693}
694
695impl<'a, 'genv, 'tcx> std::ops::Deref for InferCtxtAt<'_, 'a, 'genv, 'tcx> {
696 type Target = InferCtxt<'a, 'genv, 'tcx>;
697
698 fn deref(&self) -> &Self::Target {
699 self.infcx
700 }
701}
702
703impl std::ops::DerefMut for InferCtxtAt<'_, '_, '_, '_> {
704 fn deref_mut(&mut self) -> &mut Self::Target {
705 self.infcx
706 }
707}
708
709#[derive(TypeVisitable, TypeFoldable)]
713pub(crate) enum TypeTrace {
714 Types(Ty, Ty),
715 BaseTys(BaseTy, BaseTy),
716}
717
718#[expect(dead_code, reason = "we use this for debugging some time")]
719impl TypeTrace {
720 fn tys(a: &Ty, b: &Ty) -> Self {
721 Self::Types(a.clone(), b.clone())
722 }
723
724 fn btys(a: &BaseTy, b: &BaseTy) -> Self {
725 Self::BaseTys(a.clone(), b.clone())
726 }
727}
728
729impl fmt::Debug for TypeTrace {
730 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
731 match self {
732 TypeTrace::Types(a, b) => write!(f, "{a:?} - {b:?}"),
733 TypeTrace::BaseTys(a, b) => write!(f, "{a:?} - {b:?}"),
734 }
735 }
736}
737
738pub trait LocEnv {
739 fn ptr_to_ref(
740 &mut self,
741 infcx: &mut InferCtxtAt,
742 reason: ConstrReason,
743 re: Region,
744 path: &Path,
745 bound: Ty,
746 ) -> InferResult<Ty>;
747
748 fn unfold_strg_ref(&mut self, infcx: &mut InferCtxt, path: &Path, ty: &Ty) -> InferResult<Loc>;
749
750 fn get(&self, path: &Path) -> Ty;
751}
752
753struct DummyEnv;
754
755impl LocEnv for DummyEnv {
756 fn ptr_to_ref(
757 &mut self,
758 _: &mut InferCtxtAt,
759 _: ConstrReason,
760 _: Region,
761 _: &Path,
762 _: Ty,
763 ) -> InferResult<Ty> {
764 tracked_span_bug!("call to `ptr_to_ref` on `DummyEnv`")
765 }
766
767 fn unfold_strg_ref(&mut self, _: &mut InferCtxt, _: &Path, _: &Ty) -> InferResult<Loc> {
768 tracked_span_bug!("call to `unfold_str_ref` on `DummyEnv`")
769 }
770
771 fn get(&self, _: &Path) -> Ty {
772 tracked_span_bug!("call to `get` on `DummyEnv`")
773 }
774}
775
776struct Sub<'a, E> {
778 env: &'a mut E,
780 reason: ConstrReason,
781 span: Span,
782 obligations: Vec<Binder<rty::CoroutineObligPredicate>>,
786}
787
788impl<'a, E: LocEnv> Sub<'a, E> {
789 fn new(env: &'a mut E, reason: ConstrReason, span: Span) -> Self {
790 Self { env, reason, span, obligations: vec![] }
791 }
792
793 fn tag(&self) -> Tag {
794 Tag::new(self.reason, self.span)
795 }
796
797 fn tys(&mut self, infcx: &mut InferCtxt, a: &Ty, b: &Ty) -> InferResult {
798 let infcx = &mut infcx.branch();
799 let a = infcx.unpack(a);
805
806 match (a.kind(), b.kind()) {
807 (TyKind::Exists(..), _) => {
808 bug!("existentials should have been removed by the unpacking above");
809 }
810 (TyKind::Constr(..), _) => {
811 bug!("constraint types should have been removed by the unpacking above");
812 }
813
814 (_, TyKind::Exists(ctor_b)) => {
815 infcx.enter_exists(ctor_b, |infcx, ty_b| self.tys(infcx, &a, &ty_b))
816 }
817 (_, TyKind::Constr(pred_b, ty_b)) => {
818 infcx.check_pred(pred_b, self.tag());
819 self.tys(infcx, &a, ty_b)
820 }
821
822 (TyKind::Ptr(PtrKind::Mut(_), path_a), TyKind::StrgRef(_, path_b, ty_b)) => {
823 let ty_a = self.env.get(path_a);
827 infcx.unify_exprs(&path_a.to_expr(), &path_b.to_expr());
828 self.tys(infcx, &ty_a, ty_b)
829 }
830 (TyKind::StrgRef(_, path_a, ty_a), TyKind::StrgRef(_, path_b, ty_b)) => {
831 self.env.unfold_strg_ref(infcx, path_a, ty_a)?;
843 let ty_a = self.env.get(path_a);
844 infcx.unify_exprs(&path_a.to_expr(), &path_b.to_expr());
845 self.tys(infcx, &ty_a, ty_b)
846 }
847 (
848 TyKind::Ptr(PtrKind::Mut(re), path),
849 TyKind::Indexed(BaseTy::Ref(_, bound, Mutability::Mut), idx),
850 ) => {
851 self.idxs_eq(infcx, &Expr::unit(), idx);
854
855 self.env.ptr_to_ref(
856 &mut infcx.at(self.span),
857 self.reason,
858 *re,
859 path,
860 bound.clone(),
861 )?;
862 Ok(())
863 }
864
865 (TyKind::Indexed(bty_a, idx_a), TyKind::Indexed(bty_b, idx_b)) => {
866 self.btys(infcx, bty_a, bty_b)?;
867 self.idxs_eq(infcx, idx_a, idx_b);
868 Ok(())
869 }
870 (TyKind::Ptr(pk_a, path_a), TyKind::Ptr(pk_b, path_b)) => {
871 debug_assert_eq!(pk_a, pk_b);
872 debug_assert_eq!(path_a, path_b);
873 Ok(())
874 }
875 (TyKind::Param(param_ty_a), TyKind::Param(param_ty_b)) => {
876 debug_assert_eq!(param_ty_a, param_ty_b);
877 Ok(())
878 }
879 (_, TyKind::Uninit) => Ok(()),
880 (TyKind::Downcast(.., fields_a), TyKind::Downcast(.., fields_b)) => {
881 debug_assert_eq!(fields_a.len(), fields_b.len());
882 for (ty_a, ty_b) in iter::zip(fields_a, fields_b) {
883 self.tys(infcx, ty_a, ty_b)?;
884 }
885 Ok(())
886 }
887 _ => Err(query_bug!("incompatible types: `{a:?}` - `{b:?}`"))?,
888 }
889 }
890
891 fn btys(&mut self, infcx: &mut InferCtxt, a: &BaseTy, b: &BaseTy) -> InferResult {
892 match (a, b) {
895 (BaseTy::Int(int_ty_a), BaseTy::Int(int_ty_b)) => {
896 debug_assert_eq!(int_ty_a, int_ty_b);
897 Ok(())
898 }
899 (BaseTy::Uint(uint_ty_a), BaseTy::Uint(uint_ty_b)) => {
900 debug_assert_eq!(uint_ty_a, uint_ty_b);
901 Ok(())
902 }
903 (BaseTy::Adt(a_adt, a_args), BaseTy::Adt(b_adt, b_args)) => {
904 tracked_span_dbg_assert_eq!(a_adt.did(), b_adt.did());
905 tracked_span_dbg_assert_eq!(a_args.len(), b_args.len());
906 let variances = infcx.genv.variances_of(a_adt.did());
907 for (variance, ty_a, ty_b) in izip!(variances, a_args.iter(), b_args.iter()) {
908 self.generic_args(infcx, *variance, ty_a, ty_b)?;
909 }
910 Ok(())
911 }
912 (BaseTy::FnDef(a_def_id, a_args), BaseTy::FnDef(b_def_id, b_args)) => {
913 debug_assert_eq!(a_def_id, b_def_id);
914 debug_assert_eq!(a_args.len(), b_args.len());
915 for (arg_a, arg_b) in iter::zip(a_args, b_args) {
923 match (arg_a, arg_b) {
924 (GenericArg::Ty(ty_a), GenericArg::Ty(ty_b)) => {
925 let bty_a = ty_a.as_bty_skipping_existentials();
926 let bty_b = ty_b.as_bty_skipping_existentials();
927 tracked_span_dbg_assert_eq!(bty_a, bty_b);
928 }
929 (GenericArg::Base(ctor_a), GenericArg::Base(ctor_b)) => {
930 let bty_a = ctor_a.as_bty_skipping_binder();
931 let bty_b = ctor_b.as_bty_skipping_binder();
932 tracked_span_dbg_assert_eq!(bty_a, bty_b);
933 }
934 (_, _) => tracked_span_dbg_assert_eq!(arg_a, arg_b),
935 }
936 }
937 Ok(())
938 }
939 (BaseTy::Float(float_ty_a), BaseTy::Float(float_ty_b)) => {
940 debug_assert_eq!(float_ty_a, float_ty_b);
941 Ok(())
942 }
943 (BaseTy::Slice(ty_a), BaseTy::Slice(ty_b)) => self.tys(infcx, ty_a, ty_b),
944
945 (BaseTy::RawPtr(ty_a, mut_a), BaseTy::RawPtr(ty_b, mut_b)) => {
946 debug_assert_eq!(mut_a, mut_b);
947 self.tys(infcx, ty_a, ty_b)?;
948 if matches!(mut_a, Mutability::Mut) {
949 self.tys(infcx, ty_b, ty_a)?;
950 }
951 Ok(())
952 }
953
954 (BaseTy::Ref(_, ty_a, Mutability::Mut), BaseTy::Ref(_, ty_b, Mutability::Mut)) => {
955 if ty_a.is_slice()
956 && let TyKind::Indexed(_, idx_a) = ty_a.kind()
957 && let TyKind::Exists(bty_b) = ty_b.kind()
958 {
959 self.tys(infcx, ty_a, ty_b)?;
964 self.tys(infcx, &bty_b.replace_bound_reft(idx_a), ty_a)
965 } else {
966 self.tys(infcx, ty_a, ty_b)?;
967 self.tys(infcx, ty_b, ty_a)
968 }
969 }
970 (BaseTy::Ref(_, ty_a, Mutability::Not), BaseTy::Ref(_, ty_b, Mutability::Not)) => {
971 self.tys(infcx, ty_a, ty_b)
972 }
973 (BaseTy::Tuple(tys_a), BaseTy::Tuple(tys_b)) => {
974 debug_assert_eq!(tys_a.len(), tys_b.len());
975 for (ty_a, ty_b) in iter::zip(tys_a, tys_b) {
976 self.tys(infcx, ty_a, ty_b)?;
977 }
978 Ok(())
979 }
980 (
981 BaseTy::Alias(AliasKind::Opaque, alias_ty_a),
982 BaseTy::Alias(AliasKind::Opaque, alias_ty_b),
983 ) => {
984 debug_assert_eq!(alias_ty_a.def_id, alias_ty_b.def_id);
985
986 for (ty_a, ty_b) in izip!(alias_ty_a.args.iter(), alias_ty_b.args.iter()) {
988 self.generic_args(infcx, Invariant, ty_a, ty_b)?;
989 }
990
991 debug_assert_eq!(alias_ty_a.refine_args.len(), alias_ty_b.refine_args.len());
993 iter::zip(alias_ty_a.refine_args.iter(), alias_ty_b.refine_args.iter())
994 .for_each(|(expr_a, expr_b)| infcx.unify_exprs(expr_a, expr_b));
995
996 Ok(())
997 }
998 (_, BaseTy::Alias(AliasKind::Opaque, alias_ty_b)) => {
999 self.handle_opaque_type(infcx, a, alias_ty_b)
1001 }
1002 (
1003 BaseTy::Alias(AliasKind::Projection, alias_ty_a),
1004 BaseTy::Alias(AliasKind::Projection, alias_ty_b),
1005 ) => {
1006 tracked_span_dbg_assert_eq!(alias_ty_a.erase_regions(), alias_ty_b.erase_regions());
1007 Ok(())
1008 }
1009 (BaseTy::Array(ty_a, len_a), BaseTy::Array(ty_b, len_b)) => {
1010 tracked_span_dbg_assert_eq!(len_a, len_b);
1011 self.tys(infcx, ty_a, ty_b)
1012 }
1013 (BaseTy::Param(param_a), BaseTy::Param(param_b)) => {
1014 debug_assert_eq!(param_a, param_b);
1015 Ok(())
1016 }
1017 (BaseTy::Bool, BaseTy::Bool)
1018 | (BaseTy::Str, BaseTy::Str)
1019 | (BaseTy::Char, BaseTy::Char)
1020 | (BaseTy::RawPtrMetadata(_), BaseTy::RawPtrMetadata(_)) => Ok(()),
1021 (BaseTy::Dynamic(preds_a, _), BaseTy::Dynamic(preds_b, _)) => {
1022 tracked_span_assert_eq!(preds_a.erase_regions(), preds_b.erase_regions());
1023 Ok(())
1024 }
1025 (BaseTy::Closure(did1, tys_a, _, _), BaseTy::Closure(did2, tys_b, _, _))
1026 if did1 == did2 =>
1027 {
1028 debug_assert_eq!(tys_a.len(), tys_b.len());
1029 for (ty_a, ty_b) in iter::zip(tys_a, tys_b) {
1030 self.tys(infcx, ty_a, ty_b)?;
1031 }
1032 Ok(())
1033 }
1034 (BaseTy::FnPtr(sig_a), BaseTy::FnPtr(sig_b)) => {
1035 tracked_span_assert_eq!(sig_a.erase_regions(), sig_b.erase_regions());
1036 Ok(())
1037 }
1038 (BaseTy::Never, BaseTy::Never) => Ok(()),
1039 (
1040 BaseTy::Coroutine(did1, resume_ty_a, tys_a, _),
1041 BaseTy::Coroutine(did2, resume_ty_b, tys_b, _),
1042 ) if did1 == did2 => {
1043 debug_assert_eq!(tys_a.len(), tys_b.len());
1044 for (ty_a, ty_b) in iter::zip(tys_a, tys_b) {
1045 self.tys(infcx, ty_a, ty_b)?;
1046 }
1047 self.tys(infcx, resume_ty_b, resume_ty_a)?;
1049 self.tys(infcx, resume_ty_a, resume_ty_b)?;
1050
1051 Ok(())
1052 }
1053 (BaseTy::Foreign(did_a), BaseTy::Foreign(did_b)) if did_a == did_b => Ok(()),
1054 _ => Err(query_bug!("incompatible base types: `{a:#?}` - `{b:#?}`"))?,
1055 }
1056 }
1057
1058 fn generic_args(
1059 &mut self,
1060 infcx: &mut InferCtxt,
1061 variance: Variance,
1062 a: &GenericArg,
1063 b: &GenericArg,
1064 ) -> InferResult {
1065 let (ty_a, ty_b) = match (a, b) {
1066 (GenericArg::Ty(ty_a), GenericArg::Ty(ty_b)) => (ty_a.clone(), ty_b.clone()),
1067 (GenericArg::Base(ctor_a), GenericArg::Base(ctor_b)) => {
1068 tracked_span_dbg_assert_eq!(
1069 ctor_a.sort().erase_regions(),
1070 ctor_b.sort().erase_regions()
1071 );
1072 (ctor_a.to_ty(), ctor_b.to_ty())
1073 }
1074 (GenericArg::Lifetime(_), GenericArg::Lifetime(_)) => return Ok(()),
1075 (GenericArg::Const(cst_a), GenericArg::Const(cst_b)) => {
1076 debug_assert_eq!(cst_a, cst_b);
1077 return Ok(());
1078 }
1079 _ => Err(query_bug!("incompatible generic args: `{a:?}` `{b:?}`"))?,
1080 };
1081 match variance {
1082 Variance::Covariant => self.tys(infcx, &ty_a, &ty_b),
1083 Variance::Invariant => {
1084 self.tys(infcx, &ty_a, &ty_b)?;
1085 self.tys(infcx, &ty_b, &ty_a)
1086 }
1087 Variance::Contravariant => self.tys(infcx, &ty_b, &ty_a),
1088 Variance::Bivariant => Ok(()),
1089 }
1090 }
1091
1092 fn idxs_eq(&mut self, infcx: &mut InferCtxt, a: &Expr, b: &Expr) {
1093 if a == b {
1094 return;
1095 }
1096 match (a.kind(), b.kind()) {
1097 (
1098 ExprKind::Ctor(Ctor::Struct(did_a), flds_a),
1099 ExprKind::Ctor(Ctor::Struct(did_b), flds_b),
1100 ) => {
1101 debug_assert_eq!(did_a, did_b);
1102 for (a, b) in iter::zip(flds_a, flds_b) {
1103 self.idxs_eq(infcx, a, b);
1104 }
1105 }
1106 (ExprKind::Tuple(flds_a), ExprKind::Tuple(flds_b)) => {
1107 for (a, b) in iter::zip(flds_a, flds_b) {
1108 self.idxs_eq(infcx, a, b);
1109 }
1110 }
1111 (ExprKind::Ctor(Ctor::RawPtr, flds_a), ExprKind::Ctor(Ctor::RawPtr, flds_b)) => {
1112 for (a, b) in iter::zip(flds_a, flds_b) {
1113 self.idxs_eq(infcx, a, b);
1114 }
1115 }
1116 (_, ExprKind::Tuple(flds_b)) => {
1117 for (f, b) in flds_b.iter().enumerate() {
1118 let proj = FieldProj::Tuple { arity: flds_b.len(), field: f as u32 };
1119 let a = a.proj_and_reduce(proj);
1120 self.idxs_eq(infcx, &a, b);
1121 }
1122 }
1123 (_, ExprKind::Ctor(Ctor::RawPtr, flds_b)) => {
1124 for (f, b) in flds_b.iter().enumerate() {
1125 let field = rty::RawPtrField::from_index(f as u32).unwrap();
1126 let a = a.proj_and_reduce(FieldProj::RawPtr { field });
1127 self.idxs_eq(infcx, &a, b);
1128 }
1129 }
1130
1131 (_, ExprKind::Ctor(Ctor::Struct(def_id), flds_b)) => {
1132 for (f, b) in flds_b.iter().enumerate() {
1133 let proj = FieldProj::Adt { def_id: *def_id, field: f as u32 };
1134 let a = a.proj_and_reduce(proj);
1135 self.idxs_eq(infcx, &a, b);
1136 }
1137 }
1138
1139 (ExprKind::Tuple(flds_a), _) => {
1140 infcx.unify_exprs(a, b);
1141 for (f, a) in flds_a.iter().enumerate() {
1142 let proj = FieldProj::Tuple { arity: flds_a.len(), field: f as u32 };
1143 let b = b.proj_and_reduce(proj);
1144 self.idxs_eq(infcx, a, &b);
1145 }
1146 }
1147 (ExprKind::Ctor(Ctor::RawPtr, flds_a), _) => {
1148 infcx.unify_exprs(a, b);
1149 for (f, a) in flds_a.iter().enumerate() {
1150 let field = rty::RawPtrField::from_index(f as u32).unwrap();
1151 let b = b.proj_and_reduce(FieldProj::RawPtr { field });
1152 self.idxs_eq(infcx, a, &b);
1153 }
1154 }
1155 (ExprKind::Ctor(Ctor::Struct(def_id), flds_a), _) => {
1156 infcx.unify_exprs(a, b);
1157 for (f, a) in flds_a.iter().enumerate() {
1158 let proj = FieldProj::Adt { def_id: *def_id, field: f as u32 };
1159 let b = b.proj_and_reduce(proj);
1160 self.idxs_eq(infcx, a, &b);
1161 }
1162 }
1163 (ExprKind::Abs(lam_a), ExprKind::Abs(lam_b)) => {
1164 self.abs_eq(infcx, lam_a, lam_b);
1165 }
1166 (_, ExprKind::Abs(lam_b)) => {
1167 self.abs_eq(infcx, &a.eta_expand_abs(lam_b.vars(), lam_b.output()), lam_b);
1168 }
1169 (ExprKind::Abs(lam_a), _) => {
1170 infcx.unify_exprs(a, b);
1171 self.abs_eq(infcx, lam_a, &b.eta_expand_abs(lam_a.vars(), lam_a.output()));
1172 }
1173 (ExprKind::KVar(_), _) | (_, ExprKind::KVar(_)) => {
1174 infcx.check_impl(a, b, self.tag());
1175 infcx.check_impl(b, a, self.tag());
1176 }
1177 _ => {
1178 infcx.unify_exprs(a, b);
1179 let span = b.span();
1180 infcx.check_pred(Expr::binary_op(rty::BinOp::Eq, a, b).at_opt(span), self.tag());
1181 }
1182 }
1183 }
1184
1185 fn abs_eq(&mut self, infcx: &mut InferCtxt, a: &Lambda, b: &Lambda) {
1186 debug_assert_eq!(a.vars().len(), b.vars().len());
1187 let vars = a
1188 .vars()
1189 .iter()
1190 .map(|kind| {
1191 let (sort, _, kind) = kind.expect_refine();
1192 Expr::fvar(infcx.define_bound_reft_var(sort, kind))
1193 })
1194 .collect_vec();
1195 let body_a = a.apply(&vars);
1196 let body_b = b.apply(&vars);
1197 self.idxs_eq(infcx, &body_a, &body_b);
1198 }
1199
1200 fn handle_opaque_type(
1201 &mut self,
1202 infcx: &mut InferCtxt,
1203 bty: &BaseTy,
1204 alias_ty: &AliasTy,
1205 ) -> InferResult {
1206 if let BaseTy::Coroutine(def_id, resume_ty, upvar_tys, args) = bty {
1207 let obligs = mk_coroutine_obligations(
1208 infcx.genv,
1209 def_id,
1210 resume_ty,
1211 upvar_tys,
1212 &alias_ty.def_id,
1213 args.clone(),
1214 )?;
1215 self.obligations.extend(obligs);
1216 } else {
1217 let bounds = infcx.genv.item_bounds(alias_ty.def_id)?.instantiate(
1218 infcx.tcx(),
1219 &alias_ty.args,
1220 &alias_ty.refine_args,
1221 );
1222 for clause in &bounds {
1223 if !clause.kind().vars().is_empty() {
1224 Err(query_bug!("handle_opaque_types: clause with bound vars: `{clause:?}`"))?;
1225 }
1226 if let rty::ClauseKind::Projection(pred) = clause.kind_skipping_binder() {
1227 let alias_ty = pred.projection_ty.with_self_ty(bty.to_subset_ty_ctor());
1228 let ty1 = BaseTy::Alias(AliasKind::Projection, alias_ty)
1229 .to_ty()
1230 .deeply_normalize(&mut infcx.at(self.span))?;
1231 let ty2 = pred.term.to_ty();
1232 self.tys(infcx, &ty1, &ty2)?;
1233 }
1234 }
1235 }
1236 Ok(())
1237 }
1238}
1239
1240fn mk_coroutine_obligations(
1241 genv: GlobalEnv,
1242 generator_did: &DefId,
1243 resume_ty: &Ty,
1244 upvar_tys: &List<Ty>,
1245 opaque_def_id: &DefId,
1246 args: flux_rustc_bridge::ty::GenericArgs,
1247) -> InferResult<Vec<Binder<rty::CoroutineObligPredicate>>> {
1248 let bounds = genv.item_bounds(*opaque_def_id)?.skip_binder();
1249 for bound in &bounds {
1250 if let Some(proj_clause) = bound.as_projection_clause() {
1251 return Ok(vec![proj_clause.map(|proj_clause| {
1252 let output = proj_clause.term;
1253 CoroutineObligPredicate {
1254 def_id: *generator_did,
1255 resume_ty: resume_ty.clone(),
1256 upvar_tys: upvar_tys.clone(),
1257 output: output.to_ty(),
1258 args,
1259 }
1260 })]);
1261 }
1262 }
1263 bug!("no projection predicate")
1264}
1265
1266#[derive(Debug)]
1267pub enum InferErr {
1268 UnsolvedEvar(EVid),
1269 Query(QueryErr),
1270}
1271
1272impl From<QueryErr> for InferErr {
1273 fn from(v: QueryErr) -> Self {
1274 Self::Query(v)
1275 }
1276}
1277
1278mod pretty {
1279 use std::fmt;
1280
1281 use flux_middle::pretty::*;
1282
1283 use super::*;
1284
1285 impl Pretty for Tag {
1286 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1287 w!(cx, f, "{:?} at {:?}", ^self.reason, self.src_span)?;
1288 if let Some(dst_span) = self.dst_span {
1289 w!(cx, f, " ({:?})", ^dst_span)?;
1290 }
1291 Ok(())
1292 }
1293 }
1294
1295 impl_debug_with_default_cx!(Tag);
1296}