flux_metadata/
lib.rs

1#![allow(incomplete_features)]
2#![feature(rustc_private, specialization, if_let_guard)]
3
4extern crate rustc_ast;
5extern crate rustc_data_structures;
6extern crate rustc_errors;
7extern crate rustc_hir;
8extern crate rustc_macros;
9extern crate rustc_metadata;
10extern crate rustc_middle;
11extern crate rustc_serialize;
12extern crate rustc_session;
13extern crate rustc_span;
14
15mod decoder;
16mod encoder;
17
18// Tags used for encoding Spans:
19const TAG_FULL_SPAN: u8 = 0;
20const TAG_PARTIAL_SPAN: u8 = 1;
21
22use std::{hash::Hash, path::PathBuf, rc::Rc};
23
24use decoder::decode_crate_metadata;
25use derive_where::derive_where;
26use flux_errors::FluxSession;
27use flux_macros::fluent_messages;
28use flux_middle::{
29    cstore::{CrateStore, OptResult},
30    def_id::{FluxDefId, FluxId},
31    fhir,
32    global_env::GlobalEnv,
33    queries::QueryResult,
34    rty,
35};
36use rustc_data_structures::{
37    fx::FxHashMap,
38    unord::{ExtendUnord, UnordMap},
39};
40use rustc_hir::{
41    def::{CtorKind, DefKind},
42    def_id::{LOCAL_CRATE, LocalDefId},
43};
44use rustc_macros::{Decodable, Encodable, TyDecodable, TyEncodable};
45use rustc_middle::ty::TyCtxt;
46use rustc_session::config::OutFileName;
47use rustc_span::{
48    SourceFile, Span, StableSourceFileId,
49    def_id::{CrateNum, DefId, DefIndex, StableCrateId},
50};
51
52pub use crate::encoder::encode_metadata;
53
54fluent_messages! { "../locales/en-US.ftl" }
55
56const METADATA_VERSION: u8 = 0;
57const METADATA_HEADER: &[u8] = &[b'f', b'l', b'u', b'x', 0, 0, 0, METADATA_VERSION];
58
59#[derive(Default)]
60pub struct CStore {
61    local_tables: UnordMap<CrateNum, Tables<DefIndex>>,
62    extern_tables: Tables<DefId>,
63}
64
65/// From CREUSOT: used to store the info about source files
66/// An `EncodedSourceFileId` is the same as a `StableSourceFileId` except that
67/// the source crate is represented as a [StableCrateId] instead of as a
68/// `CrateNum`. This way `EncodedSourceFileId` can be encoded and decoded
69/// without any additional context, i.e. with a simple `opaque::Decoder` (which
70/// is the only thing available when decoding the [Footer].
71#[derive(Encodable, Decodable, Clone, Debug)]
72struct EncodedSourceFileId {
73    stable_source_file_id: StableSourceFileId,
74    stable_crate_id: StableCrateId,
75}
76
77impl EncodedSourceFileId {
78    #[inline]
79    fn new(tcx: TyCtxt<'_>, file: &SourceFile) -> EncodedSourceFileId {
80        EncodedSourceFileId {
81            stable_source_file_id: file.stable_id,
82            stable_crate_id: tcx.stable_crate_id(file.cnum),
83        }
84    }
85}
86
87// Taken from CREUSOT; used to store the info about syntax contexts
88#[derive(Default, Decodable, Encodable)]
89pub struct Footer {
90    file_index_to_stable_id: FxHashMap<SourceFileIndex, EncodedSourceFileId>,
91    syntax_contexts: FxHashMap<u32, AbsoluteBytePos>,
92    expn_data: FxHashMap<(StableCrateId, u32), AbsoluteBytePos>,
93}
94#[derive(Encodable, Decodable, Eq, PartialEq, Hash, Clone, Copy, Debug)]
95struct SourceFileIndex(u32);
96
97#[derive(Encodable, Decodable, Clone, Copy)]
98pub struct AbsoluteBytePos(u64);
99
100impl AbsoluteBytePos {
101    fn new(pos: usize) -> AbsoluteBytePos {
102        AbsoluteBytePos(pos.try_into().unwrap())
103    }
104
105    fn to_usize(self) -> usize {
106        self.0 as usize
107    }
108}
109
110#[derive(Default, TyEncodable, TyDecodable)]
111pub struct CrateMetadata {
112    local_tables: Tables<DefIndex>,
113    extern_tables: Tables<DefId>,
114}
115
116/// Trait to deal with the fact that `assoc_refinmenents_of` and `assoc_refinements_def` use
117/// `FluxId<K>` as key;
118trait Key {
119    type KeyIndex;
120    fn crate_num(self) -> CrateNum;
121    fn to_index(self) -> Self::KeyIndex;
122    fn name(self, tcx: TyCtxt) -> String;
123}
124
125impl Key for DefId {
126    type KeyIndex = DefIndex;
127
128    fn crate_num(self) -> CrateNum {
129        self.krate
130    }
131
132    fn to_index(self) -> Self::KeyIndex {
133        self.index
134    }
135
136    fn name(self, tcx: TyCtxt) -> String {
137        tcx.def_path_str(self)
138    }
139}
140
141impl Key for FluxDefId {
142    type KeyIndex = FluxId<DefIndex>;
143
144    fn crate_num(self) -> CrateNum {
145        self.parent().krate
146    }
147
148    fn to_index(self) -> Self::KeyIndex {
149        self.index()
150    }
151
152    fn name(self, tcx: TyCtxt) -> String {
153        format!("{}::{}", tcx.def_path_str(self.parent()), self.name())
154    }
155}
156
157#[derive_where(Default)]
158#[derive(TyEncodable, TyDecodable)]
159pub struct Tables<K: Eq + Hash> {
160    generics_of: UnordMap<K, QueryResult<rty::Generics>>,
161    refinement_generics_of: UnordMap<K, QueryResult<rty::EarlyBinder<rty::RefinementGenerics>>>,
162    predicates_of: UnordMap<K, QueryResult<rty::EarlyBinder<rty::GenericPredicates>>>,
163    item_bounds: UnordMap<K, QueryResult<rty::EarlyBinder<rty::Clauses>>>,
164    assoc_refinements_of: UnordMap<K, QueryResult<rty::AssocRefinements>>,
165    assoc_refinements_def: UnordMap<FluxId<K>, QueryResult<rty::EarlyBinder<rty::Lambda>>>,
166    default_assoc_refinements_def:
167        UnordMap<FluxId<K>, QueryResult<Option<rty::EarlyBinder<rty::Lambda>>>>,
168    sort_of_assoc_reft: UnordMap<FluxId<K>, QueryResult<rty::EarlyBinder<rty::FuncSort>>>,
169    fn_sig: UnordMap<K, QueryResult<rty::EarlyBinder<rty::PolyFnSig>>>,
170    adt_def: UnordMap<K, QueryResult<rty::AdtDef>>,
171    constant_info: UnordMap<K, QueryResult<rty::ConstantInfo>>,
172    adt_sort_def: UnordMap<K, QueryResult<rty::AdtSortDef>>,
173    variants_of: UnordMap<K, QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>>>,
174    type_of: UnordMap<K, QueryResult<rty::EarlyBinder<rty::TyOrCtor>>>,
175    normalized_defns: Rc<rty::NormalizedDefns>,
176    func_sort: UnordMap<FluxId<K>, rty::PolyFuncSort>,
177    func_span: UnordMap<FluxId<K>, Span>,
178    sort_decl_param_count: UnordMap<FluxId<K>, usize>,
179}
180
181impl CStore {
182    pub fn load(tcx: TyCtxt, sess: &FluxSession) -> Self {
183        let mut cstore = CStore::default();
184        for crate_num in tcx.used_crates(()) {
185            let Some(path) = flux_metadata_extern_location(tcx, *crate_num) else { continue };
186            let Some(meta) = decode_crate_metadata(tcx, sess, path.as_path()) else { continue };
187            cstore.local_tables.insert(*crate_num, meta.local_tables);
188            cstore.merge_extern_tables(tcx, sess, meta.extern_tables);
189        }
190        cstore
191    }
192
193    fn merge_extern_tables(
194        &mut self,
195        tcx: TyCtxt,
196        sess: &FluxSession,
197        extern_tables: Tables<DefId>,
198    ) {
199        macro_rules! merge_extern_table {
200            ($self:expr, $tcx:expr, $table:ident, $extern_tables:expr) => {{
201                // This is technically observing the order because it has side effects, but it's ok
202                // because we emit a fatal error and abort the process
203                $extern_tables.$table.keys().map(|k| {
204                    if self.$table(*k).is_some() {
205                        sess.emit_fatal(errors::DuplicateSpec::new($tcx, *k));
206                    }
207                });
208                $self
209                    .extern_tables
210                    .$table
211                    .extend_unord(extern_tables.$table.into_items());
212            }};
213        }
214        merge_extern_table!(self, tcx, generics_of, extern_tables);
215        merge_extern_table!(self, tcx, refinement_generics_of, extern_tables);
216        merge_extern_table!(self, tcx, predicates_of, extern_tables);
217        merge_extern_table!(self, tcx, item_bounds, extern_tables);
218        merge_extern_table!(self, tcx, assoc_refinements_of, extern_tables);
219        merge_extern_table!(self, tcx, default_assoc_refinements_def, extern_tables);
220        merge_extern_table!(self, tcx, assoc_refinements_def, extern_tables);
221        merge_extern_table!(self, tcx, sort_of_assoc_reft, extern_tables);
222        merge_extern_table!(self, tcx, fn_sig, extern_tables);
223        merge_extern_table!(self, tcx, adt_def, extern_tables);
224        merge_extern_table!(self, tcx, adt_sort_def, extern_tables);
225        merge_extern_table!(self, tcx, variants_of, extern_tables);
226        merge_extern_table!(self, tcx, type_of, extern_tables);
227    }
228}
229
230macro_rules! get {
231    ($self:expr, $table:ident, $key:expr) => {{
232        let key = $key;
233        let this = $self;
234        if let Some(tables) = this.local_tables.get(&key.crate_num()) {
235            tables.$table.get(&key.to_index()).cloned()
236        } else {
237            this.extern_tables.$table.get(&key).cloned()
238        }
239    }};
240}
241
242impl CrateStore for CStore {
243    fn fn_sig(&self, def_id: DefId) -> OptResult<rty::EarlyBinder<rty::PolyFnSig>> {
244        get!(self, fn_sig, def_id)
245    }
246
247    fn adt_def(&self, def_id: DefId) -> OptResult<rty::AdtDef> {
248        get!(self, adt_def, def_id)
249    }
250
251    fn adt_sort_def(&self, def_id: DefId) -> OptResult<rty::AdtSortDef> {
252        get!(self, adt_sort_def, def_id)
253    }
254
255    fn variants_of(
256        &self,
257        def_id: DefId,
258    ) -> OptResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>> {
259        get!(self, variants_of, def_id)
260    }
261
262    fn type_of(&self, def_id: DefId) -> OptResult<rty::EarlyBinder<rty::TyOrCtor>> {
263        get!(self, type_of, def_id)
264    }
265
266    fn generics_of(&self, def_id: DefId) -> OptResult<rty::Generics> {
267        get!(self, generics_of, def_id)
268    }
269
270    fn refinement_generics_of(
271        &self,
272        def_id: DefId,
273    ) -> OptResult<rty::EarlyBinder<rty::RefinementGenerics>> {
274        get!(self, refinement_generics_of, def_id)
275    }
276
277    fn item_bounds(&self, def_id: DefId) -> OptResult<rty::EarlyBinder<rty::Clauses>> {
278        get!(self, item_bounds, def_id)
279    }
280
281    fn predicates_of(&self, def_id: DefId) -> OptResult<rty::EarlyBinder<rty::GenericPredicates>> {
282        get!(self, predicates_of, def_id)
283    }
284
285    fn assoc_refinements_of(&self, def_id: DefId) -> OptResult<rty::AssocRefinements> {
286        get!(self, assoc_refinements_of, def_id)
287    }
288
289    fn assoc_refinements_def(&self, key: FluxDefId) -> OptResult<rty::EarlyBinder<rty::Lambda>> {
290        get!(self, assoc_refinements_def, key)
291    }
292
293    fn default_assoc_refinements_def(
294        &self,
295        key: FluxDefId,
296    ) -> OptResult<Option<rty::EarlyBinder<rty::Lambda>>> {
297        get!(self, default_assoc_refinements_def, key)
298    }
299
300    fn sort_of_assoc_reft(&self, key: FluxDefId) -> OptResult<rty::EarlyBinder<rty::FuncSort>> {
301        get!(self, sort_of_assoc_reft, key)
302    }
303
304    fn constant_info(&self, key: DefId) -> OptResult<rty::ConstantInfo> {
305        get!(self, constant_info, key)
306    }
307
308    fn normalized_defns(&self, krate: CrateNum) -> std::rc::Rc<rty::NormalizedDefns> {
309        self.local_tables[&krate].normalized_defns.clone()
310    }
311
312    fn func_sort(&self, key: FluxDefId) -> Option<rty::PolyFuncSort> {
313        get!(self, func_sort, key)
314    }
315
316    fn func_span(&self, key: FluxDefId) -> Option<Span> {
317        get!(self, func_span, key)
318    }
319
320    fn sort_decl_param_count(&self, key: FluxDefId) -> Option<usize> {
321        get!(self, sort_decl_param_count, key)
322    }
323}
324
325impl CrateMetadata {
326    fn new(genv: GlobalEnv) -> Self {
327        let mut local_tables = Tables::default();
328        encode_def_ids(
329            genv,
330            genv.iter_local_def_id().map(LocalDefId::to_def_id),
331            &mut local_tables,
332            DefId::to_index,
333            FluxDefId::to_index,
334        );
335        encode_flux_defs(genv, &mut local_tables);
336
337        let mut extern_tables = Tables::default();
338        encode_def_ids(
339            genv,
340            genv.iter_extern_def_id(),
341            &mut extern_tables,
342            |def_id| def_id,
343            |flux_id| flux_id,
344        );
345
346        CrateMetadata { local_tables, extern_tables }
347    }
348}
349
350fn encode_flux_defs(genv: GlobalEnv, tables: &mut Tables<DefIndex>) {
351    tables.normalized_defns = genv.normalized_defns(LOCAL_CRATE);
352
353    for (def_id, item) in genv.fhir_iter_flux_items() {
354        match item {
355            fhir::FluxItem::Func(spec_func) => {
356                tables
357                    .func_sort
358                    .insert(def_id.local_def_index(), genv.func_sort(def_id));
359                tables
360                    .func_span
361                    .insert(def_id.local_def_index(), spec_func.ident_span);
362            }
363            fhir::FluxItem::SortDecl(_sort_decl) => {
364                tables
365                    .sort_decl_param_count
366                    .insert(def_id.local_def_index(), genv.sort_decl_param_count(def_id));
367            }
368            fhir::FluxItem::PrimOpProp(_) | fhir::FluxItem::Qualifier(_) => {}
369        }
370    }
371}
372
373fn encode_def_ids<K: Eq + Hash + Copy>(
374    genv: GlobalEnv,
375    def_ids: impl IntoIterator<Item = DefId>,
376    tables: &mut Tables<K>,
377    did_to_key: fn(DefId) -> K,
378    assoc_id_to_key: fn(FluxDefId) -> FluxId<K>,
379) {
380    for def_id in def_ids {
381        let def_kind = genv.def_kind(def_id);
382        let key = did_to_key(def_id);
383
384        match def_kind {
385            DefKind::Trait => {
386                tables.generics_of.insert(key, genv.generics_of(def_id));
387                tables.predicates_of.insert(key, genv.predicates_of(def_id));
388                tables
389                    .refinement_generics_of
390                    .insert(key, genv.refinement_generics_of(def_id));
391                let assocs = genv.assoc_refinements_of(def_id);
392                if let Ok(assocs) = &assocs {
393                    for assoc_reft in &assocs.items {
394                        let def_id = assoc_reft.def_id();
395                        let key = assoc_id_to_key(def_id);
396                        tables
397                            .default_assoc_refinements_def
398                            .insert(key, genv.default_assoc_refinement_body(def_id));
399                        tables
400                            .sort_of_assoc_reft
401                            .insert(key, genv.sort_of_assoc_reft(def_id));
402                    }
403                }
404                tables.assoc_refinements_of.insert(key, assocs);
405            }
406            DefKind::Impl { of_trait } => {
407                tables.generics_of.insert(key, genv.generics_of(def_id));
408                tables.predicates_of.insert(key, genv.predicates_of(def_id));
409                tables
410                    .refinement_generics_of
411                    .insert(key, genv.refinement_generics_of(def_id));
412
413                if of_trait {
414                    let assocs = genv.assoc_refinements_of(def_id);
415                    if let Ok(assocs) = &assocs {
416                        for assoc_reft in &assocs.items {
417                            let def_id = assoc_reft.def_id();
418                            let key = assoc_id_to_key(def_id);
419                            tables
420                                .assoc_refinements_def
421                                .insert(key, genv.assoc_refinement_body(def_id));
422                            tables
423                                .sort_of_assoc_reft
424                                .insert(key, genv.sort_of_assoc_reft(def_id));
425                        }
426                    }
427                    tables.assoc_refinements_of.insert(key, assocs);
428                }
429            }
430            DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) => {
431                tables.generics_of.insert(key, genv.generics_of(def_id));
432                tables.predicates_of.insert(key, genv.predicates_of(def_id));
433                tables
434                    .refinement_generics_of
435                    .insert(key, genv.refinement_generics_of(def_id));
436                tables.fn_sig.insert(key, genv.fn_sig(def_id));
437            }
438            DefKind::Enum | DefKind::Struct => {
439                tables.generics_of.insert(key, genv.generics_of(def_id));
440                tables.predicates_of.insert(key, genv.predicates_of(def_id));
441                tables
442                    .refinement_generics_of
443                    .insert(key, genv.refinement_generics_of(def_id));
444                tables.adt_def.insert(key, genv.adt_def(def_id));
445                tables
446                    .adt_sort_def
447                    .insert(key, genv.adt_sort_def_of(def_id));
448                tables.variants_of.insert(key, genv.variants_of(def_id));
449                tables.type_of.insert(key, genv.type_of(def_id));
450            }
451            DefKind::TyAlias => {
452                tables.generics_of.insert(key, genv.generics_of(def_id));
453                tables.predicates_of.insert(key, genv.predicates_of(def_id));
454                tables
455                    .refinement_generics_of
456                    .insert(key, genv.refinement_generics_of(def_id));
457                tables.type_of.insert(key, genv.type_of(def_id));
458            }
459            DefKind::OpaqueTy => {
460                tables.generics_of.insert(key, genv.generics_of(def_id));
461                tables.predicates_of.insert(key, genv.predicates_of(def_id));
462                tables.item_bounds.insert(key, genv.item_bounds(def_id));
463                tables
464                    .refinement_generics_of
465                    .insert(key, genv.refinement_generics_of(def_id));
466            }
467            _ => {}
468        }
469    }
470}
471
472pub fn filename_for_metadata(tcx: TyCtxt) -> OutFileName {
473    match rustc_session::output::filename_for_metadata(tcx.sess, tcx.output_filenames(())) {
474        OutFileName::Real(path) => OutFileName::Real(path.with_extension("fluxmeta")),
475        OutFileName::Stdout => OutFileName::Stdout,
476    }
477}
478
479fn flux_metadata_extern_location(tcx: TyCtxt, crate_num: CrateNum) -> Option<PathBuf> {
480    // Since we only save metadata when `--emit=metadata` is passed, we also only load flux metadata
481    // when the crate source is a `.rmeta` file.
482    tcx.used_crate_source(crate_num)
483        .rmeta
484        .as_ref()
485        .map(|(path, _)| path.with_extension("fluxmeta"))
486}
487
488// Tags for encoding Symbol's
489const SYMBOL_STR: u8 = 0;
490const SYMBOL_OFFSET: u8 = 1;
491const SYMBOL_PREDEFINED: u8 = 2;
492
493mod errors {
494    use flux_errors::E0999;
495    use flux_macros::Diagnostic;
496    use rustc_middle::ty::TyCtxt;
497
498    use crate::Key;
499
500    #[derive(Diagnostic)]
501    #[diag(metadata_duplicate_spec, code = E0999)]
502    pub(super) struct DuplicateSpec {
503        def_name: String,
504    }
505
506    impl DuplicateSpec {
507        pub(super) fn new(tcx: TyCtxt, key: impl Key) -> Self {
508            Self { def_name: key.name(tcx) }
509        }
510    }
511}