flux_desugar/desugar/
lift.rs

1//! "Lift" HIR types into  FHIR types.
2
3use flux_common::{bug, iter::IterExt, result::ErrorEmitter as _};
4use flux_errors::ErrorGuaranteed;
5use flux_middle::{
6    def_id::MaybeExternId,
7    fhir::{self, FhirId, FluxOwnerId},
8    try_alloc_slice,
9};
10use rustc_hir::{
11    self as hir, FnHeader,
12    def_id::{DefId, LocalDefId},
13};
14use rustc_span::Span;
15
16use super::{DesugarCtxt, RustItemCtxt};
17
18type Result<T = ()> = std::result::Result<T, ErrorGuaranteed>;
19
20impl<'genv> RustItemCtxt<'_, 'genv, '_> {
21    pub fn lift_generics(&mut self) -> fhir::Generics<'genv> {
22        let generics = self.genv.tcx().hir_get_generics(self.local_id()).unwrap();
23        self.lift_generics_inner(generics)
24    }
25
26    pub fn lift_generic_param(&mut self, param: &hir::GenericParam) -> fhir::GenericParam<'genv> {
27        let kind = match param.kind {
28            hir::GenericParamKind::Lifetime { .. } => fhir::GenericParamKind::Lifetime,
29            hir::GenericParamKind::Type { default, .. } => {
30                fhir::GenericParamKind::Type { default: default.map(|ty| self.lift_ty(ty)) }
31            }
32            hir::GenericParamKind::Const { ty, .. } => {
33                let ty = self.lift_ty(ty);
34                fhir::GenericParamKind::Const { ty }
35            }
36        };
37        fhir::GenericParam {
38            def_id: self.genv.maybe_extern_id(param.def_id),
39            name: param.name,
40            kind,
41        }
42    }
43
44    fn lift_generics_inner(&mut self, generics: &hir::Generics) -> fhir::Generics<'genv> {
45        let params = self.genv.alloc_slice_fill_iter(
46            generics
47                .params
48                .iter()
49                .map(|param| self.lift_generic_param(param)),
50        );
51
52        fhir::Generics { params, refinement_params: &[], predicates: None }
53    }
54
55    fn lift_generic_bound(
56        &mut self,
57        bound: &hir::GenericBound,
58    ) -> Result<fhir::GenericBound<'genv>> {
59        match bound {
60            hir::GenericBound::Trait(poly_trait_ref) => {
61                Ok(fhir::GenericBound::Trait(self.lift_poly_trait_ref(*poly_trait_ref)?))
62            }
63            hir::GenericBound::Outlives(lft) => {
64                let lft = self.lift_lifetime(lft);
65                Ok(fhir::GenericBound::Outlives(lft))
66            }
67            _ => Err(self.emit_unsupported(&format!("unsupported generic bound: `{bound:?}`"))),
68        }
69    }
70
71    fn lift_poly_trait_ref(
72        &mut self,
73        poly_trait_ref: hir::PolyTraitRef,
74    ) -> Result<fhir::PolyTraitRef<'genv>> {
75        let modifiers = match poly_trait_ref.modifiers {
76            rustc_hir::TraitBoundModifiers {
77                constness: rustc_hir::BoundConstness::Never,
78                polarity: rustc_hir::BoundPolarity::Positive,
79            } => fhir::TraitBoundModifier::None,
80            rustc_hir::TraitBoundModifiers {
81                constness: rustc_hir::BoundConstness::Never,
82                polarity: rustc_hir::BoundPolarity::Maybe(_),
83            } => fhir::TraitBoundModifier::Maybe,
84            _ => {
85                return Err(self.emit_unsupported(&format!(
86                    "unsupported trait modifiers: `{:?}`",
87                    poly_trait_ref.modifiers,
88                )));
89            }
90        };
91        let bound_generic_params = self.genv.alloc_slice_fill_iter(
92            poly_trait_ref
93                .bound_generic_params
94                .iter()
95                .map(|param| self.lift_generic_param(param)),
96        );
97        let trait_ref = self.lift_path(poly_trait_ref.trait_ref.path)?;
98        Ok(fhir::PolyTraitRef {
99            bound_generic_params,
100            refine_params: &[],
101            modifiers,
102            trait_ref,
103            span: poly_trait_ref.span,
104        })
105    }
106
107    fn lift_opaque_ty(&mut self, opaque_ty: &hir::OpaqueTy) -> Result<fhir::OpaqueTy<'genv>> {
108        let bounds =
109            try_alloc_slice!(self.genv, &opaque_ty.bounds, |bound| self.lift_generic_bound(bound))?;
110
111        Ok(fhir::OpaqueTy { def_id: MaybeExternId::Local(opaque_ty.def_id), bounds })
112    }
113
114    pub fn lift_fn_header(&mut self) -> FnHeader {
115        let hir_id = self.genv.tcx().local_def_id_to_hir_id(self.local_id());
116        self.genv
117            .tcx()
118            .hir_fn_sig_by_hir_id(hir_id)
119            .expect("item does not have a `FnDecl`")
120            .header
121    }
122
123    pub fn lift_fn_decl(&mut self) -> fhir::FnDecl<'genv> {
124        let hir_id = self.genv.tcx().local_def_id_to_hir_id(self.local_id());
125        let fn_sig = self
126            .genv
127            .tcx()
128            .hir_fn_sig_by_hir_id(hir_id)
129            .expect("item does not have a `FnDecl`");
130
131        self.lift_fn_decl_inner(fn_sig.span, fn_sig.decl)
132    }
133
134    fn lift_fn_decl_inner(&mut self, span: Span, decl: &hir::FnDecl) -> fhir::FnDecl<'genv> {
135        let inputs = self
136            .genv
137            .alloc_slice_fill_iter(decl.inputs.iter().map(|ty| self.lift_ty(ty)));
138
139        let output =
140            fhir::FnOutput { params: &[], ensures: &[], ret: self.lift_fn_ret_ty(&decl.output) };
141
142        fhir::FnDecl { requires: &[], inputs, output, span, lifted: true }
143    }
144
145    fn lift_fn_ret_ty(&mut self, ret_ty: &hir::FnRetTy) -> fhir::Ty<'genv> {
146        match ret_ty {
147            hir::FnRetTy::DefaultReturn(_) => {
148                let kind = fhir::TyKind::Tuple(&[]);
149                fhir::Ty { kind, span: ret_ty.span() }
150            }
151            hir::FnRetTy::Return(ty) => self.lift_ty(ty),
152        }
153    }
154
155    pub fn lift_type_alias(&mut self) -> fhir::Item<'genv> {
156        let item = self.genv.tcx().hir_expect_item(self.local_id());
157        let hir::ItemKind::TyAlias(_, _, ty) = item.kind else {
158            bug!("expected type alias");
159        };
160
161        let generics = self.lift_generics();
162        let ty = self.lift_ty(ty);
163        let ty_alias =
164            self.genv
165                .alloc(fhir::TyAlias { index: None, ty, span: item.span, lifted: true });
166
167        fhir::Item { generics, kind: fhir::ItemKind::TyAlias(ty_alias), owner_id: self.owner }
168    }
169
170    pub fn lift_field_def(&mut self, field_def: &hir::FieldDef) -> fhir::FieldDef<'genv> {
171        let ty = self.lift_ty(field_def.ty);
172        fhir::FieldDef { ty, lifted: true }
173    }
174
175    pub fn lift_enum_variant(&mut self, variant: &hir::Variant) -> fhir::VariantDef<'genv> {
176        let item = self.genv.tcx().hir_expect_item(self.local_id());
177        let hir::ItemKind::Enum(_, generics, _) = &item.kind else { bug!("expected an enum") };
178
179        let fields = self.genv.alloc_slice_fill_iter(
180            variant
181                .data
182                .fields()
183                .iter()
184                .map(|field| self.lift_field_def(field)),
185        );
186
187        let ret = self.lift_variant_ret_inner(generics);
188
189        fhir::VariantDef {
190            def_id: variant.def_id,
191            params: &[],
192            fields,
193            ret,
194            span: variant.span,
195            lifted: true,
196        }
197    }
198
199    pub fn lift_variant_ret(&mut self) -> fhir::VariantRet<'genv> {
200        let item = self.genv.tcx().hir_expect_item(self.local_id());
201        let hir::ItemKind::Enum(_, generics, _) = &item.kind else { bug!("expected an enum") };
202        self.lift_variant_ret_inner(generics)
203    }
204
205    fn lift_variant_ret_inner(&mut self, generics: &hir::Generics) -> fhir::VariantRet<'genv> {
206        let kind = fhir::ExprKind::Record(&[]);
207        fhir::VariantRet {
208            enum_id: self.owner.resolved_id(),
209            idx: fhir::Expr {
210                kind,
211                fhir_id: self.next_fhir_id(),
212                span: generics.span.shrink_to_hi(),
213            },
214        }
215    }
216
217    pub fn lift_ty(&mut self, ty: &hir::Ty) -> fhir::Ty<'genv> {
218        let kind = match ty.kind {
219            hir::TyKind::Slice(ty) => {
220                let ty = self.lift_ty(ty);
221                let kind = fhir::BaseTyKind::Slice(self.genv.alloc(ty));
222                let bty = fhir::BaseTy { kind, fhir_id: self.next_fhir_id(), span: ty.span };
223                return fhir::Ty { kind: fhir::TyKind::BaseTy(bty), span: ty.span };
224            }
225            hir::TyKind::Array(ty, len) => {
226                let ty = self.lift_ty(ty);
227                fhir::TyKind::Array(self.genv.alloc(ty), self.lift_const_arg(len))
228            }
229            hir::TyKind::Ref(lft, mut_ty) => {
230                fhir::TyKind::Ref(self.lift_lifetime(lft), self.lift_mut_ty(mut_ty))
231            }
232            hir::TyKind::FnPtr(fn_ptr) => {
233                let bare_fn = self.lift_bare_fn(ty.span, fn_ptr);
234                fhir::TyKind::BareFn(self.genv.alloc(bare_fn))
235            }
236            hir::TyKind::Never => fhir::TyKind::Never,
237            hir::TyKind::Tup(tys) => {
238                let tys = self
239                    .genv
240                    .alloc_slice_fill_iter(tys.iter().map(|ty| self.lift_ty(ty)));
241                fhir::TyKind::Tuple(tys)
242            }
243            hir::TyKind::Path(qpath) => {
244                match self.lift_qpath(qpath) {
245                    Ok(qpath) => {
246                        let bty = fhir::BaseTy::from_qpath(qpath, self.next_fhir_id());
247                        fhir::TyKind::BaseTy(bty)
248                    }
249                    Err(err) => fhir::TyKind::Err(err),
250                }
251            }
252            hir::TyKind::Ptr(mut_ty) => {
253                let ty = self.lift_ty(mut_ty.ty);
254                fhir::TyKind::RawPtr(self.genv.alloc(ty), mut_ty.mutbl)
255            }
256            hir::TyKind::OpaqueDef(opaque_ty) => {
257                match self.lift_opaque_ty(opaque_ty) {
258                    Ok(opaque_ty) => {
259                        let opaque_ty = self.insert_opaque_ty(opaque_ty);
260                        fhir::TyKind::OpaqueDef(opaque_ty)
261                    }
262                    Err(err) => fhir::TyKind::Err(err),
263                }
264            }
265            hir::TyKind::TraitObject(poly_traits, lt) => {
266                let poly_traits = try_alloc_slice!(self.genv, poly_traits, |poly_trait| {
267                    if poly_trait.modifiers != hir::TraitBoundModifiers::NONE {
268                        return Err(self.emit_unsupported(&format!(
269                            "unsupported type: `{}`",
270                            rustc_hir_pretty::ty_to_string(&self.genv.tcx(), ty)
271                        )));
272                    }
273                    self.lift_poly_trait_ref(*poly_trait)
274                });
275                match poly_traits {
276                    Ok(poly_traits) => {
277                        let lft = self.lift_lifetime(lt.pointer());
278                        fhir::TyKind::TraitObject(poly_traits, lft, lt.tag())
279                    }
280                    Err(err) => fhir::TyKind::Err(err),
281                }
282            }
283            _ => {
284                fhir::TyKind::Err(self.emit_unsupported(&format!(
285                    "unsupported type: `{}`",
286                    rustc_hir_pretty::ty_to_string(&self.genv.tcx(), ty)
287                )))
288            }
289        };
290        fhir::Ty { kind, span: ty.span }
291    }
292
293    fn lift_bare_fn(&mut self, span: Span, fn_ptr: &hir::FnPtrTy) -> fhir::BareFnTy<'genv> {
294        let generic_params = self.genv.alloc_slice_fill_iter(
295            fn_ptr
296                .generic_params
297                .iter()
298                .map(|param| self.lift_generic_param(param)),
299        );
300        let decl = self.lift_fn_decl_inner(span, fn_ptr.decl);
301        fhir::BareFnTy {
302            safety: fn_ptr.safety,
303            abi: fn_ptr.abi,
304            generic_params,
305            decl: self.genv.alloc(decl),
306            param_idents: self.genv.alloc_slice(fn_ptr.param_idents),
307        }
308    }
309
310    fn lift_lifetime(&self, lft: &hir::Lifetime) -> fhir::Lifetime {
311        if let Some(resolved) = self.genv.tcx().named_bound_var(lft.hir_id) {
312            fhir::Lifetime::Resolved(resolved)
313        } else {
314            self.mk_lft_hole()
315        }
316    }
317
318    fn lift_mut_ty(&mut self, mut_ty: hir::MutTy) -> fhir::MutTy<'genv> {
319        let ty = self.lift_ty(mut_ty.ty);
320        fhir::MutTy { ty: self.genv.alloc(ty), mutbl: mut_ty.mutbl }
321    }
322
323    fn lift_qpath(&mut self, qpath: hir::QPath) -> Result<fhir::QPath<'genv>> {
324        match qpath {
325            hir::QPath::Resolved(qself, path) => {
326                let qself = if let Some(ty) = qself {
327                    let ty = self.lift_ty(ty);
328                    Some(self.genv.alloc(ty))
329                } else {
330                    None
331                };
332                let path = self.lift_path(path)?;
333                Ok(fhir::QPath::Resolved(qself, path))
334            }
335            hir::QPath::TypeRelative(qself, segment) => {
336                let qself = self.lift_ty(qself);
337                let segment = self.lift_path_segment(segment)?;
338                Ok(fhir::QPath::TypeRelative(self.genv.alloc(qself), self.genv.alloc(segment)))
339            }
340            hir::QPath::LangItem(_, _) => {
341                Err(self.emit_unsupported(&format!(
342                    "unsupported type: `{}`",
343                    rustc_hir_pretty::qpath_to_string(&self.genv.tcx(), &qpath)
344                )))
345            }
346        }
347    }
348
349    fn lift_path(&mut self, path: &hir::Path) -> Result<fhir::Path<'genv>> {
350        let Ok(res) = path.res.try_into() else {
351            return Err(self.emit_unsupported(&format!("unsupported res: `{:?}`", path.res)));
352        };
353        let segments =
354            try_alloc_slice!(self.genv, path.segments, |segment| self.lift_path_segment(segment))?;
355
356        Ok(fhir::Path {
357            res: self.fix_maybe_extern_id_in_res(res),
358            fhir_id: self.next_fhir_id(),
359            segments,
360            refine: &[],
361            span: path.span,
362        })
363    }
364
365    fn lift_path_segment(
366        &mut self,
367        segment: &hir::PathSegment,
368    ) -> Result<fhir::PathSegment<'genv>> {
369        let Ok(res) = segment.res.try_into() else {
370            return Err(self.emit_unsupported(&format!("unsupported res: `{:?}`", segment.res)));
371        };
372        let (args, bindings) = {
373            match segment.args {
374                Some(args) => {
375                    (
376                        self.lift_generic_args(args.args)?,
377                        self.lift_assoc_item_constraints(args.constraints)?,
378                    )
379                }
380                None => ([].as_slice(), [].as_slice()),
381            }
382        };
383
384        Ok(fhir::PathSegment { res, ident: segment.ident, args, constraints: bindings })
385    }
386
387    fn lift_generic_args(
388        &mut self,
389        args: &[hir::GenericArg<'_>],
390    ) -> Result<&'genv [fhir::GenericArg<'genv>]> {
391        try_alloc_slice!(self.genv, args, |arg| {
392            match arg {
393                hir::GenericArg::Lifetime(lft) => {
394                    let lft = self.lift_lifetime(lft);
395                    Ok(fhir::GenericArg::Lifetime(lft))
396                }
397                hir::GenericArg::Type(ty) => {
398                    let ty = self.lift_ty(ty.as_unambig_ty());
399                    Ok(fhir::GenericArg::Type(self.genv.alloc(ty)))
400                }
401                hir::GenericArg::Const(const_arg) => {
402                    Ok(fhir::GenericArg::Const(self.lift_const_arg(const_arg.as_unambig_ct())))
403                }
404                hir::GenericArg::Infer(_) => {
405                    Err(self.emit_unsupported("unsupported inference generic argument"))
406                }
407            }
408        })
409    }
410
411    fn lift_assoc_item_constraints(
412        &mut self,
413        constraints: &[hir::AssocItemConstraint<'_>],
414    ) -> Result<&'genv [fhir::AssocItemConstraint<'genv>]> {
415        try_alloc_slice!(self.genv, constraints, |cstr| {
416            let hir::AssocItemConstraintKind::Equality { term } = cstr.kind else {
417                return Err(self.emit_unsupported("unsupported type binding"));
418            };
419            let hir::Term::Ty(term) = term else {
420                return Err(self.emit_unsupported("unsupported type binding"));
421            };
422            let kind = fhir::AssocItemConstraintKind::Equality { term: self.lift_ty(term) };
423            Ok(fhir::AssocItemConstraint { ident: cstr.ident, kind })
424        })
425    }
426
427    fn lift_const_arg(&mut self, const_arg: &hir::ConstArg) -> fhir::ConstArg {
428        fhir::ConstArg { kind: fhir::ConstArgKind::Infer, span: const_arg.span() }
429    }
430
431    #[track_caller]
432    fn emit_unsupported(&self, note: &str) -> ErrorGuaranteed {
433        let tcx = self.genv.tcx();
434        let local_id = self.owner.local_id().def_id;
435        let span = tcx.def_span(local_id);
436        let def_kind = tcx.def_descr(local_id.to_def_id());
437        self.emit(errors::UnsupportedHir { span, def_kind, note })
438    }
439
440    fn next_fhir_id(&self) -> FhirId {
441        FhirId {
442            owner: FluxOwnerId::Rust(self.owner.local_id()),
443            local_id: self.local_id_gen.fresh(),
444        }
445    }
446
447    fn local_id(&self) -> LocalDefId {
448        self.owner.local_id().def_id
449    }
450
451    fn lift_fn_sig(&mut self, fn_sig: hir::FnSig) -> fhir::FnSig<'genv> {
452        let decl = self.lift_fn_decl_inner(fn_sig.span, fn_sig.decl);
453        fhir::FnSig {
454            header: fn_sig.header,
455            qualifiers: &[],
456            reveals: &[],
457            decl: self.genv.alloc(decl),
458        }
459    }
460
461    pub fn lift_foreign_item(
462        &mut self,
463        foreign_item: hir::ForeignItem,
464    ) -> Result<fhir::ForeignItem<'genv>> {
465        let hir::ForeignItemKind::Fn(fnsig, _, _) = foreign_item.kind else {
466            return Err(self.emit_unsupported("Static and type in extern_item are not supported."));
467        };
468
469        let lifted_fnsig = self.lift_fn_sig(fnsig);
470        let fnsig = self.genv.alloc(lifted_fnsig);
471        let lifted_generics = self.lift_generics();
472        let generics = self.genv.alloc(lifted_generics);
473        let kind = fhir::ForeignItemKind::Fn(*fnsig, generics);
474
475        Ok(fhir::ForeignItem {
476            ident: foreign_item.ident,
477            kind,
478            owner_id: MaybeExternId::Local(foreign_item.owner_id),
479            span: foreign_item.span,
480        })
481    }
482
483    /// Fixes the def ids inside `res` to point to resolved ids.
484    fn fix_maybe_extern_id_in_res(&self, res: fhir::Res) -> fhir::Res {
485        match res {
486            fhir::Res::SelfTyParam { trait_ } => {
487                fhir::Res::SelfTyParam { trait_: self.fix_maybe_extern_id(trait_) }
488            }
489            fhir::Res::SelfTyAlias { alias_to, is_trait_impl } => {
490                fhir::Res::SelfTyAlias {
491                    alias_to: self.fix_maybe_extern_id(alias_to),
492                    is_trait_impl,
493                }
494            }
495            fhir::Res::Def(kind, def_id) => fhir::Res::Def(kind, self.fix_maybe_extern_id(def_id)),
496            _ => res,
497        }
498    }
499
500    /// Fixes a [`DefId`] that may correspond to a dummy local item for an extern spec to be the
501    /// "resolved id". This is to upholad the invariant that a [`DefId`] always corresponds to
502    /// the resolved item.
503    #[allow(
504        clippy::disallowed_methods,
505        reason = "we are fixing the def_id to uphold the invariant on extern specs"
506    )]
507    fn fix_maybe_extern_id(&self, def_id: DefId) -> DefId {
508        if let Some(def_id) = def_id.as_local() {
509            self.genv.maybe_extern_id(def_id).resolved_id()
510        } else {
511            def_id
512        }
513    }
514}
515
516pub mod errors {
517    use flux_errors::E0999;
518    use flux_macros::Diagnostic;
519    use rustc_span::Span;
520
521    #[derive(Diagnostic)]
522    #[diag(desugar_unsupported_hir, code = E0999)]
523    #[note]
524    pub(super) struct UnsupportedHir<'a> {
525        #[primary_span]
526        #[label]
527        pub span: Span,
528        pub def_kind: &'static str,
529        pub note: &'a str,
530    }
531}