Properly handle SyntaxContext of dummy spans in incr comp

Fixes #80336

Due to macro expansion, we may end up with spans with an invalid
location and non-root `SyntaxContext`. This commits preserves the
`SyntaxContext` of such spans in the incremental cache, and ensures
that we always hash the `SyntaxContext` when computing the `Fingerprint`
of a `Span`

Previously, we would discard the `SyntaxContext` during serialization to
the incremental cache, causing the span's `Fingerprint` to change across
compilation sessions.
This commit is contained in:
Aaron Hill 2021-01-03 10:09:32 -05:00
parent fd2df74902
commit 482a67d20f
No known key found for this signature in database
GPG Key ID: B4087E510E98B164
3 changed files with 32 additions and 13 deletions

View File

@ -32,8 +32,10 @@ use std::mem;
const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE;
const TAG_VALID_SPAN: u8 = 0;
const TAG_INVALID_SPAN: u8 = 1;
// A normal span encoded with both location information and a `SyntaxContext`
const TAG_FULL_SPAN: u8 = 0;
// A partial span with no location information, encoded only with a `SyntaxContext`
const TAG_PARTIAL_SPAN: u8 = 1;
const TAG_SYNTAX_CONTEXT: u8 = 0;
const TAG_EXPN_DATA: u8 = 1;
@ -864,10 +866,11 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for Span {
fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Result<Self, String> {
let tag: u8 = Decodable::decode(decoder)?;
if tag == TAG_INVALID_SPAN {
return Ok(DUMMY_SP);
if tag == TAG_PARTIAL_SPAN {
let ctxt = SyntaxContext::decode(decoder)?;
return Ok(DUMMY_SP.with_ctxt(ctxt));
} else {
debug_assert_eq!(tag, TAG_VALID_SPAN);
debug_assert_eq!(tag, TAG_FULL_SPAN);
}
let file_lo_index = SourceFileIndex::decode(decoder)?;
@ -1057,24 +1060,29 @@ where
{
fn encode(&self, s: &mut CacheEncoder<'a, 'tcx, E>) -> Result<(), E::Error> {
if *self == DUMMY_SP {
return TAG_INVALID_SPAN.encode(s);
TAG_PARTIAL_SPAN.encode(s)?;
return SyntaxContext::root().encode(s);
}
let span_data = self.data();
let (file_lo, line_lo, col_lo) = match s.source_map.byte_pos_to_line_and_col(span_data.lo) {
Some(pos) => pos,
None => return TAG_INVALID_SPAN.encode(s),
let pos = s.source_map.byte_pos_to_line_and_col(span_data.lo);
let partial_span = match &pos {
Some((file_lo, _, _)) => !file_lo.contains(span_data.hi),
None => true,
};
if !file_lo.contains(span_data.hi) {
return TAG_INVALID_SPAN.encode(s);
if partial_span {
TAG_PARTIAL_SPAN.encode(s)?;
return span_data.ctxt.encode(s);
}
let (file_lo, line_lo, col_lo) = pos.unwrap();
let len = span_data.hi - span_data.lo;
let source_file_index = s.source_file_index(file_lo);
TAG_VALID_SPAN.encode(s)?;
TAG_FULL_SPAN.encode(s)?;
source_file_index.encode(s)?;
line_lo.encode(s)?;
col_lo.encode(s)?;

View File

@ -1899,8 +1899,9 @@ where
return;
}
if *self == DUMMY_SP {
if self.is_dummy() {
Hash::hash(&TAG_INVALID_SPAN, hasher);
self.ctxt().hash_stable(ctx, hasher);
return;
}

View File

@ -0,0 +1,10 @@
// Regression test for issue #80336
// Test that we properly handle encoding, decoding, and hashing
// of spans with an invalid location and non-root `SyntaxContext`
// revisions:rpass1 rpass2
// only-x86_64
pub fn main() {
let _ = is_x86_feature_detected!("avx2");
}