Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression: implementing Clone for trait objects #61759

Closed
ryzhyk opened this issue Jun 12, 2019 · 8 comments
Closed

Regression: implementing Clone for trait objects #61759

ryzhyk opened this issue Jun 12, 2019 · 8 comments
Assignees
Labels
C-bug Category: This is a bug. P-high High priority regression-from-stable-to-stable Performance or correctness regression from one stable version to another. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@ryzhyk
Copy link

ryzhyk commented Jun 12, 2019

The following code used to work with rustc 1.32 and earlier, but it crashes with stack overflow due to infinite recursion in 1.35

pub trait CBFn: Fn() {
   fn clone_boxed(&self) -> Box<dyn CBFn>;
}

impl<T> CBFn for T
where
   T: 'static + Clone + Fn()
{
   fn clone_boxed(&self) -> Box<dyn CBFn> {
       Box::new(T::clone(self))
   }
}

impl Clone for Box<dyn CBFn> {
   fn clone(&self) -> Self {
       self.clone_boxed()
   }
}

fn main() { 
    let f = || {};
    let b: Box<dyn CBFn> = Box::new(f);
    let bb = b.clone();
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
warning: unused variable: `bb`
  --> src/main.rs:23:9
   |
23 |     let bb = b.clone();
   |         ^^ help: consider prefixing with an underscore: `_bb`
   |
   = note: #[warn(unused_variables)] on by default

    Finished dev [unoptimized + debuginfo] target(s) in 0.81s
     Running `target/debug/playground`

thread 'main' has overflowed its stack
fatal runtime error: stack overflow
timeout: the monitored command dumped core
/root/entrypoint.sh: line 8:     8 Aborted                 timeout --signal=KILL ${timeout} "$@"

Update

It does work correctly with a slightly modified main using as_ref() to explicitly extract reference out of the box.

fn main() { 
    let f = || {};
    let b: Box<dyn CBFn> = Box::new(f);
    let bb = b.as_ref().clone();
}
@jonas-schievink jonas-schievink added C-bug Category: This is a bug. I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. I-nominated regression-from-stable-to-stable Performance or correctness regression from one stable version to another. labels Jun 12, 2019
@RustyYato
Copy link
Contributor

RustyYato commented Jun 12, 2019

This also works if you extract the reference out of the box inside the Clone impl

impl Clone for Box<dyn CBFn> {
   fn clone(&self) -> Self {
       self.as_ref().clone_boxed()
   }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a2d576b485eee34dc1cecdb06e78a0f6

It looks like Rust sees that Box<dyn CBFn>: 'static + Clone + Fn() then applies the blanket impl for CBFn to Box<dyn CBFn>. This in turn leads self.clone_box() using <Box<dyn CBFn> as CBFn>::clone_boxed(&self), which leads to an infinite recursion problem.
This can be fixed by explicitly getting the reference to the trait object using as_ref.

@ryzhyk
Copy link
Author

ryzhyk commented Jun 12, 2019

Thanks, @KrishnaSannasi. This makes sense, but I find it disturbing that the behavior changed in one of the recent compiler releases, potentially breaking software that relied on the old behavior. I wonder if this is deliberate or a side effect of some other change.

@RustyYato
Copy link
Contributor

RustyYato commented Jun 12, 2019

This is likely a side effect of changes/optimizations to how blanket trait impls interact with each other.

@ollie27
Copy link
Member

ollie27 commented Jun 13, 2019

This looks like fallout from #59500.

@jonas-schievink jonas-schievink removed the I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. label Jun 13, 2019
@pnkfelix pnkfelix added the E-needs-bisection Call for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc label Jun 27, 2019
@pnkfelix pnkfelix self-assigned this Jun 27, 2019
@pnkfelix
Copy link
Member

pnkfelix commented Jun 27, 2019

pre-triage: P-high (for bisection to verify hypothesis that this was injected by #59500), unnominating, assigning to self.

@pnkfelix pnkfelix added P-high High priority and removed I-nominated labels Jun 27, 2019
@lqd
Copy link
Member

lqd commented Jun 28, 2019

Bisected to nightly-2019-04-06, acd8dd6, which is indeed PR #59500.

@lqd lqd removed the E-needs-bisection Call for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc label Jun 28, 2019
@pnkfelix
Copy link
Member

During the T-compiler pre-meeting triage yesterday, @Centril said that if this is an injection from #59500, and it is expected fallout from that. @lqd has confirmed this.

In addition to using as_ref to force the compiler to choose the appropriate method implementation, you can also explicitly deref: (**self).clone_boxed() or write out the specific implementing types via Universal Function Call Syntax (UFCS): <dyn CBFn as CBFn>::clone_boxed(&**self).

Closing as not-a-bug.

@gitnlsn
Copy link

gitnlsn commented Sep 22, 2020

This also works if you extract the reference out of the box inside the Clone impl

impl Clone for Box<dyn CBFn> {
   fn clone(&self) -> Self {
       self.as_ref().clone_boxed()
   }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a2d576b485eee34dc1cecdb06e78a0f6

It looks like Rust sees that Box<dyn CBFn>: 'static + Clone + Fn() then applies the blanket impl for CBFn to Box<dyn CBFn>. This in turn leads self.clone_box() using <Box<dyn CBFn> as CBFn>::clone_boxed(&self), which leads to an infinite recursion problem.
This can be fixed by explicitly getting the reference to the trait object using as_ref.

I just came to a similar issue. This code was helpful to me. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. P-high High priority regression-from-stable-to-stable Performance or correctness regression from one stable version to another. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

7 participants