Pyrefly
a Python typechecker
pyrefly.org
Why Pyrefly?
Meta’s type checker Pyre did not meet all our needs
Existing type checkers might not meet our needs
Why Pyrefly?
Our goals for the project
Written in Rust
A Couple New Ideas
The Pyrefly sandbox includes a Python interpreter
Attribute access uses an API designed around the descriptor API
Multi-Pass Architecture
Pyrefly has three phases:
Each phase is about 10x more expensive than the previous one
Multi-Pass Architecture
The Binding phase produces a table of Bindings, forming a graph of computations of various kinds (most of them resolve to a TypeInfo, which is a Type with possible attribute narrows).
I like to think of this in two ways:
Binding Example
1: x = f()
2: if isinstance(x, int):
3: y = x
4: else:
5: y = “y”
6: #
7: y
Binding Example
1: x = f()
2: if isinstance(x, int):
3: y = x
4: else:
5: y = “y”
6: #
7: y
Use(f@line1) => Forward(...)
Def(x@line1) => Expr(x, f())
Binding Example
1: x = f()
2: if isinstance(x, int):
3: y = x
4: else:
5: y = “y”
6: #
7: y
Use(f@line1) => Forward(...)
Def(x@line1) => Expr(x, f())
Use(x@line2) => Forward(Def(x@line1))
Use(int@line2) => Forward(...)
Binding Example
1: x = f()
2: if isinstance(x, int):
3: y = x
4: else:
5: y = “y”
6: #
7: y
Use(f@line1) => Forward(...)
Def(x@line1) => Expr(x, f())
Use(x@line2) => Forward(Def(x@line1))
Use(int@line2) => Forward(...)
N(x@line2, line3) => N(Use(x@line2), IsInstance(int))
N(x@line2, line4) => N(Use(x@line2), NotInstance(int))
Binding Example
1: x = f()
2: if isinstance(x, int):
3: y = x
4: else:
5: y = “y”
6: #
7: y
Use(f@line1) => Forward(...)
Def(x@line1) => Expr(x, f())
Use(x@line2) => Forward(Def(x@line1))
Use(int@line2) => Forward(...)
N(x@line2, line3) => N(Use(x@line2), IsInstance(int))
N(x@line2, line4) => N(Use(x@line2), NotInstance(int))
Use(x@line3) => Forward(N(x@line2, line3))
Binding Example
1: x = f()
2: if isinstance(x, int):
3: y = x
4: else:
5: y = “y”
6: #
7: y
Use(f@line1) => Forward(...)
Def(x@line1) => Expr(x, f())
Use(x@line2) => Forward(Def(x@line1))
Use(int@line2) => Forward(...)
N(x@line2, line3) => N(Use(x@line2), IsInstance(int))
N(x@line2, line4) => N(Use(x@line2), NotInstance(int))
Use(x@line3) => Forward(N(x@line2, line3))
Def(y@line3) => Expr(x)
Binding Example
1: x = f()
2: if isinstance(x, int):
3: y = x
4: else:
5: y = “y”
6: #
7: y
Use(f@line1) => Forward(...)
Def(x@line1) => Expr(x, f())
Use(x@line2) => Forward(Def(x@line1))
Use(int@line2) => Forward(...)
N(x@line2, line3) => N(Use(x@line2), IsInstance(int))
N(x@line2, line4) => N(Use(x@line2), NotInstance(int))
Use(x@line3) => Forward(N(x@line2, line3))
Def(y@line3) => Expr(x)
Def(y@line5) => Expr(“y”)
Binding Example
1: x = f()
2: if isinstance(x, int):
3: y = x
4: else:
5: y = “y”
6: #
7: y
Use(f@line1) => Forward(...)
Def(x@line1) => Expr(x, f())
Use(x@line2) => Forward(Def(x@line1))
Use(int@line2) => Forward(...)
N(x@line2, line3) => N(Use(x@line2), IsInstance(int))
N(x@line2, line4) => N(Use(x@line2), NotInstance(int))
Use(x@line3) => Forward(N(x@line2, line3))
Def(y@line3) => Expr(x)
Def(y@line5) => Expr(“y”)
Phi(y@line6) => Phi(Def(y@line3), Def(y@line5))
Binding Example
1: x = f()
2: if isinstance(x, int):
3: y = x
4: else:
5: y = “y”
6: #
7: y
Use(f@line1) => Forward(...)
Def(x@line1) => Expr(x, f())
Use(x@line2) => Forward(Def(x@line1))
Use(int@line2) => Forward(...)
N(x@line2, line3) => N(Use(x@line2), IsInstance(int))
N(x@line2, line4) => N(Use(x@line2), NotInstance(int))
Use(x@line3) => Forward(N(x@line2, line3))
Def(y@line3) => Expr(x)
Def(y@line5) => Expr(“y”)
Phi(y@line6) => Phi(Def(y@line3), Def(y@line5))
Use(y@line7) => Forward(Phi(y@line6))
Binding Example
1: x = f()
2: if isinstance(x, int):
3: y = x
4: else:
5: y = “y”
6: #
7: y
Use(f@line1) => Forward(...)
Def(x@line1) => Expr(x, f())
Use(x@line2) => Forward(Def(x@line1))
Use(int@line2) => Forward(...)
N(x@line2, line3) => N(Use(x@line2), IsInstance(int))
N(x@line2, line4) => N(Use(x@line2), NotInstance(int))
Use(x@line3) => Forward(N(x@line2, line3))
Def(y@line3) => Expr(x)
Def(y@line5) => Expr(“y”)
Phi(y@line6) => Phi(Def(y@line3), Def(y@line5))
Use(y@line7) => Forward(Phi(y@line6))