Dealing with var, parts 1 and 2#1696
Conversation
1. A *var_pattern* can't be used if an identifier `var` is in scope. 2. If `var` is a type in scope, it can't be used as a *declaration_pattern*. However, other "spellings" of `var` are allowed as a type.
|
Just to set expectations, I'm out for the next week or so - will review all of these pending PRs when I get back. |
Nigel-Ecma
left a comment
There was a problem hiding this comment.
Unfortunately poking these issues brings out more :-(
If I am reading this correctly the Note on line 89 contains normative information. I think the cases maybe:
When there is no declaration of var:
- Then
var x; wherexis an discard, identifier, tuple; is a var_pattern
When a type declaration for var is in scope:
- Then
var xis neither a var_pattern or declaration_pattern - However
@var x; and other ugly ways to write “var”; is a declaration_pattern
When var is declared in scope as something other than a type:
- I’ve no idea offhand
I don’t think this is clear in the text of either §11.2.2 or §11.2.4
Would it be better if these two clauses were adjacent? Indeed maybe investigating a “Declaration patterns” clause with subclauses General (includes the disambig stuff), Explicitly typed (declaration_pattern), and Implicitly type (var_pattern), could be considered?
Tuples: consider the ways to match a pair of ints:
(int, int) x– declaration_pattern- `var (x, y) – var_pattern
- (int x, int y) - positional_pattern
(int, int) (x, y)– syntactically invalid
Should this be made clear somewhere?
dynamic: The syntax of declaration_pattern permits dynamic but this is I understand invalid as the semantics are defined in terms of is (line 91) which disallows dynamic. This should be covered in §11.2.2
I knew this looked to easy. First, moved "Constant Pattern" before "Declaration Pattern". Now, *declaration_pattern* and *var_pattern* are adjacent. That should make it easier to determine if we want to combine them. The flow seems reasonable. Next, handle `var` in general. I moved these rules into the "general" section, because they apply across all patterns. If `var` is an identifier, it binds to that element. If `var` is a constant, it's a constant pattern. If it's a type, it's a type pattern. If it's a variable, it's an invalid pattern. (Similarly if its a method group or namespace). Finally, add a note about how tuple literals are parsed as different patterns depending on their form.
|
2nd commit addresses #1696 (review). I knew this looked to easy. First, moved "Constant Pattern" before "Declaration Pattern". Now, declaration_pattern and var_pattern are adjacent. That should make it easier to determine if we want to combine them. The flow seems reasonable. Next, handle Finally, add a note about how tuple literals are parsed as different patterns depending on their form. |
|
@Nigel-Ecma I believe I've addressed your review comments. Can you take another look? |
jskeet
left a comment
There was a problem hiding this comment.
Will look again once the constant pattern movement has been sorted out, to avoid missing something.
|
|
||
| The order of evaluation of operations and side effects during pattern-matching (calls to `Deconstruct`, property accesses, and invocations of methods in `System.ITuple`) is not specified. | ||
|
|
||
| The *identifier* `var` appearing in any *pattern* is bound using the normal identifier-resolution rules. The *identifier* `var` may appear in patterns only where the entity to which it resolves is valid in that position. When no declaration of the *identifier* `var` is in scope, the form `var` *designation* is recognised as a *var_pattern* ([§11.2.4](patterns.md#1124-var-pattern)); when any declaration of the *identifier* `var` is in scope, a *var_pattern* cannot be used. The verbatim identifier `@var` and Unicode-escaped equivalents are distinct identifiers from the contextual keyword `var` and are bound and used in patterns according to the entities they denote, like any other identifier. |
There was a problem hiding this comment.
Not sure about the verbatim identifier part - that would suggest this should compile:
using System;
class @var {}
class Test
{
static void Main()
{
object x = "";
if (x is var y)
{
Console.WriteLine("yes");
}
}
}
... but it doesn't. I think you're trying to say that using @var in a pattern isn't equivalent to using var, but if that's the case, we should be more explicit about it.
| > | ||
| > - If `var` denotes a type, then `var` may be the *type* of a *declaration_pattern* ([§11.2.2](patterns.md#1122-declaration-pattern)). | ||
| > - If `var` denotes a constant, then `var` may be the *constant_expression* of a *constant_pattern* ([§11.2.3](patterns.md#1123-constant-pattern)). | ||
| > - Otherwise (for example, when `var` denotes a field, property, local variable, parameter, or any other entity that is neither a type nor a constant), `var` is not valid in pattern position. |
There was a problem hiding this comment.
I'm not sure what "in pattern position" means here. (I suspect just adding a couple of words will be fine.)
There was a problem hiding this comment.
Whatever we decide to do, we should apply to line 191 as well.
There was a problem hiding this comment.
@jskeet – I think the intention is “is not valid as the first token of a pattern”
Nigel-Ecma
left a comment
There was a problem hiding this comment.
A step in the right direction, but describing this cleanly is a challenge… :-(
|
|
||
| > *Note*: When a declaration of the *identifier* `var` is in scope, the consequences of the rule above are: | ||
| > | ||
| > - If `var` denotes a type, then `var` may be the *type* of a *declaration_pattern* ([§11.2.2](patterns.md#1122-declaration-pattern)). |
There was a problem hiding this comment.
I believe this is incorrect, once var is declared as a type then it cannot be used as the type in a declaration_pattern – you must go ugly (@var et al) – but it can be used in is *type*[*].
If var is declared as a constant then both is var – constant_pattern and is var y – var_pattern are valid. However is @var y is invalid as that references the constant…
Or something like that!
[*] This is testing for v8, for v9 is *type* vs is *pattern* where pattern is type_pattern throws a spanner, and the rest of the toolbox, into the works as both in theory exist and have different semantics yet are syntactically indistinguishable – at least that’s what the source material says…
| > | ||
| > - If `var` denotes a type, then `var` may be the *type* of a *declaration_pattern* ([§11.2.2](patterns.md#1122-declaration-pattern)). | ||
| > - If `var` denotes a constant, then `var` may be the *constant_expression* of a *constant_pattern* ([§11.2.3](patterns.md#1123-constant-pattern)). | ||
| > - Otherwise (for example, when `var` denotes a field, property, local variable, parameter, or any other entity that is neither a type nor a constant), `var` is not valid in pattern position. |
There was a problem hiding this comment.
@jskeet – I think the intention is “is not valid as the first token of a pattern”
|
@jskeet is taking an action to create as exhaustive a list of test cases as feasible. |
|
Starting point of test cases: #if VAR_IS_NAMESPACE
namespace var
{
#endif
public class VarTests
{
#if VAR_IS_CONSTANT
private const int var = 5;
#elif VAR_IS_TYPE
private class @var { }
#endif
static void Tests()
{
object o = new object();
bool t1 = o is var;
bool t2 = o is @var;
bool t3 = o is (var);
bool t4 = o is (@var);
if (o is var t5) { }
if (o is @var t6) { }
if (o is (var t7a, var t7b)) { }
if (o is (@var t8a, @var t8b)) { }
}
}
#if VAR_IS_NAMESPACE
}
#endifThe Note: when Without defining anything:
With
With
With
|
Fixes #1663
Fixes #1665
Because these are related, and small in scope (even if combined) put them both in one PR:
varis in scope.varis a type in scope, it can't be used as a declaration_pattern. However, other "spellings" ofvarare allowed as a type.