Skip to content

HRTB bounds not resolving correctly, but they do work if you introduce a reference. #85636

@Manishearth

Description

@Manishearth
Member

I'm trying to write Debug impls for the yoke crate:

Non-compiling code
use std::borrow::*;
use std::fmt;

pub trait Yokeable<'a>: 'static {
    type Output: 'a;
}

impl<'a, T: 'static + ToOwned> Yokeable<'a> for Cow<'static, T> {
    type Output = Cow<'a, T>;
}

pub struct Yoke<Y: for<'a> Yokeable<'a>> {
    y: Y
}

impl<Y: for<'a> Yokeable<'a>> Yoke<Y> {
    pub fn get<'a>(&'a self) -> &'a <Y as Yokeable<'a>>::Output {
        unimplemented!()
    }
}

impl<Y> fmt::Debug for Yoke<Y> where Y: for<'a> Yokeable<'a>, for<'a> <Y as Yokeable<'a>>::Output: fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.get().fmt(f)
    }
}

// impl<Y> fmt::Debug for Yoke<Y> where Y: for<'a> Yokeable<'a>, for<'a> &'a <Y as Yokeable<'a>>::Output: fmt::Debug {
//     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
//         self.get().fmt(f)
//     }
// }

fn format_yoke(x: Yoke<Cow<'static, u8>>) {
    println!("{:?}", x)
}

(playpen)

This code fails to compile:

error[E0277]: `<std::borrow::Cow<'_, u8> as Yokeable<'a>>::Output` doesn't implement `Debug`
  --> src/main.rs:35:22
   |
35 |     println!("{:?}", x)
   |                      ^ `<std::borrow::Cow<'_, u8> as Yokeable<'a>>::Output` cannot be formatted using `{:?}` because it doesn't implement `Debug`
   |
   = help: the trait `for<'a> Debug` is not implemented for `<std::borrow::Cow<'_, u8> as Yokeable<'a>>::Output`
   = note: required because of the requirements on the impl of `Debug` for `Yoke<std::borrow::Cow<'_, u8>>`
   = note: required by `std::fmt::Debug::fmt`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

However, swapping the for<'a> <Y as Yokeable<'a>>::Output: fmt::Debug bound with for<'a> &'a <Y as Yokeable<'a>>::Output: fmt::Debug has it start compiling again.

Compiling code
use std::borrow::*;
use std::fmt;

pub trait Yokeable<'a>: 'static {
    type Output: 'a;
}

impl<'a, T: 'static + ToOwned> Yokeable<'a> for Cow<'static, T> {
    type Output = Cow<'a, T>;
}

pub struct Yoke<Y: for<'a> Yokeable<'a>> {
    y: Y
}

impl<Y: for<'a> Yokeable<'a>> Yoke<Y> {
    pub fn get<'a>(&'a self) -> &'a <Y as Yokeable<'a>>::Output {
        unimplemented!()
    }
}

// impl<Y> fmt::Debug for Yoke<Y> where Y: for<'a> Yokeable<'a>, for<'a> <Y as Yokeable<'a>>::Output: fmt::Debug {
//     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
//         self.get().fmt(f)
//     }
// }

impl<Y> fmt::Debug for Yoke<Y> where Y: for<'a> Yokeable<'a>, for<'a> &'a <Y as Yokeable<'a>>::Output: fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.get().fmt(f)
    }
}

fn format_yoke(x: Yoke<Cow<'static, u8>>) {
    println!("{:?}", x)
}

(playpen)

Note that Yoke::get() does not need to exist to reproduce this bug, however it helps motivate why an HRTB bound is necessary.

It seems like both cases should successfully compile, all Cow<u8>s implement Debug.

cc @tmandry @eddyb

Activity

Manishearth

Manishearth commented on Jun 19, 2021

@Manishearth
MemberAuthor

Interestingly, if you want this to work for Clone, you can just introduce #[derive(Clone)] struct Wrap<T>(T); and use that instead of &T. It's probably similar for other traits that are not using Self as the receiver.

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

Manishearth

Manishearth commented on Jun 19, 2021

@Manishearth
MemberAuthor

I need some further trickery to make it work for Clone on Yoke but this works for me:

#[derive(Clone)]
#[repr(transparent)]
pub struct Wrap<T>(T);

impl<T> Wrap<T> {
    fn wrap(t: &T) -> &Self {
        unsafe { std::mem::transmute(t) }
    }
}

/// Clone requires that the cart derefs to the same address after it is cloned. This works for Rc, Arc, and &'a T.
/// For all other cart types, clone `.baking_cart()` and re-use `attach_to_cart()`.
impl<Y: for<'a> Yokeable<'a>, T: ?Sized> Clone for Yoke<Y, Rc<T>>
where
    for<'a> Wrap<<Y as Yokeable<'a>>::Output>: Clone,
{
    fn clone(&self) -> Self {
        Yoke {
            yokeable: unsafe { Y::make(Wrap::wrap(self.get()).clone().0) },
            cart: self.cart.clone(),
        }
    }
}
Manishearth

Manishearth commented on Jul 23, 2021

@Manishearth
MemberAuthor

@jackh726 btw, #85499 seems to fix this, care to add a testcase?

jackh726

jackh726 commented on Aug 26, 2021

@jackh726
Member

Fixed by #85499, closing since #56556 has a similar test

sffc

sffc commented on Sep 23, 2021

@sffc

Not completely fixed yet. Here is a different test case that still reproduces the error 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

10 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @sffc@Manishearth@jackh726

        Issue actions

          HRTB bounds not resolving correctly, but they do work if you introduce a reference. · Issue #85636 · rust-lang/rust