Finish meeting notes

This commit is contained in:
Mads Torgersen 2017-03-24 12:28:59 -07:00
parent d7c01cd036
commit f639a8d2cf
3 changed files with 173 additions and 57 deletions

View file

@ -1,9 +1,5 @@
# C# Language Design Notes for Feb 22, 2017
***Raw notes, yet to be cleaned up - read at your own peril***
## Agenda
We went over the proposal for `ref readonly`: [Champion "Readonly ref"](https://github.com/dotnet/csharplang/issues/38).
@ -17,6 +13,7 @@ This proposal introduces a `readonly` modifier on ref parameters and returns tha
## Conclusion
We want to support this.
# readonly struct types
In and of itself, though, readonly refs contribute to another source of struct copying: when a method or property is invoked on a `readonly` struct (including now a readonly ref parameter or local), the C# compiler *implicitly* copies the struct, as a defense against the method or property invocation mutating the original struct. In the common case where the member is not actually mutating the struct, this is pure waste.
@ -28,7 +25,8 @@ Once we have this feature, we could start warning on the compiler's defensive co
More detailed versions are possible, where readonly-ness can be per struct member. This may not be necessary in practice, based on experience from the Midori project. (Or we can make it not strictly enforced, or allow exceptions that are somehow marked.)
## Conclusion
Support readonly struct types for now, until and unless evidence shows the need for per-member decisions.
Support `readonly` just on whole struct types for now, until and unless evidence shows the need for per-member decisions.
# Syntax
@ -44,75 +42,48 @@ Currently, refs *themselves* are always single-assignment in C#, but if we decid
`ref readonly` is a bit of a mouthful. For parameters, `ref readonly` is in fact the exact opposite of `out`: something that comes in but cannot be modified. It would make total sense to call them `in` parameters. However, it would probably be confusing to call a *return* value `in`, so `in` should be allowed as a shorthand for `ref readonly` exclusively on parameters.
``` c#
public ref readonly Choose(in int i1, in int i2) { ... } // Equivalent to the above
```
## Call site annotations
With today's refs you need a call site `ref` annotation. This is to warn the caller of potential side effects and make sure they buy into them. Do we need it for `ref readonly` parameters, where there are no side effects by definition?
With today's refs you need a call site `ref` annotation. This is to warn the caller of potential side effects and make sure they buy into them. Do we need it for `ref readonly` parameters, where there are no side effects by definition? We believe not.
## Explicit or implicit
``` c#
int x = 1, y = 2;
int z = Choose(x, y); // implicitly passed by readonly ref
```
Are fields in readonly structs automatically readonly?
We didn't question the `ref` annotation on return statements in `ref readonly` returning methods, but maybe we should.
## Values as `in` arguments?
The proposal allows literals to be passed as an `in` argument. A variable is created under the hood, assigned the value and passed along. Is that bad? On the one hand, it makes for less predictable performance, in that some arguments cause copying (into a fresh variable), and others do not. On the other hand, since no `ref` is required, the arguments look like value arguments, that are already copied.
At least initially, as we prototype this, we will allow any expression of the right type, and we will just copy into a shadow variable when necessary. Even when a variable of a different but convertible type is passed. If this turns out to be a problem, or a cause of confusion or overly defensive programming, then we'll reevaluate.
# Extension methods
We want extension methods on structs to be able to work by ref, so they don't copy. We should allow ref and ref readonly extension methods on value types.
We want extension methods on structs to be able to work by ref, so that they don't copy. VB already allows this. We should allow both ref and ref readonly extension methods on value types.
Ref extension methods on classes could be controversial - like an `EnsureNotNull` method that replaces the object reference in the variable. We are not necessarily opposed to this in principle, but we don't need it for this scenario, and we'd have to track down weird consequences of it.
Ref extension methods on classes could be controversial - like an `EnsureNotNull` method that replaces the object reference in the variable. We are not necessarily opposed to this in principle, but we don't need it for this scenario, and we'd have to track down weird consequences of it. So for now, extension methods with `ref` and `in` this-parameters must extend a value type.
## Readonly parameters and locals
Not required for the scenario, but seems to be part of the same package, and is a long standing request.
# Readonly parameters and locals
## Readonly ref or ref readonly
The thing that's readonly is the thing being ref'ed, not the ref itself. The work `readonly` should probably come after the `ref`, right before the type.
Later we may make refs reassignable. At that point we'd want to allow an optional readonly *in front of* ref.
A separate feature, but seems to be part of the same package, and is a long standing request. Let's try to do these at the same time.
# Versioning
What happens when older code references newer code with ref readonly return. Upgrade the compiler, and you're broken. Can we encode it in metadata in such a way that it breaks downlevel? modreqs? Possibly, but then putting it on existing libraries is a library breaking change.
What happens when older code references newer code with ref readonly return types? The old compiler might not understand the `readonly` annotation, and would happily allow mutation. Upgrade the compiler, and you're broken. Can we encode it in metadata in such a way that it breaks downlevel? modreqs? Possibly, but then putting it on existing libraries is a library breaking change.
The CLR already doesn't protect this feature.
The CLR already does not protect readonly-ness, and there are therefore already ways you can circumvent it and mutate anyway. You can't avoid bad actors (reflection etc), but we would like to avoid accidentally breaking guarantees, and also breaking code.
It's very similar to nullability. We need a general philosophy for how we add these features.
## Conclusion
You can't avoid bad actors (reflection etc), but we want to avoid accidentally breaking guarantees, and also breaking code.
1. ref readonly parameter on a non-virtual method
2. ref readonly parameter on a virtual method
3. ref readonly return
2 and 3 create a loophole - those are the ones we would crack down on with modreqs if we want to prevent accidental non-readonly ref calling from older versions of C#.
We'll build the first version using attributes, and therefore leaving us open to breaking on upgrade. We'll consider if there are mitigations, but poisoning downlevel code, e.g. with modreqs, seems like too big of a hammer.
# Callsite annotations? Values?
``` c#
void M(ref readonly S s) { ... }
S s = ...;
M(ref readonly s); // 1
M(ref s); // 2
M(s); // 3
M(DateTime.Now); // 4
M(SReturningMethod()); // 5
```
It seems that we'd want 3 as the best syntax, since the user doesn't have side effects to opt into.
If we allow 4 and 5, it would introduce a copy. This may be worrisome (perf not obvious).
Nevertheless, let's as a strawman allow all of 3, 4, and 5. This also makes it possible to do efficient operators, e.g. on matrices and vectors
Should we allowed conversions even when a variable is passed in (which would then be copied)? In the strawman: yes.
Kestrel will give us good feedback.
# Syntax decisions:
- `ref readonly` annotations everywhere, `in` as a shorthand in parameter position
- `return ref v` for returns
- callsites no annotation

View file

@ -0,0 +1,138 @@
# C# Language Design Notes for Mar 21, 2017
Traits: Not what we're out to address! It may have similar capabilites, but the design point is different, based on different motivations.
We are also *not* out to add multiple inheritance to the language - we already have it with interfaces, and we're just making sure that continues to work.
It's a bit funny that members of interfaces are inherited into interfaces but not into classes.
There's a new kind of breaking change that may arise from adding new members to interfaces, where derived interfaces will get name lookup conflicts in their concrete implementations.
``` c#
itf IA { void Foo() {} }
itf IB {}
itf IC : IA, IB
{
void Bar()
{
Foo(); // allowed? What if IB adds Foo()
this.Foo(); // same? different?
((IA)this).Foo(); // would be allowed
this(IA).Foo(); // new syntax?
}
}
```
Summary of negative comments:
- OMG, you're adding MI to C#. Interfaces used to be clean, now they are kind of like classes.
- Reaction to the pulling in of class things on the slippery slope
- Let's *not* have traits
Interesting design point: This isn't really fully traits, because interface members aren't inherited. The interfaces can't be building blocks for classes.
We could consider letting default-implemented interface members inherit. That would be a big break away from interfaces today, where an implementing class always has to do *something* to get a corresponding public member. It would also probably make it a breaking change to *add* a default implementation to an existing member, which does not seem attractive.
We could have a feature where when you specify a base interface you can ask to have its default implemented members inherited.
For structs, there aren't really any other ways to factor out behavior.
Java did this years ago, and the world did not fall apart.
## Events?
Absolutely. They aren't material different. Any member you can declare in an interface.
## Modifiers
Virtualness is default in interfaces today. We could allow `sealed` to mean non-virtual if you want to override that.
Public is default in interface. Should we allow the default to be explicitly stated? All other accessibilities are potentially interesting as well.
`extern` scenario: people really like the idea of a scoped PInvoke. People want it for local functions as well.
`static`? Yes
Nested types?
**Philosophically**, we can either go all (or most of) the way to what classes can do, or do it as sparingly as possible, adding only things that are really needed. Once I can put implementation in an interface, I want all of the other stuff. Moving a method implementation from a class up to an interface shouldn't all of a sudden put a bunch of limitations on how to implement it.
Move from "let me have this" to "tell me why I can't have this".
# Methods
Can I use plain old `base`?
- Is it of type `object`, and exposes `object` methods
- disallow?
- `base` means "the" base interface, if applicable - may even "mesh" the base interfaces.
No `base` for now, just for initial simplicity.
# Properties
Abstract properties and events let you implement just one accessor. Can interface properties do that? Our strawman is "yes".
# Overrides
- Allow explicitly overriding an interface member that you override?
- Allow implicitly overriding ...?
The latter means that the `override` keyword is needed to express this. The latter may override *all* such members? Yes. Direct *and* indirect.
There's a bit here about which "class" analogy we apply: the "class inherits class" analogy, or the "class implements interface" analogy. The latter doesn't really apply anyway, so it's more similar to "class inherits class". Therefore we should require the `override` keyword even on `IA.M` which explicit member.
Properties, can override just one.
Tuple names must match, just as between classes. dynamic, same as between classes.
# reabstraction
Allow an override to remove implementation. `abstract` is optional.
# Most specific override
There has to be a most specific override, otherwise it's an error.
Properties and events, the accessors could be treated independently. There may be a problem with the Roslyn API. Let's instead say that it happens at the whole property level: there must be a most specific override of the property itself.
# Explicit abstract overrides in classes
Not for now. Understand the scenario, but it's small and this is complicated.
# static members
Would be useful even without this feature. But a shared helper may also be static.
# all accessibility?
public and private. Let's investigate what internal and protected mean.
For the non-virtual base call, you need to disambiguate. Possible syntaxes:
`T.base.M()`
`base(T).M()`
`base<T>.M()`
# Existing programs
We think that example 3 is analogous to 2, and should be allowed. However, how can we help someone who thought they were implementing the interface member, but were writing an unrelated private method?
Deciding whether a method implicitly implements an interface method is not just matching by signature.
We should accept the behavior now, and then look at pitfalls here later.

View file

@ -30,7 +30,7 @@ Overview of meetings and agendas for 2017
## Feb 21, 2017
[# C# Language Design Notes for Feb 21, 2017](LDM-2017-02-21.md)
[C# Language Design Notes for Feb 21, 2017](LDM-2017-02-21.md)
We triaged some of the [championed features](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+label%3A%22Proposal+champion%22), to give them a tentative milestone and ensure they had a champion.
@ -48,3 +48,10 @@ As part of this we revisited potential 7.1 features and pushed several out.
10. Implementing `==` and `!=` on tuple types *(C# 7.X)*
11. Declarations in embedded statements *(No)*
12. Field targeted attributes on auto-properties *(C# 7.1)*
## Feb 22, 2017
[C# Language Design Notes for Feb 22, 2017](LDM-2017-02-22.md)
We went over the proposal for `ref readonly`: [Champion "Readonly ref"](https://github.com/dotnet/csharplang/issues/38).