Skip to content

Conversation

@hvitved
Copy link
Contributor

@hvitved hvitved commented Dec 8, 2025

This PR adds general support for the Deref trait when resolving method calls, which means both supporting it when actually resolving method calls in the type inference library, but also inserting the implicit deref calls in data flow.

Type inference

When resolving method calls, a set of candidate receiver types are constructed by repeatedly dereferencing the receiver using applicable Deref implementations. Before this PR, we had limited support, namely &(mut) T -> T and String -> str. In order to handle arbitrary chains of dereferences, we introduce a new class DerefChain, based on the shared UnboundList library, which records the chain of deref calls needed to resolve a method call.

After having resolved a method call, type information may also have to flow backwards through the chain of dereferences. Example:

use std::ops::Deref;
use std::ops::DerefMut;

struct S<T>(T);

impl<T> Deref for S<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T> DerefMut for S<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

fn f() {
    let v = Vec::new();
    let mut s = S(v);
    s.push(0); // resolves to `Vec::push`, forces `v` to be of type `Vec<i32>`
}

Support for this is implemented in the inferMethodCallTypeSelf predicate, where the DerefChain is applied in reverse order, peeling off the top element until the chain becomes empty.

Data flow

A method call x.m() with an implicit dereference desugars to (*Deref::deref(&x)).m(), so we need to add data flow nodes for &x, Deref::deref(&x), and *Deref::deref(&x), as well as the implicit call to Deref::deref. This means we will have a reference store-step from x to &x and a reference read-step from Deref::deref(&x) to *Deref::deref(&x).

The three different data flow nodes are represented by a state called ImplicitDerefNodeState, and since we need to support arbitrary dereference chains, each synthetic node is additionally tagged with a DerefChain as well as an index into that chain.

A small, but important, performance improvement is made when the targeted deref method is one of the two built-in implementations; in this case, we can add a local flow step directly from x to Deref::deref(&x), which avoids the need for inter-procedural flow.

@github-actions github-actions bot added the Rust Pull requests that update Rust code label Dec 8, 2025
Comment on lines 265 to 297
/**
* Index assignments like `a[i] = rhs` are treated as `*a.index_mut(i) = rhs`,
* so they should in principle be handled by `referenceAssignment`.
*
* However, this would require support for [generalized reverse flow][1], which
* is not yet implemented, so instead we simulate reverse flow where it would
* have applied via the model for `<_ as core::ops::index::IndexMut>::index_mut`.
*
* The same is the case for compound assignments like `a[i] += rhs`, which are
* treated as `(*a.index_mut(i)).add_assign(rhs)`.
*
* [1]: https://github.com/github/codeql/pull/18109
*/

Check warning

Code scanning / CodeQL

Predicate QLDoc style Warning

The QLDoc for a predicate without a result should start with 'Holds'.
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch 3 times, most recently from baba061 to 5dda9be Compare December 15, 2025 09:17
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch 2 times, most recently from eb1db27 to e269016 Compare December 16, 2025 13:44
pragma[nomagic]
Type getACandidateReceiverTypeAtSubstituteLookupTraits(
string derefChain, boolean borrow, TypePath path
Type getANonPseudoCandidateReceiverTypeAt(

Check warning

Code scanning / CodeQL

Missing QLDoc for parameter Warning

The QLDoc has no documentation for borrow, or derefChain, or path, but the QLDoc mentions unknown
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch from 8266451 to 14037e4 Compare December 16, 2025 14:38
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch from 14037e4 to 0c7b1d0 Compare December 17, 2025 10:42
)
)
}

Check warning

Code scanning / CodeQL

Omittable 'exists' variable Warning

This exists variable can be omitted by using a don't-care expression
in this argument
.
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch 2 times, most recently from 7880183 to be3a16a Compare December 17, 2025 12:40
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch 6 times, most recently from cc413c1 to adcbfc8 Compare December 18, 2025 13:42
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch from dd62164 to dc0c45b Compare December 18, 2025 16:18
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch 4 times, most recently from f3bad27 to c23d528 Compare December 19, 2025 15:02
@hvitved hvitved changed the title Rust: Handle Deref trait in type inference Rust: Handle Deref trait in type inference and data flow Dec 19, 2025
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch from c23d528 to 76ad749 Compare December 19, 2025 18:44
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch from 76ad749 to 2927d7d Compare January 5, 2026 09:40
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch 2 times, most recently from 60d9084 to de3a73e Compare January 5, 2026 13:25
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch 2 times, most recently from 5c70ae0 to 20ebba3 Compare January 5, 2026 14:04
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch 4 times, most recently from 9190142 to 00b243a Compare January 6, 2026 09:19
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch from 00b243a to a291add Compare January 6, 2026 09:55
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch from a291add to 452da63 Compare January 6, 2026 14:00
@hvitved hvitved force-pushed the rust/type-inference-deref-trait branch from 452da63 to 2d8da40 Compare January 6, 2026 14:18
}

private class ImplicitDerefOutNode extends ImplicitDerefNode, OutNode {
private DataFlowCall call;

Check warning

Code scanning / CodeQL

Dead code Warning

This code is never used, and it's not publicly exported.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant