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