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

HRTB bug in streaming iterator #30786

Closed
Stebalien opened this issue Jan 8, 2016 · 1 comment · Fixed by #62519
Closed

HRTB bug in streaming iterator #30786

Stebalien opened this issue Jan 8, 2016 · 1 comment · Fixed by #62519
Labels
A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@Stebalien
Copy link
Contributor

Stebalien commented Jan 8, 2016

Full code playpen

So, I've been working on a hacky streaming iterator library and ran into what looks like a bug in the type checker involving higher ranked trait bounds.

In the code below, I've defined a Stream trait. To implement a stream, one implements Stream on &'a mut MyStream for all lifetimes 'a.

pub trait Stream {
    type Item;
    fn next(self) -> Option<Self::Item>;
}

// Example stream
pub struct Repeat(u64);

impl<'a> Stream for &'a mut Repeat {
    type Item = &'a u64;
    fn next(self) -> Option<Self::Item> {
        Some(&self.0)
    }
}

I then went ahead and implemented adapters for filter and map and an extension trait for applying these adapters:

pub struct Map<S, F> {
    stream: S,
    func: F,
}

impl<'a, A, F, T> Stream for &'a mut Map<A, F>
    where &'a mut A: Stream,
          F: FnMut(<&'a mut A as Stream>::Item) -> T,
{
    type Item = T;
    fn next(self) -> Option<T> {
        match self.stream.next() {
            Some(item) => Some((self.func)(item)),
            None => None,
        }
    }
}

pub struct Filter<S, F> {
    stream: S,
    func: F,
}

impl<'a, A, F, T> Stream for &'a mut Filter<A, F>
    where for<'b> &'b mut A: Stream<Item=T>, // <---- BAD
          F: FnMut(&T) -> bool,
{
    type Item = <&'a mut A as Stream>::Item;
    fn next(self) -> Option<Self::Item> {
        while let Some(item) = self.stream.next() {
            if (self.func)(&item) {
                return Some(item);
            }
        }
        None
    }
}

pub trait StreamExt where for<'b> &'b mut Self: Stream {
    fn map<F>(self, func: F) -> Map<Self, F>
        where Self: Sized,
              for<'a> &'a mut Map<Self, F>: Stream,
    {
        Map {
            func: func,
            stream: self,
        }
    }

    fn filter<F>(self, func: F) -> Filter<Self, F>
        where Self: Sized,
              for<'a> &'a mut Filter<Self, F>: Stream,
    {
        Filter {
            func: func,
            stream: self,
        }
    }

    fn count(mut self) -> usize
        where Self: Sized,
    {
        let mut count = 0;
        while let Some(_) = self.next() {
            count += 1;
        }
        count
    }
}

impl<T> StreamExt for T where for<'a> &'a mut T: Stream { }

The problem is the interaction between the Filter and Map adapters. The following works iff the map adapter appears before the filter adapter.

fn main() {
    let source = Repeat(10);
    let map = source.map(|x: &_| x); // Remove to break.
    let filter = map.filter(|x: &_| true);
    let count = filter.count(); // Assert that we still have a valid stream.
}

However, this should never work due to the for<'b> &'b mut A: Stream<Item=T> constraint on the Filter implementation above (marked BAD). Basically, this constraint should assert the stream's Item is the same independent of 'b which should mean that this item doesn't borrow from it's stream (as a matter of fact, I'm using this exact constraint to implement an adapter for converting streams to iterators). However, items returned by both the map and source streams above do obviously borrow from their streams.

Correct error without map:

test.rs:90:12: 90:32 error: type mismatch resolving `for<'b> <&'b mut Repeat as Stream>::Item == _`:
 expected bound lifetime parameter 'b,
    found concrete lifetime [E0271]
test.rs:90           .filter(|l: &_| true)
                      ^~~~~~~~~~~~~~~~~~~~
test.rs:90:12: 90:32 help: run `rustc --explain E0271` to see a detailed explanation
test.rs:91:12: 91:19 error: no method named `count` found for type `Filter<Repeat, [closure@test.rs:90:19: 90:31]>` in the current scope
test.rs:91           .count();
                      ^~~~~~~
test.rs:91:12: 91:19 note: the method `count` exists but the following trait bounds were not satisfied: `&'a mut Filter<Repeat, [closure@test.rs:90:19: 90:31]> : Stream`, `&'a mut &Filter<Repeat, [closure@test.rs:90:19: 90:31]> : Stream`, `&'a mut &mut Filter<Repeat, [closure@test.rs:90:19: 90:31]> : Stream`, `Filter<Repeat, [closure@test.rs:90:19: 90:31]> : core::iter::Iterator`
test.rs:91:12: 91:19 help: items from traits can only be used if the trait is implemented and in scope; the following traits define an item `count`, perhaps you need to implement one of them:
test.rs:91:12: 91:19 help: candidate #1: `StreamExt`
test.rs:91:12: 91:19 help: candidate #2: `core::iter::Iterator`
error: aborting due to 2 previous errors
@Mark-Simulacrum Mark-Simulacrum added A-lifetimes Area: Lifetimes / regions T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 23, 2017
@Mark-Simulacrum Mark-Simulacrum added the C-bug Category: This is a bug. label Jul 24, 2017
@pnkfelix
Copy link
Member

This is now rejected in both editions on both beta+nightly with the following diagnostic:

error: implementation of `Stream` is not general enough
  --> src/main.rs:90:22
   |
90 |     let map = source.map(|x: &_| x); // Remove to break.
   |                      ^^^
   |
   = note: `Stream` would have to be implemented for the type `&'0 mut Map<Repeat, [closure@src/main.rs:90:26: 90:35]>`, for any lifetime `'0`
   = note: but `Stream` is actually implemented for the type `&'1 mut Map<Repeat, [closure@src/main.rs:90:26: 90:35]>`, for some specific lifetime `'1`

error: aborting due to previous error

Tagging as E-needstest

@pnkfelix pnkfelix added the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Feb 27, 2019
Centril added a commit to Centril/rust that referenced this issue Jul 9, 2019
…crichton

Regression test for HRTB bug (issue 30786).

Close rust-lang#30786.
Centril added a commit to Centril/rust that referenced this issue Jul 11, 2019
…elix

Regression test for HRTB bug (issue 30786).

Close rust-lang#30786.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants