{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/csharplang/issues/1054","repository_url":"https://api.github.com/repos/dotnet/csharplang","labels_url":"https://api.github.com/repos/dotnet/csharplang/issues/1054/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/csharplang/issues/1054/comments","events_url":"https://api.github.com/repos/dotnet/csharplang/issues/1054/events","html_url":"https://github.com/dotnet/csharplang/issues/1054","id":269271381,"node_id":"MDU6SXNzdWUyNjkyNzEzODE=","number":1054,"title":"Open LDM Issues in Pattern-Matching","user":{"login":"gafter","id":3804346,"node_id":"MDQ6VXNlcjM4MDQzNDY=","avatar_url":"https://avatars3.githubusercontent.com/u/3804346?v=4","gravatar_id":"","url":"https://api.github.com/users/gafter","html_url":"https://github.com/gafter","followers_url":"https://api.github.com/users/gafter/followers","following_url":"https://api.github.com/users/gafter/following{/other_user}","gists_url":"https://api.github.com/users/gafter/gists{/gist_id}","starred_url":"https://api.github.com/users/gafter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gafter/subscriptions","organizations_url":"https://api.github.com/users/gafter/orgs","repos_url":"https://api.github.com/users/gafter/repos","events_url":"https://api.github.com/users/gafter/events{/privacy}","received_events_url":"https://api.github.com/users/gafter/received_events","type":"User","site_admin":false},"labels":[{"id":537024200,"node_id":"MDU6TGFiZWw1MzcwMjQyMDA=","url":"https://api.github.com/repos/dotnet/csharplang/labels/Discussion","name":"Discussion","color":"c2e0c6","default":false},{"id":513192335,"node_id":"MDU6TGFiZWw1MTMxOTIzMzU=","url":"https://api.github.com/repos/dotnet/csharplang/labels/Spec","name":"Spec","color":"d93f0b","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/dotnet/csharplang/milestones/8","html_url":"https://github.com/dotnet/csharplang/milestone/8","labels_url":"https://api.github.com/repos/dotnet/csharplang/milestones/8/labels","id":2339243,"node_id":"MDk6TWlsZXN0b25lMjMzOTI0Mw==","number":8,"title":"8.0 candidate","description":"Candidates for the C# 8.0 release","creator":{"login":"gafter","id":3804346,"node_id":"MDQ6VXNlcjM4MDQzNDY=","avatar_url":"https://avatars3.githubusercontent.com/u/3804346?v=4","gravatar_id":"","url":"https://api.github.com/users/gafter","html_url":"https://github.com/gafter","followers_url":"https://api.github.com/users/gafter/followers","following_url":"https://api.github.com/users/gafter/following{/other_user}","gists_url":"https://api.github.com/users/gafter/gists{/gist_id}","starred_url":"https://api.github.com/users/gafter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gafter/subscriptions","organizations_url":"https://api.github.com/users/gafter/orgs","repos_url":"https://api.github.com/users/gafter/repos","events_url":"https://api.github.com/users/gafter/events{/privacy}","received_events_url":"https://api.github.com/users/gafter/received_events","type":"User","site_admin":false},"open_issues":30,"closed_issues":1,"state":"open","created_at":"2017-02-21T23:48:05Z","updated_at":"2018-07-27T18:01:46Z","due_on":"2080-01-01T08:00:00Z","closed_at":null},"comments":300,"created_at":"2017-10-28T00:54:22Z","updated_at":"2018-08-01T23:15:32Z","closed_at":null,"author_association":"MEMBER","body":"Here are my top open issues for pattern matching after C# 7.\r\n\r\n# Open LDM Issues in Pattern-Matching\r\n\r\n### Recursive pattern forms\r\n\r\nThe following is the working grammar for recursive patterns. It requires LDM review.\r\n\r\n``` antlr\r\npattern\r\n\t: declaration_pattern\r\n\t| constant_pattern\r\n\t| deconstruction_pattern\r\n\t| property_pattern\r\n\t;\r\ndeclaration_pattern\r\n\t: type identifier\r\n\t;\r\nconstant_pattern\r\n\t: expression\r\n\t;\r\ndeconstruction_pattern\r\n\t: type? '(' subpatterns? ')' property_subpattern? simple_designation?\r\n\t;\r\nsubpatterns\r\n\t: subpattern\r\n\t| subpattern ',' subpatterns\r\n\t;\r\nsubpattern\r\n\t: pattern\r\n\t| identifier ':' pattern\r\n\t;\r\nproperty_subpattern\r\n\t: '{' subpatterns? '}'\r\n\t;\r\nproperty_pattern\r\n\t: type? property_subpattern simple_designation?\r\n\t;\r\n\r\nsimple_designation\r\n\t: single_variable_designation\r\n\t| discard_designation\r\n        ;\r\n```\r\n\r\nIt is a semantic error if any _subpattern_ of a _property_pattern_ does not contain an _identifier_ (it must be of the second form, which has an _identifier_).\r\n\r\nNote that a null-checking pattern falls out of a trivial property pattern. To check if the string `s` is non-null, you can write any of the following forms\r\n\r\n``` c#\r\nif (s is object o) ... // o is of type object\r\nif (s is string x) ... // x is of type string\r\nif (s is {} x) ... // x is of type string\r\nif (s is {}) ...\r\n```\r\n\r\nI suspect that the LDM will prefer `is` instead of `:` in the _property_subpattern_.\r\n\r\n**Resolution 2017-11-20 LDM**: we want to forbid a deconstruction pattern with a single subpattern but an omitted type. We may consider ways to relax this later. The `identifier`s in the syntax above should be designations.]\r\n\r\n### Matching via `ITuple`\r\n\r\nWe need to specify/decide under what conditions the compiler will attempt to match a positional pattern using `ITuple`.\r\n\r\n**Resolution 2017-11-20 LDM** permit matching via `ITuple` only for `object`, `ITuple`, and types that are declared to implement `ITuple` but contain no `Deconstruct` methods.]\r\n\r\n### Parenthesized expression vs tuple pattern\r\n\r\nThere is an ambiguity between a constant pattern expressed using a parenthesized expression, and a “positional” (Deconstruct) recursive pattern with one element:\r\n\r\n``` c#\r\ncase (2):\r\n```\r\n\r\nHere are the two possible meanings:\r\n``` c#\r\nswitch (2)\r\n{\r\n    case (2): // a parenthesized expression\r\n```\r\nand\r\n``` c#\r\nclass C { public void Deconstruct(out int x) … }\r\n\r\nswitch (new C())\r\n{\r\n    case (2): // Call Deconstruct once.\r\n```\r\nWhile it is possible for the programmer to disambiguate (e.g. `case +(2):` or `case (2) {}`), we still need a policy for the compiler to disambiguate when the programmer has not done so. The proposed policy is that this will be parsed as a parenthesized expression. However, if the type of the parenthesized expression is not suitable given the switch expression, semantic analysis will notice the set of parentheses and fall back to trying `Deconstruct`. That will allow both of the above examples to “work” as written.\r\n\r\nIt does this recursively, so this will work too:\r\n``` c#\r\nclass C { public void Deconstruct(out D d) … }\r\nclass D { public void Deconstruct(out int x) … }\r\n\r\nswitch (new C())\r\n{\r\n    case ((2)): // Call Deconstruct twice.\r\n```\r\nA semantic ambiguity arises between these two interpretations when the switched type is, for example, `object` (we deconstruct using `ITuple`). Another way the ambiguity could arise would be if someone were to write an (extension) `Deconstruct` method for one of the built-in types. In either case we will just treat it as a parenthesized expression (i.e. the simplest interpretation that is semantically meaningful), and require explicit disambiguation if another interpretation is intended. \r\n\r\n**Resolution 2017-11-20 LDM**: disallow a deconstruction pattern that contains a single subpattern but for which the type is omitted. We'll look at other ways of disambiguating later, such as perhaps permitting `var` to infer the type, or a trailing comma. This also keeps the design space open for using parens for grouping patterns in the future, e.g. if we introduce `or` and `and` patterns.]\r\n\r\n### Cast expression ambiguity\r\n\r\nThere is a syntactic ambiguity between a cast expression and a positional pattern. The switch case\r\n``` c#\r\ncase (A)B:\r\n```\r\ncould be either a cast (i.e. existing code, if A is the name of an enum type, `Int32` or a using-alias)\r\n``` c#\r\ncase (Int32)MyConstantValue:\r\n```\r\nor a single-element deconstruction pattern with the constant subpattern A being named B.\r\n``` c#\r\ncase (MyConstantValue) x:\r\n```\r\nWhen such an ambiguity occurs, it (is proposed that it) will be parsed as a cast expression (for compatibility with existing code). To disambiguate, you can add an empty property pattern part:\r\n``` c#\r\ncase (MyConstantValue) {} x;\r\n```\r\n\r\n**Resolution 2017-11-20 LDM**: this issue is moot given the resolution to the previous issue. It is a cast if it can be a cast, otherwise it is an error.]\r\n\r\n### Short discard\r\n\r\nI think that in recursive patterns the syntax for a discard `var _` will be a bit too verbose. I'd rather see `_` used as an alternative, e.g. `o is Point(3, _)`. But that conflicts with its use as a constant pattern. That also gets mixed up with the parenthesized expression ambiguity described above. My proposal would be to say that `_` in a pattern is *always* a discard, but with an error reported if there is a constant named `_` in scope. That error prevents a silent change to existing code, where `x is _` may treat the `_` as a constant.\r\n\r\nThis was part of the motivation for #1064, but that may be going too far.\r\n\r\n**Resolution 2017-11-20 LDM**: we want to permit the short discard. We don't want to break existing code that tests for a type, e.g. `expr is _` where there is a type named `_`. So that will continue to work. However, we do not want to permit a short discard at the top level in an is-pattern expression. We also want to forbid matching a named constant named `_`. The latter may be a breaking change, but only from a recently permitted coding pattern, so it is probably a break we can take.]\r\n\r\n### Match expression\r\n\r\nWe need to define the _syntax_ and _semantics_ of the match expression.\r\n\r\nThere are many syntax options listed below. Here is one proposal to get the discussion started:\r\n\r\n``` c#\r\n    state = (state, action) switch {\r\n        (DoorState.Closed, Action.Open) => DoorState.Opened,\r\n        (DoorState.Opened, Action.Close) => DoorState.Closed,\r\n        (DoorState.Closed, Action.Lock) => DoorState.Locked,\r\n        (DoorState.Locked, Action.Unlock) => DoorState.Closed,\r\n        _ => state};\r\n```\r\n\r\nthis is based on the following grammar\r\n\r\n``` antlr\r\nswitch_expression\r\n    : null_coalescing_expression switch '{' switch_expression_case_list '}'\r\n    ;\r\nswitch_expression_case_list\r\n    : switch_expression_case\r\n    | switch_expression_case_list ',' switch_expression_case\r\n    ;\r\nswitch_expression_case\r\n    : pattern when_clause? '=>' expression\r\n    ;\r\n```\r\n\r\nWith *switch_expression* added at roughly the same precedence as a conditional expression but left associative.\r\n\r\n``` antlr\r\nnon_assignment_expression\r\n    : conditional_expression\r\n    | lambda_expression\r\n    | query_expression\r\n    | switch_expression\r\n    ;\r\n```\r\n\r\nThe main semantic question is what should happen when the set of patterns is incomplete: should the compiler silently accept the code (or perhaps with a warning) and allow it to throw a runtime exception when the input is unmatched, or should it be a compile-time error?\r\n\r\n**Discussion 2018-04-04 LDM**: Syntax OK for now. We will probably revisit. Warning and exception OK.\r\n\r\n### Scope of expression variables in initializers\r\n\r\nSee https://github.com/dotnet/csharplang/issues/32 for details.\r\n\r\n### Order of evaluation in pattern-matching\r\n\r\nGiving the compiler flexibility in reordering the operations executed during pattern-matching can permit flexibility that can be used to improve the efficiency of pattern-matching. The (unenforced) requirement would be that properties accessed in a pattern, and the Deconstruct methods, are required to be \"pure\" (side-effect free, idempotent, etc). That doesn't mean that we would add purity as a language concept, only that we would allow the compiler flexibility in reordering operations.\r\n\r\n**Resolution 2018-04-04 LDM**: confirmed: the compiler is permitted to reorder calls to `Deconstruct`, property accesses, and invocations of methods in `ITuple`, and may assume that returned values are the same from multiple calls. The compiler should not invoke functions that cannot affect the result, and we will be very careful before making any changes to the compiler-generated order of evaluation in the future.\r\n\r\n### `default` pattern\r\n\r\nOnce we start having a match expression that doesn't use the keyword `case`, a constant pattern that uses the simple literal `default` will look like a default branch of the switch expression. To avoid any confusion, I would like to disallow a simple expression `default` as a constant pattern. We could leave it as a warning, as today, or make it an error in a switch statement. Note that you can always write either `null`, `0`, or `'\\0'` instead of `default`.\r\n\r\n**Resolution**: See https://github.com/dotnet/roslyn/issues/23499 which documents our tentative decision, taken via email, to forbid `default` as a pattern.\r\n\r\n### Range Pattern\r\n\r\nIf we have a range operator `1..10`, would we similarly have a range pattern? How would it work?\r\n\r\n``` c#\r\n    if (ch is in 'a' to 'z')\r\n    switch (ch) {\r\n        case in 'a' to 'z':\r\n```\r\n    \r\n**Discussion 2018-04-04 LDM**: Keep this on the back burner. It is not clear how important it is. We would want to discuss this in the context of also extending other constructs:\r\n``` c#\r\nfrom i in 1 to 100 select ...\r\n\r\nforeach (var i in 0 to s.Length-1) ...\r\n```\r\n\r\n### `var` deconstruct pattern\r\n\r\nIt would be nice if there were a way to pattern-match a tuple (or Deconstructable) into a set of variables declared only by their designator, e.g. the last line in this match expression\r\n\r\n``` c#\r\n    var newState = (GetState(), action, hasKey) switch {\r\n        (DoorState.Closed, Action.Open, _) => DoorState.Opened,\r\n        (DoorState.Opened, Action.Close, _) => DoorState.Closed,\r\n        (DoorState.Closed, Action.Lock, true) => DoorState.Locked,\r\n        (DoorState.Locked, Action.Unlock, true) => DoorState.Closed,\r\n        var (state, _, _) => state };\r\n```\r\n\r\n(Perhaps not the best example since it only declares one thing on the last line)\r\n\r\nThis would be based on some grammar like this\r\n\r\n``` antlr\r\nvar_pattern\r\n    : 'var' variable_designation\r\n    ;\r\n```\r\n\r\nwhere the _variable_designation_ could be a _parenthesized_variable_designation_, i.e. generalizing the current construct.\r\n\r\nTo make this syntactically unambiguous, we would no longer allow `var` to bind to a user-declared type in a pattern. Forbidding it from binding to a constant would also simplify things, but probably isn't strictly necessary.\r\n\r\nI’m suggesting this now because at a recent LDM it was suggested that `var` could perhaps be used as a placeholder for an unknown type taken from context. But that would conflict with this usage because this usage changes the syntax of what is permitted between the parens (designators vs patterns).\r\n\r\n**Resolution 2018-04-04 LDM**: YES! Approved.\r\n\r\n### ref/lvalue-producing pattern switch expression\r\n\r\nAs currently designed, the “switch expression” yields an rvalue.\r\n\r\n``` c#\r\n    e switch { p1 when c1 => v1, p2 when c2 => v2 }\r\n```\r\n@agocke pointed out that it might be valuable for there to be a variant that produces a ref or an lvalue.\r\n\r\n1.\tShould we pursue this?\r\n2.\tWhat would the syntax be?\r\n    `e switch { p1 when c1 => ref v1, p2 when c2 => ref v2 }`\r\n3.\tWould the same syntax be used to produce a ref and an lvalue?\r\n\r\n**Discussion 2018-04-04 LDM**: Lets keep this on the back burner and see if there are requests based on actual use cases.\r\n\r\n### switching on a tuple literal\r\n\r\nIn order to switch on a tuple literal, you have to write what appear to be redundant parens\r\n\r\n``` c#\r\nswitch ((a, b))\r\n{\r\n```\r\n\r\nIt has been proposed that we permit\r\n\r\n``` c#\r\nswitch (a, b)\r\n{\r\n```\r\n\r\n**Resolution 2018-04-04 LDM**: Yes. We discussed a couple of ways of doing this, and settled on making the parens of the switch statement optional when the expression being switched on is a tuple literal.\r\n\r\n### Short discard diagnostics\r\n\r\nRegarding the previous short discard diagnostics approved by the LDM (see above), there are two possibly breaking situations.\r\n\r\n`case _` where there is a named constant `_` is now an error.\r\n\r\n`e is _` is proposed to be a warning: `The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.`\r\n\r\n### Nullable Reference Types vs switch analysis\r\n\r\nWe give a warning when a switch expression does not exhaustively handle all possible inputs. What if the input is a non-nullable string `s`, in the code\r\n\r\n`s switch { string t => t }`\r\n\r\nIs a warning deserved because the switch expression does not handle all possible inputs (it doesn't handle a possible null)? Note that although we *think* s cannot be null, we still generate code for a null check and throw an exception when the null input is not handled.\r\n\r\nIf we decide *not* to generate a warning here, we may have to carefully structure the phases of the compiler so we do this exhaustiveness analysis after the nullable pass of the compiler, which will now have to record its conclusions in a rewritten bound tree.\r\n\r\nSimilarly, in the code\r\n\r\n``` c#\r\nint i;\r\nswitch (s)\r\n{\r\n    case string t: i = 0; break;\r\n}\r\nConsole.WriteLine(i); // is i definitely assigned?\r\n```\r\n\r\nIs the last line an error because `i` is not definitely assigned?\r\n\r\n### Use type unification to rule out some pattern-matching situations?\r\n\r\nSee https://github.com/dotnet/roslyn/issues/26100. The possible new warnings for the is-type operator correspond to possible errors for the is-pattern operator. We could use type unification subroutines, which are already in the compiler, to detect some situations and make them errors. Should we? Sooner is better.\r\n\r\n### Permit optionally omitting the pattern on the last branch of the switch expression\r\n\r\nWe wonder if it would be helpful to sometime omit the pattern on the last branch of a switch expression, in cases where the pattern would be `_`.\r\n\r\n### Should exhaustiveness affect definite assignment\r\n\r\nIn a non-pattern switch statement, [we do not currently (in any previous C# version) do \"exhaustiveness\" analysis](https://github.com/dotnet/roslyn/issues/24865):\r\n\r\n``` c#\r\n    int M(bool b)\r\n    {\r\n        int result;\r\n        switch (b)\r\n        {\r\n            case false:\r\n                result = 0;\r\n                break;\r\n            case true:\r\n                result = 1;\r\n                break;\r\n        }\r\n        return result; // error: not definitely assigned\r\n    }\r\n```\r\n\r\nBut in a pattern-based switch statement we do:\r\n\r\n``` c#\r\n    int M(bool b)\r\n    {\r\n        int result;\r\n        switch (b)\r\n        {\r\n            case false when true:\r\n                result = 0;\r\n                break;\r\n            case true:\r\n                result = 1;\r\n                break;\r\n        }\r\n        return result; // ok: definitely assigned\r\n    }\r\n```\r\n\r\nWe should decide and confirm the intended behavior for both cases. This also includes [reachability of statements](https://github.com/dotnet/roslyn/issues/11438). The current prototype does exhaustiveness analysis for switch statements based on a [previous informal decision](https://github.com/dotnet/roslyn/issues/11438#issuecomment-232770018). We should confirm that in the LDM.\r\n\r\n### Switch expression as a statement expression\r\n\r\nIt has been requested that we permit a switch expression to be used as a statement expression. In order to make this work, we'd\r\n1. Permit its type to be `void`.\r\n2. Permit a switch expression to be used as a statement expression if all of the switch arm expressions are statement expressions.\r\n3. Adjust type inference so that `void` is more specific than any other type, so it can be inferred as the result type.\r\n\r\n### Single-element positional deconstruction\r\n\r\nThe current LDM decision on single-element positional deconstruction pattern is that the type is required.\r\n\r\nThis is particularly inconvenient for tuple types and other generic types, and perhaps impossible for values deconstructed by a dynamic check via `ITuple`.\r\n\r\nWe should consider if other disambiguation should be permitted (e.g. the presence of a designation).\r\n\r\n``` c#\r\nif (o is (3) _)\r\n```\r\n\r\n### Disambiguating deconstruction using parameter names\r\n\r\nWe could permit more than one `Deconstruct` method if we permit disambiguating by parameter names.\r\n\r\n``` c#\r\nclass Point\r\n{\r\n    public void Deconstruct(double Angle, double Length) => ...;\r\n    public void Deconstruct(int X, int Y) => ...;\r\n}\r\n\r\nPoint p = ...;\r\nif (p is (X: 3, Y: 4)) ...;\r\n```\r\n\r\nShould we do this?\r\n\r\n### var pattern for 0 and 1 elements\r\n\r\nThe var pattern currently requires 2 or more elements because it inherits the grammar for *variable_designation*. However, both 0-element and 1-element var patterns would make sense and are syntactically unambiguous.\r\n\r\n``` c#\r\npublic class C {\r\n    public void Deconstruct() => throw null;\r\n    public void Deconstruct(out int i) => throw null;\r\n    public void Deconstruct(out int i, out int j) => throw null;\r\n    void M() {\r\n        if (this is var ()) { }          // error\r\n        if (this is var (x1)) { }        // error\r\n        if (this is var (x2, y2)) { }    // ok\r\n    }\r\n}\r\n```\r\n\r\nI propose we relax the grammar to permit 0-element and 1-element var patterns.\r\n\r\nWe could do the same for deconstruction.\r\n\r\n### Syntax model for var patterns\r\n\r\nIn C# 7, we treat both `Type identifier` and `var identifier` as a DeclarationPatternSyntax, even though they have quite different semantics.\r\n\r\nIn C# 8, we introduce a more general *var pattern* that accepts any designation on the right-hand-side.\r\n\r\nWe would prefer to use the new node for `var identifier` even though those were represented by a declaration pattern previously. Clients of the syntax API would see a change (to using a new node) when parsing existing code.\r\n\r\nIs this an acceptable change to make?\r\n\r\n### Kind of member in a property pattern\r\n\r\nA property pattern `{ PropName : Pattern }` is used to check if the value of a property or field matches the given pattern.\r\n\r\nBesides readable properties and fields, what other kinds of members should be permitted here?\r\n- An indexed property? Possible indexed with a constant?\r\n- An event reference?\r\n- Anything else?\r\n\r\n### Restricted types\r\n\r\nA recent bug (https://github.com/dotnet/roslyn/pull/27803) revealed how pattern matching interacts with restricted types in interesting ways (when doing type checks).\r\nWe should discuss with LDM on whether this should be banned (like pointer types), and if there are actual use cases that need it.\r\n\r\n### Matching using ITuple\r\n\r\nI assume that we only intend to permit matching using `ITuple` when the type is omitted?\r\n\r\n``` c#\r\nIComparable x = ...;\r\nif (x is ITuple(3, 4)) // (1) permitted?\r\nif (x is object(3, 4)) // (2) permitted?\r\nif (x is SomeTypeThatImplementsITuple(3, 4)) // (3) permitted?\r\n```\r\n\r\n### Matching using `ITuple` in the presence of an extension `Deconstruct`\r\n\r\nWhen an extension `Deconstruct` method is present and a type also implements the `ITuple` interface, it is not clear which should take priority. I believe the current LDM position (probably not intentional) is that the extension method is used for the deconstruction declaration or deconstruction assignment, and `ITuple` is used for pattern-matching. We probably want to reconcile these to be the same.\r\n\r\n``` c#\r\npublic class C : ITuple\r\n{\r\n    int ITuple.Length => 3;\r\n    object ITuple.this[int i] => i + 3;\r\n    public static void Main()\r\n    {\r\n        var t = new C();\r\n        Console.WriteLine(t is (3, 4)); // true? or a compile-time error?\r\n        Console.WriteLine(t is (3, 4, 5)); // true? or a compile-time error?\r\n    }\r\n}\r\nstatic class Extensions\r\n{\r\n    public static void Deconstruct(this C c, out int X, out int Y) => (X, Y) = (3, 4);\r\n}\r\n```\r\n\r\n### ITuple vs unconstrained type parameter\r\n\r\nOur current rules require an error in the following, and it will not attempt to match using `ITuple`. Is that what we want?\r\n\r\n``` c#\r\n    public static void M<T>(T t)\r\n    {\r\n        Console.WriteLine(t is (3, 4)); // error: no Deconstruct\r\n    }\r\n```\r\n"},"comment":{"url":"https://api.github.com/repos/dotnet/csharplang/issues/comments/409755613","html_url":"https://github.com/dotnet/csharplang/issues/1054#issuecomment-409755613","issue_url":"https://api.github.com/repos/dotnet/csharplang/issues/1054","id":409755613,"node_id":"MDEyOklzc3VlQ29tbWVudDQwOTc1NTYxMw==","user":{"login":"gafter","id":3804346,"node_id":"MDQ6VXNlcjM4MDQzNDY=","avatar_url":"https://avatars3.githubusercontent.com/u/3804346?v=4","gravatar_id":"","url":"https://api.github.com/users/gafter","html_url":"https://github.com/gafter","followers_url":"https://api.github.com/users/gafter/followers","following_url":"https://api.github.com/users/gafter/following{/other_user}","gists_url":"https://api.github.com/users/gafter/gists{/gist_id}","starred_url":"https://api.github.com/users/gafter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gafter/subscriptions","organizations_url":"https://api.github.com/users/gafter/orgs","repos_url":"https://api.github.com/users/gafter/repos","events_url":"https://api.github.com/users/gafter/events{/privacy}","received_events_url":"https://api.github.com/users/gafter/received_events","type":"User","site_admin":false},"created_at":"2018-08-01T23:15:32Z","updated_at":"2018-08-01T23:15:32Z","author_association":"MEMBER","body":"Added\r\n\r\n\r\n### ITuple vs unconstrained type parameter\r\n\r\nOur current rules require an error in the following, and it will not attempt to match using `ITuple`. Is that what we want?\r\n\r\n``` c#\r\n    public static void M<T>(T t)\r\n    {\r\n        Console.WriteLine(t is (3, 4)); // error: no Deconstruct\r\n    }\r\n```\r\n"}}
{"actor":{"display_login":"gafter"}}