flux_metadata/
encoder.rs

1use std::collections::hash_map::Entry;
2
3use flux_middle::global_env::GlobalEnv;
4use rustc_data_structures::fx::FxHashMap;
5use rustc_hir::def_id::{DefId, LOCAL_CRATE};
6use rustc_metadata::errors::FailCreateFileEncoder;
7use rustc_middle::{
8    bug,
9    ty::{self, TyCtxt},
10};
11use rustc_serialize::{Encodable, Encoder, opaque};
12use rustc_session::config::CrateType;
13use rustc_span::{
14    ExpnId, FileName, SourceFile, Span, SpanEncoder, StableSourceFileId, Symbol, SyntaxContext,
15    def_id::{CrateNum, DefIndex},
16    hygiene::{ExpnIndex, HygieneEncodeContext},
17};
18use rustc_type_ir::TyEncoder;
19
20use crate::{CrateMetadata, METADATA_HEADER, SYMBOL_OFFSET, SYMBOL_PREINTERNED, SYMBOL_STR};
21
22struct EncodeContext<'a, 'tcx> {
23    tcx: TyCtxt<'tcx>,
24    opaque: opaque::FileEncoder,
25    type_shorthands: FxHashMap<ty::Ty<'tcx>, usize>,
26    predicate_shorthands: FxHashMap<ty::PredicateKind<'tcx>, usize>,
27    is_proc_macro: bool,
28    hygiene_ctxt: &'a HygieneEncodeContext,
29    symbol_table: FxHashMap<Symbol, usize>, // interpret_allocs: FxIndexSet<interpret::AllocId>,
30}
31
32pub fn encode_metadata(genv: GlobalEnv, path: &std::path::Path) {
33    let mut encoder = opaque::FileEncoder::new(path).unwrap_or_else(|err| {
34        genv.tcx()
35            .sess
36            .dcx()
37            .emit_fatal(FailCreateFileEncoder { err })
38    });
39
40    encoder.emit_raw_bytes(METADATA_HEADER);
41
42    let crate_root = CrateMetadata::new(genv);
43
44    let hygiene_ctxt = HygieneEncodeContext::default();
45    let mut ecx = EncodeContext {
46        tcx: genv.tcx(),
47        opaque: encoder,
48        type_shorthands: Default::default(),
49        predicate_shorthands: Default::default(),
50        is_proc_macro: genv.tcx().crate_types().contains(&CrateType::ProcMacro),
51        hygiene_ctxt: &hygiene_ctxt,
52        symbol_table: Default::default(),
53    };
54
55    crate_root.encode(&mut ecx);
56
57    ecx.opaque.finish().unwrap();
58}
59
60impl SpanEncoder for EncodeContext<'_, '_> {
61    fn encode_crate_num(&mut self, crate_num: CrateNum) {
62        if crate_num != LOCAL_CRATE && self.is_proc_macro {
63            bug!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate");
64        }
65        self.tcx.stable_crate_id(crate_num).encode(self);
66    }
67
68    fn encode_def_index(&mut self, def_index: DefIndex) {
69        self.emit_u32(def_index.as_u32());
70    }
71
72    fn encode_def_id(&mut self, def_id: DefId) {
73        def_id.krate.encode(self);
74        def_id.index.encode(self);
75    }
76
77    fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) {
78        rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self);
79    }
80
81    fn encode_expn_id(&mut self, expn_id: ExpnId) {
82        if expn_id.krate == LOCAL_CRATE {
83            // We will only write details for local expansions. Non-local expansions will fetch
84            // data from the corresponding crate's metadata.
85            // FIXME(#43047) FIXME(#74731) We may eventually want to avoid relying on external
86            // metadata from proc-macro crates.
87            self.hygiene_ctxt.schedule_expn_data_for_encoding(expn_id);
88        }
89        expn_id.krate.encode(self);
90        expn_id.local_id.encode(self);
91    }
92
93    // Code adapted from prusti
94    fn encode_span(&mut self, span: Span) {
95        let sm = self.tcx.sess.source_map();
96        for bp in [span.lo(), span.hi()] {
97            let sf = sm.lookup_source_file(bp);
98            let ssfi = stable_source_file_id_for_export(self.tcx, &sf);
99            ssfi.encode(self);
100            // Not sure if this is the most stable way to encode a BytePos. If it fails
101            // try finding a function in `SourceMap` or `SourceFile` instead. E.g. the
102            // `bytepos_to_file_charpos` fn which returns `CharPos` (though there is
103            // currently no fn mapping back to `BytePos` for decode)
104            (bp - sf.start_pos).encode(self);
105        }
106    }
107
108    fn encode_symbol(&mut self, symbol: Symbol) {
109        // if symbol preinterned, emit tag and symbol index
110        if symbol.is_preinterned() {
111            self.opaque.emit_u8(SYMBOL_PREINTERNED);
112            self.opaque.emit_u32(symbol.as_u32());
113        } else {
114            // otherwise write it as string or as offset to it
115            match self.symbol_table.entry(symbol) {
116                Entry::Vacant(o) => {
117                    self.opaque.emit_u8(SYMBOL_STR);
118                    let pos = self.opaque.position();
119                    o.insert(pos);
120                    self.emit_str(symbol.as_str());
121                }
122                Entry::Occupied(o) => {
123                    let x = *o.get();
124                    self.emit_u8(SYMBOL_OFFSET);
125                    self.emit_usize(x);
126                }
127            }
128        }
129    }
130}
131
132impl<'tcx> TyEncoder for EncodeContext<'_, 'tcx> {
133    const CLEAR_CROSS_CRATE: bool = true;
134
135    type I = TyCtxt<'tcx>;
136
137    fn position(&self) -> usize {
138        self.opaque.position()
139    }
140
141    fn type_shorthands(&mut self) -> &mut FxHashMap<ty::Ty<'tcx>, usize> {
142        &mut self.type_shorthands
143    }
144
145    fn predicate_shorthands(&mut self) -> &mut FxHashMap<ty::PredicateKind<'tcx>, usize> {
146        &mut self.predicate_shorthands
147    }
148
149    fn encode_alloc_id(&mut self, _alloc_id: &rustc_middle::mir::interpret::AllocId) {
150        bug!("Encoding `interpret::AllocId` is not supported");
151        // let (index, _) = self.interpret_allocs.insert_full(*alloc_id);
152        // index.encode(self);
153    }
154}
155
156impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnIndex {
157    fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
158        s.emit_u32(self.as_u32());
159    }
160}
161
162macro_rules! encoder_methods {
163    ($($name:ident($ty:ty);)*) => {
164        $(fn $name(&mut self, value: $ty) {
165            self.opaque.$name(value)
166        })*
167    }
168}
169
170impl Encoder for EncodeContext<'_, '_> {
171    encoder_methods! {
172        emit_usize(usize);
173        emit_u128(u128);
174        emit_u64(u64);
175        emit_u32(u32);
176        emit_u16(u16);
177        emit_u8(u8);
178
179        emit_isize(isize);
180        emit_i128(i128);
181        emit_i64(i64);
182        emit_i32(i32);
183        emit_i16(i16);
184        emit_i8(i8);
185
186        emit_bool(bool);
187        emit_char(char);
188        emit_str(&str);
189        emit_raw_bytes(&[u8]);
190    }
191}
192
193fn stable_source_file_id_for_export(tcx: TyCtxt, sf: &SourceFile) -> StableSourceFileId {
194    let working_directory = &tcx.sess.opts.working_dir;
195    let crate_stable_id = tcx.stable_crate_id(sf.cnum);
196    let mut filename = sf.name.clone();
197    if let FileName::Real(original_file_name) = filename {
198        let adapted_file_name = tcx
199            .sess
200            .source_map()
201            .path_mapping()
202            .to_embeddable_absolute_path(original_file_name.clone(), working_directory);
203
204        filename = FileName::Real(adapted_file_name);
205    }
206    StableSourceFileId::from_filename_for_export(&filename, crate_stable_id)
207}