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 ExpnId, FileName, SourceFile, Span, SpanEncoder, StableSourceFileId, Symbol, SyntaxContext,
15 def_id::{CrateNum, DefIndex},
16 hygiene::{ExpnIndex, HygieneEncodeContext},
17};
18
19use crate::{CrateMetadata, METADATA_HEADER, SYMBOL_OFFSET, SYMBOL_PREINTERNED, SYMBOL_STR};
20
21struct EncodeContext<'a, 'tcx> {
22 tcx: TyCtxt<'tcx>,
23 opaque: opaque::FileEncoder,
24 type_shorthands: FxHashMap<ty::Ty<'tcx>, usize>,
25 predicate_shorthands: FxHashMap<ty::PredicateKind<'tcx>, usize>,
26 is_proc_macro: bool,
27 hygiene_ctxt: &'a HygieneEncodeContext,
28 symbol_table: FxHashMap<Symbol, usize>, }
30
31pub fn encode_metadata(genv: GlobalEnv, path: &std::path::Path) {
32 let mut encoder = opaque::FileEncoder::new(path).unwrap_or_else(|err| {
33 genv.tcx()
34 .sess
35 .dcx()
36 .emit_fatal(FailCreateFileEncoder { err })
37 });
38
39 encoder.emit_raw_bytes(METADATA_HEADER);
40
41 let crate_root = CrateMetadata::new(genv);
42
43 let hygiene_ctxt = HygieneEncodeContext::default();
44 let mut ecx = EncodeContext {
45 tcx: genv.tcx(),
46 opaque: encoder,
47 type_shorthands: Default::default(),
48 predicate_shorthands: Default::default(),
49 is_proc_macro: genv.tcx().crate_types().contains(&CrateType::ProcMacro),
50 hygiene_ctxt: &hygiene_ctxt,
51 symbol_table: Default::default(),
52 };
53
54 crate_root.encode(&mut ecx);
55
56 ecx.opaque.finish().unwrap();
57}
58
59impl SpanEncoder for EncodeContext<'_, '_> {
60 fn encode_crate_num(&mut self, crate_num: CrateNum) {
61 if crate_num != LOCAL_CRATE && self.is_proc_macro {
62 bug!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate");
63 }
64 self.tcx.stable_crate_id(crate_num).encode(self);
65 }
66
67 fn encode_def_index(&mut self, def_index: DefIndex) {
68 self.emit_u32(def_index.as_u32());
69 }
70
71 fn encode_def_id(&mut self, def_id: DefId) {
72 def_id.krate.encode(self);
73 def_id.index.encode(self);
74 }
75
76 fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) {
77 rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self);
78 }
79
80 fn encode_expn_id(&mut self, expn_id: ExpnId) {
81 if expn_id.krate == LOCAL_CRATE {
82 self.hygiene_ctxt.schedule_expn_data_for_encoding(expn_id);
87 }
88 expn_id.krate.encode(self);
89 expn_id.local_id.encode(self);
90 }
91
92 fn encode_span(&mut self, span: Span) {
94 let sm = self.tcx.sess.source_map();
95 for bp in [span.lo(), span.hi()] {
96 let sf = sm.lookup_source_file(bp);
97 let ssfi = stable_source_file_id_for_export(self.tcx, &sf);
98 ssfi.encode(self);
99 (bp - sf.start_pos).encode(self);
104 }
105 }
106
107 fn encode_symbol(&mut self, symbol: Symbol) {
108 if symbol.is_predefined() {
110 self.opaque.emit_u8(SYMBOL_PREINTERNED);
111 self.opaque.emit_u32(symbol.as_u32());
112 } else {
113 match self.symbol_table.entry(symbol) {
115 Entry::Vacant(o) => {
116 self.opaque.emit_u8(SYMBOL_STR);
117 let pos = self.opaque.position();
118 o.insert(pos);
119 self.emit_str(symbol.as_str());
120 }
121 Entry::Occupied(o) => {
122 let x = *o.get();
123 self.emit_u8(SYMBOL_OFFSET);
124 self.emit_usize(x);
125 }
126 }
127 }
128 }
129}
130
131impl<'tcx> TyEncoder<'tcx> for EncodeContext<'_, 'tcx> {
132 const CLEAR_CROSS_CRATE: bool = true;
133
134 fn position(&self) -> usize {
135 self.opaque.position()
136 }
137
138 fn type_shorthands(&mut self) -> &mut FxHashMap<ty::Ty<'tcx>, usize> {
139 &mut self.type_shorthands
140 }
141
142 fn predicate_shorthands(&mut self) -> &mut FxHashMap<ty::PredicateKind<'tcx>, usize> {
143 &mut self.predicate_shorthands
144 }
145
146 fn encode_alloc_id(&mut self, _alloc_id: &rustc_middle::mir::interpret::AllocId) {
147 bug!("Encoding `interpret::AllocId` is not supported");
148 }
151}
152
153impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnIndex {
154 fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
155 s.emit_u32(self.as_u32());
156 }
157}
158
159macro_rules! encoder_methods {
160 ($($name:ident($ty:ty);)*) => {
161 $(fn $name(&mut self, value: $ty) {
162 self.opaque.$name(value)
163 })*
164 }
165}
166
167impl Encoder for EncodeContext<'_, '_> {
168 encoder_methods! {
169 emit_usize(usize);
170 emit_u128(u128);
171 emit_u64(u64);
172 emit_u32(u32);
173 emit_u16(u16);
174 emit_u8(u8);
175
176 emit_isize(isize);
177 emit_i128(i128);
178 emit_i64(i64);
179 emit_i32(i32);
180 emit_i16(i16);
181 emit_i8(i8);
182
183 emit_bool(bool);
184 emit_char(char);
185 emit_str(&str);
186 emit_raw_bytes(&[u8]);
187 }
188}
189
190fn stable_source_file_id_for_export(tcx: TyCtxt, sf: &SourceFile) -> StableSourceFileId {
191 let working_directory = &tcx.sess.opts.working_dir;
192 let crate_stable_id = tcx.stable_crate_id(sf.cnum);
193 let mut filename = sf.name.clone();
194 if let FileName::Real(original_file_name) = filename {
195 let adapted_file_name = tcx
196 .sess
197 .source_map()
198 .path_mapping()
199 .to_embeddable_absolute_path(original_file_name.clone(), working_directory);
200
201 filename = FileName::Real(adapted_file_name);
202 }
203 StableSourceFileId::from_filename_for_export(&filename, crate_stable_id)
204}