Skip to content

Commit e5897a1

Browse files
Add faillible version of size_hint to properly handle recursive structures
The default implementation forwards to the current size_hint, so that it can be relied upon also for structures that still use the default implementation. The default implementation of `size_hint` is not changed, so that it does not break existing implementations
1 parent 17647a1 commit e5897a1

File tree

16 files changed

+384
-81
lines changed

16 files changed

+384
-81
lines changed

derive/src/lib.rs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -364,27 +364,27 @@ fn gen_size_hint_method(input: &DeriveInput) -> Result<TokenStream> {
364364
determine_field_constructor(f).map(|field_constructor| {
365365
match field_constructor {
366366
FieldConstructor::Default | FieldConstructor::Value(_) => {
367-
quote!((0, Some(0)))
367+
quote!(Ok((0, Some(0))))
368368
}
369369
FieldConstructor::Arbitrary => {
370-
quote! { <#ty as arbitrary::Arbitrary>::size_hint(depth) }
370+
quote! { <#ty as arbitrary::Arbitrary>::try_size_hint(depth) }
371371
}
372372

373373
// Note that in this case it's hard to determine what size_hint must be, so size_of::<T>() is
374374
// just an educated guess, although it's gonna be inaccurate for dynamically
375375
// allocated types (Vec, HashMap, etc.).
376376
FieldConstructor::With(_) => {
377-
quote! { (::core::mem::size_of::<#ty>(), None) }
377+
quote! { Ok((::core::mem::size_of::<#ty>(), None)) }
378378
}
379379
}
380380
})
381381
})
382382
.collect::<Result<Vec<TokenStream>>>()
383383
.map(|hints| {
384384
quote! {
385-
arbitrary::size_hint::and_all(&[
386-
#( #hints ),*
387-
])
385+
Ok(arbitrary::size_hint::and_all(&[
386+
#( #hints? ),*
387+
]))
388388
}
389389
})
390390
};
@@ -393,7 +393,12 @@ fn gen_size_hint_method(input: &DeriveInput) -> Result<TokenStream> {
393393
quote! {
394394
#[inline]
395395
fn size_hint(depth: usize) -> (usize, ::core::option::Option<usize>) {
396-
arbitrary::size_hint::recursion_guard(depth, |depth| #hint)
396+
Self::try_size_hint(depth).unwrap_or_default()
397+
}
398+
399+
#[inline]
400+
fn try_size_hint(depth: usize) -> Result<(usize, ::core::option::Option<usize>), arbitrary::MaxRecursionReached> {
401+
arbitrary::size_hint::try_recursion_guard(depth, |depth| #hint)
397402
}
398403
}
399404
})
@@ -413,14 +418,17 @@ fn gen_size_hint_method(input: &DeriveInput) -> Result<TokenStream> {
413418
.collect::<Result<Vec<TokenStream>>>()
414419
.map(|variants| {
415420
quote! {
416-
#[inline]
417421
fn size_hint(depth: usize) -> (usize, ::core::option::Option<usize>) {
418-
arbitrary::size_hint::and(
419-
<u32 as arbitrary::Arbitrary>::size_hint(depth),
420-
arbitrary::size_hint::recursion_guard(depth, |depth| {
421-
arbitrary::size_hint::or_all(&[ #( #variants ),* ])
422-
}),
423-
)
422+
Self::try_size_hint(depth).unwrap_or_default()
423+
}
424+
#[inline]
425+
fn try_size_hint(depth: usize) -> Result<(usize, ::core::option::Option<usize>), arbitrary::MaxRecursionReached> {
426+
Ok(arbitrary::size_hint::and(
427+
<u32 as arbitrary::Arbitrary>::try_size_hint(depth)?,
428+
arbitrary::size_hint::try_recursion_guard(depth, |depth| {
429+
Ok(arbitrary::size_hint::or_all(&[ #( #variants? ),* ]))
430+
})?,
431+
))
424432
}
425433
}
426434
}),

src/foreign/alloc/borrow.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@ where
1414

1515
#[inline]
1616
fn size_hint(depth: usize) -> (usize, Option<usize>) {
17-
size_hint::recursion_guard(depth, |depth| {
18-
<<A as ToOwned>::Owned as Arbitrary>::size_hint(depth)
17+
Self::try_size_hint(depth).unwrap_or_default()
18+
}
19+
20+
#[inline]
21+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
22+
size_hint::try_recursion_guard(depth, |depth| {
23+
<<A as ToOwned>::Owned as Arbitrary>::try_size_hint(depth)
1924
})
2025
}
2126
}

src/foreign/alloc/boxed.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ where
1313

1414
#[inline]
1515
fn size_hint(depth: usize) -> (usize, Option<usize>) {
16-
size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
16+
Self::try_size_hint(depth).unwrap_or_default()
17+
}
18+
19+
#[inline]
20+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
21+
size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
1722
}
1823
}
1924

src/foreign/alloc/rc.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ where
1313

1414
#[inline]
1515
fn size_hint(depth: usize) -> (usize, Option<usize>) {
16-
size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
16+
Self::try_size_hint(depth).unwrap_or_default()
17+
}
18+
19+
#[inline]
20+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
21+
size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
1722
}
1823
}
1924

src/foreign/alloc/sync.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ where
1313

1414
#[inline]
1515
fn size_hint(depth: usize) -> (usize, Option<usize>) {
16-
size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
16+
Self::try_size_hint(depth).unwrap_or_default()
17+
}
18+
19+
#[inline]
20+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
21+
size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
1722
}
1823
}
1924

src/foreign/core/array.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,13 @@ where
6464
}
6565

6666
#[inline]
67-
fn size_hint(d: usize) -> (usize, Option<usize>) {
68-
size_hint::and_all(&array::from_fn::<_, N, _>(|_| {
69-
<T as Arbitrary>::size_hint(d)
70-
}))
67+
fn size_hint(depth: usize) -> (usize, Option<usize>) {
68+
Self::try_size_hint(depth).unwrap_or_default()
69+
}
70+
71+
#[inline]
72+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
73+
let hint = <T as Arbitrary>::try_size_hint(depth)?;
74+
Ok(size_hint::and_all(&array::from_fn::<_, N, _>(|_| hint)))
7175
}
7276
}

src/foreign/core/cell.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use {
2-
crate::{Arbitrary, Result, Unstructured},
2+
crate::{Arbitrary, MaxRecursionReached, Result, Unstructured},
33
core::cell::{Cell, RefCell, UnsafeCell},
44
};
55

@@ -13,7 +13,12 @@ where
1313

1414
#[inline]
1515
fn size_hint(depth: usize) -> (usize, Option<usize>) {
16-
<A as Arbitrary<'a>>::size_hint(depth)
16+
Self::try_size_hint(depth).unwrap_or_default()
17+
}
18+
19+
#[inline]
20+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
21+
<A as Arbitrary<'a>>::try_size_hint(depth)
1722
}
1823
}
1924

@@ -27,7 +32,12 @@ where
2732

2833
#[inline]
2934
fn size_hint(depth: usize) -> (usize, Option<usize>) {
30-
<A as Arbitrary<'a>>::size_hint(depth)
35+
Self::try_size_hint(depth).unwrap_or_default()
36+
}
37+
38+
#[inline]
39+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
40+
<A as Arbitrary<'a>>::try_size_hint(depth)
3141
}
3242
}
3343

@@ -41,6 +51,11 @@ where
4151

4252
#[inline]
4353
fn size_hint(depth: usize) -> (usize, Option<usize>) {
44-
<A as Arbitrary<'a>>::size_hint(depth)
54+
Self::try_size_hint(depth).unwrap_or_default()
55+
}
56+
57+
#[inline]
58+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
59+
<A as Arbitrary<'a>>::try_size_hint(depth)
4560
}
4661
}

src/foreign/core/num.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use {
2-
crate::{Arbitrary, Error, Result, Unstructured},
2+
crate::{Arbitrary, Error, MaxRecursionReached, Result, Unstructured},
33
core::{
44
mem,
55
num::{
@@ -131,6 +131,11 @@ where
131131

132132
#[inline]
133133
fn size_hint(depth: usize) -> (usize, Option<usize>) {
134-
<A as Arbitrary<'a>>::size_hint(depth)
134+
Self::try_size_hint(depth).unwrap_or_default()
135+
}
136+
137+
#[inline]
138+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
139+
<A as Arbitrary<'a>>::try_size_hint(depth)
135140
}
136141
}

src/foreign/core/ops.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use {
2-
crate::{size_hint, Arbitrary, Result, Unstructured},
2+
crate::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured},
33
core::{
44
mem,
55
ops::{Bound, Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive},
@@ -25,53 +25,57 @@ macro_rules! impl_range {
2525

2626
#[inline]
2727
fn size_hint(depth: usize) -> (usize, Option<usize>) {
28+
Self::try_size_hint(depth).unwrap_or_default()
29+
}
30+
31+
#[inline]
32+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
2833
#[allow(clippy::redundant_closure_call)]
2934
$size_hint_closure(depth)
3035
}
3136
}
3237
};
3338
}
34-
3539
impl_range!(
3640
Range<A>,
3741
|r: &Range<A>| (r.start.clone(), r.end.clone()),
3842
(A, A),
3943
bounded_range(|(a, b)| a..b),
40-
|depth| size_hint::and(
41-
<A as Arbitrary>::size_hint(depth),
42-
<A as Arbitrary>::size_hint(depth)
43-
)
44+
|depth| Ok(crate::size_hint::and(
45+
<A as Arbitrary>::try_size_hint(depth)?,
46+
<A as Arbitrary>::try_size_hint(depth)?,
47+
))
4448
);
4549
impl_range!(
4650
RangeFrom<A>,
4751
|r: &RangeFrom<A>| r.start.clone(),
4852
A,
4953
unbounded_range(|a| a..),
50-
|depth| <A as Arbitrary>::size_hint(depth)
54+
|depth| <A as Arbitrary>::try_size_hint(depth)
5155
);
5256
impl_range!(
5357
RangeInclusive<A>,
5458
|r: &RangeInclusive<A>| (r.start().clone(), r.end().clone()),
5559
(A, A),
5660
bounded_range(|(a, b)| a..=b),
57-
|depth| size_hint::and(
58-
<A as Arbitrary>::size_hint(depth),
59-
<A as Arbitrary>::size_hint(depth)
60-
)
61+
|depth| Ok(crate::size_hint::and(
62+
<A as Arbitrary>::try_size_hint(depth)?,
63+
<A as Arbitrary>::try_size_hint(depth)?,
64+
))
6165
);
6266
impl_range!(
6367
RangeTo<A>,
6468
|r: &RangeTo<A>| r.end.clone(),
6569
A,
6670
unbounded_range(|b| ..b),
67-
|depth| <A as Arbitrary>::size_hint(depth)
71+
|depth| <A as Arbitrary>::try_size_hint(depth)
6872
);
6973
impl_range!(
7074
RangeToInclusive<A>,
7175
|r: &RangeToInclusive<A>| r.end.clone(),
7276
A,
7377
unbounded_range(|b| ..=b),
74-
|depth| <A as Arbitrary>::size_hint(depth)
78+
|depth| <A as Arbitrary>::try_size_hint(depth)
7579
);
7680

7781
pub(crate) fn bounded_range<CB, I, R>(bounds: (I, I), cb: CB) -> R
@@ -110,9 +114,14 @@ where
110114

111115
#[inline]
112116
fn size_hint(depth: usize) -> (usize, Option<usize>) {
113-
size_hint::or(
114-
size_hint::and((1, Some(1)), A::size_hint(depth)),
117+
Self::try_size_hint(depth).unwrap_or_default()
118+
}
119+
120+
#[inline]
121+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
122+
Ok(size_hint::or(
123+
size_hint::and((1, Some(1)), A::try_size_hint(depth)?),
115124
(1, Some(1)),
116-
)
125+
))
117126
}
118127
}

src/foreign/core/option.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{size_hint, Arbitrary, Result, Unstructured};
1+
use crate::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured};
22

33
impl<'a, A> Arbitrary<'a> for Option<A>
44
where
@@ -14,9 +14,14 @@ where
1414

1515
#[inline]
1616
fn size_hint(depth: usize) -> (usize, Option<usize>) {
17-
size_hint::and(
18-
<bool as Arbitrary>::size_hint(depth),
19-
size_hint::or((0, Some(0)), <A as Arbitrary>::size_hint(depth)),
20-
)
17+
Self::try_size_hint(depth).unwrap_or_default()
18+
}
19+
20+
#[inline]
21+
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
22+
Ok(size_hint::and(
23+
<bool as Arbitrary>::try_size_hint(depth)?,
24+
size_hint::or((0, Some(0)), <A as Arbitrary>::try_size_hint(depth)?),
25+
))
2126
}
2227
}

0 commit comments

Comments
 (0)