flux_fhir_analysis/
lib.rs

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