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 flux_syntax::surface;
17use itertools::Itertools as _;
18use rustc_data_structures::unord::UnordMap;
19
20fluent_messages! { "../locales/en-US.ftl" }
21
22mod desugar;
23mod errors;
24pub mod resolver;
25
26use flux_middle::{
27    ResolverOutput,
28    def_id::FluxLocalDefId,
29    fhir,
30    global_env::GlobalEnv,
31    queries::{Providers, QueryErr, QueryResult},
32    query_bug,
33};
34use rustc_errors::ErrorGuaranteed;
35use rustc_hir::OwnerId;
36use rustc_span::def_id::LocalDefId;
37
38use crate::desugar::FluxItemCtxt;
39
40type Result<T = ()> = std::result::Result<T, ErrorGuaranteed>;
41
42pub fn provide(providers: &mut Providers) {
43    providers.resolve_crate = resolver::resolve_crate;
44    providers.desugar = desugar;
45    providers.fhir_attr_map = fhir_attr_map;
46    providers.fhir_crate = desugar_crate;
47}
48
49pub fn desugar<'genv>(
50    genv: GlobalEnv<'genv, '_>,
51    def_id: LocalDefId,
52) -> QueryResult<UnordMap<LocalDefId, fhir::Node<'genv>>> {
53    if genv.ignored(def_id) {
54        return Err(QueryErr::Ignored { def_id: def_id.to_def_id() });
55    }
56
57    let cx = DesugarCtxt { genv, resolver_output: genv.resolve_crate() };
58    let specs = genv.collect_specs();
59    let owner_id = OwnerId { def_id };
60    let mut nodes = UnordMap::default();
61
62    let mut opaque_tys = Default::default();
63    let node = match genv.tcx().hir_node_by_def_id(def_id) {
64        rustc_hir::Node::Item(_) => {
65            let item = cx.with_rust_item_ctxt(owner_id, Some(&mut opaque_tys), |cx| {
66                match specs.get_item(owner_id) {
67                    Some(item) => cx.desugar_item(item),
68                    None => cx.lift_item(),
69                }
70            })?;
71            fhir::Node::Item(genv.alloc(item))
72        }
73        rustc_hir::Node::TraitItem(_) => {
74            let item = cx.with_rust_item_ctxt(owner_id, Some(&mut opaque_tys), |cx| {
75                match specs.get_trait_item(owner_id) {
76                    Some(item) => cx.desugar_trait_item(item),
77                    None => Ok(cx.lift_trait_item()),
78                }
79            })?;
80            fhir::Node::TraitItem(genv.alloc(item))
81        }
82        rustc_hir::Node::ImplItem(..) => {
83            let item = cx.with_rust_item_ctxt(owner_id, Some(&mut opaque_tys), |cx| {
84                match specs.get_impl_item(owner_id) {
85                    Some(item) => cx.desugar_impl_item(item),
86                    None => Ok(cx.lift_impl_item()),
87                }
88            })?;
89            fhir::Node::ImplItem(genv.alloc(item))
90        }
91        rustc_hir::Node::AnonConst(..) => fhir::Node::AnonConst,
92        rustc_hir::Node::Expr(..) => fhir::Node::Expr,
93        rustc_hir::Node::ForeignItem(foreign) => {
94            let item =
95                cx.with_rust_item_ctxt(owner_id, None, |cx| cx.lift_foreign_item(*foreign))?;
96            fhir::Node::ForeignItem(genv.alloc(item))
97        }
98        rustc_hir::Node::Ctor(rustc_hir::VariantData::Tuple(_, _, _)) => fhir::Node::Ctor,
99        // HACK: we shouldn't desugar opaque types separately from their parent items, but happens
100        // e.g. if you have an `impl Trait` in a function return type or `async fn` where the flux-spec does
101        // not mention the opaque type.
102        rustc_hir::Node::OpaqueTy(opaque_ty) => {
103            let item = cx.with_rust_item_ctxt(owner_id, None, |cx| cx.lift_opaque_ty(opaque_ty))?;
104            fhir::Node::OpaqueTy(genv.alloc(item))
105        }
106        _ => {
107            return Err(query_bug!(
108                def_id,
109                "unexpected {} found in desugaring",
110                genv.tcx().def_descr(def_id.to_def_id())
111            ));
112        }
113    };
114    nodes.insert(def_id, node);
115    nodes.extend(
116        opaque_tys
117            .into_iter()
118            .map(|opaque_ty| (opaque_ty.def_id.local_id(), fhir::Node::OpaqueTy(opaque_ty))),
119    );
120    Ok(nodes)
121}
122
123struct DesugarCtxt<'genv, 'tcx> {
124    genv: GlobalEnv<'genv, 'tcx>,
125    resolver_output: &'genv ResolverOutput,
126}
127
128impl<'genv, 'tcx> DesugarCtxt<'genv, 'tcx> {
129    fn with_rust_item_ctxt<'a, T>(
130        &'a self,
131        owner_id: OwnerId,
132        opaque_tys: Option<&'a mut Vec<&'genv fhir::OpaqueTy<'genv>>>,
133        f: impl FnOnce(&mut RustItemCtxt<'a, 'genv, 'tcx>) -> Result<T>,
134    ) -> Result<T> {
135        let owner_id = self
136            .genv
137            .maybe_extern_id(owner_id.def_id)
138            .map(|def_id| self.genv.tcx().local_def_id_to_hir_id(def_id).owner);
139        RustItemCtxt::with(self.genv, owner_id, self.resolver_output, opaque_tys, f)
140    }
141}
142
143fn desugar_crate<'genv>(genv: GlobalEnv<'genv, '_>) -> fhir::FluxItems<'genv> {
144    match try_desugar_crate(genv) {
145        Ok(fhir) => fhir,
146        Err(err) => {
147            // There's too much code down the pipeline that relies on having the fhir, so we abort
148            // if there are any error during desugaring to avoid propagating the error back the query
149            // system. We should probably move away from desugaring the entire crate in one go and
150            // instead desugar items on demand so we can fail on a per item basis.
151            genv.sess().abort(err);
152        }
153    }
154}
155
156#[allow(clippy::disallowed_methods, reason = "Ths is the source of truth for FluxDefId's")]
157fn try_desugar_crate<'genv>(genv: GlobalEnv<'genv, '_>) -> Result<fhir::FluxItems<'genv>> {
158    let specs = genv.collect_specs();
159    let resolver_output = genv.resolve_crate();
160
161    let mut fhir = fhir::FluxItems::new();
162    let mut err: Option<ErrorGuaranteed> = None;
163    for (parent, items) in &specs.flux_items_by_parent {
164        for item in items {
165            let def_id = FluxLocalDefId::new(parent.def_id, item.name().name);
166            FluxItemCtxt::with(genv, resolver_output, def_id, |cx| {
167                fhir.items.insert(def_id, cx.desugar_flux_item(item));
168            })
169            .collect_err(&mut err);
170        }
171    }
172    err.into_result()?;
173
174    Ok(fhir)
175}
176
177fn fhir_attr_map<'genv>(genv: GlobalEnv<'genv, '_>, def_id: LocalDefId) -> fhir::AttrMap<'genv> {
178    let owner_id = OwnerId { def_id };
179    let specs = genv.collect_specs();
180
181    let (node_id, attrs) = if let Some(item) = specs.get_item(owner_id) {
182        (item.node_id, &item.attrs)
183    } else if let Some(impl_item) = specs.get_impl_item(owner_id) {
184        (impl_item.node_id, &impl_item.attrs)
185    } else if let Some(trait_item) = specs.get_trait_item(owner_id) {
186        (trait_item.node_id, &trait_item.attrs)
187    } else {
188        return fhir::AttrMap::default();
189    };
190
191    let resolver_output = genv.resolve_crate();
192    fhir::AttrMap {
193        attrs: genv.alloc_slice_fill_iter(
194            attrs
195                .iter()
196                .filter_map(|attr| {
197                    match *attr {
198                        surface::Attr::Trusted(trusted) => Some(fhir::Attr::Trusted(trusted)),
199                        surface::Attr::TrustedImpl(trusted) => {
200                            Some(fhir::Attr::TrustedImpl(trusted))
201                        }
202                        surface::Attr::Ignore(ignored) => Some(fhir::Attr::Ignore(ignored)),
203                        surface::Attr::ProvenExternally(span) => {
204                            Some(fhir::Attr::ProvenExternally(span))
205                        }
206                        surface::Attr::ShouldFail => Some(fhir::Attr::ShouldFail),
207                        surface::Attr::InferOpts(opts) => Some(fhir::Attr::InferOpts(opts)),
208                        surface::Attr::NoPanic => Some(fhir::Attr::NoPanic),
209                        surface::Attr::Qualifiers(_) | surface::Attr::Reveal(_) => None,
210                    }
211                })
212                .collect_vec(),
213        ),
214        qualifiers: resolver_output
215            .qualifier_res_map
216            .get(&node_id)
217            .map_or(&[][..], Vec::as_slice),
218        reveals: resolver_output
219            .reveal_res_map
220            .get(&node_id)
221            .map_or(&[][..], Vec::as_slice),
222    }
223}