Skip to main content

flux_fhir_analysis/wf/
sortck.rs

1use std::iter;
2
3use derive_where::derive_where;
4use ena::unify::InPlaceUnificationTable;
5use flux_common::{bug, iter::IterExt, span_bug, tracked_span_bug};
6use flux_errors::{ErrorGuaranteed, Errors};
7use flux_infer::projections::NormalizeExt;
8use flux_middle::{
9    fhir::{self, FhirId, FluxOwnerId, QPathExpr, visit::Visitor as _},
10    global_env::GlobalEnv,
11    queries::QueryResult,
12    rty::{
13        self, AdtSortDef, FuncSort, List, RecordCtor, WfckResults,
14        fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable},
15    },
16};
17use itertools::{Itertools, izip};
18use rustc_data_structures::unord::UnordMap;
19use rustc_errors::Diagnostic;
20use rustc_hash::FxHashMap;
21use rustc_hir::def::DefKind;
22use rustc_middle::ty::TypingMode;
23use rustc_span::{Span, def_id::DefId, symbol::Ident};
24
25use super::errors;
26use crate::rustc_infer::infer::TyCtxtInferExt;
27
28type Result<T = ()> = std::result::Result<T, ErrorGuaranteed>;
29
30pub(super) struct InferCtxt<'genv, 'tcx> {
31    pub genv: GlobalEnv<'genv, 'tcx>,
32    pub owner: FluxOwnerId,
33    pub wfckresults: WfckResults,
34    sort_unification_table: InPlaceUnificationTable<rty::SortVid>,
35    bv_size_unification_table: InPlaceUnificationTable<rty::BvSizeVid>,
36    params: FxHashMap<fhir::ParamId, (fhir::RefineParam<'genv>, rty::Sort)>,
37    node_sort: FxHashMap<FhirId, rty::Sort>,
38    path_args: UnordMap<FhirId, rty::GenericArgs>,
39    sort_of_alias_reft: FxHashMap<FhirId, rty::FuncSort>,
40    sort_of_literal: NodeMap<'genv, rty::Sort>,
41    sort_of_bin_op: NodeMap<'genv, rty::Sort>,
42    sort_args_of_app: NodeMap<'genv, List<rty::SortArg>>,
43}
44
45pub fn prim_op_sort(op: &fhir::BinOp) -> Option<(Vec<rty::Sort>, rty::Sort)> {
46    match op {
47        fhir::BinOp::BitAnd
48        | fhir::BinOp::BitOr
49        | fhir::BinOp::BitXor
50        | fhir::BinOp::BitShl
51        | fhir::BinOp::BitShr => Some((vec![rty::Sort::Int, rty::Sort::Int], rty::Sort::Int)),
52        _ => None,
53    }
54}
55
56/// The sort of an *application* `[op](e1, e2)` of a primop, which besides the bitwise operations
57/// of [`prim_op_sort`] can also be an arithmetic operation on `int`, i.e., any of the operators
58/// that can be made uninterpreted with `uif_ops`.
59fn prim_app_sort(op: &fhir::BinOp) -> Option<(Vec<rty::Sort>, rty::Sort)> {
60    match op {
61        fhir::BinOp::Add
62        | fhir::BinOp::Sub
63        | fhir::BinOp::Mul
64        | fhir::BinOp::Div
65        | fhir::BinOp::Mod => Some((vec![rty::Sort::Int, rty::Sort::Int], rty::Sort::Int)),
66        _ => prim_op_sort(op),
67    }
68}
69
70impl<'genv, 'tcx> InferCtxt<'genv, 'tcx> {
71    pub(super) fn new(genv: GlobalEnv<'genv, 'tcx>, owner: FluxOwnerId) -> Self {
72        // We skip 0 because that's used for sort dummy self types during conv.
73        let mut sort_unification_table = InPlaceUnificationTable::new();
74        sort_unification_table.new_key(Default::default());
75        Self {
76            genv,
77            owner,
78            wfckresults: WfckResults::new(owner),
79            sort_unification_table,
80            bv_size_unification_table: InPlaceUnificationTable::new(),
81            params: Default::default(),
82            node_sort: Default::default(),
83            path_args: Default::default(),
84            sort_of_alias_reft: Default::default(),
85            sort_of_literal: Default::default(),
86            sort_of_bin_op: Default::default(),
87            sort_args_of_app: Default::default(),
88        }
89    }
90
91    fn check_abs(
92        &mut self,
93        expr: &fhir::Expr<'genv>,
94        params: &[fhir::RefineParam],
95        body: &fhir::Expr<'genv>,
96        expected: &rty::Sort,
97    ) -> Result {
98        let Some(fsort) = self.is_coercible_from_func(expected, expr.fhir_id) else {
99            return Err(self.emit_err(errors::UnexpectedFun::new(expr.span, expected)));
100        };
101
102        let fsort = fsort.expect_mono();
103
104        if params.len() != fsort.inputs().len() {
105            return Err(self.emit_err(errors::ParamCountMismatch::new(
106                expr.span,
107                fsort.inputs().len(),
108                params.len(),
109            )));
110        }
111        iter::zip(params, fsort.inputs()).try_for_each_exhaust(|(param, expected)| {
112            let found = self.param_sort(param.id);
113            if self.try_equate(&found, expected).is_none() {
114                return Err(self.emit_sort_mismatch(param.span, expected, &found));
115            }
116            Ok(())
117        })?;
118        self.check_expr(body, fsort.output())?;
119        self.wfckresults
120            .node_sorts_mut()
121            .insert(body.fhir_id, fsort.output().clone());
122        Ok(())
123    }
124
125    fn check_field_exprs(
126        &mut self,
127        expr_span: Span,
128        sort_def: &AdtSortDef,
129        sort_args: &[rty::Sort],
130        field_exprs: &[fhir::FieldExpr<'genv>],
131        spread: Option<&fhir::Spread<'genv>>,
132        expected: &rty::Sort,
133    ) -> Result {
134        let sort_by_field_name = sort_def.struct_variant().sort_by_field_name(sort_args);
135        let mut used_fields = FxHashMap::default();
136        for expr in field_exprs {
137            // make sure that the field is actually a field
138            let Some(sort) = sort_by_field_name.get(&expr.ident.name) else {
139                return Err(self.emit_err(errors::FieldNotFound::new(expected.clone(), expr.ident)));
140            };
141            if let Some(old_field) = used_fields.insert(expr.ident.name, expr.ident) {
142                return Err(self.emit_err(errors::DuplicateFieldUsed::new(expr.ident, old_field)));
143            }
144            self.check_expr(&expr.expr, sort)?;
145        }
146        if let Some(spread) = spread {
147            // must check that the spread is of the same sort as the constructor
148            self.check_expr(&spread.expr, expected)
149        } else if sort_by_field_name.len() != used_fields.len() {
150            // emit an error because all fields are not used
151            let missing_fields = sort_by_field_name
152                .into_keys()
153                .filter(|x| !used_fields.contains_key(x))
154                .collect();
155            Err(self.emit_err(errors::ConstructorMissingFields::new(expr_span, missing_fields)))
156        } else {
157            Ok(())
158        }
159    }
160
161    fn check_constructor(
162        &mut self,
163        expr: &fhir::Expr,
164        field_exprs: &[fhir::FieldExpr<'genv>],
165        spread: Option<&fhir::Spread<'genv>>,
166        expected: &rty::Sort,
167    ) -> Result {
168        let expected = self.resolve_vars_if_possible(expected);
169        if let rty::Sort::App(rty::SortCtor::Adt(sort_def), sort_args) = &expected {
170            self.wfckresults
171                .record_ctors_mut()
172                .insert(expr.fhir_id, RecordCtor::Struct(sort_def.did()));
173            self.check_field_exprs(expr.span, sort_def, sort_args, field_exprs, spread, &expected)?;
174            Ok(())
175        } else {
176            Err(self.emit_err(errors::UnexpectedConstructor::new(expr.span, &expected)))
177        }
178    }
179
180    fn check_record(
181        &mut self,
182        arg: &fhir::Expr<'genv>,
183        flds: &[fhir::Expr<'genv>],
184        expected: &rty::Sort,
185    ) -> Result {
186        if let Some(sorts) = expected.field_sorts() {
187            if flds.len() != sorts.len() {
188                return Err(self.emit_err(errors::ArgCountMismatch::new(
189                    Some(arg.span),
190                    String::from("type"),
191                    sorts.len(),
192                    flds.len(),
193                )));
194            }
195            if let rty::Sort::App(rty::SortCtor::Adt(sort_def), _) = expected {
196                self.wfckresults
197                    .record_ctors_mut()
198                    .insert(arg.fhir_id, RecordCtor::Struct(sort_def.did()));
199            } else if matches!(expected, rty::Sort::RawPtr) {
200                self.wfckresults
201                    .record_ctors_mut()
202                    .insert(arg.fhir_id, RecordCtor::RawPtr);
203            }
204
205            izip!(flds, &sorts)
206                .map(|(arg, expected)| self.check_expr(arg, expected))
207                .try_collect_exhaust()
208        } else {
209            Err(self.emit_err(errors::ArgCountMismatch::new(
210                Some(arg.span),
211                String::from("type"),
212                1,
213                flds.len(),
214            )))
215        }
216    }
217
218    pub(super) fn check_expr(&mut self, expr: &fhir::Expr<'genv>, expected: &rty::Sort) -> Result {
219        match expr.kind {
220            fhir::ExprKind::IfThenElse(p, e1, e2) => {
221                self.check_expr(p, &rty::Sort::Bool)?;
222                self.check_expr(e1, expected)?;
223                self.check_expr(e2, expected)?;
224                return Ok(());
225            }
226            fhir::ExprKind::Abs(params, body) => {
227                self.check_abs(expr, params, body, expected)?;
228                return Ok(());
229            }
230            fhir::ExprKind::Record(fields) => {
231                self.check_record(expr, fields, expected)?;
232                return Ok(());
233            }
234            fhir::ExprKind::Constructor(None, exprs, spread) => {
235                self.check_constructor(expr, exprs, spread, expected)?;
236                return Ok(());
237            }
238            fhir::ExprKind::Tuple(exprs) => {
239                // Check tuple against expected tuple sort
240                if let rty::Sort::Tuple(expected_sorts) = expected
241                    && exprs.len() == expected_sorts.len()
242                {
243                    for (expr, expected_sort) in iter::zip(exprs, expected_sorts) {
244                        self.check_expr(expr, expected_sort)?;
245                    }
246                    return Ok(());
247                }
248            }
249            fhir::ExprKind::Err(_) => {
250                // an error has already been reported so we can just skip
251                return Ok(());
252            }
253            fhir::ExprKind::UnaryOp(..)
254            | fhir::ExprKind::SetLiteral(..)
255            | fhir::ExprKind::BinaryOp(..)
256            | fhir::ExprKind::Dot(..)
257            | fhir::ExprKind::App(..)
258            | fhir::ExprKind::Alias(..)
259            | fhir::ExprKind::Var(..)
260            | fhir::ExprKind::Literal(..)
261            | fhir::ExprKind::Quant(..)
262            | fhir::ExprKind::Block(..)
263            | fhir::ExprKind::Constructor(..)
264            | fhir::ExprKind::PrimApp(..) => {}
265        }
266        // Fallback to synthesis and then check
267        let found = self.synth_expr(expr)?;
268        let found = self.resolve_vars_if_possible(&found);
269        let expected = self.resolve_vars_if_possible(expected);
270        if !self.is_coercible(&found, &expected, expr.fhir_id) {
271            return Err(self.emit_sort_mismatch(expr.span, &expected, &found));
272        }
273        Ok(())
274    }
275
276    pub(super) fn check_loc(&mut self, loc: &fhir::PathExpr) -> Result {
277        let found = self.synth_path(loc);
278        if found == rty::Sort::Loc {
279            Ok(())
280        } else {
281            Err(self.emit_sort_mismatch(loc.span, &rty::Sort::Loc, &found))
282        }
283    }
284
285    fn synth_lit(&mut self, lit: fhir::Lit, expr: &fhir::Expr<'genv>) -> rty::Sort {
286        match lit {
287            fhir::Lit::Int(_) => {
288                let sort = self.next_sort_var_with_cstr(rty::SortCstr::NUMERIC);
289                self.sort_of_literal.insert(*expr, sort.clone());
290                sort
291            }
292            fhir::Lit::Real(_) => rty::Sort::Real,
293            fhir::Lit::Bool(_) => rty::Sort::Bool,
294            fhir::Lit::Str(_) => rty::Sort::Str,
295            fhir::Lit::Char(_) => rty::Sort::Char,
296        }
297    }
298
299    fn synth_prim_app(
300        &mut self,
301        op: &fhir::BinOp,
302        e1: &fhir::Expr<'genv>,
303        e2: &fhir::Expr<'genv>,
304        span: Span,
305    ) -> Result<rty::Sort> {
306        let Some((inputs, output)) = prim_app_sort(op) else {
307            return Err(self.emit_err(errors::UnsupportedPrimOp::new(span, *op)));
308        };
309        let [sort1, sort2] = &inputs[..] else {
310            return Err(self.emit_err(errors::ArgCountMismatch::new(
311                Some(span),
312                String::from("primop app"),
313                inputs.len(),
314                2,
315            )));
316        };
317        self.check_expr(e1, sort1)?;
318        self.check_expr(e2, sort2)?;
319        Ok(output)
320    }
321
322    fn synth_expr(&mut self, expr: &fhir::Expr<'genv>) -> Result<rty::Sort> {
323        match expr.kind {
324            fhir::ExprKind::Var(QPathExpr::Resolved(path, _)) => Ok(self.synth_path(&path)),
325            fhir::ExprKind::Var(QPathExpr::TypeRelative(..)) => {
326                Ok(self.synth_type_relative_path(expr))
327            }
328            fhir::ExprKind::Literal(lit) => Ok(self.synth_lit(lit, expr)),
329            fhir::ExprKind::BinaryOp(op, e1, e2) => self.synth_binary_op(expr, op, e1, e2),
330            fhir::ExprKind::PrimApp(op, e1, e2) => self.synth_prim_app(&op, e1, e2, expr.span),
331            fhir::ExprKind::UnaryOp(op, e) => self.synth_unary_op(op, e),
332            fhir::ExprKind::App(callee, args) => {
333                let sort = self.ensure_resolved_path(&callee)?;
334                let Some(poly_fsort) = self.is_coercible_to_func(&sort, callee.fhir_id) else {
335                    return Err(self.emit_err(errors::ExpectedFun::new(callee.span, &sort)));
336                };
337                let fsort = self.instantiate_func_sort(expr, poly_fsort);
338                self.synth_app(fsort, args, expr.span)
339            }
340            fhir::ExprKind::Quant(_, param, dom, body) => {
341                let sort = self.param_sort(param.id);
342                if let fhir::QuantDom::Bounded { .. } = dom
343                    && self.try_equate(&sort, &rty::Sort::Int).is_none()
344                {
345                    return Err(self.emit_err(errors::IllSortedQuantifier::new(param.span, sort)));
346                }
347                self.check_expr(body, &rty::Sort::Bool)?;
348                Ok(rty::Sort::Bool)
349            }
350            fhir::ExprKind::Alias(_alias_reft, args) => {
351                // To check the application we only need the sort of `_alias_reft` which we collected
352                // during early conv, but should we do any extra checks on `_alias_reft`?
353                let fsort = self.sort_of_alias_reft(expr.fhir_id);
354                self.synth_app(fsort, args, expr.span)
355            }
356            fhir::ExprKind::IfThenElse(p, e1, e2) => {
357                self.check_expr(p, &rty::Sort::Bool)?;
358                let sort = self.synth_expr(e1)?;
359                self.check_expr(e2, &sort)?;
360                Ok(sort)
361            }
362            fhir::ExprKind::Dot(base, fld) => {
363                let sort = self.synth_expr(base)?;
364                let sort = self
365                    .fully_resolve(&sort)
366                    .map_err(|_| self.emit_err(errors::CannotInferSort::new(base.span)))?;
367                match &sort {
368                    rty::Sort::App(rty::SortCtor::Adt(sort_def), sort_args) => {
369                        let (proj, sort) = sort_def
370                            .struct_variant()
371                            .field_by_name(sort_def.did(), sort_args, fld.name)
372                            .ok_or_else(|| self.emit_field_not_found(&sort, fld))?;
373                        self.wfckresults
374                            .field_projs_mut()
375                            .insert(expr.fhir_id, proj);
376                        Ok(sort)
377                    }
378                    rty::Sort::Tuple(sorts) => {
379                        // Parse field name as integer for tuple field access
380                        if let Ok(field_idx) = fld.name.as_str().parse::<usize>()
381                            && field_idx < sorts.len()
382                        {
383                            let proj = rty::FieldProj::Tuple {
384                                arity: sorts.len(),
385                                field: field_idx as u32,
386                            };
387                            self.wfckresults
388                                .field_projs_mut()
389                                .insert(expr.fhir_id, proj);
390                            Ok(sorts[field_idx].clone())
391                        } else {
392                            Err(self.emit_field_not_found(&sort, fld))
393                        }
394                    }
395                    rty::Sort::RawPtr => {
396                        let field = rty::RawPtrField::from_name(fld.name)
397                            .ok_or_else(|| self.emit_field_not_found(&sort, fld))?;
398                        self.wfckresults
399                            .field_projs_mut()
400                            .insert(expr.fhir_id, rty::FieldProj::RawPtr { field });
401                        Ok(rty::Sort::Int)
402                    }
403                    rty::Sort::Bool | rty::Sort::Int | rty::Sort::Real => {
404                        Err(self.emit_err(errors::InvalidPrimitiveDotAccess::new(&sort, fld)))
405                    }
406                    _ => Err(self.emit_field_not_found(&sort, fld)),
407                }
408            }
409            fhir::ExprKind::Constructor(Some(path), field_exprs, spread) => {
410                // if we have S then we can synthesize otherwise we fail
411                // first get the sort based on the path - for example S { ... } => S
412                // and we should expect sort to be a struct or enum app
413                let path_def_id = match path.res {
414                    fhir::Res::Def(DefKind::Enum | DefKind::Struct, def_id) => def_id,
415                    _ => span_bug!(expr.span, "unexpected path in constructor"),
416                };
417                let sort_def = self
418                    .genv
419                    .adt_sort_def_of(path_def_id)
420                    .map_err(|e| self.emit_err(e))?;
421                // generate fresh inferred sorts for each param
422                let fresh_args: rty::List<_> = (0..sort_def.param_count())
423                    .map(|_| self.next_sort_var())
424                    .collect();
425                let sort = rty::Sort::App(rty::SortCtor::Adt(sort_def.clone()), fresh_args.clone());
426                // check fields & spread against fresh args
427                self.check_field_exprs(
428                    expr.span,
429                    &sort_def,
430                    &fresh_args,
431                    field_exprs,
432                    spread,
433                    &sort,
434                )?;
435                Ok(sort)
436            }
437            fhir::ExprKind::Block(decls, body) => {
438                for decl in decls {
439                    self.check_expr(&decl.init, &self.param_sort(decl.param.id))?;
440                }
441                self.synth_expr(body)
442            }
443            fhir::ExprKind::SetLiteral(elems) => {
444                let elem_sort = self.next_sort_var();
445                for elem in elems {
446                    self.check_expr(elem, &elem_sort)?;
447                }
448                Ok(rty::Sort::App(rty::SortCtor::Set, List::singleton(elem_sort)))
449            }
450            fhir::ExprKind::Tuple(exprs) => {
451                let sorts = exprs
452                    .iter()
453                    .map(|expr| self.synth_expr(expr))
454                    .try_collect()?;
455                Ok(rty::Sort::Tuple(sorts))
456            }
457            _ => Err(self.emit_err(errors::CannotInferSort::new(expr.span))),
458        }
459    }
460
461    fn synth_path(&mut self, path: &fhir::PathExpr) -> rty::Sort {
462        self.node_sort
463            .get(&path.fhir_id)
464            .unwrap_or_else(|| tracked_span_bug!("no sort found for path: `{path:?}`"))
465            .clone()
466    }
467
468    fn synth_type_relative_path(&mut self, expr: &fhir::Expr) -> rty::Sort {
469        self.node_sort
470            .get(&expr.fhir_id)
471            .unwrap_or_else(|| tracked_span_bug!("no sort found for: `{expr:?}`"))
472            .clone()
473    }
474
475    fn synth_binary_op(
476        &mut self,
477        expr: &fhir::Expr<'genv>,
478        op: fhir::BinOp,
479        e1: &fhir::Expr<'genv>,
480        e2: &fhir::Expr<'genv>,
481    ) -> Result<rty::Sort> {
482        match op {
483            fhir::BinOp::Or | fhir::BinOp::And | fhir::BinOp::Iff | fhir::BinOp::Imp => {
484                self.check_expr(e1, &rty::Sort::Bool)?;
485                self.check_expr(e2, &rty::Sort::Bool)?;
486                Ok(rty::Sort::Bool)
487            }
488            fhir::BinOp::Eq | fhir::BinOp::Ne => {
489                let sort = self.next_sort_var();
490                self.check_expr(e1, &sort)?;
491                self.check_expr(e2, &sort)?;
492                Ok(rty::Sort::Bool)
493            }
494            fhir::BinOp::Lt | fhir::BinOp::Le | fhir::BinOp::Gt | fhir::BinOp::Ge => {
495                let sort = self.next_sort_var();
496                self.check_expr(e1, &sort)?;
497                self.check_expr(e2, &sort)?;
498                self.sort_of_bin_op.insert(*expr, sort.clone());
499                Ok(rty::Sort::Bool)
500            }
501            fhir::BinOp::Add
502            | fhir::BinOp::Sub
503            | fhir::BinOp::Mul
504            | fhir::BinOp::Div
505            | fhir::BinOp::Mod
506            | fhir::BinOp::BitAnd
507            | fhir::BinOp::BitOr
508            | fhir::BinOp::BitXor
509            | fhir::BinOp::BitShl
510            | fhir::BinOp::BitShr => {
511                let sort = self.next_sort_var_with_cstr(rty::SortCstr::from_bin_op(op));
512                self.check_expr(e1, &sort)?;
513                self.check_expr(e2, &sort)?;
514                self.sort_of_bin_op.insert(*expr, sort.clone());
515                Ok(sort)
516            }
517        }
518    }
519
520    fn synth_unary_op(&mut self, op: fhir::UnOp, e: &fhir::Expr<'genv>) -> Result<rty::Sort> {
521        match op {
522            fhir::UnOp::Not => {
523                self.check_expr(e, &rty::Sort::Bool)?;
524                Ok(rty::Sort::Bool)
525            }
526            fhir::UnOp::Neg => {
527                self.check_expr(e, &rty::Sort::Int)?;
528                Ok(rty::Sort::Int)
529            }
530        }
531    }
532
533    fn synth_app(
534        &mut self,
535        fsort: FuncSort,
536        args: &[fhir::Expr<'genv>],
537        span: Span,
538    ) -> Result<rty::Sort> {
539        if args.len() != fsort.inputs().len() {
540            return Err(self.emit_err(errors::ArgCountMismatch::new(
541                Some(span),
542                String::from("function"),
543                fsort.inputs().len(),
544                args.len(),
545            )));
546        }
547
548        iter::zip(args, fsort.inputs())
549            .try_for_each_exhaust(|(arg, formal)| self.check_expr(arg, formal))?;
550
551        Ok(fsort.output().clone())
552    }
553
554    fn instantiate_func_sort(
555        &mut self,
556        app_expr: &fhir::Expr<'genv>,
557        fsort: rty::PolyFuncSort,
558    ) -> rty::FuncSort {
559        let args = fsort
560            .params()
561            .map(|kind| {
562                match kind {
563                    rty::SortParamKind::Sort => rty::SortArg::Sort(self.next_sort_var()),
564                    rty::SortParamKind::BvSize => rty::SortArg::BvSize(self.next_bv_size_var()),
565                }
566            })
567            .collect_vec();
568        self.sort_args_of_app
569            .insert(*app_expr, List::from_slice(&args));
570        fsort.instantiate(&args)
571    }
572
573    pub(crate) fn insert_node_sort(&mut self, fhir_id: FhirId, sort: rty::Sort) {
574        self.node_sort.insert(fhir_id, sort);
575    }
576
577    pub(crate) fn sort_of_bty(&self, bty: &fhir::BaseTy) -> rty::Sort {
578        self.node_sort
579            .get(&bty.fhir_id)
580            .unwrap_or_else(|| tracked_span_bug!("no sort found for bty: `{bty:?}`"))
581            .clone()
582    }
583
584    pub(crate) fn insert_path_args(&mut self, fhir_id: FhirId, args: rty::GenericArgs) {
585        self.path_args.insert(fhir_id, args);
586    }
587
588    pub(crate) fn path_args(&self, fhir_id: FhirId) -> rty::GenericArgs {
589        self.path_args
590            .get(&fhir_id)
591            .unwrap_or_else(|| tracked_span_bug!("no entry found for `{fhir_id:?}`"))
592            .clone()
593    }
594
595    pub(crate) fn insert_sort_for_alias_reft(&mut self, fhir_id: FhirId, fsort: rty::FuncSort) {
596        self.sort_of_alias_reft.insert(fhir_id, fsort);
597    }
598
599    fn sort_of_alias_reft(&self, fhir_id: FhirId) -> rty::FuncSort {
600        self.sort_of_alias_reft
601            .get(&fhir_id)
602            .unwrap_or_else(|| tracked_span_bug!("no entry found for `{fhir_id:?}`"))
603            .clone()
604    }
605
606    fn deeply_normalize_sorts<T: TypeFoldable + Clone>(
607        genv: GlobalEnv,
608        owner: FluxOwnerId,
609        t: &T,
610    ) -> QueryResult<T> {
611        let infcx = genv
612            .tcx()
613            .infer_ctxt()
614            .with_next_trait_solver(true)
615            .build(TypingMode::non_body_analysis());
616        if let Some(def_id) = owner.resolved_id() {
617            t.deeply_normalize_sorts(def_id, genv, &infcx)
618        } else {
619            Ok(t.clone())
620        }
621    }
622
623    // FIXME(nilehmann) this is a bit of a hack. We should find a more robust way to do normalization
624    // for sort checking, including normalizing projections. [RJ: normalizing_projections is done now]
625    // Maybe we can do lazy normalization. Once we do that maybe we can also stop
626    // expanding aliases in `ConvCtxt::conv_sort`.
627    pub(crate) fn normalize_sorts(&mut self) -> QueryResult {
628        for sort in self.node_sort.values_mut() {
629            *sort = Self::deeply_normalize_sorts(self.genv, self.owner, sort)?;
630        }
631        for fsort in self.sort_of_alias_reft.values_mut() {
632            *fsort = Self::deeply_normalize_sorts(self.genv, self.owner, fsort)?;
633        }
634        Ok(())
635    }
636}
637
638impl<'genv> InferCtxt<'genv, '_> {
639    pub(super) fn declare_param(&mut self, param: fhir::RefineParam<'genv>, sort: rty::Sort) {
640        self.params.insert(param.id, (param, sort));
641    }
642
643    /// Whether a value of `sort1` can be automatically coerced to a value of `sort2`. A value of an
644    /// [`rty::SortCtor::Adt`] sort with a single field of sort `s` can be coerced to a value of sort
645    /// `s` and vice versa, i.e., we can automatically project the field out of the record or inject
646    /// a value into a record.
647    fn is_coercible(&mut self, sort1: &rty::Sort, sort2: &rty::Sort, fhir_id: FhirId) -> bool {
648        if self.try_equate(sort1, sort2).is_some() {
649            return true;
650        }
651
652        let mut sort1 = sort1.clone();
653        let mut sort2 = sort2.clone();
654
655        let mut coercions = vec![];
656        if let Some((def_id, sort)) = self.is_single_field_struct(&sort1) {
657            coercions.push(rty::Coercion::Project(def_id));
658            sort1 = sort.clone();
659        }
660        if let Some((def_id, sort)) = self.is_single_field_struct(&sort2) {
661            coercions.push(rty::Coercion::Inject(def_id));
662            sort2 = sort.clone();
663        }
664        self.wfckresults.coercions_mut().insert(fhir_id, coercions);
665        self.try_equate(&sort1, &sort2).is_some()
666    }
667
668    fn is_coercible_from_func(
669        &mut self,
670        sort: &rty::Sort,
671        fhir_id: FhirId,
672    ) -> Option<rty::PolyFuncSort> {
673        if let rty::Sort::Func(fsort) = sort {
674            Some(fsort.clone())
675        } else if let Some((def_id, rty::Sort::Func(fsort))) = self.is_single_field_struct(sort) {
676            self.wfckresults
677                .coercions_mut()
678                .insert(fhir_id, vec![rty::Coercion::Inject(def_id)]);
679            Some(fsort.clone())
680        } else {
681            None
682        }
683    }
684
685    fn is_coercible_to_func(
686        &mut self,
687        sort: &rty::Sort,
688        fhir_id: FhirId,
689    ) -> Option<rty::PolyFuncSort> {
690        if let rty::Sort::Func(fsort) = sort {
691            Some(fsort.clone())
692        } else if let Some((def_id, rty::Sort::Func(fsort))) = self.is_single_field_struct(sort) {
693            self.wfckresults
694                .coercions_mut()
695                .insert(fhir_id, vec![rty::Coercion::Project(def_id)]);
696            Some(fsort.clone())
697        } else {
698            None
699        }
700    }
701
702    fn try_equate(&mut self, sort1: &rty::Sort, sort2: &rty::Sort) -> Option<rty::Sort> {
703        let sort1 = self.resolve_vars_if_possible(sort1);
704        let sort2 = self.resolve_vars_if_possible(sort2);
705        self.try_equate_inner(&sort1, &sort2)
706    }
707
708    fn try_equate_inner(&mut self, sort1: &rty::Sort, sort2: &rty::Sort) -> Option<rty::Sort> {
709        match (sort1, sort2) {
710            (rty::Sort::Infer(vid1), rty::Sort::Infer(vid2)) => {
711                self.sort_unification_table
712                    .unify_var_var(*vid1, *vid2)
713                    .ok()?;
714            }
715            (rty::Sort::Infer(vid), sort) | (sort, rty::Sort::Infer(vid)) => {
716                self.sort_unification_table
717                    .unify_var_value(*vid, rty::SortVarVal::Solved(sort.clone()))
718                    .ok()?;
719            }
720            (rty::Sort::App(ctor1, args1), rty::Sort::App(ctor2, args2)) => {
721                if ctor1 != ctor2 || args1.len() != args2.len() {
722                    return None;
723                }
724                for (s1, s2) in iter::zip(args1, args2) {
725                    self.try_equate_inner(s1, s2)?;
726                }
727            }
728            (rty::Sort::Tuple(sorts1), rty::Sort::Tuple(sorts2)) => {
729                if sorts1.len() != sorts2.len() {
730                    return None;
731                }
732                for (s1, s2) in iter::zip(sorts1, sorts2) {
733                    self.try_equate_inner(s1, s2)?;
734                }
735            }
736            (rty::Sort::BitVec(size1), rty::Sort::BitVec(size2)) => {
737                self.try_equate_bv_sizes(*size1, *size2)?;
738            }
739            _ if sort1 == sort2 => {}
740            _ => return None,
741        }
742        Some(sort1.clone())
743    }
744
745    fn try_equate_bv_sizes(
746        &mut self,
747        size1: rty::BvSize,
748        size2: rty::BvSize,
749    ) -> Option<rty::BvSize> {
750        match (size1, size2) {
751            (rty::BvSize::Infer(vid1), rty::BvSize::Infer(vid2)) => {
752                self.bv_size_unification_table
753                    .unify_var_var(vid1, vid2)
754                    .ok()?;
755            }
756            (rty::BvSize::Infer(vid), size) | (size, rty::BvSize::Infer(vid)) => {
757                self.bv_size_unification_table
758                    .unify_var_value(vid, Some(size))
759                    .ok()?;
760            }
761            _ if size1 == size2 => {}
762            _ => return None,
763        }
764        Some(size1)
765    }
766
767    fn next_sort_var(&mut self) -> rty::Sort {
768        rty::Sort::Infer(self.next_sort_vid(Default::default()))
769    }
770
771    fn next_sort_var_with_cstr(&mut self, cstr: rty::SortCstr) -> rty::Sort {
772        rty::Sort::Infer(self.next_sort_vid(rty::SortVarVal::Unsolved(cstr)))
773    }
774
775    pub(crate) fn next_sort_vid(&mut self, val: rty::SortVarVal) -> rty::SortVid {
776        self.sort_unification_table.new_key(val)
777    }
778
779    fn next_bv_size_var(&mut self) -> rty::BvSize {
780        rty::BvSize::Infer(self.next_bv_size_vid())
781    }
782
783    fn next_bv_size_vid(&mut self) -> rty::BvSizeVid {
784        self.bv_size_unification_table.new_key(None)
785    }
786
787    fn ensure_resolved_path(&mut self, path: &fhir::PathExpr) -> Result<rty::Sort> {
788        let sort = self.synth_path(path);
789        self.fully_resolve(&sort)
790            .map_err(|_| self.emit_err(errors::CannotInferSort::new(path.span)))
791    }
792
793    fn is_single_field_struct(&mut self, sort: &rty::Sort) -> Option<(DefId, rty::Sort)> {
794        if let rty::Sort::App(rty::SortCtor::Adt(sort_def), sort_args) = sort
795            && let Some(variant) = sort_def.opt_struct_variant()
796            && let [sort] = &variant.field_sorts(sort_args)[..]
797        {
798            Some((sort_def.did(), sort.clone()))
799        } else {
800            None
801        }
802    }
803
804    pub(crate) fn into_results(mut self) -> Result<WfckResults> {
805        // Make sure the int literals are fully resolved. This must happen before everything else
806        // such that we properly apply the fallback for unconstrained num vars.
807        for (node, sort) in std::mem::take(&mut self.sort_of_literal) {
808            // Fallback to `int` when a num variable is unconstrained. Note that we unconditionally
809            // unify the variable. This is fine because if the variable has already been unified,
810            // the operation will fail and this won't have any effect. Also note that unifying a
811            // variable could solve variables that appear later in this for loop. This is also fine
812            // because the order doesn't matter as we are unifying everything to `int`.
813            if let rty::Sort::Infer(vid) = &sort {
814                let _ = self
815                    .sort_unification_table
816                    .unify_var_value(*vid, rty::SortVarVal::Solved(rty::Sort::Int));
817            }
818
819            let sort = self
820                .fully_resolve(&sort)
821                .map_err(|_| self.emit_err(errors::CannotInferSort::new(node.span)))?;
822            self.wfckresults.node_sorts_mut().insert(node.fhir_id, sort);
823        }
824
825        // Make sure the binary operators are fully resolved
826        for (node, sort) in std::mem::take(&mut self.sort_of_bin_op) {
827            let sort = self
828                .fully_resolve(&sort)
829                .map_err(|_| self.emit_err(errors::CannotInferSort::new(node.span)))?;
830            self.wfckresults
831                .bin_op_sorts_mut()
832                .insert(node.fhir_id, sort);
833        }
834
835        let allow_uninterpreted_cast =
836            self.owner
837                .as_rust()
838                .map_or_else(flux_config::allow_uninterpreted_cast, |def_id| {
839                    self.genv
840                        .infer_opts(def_id.local_id().def_id)
841                        .allow_uninterpreted_cast
842                });
843
844        // Make sure that function applications are fully resolved
845        for (node, sort_args) in std::mem::take(&mut self.sort_args_of_app) {
846            let mut res = vec![];
847            for sort_arg in &sort_args {
848                let sort_arg = match sort_arg {
849                    rty::SortArg::Sort(sort) => {
850                        let sort = self
851                            .fully_resolve(sort)
852                            .map_err(|_| self.emit_err(errors::CannotInferSort::new(node.span)))?;
853                        rty::SortArg::Sort(sort)
854                    }
855                    rty::SortArg::BvSize(rty::BvSize::Infer(vid)) => {
856                        let size = self
857                            .bv_size_unification_table
858                            .probe_value(*vid)
859                            .ok_or_else(|| {
860                                self.emit_err(errors::CannotInferSort::new(node.span))
861                            })?;
862                        rty::SortArg::BvSize(size)
863                    }
864                    _ => sort_arg.clone(),
865                };
866                res.push(sort_arg);
867            }
868            if let fhir::ExprKind::App(callee, _) = node.kind
869                && matches!(callee.res, fhir::Res::GlobalFunc(fhir::SpecFuncKind::Cast))
870            {
871                let [rty::SortArg::Sort(from), rty::SortArg::Sort(to)] = &res[..] else {
872                    span_bug!(node.span, "invalid sort args!")
873                };
874                if !allow_uninterpreted_cast
875                    && matches!(from.cast_kind(to), rty::CastKind::Uninterpreted)
876                {
877                    return Err(self.emit_err(errors::InvalidCast::new(node.span, from, to)));
878                }
879            }
880            self.wfckresults
881                .fn_app_sorts_mut()
882                .insert(node.fhir_id, res.into());
883        }
884
885        // Make sure all parameters are fully resolved
886        for (_, (param, sort)) in std::mem::take(&mut self.params) {
887            let sort = self
888                .fully_resolve(&sort)
889                .map_err(|_| self.emit_err(errors::SortAnnotationNeeded::new(&param)))?;
890            self.wfckresults.param_sorts_mut().insert(param.id, sort);
891        }
892
893        Ok(self.wfckresults)
894    }
895
896    pub(crate) fn infer_mode(&self, id: fhir::ParamId) -> fhir::InferMode {
897        fhir::InferMode::from_param_kind(self.params[&id].0.kind)
898    }
899
900    #[track_caller]
901    pub(crate) fn param_sort(&self, id: fhir::ParamId) -> rty::Sort {
902        self.params
903            .get(&id)
904            .unwrap_or_else(|| bug!("no entry found for `{id:?}`"))
905            .1
906            .clone()
907    }
908
909    fn shallow_resolve(&mut self, sort: &rty::Sort) -> rty::Sort {
910        sort.fold_with(&mut ShallowResolver { infcx: self })
911    }
912
913    fn resolve_vars_if_possible(&mut self, sort: &rty::Sort) -> rty::Sort {
914        sort.fold_with(&mut OpportunisticResolver { infcx: self })
915    }
916
917    pub(crate) fn fully_resolve(&mut self, sort: &rty::Sort) -> std::result::Result<rty::Sort, ()> {
918        sort.try_fold_with(&mut FullResolver { infcx: self })
919    }
920}
921
922/// Before the main sort inference, we do a first traversal checking all implicitly scoped parameters
923/// declared with `@` or `#` and infer their sort based on the type they are indexing, e.g., if `n` was
924/// declared as `i32[@n]`, we infer `int` for `n`.
925///
926/// This prepass is necessary because sometimes the order in which we traverse expressions can
927/// affect what we can infer. By resolving implicit parameters first, we ensure more consistent and
928/// complete inference regardless of how expressions are later traversed.
929///
930/// It should be possible to improve sort checking (e.g., by allowing partially resolved sorts in
931/// function position) such that we don't need this anymore.
932pub(crate) struct ImplicitParamInferer<'a, 'genv, 'tcx> {
933    infcx: &'a mut InferCtxt<'genv, 'tcx>,
934    errors: Errors<'genv>,
935}
936
937impl<'a, 'genv, 'tcx> ImplicitParamInferer<'a, 'genv, 'tcx> {
938    pub(crate) fn infer(
939        infcx: &'a mut InferCtxt<'genv, 'tcx>,
940        node: &fhir::OwnerNode<'genv>,
941    ) -> Result {
942        let errors = Errors::new(infcx.genv.sess());
943        let mut vis = Self { infcx, errors };
944        vis.visit_node(node);
945        vis.errors.to_result()
946    }
947
948    fn infer_implicit_params(&mut self, idx: &fhir::Expr, expected: &rty::Sort) {
949        match idx.kind {
950            fhir::ExprKind::Var(QPathExpr::Resolved(var, Some(_))) => {
951                let (_, id) = var.res.expect_param();
952                let found = self.infcx.param_sort(id);
953                self.infcx.try_equate(&found, expected).unwrap_or_else(|| {
954                    span_bug!(idx.span, "failed to equate sorts: `{found:?}` `{expected:?}`")
955                });
956            }
957            fhir::ExprKind::Record(flds) => {
958                if let Some(sorts) = expected.field_sorts() {
959                    if flds.len() != sorts.len() {
960                        self.errors.emit(errors::ArgCountMismatch::new(
961                            Some(idx.span),
962                            String::from("type"),
963                            sorts.len(),
964                            flds.len(),
965                        ));
966                        return;
967                    }
968                    for (f, sort) in iter::zip(flds, &sorts) {
969                        self.infer_implicit_params(f, sort);
970                    }
971                } else {
972                    self.errors.emit(errors::ArgCountMismatch::new(
973                        Some(idx.span),
974                        String::from("type"),
975                        1,
976                        flds.len(),
977                    ));
978                }
979            }
980            _ => {}
981        }
982    }
983}
984
985impl<'genv> fhir::visit::Visitor<'genv> for ImplicitParamInferer<'_, 'genv, '_> {
986    fn visit_ty(&mut self, ty: &fhir::Ty<'genv>) {
987        if let fhir::TyKind::Indexed(bty, idx) = &ty.kind {
988            let expected = self.infcx.sort_of_bty(bty);
989            self.infer_implicit_params(idx, &expected);
990        }
991        fhir::visit::walk_ty(self, ty);
992    }
993}
994
995impl InferCtxt<'_, '_> {
996    #[track_caller]
997    fn emit_sort_mismatch(
998        &mut self,
999        span: Span,
1000        expected: &rty::Sort,
1001        found: &rty::Sort,
1002    ) -> ErrorGuaranteed {
1003        let expected = self.resolve_vars_if_possible(expected);
1004        let found = self.resolve_vars_if_possible(found);
1005        self.emit_err(errors::SortMismatch::new(span, expected, found))
1006    }
1007
1008    fn emit_field_not_found(&mut self, sort: &rty::Sort, field: Ident) -> ErrorGuaranteed {
1009        self.emit_err(errors::FieldNotFound::new(sort.clone(), field))
1010    }
1011
1012    #[track_caller]
1013    fn emit_err<'b>(&'b self, err: impl Diagnostic<'b>) -> ErrorGuaranteed {
1014        self.genv.sess().emit_err(err)
1015    }
1016}
1017
1018struct ShallowResolver<'a, 'genv, 'tcx> {
1019    infcx: &'a mut InferCtxt<'genv, 'tcx>,
1020}
1021
1022impl TypeFolder for ShallowResolver<'_, '_, '_> {
1023    fn fold_sort(&mut self, sort: &rty::Sort) -> rty::Sort {
1024        match sort {
1025            rty::Sort::Infer(vid) => {
1026                // if `sort` is a sort variable, it can be resolved to a bit-vec variable,
1027                // which can then be recursively resolved, hence the recursion. Note though that
1028                // we prevent sort variables from unifying to other sort variables directly (though
1029                // they may be embedded structurally), so this recursion should always be of very
1030                // limited depth.
1031                self.infcx
1032                    .sort_unification_table
1033                    .probe_value(*vid)
1034                    .map_solved(|sort| sort.fold_with(self))
1035                    .solved_or(sort)
1036            }
1037            rty::Sort::BitVec(rty::BvSize::Infer(vid)) => {
1038                self.infcx
1039                    .bv_size_unification_table
1040                    .probe_value(*vid)
1041                    .map(rty::Sort::BitVec)
1042                    .unwrap_or(sort.clone())
1043            }
1044            _ => sort.clone(),
1045        }
1046    }
1047}
1048
1049struct OpportunisticResolver<'a, 'genv, 'tcx> {
1050    infcx: &'a mut InferCtxt<'genv, 'tcx>,
1051}
1052
1053impl TypeFolder for OpportunisticResolver<'_, '_, '_> {
1054    fn fold_sort(&mut self, sort: &rty::Sort) -> rty::Sort {
1055        let s = self.infcx.shallow_resolve(sort);
1056        s.super_fold_with(self)
1057    }
1058}
1059
1060struct FullResolver<'a, 'genv, 'tcx> {
1061    infcx: &'a mut InferCtxt<'genv, 'tcx>,
1062}
1063
1064impl FallibleTypeFolder for FullResolver<'_, '_, '_> {
1065    type Error = ();
1066
1067    fn try_fold_sort(&mut self, sort: &rty::Sort) -> std::result::Result<rty::Sort, Self::Error> {
1068        let s = self.infcx.shallow_resolve(sort);
1069        match s {
1070            rty::Sort::Infer(_) | rty::Sort::BitVec(rty::BvSize::Infer(_)) => Err(()),
1071            _ => s.try_super_fold_with(self),
1072        }
1073    }
1074}
1075
1076/// Map to associate data to a node (i.e., an expression).
1077///
1078/// Used to record elaborated information.
1079#[derive_where(Default)]
1080struct NodeMap<'genv, T> {
1081    map: FxHashMap<FhirId, (fhir::Expr<'genv>, T)>,
1082}
1083
1084impl<'genv, T> NodeMap<'genv, T> {
1085    /// Add a `node` to the map with associated `data`
1086    fn insert(&mut self, node: fhir::Expr<'genv>, data: T) {
1087        assert!(self.map.insert(node.fhir_id, (node, data)).is_none());
1088    }
1089}
1090
1091impl<'genv, T> IntoIterator for NodeMap<'genv, T> {
1092    type Item = (fhir::Expr<'genv>, T);
1093
1094    type IntoIter = std::collections::hash_map::IntoValues<FhirId, Self::Item>;
1095
1096    fn into_iter(self) -> Self::IntoIter {
1097        self.map.into_values()
1098    }
1099}