Skip to content

Commit eebf674

Browse files
committed
Auto merge of #30140 - michaelwoerister:tls-encoding, r=nikomatsakis
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. Some notes: - `tls::with_encoding_context()` and `tls::with_decoding_context()` (as opposed to their unsafe versions) try to prevent the TLS data getting out-of-sync by making sure that the encoder/decoder passed in is actually the same as the one stored in the context. This should prevent accidentally reading from the wrong decoder. - There are no real tests in this PR. I had a unit tests for some of the core aspects of the TLS implementation but it was kind of brittle, a lot of code for mocking `ty::ctxt`, `crate_metadata`, etc and did actually test not so much. The code will soon be tested by the first incremental compilation auto-tests that rely on MIR being properly serialized. However, if people think that some tests should be added before this can land, I'll try to provide some that make sense. r? @nikomatsakis
2 parents 2e48b59 + f65823e commit eebf674

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
@@ -638,6 +638,14 @@ pub mod reader {
638638
self.pos = old_pos;
639639
Ok(result)
640640
}
641+
642+
pub fn position(&self) -> usize {
643+
self.pos
644+
}
645+
646+
pub fn advance(&mut self, bytes: usize) {
647+
self.pos += bytes;
648+
}
641649
}
642650

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

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ extern crate fmt_macros;
5959
extern crate getopts;
6060
extern crate graphviz;
6161
extern crate libc;
62+
extern crate rbml;
6263
extern crate rustc_llvm;
6364
extern crate rustc_back;
6465
extern crate rustc_front;

src/librustc/middle/cstore.rs

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

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)