-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create spec for const expressions for is patterns #7589
base: main
Are you sure you want to change the base?
Changes from 4 commits
ffb40d0
d5e4d8b
2767290
438900c
d66f305
1890653
259d137
9234d41
b065e8a
9eee89d
f8d2b7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# Constant `is` pattern expressions | ||
|
||
* [x] Proposed | ||
* [ ] Prototype: None | ||
* [ ] Implementation: None | ||
* [ ] Specification: Started, below | ||
|
||
## Summary | ||
[summary]: #summary | ||
|
||
Consider `is` pattern expressions as constant if the LHS is constant. | ||
|
||
## Motivation | ||
[motivation]: #motivation | ||
|
||
Certain pattern expressions that can be converted into simpler boolean expressions using equality and comparison operators are never considered constant expressions, even when all parts of the expression are considered constant and can be evaluated during compilation. However, the lowered versions of those expressions (that only involve equality and comparison operators) are always considered constant. This prohibits the ability to utilize `is` expressions for operations like comparing against a range (e.g. `x is >= 'a' and 'z'`), or checking against distinct values (e.g. `x is Values.A or Values.B or Values.C`). | ||
|
||
## Detailed design | ||
[design]: #detailed-design | ||
|
||
### Spec | ||
[spec]: #spec | ||
|
||
The [section](https://github.com/dotnet/csharpstandard/blob/draft-v7/standard/expressions.md#1223-constant-expressions) in the spec needs to be updated accordingly, in the following segments: | ||
|
||
> Only the following constructs are permitted in constant expressions: | ||
> - Literals (including the null literal). | ||
> - References to const members of class and struct types. | ||
> - References to members of enumeration types. | ||
> - References to local constants. | ||
> - Parenthesized subexpressions, which are themselves constant expressions. | ||
> - Cast expressions. | ||
> - checked and unchecked expressions. | ||
> - nameof expressions. | ||
> - The predefined +, –, !, and ~ unary operators. | ||
> - The predefined +, –, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, and >= binary operators. | ||
> - The ?: conditional operator. | ||
> - **Pattern expressions with only the following subpatterns:** | ||
> - **Boolean literal patterns.** | ||
> - **Numeric literal patterns.** | ||
> - **Character literal patterns.** | ||
> - **String literal patterns.** | ||
> - **Relative numeric literal patterns.** | ||
> - **Relative character literal patterns.** | ||
> - **Null literal patterns.** | ||
> - **Default literal patterns.** | ||
> - sizeof expressions, provided the unmanaged-type is one of the types specified in §23.6.9 for which sizeof returns a constant value. | ||
> - Default value expressions, provided the type is one of the types listed above. | ||
|
||
The allowed subpatterns as shown in the list above are called "constant subpatterns", as they will be eligible for compile-time evaluation. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. presumably the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And parenthesized ones too? I didn't initially consider them to be separate kinds of patterns. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'd enumerate the exact set of patterns we expect this to work for (same as how we enumerate the exact set of unary and binary operators, etc.). note: based on how the constant-expressions spec works out, i think everything else will likely fall out. You can then give examples for each case. I don't htink you have to specify how it evaluates as the constant expression section already says this:
-- Note: @333fred if we're doing this spec, would it makes sense to roll in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LDM did express interest in it, but didn't directly approve it. Probably best to leave it as an open question for now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That works for me. We could bring for an LDM read and easily add if desired. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note, there is a discussion for the equivalent extension to support switch expressions in #7489, which will also be noted in the spec doc |
||
|
||
### Grammar | ||
[grammar]: #grammar | ||
The grammar remains untouched, as nothing changes syntactically for this change. | ||
|
||
### Semantics | ||
[semantics]: #semantics | ||
A pattern expression only consisting of the above subpatterns with a constant LHS may be evaluated at compile time currently, but it cannot be assigned to a constant symbol. With this change, constant fields and locals of type `bool` may be assigned pattern expressions, as already evaluated during compilation. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is overly specific. For example, it would prevent this code, which I would expect to be valid: const int i = (1 is 2) ? 3 : 4; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't intend to exclude support for this valid expression, and I think the document doesn't imply the inability to use constant pattern expressions in the evaluated expression of a conditional operator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the wording says:
Which indicates, only fields/locals could benefit from this. My recommendation would be to go to the part of the spec on constant evaluation and just update it as necessary. The cases you want will fall out. By adding these extra clauses, it seems to be adding more restrictive requirements. |
||
|
||
### Examples | ||
[examples]: #examples | ||
The below example shows pattern expressions being assigned to a constant field. | ||
```csharp | ||
public const int A = 4; | ||
public const bool B = A is 4; | ||
public const bool C = A is not 3 and <= 4 or 6; | ||
|
||
// DeploymentEnvironment is an enum type | ||
public const DeploymentEnvironment Environment = DeploymentEnvironment.Production; | ||
public const bool D = Environment | ||
is DeploymentEnvironment.Production | ||
or DeploymentEnvironment.Test; | ||
``` | ||
|
||
Since `A` is constant, and all the operands on the right are constant, the entire expression only consists of constants, thus the expression is evaluated during compilation. This result will then be assigned to the constant field. Likewise, `Environment` is also constant as an enum value, and so are the other subpatterns of the expression. | ||
|
||
Another example, using `null` and default value checks: | ||
```csharp | ||
const int a = 4; | ||
const bool b = false; | ||
const long c = 4; | ||
const string d = "hello"; | ||
|
||
const bool x = a is default(int); // always false | ||
const bool y = b is default(bool); // always true | ||
const bool z = d is not null; // always true | ||
const bool p = b is null; // always false | ||
``` | ||
|
||
All the above are currently valid pattern matching expressions, that also emit warnings about their constant evaluation results, about them being always true or false. | ||
333fred marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
When assigning those expressions to a constant symbol, these warnings about the constant result of the expression will **not** be reported, as the user intends to capture the constant value of the expression. | ||
|
||
Pattern expressions containing non-constant subpatterns, like accessing properties, list patterns and var patterns, are **not** constant. In the below examples, all expressions will report compiler errors: | ||
|
||
```csharp | ||
const bool q = d is string { Length: 5 }; // Error: not a constant expression | ||
333fred marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const bool r = d is [.. var prefix, 'l', 'o']; // Error: not a constant expression | ||
const bool s = d is var someString; // Error: not a constant expression | ||
``` | ||
|
||
## Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
None. | ||
|
||
## Alternatives | ||
[alternatives]: #alternatives | ||
|
||
Currently, equality and comparison operators can be used to compare against other literals, including nullability of the objects (e.g. `x != null`, or `y == 4 || y < 3`). | ||
|
||
## Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
- [ ] Requires LDM review | ||
|
||
## Design meetings | ||
[meetings]: #design-meetings | ||
|
||
- Approval for Any Time milestone: [Here](https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-10-09.md#is-expression-evaluating-const-expression-should-be-considered-constant) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm confused by this statement.
'is'
is not prohibited in either of these cases. Both are legal.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bad wording, I meant that we are not currently able to use this kind of syntax for that purpose, and use that result as a const expression. Will update to make it clearer.