1mod place_ty;
2
3use std::{iter, ops::ControlFlow};
4
5use flux_common::{
6 bug,
7 dbg::{SpanTrace, debug_assert_eq3},
8 tracked_span_bug, tracked_span_dbg_assert_eq,
9};
10use flux_infer::{
11 infer::{ConstrReason, InferCtxt, InferCtxtAt, InferCtxtRoot, InferResult},
12 refine_tree::Scope,
13};
14use flux_macros::DebugAsJson;
15use flux_middle::{
16 PlaceExt as _,
17 global_env::GlobalEnv,
18 pretty::{PrettyCx, PrettyNested},
19 queries::QueryResult,
20 rty::{
21 BaseTy, Binder, BoundReftKind, BoundVariableKind, Ctor, Expr, ExprKind, FnOutput, FnSig,
22 GenericArg, HoleKind, INNERMOST, Lambda, List, Loc, Mutability, Path, PolyFnSig, PtrKind,
23 Region, SortCtor, SubsetTy, SubsetTyCtor, Ty, TyKind, VariantIdx,
24 canonicalize::{Hoister, LocalHoister},
25 fold::{FallibleTypeFolder, TypeFoldable, TypeVisitable, TypeVisitor},
26 region_matching::{rty_match_regions, ty_match_regions},
27 },
28};
29use flux_rustc_bridge::{
30 self,
31 mir::{BasicBlock, Body, Local, LocalDecl, LocalDecls, Place, PlaceElem},
32 ty,
33};
34use itertools::{Itertools, izip};
35use rustc_data_structures::unord::UnordMap;
36use rustc_index::{IndexSlice, IndexVec};
37use rustc_middle::{mir::RETURN_PLACE, ty::TyCtxt};
38use rustc_span::{Span, Symbol};
39use rustc_type_ir::BoundVar;
40use serde::Serialize;
41
42use self::place_ty::{LocKind, PlacesTree};
43use super::rty::Sort;
44
45#[derive(Clone, Default)]
46pub struct TypeEnv<'a> {
47 bindings: PlacesTree,
48 local_decls: &'a LocalDecls,
49}
50
51pub struct BasicBlockEnvShape {
52 scope: Scope,
53 bindings: PlacesTree,
54}
55
56pub struct BasicBlockEnv {
57 data: Binder<BasicBlockEnvData>,
58 scope: Scope,
59}
60
61#[derive(Debug)]
62struct BasicBlockEnvData {
63 constrs: List<Expr>,
64 bindings: PlacesTree,
65}
66
67impl<'a> TypeEnv<'a> {
68 pub fn new(infcx: &mut InferCtxt, body: &'a Body, fn_sig: &FnSig) -> TypeEnv<'a> {
69 let mut env = TypeEnv { bindings: PlacesTree::default(), local_decls: &body.local_decls };
70
71 for requires in fn_sig.requires() {
72 infcx.assume_pred(requires);
73 }
74
75 for (local, ty) in body.args_iter().zip(fn_sig.inputs()) {
76 let ty = infcx.unpack(ty);
77 infcx.assume_invariants(&ty);
78 env.alloc_with_ty(local, ty);
79 }
80
81 for local in body.vars_and_temps_iter() {
82 env.alloc(local);
83 }
84
85 env.alloc(RETURN_PLACE);
86 env
87 }
88
89 pub fn empty() -> TypeEnv<'a> {
90 TypeEnv { bindings: PlacesTree::default(), local_decls: IndexSlice::empty() }
91 }
92
93 fn alloc_with_ty(&mut self, local: Local, ty: Ty) {
94 let ty = ty_match_regions(&ty, &self.local_decls[local].ty);
95 self.bindings.insert(local.into(), LocKind::Local, ty);
96 }
97
98 fn alloc(&mut self, local: Local) {
99 self.bindings
100 .insert(local.into(), LocKind::Local, Ty::uninit());
101 }
102
103 pub(crate) fn into_infer(self, scope: Scope) -> BasicBlockEnvShape {
104 BasicBlockEnvShape::new(scope, self)
105 }
106
107 pub(crate) fn lookup_rust_ty(&self, genv: GlobalEnv, place: &Place) -> QueryResult<ty::Ty> {
108 Ok(place.ty(genv, self.local_decls)?.ty)
109 }
110
111 pub(crate) fn lookup_place(
112 &mut self,
113 infcx: &mut InferCtxtAt,
114 place: &Place,
115 ) -> InferResult<Ty> {
116 let span = infcx.span;
117 let result = self.bindings.lookup_unfolding(infcx, place, span)?;
118 Ok(result.ty)
119 }
120
121 pub(crate) fn get(&self, path: &Path) -> Ty {
122 self.bindings.get(path)
123 }
124
125 pub fn update_path(&mut self, path: &Path, new_ty: Ty, span: Span) {
126 self.bindings.lookup(path, span).update(new_ty);
127 }
128
129 pub(crate) fn borrow(
133 &mut self,
134 infcx: &mut InferCtxtAt,
135 re: Region,
136 mutbl: Mutability,
137 place: &Place,
138 ) -> InferResult<Ty> {
139 let span = infcx.span;
140 let result = self.bindings.lookup_unfolding(infcx, place, span)?;
141 if result.is_strg && mutbl == Mutability::Mut {
142 Ok(Ty::ptr(PtrKind::Mut(re), result.path()))
143 } else {
144 Ok(Ty::mk_ref(re, result.ty, mutbl))
148 }
149 }
150
151 pub(crate) fn ptr_to_ref_at_place(
154 &mut self,
155 infcx: &mut InferCtxtAt,
156 place: &Place,
157 ) -> InferResult {
158 let lookup = self.bindings.lookup(place, infcx.span);
159 let TyKind::Ptr(PtrKind::Mut(re), path) = lookup.ty.kind() else {
160 tracked_span_bug!("ptr_to_borrow called on non mutable pointer type")
161 };
162
163 let ref_ty =
164 self.ptr_to_ref(infcx, ConstrReason::Other, *re, path, PtrToRefBound::Infer)?;
165
166 self.bindings.lookup(place, infcx.span).update(ref_ty);
167
168 Ok(())
169 }
170
171 pub(crate) fn ptr_to_ref(
195 &mut self,
196 infcx: &mut InferCtxtAt,
197 reason: ConstrReason,
198 re: Region,
199 path: &Path,
200 bound: PtrToRefBound,
201 ) -> InferResult<Ty> {
202 let t1 = self.bindings.lookup(path, infcx.span).fold(infcx)?;
204
205 let t2 = match bound {
207 PtrToRefBound::Ty(t2) => {
208 let t2 = rty_match_regions(&t2, &t1);
209 infcx.subtyping_with_env(self, &t1, &t2, reason)?;
210 t2
211 }
212 PtrToRefBound::Infer => {
213 let t2 = t1.with_holes().replace_holes(|sorts, kind| {
214 debug_assert_eq!(kind, HoleKind::Pred);
215 infcx.fresh_kvar(sorts)
216 });
217 infcx.subtyping_with_env(self, &t1, &t2, reason)?;
218 t2
219 }
220 PtrToRefBound::Identity => t1.clone(),
221 };
222
223 self.bindings
225 .lookup(path, infcx.span)
226 .block_with(t2.clone());
227
228 Ok(Ty::mk_ref(re, t2, Mutability::Mut))
229 }
230
231 pub(crate) fn fold_local_ptrs(&mut self, infcx: &mut InferCtxtAt) -> InferResult {
232 for (loc, bound, ty) in self.bindings.local_ptrs() {
233 infcx.subtyping(&ty, &bound, ConstrReason::FoldLocal)?;
234 self.bindings.remove_local(&loc);
235 }
236 Ok(())
237 }
238
239 pub(crate) fn assign(
251 &mut self,
252 infcx: &mut InferCtxtAt,
253 place: &Place,
254 new_ty: Ty,
255 ) -> InferResult {
256 let rustc_ty = place.ty(infcx.genv, self.local_decls)?.ty;
257 let new_ty = ty_match_regions(&new_ty, &rustc_ty);
258 let span = infcx.span;
259 let result = self.bindings.lookup_unfolding(infcx, place, span)?;
260 if result.is_strg {
261 result.update(new_ty);
262 } else if !place.behind_raw_ptr(infcx.genv, self.local_decls)? {
263 infcx.subtyping(&new_ty, &result.ty, ConstrReason::Assign)?;
264 }
265 Ok(())
266 }
267
268 pub(crate) fn move_place(&mut self, infcx: &mut InferCtxtAt, place: &Place) -> InferResult<Ty> {
269 let span = infcx.span;
270 let result = self.bindings.lookup_unfolding(infcx, place, span)?;
271 if result.is_strg {
272 let uninit = Ty::uninit();
273 Ok(result.update(uninit))
274 } else {
275 Ok(result.ty)
278 }
279 }
280
281 pub(crate) fn unpack(&mut self, infcx: &mut InferCtxt) {
282 self.bindings
283 .fmap_mut(|_loc, ty| infcx.hoister(true).hoist(ty));
284 }
285
286 pub(crate) fn unblock(&mut self, infcx: &mut InferCtxt, place: &Place) {
287 self.bindings.unblock(infcx, place);
288 }
289
290 pub(crate) fn check_goto(
291 self,
292 infcx: &mut InferCtxtAt,
293 bb_env: &BasicBlockEnv,
294 target: BasicBlock,
295 ) -> InferResult {
296 infcx.ensure_resolved_evars(|infcx| {
297 let bb_env = bb_env
298 .data
299 .replace_bound_refts_with(|sort, mode, _| infcx.fresh_infer_var(sort, mode));
300
301 for constr in &bb_env.constrs {
303 infcx.check_pred(constr, ConstrReason::Goto(target));
304 }
305
306 let bb_env = bb_env.bindings.flatten();
308 for (path, _, ty2) in bb_env {
309 let ty1 = self.bindings.get(&path);
310 infcx.subtyping(&ty1.unblocked(), &ty2.unblocked(), ConstrReason::Goto(target))?;
311 }
312 Ok(())
313 })
314 }
315
316 pub(crate) fn fold(&mut self, infcx: &mut InferCtxtAt, place: &Place) -> InferResult {
317 let span = infcx.span;
318 self.bindings.lookup(place, span).fold(infcx)?;
319 Ok(())
320 }
321
322 pub(crate) fn unfold_local_ptr(
323 &mut self,
324 infcx: &mut InferCtxt,
325 bound: &Ty,
326 ) -> InferResult<Loc> {
327 let name = infcx.define_unknown_var(&Sort::Loc);
328 let loc = Loc::from(name);
329 let ty = infcx.unpack(bound);
330 self.bindings
331 .insert(loc, LocKind::LocalPtr(bound.clone()), ty);
332 Ok(loc)
333 }
334
335 pub(crate) fn unfold_strg_ref(
340 &mut self,
341 infcx: &mut InferCtxt,
342 path: &Path,
343 ty: &Ty,
344 ) -> InferResult<Loc> {
345 if let Some(loc) = path.to_loc() {
346 let ty = infcx.unpack(ty);
347 self.bindings.insert(loc, LocKind::Universal, ty);
348 Ok(loc)
349 } else {
350 bug!("unfold_strg_ref: unexpected path {path:?}")
351 }
352 }
353
354 pub(crate) fn unfold(
355 &mut self,
356 infcx: &mut InferCtxt,
357 place: &Place,
358 span: Span,
359 ) -> InferResult {
360 self.bindings.unfold(infcx, place, span)
361 }
362
363 pub(crate) fn downcast(
364 &mut self,
365 infcx: &mut InferCtxtAt,
366 place: &Place,
367 variant_idx: VariantIdx,
368 ) -> InferResult {
369 let mut down_place = place.clone();
370 let span = infcx.span;
371 down_place
372 .projection
373 .push(PlaceElem::Downcast(None, variant_idx));
374 self.bindings.unfold(infcx, &down_place, span)?;
375 Ok(())
376 }
377
378 pub fn fully_resolve_evars(&mut self, infcx: &InferCtxt) {
379 self.bindings
380 .fmap_mut(|_loc, ty| infcx.fully_resolve_evars(ty));
381 }
382}
383
384pub(crate) enum PtrToRefBound {
385 Ty(Ty),
386 Infer,
387 Identity,
388}
389
390impl flux_infer::infer::LocEnv for TypeEnv<'_> {
391 fn ptr_to_ref(
392 &mut self,
393 infcx: &mut InferCtxtAt,
394 reason: ConstrReason,
395 re: Region,
396 path: &Path,
397 bound: Ty,
398 ) -> InferResult<Ty> {
399 self.ptr_to_ref(infcx, reason, re, path, PtrToRefBound::Ty(bound))
400 }
401
402 fn get(&self, path: &Path) -> Ty {
403 self.get(path)
404 }
405
406 fn unfold_strg_ref(&mut self, infcx: &mut InferCtxt, path: &Path, ty: &Ty) -> InferResult<Loc> {
407 self.unfold_strg_ref(infcx, path, ty)
408 }
409
410 fn unfold_local_ptr(&mut self, infcx: &mut InferCtxt, bound: &Ty) -> InferResult<Loc> {
411 self.unfold_local_ptr(infcx, bound)
412 }
413
414 fn fold_local_ptrs(&mut self, infcx: &mut InferCtxtAt) -> InferResult {
415 self.fold_local_ptrs(infcx)
416 }
417
418 fn update_path(&mut self, path: &Path, new_ty: Ty, span: Span) {
419 self.update_path(path, new_ty, span);
420 }
421}
422
423impl BasicBlockEnvShape {
424 pub fn enter<'a>(&self, local_decls: &'a LocalDecls) -> TypeEnv<'a> {
425 TypeEnv { bindings: self.bindings.clone(), local_decls }
426 }
427
428 fn new(scope: Scope, env: TypeEnv) -> BasicBlockEnvShape {
429 let mut bindings = env.bindings;
430 bindings.fmap_mut(|_loc, ty| BasicBlockEnvShape::pack_ty(&scope, ty));
431 BasicBlockEnvShape { scope, bindings }
432 }
433
434 fn pack_ty(scope: &Scope, ty: &Ty) -> Ty {
435 match ty.kind() {
436 TyKind::Indexed(bty, idxs) => {
437 let bty = BasicBlockEnvShape::pack_bty(scope, bty);
438 if scope.has_free_vars(idxs) {
439 Ty::exists_with_constr(bty, Expr::hole(HoleKind::Pred))
440 } else {
441 Ty::indexed(bty, idxs.clone())
442 }
443 }
444 TyKind::Downcast(adt, args, ty, variant, fields) => {
445 debug_assert!(!scope.has_free_vars(args));
446 debug_assert!(!scope.has_free_vars(ty));
447 let fields = fields.iter().map(|ty| Self::pack_ty(scope, ty)).collect();
448 Ty::downcast(adt.clone(), args.clone(), ty.clone(), *variant, fields)
449 }
450 TyKind::Blocked(ty) => Ty::blocked(BasicBlockEnvShape::pack_ty(scope, ty)),
451 TyKind::Exists(_)
453 | TyKind::Discr(..)
454 | TyKind::Ptr(..)
455 | TyKind::Uninit
456 | TyKind::Param(_)
457 | TyKind::Constr(_, _) => ty.clone(),
458 TyKind::Infer(_) => bug!("unexpected hole whecn checking function body"),
459 TyKind::StrgRef(..) => bug!("unexpected strong reference when checking function body"),
460 }
461 }
462
463 fn pack_bty(scope: &Scope, bty: &BaseTy) -> BaseTy {
464 match bty {
465 BaseTy::Adt(adt_def, args) => {
466 let args = List::from_vec(
467 args.iter()
468 .map(|arg| Self::pack_generic_arg(scope, arg))
469 .collect(),
470 );
471 BaseTy::adt(adt_def.clone(), args)
472 }
473 BaseTy::FnDef(def_id, args) => {
474 let args = List::from_vec(
475 args.iter()
476 .map(|arg| Self::pack_generic_arg(scope, arg))
477 .collect(),
478 );
479 BaseTy::fn_def(*def_id, args)
480 }
481 BaseTy::Tuple(tys) => {
482 let tys = tys
483 .iter()
484 .map(|ty| BasicBlockEnvShape::pack_ty(scope, ty))
485 .collect();
486 BaseTy::Tuple(tys)
487 }
488 BaseTy::Slice(ty) => BaseTy::Slice(Self::pack_ty(scope, ty)),
489 BaseTy::Ref(r, ty, mutbl) => BaseTy::Ref(*r, Self::pack_ty(scope, ty), *mutbl),
490 BaseTy::Array(ty, c) => BaseTy::Array(Self::pack_ty(scope, ty), c.clone()),
491 BaseTy::Int(_)
492 | BaseTy::Param(_)
493 | BaseTy::Uint(_)
494 | BaseTy::Bool
495 | BaseTy::Float(_)
496 | BaseTy::Str
497 | BaseTy::RawPtr(_, _)
498 | BaseTy::RawPtrMetadata(_)
499 | BaseTy::Char
500 | BaseTy::Never
501 | BaseTy::Closure(..)
502 | BaseTy::Dynamic(..)
503 | BaseTy::Alias(..)
504 | BaseTy::FnPtr(..)
505 | BaseTy::Foreign(..)
506 | BaseTy::Coroutine(..) => {
507 if scope.has_free_vars(bty) {
508 tracked_span_bug!("unexpected type with free vars")
509 } else {
510 bty.clone()
511 }
512 }
513 BaseTy::Pat => {
514 todo!()
515 }
516 BaseTy::Infer(..) => {
517 tracked_span_bug!("unexpected infer type")
518 }
519 }
520 }
521
522 fn pack_generic_arg(scope: &Scope, arg: &GenericArg) -> GenericArg {
523 match arg {
524 GenericArg::Ty(ty) => GenericArg::Ty(Self::pack_ty(scope, ty)),
525 GenericArg::Base(ctor) => GenericArg::Base(Self::pack_subset_ty_ctor(scope, ctor)),
526 GenericArg::Lifetime(re) => GenericArg::Lifetime(*re),
527 GenericArg::Const(c) => GenericArg::Const(c.clone()),
528 }
529 }
530
531 fn pack_subset_ty_ctor(scope: &Scope, ctor: &SubsetTyCtor) -> SubsetTyCtor {
532 let sty = ctor.as_ref().skip_binder();
533 debug_assert!(sty.idx.is_nu());
534 let bty = Self::pack_bty(scope, &sty.bty);
535 let pred = if scope.has_free_vars(&sty.pred) {
536 Expr::hole(HoleKind::Pred)
537 } else {
538 sty.pred.clone()
539 };
540 let sort = bty.sort();
541 Binder::bind_with_sort(SubsetTy::new(bty, Expr::nu(), pred), sort)
542 }
543
544 fn update(&mut self, path: &Path, ty: Ty, span: Span) {
545 self.bindings.lookup(path, span).update(ty);
546 }
547
548 pub(crate) fn join(&mut self, other: TypeEnv, span: Span) -> bool {
552 let paths = self.bindings.paths();
553
554 let mut modified = false;
556 for path in &paths {
557 let ty1 = self.bindings.get(path);
558 let ty2 = other.bindings.get(path);
559 let ty = if ty1 == ty2 { ty1.clone() } else { self.join_ty(&ty1, &ty2) };
560 modified |= ty1 != ty;
561 self.update(path, ty, span);
562 }
563
564 modified
565 }
566
567 fn join_ty(&self, ty1: &Ty, ty2: &Ty) -> Ty {
568 match (ty1.kind(), ty2.kind()) {
569 (TyKind::Blocked(ty1), _) => Ty::blocked(self.join_ty(ty1, &ty2.unblocked())),
570 (_, TyKind::Blocked(ty2)) => Ty::blocked(self.join_ty(&ty1.unblocked(), ty2)),
571 (TyKind::Uninit, _) | (_, TyKind::Uninit) => Ty::uninit(),
572 (TyKind::Exists(ty1), _) => self.join_ty(ty1.as_ref().skip_binder(), ty2),
573 (_, TyKind::Exists(ty2)) => self.join_ty(ty1, ty2.as_ref().skip_binder()),
574 (TyKind::Constr(_, ty1), _) => self.join_ty(ty1, ty2),
575 (_, TyKind::Constr(_, ty2)) => self.join_ty(ty1, ty2),
576 (TyKind::Indexed(bty1, idx1), TyKind::Indexed(bty2, idx2)) => {
577 let bty = self.join_bty(bty1, bty2);
578 let mut sorts = vec![];
579 let idx = self.join_idx(idx1, idx2, &bty.sort(), &mut sorts);
580 if sorts.is_empty() {
581 Ty::indexed(bty, idx)
582 } else {
583 let ty = Ty::constr(Expr::hole(HoleKind::Pred), Ty::indexed(bty, idx));
584 Ty::exists(Binder::bind_with_sorts(ty, &sorts))
585 }
586 }
587 (TyKind::Ptr(rk1, path1), TyKind::Ptr(rk2, path2)) => {
588 debug_assert_eq!(rk1, rk2);
589 debug_assert_eq!(path1, path2);
590 Ty::ptr(*rk1, path1.clone())
591 }
592 (TyKind::Param(param_ty1), TyKind::Param(param_ty2)) => {
593 debug_assert_eq!(param_ty1, param_ty2);
594 Ty::param(*param_ty1)
595 }
596 (
597 TyKind::Downcast(adt1, args1, ty1, variant1, fields1),
598 TyKind::Downcast(adt2, args2, ty2, variant2, fields2),
599 ) => {
600 debug_assert_eq!(adt1, adt2);
601 debug_assert_eq!(args1, args2);
602 debug_assert!(ty1 == ty2 && !self.scope.has_free_vars(ty2));
603 debug_assert_eq!(variant1, variant2);
604 debug_assert_eq!(fields1.len(), fields2.len());
605 let fields = iter::zip(fields1, fields2)
606 .map(|(ty1, ty2)| self.join_ty(ty1, ty2))
607 .collect();
608 Ty::downcast(adt1.clone(), args1.clone(), ty1.clone(), *variant1, fields)
609 }
610 _ => tracked_span_bug!("unexpected types: `{ty1:?}` - `{ty2:?}`"),
611 }
612 }
613
614 fn join_idx(&self, e1: &Expr, e2: &Expr, sort: &Sort, bound_sorts: &mut Vec<Sort>) -> Expr {
615 match (e1.kind(), e2.kind(), sort) {
616 (ExprKind::Tuple(es1), ExprKind::Tuple(es2), Sort::Tuple(sorts)) => {
617 debug_assert_eq3!(es1.len(), es2.len(), sorts.len());
618 Expr::tuple(
619 izip!(es1, es2, sorts)
620 .map(|(e1, e2, sort)| self.join_idx(e1, e2, sort, bound_sorts))
621 .collect(),
622 )
623 }
624 (
625 ExprKind::Ctor(Ctor::Struct(_), flds1),
626 ExprKind::Ctor(Ctor::Struct(_), flds2),
627 Sort::App(SortCtor::Adt(sort_def), args),
628 ) => {
629 let sorts = sort_def.struct_variant().field_sorts(args);
630 debug_assert_eq3!(flds1.len(), flds2.len(), sorts.len());
631
632 Expr::ctor_struct(
633 sort_def.did(),
634 izip!(flds1, flds2, &sorts)
635 .map(|(f1, f2, sort)| self.join_idx(f1, f2, sort, bound_sorts))
636 .collect(),
637 )
638 }
639 _ => {
640 let has_free_vars2 = self.scope.has_free_vars(e2);
641 let has_escaping_vars1 = e1.has_escaping_bvars();
642 let has_escaping_vars2 = e2.has_escaping_bvars();
643 if !has_free_vars2 && !has_escaping_vars1 && !has_escaping_vars2 && e1 == e2 {
644 e1.clone()
645 } else if sort.is_pred() {
646 let fsort = sort.expect_func().expect_mono();
649 Expr::abs(Lambda::bind_with_fsort(Expr::hole(HoleKind::Pred), fsort))
650 } else {
651 bound_sorts.push(sort.clone());
652 Expr::bvar(
653 INNERMOST,
654 BoundVar::from_usize(bound_sorts.len() - 1),
655 BoundReftKind::Anon,
656 )
657 }
658 }
659 }
660 }
661
662 fn join_bty(&self, bty1: &BaseTy, bty2: &BaseTy) -> BaseTy {
663 match (bty1, bty2) {
664 (BaseTy::Adt(def1, args1), BaseTy::Adt(def2, args2)) => {
665 tracked_span_dbg_assert_eq!(def1.did(), def2.did());
666 let args = iter::zip(args1, args2)
667 .map(|(arg1, arg2)| self.join_generic_arg(arg1, arg2))
668 .collect();
669 BaseTy::adt(def1.clone(), List::from_vec(args))
670 }
671 (BaseTy::Tuple(fields1), BaseTy::Tuple(fields2)) => {
672 let fields = iter::zip(fields1, fields2)
673 .map(|(ty1, ty2)| self.join_ty(ty1, ty2))
674 .collect();
675 BaseTy::Tuple(fields)
676 }
677 (BaseTy::Alias(alias_ty1), BaseTy::Alias(alias_ty2)) => {
678 tracked_span_dbg_assert_eq!(alias_ty1, alias_ty2);
679 BaseTy::Alias(alias_ty1.clone())
680 }
681 (BaseTy::Ref(r1, ty1, mutbl1), BaseTy::Ref(r2, ty2, mutbl2)) => {
682 tracked_span_dbg_assert_eq!(r1, r2);
683 tracked_span_dbg_assert_eq!(mutbl1, mutbl2);
684 BaseTy::Ref(*r1, self.join_ty(ty1, ty2), *mutbl1)
685 }
686 (BaseTy::Array(ty1, len1), BaseTy::Array(ty2, len2)) => {
687 tracked_span_dbg_assert_eq!(len1, len2);
688 BaseTy::Array(self.join_ty(ty1, ty2), len1.clone())
689 }
690 (BaseTy::Slice(ty1), BaseTy::Slice(ty2)) => BaseTy::Slice(self.join_ty(ty1, ty2)),
691 (BaseTy::FnPtr(sig1), BaseTy::FnPtr(sig2)) if sig1 != sig2 => {
692 BaseTy::FnPtr(generalize_fn_sig(sig1))
696 }
697 _ => {
698 tracked_span_dbg_assert_eq!(bty1, bty2);
699 bty1.clone()
700 }
701 }
702 }
703
704 fn join_generic_arg(&self, arg1: &GenericArg, arg2: &GenericArg) -> GenericArg {
705 match (arg1, arg2) {
706 (GenericArg::Ty(ty1), GenericArg::Ty(ty2)) => GenericArg::Ty(self.join_ty(ty1, ty2)),
707 (GenericArg::Base(ctor1), GenericArg::Base(ctor2)) => {
708 let sty1 = ctor1.as_ref().skip_binder();
709 let sty2 = ctor2.as_ref().skip_binder();
710 debug_assert!(sty1.idx.is_nu());
711 debug_assert!(sty2.idx.is_nu());
712
713 let bty = self.join_bty(&sty1.bty, &sty2.bty);
714 let pred = if self.scope.has_free_vars(&sty2.pred) || sty1.pred != sty2.pred {
715 Expr::hole(HoleKind::Pred)
716 } else {
717 sty1.pred.clone()
718 };
719 let sort = bty.sort();
720 let ctor = Binder::bind_with_sort(SubsetTy::new(bty, Expr::nu(), pred), sort);
721 GenericArg::Base(ctor)
722 }
723 (GenericArg::Lifetime(re1), GenericArg::Lifetime(_re2)) => {
724 GenericArg::Lifetime(*re1)
728 }
729 (GenericArg::Const(c1), GenericArg::Const(c2)) => {
730 debug_assert_eq!(c1, c2);
731 GenericArg::Const(c1.clone())
732 }
733 _ => tracked_span_bug!("unexpected generic args: `{arg1:?}` - `{arg2:?}`"),
734 }
735 }
736
737 pub fn into_bb_env(self, infcx: &mut InferCtxtRoot, body: &Body) -> BasicBlockEnv {
738 let mut delegate = LocalHoister::default();
739 let mut hoister = Hoister::with_delegate(&mut delegate).transparent();
740
741 let mut bindings = self.bindings;
742 bindings.fmap_mut(|loc, ty| {
743 let name = if let Loc::Local(local) = loc {
744 body.local_names.get(local).copied()
745 } else {
746 None
747 };
748 hoister.delegate.name = name;
749 hoister.hoist(ty)
750 });
751
752 BasicBlockEnv {
753 data: delegate.bind(|vars, preds| {
756 let mut constrs = preds
758 .into_iter()
759 .filter(|pred| !matches!(pred.kind(), ExprKind::Hole(HoleKind::Pred)))
760 .collect_vec();
761 let kvar = infcx.fresh_kvar_in_scope(std::slice::from_ref(&vars), &self.scope);
762 constrs.push(kvar);
763
764 let mut kvar_gen = |binders: &[_], kind| {
766 debug_assert_eq!(kind, HoleKind::Pred);
767 let binders = std::iter::once(vars.clone())
768 .chain(binders.iter().cloned())
769 .collect_vec();
770 infcx.fresh_kvar_in_scope(&binders, &self.scope)
771 };
772 bindings.fmap_mut(|_, binding| binding.replace_holes(&mut kvar_gen));
773
774 BasicBlockEnvData { constrs: constrs.into(), bindings }
775 }),
776 scope: self.scope,
777 }
778 }
779}
780
781fn generalize_fn_sig(sig: &PolyFnSig) -> PolyFnSig {
792 let vars = sig
793 .vars()
794 .iter()
795 .filter(|var| matches!(var, BoundVariableKind::Region(_)))
796 .cloned()
797 .collect();
798 let fn_sig = sig.skip_binder_ref();
799 let ret = fn_sig.output().skip_binder().ret.with_holes();
800 let fn_sig = FnSig::new(
801 fn_sig.safety,
802 fn_sig.abi,
803 List::empty(),
804 fn_sig.inputs.with_holes(),
805 Binder::dummy(FnOutput::new(ret, vec![])),
806 Expr::ff(),
807 fn_sig.lifted,
808 );
809 Binder::bind_with_vars(fn_sig, vars)
810}
811
812impl TypeVisitable for BasicBlockEnvData {
813 fn visit_with<V: TypeVisitor>(&self, _visitor: &mut V) -> ControlFlow<V::BreakTy> {
814 unimplemented!()
815 }
816}
817
818impl TypeFoldable for BasicBlockEnvData {
819 fn try_fold_with<F: FallibleTypeFolder>(
820 &self,
821 folder: &mut F,
822 ) -> std::result::Result<Self, F::Error> {
823 Ok(BasicBlockEnvData {
824 constrs: self.constrs.try_fold_with(folder)?,
825 bindings: self.bindings.try_fold_with(folder)?,
826 })
827 }
828}
829
830impl BasicBlockEnv {
831 pub(crate) fn enter<'a>(
832 &self,
833 infcx: &mut InferCtxt,
834 local_decls: &'a LocalDecls,
835 ) -> TypeEnv<'a> {
836 let data = self.data.replace_bound_refts_with(|sort, _, kind| {
837 Expr::fvar(infcx.define_bound_reft_var(sort, kind))
838 });
839 for constr in &data.constrs {
840 infcx.assume_pred(constr);
841 }
842 TypeEnv { bindings: data.bindings, local_decls }
843 }
844
845 pub(crate) fn scope(&self) -> &Scope {
846 &self.scope
847 }
848}
849
850mod pretty {
851 use std::fmt;
852
853 use flux_middle::pretty::*;
854
855 use super::*;
856
857 impl Pretty for TypeEnv<'_> {
858 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
859 w!(cx, f, "{:?}", &self.bindings)
860 }
861
862 fn default_cx(tcx: TyCtxt) -> PrettyCx {
863 PlacesTree::default_cx(tcx)
864 }
865 }
866
867 impl Pretty for BasicBlockEnvShape {
868 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
869 w!(cx, f, "{:?} {:?}", &self.scope, &self.bindings)
870 }
871
872 fn default_cx(tcx: TyCtxt) -> PrettyCx {
873 PlacesTree::default_cx(tcx)
874 }
875 }
876
877 impl Pretty for BasicBlockEnv {
878 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
879 w!(cx, f, "{:?} ", &self.scope)?;
880
881 let vars = self.data.vars();
882 cx.with_bound_vars(vars, || {
883 if !vars.is_empty() {
884 cx.fmt_bound_vars(true, "for<", vars, "> ", f)?;
885 }
886 let data = self.data.as_ref().skip_binder();
887 if !data.constrs.is_empty() {
888 w!(
889 cx,
890 f,
891 "{:?} ⇒ ",
892 join!(", ", data.constrs.iter().filter(|pred| !pred.is_trivially_true()))
893 )?;
894 }
895 w!(cx, f, "{:?}", &data.bindings)
896 })
897 }
898
899 fn default_cx(tcx: TyCtxt) -> PrettyCx {
900 PlacesTree::default_cx(tcx)
901 }
902 }
903
904 impl_debug_with_default_cx! {
905 TypeEnv<'_> => "type_env",
906 BasicBlockEnvShape => "basic_block_env_shape",
907 BasicBlockEnv => "basic_block_env"
908 }
909}
910
911#[derive(Serialize, DebugAsJson)]
913pub struct TypeEnvTrace(Vec<TypeEnvBind>);
914
915#[derive(Serialize)]
916struct TypeEnvBind {
917 local: LocInfo,
918 name: Option<String>,
919 kind: String,
920 ty: String,
921 span: Option<SpanTrace>,
922}
923
924#[derive(Serialize)]
925enum LocInfo {
926 Local(String),
927 Var(String),
928}
929
930fn loc_info(loc: &Loc) -> LocInfo {
931 match loc {
932 Loc::Local(local) => LocInfo::Local(format!("{local:?}")),
933 Loc::Var(var) => LocInfo::Var(format!("{var:?}")),
934 }
935}
936
937fn loc_name(local_names: &UnordMap<Local, Symbol>, loc: &Loc) -> Option<String> {
938 if let Loc::Local(local) = loc {
939 let name = local_names.get(local)?;
940 return Some(format!("{name}"));
941 }
942 None
943}
944
945fn loc_span(
946 genv: GlobalEnv,
947 local_decls: &IndexVec<Local, LocalDecl>,
948 loc: &Loc,
949) -> Option<SpanTrace> {
950 if let Loc::Local(local) = loc {
951 return local_decls
952 .get(*local)
953 .map(|local_decl| SpanTrace::new(genv.tcx(), local_decl.source_info.span));
954 }
955 None
956}
957
958impl TypeEnvTrace {
959 pub fn new(
960 genv: GlobalEnv,
961 local_names: &UnordMap<Local, Symbol>,
962 local_decls: &IndexVec<Local, LocalDecl>,
963 cx: PrettyCx,
964 env: &TypeEnv,
965 ) -> Self {
966 let mut bindings = vec![];
967 env.bindings
968 .iter()
969 .filter(|(_, binding)| !binding.ty.is_uninit())
970 .sorted_by(|(loc1, _), (loc2, _)| loc1.cmp(loc2))
971 .for_each(|(loc, binding)| {
972 let name = loc_name(local_names, loc);
973 let local = loc_info(loc);
974 let kind = format!("{:?}", binding.kind);
975 let ty = binding.ty.nested_string(&cx);
976 let span = loc_span(genv, local_decls, loc);
977 bindings.push(TypeEnvBind { name, local, kind, ty, span });
978 });
979
980 TypeEnvTrace(bindings)
981 }
982}