-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #85211 - Aaron1011:metadata-invalid-span, r=michaelwoer…
…ister Preserve `SyntaxContext` for invalid/dummy spans in crate metadata Fixes #85197 We already preserved the `SyntaxContext` for invalid/dummy spans in the incremental cache, but we weren't doing the same for crate metadata. If an invalid (lo/hi from different files) span is written to the incremental cache, we will decode it with a 'dummy' location, but keep the original `SyntaxContext`. Since the crate metadata encoder was only checking for `DUMMY_SP` (dummy location + root `SyntaxContext`), the metadata encoder would treat it as a normal span, encoding the `SyntaxContext`. As a result, the final span encoded to the metadata would change across sessions, even if the crate itself was unchanged. This could lead to an 'unstable fingerprint' ICE under the following conditions: 1. We compile a crate with an invalid span using incremental compilation. The metadata encoder discards the `SyntaxContext` since the span is invalid, while the incremental cache encoder preserves the `SyntaxContext` 2. From another crate, we execute a foreign query, decoding the invalid span from the metadata as `DUMMY_SP` (e.g. with `SyntaxContext::root()`). This span gets hashed into the query fingerprint. So far, this has always happened through the `optimized_mir` query. 3. We recompile the first crate using our populated incremental cache, without changing anything. We load the (previously) invalid span from our incremental cache - it gets converted to a span with a dummy (but valid) location, along with the original `SyntaxContext`. This span gets written out to the crate metadata - since it now has a valid location, we preserve its `SyntaxContext`. 4. We recompile the second crate, again using a populated incremental cache. We now re-run the foreign query `optimized_mir` - the foreign crate hash is unchanged, but we end up decoding a different span (it now ha a non-root `SyntaxContext`). This results in the fingerprint changing, resulting in an ICE. This PR updates our encoding of spans in the crate metadata to mirror the encoding of spans into the incremental cache. We now always encode a `SyntaxContext`, and encode location information for spans with a non-dummy location.
- Loading branch information
Showing
7 changed files
with
113 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
src/test/incremental/issue-85197-invalid-span/auxiliary/invalid-span-helper-lib.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// revisions: rpass1 rpass2 | ||
|
||
extern crate respan; | ||
|
||
#[macro_use] | ||
#[path = "invalid-span-helper-mod.rs"] | ||
mod invalid_span_helper_mod; | ||
|
||
// Invoke a macro from a different file - this | ||
// allows us to get tokens with spans from different files | ||
helper!(1); |
14 changes: 14 additions & 0 deletions
14
src/test/incremental/issue-85197-invalid-span/auxiliary/invalid-span-helper-mod.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#[macro_export] | ||
macro_rules! helper { | ||
// Use `:tt` instead of `:ident` so that we don't get a `None`-delimited group | ||
($first:tt) => { | ||
pub fn foo<T>() { | ||
// The span of `$first` comes from another file, | ||
// so the expression `1 + $first` ends up with an | ||
// 'invalid' span that starts and ends in different files. | ||
// We use the `respan!` macro to give all tokens the same | ||
// `SyntaxContext`, so that the parser will try to merge the spans. | ||
respan::respan!(let a = 1 + $first;); | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/test/incremental/issue-85197-invalid-span/auxiliary/respan.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// force-host | ||
// no-prefer-dynamic | ||
|
||
#![crate_type = "proc-macro"] | ||
|
||
extern crate proc_macro; | ||
use proc_macro::TokenStream; | ||
|
||
|
||
/// Copies the resolution information (the `SyntaxContext`) of the first | ||
/// token to all other tokens in the stream. Does not recurse into groups. | ||
#[proc_macro] | ||
pub fn respan(input: TokenStream) -> TokenStream { | ||
let first_span = input.clone().into_iter().next().unwrap().span(); | ||
input.into_iter().map(|mut tree| { | ||
tree.set_span(tree.span().resolved_at(first_span)); | ||
tree | ||
}).collect() | ||
} |
24 changes: 24 additions & 0 deletions
24
src/test/incremental/issue-85197-invalid-span/invalid_span_main.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// revisions: rpass1 rpass2 | ||
// aux-build:respan.rs | ||
// aux-build:invalid-span-helper-lib.rs | ||
|
||
// This issue has several different parts. The high level idea is: | ||
// 1. We create an 'invalid' span with the help of the `respan` proc-macro, | ||
// The compiler attempts to prevent the creation of invalid spans by | ||
// refusing to join spans with different `SyntaxContext`s. We work around | ||
// this by applying the same `SyntaxContext` to the span of every token, | ||
// using `Span::resolved_at` | ||
// 2. We using this invalid span in the body of a function, causing it to get | ||
// encoded into the `optimized_mir` | ||
// 3. We call the function from a different crate - since the function is generic, | ||
// monomorphization runs, causing `optimized_mir` to get called. | ||
// 4. We re-run compilation using our populated incremental cache, but without | ||
// making any changes. When we recompile the crate containing our generic function | ||
// (`invalid_span_helper_lib`), we load the span from the incremental cache, and | ||
// write it into the crate metadata. | ||
|
||
extern crate invalid_span_helper_lib; | ||
|
||
fn main() { | ||
invalid_span_helper_lib::foo::<u8>(); | ||
} |