130 lines
4.9 KiB
Markdown
130 lines
4.9 KiB
Markdown
# C# Language Design Notes for Dec 4, 2017
|
|
|
|
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
|
|
|
QOTD: "Days since working around the JIT: 0"
|
|
QOTD: "What's wrong with dangerous?"
|
|
QOTD: "If we disallowed `Dangerous` in method names, Dangerous Dave wouldn't compile!"
|
|
|
|
# Foreach for Span and ReadOnlySpan
|
|
|
|
`Span` and `ReadOnlySpan` implement the foreach pattern with `GetEnumerator()` etc., even though they don't implement the `IEnumerable<T>` interface. (Interesting aside, the `Current` property is ref-returning, which the compiler is quite happy with.)
|
|
|
|
But it's faster to iterate them like an array, by using the `Length` and the indexer. We want to allow that for spans as well. We would keep the enumerable around, which also allows write access to the elements of a `Span`.
|
|
|
|
Interesting to consider opening up for a new `Length`/indexer `foreach` pattern, that's opted into e.g. with an attribute.
|
|
|
|
## Conclusion
|
|
Definitely allow it as an optimization. The general case; not now, and maybe not ever. Better to limit it to compiler-known types, that are guaranteed to have equivalent semantics.
|
|
|
|
|
|
# In vs value parameter ambiguity
|
|
|
|
If you have overloads
|
|
|
|
``` c#
|
|
void M(in DateTime d) { }
|
|
void M(DateTime d) {}
|
|
|
|
M(myDT); / ambiguity
|
|
```
|
|
|
|
Options in tie breaker:
|
|
|
|
1. Choose based on LValue vs RValue
|
|
2. val preferred
|
|
3. Do nothing
|
|
|
|
How would you legitimately end up in this situation:
|
|
|
|
- Want to "change" to `in`, but must keep value for binary compat
|
|
- But now you have source break
|
|
|
|
|
|
We currently do 3. We should do 2. 1 is too arbitrary.
|
|
|
|
People might want to move existing calls to the `in` version, and we won't be helping them. But that's an analyzer.
|
|
|
|
Problem with operators. They can't use `in` when applied, so can't move away from val behavior. Oh well.
|
|
|
|
## Conclusion
|
|
|
|
Go with 2. Roll out as bug fix.
|
|
|
|
|
|
# Pattern-based fixed
|
|
|
|
Lots of types (especially new ones, like `Span`, `Memory` etc., but also `string`) would benefit from being `fixed`.
|
|
|
|
`Span<T>` currently has a `DangerousGetPinnableReference` method for this. We can make this a pattern.
|
|
|
|
It's a thin layer of syntactic sugar.
|
|
|
|
## Conclusion
|
|
|
|
Let's do it. Extension methods are allowed (as always when we add new patterns). Let's aim for 7.3, but not super high pri.
|
|
|
|
|
|
# this ref vs ref this
|
|
|
|
We allow `ref this` but not the other way around. That's wrong! We can't disallow `ref this`, but we should allow and prefer `this ref`.
|
|
|
|
## Conclusion
|
|
|
|
Fix it. Go out as a bug fix.
|
|
|
|
|
|
# Reachability, definite assignment and local functions
|
|
|
|
There's a bit of a mess around when captured variables in local functions are definitely assigned.
|
|
|
|
This is different from lambdas, because local functions take all calls into account.
|
|
|
|
The forthcoming ECMA spec changes the wording of these rules, so that they are not defined based on reachability.
|
|
|
|
Proposal:
|
|
- Make beginning of local functions always reachable. (And lambdas, which is implemented but not spec'ed).
|
|
- A captured local in a local function is considered definitely assigned if it's definitely assigned before every call of it.
|
|
- That last one can be vacuously true.
|
|
|
|
## Conclusion
|
|
|
|
We like this. It could give errors in a few cases that don't today (including the bug report).
|
|
|
|
Is this an acceptable breaking change? In order to get an error, you'd have to have:
|
|
- an unassigned local variable
|
|
- an uncalled local function that uses it
|
|
|
|
Both are unlikely, undesirable and easily fixed. We'll check with compat council.
|
|
|
|
|
|
# Equality operators on tuples
|
|
|
|
Needs to be defined by the language, in order to deal with long tuples, and to recursively apply `==` rather than `Equals`.
|
|
|
|
If somebody wrote their own `ValueTuple` *with* user defined equality, then this would break the use of that. That's not a supported scenario; you could get into the same kind of trouble with `Nullable<T>`.
|
|
|
|
For tuple literals, there is a tension between left-to-right evaluation and performance.
|
|
|
|
It seems that `t1 == t2` should mean the same as `t1 == (t2.Item1, t2.Item2)`. The upshot of the feature really is to deconstruct tuples (if they aren't already, by being tuple literals), then do point-wise comparison.
|
|
|
|
We will evaluate all operands left to right, recursively through tuple literals, and save to temps. We don't evaluate target-typed things, and we don't yet convert them.
|
|
|
|
Then we do point-wise `==` (or `!=`, in element position order, separated by `&&` (Or for `!=`, `||`). This may involve conversions. We are ok with those happening "late", and conditionally (won't happen if previous comparison failed), because conversions aren't usually expected to have side effects.
|
|
|
|
For dynamic we think we can allow it by just converting the result of, e.g. `d == e` (where `d` is dynamic) to `bool`.
|
|
|
|
Conversion from tuples matter, but not to tuples. If I have a `Foo` and a tuple, and `Foo` converts to tuple, then that won't help you.
|
|
|
|
## Conclusion
|
|
|
|
We think we have it, but will revisit during implementation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|