Skip to content

Unexpected compilation error via a combination of binary dependencies, proc macros, crate features, and the target key #10525

Closed
@bstrie

Description

@bstrie

Here's a tricky failure distilled from a real-world codebase. I've aggressively minimized the reproduction (including each of the individual dependencies; no remote dependencies remain); everything remaining is seemingly necessary to reproduce the failure (crate, item, and feature names have been preserved for easy comparison with the original).

Overview of the minimized dependency graph:

flowchart TD
    mycrate-- "artifact = #quot;bin#quot;\ntarget = #quot;x86_64-unknown-linux-gnu#quot;" -->mybindep
    mycrate-->A["structopt-derive\n(proc macro)"]
    mybindep-->B["clap_derive\n(proc macro)"]
    B["clap_derive\n(proc macro)"]-->proc-macro-error
    A["structopt-derive\n(proc macro)"]-- "features = [#quot;parsing#quot;]" -->syn
    A["structopt-derive\n(proc macro)"]-->proc-macro-error
    proc-macro-error-->syn
Loading

I've published the minimized repo here: https://github.com/bstrie/bindeperror1 .

For completeness, here's a branch with the maximized reproduction in the original codebase: https://github.com/bstrie/enarx/tree/bindeperror1 (revert the last commit to observe the code compiles properly; if testing fixes against this branch be sure to remove the rust-toolchain file).

For posterity I've inlined the minimized repo at the bottom of this issue.

The code in question should compile. Instead, cargo build produces the following error:

error[E0599]: the method `unwrap_or_abort` exists for enum `Result<(), syn::Error>`, but its trait bounds were not satisfied
   --> structopt-derive/src/lib.rs:4:30
    |
4   |     Ok::<(), syn::Error>(()).unwrap_or_abort()
    |                              ^^^^^^^^^^^^^^^
    |
   ::: /home/ben/code/scrap/bindeperror1/syn/src/lib.rs:1:1
    |
1   | pub struct Error;
    | ----------------- doesn't satisfy `syn::Error: Into<proc_macro_error::Diagnostic>`
    |
   ::: /home/ben/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:504:1
    |
504 | pub enum Result<T, E> {
    | --------------------- doesn't satisfy `Result<(), syn::Error>: ResultExt<()>`
    |
    = note: the following trait bounds were not satisfied:
            `syn::Error: Into<proc_macro_error::Diagnostic>`
            which is required by `Result<(), syn::Error>: ResultExt<()>`

warning: unused import: `proc_macro_error::ResultExt`
 --> structopt-derive/src/lib.rs:1:5
  |
1 | use proc_macro_error::ResultExt;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

For more information about this error, try `rustc --explain E0599`.

Interestingly, all of the following will cause the code to compile successfully:

  1. Removing the target key in mycrate's Cargo.toml, i.e.:
    - mybindep = { path = "mybindep", artifact = "bin", target = "x86_64-unknown-linux-gnu" }
    + mybindep = { path = "mybindep", artifact = "bin" }
    causes the code to compile. This is extremely curious because the host machine is x86_64-unknown-linux-gnu, so conceptually this should be a no-op. Furthermore, since these crates are proc macros, they should already be targeting the host regardless of the value of the target field. Any other value in the target field (e.g. x86_64-unknown-linux-musl) produces the same error, so it is the presence of this key that is essential.
  2. Removing the features key in structopt-derive's Cargo.toml, i.e.:
    - syn = { path = "../syn", features = ["parsing"] }
    + syn = { path = "../syn" }
    causes the code to compile. Note that in our minimized syn crate this feature is completely vestigial and has no behavior at all.
  3. Making both mycrate and mybindep rely on the same proc macro crate. This demonstrates that there's nothing problematic about structopt-derive or clap_derive in isolation, and that this error only occurs when they're both present.

Output of cargo version --verbose:

cargo 1.61.0-nightly (109bfbd 2022-03-17)
release: 1.61.0-nightly
commit-hash: 109bfbd055325ef87a6e7f63d67da7e838f8300b
commit-date: 2022-03-17
host: x86_64-unknown-linux-gnu
libgit2: 1.4.2 (sys:0.14.2 vendored)
libcurl: 7.80.0-DEV (sys:0.4.51+curl-7.80.0 vendored ssl:OpenSSL/1.1.1m)
os: Pop!_OS 21.10 (impish) [64-bit]

cc @Byron

Code for reference:


# Cargo.toml
[package]
name = "mycrate"
version = "0.0.0"
edition = "2021"

[dependencies]
structopt-derive = { path = "structopt-derive" }
mybindep = { path = "mybindep", artifact = "bin", target = "x86_64-unknown-linux-gnu" }
// src/main.rs
fn main() {
    env!("CARGO_BIN_FILE_MYBINDEP");
}
# mybindep/Cargo.toml
[package]
name = "mybindep"
version = "0.0.0"
edition = "2021"

[dependencies]
clap_derive = { path = "../clap_derive" }
// mybindep/src/main.rs
fn main() {}
# clap_derive/Cargo.toml
[package]
name = "clap_derive"
version = "0.0.0"
edition = "2021"

[dependencies]
proc-macro-error = { path = "../proc-macro-error" }

[lib]
proc-macro = true
// clap_derive/src/lib.rs
# structopt-derive/Cargo.toml
[package]
name = "structopt-derive"
version = "0.0.0"
edition = "2021"

[dependencies]
syn = { path = "../syn", features = ["parsing"] }
proc-macro-error = { path = "../proc-macro-error" }

[lib]
proc-macro = true
// structopt-derive/src/lib.rs
use proc_macro_error::ResultExt;

fn _parse_structopt_attributes() {
    Ok::<(), syn::Error>(()).unwrap_or_abort()
}
# proc-macro-error/Cargo.toml
[package]
name = "proc-macro-error"
version = "0.0.0"
edition = "2021"

[dependencies]
syn = { path = "../syn" }
// proc-macro-error/src/lib.rs
pub trait ResultExt<T> {
    fn unwrap_or_abort(self) -> T;
}

impl<T, E: Into<Diagnostic>> ResultExt<T> for Result<T, E> {
    fn unwrap_or_abort(self) -> T {
        panic!()
    }
}

pub struct Diagnostic;

impl From<syn::Error> for Diagnostic {
    fn from(_: syn::Error) -> Self {
        panic!()
    }
}
# syn/Cargo.toml
[package]
name = "syn"
version = "0.0.0"
edition = "2021"

[features]
parsing = []
// syn/src/lib.rs
pub struct Error;

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-features2Area: issues specifically related to the v2 feature resolverC-bugCategory: bugZ-bindepsNightly: binary artifact dependencies

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions