Why doesn't Cargo compile with a union of features?

(Prerequisites: As of writing, url on crates.io is 2.5.3, idna is 1.0.3, and sqlx is 0.8.2.)

Steps to reproduce:

rustup default 1.80
cargo new idna-992-repro
cd idna-992-repro/
cargo add sqlx
cargo add url
cargo check

This fails with an error that indicates that idna and url are being compiled without the default features of url. sqlx-core and sqlx-macros-core turn off the default features of url, but cargo add url adds a dependency on url with the default features enabled.

Cargo.lock shows only one instance of url (at 2.5.3).

Why does url end up being compiled without its default features when there is a dependency that leaves default features enabled? Shouldn't feature unioning result in url being compiled with its default features?

See also Compilation error with url 2.5.3 / idna 1.0.3 with Rust < 1.81 when sqlx-core is in the dependency graph ยท Issue #992 ยท servo/rust-url ยท GitHub and Please do not turn off default features for url ยท Issue #3589 ยท launchbadge/sqlx ยท GitHub

Take a look at the tree:

$ cargo tree -i url
url v2.5.3
โ”œโ”€โ”€ sqlx-core v0.8.2
โ”‚   โ”œโ”€โ”€ sqlx-macros v0.8.2 (proc-macro)
โ”‚   โ”‚   โ””โ”€โ”€ sqlx v0.8.2
โ”‚   โ”‚       โ””โ”€โ”€ idna-992-repro v0.1.0 (/tmp/idna-992-repro)
โ”‚   โ””โ”€โ”€ sqlx-macros-core v0.8.2
โ”‚       โ””โ”€โ”€ sqlx-macros v0.8.2 (proc-macro) (*)
โ””โ”€โ”€ sqlx-macros-core v0.8.2 (*)

url v2.5.3
โ”œโ”€โ”€ idna-992-repro v0.1.0 (/tmp/idna-992-repro)
โ””โ”€โ”€ sqlx-core v0.8.2
    โ””โ”€โ”€ sqlx v0.8.2 (*)

Basically, feature resolution for the url dependency in a proc-macro is separate from the "normal" build. This is a 2021 edition change, Default Cargo feature resolver. If you switch back to 2018, they are unified:

$ cargo tree -i url
url v2.5.3
โ”œโ”€โ”€ idna-992-repro v0.1.0 (/tmp/idna-992-repro)
โ”œโ”€โ”€ sqlx-core v0.8.2
โ”‚   โ”œโ”€โ”€ sqlx v0.8.2
โ”‚   โ”‚   โ””โ”€โ”€ idna-992-repro v0.1.0 (/tmp/idna-992-repro)
โ”‚   โ”œโ”€โ”€ sqlx-macros v0.8.2 (proc-macro)
โ”‚   โ”‚   โ””โ”€โ”€ sqlx v0.8.2 (*)
โ”‚   โ””โ”€โ”€ sqlx-macros-core v0.8.2
โ”‚       โ””โ”€โ”€ sqlx-macros v0.8.2 (proc-macro) (*)
โ””โ”€โ”€ sqlx-macros-core v0.8.2 (*)

You can also add cargo tree -e features to get details of which features are activated where.

3 Likes

That's good to know. I now understand why some of my dependencies were being recompiled.

If I really want to save on project compilation time maybe it makes sense to downgrade the resolver for extremely large workspaces?

Are there other downsides of downgrading to the v1 resolver? I'm not compiling for a no_std target, which seems to be one of the motivating factors for the introduction of this feature.

there's several downsides of the old approach:

  1. if the proc macro is the one enabling extra features, you may end up with unnecessary code in the final binary
  2. if you are doing cross-compilation, the crate will be built twice anyways

In any case putting existing api's behind new cargo features is a semver breaking change, even if the new features are enabled by default. Precisely because of the existence of default-features=false. Roll back impl core::error::Error by hsivonen ยท Pull Request #994 ยท servo/rust-url ยท GitHub and reintroducing the change in a new major version is strictly speaking the correct way to handle a breaking change like this.

1 Like

I did, but I didn't understand what it was saying.

Thank you! This is very surprising (but also understandable for the reasons stated by binarycat).

My understanding is that, despite dissent existing, overall the community does not accept raising MSRV as semver-breaking, and this is about conditionally raising MSRV. (I wasn't part of the discussion to make the change, but from reading the thread, I gather that this was part of the rationale of why it was OK to do this.)

(url 2.5.3 has three possible MSRVs, 1.57, 1.67, and 1.81) depending on how it is used from Cargo,)

1 Like