fix(types): make the arbitrary feature self-contained - #231
Conversation
There was a problem hiding this comment.
Disclosure: I'm not affiliated with Circle — an external community contributor, not a maintainer. I have no write access to this repository, so any review state I set (approval or change request) carries no merge authority and is advisory only. Please treat this as one contributor's technical assessment, and defer to Circle maintainers for the binding review.
Verified everything independently against main before reviewing:
- The broken state is as described:
arbitrary = ["dep:arbitrary"]with noderiveand noalloy-primitives/arbitraryforwarding, whileaddress.rsgatesderive(arbitrary::Arbitrary)on the feature. Both missing pieces are real and both are needed — fixing only one moves the error fromE0433to theE0277bound failure, exactly as the issue documents. - The
arc-evmprecedent holds: itsarbitraryfeature forwards to the alloy/revm/reth deps explicitly, so this change follows existing workspace convention rather than inventing one. - The masking analysis is the most valuable part of the PR and it's correct, with one detail worth making explicit: this only stays hidden because workspace-level commands (
--workspace --all-features) unify features across all members. With resolver v2,cargo check -p <crate>resolves only that crate's own feature graph — which is why the standalone check is both the honest reproduction and a valid verification that the fix is complete (no unification bailing it out).
Two notes, neither blocking:
1. Placement of derive is equivalent to forwarding, with one subtle property worth knowing. Putting features = ["derive"] on the optional dependency declaration (rather than "arbitrary/derive" in the feature array) works because features on an optional dep only activate when the dep does. The one behavioral difference: if a second feature ever enables dep:arbitrary for a different purpose, it inherits derive whether it wants it or not, whereas the feature-array form keeps the choice per-feature. For a proc-macro feature on a fuzzing dep, that's a non-concern in practice — just noting it's a deliberate trade, and the current form is the more readable of the two.
2. This bug class is systematic, and there's a cheap guard. Feature-unification masking will re-occur for any crate whose optional features are exercised in CI only via workspace-wide builds — this crate just happens to be the first one caught building standalone. cargo hack check --each-feature -p <crate> (or workspace-wide cargo hack check --each-feature --workspace, which is the standard tool for exactly this) would catch the whole class in CI. Worth a follow-up issue rather than this PR, but without it the next #[cfg_attr(feature = ...)] derive added to any crate can silently regress the same way.
The restraint on the workspace-level arbitrary = "1.3" is also correct — pushing derive into the workspace declaration would force the proc-macro dependency on every consumer to fix one crate's declaration gap.
Minimal, correct, well-evidenced. Approving.
`cargo check -p arc-consensus-types --features arbitrary` fails:
error[E0433]: failed to resolve: could not find `Arbitrary` in `arbitrary`
--> crates/types/src/address.rs:39
| #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
The feature gates `derive(arbitrary::Arbitrary)` on `Address`, but it
declared neither of the two things that derive needs:
- the `derive` feature of the `arbitrary` crate, which provides the
derive macro itself;
- `alloy-primitives/arbitrary`, without which the generated impl fails
with `the trait bound alloy_primitives::Address: Arbitrary<'_> is not
satisfied`, since the wrapped type has no `Arbitrary` impl.
This is invisible in a workspace build: `arc-consensus-db` and
`arc-node-consensus` both depend on `alloy-rpc-types-engine` with
`features = ["arbitrary"]`, and feature unification turns on what this
crate omitted. Building the crate on its own is what exposes it.
Declare both, following the pattern already used by `arc-evm` and
`arc-node`, whose `arbitrary` features forward to their alloy
dependencies explicitly.
Cargo.lock is unchanged. `cargo check --workspace --all-features` still
passes, and `cargo test -p arc-consensus-types --features arbitrary`
passes (192 tests).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
00fe684 to
00c4cce
Compare
|
Rebased onto Re-measured on the new base rather than carrying the old numbers over: # main (v0.8.0)
$ cargo check -p arc-consensus-types --features arbitrary --locked
error[E0433]: cannot find `Arbitrary` in `arbitrary`
# this branch
$ cargo check -p arc-consensus-types --features arbitrary --locked
Finished `dev` profile in 8.61sSo the gap survived the sync: v0.8.0 edited this file only to add This stays independent of #237 — that one removes |
|
Re-verified the rebase ( Confirmed:
The correction — "either order works now" is not what git reports. I merge-tested against #237's fresh rebase ( "No longer touch the same lines" is literally true — but git merges hunks, not lines, and #237 deletes To be clear about the size of this: the resolution is trivial and obvious — keep both edits — and the two changes are still semantically independent, so this is a sequencing note for whoever merges, not a flaw in either PR. But a maintainer landing one should expect the other to go red on mergeability rather than composing silently. |
Fixes #233.
Summary
The
arbitraryfeature ofarc-consensus-typesdoes not build on its own:The feature gates
derive(arbitrary::Arbitrary)onAddress, but declares neither of the two things that derive needs:arbitrary/derive— the derive macro itself lives behind that feature, hence theE0433above.alloy-primitives/arbitrary— without it the generated impl fails withthe trait bound alloy_primitives::Address: Arbitrary<'_> is not satisfied, since the wrapped type has noArbitraryimpl of its own.Why CI does not catch this
arc-consensus-dbandarc-node-consensusboth depend onalloy-rpc-types-enginewithfeatures = ["arbitrary"]. In a workspace build, feature unification turns on exactly what this crate omitted, so--all-featuresat the workspace level compiles and the gap stays hidden. It only surfaces when the crate is built alone — which is also how a consumer would build it, andcrates/typesinheritspublishfrom the workspace rather than opting out.Change
Declare both, following the pattern
arc-evmandarc-nodealready use, where thearbitraryfeature forwards to the alloy dependencies explicitly:Testing
cargo check -p arc-consensus-types --features arbitrary— now compiles (this is the command that fails onmain).cargo check --workspace --all-features— still passes, no regression.cargo test -p arc-consensus-types --features arbitrary— 192 tests pass.cargo fmt --all --check— clean.Cargo.lockis unchanged:arbitrary'sderivefeature andalloy-primitives/arbitraryare both already enabled somewhere in the graph, so no version resolution moves.I left the workspace-level
arbitrary = "1.3"in the rootCargo.tomlalone deliberately — addingderivethere would enable it for every consumer of the workspace dependency, and only this crate needs it.