Uh oh!
There was an error while loading. Please reload this page.
[release/9.0-staging] Fix LINQ handling of iterator.Take(...).Last(...) - #112714
Conversation
When the Take amount is larger than the number of elements in the source `Iterator<T>`, Last ends up throwing an exception and LastOrDefault ends up returning the default value, rather than returning the last value in the taken region. As part of fixing this, I sured up the tests to try to cover more such sequences of operations. In doing so, the tests got a lot slower, so I tracked down and fixed places where we were doing a lot of unnecessary work.
Tagging subscribers to this area: @dotnet/area-system-linq |
There was a problem hiding this comment.
PR Overview
This pull request backports a fix to the LINQ implementation so that calls to iterator.Take(...).Last(...) now correctly return the actual last element rather than a default value or throwing an exception. Key changes include:
- Adjusting a condition in the SkipTake speed‐optimized code to correctly select the proper branch when handling the last element.
- Updating internal transformation helpers and test cases to use the new CreateSources method instead of IdentityTransforms.
- Revising various test methods to improve coverage and consistency across different enumerable scenarios.
Reviewed Changes
| File | Description |
|---|---|
| src/libraries/System.Linq/tests/EnumerableTests.cs | Updated transformation helpers and test cases to use CreateSources for more robust scenarios. |
| src/libraries/System.Linq/tests/SelectManyTests.cs | Revised parameterized tests to use a fixed loop for testing multiple ranges. |
| src/libraries/System.Linq/src/System/Linq/SkipTake.SpeedOpt.cs | Changed a condition in the Take method to address an issue with incorrectly retrieving the last element. |
| Other test files | Adjusted test expectations and minor refactorings for consistency in enumerable behavior. |
Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.
Comments suppressed due to low confidence (1)
src/libraries/System.Linq/src/System/Linq/SkipTake.SpeedOpt.cs:433
- Changing the condition from '>= _minIndexInclusive' to '> _minIndexInclusive' directly affects branch selection for retrieving the last element. Please verify that this change correctly resolves the issue without introducing unintended side effects.
count > _minIndexInclusive)
Tip: Copilot code review supports C#, Go, Java, JavaScript, Markdown, Python, Ruby and TypeScript, with more languages coming soon. Learn more
artl93
commented
Feb 20, 2025
Adding approved per-email with Steve |
Backport of #112680 to release/9.0-staging
Customer Impact
Code that does
commonLINQOperators.Take(...).Last(), wherecertainLINQOperatorsare things likeRange,Select,Where, etc., if the value passed to take is larger than the number of elements in the source,Lastwill throw an exception, andLastOrDefaultwill end up returning the default value rather than the actual last value. Essentially, the consumer can start operating on incorrect data or hit unexpected failures.Regression
This was introduced accidentally in .NET 9 as part of a change that consolidated several internal abstractions in LINQ.
Testing
Revamped the LINQ tests to execute various combinations that lead to this, but also a bunch of other combinations to try to root out any other similar issues.
Risk
Low. The change is isolated to a single block and is now much better tested. Developers would have either been getting back erroneous results, or erroneous exceptions; it'd be difficult for someone to have taken a meaningful dependency on the bug.