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