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