Skip to content

Commit 7a632ba

Browse files
authored
Merge 804cf26 into daf5985
2 parents daf5985 + 804cf26 commit 7a632ba

File tree

3 files changed

+55
-14
lines changed

3 files changed

+55
-14
lines changed

library/alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
#![feature(hashmap_internals)]
174174
#![feature(intrinsics)]
175175
#![feature(lang_items)]
176+
#![feature(let_chains)]
176177
#![feature(min_specialization)]
177178
#![feature(multiple_supertrait_upcastable)]
178179
#![feature(negative_impls)]

library/alloc/src/vec/spec_from_iter_nested.rs

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,45 @@ where
2222
// empty, but the loop in extend_desugared() is not going to see the
2323
// vector being full in the few subsequent loop iterations.
2424
// So we get better branch prediction.
25-
let mut vector = match iterator.next() {
26-
None => return Vec::new(),
27-
Some(element) => {
28-
let (lower, _) = iterator.size_hint();
29-
let initial_capacity =
30-
cmp::max(RawVec::<T>::MIN_NON_ZERO_CAP, lower.saturating_add(1));
31-
let mut vector = Vec::with_capacity(initial_capacity);
32-
unsafe {
33-
// SAFETY: We requested capacity at least 1
34-
ptr::write(vector.as_mut_ptr(), element);
35-
vector.set_len(1);
36-
}
37-
vector
38-
}
25+
let (low, high) = iterator.size_hint();
26+
assert!(
27+
high.is_none_or(|high| low <= high),
28+
"size_hint ({low:?}, {high:?}) is malformed from iterator {} collecting into {}",
29+
core::any::type_name::<I>(), core::any::type_name::<Self>(),
30+
);
31+
32+
let Some(first) = iterator.next() else {
33+
return Vec::new();
34+
};
35+
// `push`'s growth strategy is (currently) to double the capacity if
36+
// there's no space available, so it can have up to 50% "wasted" space.
37+
// Thus if the upper-bound on the size_hint also wouldn't waste more
38+
// than that, just allocate it from the start. (After all, it's silly
39+
// to allocate 254 for a hint of `(254, Some(255)`.)
40+
let initial_capacity = {
41+
// This is written like this to not overflow on any well-behaved iterator,
42+
// even things like `repeat_n(val, isize::MAX as usize + 10)`
43+
// where `low * 2` would need checking.
44+
// A bad (but safe) iterator might have `low > high`, but if so it'll
45+
// produce a huge `extra` that'll probably fail the following check.
46+
let hint = if let Some(high) = high
47+
&& let extra = high - low
48+
&& extra < low
49+
{
50+
high
51+
} else {
52+
low
53+
};
54+
cmp::max(RawVec::<T>::MIN_NON_ZERO_CAP, hint)
3955
};
56+
let mut vector = Vec::with_capacity(initial_capacity);
57+
// SAFETY: We requested capacity at least MIN_NON_ZERO_CAP, which
58+
// is never zero, so there's space for at least one element.
59+
unsafe {
60+
ptr::write(vector.as_mut_ptr(), first);
61+
vector.set_len(1);
62+
}
63+
4064
// must delegate to spec_extend() since extend() itself delegates
4165
// to spec_from for empty Vecs
4266
<Vec<T> as SpecExtend<T, I>>::spec_extend(&mut vector, iterator);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//@ compile-flags: -Copt-level=3
2+
#![crate_type = "lib"]
3+
4+
#[no_mangle]
5+
pub fn should_use_low(a: [i32; 10], b: [i32; 100], p: fn(i32) -> bool) -> Vec<i32> {
6+
// CHECK-LABEL: define void @should_use_low
7+
// CHECK: call{{.+}}dereferenceable_or_null(40){{.+}}@__rust_alloc(
8+
a.iter().copied().chain(b.iter().copied().filter(|x| p(*x))).collect()
9+
}
10+
11+
#[no_mangle]
12+
pub fn should_use_high(a: [i32; 100], b: [i32; 10], p: fn(i32) -> bool) -> Vec<i32> {
13+
// CHECK-LABEL: define void @should_use_high
14+
// CHECK: call{{.+}}dereferenceable_or_null(440){{.+}}@__rust_alloc(
15+
a.iter().copied().chain(b.iter().copied().filter(|x| p(*x))).collect()
16+
}

0 commit comments

Comments
 (0)