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    static_info: UnordMap<K, QueryResult<rty::StaticInfo>>,
173    adt_sort_def: UnordMap<K, QueryResult<rty::AdtSortDef>>,
174    variants_of: UnordMap<K, QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>>>,
175    type_of: UnordMap<K, QueryResult<rty::EarlyBinder<rty::TyOrCtor>>>,
176    normalized_defns: Rc<rty::NormalizedDefns>,
177    func_sort: UnordMap<FluxId<K>, rty::PolyFuncSort>,
178    func_span: UnordMap<FluxId<K>, Span>,
179    sort_decl_param_count: UnordMap<FluxId<K>, usize>,
180    no_panic: UnordMap<K, bool>,
181}
182
183impl CStore {
184    pub fn load(tcx: TyCtxt, sess: &FluxSession) -> Self {
185        let mut cstore = CStore::default();
186        for crate_num in tcx.used_crates(()) {
187            let Some(path) = flux_metadata_extern_location(tcx, *crate_num) else { continue };
188            let Some(meta) = decode_crate_metadata(tcx, sess, path.as_path()) else { continue };
189            cstore.local_tables.insert(*crate_num, meta.local_tables);
190            cstore.merge_extern_tables(tcx, sess, meta.extern_tables);
191        }
192        cstore
193    }
194
195    fn merge_extern_tables(
196        &mut self,
197        tcx: TyCtxt,
198        sess: &FluxSession,
199        extern_tables: Tables<DefId>,
200    ) {
201        macro_rules! merge_extern_table {
202            ($self:expr, $tcx:expr, $table:ident, $extern_tables:expr) => {{
203                // This is technically observing the order because it has side effects, but it's ok
204                // because we emit a fatal error and abort the process
205                $extern_tables.$table.keys().map(|k| {
206                    if self.$table(*k).is_some() {
207                        sess.emit_fatal(errors::DuplicateSpec::new($tcx, *k));
208                    }
209                });
210                $self
211                    .extern_tables
212                    .$table
213                    .extend_unord(extern_tables.$table.into_items());
214            }};
215        }
216        merge_extern_table!(self, tcx, generics_of, extern_tables);
217        merge_extern_table!(self, tcx, refinement_generics_of, extern_tables);
218        merge_extern_table!(self, tcx, predicates_of, extern_tables);
219        merge_extern_table!(self, tcx, item_bounds, extern_tables);
220        merge_extern_table!(self, tcx, assoc_refinements_of, extern_tables);
221        merge_extern_table!(self, tcx, default_assoc_refinements_def, extern_tables);
222        merge_extern_table!(self, tcx, assoc_refinements_def, extern_tables);
223        merge_extern_table!(self, tcx, sort_of_assoc_reft, extern_tables);
224        merge_extern_table!(self, tcx, fn_sig, extern_tables);
225        merge_extern_table!(self, tcx, adt_def, extern_tables);
226        merge_extern_table!(self, tcx, adt_sort_def, extern_tables);
227        merge_extern_table!(self, tcx, variants_of, extern_tables);
228        merge_extern_table!(self, tcx, type_of, extern_tables);
229        merge_extern_table!(self, tcx, no_panic, extern_tables);
230        merge_extern_table!(self, tcx, static_info, extern_tables);
231    }
232}
233
234macro_rules! get {
235    ($self:expr, $table:ident, $key:expr) => {{
236        let key = $key;
237        let this = $self;
238        if let Some(tables) = this.local_tables.get(&key.crate_num()) {
239            tables.$table.get(&key.to_index()).cloned()
240        } else {
241            this.extern_tables.$table.get(&key).cloned()
242        }
243    }};
244}
245
246impl CrateStore for CStore {
247    fn fn_sig(&self, def_id: DefId) -> OptResult<rty::EarlyBinder<rty::PolyFnSig>> {
248        get!(self, fn_sig, def_id)
249    }
250
251    fn adt_def(&self, def_id: DefId) -> OptResult<rty::AdtDef> {
252        get!(self, adt_def, def_id)
253    }
254
255    fn adt_sort_def(&self, def_id: DefId) -> OptResult<rty::AdtSortDef> {
256        get!(self, adt_sort_def, def_id)
257    }
258
259    fn no_panic(&self, def_id: DefId) -> Option<bool> {
260        get!(self, no_panic, def_id)
261    }
262
263    fn variants_of(
264        &self,
265        def_id: DefId,
266    ) -> OptResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>> {
267        get!(self, variants_of, def_id)
268    }
269
270    fn type_of(&self, def_id: DefId) -> OptResult<rty::EarlyBinder<rty::TyOrCtor>> {
271        get!(self, type_of, def_id)
272    }
273
274    fn generics_of(&self, def_id: DefId) -> OptResult<rty::Generics> {
275        get!(self, generics_of, def_id)
276    }
277
278    fn refinement_generics_of(
279        &self,
280        def_id: DefId,
281    ) -> OptResult<rty::EarlyBinder<rty::RefinementGenerics>> {
282        get!(self, refinement_generics_of, def_id)
283    }
284
285    fn item_bounds(&self, def_id: DefId) -> OptResult<rty::EarlyBinder<rty::Clauses>> {
286        get!(self, item_bounds, def_id)
287    }
288
289    fn predicates_of(&self, def_id: DefId) -> OptResult<rty::EarlyBinder<rty::GenericPredicates>> {
290        get!(self, predicates_of, def_id)
291    }
292
293    fn assoc_refinements_of(&self, def_id: DefId) -> OptResult<rty::AssocRefinements> {
294        get!(self, assoc_refinements_of, def_id)
295    }
296
297    fn assoc_refinements_def(&self, key: FluxDefId) -> OptResult<rty::EarlyBinder<rty::Lambda>> {
298        get!(self, assoc_refinements_def, key)
299    }
300
301    fn default_assoc_refinements_def(
302        &self,
303        key: FluxDefId,
304    ) -> OptResult<Option<rty::EarlyBinder<rty::Lambda>>> {
305        get!(self, default_assoc_refinements_def, key)
306    }
307
308    fn sort_of_assoc_reft(&self, key: FluxDefId) -> OptResult<rty::EarlyBinder<rty::FuncSort>> {
309        get!(self, sort_of_assoc_reft, key)
310    }
311
312    fn constant_info(&self, key: DefId) -> OptResult<rty::ConstantInfo> {
313        get!(self, constant_info, key)
314    }
315
316    fn static_info(&self, key: DefId) -> OptResult<rty::StaticInfo> {
317        get!(self, static_info, key)
318    }
319
320    fn normalized_defns(&self, krate: CrateNum) -> std::rc::Rc<rty::NormalizedDefns> {
321        self.local_tables[&krate].normalized_defns.clone()
322    }
323
324    fn func_sort(&self, key: FluxDefId) -> Option<rty::PolyFuncSort> {
325        get!(self, func_sort, key)
326    }
327
328    fn func_span(&self, key: FluxDefId) -> Option<Span> {
329        get!(self, func_span, key)
330    }
331
332    fn sort_decl_param_count(&self, key: FluxDefId) -> Option<usize> {
333        get!(self, sort_decl_param_count, key)
334    }
335}
336
337impl CrateMetadata {
338    fn new(genv: GlobalEnv) -> Self {
339        let mut local_tables = Tables::default();
340        encode_def_ids(
341            genv,
342            genv.iter_local_def_id().map(LocalDefId::to_def_id),
343            &mut local_tables,
344            DefId::to_index,
345            FluxDefId::to_index,
346        );
347        encode_flux_defs(genv, &mut local_tables);
348
349        let mut extern_tables = Tables::default();
350        encode_def_ids(
351            genv,
352            genv.iter_extern_def_id(),
353            &mut extern_tables,
354            |def_id| def_id,
355            |flux_id| flux_id,
356        );
357
358        CrateMetadata { local_tables, extern_tables }
359    }
360}
361
362fn encode_flux_defs(genv: GlobalEnv, tables: &mut Tables<DefIndex>) {
363    tables.normalized_defns = genv.normalized_defns(LOCAL_CRATE);
364
365    for (def_id, item) in genv.fhir_iter_flux_items() {
366        match item {
367            fhir::FluxItem::Func(spec_func) => {
368                tables
369                    .func_sort
370                    .insert(def_id.local_def_index(), genv.func_sort(def_id));
371                tables
372                    .func_span
373                    .insert(def_id.local_def_index(), spec_func.ident_span);
374            }
375            fhir::FluxItem::SortDecl(_sort_decl) => {
376                tables
377                    .sort_decl_param_count
378                    .insert(def_id.local_def_index(), genv.sort_decl_param_count(def_id));
379            }
380            fhir::FluxItem::PrimOpProp(_) | fhir::FluxItem::Qualifier(_) => {}
381        }
382    }
383}
384
385fn encode_def_ids<K: Eq + Hash + Copy>(
386    genv: GlobalEnv,
387    def_ids: impl IntoIterator<Item = DefId>,
388    tables: &mut Tables<K>,
389    did_to_key: fn(DefId) -> K,
390    assoc_id_to_key: fn(FluxDefId) -> FluxId<K>,
391) {
392    for def_id in def_ids {
393        let def_kind = genv.def_kind(def_id);
394        let key = did_to_key(def_id);
395
396        match def_kind {
397            DefKind::Trait => {
398                tables
399                    .generics_of
400                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::generics_of));
401                tables
402                    .predicates_of
403                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::predicates_of));
404                tables.refinement_generics_of.insert(
405                    key,
406                    genv.run_query_if_reached(def_id, GlobalEnv::refinement_generics_of),
407                );
408                let assocs = genv.run_query_if_reached(def_id, GlobalEnv::assoc_refinements_of);
409                if let Ok(assocs) = &assocs {
410                    for assoc_reft in &assocs.items {
411                        let def_id = assoc_reft.def_id();
412                        let key = assoc_id_to_key(def_id);
413                        tables.default_assoc_refinements_def.insert(
414                            key,
415                            genv.run_query_if_reached(
416                                def_id,
417                                GlobalEnv::default_assoc_refinement_body,
418                            ),
419                        );
420                        tables.sort_of_assoc_reft.insert(
421                            key,
422                            genv.run_query_if_reached(def_id, GlobalEnv::sort_of_assoc_reft),
423                        );
424                    }
425                }
426                tables.assoc_refinements_of.insert(key, assocs);
427            }
428            DefKind::Impl { of_trait } => {
429                tables
430                    .generics_of
431                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::generics_of));
432                tables
433                    .predicates_of
434                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::predicates_of));
435                tables.refinement_generics_of.insert(
436                    key,
437                    genv.run_query_if_reached(def_id, GlobalEnv::refinement_generics_of),
438                );
439
440                if of_trait {
441                    let assocs = genv.run_query_if_reached(def_id, GlobalEnv::assoc_refinements_of);
442                    if let Ok(assocs) = &assocs {
443                        for assoc_reft in &assocs.items {
444                            let def_id = assoc_reft.def_id();
445                            let key = assoc_id_to_key(def_id);
446                            tables.assoc_refinements_def.insert(
447                                key,
448                                genv.run_query_if_reached(def_id, GlobalEnv::assoc_refinement_body),
449                            );
450                            tables.sort_of_assoc_reft.insert(
451                                key,
452                                genv.run_query_if_reached(def_id, GlobalEnv::sort_of_assoc_reft),
453                            );
454                        }
455                    }
456                    tables.assoc_refinements_of.insert(key, assocs);
457                }
458            }
459            DefKind::Fn | DefKind::AssocFn => {
460                tables
461                    .generics_of
462                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::generics_of));
463                tables
464                    .predicates_of
465                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::predicates_of));
466                tables.refinement_generics_of.insert(
467                    key,
468                    genv.run_query_if_reached(def_id, GlobalEnv::refinement_generics_of),
469                );
470                tables
471                    .fn_sig
472                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::fn_sig));
473                tables.no_panic.insert(key, genv.no_panic(def_id));
474            }
475            DefKind::Ctor(_, CtorKind::Fn) => {
476                tables
477                    .generics_of
478                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::generics_of));
479                tables.refinement_generics_of.insert(
480                    key,
481                    genv.run_query_if_reached(def_id, GlobalEnv::refinement_generics_of),
482                );
483                tables
484                    .fn_sig
485                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::fn_sig));
486            }
487            DefKind::Enum | DefKind::Struct => {
488                tables
489                    .generics_of
490                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::generics_of));
491                tables
492                    .predicates_of
493                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::predicates_of));
494                tables.refinement_generics_of.insert(
495                    key,
496                    genv.run_query_if_reached(def_id, GlobalEnv::refinement_generics_of),
497                );
498                tables
499                    .adt_def
500                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::adt_def));
501                tables
502                    .adt_sort_def
503                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::adt_sort_def_of));
504                tables
505                    .variants_of
506                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::variants_of));
507                tables
508                    .type_of
509                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::type_of));
510            }
511            DefKind::TyAlias => {
512                tables
513                    .generics_of
514                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::generics_of));
515                tables
516                    .predicates_of
517                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::predicates_of));
518                tables.refinement_generics_of.insert(
519                    key,
520                    genv.run_query_if_reached(def_id, GlobalEnv::refinement_generics_of),
521                );
522                tables
523                    .type_of
524                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::type_of));
525            }
526            DefKind::OpaqueTy => {
527                tables
528                    .generics_of
529                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::generics_of));
530                tables
531                    .predicates_of
532                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::predicates_of));
533                tables
534                    .item_bounds
535                    .insert(key, genv.run_query_if_reached(def_id, GlobalEnv::item_bounds));
536                tables.refinement_generics_of.insert(
537                    key,
538                    genv.run_query_if_reached(def_id, GlobalEnv::refinement_generics_of),
539                );
540            }
541            DefKind::Static { .. } => {
542                tables.static_info.insert(key, genv.static_info(def_id));
543            }
544            _ => {}
545        }
546    }
547}
548
549pub fn filename_for_metadata(tcx: TyCtxt) -> OutFileName {
550    match rustc_session::output::filename_for_metadata(tcx.sess, tcx.output_filenames(())) {
551        OutFileName::Real(path) => OutFileName::Real(path.with_extension("fluxmeta")),
552        OutFileName::Stdout => OutFileName::Stdout,
553    }
554}
555
556fn flux_metadata_extern_location(tcx: TyCtxt, crate_num: CrateNum) -> Option<PathBuf> {
557    // Since we only save metadata when `--emit=metadata` is passed, we also only load flux metadata
558    // when the crate source is a `.rmeta` file.
559    tcx.used_crate_source(crate_num)
560        .rmeta
561        .as_ref()
562        .map(|(path, _)| path.with_extension("fluxmeta"))
563}
564
565// Tags for encoding Symbol's
566const SYMBOL_STR: u8 = 0;
567const SYMBOL_OFFSET: u8 = 1;
568const SYMBOL_PREDEFINED: u8 = 2;
569
570mod errors {
571    use flux_errors::E0999;
572    use flux_macros::Diagnostic;
573    use rustc_middle::ty::TyCtxt;
574
575    use crate::Key;
576
577    #[derive(Diagnostic)]
578    #[diag(metadata_duplicate_spec, code = E0999)]
579    pub(super) struct DuplicateSpec {
580        def_name: String,
581    }
582
583    impl DuplicateSpec {
584        pub(super) fn new(tcx: TyCtxt, key: impl Key) -> Self {
585            Self { def_name: key.name(tcx) }
586        }
587    }
588}