Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stackalloc array initializers. #1122

Merged
merged 1 commit into from
Jan 22, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions proposals/stackalloc-array-initializers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Stackalloc array initializers.

* [x] Proposed
* [ ] Prototype: Not Started
* [ ] Implementation: Not Started
* [ ] Specification: Not Started

## Summary
[summary]: #summary

Allow array initializer syntax to be used with `stackalloc`

## Motivation
[motivation]: #motivation

Ordinary arrays can have their elements initialized at creation time. It seems reasonable to allow that in `stackalloc` case.

The question of why such syntax is not allowed with `stackalloc` arises fairly frequently.
See, for example, https:/dotnet/csharplang/issues/1112

## Detailed design

Ordinary arrays can be created through the following syntax:

```C#
new int[3]
new int[3] { 1, 2, 3 }
new int[] { 1, 2, 3 }
new[] { 1, 2, 3 }
```

We should allow stack allocated arrays be created through:

```C#
stackalloc int[3] // currently allowed
stackalloc int[3] { 1, 2, 3 }
stackalloc int[] { 1, 2, 3 }
stackalloc[] { 1, 2, 3 }
```

The semantics of all cases is roughly the same as with arrays.
For example: in the last case the element type is inferred from the initializer and must be an "unmanaged" type.

NOTE: the feature is not dependent on the target being a `Span<T>`. It is just as applicable in `T*` case, so it does not seem reasonable to predicate it on `Span<T>` case.
Copy link
Member

Choose a reason for hiding this comment

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

What are the requirements on the target? Does it work for any type with void*, int length constructor?

(I assume that this is answered in the spec for the currently allowed case without initializer. The link to that spec may be helpful.)

Copy link
Member Author

Choose a reason for hiding this comment

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

It works with any type that has implicit conversion from Span<T>.

Copy link
Member Author

@VSadov VSadov Nov 20, 2017

Choose a reason for hiding this comment

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

From the language semantics stackalloc T[size] expression has Span<T> type, unless it matches backwards-compatible pattern where it must have type T*.

The implementation will call well-known constructor Span<T>(void*, int). Essentially there is one "magic" type - Span<T> and one "magic" method Span<T>(void*, int), that we know about.

Other types opt-in by providing a conversion operator. It is more intentional than matching (void*, int) constructors on random types - who knows what they expect, is that always {ptr, size}, is that size in bytes on elements?
With Span<T> there could be no doubts on {ptr, size} part.

This is also how stackalloc always worked. - you could always participate by having a conversion from T* . Opting in via a conversion is not a new feature.

Copy link
Member

Choose a reason for hiding this comment

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

Essentially there is one "magic" type - Span<T> and one "magic" method Span<T>(void*, int), that we know about.

Ok, that make sense. I was under impression that you are somehow doing this without knowing about magic Span<T> type.

Copy link
Member Author

Choose a reason for hiding this comment

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

We tried avoiding magic types, but arrived to a conclusion that for a robust pattern we would need one type - a generally accepted representation of {ptr, size} tuple.

And then realized, that Span<T> works just fine for that purpose :-)

Copy link
Member

@stephentoub stephentoub Nov 27, 2017

Choose a reason for hiding this comment

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

Since it's based on Span<T>, I assume I'd be able to write:

ReadOnlySpan<char> ros = stackalloc char[] { ch };
...

? Today to do the same thing I need to write:

Span<char> s = stackalloc char[1];
s[0] = ch;
ReadOnlySpan<char> ros = s;
...

Copy link
Member Author

Choose a reason for hiding this comment

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

@stephentoub Yes. That should work.


## Translation ##

The naive implementation could just initialize the array right after creation through a series of element-wise assignments.

Similarly to the case with arrays, it might be possible and desirable to detect cases where all or most of the elements are blittable types and use more efficient techniques by copying over the pre-created state of all the constant elements.

## Drawbacks
[drawbacks]: #drawbacks

Copy link
Member

Choose a reason for hiding this comment

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

I like how there are no drawbacks :-)

Copy link
Member Author

@VSadov VSadov Nov 20, 2017

Choose a reason for hiding this comment

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

Well, implementing a feature has cost, but it is a kind of obvious drawback. Could not find any other.

## Alternatives
[alternatives]: #alternatives

This is a convenience feature. It is possible to just do nothing.

## Unresolved questions
[unresolved]: #unresolved-questions

## Design meetings

None yet.