1use std::{fmt, iter};
7
8use flux_common::bug;
9use flux_errors::Errors;
10use flux_middle::{
11 def_id::MaybeExternId,
12 fhir,
13 global_env::GlobalEnv,
14 queries::QueryResult,
15 rty::{
16 self,
17 fold::{TypeFoldable, TypeFolder, TypeSuperFoldable},
18 refining::{Refine as _, Refiner},
19 },
20};
21use flux_rustc_bridge::ty::{self, FieldIdx, VariantIdx};
22use rustc_ast::Mutability;
23use rustc_data_structures::unord::UnordMap;
24use rustc_type_ir::{DebruijnIndex, INNERMOST, InferConst};
25
26pub(crate) fn type_alias(
27 genv: GlobalEnv,
28 alias: &fhir::TyAlias,
29 alias_ty: &rty::TyCtor,
30 def_id: MaybeExternId,
31) -> QueryResult<rty::TyCtor> {
32 let rust_ty = genv.lower_type_of(def_id.resolved_id())?.skip_binder();
33 let expected = rust_ty.refine(&Refiner::default_for_item(genv, def_id.resolved_id())?)?;
34 let mut zipper = Zipper::new(genv, def_id);
35
36 if zipper
37 .enter_a_binder(alias_ty, |zipper, ty| zipper.zip_ty(ty, &expected))
38 .is_err()
39 {
40 zipper
41 .errors
42 .emit(errors::IncompatibleRefinement::type_alias(genv, def_id, alias));
43 }
44
45 zipper.errors.to_result()?;
46
47 Ok(zipper.holes.replace_holes(alias_ty))
48}
49
50pub(crate) fn fn_sig(
51 genv: GlobalEnv,
52 decl: &fhir::FnDecl,
53 fn_sig: &rty::PolyFnSig,
54 def_id: MaybeExternId,
55) -> QueryResult<rty::PolyFnSig> {
56 let rust_fn_sig = genv.lower_fn_sig(def_id.resolved_id())?.skip_binder();
57
58 let expected = Refiner::default_for_item(genv, def_id.resolved_id())?.refine(&rust_fn_sig)?;
59
60 let mut zipper = Zipper::new(genv, def_id);
61 if let Err(err) = zipper.zip_poly_fn_sig(fn_sig, &expected) {
62 zipper.emit_fn_sig_err(err, decl);
63 }
64
65 zipper.errors.to_result()?;
66
67 Ok(zipper.holes.replace_holes(fn_sig))
68}
69
70pub(crate) fn variants(
71 genv: GlobalEnv,
72 variants: &[rty::PolyVariant],
73 adt_def_id: MaybeExternId,
74) -> QueryResult<Vec<rty::PolyVariant>> {
75 let refiner = Refiner::default_for_item(genv, adt_def_id.resolved_id())?;
76 let mut zipper = Zipper::new(genv, adt_def_id);
77 for (i, variant) in variants.iter().enumerate() {
79 let variant_idx = VariantIdx::from_usize(i);
80 let expected = refiner.refine_variant_def(adt_def_id.resolved_id(), variant_idx)?;
81 zipper.zip_variant(variant, &expected, variant_idx);
82 }
83
84 zipper.errors.to_result()?;
85
86 Ok(variants
87 .iter()
88 .map(|v| zipper.holes.replace_holes(v))
89 .collect())
90}
91
92struct Zipper<'genv, 'tcx> {
93 genv: GlobalEnv<'genv, 'tcx>,
94 owner_id: MaybeExternId,
95 locs: UnordMap<rty::Loc, rty::Ty>,
96 holes: Holes,
97 a_binders: u32,
99 b_binder_to_a_binder: Vec<Option<u32>>,
103 errors: Errors<'genv>,
104}
105
106#[derive(Default)]
107struct Holes {
108 sorts: UnordMap<rty::SortVid, rty::Sort>,
109 subset_tys: UnordMap<rty::TyVid, rty::SubsetTy>,
110 types: UnordMap<rty::TyVid, rty::Ty>,
111 regions: UnordMap<rty::RegionVid, rty::Region>,
112 consts: UnordMap<rty::ConstVid, rty::Const>,
113}
114
115impl TypeFolder for &Holes {
116 fn fold_sort(&mut self, sort: &rty::Sort) -> rty::Sort {
117 if let rty::Sort::Infer(vid) = sort {
118 self.sorts
119 .get(vid)
120 .cloned()
121 .unwrap_or_else(|| bug!("unfilled sort hole {vid:?}"))
122 } else {
123 sort.super_fold_with(self).erase_regions_including_bound()
126 }
127 }
128
129 fn fold_ty(&mut self, ty: &rty::Ty) -> rty::Ty {
130 if let rty::TyKind::Infer(vid) = ty.kind() {
131 self.types
132 .get(vid)
133 .cloned()
134 .unwrap_or_else(|| bug!("unfilled type hole {vid:?}"))
135 } else {
136 ty.super_fold_with(self)
137 }
138 }
139
140 fn fold_subset_ty(&mut self, constr: &rty::SubsetTy) -> rty::SubsetTy {
141 if let rty::BaseTy::Infer(vid) = &constr.bty {
142 self.subset_tys
143 .get(vid)
144 .cloned()
145 .unwrap_or_else(|| bug!("unfilled type hole {vid:?}"))
146 } else {
147 constr.super_fold_with(self)
148 }
149 }
150
151 fn fold_region(&mut self, r: &rty::Region) -> rty::Region {
152 if let rty::Region::ReVar(vid) = r {
153 self.regions
154 .get(vid)
155 .copied()
156 .unwrap_or_else(|| bug!("unfilled region hole {vid:?}"))
157 } else {
158 *r
159 }
160 }
161
162 fn fold_const(&mut self, ct: &rty::Const) -> rty::Const {
163 if let rty::ConstKind::Infer(InferConst::Var(cid)) = ct.kind {
164 self.consts
165 .get(&cid)
166 .cloned()
167 .unwrap_or_else(|| bug!("unfilled const hole {cid:?}"))
168 } else {
169 ct.super_fold_with(self)
170 }
171 }
172}
173
174impl Holes {
175 fn replace_holes<T: TypeFoldable>(&self, t: &T) -> T {
176 let mut this = self;
177 t.fold_with(&mut this)
178 }
179}
180
181impl<'genv, 'tcx> Zipper<'genv, 'tcx> {
182 fn new(genv: GlobalEnv<'genv, 'tcx>, owner_id: MaybeExternId) -> Self {
183 Self {
184 genv,
185 owner_id,
186 locs: UnordMap::default(),
187 holes: Default::default(),
188 a_binders: 0,
189 b_binder_to_a_binder: vec![],
190 errors: Errors::new(genv.sess()),
191 }
192 }
193
194 fn is_async_fn(&self) -> bool {
195 self.genv
196 .tcx()
197 .asyncness(self.owner_id.resolved_id())
198 .is_async()
199 }
200
201 fn zip_poly_fn_sig(&mut self, a: &rty::PolyFnSig, b: &rty::PolyFnSig) -> Result<(), FnSigErr> {
202 self.enter_binders(a, b, |this, a, b| this.zip_fn_sig(a, b))
203 }
204
205 fn zip_variant(&mut self, a: &rty::PolyVariant, b: &rty::PolyVariant, variant_idx: VariantIdx) {
206 self.enter_binders(a, b, |this, a, b| {
207 debug_assert_eq!(a.args, b.args);
209
210 if a.fields.len() != b.fields.len() {
211 this.errors.emit(errors::FieldCountMismatch::new(
212 this.genv,
213 a.fields.len(),
214 this.owner_id,
215 variant_idx,
216 ));
217 return;
218 }
219 for (i, (ty_a, ty_b)) in iter::zip(&a.fields, &b.fields).enumerate() {
220 let field_idx = FieldIdx::from_usize(i);
221 if this.zip_ty(ty_a, ty_b).is_err() {
222 this.errors.emit(errors::IncompatibleRefinement::field(
223 this.genv,
224 this.owner_id,
225 variant_idx,
226 field_idx,
227 ));
228 }
229 }
230 });
231 }
232
233 fn zip_fn_sig(&mut self, a: &rty::FnSig, b: &rty::FnSig) -> Result<(), FnSigErr> {
234 if a.inputs().len() != b.inputs().len() {
235 Err(FnSigErr::ArgCountMismatch)?;
236 }
237 for (i, (ty_a, ty_b)) in iter::zip(a.inputs(), b.inputs()).enumerate() {
238 self.zip_ty(ty_a, ty_b).map_err(|_| FnSigErr::FnInput(i))?;
239 }
240 self.enter_binders(&a.output, &b.output, |this, output_a, output_b| {
241 this.zip_output(output_a, output_b)
242 })
243 }
244
245 fn zip_output(&mut self, a: &rty::FnOutput, b: &rty::FnOutput) -> Result<(), FnSigErr> {
246 self.zip_ty(&a.ret, &b.ret).map_err(FnSigErr::FnOutput)?;
247
248 for (i, ensures) in a.ensures.iter().enumerate() {
249 if let rty::Ensures::Type(path, ty_a) = ensures {
250 let loc = path.to_loc().unwrap();
251 let ty_b = self.locs.get(&loc).unwrap().shift_in_escaping(1);
252 self.zip_ty(ty_a, &ty_b)
253 .map_err(|_| FnSigErr::Ensures { i, expected: ty_b })?;
254 }
255 }
256 Ok(())
257 }
258
259 fn zip_ty(&mut self, a: &rty::Ty, b: &rty::Ty) -> Result<(), Mismatch> {
260 match (a.kind(), b.kind()) {
261 (rty::TyKind::Infer(vid), _) => {
262 assert_ne!(vid.as_u32(), 0);
263 let b = self.adjust_bvars(b);
264 self.holes.types.insert(*vid, b);
265 Ok(())
266 }
267 (rty::TyKind::Exists(ctor_a), _) => {
268 self.enter_a_binder(ctor_a, |this, ty_a| this.zip_ty(ty_a, b))
269 }
270 (_, rty::TyKind::Exists(ctor_b)) => {
271 self.enter_b_binder(ctor_b, |this, ty_b| this.zip_ty(a, ty_b))
272 }
273 (rty::TyKind::Constr(_, ty_a), _) => self.zip_ty(ty_a, b),
274 (_, rty::TyKind::Constr(_, ty_b)) => self.zip_ty(a, ty_b),
275 (rty::TyKind::Indexed(bty_a, _), rty::TyKind::Indexed(bty_b, _)) => {
276 self.zip_bty(bty_a, bty_b)
277 }
278 (rty::TyKind::StrgRef(re_a, path, ty_a), rty::Ref!(re_b, ty_b, Mutability::Mut)) => {
279 let loc = path.to_loc().unwrap();
280 self.locs.insert(loc, ty_b.clone());
281
282 self.zip_region(re_a, re_b);
283 self.zip_ty(ty_a, ty_b)
284 }
285 (rty::TyKind::Param(pty_a), rty::TyKind::Param(pty_b)) => {
286 assert_eq_or_incompatible(pty_a, pty_b)
287 }
288 (
289 rty::TyKind::Ptr(_, _)
290 | rty::TyKind::Discr(..)
291 | rty::TyKind::Downcast(_, _, _, _, _)
292 | rty::TyKind::Blocked(_)
293 | rty::TyKind::Uninit,
294 _,
295 ) => {
296 bug!("unexpected type {a:?}");
297 }
298 _ => Err(Mismatch::new(a, b)),
299 }
300 }
301
302 fn zip_bty(&mut self, a: &rty::BaseTy, b: &rty::BaseTy) -> Result<(), Mismatch> {
303 match (a, b) {
304 (rty::BaseTy::Int(ity_a), rty::BaseTy::Int(ity_b)) => {
305 assert_eq_or_incompatible(ity_a, ity_b)
306 }
307 (rty::BaseTy::Uint(uity_a), rty::BaseTy::Uint(uity_b)) => {
308 assert_eq_or_incompatible(uity_a, uity_b)
309 }
310 (rty::BaseTy::Bool, rty::BaseTy::Bool) => Ok(()),
311 (rty::BaseTy::Str, rty::BaseTy::Str) => Ok(()),
312 (rty::BaseTy::Char, rty::BaseTy::Char) => Ok(()),
313 (rty::BaseTy::Float(fty_a), rty::BaseTy::Float(fty_b)) => {
314 assert_eq_or_incompatible(fty_a, fty_b)
315 }
316 (rty::BaseTy::Slice(ty_a), rty::BaseTy::Slice(ty_b)) => self.zip_ty(ty_a, ty_b),
317 (rty::BaseTy::Adt(adt_def_a, args_a), rty::BaseTy::Adt(adt_def_b, args_b)) => {
318 assert_eq_or_incompatible(adt_def_a.did(), adt_def_b.did())?;
319 assert_eq_or_incompatible(args_a.len(), args_b.len())?;
320 for (arg_a, arg_b) in iter::zip(args_a, args_b) {
321 self.zip_generic_arg(arg_a, arg_b)?;
322 }
323 Ok(())
324 }
325 (rty::BaseTy::RawPtr(ty_a, mutbl_a), rty::BaseTy::RawPtr(ty_b, mutbl_b)) => {
326 assert_eq_or_incompatible(mutbl_a, mutbl_b)?;
327 self.zip_ty(ty_a, ty_b)
328 }
329 (rty::BaseTy::Ref(re_a, ty_a, mutbl_a), rty::BaseTy::Ref(re_b, ty_b, mutbl_b)) => {
330 assert_eq_or_incompatible(mutbl_a, mutbl_b)?;
331 self.zip_region(re_a, re_b);
332 self.zip_ty(ty_a, ty_b)
333 }
334 (rty::BaseTy::FnPtr(poly_sig_a), rty::BaseTy::FnPtr(poly_sig_b)) => {
335 let (sig_a, sig_b) = (poly_sig_a.skip_binder_ref(), poly_sig_b.skip_binder_ref());
338 assert_eq_or_incompatible(sig_a.safety, sig_b.safety)?;
339 assert_eq_or_incompatible(sig_a.abi, sig_b.abi)?;
340 self.zip_poly_fn_sig(poly_sig_a, poly_sig_b)
341 .map_err(|_| Mismatch::new(poly_sig_a, poly_sig_b))
342 }
343 (rty::BaseTy::Tuple(tys_a), rty::BaseTy::Tuple(tys_b)) => {
344 assert_eq_or_incompatible(tys_a.len(), tys_b.len())?;
345 for (ty_a, ty_b) in iter::zip(tys_a, tys_b) {
346 self.zip_ty(ty_a, ty_b)?;
347 }
348 Ok(())
349 }
350 (rty::BaseTy::Alias(aty_a), rty::BaseTy::Alias(aty_b)) => {
351 assert_eq_or_incompatible(aty_a.kind, aty_b.kind)?;
352 assert_eq_or_incompatible(aty_a.args.len(), aty_b.args.len())?;
353 for (arg_a, arg_b) in iter::zip(&aty_a.args, &aty_b.args) {
354 self.zip_generic_arg(arg_a, arg_b)?;
355 }
356 Ok(())
357 }
358 (rty::BaseTy::Array(ty_a, len_a), rty::BaseTy::Array(ty_b, len_b)) => {
359 self.zip_const(len_a, len_b)?;
360 self.zip_ty(ty_a, ty_b)
361 }
362 (rty::BaseTy::Never, rty::BaseTy::Never) => Ok(()),
363 (rty::BaseTy::Param(pty_a), rty::BaseTy::Param(pty_b)) => {
364 assert_eq_or_incompatible(pty_a, pty_b)
365 }
366 (rty::BaseTy::Dynamic(preds_a, re_a), rty::BaseTy::Dynamic(preds_b, re_b)) => {
367 assert_eq_or_incompatible(preds_a.len(), preds_b.len())?;
368 for (pred_a, pred_b) in iter::zip(preds_a, preds_b) {
369 self.zip_poly_existential_pred(pred_a, pred_b)?;
370 }
371 self.zip_region(re_a, re_b);
372 Ok(())
373 }
374 (rty::BaseTy::Foreign(def_id_a), rty::BaseTy::Foreign(def_id_b)) => {
375 assert_eq_or_incompatible(def_id_a, def_id_b)
376 }
377 (rty::BaseTy::Closure(..) | rty::BaseTy::Coroutine(..), _) => {
378 bug!("unexpected type `{a:?}`");
379 }
380 _ => Err(Mismatch::new(a, b)),
381 }
382 }
383
384 fn zip_generic_arg(
385 &mut self,
386 a: &rty::GenericArg,
387 b: &rty::GenericArg,
388 ) -> Result<(), Mismatch> {
389 match (a, b) {
390 (rty::GenericArg::Ty(ty_a), rty::GenericArg::Ty(ty_b)) => self.zip_ty(ty_a, ty_b),
391 (rty::GenericArg::Base(ctor_a), rty::GenericArg::Base(ctor_b)) => {
392 self.zip_sorts(&ctor_a.sort(), &ctor_b.sort());
393 self.enter_binders(ctor_a, ctor_b, |this, sty_a, sty_b| {
394 this.zip_subset_ty(sty_a, sty_b)
395 })
396 }
397 (rty::GenericArg::Lifetime(re_a), rty::GenericArg::Lifetime(re_b)) => {
398 self.zip_region(re_a, re_b);
399 Ok(())
400 }
401 (rty::GenericArg::Const(ct_a), rty::GenericArg::Const(ct_b)) => {
402 self.zip_const(ct_a, ct_b)
403 }
404 _ => Err(Mismatch::new(a, b)),
405 }
406 }
407
408 fn zip_sorts(&mut self, a: &rty::Sort, b: &rty::Sort) {
409 if let rty::Sort::Infer(vid) = a {
410 assert_ne!(vid.as_u32(), 0);
411 self.holes.sorts.insert(*vid, b.clone());
412 }
413 }
414
415 fn zip_subset_ty(&mut self, a: &rty::SubsetTy, b: &rty::SubsetTy) -> Result<(), Mismatch> {
416 if let rty::BaseTy::Infer(vid) = a.bty {
417 assert_ne!(vid.as_u32(), 0);
418 let b = self.adjust_bvars(b);
419 self.holes.subset_tys.insert(vid, b);
420 Ok(())
421 } else {
422 self.zip_bty(&a.bty, &b.bty)
423 }
424 }
425
426 fn zip_const(&mut self, a: &rty::Const, b: &ty::Const) -> Result<(), Mismatch> {
427 match (&a.kind, &b.kind) {
428 (rty::ConstKind::Infer(ty::InferConst::Var(cid)), _) => {
429 self.holes.consts.insert(*cid, b.clone());
430 Ok(())
431 }
432 (rty::ConstKind::Param(param_const_a), ty::ConstKind::Param(param_const_b)) => {
433 assert_eq_or_incompatible(param_const_a, param_const_b)
434 }
435 (rty::ConstKind::Value(ty_a, val_a), ty::ConstKind::Value(ty_b, val_b)) => {
436 assert_eq_or_incompatible(ty_a, ty_b)?;
437 assert_eq_or_incompatible(val_a, val_b)
438 }
439 (rty::ConstKind::Alias(c1), ty::ConstKind::Alias(c2)) => {
440 assert_eq_or_incompatible(c1, c2)
441 }
442 _ => Err(Mismatch::new(a, b)),
443 }
444 }
445
446 fn zip_region(&mut self, a: &rty::Region, b: &ty::Region) {
447 if let rty::Region::ReVar(vid) = a {
448 let re = self.adjust_bvars(b);
449 self.holes.regions.insert(*vid, re);
450 }
451 }
452
453 fn zip_poly_existential_pred(
454 &mut self,
455 a: &rty::Binder<rty::ExistentialPredicate>,
456 b: &rty::Binder<rty::ExistentialPredicate>,
457 ) -> Result<(), Mismatch> {
458 self.enter_binders(a, b, |this, a, b| {
459 match (a, b) {
460 (
461 rty::ExistentialPredicate::Trait(trait_ref_a),
462 rty::ExistentialPredicate::Trait(trait_ref_b),
463 ) => {
464 assert_eq_or_incompatible(trait_ref_a.def_id, trait_ref_b.def_id)?;
465 assert_eq_or_incompatible(trait_ref_a.args.len(), trait_ref_b.args.len())?;
466 for (arg_a, arg_b) in iter::zip(&trait_ref_a.args, &trait_ref_b.args) {
467 this.zip_generic_arg(arg_a, arg_b)?;
468 }
469 Ok(())
470 }
471 (
472 rty::ExistentialPredicate::Projection(projection_a),
473 rty::ExistentialPredicate::Projection(projection_b),
474 ) => {
475 assert_eq_or_incompatible(projection_a.def_id, projection_b.def_id)?;
476 assert_eq_or_incompatible(projection_a.args.len(), projection_b.args.len())?;
477 for (arg_a, arg_b) in iter::zip(&projection_a.args, &projection_b.args) {
478 this.zip_generic_arg(arg_a, arg_b)?;
479 }
480 this.enter_binders(&projection_a.term, &projection_b.term, |this, a, b| {
481 this.zip_bty(&a.bty, &b.bty)
482 })
483 }
484 (
485 rty::ExistentialPredicate::AutoTrait(def_id_a),
486 rty::ExistentialPredicate::AutoTrait(def_id_b),
487 ) => assert_eq_or_incompatible(def_id_a, def_id_b),
488 _ => Err(Mismatch::new(a, b)),
489 }
490 })
491 }
492
493 fn enter_binders<T, R>(
495 &mut self,
496 a: &rty::Binder<T>,
497 b: &rty::Binder<T>,
498 f: impl FnOnce(&mut Self, &T, &T) -> R,
499 ) -> R {
500 self.b_binder_to_a_binder.push(Some(self.a_binders));
501 self.a_binders += 1;
502 let r = f(self, a.skip_binder_ref(), b.skip_binder_ref());
503 self.a_binders -= 1;
504 self.b_binder_to_a_binder.pop();
505 r
506 }
507
508 fn enter_a_binder<T, R>(
510 &mut self,
511 t: &rty::Binder<T>,
512 f: impl FnOnce(&mut Self, &T) -> R,
513 ) -> R {
514 self.a_binders += 1;
515 let r = f(self, t.skip_binder_ref());
516 self.a_binders -= 1;
517 r
518 }
519
520 fn enter_b_binder<T, R>(
522 &mut self,
523 t: &rty::Binder<T>,
524 f: impl FnOnce(&mut Self, &T) -> R,
525 ) -> R {
526 self.b_binder_to_a_binder.push(None);
527 let r = f(self, t.skip_binder_ref());
528 self.b_binder_to_a_binder.pop();
529 r
530 }
531
532 fn adjust_bvars<T: TypeFoldable + Clone + std::fmt::Debug>(&self, t: &T) -> T {
533 struct Adjuster<'a, 'genv, 'tcx> {
534 current_index: DebruijnIndex,
535 zipper: &'a Zipper<'genv, 'tcx>,
536 }
537
538 impl Adjuster<'_, '_, '_> {
539 fn adjust(&self, debruijn: DebruijnIndex) -> DebruijnIndex {
540 let b_binders = self.zipper.b_binder_to_a_binder.len();
541 let mapped_binder = self.zipper.b_binder_to_a_binder
542 [b_binders - debruijn.as_usize() - 1]
543 .unwrap_or_else(|| {
544 bug!("bound var without corresponding binder: `{debruijn:?}`")
545 });
546 DebruijnIndex::from_u32(self.zipper.a_binders - mapped_binder - 1)
547 .shifted_in(self.current_index.as_u32())
548 }
549 }
550
551 impl TypeFolder for Adjuster<'_, '_, '_> {
552 fn enter_binder(&mut self, _: &rty::BoundVariableKinds) {
553 self.current_index.shift_in(1);
554 }
555
556 fn exit_binder(&mut self) {
557 self.current_index.shift_out(1);
558 }
559
560 fn fold_region(&mut self, re: &rty::Region) -> rty::Region {
561 if let rty::ReBound(debruijn, br) = *re
562 && debruijn >= self.current_index
563 {
564 rty::ReBound(self.adjust(debruijn), br)
565 } else {
566 *re
567 }
568 }
569
570 fn fold_expr(&mut self, expr: &rty::Expr) -> rty::Expr {
571 if let rty::ExprKind::Var(rty::Var::Bound(debruijn, breft)) = expr.kind()
572 && *debruijn >= self.current_index
573 {
574 rty::Expr::bvar(self.adjust(*debruijn), breft.var, breft.kind)
575 } else {
576 expr.super_fold_with(self)
577 }
578 }
579 }
580 t.fold_with(&mut Adjuster { current_index: INNERMOST, zipper: self })
581 }
582
583 fn emit_fn_sig_err(&mut self, err: FnSigErr, decl: &fhir::FnDecl) {
584 match err {
585 FnSigErr::ArgCountMismatch => {
586 self.errors.emit(errors::IncompatibleParamCount::new(
587 self.genv,
588 decl,
589 self.owner_id,
590 ));
591 }
592 FnSigErr::FnInput(i) => {
593 self.errors.emit(errors::IncompatibleRefinement::fn_input(
594 self.genv,
595 self.owner_id,
596 decl,
597 i,
598 ));
599 }
600 FnSigErr::FnOutput(_) => {
601 self.errors.emit(errors::IncompatibleRefinement::fn_output(
602 self.genv,
603 self.owner_id,
604 decl,
605 self.is_async_fn(),
606 ));
607 }
608 FnSigErr::Ensures { i, expected } => {
609 self.errors.emit(errors::IncompatibleRefinement::ensures(
610 self.genv,
611 self.owner_id,
612 decl,
613 &expected,
614 i,
615 ));
616 }
617 }
618 }
619}
620
621fn assert_eq_or_incompatible<T: Eq + fmt::Debug>(a: T, b: T) -> Result<(), Mismatch> {
622 if a != b {
623 return Err(Mismatch::new(a, b));
624 }
625 Ok(())
626}
627
628#[expect(dead_code, reason = "we use the the String for debugging")]
629struct Mismatch(String);
630
631impl Mismatch {
632 fn new<T: fmt::Debug>(a: T, b: T) -> Self {
633 Self(format!("{a:?} != {b:?}"))
634 }
635}
636
637enum FnSigErr {
638 ArgCountMismatch,
639 FnInput(usize),
640 #[expect(dead_code, reason = "we use the struct for debugging")]
641 FnOutput(Mismatch),
642 Ensures {
643 i: usize,
644 expected: rty::Ty,
645 },
646}
647
648mod errors {
649 use flux_common::span_bug;
650 use flux_errors::E0999;
651 use flux_macros::Diagnostic;
652 use flux_middle::{def_id::MaybeExternId, fhir, global_env::GlobalEnv, rty};
653 use flux_rustc_bridge::{
654 ToRustc,
655 ty::{FieldIdx, VariantIdx},
656 };
657 use rustc_span::{DUMMY_SP, Span};
658
659 #[derive(Diagnostic)]
660 #[diag("{$def_descr} has an incompatible refinement annotation", code = E0999)]
661 #[note("a refinement annotation must match the unrefined definition structurally")]
662 pub(super) struct IncompatibleRefinement<'tcx> {
663 #[primary_span]
664 #[label("expected a refinement of `{$expected_ty}`")]
665 span: Span,
666 #[label("unrefined {$def_descr} found here")]
667 expected_span: Option<Span>,
668 expected_ty: rustc_middle::ty::Ty<'tcx>,
669 def_descr: &'static str,
670 #[help("mark the flux signature as `async fn`")]
671 async_hint: Option<()>,
672 }
673
674 impl<'tcx> IncompatibleRefinement<'tcx> {
675 pub(super) fn type_alias(
676 genv: GlobalEnv<'_, 'tcx>,
677 def_id: MaybeExternId,
678 type_alias: &fhir::TyAlias,
679 ) -> Self {
680 let tcx = genv.tcx();
681 Self {
682 span: type_alias.ty.span,
683 def_descr: tcx.def_descr(def_id.resolved_id()),
684 expected_span: Some(tcx.def_span(def_id)),
685 expected_ty: tcx.type_of(def_id).skip_binder(),
686 async_hint: None,
687 }
688 }
689
690 pub(super) fn fn_input(
691 genv: GlobalEnv<'_, 'tcx>,
692 fn_id: MaybeExternId,
693 decl: &fhir::FnDecl,
694 pos: usize,
695 ) -> Self {
696 let expected_span = match fn_id {
697 MaybeExternId::Local(local_id) => {
698 genv.tcx()
699 .hir_node_by_def_id(local_id)
700 .fn_decl()
701 .and_then(|fn_decl| fn_decl.inputs.get(pos))
702 .map(|input| input.span)
703 }
704 MaybeExternId::Extern(_, extern_id) => Some(genv.tcx().def_span(extern_id)),
705 };
706
707 let expected_ty = genv
708 .tcx()
709 .fn_sig(fn_id.resolved_id())
710 .skip_binder()
711 .inputs()
712 .map_bound(|inputs| inputs[pos])
713 .skip_binder();
714
715 Self {
716 span: decl.inputs[pos].span,
717 def_descr: genv.tcx().def_descr(fn_id.resolved_id()),
718 expected_span,
719 expected_ty,
720 async_hint: None,
721 }
722 }
723
724 pub(super) fn fn_output(
725 genv: GlobalEnv<'_, 'tcx>,
726 fn_id: MaybeExternId,
727 decl: &fhir::FnDecl,
728 is_async: bool,
729 ) -> Self {
730 let expected_span = match fn_id {
731 MaybeExternId::Local(local_id) => {
732 genv.tcx()
733 .hir_node_by_def_id(local_id)
734 .fn_decl()
735 .map(|fn_decl| fn_decl.output.span())
736 }
737 MaybeExternId::Extern(_, extern_id) => Some(genv.tcx().def_span(extern_id)),
738 };
739
740 let expected_ty = genv
741 .tcx()
742 .fn_sig(fn_id.resolved_id())
743 .skip_binder()
744 .output()
745 .skip_binder();
746 let spec_span = decl.output.ret.span;
747
748 let async_hint = if is_async { Some(()) } else { None };
749
750 Self {
751 span: spec_span,
752 def_descr: genv.tcx().def_descr(fn_id.resolved_id()),
753 expected_span,
754 expected_ty,
755 async_hint,
756 }
757 }
758
759 pub(super) fn ensures(
760 genv: GlobalEnv<'_, 'tcx>,
761 fn_id: MaybeExternId,
762 decl: &fhir::FnDecl,
763 expected: &rty::Ty,
764 i: usize,
765 ) -> Self {
766 let fhir::Ensures::Type(_, ty) = &decl.output.ensures[i] else {
767 span_bug!(decl.span, "expected `fhir::Ensures::Type`");
768 };
769 let tcx = genv.tcx();
770 Self {
771 span: ty.span,
772 def_descr: tcx.def_descr(fn_id.resolved_id()),
773 expected_span: None,
774 expected_ty: expected.to_rustc(tcx),
775 async_hint: None,
776 }
777 }
778
779 pub(super) fn field(
780 genv: GlobalEnv<'_, 'tcx>,
781 adt_id: MaybeExternId,
782 variant_idx: VariantIdx,
783 field_idx: FieldIdx,
784 ) -> Self {
785 let tcx = genv.tcx();
786 let adt_def = tcx.adt_def(adt_id);
787 let field_def = &adt_def.variant(variant_idx).fields[field_idx];
788
789 let item = genv.fhir_expect_item(adt_id.local_id()).unwrap();
790 let span = match &item.kind {
791 fhir::ItemKind::Enum(enum_def) => {
792 enum_def.variants[variant_idx.as_usize()].fields[field_idx.as_usize()]
793 .ty
794 .span
795 }
796 fhir::ItemKind::Struct(struct_def)
797 if let fhir::StructKind::Transparent { fields } = &struct_def.kind =>
798 {
799 fields[field_idx.as_usize()].ty.span
800 }
801 _ => DUMMY_SP,
802 };
803
804 Self {
805 span,
806 def_descr: tcx.def_descr(field_def.did),
807 expected_span: Some(tcx.def_span(field_def.did)),
808 expected_ty: tcx.type_of(field_def.did).skip_binder(),
809 async_hint: None,
810 }
811 }
812 }
813
814 #[derive(Diagnostic)]
815 #[diag("{$def_descr} has an incompatible refinement annotation", code = E0999)]
816 pub(super) struct IncompatibleParamCount {
817 #[primary_span]
818 #[label(
819 "refined signature has {$found} {$found ->
820 [one] parameter
821 *[other] parameters
822 }"
823 )]
824 span: Span,
825 found: usize,
826 #[label(
827 "unrefined signature has {$expected} {$expected ->
828 [one] parameter
829 *[other] parameters
830 }"
831 )]
832 expected_span: Span,
833 expected: usize,
834 def_descr: &'static str,
835 }
836
837 impl IncompatibleParamCount {
838 pub(super) fn new(genv: GlobalEnv, decl: &fhir::FnDecl, def_id: MaybeExternId) -> Self {
839 let def_descr = genv.tcx().def_descr(def_id.resolved_id());
840
841 let span = if !decl.inputs.is_empty() {
842 decl.inputs[decl.inputs.len() - 1]
843 .span
844 .with_lo(decl.inputs[0].span.lo())
845 } else {
846 decl.span
847 };
848
849 let expected_span = if let Some(local_id) = def_id.as_local()
850 && let expected_decl = genv.tcx().hir_node_by_def_id(local_id).fn_decl().unwrap()
851 && !expected_decl.inputs.is_empty()
852 {
853 expected_decl.inputs[expected_decl.inputs.len() - 1]
854 .span
855 .with_lo(expected_decl.inputs[0].span.lo())
856 } else {
857 genv.tcx().def_span(def_id)
858 };
859
860 let expected = genv
861 .tcx()
862 .fn_sig(def_id)
863 .skip_binder()
864 .skip_binder()
865 .inputs()
866 .len();
867
868 Self { span, found: decl.inputs.len(), expected_span, expected, def_descr }
869 }
870 }
871
872 #[derive(Diagnostic)]
873 #[diag("variant has an incompatible refinement annotation", code = E0999)]
874 pub(super) struct FieldCountMismatch {
875 #[primary_span]
876 #[label(
877 "expected {$expected_fields} {$expected_fields ->
878 [one] field
879 *[other] fields
880 }, found {$fields}"
881 )]
882 span: Span,
883 fields: usize,
884 #[label("unrefined variant defined here")]
885 expected_span: Span,
886 expected_fields: usize,
887 }
888
889 impl FieldCountMismatch {
890 pub(super) fn new(
891 genv: GlobalEnv,
892 found: usize,
893 adt_def_id: MaybeExternId,
894 variant_idx: VariantIdx,
895 ) -> Self {
896 let adt_def = genv.tcx().adt_def(adt_def_id);
897 let expected_variant = adt_def.variant(variant_idx);
898
899 let span = if let Ok(fhir::Node::Item(item)) = genv.fhir_node(adt_def_id.local_id())
902 && let fhir::ItemKind::Enum(enum_def) = &item.kind
903 && let Some(variant) = enum_def.variants.get(variant_idx.as_usize())
904 {
905 variant.span
906 } else {
907 DUMMY_SP
908 };
909
910 Self {
911 span,
912 fields: found,
913 expected_span: genv.tcx().def_span(expected_variant.def_id),
914 expected_fields: expected_variant.fields.len(),
915 }
916 }
917 }
918}