rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.87k stars 12.52k forks source link

Tracking issue for RFC 1977: public & private dependencies #44663

Open withoutboats opened 7 years ago

withoutboats commented 7 years ago

Summary

RFC (original, superseded): #1977 RFC: #3516 Cargo tracking issue: https://github.com/rust-lang/cargo/issues/6129 Issues: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+label%3AF-public_private_dependencies Cargo issues: https://github.com/rust-lang/cargo/issues?q=is%3Aopen+is%3Aissue+label%3AZ-public-dependency Documentation: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#public-dependency

This feature enables the ability to track which dependencies are publicly exposed through a library's interface. It has two sides to the implementation: rustc (lint and --extern flag), and cargo (Cargo.toml syntax, and passing --extern flags).

This feature was originally specified in rust-lang/rfcs#1977, but was later down-scoped in rust-lang/rfcs#3516.

About tracking issues

Tracking issues are used to record the overall progress of implementation. They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions. A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature. Instead, open a dedicated issue for the specific matter and add the relevant feature gate label. Discussion comments will get marked as off-topic or deleted. Repeated discussions on the tracking issue may lead to the tracking issue getting locked.

Unresolved Questions

Steps

Non-blocking further improvements

Changes from RFC

cc @rust-lang/cargo @epage

Boscop commented 6 years ago

Maybe it's useful to look at how this problem is approached in Haskell. Here are some relevant slides: https://wiki.haskell.org/wikiupload/b/b4/HIW2011-Talk-Loeh.pdf

Here is an example of a subtle issue that occurs with private dependencies:

Btw, I found it through this post: http://harry.garrood.me/blog/purescript-why-bower/

Eh2406 commented 6 years ago

Would Rust2018 be a good opportunity to make the braking changes to Cargo.toml?

SimonSapin commented 6 years ago

Maybe. Though such changes should be designed and proposed soon, have an easy migration path, and preferably only affect a minority of existing crates.

Nemo157 commented 5 years ago

Thought I just had related to ecosystems like futures that provide a "core" stable crate along with less stable utilities re-exported from a "facade": items from a public dependency re-exported through a private dependency should be treated as public. (This is likely how the implementation would behave anyway, but having a testcase for this would be nice). Basically how I see using a tightly coupled ecosystem like that work is that you would declare both the "facade" crate and "core" crate as dependencies, but only the "core" crate as public:

[dependencies]
futures = { version = "10.17", public = false }
futures-core = { version = "1.2", public = true }

But then, in code you would never import items from the "core" crate, instead you would simply have public APIs that return types which have been re-exported through the "facade" from the "core" crate and rely on the compiler to warn you if you accidentally use something that didn't originate in the "core" crate.

Eh2406 commented 5 years ago

I don't quite understand something about your example. Can you elaborate on what the Toml and the code in the futures crate look like?

Nemo157 commented 5 years ago

@Eh2406 the setup's a little annoying to just explain, so here's a full compiling set of example crates: https://github.com/Nemo157/rust-44663-facade-example

In this example futures-core exports a single trait Future, futures re-exports this trait from futures-core along with a "utility" Foo.

Now mylib wants to use futures, including returning some Futures from its public API. But futures itself intends to have breaking changes to update utilities, users that care about interoperability should only use types from futures-core in their public APIs. Even as new major versions of futures are released they will all link against the same version of futures-core so the underlying traits will be interoperable.

So, to have the compiler enforce this restriction the developers of mylib want to use the public/private dependencies feature, they have both futures and futures-core as dependency but only futures-core marked as public. But for ergonomics it's nicer to just use the re-exports directly from futures, instead of having to remember which parts of the API need to be imported from futures and which from futures-core.

Aaron1011 commented 5 years ago

I'd like to work on this.

Eh2406 commented 5 years ago

Thanks for offering to work on this! @alexcrichton, do you have any mentoring advice?

I can at any time get a branch ready for the resolver part of cargo, it will be good enough to start experimentation, although not good enough to be ready to stabilize.

alexcrichton commented 5 years ago

Sure! @Aaron1011 so this is split roughly into two features, one being rustc knowing what crates are in the "public interface" and a second being the support in Cargo's resolver to make various decisions differently. Are you interested in one of those in particular?

Aaron1011 commented 5 years ago

@alexcrichton: I've started working on the rustc part in a branch: https://github.com/Aaron1011/rust/commits/feature/pub-priv-dep, and I plan to submit a (WIP) PR soon. I might also be interested in working on the cargo part as well.

Eh2406 commented 5 years ago

@Aaron1011 please cc me when you make that PR. I know nothing about the internals of Rust, so will be of no help, but I do want to follow the discussion.

I will redouble my efforts to get the Cargo's resolver part mergeable. I have been enjoying ( and hating ) it because it is the hardest algorithmic problem I have ever worked on. I would be happy for some help, perhaps a new perspective will get me un-stuck.

alexcrichton commented 5 years ago

@Aaron1011 ok and it seems like you're off to a great start! I'd be up for reviewing that PR, and if you've got any questions along the way just let me know

Nemo157 commented 5 years ago

There was another usecase for this mentioned on i.rl.o, if cargo doc had a form of "local development" mode to build the documentation you care about when working on a crate, this would allow that mode to know exactly which dependencies it needs to produce the documentation for (all direct dependencies + their public dependencies).

Similarly, if you're self-hosting documentation for a crate somewhere you may want to be able to generate a documentation bundle including the crate and all its public dependencies so that it is fully self-contained.

petrochenkov commented 5 years ago

The current command line syntax for passing private and public extern crates to rustc is

--extern-private name=PATH
--extern name=PATH

(this differs from what was written in the RFC).

I think the syntax would benefit from being slightly more general

--extern:modifier name=PATH

with private and public extern crates being written like this

--extern:priv name=PATH
--extern:pub name=PATH
--extern name=PATH // OK, `pub` is the default

(We may also want other modifiers, or multiple modifiers (--extern:m1:m2) in the future.)

Aaron1011 commented 5 years ago

@petrochenkov Do you want me to modify my PR to use that syntax instead?

petrochenkov commented 5 years ago

@Aaron1011 A separate PR would probably be better, this is a novel syntax in general, it may require an FCP etc.

Aaron1011 commented 5 years ago

The Cargo frontend of the implementation has been merged

Things still left to do:

Eh2406 commented 5 years ago

The discussion of the first point is continuing at https://github.com/rust-lang/cargo/issues/6129#issuecomment-464845052

minimal versions is being tracked at https://github.com/rust-lang/cargo/issues/5657. It is implemented, but is not wide enough used to be stabilized as a cli flag. I doubt we can add it to cargo publish, without a magere rethink.

ehuss commented 5 years ago

This is now available on the latest nightly (2019-05-07) if anyone wants to experiment with it (docs). Just add cargo-features = ["public-dependency"] to the top of Cargo.toml of your project to get things started. You can then look at the warnings produced by rustc to understand which dependencies are exposed in your public API.

dekellum commented 5 years ago

I like this feature! The produced warning messages were very clear. In 1 out of 5 crates trialed, I decided based on the results to make 2 dependencies private that were needlessly public. This makes me think there is value and wonder if there is an opportunity to stabilize just the information collection part of this sooner (public dependency attribute, rustc warnings, uploads to the crates.io index) and the dependency resolution changes (warnings or any resolution errors) later? In particular, wouldn't that allow progress on information collection and utility without serializing on rust-lang/cargo#5657? Or perhaps the tracking details just need to be updated here?

Some additional minor feedback below:

% rustc --version
rustc 1.36.0-nightly (a784a8022 2019-05-09)
% cargo --version
cargo 1.36.0-nightly (759b6161a 2019-05-06)
cargo doc --no-deps --open
 Documenting barc v1.2.0 (/home/david/src/body-image/barc)
error: Unrecognized option: 'extern-private'

error: Could not document `barc`.

Caused by:
  process didn't exit successfully: `rustdoc --edition=2018 --crate-name barc barc/src/lib.rs --color always -o /home/david/src/body-image/target/doc --cfg 'feature="body-image"' --cfg 'feature="brotli"' --cfg 'feature="default"' --cfg 'feature="memmap"' --cfg 'feature="mmap"' --cfg 'feature="olio"' -L dependency=/home/david/src/body-image/target/debug/deps --extern body_image=/home/david/src/body-image/target/debug/deps/libbody_image-f6307513eafde4da.rmeta --extern-private brotli=/home/david/src/body-image/target/debug/deps/libbrotli-914be93b87d00a74.rmeta --extern-private bytes=/home/david/src/body-image/target/debug/deps/libbytes-60c7a2c9551c05c4.rmeta --extern-private flate2=/home/david/src/body-image/target/debug/deps/libflate2-3410ad175205616a.rmeta --extern http=/home/david/src/body-image/target/debug/deps/libhttp-1bcf97f938c6fdd7.rmeta --extern-private httparse=/home/david/src/body-image/target/debug/deps/libhttparse-b04e539fbadba356.rmeta --extern-private memmap=/home/david/src/body-image/target/debug/deps/libmemmap-b29e83fdba55bd43.rmeta --extern-private mime=/home/david/src/body-image/target/debug/deps/libmime-df20e72307836353.rmeta --extern-private olio=/home/david/src/body-image/target/debug/deps/libolio-c7a6754af0be8b56.rmeta --extern-private tao_log=/home/david/src/body-image/target/debug/deps/libtao_log-7da4b359386cbcf7.rmeta --extern-private tempfile=/home/david/src/body-image/target/debug/deps/libtempfile-5c5a144b50e3000b.rmeta -Z unstable-options --cfg barc_std_try_from` (exit code: 1)

I'm happy to elaborate here or elsewhere if its useful.

Aaron1011 commented 5 years ago

If you label something public=true that isn't actually publicly exposed, you currently don't get a warning. Would a warning or lint for that case be an appropriate future addition?

That sounds like a good idea to me!

Implementing that will be a little tricky. Currently, rustc isn't explicitly aware of the underlying public-dependency feature - it simply treats all dependencies as public by default. We would want to avoid showing 'unecessary public dependency' warnings to users who don't have the public-dependency feature enabled, as these warnings would be both useless and unfixable.

We would probably need to add a -Z public-dependencies flag to rustc to ensure that we only enable the lint when the cargo feature is also enabled.

Is it necessary to make another RFC for something like this?

dekellum commented 5 years ago

Or the alternative "--extern(:pub|:private)? name=PATH" syntax proposed by @petrochenkov could also get rustc that detail, if it chose to preserve the distinction for this? It sounds like an implementation detail to me, but what do I know! ☺

dekellum commented 5 years ago

I just tried enabling this feature on another lib crate (also for the purpose of collecting information on public deps) and noticed that examples/*.rs are another interesting edge case. If the examples define any pub items (here really just for purpose of example) that reference the lib crate types, these will result in warnings of the pattern:

warning: type `Foo` from private dependency 'libcrate' in public interface

This is because each example is compiled, including as part of cargo test, with --extern-private libcrate=... Note this dependency isn't written in the Cargo.toml, its just implied for examples. Shouldn't examples be compiled with --extern:pub libcrate=... instead (using newer proposed syntax just for clarity)?

Cc: @Eh2406 this last case (and possibly the one above about cargo doc) might be more specific to cargo?

petrochenkov commented 4 years ago

The feature's implementation doesn't currently work correctly with transitive dependencies. If we load a crate named foo as a transitive dependency, we still mark it private (or not) by looking at the --extern foo option, even if refers to an unrelated crate and --extern options in general are entirely irrelevant to transitive dependencies.

The dependency private-ness should be written into metadata and read from there for transitively loaded crates.

This brings a question - what if the same crate is private direct dependency and a public transitive dependency (or vice versa)? Should it be treated as private or public?

(I'm currently refactoring code in that area and may fix this issue as well.)

petrochenkov commented 4 years ago

@Aaron1011 By the way, do you still have plans to implement the --extern:modifier syntax from https://github.com/rust-lang/rust/issues/44663#issuecomment-475853173?

ehuss commented 4 years ago

@petrochenkov I like the idea of the modifier syntax, but I'm curious how that would work with getopts. If the intent would be to support multiple modifiers, would every permutation need to be registered as separate flags?

petrochenkov commented 4 years ago

how that would work with getopts.

No idea :) I didn't look into implementation details, only suggested a syntax that's more generic than --extern-private.

ehuss commented 4 years ago

I have posted a proposal for a new --extern flag syntax at #67074.

ehuss commented 4 years ago

I'm a bit confused about how re-exports are being treated. From the RFC: "Q: Can I export a type from a private dependency as my own?" says NO. However, it looks like various ways of re-exporting do not generate a warning:

Is that intentional?

Ericson2314 commented 3 years ago

@Eh2406 in https://github.com/rust-lang/rfcs/pull/3146#issuecomment-881957976 wrote

The problem with "Public and private dependencies" is not the theory. It is fairly clear what we want it to mean. It is implementation that is the problem. In a SAT/SMT reduction of Dependency resolution it adds O( (numbers of packages considered) ^ 2) terms to the problem. More importantly in the Resolver as implemented it takes common use cases from sub second time to multiple hours time. There are a lot of changes to the Resolver that we can not stabilize because of limitations to performance or error messages even though we have a clear understanding of exactly what behavior we want and why.

Is more written about this? It definitely does make it harder, but I am surprised it is so bad in practice when Cabal etc. do this today. Were you ret-conning existing deps as public deps? (I thought the plan was to keep them private which I have my doubts about but which I thought would not suddenly increase the costs.)

Eh2406 commented 3 years ago

I wrote that from memory, and wanted to go back and reread the records (mostly https://github.com/rust-lang/cargo/issues/6129) before expanding on it. It seems like my memory had lost some of the subtlety of the situation. Sorry.

There are some open question about behavior, best somerized at https://github.com/rust-lang/cargo/issues/6129#issuecomment-501783242. However, except for important surface level sintacs discussions, the available design space is pretty small. For various reasons there needs to be a "existing behavior" option in addition to "public" or "private" dependencies.

As to the runtime overhead for existing dependencies it is not large. As everything is marked "existing behavior", so it can create no conflicts. Even in the current implementation where everything is marked as private, which means that this can only add a conflict for the parent, conflicts tend to be small and simple. However the Resolver is a tricky and organically grown peace of code. I am not willing to stabilize new behavior for it that is not end-to-end property tested. If you turn on public & private dependencies in our test case generator here, it will find a case where things take over 60 seconds to resolve. If you comment out that line or run the tests in release, it will find a case where our organically grown form of learned clauses has over simplified the problem, so the Resolver decided there is no valid solution, when there is one and the "Reduction to SAT" test can find it. I burnt myself out trying to find the sweet spot, a learned clause representation sparse enough for efficient back jumping with enough information to not miss cases.

Since then, I have been spending my time on the PubGrub project in the hope that by copying from the state of the art and rewriting from first principles, we will have a more solid foundation for adding these kinds of features.

The RFC pre dates the "Prior art" section. Do you have links to info about the corresponding features in "Cabal etc."?

Ericson2314 commented 3 years ago

@Eh2406 Sorry to leave your nice response hanging.

The RFC pre dates the "Prior art" section. Do you have links to info about the corresponding features in "Cabal etc."?

Off hand, just https://wiki.haskell.org/wikiupload/b/b4/HIW2011-Talk-Loeh.pdf linked in the other thread, and I found https://opam.ocaml.org/doc/External_solvers.html for OPAM which links some older research. But I would be happy to ask around to get more up to date info for Cabal.

Since then, I have been spending my time on the PubGrub project in the hope that by copying from the state of the art and rewriting from first principles, we will have a more solid foundation for adding these kinds of features.v

I read up a bit on that. Interesting and looks quite nice! It does seem the PubGrub algorithm out of the box is just for the all-public-deps case right? So if we switched to that it would be the solid foundation and the new features together?

Eh2406 commented 3 years ago

PubGrub simplifies dependency resolution to a platonic ideal of it self. There is a lot of work to expand it to handle compatibility with all the gnarly details of Cargos resolver. A full discussion of each problem and are current thoughts for how to work around it is somewhat out of scope here, luckily @mpizenberg has written up a guide here with a section on "Advanced usage and limitations". We have a plan for how to reduce Cargo's "one per name and semver compatibility range" to PubGrub's "one per name". Figuring out how to extend that to also have "scoped goals" or "public dependencies", has not yet been figured out.

Despite having to do a lot of work to get back to where we are, I am hopeful that building off this base will work out better because PubGrub simplifications run deep. It has combined many different concepts (that Cargo's resolver has as different types) into one comparatively simple "incompatibility" type. All the different aspects of the problem (Error reporting, Conflict resolution, Unit propagation, ... ) share that vocabulary type. In Cargo's resolver adding a new "learned clause" kind involves interacting the new enum variant with all the moving parts, and thanks to the end-to-end property tests finding all the corner cases where they interact badly. I am hopeful that in PubGrub it is "express your property's as incompatibilities" (ether by reduction to the existing simplified problem, or by adding a new API) and then all the existing pieces will work with it.

Maybe someone will find a way to add it before the rebuilding compleats, but PubGrub is where I will be spending my time for now.

Ericson2314 commented 3 years ago

Maybe someone will find a way to add it before the rebuilding compleats, but PubGrub is where I will be spending my time for now.

Yes I definitely agree with this approach, to be clear.

We have a plan for how to reduce Cargo's "one per name and semver compatibility range" to PubGrub's "one per name". Figuring out how to extend that to also have "scoped goals" or "public dependencies", has not yet been figured out.

Heh that's the tricky bit. I would say "one per name" means all public dependencies. Cargo's "one per name and semver compatibility range" to me feel more like an optimization that all-private/unrestricted deps don't cause too much duplicative bloat -- I can't see how having multiple versions that that are semver compatible would actually break things, just cause a nuscience.

"scoped goals" sounds like private dependencies to me. Or rather, it's the combination of public and private deps that induces local non-global coherence issues. (All private means solving is trivial, all public means PubGrub as written works.)

I do see how one can turn foo = ">5" into foo-6 = * or foo-7 = * or ..., but I worry it's a bit of a dead end. By all means, do something like that for now if you gets Cargo on PubGrub sooner, but I think changes will need to be made to PubGrub to actually track how packages are instantiated. Trying to defunctionalize something like foo ^>= 7 & bar(foo) ^>= 9 gets too messy.

mpizenberg commented 3 years ago

Hi @Ericson2314 I'm just adding a few notes regarding PubGrub. We have to think more about the public/private feature, but I think it will be doable with a "proxy and bucket" scheme similar to the one presented in the guide. Anyway, we'll have a look at it and report how that goes.

Ericson2314 commented 3 years ago

@mpizenberg Thanks for that info! One thing I am slightly unclear on is how the "coherence" of public deps is maintained. I think you might need equality constraints, like a->c = a->b->c for the case where a has a public dep on b and c, and b also has a public dep on c.

mpizenberg commented 3 years ago

@Ericson2314 for more details, I've written a new section in the guide specifically about implementing public/private dependencies. It is currently under review with @Eh2406 so it's not published yet. But if you want to participate in the review of this section it lives here: https://github.com/pubgrub-rs/guide/pull/4

djc commented 2 years ago

What is the status of this effort? Is someone working on implementing it? What's the next step? I just got bitten by this and would be willing to help out with the implementation.

mpizenberg commented 2 years ago

@djc we have a working prototype for very similar public/private scheme in pubgrub presented here: https://pubgrub-rs-guide.netlify.app/limitations/public_private.html

So it will be on our radar with @Eh2406 as soon as we make progress on trying integrating pubgrub-rs with cargo, which will be the main focus of our effort in a couple months.

As for current cargo, I have no information.

djc commented 2 years ago

I imagine that getting pubgrub-rs to stable Cargo might take relatively long...

Eh2406 commented 2 years ago

This comment up thread still describes the current state of things.

djc commented 2 years ago

Thanks! So I've read the RFC in more detail. It seems to me that a lot of the complexity in implementing this RFC is in the impact on the resolver/dependency resolution, but that the other parts of the RFC would be quite valuable on their own and would be substantially less complex to implement. How do people feel about starting with an MVP that's basically only:

Here's the angle I'm coming from: as the maintainer of a public library with fairly broad usage, exposing API types from a dependency publicly is a hazard because it means your semver-incompatible version bumps (which we generally like to avoid so as to avoid waste of downstream work) are coupled to your dependency's semver-incompatible bumps. Inclusion of duplicate downstream dependencies are something of a separate issue that's easier to diagnose nowadays than it was when the RFC was written (I usually use cargo deny for this).

In particular, I'm coming at this as the rustls maintainer. For rustls 0.20 we tried to make the webpki and ring dependencies private, and it turns out that I missed one case, meaning we now might need to force another semver-incompatible bump on a large number of downstream dependencies for this reason. While this case is silly because I could have found it by grepping the source code, in general auditing a largish crate for types/traits that appear in the public API is non-trivial, and AIUI the compiler already implements most of this analysis to warn about intra-crate privacy issues.

HKalbasi commented 2 years ago

What about mandating every crate to explicitly depend on public dependencies of their dependencies? That is, if you depend on A which has B as its public dependency (export some B types) you should add B in your cargo.toml explicitly with a explicit version. In this way, problem of finding a solution for a SAT system will reduced to checking a particular solution for a SAT system, which can be done in linear time.

This is not perfect, but:

  1. In many cases we naturally want it, for example when we want to use util functions of B on the exported types from A.
  2. This restriction can be lifted any time in a backward compatible way.
CAD97 commented 2 years ago

@HKalbasi this idea breaks for "facade crates," (like rand, futures, bevy, ...) where they reëxport all of the public dependency's types and you're expected to use them primarily, if not even exclusively, through the facade crate.

Especially for e.g. rand, where using rand_core is encouraged if you're a library just implementing rand_core traits, whereas consumers want to consume via rand. For rand, the facade is meant to be open, and not opaque to the outside world (as opposed to e.g. bevy, which is primarily only really intended to be consumed from the outside as a whole, through the facade, and the inner crates could be considered an implementation detail).

jpetkau commented 2 years ago

I question the assumption that "public/private" is the right indicator for whether deps should be duplicated.

It's easy to construct cases where public deps are nevertheless OK to duplicate. E.g. any time the final bin ends up publically depending on a lib at two different versions, but doesn't actually try to pass values between them.

Conversely, even if a dep is private, it's reasonable to want to avoid unnecessary copies to reduce binary size and compile time. This is mentioned as an aside in various other comments, but it's a real issue.

What I wish I had is a much simpler system, something like: dependencies never duplicated by default, but the final binary can explicitly allow duplicates in its Cargo.toml. (Or, for easier migration, allow by default but explicitly deny.) cargo update barfs it it can't find a solution, tells you what crates would need duplicates, and you edit Cargo.toml if that's what you want.

This doesn't change the requirements for the underlying constraint solver, but the input to it gets conceptually simpler.

Of course it would be better if intermediate libraries could also indicate which duplicates they cared about, but that starts adding complexities again. I'd much rather have a system where the final binary could just declare what to allow, than the current system where I'm fighting with Cargo that thinks it knows better.

CAD97 commented 2 years ago

It's easy to construct cases where public deps are nevertheless OK to duplicate. E.g. any time the final bin ends up publically depending on a lib at two different versions, but doesn't actually try to pass values between them.

And it's easy enough to allow the crate depending on two crates publicly exposing conflicting versions of a transitive dep to acknowledge and allow this.

Conversely, even if a dep is private, it's reasonable to want to avoid unnecessary copies to reduce binary size and compile time.

Everyone agrees that better controls over duplicated dependencies is a good thing. public/private dependencies is distinct from that, though; it's about allowing more things that should work to compile, and about controlling whether you expose a dependency or encapsulate it fully.

tells you what crates would need duplicates, and you edit Cargo.toml if that's what you want.

This doesn't change the requirements for the underlying constraint solver, but the input to it gets conceptually simpler.

If it's still going to allow the cases where you've explicitly allowed duplicates (and notably, to determine what/how to duplicate), the constraint solver still needs to support working with said duplicates, so you don't simplify anything.

This is a good tool to have in order to control dependency duplication (it's in cargo deny, in fact), but it doesn't influence what the constraint solver needs to be able to do.[^1]

[^1]: Well, actually, the constraint solver doesn't even support solving for "fewest duplicates" over the whole tree currently. In fact, that's a new thing to optimize for. It doesn't really add complexity to the existing system — you can just model it as making everything semver compatible — but it is "a new thing the solver would need to do."

pnkfelix commented 2 years ago

Visiting during T-compiler backlog bonanza.

It would be nice if it had a S-tracking-* label that reflected its status.

Hey @rust-lang/cargo , do you think this can be tagged with S-tracking-unimplemented? Or should it be classified as S-tracking-design-concerns?

epage commented 2 years ago

We might want to update cargo add so that when it infers a dependency version, it checks from among its dependencies' public dependencies. This will make it more likely that a compatible version will be selected.

notriddle commented 1 year ago

Some behaviour from the compiler lint that I'm not sure about. This code produces a public-in-private warning on use_advice, even though that function is not accessible to the public. Is this intentional?

# Cargo.toml
cargo-features = ["public-dependency"]

[package]
name = "testfile"
version = "0.1.0"
edition = "2021"

[dependencies]
wasi = { version = "0.11", default-features = false }
// src/lib.rs
mod inner_private {
    pub fn use_advice() -> wasi::Advice {
        wasi::ADVICE_NORMAL
    }
}
pub fn do_stuff() {
    assert_eq!(wasi::ADVICE_NORMAL, inner_private::use_advice());
}
warning: type `Advice` from private dependency 'wasi' in public interface
 --> src/lib.rs:4:5
  |
4 |     pub fn use_advice() -> wasi::Advice {
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `testfile` (lib) generated 1 warning

This can be worked around by using pub(crate), as in ce607bbcb605944073185c287716adc738685d2e

bjorn3 commented 1 year ago

We consider any pub item to be public, even if not actually reachable. This is also why for example mod sealed { pub trait Sealed {} } pub trait MyTrait: Sealed {} is allowed despite Sealed not being reachable.