csharplang/meetings/2018/LDM-2018-07-09.md
2018-07-12 23:33:10 -07:00

237 lines
No EOL
8.3 KiB
Markdown

LDM July 9th, 2018
-------------------
_QOTD: "Yeah, it's easy if you do it in a shi**y way"_
## Agenda
1. `using var` feature
1. Overview
2. Tuple deconstruction grammar form
3. `using expr;` grammar form
4. Flow control safety
2. Pattern-based Dispose in the `using` statement
3. Relax Multiline interpolated string syntax (`$@`)
# `using var` Feature
**Motivation**
Proposal: https://github.com/dotnet/csharplang/pull/1703
It's a common problem that multiple `using` statements can require successive
nesting, causing what is mostly linear code to have the "down and to the
right" problem, where increasing indentation makes the code less readable,
not more. One way people try to solve this is using the
```C#
{
using (expr1)
using (expr2)
using (expr3)
{ ... }
}
```
syntax, but that has two problems. First, many style guidelines prohibit
"braceless" usings, but make an exception for this specific case. Second, if
there is any intervening code required between the `using` expressions, this
syntax form is not allowed.
**Objections**
Objections to this feature fall mainly in two categories. Either there is
worry about determinism and ordering, or that this feature isn't sufficiently
general to encompass the scenarios we would consider making the feature
"worth it."
The determinism concern is that refactoring from the `using (...) {...}` form
could unintentionally lengthen the liveness scope to the entire method,
instead of just to the closing brace of the using. The ordering concern is
that nesting provides very clear ordering semantics, and the "stacked using"
form also has a clear ordering, since there cannot be any code in between
each `using`. This isn't necessarily true for using-variables. It's possible
that both of these concerns could be mitigated by better refactoring and
analysis tools.
The generality concern is mainly around the `using (expr) { ... }` statement
form, which doesn't have an equivalent using-variable form in the current
design.
**Conclusion**
It's worth it. The concerns are valid, but don't seem bad enough to block the feature.
## Tuple deconstruction grammar form
The first question was about the proposed grammar. The current design is a
new type of statement (`local-using-declaration`). There are two potential
holes in the grammar: no space for tuple deconstructions and no `using expr;`
form.
For deconstruction, we came up with a number of potential forms:
```C#
(using var x, using var y) = M(); // Form 0
using (x, y) = M(); // Form 1
using (var x, var y) = M(); // Form 2
using var (x, y) = M(); // Form 3
using var t = M(); // Form 4
```
Of these, only (4) would be legal in the current proposal. Of the remaining
forms, form (0) seemed the clearest. There was consensus that this implied
the declaration of two new variables, each of which was independently
disposed, in the style of
```C#
using var x = M1(), y = M2();
```
It was not immediately clear whether the tuple itself was disposed in form
(0). This was a common complaint with the rest of the forms as well: it is
unclear what the semantics of each statement is. Is the tuple itself being
disposed? Is disposal distributed over the elements? Both? Some tuple
deconstructions also happen in "reverse" order of the tuple elements'
lexical ordering. If dispose is distributed, what order are the elements
disposed in?
This also raised the question of nested declarations in the initializer, e.g.
```C#
using var x = M1(out var y)
```
Is `x` the only `using` variable? Or is `y` one as well?
**Conclusion**
Let's continue with the proposal as-is. Form (4) works and should work. There
may be compelling scenarios to open up the syntax to tuple deconstructions,
but we don't have a convincing argument yet. We also don't have a clear rule
for prohibition. For nested declarations, they are not declared as `using`
variables.
## `using expr` grammar form
These concerns dovetailed into discussion of the `using expr;` form, because some of these grammar
forms may compose. For example, since `var (x, y)` is an expression in C#, the following could be
a potentially legal statement with no modification:
```C#
using var (x, y) = M();
```
In this case `var (x, y) = M()` would be the `expr` in `using expr;`.
This form seems desirable to round out the feature, but it isn't clear how it fits into the language.
The previous decision seems to imply we don't want `using` deconstructions, but it isn't clear what
rule we would use to prohibit them, in a principled sense. The feature also has some integration
concerns. `using (expr);` is already a legal construct in the C# language with different semantics,
although the compiler gives a warning about it today. There is some concern that `using expr;` and
`using (expr);` are too close grammatically and that the syntax effectively rules out parenthesized expressions.
Finally, there were questions about grammatical ambiguity with possible
future language features. If C# were to allow statements on the top level, a
`using System;` line could either be a using-directive if `System` is a
namespace, or a using-statement if `System` is a type. The same problem could
occur if we were to allow using-directives at the statement level. This
doesn't seem very bad since we already have similar ambiguities with `Color
Color` rules and resolve them properly during semantic analysis. These
ambiguities are also probably present for using-directive aliases.
There were a couple proposals to try to deal with some of these problems:
1. Any expression that declares variables is disallowed as a `using expr;`
2. Hold off on `using expr;` for now.
3. Allow `_` as a discard for `using var _ = expr;`
- Or `using _ = expr;`
**Conclusion**
This is a blocking issue that we must decide on for C# 8.0. Either we should
disallow this form entirely or find some principle to use to reject the
constructions we find confusing. However, we think this problem is solvable
and shouldn't block continued work on the feature.
## Flow control safety
The last design issue was safety in the presence of `goto` and similar flow
control features (e.g., local functions). The existing spec notes that backward
flow control is not a problem, but what about forward flow control? For example,
```C#
{
goto target;
using var x = new FileStream(...);
target:
var y = x;
return;
}
```
In the previous example, this is an error, because `x` is not definitely assigned.
In fact, all uses in this category, where flow is manipulated to skip over the
variable definition before a read, are safe because the variable will not be
definitely assigned. In addition, because `using` variables are read-only, it also
cannot be assigned later.
One case which the spec does not currently handle is
```C#
{
goto target;
using var x = new FileStream(...);
target:
return;
}
```
Here `x` is never read, so there would be no definite assignment errors. However,
there is an implicit read of the variable at the end of the variable lifetime, which
could be a read of an unassigned variable.
**Conclusion**
A new line to the spec should be added saying that, if the end of a `using` variable's
lifetime is reachable, that variable must be definitely assigned at that point.
*Open question*
* [ ] This needs more precise language. The spec does not have reachability at "points".
What do we mean when we say the end of a block is "reachable"?
# Pattern-based `using` statement
We like the feature. Main question: what type of pattern do we look for? As a general
guideline, we don't want to have another special case pattern. However, it seems like
we have multiple styles already.
- `GetAwaiter` doesn't allow `params` or optional parameters
- LINQ does
Do we want `using` to be like `await` or like LINQ?
Also, do we require `void` return type? Most of the patterns today have
strict requirements on return type, but they also usually consume the return
type. `using` does not.
**Conclusion**
Keep the spec as is: `Dispose` must be parameter-less in instance-form,
`void`-returning, and accessible. This allows for extension methods, but
not optional parameters or `params`.
# Multiline interpolated string syntax
It's hard to remember which is the correct syntax: `$@""` or `@$""`. The
proposal is to allow either.
**Conclusion**
No objections.