11use crate :: arena:: Arena ;
2-
32use rustc_serialize:: { Encodable , Encoder } ;
4-
53use std:: alloc:: Layout ;
64use std:: cmp:: Ordering ;
75use std:: fmt;
@@ -12,49 +10,69 @@ use std::ops::Deref;
1210use std:: ptr;
1311use std:: slice;
1412
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 .
3129#[ repr( C ) ]
3230pub struct List < T > {
3331 len : usize ,
32+
33+ /// Although this claims to be a zero-length array, in practice `len`
34+ /// elements are actually present.
3435 data : [ T ; 0 ] ,
36+
3537 opaque : OpaqueListContents ,
3638}
3739
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 ;
5344}
5445
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+ }
5665
5766impl < 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()`).
5876 #[ inline]
5977 pub ( super ) fn from_arena < ' tcx > ( arena : & ' tcx Arena < ' tcx > , slice : & [ T ] ) -> & ' tcx List < T > {
6078 assert ! ( !mem:: needs_drop:: <T >( ) ) ;
@@ -73,7 +91,7 @@ impl<T: Copy> List<T> {
7391 . cast :: < T > ( )
7492 . copy_from_nonoverlapping ( slice. as_ptr ( ) , slice. len ( ) ) ;
7593
76- & mut * mem
94+ & * mem
7795 }
7896 }
7997
@@ -107,11 +125,24 @@ impl<S: Encoder, T: Encodable<S>> Encodable<S> for &List<T> {
107125 }
108126}
109127
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+
110139impl < T > Ord for List < T >
111140where
112141 T : Ord ,
113142{
114143 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.
115146 if self == other { Ordering :: Equal } else { <[ T ] as Ord >:: cmp ( & * * self , & * * other) }
116147 }
117148}
@@ -121,6 +152,8 @@ where
121152 T : PartialOrd ,
122153{
123154 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.
124157 if self == other {
125158 Some ( Ordering :: Equal )
126159 } else {
@@ -129,17 +162,11 @@ where
129162 }
130163}
131164
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-
140165impl < T > Hash for List < T > {
141166 #[ inline]
142167 fn hash < H : Hasher > ( & self , s : & mut H ) {
168+ // Pointer hashing is sufficient (due to the unique contents
169+ // assumption).
143170 ( self as * const List < T > ) . hash ( s)
144171 }
145172}
@@ -168,13 +195,24 @@ impl<'a, T: Copy> IntoIterator for &'a List<T> {
168195 }
169196}
170197
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)
179217 }
180218}
0 commit comments