Undetected unconditional recursion in Clone impl using to_owned()

2412516
Opened by crumblingstatue at 2024-12-21 04:50:10
fn main() {
    struct Foo;
    impl Clone for Foo {
        fn clone(&self) -> Self {
            self.to_owned()
        }
    }
    Foo.clone();
}

Rustc gives no warning, but this overflows the stack.

Slightly more realistic example:

fn main() {
    use std::borrow::{Borrow, ToOwned};
    use std::ops::Deref;
    struct Foo;
    struct Borrowed;
    impl Deref for Foo {
        type Target = Borrowed;
        fn deref(&self) -> &Borrowed {
            unimplemented!()
        }
    }
    impl Borrow<Borrowed> for Foo {
        fn borrow(&self) -> &Borrowed {
            &*self
        }
    }
    impl ToOwned for Borrowed {
        type Owned = Foo;
        fn to_owned(&self) -> Foo {
            unimplemented!()
        }
    }
    impl Clone for Foo {
        fn clone(&self) -> Self {
            (*self).to_owned() // Oops, should have dereferenced twice
        }
    }
    Foo.clone();
}

(Real life use case for implementing Clone through deref -> to_owned)

Is it feasible for rustc to detect this kind of cross-trait unconditional recursion?

  1. Also see https://github.com/rust-lang/rust/issues/50049.

    Mark Rousskov at 2018-05-29 14:30:17