Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Portable (and thus slower) version of Span<>#13562

Merged
stephentoub merged 26 commits into
masterfrom
unknown repository
Nov 15, 2016
Merged

Portable (and thus slower) version of Span<>#13562
stephentoub merged 26 commits into
masterfrom
unknown repository

Conversation

@ghost
Copy link
Copy Markdown

@ghost ghost commented Nov 10, 2016

  • Holding off on ReadOnlySpan<> until we're happy
    with Span so I don't have keep making fixes in two
    places.

  • Not too happy with the hoops DangerousGetPinnedReference
    has to go through: we are looking at an alternative
    that involves adding two new Unsafe apis:

    https://github.com/dotnet/corefx/issues/13426

    I'll have to do some benchmarking to see which one's faster,
    though. In the meantime, there's plenty to review
    that won't be affected by that.

@ghost
Copy link
Copy Markdown
Author

ghost commented Nov 10, 2016

cc @KrzysztofCwalina
cc @jkotas

// As the name suggests, the offset is measured in bytes, not T's.
//
private readonly object _object;
private readonly IntPtr _byteOffset;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Vast majority of code in the repo has fields at the top. Can we do the same here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sorry, not a style I favor and not mandated by the guidelines.

Copy link
Copy Markdown
Member

@KrzysztofCwalina KrzysztofCwalina Nov 10, 2016

Choose a reason for hiding this comment

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

@weshaggard / @stephentoub, thoughts?

It might be not mandated by the guidelines, but it might be better to be consistent with the rest of the repo (and add a guideline :-))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would like them to be at the top. It's how we do it basically everywhere else in the repo, and where we don't it's effectively a style bug we fix. If it's not in the guidelines, it should be.

@omariom
Copy link
Copy Markdown
Contributor

omariom commented Nov 10, 2016

I think it is important to have the indexer, ctors and Slice methods inlined.

@ghost
Copy link
Copy Markdown
Author

ghost commented Nov 10, 2016

I think it is important to have the indexer, ctors and Slice methods inlined.

Wasn't sure they'd inline with all the parameter validation code but sure, I can always put the MethodImpl attribute on them.

@omariom
Copy link
Copy Markdown
Contributor

omariom commented Nov 10, 2016

Inlining should be checked on the older runtimes as the current JIT is different (more capable).

/// </summary>
public static bool IsReferenceFree<T>()
{
if (PerTypeLatches<T>.IsReferenceFree)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be just PerTypeLatches<T>.IsReferenceFree and the initialization should done via static constructor in PerTypeLatches


/// <summary>
/// Returns a cached empty array (Array.Empty not available on S.R. 4.0.0.0)
/// </summary>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It seems to return new array every time, not cached as stated in the comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Oops - yeah - last minute code thrown in in the migrate to corefx...

Comment thread src/System.Memory/src/System/Span.cs Outdated
/// <summary>
/// The number of items in the span.
/// </summary>
public int Length { get; }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A getter-only property is fine to use here, but I wonder if it'd be better for this particular struct to have an explicit readonly _length field (declared along with the other fields), to make it more clear to readers what the size of the struct is.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Seems reasonable.

@@ -0,0 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What does the ".Microsoft" in the filename refer to? If it's about a particular platform, we should use a word or words that best describes the target.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Microsoft .NET runtimes (CLR+CoreClr vs. as opposed to say, Mono.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Mono is Microsoft runtime too...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Hence the difficulty in choosing a good name - with the "store as Pinnable" trick, though, we can mothball this file altogether and have a implementation that works on Mono too. So this is moot.

Comment thread src/System.Memory/src/System/Span.cs Outdated
if (array == null)
throw new ArgumentNullException(nameof(array));
if (default(T) == null && array.GetType() != typeof(T[]))
throw new ArrayTypeMismatchException(SR.Format(SR.ArrayTypeMustBeExactMatch, typeof(T)));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I believe there were some recent JIT improvements around conditionalized exception throwing like this regarding inlining, but even with that, should we separate out these throws into their own methods?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

JIT improvements around conditionalized exception throwing

Note that this implementation is primarily meant for existing full framework runtimes that do not have these improvements.

should we separate out these throws into their own methods?

Yes

Comment thread src/System.Memory/src/System/Span.cs Outdated
if ((uint)Length > (uint)destination.Length)
return false;

// TODO: This is a tide-over implementation as we plan to add a overlap-safe cpblk-based api to Unsafe.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there an issue # we can add to the TODO?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Comment thread src/System.Memory/src/System/Span.cs Outdated
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown when the specified <paramref name="length"/> is negative.
/// </exception>
public static Span<T> DangerousCreate(object obj, ref T rawPointer, int length)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Did we decide for the second parameter to be ref T or IntPtr? I honestly don't remember. If we decided for ref T, do you remember why?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's ref, because a ref T is easy to come up with.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

"ref T" is how it ended up in the final ApiReview:

https://github.com/dotnet/apireviews/tree/master/2016/11-04-SpanOfT

/// Start and index are non-negative, and already pre-validated to be within the valid range of their containing Span.
///
/// If the byte length (Span.Length * sizeof(T)) does an unsigned overflow (i.e. the buffer wraps or is too big to fit within the address space),
/// the behavior is undefined.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we add a Debug.Assert for it just to help catch some potential misuse?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I have asserts for start and index being negative (as those are already validated.)

The unsigned overflow is unvalidated at this point and they come from the application so it's not a condition I can assert on.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

so it's not a condition I can assert on

Are there valid uses cases where it would overflow?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

They're probably not valid, per se, (an app would be asking for a Span that wraps the address space), but usually we reserve asserts to detect conditions that are the framework's fault, not the app's fault.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So if it's possible for an app to get data to this internal helper that's invalid, shouldn't there be an exception to catch that earlier in the call path? And if there is, then we can assert here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The only way an unsigned overflow can get here are the two unsafe constructions (.ctor(void*) and DangerousCreate()).

Adding parameter validation to those power routines would probably not go down well around these parts.

I'd be open to adding the assert to the safe constructors with arrays if I could find an unobstrusive way to code it. But between the 32-64 bit dichotomy and all the restrictions on IntPtr arithmetic, such an assert would end up overwhelming the code and it's a really unlikely case since it would mean managed array construction is unsafe.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The only way an unsigned overflow can get here are the two unsafe constructions (.ctor(void*) and DangerousCreate()).

Ok


private static bool IsReferenceFree(Type type)
{
if (type.GetTypeInfo().IsPrimitive) // This is hopefully the common case. All types that return true for this are value types w/out embedded references.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we add a check for IsEnum?

Comment thread src/System.Memory/src/System/Span.cs Outdated
if (srcGreaterThanDst)
{
// Source address greater than or equal to destination address. Can do normal copy.
for (int i = 0; i < Length; i++)
Copy link
Copy Markdown
Member

@KrzysztofCwalina KrzysztofCwalina Nov 10, 2016

Choose a reason for hiding this comment

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

Why don't we do a bulk copy here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

See the TODO a few lines up.

<AssemblyVersion>4.0.3.0</AssemblyVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CLSCompliant>false</CLSCompliant>
<NuGetTargetMoniker>.NETStandard,Version=v1.0</NuGetTargetMoniker>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should add:

<DocumentationFile>$(OutputPath)$(AssemblyName).xml</DocumentationFile>

so that all of your good docs end up getting validated and generated into a docs file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done.

Comment thread src/System.Memory/src/System/Span.cs Outdated
{
get
{
switch (_object)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is super slow. Instead, this should be something like:

public struct Span<T> 
{
    [StructLayout(LayoutKind.Sequential)] 
    private sealed class Pinnable
    {
        public T Data;
    }

    private readonly Pinnable _pinnable;
    private readonly IntPtr _byteOffset;


    public ref T DangerousGetPinnableReference
    {
        get
        {
            return (_pinnable != null) ? Unsafe.AddByteOffset<T>(ref _pinnable.Data, _byteOffset) : Unsafe.AsRef<T>((void*)_byteOffset);
        }
    }
}

Also, I would manually inline the line into the indexer so that the JIT has less inlining to do for it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This will cause us to take a ref of the address within the array header (i.e. the hidden "length+padding" "field"). You may recall I asked a couple of days ago if that was safe to do.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I do not recall this question being asked ... It is safe to do on .NET Framework/.NET Core runtimes that this implementation seems to be specific to anyway.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

With this trick, it might become less specific as we can avoid hard-coding the Mono layout. But let's go with this for now.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How does this trick work?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Won't this be broken for arrays?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah I couldn't understand why the @jkotas version should not be possible, see https://github.com/dotnet/corefx/issues/13426#issuecomment-259913759 This also means we do not need extra overloads for Unsafe.

@joshfree
Copy link
Copy Markdown
Member

/cc @davidfowl

Comment thread src/System.Memory/src/System/Span.cs Outdated
if (srcGreaterThanDst)
{
// Source address greater than or equal to destination address. Can do normal copy.
for (int i = 0; i < Length; i++)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cache Length into local for better perf?

}

[Fact]
public static void Overlapping2()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why are you not using [Theory] instead of having Overlapping1, Overlapping2, ...?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Partly because I wrote all this in a desktop infrastructure where I have only a minimal XUnit emulator.

And partly because I frankly don't care for the [Theory] construct. I like unit tests that I can drag out to a simple standalone app for me to study when they break.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Theories are much better for exactly this reason though though 😄 . Same logical test with different data sets.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also, i don't think not using theories scales past span. Many other APIs that we will be adding (e.g. formatting) are so much more complicated than spans that we won't be able to avoid using theories.

int[] src = { 1, 2, 3 };
int[] dst = { 99, 100, 101 };

Span<int> srcSpan = new Span<int>(src);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How are we going to ensure that the fast-span and slow-span are tested? i.e. the tests run in the context of coreclr, which has the fast span.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I believe that's done by building a facade for CoreClr. It's not something that can be right now until the CoreClr implementation is brought up to date with the spec.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

BTW: It would be fine to do the reference assembly and CoreCLR façade plumbing right away. Most of the APIs are in CoreCLR already, and it is easy to add baseline for the few APIs that are not there yet.

Comment thread src/System.Memory/src/System/Span.cs Outdated
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown when the specified <paramref name="length"/> is negative.
/// </exception>
public static Span<T> DangerousCreate(object obj, ref T rawPointer, int length)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's ref, because a ref T is easy to come up with.

Comment thread src/System.Memory/src/System/Span.cs Outdated
// Disallow "special" objects that have their own object headers and would yield unexpected (and runtime-dependent) results.
// We already have constructors for arrays (and that enforce the passing of the properly typed single-dimensional array.)
// This constructor is not useful for strings (cannot express"ref s[i]").
if (obj is Array)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We're explicitly blocking these? Why?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

string: an immutable type being wrapped inside a mutable Span... and you can't take a "ref s[0]" so there's no correct way to use it anyway.

array: this boils down to - do we want to allow multidim arrays or not? (what guarantees do we make about the layout of those (row order or column order?) We could allow T[] to go through but that's another check and it duplicates functionality that we have more strongly typed constructors for.

The offered up rationale for these were plain old objects with "fixed array-like stuff in them" so that's what I'm scoping these to.

Copy link
Copy Markdown
Member

@jkotas jkotas Nov 11, 2016

Choose a reason for hiding this comment

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

We should not be in the business of enforcing what kind of stuff can or cannot be used with the DangerousCreate. This API is for very advanced users that know what they are doing:

  • It costs perf. The advanced users call this API for perf. Wasting perf for unnecessary checks makes this API useless.
  • The advanced users always find a valid reasons for why they want to do something, and these unnecessary checks always get in their way.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yes, we definitely don't want all these checks in. And calling ref T rawPointer seems wrong to me, it is not a pointer, it is a ref. The description of this parameter makes this clear, "A reference to data within that object", so call it objectData the ref is clear from the type. It is not a raw pointer at all.

Comment thread src/System.Memory/src/System/Span.cs Outdated
/// <exception cref="System.IndexOutOfRangeException">
/// Thrown when index less than 0 or index greater than or equal to Length
/// </exception>
public ref T this[int index]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is correct but none of the tooling outside of corefx supports ref returns. I think we need to return a T for now with a setter and a TODO to unblock this once the tooling is far along enough. I believe these methods would be filtered out if using C#6

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It's not so easy to change after it's out in the wild - it's a breaking change and things that depend on it will have to be recompiled at least. I believe the decision was that we'd accept C#7 being a prereq.

Copy link
Copy Markdown
Member

@davidfowl davidfowl Nov 11, 2016

Choose a reason for hiding this comment

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

We shouldn't ship this way (to nuget.org). I'm talking about a few months. Maybe we'll have to expedite that somehow..... Im worried about being able to use this immediately (right after this goes in).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What prevents you from picking up latest Roslyn beta for your experiments? It is in the current dotnet CLI builds; it is easy to add it to .csproj file if you are using classic VS projects; ... .

We have even said that C# 8 will be eventual prereq for Span to get enforcement that it is used correctly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

xproj and project.json. Everything else is using that and we'd need to make a new build with C# 7 which nobody is looking at. The next version of Visual Studio will have proper support.... but I'd like to use it with VS2015 and xproj since all of the existing .NET Core tooling is based on that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, absolutely, have you tried it? 😄 There is no VS2015 support...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

They use VSCode/Sublime on the CLI itself... I actually like using visual studio

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

installing preview3 currently breaks VS 2015 (or exposes a bug in VS depending how you look at it) - and I think VS 2015 will need a patch to work; with Core, when it is installed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

e.g. you can't File->New to get a working project and need to do all restores from command line https://github.com/dotnet/cli/issues/4248#issuecomment-259551612

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@davidfowl - the C# workaround is in. If it meets with your approval, pls sign off (or better yet, hit the merge button.)

@davidfowl
Copy link
Copy Markdown
Member

@atsushikan You should avoid merging commits during review. This PR is now unreadable as it has 323 changed files...

@ghost
Copy link
Copy Markdown
Author

ghost commented Nov 11, 2016

Unfortunately, these frequent version bumps make it necessary - otherwise, CI's become useless.

@davidfowl
Copy link
Copy Markdown
Member

image

@stephentoub
Copy link
Copy Markdown
Member

Unfortunately, these frequent version bumps make it necessary - otherwise, CI's become useless.

You can rebase instead of merging.

<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<ItemGroup>
<Project Include="System.Memory.csproj" Condition="'$(OS)'=='Windows_NT'" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this windows only? (I have no idea how the corefx builds work)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Because S.R.C.Unsafe is Windows only. No, I don't know why.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

S.R.CS.Unsafe is built on Windows only. I suspect that it is because of ilproj support was not available in Unix buildtools at the time it was introduced. (It is no longer the case so it should be possible to remove this condition even for S.R.CS.Unsafe.)

System.Memory does not need ilasm to build. It uses S.R.CS.Unsafe from package so I do not think there is good reason for having this condition here.

Atsushi Kanamori added 19 commits November 14, 2016 13:02
What's the matter, don't we have any version
numbers you like??
(all this repeated source is annoying - unfortunately,
Rosylin at this point at least, insists that "ref"
variables be initialized once only at declaration point and
doesn't allow the conditional ternary operator on ref types.
This makes it hard to manually inline DangerousGetPinnableReference
in a nice way...)
Allow JIT to recognize the throw path as a
no-return path so it can throw it into the
cold region.
It pessimizes the routine on the desktop CLR.
Description: "Provide classes" => "Provide types"

Add fastpath for specific types in IsReferenceFree

Make MeasureArrayAdjustment a PerTypeValues private
Eh... hard to tell with all the noise but
it did seems to shave a couple of percentage
points off.
Ah, the fun of writing Span code w/out stack-only enforcement.
...to typeforward on CoreClr. Right now, CoreClr
fails 44 out of 55 tests so I believe it's
premature to throw the switch just yet. This just
gets some of the grunt work out of the way.
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>Library</OutputType>
<CLSCompliant>false</CLSCompliant>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this 4.0.0.0?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

What else would it be?

Atsushi Kanamori added 2 commits November 15, 2016 11:54
After chatting offline, we'll accomodate C# 6.0 for now
by having the indexer return "T" rather than "ref T".

Some performant code require the "ref T" version so
as another stopgap, we'll provide a GetItem() method
that returns "ref T".

Once the tooling story is in better shape,
we'll merge GetItem() and the indexer into a single
indexer that returns "ref T".
This is getting really monotonous.
// TODO: https://github.com/dotnet/corefx/issues/13681
// Until we get over the hurdle of C# 7 tooling, this indexer will return "T" and have a setter rather than a "ref T". (The doc comments
// continue to reflect the original intent of returning "ref T")
public T this[int index]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good. Thanks!

@KrzysztofCwalina
Copy link
Copy Markdown
Member

I think it's ready to merge after the tests pass.
Ati, thanks for all the good work!

@stephentoub stephentoub merged commit ccc44b7 into dotnet:master Nov 15, 2016
@ghost ghost deleted the span branch November 15, 2016 21:28
@karelz karelz modified the milestone: 1.2.0 Dec 3, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.