From acd811f1af0fc7a962d4b5e98d89b7b03fe23002 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Sun, 28 Mar 2021 05:39:22 +0430 Subject: [PATCH 01/18] Create pattern-variables.md --- proposals/pattern-variables.md | 139 +++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 proposals/pattern-variables.md diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md new file mode 100644 index 0000000000..a9fff3ba92 --- /dev/null +++ b/proposals/pattern-variables.md @@ -0,0 +1,139 @@ +# Variable declarations under pattern combinators + +## Summary + +## Motivation + +## Specification + +### Scope + +#### Pattern variable redeclaration + +Introducing a new concept similar to *name hiding*, but these names can possibly reference either of variables based on the result of the pattern-matching at runtime. + +For a *disjunctive_pattern* of the form `left_pattern or right_pattern`: +- Pattern variables declared in the *left_pattern* can be redeclared in the *right_pattern*; permitting: + + ```cs + e is (0, var x) or (var x, 0) + ``` + +For a *logical_or_expression* of the form `left_expr || right_expr`: +- Pattern variables declared in *left_expr* can be redeclared in the *right_expr*; permitting: + + ```cs + e is (0, var x) || e is (var x, 0) + ``` + Note: This is the expression variation of the previous case. + +For a *negated_pattern* of the form `not pattern_operand`: +- Pattern variables declared in the *pattern_operand* can be redeclared anywhere in the containing expression; permitting: + + ```cs + e is not (0, var x) and not (var x, 0) + e is not (0, var x) && e is not (var x, 0) + ``` + Note: This is the DeMorgan's transformation of the previous case. + +For a *logical_not_expression* of the form `!expr_operand`: +- Pattern variables declared in the *expr_operand* can be redeclared anywhere in the containing expression; permitting: + + ```cs + !(e is (0, var x)) && !(e is (var x, 0)) + ``` + Note: This is the expression variation of the previous case. + +For a *switch_case_label* of the form `case case_pattern when when_expr`: +- Pattern variables declared in the *case_pattern* or *when_expr* can be redeclared in other case labels; permitting: + + ```cs + case (var x, 0) when e is int i: + case (0, var x) when a is int i: + ``` + + Note: This rule is currently defined for each individual *switch_section* only. + +For a *conditional_expression* of the form `expr_cond ? expr_true : expr_false`: +- Pattern variables declared in *expr_true* can be redeclared in *expr_false*; permitting: + + ```cs + b ? x is int i : y is int i + ``` + +Redeclaring pattern variables is only permitted for variables of the same type. + +> **Open question**: How identical these types should be? + +Redeclaring pattern variables in any other case follows the usual scoping rules and is disallowed. + +### Definite assignment + +Note: This section is unchanged and included for the sake of completeness. + +For an *is_pattern_expression* of the form `e is pattern`: + +- The state of *v* after *is_pattern_expression* is the same as the state of *v* after *pattern*. + +#### General rules for pattern variables in simple patterns + +Note: This section is unchanged and included for the sake of completeness. + +The following rules applies to any *primary_pattern* that declares a variable: + +- The state of *v* is definitely assigned after *primary_pattern* if the pattern is irrefutable; permitting use after: + + ```cs + _ = x is var x; + _ = 1 is int x; + _ = (1, 2) is var (x, y); + ``` + +- Otherwise, the state of *v* is "definitely assigned when true" after *primary_pattern*; permitting use after: + + ```cs + if (o is int x) + ``` + +#### Definite assignment rules for pattern variables in pattern combinators + +Note: This section is derived by definite assignment rules for expressions with an exception: for patterns we strictly require both operands to assign the same set of variables. For example, even though `e is var x or 1` is logically valid, the variable `x` is not definitely assigned. That said, some of cases might still result in impossible or irrefutable patterns by other measures. + +For a *disjunctive_pattern* of the form `left_pattern or right_pattern`: + +- The definite assignment state of *v* after *disjunctive_pattern* is determined by: + - If the state of *v* after *left_pattern* is definitely assigned, and the state of *v* after *right_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *disjunctive_pattern* is definitely assigned. + - Otherwise, if the state of *v* after *right_pattern* is definitely assigned, and the state of *v* after *left_pattern* is "definitely assigned when true", then the state of *v* after *disjunctive_pattern* is definitely assigned. + - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when false", and the state of *v* after *right_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *disjunctive_pattern* is "definitely assigned when false". + - Otherwise, if the state of *v* after *left_pattern* is "definitely assigned when true", and the state of *v* after *right_pattern* is "definitely assigned when true", then the state of *v* after *disjunctive_pattern* is "definitely assigned when true". + - Otherwise, the state of *v* after *disjunctive_pattern* is not definitely assigned. + +For a *conjunctive_pattern* of the form `left_pattern and right_pattern`: + +- The definite assignment state of *v* after *conjunctive_pattern* is determined by: + - If the state of *v* after *left_pattern* is definitely assigned, and the state of *v* after *right_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *conjunctive_pattern* is definitely assigned. + - Otherwise, if the state of *v* after *right_pattern* is definitely assigned, and the state of *v* after *left_pattern* is "definitely assigned when false", then the state of *v* after *conjunctive_pattern* is definitely assigned. + - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when true", and the state of *v* after *right_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *conjunctive_pattern* is "definitely assigned when true". + - Otherwise, if the state of *v* after *left_pattern* is "definitely assigned when false", and the state of *v* after *right_pattern* is "definitely assigned when false", then the state of *v* after *conjunctive_pattern* is "definitely assigned when false". + - Otherwise, the state of *v* after *conjunctive_pattern* is not definitely assigned. + +For a *negated_pattern* of the form `not pattern_operand`: + +- The definite assignment state of *v* after *negated_pattern* is determined by: + - If the state of *v* after *pattern_operand* is "definitely assigned when true", then the state of *v* after *negated_pattern* is "definitely assigned when false". + - If the state of *v* after *pattern_operand* is "definitely assigned when false", then the state of *v* after *negated_pattern* is "definitely assigned when true". + - If the state of *v* after *pattern_operand* is definitely assigned, then the state of *v* after *negated_pattern* is definitely assigned. + +Note that these rules cover the existing top-level `is not` patterns. However, in other cases instead of a hard error, the variables will be left unassigned. + +### Remarks + +Definite assignment specification in any other case is unchanged. For instance, for a *conditional_expression*: + +- If *expr_cond* is a constant expression with value `true` then the state of *v* after *expr* is the same as the state of *v* after *expr_true*; permitting use after: + + ```cs + if (true ? x is int i : y is int i) + ``` + +This document does not propose any changes to definite assignment rules for a *conditional_expression* to propagage conditional states when *cond_expr* is not a constant as it is covered by [improved definite assignment](https://github.com/dotnet/csharplang/blob/main/proposals/improved-definite-assignment.md#specification) proposal. From efa5e0c383b58a9a44c59540acb3da70613e1ae3 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Sun, 28 Mar 2021 05:51:45 +0430 Subject: [PATCH 02/18] Update pattern-variables.md --- proposals/pattern-variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index a9fff3ba92..9ca8f5f8a6 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -136,4 +136,4 @@ Definite assignment specification in any other case is unchanged. For instance, if (true ? x is int i : y is int i) ``` -This document does not propose any changes to definite assignment rules for a *conditional_expression* to propagage conditional states when *cond_expr* is not a constant as it is covered by [improved definite assignment](https://github.com/dotnet/csharplang/blob/main/proposals/improved-definite-assignment.md#specification) proposal. +This document does not propose any changes to definite assignment rules for a *conditional_expression* to propagate conditional states when *cond_expr* is not a constant as it is covered by [improved definite assignment](https://github.com/dotnet/csharplang/blob/main/proposals/improved-definite-assignment.md#specification) proposal. From d9b438d595c28f9e4b58cf179773d174c946cc22 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Sun, 28 Mar 2021 06:25:25 +0430 Subject: [PATCH 03/18] Update pattern-variables.md --- proposals/pattern-variables.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 9ca8f5f8a6..1cff680d38 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -104,7 +104,7 @@ For a *disjunctive_pattern* of the form `left_pattern or right_pattern`: - The definite assignment state of *v* after *disjunctive_pattern* is determined by: - If the state of *v* after *left_pattern* is definitely assigned, and the state of *v* after *right_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *disjunctive_pattern* is definitely assigned. - Otherwise, if the state of *v* after *right_pattern* is definitely assigned, and the state of *v* after *left_pattern* is "definitely assigned when true", then the state of *v* after *disjunctive_pattern* is definitely assigned. - - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when false", and the state of *v* after *right_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *disjunctive_pattern* is "definitely assigned when false". + - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when false", and the state of *v* after *left_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *disjunctive_pattern* is "definitely assigned when false". - Otherwise, if the state of *v* after *left_pattern* is "definitely assigned when true", and the state of *v* after *right_pattern* is "definitely assigned when true", then the state of *v* after *disjunctive_pattern* is "definitely assigned when true". - Otherwise, the state of *v* after *disjunctive_pattern* is not definitely assigned. @@ -113,7 +113,7 @@ For a *conjunctive_pattern* of the form `left_pattern and right_pattern`: - The definite assignment state of *v* after *conjunctive_pattern* is determined by: - If the state of *v* after *left_pattern* is definitely assigned, and the state of *v* after *right_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *conjunctive_pattern* is definitely assigned. - Otherwise, if the state of *v* after *right_pattern* is definitely assigned, and the state of *v* after *left_pattern* is "definitely assigned when false", then the state of *v* after *conjunctive_pattern* is definitely assigned. - - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when true", and the state of *v* after *right_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *conjunctive_pattern* is "definitely assigned when true". + - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when true", and the state of *v* after *left_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *conjunctive_pattern* is "definitely assigned when true". - Otherwise, if the state of *v* after *left_pattern* is "definitely assigned when false", and the state of *v* after *right_pattern* is "definitely assigned when false", then the state of *v* after *conjunctive_pattern* is "definitely assigned when false". - Otherwise, the state of *v* after *conjunctive_pattern* is not definitely assigned. From 044b4a85d75934f319510da5373c7e23b9972ca8 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Tue, 30 Mar 2021 12:14:13 +0430 Subject: [PATCH 04/18] Update pattern-variables.md --- proposals/pattern-variables.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 1cff680d38..3ea59c2250 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -97,23 +97,25 @@ The following rules applies to any *primary_pattern* that declares a variable: #### Definite assignment rules for pattern variables in pattern combinators -Note: This section is derived by definite assignment rules for expressions with an exception: for patterns we strictly require both operands to assign the same set of variables. For example, even though `e is var x or 1` is logically valid, the variable `x` is not definitely assigned. That said, some of cases might still result in impossible or irrefutable patterns by other measures. +Note: This section is derived by definite assignment rules for expressions. + +> *Open question*: We should confirm if we want to allow patterns like `var x and 1`. For a *disjunctive_pattern* of the form `left_pattern or right_pattern`: - The definite assignment state of *v* after *disjunctive_pattern* is determined by: - - If the state of *v* after *left_pattern* is definitely assigned, and the state of *v* after *right_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *disjunctive_pattern* is definitely assigned. + - If the state of *v* after *left_pattern* is definitely assigned, then the state of *v* after *disjunctive_pattern* is definitely assigned. - Otherwise, if the state of *v* after *right_pattern* is definitely assigned, and the state of *v* after *left_pattern* is "definitely assigned when true", then the state of *v* after *disjunctive_pattern* is definitely assigned. - - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when false", and the state of *v* after *left_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *disjunctive_pattern* is "definitely assigned when false". + - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when false", then the state of *v* after *disjunctive_pattern* is "definitely assigned when false". - Otherwise, if the state of *v* after *left_pattern* is "definitely assigned when true", and the state of *v* after *right_pattern* is "definitely assigned when true", then the state of *v* after *disjunctive_pattern* is "definitely assigned when true". - Otherwise, the state of *v* after *disjunctive_pattern* is not definitely assigned. For a *conjunctive_pattern* of the form `left_pattern and right_pattern`: - The definite assignment state of *v* after *conjunctive_pattern* is determined by: - - If the state of *v* after *left_pattern* is definitely assigned, and the state of *v* after *right_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *conjunctive_pattern* is definitely assigned. + - If the state of *v* after *left_pattern* is definitely assigned, then the state of *v* after *conjunctive_pattern* is definitely assigned. - Otherwise, if the state of *v* after *right_pattern* is definitely assigned, and the state of *v* after *left_pattern* is "definitely assigned when false", then the state of *v* after *conjunctive_pattern* is definitely assigned. - - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when true", and the state of *v* after *left_pattern* is conditionally or unconditionally definitely assigned, then the state of *v* after *conjunctive_pattern* is "definitely assigned when true". + - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when true", then the state of *v* after *conjunctive_pattern* is "definitely assigned when true". - Otherwise, if the state of *v* after *left_pattern* is "definitely assigned when false", and the state of *v* after *right_pattern* is "definitely assigned when false", then the state of *v* after *conjunctive_pattern* is "definitely assigned when false". - Otherwise, the state of *v* after *conjunctive_pattern* is not definitely assigned. From ea92dc85e146393a611436067eb2c2d7597d3143 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Tue, 30 Mar 2021 12:23:29 +0430 Subject: [PATCH 05/18] Update pattern-variables.md --- proposals/pattern-variables.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 3ea59c2250..ec8c1ee5c2 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -73,7 +73,22 @@ Note: This section is unchanged and included for the sake of completeness. For an *is_pattern_expression* of the form `e is pattern`: -- The state of *v* after *is_pattern_expression* is the same as the state of *v* after *pattern*. +- The state of *v* after *is_pattern_expression* is definitely assigned, if the *pattern* is irrefutable. permitting use after: + + ```cs + _ = x is var x; + _ = 1 is int x; + _ = (1, 2) is var (x, y) and var z; + ``` + +- Otherwise, the state of *v* after *is_pattern_expression* is the same as the state of *v* after *pattern*. + +For a *var_pattern* of the form `var variable_designation`: + +- The definite assignment state of *v* after *var_pattern* is determined by: + + - The state of *v* after *var-pattern* is definitely assigned if *variable_designation* is a *single_variable_designation*. + - Otherwise, the state of *v* after *var-pattern* is "definitely assigned when true". #### General rules for pattern variables in simple patterns @@ -81,15 +96,7 @@ Note: This section is unchanged and included for the sake of completeness. The following rules applies to any *primary_pattern* that declares a variable: -- The state of *v* is definitely assigned after *primary_pattern* if the pattern is irrefutable; permitting use after: - - ```cs - _ = x is var x; - _ = 1 is int x; - _ = (1, 2) is var (x, y); - ``` - -- Otherwise, the state of *v* is "definitely assigned when true" after *primary_pattern*; permitting use after: +- The state of *v* is "definitely assigned when true" after *primary_pattern*; permitting use after: ```cs if (o is int x) From 49036d645c58a9e83fb6ebbea6fe19cd81715fa8 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Tue, 30 Mar 2021 12:23:54 +0430 Subject: [PATCH 06/18] Update pattern-variables.md --- proposals/pattern-variables.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index ec8c1ee5c2..ed2610a1d2 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -87,8 +87,8 @@ For a *var_pattern* of the form `var variable_designation`: - The definite assignment state of *v* after *var_pattern* is determined by: - - The state of *v* after *var-pattern* is definitely assigned if *variable_designation* is a *single_variable_designation*. - - Otherwise, the state of *v* after *var-pattern* is "definitely assigned when true". + - The state of *v* after *var_pattern* is definitely assigned if *variable_designation* is a *single_variable_designation*. + - Otherwise, the state of *v* after *var_pattern* is "definitely assigned when true". #### General rules for pattern variables in simple patterns From 1ba7b0ca3ea14a32ee22270678a3765a99f639fc Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Tue, 30 Mar 2021 12:25:43 +0430 Subject: [PATCH 07/18] Update pattern-variables.md --- proposals/pattern-variables.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index ed2610a1d2..7d8cf09a52 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -73,15 +73,17 @@ Note: This section is unchanged and included for the sake of completeness. For an *is_pattern_expression* of the form `e is pattern`: -- The state of *v* after *is_pattern_expression* is definitely assigned, if the *pattern* is irrefutable. permitting use after: +- The definite assignment state of *v* after *is_pattern_expression* is determined by: + + - The state of *v* after *is_pattern_expression* is definitely assigned, if the *pattern* is irrefutable. permitting use after: - ```cs - _ = x is var x; - _ = 1 is int x; - _ = (1, 2) is var (x, y) and var z; - ``` + ```cs + _ = x is var x; + _ = 1 is int x; + _ = (1, 2) is var (x, y) and var z; + ``` -- Otherwise, the state of *v* after *is_pattern_expression* is the same as the state of *v* after *pattern*. + - Otherwise, the state of *v* after *is_pattern_expression* is the same as the state of *v* after *pattern*. For a *var_pattern* of the form `var variable_designation`: From ba997801ecf3133831cad38f5bef01e1f4c1e30a Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Tue, 30 Mar 2021 12:26:55 +0430 Subject: [PATCH 08/18] Update pattern-variables.md --- proposals/pattern-variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 7d8cf09a52..62cbe20050 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -108,7 +108,7 @@ The following rules applies to any *primary_pattern* that declares a variable: Note: This section is derived by definite assignment rules for expressions. -> *Open question*: We should confirm if we want to allow patterns like `var x and 1`. +> **Open question**: We should confirm if we want to allow patterns like `var x and 1`. For a *disjunctive_pattern* of the form `left_pattern or right_pattern`: From 30d72fb961abe49eba397bfc722df9063d300b34 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Tue, 30 Mar 2021 12:59:21 +0430 Subject: [PATCH 09/18] Update pattern-variables.md --- proposals/pattern-variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 62cbe20050..4170ee42a1 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -108,7 +108,7 @@ The following rules applies to any *primary_pattern* that declares a variable: Note: This section is derived by definite assignment rules for expressions. -> **Open question**: We should confirm if we want to allow patterns like `var x and 1`. +> **Open question**: We should confirm if we want to allow patterns like `var x or 1`. For a *disjunctive_pattern* of the form `left_pattern or right_pattern`: From 8c1e03a2b519db553225eba1a4868c748f8e16e2 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Thu, 8 Apr 2021 03:15:03 +0430 Subject: [PATCH 10/18] Update pattern-variables.md --- proposals/pattern-variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 4170ee42a1..7dfc6baa83 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -1,4 +1,4 @@ -# Variable declarations under pattern combinators +# Variable declarations under disjunctive patterns ## Summary From 6f7afa14f07e4873fd065eaf8e1a67447e8475e3 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Tue, 13 Apr 2021 17:10:25 +0430 Subject: [PATCH 11/18] Update pattern-variables.md --- proposals/pattern-variables.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 7dfc6baa83..631c697100 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -12,15 +12,20 @@ Introducing a new concept similar to *name hiding*, but these names can possibly reference either of variables based on the result of the pattern-matching at runtime. +There's two degrees of enforcement as to whether variables may be redeclared: + +- Pattern variables within pattern boundaries *must* be redeclared because assignment of such variables depend on the order of evaluation which is undefined for patterns. +- Pattern variables outside pattern boundaries *can* be redeclared, definitive assignment rules for the containing expression specify where such variables are usable. + For a *disjunctive_pattern* of the form `left_pattern or right_pattern`: -- Pattern variables declared in the *left_pattern* can be redeclared in the *right_pattern*; permitting: +- Pattern variables declared in the *left_pattern* **must be redeclared** in the *right_pattern*; permitting: ```cs e is (0, var x) or (var x, 0) ``` For a *logical_or_expression* of the form `left_expr || right_expr`: -- Pattern variables declared in *left_expr* can be redeclared in the *right_expr*; permitting: +- Pattern variables declared in *left_expr* **can be redeclared** in the *right_expr*; permitting: ```cs e is (0, var x) || e is (var x, 0) @@ -28,7 +33,7 @@ For a *logical_or_expression* of the form `left_expr || right_expr`: Note: This is the expression variation of the previous case. For a *negated_pattern* of the form `not pattern_operand`: -- Pattern variables declared in the *pattern_operand* can be redeclared anywhere in the containing expression; permitting: +- Pattern variables declared in the *pattern_operand* **must be redeclared** inside any other *negated_pattern* in the containing expression; permitting: ```cs e is not (0, var x) and not (var x, 0) @@ -37,7 +42,7 @@ For a *negated_pattern* of the form `not pattern_operand`: Note: This is the DeMorgan's transformation of the previous case. For a *logical_not_expression* of the form `!expr_operand`: -- Pattern variables declared in the *expr_operand* can be redeclared anywhere in the containing expression; permitting: +- Pattern variables declared in the *expr_operand* **can be redeclared** anywhere in the containing expression; permitting: ```cs !(e is (0, var x)) && !(e is (var x, 0)) @@ -45,7 +50,7 @@ For a *logical_not_expression* of the form `!expr_operand`: Note: This is the expression variation of the previous case. For a *switch_case_label* of the form `case case_pattern when when_expr`: -- Pattern variables declared in the *case_pattern* or *when_expr* can be redeclared in other case labels; permitting: +- Pattern variables declared in the *case_pattern* or *when_expr* **can be redeclared** in other case labels; permitting: ```cs case (var x, 0) when e is int i: @@ -55,7 +60,7 @@ For a *switch_case_label* of the form `case case_pattern when when_expr`: Note: This rule is currently defined for each individual *switch_section* only. For a *conditional_expression* of the form `expr_cond ? expr_true : expr_false`: -- Pattern variables declared in *expr_true* can be redeclared in *expr_false*; permitting: +- Pattern variables declared in *expr_true* **can be redeclared** in *expr_false*; permitting: ```cs b ? x is int i : y is int i From 0b2945af792a041b4271c0a17f7181dd47e408d9 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Tue, 13 Apr 2021 21:14:24 +0430 Subject: [PATCH 12/18] Update pattern-variables.md --- proposals/pattern-variables.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 631c697100..43d95d554d 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -33,13 +33,18 @@ For a *logical_or_expression* of the form `left_expr || right_expr`: Note: This is the expression variation of the previous case. For a *negated_pattern* of the form `not pattern_operand`: -- Pattern variables declared in the *pattern_operand* **must be redeclared** inside any other *negated_pattern* in the containing expression; permitting: +- Pattern variables declared in the *pattern_operand* **must be redeclared** inside any other *negated_pattern* in the containing pattern; permitting: - ```cs - e is not (0, var x) and not (var x, 0) - e is not (0, var x) && e is not (var x, 0) - ``` + ```cs + e is not (0, var x) and not (var x, 0) + ``` Note: This is the DeMorgan's transformation of the previous case. +- Pattern variables declared in the *pattern_operand* **can be redeclared** anywhere in the containing expression; permitting: + + ```cs + e is not (0, var x) && e is not (var x, 0) + ``` + Note: This is the expression variation of the previous case. For a *logical_not_expression* of the form `!expr_operand`: - Pattern variables declared in the *expr_operand* **can be redeclared** anywhere in the containing expression; permitting: From 65f77373e63bf7c77e4c7e2c71a143fd5c65d82f Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Tue, 13 Apr 2021 21:16:09 +0430 Subject: [PATCH 13/18] Update pattern-variables.md --- proposals/pattern-variables.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 43d95d554d..b90bc83e59 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -118,8 +118,6 @@ The following rules applies to any *primary_pattern* that declares a variable: Note: This section is derived by definite assignment rules for expressions. -> **Open question**: We should confirm if we want to allow patterns like `var x or 1`. - For a *disjunctive_pattern* of the form `left_pattern or right_pattern`: - The definite assignment state of *v* after *disjunctive_pattern* is determined by: From 1fe9041ace6d2fac10299ca1ccdff68bc0dbb0db Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Fri, 1 Oct 2021 14:08:34 +0330 Subject: [PATCH 14/18] Update pattern-variables.md --- proposals/pattern-variables.md | 204 ++++++++++----------------------- 1 file changed, 58 insertions(+), 146 deletions(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index b90bc83e59..63428c9f4e 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -2,157 +2,69 @@ ## Summary -## Motivation - -## Specification - -### Scope - -#### Pattern variable redeclaration - -Introducing a new concept similar to *name hiding*, but these names can possibly reference either of variables based on the result of the pattern-matching at runtime. - -There's two degrees of enforcement as to whether variables may be redeclared: - -- Pattern variables within pattern boundaries *must* be redeclared because assignment of such variables depend on the order of evaluation which is undefined for patterns. -- Pattern variables outside pattern boundaries *can* be redeclared, definitive assignment rules for the containing expression specify where such variables are usable. - -For a *disjunctive_pattern* of the form `left_pattern or right_pattern`: -- Pattern variables declared in the *left_pattern* **must be redeclared** in the *right_pattern*; permitting: - - ```cs - e is (0, var x) or (var x, 0) - ``` - -For a *logical_or_expression* of the form `left_expr || right_expr`: -- Pattern variables declared in *left_expr* **can be redeclared** in the *right_expr*; permitting: - - ```cs - e is (0, var x) || e is (var x, 0) - ``` - Note: This is the expression variation of the previous case. - -For a *negated_pattern* of the form `not pattern_operand`: -- Pattern variables declared in the *pattern_operand* **must be redeclared** inside any other *negated_pattern* in the containing pattern; permitting: - - ```cs - e is not (0, var x) and not (var x, 0) - ``` - Note: This is the DeMorgan's transformation of the previous case. -- Pattern variables declared in the *pattern_operand* **can be redeclared** anywhere in the containing expression; permitting: +Allow variable declarations under `or` patterns and across `case` labels in a `switch` section. - ```cs - e is not (0, var x) && e is not (var x, 0) - ``` - Note: This is the expression variation of the previous case. - -For a *logical_not_expression* of the form `!expr_operand`: -- Pattern variables declared in the *expr_operand* **can be redeclared** anywhere in the containing expression; permitting: - - ```cs - !(e is (0, var x)) && !(e is (var x, 0)) - ``` - Note: This is the expression variation of the previous case. - -For a *switch_case_label* of the form `case case_pattern when when_expr`: -- Pattern variables declared in the *case_pattern* or *when_expr* **can be redeclared** in other case labels; permitting: - - ```cs - case (var x, 0) when e is int i: - case (0, var x) when a is int i: - ``` - - Note: This rule is currently defined for each individual *switch_section* only. - -For a *conditional_expression* of the form `expr_cond ? expr_true : expr_false`: -- Pattern variables declared in *expr_true* **can be redeclared** in *expr_false*; permitting: +## Motivation - ```cs - b ? x is int i : y is int i - ``` +This feature would reduce code duplication where we could use the same piece of code if either of patterns is satisfied. For instance: +```cs +if (e is (int x, 0) or (0, int x)) + Use(x); + +switch (e) +{ + case (int x, 0): + case (0, int x): + Use(x); + break; +} +``` +Instead of: + +```cs +if (e is (int x, 0)) + Use(x); +else + Use(x) + +switch (e) +{ + case (int x, 0): + Use(x); + break; + case (0, int x): + Use(x); + break; +} +``` + +## Detailed design + +Variables *must* be redeclared under all disjuncitve patterns because assignment of such variables depend on the order of evaluation which is undefined in the context of pattern-matching. + +- In a *disjunctive_pattern*, pattern variables declared under the left-hand-side must be redeclared under the right-hand-side. +- In a *switch_section*, pattern variables declared under each case label must be redeclared under every other case label. + +In any other case, variable declaration follows the usual scoping rules and is disallowed. + +These names can reference either of variables based on the result of the pattern-matching at runtime. Under the hood, it's the same local being assigned in each pattern. Redeclaring pattern variables is only permitted for variables of the same type. -> **Open question**: How identical these types should be? - -Redeclaring pattern variables in any other case follows the usual scoping rules and is disallowed. - -### Definite assignment - -Note: This section is unchanged and included for the sake of completeness. - -For an *is_pattern_expression* of the form `e is pattern`: - -- The definite assignment state of *v* after *is_pattern_expression* is determined by: - - - The state of *v* after *is_pattern_expression* is definitely assigned, if the *pattern* is irrefutable. permitting use after: - - ```cs - _ = x is var x; - _ = 1 is int x; - _ = (1, 2) is var (x, y) and var z; - ``` - - - Otherwise, the state of *v* after *is_pattern_expression* is the same as the state of *v* after *pattern*. - -For a *var_pattern* of the form `var variable_designation`: - -- The definite assignment state of *v* after *var_pattern* is determined by: - - - The state of *v* after *var_pattern* is definitely assigned if *variable_designation* is a *single_variable_designation*. - - Otherwise, the state of *v* after *var_pattern* is "definitely assigned when true". - -#### General rules for pattern variables in simple patterns - -Note: This section is unchanged and included for the sake of completeness. - -The following rules applies to any *primary_pattern* that declares a variable: - -- The state of *v* is "definitely assigned when true" after *primary_pattern*; permitting use after: - - ```cs - if (o is int x) - ``` - -#### Definite assignment rules for pattern variables in pattern combinators - -Note: This section is derived by definite assignment rules for expressions. - -For a *disjunctive_pattern* of the form `left_pattern or right_pattern`: - -- The definite assignment state of *v* after *disjunctive_pattern* is determined by: - - If the state of *v* after *left_pattern* is definitely assigned, then the state of *v* after *disjunctive_pattern* is definitely assigned. - - Otherwise, if the state of *v* after *right_pattern* is definitely assigned, and the state of *v* after *left_pattern* is "definitely assigned when true", then the state of *v* after *disjunctive_pattern* is definitely assigned. - - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when false", then the state of *v* after *disjunctive_pattern* is "definitely assigned when false". - - Otherwise, if the state of *v* after *left_pattern* is "definitely assigned when true", and the state of *v* after *right_pattern* is "definitely assigned when true", then the state of *v* after *disjunctive_pattern* is "definitely assigned when true". - - Otherwise, the state of *v* after *disjunctive_pattern* is not definitely assigned. - -For a *conjunctive_pattern* of the form `left_pattern and right_pattern`: - -- The definite assignment state of *v* after *conjunctive_pattern* is determined by: - - If the state of *v* after *left_pattern* is definitely assigned, then the state of *v* after *conjunctive_pattern* is definitely assigned. - - Otherwise, if the state of *v* after *right_pattern* is definitely assigned, and the state of *v* after *left_pattern* is "definitely assigned when false", then the state of *v* after *conjunctive_pattern* is definitely assigned. - - Otherwise, if the state of *v* after *right_pattern* is definitely assigned or "definitely assigned when true", then the state of *v* after *conjunctive_pattern* is "definitely assigned when true". - - Otherwise, if the state of *v* after *left_pattern* is "definitely assigned when false", and the state of *v* after *right_pattern* is "definitely assigned when false", then the state of *v* after *conjunctive_pattern* is "definitely assigned when false". - - Otherwise, the state of *v* after *conjunctive_pattern* is not definitely assigned. - -For a *negated_pattern* of the form `not pattern_operand`: - -- The definite assignment state of *v* after *negated_pattern* is determined by: - - If the state of *v* after *pattern_operand* is "definitely assigned when true", then the state of *v* after *negated_pattern* is "definitely assigned when false". - - If the state of *v* after *pattern_operand* is "definitely assigned when false", then the state of *v* after *negated_pattern* is "definitely assigned when true". - - If the state of *v* after *pattern_operand* is definitely assigned, then the state of *v* after *negated_pattern* is definitely assigned. - -Note that these rules cover the existing top-level `is not` patterns. However, in other cases instead of a hard error, the variables will be left unassigned. - -### Remarks - -Definite assignment specification in any other case is unchanged. For instance, for a *conditional_expression*: - -- If *expr_cond* is a constant expression with value `true` then the state of *v* after *expr* is the same as the state of *v* after *expr_true*; permitting use after: +## Unresolved questions +- How identical these types should be? +- Could we support variable declarations under `not` patterns? +- Could we relax the scoping rules beyond pattern boundaries? + ```cs + if (e is (int x, 0) || a is (0, int x)) + ``` +- Could we relax the redeclaration requirement in a switch section? ```cs - if (true ? x is int i : y is int i) + case (int x, 0) a when Use(x, a): // ok + case (0, int x) b when Use(x, b): // ok + Use(x); // ok + Use(a); // error; not definitely assigned + Use(b); // error; not definitely assigned + break; ``` - -This document does not propose any changes to definite assignment rules for a *conditional_expression* to propagate conditional states when *cond_expr* is not a constant as it is covered by [improved definite assignment](https://github.com/dotnet/csharplang/blob/main/proposals/improved-definite-assignment.md#specification) proposal. From c7e019cd291ebf1fb7ef925277bf060769a7b6c1 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Fri, 1 Oct 2021 21:52:57 +0330 Subject: [PATCH 15/18] Update pattern-variables.md --- proposals/pattern-variables.md | 37 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 63428c9f4e..d9450bfb2e 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -15,17 +15,17 @@ switch (e) { case (int x, 0): case (0, int x): - Use(x); - break; + Use(x); + break; } ``` Instead of: ```cs -if (e is (int x, 0)) - Use(x); -else - Use(x) +if (e is (int x1, 0)) + Use(x1); +else (e is (0, int x2)) + Use(x2) switch (e) { @@ -55,16 +55,19 @@ Redeclaring pattern variables is only permitted for variables of the same type. - How identical these types should be? - Could we support variable declarations under `not` patterns? + ```cs + if (e is not (int x, 0) and not (0, int x)) + ``` - Could we relax the scoping rules beyond pattern boundaries? - ```cs - if (e is (int x, 0) || a is (0, int x)) - ``` + ```cs + if (e is (int x, 0) || a is (0, int x)) + ``` - Could we relax the redeclaration requirement in a switch section? - ```cs - case (int x, 0) a when Use(x, a): // ok - case (0, int x) b when Use(x, b): // ok - Use(x); // ok - Use(a); // error; not definitely assigned - Use(b); // error; not definitely assigned - break; - ``` + ```cs + case (int x, 0) a when Use(x, a): // ok + case (0, int x) b when Use(x, b): // ok + Use(x); // ok + Use(a); // error; not definitely assigned + Use(b); // error; not definitely assigned + break; + ``` From 9ac4b5c0da7d44aa9d721f7961916a79b52ec5d5 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Fri, 1 Oct 2021 21:53:32 +0330 Subject: [PATCH 16/18] Update pattern-variables.md --- proposals/pattern-variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index d9450bfb2e..4cdfe4bc8e 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -24,7 +24,7 @@ Instead of: ```cs if (e is (int x1, 0)) Use(x1); -else (e is (0, int x2)) +else if (e is (0, int x2)) Use(x2) switch (e) From 43e54a14341316091449e7418b3ee6cfc150cd15 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Fri, 1 Oct 2021 21:53:59 +0330 Subject: [PATCH 17/18] Update pattern-variables.md --- proposals/pattern-variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 4cdfe4bc8e..811ea59c35 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -25,7 +25,7 @@ Instead of: if (e is (int x1, 0)) Use(x1); else if (e is (0, int x2)) - Use(x2) + Use(x2); switch (e) { From ec778534e60bf19afc44bc1acaf8597e6e576245 Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Sat, 2 Oct 2021 09:51:31 +0330 Subject: [PATCH 18/18] Update pattern-variables.md --- proposals/pattern-variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/pattern-variables.md b/proposals/pattern-variables.md index 811ea59c35..2a78cb9c0a 100644 --- a/proposals/pattern-variables.md +++ b/proposals/pattern-variables.md @@ -42,7 +42,7 @@ switch (e) Variables *must* be redeclared under all disjuncitve patterns because assignment of such variables depend on the order of evaluation which is undefined in the context of pattern-matching. -- In a *disjunctive_pattern*, pattern variables declared under the left-hand-side must be redeclared under the right-hand-side. +- In a *disjunctive_pattern*, pattern variables declared on one side must be redeclared on the other side. - In a *switch_section*, pattern variables declared under each case label must be redeclared under every other case label. In any other case, variable declaration follows the usual scoping rules and is disallowed.