feat: wdk_build::DerivesMap for derives collection of bindgen output#652
feat: wdk_build::DerivesMap for derives collection of bindgen output#652leon-xd wants to merge 7 commits into
wdk_build::DerivesMap for derives collection of bindgen output#652Conversation
There was a problem hiding this comment.
Pull request overview
Adds an internal wdk_build::derives module that can parse bindgen-generated Rust (types.rs-style) and reconstruct which auto-derives apply to each emitted type, enabling future multi-pass bindgen runs (e.g., mutually exclusive headers) to preserve derive parity via blocklisted_type_implements_trait.
Changes:
- Introduces
crates/wdk-build/src/derives.rswithDerivesMap, alias resolution, and aParseCallbacksimplementation (BaseDerivesCallback). - Updates the default bindgen builder configuration to force
size_t/ssize_tto map tousize/isize. - Bumps workspace
bindgenand adds workspacebitflags, plussynparsing support inwdk-build.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| Cargo.toml | Bumps bindgen and adds workspace bitflags dependency. |
| Cargo.lock | Lockfile updates for new/updated dependencies. |
| crates/wdk-build/Cargo.toml | Adds bitflags and syn(parsing) dependencies for derive parsing. |
| crates/wdk-build/src/lib.rs | Exposes internal derives module (doc-hidden). |
| crates/wdk-build/src/bindgen.rs | Sets size_t_is_usize(true) to match Windows driver targets and derive-map assumptions. |
| crates/wdk-build/src/derives.rs | New derive parsing + alias-resolution logic + unit tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #652 +/- ##
==========================================
+ Coverage 79.45% 80.55% +1.10%
==========================================
Files 26 27 +1
Lines 5500 5967 +467
Branches 5500 5967 +467
==========================================
+ Hits 4370 4807 +437
- Misses 1001 1016 +15
- Partials 129 144 +15 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@leon-xd can parsing base types have any measurable impact on build time? |
@gurry in the pr description of Leon's other pr which uses this new feature, he says:
So while technically its not attributable to only this change, as a whole the change reduces build time. I think a theory was that bindgen's parsing of header seems to scale non-linearly |
gurry
left a comment
There was a problem hiding this comment.
LGTM. Some minor suggestions
| /// Encountered a top-level [`syn::Item`] variant this parser does not | ||
| /// handle. | ||
| #[error("unhandled syn node: {node}")] | ||
| UnhandledSynCase { |
There was a problem hiding this comment.
| UnhandledSynCase { | |
| UnsupportedSynItem { |
This is clearer IMO
| .is_some_and(|&set| set.implements(derive_trait)) | ||
| } | ||
|
|
||
| /// Parses a Rust source file and records the derive set for every |
There was a problem hiding this comment.
| /// Parses a Rust source file and records the derive set for every | |
| /// Parses given Rust source file content and records the derive set for every |
I feel this is clearer since the method does not operate on physical files
| Type::Ptr(_) => Ok(DerivesSource::Direct(DerivesSet::all())), | ||
| Type::Array(arr) => derives_for_type(&arr.elem), | ||
| Type::Path(tp) => { | ||
| if path_is_option(&tp.path) && inner_is_bare_fn(&tp.path) { |
There was a problem hiding this comment.
Might wanna add a comment saying that this check is for C callbacks or simply have a function called is_callback_type()
| return Ok(DerivesSource::Direct(DerivesSet::all())); | ||
| } | ||
|
|
||
| Ok(DerivesSource::Alias(last.ident.to_string())) |
There was a problem hiding this comment.
We are using only the last segment as key here. Can bindgen output ever have two distinct types with the same last segment and they are both used as alias RHSes?
| /// Parses a Rust source file and records the derive set for every | ||
| /// top-level `struct`, `union`, `enum`, and type alias. Unknown derive | ||
| /// idents are ignored. | ||
| /// |
There was a problem hiding this comment.
May be worth calling out in the comment that this method does not work for bindgen output containing function declarations (i.e. output generated with CodegenConfig::FUNCTIONS)
| let ty: Type = parse_str("[u32; 4]").unwrap(); | ||
| assert_direct_full(derives_for_type(&ty).unwrap()); |
There was a problem hiding this comment.
Perhaps add an assert for arrays element types that do not implement all traits.
| Err(DerivesError::UnhandledSynCase { .. }) | ||
| )); | ||
| } | ||
| } |
There was a problem hiding this comment.
Add a test verifying that foreign mods like this are rejected with error:
extern "C" {
pub fn some_ffi_func(...);
pub static mut some_const: SOME_TYPE;
}| fn derives_for_type_primitive_path_gets_all() { | ||
| let ty: Type = parse_str("u32").unwrap(); | ||
| assert_direct_full(derives_for_type(&ty).unwrap()); | ||
| } |
There was a problem hiding this comment.
Maybe we should check for all primitive types in the PRIMITIVES here just like we do for stdints in stdint_names_all_derive_standard_set()
| /// the chain-walking loop detects it when a step revisits a name | ||
| /// already in the walked set. | ||
| #[test] | ||
| fn alias_cycle_terminates() { |
There was a problem hiding this comment.
Will be useful to test for a cycle with three types. Tests for two type cycles may be too narrow to exercise the full logic.
| matches!(err, DerivesError::Parse(_)), | ||
| "expected Parse, got {err:?}" | ||
| ); | ||
| } |
There was a problem hiding this comment.
Not super important, but may want to also test for bindgen output containing foreign mods i.e. it gets rejected.
This PR introduces
wdk_build::DerivesMap, a struct to collect the info of all#[derive()]attributes for each type emitted bybindgen. See #654 for the related PR.Purpose
These changes are a pre-requisite to solve the current issues with generating bindings for mutually exclusive WDK headers (tracked by #514 , #515 , and #516 ). The solution requires types and constants for subsystems to be generated in independent
bindgenruns, and will be published in a follow-up PR. In order to prevent duplicate type generation,bindgeninvocation for subsystem types will utilize ablocklist_filefor every file traversed in the initial base types run.bindgenby default assumes very minimal derives when encountering a blocklisted type, so this implementation on its own would leave most subsystem types with a sparse set of automaticderives. This is a regression that is unacceptable. In order to prevent this while still keeping our mutual exclusive headers solution, we need to gather the derive information for all of the generated base types, and pass it off to subsystembindgentype generators via the callbackblocklisted_type_implements_trait.This PR introduces the mechanism to be able to parse a
bindgengenerated file usingsynand extract the derive information for each individual type. In practice, the future bindings generation pipeline will generate the types for thebaselayer of the WDK, parse it viaDerivesMap::from_file, and then insert the createdDerivesMapinto each subsystem typebindgenviaBaseDerivesCallback.Changes
Cargo.tomlbindgenfrom0.71.0to0.72.1bitflags = "2.6.0"workspace dependencycrates/wdk-build/Cargo.tomlbitflagsdependencysyndependency with theparsingfeature enabledcrates/wdk-build/src/lib.rspub mod derives(gated with#[doc(hidden)]since the module is intended for internalwdk-sysbuild-script use, not public API)crates/wdk-build/src/bindgen.rssize_t_is_usize(true)onwdk_default.size_t/ssize_tare pointer-width on every supported Windows driver target (x64, ARM64, x86), matching Rust'susize/isize. This is the invariant thatderives.rsrelies on when seeding stdint integer types into its derive map.crates/wdk-build/src/derives.rsDerivesError: typed error enum for derive-map construction failures.DerivesSet: abitflags!set over the five derive traits bindgen tracks (Copy,Clone,Debug,Default,PartialEq).DerivesSource: enum distinguishing whether a type's derives are immediately known (from a#[derive()]attribute or from a base type) or come from an aliased type.BaseDerivesCallback: abindgen::callbacks::ParseCallbacksimplementation that answersblocklisted_type_implements_traitfrom a populatedDerivesMap. Plugs into bindgen's chain so types blocklisted in one bindings module can still be referenced from another. Used in follow up PR.DerivesMap: a map from type name toDerivesSet. Builds incrementally from Rust source files viasyn, and resolves alias chains.synhelpers:idents_and_derives_for_items: walk a slice ofItems, collect(name, DerivesSource)pairs.idents_and_derives_for_mod: recurse into amodand forward toidents_and_derives_for_items.ident_and_derives_for_use: resolve ause a::b as Foo;re-export to(Foo, DerivesSource::Alias(...)).derives_for_type: given aType, classify into aDerivesSource(named alias, function pointer, primitive, etc.).derives_from_attrs: extract the derive-trait names from a#[derive(...)]attribute list.inner_is_bare_fn,path_is_option,path_is_core_ffi_type#[derive(...)]attributes, and stdint seeding.Validation
Tested this with the full implementation of the mutually exclusive header resolution and ensured that all outputted bindings had full
#[derive]parity against bindings generated frommicrosoft/main.