Skip to content

incr.comp.: Implement compiler diagnostic persistence. #45472

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/librustc/dep_graph/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use errors::DiagnosticBuilder;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
StableHashingContextProvider};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
Expand Down Expand Up @@ -568,6 +569,24 @@ impl DepGraph {
"DepGraph::try_mark_green() - Duplicate fingerprint \
insertion for {:?}", dep_node);

// ... emitting any stored diagnostic ...
{
let diagnostics = tcx.on_disk_query_result_cache
.load_diagnostics(prev_dep_node_index);

if diagnostics.len() > 0 {
let handle = tcx.sess.diagnostic();

// Promote the previous diagnostics to the current session.
tcx.on_disk_query_result_cache
.store_diagnostics(dep_node_index, diagnostics.clone());

for diagnostic in diagnostics {
DiagnosticBuilder::new_diagnostic(handle, diagnostic).emit();
}
}
}

// ... and finally storing a "Green" entry in the color map.
let old_color = data.colors
.borrow_mut()
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/dep_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ pub use self::prev::PreviousDepGraph;
pub use self::query::DepGraphQuery;
pub use self::safe::AssertDepGraphSafe;
pub use self::safe::DepGraphSafe;
pub use self::serialized::SerializedDepGraph;
pub use self::serialized::{SerializedDepGraph, SerializedDepNodeIndex};
1 change: 1 addition & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(i128_type)]
#![feature(inclusive_range_syntax)]
#![cfg_attr(windows, feature(libc))]
#![feature(never_type)]
#![feature(nonzero)]
Expand Down
243 changes: 243 additions & 0 deletions src/librustc/ty/codec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This module contains some shared code for encoding and decoding various
// things from the `ty` module, and in particular implements support for
// "shorthands" which allow to have pointers back into the already encoded
// stream instead of re-encoding the same thing twice.
//
// The functionality in here is shared between persisting to crate metadata and
// persisting to incr. comp. caches.

use hir::def_id::{DefId, CrateNum};
use middle::const_val::ByteArray;
use rustc_data_structures::fx::FxHashMap;
use rustc_serialize::{Decodable, Decoder, Encoder, Encodable};
use std::hash::Hash;
use std::intrinsics;
use ty::{self, Ty, TyCtxt};
use ty::subst::Substs;

/// The shorthand encoding uses an enum's variant index `usize`
/// and is offset by this value so it never matches a real variant.
/// This offset is also chosen so that the first byte is never < 0x80.
pub const SHORTHAND_OFFSET: usize = 0x80;

pub trait EncodableWithShorthand: Clone + Eq + Hash {
type Variant: Encodable;
fn variant(&self) -> &Self::Variant;
}

impl<'tcx> EncodableWithShorthand for Ty<'tcx> {
type Variant = ty::TypeVariants<'tcx>;
fn variant(&self) -> &Self::Variant {
&self.sty
}
}

impl<'tcx> EncodableWithShorthand for ty::Predicate<'tcx> {
type Variant = ty::Predicate<'tcx>;
fn variant(&self) -> &Self::Variant {
self
}
}

pub trait TyEncoder: Encoder {
fn position(&self) -> usize;
}

/// Encode the given value or a previously cached shorthand.
pub fn encode_with_shorthand<E, T, M>(encoder: &mut E,
value: &T,
cache: M)
-> Result<(), E::Error>
where E: TyEncoder,
M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap<T, usize>,
T: EncodableWithShorthand,
{
let existing_shorthand = cache(encoder).get(value).cloned();
if let Some(shorthand) = existing_shorthand {
return encoder.emit_usize(shorthand);
}

let variant = value.variant();

let start = encoder.position();
variant.encode(encoder)?;
let len = encoder.position() - start;

// The shorthand encoding uses the same usize as the
// discriminant, with an offset so they can't conflict.
let discriminant = unsafe { intrinsics::discriminant_value(variant) };
assert!(discriminant < SHORTHAND_OFFSET as u64);
let shorthand = start + SHORTHAND_OFFSET;

// Get the number of bits that leb128 could fit
// in the same space as the fully encoded type.
let leb128_bits = len * 7;

// Check that the shorthand is a not longer than the
// full encoding itself, i.e. it's an obvious win.
if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) {
cache(encoder).insert(value.clone(), shorthand);
}

Ok(())
}

pub fn encode_predicates<'tcx, E, C>(encoder: &mut E,
predicates: &ty::GenericPredicates<'tcx>,
cache: C)
-> Result<(), E::Error>
where E: TyEncoder,
C: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap<ty::Predicate<'tcx>, usize>,
{
predicates.parent.encode(encoder)?;
predicates.predicates.len().encode(encoder)?;
for predicate in &predicates.predicates {
encode_with_shorthand(encoder, predicate, &cache)?
}
Ok(())
}

pub trait TyDecoder<'a, 'tcx: 'a>: Decoder {

fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx>;

fn peek_byte(&self) -> u8;

fn cached_ty_for_shorthand<F>(&mut self,
shorthand: usize,
or_insert_with: F)
-> Result<Ty<'tcx>, Self::Error>
where F: FnOnce(&mut Self) -> Result<Ty<'tcx>, Self::Error>;

fn with_position<F, R>(&mut self, pos: usize, f: F) -> R
where F: FnOnce(&mut Self) -> R;

fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum;

fn positioned_at_shorthand(&self) -> bool {
(self.peek_byte() & (SHORTHAND_OFFSET as u8)) != 0
}
}

pub fn decode_cnum<'a, 'tcx, D>(decoder: &mut D) -> Result<CrateNum, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
let cnum = CrateNum::from_u32(u32::decode(decoder)?);
Ok(decoder.map_encoded_cnum_to_current(cnum))
}

pub fn decode_ty<'a, 'tcx, D>(decoder: &mut D) -> Result<Ty<'tcx>, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
// Handle shorthands first, if we have an usize > 0x80.
// if self.opaque.data[self.opaque.position()] & 0x80 != 0 {
if decoder.positioned_at_shorthand() {
let pos = decoder.read_usize()?;
assert!(pos >= SHORTHAND_OFFSET);
let shorthand = pos - SHORTHAND_OFFSET;

decoder.cached_ty_for_shorthand(shorthand, |decoder| {
decoder.with_position(shorthand, Ty::decode)
})
} else {
let tcx = decoder.tcx();
Ok(tcx.mk_ty(ty::TypeVariants::decode(decoder)?))
}
}

pub fn decode_predicates<'a, 'tcx, D>(decoder: &mut D)
-> Result<ty::GenericPredicates<'tcx>, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
Ok(ty::GenericPredicates {
parent: Decodable::decode(decoder)?,
predicates: (0..decoder.read_usize()?).map(|_| {
// Handle shorthands first, if we have an usize > 0x80.
if decoder.positioned_at_shorthand() {
let pos = decoder.read_usize()?;
assert!(pos >= SHORTHAND_OFFSET);
let shorthand = pos - SHORTHAND_OFFSET;

decoder.with_position(shorthand, ty::Predicate::decode)
} else {
ty::Predicate::decode(decoder)
}
})
.collect::<Result<Vec<_>, _>>()?,
})
}

pub fn decode_substs<'a, 'tcx, D>(decoder: &mut D) -> Result<&'tcx Substs<'tcx>, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
let len = decoder.read_usize()?;
let tcx = decoder.tcx();
Ok(tcx.mk_substs((0..len).map(|_| Decodable::decode(decoder)))?)
}

pub fn decode_region<'a, 'tcx, D>(decoder: &mut D) -> Result<ty::Region<'tcx>, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
Ok(decoder.tcx().mk_region(Decodable::decode(decoder)?))
}

pub fn decode_ty_slice<'a, 'tcx, D>(decoder: &mut D)
-> Result<&'tcx ty::Slice<Ty<'tcx>>, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
let len = decoder.read_usize()?;
Ok(decoder.tcx().mk_type_list((0..len).map(|_| Decodable::decode(decoder)))?)
}

pub fn decode_adt_def<'a, 'tcx, D>(decoder: &mut D)
-> Result<&'tcx ty::AdtDef, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
let def_id = DefId::decode(decoder)?;
Ok(decoder.tcx().adt_def(def_id))
}

pub fn decode_existential_predicate_slice<'a, 'tcx, D>(decoder: &mut D)
-> Result<&'tcx ty::Slice<ty::ExistentialPredicate<'tcx>>, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
let len = decoder.read_usize()?;
Ok(decoder.tcx()
.mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?)
}

pub fn decode_byte_array<'a, 'tcx, D>(decoder: &mut D)
-> Result<ByteArray<'tcx>, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
Ok(ByteArray {
data: decoder.tcx().alloc_byte_array(&Vec::decode(decoder)?)
})
}

pub fn decode_const<'a, 'tcx, D>(decoder: &mut D)
-> Result<&'tcx ty::Const<'tcx>, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
Ok(decoder.tcx().mk_const(Decodable::decode(decoder)?))
}
16 changes: 16 additions & 0 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,11 @@ pub struct GlobalCtxt<'tcx> {

pub dep_graph: DepGraph,

/// This provides access to the incr. comp. on-disk cache for query results.
/// Do not access this directly. It is only meant to be used by
/// `DepGraph::try_mark_green()` and the query infrastructure in `ty::maps`.
pub(crate) on_disk_query_result_cache: maps::OnDiskCache<'tcx>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as an aside, it feels to me like "pub(crate) visibility with scary comment" is a sign that this code is not "well aligned" in terms of module structure. We should review at some point.


/// Common types, pre-interned for your convenience.
pub types: CommonTypes<'tcx>,

Expand Down Expand Up @@ -1054,6 +1059,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
resolutions: ty::Resolutions,
named_region_map: resolve_lifetime::NamedRegionMap,
hir: hir_map::Map<'tcx>,
on_disk_query_result_cache: maps::OnDiskCache<'tcx>,
crate_name: &str,
tx: mpsc::Sender<Box<Any + Send>>,
output_filenames: &OutputFilenames,
Expand Down Expand Up @@ -1137,6 +1143,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
global_arenas: arenas,
global_interners: interners,
dep_graph: dep_graph.clone(),
on_disk_query_result_cache,
types: common_types,
named_region_map: NamedRegionMap {
defs,
Expand Down Expand Up @@ -1298,6 +1305,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.in_scope_traits_map(def_index);
}
}

pub fn serialize_query_result_cache<E>(self,
encoder: &mut E)
-> Result<(), E::Error>
where E: ::rustc_serialize::Encoder
{
self.on_disk_query_result_cache.serialize(encoder)
}

}

impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/ty/maps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ mod config;
pub use self::config::QueryConfig;
use self::config::QueryDescription;

mod on_disk_cache;
pub use self::on_disk_cache::OnDiskCache;

// Each of these maps also corresponds to a method on a
// `Provider` trait for requesting a value of that type,
// and a method on `Maps` itself for doing that in a
Expand Down
Loading