Skip to content

Commit fe531d5

Browse files
committed
Auto merge of #79012 - tgnottingham:span_data_to_lines_and_cols, r=estebank
rustc_span: add span_data_to_lines_and_cols to caching source map view
2 parents c5eae56 + 75de828 commit fe531d5

File tree

3 files changed

+222
-56
lines changed

3 files changed

+222
-56
lines changed

compiler/rustc_middle/src/ich/hcx.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_hir::definitions::{DefPathHash, Definitions};
1212
use rustc_session::Session;
1313
use rustc_span::source_map::SourceMap;
1414
use rustc_span::symbol::Symbol;
15-
use rustc_span::{BytePos, CachingSourceMapView, SourceFile};
15+
use rustc_span::{BytePos, CachingSourceMapView, SourceFile, SpanData};
1616

1717
use rustc_span::def_id::{CrateNum, CRATE_DEF_INDEX};
1818
use smallvec::SmallVec;
@@ -248,6 +248,13 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> {
248248
) -> Option<(Lrc<SourceFile>, usize, BytePos)> {
249249
self.source_map().byte_pos_to_line_and_col(byte)
250250
}
251+
252+
fn span_data_to_lines_and_cols(
253+
&mut self,
254+
span: &SpanData,
255+
) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)> {
256+
self.source_map().span_data_to_lines_and_cols(span)
257+
}
251258
}
252259

253260
pub fn hash_stable_trait_impls<'a>(

compiler/rustc_span/src/caching_source_map_view.rs

+207-38
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::source_map::SourceMap;
2-
use crate::{BytePos, SourceFile};
2+
use crate::{BytePos, SourceFile, SpanData};
33
use rustc_data_structures::sync::Lrc;
44
use std::ops::Range;
55

@@ -24,6 +24,32 @@ struct CacheEntry {
2424
file_index: usize,
2525
}
2626

27+
impl CacheEntry {
28+
#[inline]
29+
fn update(
30+
&mut self,
31+
new_file_and_idx: Option<(Lrc<SourceFile>, usize)>,
32+
pos: BytePos,
33+
time_stamp: usize,
34+
) {
35+
if let Some((file, file_idx)) = new_file_and_idx {
36+
self.file = file;
37+
self.file_index = file_idx;
38+
}
39+
40+
let line_index = self.file.lookup_line(pos).unwrap();
41+
let line_bounds = self.file.line_bounds(line_index);
42+
self.line_number = line_index + 1;
43+
self.line = line_bounds;
44+
self.touch(time_stamp);
45+
}
46+
47+
#[inline]
48+
fn touch(&mut self, time_stamp: usize) {
49+
self.time_stamp = time_stamp;
50+
}
51+
}
52+
2753
#[derive(Clone)]
2854
pub struct CachingSourceMapView<'sm> {
2955
source_map: &'sm SourceMap,
@@ -57,59 +83,202 @@ impl<'sm> CachingSourceMapView<'sm> {
5783
self.time_stamp += 1;
5884

5985
// Check if the position is in one of the cached lines
60-
for cache_entry in self.line_cache.iter_mut() {
61-
if cache_entry.line.contains(&pos) {
62-
cache_entry.time_stamp = self.time_stamp;
86+
let cache_idx = self.cache_entry_index(pos);
87+
if cache_idx != -1 {
88+
let cache_entry = &mut self.line_cache[cache_idx as usize];
89+
cache_entry.touch(self.time_stamp);
6390

64-
return Some((
65-
cache_entry.file.clone(),
66-
cache_entry.line_number,
67-
pos - cache_entry.line.start,
68-
));
69-
}
91+
return Some((
92+
cache_entry.file.clone(),
93+
cache_entry.line_number,
94+
pos - cache_entry.line.start,
95+
));
7096
}
7197

7298
// No cache hit ...
73-
let mut oldest = 0;
74-
for index in 1..self.line_cache.len() {
75-
if self.line_cache[index].time_stamp < self.line_cache[oldest].time_stamp {
76-
oldest = index;
77-
}
78-
}
99+
let oldest = self.oldest_cache_entry_index();
100+
101+
// If the entry doesn't point to the correct file, get the new file and index.
102+
let new_file_and_idx = if !file_contains(&self.line_cache[oldest].file, pos) {
103+
Some(self.file_for_position(pos)?)
104+
} else {
105+
None
106+
};
79107

80108
let cache_entry = &mut self.line_cache[oldest];
109+
cache_entry.update(new_file_and_idx, pos, self.time_stamp);
81110

82-
// If the entry doesn't point to the correct file, fix it up
83-
if !file_contains(&cache_entry.file, pos) {
84-
let file_valid;
85-
if self.source_map.files().len() > 0 {
86-
let file_index = self.source_map.lookup_source_file_idx(pos);
87-
let file = self.source_map.files()[file_index].clone();
88-
89-
if file_contains(&file, pos) {
90-
cache_entry.file = file;
91-
cache_entry.file_index = file_index;
92-
file_valid = true;
93-
} else {
94-
file_valid = false;
111+
Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line.start))
112+
}
113+
114+
pub fn span_data_to_lines_and_cols(
115+
&mut self,
116+
span_data: &SpanData,
117+
) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)> {
118+
self.time_stamp += 1;
119+
120+
// Check if lo and hi are in the cached lines.
121+
let lo_cache_idx = self.cache_entry_index(span_data.lo);
122+
let hi_cache_idx = self.cache_entry_index(span_data.hi);
123+
124+
if lo_cache_idx != -1 && hi_cache_idx != -1 {
125+
// Cache hit for span lo and hi. Check if they belong to the same file.
126+
let result = {
127+
let lo = &self.line_cache[lo_cache_idx as usize];
128+
let hi = &self.line_cache[hi_cache_idx as usize];
129+
130+
if lo.file_index != hi.file_index {
131+
return None;
95132
}
96-
} else {
97-
file_valid = false;
133+
134+
(
135+
lo.file.clone(),
136+
lo.line_number,
137+
span_data.lo - lo.line.start,
138+
hi.line_number,
139+
span_data.hi - hi.line.start,
140+
)
141+
};
142+
143+
self.line_cache[lo_cache_idx as usize].touch(self.time_stamp);
144+
self.line_cache[hi_cache_idx as usize].touch(self.time_stamp);
145+
146+
return Some(result);
147+
}
148+
149+
// No cache hit or cache hit for only one of span lo and hi.
150+
let oldest = if lo_cache_idx != -1 || hi_cache_idx != -1 {
151+
let avoid_idx = if lo_cache_idx != -1 { lo_cache_idx } else { hi_cache_idx };
152+
self.oldest_cache_entry_index_avoid(avoid_idx as usize)
153+
} else {
154+
self.oldest_cache_entry_index()
155+
};
156+
157+
// If the entry doesn't point to the correct file, get the new file and index.
158+
// Return early if the file containing beginning of span doesn't contain end of span.
159+
let new_file_and_idx = if !file_contains(&self.line_cache[oldest].file, span_data.lo) {
160+
let new_file_and_idx = self.file_for_position(span_data.lo)?;
161+
if !file_contains(&new_file_and_idx.0, span_data.hi) {
162+
return None;
98163
}
99164

100-
if !file_valid {
165+
Some(new_file_and_idx)
166+
} else {
167+
let file = &self.line_cache[oldest].file;
168+
if !file_contains(&file, span_data.hi) {
101169
return None;
102170
}
171+
172+
None
173+
};
174+
175+
// Update the cache entries.
176+
let (lo_idx, hi_idx) = match (lo_cache_idx, hi_cache_idx) {
177+
// Oldest cache entry is for span_data.lo line.
178+
(-1, -1) => {
179+
let lo = &mut self.line_cache[oldest];
180+
lo.update(new_file_and_idx, span_data.lo, self.time_stamp);
181+
182+
if !lo.line.contains(&span_data.hi) {
183+
let new_file_and_idx = Some((lo.file.clone(), lo.file_index));
184+
let next_oldest = self.oldest_cache_entry_index_avoid(oldest);
185+
let hi = &mut self.line_cache[next_oldest];
186+
hi.update(new_file_and_idx, span_data.hi, self.time_stamp);
187+
(oldest, next_oldest)
188+
} else {
189+
(oldest, oldest)
190+
}
191+
}
192+
// Oldest cache entry is for span_data.lo line.
193+
(-1, _) => {
194+
let lo = &mut self.line_cache[oldest];
195+
lo.update(new_file_and_idx, span_data.lo, self.time_stamp);
196+
let hi = &mut self.line_cache[hi_cache_idx as usize];
197+
hi.touch(self.time_stamp);
198+
(oldest, hi_cache_idx as usize)
199+
}
200+
// Oldest cache entry is for span_data.hi line.
201+
(_, -1) => {
202+
let hi = &mut self.line_cache[oldest];
203+
hi.update(new_file_and_idx, span_data.hi, self.time_stamp);
204+
let lo = &mut self.line_cache[lo_cache_idx as usize];
205+
lo.touch(self.time_stamp);
206+
(lo_cache_idx as usize, oldest)
207+
}
208+
_ => {
209+
panic!();
210+
}
211+
};
212+
213+
let lo = &self.line_cache[lo_idx];
214+
let hi = &self.line_cache[hi_idx];
215+
216+
// Span lo and hi may equal line end when last line doesn't
217+
// end in newline, hence the inclusive upper bounds below.
218+
debug_assert!(span_data.lo >= lo.line.start);
219+
debug_assert!(span_data.lo <= lo.line.end);
220+
debug_assert!(span_data.hi >= hi.line.start);
221+
debug_assert!(span_data.hi <= hi.line.end);
222+
debug_assert!(lo.file.contains(span_data.lo));
223+
debug_assert!(lo.file.contains(span_data.hi));
224+
debug_assert_eq!(lo.file_index, hi.file_index);
225+
226+
Some((
227+
lo.file.clone(),
228+
lo.line_number,
229+
span_data.lo - lo.line.start,
230+
hi.line_number,
231+
span_data.hi - hi.line.start,
232+
))
233+
}
234+
235+
fn cache_entry_index(&self, pos: BytePos) -> isize {
236+
for (idx, cache_entry) in self.line_cache.iter().enumerate() {
237+
if cache_entry.line.contains(&pos) {
238+
return idx as isize;
239+
}
240+
}
241+
242+
-1
243+
}
244+
245+
fn oldest_cache_entry_index(&self) -> usize {
246+
let mut oldest = 0;
247+
248+
for idx in 1..self.line_cache.len() {
249+
if self.line_cache[idx].time_stamp < self.line_cache[oldest].time_stamp {
250+
oldest = idx;
251+
}
103252
}
104253

105-
let line_index = cache_entry.file.lookup_line(pos).unwrap();
106-
let line_bounds = cache_entry.file.line_bounds(line_index);
254+
oldest
255+
}
107256

108-
cache_entry.line_number = line_index + 1;
109-
cache_entry.line = line_bounds;
110-
cache_entry.time_stamp = self.time_stamp;
257+
fn oldest_cache_entry_index_avoid(&self, avoid_idx: usize) -> usize {
258+
let mut oldest = if avoid_idx != 0 { 0 } else { 1 };
111259

112-
Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line.start))
260+
for idx in 0..self.line_cache.len() {
261+
if idx != avoid_idx
262+
&& self.line_cache[idx].time_stamp < self.line_cache[oldest].time_stamp
263+
{
264+
oldest = idx;
265+
}
266+
}
267+
268+
oldest
269+
}
270+
271+
fn file_for_position(&self, pos: BytePos) -> Option<(Lrc<SourceFile>, usize)> {
272+
if !self.source_map.files().is_empty() {
273+
let file_idx = self.source_map.lookup_source_file_idx(pos);
274+
let file = &self.source_map.files()[file_idx];
275+
276+
if file_contains(file, pos) {
277+
return Some((file.clone(), file_idx));
278+
}
279+
}
280+
281+
None
113282
}
114283
}
115284

compiler/rustc_span/src/lib.rs

+7-17
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,10 @@ pub trait HashStableContext {
18711871
&mut self,
18721872
byte: BytePos,
18731873
) -> Option<(Lrc<SourceFile>, usize, BytePos)>;
1874+
fn span_data_to_lines_and_cols(
1875+
&mut self,
1876+
span: &SpanData,
1877+
) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)>;
18741878
}
18751879

18761880
impl<CTX> HashStable<CTX> for Span
@@ -1904,22 +1908,8 @@ where
19041908
// position that belongs to it, as opposed to hashing the first
19051909
// position past it.
19061910
let span = self.data();
1907-
let (file_lo, line_lo, col_lo) = match ctx.byte_pos_to_line_and_col(span.lo) {
1908-
Some(pos) => pos,
1909-
None => {
1910-
Hash::hash(&TAG_INVALID_SPAN, hasher);
1911-
span.ctxt.hash_stable(ctx, hasher);
1912-
return;
1913-
}
1914-
};
1915-
1916-
if !file_lo.contains(span.hi) {
1917-
Hash::hash(&TAG_INVALID_SPAN, hasher);
1918-
span.ctxt.hash_stable(ctx, hasher);
1919-
return;
1920-
}
1921-
1922-
let (_, line_hi, col_hi) = match ctx.byte_pos_to_line_and_col(span.hi) {
1911+
let (file, line_lo, col_lo, line_hi, col_hi) = match ctx.span_data_to_lines_and_cols(&span)
1912+
{
19231913
Some(pos) => pos,
19241914
None => {
19251915
Hash::hash(&TAG_INVALID_SPAN, hasher);
@@ -1931,7 +1921,7 @@ where
19311921
Hash::hash(&TAG_VALID_SPAN, hasher);
19321922
// We truncate the stable ID hash and line and column numbers. The chances
19331923
// of causing a collision this way should be minimal.
1934-
Hash::hash(&(file_lo.name_hash as u64), hasher);
1924+
Hash::hash(&(file.name_hash as u64), hasher);
19351925

19361926
// Hash both the length and the end location (line/column) of a span. If we
19371927
// hash only the length, for example, then two otherwise equal spans with

0 commit comments

Comments
 (0)