Skip to content

Conversation

@cormacrelf
Copy link
Contributor

@cormacrelf cormacrelf commented Nov 19, 2025

This fixes a number of issues in the typechecker. I think it's fair to say before this PR we were only really checking function call sites and return types. The improvements here make type annotations on regular bindings significantly more useful. In general, we behave a lot more like a Python typechecker, as I looked at what basedpyright does in these situations and made starlark do that.

  1. x: str = "" checked the immediately assigned expression, but no other assignments to x. In fact x was fed to the binding solver anyway, which meant a binding could hold whatever you wanted it to. Now we obey the annotation for all assignments, including locals from function parameters. That's almost all of the churn in the prelude, reassigning to function parameters with a different type.
  2. x: str = ...; x: int = ... was accepted and was equivalent to int | str because of point (1). Now this is disallowed, you can only annotate a binding once.
  3. x[0]: int = 5 was allowed and ignored. No longer.
  4. Top level module bindings were not checked. Now they are. I wrote a recursive binding solver that checks the current scope before any child defs, so this manages to feed even fewer bindings to solve_bindings at once than before.
  5. I forget if this worked before this PR, probably not because of point (1), but I added tests and made sure x: str = ...; x, y = (1, 2) was caught as a type error.

I fixed the prelude's type errors, using some separate LSP improvements beyond the ones I've submitted already.

As for reviewers, maybe @ndmitchell? A few old diff messages refer to you as knowing the most about this.


A couple of TODOs for me:

  • change the golden test not to have double decl
  • failing B2IFB test because assert_eq redefined

@meta-cla meta-cla bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 19, 2025
@meta-codesync
Copy link
Contributor

meta-codesync bot commented Nov 19, 2025

@facebook-github-bot has imported this pull request. If you are a Meta employee, you can view this in D87432029. (Because this pull request was imported automatically, there will not be any future comments.)

This adds BindingType.

This distinguishes annotated types from solved ones, and means
we no longer try to solve types that the user annotated.
We need to do this without stopping the walk of the AST
so that LSP can continue typechecking if it wants.
Do not allow `x=5; x: str = ""`, or `x: int = ...; x: str = ...`,
or `def func(): pass; def func(): pass`. This is invalid in python,
so should not be valid starlark.

Updates a golden test, which made this error. Also removes a `def assert_eq`
in another test as the function already exists.
This is invalid python, and starlark is meant to be a subset.
There was a test that this at least parsed, but such annotations were
completely ignored anyway.
For use in BindingsCollect.
Previously, we gathered and solved bindings for every top-level def,
and not for a module's root bindings.

This was a deliberate limitation for typechecker performance reasons,
introduced in D48752028/D48752027 back in 2023.

This completes the work described in those diffs, which said ideally:

1. typecheck top-level assignments
2. then evaluate defs

This does one better and typechecks even nested defs individually. This achieves the
goal of reducing the iterations needed in solve_bindings by passing the
smallest possible quantity of bindings to solve_bindings at any one time.
Add a type check for any assignment to a binding that was annotated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant