From efc53929f761125f5d7d16fcd30c138e7bf81e2e Mon Sep 17 00:00:00 2001 From: chiri Date: Fri, 12 Jun 2026 23:08:09 +0300 Subject: [PATCH 01/14] add `assert_unchecked` to `BoundTupleIterator` and `BorrowedTupleIterator` `len()` --- src/types/tuple.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 08256d7156d..25c63883598 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -510,6 +510,7 @@ impl DoubleEndedIterator for BoundTupleIterator<'_> { impl ExactSizeIterator for BoundTupleIterator<'_> { fn len(&self) -> usize { + unsafe { std::hint::assert_unchecked(self.index <= self.length) }; self.length.saturating_sub(self.index) } } @@ -608,6 +609,7 @@ impl DoubleEndedIterator for BorrowedTupleIterator<'_, '_> { impl ExactSizeIterator for BorrowedTupleIterator<'_, '_> { fn len(&self) -> usize { + unsafe { std::hint::assert_unchecked(self.index <= self.length) }; self.length.saturating_sub(self.index) } } From a812a1aa691f376b55b85d316d311a2d45f2821a Mon Sep 17 00:00:00 2001 From: chiri Date: Fri, 12 Jun 2026 23:11:56 +0300 Subject: [PATCH 02/14] add SAFETY --- src/types/tuple.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 25c63883598..8652f8e58a6 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -510,6 +510,7 @@ impl DoubleEndedIterator for BoundTupleIterator<'_> { impl ExactSizeIterator for BoundTupleIterator<'_> { fn len(&self) -> usize { + // SAFETY: `index <= length` is maintained by all iterator methods: `new()`, `next_back()` unsafe { std::hint::assert_unchecked(self.index <= self.length) }; self.length.saturating_sub(self.index) } @@ -609,6 +610,7 @@ impl DoubleEndedIterator for BorrowedTupleIterator<'_, '_> { impl ExactSizeIterator for BorrowedTupleIterator<'_, '_> { fn len(&self) -> usize { + // SAFETY: same invariant as BoundTupleIterator unsafe { std::hint::assert_unchecked(self.index <= self.length) }; self.length.saturating_sub(self.index) } From 738f8a5d8e6a353d9a399031832f57aa413ea114 Mon Sep 17 00:00:00 2001 From: chiri Date: Fri, 12 Jun 2026 23:25:03 +0300 Subject: [PATCH 03/14] fix clippy --- src/types/tuple.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 8652f8e58a6..1bac2fd620b 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -511,7 +511,7 @@ impl DoubleEndedIterator for BoundTupleIterator<'_> { impl ExactSizeIterator for BoundTupleIterator<'_> { fn len(&self) -> usize { // SAFETY: `index <= length` is maintained by all iterator methods: `new()`, `next_back()` - unsafe { std::hint::assert_unchecked(self.index <= self.length) }; + unsafe { core::hint::assert_unchecked(self.index <= self.length) }; self.length.saturating_sub(self.index) } } @@ -611,7 +611,7 @@ impl DoubleEndedIterator for BorrowedTupleIterator<'_, '_> { impl ExactSizeIterator for BorrowedTupleIterator<'_, '_> { fn len(&self) -> usize { // SAFETY: same invariant as BoundTupleIterator - unsafe { std::hint::assert_unchecked(self.index <= self.length) }; + unsafe { core::hint::assert_unchecked(self.index <= self.length) }; self.length.saturating_sub(self.index) } } From 33a06e62cdd93eff9be7b2cd29435afede3706b8 Mon Sep 17 00:00:00 2001 From: chiri Date: Sat, 13 Jun 2026 00:16:23 +0300 Subject: [PATCH 04/14] more `core::hint::assert_unchecked` --- src/conversions/jiff.rs | 20 ++++++++++++++++---- src/conversions/num_bigint.rs | 5 ++++- src/types/list.rs | 3 +++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/conversions/jiff.rs b/src/conversions/jiff.rs index 593811f7641..d5dbe208d44 100644 --- a/src/conversions/jiff.rs +++ b/src/conversions/jiff.rs @@ -68,6 +68,9 @@ fn datetime_to_pydatetime<'py>( fold: bool, timezone: Option<&TimeZone>, ) -> PyResult> { + let micros = datetime.subsec_nanosecond() / 1000; + // SAFETY: `subsec_nanosecond()` [0, 999_999_999], after / 1000 always non-negative + unsafe { core::hint::assert_unchecked(micros >= 0) }; PyDateTime::new_with_fold( py, datetime.year().into(), @@ -76,7 +79,7 @@ fn datetime_to_pydatetime<'py>( datetime.hour().try_into()?, datetime.minute().try_into()?, datetime.second().try_into()?, - (datetime.subsec_nanosecond() / 1000).try_into()?, + micros as u32, timezone .map(|tz| tz.into_pyobject(py)) .transpose()? @@ -87,10 +90,17 @@ fn datetime_to_pydatetime<'py>( #[cfg(not(Py_LIMITED_API))] fn pytime_to_time(time: &impl PyTimeAccess) -> PyResult