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, LocalDefId},
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 wf.check_hides_and_reveals(node.owner_id().local_id().def_id);
107 Ok(())
108 })?;
109
110 param_usage::check(&infcx, node)?;
111
112 infcx.into_results()
113}
114
115struct Wf<'a, 'genv, 'tcx> {
116 infcx: &'a mut InferCtxt<'genv, 'tcx>,
117 errors: Errors<'genv>,
118 next_type_index: u32,
119 next_region_index: u32,
120 next_const_index: u32,
121}
122
123impl<'a, 'genv, 'tcx> Wf<'a, 'genv, 'tcx> {
124 fn with(infcx: &'a mut InferCtxt<'genv, 'tcx>, f: impl FnOnce(&mut Self) -> Result) -> Result {
125 let errors = Errors::new(infcx.genv.sess());
126 let mut wf = Self {
127 infcx,
128 errors,
129 next_type_index: 1,
132 next_region_index: 0,
133 next_const_index: 0,
134 };
135 f(&mut wf)?;
136 wf.errors.into_result()
137 }
138
139 fn check_flux_item(&mut self, item: fhir::FluxItem<'genv>) {
140 self.visit_flux_item(&item);
141 }
142
143 fn check_node(&mut self, node: &fhir::OwnerNode<'genv>) {
144 self.visit_node(node);
145 }
146
147 fn check_hides_and_reveals(&mut self, def_id: LocalDefId) {
151 let genv = self.infcx.genv;
152 let attr_map = genv.fhir_attr_map(def_id);
153 for &(revealed, span) in attr_map.reveals {
154 match genv.spec_func(revealed) {
155 rty::SpecFunc::Uif => {
156 self.errors
157 .emit(errors::RevealUninterpretedFunction::new(span, revealed));
158 }
159 rty::SpecFunc::Defined { hide: false, .. } => {
160 genv.sess()
161 .dcx()
162 .emit_warn(errors::RevealNotHiddenDefinition::new(span, revealed));
163 }
164 rty::SpecFunc::Defined { hide: true, .. } => {}
165 }
166 }
167 for &(hidden, span) in attr_map.hides {
168 if let rty::SpecFunc::Uif = genv.spec_func(hidden) {
169 genv.sess()
170 .dcx()
171 .emit_warn(errors::HideUninterpretedFunction::new(span, hidden));
172 } else if genv.should_inline_fun(hidden) {
173 self.errors
174 .emit(errors::HideInlinedDefinition::new(span, hidden));
175 }
176 if attr_map
177 .reveals
178 .iter()
179 .any(|(revealed, _)| *revealed == hidden)
180 {
181 self.errors
182 .emit(errors::HideAndRevealDefinition::new(span, hidden));
183 }
184 }
185 }
186
187 fn check_expr(&mut self, expr: &fhir::Expr<'genv>, sort: &rty::Sort) {
188 self.infcx
189 .check_expr(expr, sort)
190 .collect_err(&mut self.errors);
191 }
192
193 fn declare_params_for_primop_prop(&mut self, primop_prop: &fhir::PrimOpProp<'genv>) -> Result {
196 let Some((sorts, _)) = prim_op_sort(&primop_prop.op) else {
197 return Err(self
198 .errors
199 .emit(errors::UnsupportedPrimOp::new(primop_prop.span, primop_prop.op)));
200 };
201 if primop_prop.args.len() != sorts.len() {
202 return Err(self.errors.emit(errors::ArgCountMismatch::new(
203 Some(primop_prop.span),
204 String::from("primop"),
205 sorts.len(),
206 primop_prop.args.len(),
207 )));
208 }
209 for (arg, sort) in primop_prop.args.iter().zip(sorts) {
210 self.infcx.declare_param(*arg, sort);
211 }
212 visit_refine_params(
213 |vis| vis.visit_expr(&primop_prop.body),
214 |param| self.declare_param(param),
215 )
216 }
217
218 fn declare_params_for_flux_item(&mut self, item: fhir::FluxItem<'genv>) -> Result {
220 if let fhir::FluxItem::PrimOpProp(primop_prop) = item {
221 self.declare_params_for_primop_prop(primop_prop)
222 } else {
223 visit_refine_params(|vis| vis.visit_flux_item(&item), |param| self.declare_param(param))
224 }
225 }
226
227 fn declare_params_for_node(&mut self, node: &fhir::OwnerNode<'genv>) -> Result {
229 visit_refine_params(|vis| vis.visit_node(node), |param| self.declare_param(param))
230 }
231
232 fn declare_params_for_invariants(
234 &mut self,
235 params: &[fhir::RefineParam<'genv>],
236 invariants: &[fhir::Expr<'genv>],
237 ) -> Result {
238 for param in params {
239 self.declare_param(param)?;
240 }
241 for expr in invariants {
242 self.declare_params_in_expr(expr)?;
243 }
244 Ok(())
245 }
246
247 fn declare_params_in_expr(&mut self, expr: &fhir::Expr<'genv>) -> Result {
248 visit_refine_params(|vis| vis.visit_expr(expr), |param| self.declare_param(param))
249 }
250
251 fn declare_param(&mut self, param: &fhir::RefineParam<'genv>) -> Result {
252 let sort = self
253 .as_conv_ctxt()
254 .conv_sort(¶m.sort)
255 .emit(&self.genv())?;
256 self.infcx.declare_param(*param, sort);
257 Ok(())
258 }
259
260 fn init_infcx_for_node(&mut self, node: &fhir::OwnerNode<'genv>) -> QueryResult {
277 let def_id = node.owner_id().map(|id| id.def_id);
278 self.declare_params_for_node(node)?;
279 let cx = self.as_conv_ctxt();
280 match node {
281 fhir::OwnerNode::Item(item) => {
282 match &item.kind {
283 fhir::ItemKind::Enum(enum_def) => {
284 cx.conv_enum_variants(def_id, enum_def)?;
285 cx.conv_generic_predicates(def_id, &item.generics)?;
286 }
287 fhir::ItemKind::Struct(struct_def) => {
288 cx.conv_struct_variant(def_id, struct_def)?;
289 cx.conv_generic_predicates(def_id, &item.generics)?;
290 }
291 fhir::ItemKind::TyAlias(ty_alias) => {
292 cx.conv_type_alias(def_id, ty_alias)?;
293 cx.conv_generic_predicates(def_id, &item.generics)?;
294 }
295 fhir::ItemKind::Trait(trait_) => {
296 for assoc_reft in trait_.assoc_refinements {
297 if let Some(body) = assoc_reft.body {
298 cx.conv_assoc_reft_body(
299 assoc_reft.params,
300 &body,
301 &assoc_reft.output,
302 )?;
303 }
304 }
305 cx.conv_generic_predicates(def_id, &item.generics)?;
306 }
307 fhir::ItemKind::Impl(impl_) => {
308 for assoc_reft in impl_.assoc_refinements {
309 cx.conv_assoc_reft_body(
310 assoc_reft.params,
311 &assoc_reft.body,
312 &assoc_reft.output,
313 )?;
314 }
315 cx.conv_generic_predicates(def_id, &item.generics)?;
316 }
317 fhir::ItemKind::Fn(fn_sig) => {
318 cx.conv_fn_sig(def_id, fn_sig)?;
319 cx.conv_generic_predicates(def_id, &item.generics)?;
320 }
321 fhir::ItemKind::Static(ty) => {
322 if let Some(ty) = ty {
323 cx.conv_static_ty(ty)?;
324 }
325 }
326 fhir::ItemKind::Const(_) => {}
327 }
328 }
329 fhir::OwnerNode::TraitItem(trait_item) => {
330 match trait_item.kind {
331 fhir::TraitItemKind::Fn(fn_sig) => {
332 cx.conv_fn_sig(def_id, &fn_sig)?;
333 cx.conv_generic_predicates(def_id, &trait_item.generics)?;
334 }
335 fhir::TraitItemKind::Type => {}
336 fhir::TraitItemKind::Const => {}
337 }
338 }
339 fhir::OwnerNode::ImplItem(impl_item) => {
340 match impl_item.kind {
341 fhir::ImplItemKind::Fn(fn_sig) => {
342 cx.conv_fn_sig(def_id, &fn_sig)?;
343 cx.conv_generic_predicates(def_id, &impl_item.generics)?;
344 }
345 fhir::ImplItemKind::Type => {}
346 fhir::ImplItemKind::Const => {}
347 }
348 }
349 fhir::OwnerNode::ForeignItem(impl_item) => {
350 match impl_item.kind {
351 fhir::ForeignItemKind::Fn(fn_sig, generics) => {
352 cx.conv_fn_sig(def_id, &fn_sig)?;
353 cx.conv_generic_predicates(def_id, generics)?;
354 }
355 fhir::ForeignItemKind::Static(_, _, _, _) => {
356 }
358 }
359 }
360 }
361 self.infcx.normalize_sorts()
362 }
363
364 fn init_infcx_for_flux_item(&mut self, item: fhir::FluxItem<'genv>) -> QueryResult {
365 self.declare_params_for_flux_item(item)?;
366 let cx = self.as_conv_ctxt();
367 match item {
368 fhir::FluxItem::Qualifier(qualifier) => {
369 cx.conv_qualifier(qualifier)?;
370 }
371 fhir::FluxItem::Func(spec_func) => {
372 cx.conv_spec_func(spec_func)?;
373 }
374 fhir::FluxItem::PrimOpProp(prim_op_prop) => {
375 cx.conv_primop_prop(prim_op_prop)?;
376 }
377 fhir::FluxItem::Axiom(axiom) => {
378 cx.conv_axiom(axiom)?;
379 }
380 fhir::FluxItem::SortDecl(_sort_decl) => {}
381 }
382 Ok(())
383 }
384
385 fn check_output_locs(&mut self, fn_decl: &fhir::FnDecl) {
386 let mut output_locs = UnordSet::new();
387 for ens in fn_decl.output.ensures {
388 if let fhir::Ensures::Type(loc, ..) = ens
389 && let (_, id) = loc.res.expect_param()
390 && !output_locs.insert(id)
391 {
392 self.errors.emit(errors::DuplicatedEnsures::new(loc));
393 }
394 }
395
396 for ty in fn_decl.inputs {
397 if let fhir::TyKind::StrgRef(loc, _) = ty.kind
398 && let (_, id) = loc.res.expect_param()
399 && !output_locs.contains(&id)
400 {
401 self.errors.emit(errors::MissingEnsures::new(loc));
402 }
403 }
404 }
405}
406
407impl<'genv> fhir::visit::Visitor<'genv> for Wf<'_, 'genv, '_> {
408 fn visit_qualifier(&mut self, qual: &fhir::Qualifier<'genv>) {
409 self.check_expr(&qual.expr, &rty::Sort::Bool);
410
411 let wildcards = std::iter::zip(qual.args, qual.wildcards)
413 .filter_map(|(param, &is_wildcard)| is_wildcard.then_some(param));
414 for param in wildcards {
415 let sort = self.infcx.param_sort(param.id);
416 if !matches!(
417 sort,
418 rty::Sort::Int | rty::Sort::Real | rty::Sort::Str | rty::Sort::BitVec(_)
419 ) {
420 self.errors
421 .emit(errors::InvalidWildcardSort::new(param.span, sort));
422 }
423 }
424 }
425
426 fn visit_primop_prop(&mut self, primop_prop: &fhir::PrimOpProp<'genv>) {
427 let Some((sorts, _)) = prim_op_sort(&primop_prop.op) else {
428 self.errors
429 .emit(errors::UnsupportedPrimOp::new(primop_prop.span, primop_prop.op));
430 return;
431 };
432
433 if primop_prop.args.len() != sorts.len() {
434 self.errors.emit(errors::ArgCountMismatch::new(
435 Some(primop_prop.span),
436 String::from("primop"),
437 sorts.len(),
438 primop_prop.args.len(),
439 ));
440 return;
441 }
442 self.check_expr(&primop_prop.body, &rty::Sort::Bool);
443 }
444
445 fn visit_axiom(&mut self, axiom: &fhir::Axiom<'genv>) {
446 self.check_expr(&axiom.body, &rty::Sort::Bool);
447 }
448
449 fn visit_func(&mut self, func: &fhir::SpecFunc<'genv>) {
450 if let Some(body) = &func.body {
451 let Ok(output) = self.as_conv_ctxt().conv_sort(&func.sort).emit(&self.errors) else {
452 return;
453 };
454 self.check_expr(body, &output);
455 }
456 }
457
458 fn visit_impl_assoc_reft(&mut self, assoc_reft: &fhir::ImplAssocReft<'genv>) {
459 let Ok(output) = self
460 .as_conv_ctxt()
461 .conv_sort(&assoc_reft.output)
462 .emit(&self.errors)
463 else {
464 return;
465 };
466 self.check_expr(&assoc_reft.body, &output);
467 }
468
469 fn visit_trait_assoc_reft(&mut self, assoc_reft: &fhir::TraitAssocReft<'genv>) {
470 if let Some(body) = &assoc_reft.body {
471 let Ok(output) = self
472 .as_conv_ctxt()
473 .conv_sort(&assoc_reft.output)
474 .emit(&self.errors)
475 else {
476 return;
477 };
478 self.check_expr(body, &output);
479 }
480 }
481
482 fn visit_variant_ret(&mut self, ret: &fhir::VariantRet<'genv>) {
483 let genv = self.infcx.genv;
484 let enum_id = ret.enum_id;
485 let Ok(adt_sort_def) = genv.adt_sort_def_of(enum_id).emit(&self.errors) else { return };
486 if adt_sort_def.is_reflected() {
487 return;
488 }
489 let Ok(args) = rty::GenericArg::identity_for_item(genv, enum_id).emit(&self.errors) else {
490 return;
491 };
492 let expected = adt_sort_def.to_sort(&args);
493 self.check_expr(&ret.idx, &expected);
494 }
495
496 fn visit_fn_sig(&mut self, sig: &fhir::FnSig<'genv>) {
497 fhir::visit::walk_fn_sig(self, sig);
498 if let Some(e) = sig.no_panic_if {
499 self.check_expr(&e, &rty::Sort::Bool);
500 }
501 }
502
503 fn visit_fn_decl(&mut self, decl: &fhir::FnDecl<'genv>) {
504 fhir::visit::walk_fn_decl(self, decl);
505 self.check_output_locs(decl);
506 }
507
508 fn visit_requires(&mut self, requires: &fhir::Requires<'genv>) {
509 self.check_expr(&requires.pred, &rty::Sort::Bool);
510 }
511
512 fn visit_ensures(&mut self, ensures: &fhir::Ensures<'genv>) {
513 match ensures {
514 fhir::Ensures::Type(loc, ty) => {
515 self.infcx.check_loc(loc).collect_err(&mut self.errors);
516 self.visit_ty(ty);
517 }
518 fhir::Ensures::Pred(pred) => {
519 self.check_expr(pred, &rty::Sort::Bool);
520 }
521 }
522 }
523
524 fn visit_ty(&mut self, ty: &fhir::Ty<'genv>) {
525 match &ty.kind {
526 fhir::TyKind::Indexed(bty, idx) => {
527 let expected = self.infcx.sort_of_bty(bty);
528 self.check_expr(idx, &expected);
529 self.visit_bty(bty);
530 }
531 fhir::TyKind::StrgRef(loc, ty) => {
532 self.infcx.check_loc(loc).collect_err(&mut self.errors);
533 self.visit_ty(ty);
534 }
535 fhir::TyKind::Constr(pred, ty) => {
536 self.visit_ty(ty);
537 self.check_expr(pred, &rty::Sort::Bool);
538 }
539 _ => fhir::visit::walk_ty(self, ty),
540 }
541 }
542
543 fn visit_path(&mut self, path: &fhir::Path<'genv>) {
544 let genv = self.genv();
545 if let fhir::Res::Def(DefKind::TyAlias, def_id) = path.res {
546 let Ok(generics) = genv.refinement_generics_of(def_id).emit(&self.errors) else {
547 return;
548 };
549
550 let args = self.infcx.path_args(path.fhir_id);
551 for (i, expr) in path.refine.iter().enumerate() {
552 let Ok(param) = generics.param_at(i, genv).emit(&self.errors) else { return };
553 let param = param.instantiate(genv.tcx(), &args, &[]);
554 self.check_expr(expr, ¶m.sort);
555 }
556 };
557 fhir::visit::walk_path(self, path);
558 }
559}
560
561struct RefineParamVisitor<F> {
562 f: F,
563 err: Option<ErrorGuaranteed>,
564}
565
566impl<'v, F> fhir::visit::Visitor<'v> for RefineParamVisitor<F>
567where
568 F: FnMut(&fhir::RefineParam<'v>) -> Result,
569{
570 fn visit_refine_param(&mut self, param: &fhir::RefineParam<'v>) {
571 (self.f)(param).collect_err(&mut self.err);
572 }
573}
574
575fn visit_refine_params<'a, F>(visit: impl FnOnce(&mut RefineParamVisitor<F>), f: F) -> Result
576where
577 F: FnMut(&fhir::RefineParam<'a>) -> Result,
578{
579 let mut visitor = RefineParamVisitor { f, err: None };
580 visit(&mut visitor);
581 visitor.err.into_result()
582}
583
584impl<'genv, 'tcx> ConvPhase<'genv, 'tcx> for Wf<'_, 'genv, 'tcx> {
585 const EXPAND_TYPE_ALIASES: bool = false;
588 const HAS_ELABORATED_INFORMATION: bool = false;
589
590 type Results = InferCtxt<'genv, 'tcx>;
591
592 fn genv(&self) -> GlobalEnv<'genv, 'tcx> {
593 self.infcx.genv
594 }
595
596 fn owner(&self) -> FluxOwnerId {
597 self.infcx.wfckresults.owner
598 }
599
600 fn next_sort_vid(&mut self) -> rty::SortVid {
601 self.infcx.next_sort_vid(Default::default())
602 }
603
604 fn next_type_vid(&mut self) -> rty::TyVid {
605 self.next_type_index = self.next_type_index.checked_add(1).unwrap();
606 rty::TyVid::from_u32(self.next_type_index - 1)
607 }
608
609 fn next_region_vid(&mut self) -> rty::RegionVid {
610 self.next_region_index = self.next_region_index.checked_add(1).unwrap();
611 rty::RegionVid::from_u32(self.next_region_index - 1)
612 }
613
614 fn next_const_vid(&mut self) -> rty::ConstVid {
615 self.next_const_index = self.next_const_index.checked_add(1).unwrap();
616 rty::ConstVid::from_u32(self.next_const_index - 1)
617 }
618
619 fn results(&self) -> &Self::Results {
620 self.infcx
621 }
622
623 fn insert_node_sort(&mut self, fhir_id: FhirId, sort: rty::Sort) {
624 self.infcx.insert_node_sort(fhir_id, sort);
625 }
626
627 fn insert_path_args(&mut self, fhir_id: FhirId, args: rty::GenericArgs) {
628 self.infcx.insert_path_args(fhir_id, args);
629 }
630
631 fn insert_alias_reft_sort(&mut self, fhir_id: FhirId, fsort: rty::FuncSort) {
632 self.infcx.insert_sort_for_alias_reft(fhir_id, fsort);
633 }
634}
635
636impl WfckResultsProvider for InferCtxt<'_, '_> {
650 fn bin_op_sort(&self, _: FhirId) -> rty::Sort {
651 rty::Sort::Err
652 }
653
654 fn coercions_for(&self, _: FhirId) -> &[rty::Coercion] {
655 &[]
656 }
657
658 fn field_proj(&self, _: FhirId) -> rty::FieldProj {
659 rty::FieldProj::Tuple { arity: 0, field: 0 }
660 }
661
662 fn record_ctor(&self, _: FhirId) -> RecordCtor {
663 RecordCtor::Struct(DefId { index: DefIndex::from_u32(0), krate: CrateNum::from_u32(0) })
664 }
665
666 fn param_sort(&self, param_id: fhir::ParamId) -> rty::Sort {
667 self.param_sort(param_id)
668 }
669
670 fn node_sort(&self, _: FhirId) -> rty::Sort {
671 rty::Sort::Err
672 }
673
674 fn node_sort_args(&self, _: FhirId) -> rty::List<rty::SortArg> {
675 rty::List::empty()
676 }
677}