-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Rust: Implement type inference for closures and calls to closures #20130
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?
Conversation
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 implements type inference for closure expressions and calls to closures in Rust, including proper handling of FnOnce
trait bounds. The implementation reuses tuple types for function parameters and represents closures as dyn FnOnce
types.
- Adds type inference support for closure expressions, their parameters, and return types
- Implements type propagation for calls to closures and higher-order functions
- Introduces handling of the
FnOnce
trait with syntactic sugar for function types
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
File | Description |
---|---|
closure.rs | New test file with comprehensive closure type inference test cases |
main.rs | Removes old closure test module and adds reference to new test file |
TypeMention.qll | Adds support for parenthesized argument lists and FnOnce trait handling |
TypeInference.qll | Implements core closure type inference logic and call expression handling |
Stdlib.qll | Defines the FnOnce trait class for type system integration |
type-inference.expected | Expected test output showing inferred types for closure expressions |
private CallExpr call; | ||
|
||
InvokedClosureExpr() { | ||
call.getFunction() = this and |
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] The condition call.getFunction() = this
could be inefficient as it requires checking all CallExpr nodes. Consider using a more targeted approach or adding an index if this becomes a performance bottleneck.
call.getFunction() = this and | |
/** | |
* Associates a CallExpr with its function. | |
*/ | |
private predicate isFunctionOfCall(Expr function, CallExpr call) { | |
call.getFunction() = function | |
} | |
InvokedClosureExpr() { | |
isFunctionOfCall(this, call) and |
Copilot uses AI. Check for mistakes.
// _If_ the invoked expression has the type of a closure, then we propagate | ||
// the surrounding types into the closure. | ||
exists(int arity, TypePath path0 | | ||
ce.getTypeAt(TypePath::nil()).(DynTraitType).getTrait() instanceof FnOnceTrait |
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.
This condition performs multiple method calls and type checks in sequence. Consider caching the result of ce.getTypeAt(TypePath::nil())
or restructuring to avoid repeated computation.
ce.getTypeAt(TypePath::nil()).(DynTraitType).getTrait() instanceof FnOnceTrait | |
let cachedType = ce.getTypeAt(TypePath::nil()) | | |
cachedType.(DynTraitType).getTrait() instanceof FnOnceTrait |
Copilot uses AI. Check for mistakes.
This PR implements type inference for closure expressions, calls to closures, and correctly handles
FnOnce
trait bounds.A few notes on the implementation and the PR:
FnOnce
trait has a type parameterArgs
which is a tuple of all the arguments. That is reflected in our implementation as well, i.e., we re-use our tuple types for parameters.FnOnce
,FnMut
, andFn
traits. They form a trait hierarchy withFnOnce
at the top.FnOnce
uses an associated type for the return type and since we don't support associated types inherited in subtraits (tests for this was added in Rust: Fix type inference for trait objects for traits with associated types #20122) we can right now only really understand theFnOnce
trait and not the others.dyn FnOnce
type. That is, instead of creating some new type for closures we just reusedyn
. The Rust language itself says that closures have some internal type that can't be written by users, and all that known about it is that it implements some of theFnOnce
/Fn
/FnMut
traits.dyn
gives us this effect. rust-analyzer shows closures as having inimpl FnOnce
type. I think that makes a bit more sense thandyn
, but usingdyn
was simpler.Fn
trait for closures, as it is a subtrait of the others, but, again, we can't understandFn
yet.DCA seems fine, though we only get a 0.04% point increase in resolved calls, which is less than I had hoped.