flux_desugar/
lib.rs

1//! Desugaring from types in [`flux_syntax::surface`] to types in [`flux_middle::fhir`]
2
3#![feature(rustc_private, min_specialization, box_patterns, never_type, unwrap_infallible)]
4
5extern crate rustc_data_structures;
6extern crate rustc_errors;
7
8extern crate rustc_hir;
9extern crate rustc_hir_pretty;
10extern crate rustc_middle;
11extern crate rustc_span;
12
13use desugar::RustItemCtxt;
14use flux_common::result::{ErrorCollector, ResultExt};
15use flux_macros::fluent_messages;
16use rustc_data_structures::unord::UnordMap;
17
18fluent_messages! { "../locales/en-US.ftl" }
19
20mod desugar;
21mod errors;
22pub mod resolver;
23
24use flux_middle::{
25    ResolverOutput, Specs,
26    def_id::FluxLocalDefId,
27    fhir,
28    global_env::GlobalEnv,
29    queries::{Providers, QueryErr, QueryResult},
30    query_bug,
31};
32use flux_syntax::surface;
33use rustc_errors::ErrorGuaranteed;
34use rustc_hir::{self as hir, OwnerId};
35use rustc_span::def_id::LocalDefId;
36
37type Result<T = ()> = std::result::Result<T, ErrorGuaranteed>;
38
39pub fn provide(providers: &mut Providers) {
40    providers.resolve_crate = resolver::resolve_crate;
41    providers.desugar = desugar;
42    providers.fhir_crate = desugar_crate;
43}
44
45pub fn desugar<'genv>(
46    genv: GlobalEnv<'genv, '_>,
47    def_id: LocalDefId,
48) -> QueryResult<UnordMap<LocalDefId, fhir::Node<'genv>>> {
49    if genv.ignored(def_id) {
50        return Err(QueryErr::Ignored { def_id: def_id.to_def_id() });
51    }
52
53    let cx = DesugarCtxt { genv, resolver_output: genv.resolve_crate() };
54    let specs = genv.collect_specs();
55    let owner_id = OwnerId { def_id };
56    let mut nodes = UnordMap::default();
57
58    match genv.tcx().hir_node_by_def_id(def_id) {
59        rustc_hir::Node::Item(item) => {
60            match item.kind {
61                hir::ItemKind::Fn { .. } => {
62                    let fn_spec = specs.fn_sigs.get(&owner_id).unwrap();
63                    let mut opaque_tys = Default::default();
64                    let item = cx.with_rust_item_ctxt(owner_id, Some(&mut opaque_tys), |cx| {
65                        cx.desugar_item_fn(fn_spec)
66                    })?;
67                    nodes.extend(opaque_tys.into_iter().map(|opaque_ty| {
68                        (opaque_ty.def_id.local_id(), fhir::Node::OpaqueTy(opaque_ty))
69                    }));
70                    nodes.insert(def_id, fhir::Node::Item(genv.alloc(item)));
71                }
72                hir::ItemKind::TyAlias(..) => {
73                    let ty_alias = specs.ty_aliases[&owner_id].as_ref();
74                    nodes.insert(
75                        def_id,
76                        fhir::Node::Item(genv.alloc(cx.with_rust_item_ctxt(
77                            owner_id,
78                            None,
79                            |cx| Ok(cx.desugar_type_alias(ty_alias)),
80                        )?)),
81                    );
82                }
83
84                hir::ItemKind::Enum(..) => {
85                    let enum_def = &specs.enums[&owner_id];
86                    nodes.insert(
87                        def_id,
88                        fhir::Node::Item(genv.alloc(cx.with_rust_item_ctxt(
89                            owner_id,
90                            None,
91                            |cx| cx.desugar_enum_def(enum_def),
92                        )?)),
93                    );
94                }
95                hir::ItemKind::Union(..) => {
96                    let union_def = &specs.structs[&owner_id];
97                    nodes.insert(
98                        def_id,
99                        fhir::Node::Item(genv.alloc(cx.with_rust_item_ctxt(
100                            owner_id,
101                            None,
102                            |cx| Ok(cx.desugar_struct_def(union_def)),
103                        )?)),
104                    );
105                }
106                hir::ItemKind::Struct(..) => {
107                    let struct_def = &specs.structs[&owner_id];
108                    nodes.insert(
109                        def_id,
110                        fhir::Node::Item(genv.alloc(cx.with_rust_item_ctxt(
111                            owner_id,
112                            None,
113                            |cx| Ok(cx.desugar_struct_def(struct_def)),
114                        )?)),
115                    );
116                }
117                hir::ItemKind::Trait(..) => {
118                    let trait_ = &specs.traits[&owner_id];
119                    nodes.insert(
120                        def_id,
121                        fhir::Node::Item(genv.alloc(cx.with_rust_item_ctxt(
122                            owner_id,
123                            None,
124                            |cx| cx.desugar_trait(trait_),
125                        )?)),
126                    );
127                }
128                hir::ItemKind::Impl(..) => {
129                    let impl_ = &specs.impls[&owner_id];
130                    nodes.insert(
131                        def_id,
132                        fhir::Node::Item(genv.alloc(cx.with_rust_item_ctxt(
133                            owner_id,
134                            None,
135                            |cx| Ok(cx.desugar_impl(impl_)),
136                        )?)),
137                    );
138                }
139                hir::ItemKind::Const(..) => {
140                    let constant_ = match specs.constants.get(&owner_id) {
141                        Some(constant_) => constant_,
142                        None => &surface::ConstantInfo { expr: None },
143                    };
144
145                    nodes.insert(
146                        def_id,
147                        fhir::Node::Item(genv.alloc(cx.with_rust_item_ctxt(
148                            owner_id,
149                            None,
150                            |cx| Ok(cx.desugar_const(constant_)),
151                        )?)),
152                    );
153                }
154                _ => Err(query_bug!(def_id, "unsupported item"))?,
155            }
156        }
157        rustc_hir::Node::TraitItem(trait_item) => {
158            match trait_item.kind {
159                rustc_hir::TraitItemKind::Fn(..) => {
160                    let fn_spec = specs.fn_sigs.get(&owner_id).unwrap();
161                    let mut opaque_tys = Default::default();
162                    let item = cx.with_rust_item_ctxt(owner_id, Some(&mut opaque_tys), |cx| {
163                        cx.desugar_trait_fn(fn_spec)
164                    })?;
165                    nodes.extend(opaque_tys.into_iter().map(|opaque_ty| {
166                        (opaque_ty.def_id.local_id(), fhir::Node::OpaqueTy(opaque_ty))
167                    }));
168                    nodes.insert(def_id, fhir::Node::TraitItem(genv.alloc(item)));
169                }
170                rustc_hir::TraitItemKind::Type(..) => {
171                    let item = cx.with_rust_item_ctxt(owner_id, None, |cx| {
172                        Ok(cx.desugar_trait_assoc_ty())
173                    })?;
174                    nodes.insert(owner_id.def_id, fhir::Node::TraitItem(genv.alloc(item)));
175                }
176                rustc_hir::TraitItemKind::Const(..) => {
177                    nodes.insert(
178                        def_id,
179                        fhir::Node::TraitItem(genv.alloc(cx.with_rust_item_ctxt(
180                            owner_id,
181                            None,
182                            |cx| Ok(cx.desugar_trait_const()),
183                        )?)),
184                    );
185                }
186            }
187        }
188        rustc_hir::Node::ImplItem(impl_item) => {
189            match &impl_item.kind {
190                rustc_hir::ImplItemKind::Fn(..) => {
191                    let fn_spec = specs.fn_sigs.get(&owner_id).unwrap();
192                    let mut opaque_tys = Default::default();
193                    let item = cx.with_rust_item_ctxt(owner_id, Some(&mut opaque_tys), |cx| {
194                        cx.desugar_impl_fn(fn_spec)
195                    })?;
196                    nodes.extend(opaque_tys.into_iter().map(|opaque_ty| {
197                        (opaque_ty.def_id.local_id(), fhir::Node::OpaqueTy(opaque_ty))
198                    }));
199                    nodes.insert(def_id, fhir::Node::ImplItem(genv.alloc(item)));
200                }
201                rustc_hir::ImplItemKind::Type(..) => {
202                    let item = cx
203                        .with_rust_item_ctxt(owner_id, None, |cx| Ok(cx.desugar_impl_assoc_ty()))?;
204                    nodes.insert(owner_id.def_id, fhir::Node::ImplItem(genv.alloc(item)));
205                }
206                rustc_hir::ImplItemKind::Const(..) => {
207                    nodes.insert(
208                        def_id,
209                        fhir::Node::ImplItem(genv.alloc(cx.with_rust_item_ctxt(
210                            owner_id,
211                            None,
212                            |cx| Ok(cx.desugar_impl_const()),
213                        )?)),
214                    );
215                }
216            }
217        }
218        rustc_hir::Node::AnonConst(..) => {
219            nodes.insert(def_id, fhir::Node::AnonConst);
220        }
221        rustc_hir::Node::Expr(..) => {
222            nodes.insert(def_id, fhir::Node::Expr);
223        }
224        rustc_hir::Node::ForeignItem(foreign) => {
225            let foreign_item = fhir::Node::ForeignItem(genv.alloc(cx.with_rust_item_ctxt(
226                owner_id,
227                None,
228                |cx| cx.desugar_foreign_item(*foreign),
229            )?));
230            nodes.insert(def_id, foreign_item);
231        }
232        rustc_hir::Node::Ctor(rustc_hir::VariantData::Tuple(_, _, _)) => {
233            nodes.insert(def_id, fhir::Node::Ctor);
234        }
235        node => {
236            if let Some(ident) = node.ident() {
237                Err(query_bug!(def_id, "unsupported item {ident:?}"))?;
238            } else {
239                Err(query_bug!(def_id, "unsupported item"))?;
240            }
241        }
242    }
243    Ok(nodes)
244}
245
246struct DesugarCtxt<'genv, 'tcx> {
247    genv: GlobalEnv<'genv, 'tcx>,
248    resolver_output: &'genv ResolverOutput,
249}
250
251impl<'genv, 'tcx> DesugarCtxt<'genv, 'tcx> {
252    fn with_rust_item_ctxt<'a, T>(
253        &'a self,
254        owner_id: OwnerId,
255        opaque_tys: Option<&'a mut Vec<&'genv fhir::OpaqueTy<'genv>>>,
256        f: impl FnOnce(&mut RustItemCtxt<'a, 'genv, 'tcx>) -> Result<T>,
257    ) -> Result<T> {
258        let owner_id = self
259            .genv
260            .maybe_extern_id(owner_id.def_id)
261            .map(|def_id| OwnerId { def_id });
262        RustItemCtxt::with(self.genv, owner_id, self.resolver_output, opaque_tys, f)
263    }
264}
265
266fn desugar_crate<'genv>(genv: GlobalEnv<'genv, '_>) -> fhir::FluxItems<'genv> {
267    match try_desugar_crate(genv) {
268        Ok(fhir) => fhir,
269        Err(err) => {
270            // There's too much code down the pipeline that relies on having the fhir, so we abort
271            // if there are any error during desugaring to avoid propagating the error back the query
272            // system. We should probably move away from desugaring the entire crate in one go and
273            // instead desugar items on demand so we can fail on a per item basis.
274            genv.sess().abort(err);
275        }
276    }
277}
278
279fn try_desugar_crate<'genv>(genv: GlobalEnv<'genv, '_>) -> Result<fhir::FluxItems<'genv>> {
280    let specs = genv.collect_specs();
281    let fhir = fhir::FluxItems::new();
282    let resolver_output = genv.resolve_crate();
283    let mut cx = CrateDesugar::new(genv, fhir, resolver_output);
284    cx.desugar_flux_items(specs);
285
286    cx.err.into_result()?;
287    Ok(cx.fhir)
288}
289
290struct CrateDesugar<'genv, 'tcx> {
291    genv: GlobalEnv<'genv, 'tcx>,
292    fhir: fhir::FluxItems<'genv>,
293    resolver_output: &'genv ResolverOutput,
294    err: Option<ErrorGuaranteed>,
295}
296
297impl<'genv, 'tcx> CrateDesugar<'genv, 'tcx> {
298    fn new(
299        genv: GlobalEnv<'genv, 'tcx>,
300        fhir: fhir::FluxItems<'genv>,
301        resolver_output: &'genv ResolverOutput,
302    ) -> Self {
303        Self { genv, fhir, resolver_output, err: None }
304    }
305}
306
307impl CrateDesugar<'_, '_> {
308    #[allow(clippy::disallowed_methods, reason = "`flux_items_by_parent` is the source of truth")]
309    fn desugar_flux_items(&mut self, specs: &Specs) {
310        for (parent, items) in &specs.flux_items_by_parent {
311            for item in items {
312                let def_id = FluxLocalDefId::new(parent.def_id, item.name().name);
313                match item {
314                    surface::Item::Qualifier(qual) => {
315                        self.desugar_qualifier(def_id, qual)
316                            .collect_err(&mut self.err);
317                    }
318                    surface::Item::FuncDef(defn) => {
319                        self.desugar_func_defn(def_id, defn)
320                            .collect_err(&mut self.err);
321                    }
322                    surface::Item::SortDecl(_) => {}
323                    surface::Item::PrimOpProp(primop_prop) => {
324                        self.desugar_primop_prop(def_id, primop_prop)
325                            .collect_err(&mut self.err);
326                    }
327                }
328            }
329        }
330    }
331
332    fn desugar_primop_prop(
333        &mut self,
334        def_id: FluxLocalDefId,
335        prop: &surface::PrimOpProp,
336    ) -> Result {
337        let prop = desugar::desugar_primop_prop(self.genv, self.resolver_output, def_id, prop)?;
338        self.fhir
339            .items
340            .insert(def_id, fhir::FluxItem::PrimOpProp(self.genv.alloc(prop)));
341        Ok(())
342    }
343
344    fn desugar_func_defn(&mut self, def_id: FluxLocalDefId, func: &surface::SpecFunc) -> Result {
345        let func = desugar::desugar_spec_func(self.genv, self.resolver_output, def_id, func)?;
346        self.fhir
347            .items
348            .insert(def_id, fhir::FluxItem::Func(self.genv.alloc(func)));
349        Ok(())
350    }
351
352    fn desugar_qualifier(
353        &mut self,
354        def_id: FluxLocalDefId,
355        qualifier: &surface::Qualifier,
356    ) -> Result {
357        let qualifier =
358            desugar::desugar_qualifier(self.genv, self.resolver_output, def_id, qualifier)?;
359        self.fhir
360            .items
361            .insert(def_id, fhir::FluxItem::Qualifier(self.genv.alloc(qualifier)));
362        Ok(())
363    }
364}