Skip to content

Dealing with var, parts 1 and 2#1696

Open
BillWagner wants to merge 2 commits into
dotnet:draft-v8from
BillWagner:dealing-with-var
Open

Dealing with var, parts 1 and 2#1696
BillWagner wants to merge 2 commits into
dotnet:draft-v8from
BillWagner:dealing-with-var

Conversation

@BillWagner

Copy link
Copy Markdown
Member

Fixes #1663
Fixes #1665

Because these are related, and small in scope (even if combined) put them both in one PR:

  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.

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.
@BillWagner BillWagner requested review from Nigel-Ecma and jskeet May 19, 2026 16:07
@jskeet

jskeet commented May 19, 2026

Copy link
Copy Markdown
Contributor

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 Nigel-Ecma left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; where x is an discard, identifier, tuple; is a var_pattern

When a type declaration for var is in scope:

  • Then var x is 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) xdeclaration_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.
@BillWagner

Copy link
Copy Markdown
Member Author

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 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.

@BillWagner BillWagner added the meeting: discuss This issue should be discussed at the next TC49-TG2 meeting label Jun 3, 2026
@BillWagner

Copy link
Copy Markdown
Member Author

@Nigel-Ecma I believe I've addressed your review comments. Can you take another look?

@jskeet jskeet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will look again once the constant pattern movement has been sorted out, to avoid missing something.

Comment thread standard/patterns.md

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread standard/patterns.md
>
> - 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what "in pattern position" means here. (I suspect just adding a couple of words will be fine.)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whatever we decide to do, we should apply to line 191 as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jskeet – I think the intention is “is not valid as the first token of a pattern

Comment thread standard/patterns.md

@Nigel-Ecma Nigel-Ecma left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A step in the right direction, but describing this cleanly is a challenge… :-(

Comment thread standard/patterns.md

> *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)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 varconstant_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…

Comment thread standard/patterns.md
>
> - 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jskeet – I think the intention is “is not valid as the first token of a pattern

Comment thread standard/patterns.md
@jskeet

jskeet commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@jskeet is taking an action to create as exhaustive a list of test cases as feasible.

@jskeet

jskeet commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

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
}
#endif

The t3 and t4 cases exist for the sake of C# 9, where the rules will change slightly.

Note: when var refers to a namespace,

Without defining anything:

  • t1 fails with "The contextual keyword 'var' may only appear within a local variable declaration or in script code"
  • t2, t6, t8 fail with "The type or namespace name 'var' could not be found (are you missing a using directive or an assembly reference?)"
  • t3, t4 fail with "The name 'var' does not exist in the current context"
  • t5, t7 are valid

With VAR_IS_NAMESPACE defined:

  • t1, t2, t6, t8 all fail with "'var' (or '@var') is a namespace but is used like a type"
  • t3, t4 fail with "'var' is a namespace but is used like a variable"
  • t5 and t7 are valid

With VAR_IS_CONSTANT defined:

  • t6 and t8 fail with "The type or namespace name 'var' could not be found (are you missing a using directive or an assembly reference?)"
  • t1, t2, t3, t4, t5, t7 are valid

With VAR_IS_TYPE defined:

  • t5 and t7 fail with "The syntax 'var' for a pattern is not permitted to refer to a type, but 'VarTests.var' is in scope here."
  • t3, t4 fail with "Feature 'type pattern' is not available in C# 8.0. Please use language version 9.0 or greater."
  • t1, t2, t6 and t8 are valid

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

meeting: discuss This issue should be discussed at the next TC49-TG2 meeting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dealing with “var x” – Part 1 Dealing with “var x” – Part 2

3 participants