Skip to content

Commit 5db57c8

Browse files
authored
Unrolled build for #155858
Rollup merge of #155858 - ChrisDenton:borrowed-len, r=jhpratt `slice::join`: borrow only once during length calc This ensures the length calculation will always be self-consistent even if the real length changes when we actually come to use them. This is a follow up to #155708
2 parents 4ddb0b7 + b7424ba commit 5db57c8

2 files changed

Lines changed: 15 additions & 14 deletions

File tree

library/alloc/src/str.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,9 @@ macro_rules! copy_slice_and_advance {
135135
//
136136
// This implementation calls `borrow()` multiple times:
137137
// 1. To calculate `reserved_len`, all elements are borrowed once.
138-
// 2. The first element is borrowed again when copied via `extend_from_slice`.
139-
// 3. Subsequent elements are borrowed a second time when building the mapped iterator.
138+
// 2. All elements, except the first, are borrowed a second time when building the mapped iterator.
140139
//
141140
// Risks and Mitigations:
142-
// - If the first element GROWS on the second borrow, the length subtraction underflows.
143-
// We mitigate this by doing a `checked_sub` to panic rather than allowing an underflow
144-
// that fabricates a huge destination slice.
145141
// - If elements 2..N GROW on their second borrow, the target slice bounds set by `checked_sub`
146142
// means that `split_at_mut` inside `copy_slice_and_advance!` will correctly panic.
147143
// - If elements SHRINK on their second borrow, the spare space is never written, and the final
@@ -157,8 +153,10 @@ where
157153
let mut iter = slice.iter();
158154

159155
// the first slice is the only one without a separator preceding it
156+
// we take care to only borrow this once during the length calculation
157+
// to avoid inconsistent Borrow implementations from breaking our assumptions
160158
let first = match iter.next() {
161-
Some(first) => first,
159+
Some(first) => first.borrow().as_ref(),
162160
None => return vec![],
163161
};
164162

@@ -168,21 +166,24 @@ where
168166
// the entire Vec pre-allocated for safety
169167
let reserved_len = sep_len
170168
.checked_mul(iter.len())
169+
.and_then(|n| n.checked_add(first.len()))
171170
.and_then(|n| {
172-
slice.iter().map(|s| s.borrow().as_ref().len()).try_fold(n, usize::checked_add)
171+
// iter starts from the second element as we've already taken the first
172+
// it's cloned so we can reuse the same iterator below
173+
iter.clone().map(|s| s.borrow().as_ref().len()).try_fold(n, usize::checked_add)
173174
})
174175
.expect("attempt to join into collection with len > usize::MAX");
175176

176177
// prepare an uninitialized buffer
177178
let mut result = Vec::with_capacity(reserved_len);
178179
debug_assert!(result.capacity() >= reserved_len);
179180

180-
result.extend_from_slice(first.borrow().as_ref());
181+
result.extend_from_slice(first);
181182

182183
unsafe {
183184
let pos = result.len();
184-
let target_len = reserved_len.checked_sub(pos).expect("inconsistent Borrow implementation");
185-
let target = result.spare_capacity_mut().get_unchecked_mut(..target_len);
185+
debug_assert!(reserved_len >= pos);
186+
let target = result.spare_capacity_mut().get_unchecked_mut(..reserved_len - pos);
186187

187188
// Convert the separator and slices to slices of MaybeUninit
188189
// to simplify implementation in specialize_for_lengths.

library/alloctests/tests/str.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ fn test_join_for_different_lengths_with_long_separator() {
164164
}
165165

166166
#[test]
167-
fn test_join_issue_80335() {
167+
fn test_join_inconsistent_borrow_shrink() {
168168
use core::borrow::Borrow;
169169
use core::cell::Cell;
170170

@@ -191,12 +191,12 @@ fn test_join_issue_80335() {
191191
}
192192

193193
let arr: [WeirdBorrow; 3] = Default::default();
194-
test_join!("0-0-0", arr, "-");
194+
test_join!("123456-0-0", arr, "-");
195195
}
196196

197197
#[test]
198-
#[should_panic(expected = "inconsistent Borrow implementation")]
199-
fn test_join_inconsistent_borrow() {
198+
#[should_panic(expected = "mid > len")]
199+
fn test_join_inconsistent_borrow_grow() {
200200
use std::borrow::Borrow;
201201
use std::cell::Cell;
202202

0 commit comments

Comments
 (0)