private_in_public lint triggered for pub associated type computed using non-pub trait

092faa9
Opened by scottmcm at 2024-06-06 15:37:52

I was surprised to get a private_in_public deprecation warning for using a private trait to compute an associated type that's itself pub.

De-macro'd, shortened example of what I was doing:

#![feature(try_from)]

trait Bar {
    type Inner;
}

pub struct B16(u16);
pub struct B32(u32);

impl Bar for B16 {
    type Inner = u16;
}
impl Bar for B32 {
    type Inner = u32;
}

use std::convert::{TryFrom, TryInto};
impl TryFrom<B32> for B16 {
    type Error = <<B16 as Bar>::Inner as TryFrom<<B32 as Bar>::Inner>>::Error;
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//  warning: private type `<B16 as Bar>::Inner` in public interface (error E0446)
//  (Despite the fact that that actual type is core::num::TryFromIntError)
    fn try_from(x: B32) -> Result<Self, Self::Error> {
       Ok(B16(x.0.try_into()?))
    }
}

fn main() {}

Repro: https://play.rust-lang.org/?gist=1f84c630e07ddd54d2bf208aa85ed8bb&version=nightly

I don't understand how that type is part of the public interface, since I can't get to it using TryFrom.

(Do close if this is known and covered by things like https://github.com/rust-lang/rfcs/pull/1671#issuecomment-268422405, but I couldn't find any issues talking about this part at least.)

  1. This is a limitation of implementation, not some language privacy rule.

    From RFC 2145:

    For technical reasons it's not always desirable or possible to fully normalize associated types before checking them for privacy. So, if we see <Type as Trait>::AssocType we can guaranteedly check only Type and Trait, but not the resulting type.

    That's exactly the case here, the type turns out to not be normalized enough before privacy checking. The reason lies in parts of the compiler totally unrelated to privacy and I didn't dig into why exactly this happens (types are not fully normalized in signatures). Maybe we should somehow force normalization in the privacy pass, or do some custom normalization, I don't know. Pinging @nikomatsakis for more information.

    Vadim Petrochenkov at 2017-11-18 19:16:53

  2. ping @lcnr

    I strongly suspect that we have some "fully normalize" type operation now that would work in signatures and would help us to eliminate all intermediate (associated) type aliases from types during privacy checking.

    (I.e. going from <<B16 as Bar>::Inner as TryFrom<<B32 as Bar>::Inner>>::Error to core::num::TryFromIntError in the example above.)

    If you could point to the right tools, then we could eliminate this piece of technical debt from privacy checking.

    Vadim Petrochenkov at 2023-06-16 15:06:12

  3. https://github.com/rust-lang/rust/pull/113671 is starting to address this issue.

    Vadim Petrochenkov at 2023-07-13 20:58:29

  4. This is affecting zerocopy: https://github.com/google/zerocopy/issues/1292

    Joshua Liebow-Feeser at 2024-05-20 20:40:15

  5. ping @lcnr

    I strongly suspect that we have some "fully normalize" type operation now that would work in signatures and would help us to eliminate all intermediate (associated) type aliases from types during privacy checking.

    (I.e. going from <<B16 as Bar>::Inner as TryFrom<<B32 as Bar>::Inner>>::Error to core::num::TryFromIntError in the example above.)

    If you could point to the right tools, then we could eliminate this piece of technical debt from privacy checking.

    If you're dealing with ty::Ty now, you can normalize them by creating an ObligationCtxt and then using ocx.deelpy_normalize(whatever) to get a fully normalized version of whatever.

    lcnr at 2024-05-20 20:46:29

  6. I've put up a PR: https://github.com/rust-lang/rust/pull/126076

    Jack Wrenn at 2024-06-03 19:10:57