Added LDM notes for April 5th, 2021.
This commit is contained in:
parent
1d59a69eff
commit
e01b11a71e
151
meetings/2021/LDM-2021-04-05.md
Normal file
151
meetings/2021/LDM-2021-04-05.md
Normal file
|
@ -0,0 +1,151 @@
|
|||
# C# Language Design Meeting for April 5th, 2021
|
||||
|
||||
## Agenda
|
||||
|
||||
1. [Interpolated string improvements](#interpolated-string-improvements)
|
||||
2. [Abstract statics in interfaces](#abstract-statics-in-interfaces)
|
||||
|
||||
## Quote of the Day
|
||||
|
||||
- "Every time someone tells me they have a printer that works, I believe they're part of the conspiracy"...
|
||||
"So what I'm getting from this is that you've given up on your printer and are now printing at the office?"
|
||||
|
||||
NB: This is, to my knowledge, the largest amount of time between parts 1 and 2 of an LDM quote (approximately 1 hour and 10 minutes,
|
||||
in this instance).
|
||||
|
||||
## Discussion
|
||||
|
||||
### Interpolated string improvements
|
||||
|
||||
https://github.com/dotnet/csharplang/issues/4487
|
||||
|
||||
Our focus today was in getting enough of the open questions in this proposal completed for the [dotnet/runtime API proposal](https://github.com/dotnet/runtime/issues/50601)
|
||||
to move forward.
|
||||
|
||||
#### Create method shape
|
||||
|
||||
We adjusted the shape of the `Create` method from the initial proposal to facilitate some improvements:
|
||||
|
||||
* In some cases, `out`ing a parameter with a reference type field can cause the GC to introduce a write barrier.
|
||||
* For the non-`bool` case, `Create` feels more natural.
|
||||
|
||||
This may be a micro-optimization, but given that this pattern is not intended to be user-written, but instead lowered to by the compiler,
|
||||
we feel that it's worth it.
|
||||
|
||||
##### Conclusion
|
||||
|
||||
Approved.
|
||||
|
||||
#### TryFormatX method shape
|
||||
|
||||
The proposal suggests that we should allow `TryFormatX` calls to either return `bool` or `void`, as some builders will want to stop formatting
|
||||
after a certain point if they run out of space, or if they know that their output will not be used. While a `void`-returning method named `TryX`
|
||||
isn't necessarily in-keeping with C# style, we will keep the single name for a few reasons:
|
||||
|
||||
* Again, this pattern isn't intended to be directly consumed by the end user, they'll be writing interpolated strings that lower to this.
|
||||
* It's harder for API authors to mess up and mix `void`-returning and `bool`-returning `TryFormatX` methods, because you can't overload on
|
||||
return type.
|
||||
* It's easier to implement.
|
||||
|
||||
We do want to allow mixing of a `Create` method that has an `out bool` parameter, as some builder are never going to fail after the first create
|
||||
method. A logger, for example, would return `false` from `Create` if the log level wasn't enabled, but the actual format calls will always succeed.
|
||||
|
||||
We also looked at ensuring that a single `TryFormatInterpolationHole` method with optional parameters for both alignment and format components can
|
||||
be used with this pattern. As specified today, these parameters are not named so overload resolution can only look for signatures by parameter type.
|
||||
This means there's no single signature that handle just a format or just an alignment. To resolve this, we will specify the parameter names we use
|
||||
for the alignment and format components (their names will be `alignment` and `format`, respectively). The first parameter will still be unnamed.
|
||||
|
||||
Another discussion point was on whether we should require builders to _always_ support all possible combinations of a format or alignment component
|
||||
being present. For some types (such as `ReadOnlySpan<T>`), we're imagining that such components would be just ignored by the builder, and it would
|
||||
be interesting to just have that be a compile error instead of just being silently ignored at runtime. However, the ship on invalid format specifiers
|
||||
sailed a long time ago, and there is potential difficulty in making a good compile-time error experience for these scenarios. Thus, our recommendation
|
||||
is to always include overloads that support these specifiers, even when ignored at runtime.
|
||||
|
||||
##### Conclusion
|
||||
|
||||
API shape is approved. We will use named parameters for `alignment` and `format` parameters, as appropriate.
|
||||
|
||||
#### Disposal
|
||||
|
||||
Finally in interpolated string improvements, we looked at supporting disposal of builder types. Our decision that we will have conditional execution
|
||||
of interpolated string holes means that user code will be running the middle of builder code. This means that an exception can be thrown, and if the
|
||||
builder acquired resources (such as renting from the `ArrayPool`), it has the potential to be lost if we do not wrap the builder (and potentially
|
||||
larger method call that consumes the builder for non-`string` arguments) in a try/finally that calls dispose on the builder. There's also an additional
|
||||
complication to the disposal scenario, which is whether we trust the compile-time type of the builder. If the builder is a non-sealed reference type
|
||||
or a type parameter constrained to an interface type that has an `abstract static Create` method, the actual runtime type could implement `IDisposable`
|
||||
that we do not know about at runtime. In the language, we're not hugely consistent on this. In some cases we trust that the compile-time type is correct
|
||||
(such as when determining if a sealed/struct type is disposable), and in some cases we emit a runtime check for `IDisposable` (such as if the enumerator
|
||||
type is a non-sealed type or interface). We also need to consider whether to do pattern-based `Dispose` for all types (as we do in `await foreach`) or
|
||||
just for `ref struct`s (as we do for regular `foreach`).
|
||||
|
||||
Even the concept of disposal for these builder types might not be needed. The runtime is a bit leary of having `InterpolatedStringBuilder` be disposable,
|
||||
because it means that every interpolated string will introduce a `try/finally` in a method. We really don't want to see people create performance analyzers
|
||||
that tell users to avoid interpolated strings in methods because it will affect inlining decisions. We have some ideas on avoiding this for interpolated
|
||||
strings converted to strings specifically, but a general pattern is concerning. If the main builder type won't be disposable, are we concerned that we're
|
||||
trying to engineer a solution to a problem that doesn't actually exist?
|
||||
|
||||
##### Conclusion
|
||||
|
||||
No conclusion on this point today. A small group will examine this in more detail and make recommendations to the LDM based on evaluation.
|
||||
|
||||
### Abstract statics in interfaces
|
||||
|
||||
https://github.com/dotnet/csharplang/issues/4436
|
||||
|
||||
We looked at 2 major changes to the proposal today: allowing default implementations and changes to operator restrictions.
|
||||
|
||||
#### Default implementations
|
||||
|
||||
The existing proposal specifically scopes default implementations of virtual statics in interfaces out because of concerns about runtime implementation
|
||||
costs. In particular, in order to make default implementations work and be able to call other virtual static members, there must be a hidden "self" type
|
||||
parameter so that the runtime knows what type to call the virtual static method on. That work is still too complicated to bring into C# 10, but a simple
|
||||
change of scope can make a subset of these cases work: we could require that all static virtual members _must_ be called on a type parameter. This takes
|
||||
that hidden "self" parameter and makes it no longer hidden, because there must always be a thing that the user calls that actually contains the type. It's
|
||||
not always necessary to write `T` itself: for example, `t1 + t2` would reference the static virtual `+` operator on `T`, so it's being accessed on the
|
||||
type parameter, not on the interface.
|
||||
|
||||
However, there are some serious usability concerns for this approach. A default implementation of a virtual static member cannot be inherited by any
|
||||
concrete types that inherit from that interface. This is true for instance methods as well, but that method (or a more derived type's implementation of
|
||||
the method) can be accessed by casting that instance to the interface type in question. For a DIM of a virtual static member, there is no instance that
|
||||
can be cast to the base interface to access the member, so a concrete type must always reimplement the virtual static member or it will be truly inaccessible.
|
||||
For example:
|
||||
|
||||
```cs
|
||||
interface I<T> where T : I<T>
|
||||
{
|
||||
public static abstract bool operator ==(T t1, T t2);
|
||||
public static virtual bool operator !=(T t1, T t2) => !(t1 == t2);
|
||||
public void M() {}
|
||||
}
|
||||
class C : I<C>
|
||||
{
|
||||
public static bool operator ==(C c1, C c2) => ...;
|
||||
}
|
||||
|
||||
C c = new C();
|
||||
c.M(); // M is not accessible
|
||||
((I)c).M(); // This works, however
|
||||
|
||||
_ = c == c; // Fine: C implements ==
|
||||
_ = c != c; // Not fine: I.!= is a default implementation that C does not inherit. This method cannot be called.
|
||||
```
|
||||
|
||||
There are 2 workarounds a user could use for this problem: make a generic method that takes a type parameter constrained to I and invoking the member there,
|
||||
or reimplementing the operator on C, thereby removing the benefit of the default implementation in the first place. Neither of these are particularly appealing.
|
||||
|
||||
##### Conclusion
|
||||
|
||||
A smaller group will revisit this and decide whether it is useful. We are skeptical of it based on the above problems currently.
|
||||
|
||||
#### Operator restrictions
|
||||
|
||||
Today, interfaces are prohibited from implementing `==` or `!=`, and from appearing in user-defined conversion operators. We have a long history of this, mainly
|
||||
because with interfaces there is always the change that the underlying type actually implements the interface at runtime, and the language should always prefer
|
||||
built-in conversions to user-defined conversions. However, these operators cannot actually be accessed when the user only has an instance of the interface, they
|
||||
can only be accessed when using a concrete derived type or a type parameter constrained to the interface type. This addresses our concerns about interface
|
||||
implementation at runtime, and conversions from types defined in interfaces is particularly useful for numeric contexts such as being able to allow `T t = 1;`
|
||||
because `T` is constrained to an interface that has a user-defined conversion from integers.
|
||||
|
||||
##### Conclusion
|
||||
|
||||
Lifting these restrictions is approved.
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
## Schedule when convenient
|
||||
|
||||
- Open questions in interpolated strings (Fred): https://github.com/dotnet/csharplang/blob/main/proposals/improved-interpolated-strings.md
|
||||
|
||||
## Recurring topics
|
||||
|
||||
- *Triage championed features and milestones*
|
||||
|
@ -30,15 +32,17 @@
|
|||
|
||||
- MVP session
|
||||
|
||||
## Apr 5, 2021
|
||||
|
||||
- Open questions in interpolated strings (Fred): https://github.com/dotnet/csharplang/blob/main/proposals/improved-interpolated-strings.md
|
||||
- Statics in interfaces (Mads): https://github.com/dotnet/csharplang/issues/4436
|
||||
|
||||
# C# Language Design Notes for 2021
|
||||
|
||||
Overview of meetings and agendas for 2021
|
||||
|
||||
## Apr 5, 2021
|
||||
|
||||
[C# Language Design Notes for April 5th, 2021](https://github.com/dotnet/csharplang/blob/master/meetings/2021/LDM-2021-04-05.md)
|
||||
|
||||
1. [Interpolated string improvements](#interpolated-string-improvements)
|
||||
2. [Abstract statics in interfaces](#abstract-statics-in-interfaces)
|
||||
|
||||
## Mar 29, 2021
|
||||
|
||||
[C# Language Design Notes for March 29th, 2021](https://github.com/dotnet/csharplang/blob/master/meetings/2021/LDM-2021-03-29.md)
|
||||
|
|
Loading…
Reference in a new issue