@@ -305,10 +305,33 @@ fn and_then_or_clear<T, U>(opt: &mut Option<T>, f: impl FnOnce(&mut T) -> Option
305305 x
306306}
307307
308+ /// Marks the two generic parameters of Chain as sufficiently equal that their values can be swapped
309+ ///
310+ /// # Safety
311+ ///
312+ /// This would be trivially safe if both types were identical, including lifetimes.
313+ /// However we can't specify bounds like that and it would be overly restrictive since it's not
314+ /// uncommon for borrowing iterators to have slightly different lifetimes.
315+ ///
316+ /// We can relax this by only requiring that the base struct type is the same while ignoring
317+ /// lifetime parameters as long as
318+ /// * the actual runtime lifespan of the values is capped by the shorter of the two lifetimes
319+ /// * all invoked trait methods (and drop code) monomorphize down to the same code
308320#[ rustc_unsafe_specialization_marker]
309- trait SymmetricalArms { }
310-
311- impl < A > SymmetricalArms for Chain < A , A > { }
321+ unsafe trait SymmetricalModuloLifetimes { }
322+
323+ /// Safety:
324+ /// * <A, A> ensures that the basic type is the same
325+ /// * actual lifespan of the values is capped by the combined lifetime of Chain's fields as long as
326+ /// there is no way to destructure Chain into. I.e. Chain must not implement `SourceIter`,
327+ /// `into_parts(self)` or similar methods.
328+ /// * we rely on the language currently having no mechanism that would allow lifetime-dependent
329+ /// code paths. Specialization forbids `where T: 'static` and similar bounds (modulo the exposed
330+ /// `#[rustc_unsafe_specialization_marker]` traits).
331+ /// And any trait depending on `Any` would have to be 'static in *both* arms to make a useful Chain.
332+ /// This is only true as long as *all* impls on `Chain` have the same bounds for A and B,
333+ /// which currently is the case.
334+ unsafe impl < A > SymmetricalModuloLifetimes for Chain < A , A > { }
312335
313336trait SpecChain : Iterator {
314337 fn next ( & mut self ) -> Option < Self :: Item > ;
@@ -329,13 +352,13 @@ impl<A, B> SpecChain for Chain<A, B>
329352where
330353 A : Iterator ,
331354 B : Iterator < Item = A :: Item > ,
332- Self : SymmetricalArms ,
355+ Self : SymmetricalModuloLifetimes ,
333356{
334357 #[ inline]
335358 fn next ( & mut self ) -> Option < A :: Item > {
336359 let mut result = and_then_or_clear ( & mut self . a , Iterator :: next) ;
337360 if result. is_none ( ) {
338- // SAFETY: SymmetricalArms guarantees that A and B are the same type.
361+ // SAFETY: SymmetricalModuloLifetimes guarantees that A and B are safe to swap
339362 unsafe { mem:: swap ( & mut self . a , & mut * ( & mut self . b as * mut _ as * mut Option < A > ) ) } ;
340363 result = and_then_or_clear ( & mut self . a , Iterator :: next) ;
341364 }
0 commit comments