diff --git a/LanguageFeatures/Patterns/execution_pattern_assignment_A01_t01.dart b/LanguageFeatures/Patterns/execution_pattern_assignment_A01_t01.dart new file mode 100644 index 0000000000..1b4db35381 --- /dev/null +++ b/LanguageFeatures/Patterns/execution_pattern_assignment_A01_t01.dart @@ -0,0 +1,63 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// Pattern assignment +/// 1. Evaluate the right-hand side expression to a value v. +/// 2. Match v against the pattern on the left. When matching a variable pattern +/// against a value o, record that o will be the new value for the +/// corresponding variable, but do not store the variable. +/// 3. Once all destructuring and matching is done, store all of the assigned +/// variables with their corresponding values. +/// +/// @description Check that if matching fails then no values are assigned +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns,records + +import "../../Utils/expect.dart"; +import "patterns_lib.dart"; + +main() { + int v1 = 42; + Expect.throws(() { + (v1) = (3.14) as dynamic; + }); + Expect.equals(42, v1); + + int l1 = 42; + int l2 = 42; + Expect.throws(() { + dynamic pi = 3.14; + [l1, l2] = [0, pi]; + }); + Expect.equals(42, l1); + Expect.equals(42, l2); + + int m1 = 42; + int m2 = 42; + Expect.throws(() { + dynamic pi = 3.14; + {"k1": m1, "k2": m2} = {"k1": 0, "k2": pi}; + }); + Expect.equals(42, m1); + Expect.equals(42, m2); + + int r1 = 42; + int r2 = 42; + Expect.throws(() { + dynamic pi = 3.14; + (r1, r2) = (0, pi); + }); + Expect.equals(42, r1); + Expect.equals(42, r2); + + int o1 = 42; + int o2 = 42; + Expect.throws(() { + Circle(sizeAsInt: o1, areaAsDynamicDouble: o2) = Circle(1); + }); + Expect.equals(42, o1); + Expect.equals(42, o2); +} diff --git a/LanguageFeatures/Patterns/execution_switch_statement_A01_t01.dart b/LanguageFeatures/Patterns/execution_switch_statement_A01_t01.dart new file mode 100644 index 0000000000..9667588efa --- /dev/null +++ b/LanguageFeatures/Patterns/execution_switch_statement_A01_t01.dart @@ -0,0 +1,68 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// Switch statement +/// 1. Evaluate the switch value producing v. +/// 2. For each case: +/// i. Match the case's pattern against v. If the match fails then continue to +/// the next case (or default clause or exit the switch if there are no +/// other cases). +/// ii. If there is a guard clause, evaluate it. If it does not evaluate to a +/// Boolean, throw a runtime error. This can happen if the guard +/// expression's type is dynamic. If it evaluates to false, continue to the +/// next case (or default or exit). +/// iii. Find the nearest non-empty case body at or following this case. +/// You're allowed to have multiple empty cases where all preceding ones +/// share the same body with the last case. +/// iv. If the enclosing scope for the body is a shared case scope, then +/// initialize all shared variables the values of the corresponding +/// variables from the case scope. There will be no shared case scope and +/// nothing to copy if the body is only used by a single case. +/// v. Execute the body statement. +/// vi. If execution of the body statement continues with a label, and that +/// label is labeling a switch case of this switch, go to step 3 and +/// continue from that label. +/// vii. Otherwise the switch statement completes normally. An explicit break +/// is no longer required. +/// 3. If no case pattern matched and there is a default clause, execute the +/// statements after it. +/// 4. If the static type of v is an always-exhaustive type, no case matches, +/// and there is no default clause, then throw a runtime error. +/// +/// @description Check that if matching fails then execution continues to the +/// next case or default (if any) +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +main() { + String log = ""; + var v = [42]; + switch (v) { + case [String s]: + log += "case-1;"; + case [bool b]: + log += "case-2;"; + break; + case [1, 2]: + log += "case-3;"; + } + Expect.equals("", log); + + switch (v) { + case [_, 2]: + log += "case-1;"; + case [bool _]: + log += "case-2;"; + break; + case [1, _]: + log += "case-3;"; + default: + log += "default"; + } + Expect.equals("default", log); +} diff --git a/LanguageFeatures/Patterns/execution_switch_statement_A02_t01.dart b/LanguageFeatures/Patterns/execution_switch_statement_A02_t01.dart new file mode 100644 index 0000000000..769b6de39b --- /dev/null +++ b/LanguageFeatures/Patterns/execution_switch_statement_A02_t01.dart @@ -0,0 +1,63 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// Switch statement +/// 1. Evaluate the switch value producing v. +/// 2. For each case: +/// i. Match the case's pattern against v. If the match fails then continue to +/// the next case (or default clause or exit the switch if there are no +/// other cases). +/// ii. If there is a guard clause, evaluate it. If it does not evaluate to a +/// Boolean, throw a runtime error. This can happen if the guard +/// expression's type is dynamic. If it evaluates to false, continue to the +/// next case (or default or exit). +/// iii. Find the nearest non-empty case body at or following this case. +/// You're allowed to have multiple empty cases where all preceding ones +/// share the same body with the last case. +/// iv. If the enclosing scope for the body is a shared case scope, then +/// initialize all shared variables the values of the corresponding +/// variables from the case scope. There will be no shared case scope and +/// nothing to copy if the body is only used by a single case. +/// v. Execute the body statement. +/// vi. If execution of the body statement continues with a label, and that +/// label is labeling a switch case of this switch, go to step 3 and +/// continue from that label. +/// vii. Otherwise the switch statement completes normally. An explicit break +/// is no longer required. +/// 3. If no case pattern matched and there is a default clause, execute the +/// statements after it. +/// 4. If the static type of v is an always-exhaustive type, no case matches, +/// and there is no default clause, then throw a runtime error. +/// +/// @description Check that if there is a guard clause, it is evaluated if match +/// success +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +String log = ""; + +bool guard(int v) { + log += "guard($v);"; + return v == 42; +} + +main() { + var v = [0, 42]; + switch (v) { + case [int v1, var v2] when guard(v1): + log += "case-1;"; + case [1, final v3] when guard(v3): + log += "case-2"; + break; + case [0, var v4] when guard(v4): + log += "case-3;"; + default: + log += "default"; + } + Expect.equals("guard(0);guard(42);case-3;", log); +} diff --git a/LanguageFeatures/Patterns/execution_switch_statement_A02_t02.dart b/LanguageFeatures/Patterns/execution_switch_statement_A02_t02.dart new file mode 100644 index 0000000000..f73a83c150 --- /dev/null +++ b/LanguageFeatures/Patterns/execution_switch_statement_A02_t02.dart @@ -0,0 +1,57 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// Switch statement +/// 1. Evaluate the switch value producing v. +/// 2. For each case: +/// i. Match the case's pattern against v. If the match fails then continue to +/// the next case (or default clause or exit the switch if there are no +/// other cases). +/// ii. If there is a guard clause, evaluate it. If it does not evaluate to a +/// Boolean, throw a runtime error. This can happen if the guard +/// expression's type is dynamic. If it evaluates to false, continue to the +/// next case (or default or exit). +/// iii. Find the nearest non-empty case body at or following this case. +/// You're allowed to have multiple empty cases where all preceding ones +/// share the same body with the last case. +/// iv. If the enclosing scope for the body is a shared case scope, then +/// initialize all shared variables the values of the corresponding +/// variables from the case scope. There will be no shared case scope and +/// nothing to copy if the body is only used by a single case. +/// v. Execute the body statement. +/// vi. If execution of the body statement continues with a label, and that +/// label is labeling a switch case of this switch, go to step 3 and +/// continue from that label. +/// vii. Otherwise the switch statement completes normally. An explicit break +/// is no longer required. +/// 3. If no case pattern matched and there is a default clause, execute the +/// statements after it. +/// 4. If the static type of v is an always-exhaustive type, no case matches, +/// and there is no default clause, then throw a runtime error. +/// +/// @description Check that it is a runtime error if guard value is not +/// assignable to bool +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +dynamic guard(v) => v; + +String test(v) { + switch(42) { + case 42 when guard(v): + return "Case body"; + default: + return "default"; + } +} + +main() { + Expect.equals("Case body", test(true)); + Expect.equals("default", test(false)); + Expect.throws(() {test(42);}); +} diff --git a/LanguageFeatures/Patterns/execution_switch_statement_A03_t01.dart b/LanguageFeatures/Patterns/execution_switch_statement_A03_t01.dart new file mode 100644 index 0000000000..e409fa8485 --- /dev/null +++ b/LanguageFeatures/Patterns/execution_switch_statement_A03_t01.dart @@ -0,0 +1,81 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// Switch statement +/// 1. Evaluate the switch value producing v. +/// 2. For each case: +/// i. Match the case's pattern against v. If the match fails then continue to +/// the next case (or default clause or exit the switch if there are no +/// other cases). +/// ii. If there is a guard clause, evaluate it. If it does not evaluate to a +/// Boolean, throw a runtime error. This can happen if the guard +/// expression's type is dynamic. If it evaluates to false, continue to the +/// next case (or default or exit). +/// iii. Find the nearest non-empty case body at or following this case. +/// You're allowed to have multiple empty cases where all preceding ones +/// share the same body with the last case. +/// iv. If the enclosing scope for the body is a shared case scope, then +/// initialize all shared variables the values of the corresponding +/// variables from the case scope. There will be no shared case scope and +/// nothing to copy if the body is only used by a single case. +/// v. Execute the body statement. +/// vi. If execution of the body statement continues with a label, and that +/// label is labeling a switch case of this switch, go to step 3 and +/// continue from that label. +/// vii. Otherwise the switch statement completes normally. An explicit break +/// is no longer required. +/// 3. If no case pattern matched and there is a default clause, execute the +/// statements after it. +/// 4. If the static type of v is an always-exhaustive type, no case matches, +/// and there is no default clause, then throw a runtime error. +/// +/// @description Check that body of the matched case is executed +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +String log = ""; + +bool guard(int n, bool b) { + log += "guard$n($b);"; + return b; +} + +void test(List list) { + log = ""; + switch (list) { + case [1, var v1] when guard(1, v1 == 1): + case [2, final v2] when guard(2, v2 == 2): + case [3, var v3] when guard(3, v3 == 3): + log += "shared body;"; + case [4, var v4] when guard(4, v4 == 4): + log += "not shared body;"; + default: + log += "default;"; + } +} + +main() { + test([1, 1]); + Expect.equals("guard1(true);shared body;", log); + test([2, 2]); + Expect.equals("guard2(true);shared body;", log); + test([3, 3]); + Expect.equals("guard3(true);shared body;", log); + test([4, 4]); + Expect.equals("guard4(true);not shared body;", log); + test([1, 42]); + Expect.equals("guard1(false);default;", log); + test([2, 42]); + Expect.equals("guard2(false);default;", log); + test([3, 42]); + Expect.equals("guard3(false);default;", log); + test([4, 42]); + Expect.equals("guard4(false);default;", log); + test([]); + Expect.equals("default;", log); +} diff --git a/LanguageFeatures/Patterns/execution_switch_statement_A03_t02.dart b/LanguageFeatures/Patterns/execution_switch_statement_A03_t02.dart new file mode 100644 index 0000000000..5faee508f8 --- /dev/null +++ b/LanguageFeatures/Patterns/execution_switch_statement_A03_t02.dart @@ -0,0 +1,73 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// Switch statement +/// 1. Evaluate the switch value producing v. +/// 2. For each case: +/// i. Match the case's pattern against v. If the match fails then continue to +/// the next case (or default clause or exit the switch if there are no +/// other cases). +/// ii. If there is a guard clause, evaluate it. If it does not evaluate to a +/// Boolean, throw a runtime error. This can happen if the guard +/// expression's type is dynamic. If it evaluates to false, continue to the +/// next case (or default or exit). +/// iii. Find the nearest non-empty case body at or following this case. +/// You're allowed to have multiple empty cases where all preceding ones +/// share the same body with the last case. +/// iv. If the enclosing scope for the body is a shared case scope, then +/// initialize all shared variables the values of the corresponding +/// variables from the case scope. There will be no shared case scope and +/// nothing to copy if the body is only used by a single case. +/// v. Execute the body statement. +/// vi. If execution of the body statement continues with a label, and that +/// label is labeling a switch case of this switch, go to step 3 and +/// continue from that label. +/// vii. Otherwise the switch statement completes normally. An explicit break +/// is no longer required. +/// 3. If no case pattern matched and there is a default clause, execute the +/// statements after it. +/// 4. If the static type of v is an always-exhaustive type, no case matches, +/// and there is no default clause, then throw a runtime error. +/// +/// @description Check that body of the matched case is executed. Test that if +/// matched value is modified in the case body this new value is not tested +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +String log = ""; + +bool guard(int n, bool b) { + log += "guard$n($b);"; + return b; +} + +void test(List list) { + log = ""; + switch (list) { + case [1, var v1] when guard(1, v1 == 1): + case [2, final v2] when guard(2, v2 == 2): + case [3, var v3] when guard(3, v3 == 3): + log += "shared body;"; + list = [4, 4]; + case [4, var v4] when guard(4, v4 == 4): + log += "not shared body;"; + default: + log += "default;"; + } +} + +main() { + test([1, 1]); + Expect.equals("guard1(true);shared body;", log); + test([2, 2]); + Expect.equals("guard2(true);shared body;", log); + test([3, 3]); + Expect.equals("guard3(true);shared body;", log); + test([4, 4]); + Expect.equals("guard4(true);not shared body;", log); +} diff --git a/LanguageFeatures/Patterns/execution_switch_statement_A04_t01.dart b/LanguageFeatures/Patterns/execution_switch_statement_A04_t01.dart new file mode 100644 index 0000000000..8e683273c8 --- /dev/null +++ b/LanguageFeatures/Patterns/execution_switch_statement_A04_t01.dart @@ -0,0 +1,100 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// Switch statement +/// 1. Evaluate the switch value producing v. +/// 2. For each case: +/// i. Match the case's pattern against v. If the match fails then continue to +/// the next case (or default clause or exit the switch if there are no +/// other cases). +/// ii. If there is a guard clause, evaluate it. If it does not evaluate to a +/// Boolean, throw a runtime error. This can happen if the guard +/// expression's type is dynamic. If it evaluates to false, continue to the +/// next case (or default or exit). +/// iii. Find the nearest non-empty case body at or following this case. +/// You're allowed to have multiple empty cases where all preceding ones +/// share the same body with the last case. +/// iv. If the enclosing scope for the body is a shared case scope, then +/// initialize all shared variables the values of the corresponding +/// variables from the case scope. There will be no shared case scope and +/// nothing to copy if the body is only used by a single case. +/// v. Execute the body statement. +/// vi. If execution of the body statement continues with a label, and that +/// label is labeling a switch case of this switch, go to step 3 and +/// continue from that label. +/// vii. Otherwise the switch statement completes normally. An explicit break +/// is no longer required. +/// 3. If no case pattern matched and there is a default clause, execute the +/// statements after it. +/// 4. If the static type of v is an always-exhaustive type, no case matches, +/// and there is no default clause, then throw a runtime error. +/// +/// @description Check that if execution of the body statement continues with a +/// label, and that label is labeling a switch case of this switch, execution +/// continues from that label. +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +String log = ""; + +int guard(int n, int v) { + log += "guard$n();"; + return v; +} + +void testJumpForward(List list) { + log = ""; + switch (list) { + case [1, var v1] when 1 == guard(1, v1): + log += "case-1;"; + continue case3; + case [1, final v2] when 2 == guard(2, v2): + log += "case-2;"; + case3: + case [1, var v3] when 3 == guard(3, v3): + log += "case-3;"; + default: + log += "default;"; + } +} + +void testJumpBack(List list) { + log = ""; + switch (list) { + case1: + case [1, var v1] when 1 == guard(1, v1): + log += "case-1;"; + case [1, final v2] when 2 == guard(2, v2): + log += "case-2;"; + case [1, var v3] when 3 == guard(3, v3): + log += "case-3;"; + continue case1; + default: + log += "default;"; + } +} + +main() { + testJumpForward([1, 1]); + Expect.equals("guard1();case-1;case-3;", log); + testJumpForward([1, 2]); + Expect.equals("guard1();guard2();case-2;", log); + testJumpForward([1, 3]); + Expect.equals("guard1();guard2();guard3();case-3;", log); + testJumpForward([]); + Expect.equals("default;", log); + + testJumpBack([1, 3]); + Expect.equals("guard1();guard2();guard3();case-3;case-1;", log); + testJumpBack([1, 2]); + Expect.equals("guard1();guard2();case-2;", log); + testJumpBack([1, 1]); + Expect.equals("guard1();case-1;", log); + testJumpBack([]); + Expect.equals("default;", log); +} diff --git a/LanguageFeatures/Patterns/patterns_lib.dart b/LanguageFeatures/Patterns/patterns_lib.dart index f9745b1f8e..53e2c74671 100644 --- a/LanguageFeatures/Patterns/patterns_lib.dart +++ b/LanguageFeatures/Patterns/patterns_lib.dart @@ -125,6 +125,7 @@ class Shape { num? get sizeAsNullable => sizeAsDouble; num? get areaAsNull => null; num? get sizeAsNull => null; + dynamic get areaAsDynamicDouble => areaAsDouble as dynamic; List get areaAsList => [areaAsDouble]; Map get areaAsMap => {"area": areaAsDouble}; ({num area}) get areaAsRecord => (area: areaAsDouble);