1mod errors;
6mod param_usage;
7mod sortck;
8
9use flux_common::result::{ErrorCollector, ResultExt as _};
10use flux_errors::Errors;
11use flux_middle::{
12 def_id::MaybeExternId,
13 fhir::{self, FhirId, FluxOwnerId, visit::Visitor},
14 global_env::GlobalEnv,
15 queries::QueryResult,
16 rty::{self, RecordCtor, WfckResults},
17};
18use rustc_data_structures::unord::UnordSet;
19use rustc_errors::ErrorGuaranteed;
20use rustc_hir::{
21 OwnerId,
22 def::DefKind,
23 def_id::{CrateNum, DefId, DefIndex},
24};
25
26use self::sortck::{ImplicitParamInferer, InferCtxt};
27use crate::{
28 conv::{ConvPhase, WfckResultsProvider},
29 wf::sortck::prim_op_sort,
30};
31
32type Result<T = ()> = std::result::Result<T, ErrorGuaranteed>;
33
34pub(crate) fn check_flux_item<'genv>(
35 genv: GlobalEnv<'genv, '_>,
36 item: fhir::FluxItem<'genv>,
37) -> Result<WfckResults> {
38 let owner = FluxOwnerId::Flux(item.def_id());
39 let mut infcx = InferCtxt::new(genv, owner);
40
41 Wf::with(&mut infcx, |wf| {
42 wf.init_infcx_for_flux_item(item).emit(&genv)?;
43 wf.check_flux_item(item);
44 Ok(())
45 })?;
46 infcx.into_results()
47}
48
49pub(crate) fn check_constant_expr<'genv>(
50 genv: GlobalEnv<'genv, '_>,
51 owner: MaybeExternId<OwnerId>,
52 expr: &fhir::Expr<'genv>,
53 sort: &rty::Sort,
54) -> Result<WfckResults> {
55 let mut infcx = InferCtxt::new(genv, FluxOwnerId::Rust(owner));
56 Wf::with(&mut infcx, |wf| {
57 wf.declare_params_in_expr(expr)?;
58 wf.as_conv_ctxt()
59 .conv_constant_expr(expr)
60 .emit(&wf.errors)?;
61 wf.check_expr(expr, sort);
62 Ok(())
63 })?;
64 infcx.into_results()
65}
66
67pub(crate) fn check_invariants<'genv>(
68 genv: GlobalEnv<'genv, '_>,
69 adt_def_id: MaybeExternId<OwnerId>,
70 params: &[fhir::RefineParam<'genv>],
71 invariants: &[fhir::Expr<'genv>],
72) -> Result<WfckResults> {
73 let owner = FluxOwnerId::Rust(adt_def_id);
74 let mut infcx = InferCtxt::new(genv, owner);
75 Wf::with(&mut infcx, |wf| {
76 wf.declare_params_for_invariants(params, invariants)?;
77
78 wf.as_conv_ctxt()
82 .conv_invariants(adt_def_id.map(|it| it.def_id), params, invariants)
83 .emit(&wf.errors)?;
84
85 for invariant in invariants {
86 wf.check_expr(invariant, &rty::Sort::Bool);
87 }
88 Ok(())
89 })?;
90 infcx.into_results()
91}
92
93pub(crate) fn check_node<'genv>(
94 genv: GlobalEnv<'genv, '_>,
95 node: &fhir::OwnerNode<'genv>,
96) -> Result<WfckResults> {
97 let mut infcx = InferCtxt::new(genv, node.owner_id().into());
98 Wf::with(&mut infcx, |wf| {
99 wf.init_infcx_for_node(node)
100 .map_err(|err| err.at(genv.tcx().def_span(node.owner_id().local_id())))
101 .emit(&genv)?;
102
103 ImplicitParamInferer::infer(wf.infcx, node)?;
104
105 wf.check_node(node);
106 Ok(())
107 })?;
108
109 param_usage::check(&infcx, node)?;
110
111 infcx.into_results()
112}
113
114struct Wf<'a, 'genv, 'tcx> {
115 infcx: &'a mut InferCtxt<'genv, 'tcx>,
116 errors: Errors<'genv>,
117 next_type_index: u32,
118 next_region_index: u32,
119 next_const_index: u32,
120}
121
122impl<'a, 'genv, 'tcx> Wf<'a, 'genv, 'tcx> {
123 fn with(infcx: &'a mut InferCtxt<'genv, 'tcx>, f: impl FnOnce(&mut Self) -> Result) -> Result {
124 let errors = Errors::new(infcx.genv.sess());
125 let mut wf = Self {
126 infcx,
127 errors,
128 next_type_index: 1,
131 next_region_index: 0,
132 next_const_index: 0,
133 };
134 f(&mut wf)?;
135 wf.errors.into_result()
136 }
137
138 fn check_flux_item(&mut self, item: fhir::FluxItem<'genv>) {
139 self.visit_flux_item(&item);
140 }
141
142 fn check_node(&mut self, node: &fhir::OwnerNode<'genv>) {
143 self.visit_node(node);
144 }
145
146 fn check_expr(&mut self, expr: &fhir::Expr<'genv>, sort: &rty::Sort) {
147 self.infcx
148 .check_expr(expr, sort)
149 .collect_err(&mut self.errors);
150 }
151
152 fn declare_params_for_primop_prop(&mut self, primop_prop: &fhir::PrimOpProp<'genv>) -> Result {
155 let Some((sorts, _)) = prim_op_sort(&primop_prop.op) else {
156 return Err(self
157 .errors
158 .emit(errors::UnsupportedPrimOp::new(primop_prop.span, primop_prop.op)));
159 };
160 if primop_prop.args.len() != sorts.len() {
161 return Err(self.errors.emit(errors::ArgCountMismatch::new(
162 Some(primop_prop.span),
163 String::from("primop"),
164 sorts.len(),
165 primop_prop.args.len(),
166 )));
167 }
168 for (arg, sort) in primop_prop.args.iter().zip(sorts) {
169 self.infcx.declare_param(*arg, sort);
170 }
171 visit_refine_params(
172 |vis| vis.visit_expr(&primop_prop.body),
173 |param| self.declare_param(param),
174 )
175 }
176
177 fn declare_params_for_flux_item(&mut self, item: fhir::FluxItem<'genv>) -> Result {
179 if let fhir::FluxItem::PrimOpProp(primop_prop) = item {
180 self.declare_params_for_primop_prop(primop_prop)
181 } else {
182 visit_refine_params(|vis| vis.visit_flux_item(&item), |param| self.declare_param(param))
183 }
184 }
185
186 fn declare_params_for_node(&mut self, node: &fhir::OwnerNode<'genv>) -> Result {
188 visit_refine_params(|vis| vis.visit_node(node), |param| self.declare_param(param))
189 }
190
191 fn declare_params_for_invariants(
193 &mut self,
194 params: &[fhir::RefineParam<'genv>],
195 invariants: &[fhir::Expr<'genv>],
196 ) -> Result {
197 for param in params {
198 self.declare_param(param)?;
199 }
200 for expr in invariants {
201 self.declare_params_in_expr(expr)?;
202 }
203 Ok(())
204 }
205
206 fn declare_params_in_expr(&mut self, expr: &fhir::Expr<'genv>) -> Result {
207 visit_refine_params(|vis| vis.visit_expr(expr), |param| self.declare_param(param))
208 }
209
210 fn declare_param(&mut self, param: &fhir::RefineParam<'genv>) -> Result {
211 let sort = self
212 .as_conv_ctxt()
213 .conv_sort(¶m.sort)
214 .emit(&self.genv())?;
215 self.infcx.declare_param(*param, sort);
216 Ok(())
217 }
218
219 fn init_infcx_for_node(&mut self, node: &fhir::OwnerNode<'genv>) -> QueryResult {
236 let def_id = node.owner_id().map(|id| id.def_id);
237 self.declare_params_for_node(node)?;
238 let cx = self.as_conv_ctxt();
239 match node {
240 fhir::OwnerNode::Item(item) => {
241 match &item.kind {
242 fhir::ItemKind::Enum(enum_def) => {
243 cx.conv_enum_variants(def_id, enum_def)?;
244 cx.conv_generic_predicates(def_id, &item.generics)?;
245 }
246 fhir::ItemKind::Struct(struct_def) => {
247 cx.conv_struct_variant(def_id, struct_def)?;
248 cx.conv_generic_predicates(def_id, &item.generics)?;
249 }
250 fhir::ItemKind::TyAlias(ty_alias) => {
251 cx.conv_type_alias(def_id, ty_alias)?;
252 cx.conv_generic_predicates(def_id, &item.generics)?;
253 }
254 fhir::ItemKind::Trait(trait_) => {
255 for assoc_reft in trait_.assoc_refinements {
256 if let Some(body) = assoc_reft.body {
257 cx.conv_assoc_reft_body(
258 assoc_reft.params,
259 &body,
260 &assoc_reft.output,
261 )?;
262 }
263 }
264 cx.conv_generic_predicates(def_id, &item.generics)?;
265 }
266 fhir::ItemKind::Impl(impl_) => {
267 for assoc_reft in impl_.assoc_refinements {
268 cx.conv_assoc_reft_body(
269 assoc_reft.params,
270 &assoc_reft.body,
271 &assoc_reft.output,
272 )?;
273 }
274 cx.conv_generic_predicates(def_id, &item.generics)?;
275 }
276 fhir::ItemKind::Fn(fn_sig) => {
277 cx.conv_fn_sig(def_id, fn_sig)?;
278 cx.conv_generic_predicates(def_id, &item.generics)?;
279 }
280 fhir::ItemKind::Static(ty) => {
281 if let Some(ty) = ty {
282 cx.conv_static_ty(ty)?;
283 }
284 }
285 fhir::ItemKind::Const(_) => {}
286 }
287 }
288 fhir::OwnerNode::TraitItem(trait_item) => {
289 match trait_item.kind {
290 fhir::TraitItemKind::Fn(fn_sig) => {
291 cx.conv_fn_sig(def_id, &fn_sig)?;
292 cx.conv_generic_predicates(def_id, &trait_item.generics)?;
293 }
294 fhir::TraitItemKind::Type => {}
295 fhir::TraitItemKind::Const => {}
296 }
297 }
298 fhir::OwnerNode::ImplItem(impl_item) => {
299 match impl_item.kind {
300 fhir::ImplItemKind::Fn(fn_sig) => {
301 cx.conv_fn_sig(def_id, &fn_sig)?;
302 cx.conv_generic_predicates(def_id, &impl_item.generics)?;
303 }
304 fhir::ImplItemKind::Type => {}
305 fhir::ImplItemKind::Const => {}
306 }
307 }
308 fhir::OwnerNode::ForeignItem(impl_item) => {
309 match impl_item.kind {
310 fhir::ForeignItemKind::Fn(fn_sig, generics) => {
311 cx.conv_fn_sig(def_id, &fn_sig)?;
312 cx.conv_generic_predicates(def_id, generics)?;
313 }
314 fhir::ForeignItemKind::Static(_, _, _, _) => {
315 }
317 }
318 }
319 }
320 self.infcx.normalize_sorts()
321 }
322
323 fn init_infcx_for_flux_item(&mut self, item: fhir::FluxItem<'genv>) -> QueryResult {
324 self.declare_params_for_flux_item(item)?;
325 let cx = self.as_conv_ctxt();
326 match item {
327 fhir::FluxItem::Qualifier(qualifier) => {
328 cx.conv_qualifier(qualifier)?;
329 }
330 fhir::FluxItem::Func(spec_func) => {
331 cx.conv_defn(spec_func)?;
332 }
333 fhir::FluxItem::PrimOpProp(prim_op_prop) => {
334 cx.conv_primop_prop(prim_op_prop)?;
335 }
336 fhir::FluxItem::SortDecl(_sort_decl) => {}
337 }
338 Ok(())
339 }
340
341 fn check_output_locs(&mut self, fn_decl: &fhir::FnDecl) {
342 let mut output_locs = UnordSet::new();
343 for ens in fn_decl.output.ensures {
344 if let fhir::Ensures::Type(loc, ..) = ens
345 && let (_, id) = loc.res.expect_param()
346 && !output_locs.insert(id)
347 {
348 self.errors.emit(errors::DuplicatedEnsures::new(loc));
349 }
350 }
351
352 for ty in fn_decl.inputs {
353 if let fhir::TyKind::StrgRef(_, loc, _) = ty.kind
354 && let (_, id) = loc.res.expect_param()
355 && !output_locs.contains(&id)
356 {
357 self.errors.emit(errors::MissingEnsures::new(loc));
358 }
359 }
360 }
361}
362
363impl<'genv> fhir::visit::Visitor<'genv> for Wf<'_, 'genv, '_> {
364 fn visit_qualifier(&mut self, qual: &fhir::Qualifier<'genv>) {
365 self.check_expr(&qual.expr, &rty::Sort::Bool);
366
367 let wildcards = std::iter::zip(qual.args, qual.wildcards)
369 .filter_map(|(param, &is_wildcard)| is_wildcard.then_some(param));
370 for param in wildcards {
371 let sort = self.infcx.param_sort(param.id);
372 if !matches!(
373 sort,
374 rty::Sort::Int | rty::Sort::Real | rty::Sort::Str | rty::Sort::BitVec(_)
375 ) {
376 self.errors
377 .emit(errors::InvalidWildcardSort::new(param.span, sort));
378 }
379 }
380 }
381
382 fn visit_primop_prop(&mut self, primop_prop: &fhir::PrimOpProp<'genv>) {
383 let Some((sorts, _)) = prim_op_sort(&primop_prop.op) else {
384 self.errors
385 .emit(errors::UnsupportedPrimOp::new(primop_prop.span, primop_prop.op));
386 return;
387 };
388
389 if primop_prop.args.len() != sorts.len() {
390 self.errors.emit(errors::ArgCountMismatch::new(
391 Some(primop_prop.span),
392 String::from("primop"),
393 sorts.len(),
394 primop_prop.args.len(),
395 ));
396 return;
397 }
398 self.check_expr(&primop_prop.body, &rty::Sort::Bool);
399 }
400
401 fn visit_func(&mut self, func: &fhir::SpecFunc<'genv>) {
402 if let Some(body) = &func.body {
403 let Ok(output) = self.as_conv_ctxt().conv_sort(&func.sort).emit(&self.errors) else {
404 return;
405 };
406 self.check_expr(body, &output);
407 }
408 }
409
410 fn visit_impl_assoc_reft(&mut self, assoc_reft: &fhir::ImplAssocReft<'genv>) {
411 let Ok(output) = self
412 .as_conv_ctxt()
413 .conv_sort(&assoc_reft.output)
414 .emit(&self.errors)
415 else {
416 return;
417 };
418 self.check_expr(&assoc_reft.body, &output);
419 }
420
421 fn visit_trait_assoc_reft(&mut self, assoc_reft: &fhir::TraitAssocReft<'genv>) {
422 if let Some(body) = &assoc_reft.body {
423 let Ok(output) = self
424 .as_conv_ctxt()
425 .conv_sort(&assoc_reft.output)
426 .emit(&self.errors)
427 else {
428 return;
429 };
430 self.check_expr(body, &output);
431 }
432 }
433
434 fn visit_variant_ret(&mut self, ret: &fhir::VariantRet<'genv>) {
435 let genv = self.infcx.genv;
436 let enum_id = ret.enum_id;
437 let Ok(adt_sort_def) = genv.adt_sort_def_of(enum_id).emit(&self.errors) else { return };
438 if adt_sort_def.is_reflected() {
439 return;
440 }
441 let Ok(args) = rty::GenericArg::identity_for_item(genv, enum_id).emit(&self.errors) else {
442 return;
443 };
444 let expected = adt_sort_def.to_sort(&args);
445 self.check_expr(&ret.idx, &expected);
446 }
447
448 fn visit_fn_sig(&mut self, sig: &fhir::FnSig<'genv>) {
449 fhir::visit::walk_fn_sig(self, sig);
450 if let Some(e) = sig.no_panic_if {
451 self.check_expr(&e, &rty::Sort::Bool);
452 }
453 }
454
455 fn visit_fn_decl(&mut self, decl: &fhir::FnDecl<'genv>) {
456 fhir::visit::walk_fn_decl(self, decl);
457 self.check_output_locs(decl);
458 }
459
460 fn visit_requires(&mut self, requires: &fhir::Requires<'genv>) {
461 self.check_expr(&requires.pred, &rty::Sort::Bool);
462 }
463
464 fn visit_ensures(&mut self, ensures: &fhir::Ensures<'genv>) {
465 match ensures {
466 fhir::Ensures::Type(loc, ty) => {
467 self.infcx.check_loc(loc).collect_err(&mut self.errors);
468 self.visit_ty(ty);
469 }
470 fhir::Ensures::Pred(pred) => {
471 self.check_expr(pred, &rty::Sort::Bool);
472 }
473 }
474 }
475
476 fn visit_ty(&mut self, ty: &fhir::Ty<'genv>) {
477 match &ty.kind {
478 fhir::TyKind::Indexed(bty, idx) => {
479 let expected = self.infcx.sort_of_bty(bty);
480 self.check_expr(idx, &expected);
481 self.visit_bty(bty);
482 }
483 fhir::TyKind::StrgRef(_, loc, ty) => {
484 self.infcx.check_loc(loc).collect_err(&mut self.errors);
485 self.visit_ty(ty);
486 }
487 fhir::TyKind::Constr(pred, ty) => {
488 self.visit_ty(ty);
489 self.check_expr(pred, &rty::Sort::Bool);
490 }
491 _ => fhir::visit::walk_ty(self, ty),
492 }
493 }
494
495 fn visit_path(&mut self, path: &fhir::Path<'genv>) {
496 let genv = self.genv();
497 if let fhir::Res::Def(DefKind::TyAlias, def_id) = path.res {
498 let Ok(generics) = genv.refinement_generics_of(def_id).emit(&self.errors) else {
499 return;
500 };
501
502 let args = self.infcx.path_args(path.fhir_id);
503 for (i, expr) in path.refine.iter().enumerate() {
504 let Ok(param) = generics.param_at(i, genv).emit(&self.errors) else { return };
505 let param = param.instantiate(genv.tcx(), &args, &[]);
506 self.check_expr(expr, ¶m.sort);
507 }
508 };
509 fhir::visit::walk_path(self, path);
510 }
511}
512
513struct RefineParamVisitor<F> {
514 f: F,
515 err: Option<ErrorGuaranteed>,
516}
517
518impl<'v, F> fhir::visit::Visitor<'v> for RefineParamVisitor<F>
519where
520 F: FnMut(&fhir::RefineParam<'v>) -> Result,
521{
522 fn visit_refine_param(&mut self, param: &fhir::RefineParam<'v>) {
523 (self.f)(param).collect_err(&mut self.err);
524 }
525}
526
527fn visit_refine_params<'a, F>(visit: impl FnOnce(&mut RefineParamVisitor<F>), f: F) -> Result
528where
529 F: FnMut(&fhir::RefineParam<'a>) -> Result,
530{
531 let mut visitor = RefineParamVisitor { f, err: None };
532 visit(&mut visitor);
533 visitor.err.into_result()
534}
535
536impl<'genv, 'tcx> ConvPhase<'genv, 'tcx> for Wf<'_, 'genv, 'tcx> {
537 const EXPAND_TYPE_ALIASES: bool = false;
540 const HAS_ELABORATED_INFORMATION: bool = false;
541
542 type Results = InferCtxt<'genv, 'tcx>;
543
544 fn genv(&self) -> GlobalEnv<'genv, 'tcx> {
545 self.infcx.genv
546 }
547
548 fn owner(&self) -> FluxOwnerId {
549 self.infcx.wfckresults.owner
550 }
551
552 fn next_sort_vid(&mut self) -> rty::SortVid {
553 self.infcx.next_sort_vid(Default::default())
554 }
555
556 fn next_type_vid(&mut self) -> rty::TyVid {
557 self.next_type_index = self.next_type_index.checked_add(1).unwrap();
558 rty::TyVid::from_u32(self.next_type_index - 1)
559 }
560
561 fn next_region_vid(&mut self) -> rty::RegionVid {
562 self.next_region_index = self.next_region_index.checked_add(1).unwrap();
563 rty::RegionVid::from_u32(self.next_region_index - 1)
564 }
565
566 fn next_const_vid(&mut self) -> rty::ConstVid {
567 self.next_const_index = self.next_const_index.checked_add(1).unwrap();
568 rty::ConstVid::from_u32(self.next_const_index - 1)
569 }
570
571 fn results(&self) -> &Self::Results {
572 self.infcx
573 }
574
575 fn insert_node_sort(&mut self, fhir_id: FhirId, sort: rty::Sort) {
576 self.infcx.insert_node_sort(fhir_id, sort);
577 }
578
579 fn insert_path_args(&mut self, fhir_id: FhirId, args: rty::GenericArgs) {
580 self.infcx.insert_path_args(fhir_id, args);
581 }
582
583 fn insert_alias_reft_sort(&mut self, fhir_id: FhirId, fsort: rty::FuncSort) {
584 self.infcx.insert_sort_for_alias_reft(fhir_id, fsort);
585 }
586}
587
588impl WfckResultsProvider for InferCtxt<'_, '_> {
602 fn bin_op_sort(&self, _: FhirId) -> rty::Sort {
603 rty::Sort::Err
604 }
605
606 fn coercions_for(&self, _: FhirId) -> &[rty::Coercion] {
607 &[]
608 }
609
610 fn field_proj(&self, _: FhirId) -> rty::FieldProj {
611 rty::FieldProj::Tuple { arity: 0, field: 0 }
612 }
613
614 fn record_ctor(&self, _: FhirId) -> RecordCtor {
615 RecordCtor::Struct(DefId { index: DefIndex::from_u32(0), krate: CrateNum::from_u32(0) })
616 }
617
618 fn param_sort(&self, param_id: fhir::ParamId) -> rty::Sort {
619 self.param_sort(param_id)
620 }
621
622 fn node_sort(&self, _: FhirId) -> rty::Sort {
623 rty::Sort::Err
624 }
625
626 fn node_sort_args(&self, _: FhirId) -> rty::List<rty::SortArg> {
627 rty::List::empty()
628 }
629}