Skip to content

Rust: Generalize certain type inference logic #20179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

hvitved
Copy link
Contributor

@hvitved hvitved commented Aug 6, 2025

This PR generalizes @paldepind's #20155:

Previously, we would only establish certain type information when all parts of the type were known. For example, if we have let x : (i64, bool) = ..., then we would establish certain type information about x, but not if we have let x = (i64, _) = ..., because the type of the second component of the pair needs to be inferred.

With this PR, we generalize certain type information to include a type path. For example, for let x = (i64, _) = ... we know for sure that the type at the empty type path is (,), and we also know for sure that the type at T1 is i64, but we don't know anything for the type path T2.

Using this generalization, we are also able to generalize when uncertainly inferred types should be discarded: If we are trying to infer the type T at type path T1....Tn for AST node n, then the following two conditions must be checked

  1. It should not be the case that we have different certain type information at T1....Tn.
  2. It should not be the case that we have certain type C at T1...T(i+1), i <=0 < n, where Ti is not a type parameter belonging to C.

Using the let x = (i64, _) = ... example from above, 1. prevents us from e.g. trying to infer the type bool at both the empty type path and at T1, and 2. prevents us from e.g. trying to infer any type that extends T1, since i64 does not contain any type parameters. In contrast, trying to infer any type at type paths extending T2 is valid.

In addition to the above, we now also handle type arguments supplied for function type parameters (in addition to impl type parameters), and derive certain type information for literals.

As usual, commit-by-commit review is suggested.

DCA looks good: More resolved calls and a slight performance improvement.

@github-actions github-actions bot added the Rust Pull requests that update Rust code label Aug 6, 2025
@hvitved hvitved force-pushed the rust/type-inference-certain-follow-up branch 2 times, most recently from e41fbea to 17ad074 Compare August 7, 2025 07:56
@hvitved hvitved added the no-change-note-required This PR does not need a change note label Aug 7, 2025
@hvitved hvitved force-pushed the rust/type-inference-certain-follow-up branch from 17ad074 to e172e74 Compare August 7, 2025 08:37
@hvitved hvitved marked this pull request as ready for review August 7, 2025 10:04
@hvitved hvitved requested a review from a team as a code owner August 7, 2025 10:04
@hvitved hvitved requested review from Copilot and geoffw0 August 7, 2025 10:04
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR generalizes type inference logic to handle partial type information with type paths. Previously, type information was only established when all parts of a type were known. Now the system can track certain type information at specific type paths, enabling inference even when some type components are unknown (e.g., (i64, _)).

Key changes include:

  • Enhanced type path handling with new isSnoc predicate for backward traversal
  • Generalized certain type inference to work with type paths and handle conflicts
  • Extended literal type inference to distinguish certain vs uncertain types

Reviewed Changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.

File Description
shared/typeinference/codeql/typeinference/internal/TypeInference.qll Adds isSnoc predicate for type path backward traversal
rust/ql/lib/codeql/rust/internal/TypeInference.qll Major refactoring of certain type inference logic with type path support and conflict detection
rust/ql/test/library-tests/type-inference/main.rs Updates test comments to reflect improved type inference (removes spurious type annotation)
rust/ql/test/library-tests/type-inference/CONSISTENCY/PathResolutionConsistency.expected Updates line number references due to code changes

@@ -334,7 +334,19 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
/** Holds if this path starts with `tp`, followed by `suffix`. */
bindingset[this]
predicate isCons(TypeParameter tp, TypePath suffix) {
suffix = this.stripPrefix(TypePath::singleton(tp))
exists(string regexp | regexp = "([0-9]+)\\.(.*)" |
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

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

The regex pattern "([0-9]+)\\.(.*)" is duplicated in concept with the pattern in isSnoc. Consider extracting these patterns as constants to improve maintainability and reduce the risk of inconsistencies.

Suggested change
exists(string regexp | regexp = "([0-9]+)\\.(.*)" |
exists(string regexp | regexp = CONS_REGEXP |

Copilot uses AI. Check for mistakes.

exists(string regexp | regexp = "(|.+\\.)([0-9]+)\\." |
prefix = this.regexpCapture(regexp, 1) and
tp = TypeParameter::decode(this.regexpCapture(regexp, 2))
)
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

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

The regex pattern "(|.+\\.)([0-9]+)\\." could benefit from a comment explaining its structure, particularly the (|.+\\.) part which matches either empty string or a non-empty prefix ending with a dot.

Copilot uses AI. Check for mistakes.

TMethodTypeArgumentPosition(int pos) {
exists(any(MethodCallExpr mce).getGenericArgList().getTypeArg(pos))
} or
TTypeParamTypeArgumentPosition(TypeParam tp)
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

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

[nitpick] This newtype definition has been moved from inside Input1 module to the top level but is still used within Input1. This creates a dependency where the module depends on types defined outside itself, which could affect modularity.

Note: See the diff below for a potential fix:

@@ -15,19 +15,27 @@
 
 class Type = T::Type;
 
-private newtype TTypeArgumentPosition =
-  // method type parameters are matched by position instead of by type
-  // parameter entity, to avoid extra recursion through method call resolution
-  TMethodTypeArgumentPosition(int pos) {
-    exists(any(MethodCallExpr mce).getGenericArgList().getTypeArg(pos))
-  } or
-  TTypeParamTypeArgumentPosition(TypeParam tp)
 
+
+
+
+
+
+
+
 private module Input1 implements InputSig1<Location> {
   private import Type as T
   private import codeql.rust.elements.internal.generated.Raw
   private import codeql.rust.elements.internal.generated.Synth
 
+  private newtype TTypeArgumentPosition =
+    // method type parameters are matched by position instead of by type
+    // parameter entity, to avoid extra recursion through method call resolution
+    TMethodTypeArgumentPosition(int pos) {
+      exists(any(MethodCallExpr mce).getGenericArgList().getTypeArg(pos))
+    } or
+    TTypeParamTypeArgumentPosition(TypeParam tp)
+
   class Type = T::Type;
 
   class TypeParameter = T::TypeParameter;

Copilot uses AI. Check for mistakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-change-note-required This PR does not need a change note Rust Pull requests that update Rust code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy