Skip to content

Commit 803ddb8

Browse files
committed
Auto merge of #83726 - the8472:large-trustedlen-fail-fast, r=kennytm
panic early when `TrustedLen` indicates a `length > usize::MAX` Changes `TrustedLen` specializations to immediately panic when `size_hint().1 == None`. As far as I can tell this is ~not a change~ a minimal change in observable behavior for anything except ZSTs because the fallback path would go through `extend_desugared()` which tries to `reserve(lower_bound)` which already is `usize::MAX` and that would also lead to a panic. Before it might have popped somewhere between zero and a few elements from the iterator before panicking while it now panics immediately. Overall this should reduce codegen by eliminating the fallback paths. While looking into the `with_capacity()` behavior I also noticed that its documentation didn't have a *Panics* section, so I added that.
2 parents 49e1ec0 + ad3a791 commit 803ddb8

File tree

5 files changed

+30
-8
lines changed

5 files changed

+30
-8
lines changed

library/alloc/src/rc.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1848,8 +1848,11 @@ impl<T, I: iter::TrustedLen<Item = T>> ToRcSlice<T> for I {
18481848
Rc::from_iter_exact(self, low)
18491849
}
18501850
} else {
1851-
// Fall back to normal implementation.
1852-
self.collect::<Vec<T>>().into()
1851+
// TrustedLen contract guarantees that `upper_bound == `None` implies an iterator
1852+
// length exceeding `usize::MAX`.
1853+
// The default implementation would collect into a vec which would panic.
1854+
// Thus we panic here immediately without invoking `Vec` code.
1855+
panic!("capacity overflow");
18531856
}
18541857
}
18551858
}

library/alloc/src/sync.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -2481,8 +2481,11 @@ impl<T, I: iter::TrustedLen<Item = T>> ToArcSlice<T> for I {
24812481
Arc::from_iter_exact(self, low)
24822482
}
24832483
} else {
2484-
// Fall back to normal implementation.
2485-
self.collect::<Vec<T>>().into()
2484+
// TrustedLen contract guarantees that `upper_bound == `None` implies an iterator
2485+
// length exceeding `usize::MAX`.
2486+
// The default implementation would collect into a vec which would panic.
2487+
// Thus we panic here immediately without invoking `Vec` code.
2488+
panic!("capacity overflow");
24862489
}
24872490
}
24882491
}

library/alloc/src/vec/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,10 @@ impl<T> Vec<T> {
410410
///
411411
/// [Capacity and reallocation]: #capacity-and-reallocation
412412
///
413+
/// # Panics
414+
///
415+
/// Panics if the new capacity exceeds `isize::MAX` bytes.
416+
///
413417
/// # Examples
414418
///
415419
/// ```
@@ -541,6 +545,10 @@ impl<T, A: Allocator> Vec<T, A> {
541545
///
542546
/// [Capacity and reallocation]: #capacity-and-reallocation
543547
///
548+
/// # Panics
549+
///
550+
/// Panics if the new capacity exceeds `isize::MAX` bytes.
551+
///
544552
/// # Examples
545553
///
546554
/// ```

library/alloc/src/vec/spec_extend.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ where
4747
});
4848
}
4949
} else {
50-
self.extend_desugared(iterator)
50+
// Per TrustedLen contract a `None` upper bound means that the iterator length
51+
// truly exceeds usize::MAX, which would eventually lead to a capacity overflow anyway.
52+
// Since the other branch already panics eagerly (via `reserve()`) we do the same here.
53+
// This avoids additional codegen for a fallback code path which would eventually
54+
// panic anyway.
55+
panic!("capacity overflow");
5156
}
5257
}
5358
}

library/alloc/src/vec/spec_from_iter_nested.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,13 @@ where
4646
fn from_iter(iterator: I) -> Self {
4747
let mut vector = match iterator.size_hint() {
4848
(_, Some(upper)) => Vec::with_capacity(upper),
49-
_ => Vec::new(),
49+
// TrustedLen contract guarantees that `size_hint() == (_, None)` means that there
50+
// are more than `usize::MAX` elements.
51+
// Since the previous branch would eagerly panic if the capacity is too large
52+
// (via `with_capacity`) we do the same here.
53+
_ => panic!("capacity overflow"),
5054
};
51-
// must delegate to spec_extend() since extend() itself delegates
52-
// to spec_from for empty Vecs
55+
// reuse extend specialization for TrustedLen
5356
vector.spec_extend(iterator);
5457
vector
5558
}

0 commit comments

Comments
 (0)