flux_metadata/
decoder.rs

1use std::{
2    fs,
3    io::{self, Read},
4    mem,
5    path::Path,
6    sync::Arc,
7};
8
9use flux_common::bug;
10use flux_errors::FluxSession;
11use rustc_data_structures::{fx::FxHashMap, sync::HashMapExt};
12use rustc_hir::def_id::DefId;
13use rustc_middle::{
14    implement_ty_decoder,
15    ty::{self, TyCtxt, codec::TyDecoder},
16};
17use rustc_serialize::{
18    Decodable, Decoder as _,
19    opaque::{IntEncodedWithFixedSize, MemDecoder},
20};
21use rustc_session::StableCrateId;
22use rustc_span::{
23    BytePos, ByteSymbol, DUMMY_SP, SourceFile, Span, SpanDecoder, Symbol, SyntaxContext,
24    def_id::{CrateNum, DefIndex},
25    hygiene::{HygieneDecodeContext, SyntaxContextKey},
26};
27
28use crate::{
29    AbsoluteBytePos, CrateMetadata, EncodedSourceFileId, Footer, METADATA_HEADER, SYMBOL_OFFSET,
30    SYMBOL_PREDEFINED, SYMBOL_STR, SourceFileIndex, TAG_FULL_SPAN, TAG_PARTIAL_SPAN,
31};
32
33struct DecodeContext<'a, 'tcx> {
34    tcx: TyCtxt<'tcx>,
35    opaque: MemDecoder<'a>,
36    file_index_to_file: FxHashMap<SourceFileIndex, Arc<SourceFile>>,
37    file_index_to_stable_id: FxHashMap<SourceFileIndex, EncodedSourceFileId>,
38    syntax_contexts: &'a FxHashMap<u32, AbsoluteBytePos>,
39    expn_data: FxHashMap<(StableCrateId, u32), AbsoluteBytePos>,
40    hygiene_context: &'a HygieneDecodeContext,
41}
42
43impl<'a, 'tcx> DecodeContext<'a, 'tcx> {
44    fn file_index_to_file(&mut self, index: SourceFileIndex) -> Arc<SourceFile> {
45        self.file_index_to_file
46            .entry(index)
47            .or_insert_with(|| {
48                let source_file_id = &self.file_index_to_stable_id[&index];
49                let source_file_cnum = self
50                    .tcx
51                    .stable_crate_id_to_crate_num(source_file_id.stable_crate_id);
52
53                self.tcx.import_source_files(source_file_cnum);
54                self.tcx
55                    .sess
56                    .source_map()
57                    .source_file_by_stable_id(source_file_id.stable_source_file_id)
58                    .expect("failed to lookup `SourceFile` in new context")
59            })
60            .clone()
61    }
62}
63
64pub(super) fn decode_crate_metadata(
65    tcx: TyCtxt,
66    sess: &FluxSession,
67    path: &Path,
68) -> Option<CrateMetadata> {
69    let mut file = match fs::File::open(path) {
70        Ok(file) => file,
71        Err(err) if let io::ErrorKind::NotFound = err.kind() => return None,
72        Err(err) => sess.emit_fatal(errors::DecodeFileError::new(path, err)),
73    };
74    let mut buf = vec![];
75    file.read_to_end(&mut buf)
76        .unwrap_or_else(|err| sess.emit_fatal(errors::DecodeFileError::new(path, err)));
77
78    if !buf.starts_with(METADATA_HEADER) {
79        bug!("incompatible metadata version");
80    }
81
82    let footer = {
83        let mut decoder = MemDecoder::new(&buf, 0).unwrap();
84        let footer_pos = decoder
85            .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |d| {
86                IntEncodedWithFixedSize::decode(d).0 as usize
87            });
88        decoder.with_position(footer_pos, Footer::decode)
89    };
90
91    let mut decoder = DecodeContext {
92        tcx,
93        opaque: MemDecoder::new(&buf, METADATA_HEADER.len()).unwrap(),
94        file_index_to_stable_id: footer.file_index_to_stable_id,
95        file_index_to_file: Default::default(),
96        syntax_contexts: &footer.syntax_contexts,
97        expn_data: footer.expn_data,
98        hygiene_context: &Default::default(),
99    };
100
101    Some(CrateMetadata::decode(&mut decoder))
102}
103
104implement_ty_decoder!(DecodeContext<'a, 'tcx>);
105
106impl SpanDecoder for DecodeContext<'_, '_> {
107    fn decode_attr_id(&mut self) -> rustc_ast::AttrId {
108        self.tcx.sess.psess.attr_id_generator.mk_attr_id()
109    }
110
111    fn decode_crate_num(&mut self) -> CrateNum {
112        let stable_id = StableCrateId::decode(self);
113        self.tcx.stable_crate_id_to_crate_num(stable_id)
114    }
115
116    fn decode_def_index(&mut self) -> DefIndex {
117        DefIndex::from_u32(self.read_u32())
118    }
119
120    fn decode_def_id(&mut self) -> DefId {
121        DefId { krate: Decodable::decode(self), index: Decodable::decode(self) }
122    }
123
124    fn decode_syntax_context(&mut self) -> SyntaxContext {
125        let syntax_contexts = self.syntax_contexts;
126        rustc_span::hygiene::decode_syntax_context(self, self.hygiene_context, |this, id| {
127            // This closure is invoked if we haven't already decoded the data for the `SyntaxContext` we are deserializing.
128            // We look up the position of the associated `SyntaxData` and decode it.
129            let pos = syntax_contexts.get(&id).unwrap();
130            this.with_position(pos.to_usize(), SyntaxContextKey::decode)
131        })
132    }
133
134    fn decode_expn_id(&mut self) -> rustc_span::ExpnId {
135        let stable_id = StableCrateId::decode(self);
136        let cnum = self.tcx.stable_crate_id_to_crate_num(stable_id);
137        let index = u32::decode(self);
138
139        rustc_span::hygiene::decode_expn_id(cnum, index, |_| {
140            let pos = self.expn_data.get(&(stable_id, index)).unwrap();
141            self.with_position(pos.to_usize(), |decoder| {
142                let data = rustc_span::ExpnData::decode(decoder);
143                let hash = rustc_span::ExpnHash::decode(decoder);
144                (data, hash)
145            })
146        })
147    }
148
149    fn decode_span(&mut self) -> rustc_span::Span {
150        let ctxt = SyntaxContext::decode(self);
151        let tag: u8 = Decodable::decode(self);
152
153        if tag == TAG_PARTIAL_SPAN {
154            return DUMMY_SP.with_ctxt(ctxt);
155        }
156
157        debug_assert!(tag == TAG_FULL_SPAN);
158
159        let source_file_index = SourceFileIndex::decode(self);
160        let lo = BytePos::decode(self);
161        let len = BytePos::decode(self);
162        let file = self.file_index_to_file(source_file_index);
163        let lo = file.start_pos + lo;
164        let hi = lo + len;
165
166        Span::new(lo, hi, ctxt, None)
167    }
168
169    fn decode_symbol(&mut self) -> rustc_span::Symbol {
170        let tag = self.read_u8();
171
172        match tag {
173            SYMBOL_STR => {
174                let s = self.read_str();
175                Symbol::intern(s)
176            }
177            SYMBOL_OFFSET => {
178                // read str offset
179                let pos = self.read_usize();
180
181                // move to str offset and read
182                self.opaque.with_position(pos, |d| {
183                    let s = d.read_str();
184                    Symbol::intern(s)
185                })
186            }
187            SYMBOL_PREDEFINED => {
188                let symbol_index = self.read_u32();
189                Symbol::new(symbol_index)
190            }
191            _ => unreachable!(),
192        }
193    }
194
195    fn decode_byte_symbol(&mut self) -> ByteSymbol {
196        ByteSymbol::intern(self.read_byte_str())
197    }
198}
199
200impl<'tcx> TyDecoder<'tcx> for DecodeContext<'_, 'tcx> {
201    const CLEAR_CROSS_CRATE: bool = true;
202
203    fn interner(&self) -> TyCtxt<'tcx> {
204        self.tcx
205    }
206
207    fn cached_ty_for_shorthand<F>(&mut self, shorthand: usize, or_insert_with: F) -> ty::Ty<'tcx>
208    where
209        F: FnOnce(&mut Self) -> ty::Ty<'tcx>,
210    {
211        let tcx = self.tcx;
212
213        let cache_key = ty::CReaderCacheKey { cnum: None, pos: shorthand };
214
215        if let Some(&ty) = tcx.ty_rcache.borrow().get(&cache_key) {
216            return ty;
217        }
218
219        let ty = or_insert_with(self);
220        // This may overwrite the entry, but it should overwrite with the same value.
221        tcx.ty_rcache.borrow_mut().insert_same(cache_key, ty);
222        ty
223    }
224
225    fn with_position<F, R>(&mut self, pos: usize, f: F) -> R
226    where
227        F: FnOnce(&mut Self) -> R,
228    {
229        let new_opaque = self.opaque.split_at(pos);
230        let old_opaque = mem::replace(&mut self.opaque, new_opaque);
231        let r = f(self);
232        self.opaque = old_opaque;
233        r
234    }
235
236    fn decode_alloc_id(&mut self) -> rustc_middle::mir::interpret::AllocId {
237        bug!("Encoding `interpret::AllocId` is not supported")
238    }
239}
240
241mod errors {
242    use std::{io, path::Path};
243
244    use flux_errors::E0999;
245    use flux_macros::Diagnostic;
246
247    #[derive(Diagnostic)]
248    #[diag(metadata_decode_file_error, code = E0999)]
249    pub(super) struct DecodeFileError<'a> {
250        path: &'a Path,
251        err: io::Error,
252    }
253
254    impl<'a> DecodeFileError<'a> {
255        pub(super) fn new(path: &'a Path, err: io::Error) -> Self {
256            Self { path, err }
257        }
258    }
259}