Skip to content

Conversation

xtqqczze
Copy link
Contributor

@xtqqczze xtqqczze commented Aug 11, 2025

  • desiredStartIndex cannot be negative as high order bit of _index has been removed.
  • desiredLength cannot be negative as all public constructors validate _length is not negative.
  • lengthOfUnderlyingSpan cannot be negative as _object.Length as the length property of an array, string, or span cannot be negative.

@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Aug 11, 2025
Copy link
Contributor

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

#else
if ((uint)desiredStartIndex > (uint)lengthOfUnderlyingSpan || (uint)desiredLength > (uint)lengthOfUnderlyingSpan - (uint)desiredStartIndex)
Debug.Assert((int)desiredStartIndex >= 0 && lengthOfUnderlyingSpan >= 0);
if ((uint)desiredStartIndex + (uint)desiredLength > (uint)lengthOfUnderlyingSpan)
Copy link
Member

Choose a reason for hiding this comment

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

if this + overflows the check is passed, isn't ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

desiredLength should never be negative, this could do with an assert

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes - it's why the 32-bit target check is written the way it was below (which is a common overflow-avoiding pattern).

We can't "simplify" this, though - the 64 and 32 bit targets are different for a reason; they're taking advantage of different bit widths (or not able to) on different platforms.

Copy link
Member

Choose a reason for hiding this comment

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

This is the kind of thing where the logic is getting even more dense and hard to comprehend

I would much rather we do such transforms in the JIT when we know that a given invariant is held, so that the high level managed code can remain easy to understand.

Otherwise, this needs a significant number of comments and asserts covering those invariants and why the various overflow considerations are "safe" before it could be accepted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The check here is only to prevent undefined behaviour if the struct is torn, if it wasn't for this it could be removed completely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would much rather we do such transforms in the JIT when we know that a given invariant is held, so that the high level managed code can remain easy to understand.

Related issue: #118587

Copy link
Contributor Author

Choose a reason for hiding this comment

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

desiredLength should never be negative, this could do with an assert

We already assume the invariant _length >= 0 holds

public Memory<T> Slice(int start)
{
if ((uint)start > (uint)_length)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
}

Copy link
Member

Choose a reason for hiding this comment

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

The check here is only to prevent undefined behaviour if the struct is torn, if it wasn't for this it could be removed completely.

Yes, which is necessary/important. The team has a firm stance that we can't rely on users doing safe threading and that we need to do the "right things".

It's the same general reason we have both sets of lengths checks when dealing with List<T> (CC. @GrabYourPitchforks)

We already assume the invariant _length >= 0 holds

It's still something where adding an assert is beneficial as it explicitly documents the expectation.

But, I'd generally rather we have the JIT doing these types of optimizations. The managed code should prefer being readable/understandable first. If something is truly perf critical, then making it less readable with added comments/asserts is ok. However, the JIT automatically recognizing the critical pattern and doing the right thing is even better, as then the code stays readable and other paths likely benefit as well.

@xtqqczze
Copy link
Contributor Author

@MihuBot

@MihaZupan
Copy link
Member

Changes here overlap with #115275

#else
if ((uint)desiredStartIndex > (uint)lengthOfUnderlyingSpan || (uint)desiredLength > (uint)lengthOfUnderlyingSpan - (uint)desiredStartIndex)
Debug.Assert((int)desiredStartIndex >= 0 && lengthOfUnderlyingSpan >= 0);
if ((uint)desiredStartIndex + (uint)desiredLength > (uint)lengthOfUnderlyingSpan)
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes - it's why the 32-bit target check is written the way it was below (which is a common overflow-avoiding pattern).

We can't "simplify" this, though - the 64 and 32 bit targets are different for a reason; they're taking advantage of different bit widths (or not able to) on different platforms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Memory community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants