From e67f5294df55176213d0f1e8db0e8a27fd0155dc Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 13 Mar 2024 15:04:07 +0100 Subject: [PATCH 1/4] Regroup mir-opt tests of match building --- .../deref-patterns/string.foo.PreCodegen.after.mir | 0 .../{ => building/match}/deref-patterns/string.rs | 0 ...onential_or.match_tuple.SimplifyCfg-initial.after.mir | 0 tests/mir-opt/{ => building/match}/exponential_or.rs | 0 .../match_false_edges.full_tested_match.built.after.mir | 0 .../match_false_edges.full_tested_match2.built.after.mir | 0 .../{ => match}/match_false_edges.main.built.after.mir | 0 tests/mir-opt/building/{ => match}/match_false_edges.rs | 0 .../{ => match}/simple_match.match_bool.built.after.mir | 0 tests/mir-opt/building/{ => match}/simple_match.rs | 0 ...idates.disjoint_ranges.SimplifyCfg-initial.after.mir} | 4 ++-- .../{match_test.rs => building/match/sort_candidates.rs} | 9 +++++---- 12 files changed, 7 insertions(+), 6 deletions(-) rename tests/mir-opt/{ => building/match}/deref-patterns/string.foo.PreCodegen.after.mir (100%) rename tests/mir-opt/{ => building/match}/deref-patterns/string.rs (100%) rename tests/mir-opt/{ => building/match}/exponential_or.match_tuple.SimplifyCfg-initial.after.mir (100%) rename tests/mir-opt/{ => building/match}/exponential_or.rs (100%) rename tests/mir-opt/building/{ => match}/match_false_edges.full_tested_match.built.after.mir (100%) rename tests/mir-opt/building/{ => match}/match_false_edges.full_tested_match2.built.after.mir (100%) rename tests/mir-opt/building/{ => match}/match_false_edges.main.built.after.mir (100%) rename tests/mir-opt/building/{ => match}/match_false_edges.rs (100%) rename tests/mir-opt/building/{ => match}/simple_match.match_bool.built.after.mir (100%) rename tests/mir-opt/building/{ => match}/simple_match.rs (100%) rename tests/mir-opt/{match_test.main.SimplifyCfg-initial.after.mir => building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir} (95%) rename tests/mir-opt/{match_test.rs => building/match/sort_candidates.rs} (64%) diff --git a/tests/mir-opt/deref-patterns/string.foo.PreCodegen.after.mir b/tests/mir-opt/building/match/deref-patterns/string.foo.PreCodegen.after.mir similarity index 100% rename from tests/mir-opt/deref-patterns/string.foo.PreCodegen.after.mir rename to tests/mir-opt/building/match/deref-patterns/string.foo.PreCodegen.after.mir diff --git a/tests/mir-opt/deref-patterns/string.rs b/tests/mir-opt/building/match/deref-patterns/string.rs similarity index 100% rename from tests/mir-opt/deref-patterns/string.rs rename to tests/mir-opt/building/match/deref-patterns/string.rs diff --git a/tests/mir-opt/exponential_or.match_tuple.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir similarity index 100% rename from tests/mir-opt/exponential_or.match_tuple.SimplifyCfg-initial.after.mir rename to tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir diff --git a/tests/mir-opt/exponential_or.rs b/tests/mir-opt/building/match/exponential_or.rs similarity index 100% rename from tests/mir-opt/exponential_or.rs rename to tests/mir-opt/building/match/exponential_or.rs diff --git a/tests/mir-opt/building/match_false_edges.full_tested_match.built.after.mir b/tests/mir-opt/building/match/match_false_edges.full_tested_match.built.after.mir similarity index 100% rename from tests/mir-opt/building/match_false_edges.full_tested_match.built.after.mir rename to tests/mir-opt/building/match/match_false_edges.full_tested_match.built.after.mir diff --git a/tests/mir-opt/building/match_false_edges.full_tested_match2.built.after.mir b/tests/mir-opt/building/match/match_false_edges.full_tested_match2.built.after.mir similarity index 100% rename from tests/mir-opt/building/match_false_edges.full_tested_match2.built.after.mir rename to tests/mir-opt/building/match/match_false_edges.full_tested_match2.built.after.mir diff --git a/tests/mir-opt/building/match_false_edges.main.built.after.mir b/tests/mir-opt/building/match/match_false_edges.main.built.after.mir similarity index 100% rename from tests/mir-opt/building/match_false_edges.main.built.after.mir rename to tests/mir-opt/building/match/match_false_edges.main.built.after.mir diff --git a/tests/mir-opt/building/match_false_edges.rs b/tests/mir-opt/building/match/match_false_edges.rs similarity index 100% rename from tests/mir-opt/building/match_false_edges.rs rename to tests/mir-opt/building/match/match_false_edges.rs diff --git a/tests/mir-opt/building/simple_match.match_bool.built.after.mir b/tests/mir-opt/building/match/simple_match.match_bool.built.after.mir similarity index 100% rename from tests/mir-opt/building/simple_match.match_bool.built.after.mir rename to tests/mir-opt/building/match/simple_match.match_bool.built.after.mir diff --git a/tests/mir-opt/building/simple_match.rs b/tests/mir-opt/building/match/simple_match.rs similarity index 100% rename from tests/mir-opt/building/simple_match.rs rename to tests/mir-opt/building/match/simple_match.rs diff --git a/tests/mir-opt/match_test.main.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir similarity index 95% rename from tests/mir-opt/match_test.main.SimplifyCfg-initial.after.mir rename to tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir index 107f56f7f6912..149c13a8c2f58 100644 --- a/tests/mir-opt/match_test.main.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir @@ -1,6 +1,6 @@ -// MIR for `main` after SimplifyCfg-initial +// MIR for `disjoint_ranges` after SimplifyCfg-initial -fn main() -> () { +fn disjoint_ranges() -> () { let mut _0: (); let _1: i32; let _3: i32; diff --git a/tests/mir-opt/match_test.rs b/tests/mir-opt/building/match/sort_candidates.rs similarity index 64% rename from tests/mir-opt/match_test.rs rename to tests/mir-opt/building/match/sort_candidates.rs index e465289e427f4..755e445ed18ca 100644 --- a/tests/mir-opt/match_test.rs +++ b/tests/mir-opt/building/match/sort_candidates.rs @@ -1,10 +1,9 @@ // skip-filecheck -// Make sure redundant testing paths in `match` expressions are sorted out. - +// Check specific cases of sorting candidates in match lowering. #![feature(exclusive_range_pattern)] -// EMIT_MIR match_test.main.SimplifyCfg-initial.after.mir -fn main() { +// EMIT_MIR sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir +fn disjoint_ranges() { let x = 3; let b = true; @@ -17,3 +16,5 @@ fn main() { _ => 3, }; } + +fn main() {} From 5ef9ad37abf51d7346f7ac68b910f1a18d86f65b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 13 Mar 2024 22:20:40 +0100 Subject: [PATCH 2/4] Add test --- ....constant_eq.SimplifyCfg-initial.after.mir | 127 ++++++++++++++++++ .../mir-opt/building/match/sort_candidates.rs | 12 ++ 2 files changed, 139 insertions(+) create mode 100644 tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir diff --git a/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..d515a676bb09d --- /dev/null +++ b/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir @@ -0,0 +1,127 @@ +// MIR for `constant_eq` after SimplifyCfg-initial + +fn constant_eq(_1: &str, _2: bool) -> u32 { + debug s => _1; + debug b => _2; + let mut _0: u32; + let mut _3: (&str, bool); + let mut _4: &str; + let mut _5: bool; + let mut _6: bool; + let mut _7: bool; + let mut _8: bool; + let mut _9: &&str; + let mut _10: &bool; + let mut _11: bool; + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = _1; + StorageLive(_5); + _5 = _2; + _3 = (move _4, move _5); + StorageDead(_5); + StorageDead(_4); + PlaceMention(_3); + _8 = ::eq((_3.0: &str), const "a") -> [return: bb13, unwind: bb21]; + } + + bb1: { + _7 = ::eq((_3.0: &str), const "b") -> [return: bb11, unwind: bb21]; + } + + bb2: { + _6 = ::eq((_3.0: &str), const "a") -> [return: bb8, unwind: bb21]; + } + + bb3: { + switchInt((_3.1: bool)) -> [0: bb4, otherwise: bb5]; + } + + bb4: { + _0 = const 5_u32; + goto -> bb20; + } + + bb5: { + falseEdge -> [real: bb19, imaginary: bb4]; + } + + bb6: { + switchInt((_3.1: bool)) -> [0: bb3, otherwise: bb7]; + } + + bb7: { + falseEdge -> [real: bb18, imaginary: bb5]; + } + + bb8: { + switchInt(move _6) -> [0: bb3, otherwise: bb6]; + } + + bb9: { + switchInt((_3.1: bool)) -> [0: bb2, otherwise: bb10]; + } + + bb10: { + falseEdge -> [real: bb17, imaginary: bb7]; + } + + bb11: { + switchInt(move _7) -> [0: bb2, otherwise: bb9]; + } + + bb12: { + falseEdge -> [real: bb14, imaginary: bb10]; + } + + bb13: { + switchInt(move _8) -> [0: bb1, otherwise: bb12]; + } + + bb14: { + _9 = &fake (_3.0: &str); + _10 = &fake (_3.1: bool); + StorageLive(_11); + _11 = const true; + switchInt(move _11) -> [0: bb16, otherwise: bb15]; + } + + bb15: { + StorageDead(_11); + FakeRead(ForMatchGuard, _9); + FakeRead(ForMatchGuard, _10); + _0 = const 1_u32; + goto -> bb20; + } + + bb16: { + StorageDead(_11); + falseEdge -> [real: bb1, imaginary: bb10]; + } + + bb17: { + _0 = const 2_u32; + goto -> bb20; + } + + bb18: { + _0 = const 3_u32; + goto -> bb20; + } + + bb19: { + _0 = const 4_u32; + goto -> bb20; + } + + bb20: { + StorageDead(_3); + return; + } + + bb21 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/match/sort_candidates.rs b/tests/mir-opt/building/match/sort_candidates.rs index 755e445ed18ca..ead8e62db5209 100644 --- a/tests/mir-opt/building/match/sort_candidates.rs +++ b/tests/mir-opt/building/match/sort_candidates.rs @@ -2,6 +2,18 @@ // Check specific cases of sorting candidates in match lowering. #![feature(exclusive_range_pattern)] +// EMIT_MIR sort_candidates.constant_eq.SimplifyCfg-initial.after.mir +fn constant_eq(s: &str, b: bool) -> u32 { + // For now we test "a" twice. + match (s, b) { + ("a", _) if true => 1, + ("b", true) => 2, + ("a", true) => 3, + (_, true) => 4, + _ => 5, + } +} + // EMIT_MIR sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir fn disjoint_ranges() { let x = 3; From 75d2e67ed240731c15b697424bbd671464c4bde6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 13 Mar 2024 22:23:45 +0100 Subject: [PATCH 3/4] Sort `Eq` candidates in the failure case too --- .../rustc_mir_build/src/build/matches/test.rs | 14 ++-- ....constant_eq.SimplifyCfg-initial.after.mir | 79 ++++++++----------- .../mir-opt/building/match/sort_candidates.rs | 2 +- 3 files changed, 44 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index b66dd83b7ecbb..690879b94885a 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -650,12 +650,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - // FIXME(#29623): return `Some(1)` when the values are different. - (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) - if test_val == case_val => - { - fully_matched = true; - Some(TestBranch::Success) + (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => { + if test_val == case_val { + fully_matched = true; + Some(TestBranch::Success) + } else { + fully_matched = false; + Some(TestBranch::Failure) + } } ( diff --git a/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir index d515a676bb09d..e95a97b5b87fc 100644 --- a/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir @@ -9,10 +9,9 @@ fn constant_eq(_1: &str, _2: bool) -> u32 { let mut _5: bool; let mut _6: bool; let mut _7: bool; - let mut _8: bool; - let mut _9: &&str; - let mut _10: &bool; - let mut _11: bool; + let mut _8: &&str; + let mut _9: &bool; + let mut _10: bool; bb0: { StorageLive(_3); @@ -24,104 +23,96 @@ fn constant_eq(_1: &str, _2: bool) -> u32 { StorageDead(_5); StorageDead(_4); PlaceMention(_3); - _8 = ::eq((_3.0: &str), const "a") -> [return: bb13, unwind: bb21]; + _7 = ::eq((_3.0: &str), const "a") -> [return: bb11, unwind: bb19]; } bb1: { - _7 = ::eq((_3.0: &str), const "b") -> [return: bb11, unwind: bb21]; + switchInt((_3.1: bool)) -> [0: bb2, otherwise: bb3]; } bb2: { - _6 = ::eq((_3.0: &str), const "a") -> [return: bb8, unwind: bb21]; + _0 = const 5_u32; + goto -> bb18; } bb3: { - switchInt((_3.1: bool)) -> [0: bb4, otherwise: bb5]; + falseEdge -> [real: bb17, imaginary: bb2]; } bb4: { - _0 = const 5_u32; - goto -> bb20; + falseEdge -> [real: bb12, imaginary: bb9]; } bb5: { - falseEdge -> [real: bb19, imaginary: bb4]; + switchInt((_3.1: bool)) -> [0: bb1, otherwise: bb6]; } bb6: { - switchInt((_3.1: bool)) -> [0: bb3, otherwise: bb7]; + falseEdge -> [real: bb16, imaginary: bb3]; } bb7: { - falseEdge -> [real: bb18, imaginary: bb5]; + _6 = ::eq((_3.0: &str), const "b") -> [return: bb10, unwind: bb19]; } bb8: { - switchInt(move _6) -> [0: bb3, otherwise: bb6]; + switchInt((_3.1: bool)) -> [0: bb1, otherwise: bb9]; } bb9: { - switchInt((_3.1: bool)) -> [0: bb2, otherwise: bb10]; + falseEdge -> [real: bb15, imaginary: bb6]; } bb10: { - falseEdge -> [real: bb17, imaginary: bb7]; + switchInt(move _6) -> [0: bb1, otherwise: bb8]; } bb11: { - switchInt(move _7) -> [0: bb2, otherwise: bb9]; + switchInt(move _7) -> [0: bb7, otherwise: bb4]; } bb12: { - falseEdge -> [real: bb14, imaginary: bb10]; + _8 = &fake (_3.0: &str); + _9 = &fake (_3.1: bool); + StorageLive(_10); + _10 = const true; + switchInt(move _10) -> [0: bb14, otherwise: bb13]; } bb13: { - switchInt(move _8) -> [0: bb1, otherwise: bb12]; - } - - bb14: { - _9 = &fake (_3.0: &str); - _10 = &fake (_3.1: bool); - StorageLive(_11); - _11 = const true; - switchInt(move _11) -> [0: bb16, otherwise: bb15]; - } - - bb15: { - StorageDead(_11); + StorageDead(_10); + FakeRead(ForMatchGuard, _8); FakeRead(ForMatchGuard, _9); - FakeRead(ForMatchGuard, _10); _0 = const 1_u32; - goto -> bb20; + goto -> bb18; } - bb16: { - StorageDead(_11); - falseEdge -> [real: bb1, imaginary: bb10]; + bb14: { + StorageDead(_10); + falseEdge -> [real: bb5, imaginary: bb9]; } - bb17: { + bb15: { _0 = const 2_u32; - goto -> bb20; + goto -> bb18; } - bb18: { + bb16: { _0 = const 3_u32; - goto -> bb20; + goto -> bb18; } - bb19: { + bb17: { _0 = const 4_u32; - goto -> bb20; + goto -> bb18; } - bb20: { + bb18: { StorageDead(_3); return; } - bb21 (cleanup): { + bb19 (cleanup): { resume; } } diff --git a/tests/mir-opt/building/match/sort_candidates.rs b/tests/mir-opt/building/match/sort_candidates.rs index ead8e62db5209..2f93cd767bdc9 100644 --- a/tests/mir-opt/building/match/sort_candidates.rs +++ b/tests/mir-opt/building/match/sort_candidates.rs @@ -4,7 +4,7 @@ // EMIT_MIR sort_candidates.constant_eq.SimplifyCfg-initial.after.mir fn constant_eq(s: &str, b: bool) -> u32 { - // For now we test "a" twice. + // Check that we only test "a" once match (s, b) { ("a", _) if true => 1, ("b", true) => 2, From 65efa5b3b9ec77be6a009a08ea07971d6438ec9b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 27 Mar 2024 17:50:58 +0100 Subject: [PATCH 4/4] Add FileCheck directives to the new tests. --- ...joint_ranges.SimplifyCfg-initial.after.mir | 70 +++++++------------ .../mir-opt/building/match/sort_candidates.rs | 23 ++++-- 2 files changed, 42 insertions(+), 51 deletions(-) diff --git a/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir index 149c13a8c2f58..80d3c2e5c23e8 100644 --- a/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir @@ -1,38 +1,24 @@ // MIR for `disjoint_ranges` after SimplifyCfg-initial -fn disjoint_ranges() -> () { - let mut _0: (); - let _1: i32; - let _3: i32; +fn disjoint_ranges(_1: i32, _2: bool) -> u32 { + debug x => _1; + debug b => _2; + let mut _0: u32; + let mut _3: bool; let mut _4: bool; let mut _5: bool; let mut _6: bool; - let mut _7: bool; - let mut _8: &i32; - let mut _9: bool; - scope 1 { - debug x => _1; - let _2: bool; - scope 2 { - debug b => _2; - } - } + let mut _7: &i32; + let mut _8: bool; bb0: { - StorageLive(_1); - _1 = const 3_i32; - FakeRead(ForLet(None), _1); - StorageLive(_2); - _2 = const true; - FakeRead(ForLet(None), _2); - StorageLive(_3); PlaceMention(_1); - _6 = Le(const 0_i32, _1); - switchInt(move _6) -> [0: bb3, otherwise: bb8]; + _5 = Le(const 0_i32, _1); + switchInt(move _5) -> [0: bb3, otherwise: bb8]; } bb1: { - _3 = const 3_i32; + _0 = const 3_u32; goto -> bb14; } @@ -41,8 +27,8 @@ fn disjoint_ranges() -> () { } bb3: { - _4 = Le(const 10_i32, _1); - switchInt(move _4) -> [0: bb5, otherwise: bb7]; + _3 = Le(const 10_i32, _1); + switchInt(move _3) -> [0: bb5, otherwise: bb7]; } bb4: { @@ -58,49 +44,45 @@ fn disjoint_ranges() -> () { } bb7: { - _5 = Le(_1, const 20_i32); - switchInt(move _5) -> [0: bb5, otherwise: bb4]; + _4 = Le(_1, const 20_i32); + switchInt(move _4) -> [0: bb5, otherwise: bb4]; } bb8: { - _7 = Lt(_1, const 10_i32); - switchInt(move _7) -> [0: bb3, otherwise: bb2]; + _6 = Lt(_1, const 10_i32); + switchInt(move _6) -> [0: bb3, otherwise: bb2]; } bb9: { - _8 = &fake _1; - StorageLive(_9); - _9 = _2; - switchInt(move _9) -> [0: bb11, otherwise: bb10]; + _7 = &fake _1; + StorageLive(_8); + _8 = _2; + switchInt(move _8) -> [0: bb11, otherwise: bb10]; } bb10: { - StorageDead(_9); - FakeRead(ForMatchGuard, _8); - _3 = const 0_i32; + StorageDead(_8); + FakeRead(ForMatchGuard, _7); + _0 = const 0_u32; goto -> bb14; } bb11: { - StorageDead(_9); + StorageDead(_8); falseEdge -> [real: bb1, imaginary: bb4]; } bb12: { - _3 = const 1_i32; + _0 = const 1_u32; goto -> bb14; } bb13: { - _3 = const 2_i32; + _0 = const 2_u32; goto -> bb14; } bb14: { - StorageDead(_3); - _0 = const (); - StorageDead(_2); - StorageDead(_1); return; } } diff --git a/tests/mir-opt/building/match/sort_candidates.rs b/tests/mir-opt/building/match/sort_candidates.rs index 2f93cd767bdc9..a2583ff828484 100644 --- a/tests/mir-opt/building/match/sort_candidates.rs +++ b/tests/mir-opt/building/match/sort_candidates.rs @@ -1,10 +1,14 @@ -// skip-filecheck // Check specific cases of sorting candidates in match lowering. #![feature(exclusive_range_pattern)] // EMIT_MIR sort_candidates.constant_eq.SimplifyCfg-initial.after.mir fn constant_eq(s: &str, b: bool) -> u32 { // Check that we only test "a" once + + // CHECK-LABEL: fn constant_eq( + // CHECK: bb0: { + // CHECK: [[a:_.*]] = const "a"; + // CHECK-NOT: {{_.*}} = const "a"; match (s, b) { ("a", _) if true => 1, ("b", true) => 2, @@ -15,18 +19,23 @@ fn constant_eq(s: &str, b: bool) -> u32 { } // EMIT_MIR sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir -fn disjoint_ranges() { - let x = 3; - let b = true; +fn disjoint_ranges(x: i32, b: bool) -> u32 { + // When `(0..=10).contains(x) && !b`, we should jump to the last arm without testing the two + // other candidates. - // When `(0..=10).contains(x) && !b`, we should jump to the last arm - // without testing two other candidates. + // CHECK-LABEL: fn disjoint_ranges( + // CHECK: debug b => _2; + // CHECK: bb0: { + // CHECK: switchInt(_2) -> [0: [[jump:bb.*]], otherwise: {{bb.*}}]; + // CHECK: [[jump]]: { + // CHECK-NEXT: _0 = const 3_u32; + // CHECK-NEXT: return; match x { 0..10 if b => 0, 10..=20 => 1, -1 => 2, _ => 3, - }; + } } fn main() {}