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