Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upSemantic "private in public" enforcement. #1671
Conversation
This was referenced Jul 9, 2016
petrochenkov
reviewed
Jul 9, 2016
| # Motivation | ||
| [motivation]: #motivation | ||
|
|
||
| The "private-in-public" rules ensure the transitivity of abstraction. That is, one must be able to name types and bounds used in public APIs, from anywhere else. |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Jul 9, 2016
•
Contributor
"able to name" is not entirely correct, "able to name after addition of arbitrary number of reexports" is closer to the truth. E.g.
mod m {
mod detail {
pub struct S;
}
pub fn f() -> detail::S { detail::S }
}
is allowed because S is potentially nameable outside of m (if reexported), but not actually nameable (private-in-public checker doesn't analyze reexport chains to determine actual nameability by design).
I'd like to avoid promising namebility in docs, including RFCs, because it's already one of the most common misconceptions about our privacy system.
EDIT: The definition of "less public" below also uses nameability ("can be referred to by any name").
This comment has been minimized.
This comment has been minimized.
eddyb
Jul 9, 2016
Author
Member
Ah, I remember reading about this before, but I wasn't clear on what decisions were taken.
It does weaken the "transitivity of abstraction" argument in that case, sadly.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Jul 9, 2016
| type MyResult<T> = Result<T, String>; | ||
| #[derive(Clone)] | ||
| struct Wrap<T>(T); |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Jul 9, 2016
•
Contributor
The basic idea behind the private-in-public checker is that values of private types (in addition to types themselves) can't leave their module.
Can a public function/method be tricked into returning a private type (with traits + associated types + type parameters + whatever) if it's used in one of its predicates like this PrivateType: Bounds?
Also, where can I read something about predicate elaboration?
This comment has been minimized.
This comment has been minimized.
eddyb
Jul 9, 2016
Author
Member
The return type is in the signature and checked as part of that, regardless of what happens with bounds.
With this RFC you can still always write a perfect proxy for functions, methods, impls, etc.
"Elaboration" as used in this RFC is described in the detailed design section. I should probably be more specific about it because it only does the work necessary to rewrite private bounds in terms of more public ones.
This comment has been minimized.
This comment has been minimized.
petrochenkov
Jul 9, 2016
Contributor
Here's an example of leaking a private type through bounds, the returned type T::Alias seems to be completely public if checked separately. I wonder if something like this can be constructed with PrivateWrap<T>: Bound-like bounds. (I fully support the goals of the RFC, just trying to figure out possible holes.)
pub trait Tr<T> {
type Alias = T;
}
impl<T, U> Tr<T> for U {}
struct Priv;
pub fn f<T>() -> T::Alias
where T: Tr<Priv>
{
panic!()
}
This comment has been minimized.
This comment has been minimized.
eddyb
Jul 9, 2016
Author
Member
After associated type resolution, the return type of f is Priv and that doesn't pass the check.
Even if it didn't resolve to any concrete type, the return type would be <T as Tr<Priv>>::Alias which still contains Priv so it doesn't pass the check either.
This comment has been minimized.
This comment has been minimized.
petrochenkov
Jul 9, 2016
•
Contributor
By the way, if you do the T::Alias -> <T as Tr<Priv>>::Alias transformations before checking, then predicates can become completely unchecked, because nothing private can be leaked through them1.
This is more significant relaxation of the rules, but it is simpler and removes the need in the elaboration procedure.
We discussed this with @pnkfelix last year and both were generally in favor.
1"nothing private can be leaked" is the core guarantee from the current privacy system, as opposed to "nameability" from the old system.
This comment has been minimized.
This comment has been minimized.
eddyb
Jul 9, 2016
Author
Member
Well, that does reduce this RFC to "check types after normalization", I wish that was better advertised as an option (it seems perhaps even simpler than the current implementation).
This comment has been minimized.
This comment has been minimized.
|
I want to have this outside of diff comments too: @petrochenkov pointed out that name, type alias, UFCS and associated type resolution combined (i.e. everything the compiler does to go from the syntactic to the semantic type-system) are enough to reveal all possible private type leaks in public APIs. I agree this would greatly simplify the RFC and the implementation work and allow deriving to work even in dubious situations where the set of implementations available to a bound is completely hidden. Does anyone else (e.g. from @rust-lang/lang) have anything against that? |
This comment has been minimized.
This comment has been minimized.
|
@eddyb sounds great to me! I'd ideally like the docs to evaluate/resolve types as little as possible---just enough not leak. Maybe we can someday leverage incremental compilation for this. (Type language is pure and thus can be safely lazily evaluated.) |
This comment has been minimized.
This comment has been minimized.
The main argument against this change probably lies outside of the language - documentation. |
This comment has been minimized.
This comment has been minimized.
I should write this out in more detail. The guarantee is that entities with visibility
|
nrc
added
the
T-lang
label
Jul 10, 2016
nrc
assigned
eddyb
Jul 11, 2016
eddyb
referenced this pull request
Jul 11, 2016
Closed
Generated code triggers private_in_public future compatibility warnings. #435
This comment has been minimized.
This comment has been minimized.
crumblingstatue
commented
Jul 12, 2016
|
Does this aim to address rust-lang/rust#30905? // This type is inacessible outside of the crate, but "public" to the entire
// crate, since it's at the crate root.
struct CratePrivType;
mod private {
// This function is inacessible outside of the crate, so it should be okay
// to return a crate-private type
pub fn gimme_crate_priv() -> super::CratePrivType { unimplemented!() }
}
fn main() {}
To work around this without exposing the crate-private type, one can put it in a private module. mod crate_priv {
pub struct CratePrivType;
}
mod private {
pub fn gimme_crate_priv() -> ::crate_priv::CratePrivType { unimplemented!() }
}
fn main() {}
But this once again shows the stupidity of the current analysis. mod crate_priv {
pub struct CratePrivType;
}
mod private {
pub fn gimme_crate_priv() -> ::crate_priv::CratePrivType { unimplemented!() }
}
pub fn pub_api_func() -> ::crate_priv::CratePrivType { unimplemented!() }
fn main() {}
Even rustdoc gets confused by this, and the crate private type will not get documented, even though |
This comment has been minimized.
This comment has been minimized.
|
@crumblingstatue We could restrict the check to the same module, which would allow that code to compile. |
This comment has been minimized.
This comment has been minimized.
|
@crumblingstatue
Playpen: https://is.gd/TFqG8o |
This comment has been minimized.
This comment has been minimized.
crumblingstatue
commented
Jul 12, 2016
|
@petrochenkov You're right, pub(restricted) does solve the issue I was having. Thanks, and apologies for invading this thread. |
eddyb
added
the
I-nominated
label
Jul 13, 2016
This comment has been minimized.
This comment has been minimized.
|
Alternative proposal on discuss (also from @eddyb): https://internals.rust-lang.org/t/rfc-reduce-false-positives-for-private-type-in-public-interface/3678 |
This comment has been minimized.
This comment has been minimized.
|
cc @pnkfelix do you remember the ins and outs of this from the last RFC discussion? |
eddyb
referenced this pull request
Jul 25, 2016
Closed
rustc should warn about private types in re-exported interfaces #35005
nikomatsakis
removed
the
I-nominated
label
Aug 4, 2016
petrochenkov
referenced this pull request
Sep 22, 2016
Closed
Deriving Clone in structs with unions inside expect type parameters to be Clone, unlike structs. #36640
petrochenkov
referenced this pull request
Nov 8, 2016
Open
Tracking issue for `private_in_public` compatibility lint. #34537
This comment has been minimized.
This comment has been minimized.
|
ping @eddyb status? |
This comment has been minimized.
This comment has been minimized.
|
@ubsan See the latest comments on rust-lang/rust#34537. We're more likely to switch to a different scheme, checking types as they appear after inference, as opposed to only checking direct uses. |
This comment has been minimized.
This comment has been minimized.
|
@eddyb sounds like a close to me. Or at least a rewrite. |
This comment has been minimized.
This comment has been minimized.
|
@ubsan Oh right, it's my PR so I can just do this. |
eddyb commentedJul 9, 2016
Rendered