Skip to content

Commit f65823e

Browse files
Add scoped thread-local encoding and decoding contexts to cstore.
With this commit, metadata encoding and decoding can make use of thread-local encoding and decoding contexts. These allow implementers of serialize::Encodable and Decodable to access information and datastructures that would otherwise not be available to them. For example, we can automatically translate def-id and span information during decoding because the decoding context knows which crate the data is decoded from. Or it allows to make ty::Ty decodable because the context has access to the ty::ctxt that is needed for creating ty::Ty instances.
1 parent c212c0e commit f65823e

File tree

12 files changed

+450
-108
lines changed

12 files changed

+450
-108
lines changed

mk/crates.mk

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ DEPS_test := std getopts serialize rbml term native:rust_test_helpers
8888

8989
DEPS_syntax := std term serialize log fmt_macros arena libc rustc_bitflags
9090

91-
DEPS_rustc := syntax flate arena serialize getopts rustc_front\
91+
DEPS_rustc := syntax flate arena serialize getopts rbml rustc_front\
9292
log graphviz rustc_llvm rustc_back rustc_data_structures
9393
DEPS_rustc_back := std syntax rustc_llvm rustc_front flate log libc
9494
DEPS_rustc_borrowck := rustc rustc_front log graphviz syntax

src/librbml/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,14 @@ pub mod reader {
639639
self.pos = old_pos;
640640
Ok(result)
641641
}
642+
643+
pub fn position(&self) -> usize {
644+
self.pos
645+
}
646+
647+
pub fn advance(&mut self, bytes: usize) {
648+
self.pos += bytes;
649+
}
642650
}
643651

644652
impl<'doc> serialize::Decoder for Decoder<'doc> {

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ extern crate fmt_macros;
6262
extern crate getopts;
6363
extern crate graphviz;
6464
extern crate libc;
65+
extern crate rbml;
6566
extern crate rustc_llvm;
6667
extern crate rustc_back;
6768
extern crate rustc_front;

src/librustc/middle/cstore.rs

+138
Original file line numberDiff line numberDiff line change
@@ -405,3 +405,141 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
405405
krate: &hir::Crate) -> Vec<u8> { vec![] }
406406
fn metadata_encoding_version(&self) -> &[u8] { unimplemented!() }
407407
}
408+
409+
410+
/// Metadata encoding and decoding can make use of thread-local encoding and
411+
/// decoding contexts. These allow implementers of serialize::Encodable and
412+
/// Decodable to access information and datastructures that would otherwise not
413+
/// be available to them. For example, we can automatically translate def-id and
414+
/// span information during decoding because the decoding context knows which
415+
/// crate the data is decoded from. Or it allows to make ty::Ty decodable
416+
/// because the context has access to the ty::ctxt that is needed for creating
417+
/// ty::Ty instances.
418+
///
419+
/// Note, however, that this only works for RBML-based encoding and decoding at
420+
/// the moment.
421+
pub mod tls {
422+
use rbml::writer::Encoder as RbmlEncoder;
423+
use rbml::reader::Decoder as RbmlDecoder;
424+
use serialize;
425+
use std::mem;
426+
use middle::ty::{self, Ty};
427+
use middle::subst::Substs;
428+
use middle::def_id::DefId;
429+
430+
pub trait EncodingContext<'tcx> {
431+
fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>;
432+
fn encode_ty(&self, rbml_w: &mut RbmlEncoder, t: Ty<'tcx>);
433+
fn encode_substs(&self, rbml_w: &mut RbmlEncoder, substs: &Substs<'tcx>);
434+
}
435+
436+
/// Marker type used for the scoped TLS slot.
437+
/// The type context cannot be used directly because the scoped TLS
438+
/// in libstd doesn't allow types generic over lifetimes.
439+
struct TlsPayload;
440+
441+
scoped_thread_local!(static TLS_ENCODING: TlsPayload);
442+
443+
/// Execute f after pushing the given EncodingContext onto the TLS stack.
444+
pub fn enter_encoding_context<'tcx, F, R>(ecx: &EncodingContext<'tcx>,
445+
rbml_w: &mut RbmlEncoder,
446+
f: F) -> R
447+
where F: FnOnce(&EncodingContext<'tcx>, &mut RbmlEncoder) -> R
448+
{
449+
let tls_payload = (ecx as *const _, rbml_w as *mut _);
450+
let tls_ptr = &tls_payload as *const _ as *const TlsPayload;
451+
TLS_ENCODING.set(unsafe { &*tls_ptr }, || f(ecx, rbml_w))
452+
}
453+
454+
/// Execute f with access to the thread-local encoding context and
455+
/// rbml encoder. This function will panic if the encoder passed in and the
456+
/// context encoder are not the same.
457+
///
458+
/// Note that this method is 'practically' safe due to its checking that the
459+
/// encoder passed in is the same as the one in TLS, but it would still be
460+
/// possible to construct cases where the EncodingContext is exchanged
461+
/// while the same encoder is used, thus working with a wrong context.
462+
pub fn with_encoding_context<'tcx, E, F, R>(encoder: &mut E, f: F) -> R
463+
where F: FnOnce(&EncodingContext<'tcx>, &mut RbmlEncoder) -> R,
464+
E: serialize::Encoder
465+
{
466+
unsafe {
467+
unsafe_with_encoding_context(|ecx, rbml_w| {
468+
assert!(encoder as *mut _ as usize == rbml_w as *mut _ as usize);
469+
470+
let ecx: &EncodingContext<'tcx> = mem::transmute(ecx);
471+
472+
f(ecx, rbml_w)
473+
})
474+
}
475+
}
476+
477+
/// Execute f with access to the thread-local encoding context and
478+
/// rbml encoder.
479+
pub unsafe fn unsafe_with_encoding_context<F, R>(f: F) -> R
480+
where F: FnOnce(&EncodingContext, &mut RbmlEncoder) -> R
481+
{
482+
TLS_ENCODING.with(|tls| {
483+
let tls_payload = (tls as *const TlsPayload)
484+
as *mut (&EncodingContext, &mut RbmlEncoder);
485+
f((*tls_payload).0, (*tls_payload).1)
486+
})
487+
}
488+
489+
pub trait DecodingContext<'tcx> {
490+
fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>;
491+
fn decode_ty(&self, rbml_r: &mut RbmlDecoder) -> ty::Ty<'tcx>;
492+
fn decode_substs(&self, rbml_r: &mut RbmlDecoder) -> Substs<'tcx>;
493+
fn translate_def_id(&self, def_id: DefId) -> DefId;
494+
}
495+
496+
scoped_thread_local!(static TLS_DECODING: TlsPayload);
497+
498+
/// Execute f after pushing the given DecodingContext onto the TLS stack.
499+
pub fn enter_decoding_context<'tcx, F, R>(dcx: &DecodingContext<'tcx>,
500+
rbml_r: &mut RbmlDecoder,
501+
f: F) -> R
502+
where F: FnOnce(&DecodingContext<'tcx>, &mut RbmlDecoder) -> R
503+
{
504+
let tls_payload = (dcx as *const _, rbml_r as *mut _);
505+
let tls_ptr = &tls_payload as *const _ as *const TlsPayload;
506+
TLS_DECODING.set(unsafe { &*tls_ptr }, || f(dcx, rbml_r))
507+
}
508+
509+
/// Execute f with access to the thread-local decoding context and
510+
/// rbml decoder. This function will panic if the decoder passed in and the
511+
/// context decoder are not the same.
512+
///
513+
/// Note that this method is 'practically' safe due to its checking that the
514+
/// decoder passed in is the same as the one in TLS, but it would still be
515+
/// possible to construct cases where the DecodingContext is exchanged
516+
/// while the same decoder is used, thus working with a wrong context.
517+
pub fn with_decoding_context<'decoder, 'tcx, D, F, R>(d: &'decoder mut D, f: F) -> R
518+
where D: serialize::Decoder,
519+
F: FnOnce(&DecodingContext<'tcx>,
520+
&mut RbmlDecoder) -> R,
521+
'tcx: 'decoder
522+
{
523+
unsafe {
524+
unsafe_with_decoding_context(|dcx, rbml_r| {
525+
assert!((d as *mut _ as usize) == (rbml_r as *mut _ as usize));
526+
527+
let dcx: &DecodingContext<'tcx> = mem::transmute(dcx);
528+
529+
f(dcx, rbml_r)
530+
})
531+
}
532+
}
533+
534+
/// Execute f with access to the thread-local decoding context and
535+
/// rbml decoder.
536+
pub unsafe fn unsafe_with_decoding_context<F, R>(f: F) -> R
537+
where F: FnOnce(&DecodingContext, &mut RbmlDecoder) -> R
538+
{
539+
TLS_DECODING.with(|tls| {
540+
let tls_payload = (tls as *const TlsPayload)
541+
as *mut (&DecodingContext, &mut RbmlDecoder);
542+
f((*tls_payload).0, (*tls_payload).1)
543+
})
544+
}
545+
}

src/librustc/middle/subst.rs

+31
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
pub use self::ParamSpace::*;
1414
pub use self::RegionSubsts::*;
1515

16+
use middle::cstore;
1617
use middle::ty::{self, Ty, HasTypeFlags, RegionEscape};
1718
use middle::ty::fold::{TypeFoldable, TypeFolder};
1819

20+
use serialize::{Encodable, Encoder, Decodable, Decoder};
1921
use std::fmt;
2022
use std::iter::IntoIterator;
2123
use std::slice::Iter;
@@ -153,6 +155,35 @@ impl<'tcx> Substs<'tcx> {
153155
}
154156
}
155157

158+
impl<'tcx> Encodable for Substs<'tcx> {
159+
160+
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
161+
cstore::tls::with_encoding_context(s, |ecx, rbml_w| {
162+
ecx.encode_substs(rbml_w, self);
163+
Ok(())
164+
})
165+
}
166+
}
167+
168+
impl<'tcx> Decodable for Substs<'tcx> {
169+
fn decode<D: Decoder>(d: &mut D) -> Result<Substs<'tcx>, D::Error> {
170+
cstore::tls::with_decoding_context(d, |dcx, rbml_r| {
171+
Ok(dcx.decode_substs(rbml_r))
172+
})
173+
}
174+
}
175+
176+
impl<'tcx> Decodable for &'tcx Substs<'tcx> {
177+
fn decode<D: Decoder>(d: &mut D) -> Result<&'tcx Substs<'tcx>, D::Error> {
178+
let substs = cstore::tls::with_decoding_context(d, |dcx, rbml_r| {
179+
let substs = dcx.decode_substs(rbml_r);
180+
dcx.tcx().mk_substs(substs)
181+
});
182+
183+
Ok(substs)
184+
}
185+
}
186+
156187
impl RegionSubsts {
157188
pub fn map<F>(self, op: F) -> RegionSubsts where
158189
F: FnOnce(VecPerParamSpace<ty::Region>) -> VecPerParamSpace<ty::Region>,

src/librustc/middle/ty/mod.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub use self::LvaluePreference::*;
2222
use front::map as ast_map;
2323
use front::map::LinkedPath;
2424
use middle;
25-
use middle::cstore::{CrateStore, LOCAL_CRATE};
25+
use middle::cstore::{self, CrateStore, LOCAL_CRATE};
2626
use middle::def::{self, ExportMap};
2727
use middle::def_id::DefId;
2828
use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
@@ -35,6 +35,7 @@ use util::common::memoized;
3535
use util::nodemap::{NodeMap, NodeSet, DefIdMap};
3636
use util::nodemap::FnvHashMap;
3737

38+
use serialize::{Encodable, Encoder, Decodable, Decoder};
3839
use std::borrow::{Borrow, Cow};
3940
use std::cell::{Cell, RefCell};
4041
use std::hash::{Hash, Hasher};
@@ -479,6 +480,24 @@ impl<'tcx> Hash for TyS<'tcx> {
479480

480481
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
481482

483+
impl<'tcx> Encodable for Ty<'tcx> {
484+
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
485+
cstore::tls::with_encoding_context(s, |ecx, rbml_w| {
486+
ecx.encode_ty(rbml_w, *self);
487+
Ok(())
488+
})
489+
}
490+
}
491+
492+
impl<'tcx> Decodable for Ty<'tcx> {
493+
fn decode<D: Decoder>(d: &mut D) -> Result<Ty<'tcx>, D::Error> {
494+
cstore::tls::with_decoding_context(d, |dcx, rbml_r| {
495+
Ok(dcx.decode_ty(rbml_r))
496+
})
497+
}
498+
}
499+
500+
482501
/// Upvars do not get their own node-id. Instead, we use the pair of
483502
/// the original var id (that is, the root variable that is referenced
484503
/// by the upvar) and the id of the closure expression.
@@ -1529,6 +1548,23 @@ impl<'tcx, 'container> Hash for AdtDefData<'tcx, 'container> {
15291548
}
15301549
}
15311550

1551+
impl<'tcx> Encodable for AdtDef<'tcx> {
1552+
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
1553+
self.did.encode(s)
1554+
}
1555+
}
1556+
1557+
impl<'tcx> Decodable for AdtDef<'tcx> {
1558+
fn decode<D: Decoder>(d: &mut D) -> Result<AdtDef<'tcx>, D::Error> {
1559+
let def_id: DefId = try!{ Decodable::decode(d) };
1560+
1561+
cstore::tls::with_decoding_context(d, |dcx, _| {
1562+
let def_id = dcx.translate_def_id(def_id);
1563+
Ok(dcx.tcx().lookup_adt_def(def_id))
1564+
})
1565+
}
1566+
}
1567+
15321568

15331569
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
15341570
pub enum AdtKind { Struct, Enum }

src/librustc_metadata/astencode.rs

+9-58
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ use middle::ty::{self, Ty};
3939

4040
use syntax::{ast, ast_util, codemap};
4141
use syntax::ast::NodeIdAssigner;
42-
use syntax::codemap::Span;
4342
use syntax::ptr::P;
4443

4544
use std::cell::Cell;
@@ -116,7 +115,7 @@ impl<'a, 'b, 'c, 'tcx> ast_map::FoldOps for &'a DecodeContext<'b, 'c, 'tcx> {
116115
fn new_def_id(&self, def_id: DefId) -> DefId {
117116
self.tr_def_id(def_id)
118117
}
119-
fn new_span(&self, span: Span) -> Span {
118+
fn new_span(&self, span: codemap::Span) -> codemap::Span {
120119
self.tr_span(span)
121120
}
122121
}
@@ -219,60 +218,12 @@ impl<'a, 'b, 'tcx> DecodeContext<'a, 'b, 'tcx> {
219218
}
220219

221220
/// Translates a `Span` from an extern crate to the corresponding `Span`
222-
/// within the local crate's codemap. `creader::import_codemap()` will
223-
/// already have allocated any additionally needed FileMaps in the local
224-
/// codemap as a side-effect of creating the crate_metadata's
225-
/// `codemap_import_info`.
226-
pub fn tr_span(&self, span: Span) -> Span {
227-
let span = if span.lo > span.hi {
228-
// Currently macro expansion sometimes produces invalid Span values
229-
// where lo > hi. In order not to crash the compiler when trying to
230-
// translate these values, let's transform them into something we
231-
// can handle (and which will produce useful debug locations at
232-
// least some of the time).
233-
// This workaround is only necessary as long as macro expansion is
234-
// not fixed. FIXME(#23480)
235-
codemap::mk_sp(span.lo, span.lo)
236-
} else {
237-
span
238-
};
239-
240-
let imported_filemaps = self.cdata.imported_filemaps(self.tcx.sess.codemap());
241-
let filemap = {
242-
// Optimize for the case that most spans within a translated item
243-
// originate from the same filemap.
244-
let last_filemap_index = self.last_filemap_index.get();
245-
let last_filemap = &imported_filemaps[last_filemap_index];
246-
247-
if span.lo >= last_filemap.original_start_pos &&
248-
span.lo <= last_filemap.original_end_pos &&
249-
span.hi >= last_filemap.original_start_pos &&
250-
span.hi <= last_filemap.original_end_pos {
251-
last_filemap
252-
} else {
253-
let mut a = 0;
254-
let mut b = imported_filemaps.len();
255-
256-
while b - a > 1 {
257-
let m = (a + b) / 2;
258-
if imported_filemaps[m].original_start_pos > span.lo {
259-
b = m;
260-
} else {
261-
a = m;
262-
}
263-
}
264-
265-
self.last_filemap_index.set(a);
266-
&imported_filemaps[a]
267-
}
268-
};
269-
270-
let lo = (span.lo - filemap.original_start_pos) +
271-
filemap.translated_filemap.start_pos;
272-
let hi = (span.hi - filemap.original_start_pos) +
273-
filemap.translated_filemap.start_pos;
274-
275-
codemap::mk_sp(lo, hi)
221+
/// within the local crate's codemap.
222+
pub fn tr_span(&self, span: codemap::Span) -> codemap::Span {
223+
decoder::translate_span(self.cdata,
224+
self.tcx.sess.codemap(),
225+
&self.last_filemap_index,
226+
span)
276227
}
277228
}
278229

@@ -288,8 +239,8 @@ impl tr for Option<DefId> {
288239
}
289240
}
290241

291-
impl tr for Span {
292-
fn tr(&self, dcx: &DecodeContext) -> Span {
242+
impl tr for codemap::Span {
243+
fn tr(&self, dcx: &DecodeContext) -> codemap::Span {
293244
dcx.tr_span(*self)
294245
}
295246
}

0 commit comments

Comments
 (0)