-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Open LDM Issues in Pattern-Matching
This is a summary of the remaining open issues for pattern-matching in C# 8.
Reserve and
and or
in patterns (open)
In anticipation of possibly permitting and
and or
as pattern combinators in the future, we should forbid (or at least warn) when these identifiers are used as the designator in a declaration or recursive pattern.
See also dotnet/roslyn#33425
Resolution: We did not do this in C# 8.0.
Propose to change precedence of switch expression to primary (open)
The switch expression is currently at relational precedence. I propose to change it to primary precedence. See https://github.com/dotnet/csharplang/issues/2331 for details.
Resolution: It gets a new precedence just looser than primary.
Single-element positional deconstruction (open)
The current LDM decision on single-element positional deconstruction pattern is that the type is required.
This is particularly inconvenient for tuple types and other generic types, and perhaps impossible for values deconstructed by a dynamic check via ITuple
.
We should consider if other disambiguation should be permitted (e.g. the presence of a designation).
if (o is (3) _)
Resolution 2018-10-10: Discussed but not resolved.
Disambiguating deconstruction using parameter names (open)
We could permit more than one Deconstruct
method if we permit disambiguating by parameter names.
class Point
{
public void Deconstruct(double Angle, double Length) => ...;
public void Deconstruct(int X, int Y) => ...;
}
Point p = ...;
if (p is (X: 3, Y: 4)) ...;
Should we do this?
Resolution: No, not at this time.
var pattern for 0 and 1 elements (open)
The 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.
public class C {
public void Deconstruct() => throw null;
public void Deconstruct(out int i) => throw null;
public void Deconstruct(out int i, out int j) => throw null;
void M() {
if (this is var ()) { } // error
if (this is var (x1)) { } // error
if (this is var (x2, y2)) { } // ok
}
}
I propose we relax the grammar to permit 0-element and 1-element var patterns.
We could do the same for deconstruction.
Resolution: Approved for patterns. Left to future consideration for deconstruction.
Kind of member in a property pattern (open)
A property pattern { PropName : Pattern }
is used to check if the value of a property or field matches the given pattern.
Besides readable properties and fields, what other kinds of members should be permitted here?
- An indexed property? Possible indexed with a constant?
- An event reference?
- Anything else?
Resolution: None of these at this time.
Restricted types (open)
A recent bug (dotnet/roslyn#27803) revealed how pattern matching interacts with restricted types in interesting ways (when doing type checks).
We should discuss with LDM on whether this should be banned (like pointer types), and if there are actual use cases that need it.
Resolution: Pointers actually work, e.g. p is null
or p is var x
. Restricted types work too. No action.
Matching using ITuple (open)
I assume that we only intend to permit matching using ITuple
when the type is omitted?
IComparable x = ...;
if (x is ITuple(3, 4)) // (1) permitted?
if (x is object(3, 4)) // (2) permitted?
if (x is SomeTypeThatImplementsITuple(3, 4)) // (3) permitted?
Resolution: Confirmed.
Matching using ITuple
in the presence of an extension Deconstruct
(needs proposal)
When 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.
public class C : ITuple
{
int ITuple.Length => 3;
object ITuple.this[int i] => i + 3;
public static void Main()
{
var t = new C();
Console.WriteLine(t is (3, 4)); // true? or a compile-time error?
Console.WriteLine(t is (3, 4, 5)); // true? or a compile-time error?
}
}
static class Extensions
{
public static void Deconstruct(this C c, out int X, out int Y) => (X, Y) = (3, 4);
}
Discussion 2018-10-24: No conclusion. We need a proposal to consider.
Resolution: The current LDM position is confirmed.
Switch expression as a statement expression (closed)
It has been requested that we permit a switch expression to be used as a statement expression. In order to make this work, we'd
- Permit its type to be
void
. - Permit a switch expression to be used as a statement expression if all of the switch arm expressions are statement expressions.
- Adjust type inference so that
void
is more specific than any other type, so it can be inferred as the result type.
Resolution 2018-10-10: Yes! This requires a more precise spec, and it needs to handle a?.M()
where M()
returns an uncontrained T
. Excluded from C# 8.0.
ITuple vs unconstrained type parameter (closed)
Our current rules require an error in the following, and it will not attempt to match using ITuple
. Is that what we want?
public static void M<T>(T t)
{
Console.WriteLine(t is (3, 4)); // error: no Deconstruct
}
Resolution 2018-10-24: Lets keep it an error for now but be open to relaxing it in the future.
Permit a trailing comma in a switch expression (closed)
See #2098
Resolution 2019-01-09: Yes.
Rename deconstruct_pattern to positional_pattern (closed)
Resolution 2019-01-09: Yes.
Disambiguate declaration patterns with (non-nullable) arrays of nullable type (closed)
The introduction of nullable reference types introduced a breaking change in the parsing of array types. It is recommended that we reconsider the order in which nullable annotations are considered in multidimensional arrays to resolve the ambiguity and permit a compatible implementation.
Partial Resolution per LDM 2019-01-07: Keep the order of array designators. But a new syntax resolution rule: following an is
, as
, or new
in an expression, an array type may not end with a ?
unless the ?
is followed by a token that cannot start an expression. The ?
token (if not considered to be part of the array specifier) will then naturally be considered to be the conditional operator, which restores compatibility with existing code.
Resolution 2019-01-09: Reverted. We are going with second option in dotnet/roslyn#32141 (comment). See also the "No way to match a multidimensional array with an inner nullable" issue below.
Should SwitchExpressionException capture an unreified tuple input? (closed)
Resolution 2019-01-09: Yes.
No way to match a multidimensional array with an inner nullable (closed)
Another small issue with our resolution to See dotnet/roslyn#32141. How do I write a pattern-matching operation where the type is “an array of a nullable array”? That would (un)naturally be written
if (o is string[][]? s)
but we decided that, since there is an identifier after the ?
, the ?
designates the start of a ternary operator and this code is a syntax error. This is something I think people are likely to want to do (since an array typically may contain nulls), so it would be a shame to disallow it.
Resolution 2019-01-09: This (and its extension to a problem in recursive patterns) was the straw that broke the camel's back, and caused us to revisit the interaction of the nullable annotation and array dimension specifiers. The new resolution is that the ?
annotation is a type constructor that can be applied to array types, and an array type does not have any ?
among its array specifiers. This is the second option in dotnet/roslyn#32141 (comment).