@@ -201,19 +201,68 @@ pub unsafe trait Erasable {
201
201
202
202
/// Unerase this erased pointer.
203
203
///
204
- /// Note that this _must_ be sound for erasable pointer types
205
- /// with both unique/write provenance and shared/read provenance.
206
- /// (In other words, both `&mut _` and `&_` can be unerased with this.)
204
+ /// Note that this _must_ be sound to roundtrip pointers with any provenance,
205
+ /// shared, unique, raw, frozen, mutable, and any valid combination thereof.
206
+ /// (In other words, `&mut _` and `&_` can be safely erased and unerased, and
207
+ /// any raw pointer should roundtrip without losing the provenance it had.)
207
208
///
208
209
/// Concretely, this means that the resulting pointer _must_ be derived
209
210
/// from the input pointer without any intervening references manifested.
210
- /// Temporary shared references may be used in the implementation,
211
- /// so long as the returned pointer is not derived from them.
211
+ /// Additionally, no references to the pointee _at all_ should be created,
212
+ /// as their mere temporary existence may impact the validity and
213
+ /// usable provenance of other pointers to the same location.
214
+ ///
215
+ /// Creating a shared reference sounds on the surface like it should be ok.
216
+ /// After all, you have a known-valid pointer to your type, and you can
217
+ /// borrow from whatever pointer was erased. However, in the face of raw
218
+ /// pointers with a shared mutable provenance, this is problematic.
219
+ /// If a write to the pointee location even potentially races with any
220
+ /// invocation of `unerase`, and it creates a reference to the location,
221
+ /// we have immediate undefined behavior for writing behind a shared ref.
222
+ ///
223
+ /// The root issue is that there may be external synchronization that this
224
+ /// implementation has no way of knowing about. An implementation of this
225
+ /// trait must only read the mimimum amount of data required to re-type the
226
+ /// pointer, and must do so with a raw pointer read, or, if and only if
227
+ /// there is a known `UnsafeCell` point (such as an atomic), a reference to
228
+ /// that `UnsafeCell` point and the safe API of that `UnsafeCell` point.
212
229
///
213
230
/// # Safety
214
231
///
215
232
/// The erased pointer must have been created by `erase`ing a valid pointer.
216
233
unsafe fn unerase ( this : ErasedPtr ) -> ptr:: NonNull < Self > ;
234
+
235
+ /// Whether this implementor has acknowledged the 1.1.0 update to
236
+ /// `unerase`'s documented implementation requirements.
237
+ ///
238
+ /// Prior to 1.0.0, creating a temporary shared reference (`&_`) in
239
+ /// `unerase` was explicitly listed as allowed, but for the 1.1.0 release
240
+ /// it was determined that this in fact can cause problems for some use
241
+ /// cases that `Erasable` is designed to support.
242
+ ///
243
+ /// Implementing this as `false` is _not allowed_ and is _not_ permission
244
+ /// to create references within `unerase`. It only exists as a way to
245
+ /// make the soundness fix in 1.1.0 disallowing references not breaking.
246
+ /// You _must_ override this with a value of `true` and follow the current
247
+ /// documented requirements for `unerase`, and not create references.
248
+ ///
249
+ /// If your use of `unerase` would be problematic if it creates a temporary
250
+ /// shared reference, you should assert that this value is `true`.
251
+ /// Not doing so will expose you to potentially unsound implementations
252
+ /// written against 1.0.0 before the reference clarification was made.
253
+ ///
254
+ /// The environment variable `ACK_1_1_0` can be set to enforce that all
255
+ /// implementors have provided an override for this acknowledgement.
256
+ #[ cfg( not( enforce_1_1_0_semantics) ) ]
257
+ const ACK_1_1_0 : bool = false ;
258
+
259
+ /// Whether this implementor has acknowledged the 1.1.0 update to
260
+ /// `unerase`'s documented implementation requirements.
261
+ ///
262
+ /// Implementing this as `false` is _not allowed_.
263
+ /// You _must_ override this with a value of `true`.
264
+ #[ cfg( enforce_1_1_0_semantics) ]
265
+ const ACK_1_1_0 : bool ;
217
266
}
218
267
219
268
/// Erase a pointer.
@@ -557,6 +606,8 @@ unsafe impl<T: Sized> Erasable for T {
557
606
// SAFETY: must not read the pointer for the safety of the impl directly below.
558
607
this. cast ( )
559
608
}
609
+
610
+ const ACK_1_1_0 : bool = true ;
560
611
}
561
612
562
613
// ~~~ impl ErasablePtr ~~~ //
0 commit comments