Skip to content

Commit bff32ed

Browse files
committed
mark specialization trait as unsafe and add safety comments
1 parent 5b39ec8 commit bff32ed

File tree

1 file changed

+28
-5
lines changed

1 file changed

+28
-5
lines changed

library/core/src/iter/adapters/chain.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -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

313336
trait SpecChain: Iterator {
314337
fn next(&mut self) -> Option<Self::Item>;
@@ -329,13 +352,13 @@ impl<A, B> SpecChain for Chain<A, B>
329352
where
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

Comments
 (0)