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