Skip to content

Resolve 21,881 Rust callsites in the Linux kernel. - #81

Merged
rikvanriel merged 5 commits into
facebookexperimental:mainfrom
rikvanriel:scratch/riel/series-c-receiver-coverage
Aug 28, 2026
Merged

Resolve 21,881 Rust callsites in the Linux kernel.#81
rikvanriel merged 5 commits into
facebookexperimental:mainfrom
rikvanriel:scratch/riel/series-c-receiver-coverage

Conversation

@rikvanriel

Copy link
Copy Markdown
Contributor

Resolve 21,881 Rust callsites in the Linux kernel.

f851438 record the base and the field path of a Rust receiver
ba5a825 index the fields of a Rust struct
7a6a829 type a receiver that names a variable declared elsewhere
7998c56 see through a smart pointer to the type it holds
ac3b79e type a Rust dispatch receiver from its declaration

Every Rust dispatch site in the kernel was untyped: 18,011 of them, against
19,679 typed out of 56,140 in C. Not a hard residue — the pass that types a
receiver reads `function_definition`, `declaration` and `parameter_declaration`,
which are C grammar, so on a Rust tree it matched nothing and every site fell
through untyped.

Rust states what C leaves to a header: parameters and annotated bindings carry
their type in the source, and `self` is whatever the enclosing `impl` names.
Read those. 7,459 of 18,011 sites now carry a receiver type, and the commonest
untyped receiver, `self` at 1,345 sites, is among them.

A reference is stripped, since `&T` reaches T's methods by autoderef. A raw
pointer is not: `(*mut request).cast()` is a method of the pointer, and typing
that receiver as `request` claims a member the struct has not got. Keeping raw
pointers out drops 129 such claims.

An inferred binding still states nothing, and `Arc<TagSet>` is recorded as
`Arc`, so a call reaching through it does not join yet.

Assisted-by: claw:claude-opus-5
Signed-off-by: Rik van Riel <[email protected]>
A call through `Arc<TagSet>` dispatches on TagSet, by Deref, so recording the
receiver as `Arc` files the site under a type that has no such member and it
joins with nothing. 96 sites were typed that way.

Unwrap the pointers that deref to their argument. `Vec<Request>` is not one of
them: `.len()` is a method Vec has itself, and unwrapping would file it under
Request.

    rust sites still typed as a smart pointer: 96 -> 0

Three sites stop being typed at all, all `Arc<dyn Trait>`: the held type is a
trait object, and naming the wrapper was never an answer.

Assisted-by: claw:claude-opus-5
Signed-off-by: Rik van Riel <[email protected]>
`arm_delay_ops.delay(n)` is written in a header whose variable is declared
`extern` in that same header and defined in another file, so the analyzer sees
a plain name it cannot type and `callers __loop_delay` reported nothing. 3,631
C dispatch sites are shaped like this, most of them arch and driver ops tables.

Record file-scope variables of aggregate type and use them to type a receiver
that is a plain name, once the whole tree is available:

    (semcode) callers __loop_delay
    === Indirect Callers ===
    1 call sites can reach it through a function pointer:
      1. __delay at arch/arm/include/asm/delay.h:58 [member_dot]
         installed in arm_delay_ops::delay at arch/arm/lib/delay.c:20 (receiver type matches)

237,629 declarations over 164,549 names. Of 7,748 untyped plain-name sites,
2,177 get a type and 732 land on a slot that has something installed in it;
the rest name a variable of a type nothing is installed in, which is an
answer.

A name declared with two different aggregates at one revision is left alone.
Filing a site under the wrong type joins it with the wrong registrations,
which is worse than saying the type is unknown.

Assisted-by: claw:claude-opus-5
Signed-off-by: Rik van Riel <[email protected]>
None of the 1,169 Rust types in the kernel recorded a single member. The
parser handles C's field declarations and Zig's container fields; Rust's
`field_declaration_list` fell through to a string fallback that found nothing.
Without members, a chain like `self.inner.write()` has nothing to walk, so the
receiver cannot be typed however well the base is known.

Read them. 613 of the 1,169 now carry fields; the rest are unit structs, tuple
structs and enums, which have no named members.

A member's type is reduced to the aggregate it names, the same reduction a
receiver's type gets, so the two can be compared when the chain is walked.

C names its struct body `field_declaration_list` as well, and its fields carry
a declarator rather than a name, so the branch is taken only for Rust.

Assisted-by: claw:claude-opus-5
Signed-off-by: Rik van Riel <[email protected]>
A receiver written as `self.inner.write()` was left untyped: the pass handled
a plain name and gave up on anything with a field in it, so 5,400 sites went
unrecorded where C records the base type and the path for exactly this shape.

Record them the same way. 2,918 Rust sites now carry a base and a path, and
1,191 of those walk to a type against the types table:

    Falcon.bar : Bar0 -> .write()

which is `bar: Bar0<'a>` at drivers/gpu/nova-core/falcon.rs:360.

The remainder name a type the tree does not define, mostly core and crate
types, and stop at the first field that is not indexed rather than guessing.

Assisted-by: claw:claude-opus-5
Signed-off-by: Rik van Riel <[email protected]>
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Aug 28, 2026
@rikvanriel rikvanriel changed the title Scratch/riel/series c receiver coverage Resolve 21,881 Rust callsites in the Linux kernel. Aug 28, 2026
@rikvanriel
rikvanriel merged commit af361ca into facebookexperimental:main Aug 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant