Another instance of inconsistent type when breaking cycles
Describe the Bug
Minimal repro:
class Cursor:
def finished(self) -> bool:
...
class Query:
def send(self, cursor: Cursor | None) -> Cursor:
...
def test(q: Query) -> None:
cursor = None
while not cursor or not cursor.finished():
cursor = q.send(cursor)
Expected: No error
Actual: Cursor | None is not assignable to None (caused by inconsistent types when breaking cycles)
Workaround at the moment is to explicitly annotate the cursor: Cursor | None variable.
Sandbox Link
https://pyrefly.org/sandbox/?project=N4IgZglgNgpgziAXKOBDAdgEwEYHsAeAdAA4CeSIAxlKnHAAQDCArgE5y6uIA669-9TDDD1I6CHAAWMTAAo4MKGACU9ALQA%2BenlxQefAYcLHevarQYBFZjFal9hoSIVZ5isABp6lNhy5NfTnoAH3oAOVx0GFVNAPZOB0MBY0JTLGF6ABd4TNkAR0R6a1tSGK0IqMT%2BH3jWegBecMiYXkMAd0loGHp0XEzvQLqg3v6av0IxCWk5ZSqksaDGvMIXOQXWZV4QDxBmTOg4EnJEEABiegBVfagITNJRZnRKfci4NKdRTgBbVEyAfXQzC%2B2FssnwhQg6EyZXocEyXFaAlYMEybD4YG4IDCQJB-mA%2BAAvpitjsyMiwFBSIRMrgvlAKOcAAqkcmU2EYHAEbyRSAAczYvwgkVS6HOAGUYN1JJlMsQ4IgAPQKsnCSmETi8hUwdAKzC4ShwBWUHkQfmsQWRBWfOqoABuqGgqGwsG56D5ApefFwxE9h14ZEykkialttjgQr4jUxAGZCABGABMxPQIAJO1QzwgoYAYl0KGgsHgiGRU0A
(Only applicable for extension issues) IDE Information
No response
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!
This is already fixed - to be honest I'm not sure when, it seems like the pin should have always been consistent with the result at the end of the loop but clearly it wasn't - my guess is that flow-merging changes that improved narrowing behavior somehow helped with this.
We have separate issues covering more complex cases that are still broken, so I will close this issue
Reopening this - the example was fixed but only because a bug in our flow handling moved the narrow outside of the loop
Once we put it back in, this example is broken and now that I look at it, it is unfixable with single-shot analysis - in single shot analysis, we must evaluate the narrow before the assignment that depends on it, but it's obviously impossible that the narrow could magically know about the type resulting from an assignment we haven't looked at yet.
So without a more powerful cycle analysis, we are not going to be able to infer this example. It's straightforward to work around in this simple case by annotating cursor as Cursor | None, but there exist cases that would be more difficult to handle.