237 lines
No EOL
8.3 KiB
Markdown
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. |