flux_fhir_analysis/
lib.rs

1#![feature(rustc_private, let_chains, box_patterns, if_let_guard, once_cell_try, never_type)]
2
3extern crate rustc_abi;
4extern crate rustc_ast;
5extern crate rustc_data_structures;
6extern crate rustc_errors;
7extern crate rustc_hir;
8extern crate rustc_index;
9extern crate rustc_infer;
10extern crate rustc_middle;
11extern crate rustc_span;
12extern crate rustc_trait_selection;
13extern crate rustc_type_ir;
14
15mod conv;
16mod wf;
17use std::{iter, rc::Rc};
18
19use conv::{AfterSortck, ConvPhase, struct_compat};
20use flux_common::{bug, dbg, iter::IterExt, result::ResultExt};
21use flux_config as config;
22use flux_errors::Errors;
23use flux_macros::fluent_messages;
24use flux_middle::{
25    def_id::{FluxDefId, FluxId, FluxLocalDefId, MaybeExternId},
26    fhir::{
27        self, ForeignItem, ForeignItemKind, ImplItem, ImplItemKind, Item, ItemKind, TraitItem,
28        TraitItemKind,
29    },
30    global_env::GlobalEnv,
31    queries::{Providers, QueryResult},
32    query_bug,
33    rty::{
34        self, AssocReft, Binder, WfckResults,
35        fold::TypeFoldable,
36        refining::{self, Refiner},
37    },
38};
39use flux_rustc_bridge::lowering::Lower;
40use itertools::Itertools;
41use rustc_abi::FIRST_VARIANT;
42use rustc_data_structures::unord::UnordMap;
43use rustc_errors::ErrorGuaranteed;
44use rustc_hir::{
45    OwnerId,
46    def::{CtorOf, DefKind},
47    def_id::{DefId, LOCAL_CRATE, LocalDefId},
48};
49
50fluent_messages! { "../locales/en-US.ftl" }
51
52pub fn provide(providers: &mut Providers) {
53    providers.normalized_defns = normalized_defns;
54    providers.func_sort = func_sort;
55    providers.qualifiers = qualifiers;
56    providers.prim_rel = prim_rel;
57    providers.adt_sort_def_of = adt_sort_def_of;
58    providers.check_wf = check_wf;
59    providers.adt_def = adt_def;
60    providers.constant_info = constant_info;
61    providers.type_of = type_of;
62    providers.variants_of = variants_of;
63    providers.fn_sig = fn_sig;
64    providers.generics_of = generics_of;
65    providers.refinement_generics_of = refinement_generics_of;
66    providers.predicates_of = predicates_of;
67    providers.assoc_refinements_of = assoc_refinements_of;
68    providers.sort_of_assoc_reft = sort_of_assoc_reft;
69    providers.assoc_refinement_body = assoc_refinement_body;
70    providers.default_assoc_refinement_body = default_assoc_refinement_body;
71    providers.item_bounds = item_bounds;
72}
73
74fn adt_sort_def_of(genv: GlobalEnv, def_id: LocalDefId) -> QueryResult<rty::AdtSortDef> {
75    let def_id = genv.maybe_extern_id(def_id);
76    let kind = genv.map().refinement_kind(def_id.local_id())?;
77    conv::conv_adt_sort_def(genv, def_id, kind)
78}
79
80fn func_sort(genv: GlobalEnv, def_id: FluxLocalDefId) -> rty::PolyFuncSort {
81    let func = genv.map().spec_func(def_id).unwrap();
82    match conv::conv_func_decl(genv, func).emit(&genv) {
83        Ok(normalized) => normalized,
84        Err(err) => {
85            genv.sess().abort(err);
86        }
87    }
88}
89
90fn normalized_defns(genv: GlobalEnv) -> rty::NormalizedDefns {
91    match try_normalized_defns(genv) {
92        Ok(normalized) => normalized,
93        Err(err) => {
94            genv.sess().abort(err);
95        }
96    }
97}
98
99fn try_normalized_defns(genv: GlobalEnv) -> Result<rty::NormalizedDefns, ErrorGuaranteed> {
100    let mut defns = vec![];
101
102    // Collect and emit all errors
103    let mut errors = Errors::new(genv.sess());
104    for (_, item) in genv.map().flux_items() {
105        let fhir::FluxItem::Func(func) = item else { continue };
106        let Some(wfckresults) = wf::check_flux_item(genv, item).collect_err(&mut errors) else {
107            continue;
108        };
109        let Ok(defn) = conv::conv_defn(genv, func, &wfckresults).emit(&errors) else {
110            continue;
111        };
112
113        if let Some(defn) = defn {
114            defns.push((func.def_id, defn, func.hide));
115        }
116    }
117    errors.into_result()?;
118
119    let defns = rty::NormalizedDefns::new(genv, &defns)
120        .map_err(|cycle| {
121            let span = genv.map().spec_func(cycle[0]).unwrap().body.unwrap().span;
122            errors::DefinitionCycle::new(span, cycle)
123        })
124        .emit(&genv)?;
125
126    Ok(defns)
127}
128
129fn qualifiers(genv: GlobalEnv) -> QueryResult<Vec<rty::Qualifier>> {
130    genv.map()
131        .qualifiers()
132        .map(|qualifier| {
133            let wfckresults = wf::check_flux_item(genv, fhir::FluxItem::Qualifier(qualifier))?;
134            Ok(conv::conv_qualifier(genv, qualifier, &wfckresults)?.normalize(genv))
135        })
136        .try_collect()
137}
138
139fn prim_props(genv: GlobalEnv) -> QueryResult<Vec<rty::PrimProp>> {
140    genv.map()
141        .prim_props()
142        .map(|prim_prop| {
143            let wfckresults = wf::check_flux_item(genv, fhir::FluxItem::PrimProp(prim_prop))?;
144            Ok(conv::conv_prim_prop(genv, prim_prop, &wfckresults)?.normalize(genv))
145        })
146        .try_collect()
147}
148
149fn conjoin_bind_exprs(exprs: Vec<Binder<rty::Expr>>) -> Binder<rty::Expr> {
150    let mut iter = exprs.into_iter();
151    let first = iter.next().unwrap();
152    let sorts = first.sorts();
153    let bodies = iter::once(first.skip_binder()).chain(iter.map(|expr| expr.skip_binder()));
154    let expr = rty::Expr::and_from_iter(bodies);
155    Binder::bind_with_sorts(expr, &sorts)
156}
157
158fn prim_rel(genv: GlobalEnv) -> QueryResult<UnordMap<rty::BinOp, rty::PrimRel>> {
159    let prim_props = prim_props(genv)?
160        .into_iter()
161        .into_group_map_by(|prim_prop| prim_prop.op.clone());
162
163    let mut res = UnordMap::default();
164    for (op, props) in prim_props {
165        let exprs = props
166            .iter()
167            .map(|prop| prop.body.clone())
168            .collect::<Vec<_>>();
169        let body = conjoin_bind_exprs(exprs);
170        res.insert(op, rty::PrimRel { body });
171    }
172    Ok(res)
173}
174
175fn adt_def(genv: GlobalEnv, def_id: LocalDefId) -> QueryResult<rty::AdtDef> {
176    let def_id = genv.maybe_extern_id(def_id);
177    let item = genv.map().expect_item(def_id.local_id())?;
178    let invariants = invariants_of(genv, item)?;
179
180    let adt_def = genv.tcx().adt_def(def_id.resolved_id()).lower(genv.tcx());
181
182    let is_opaque = matches!(item.kind, fhir::ItemKind::Struct(def) if def.is_opaque());
183
184    Ok(rty::AdtDef::new(adt_def, genv.adt_sort_def_of(def_id)?, invariants, is_opaque))
185}
186
187fn constant_info(genv: GlobalEnv, local_def_id: LocalDefId) -> QueryResult<rty::ConstantInfo> {
188    let def_id = genv.maybe_extern_id(local_def_id);
189    let node = genv.map().node(def_id.local_id())?;
190    let owner = rustc_hir::OwnerId { def_id: local_def_id };
191    let Some(sort) = genv.sort_of_def_id(owner.def_id.to_def_id()).emit(&genv)? else {
192        return Ok(rty::ConstantInfo::Uninterpreted);
193    };
194    match node {
195        fhir::Node::Item(fhir::Item { kind: fhir::ItemKind::Const(Some(expr)), .. }) => {
196            // for these constants we must check and use the expression
197            let wfckresults = wf::check_constant_expr(genv, owner, expr, &sort)?;
198            conv::conv_constant_expr(genv, local_def_id.to_def_id(), expr, sort, &wfckresults)
199        }
200        fhir::Node::Item(fhir::Item { kind: fhir::ItemKind::Const(None), .. })
201        | fhir::Node::AnonConst
202        | fhir::Node::TraitItem(fhir::TraitItem { kind: fhir::TraitItemKind::Const, .. })
203        | fhir::Node::ImplItem(fhir::ImplItem { kind: fhir::ImplItemKind::Const, .. }) => {
204            // for these constants we try to evaluate if they are integral and return uninterpreted if we fail
205            conv::conv_constant(genv, local_def_id.to_def_id())
206        }
207        _ => Err(query_bug!(def_id.local_id(), "expected const item"))?,
208    }
209}
210
211fn invariants_of<'genv>(
212    genv: GlobalEnv<'genv, '_>,
213    item: &fhir::Item<'genv>,
214) -> QueryResult<Vec<rty::Invariant>> {
215    let (params, invariants) = match &item.kind {
216        fhir::ItemKind::Enum(enum_def) => (enum_def.params, enum_def.invariants),
217        fhir::ItemKind::Struct(struct_def) => (struct_def.params, struct_def.invariants),
218        _ => Err(query_bug!(item.owner_id.local_id(), "expected struct or enum"))?,
219    };
220    let wfckresults = wf::check_invariants(genv, item.owner_id, params, invariants)?;
221    AfterSortck::new(genv, &wfckresults)
222        .into_conv_ctxt()
223        .conv_invariants(item.owner_id.map(|it| it.def_id), params, invariants)
224}
225
226fn predicates_of(
227    genv: GlobalEnv,
228    def_id: LocalDefId,
229) -> QueryResult<rty::EarlyBinder<rty::GenericPredicates>> {
230    let def_id = genv.maybe_extern_id(def_id);
231    match genv.def_kind(def_id) {
232        DefKind::Impl { .. }
233        | DefKind::Struct
234        | DefKind::Enum
235        | DefKind::Union
236        | DefKind::TyAlias
237        | DefKind::AssocFn
238        | DefKind::AssocTy
239        | DefKind::Trait
240        | DefKind::Fn => {
241            let did = def_id.local_id();
242            let generics = genv
243                .map()
244                .get_generics(did)?
245                .ok_or_else(|| query_bug!(did, "no generics for {def_id:?}"))?;
246            let wfckresults = genv.check_wf(did)?;
247            AfterSortck::new(genv, &wfckresults)
248                .into_conv_ctxt()
249                .conv_generic_predicates(def_id, generics)
250        }
251        DefKind::OpaqueTy | DefKind::Closure => {
252            Ok(rty::EarlyBinder(rty::GenericPredicates {
253                parent: genv.tcx().predicates_of(def_id).parent,
254                predicates: rty::List::empty(),
255            }))
256        }
257        kind => {
258            Err(query_bug!(
259                def_id.local_id(),
260                "predicates_of called on `{def_id:?}` with kind `{kind:?}`"
261            ))?
262        }
263    }
264}
265
266fn assoc_refinements_of(
267    genv: GlobalEnv,
268    local_id: LocalDefId,
269) -> QueryResult<rty::AssocRefinements> {
270    let local_id = genv.maybe_extern_id(local_id);
271
272    #[allow(
273        clippy::disallowed_methods,
274        reason = "We are iterationg over associated refinemens in fhir, so this is the *source of of truth*"
275    )]
276    let predicates = match &genv.map().expect_item(local_id.local_id())?.kind {
277        fhir::ItemKind::Trait(trait_) => {
278            trait_
279                .assoc_refinements
280                .iter()
281                .map(|assoc_reft| {
282                    AssocReft::new(
283                        FluxDefId::new(local_id.resolved_id(), assoc_reft.name),
284                        assoc_reft.final_,
285                    )
286                })
287                .collect()
288        }
289        fhir::ItemKind::Impl(impl_) => {
290            impl_
291                .assoc_refinements
292                .iter()
293                .map(|assoc_reft| {
294                    AssocReft::new(FluxDefId::new(local_id.resolved_id(), assoc_reft.name), false)
295                })
296                .collect()
297        }
298        _ => Err(query_bug!(local_id.resolved_id(), "expected trait or impl"))?,
299    };
300    Ok(rty::AssocRefinements { items: predicates })
301}
302
303fn default_assoc_refinement_body(
304    genv: GlobalEnv,
305    trait_assoc_id: FluxId<MaybeExternId>,
306) -> QueryResult<Option<rty::EarlyBinder<rty::Lambda>>> {
307    let trait_id = trait_assoc_id.parent();
308    let assoc_reft = genv
309        .map()
310        .expect_item(trait_id.local_id())?
311        .expect_trait()
312        .find_assoc_reft(trait_assoc_id.name())
313        .unwrap();
314    let Some(body) = assoc_reft.body else { return Ok(None) };
315    let wfckresults = genv.check_wf(trait_id.local_id())?;
316    let mut cx = AfterSortck::new(genv, &wfckresults).into_conv_ctxt();
317    let body = cx.conv_assoc_reft_body(assoc_reft.params, &body, &assoc_reft.output)?;
318    Ok(Some(rty::EarlyBinder(body)))
319}
320
321fn assoc_refinement_body(
322    genv: GlobalEnv,
323    impl_assoc_id: FluxId<MaybeExternId>,
324) -> QueryResult<rty::EarlyBinder<rty::Lambda>> {
325    let impl_id = impl_assoc_id.parent();
326
327    let assoc_reft = genv
328        .map()
329        .expect_item(impl_id.local_id())?
330        .expect_impl()
331        .find_assoc_reft(impl_assoc_id.name())
332        .unwrap();
333
334    let wfckresults = genv.check_wf(impl_id.local_id())?;
335    let mut cx = AfterSortck::new(genv, &wfckresults).into_conv_ctxt();
336    let body = cx.conv_assoc_reft_body(assoc_reft.params, &assoc_reft.body, &assoc_reft.output)?;
337    Ok(rty::EarlyBinder(body))
338}
339
340fn sort_of_assoc_reft(
341    genv: GlobalEnv,
342    assoc_id: FluxId<MaybeExternId>,
343) -> QueryResult<rty::EarlyBinder<rty::FuncSort>> {
344    let container_id = assoc_id.parent();
345
346    match &genv.map().expect_item(container_id.local_id())?.kind {
347        fhir::ItemKind::Trait(trait_) => {
348            let assoc_reft = trait_.find_assoc_reft(assoc_id.name()).unwrap();
349            let wfckresults = WfckResults::new(OwnerId { def_id: container_id.local_id() });
350            let mut cx = AfterSortck::new(genv, &wfckresults).into_conv_ctxt();
351            let inputs = assoc_reft
352                .params
353                .iter()
354                .map(|p| cx.conv_sort(&p.sort))
355                .try_collect_vec()?;
356            let output = cx.conv_sort(&assoc_reft.output)?;
357            Ok(rty::EarlyBinder(rty::FuncSort::new(inputs, output)))
358        }
359        fhir::ItemKind::Impl(impl_) => {
360            let assoc_reft = impl_.find_assoc_reft(assoc_id.name()).unwrap();
361            let wfckresults = WfckResults::new(OwnerId { def_id: container_id.local_id() });
362            let mut cx = AfterSortck::new(genv, &wfckresults).into_conv_ctxt();
363            let inputs = assoc_reft
364                .params
365                .iter()
366                .map(|p| cx.conv_sort(&p.sort))
367                .try_collect_vec()?;
368            let output = cx.conv_sort(&assoc_reft.output)?;
369            Ok(rty::EarlyBinder(rty::FuncSort::new(inputs, output)))
370        }
371        _ => Err(query_bug!(container_id.local_id(), "expected trait or impl")),
372    }
373}
374
375fn item_bounds(genv: GlobalEnv, def_id: LocalDefId) -> QueryResult<rty::EarlyBinder<rty::Clauses>> {
376    let def_id = genv.maybe_extern_id(def_id);
377    let parent = genv.tcx().local_parent(def_id.local_id());
378    let wfckresults = genv.check_wf(parent)?;
379    let opaque_ty = genv.map().node(def_id.local_id())?.expect_opaque_ty();
380    Ok(rty::EarlyBinder(
381        AfterSortck::new(genv, &wfckresults)
382            .into_conv_ctxt()
383            .conv_opaque_ty(opaque_ty)?,
384    ))
385}
386
387fn generics_of(genv: GlobalEnv, def_id: LocalDefId) -> QueryResult<rty::Generics> {
388    let def_id = genv.maybe_extern_id(def_id);
389    let def_kind = genv.def_kind(def_id);
390    let generics = match def_kind {
391        DefKind::Impl { .. }
392        | DefKind::Struct
393        | DefKind::Enum
394        | DefKind::Union
395        | DefKind::TyAlias
396        | DefKind::AssocFn
397        | DefKind::AssocTy
398        | DefKind::Trait
399        | DefKind::Fn => {
400            let is_trait = def_kind == DefKind::Trait;
401            let generics = genv
402                .map()
403                .get_generics(def_id.local_id())?
404                .ok_or_else(|| query_bug!(def_id.local_id(), "no generics for {def_id:?}"))?;
405            conv::conv_generics(genv, generics, def_id, is_trait)
406        }
407        DefKind::OpaqueTy | DefKind::Closure | DefKind::TraitAlias | DefKind::Ctor(..) => {
408            let rustc_generics = genv.lower_generics_of(def_id);
409            refining::refine_generics(genv, def_id.resolved_id(), &rustc_generics)
410        }
411        kind => {
412            Err(query_bug!(
413                def_id.local_id(),
414                "generics_of called on `{def_id:?}` with kind `{kind:?}`"
415            ))?
416        }
417    };
418    if config::dump_rty() {
419        dbg::dump_item_info(genv.tcx(), def_id.resolved_id(), "generics.rty", &generics).unwrap();
420    }
421    Ok(generics)
422}
423
424fn refinement_generics_of(
425    genv: GlobalEnv,
426    local_id: LocalDefId,
427) -> QueryResult<rty::EarlyBinder<rty::RefinementGenerics>> {
428    let parent = genv.tcx().generics_of(local_id).parent;
429    let parent_count =
430        if let Some(def_id) = parent { genv.refinement_generics_of(def_id)?.count() } else { 0 };
431    let generics = match genv.map().node(local_id)? {
432        fhir::Node::Item(fhir::Item {
433            kind: fhir::ItemKind::Fn(..) | fhir::ItemKind::TyAlias(..),
434            generics,
435            ..
436        })
437        | fhir::Node::TraitItem(fhir::TraitItem {
438            kind: fhir::TraitItemKind::Fn(..),
439            generics,
440            ..
441        })
442        | fhir::Node::ImplItem(fhir::ImplItem {
443            kind: fhir::ImplItemKind::Fn(..), generics, ..
444        }) => {
445            let wfckresults = genv.check_wf(local_id)?;
446            let params = conv::conv_refinement_generics(generics.refinement_params, &wfckresults)?;
447            rty::RefinementGenerics { parent, parent_count, own_params: params }
448        }
449        _ => rty::RefinementGenerics { parent, parent_count, own_params: rty::List::empty() },
450    };
451    Ok(rty::EarlyBinder(generics))
452}
453
454fn type_of(genv: GlobalEnv, def_id: LocalDefId) -> QueryResult<rty::EarlyBinder<rty::TyOrCtor>> {
455    let def_id = genv.maybe_extern_id(def_id);
456    let ty = match genv.def_kind(def_id) {
457        DefKind::TyAlias => {
458            let fhir_ty_alias = genv
459                .map()
460                .expect_item(def_id.local_id())?
461                .expect_type_alias();
462            let wfckresults = genv.check_wf(def_id.local_id())?;
463            let mut cx = AfterSortck::new(genv, &wfckresults).into_conv_ctxt();
464            let ty_alias = cx.conv_type_alias(def_id, fhir_ty_alias)?;
465            struct_compat::type_alias(genv, fhir_ty_alias, &ty_alias, def_id)?;
466            rty::TyOrCtor::Ctor(ty_alias)
467        }
468        DefKind::TyParam => {
469            match def_id {
470                MaybeExternId::Local(local_id) => {
471                    let owner = genv.tcx().hir_ty_param_owner(local_id);
472                    let param = genv.map().get_generics(owner)?.unwrap().get_param(local_id);
473                    match param.kind {
474                        fhir::GenericParamKind::Type { default: Some(ty) } => {
475                            let parent = genv.tcx().local_parent(local_id);
476                            let wfckresults = genv.check_wf(parent)?;
477                            conv::conv_default_type_parameter(genv, def_id, &ty, &wfckresults)?
478                                .into()
479                        }
480                        k => Err(query_bug!(local_id, "non-type def def {k:?} {def_id:?}"))?,
481                    }
482                }
483                MaybeExternId::Extern(_, extern_id) => {
484                    let ty = genv.lower_type_of(extern_id)?.skip_binder();
485                    Refiner::default_for_item(genv, ty_param_owner(genv, extern_id))?
486                        .refine_ty_or_base(&ty)?
487                        .into()
488                }
489            }
490        }
491        DefKind::Impl { .. } | DefKind::Struct | DefKind::Enum | DefKind::AssocTy => {
492            let ty = genv.lower_type_of(def_id)?.skip_binder();
493            Refiner::default_for_item(genv, def_id.resolved_id())?
494                .refine_ty_or_base(&ty)?
495                .into()
496        }
497        kind => {
498            Err(query_bug!(
499                def_id.local_id(),
500                "`{:?}` not supported",
501                kind.descr(def_id.resolved_id())
502            ))?
503        }
504    };
505    Ok(rty::EarlyBinder(ty))
506}
507
508fn ty_param_owner(genv: GlobalEnv, def_id: DefId) -> DefId {
509    let def_kind = genv.def_kind(def_id);
510    match def_kind {
511        DefKind::Trait | DefKind::TraitAlias => def_id,
512        DefKind::LifetimeParam | DefKind::TyParam | DefKind::ConstParam => {
513            genv.tcx().parent(def_id)
514        }
515        _ => bug!("ty_param_owner: {:?} is a {:?} not a type parameter", def_id, def_kind),
516    }
517}
518
519fn variants_of(
520    genv: GlobalEnv,
521    def_id: LocalDefId,
522) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>> {
523    let def_id = genv.maybe_extern_id(def_id);
524    let local_id = def_id.local_id();
525
526    let item = &genv.map().expect_item(local_id)?;
527    let variants = match &item.kind {
528        fhir::ItemKind::Enum(enum_def) => {
529            let wfckresults = genv.check_wf(local_id)?;
530            let mut cx = AfterSortck::new(genv, &wfckresults).into_conv_ctxt();
531            let variants = cx.conv_enum_variants(def_id, enum_def)?;
532            let variants = rty::List::from_vec(struct_compat::variants(genv, &variants, def_id)?);
533            rty::Opaqueness::Transparent(rty::EarlyBinder(variants))
534        }
535        fhir::ItemKind::Struct(struct_def) => {
536            let wfckresults = genv.check_wf(local_id)?;
537            let mut cx = AfterSortck::new(genv, &wfckresults).into_conv_ctxt();
538            cx.conv_struct_variant(def_id, struct_def)?
539                .map(|variant| -> QueryResult<_> {
540                    let variants = struct_compat::variants(genv, &[variant], def_id)?;
541                    Ok(rty::List::from_vec(variants))
542                })
543                .transpose()?
544                .map(rty::EarlyBinder)
545        }
546        _ => Err(query_bug!(def_id.local_id(), "expected struct or enum"))?,
547    };
548    if config::dump_rty() {
549        dbg::dump_item_info(genv.tcx(), def_id.resolved_id(), "rty", &variants).unwrap();
550    }
551    Ok(variants)
552}
553
554fn fn_sig(genv: GlobalEnv, def_id: LocalDefId) -> QueryResult<rty::EarlyBinder<rty::PolyFnSig>> {
555    let def_id = genv.maybe_extern_id(def_id);
556    match genv.map().node(def_id.local_id())? {
557        fhir::Node::Item(Item { kind: ItemKind::Fn(fhir_fn_sig, ..), .. })
558        | fhir::Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fhir_fn_sig), .. })
559        | fhir::Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fhir_fn_sig), .. })
560        | fhir::Node::ForeignItem(ForeignItem {
561            kind: ForeignItemKind::Fn(fhir_fn_sig, ..), ..
562        }) => {
563            let wfckresults = genv.check_wf(def_id.local_id())?;
564            let fn_sig = AfterSortck::new(genv, &wfckresults)
565                .into_conv_ctxt()
566                .conv_fn_sig(def_id, fhir_fn_sig)?;
567            let fn_sig = struct_compat::fn_sig(genv, fhir_fn_sig.decl, &fn_sig, def_id)?;
568            let fn_sig = fn_sig.hoist_input_binders();
569
570            if config::dump_rty() {
571                let generics = genv.generics_of(def_id)?;
572                let refinement_generics = genv.refinement_generics_of(def_id)?;
573                dbg::dump_item_info(
574                    genv.tcx(),
575                    def_id.resolved_id(),
576                    "rty",
577                    (generics, refinement_generics, &fn_sig),
578                )
579                .unwrap();
580            }
581            Ok(rty::EarlyBinder(fn_sig))
582        }
583        fhir::Node::Ctor => {
584            let tcx = genv.tcx();
585            let (adt_id, variant_idx) = match tcx.def_kind(def_id) {
586                DefKind::Ctor(CtorOf::Struct, _) => {
587                    let struct_id = tcx.parent(def_id.resolved_id());
588                    (struct_id, FIRST_VARIANT)
589                }
590                DefKind::Ctor(CtorOf::Variant, _) => {
591                    let variant_id = tcx.parent(def_id.resolved_id());
592                    let enum_id = tcx.parent(variant_id);
593                    let variant_idx = tcx.adt_def(enum_id).variant_index_with_id(variant_id);
594                    (enum_id, variant_idx)
595                }
596                _ => return Err(query_bug!("invalid `DefKind` for ctor node")),
597            };
598            genv.variant_sig(adt_id, variant_idx)?
599                .map(|sig| sig.to_poly_fn_sig(None))
600                .ok_or_query_err(adt_id)
601        }
602        node => Err(query_bug!("fn_sig called on unsupported node {node:?}")),
603    }
604}
605
606fn check_wf(genv: GlobalEnv, def_id: LocalDefId) -> QueryResult<Rc<WfckResults>> {
607    let node = genv.map().expect_owner_node(def_id)?;
608    let wfckresults = wf::check_node(genv, &node)?;
609    Ok(Rc::new(wfckresults))
610}
611
612pub fn check_crate_wf(genv: GlobalEnv) -> Result<(), ErrorGuaranteed> {
613    let errors = Errors::new(genv.sess());
614    for def_id in genv.tcx().hir_crate_items(()).definitions() {
615        if genv.ignored(def_id) || genv.is_dummy(def_id) {
616            continue;
617        }
618        let def_kind = genv.def_kind(def_id);
619        match def_kind {
620            DefKind::TyAlias
621            | DefKind::Struct
622            | DefKind::Enum
623            | DefKind::Fn
624            | DefKind::AssocFn
625            | DefKind::Trait
626            | DefKind::Impl { .. }
627            // we skip checking the DefKind::OpaqueTy because they
628            // must be wf-checked (and desugared) in the context of
629            // their "parent", so we do so "lazily" when the appropriate
630            // query is called on the "parent"
631            => {
632                let _ = genv.check_wf(def_id).emit(&errors);
633            }
634            _ => {}
635        }
636    }
637
638    // Query qualifiers and spec funcs to report wf errors
639    let _ = genv.qualifiers().emit(&errors);
640    let _ = genv.normalized_defns(LOCAL_CRATE);
641
642    errors.into_result()
643}
644
645mod errors {
646    use flux_errors::E0999;
647    use flux_macros::Diagnostic;
648    use flux_middle::def_id::FluxLocalDefId;
649    use rustc_span::Span;
650
651    #[derive(Diagnostic)]
652    #[diag(fhir_analysis_definition_cycle, code = E0999)]
653    pub struct DefinitionCycle {
654        #[primary_span]
655        #[label]
656        span: Span,
657        msg: String,
658    }
659
660    impl DefinitionCycle {
661        pub(super) fn new(span: Span, cycle: Vec<FluxLocalDefId>) -> Self {
662            let root = format!("`{}`", cycle[0].name());
663            let names: Vec<String> = cycle.iter().map(|s| format!("`{}`", s.name())).collect();
664            let msg = format!("{} -> {}", names.join(" -> "), root);
665            Self { span, msg }
666        }
667    }
668}