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

Trait bounds on associated type projections via HRTB are broken #56556

Closed
sfackler opened this issue Dec 6, 2018 · 14 comments · Fixed by #90017 or #96329
Closed

Trait bounds on associated type projections via HRTB are broken #56556

sfackler opened this issue Dec 6, 2018 · 14 comments · Fixed by #90017 or #96329
Labels
A-lazy-normalization Area: Lazy normalization (tracking issue: #60471) A-trait-system Area: Trait system A-type-system Area: Type system C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@sfackler
Copy link
Member

sfackler commented Dec 6, 2018

I believe this should successfully compile, but it currently fails claiming that vec's iterator isn't ExactSizeIterator:

fn foo<T>(t: T) -> usize
where
    for<'a> &'a T: IntoIterator,
    for<'a> <&'a T as IntoIterator>::IntoIter: ExactSizeIterator,
{
    t.into_iter().len()
}

fn main() {
    foo::<Vec<u32>>(vec![]);
}
error[E0277]: the trait bound `for<'a> <&'a std::vec::Vec<u32> as std::iter::IntoIterator>::IntoIter: std::iter::ExactSizeIterator` is not satisfied
  --> src/main.rs:10:5
   |
10 |     foo::<Vec<u32>>(vec![]);
   |     ^^^^^^^^^^^^^^^ the trait `for<'a> std::iter::ExactSizeIterator` is not implemented for `<&'a std::vec::Vec<u32> as std::iter::IntoIterator>::IntoIter`
   |
   = help: the following implementations were found:
             <&'a mut I as std::iter::ExactSizeIterator>
note: required by `foo`
  --> src/main.rs:1:1
   |
1  | / fn foo<T>(t: T) -> usize
2  | | where
3  | |     for<'a> &'a T: IntoIterator,
4  | |     for<'a> <&'a T as IntoIterator>::IntoIter: ExactSizeIterator,
5  | | {
6  | |     t.into_iter().len()
7  | | }
   | |_^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.

Things compile correctly if the bounds are changed to T rather than for<'a> &'a T.

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

@Centril Centril changed the title Trait bounds on associated type projections via HKT are broken Trait bounds on associated type projections via HRTB are broken Dec 6, 2018
@Centril
Copy link
Contributor

Centril commented Dec 6, 2018

It does seem like it should compile... cc @rust-lang/wg-traits

@Centril Centril added A-type-system Area: Type system A-trait-system Area: Trait system labels Dec 6, 2018
@pnkfelix pnkfelix added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Dec 7, 2018
@jonhoo
Copy link
Contributor

jonhoo commented Jan 16, 2019

Another instance of this from #57671:

trait Bar<T> {
    type Assoc;
}
impl<'a> Bar<&'a ()> for () {
    type Assoc = Box<Send>;
}

fn oops<C>()
where
    for<'a> C: Bar<&'a ()>,
    for<'a> <C as Bar<&'a ()>>::Assoc: Send,
{
}

fn main() {
    oops::<()>();
}

fails with

error[E0277]: `<() as Bar<&'a ()>>::Assoc` cannot be sent between threads safely
  --> src/lib.rs:16:5
   |
16 |     oops::<()>();
   |     ^^^^^^^^^^ `<() as Bar<&'a ()>>::Assoc` cannot be sent between threads safely
   |
   = help: the trait `for<'a> std::marker::Send` is not implemented for `<() as Bar<&'a ()>>::Assoc`
note: required by `oops`
  --> src/lib.rs:8:1
   |
8  | / fn oops<C>()
9  | | where
10 | |     for<'a> C: Bar<&'a ()>,
11 | |     for<'a> <C as Bar<&'a ()>>::Assoc: Send,
12 | | {
13 | | }
   | |_^

@jonhoo
Copy link
Contributor

jonhoo commented Jan 17, 2019

@sfackler do you know of a workaround for this bug? It's currently blocking me and I'm not entirely sure how else to structure my code.

@sfackler
Copy link
Member Author

sfackler commented Jan 17, 2019 via email

@Centril Centril added the C-bug Category: This is a bug. label Jan 17, 2019
@devyn
Copy link

devyn commented Sep 16, 2019

I believe this issue I ran in to might be related - StackOverflow.

I was trying to put a bound on an associated type Key from two different types and require them to be PartialOrd, but Rust always just wanted to assume the types must be the same. By creating a separate trait KeyedCmp that is implemented without relying on an HRTB, and then using the HRTB around that trait, it did compile. I'm fairly sure this is a bug. rustc ought to still be able to resolve what the associated type actually is and then evaluate traits on that. It seems like it's trying to look for implementations on a hypothetical associated type black box rather than the real type it points to.

@Lucretiel
Copy link
Contributor

Another example, when trying to bind for <'a> <&'a C as IntoIterator>::Item: Display:

https://stackoverflow.com/questions/53364798/why-does-the-compiler-claim-that-a-generic-doesnt-implement-display-even-thou

@Lucretiel
Copy link
Contributor

Ran into this in one of my own projects, and used this odd workaround (suggested on satck overflow): https://github.com/Lucretiel/joinery/blob/master/src/join.rs#L174-L188

allen-marshall pushed a commit to allen-marshall/atelier-assets that referenced this issue Feb 25, 2020
This is a workaround for a Rust compiler bug/limitation: rust-lang/rust#56556
@roosmaa
Copy link

roosmaa commented Sep 7, 2020

Another variation of this issue (as far as I can tell):

trait Bar<'a> {
    type Assoc;
}

impl<'a> Bar<'a> for () {
    type Assoc = ();
}

fn oops<C>()
where
    for<'a> C: Bar<'a>,
    for<'a> <C as Bar<'a>>::Assoc: Send,
{
}

fn main() {
    oops::<()>();
}

Fails with:

error[E0277]: `<() as Bar<'a>>::Assoc` cannot be sent between threads safely
  --> src/main.rs:17:5
   |
9  | fn oops<C>()
   |    ---- required by a bound in this
...
12 |     for<'a> <C as Bar<'a>>::Assoc: Send,
   |                                    ---- required by this bound in `oops`
...
17 |     oops::<()>();
   |     ^^^^^^^^^^ `<() as Bar<'a>>::Assoc` cannot be sent between threads safely
   |
   = help: the trait `for<'a> std::marker::Send` is not implemented for `<() as Bar<'a>>::Assoc`

@matthewjasper matthewjasper added the A-lazy-normalization Area: Lazy normalization (tracking issue: #60471) label Sep 19, 2020
@H2CO3
Copy link

H2CO3 commented Jun 1, 2021

I just ran into this (playground):

#[derive(Clone, Copy, Debug)]
struct Value<'a> {
    num: NonZeroU64,
    marker: PhantomData<&'a ()>,
}

impl<'a> TryFrom<u64> for Value<'a> {
    type Error = TryFromIntError;
    
    fn try_from(value: u64) -> Result<Self, Self::Error> {
        NonZeroU64::try_from(value).map(|num| Value { num, marker: PhantomData })
    }
}

trait ToValue {
    fn to_value(&self) -> Result<Value, ValueError>;
}

#[derive(Clone, Copy, Default, Debug)]
struct ValueError;

impl From<TryFromIntError> for ValueError {
    fn from(_: TryFromIntError) -> Self {
        ValueError
    }
}

impl<T> ToValue for T
    where
        T: Clone,
        T: for<'a> TryInto<Value<'a>>,
        ValueError: for<'a> From<<T as TryInto<Value<'a>>>::Error>,
{
    fn to_value(&self) -> Result<Value, ValueError> {
        self.clone().try_into().map_err(Into::into)
    }
}

fn main() {
    let v: Value = 42_u64.to_value().unwrap();
    println!("{:#?}", v);
}

I get the following error on latest stable (1.52.1):

error[E0599]: the method `to_value` exists for type `u64`, but its trait bounds were not satisfied
  --> src/main.rs:44:27
   |
24 | struct ValueError;
   | ------------------ doesn't satisfy `_: From<<u64 as TryInto<Value<'a>>>::Error>`
...
44 |     let v: Value = 42_u64.to_value().unwrap();
   |                           ^^^^^^^^ method cannot be called on `u64` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `ValueError: From<<u64 as TryInto<Value<'a>>>::Error>`
           which is required by `u64: ToValue`

The workaround in my case was to pull out the associated type into a type parameter, and require that it be invariant w.r.t. the universally quantified lifetime parameter (playground):

impl<T, E> ToValue for T
    where
        T: Clone,
        T: for<'a> TryInto<Value<'a>, Error = E>,
        ValueError: From<E>,
{
    fn to_value(&self) -> Result<Value, ValueError> {
        self.clone().try_into().map_err(Into::into)
    }
}

bors added a commit to rust-lang-ci/rust that referenced this issue Aug 25, 2021
…komatsakis

Normalize projections under binders

Fixes rust-lang#70243
Fixes rust-lang#70120
Fixes rust-lang#62529
Fixes rust-lang#87219

Issues to followup on after (probably fixed, but no test added here):
rust-lang#76956
rust-lang#56556
rust-lang#79207
rust-lang#85636

r? `@nikomatsakis`
@jackh726
Copy link
Member

Fixed by #85499, marking as needs-test because wasn't an ICE and a simple enough example I'd like to have a test for.

@sffc
Copy link

sffc commented Sep 23, 2021

This bug might not be completely fixed. Here is another test case that still fails on nightly and beta:

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

EDIT: Made the case a bit smaller:

https://play.rust-lang.org/?version=beta&mode=debug&edition=2018&gist=025e2d4cefe09821650352a935b0baed

@fee1-dead
Copy link
Member

this is the smallest example I can find:

This below works:

fn test<T>()
where
    for<'a> &'a T: IntoIterator,
    for<'a> <&'a T as IntoIterator>::IntoIter: Clone,
{
}

fn main() {
    test::<Vec<u8>>();
}

While this doesn't:

use std::ops::Deref;

fn test<T, TDeref>()
where
    T: Deref<Target = TDeref>,
    TDeref: ?Sized,
    for<'a> &'a TDeref: IntoIterator,
    for<'a> <&'a TDeref as IntoIterator>::IntoIter: Clone,
{
}

fn main() {
    test::<Vec<u8>, _>();
}

@jackh726 Can you confirm that the second case works in your PR as well?

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Oct 25, 2021
Add a couple tests for normalize under binder issues

Closes rust-lang#56556
Closes rust-lang#76956

r? `@nikomatsakis`
@bors bors closed this as completed in 2d85c7e Oct 25, 2021
@fee1-dead
Copy link
Member

fee1-dead commented Nov 3, 2021

this is still not fixed on latest nightly.

cc @jackh726

EDIT: I suspect that this example is some form of #89196.

@fee1-dead fee1-dead reopened this Nov 3, 2021
@JohnTitor JohnTitor removed the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Nov 15, 2021
@Manishearth
Copy link
Member

Another bug of this kind: #90950

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue May 17, 2022
Add a couple tests for rust-lang#90887 fixes

closes rust-lang#56556
closes rust-lang#90875

These are confirmed fixes by rust-lang#90887, so
r? `@jackh726`
@bors bors closed this as completed in 15aa2d6 May 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lazy-normalization Area: Lazy normalization (tracking issue: #60471) A-trait-system Area: Trait system A-type-system Area: Type system C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet