1- use rustc_data_structures:: stable_hasher:: HashingControls ;
1+ use std:: hash:: Hash ;
2+
3+ use rustc_data_structures:: stable_hasher:: { HashStable , HashingControls , StableHasher } ;
24use rustc_hir:: def_id:: { DefId , LocalDefId } ;
35use rustc_hir:: definitions:: DefPathHash ;
46use rustc_session:: Session ;
57use rustc_session:: cstore:: Untracked ;
68use rustc_span:: source_map:: SourceMap ;
7- use rustc_span:: { BytePos , CachingSourceMapView , DUMMY_SP , SourceFile , Span , SpanData } ;
9+ use rustc_span:: { CachingSourceMapView , DUMMY_SP , Pos , Span } ;
810
911// Very often, we are hashing something that does not need the `CachingSourceMapView`, so we
1012// initialize it lazily.
@@ -60,16 +62,99 @@ impl<'a> StableHashingContext<'a> {
6062 }
6163 }
6264
65+ #[ inline]
66+ fn def_span ( & self , def_id : LocalDefId ) -> Span {
67+ self . untracked . source_span . get ( def_id) . unwrap_or ( DUMMY_SP )
68+ }
69+
6370 #[ inline]
6471 pub fn hashing_controls ( & self ) -> HashingControls {
6572 self . hashing_controls
6673 }
6774}
6875
6976impl < ' a > rustc_span:: HashStableContext for StableHashingContext < ' a > {
70- #[ inline]
71- fn unstable_opts_incremental_ignore_spans ( & self ) -> bool {
72- self . incremental_ignore_spans
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+ }
96+
97+ let span = span. data_untracked ( ) ;
98+ span. ctxt . hash_stable ( self , hasher) ;
99+ span. parent . hash_stable ( self , hasher) ;
100+
101+ if span. is_dummy ( ) {
102+ Hash :: hash ( & TAG_INVALID_SPAN , hasher) ;
103+ return ;
104+ }
105+
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) ;
73158 }
74159
75160 #[ inline]
@@ -81,22 +166,25 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> {
81166 }
82167 }
83168
84- #[ inline]
85- fn def_span ( & self , def_id : LocalDefId ) -> Span {
86- self . untracked . source_span . get ( def_id) . unwrap_or ( DUMMY_SP )
87- }
88-
89- #[ inline]
90- fn span_data_to_lines_and_cols (
91- & mut self ,
92- span : & SpanData ,
93- ) -> Option < ( & SourceFile , usize , BytePos , usize , BytePos ) > {
94- self . source_map ( ) . span_data_to_lines_and_cols ( span)
95- }
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;
96176
97- #[ inline]
98- fn hashing_controls ( & self ) -> HashingControls {
99- self . hashing_controls
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+ ) ;
100188 }
101189}
102190
0 commit comments