flux_desugar/
lib.rs

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