-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Fix IndexOfAnyInRange inconsistent behavior with invalid ranges across vectorized/scalar paths #123365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request fixes a bug in IndexOfAnyInRange and related methods where they behave inconsistently when highInclusive < lowInclusive depending on whether hardware intrinsics are enabled. The fix adds early validation checks in the vectorized code paths to handle invalid ranges consistently.
Changes:
- Added early validation checks in
NonPackedIndexOfAnyInRangeUnsignedNumberandLastIndexOfAnyInRangeUnsignedNumberto detect invalid ranges (wherehighInclusive < lowInclusive) - Added comprehensive tests covering invalid range scenarios for all affected APIs and data types
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs | Added invalid range checks at the beginning of NonPackedIndexOfAnyInRangeUnsignedNumber and LastIndexOfAnyInRangeUnsignedNumber methods to return -1 for In-Range variants and first/last index for Except-Range variants |
| src/libraries/System.Memory/tests/Span/IndexOfAnyInRange.cs | Added two comprehensive test methods to verify invalid range behavior across all data types and vector sizes |
|
@copilot linux-arm32 failed with: linux-arm32 has no intrinsics so it executed the non-vectorized path. Please change default value of |
Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com>
... Fixed in commit b3053c3. Added invalid range checks to the IComparable-based methods ( |
src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Head branch was pushed to by a user without write access
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
|
@copilot there are failures related to this change: Can you please address the failures? |
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
... Fixed in commit 058b758. The issue was that test values like |
src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs
Outdated
Show resolved
Hide resolved
…ns.cs Co-authored-by: Gregory Bell <65985703+grbell-ms@users.noreply.github.com>
Description
IndexOfAnyInRangeand related APIs return inconsistent results for invalid ranges (wherehighInclusive < lowInclusive) depending on whether hardware intrinsics are enabled. The vectorized path computeshighInclusive - lowInclusive, which wraps to a large positive value for unsigned types, causing incorrect matches.Changes
MemoryExtensions.cs: Added early validation in the 4 public entry points to detect
highInclusive < lowInclusivebefore any path selection:IndexOfAnyInRange<T>- Returns -1 (no value matches invalid range)IndexOfAnyExceptInRange<T>- Returns -1 for empty spans, 0 for non-empty spans (all values outside invalid range)LastIndexOfAnyInRange<T>- Returns -1 (no value matches invalid range)LastIndexOfAnyExceptInRange<T>- Returns -1 for empty spans, span.Length - 1 for non-empty spans (all values outside invalid range)All checks use
lowInclusive.CompareTo(highInclusive) > 0to work correctly with all IComparable types, and usespan.IsEmptyfor better readability. This approach centralizes validation at the public API boundary, eliminating the need for duplicate checks across 6 internal helper methods.IndexOfAnyInRange.cs: Added comprehensive tests covering invalid ranges for all supported types (byte, sbyte, short, ushort, char, int, uint, long, ulong, nint, nuint, TimeSpan) across both vectorized and scalar code paths:
InvalidRange_HighLessThanLow_ReturnsMinus1- [Theory] with 5 test cases using values that work correctly for all types: off-by-one errors (100, 99), (50, 49), and (64, 63), and invalid ranges (100, 50) and (10, 5)InvalidRange_SpecificValue_DoesNotMatch- Tests invalid range scenario with values (120, 100) that avoid wrapping for all numeric typesInvalidRange_WithNegativeValues_ReturnsMinus1- Signed-type-specific tests for negative values (0, -1) and (-10, -20) in SByte, Int16, Int32, Int64, and IntPtr test classeslowInclusive == highInclusivecaseTesting
Tests validate consistent behavior across input lengths 1-64, covering both intrinsic-enabled (vectorized) and intrinsic-disabled (scalar) scenarios. The Theory-based test approach provides comprehensive edge case coverage including off-by-one errors and negative value handling. Test values were carefully chosen to avoid integer overflow/wrapping when converted to specific numeric types (e.g., -1 wrapping to 255 for byte), ensuring tests validate invalid ranges correctly for all types. All tests now pass on platforms with and without hardware intrinsics support.
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.