csharplang/meetings/2017/LDM-2017-12-04.md

131 lines
4.9 KiB
Markdown
Raw Normal View History

2017-12-06 02:06:17 +01:00
# 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.