1- use rustc_ast as ast;
1+ use std:: hash:: Hash ;
2+
23use rustc_data_structures:: stable_hasher:: { HashStable , HashingControls , StableHasher } ;
34use rustc_hir:: def_id:: { DefId , LocalDefId } ;
45use rustc_hir:: definitions:: DefPathHash ;
56use rustc_session:: Session ;
67use rustc_session:: cstore:: Untracked ;
78use rustc_span:: source_map:: SourceMap ;
8- use rustc_span:: { BytePos , CachingSourceMapView , DUMMY_SP , SourceFile , Span , SpanData , Symbol } ;
9+ use rustc_span:: { CachingSourceMapView , DUMMY_SP , Pos , Span } ;
910
10- use crate :: ich;
11+ // Very often, we are hashing something that does not need the `CachingSourceMapView`, so we
12+ // initialize it lazily.
13+ #[ derive( Clone ) ]
14+ enum CachingSourceMap < ' a > {
15+ Unused ( & ' a SourceMap ) ,
16+ InUse ( CachingSourceMapView < ' a > ) ,
17+ }
1118
1219/// This is the context state available during incr. comp. hashing. It contains
1320/// enough information to transform `DefId`s and `HirId`s into stable `DefPath`s (i.e.,
@@ -19,10 +26,7 @@ pub struct StableHashingContext<'a> {
1926 // The value of `-Z incremental-ignore-spans`.
2027 // This field should only be used by `unstable_opts_incremental_ignore_span`
2128 incremental_ignore_spans : bool ,
22- // Very often, we are hashing something that does not need the
23- // `CachingSourceMapView`, so we initialize it lazily.
24- raw_source_map : & ' a SourceMap ,
25- caching_source_map : Option < CachingSourceMapView < ' a > > ,
29+ caching_source_map : CachingSourceMap < ' a > ,
2630 hashing_controls : HashingControls ,
2731}
2832
@@ -34,8 +38,7 @@ impl<'a> StableHashingContext<'a> {
3438 StableHashingContext {
3539 untracked,
3640 incremental_ignore_spans : sess. opts . unstable_opts . incremental_ignore_spans ,
37- caching_source_map : None ,
38- raw_source_map : sess. source_map ( ) ,
41+ caching_source_map : CachingSourceMap :: Unused ( sess. source_map ( ) ) ,
3942 hashing_controls : HashingControls { hash_spans : hash_spans_initial } ,
4043 }
4144 }
@@ -49,81 +52,143 @@ impl<'a> StableHashingContext<'a> {
4952 }
5053
5154 #[ inline]
52- pub fn def_path_hash ( & self , def_id : DefId ) -> DefPathHash {
53- if let Some ( def_id) = def_id. as_local ( ) {
54- self . local_def_path_hash ( def_id)
55- } else {
56- self . untracked . cstore . read ( ) . def_path_hash ( def_id)
57- }
58- }
59-
60- #[ inline]
61- pub fn local_def_path_hash ( & self , def_id : LocalDefId ) -> DefPathHash {
62- self . untracked . definitions . read ( ) . def_path_hash ( def_id)
63- }
64-
65- #[ inline]
66- pub fn source_map ( & mut self ) -> & mut CachingSourceMapView < ' a > {
55+ fn source_map ( & mut self ) -> & mut CachingSourceMapView < ' a > {
6756 match self . caching_source_map {
68- Some ( ref mut sm) => sm,
69- ref mut none => {
70- * none = Some ( CachingSourceMapView :: new ( self . raw_source_map ) ) ;
71- none . as_mut ( ) . unwrap ( )
57+ CachingSourceMap :: InUse ( ref mut sm) => sm,
58+ CachingSourceMap :: Unused ( sm ) => {
59+ self . caching_source_map = CachingSourceMap :: InUse ( CachingSourceMapView :: new ( sm ) ) ;
60+ self . source_map ( ) // this recursive call will hit the `InUse` case
7261 }
7362 }
7463 }
7564
7665 #[ inline]
77- pub fn is_ignored_attr ( & self , name : Symbol ) -> bool {
78- ich :: IGNORED_ATTRIBUTES . contains ( & name )
66+ fn def_span ( & self , def_id : LocalDefId ) -> Span {
67+ self . untracked . source_span . get ( def_id ) . unwrap_or ( DUMMY_SP )
7968 }
8069
8170 #[ inline]
8271 pub fn hashing_controls ( & self ) -> HashingControls {
83- self . hashing_controls . clone ( )
84- }
85- }
86-
87- impl < ' a > HashStable < StableHashingContext < ' a > > for ast:: NodeId {
88- #[ inline]
89- fn hash_stable ( & self , _: & mut StableHashingContext < ' a > , _: & mut StableHasher ) {
90- panic ! ( "Node IDs should not appear in incremental state" ) ;
72+ self . hashing_controls
9173 }
9274}
9375
9476impl < ' a > rustc_span:: HashStableContext for StableHashingContext < ' a > {
95- #[ inline]
96- fn hash_spans ( & self ) -> bool {
97- self . hashing_controls . hash_spans
98- }
77+ /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` fields (that
78+ /// would be similar to hashing pointers, since those are just offsets into the `SourceMap`).
79+ /// Instead, we hash the (file name, line, column) triple, which stays the same even if the
80+ /// containing `SourceFile` has moved within the `SourceMap`.
81+ ///
82+ /// Also note that we are hashing byte offsets for the column, not unicode codepoint offsets.
83+ /// For the purpose of the hash that's sufficient. Also, hashing filenames is expensive so we
84+ /// avoid doing it twice when the span starts and ends in the same file, which is almost always
85+ /// the case.
86+ ///
87+ /// IMPORTANT: changes to this method should be reflected in implementations of `SpanEncoder`.
88+ fn span_hash_stable ( & mut self , span : Span , hasher : & mut StableHasher ) {
89+ const TAG_VALID_SPAN : u8 = 0 ;
90+ const TAG_INVALID_SPAN : u8 = 1 ;
91+ const TAG_RELATIVE_SPAN : u8 = 2 ;
92+
93+ if !self . hashing_controls ( ) . hash_spans {
94+ return ;
95+ }
9996
100- #[ inline]
101- fn unstable_opts_incremental_ignore_spans ( & self ) -> bool {
102- self . incremental_ignore_spans
103- }
97+ let span = span. data_untracked ( ) ;
98+ span. ctxt . hash_stable ( self , hasher) ;
99+ span. parent . hash_stable ( self , hasher) ;
104100
105- # [ inline ]
106- fn def_path_hash ( & self , def_id : DefId ) -> DefPathHash {
107- self . def_path_hash ( def_id )
108- }
101+ if span . is_dummy ( ) {
102+ Hash :: hash ( & TAG_INVALID_SPAN , hasher ) ;
103+ return ;
104+ }
109105
110- #[ inline]
111- fn def_span ( & self , def_id : LocalDefId ) -> Span {
112- self . untracked . source_span . get ( def_id) . unwrap_or ( DUMMY_SP )
106+ let parent = span. parent . map ( |parent| self . def_span ( parent) . data_untracked ( ) ) ;
107+ if let Some ( parent) = parent
108+ && parent. contains ( span)
109+ {
110+ // This span is enclosed in a definition: only hash the relative position. This catches
111+ // a subset of the cases from the `file.contains(parent.lo)`. But we can do this check
112+ // cheaply without the expensive `span_data_to_lines_and_cols` query.
113+ Hash :: hash ( & TAG_RELATIVE_SPAN , hasher) ;
114+ ( span. lo - parent. lo ) . to_u32 ( ) . hash_stable ( self , hasher) ;
115+ ( span. hi - parent. lo ) . to_u32 ( ) . hash_stable ( self , hasher) ;
116+ return ;
117+ }
118+
119+ // If this is not an empty or invalid span, we want to hash the last position that belongs
120+ // to it, as opposed to hashing the first position past it.
121+ let Some ( ( file, line_lo, col_lo, line_hi, col_hi) ) =
122+ self . source_map ( ) . span_data_to_lines_and_cols ( & span)
123+ else {
124+ Hash :: hash ( & TAG_INVALID_SPAN , hasher) ;
125+ return ;
126+ } ;
127+
128+ if let Some ( parent) = parent
129+ && file. contains ( parent. lo )
130+ {
131+ // This span is relative to another span in the same file,
132+ // only hash the relative position.
133+ Hash :: hash ( & TAG_RELATIVE_SPAN , hasher) ;
134+ Hash :: hash ( & ( span. lo . 0 . wrapping_sub ( parent. lo . 0 ) ) , hasher) ;
135+ Hash :: hash ( & ( span. hi . 0 . wrapping_sub ( parent. lo . 0 ) ) , hasher) ;
136+ return ;
137+ }
138+
139+ Hash :: hash ( & TAG_VALID_SPAN , hasher) ;
140+ Hash :: hash ( & file. stable_id , hasher) ;
141+
142+ // Hash both the length and the end location (line/column) of a span. If we hash only the
143+ // length, for example, then two otherwise equal spans with different end locations will
144+ // have the same hash. This can cause a problem during incremental compilation wherein a
145+ // previous result for a query that depends on the end location of a span will be
146+ // incorrectly reused when the end location of the span it depends on has changed (see
147+ // issue #74890). A similar analysis applies if some query depends specifically on the
148+ // length of the span, but we only hash the end location. So hash both.
149+
150+ let col_lo_trunc = ( col_lo. 0 as u64 ) & 0xFF ;
151+ let line_lo_trunc = ( ( line_lo as u64 ) & 0xFF_FF_FF ) << 8 ;
152+ let col_hi_trunc = ( col_hi. 0 as u64 ) & 0xFF << 32 ;
153+ let line_hi_trunc = ( ( line_hi as u64 ) & 0xFF_FF_FF ) << 40 ;
154+ let col_line = col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc;
155+ let len = ( span. hi - span. lo ) . 0 ;
156+ Hash :: hash ( & col_line, hasher) ;
157+ Hash :: hash ( & len, hasher) ;
113158 }
114159
115160 #[ inline]
116- fn span_data_to_lines_and_cols (
117- & mut self ,
118- span : & SpanData ,
119- ) -> Option < ( & SourceFile , usize , BytePos , usize , BytePos ) > {
120- self . source_map ( ) . span_data_to_lines_and_cols ( span)
161+ fn def_path_hash ( & self , def_id : DefId ) -> DefPathHash {
162+ if let Some ( def_id) = def_id. as_local ( ) {
163+ self . untracked . definitions . read ( ) . def_path_hash ( def_id)
164+ } else {
165+ self . untracked . cstore . read ( ) . def_path_hash ( def_id)
166+ }
121167 }
122168
123- #[ inline]
124- fn hashing_controls ( & self ) -> HashingControls {
125- self . hashing_controls . clone ( )
169+ /// Assert that the provided `HashStableContext` is configured with the default
170+ /// `HashingControls`. We should always have bailed out before getting to here with a
171+ /// non-default mode. With this check in place, we can avoid the need to maintain separate
172+ /// versions of `ExpnData` hashes for each permutation of `HashingControls` settings.
173+ fn assert_default_hashing_controls ( & self , msg : & str ) {
174+ let hashing_controls = self . hashing_controls ;
175+ let HashingControls { hash_spans } = hashing_controls;
176+
177+ // Note that we require that `hash_spans` be the inverse of the global `-Z
178+ // incremental-ignore-spans` option. Normally, this option is disabled, in which case
179+ // `hash_spans` must be true.
180+ //
181+ // Span hashing can also be disabled without `-Z incremental-ignore-spans`. This is the
182+ // case for instance when building a hash for name mangling. Such configuration must not be
183+ // used for metadata.
184+ assert_eq ! (
185+ hash_spans, !self . incremental_ignore_spans,
186+ "Attempted hashing of {msg} with non-default HashingControls: {hashing_controls:?}"
187+ ) ;
126188 }
127189}
128190
191+ impl < ' a > rustc_abi:: HashStableContext for StableHashingContext < ' a > { }
192+ impl < ' a > rustc_ast:: HashStableContext for StableHashingContext < ' a > { }
193+ impl < ' a > rustc_hir:: HashStableContext for StableHashingContext < ' a > { }
129194impl < ' a > rustc_session:: HashStableContext for StableHashingContext < ' a > { }
0 commit comments