-
-
Notifications
You must be signed in to change notification settings - Fork 14.3k
Revert "Do not check privacy for RPITIT." #146470
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
base: main
Are you sure you want to change the base?
Revert "Do not check privacy for RPITIT." #146470
Conversation
This reverts commit c004a96.
|
|
|
r? cjgillot |
|
Gentle ping @cjgillot |
|
This came up in today's @rust-lang/lang meeting. It's clear why this needed an FCP (as it's a breaking change), but we didn't feel like we had the context. Could we get a clear ask for what exactly the new hard error is that we're reviewing? Does this just make it a hard error to write a public trait that has something like |
|
It is a little bit more subtle in the current form, the main weirdness I remember is that creating a required method returning a private impl trait does not error out, only providing an implementation does, so does not error while and both error out. The error is also reported when the And then for AFIT it seems to work the same after desugaring, so As I understand it, this is not as strict as it should be based on @petrochenkov's comment and even the first case of defining the trait should be rejected. Here is a playground with more cases to see what does and does not produce errors (though the errors are just comments but compiling the code on this branch should provide the stated results). To summarize, this adds errors when using a private trait in RPITIT but when the offending trait is not used in a trait bound and an implementation is not provided, there is a false negative an the error is not emitted even though it should be. |
|
@bors2 try |
This comment has been minimized.
This comment has been minimized.
…-errors, r=<try> Revert "Do not check privacy for RPITIT."
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
|
@mladedav: I'm having trouble working out the reason why we'd give a hard error for the RPIT-in-trait-impl, trait PrivTr {}
impl PrivTr for () {}
#[expect(private_bounds)]
pub trait PubTr {
fn f1() -> impl PrivTr;
}
impl<T> PubTr for T {
#[expect(private_interfaces)]
fn f1() -> impl PrivTr {}
//~^ error[E0446]: private trait `PrivTr` in public interface
//~| help: can't leak private trait
}given that we don't give an error for an RPIT-in-free-function, trait PrivTr {}
impl PrivTr for () {}
#[expect(private_interfaces)]
pub fn f2() -> impl PrivTr {} //~ OKand given that we allow the comparable associated type desugaring of the RPITIT: trait PrivTr {}
impl PrivTr for () {}
pub trait PubTr {
#[expect(private_bounds)]
type F1: PrivTr; //~ OK
fn f1() -> Self::F1;
}
impl<T> PubTr for T {
type F1 = ();
fn f1() -> Self::F1 {}
}What's the rationale here? I note that on nightly we give an error for this, when desugaring the RPIT-in-trait-impl to ATPIT: #![feature(impl_trait_in_assoc_type)]
trait PrivTr {}
impl PrivTr for () {}
pub trait PubTr {
#[expect(private_bounds)]
type F1: PrivTr; //~ OK
fn f1() -> Self::F1;
}
impl<T> PubTr for T {
type F1 = impl PrivTr;
//~^ error[E0446]: private trait `PrivTr` in public interface
fn f1() -> Self::F1 {}
}What's the rationale here? It makes sense why we can't leak a private type in this way -- we'd then be allowing a private type to be named. Why does this rise to the level of a hard error for a private trait in an impl trait bound? Also, on the PR, I notice that placing the trait PrivTr {}
impl PrivTr for () {}
pub trait PubTr {
#[expect(private_bounds)] //~ warning: this lint expectation is unfulfilled
fn f1() -> impl PrivTr;
//~^ warning: trait `PrivTr` is more private than the item `PubTr::f1::{anon_assoc#0}`
}Should it? |
|
@craterbot check |
|
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
@bors2 try |
This comment has been minimized.
This comment has been minimized.
…-errors, r=<try> Revert "Do not check privacy for RPITIT."
|
As I said in the meeting: @rfcbot reviewed This seems like a sensible extension and intuitively matches what we do for returning a value of a private struct. Longer term, I would still like to reframe our privacy rules in terms of "capabilities associated with a trait" as I described earlier, and before we actually land this I do want to see the results of the crater run, but generally convinced this is the right next step. |
|
@craterbot check |
|
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
🎉 Experiment
Footnotes
|
|
Most of the regressions are crates depending on |
|
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
We discussed this in the lang-team meeting. We agree that it isn't viable to land this as an immediat breaking change and that, if we are to do this, future-compatibility warnings would be required. I think some of us were a bit on the fence about the mental model here and seeing a lot of regressions gave us some pause; others continued to support the change, just want to be sure we approach it carefully. The main thing that is needed to finalize the decision is an analysis of the "root patterns" causing breakage. We would want to make sure that we have sensible equivalents to recommend to people. For example, I looked into the code in embassy and found this pattern: /// Implementation details for embassy macros.
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
pub mod _export {
//...
trait TaskReturnValue {}
impl TaskReturnValue for () {}
impl TaskReturnValue for Never {}
#[diagnostic::on_unimplemented(
message = "task futures must resolve to `()` or `!`",
note = "use `async fn` or change the return type to `impl Future<Output = ()>`"
)]
#[allow(private_bounds)]
pub trait TaskFn<Args>: Copy {
type Fut: Future<Output: TaskReturnValue> + 'static;
// ^^^^^^^^^^^^^^^ the error is here
}@Nadrieril pointed out that this trait could as well be a sealed trait -- seems true, although more painful, but that's because sealed traits are painful. |
|
I'll link to this comment #146470 (comment) again.
Someone needs to try and break it and leak something through various parts (Generics, Predicates, Default type, Bounds) of an associated type. (To cause a linking error, or missing encoded MIR issues, for example.) |
cc @theemathas |
|
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
🎉 Experiment
Footnotes
|
@petrochenkov How does privacy affect linking or MIR encoding? I'm guessing that an ICE would occur if there were a way to directly call a trait method defined on a private |
The changes here were first merged in #143357 and later reverted in #144098 as it introduces new hard errors. There was a crater run tracked in #144139 to see how much projects would be broken (not that many, a few repositories on github are affected).
This reenables hard errors for privacy in RPITIT.
Fixes #143531
Closes #144139
Hopefully closes #71043