flux_metadata/
encoder.rs

1use std::{collections::hash_map::Entry, sync::Arc};
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, codec::TyEncoder},
10};
11use rustc_serialize::{Encodable, Encoder, opaque, opaque::IntEncodedWithFixedSize};
12use rustc_session::config::CrateType;
13use rustc_span::{
14    ByteSymbol, ExpnId, SourceFile, Span, SpanEncoder, Symbol, SyntaxContext,
15    def_id::{CrateNum, DefIndex},
16    hygiene::{ExpnIndex, HygieneEncodeContext},
17};
18
19use crate::{
20    AbsoluteBytePos, CrateMetadata, EncodedSourceFileId, Footer, METADATA_HEADER, SYMBOL_OFFSET,
21    SYMBOL_PREDEFINED, SYMBOL_STR, SourceFileIndex, TAG_FULL_SPAN, TAG_PARTIAL_SPAN,
22    rustc_middle::dep_graph::DepContext,
23};
24
25struct EncodeContext<'a, 'tcx> {
26    tcx: TyCtxt<'tcx>,
27    opaque: opaque::FileEncoder,
28    type_shorthands: FxHashMap<ty::Ty<'tcx>, usize>,
29    predicate_shorthands: FxHashMap<ty::PredicateKind<'tcx>, usize>,
30    file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>,
31    is_proc_macro: bool,
32    hygiene_ctxt: &'a HygieneEncodeContext,
33    symbol_index_table: FxHashMap<u32, usize>,
34}
35
36impl EncodeContext<'_, '_> {
37    fn source_file_index(&mut self, source_file: Arc<SourceFile>) -> SourceFileIndex {
38        self.file_to_file_index[&(&*source_file as *const SourceFile)]
39    }
40
41    fn encode_symbol_or_byte_symbol(
42        &mut self,
43        index: u32,
44        emit_str_or_byte_str: impl Fn(&mut Self),
45    ) {
46        // if symbol/byte symbol is predefined, emit tag and symbol index
47        // TODO: we could also encode flux predefined symbols like this
48        if Symbol::is_predefined(index) {
49            self.opaque.emit_u8(SYMBOL_PREDEFINED);
50            self.opaque.emit_u32(index);
51        } else {
52            // otherwise write it as string or as offset to it
53            match self.symbol_index_table.entry(index) {
54                Entry::Vacant(o) => {
55                    self.opaque.emit_u8(SYMBOL_STR);
56                    let pos = self.opaque.position();
57                    o.insert(pos);
58                    emit_str_or_byte_str(self);
59                }
60                Entry::Occupied(o) => {
61                    let x = *o.get();
62                    self.emit_u8(SYMBOL_OFFSET);
63                    self.emit_usize(x);
64                }
65            }
66        }
67    }
68}
69
70fn file_indices(
71    tcx: TyCtxt,
72) -> (FxHashMap<*const SourceFile, SourceFileIndex>, FxHashMap<SourceFileIndex, EncodedSourceFileId>)
73{
74    let files = tcx.sess.source_map().files();
75    let mut file_to_file_index =
76        FxHashMap::with_capacity_and_hasher(files.len(), Default::default());
77    let mut file_index_to_stable_id =
78        FxHashMap::with_capacity_and_hasher(files.len(), Default::default());
79    use rustc_span::def_id::LOCAL_CRATE;
80    let source_map = tcx.sess.source_map();
81    let working_directory = &tcx.sess.opts.working_dir;
82    let local_crate_stable_id = tcx.stable_crate_id(LOCAL_CRATE);
83
84    // This portion of the code is adapted from the rustc metadata encoder, while the rest of
85    // the code in this file is based off the rustc incremental cache encoder.
86    //
87    // Probably we should refactor the code to be exclusively based on the metadata encoder
88    for (index, file) in files.iter().enumerate() {
89        let index = SourceFileIndex(index as u32);
90        let file_ptr: *const SourceFile = &**file as *const _;
91        file_to_file_index.insert(file_ptr, index);
92
93        let mut adapted_source_file = (**file).clone();
94        if adapted_source_file.cnum == LOCAL_CRATE {
95            use rustc_span::FileName;
96            match file.name {
97                FileName::Real(ref original_file_name) => {
98                    let adapted_file_name = source_map
99                        .path_mapping()
100                        .to_embeddable_absolute_path(original_file_name.clone(), working_directory);
101
102                    adapted_source_file.name = FileName::Real(adapted_file_name);
103                }
104                _ => {
105                    // expanded code, not from a file
106                }
107            };
108            use rustc_span::StableSourceFileId;
109            adapted_source_file.stable_id = StableSourceFileId::from_filename_for_export(
110                &adapted_source_file.name,
111                local_crate_stable_id,
112            );
113        }
114
115        let source_file_id = EncodedSourceFileId::new(tcx, &adapted_source_file);
116        file_index_to_stable_id.insert(index, source_file_id);
117    }
118
119    (file_to_file_index, file_index_to_stable_id)
120}
121
122pub fn encode_metadata(genv: GlobalEnv, path: &std::path::Path) {
123    let (file_to_file_index, file_index_to_stable_id) = file_indices(genv.tcx());
124
125    let mut encoder = opaque::FileEncoder::new(path).unwrap_or_else(|err| {
126        genv.tcx()
127            .sess
128            .dcx()
129            .emit_fatal(FailCreateFileEncoder { err })
130    });
131
132    encoder.emit_raw_bytes(METADATA_HEADER);
133
134    let crate_root = CrateMetadata::new(genv);
135
136    let hygiene_ctxt = HygieneEncodeContext::default();
137    let tcx = genv.tcx();
138    let mut ecx = EncodeContext {
139        tcx,
140        opaque: encoder,
141        type_shorthands: Default::default(),
142        predicate_shorthands: Default::default(),
143        file_to_file_index,
144        is_proc_macro: genv.tcx().crate_types().contains(&CrateType::ProcMacro),
145        hygiene_ctxt: &hygiene_ctxt,
146        symbol_index_table: Default::default(),
147    };
148
149    crate_root.encode(&mut ecx);
150
151    // BEGIN: CREUSOT-footer
152    let mut syntax_contexts = FxHashMap::default();
153    let mut expn_data = FxHashMap::default();
154
155    // Encode all hygiene data (`SyntaxContextData` and `ExpnData`) from the current session.
156    ecx.hygiene_ctxt.encode(
157        &mut ecx,
158        |encoder, index, ctxt_data| {
159            let pos = AbsoluteBytePos::new(encoder.position());
160            ctxt_data.encode(encoder);
161            syntax_contexts.insert(index, pos);
162        },
163        |encoder, expn_id, data, hash| {
164            let pos = AbsoluteBytePos::new(encoder.position());
165            data.encode(encoder);
166            hash.encode(encoder);
167            expn_data.insert((tcx.stable_crate_id(expn_id.krate), expn_id.local_id.as_u32()), pos);
168        },
169    );
170
171    // Encode the file footer.
172    let footer_pos = ecx.position() as u64;
173    let footer = Footer { file_index_to_stable_id, syntax_contexts, expn_data };
174    footer.encode(&mut ecx);
175
176    // Encode the position of the footer as the last 8 bytes of the
177    // file so we know where to look for it.
178    IntEncodedWithFixedSize(footer_pos).encode(&mut ecx.opaque);
179
180    // DO NOT WRITE ANYTHING TO THE ENCODER AFTER THIS POINT! The address
181    // of the footer must be the last thing in the data stream.
182    // END: CREUSOT-footer
183
184    ecx.opaque.finish().unwrap();
185}
186
187impl SpanEncoder for EncodeContext<'_, '_> {
188    fn encode_crate_num(&mut self, crate_num: CrateNum) {
189        if crate_num != LOCAL_CRATE && self.is_proc_macro {
190            bug!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate");
191        }
192        self.tcx.stable_crate_id(crate_num).encode(self);
193    }
194
195    fn encode_def_index(&mut self, def_index: DefIndex) {
196        self.emit_u32(def_index.as_u32());
197    }
198
199    fn encode_def_id(&mut self, def_id: DefId) {
200        def_id.krate.encode(self);
201        def_id.index.encode(self);
202    }
203
204    fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) {
205        rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self);
206    }
207
208    fn encode_expn_id(&mut self, expn_id: ExpnId) {
209        if expn_id.krate == LOCAL_CRATE {
210            // We will only write details for local expansions. Non-local expansions will fetch
211            // data from the corresponding crate's metadata.
212            // FIXME(#43047) FIXME(#74731) We may eventually want to avoid relying on external
213            // metadata from proc-macro crates.
214            self.hygiene_ctxt.schedule_expn_data_for_encoding(expn_id);
215        }
216        expn_id.krate.encode(self);
217        expn_id.local_id.encode(self);
218    }
219
220    // Code adapted from creusot
221    fn encode_span(&mut self, span: Span) {
222        let span = span.data();
223        span.ctxt.encode(self);
224
225        if span.is_dummy() {
226            return TAG_PARTIAL_SPAN.encode(self);
227        }
228
229        let source_file = self.tcx.sess().source_map().lookup_source_file(span.lo);
230        if !source_file.contains(span.hi) {
231            // Unfortunately, macro expansion still sometimes generates Spans
232            // that malformed in this way.
233            return TAG_PARTIAL_SPAN.encode(self);
234        }
235
236        let lo = span.lo - source_file.start_pos;
237        let len = span.hi - span.lo;
238        let source_file_index = self.source_file_index(source_file);
239
240        TAG_FULL_SPAN.encode(self);
241        source_file_index.encode(self);
242        lo.encode(self);
243        len.encode(self);
244    }
245
246    fn encode_symbol(&mut self, sym: Symbol) {
247        self.encode_symbol_or_byte_symbol(sym.as_u32(), |this| this.emit_str(sym.as_str()));
248    }
249
250    fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol) {
251        self.encode_symbol_or_byte_symbol(byte_sym.as_u32(), |this| {
252            this.emit_byte_str(byte_sym.as_byte_str());
253        });
254    }
255}
256
257impl<'tcx> TyEncoder<'tcx> for EncodeContext<'_, 'tcx> {
258    const CLEAR_CROSS_CRATE: bool = true;
259
260    fn position(&self) -> usize {
261        self.opaque.position()
262    }
263
264    fn type_shorthands(&mut self) -> &mut FxHashMap<ty::Ty<'tcx>, usize> {
265        &mut self.type_shorthands
266    }
267
268    fn predicate_shorthands(&mut self) -> &mut FxHashMap<ty::PredicateKind<'tcx>, usize> {
269        &mut self.predicate_shorthands
270    }
271
272    fn encode_alloc_id(&mut self, _alloc_id: &rustc_middle::mir::interpret::AllocId) {
273        bug!("Encoding `interpret::AllocId` is not supported");
274        // let (index, _) = self.interpret_allocs.insert_full(*alloc_id);
275        // index.encode(self);
276    }
277}
278
279impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnIndex {
280    fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
281        s.emit_u32(self.as_u32());
282    }
283}
284
285macro_rules! encoder_methods {
286    ($($name:ident($ty:ty);)*) => {
287        $(fn $name(&mut self, value: $ty) {
288            self.opaque.$name(value)
289        })*
290    }
291}
292
293impl Encoder for EncodeContext<'_, '_> {
294    encoder_methods! {
295        emit_usize(usize);
296        emit_u128(u128);
297        emit_u64(u64);
298        emit_u32(u32);
299        emit_u16(u16);
300        emit_u8(u8);
301
302        emit_isize(isize);
303        emit_i128(i128);
304        emit_i64(i64);
305        emit_i32(i32);
306        emit_i16(i16);
307        emit_i8(i8);
308
309        emit_bool(bool);
310        emit_char(char);
311        emit_str(&str);
312        emit_raw_bytes(&[u8]);
313    }
314}