@@ -83,9 +83,15 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
83
83
#[ inline]
84
84
#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
85
85
pub fn force ( this : & LazyCell < T , F > ) -> & T {
86
+ // SAFETY:
87
+ // This invalidates any mutable references to the data. The resulting
88
+ // reference lives either until the end of the borrow of `this` (in the
89
+ // initialized case) or is invalidates in `really_init` (in the
90
+ // uninitialized case).
86
91
let state = unsafe { & * this. state . get ( ) } ;
87
92
match state {
88
93
State :: Init ( data) => data,
94
+ // SAFETY: The state is uninitialized.
89
95
State :: Uninit ( _) => unsafe { LazyCell :: really_init ( this) } ,
90
96
State :: Poisoned => panic ! ( "LazyCell has previously been poisoned" ) ,
91
97
}
@@ -95,21 +101,30 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
95
101
/// May only be called when the state is `Uninit`.
96
102
#[ cold]
97
103
unsafe fn really_init ( this : & LazyCell < T , F > ) -> & T {
104
+ // SAFETY:
105
+ // This function is only called when the state is uninitialized,
106
+ // so no references to `state` can exist except for the reference
107
+ // in `force`, which is invalidated here and not accessed again.
98
108
let state = unsafe { & mut * this. state . get ( ) } ;
99
109
// Temporarily mark the state as poisoned. This prevents reentrant
100
110
// accesses and correctly poisons the cell if the closure panicked.
101
111
let State :: Uninit ( f) = mem:: replace ( state, State :: Poisoned ) else { unreachable ! ( ) } ;
102
112
103
113
let data = f ( ) ;
104
114
105
- // If the closure accessed the cell, the mutable borrow will be
106
- // invalidated, so create a new one here.
115
+ // SAFETY:
116
+ // If the closure accessed the cell through something like a reentrant
117
+ // mutex, but caught the panic resulting from the state being poisoned,
118
+ // the mutable borrow for `state` will be invalidated, so create a new
119
+ // one here.
107
120
let state = unsafe { & mut * this. state . get ( ) } ;
108
121
* state = State :: Init ( data) ;
109
122
110
- // A reference obtained by downcasting from the mutable borrow
111
- // would become stale if other references are created in `force`.
112
- // Borrow the state directly instead.
123
+ // SAFETY:
124
+ // A reference obtained by downcasting from the mutable borrow would
125
+ // become stale the next time `force` is called (since there is a conflict
126
+ // between the mutable reference here and the shared reference there).
127
+ // Do a new shared borrow of the state instead.
113
128
let state = unsafe { & * this. state . get ( ) } ;
114
129
let State :: Init ( data) = state else { unreachable ! ( ) } ;
115
130
data
@@ -119,6 +134,10 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
119
134
impl < T , F > LazyCell < T , F > {
120
135
#[ inline]
121
136
fn get ( & self ) -> Option < & T > {
137
+ // SAFETY:
138
+ // This is sound for the same reason as in `force`: once the state is
139
+ // initialized, it will not be mutably accessed again, so this reference
140
+ // will stay valid for the duration of the borrow to `self`.
122
141
let state = unsafe { & * self . state . get ( ) } ;
123
142
match state {
124
143
State :: Init ( data) => Some ( data) ,
0 commit comments