Added design notes

This commit is contained in:
Mads Torgersen 2017-01-26 18:26:33 -08:00
parent 3e2891a18b
commit 70d3c116ce

165
meetings/LDM-2016-11-01.md Normal file
View file

@ -0,0 +1,165 @@
# C# Language Design Notes for Nov 1, 2016
*Raw notes - need cleaning up*
## Agenda
- Abstracting over memory with `Span<T>`
# Abstracting over memory spans
It's common for teams building IO/data pipelines to invent a type similar to Span<T>, which is intended to abstract over native and managed memory, so that you write code over them only ones.
* Native memory
* Managed arrays
* stackalloc
* stack-allocated
We are now in a situation (unlike when C# started out) where every bit of performance counts. You can't afford overhead in terms of:
* indirection
* bounds checking
* virtual calls
* GC
* copying
Span needs to be as efficient to access/index as an array. It should essentially be a "safe pointer".
logically:
``` c#
struct Span<T>
{
ref T data;
int length;
}
```
Can't be declared like this, because the CLR doesn't support ref fields. But there are IntPtr tricks that can be used.
These need to be treated as "ref-like":
* Can't go on the heap
* Can't be captured by lambdas, iterators or async
* Can't be a type argument
* Etc...
Also need a ReadOnlySpan<T> or similar.
So need a way to specify ref-like types, so that the compiler wants to enforce it.
The stack-only-ness helps not having to spend anything on lifetime issues. It lets you pass it to something, and when they return you know they didn't keep any references to it.
Should we make existing types "span-aware"? Arrays? Strings?
We need to figure out what the right trade-off is.
Generally we would keep Span<T> at the 1% of users. It's analogous to array in the sense that it's arerely used directly in higher-level code.
For the rest there's wrapper types that are not limited to the stack, and can go into Span-land on demand.
Ref-like types
==============
Let's call the ref-like types "ref structs".
(Unlike refs these can be fields. ref fields require new runtime semantics, whereas we can otherwise make do with runtime *optimizations*, so we'll skip on ref fields for now)
Ref-like-ness should be declarative; it shouldn't be something you infer. Because something could change somewhere else that changes whether you are or not.
(The structs capturing ref-like locals when you have local functions would be inferred. But that's locally in code).
A problem something like Span may have at the language level is that it doesn't *look* like something that restrictions apply to, unlike `ref int` or `int*`. We could at least have a naming convention.
``` c#
M(await X, GetSpan());
```
Lifting out GetSpan() to a local wouldn't work. So that speaks to there being a notation on usage.
* Nothing?
* Naming conventions?
* syntax? `ref`? `*`?
Is this really only going to be done by 1% of users (which is still many).
Stackalloc
----------
``` c#
Span<byte> s = stackalloc byte[4];
```
Ref return rules
----------------
Need to be adjusted a bit. YOu should only be able to return spans to things that are safe to return.
Downlevel use
-------------
Do we need to poison these type for downlevel import, and how do we do that?
optreq`s? Mangled names? Obsolete it it and recognize something about that in the new compiler that will make it not obsolete it.
Readonly ref
============
If we want to enable spans representing strings, for instance, we need readonly spans. And their indexer needs to return a readonly ref.
Not so central to this proposal, but generally interesting: Can we make struct methods that don't copy (their `this` would be readonly ref)? In practice you'd probably want to call the whole struct readonly.
Slicing
=======
Essentially an API feature, but would want nice overloadable syntax.
Structs that can't be passed by value
-------------------------------------
TO avoid copying costs, semantic errors etc. Icing.
FOr Memory<T>
-------------
Not ready to discuss yet, but is there a way that you can prevent a Memory<T> from getting freed while a Span is "out".
Maybe some version of "fixed" can help. A challenge to generalize this from Span to any ref-like type.
``` c#
Span<byte> b = ...;
fixed(byte* p = &b.GetRawPointer())
```
Maybe this can be expressed more easily?
Limitations around ref locals today
-----------------------------------
There are many limitations to keep the rules simple, including the fact that ref locals are readonly at the ref level.
What should the restrictions be around ref-like types? One difference is thate.g. Span has a default value; 0-length span.
Next steps
----------
At the language level: Add an attribute to express ref-like-ness, and build a compiler to enforce it!