Skip to content
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

Add support for new pattern kinds #873

Open
wants to merge 13 commits into
base: draft-v8
Choose a base branch
from
4 changes: 4 additions & 0 deletions standard/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ An implicit conversion exists from a *default_literal* ([§12.8.20](expressions.

While throw expressions do not have a type, they may be implicitly converted to any type.

### §switch-expression-conversion Switch expression conversion

There is an implicit conversion from a *switch_expression* (§switch-expression-new-clause) to every type `T` for which there exists an implicit conversion from each *switch_expression_arm*'s expression to `T`.

## 10.3 Explicit conversions

### 10.3.1 General
Expand Down
70 changes: 66 additions & 4 deletions standard/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ The precedence of an operator is established by the definition of its associated
> | ----------------- | ------------------------------- | -------------------------------------------------------|
> | [§12.8](expressions.md#128-primary-expressions) | Primary | `x.y` `x?.y` `f(x)` `a[x]` `a?[x]` `x++` `x--` `new` `typeof` `default` `checked` `unchecked` `delegate` `stackalloc` |
> | [§12.9](expressions.md#129-unary-operators) | Unary | `+` `-` `!` `~` `++x` `--x` `(T)x` `await x` |
> | §switch-expression-new-clause | Switch | `switch { … }` |
> | [§12.10](expressions.md#1210-arithmetic-operators) | Multiplicative | `*` `/` `%` |
> | [§12.10](expressions.md#1210-arithmetic-operators) | Additive | `+` `-` |
> | [§12.11](expressions.md#1211-shift-operators) | Shift | `<<` `>>` |
Expand Down Expand Up @@ -3555,6 +3556,61 @@ At run-time, the expression `await t` is evaluated as follows:

An awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` should cause the delegate `r` to be invoked at most once. Otherwise, the behavior of the enclosing async function is undefined.

## §switch-expression-new-clause Switch expression

A *switch_expression* provides `switch`-like semantics in an expression context.

```ANTLR
switch_expression
: range_expression 'switch' '{' (switch_expression_arms ','?)? '}'
Copy link
Contributor

Choose a reason for hiding this comment

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

Something has gone wrong here:

  1. range_expression does not appear to be defined anywhere
  2. it appears the intention is to slot switch_expression between multiplicative_expression & unary_expression in the expression hierarchy, if this is the case then switch_expression needs to include unary_expression so as not to break the grammar layering

Copy link
Contributor Author

Choose a reason for hiding this comment

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

range_expression is defined by the V8 PR #605, "Add Support for Indexers and Ranges," which has not yet been merged.

Copy link
Contributor

@Nigel-Ecma Nigel-Ecma Oct 29, 2023

Choose a reason for hiding this comment

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

Thanks @RexJaeschke. From #605 we have:

range_expression
    : unary_expression
    | range_expression? '..' range_expression?
    ;

This does connect to unary_expression but the grammar is still broken. The switch_expression grammar allows for empty braces,switch { }, which doesn’t seem right, was the intended grammar:

switch_expression
    : range_expression ( 'switch' '{' (switch_expression_arms ','? '}' )?
    ;

I haven't checked that all fits (see next para) but it makes a bit more logical sense and should at least fix the layering issue.

Also how is e switch { ... } switch { ... } to be handled, if at all? I.e. using one switch to provide the value to switch on for a second (third, …) one? Something like:

switch_expression
    : range_expression
    | switch_expression ( 'switch' '{' (switch_expression_arms ','? '}' )?
    ;

might be wanted, but that is left-recursive… Requiring parentheses ((e switch { ... }) switch { ... } is another option.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fix to my own comment of Oct 30, 2023: It doesn’t matter if the rule is left recursive per se. While we avoid it when not required, left/right recursion is the way left/right associativity is represented.

So the question here is how should switch { … } switch { … } switch { … } associate, if it is supported?

Probably right associative surely?

Once supported/associtivity is decided the grammar will follow≥ (famous last words ;-))

(I haven’t checked what any implementation does)

;
switch_expression_arms
: switch_expression_arm
| switch_expression_arms ',' switch_expression_arm
;
switch_expression_arm
: pattern case_guard? '=>' expression
;
```

A *switch_expression* may not be used as an *expression_statement*.
The type of the *switch_expression* is the best common type [§12.6.3.15](expressions.md#126315-finding-the-best-common-type-of-a-set-of-expressions)) of the expressions appearing to the right of the `=>` tokens of the *switch_expression_arm*s if such a type exists and the expression in every arm of the switch expression can be implicitly converted (§switch-expression-conversion) to that type.
It is an error if some *switch_expression_arm*'s pattern cannot affect the result because some previous pattern and guard will always match.
A switch expression is said to be *exhaustive* if some arm of the switch expression handles every value of its input. The compiler shall produce a warning if a switch expression is not exhaustive.
At runtime, the result of the *switch_expression* is the value of the *expression* of the first *switch_expression_arm* for which the expression on the left-hand-side of the *switch_expression* matches the *switch_expression_arm*'s pattern, and for which the *case_guard* of the *switch_expression_arm*, if present, evaluates to `true`. If there is no such *switch_expression_arm*, the *switch_expression* throws an instance of the exception `System.Runtime.CompilerServices.SwitchExpressionException`.

> *Example*: The following converts values of an enum representing visual directions in an online map to the corresponding cardinal directions:
>
> <!-- Example: {template:"code-in-class-lib", name:"SwitchExpression1", ignoredWarnings:["CS8321"]} -->
> ```csharp
> static Orientation ToOrientation(Direction direction) => direction switch
> {
> Direction.Up => Orientation.North,
> Direction.Right => Orientation.East,
> Direction.Down => Orientation.South,
> Direction.Left => Orientation.West,
> _ => throw new ArgumentOutOfRangeException(direction.ToString()),
> };
>
> public enum Direction
> {
> Up,
> Down,
> Right,
> Left
> }
>
> public enum Orientation
> {
> North,
> South,
> East,
> West
> }
> ```
>
> *end example*

## 12.10 Arithmetic operators

### 12.10.1 General
Expand All @@ -3563,10 +3619,10 @@ The `*`, `/`, `%`, `+`, and `–` operators are called the arithmetic operators.

```ANTLR
multiplicative_expression
: unary_expression
| multiplicative_expression '*' unary_expression
| multiplicative_expression '/' unary_expression
| multiplicative_expression '%' unary_expression
: switch_expression
| multiplicative_expression '*' switch_expression
| multiplicative_expression '/' switch_expression
| multiplicative_expression '%' switch_expression
Comment on lines +3624 to +3627
Copy link
Contributor

Choose a reason for hiding this comment

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

Something has gone wrong here, see comment on line 3565 above

;

additive_expression
Expand Down Expand Up @@ -4027,6 +4083,10 @@ equality_expression
```

> *Note*: Lookup for the right operand of the `is` operator must first test as a *type*, then as an *expression* which may span multiple tokens. In the case where the operand is an *expreesion*, the pattern expression must have precedence at least as high as *shift_expression*. *end note*
<!-- markdownlint-disable MD028 -->

<!-- markdownlint-enable MD028 -->
> *Note*: There is a grammar ambiguity between *type* and *constant_pattern* in a `relational_expression` involved `is`; either might be a valid parse of a qualified identifier. In such a case, only if it fails to bind as a type (for compatibility with previous versions of the language), is it resolved to be the first thing found (which must be either a constant or a type). This ambiguity is only present on the right-hand side of such an expression.
Copy link
Contributor

Choose a reason for hiding this comment

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

The preceding note on line 4085 needs work and is covered under issue #935. This note also needs work (style, “first thing found”, etc.) and is related so maybe should be covered under #935 as well; i.e. remove it here and add a comment to #935.


The `is` operator is described in [§12.12.12](expressions.md#121212-the-is-operator) and the `as` operator is described in [§12.12.13](expressions.md#121213-the-as-operator).

Expand Down Expand Up @@ -4438,6 +4498,8 @@ For an expression of the form `E is P`, where `E` is a relational expression of
- `E` does not designate a value or does not have a type.
- The pattern `P` is not applicable ([§11.2](patterns.md#112-pattern-forms)) to the type `T`.

Every *identifier* of the pattern introduces a new local variable that is definitely assigned once the corresponding *relational_expression* tests `true`.
Copy link
Contributor

Choose a reason for hiding this comment

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

“…assigned if the…” maybe? "Once" sounds like it is a temporal thing waiting on the relational expression to become true.


### 12.12.13 The as operator

The `as` operator is used to explicitly convert a value to a given reference type or nullable value type. Unlike a cast expression ([§12.9.7](expressions.md#1297-cast-expressions)), the `as` operator never throws an exception. Instead, if the indicated conversion is not possible, the resulting value is `null`.
Expand Down
Loading
Loading