1
1
use crate :: arena:: Arena ;
2
-
3
2
use rustc_serialize:: { Encodable , Encoder } ;
4
-
5
3
use std:: alloc:: Layout ;
6
4
use std:: cmp:: Ordering ;
7
5
use std:: fmt;
@@ -12,49 +10,69 @@ use std::ops::Deref;
12
10
use std:: ptr;
13
11
use std:: slice;
14
12
15
- extern "C" {
16
- /// A dummy type used to force `List` to be unsized while not requiring references to it be wide
17
- /// pointers .
18
- type OpaqueListContents ;
19
- }
20
-
21
- /// A wrapper for slices with the additional invariant
22
- /// that the slice is interned and no other slice with
23
- /// the same contents can exist in the same context .
24
- /// This means we can use pointer for both
25
- /// equality comparisons and hashing .
26
- ///
27
- /// Unlike slices, the types contained in `List` are expected to be `Copy`
28
- /// and iterating over a `List` returns `T` instead of a reference.
29
- ///
30
- /// Note: `Slice` was already taken by the `Ty` .
13
+ /// `List<T>` is a bit like `&[T]`, but with some critical differences.
14
+ /// - IMPORTANT: Every `List<T>` is *required* to have unique contents. The
15
+ /// type's correctness relies on this, *but it does not enforce it* .
16
+ /// Therefore, any code that creates a `List<T>` must ensure uniqueness
17
+ /// itself. In practice this is achieved by interning.
18
+ /// - The length is stored within the `List<T>`, so `&List<Ty>` is a thin
19
+ /// pointer.
20
+ /// - Because of this, you cannot get a `List<T>` that is a sub-list of another
21
+ /// `List<T>`. You can get a sub-slice `&[T]`, however .
22
+ /// - `List<T>` can be used with `CopyTaggedPtr`, which is useful within
23
+ /// structs whose size must be minimized .
24
+ /// - Because of the uniqueness assumption, we can use the address of a
25
+ /// `List<T>` for faster equality comparisons and hashing.
26
+ /// - `T` must be `Copy`. This lets `List<T>` be stored in a dropless arena and
27
+ /// iterators return a `T` rather than a `&T`.
28
+ /// - `T` must not be zero-sized .
31
29
#[ repr( C ) ]
32
30
pub struct List < T > {
33
31
len : usize ,
32
+
33
+ /// Although this claims to be a zero-length array, in practice `len`
34
+ /// elements are actually present.
34
35
data : [ T ; 0 ] ,
36
+
35
37
opaque : OpaqueListContents ,
36
38
}
37
39
38
- unsafe impl < ' a , T : ' a > rustc_data_structures:: tagged_ptr:: Pointer for & ' a List < T > {
39
- const BITS : usize = std:: mem:: align_of :: < usize > ( ) . trailing_zeros ( ) as usize ;
40
- #[ inline]
41
- fn into_usize ( self ) -> usize {
42
- self as * const List < T > as usize
43
- }
44
- #[ inline]
45
- unsafe fn from_usize ( ptr : usize ) -> Self {
46
- & * ( ptr as * const List < T > )
47
- }
48
- unsafe fn with_ref < R , F : FnOnce ( & Self ) -> R > ( ptr : usize , f : F ) -> R {
49
- // Self: Copy so this is fine
50
- let ptr = Self :: from_usize ( ptr) ;
51
- f ( & ptr)
52
- }
40
+ extern "C" {
41
+ /// A dummy type used to force `List` to be unsized while not requiring
42
+ /// references to it be wide pointers.
43
+ type OpaqueListContents ;
53
44
}
54
45
55
- unsafe impl < T : Sync > Sync for List < T > { }
46
+ impl < T > List < T > {
47
+ /// Returns a reference to the (unique, static) empty list.
48
+ #[ inline( always) ]
49
+ pub fn empty < ' a > ( ) -> & ' a List < T > {
50
+ #[ repr( align( 64 ) ) ]
51
+ struct MaxAlign ;
52
+
53
+ assert ! ( mem:: align_of:: <T >( ) <= mem:: align_of:: <MaxAlign >( ) ) ;
54
+
55
+ #[ repr( C ) ]
56
+ struct InOrder < T , U > ( T , U ) ;
57
+
58
+ // The empty slice is static and contains a single `0` usize (for the
59
+ // length) that is 64-byte aligned, thus featuring the necessary
60
+ // trailing padding for elements with up to 64-byte alignment.
61
+ static EMPTY_SLICE : InOrder < usize , MaxAlign > = InOrder ( 0 , MaxAlign ) ;
62
+ unsafe { & * ( & EMPTY_SLICE as * const _ as * const List < T > ) }
63
+ }
64
+ }
56
65
57
66
impl < T : Copy > List < T > {
67
+ /// Allocates a list from `arena` and copies the contents of `slice` into it.
68
+ ///
69
+ /// WARNING: the contents *must be unique*, such that no list with these
70
+ /// contents has been previously created. If not, operations such as `eq`
71
+ /// and `hash` might give incorrect results.
72
+ ///
73
+ /// Panics if `T` is `Drop`, or `T` is zero-sized, or the slice is empty
74
+ /// (because the empty list exists statically, and is available via
75
+ /// `empty()`).
58
76
#[ inline]
59
77
pub ( super ) fn from_arena < ' tcx > ( arena : & ' tcx Arena < ' tcx > , slice : & [ T ] ) -> & ' tcx List < T > {
60
78
assert ! ( !mem:: needs_drop:: <T >( ) ) ;
@@ -73,7 +91,7 @@ impl<T: Copy> List<T> {
73
91
. cast :: < T > ( )
74
92
. copy_from_nonoverlapping ( slice. as_ptr ( ) , slice. len ( ) ) ;
75
93
76
- & mut * mem
94
+ & * mem
77
95
}
78
96
}
79
97
@@ -107,11 +125,24 @@ impl<S: Encoder, T: Encodable<S>> Encodable<S> for &List<T> {
107
125
}
108
126
}
109
127
128
+ impl < T : PartialEq > PartialEq for List < T > {
129
+ #[ inline]
130
+ fn eq ( & self , other : & List < T > ) -> bool {
131
+ // Pointer equality implies list equality (due to the unique contents
132
+ // assumption).
133
+ ptr:: eq ( self , other)
134
+ }
135
+ }
136
+
137
+ impl < T : Eq > Eq for List < T > { }
138
+
110
139
impl < T > Ord for List < T >
111
140
where
112
141
T : Ord ,
113
142
{
114
143
fn cmp ( & self , other : & List < T > ) -> Ordering {
144
+ // Pointer equality implies list equality (due to the unique contents
145
+ // assumption), but the contents must be compared otherwise.
115
146
if self == other { Ordering :: Equal } else { <[ T ] as Ord >:: cmp ( & * * self , & * * other) }
116
147
}
117
148
}
@@ -121,6 +152,8 @@ where
121
152
T : PartialOrd ,
122
153
{
123
154
fn partial_cmp ( & self , other : & List < T > ) -> Option < Ordering > {
155
+ // Pointer equality implies list equality (due to the unique contents
156
+ // assumption), but the contents must be compared otherwise.
124
157
if self == other {
125
158
Some ( Ordering :: Equal )
126
159
} else {
@@ -129,17 +162,11 @@ where
129
162
}
130
163
}
131
164
132
- impl < T : PartialEq > PartialEq for List < T > {
133
- #[ inline]
134
- fn eq ( & self , other : & List < T > ) -> bool {
135
- ptr:: eq ( self , other)
136
- }
137
- }
138
- impl < T : Eq > Eq for List < T > { }
139
-
140
165
impl < T > Hash for List < T > {
141
166
#[ inline]
142
167
fn hash < H : Hasher > ( & self , s : & mut H ) {
168
+ // Pointer hashing is sufficient (due to the unique contents
169
+ // assumption).
143
170
( self as * const List < T > ) . hash ( s)
144
171
}
145
172
}
@@ -168,13 +195,24 @@ impl<'a, T: Copy> IntoIterator for &'a List<T> {
168
195
}
169
196
}
170
197
171
- impl < T > List < T > {
172
- #[ inline( always) ]
173
- pub fn empty < ' a > ( ) -> & ' a List < T > {
174
- #[ repr( align( 64 ) , C ) ]
175
- struct EmptySlice ( [ u8 ; 64 ] ) ;
176
- static EMPTY_SLICE : EmptySlice = EmptySlice ( [ 0 ; 64 ] ) ;
177
- assert ! ( mem:: align_of:: <T >( ) <= 64 ) ;
178
- unsafe { & * ( & EMPTY_SLICE as * const _ as * const List < T > ) }
198
+ unsafe impl < T : Sync > Sync for List < T > { }
199
+
200
+ unsafe impl < ' a , T : ' a > rustc_data_structures:: tagged_ptr:: Pointer for & ' a List < T > {
201
+ const BITS : usize = std:: mem:: align_of :: < usize > ( ) . trailing_zeros ( ) as usize ;
202
+
203
+ #[ inline]
204
+ fn into_usize ( self ) -> usize {
205
+ self as * const List < T > as usize
206
+ }
207
+
208
+ #[ inline]
209
+ unsafe fn from_usize ( ptr : usize ) -> & ' a List < T > {
210
+ & * ( ptr as * const List < T > )
211
+ }
212
+
213
+ unsafe fn with_ref < R , F : FnOnce ( & Self ) -> R > ( ptr : usize , f : F ) -> R {
214
+ // `Self` is `&'a List<T>` which impls `Copy`, so this is fine.
215
+ let ptr = Self :: from_usize ( ptr) ;
216
+ f ( & ptr)
179
217
}
180
218
}
0 commit comments