From af7b1678038f48123402f200c53f8c4c98c1a523 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 14 Aug 2022 02:00:08 +0800 Subject: [PATCH 1/5] Avoid infinite loop in function arguments checking --- .../src/check/fn_ctxt/arg_matrix.rs | 9 ++- .../ui/argument-suggestions/issue-100478.rs | 65 +++++++++++++++++++ .../argument-suggestions/issue-100478.stderr | 41 ++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/argument-suggestions/issue-100478.rs create mode 100644 src/test/ui/argument-suggestions/issue-100478.stderr diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs index 7602f2550e85b..e72b26e36b62c 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs @@ -364,7 +364,14 @@ impl<'tcx> ArgMatrix<'tcx> { None => { // We didn't find any issues, so we need to push the algorithm forward // First, eliminate any arguments that currently satisfy their inputs - for (inp, arg) in self.eliminate_satisfied() { + let eliminated = self.eliminate_satisfied(); + if eliminated.len() == 0 { + // In every iteration, we should reduce the size of provided_indices or expected_indices + // Othewise, means we meet an weird compatibility_matrix which we can not shrink the size, + // we will get into infinite loop if we don't break the loop, see #100478 + break; + } + for (inp, arg) in eliminated { matched_inputs[arg] = Some(inp); } } diff --git a/src/test/ui/argument-suggestions/issue-100478.rs b/src/test/ui/argument-suggestions/issue-100478.rs new file mode 100644 index 0000000000000..641a090c878f8 --- /dev/null +++ b/src/test/ui/argument-suggestions/issue-100478.rs @@ -0,0 +1,65 @@ +use std::sync::Arc; +macro_rules! GenT { + ($name:tt) => { + #[derive(Default, Debug)] + struct $name { + #[allow(unused)] + val: i32, + } + + impl $name { + #[allow(unused)] + fn new(val: i32) -> Self { + $name { val } + } + } + }; +} + +GenT!(T1); +GenT!(T2); +GenT!(T3); +GenT!(T4); +GenT!(T5); +GenT!(T6); +GenT!(T7); +GenT!(T8); +GenT!(T9); +GenT!(T10); +GenT!(T11); + +#[allow(unused)] +fn foo( + p1: T1, + p2: Arc, + p3: T3, + p4: Arc, + p5: T5, + p6: T6, + p7: T7, + p8: Arc, + p9: T9, + p10: T10, + p11: T11, +) { +} + +fn main() { + let p1 = T1::new(0); + let p2 = Arc::new(T2::new(0)); + let p3 = T3::new(0); + let p4 = Arc::new(T4::new(1)); + let p5 = T5::new(0); + let p6 = T6::new(0); + let p7 = T7::new(0); + let p8 = Arc::default(); + let p9 = T9::new(0); + let p10 = T10::new(0); + let p11 = T11::new(0); + + foo( + //~^ ERROR 60:5: 60:8: this function takes 11 arguments but 10 arguments were supplied [E0061] + p1, //p2, + p3, p4, p5, p6, p7, p8, p9, p10, p11, + ); +} diff --git a/src/test/ui/argument-suggestions/issue-100478.stderr b/src/test/ui/argument-suggestions/issue-100478.stderr new file mode 100644 index 0000000000000..6122a387b96ae --- /dev/null +++ b/src/test/ui/argument-suggestions/issue-100478.stderr @@ -0,0 +1,41 @@ +error[E0061]: this function takes 11 arguments but 10 arguments were supplied + --> $DIR/issue-100478.rs:60:5 + | +LL | foo( + | ^^^ + | +note: function defined here + --> $DIR/issue-100478.rs:32:4 + | +LL | fn foo( + | ^^^ +LL | p1: T1, + | ------ +LL | p2: Arc, + | ----------- +LL | p3: T3, + | ------ +LL | p4: Arc, + | ----------- +LL | p5: T5, + | ------ +LL | p6: T6, + | ------ +LL | p7: T7, + | ------ +LL | p8: Arc, + | ----------- +LL | p9: T9, + | ------ +LL | p10: T10, + | -------- +LL | p11: T11, + | -------- +help: did you mean + | +LL | foo(p1, /* Arc */, /* T3 */, /* Arc */, /* T5 */, /* T6 */, /* T7 */, /* Arc */, /* T9 */, /* T10 */, /* T11 */); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0061`. From ea96b22172924fa8f6d7bdfc542f70b0d52729f2 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 14 Aug 2022 13:36:11 +0800 Subject: [PATCH 2/5] debugg --- .../src/check/fn_ctxt/arg_matrix.rs | 60 ++++++++-- .../ui/argument-suggestions/issue-100478.rs | 31 ++--- .../argument-suggestions/issue-100478.stderr | 106 ++++++++++++------ 3 files changed, 130 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs index e72b26e36b62c..30cd80a83746e 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs @@ -110,6 +110,36 @@ impl<'tcx> ArgMatrix<'tcx> { self.eliminate_expected(expected_idx); } + /* fn print_mat(&self, msg: &str) { + println!("================== {} ==================", msg); + let mat = &self.compatibility_matrix; + let mut head = false; + for (i, row) in mat.iter().enumerate() { + if !head { + print!("x| "); + for (j, _) in row.iter().enumerate() { + print!(" {} ", j); + } + print!("\n-| "); + for (_, _) in row.iter().enumerate() { + print!(" - "); + } + head = true; + println!(); + } + print!("{}| ", i); + for (_j, cell) in row.iter().enumerate() { + match cell { + Compatibility::Compatible => print!(" 1 "), + Compatibility::Incompatible(_err) => { + print!(" 0 "); + } + } + } + println!(); + } + } */ + // Returns a `Vec` of (user input, expected arg) of matched arguments. These // are inputs on the remaining diagonal that match. fn eliminate_satisfied(&mut self) -> Vec<(ProvidedIdx, ExpectedIdx)> { @@ -130,14 +160,18 @@ impl<'tcx> ArgMatrix<'tcx> { let ai = &self.expected_indices; let ii = &self.provided_indices; + /* println!("ai: {:?}", ai); + println!("ii: {:?}", ii); + self.print_mat("find_issue:"); */ + let mut cur_matched_idx = 0; for i in 0..cmp::max(ai.len(), ii.len()) { - // If we eliminate the last row, any left-over inputs are considered missing + // If we eliminate the last row, any left-over arguments are considered missing if i >= mat.len() { - return Some(Issue::Missing(i)); + return Some(Issue::Missing(cur_matched_idx)); } - // If we eliminate the last column, any left-over arguments are extra + // If we eliminate the last column, any left-over inputs are extra if mat[i].len() == 0 { - return Some(Issue::Extra(i)); + return Some(Issue::Extra(cur_matched_idx)); } // Make sure we don't pass the bounds of our matrix @@ -145,6 +179,7 @@ impl<'tcx> ArgMatrix<'tcx> { let is_input = i < ii.len(); if is_arg && is_input && matches!(mat[i][i], Compatibility::Compatible) { // This is a satisfied input, so move along + cur_matched_idx += 1; continue; } @@ -163,7 +198,7 @@ impl<'tcx> ArgMatrix<'tcx> { if is_input { for j in 0..ai.len() { // If we find at least one argument that could satisfy this input - // this argument isn't useless + // this input isn't useless if matches!(mat[i][j], Compatibility::Compatible) { useless = false; break; @@ -171,6 +206,10 @@ impl<'tcx> ArgMatrix<'tcx> { } } + /* println!( + "i: {}, is_arg: {}, is_input: {}, useless: {}, unsatisfiable: {}", + i, is_arg, is_input, useless, unsatisfiable + ); */ match (is_input, is_arg, useless, unsatisfiable) { // If an argument is unsatisfied, and the input in its position is useless // then the most likely explanation is that we just got the types wrong @@ -309,7 +348,9 @@ impl<'tcx> ArgMatrix<'tcx> { } while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() { - match self.find_issue() { + let res = self.find_issue(); + //println!("res: {:?}", res); + match res { Some(Issue::Invalid(idx)) => { let compatibility = self.compatibility_matrix[idx][idx].clone(); let input_idx = self.provided_indices[idx]; @@ -365,12 +406,7 @@ impl<'tcx> ArgMatrix<'tcx> { // We didn't find any issues, so we need to push the algorithm forward // First, eliminate any arguments that currently satisfy their inputs let eliminated = self.eliminate_satisfied(); - if eliminated.len() == 0 { - // In every iteration, we should reduce the size of provided_indices or expected_indices - // Othewise, means we meet an weird compatibility_matrix which we can not shrink the size, - // we will get into infinite loop if we don't break the loop, see #100478 - break; - } + assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round"); for (inp, arg) in eliminated { matched_inputs[arg] = Some(inp); } diff --git a/src/test/ui/argument-suggestions/issue-100478.rs b/src/test/ui/argument-suggestions/issue-100478.rs index 641a090c878f8..6bef6ad103862 100644 --- a/src/test/ui/argument-suggestions/issue-100478.rs +++ b/src/test/ui/argument-suggestions/issue-100478.rs @@ -24,27 +24,17 @@ GenT!(T5); GenT!(T6); GenT!(T7); GenT!(T8); -GenT!(T9); -GenT!(T10); -GenT!(T11); #[allow(unused)] -fn foo( - p1: T1, - p2: Arc, - p3: T3, - p4: Arc, - p5: T5, - p6: T6, - p7: T7, - p8: Arc, - p9: T9, - p10: T10, - p11: T11, -) { -} +fn foo(p1: T1, p2: Arc, p3: T3, p4: Arc, p5: T5, p6: T6, p7: T7, p8: Arc) {} +fn three_diff(_a: T1, _b: T2, _c: T3) {} +fn four_shuffle(_a: T1, _b: T2, _c: T3, _d: T4) {} fn main() { + three_diff(T2::new(0)); //~ ERROR this function takes + four_shuffle(T3::default(), T4::default(), T1::default(), T2::default()); //~ ERROR 35:5: 35:17: arguments to this function are incorrect [E0308] + four_shuffle(T3::default(), T2::default(), T1::default(), T3::default()); //~ ERROR 36:5: 36:17: arguments to this function are incorrect [E0308] + let p1 = T1::new(0); let p2 = Arc::new(T2::new(0)); let p3 = T3::new(0); @@ -53,13 +43,10 @@ fn main() { let p6 = T6::new(0); let p7 = T7::new(0); let p8 = Arc::default(); - let p9 = T9::new(0); - let p10 = T10::new(0); - let p11 = T11::new(0); foo( - //~^ ERROR 60:5: 60:8: this function takes 11 arguments but 10 arguments were supplied [E0061] + //~^ 47:5: 47:8: this function takes 8 arguments but 7 arguments were supplied [E0061] p1, //p2, - p3, p4, p5, p6, p7, p8, p9, p10, p11, + p3, p4, p5, p6, p7, p8, ); } diff --git a/src/test/ui/argument-suggestions/issue-100478.stderr b/src/test/ui/argument-suggestions/issue-100478.stderr index 6122a387b96ae..a77889a9679ca 100644 --- a/src/test/ui/argument-suggestions/issue-100478.stderr +++ b/src/test/ui/argument-suggestions/issue-100478.stderr @@ -1,41 +1,81 @@ -error[E0061]: this function takes 11 arguments but 10 arguments were supplied - --> $DIR/issue-100478.rs:60:5 +error[E0061]: this function takes 3 arguments but 1 argument was supplied + --> $DIR/issue-100478.rs:34:5 + | +LL | three_diff(T2::new(0)); + | ^^^^^^^^^^------------ + | || + | |an argument of type `T1` is missing + | an argument of type `T3` is missing + | +note: function defined here + --> $DIR/issue-100478.rs:30:4 + | +LL | fn three_diff(_a: T1, _b: T2, _c: T3) {} + | ^^^^^^^^^^ ------ ------ ------ +help: provide the arguments + | +LL | three_diff(/* T1 */, T2::new(0), /* T3 */); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0308]: arguments to this function are incorrect + --> $DIR/issue-100478.rs:35:5 + | +LL | four_shuffle(T3::default(), T4::default(), T1::default(), T2::default()); + | ^^^^^^^^^^^^ ------------- ------------- ------------- ------------- expected `T4`, found `T2` + | | | | + | | | expected `T3`, found `T1` + | | expected `T2`, found `T4` + | expected `T1`, found `T3` + | +note: function defined here + --> $DIR/issue-100478.rs:31:4 + | +LL | fn four_shuffle(_a: T1, _b: T2, _c: T3, _d: T4) {} + | ^^^^^^^^^^^^ ------ ------ ------ ------ +help: did you mean + | +LL | four_shuffle(T1::default(), T2::default(), T3::default(), T4::default()); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0308]: arguments to this function are incorrect + --> $DIR/issue-100478.rs:36:5 + | +LL | four_shuffle(T3::default(), T2::default(), T1::default(), T3::default()); + | ^^^^^^^^^^^^ ------------- ------------- ------------- expected struct `T4`, found struct `T3` + | | | + | | expected `T3`, found `T1` + | expected `T1`, found `T3` + | +note: function defined here + --> $DIR/issue-100478.rs:31:4 + | +LL | fn four_shuffle(_a: T1, _b: T2, _c: T3, _d: T4) {} + | ^^^^^^^^^^^^ ------ ------ ------ ------ +help: swap these arguments + | +LL | four_shuffle(T1::default(), T2::default(), T3::default(), /* T4 */); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0061]: this function takes 8 arguments but 7 arguments were supplied + --> $DIR/issue-100478.rs:47:5 | LL | foo( | ^^^ +... +LL | p3, p4, p5, p6, p7, p8, + | -- an argument of type `Arc` is missing | note: function defined here - --> $DIR/issue-100478.rs:32:4 - | -LL | fn foo( - | ^^^ -LL | p1: T1, - | ------ -LL | p2: Arc, - | ----------- -LL | p3: T3, - | ------ -LL | p4: Arc, - | ----------- -LL | p5: T5, - | ------ -LL | p6: T6, - | ------ -LL | p7: T7, - | ------ -LL | p8: Arc, - | ----------- -LL | p9: T9, - | ------ -LL | p10: T10, - | -------- -LL | p11: T11, - | -------- -help: did you mean + --> $DIR/issue-100478.rs:29:4 + | +LL | fn foo(p1: T1, p2: Arc, p3: T3, p4: Arc, p5: T5, p6: T6, p7: T7, p8: Arc) {} + | ^^^ ------ ----------- ------ ----------- ------ ------ ------ ----------- +help: provide the argument | -LL | foo(p1, /* Arc */, /* T3 */, /* Arc */, /* T5 */, /* T6 */, /* T7 */, /* Arc */, /* T9 */, /* T10 */, /* T11 */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | foo(p1, /* Arc */, p3, p4, p5, p6, p7, p8); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to previous error +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0061`. +Some errors have detailed explanations: E0061, E0308. +For more information about an error, try `rustc --explain E0061`. From 63229bfa618dceebbc132d58211a9828fd052fc4 Mon Sep 17 00:00:00 2001 From: chenyukang Date: Mon, 29 Aug 2022 21:58:31 +0800 Subject: [PATCH 3/5] debug --- .../src/check/fn_ctxt/arg_matrix.rs | 27 ++++++++------ .../ui/argument-suggestions/issue-101097.rs | 35 +++++++++++++++++++ 2 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/argument-suggestions/issue-101097.rs diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs index 30cd80a83746e..2086a1213a592 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs @@ -110,7 +110,7 @@ impl<'tcx> ArgMatrix<'tcx> { self.eliminate_expected(expected_idx); } - /* fn print_mat(&self, msg: &str) { + fn print_mat(&self, msg: &str) { println!("================== {} ==================", msg); let mat = &self.compatibility_matrix; let mut head = false; @@ -138,7 +138,7 @@ impl<'tcx> ArgMatrix<'tcx> { } println!(); } - } */ + } // Returns a `Vec` of (user input, expected arg) of matched arguments. These // are inputs on the remaining diagonal that match. @@ -160,9 +160,9 @@ impl<'tcx> ArgMatrix<'tcx> { let ai = &self.expected_indices; let ii = &self.provided_indices; - /* println!("ai: {:?}", ai); + println!("ai: {:?}", ai); println!("ii: {:?}", ii); - self.print_mat("find_issue:"); */ + self.print_mat("find_issue:"); let mut cur_matched_idx = 0; for i in 0..cmp::max(ai.len(), ii.len()) { // If we eliminate the last row, any left-over arguments are considered missing @@ -206,10 +206,10 @@ impl<'tcx> ArgMatrix<'tcx> { } } - /* println!( - "i: {}, is_arg: {}, is_input: {}, useless: {}, unsatisfiable: {}", - i, is_arg, is_input, useless, unsatisfiable - ); */ + println!( + "i: {}, is_input: {}, is_arg: {}, useless: {}, unsatisfiable: {}", + i, is_input, is_arg, useless, unsatisfiable + ); match (is_input, is_arg, useless, unsatisfiable) { // If an argument is unsatisfied, and the input in its position is useless // then the most likely explanation is that we just got the types wrong @@ -271,6 +271,7 @@ impl<'tcx> ArgMatrix<'tcx> { if matches!(c, Compatibility::Compatible) { Some(i) } else { None } }) .collect(); + println!("loop i: {} j: {} compat: {:?}", i, j, compat); if compat.len() != 1 { // this could go into multiple slots, don't bother exploring both is_cycle = false; @@ -291,6 +292,7 @@ impl<'tcx> ArgMatrix<'tcx> { // ex: [1,2,3,4]; last = 2; j = 2; // So, we want to mark 4, 3, and 2 as part of a permutation permutation_found = is_cycle; + println!("permutation_found: {}", permutation_found); while let Some(x) = stack.pop() { if is_cycle { permutation[x] = Some(Some(j)); @@ -303,6 +305,7 @@ impl<'tcx> ArgMatrix<'tcx> { } else { // Some(None) ensures we save time by skipping this argument again permutation[x] = Some(None); + println!("now permutation: {:?}", permutation); } } } @@ -349,7 +352,7 @@ impl<'tcx> ArgMatrix<'tcx> { while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() { let res = self.find_issue(); - //println!("res: {:?}", res); + println!("res: {:?}", res); match res { Some(Issue::Invalid(idx)) => { let compatibility = self.compatibility_matrix[idx][idx].clone(); @@ -406,7 +409,11 @@ impl<'tcx> ArgMatrix<'tcx> { // We didn't find any issues, so we need to push the algorithm forward // First, eliminate any arguments that currently satisfy their inputs let eliminated = self.eliminate_satisfied(); - assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round"); + //assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round"); + if eliminated.is_empty() { + println!("not found anything ...."); + return (errors, matched_inputs); + } for (inp, arg) in eliminated { matched_inputs[arg] = Some(inp); } diff --git a/src/test/ui/argument-suggestions/issue-101097.rs b/src/test/ui/argument-suggestions/issue-101097.rs new file mode 100644 index 0000000000000..de4acacb2c802 --- /dev/null +++ b/src/test/ui/argument-suggestions/issue-101097.rs @@ -0,0 +1,35 @@ +struct A; +struct B; +struct C; +struct D; + +fn f( + a1: A, + a2: A, + b1: B, + b2: B, + c1: C, + c2: C, +) {} + +fn main() { + f(C, A, A, A, B, B, C); + //f(C, C, A, A, B, B); + //f(A, A, D, D, B, B); //ok + //f(C, C, B, B, A, A); + //f(C, C, A, B, A, A); +} + + +/* +fn f(a1: A, a2: A, b1: B, c1: C) {} + +fn main() { + f( + C, + A, + A, + B, + ); +} +*/ \ No newline at end of file From ce0eea5fb9a861c4e3b06c8fbed2ae9498e239df Mon Sep 17 00:00:00 2001 From: chenyukang Date: Tue, 30 Aug 2022 09:55:48 +0800 Subject: [PATCH 4/5] debug --- compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs index 2086a1213a592..637392d135882 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs @@ -409,11 +409,7 @@ impl<'tcx> ArgMatrix<'tcx> { // We didn't find any issues, so we need to push the algorithm forward // First, eliminate any arguments that currently satisfy their inputs let eliminated = self.eliminate_satisfied(); - //assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round"); - if eliminated.is_empty() { - println!("not found anything ...."); - return (errors, matched_inputs); - } + assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round"); for (inp, arg) in eliminated { matched_inputs[arg] = Some(inp); } From 761db379744ad16c6ed1c9e88cf44c8927f3573b Mon Sep 17 00:00:00 2001 From: chenyukang Date: Tue, 30 Aug 2022 10:08:43 +0800 Subject: [PATCH 5/5] debug --- src/test/ui/argument-suggestions/issue-101097.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/argument-suggestions/issue-101097.rs b/src/test/ui/argument-suggestions/issue-101097.rs index de4acacb2c802..9a1315a6ab79e 100644 --- a/src/test/ui/argument-suggestions/issue-101097.rs +++ b/src/test/ui/argument-suggestions/issue-101097.rs @@ -13,8 +13,8 @@ fn f( ) {} fn main() { - f(C, A, A, A, B, B, C); - //f(C, C, A, A, B, B); + f(C, C, A, A, B, B); + //f(C, A, A, A, B, B, C); //f(A, A, D, D, B, B); //ok //f(C, C, B, B, A, A); //f(C, C, A, B, A, A);