-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Closed
Labels
api-ready-for-reviewAPI is ready for review, it is NOT ready for implementationAPI is ready for review, it is NOT ready for implementationarea-System.Buffersarea-System.Text.JsonblockingMarks issues that we want to fast track in order to unblock other important workMarks issues that we want to fast track in order to unblock other important work
Milestone
Description
We have a handful of public ref struct types that have instance methods that accept span arguments. Callers then run into problems when trying to use these methods with variables of mixed lifetimes, e.g.
using System;
public class C
{
public bool M(ref Reader r)
{
Span<char> span = stackalloc char[] { 'a' };
return r.Equals(span);
}
}
public ref struct Reader
{
public bool Equals(ReadOnlySpan<char> span) => true;
}This will fail with the warnings:
error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope
error CS8350: This combination of arguments to 'Reader.Equals(ReadOnlySpan<char>)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope
because the r.Equals(span) call could theoretically store that span into r, in which case the caller of M would then have a Reader storing a Span<char> that's pointing to invalid memory.
We have a handful of members that get tripped up by this:
namespace System.Buffers
{
public ref struct SequenceReader<T> where T : unmanaged, IEquatable<T>
{
public long AdvancePastAny(ReadOnlySpan<T> values);
public bool IsNext(ReadOnlySpan<T> next, bool advancePast = false);
public bool TryAdvanceToAny(ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true);
public bool TryReadTo(out ReadOnlySpan<T> span, ReadOnlySpan<T> delimiter, bool advancePastDelimiter = true);
public bool TryReadToAny(out ReadOnlySequence<T> sequence, ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true);
public bool TryReadToAny(out ReadOnlySpan<T> span, ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true);
}
}
namespace System.Text.Json
{
public ref struct Utf8JsonReader
{
public bool ValueTextEquals(ReadOnlySpan<byte> utf8Text);
public bool ValueTextEquals(ReadOnlySpan<char> text);
}
}We have two levers we can pull to address these:
- Mark the member as
readonly. This only works if a) the member actually doesn't modify instance state and b) there aren't other ref parameters / ref structs into which something could be stored. - Mark the parameter as
scoped. This is new in C# 11 and promises that the parameter won't be stored or returned.
I propose the following:
namespace System.Buffers
{
public ref struct SequenceReader<T> where T : unmanaged, IEquatable<T>
{
public long AdvancePastAny(**scoped** ReadOnlySpan<T> values);
public bool IsNext(**scoped** ReadOnlySpan<T> next, bool advancePast = false);
public bool TryAdvanceToAny(**scoped** ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true);
public bool TryReadTo(out ReadOnlySpan<T> span, **scoped** ReadOnlySpan<T> delimiter, bool advancePastDelimiter = true);
public bool TryReadToAny(out ReadOnlySequence<T> sequence, **scoped** ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true);
public bool TryReadToAny(out ReadOnlySpan<T> span, **scoped** ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true);
}
}
namespace System.Text.Json
{
public ref struct Utf8JsonReader
{
public **readonly** bool ValueTextEquals(ReadOnlySpan<byte> utf8Text);
public **readonly** bool ValueTextEquals(ReadOnlySpan<char> text);
}
}eiriktsarpalis
Metadata
Metadata
Assignees
Labels
api-ready-for-reviewAPI is ready for review, it is NOT ready for implementationAPI is ready for review, it is NOT ready for implementationarea-System.Buffersarea-System.Text.JsonblockingMarks issues that we want to fast track in order to unblock other important workMarks issues that we want to fast track in order to unblock other important work