-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
base: main
Are you sure you want to change the base?
Rust: Generalize certain type inference logic #20179
Conversation
e41fbea
to
17ad074
Compare
17ad074
to
e172e74
Compare
There was a problem hiding this 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]+)\\.(.*)" | |
There was a problem hiding this comment.
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.
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)) | ||
) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
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 aboutx
, but not if we havelet 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 atT1
isi64
, but we don't know anything for the type pathT2
.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 pathT1....Tn
for AST noden
, then the following two conditions must be checkedT1....Tn
.C
atT1...T(i+1)
,i <=0 < n
, whereTi
is not a type parameter belonging toC
.Using the
let x = (i64, _) = ...
example from above, 1. prevents us from e.g. trying to infer the typebool
at both the empty type path and atT1
, and 2. prevents us from e.g. trying to infer any type that extendsT1
, sincei64
does not contain any type parameters. In contrast, trying to infer any type at type paths extendingT2
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.