fix: prevent hints from poisoning generic function inference in OR expressions
Summary
Fixes #1635.
This PR fixes a type inference bug where generic functions (like os.getenv) returned incorrectly widened types when used on the right-hand side of an or expression.
The Issue:
When reassigning a variable like config: str | None, the type checker passed the variable's current type (str | None) as a "context hint" to the right-hand side of the or. Generic functions like os.getenv(key, default) accepted this hint, widening their return type to match the hint (inferring T as None) rather than inferring the specific type from their arguments (default="string" -> T=str).
This resulted in false positive type errors (e.g., Argument 'str | None' is not assignable...) even when a valid default value was provided.
The Fix
I updated boolop in expr.rs to adjust how hints are propagated in OR expressions:
- For Function Calls (
Expr::Call): The context hint is now dropped. This forces the function to infer its return type strictly from its arguments (Inside-Out inference), preventing "poisoning" from the surrounding context. - For Literals/Others: The context hint is preserved. This ensures that expressions like
x: List[int] = None or []still correctly inferList[int]instead ofList[Any].
Test Plan
Added a regression test test_or_generic_function_hint_poisoning_fix in operators.rs.
The test simulates the os.getenv behavior using a generic identity function and verifies that:
- A variable defined as
str | Noneset toNone. - Reassigned via
variable or identity("default"). - Correctly narrows to
str(proving the hintstr | Nonewas ignored by the function inference).
Verification:
Ran tests locally:
cargo test -p pyrefly --lib test_or_generic_function_hint_poisoning_fix (Passed)
@stroxler has imported this pull request. If you are a Meta employee, you can view this in D87651358.
This pull request has been automatically marked as stale because it has not had recent activity for more than 2 weeks.
If you are still working on this this pull request, please add a comment or push new commits to keep it active. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!