Skip to content

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint#124294

Merged
tannergooding merged 2 commits intomainfrom
copilot/fix-nindex-to-string-issue
Feb 12, 2026
Merged

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint#124294
tannergooding merged 2 commits intomainfrom
copilot/fix-nindex-to-string-issue

Conversation

Copy link
Contributor

Copilot AI commented Feb 11, 2026

Description

NIndex.ToStringFromEnd() and NRange.ToString() cast nint values to uint before formatting, truncating values exceeding uint.MaxValue on 64-bit platforms:

var ni = NIndex.FromEnd(unchecked((nint)(1L + uint.MaxValue)));
Console.WriteLine(ni.ToString()); // Prints "^0" instead of "^4294967296"

Fix

  • NIndex.cs: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs: (uint)Start.Value(nuint)Start.Value and (uint)End.Value(nuint)End.Value in ToString()

Tests

  • Replaced [Fact] ToStringTest with [Theory] + [InlineData] for both NIndexTests and NRangeTests
  • Added [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] tests exercising values above uint.MaxValue

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 11, 2026
…int, add Theory tests

Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix NIndex formatting for end index conversion Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint Feb 11, 2026
Copilot AI requested a review from vcsjones February 11, 2026 20:43
@vcsjones vcsjones added area-System.Numerics.Tensors and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 11, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics-tensors
See info in area-owners.md if you want to be subscribed.

@vcsjones vcsjones marked this pull request as ready for review February 11, 2026 20:51
Copilot AI review requested due to automatic review settings February 11, 2026 20:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect string formatting for NIndex/NRange on 64-bit platforms where values larger than uint.MaxValue were being truncated due to casts to uint during formatting.

Changes:

  • Update NIndex.ToStringFromEnd() to format using (nuint)Value rather than (uint)Value.
  • Update NRange.ToString() to format start/end using (nuint)Start.Value / (nuint)End.Value rather than uint.
  • Expand/adjust unit tests to cover 64-bit values above uint.MaxValue, converting prior [Fact] coverage into [Theory] + [InlineData] and adding 64-bit conditional cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.cs Fixes 64-bit truncation in ToStringFromEnd() by formatting via nuint.
src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.cs Fixes 64-bit truncation in ToString() by formatting start/end via nuint.
src/libraries/System.Numerics.Tensors/tests/NIndexTests.cs Adds theory-based coverage and conditional 64-bit test cases for large values.
src/libraries/System.Numerics.Tensors/tests/NRangeTests.cs Adds theory-based coverage and conditional 64-bit test cases for large range endpoints.

@vcsjones
Copy link
Member

vcsjones commented Feb 11, 2026

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

Copy link
Contributor Author

Copilot AI commented Feb 11, 2026

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

🤖 Copilot Code Review

Holistic Assessment

Motivation: The bug is real and clearly reproducible — (uint) casts truncate nint values exceeding uint.MaxValue on 64-bit platforms, causing NIndex.FromEnd(1L + uint.MaxValue).ToString() to return ^0 instead of ^4294967296. The buffer comment on line 177 ("20 for longest possible nuint value") confirms the original intent was nuint.

Approach: Changing (uint)(nuint) is the minimal correct fix. The tests use [Theory]/[InlineData] and [ConditionalTheory] with PlatformDetection.Is64BitProcess appropriately, consistent with patterns used in NFloatTests.cs and elsewhere in the test project.

Summary: ✅ LGTM. The three cast fixes are correct, minimal, and well-tested. All findings below are positive confirmations or out-of-scope observations.


Detailed Findings

✅ Correctness — Cast fix is correct in all three locations

  • NIndex.cs:178: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs:73: (uint)Start.Value(nuint)Start.Value in ToString()
  • NRange.cs:84: (uint)End.Value(nuint)End.Value in ToString()

The Value property returns nint (always non-negative for valid NIndex values), so the nuint cast is safe. The stackalloc buffer was already sized for nuint (21 chars = 1 for ^ + 20 digits for max nuint on 64-bit). The non-FromEnd path (NIndex.ToString() line 172) uses Value.ToString() which calls nint.ToString() directly — no truncation issue there.

✅ Test quality — Good coverage with appropriate conditional attributes

The tests cover both fromStart and fromEnd paths, use boundary values (int.MaxValue, uint.MaxValue + 1, long.MaxValue), and correctly gate 64-bit-specific cases with [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))]. PlatformDetection is already used in other tests in this project (TensorPrimitives.Generic.cs, TensorPrimitivesTests.cs).

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

NRange.cs:113 uses (uint)end > (uint)length || (uint)start > (uint)end — this truncates nint values on 64-bit, which could cause incorrect bounds validation for large collections. However, this is mirrored from Range.GetOffsetAndLength (which uses intuint, which is correct for that type) and is a separate issue from the ToString fix.

@tannergooding tannergooding enabled auto-merge (squash) February 11, 2026 21:25
@tannergooding
Copy link
Member

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

Copy link
Contributor Author

Copilot AI commented Feb 11, 2026

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

I'm not able to open new PRs. Could you open an issue for tracking the (uint) truncation in NRange.GetOffsetAndLength (line 113) so it can be picked up separately? The fix there would be changing (uint) to (nuint) for the bounds checks on end, length, and start.

@vcsjones
Copy link
Member

I will open a separate PR for the offset / length issue.

@tannergooding tannergooding merged commit de90299 into main Feb 12, 2026
97 of 104 checks passed
@tannergooding
Copy link
Member

This one should be backported, yes?

@tannergooding
Copy link
Member

I'm fine with taking them all together, but I do think we should take them for bar check at least. -- CC. @jeffhandley

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants