Skip to content

Type inference 2.0 - #21795

Draft
hvitved wants to merge 4 commits into
github:mainfrom
hvitved:rust/type-inference-shared
Draft

Type inference 2.0#21795
hvitved wants to merge 4 commits into
github:mainfrom
hvitved:rust/type-inference-shared

Conversation

@hvitved

@hvitved hvitved commented May 5, 2026

Copy link
Copy Markdown
Contributor

This PR makes a significant overhaul of our QL based implementation of type inference for Rust (hence the tacky PR title). At a high level, a lot of code is moved from the Rust codebase to the shared type inference library (in preparation for unified/Swift), and there is now a very clear distinction between bottom-up type inference and top-down (contextual) type inference.

Before this PR

  • The shared type inference library contained the core functionality for propagating type information through for example function calls, but all logic for mapping AST nodes to types was done outside of the library.
  • Rust type inference allowed for types to propagate bidirectionally (as in classical constrained-based implementations), but because of QL's monotonic nature, this could often result in combinatorial explosions. To circumvent such explosions, advanced logic existed for inferring types with certainty, and this logic also tried to infer certain types for calls. Another measure put in place to prevent explosions was LUB coercions, with ad hoc logic for propagating type information between LUB siblings.

Shared logic for mapping AST node to types

We introduce a new Make3 parameterization layer to the shared type inference library, which takes as input a definition of AST nodes, including common concepts such as calls and callables, as well as language-specific typing rules, and constructs the inferType predicate for recursively inferring the types of AST nodes.

The input signature of Make3 is deliberately similar to that of the shared CFG library, and it may be possible to align them at some point.

The shared library takes care of typing of many standard constructs such as calls and field accesses, and also has logic for contextual typing and typing of closures.

Bottom-up vs top-down inference

Perhaps the most important change is that we now distinguish between bottom-up type inference (the default) and top-down type inference. For example, in order to infer the type of a conditional expression,if cond { e1 } else { e2 }, we propagate type information from either of the branches e1 and e2 into the conditional expression (for simplicity, we do not attempt to calculate least-upper-bound types or similar). This corresponds to the two bottom-up type inference rules:

            e1: T
------------------------------- (cond-then)
if cond { e1 } else { e2 } : T

            e2: T
------------------------------- (cond-else)
if cond { e1 } else { e2 } : T

Now, if we have a conditional expression like

if cond { 42i64 } else { Default::default() }

where the type of Default::default() needs to be inferred from the context, we

  1. conclude that the conditional has type i64, using the cond-then rule,
  2. assign Default::default() the special UnknownType (the shared library has logic for identifying calls where (parts of) the return type needs to be inferred from the context), and
  3. since the else branch has UnknownType, we apply the cond-else rule backwards to infer that Default::default() has type i64.

Note that UnknownType can propagate bottom-up like any other type, which is needed in cases like for example

let x = if cond { Default::default() } else { Default::default() };
let y : i64 = x;

where the UknownType will propagate upwards using two bottom-up steps, and the contextual inference will then propagate the i64 type backwards using two reversed steps.

Reversal of bottom-up steps happens inside the ContextualTyping::inferTypeContextualCand0 predicate, and contextual propagation into a node n at type path path is only allowed when n has UnknownType at some prefix of path, and furthermore if path is non-empty, then it must be compatible with an already inferred type (contextually or not). The latter part means that the Rust-specific typing rule for *e expressions, when e has a raw pointer type, can be handled by a single bottom-up rule (the first disjunct of stepLanguageSpecific) instead of two rules in the old implementation.

Simplified and shared certain type inference

The logic for inferring types with certainty has been moved inside the shared library (Make3::Certain), but we no longer attempt to infer certain type inference for calls. This simplifies the implementation significantly, but without resulting in combinatorial explosions because the revised handling of contextual inference is much less prone to explosions.

Improved and shared handling of closure typing

Closures typically need to have their parameter and return types inferred from the context in which they are used.

Improved and shared handling of type arguments and type qualifiers

Other minor changes

  • We no longer have a dedicated NeverType for ! typed expressions; instead we simply use UnknownType to indicate that the actual type must be inferred from the context.
  • async return types are now also taken into account for closures.

Note for the reviewer

As usual, commit-by-commit reviewing is encouraged. The first commit (which compiles and works) moves a bunch of logic around in the Rust implementation, which is then removed in the subsequent commit (which doesn't compile). I found that doing it like this resulted in a cleaner diff on the last commit, and it also makes it more clear which parts of the logic are now handled by shared code.

Impact

Future work

  • Handle more AST constructs in the shared library, such as patterns (should be relatively straightforward).
  • Shared logic for type-based overload resolution; logic currently exists for Rust, and perhaps some of this logic can be shared.

@github-actions github-actions Bot added the Rust Pull requests that update Rust code label May 5, 2026
@hvitved
hvitved force-pushed the rust/type-inference-shared branch from 8ca252c to 30be9c4 Compare May 5, 2026 13:31
Comment thread rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll Fixed
Comment thread rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll Fixed
@github-actions github-actions Bot added the Swift label May 6, 2026
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 2 times, most recently from a9b24ec to 15c4c30 Compare May 6, 2026 18:23
Comment thread rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll Fixed
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 2 times, most recently from aefd835 to 12256f3 Compare May 7, 2026 18:15
@github-actions github-actions Bot removed the Swift label May 7, 2026
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 2 times, most recently from 657b890 to 8d0c5a3 Compare May 13, 2026 11:39
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 3 times, most recently from 654fd25 to 1d071ac Compare June 4, 2026 09:07
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 3 times, most recently from 2694a80 to 8093c96 Compare June 8, 2026 18:25
@hvitved
hvitved force-pushed the rust/type-inference-shared branch from 8093c96 to 96a5210 Compare June 15, 2026 19:17
pragma[nomagic]
predicate resolutionDependsOnReturnType() {
exists(
ImplOrTraitItemNode i, AssocFunctionDeclaration target, TypeParameter tp,
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 2 times, most recently from 981f66e to ba8029f Compare June 17, 2026 09:02
Comment thread rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll Fixed
Comment thread rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll Fixed
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 3 times, most recently from c3189e9 to 04100d4 Compare June 19, 2026 09:10
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 8 times, most recently from d518fe7 to d96e11b Compare July 6, 2026 13:04
Comment thread rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll Fixed
Comment thread rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll Fixed
Comment thread rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll Fixed
Comment thread rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll Fixed
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 7 times, most recently from 13023aa to d5fa0a3 Compare July 7, 2026 08:52
@hvitved hvitved changed the title Rust: Move more type inference logic into shared library Type inference 2.0 Jul 7, 2026
@hvitved
hvitved force-pushed the rust/type-inference-shared branch from d5fa0a3 to 02d48fe Compare July 9, 2026 14:29
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 2 times, most recently from eb47861 to 6f3e3ba Compare August 7, 2026 06:26
@hvitved
hvitved force-pushed the rust/type-inference-shared branch 5 times, most recently from ff6a4e0 to a43fd10 Compare August 17, 2026 18:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors Rust type inference around the shared library’s new bottom-up and contextual inference architecture.

Changes:

  • Adds shared AST inference, contextual typing, closure handling, and diagnostics.
  • Reimplements Rust inference through the shared Make3 interface.
  • Updates Rust tests and consistency expectations.
Show a summary per file
File Description
shared/util/codeql/util/UnboundList.qll Adds list append helper.
shared/typeinference/codeql/typeinference/internal/TypeInference.qll Implements shared inference framework.
rust/ql/test/library-tests/type-inference/type-inference.ql Uses shared type-test support.
rust/ql/test/library-tests/type-inference/pattern_matching.rs Updates inference expectations.
rust/ql/test/library-tests/type-inference/overloading.rs Records contextual inference regression.
rust/ql/test/library-tests/type-inference/main.rs Updates coverage and expectations.
rust/ql/test/library-tests/type-inference/dereference.rs Exercises inferred generic arguments.
rust/ql/test/library-tests/type-inference/CONSISTENCY/PathResolutionConsistency.expected Updates generated consistency output.
rust/ql/test/library-tests/type-inference/closure.rs Updates closure expectations.
rust/ql/test/library-tests/dataflow/sources/web_frameworks/CONSISTENCY/TypeInferenceConsistency.expected Updates generated consistency output.
rust/ql/test/library-tests/dataflow/models/CONSISTENCY/PathResolutionConsistency.expected Updates generated consistency output.
rust/ql/lib/codeql/rust/internal/typeinference/TypeMention.qll Adds contextual and constructor type mentions.
rust/ql/lib/codeql/rust/internal/typeinference/TypeInferenceConsistency.qll Adopts shared consistency checks.
rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll Adapts Rust inference to Make3.
rust/ql/lib/codeql/rust/internal/typeinference/Type.qll Introduces generalized pseudo-types.
rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll Generalizes pseudo-type filtering.
rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll Generalizes pseudo-type filtering.
rust/ql/lib/codeql/rust/internal/CachedStages.qll Uses the shared inference cache stage.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Suppressed comments (1)

shared/typeinference/codeql/typeinference/internal/TypeInference.qll:3345

  • Remove the duplicated article.
       * Holds if the the textual representation `repr` should be used for `n` in
  • Files reviewed: 17/19 changed files
  • Comments generated: 4
  • Review effort level: Balanced

Comment thread rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll
Comment thread shared/typeinference/codeql/typeinference/internal/TypeInference.qll Outdated
Comment thread shared/typeinference/codeql/typeinference/internal/TypeInference.qll Outdated
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.

3 participants