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

Rust stable, fatal runtime error: stack overflow, PartialEq #57299

Open
denisandroid opened this issue Jan 3, 2019 · 6 comments
Open

Rust stable, fatal runtime error: stack overflow, PartialEq #57299

denisandroid opened this issue Jan 3, 2019 · 6 comments
Labels
A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. C-feature-request Category: A feature request, i.e: not implemented / a PR. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@denisandroid
Copy link

Error

cargo run
   Compiling stack_overflow v0.1.0 (/d/stack_overflow)                                                                        
    Finished dev [unoptimized + debuginfo] target(s) in 7.08s                                                                 
     Running `target/debug/stack_overflow`

thread 'main' has overflowed its stack
fatal runtime error: stack overflow
Аварийный останов (стек памяти сброшен на диск)

Rust Version

RUST STABLE:
rustc 1.31.1 (b6c32da9b 2018-12-18)

RUST NIGHTLY:
rustc 1.33.0-nightly (9eac38634 2018-12-31)

Code

use std::fmt::Write;
use std::ops::Deref;
use std::fmt;

//////The structure stores in itself the changeable reference and has to clean data in "drop" of structure.
#[derive(Debug)]
pub struct DebugSliceMutString<'a>(&'a mut String);



//Stack overflow <----
//assert_eq!(&null.my_debug(&mut string).unwrap(), "=");
impl<'a> PartialEq<str> for DebugSliceMutString<'a> {   
     fn eq(&self, other: &str) -> bool {
          self == other
     }
}



//Stack overflow <----
//assert_eq!(null.my_debug(&mut string).unwrap(), "=");
impl<'a> PartialEq<&str> for DebugSliceMutString<'a> {
     fn eq(&self, other: &&str) -> bool {
          self == other
     }
}

//If to take 'deref' that there is no overflow of a stack!!
//assert_eq!(*null.my_debug(&mut string).unwrap(), "=");
impl<'a> Deref for DebugSliceMutString<'a> {
     type Target = String;
     
     fn deref(&self) -> &String {
          self.0
     }
}

impl<'a> Drop for DebugSliceMutString<'a> {
     fn drop(&mut self) {
          self.0.clear()
     }
}



//MyTrait
pub trait MyDebug {
     fn my_debug<'a>(&self, w: &'a mut String) -> Result<DebugSliceMutString<'a>, fmt::Error>;
}

impl MyDebug for (usize, usize) {
     fn my_debug<'a>(&self, w: &'a mut String) -> Result<DebugSliceMutString<'a>, fmt::Error> {
          write!(w, "{}={}", self.0, self.1)?;

          Ok( DebugSliceMutString(w) )
     }
}

//


fn main() {
	//The buffer for an exception of frequent reallocations of memory
	let mut string = String::with_capacity(9);

	//Any other type, in this case (usize, usize) is more convenient
	let null =	(10, 20);
	

	// !!!!!!!
	//thread 'main' has overflowed its stack
	//fatal runtime error: stack overflow
	//Аварийный останов (стек памяти сброшен на диск)
	assert_eq!(	null.my_debug(&mut string).unwrap(), "=");


	// !!!!!!!
	//If to use Deref that there is no overflow of a stack!!
	//assert_eq!(	*null.my_debug(&mut string).unwrap(), "=");


	// !!!!!!!OR
	//thread 'main' has overflowed its stack
	//fatal runtime error: stack overflow
	//Аварийный останов (стек памяти сброшен на диск)
	//
	//let a = null.my_debug(&mut string).unwrap()
	//	.eq(&"=");
	//

	//
	println!("string, {}, capacity: {}", string, string.capacity());
}

Run

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

@jonas-schievink
Copy link
Contributor

The self==other will call back into the same impl, which is unbounded recursion that then overflows the stack

@denisandroid
Copy link
Author

denisandroid commented Jan 3, 2019

"The self==other will call back into the same impl, which is unbounded recursion that then overflows the stack"

Perhaps, but 'RLS' cannot find a recursion in this case

@denisandroid
Copy link
Author

cargo check
    Checking stack_overflow v0.1.0 (/d/stack_overflow)
    Finished dev [unoptimized + debuginfo] target(s) in 9.54s

Empty..

@sinkuu
Copy link
Contributor

sinkuu commented Jan 3, 2019

unconditional_recursion lint detects only self-calling functions. Since self == other is equivalent to (&self).eq(&other), it recurses indirectly via PartialEq<&B> for &A branket impl. You can confirm this by trying (*self) == (*other), which generates the desired warning.

@estebank
Copy link
Contributor

estebank commented Jan 3, 2019

CC #45838

@estebank estebank added A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. C-feature-request Category: A feature request, i.e: not implemented / a PR. labels Jan 3, 2019
@jonas-schievink jonas-schievink added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jan 29, 2019
@Enyium
Copy link

Enyium commented Dec 26, 2023

I have the same problem.

rustc --version:

rustc 1.73.0 (cc66ad468 2023-10-03)

Minimal repro:

#![allow(dead_code, unused_mut)]

fn main() {
    let container_1 = Container::new(123);

    let mut container_2 = Container::new(123);
    container_2.set(234);

    println!("equal: {}", container_1 == container_2);
}

#[derive(Debug)]
pub struct Container<T: PartialEq> {
    old: T,
    new: Option<T>,
}

impl<T: PartialEq> Container<T> {
    pub fn new(value: T) -> Self {
        Self {
            old: value,
            new: None,
        }
    }

    pub fn set(&mut self, value: T) {
        self.new = Some(value);
    }
}

impl<T: PartialEq> PartialEq for Container<T> {
    fn eq(&self, other: &Self) -> bool {
        // Warning: "function cannot return without recursing".
        // *self == *other

        // No warning; just a stack overflow at runtime!
        &*self == &*other

        // My workaround.
        // self.new
        //     .as_ref()
        //     .unwrap_or(&self.old)
        //     .eq(other.new.as_ref().unwrap_or(&other.old))
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. C-feature-request Category: A feature request, i.e: not implemented / a PR. 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

5 participants