-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[ty] Implement global handling and load-before-global-declaration syntax error
#17637
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
Changes from all commits
6cd7c58
f359e77
87b177e
2f211bf
bef6172
96443aa
11f4805
6e6422d
f35849e
fc0e337
361fa8c
c4ea922
4cdb754
5f9867e
16b19f1
610f050
5cda462
e3193bf
b5dc8c4
899ffb3
d0f9520
9aef28a
6d18f68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,8 +32,14 @@ def f(): | |
| y = "" | ||
|
|
||
| global x | ||
| # TODO: error: [invalid-assignment] "Object of type `Literal[""]` is not assignable to `int`" | ||
| # error: [invalid-assignment] "Object of type `Literal[""]` is not assignable to `int`" | ||
| x = "" | ||
|
|
||
| global z | ||
| # error: [invalid-assignment] "Object of type `Literal[""]` is not assignable to `int`" | ||
| z = "" | ||
|
|
||
| z: int | ||
| ``` | ||
|
|
||
| ## Nested intervening scope | ||
|
|
@@ -48,8 +54,7 @@ def outer(): | |
|
|
||
| def inner(): | ||
| global x | ||
| # TODO: revealed: int | ||
| reveal_type(x) # revealed: str | ||
| reveal_type(x) # revealed: int | ||
| ``` | ||
|
|
||
| ## Narrowing | ||
|
|
@@ -87,8 +92,7 @@ def f(): | |
| ```py | ||
| def f(): | ||
| global x | ||
| # TODO this should also not be an error | ||
| y = x # error: [unresolved-reference] "Name `x` used when not defined" | ||
| y = x | ||
| x = 1 # No error. | ||
|
|
||
| x = 2 | ||
|
|
@@ -99,79 +103,111 @@ x = 2 | |
| Using a name prior to its `global` declaration in the same scope is a syntax error. | ||
|
|
||
| ```py | ||
| x = 1 | ||
|
|
||
| def f(): | ||
| print(x) # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x | ||
| print(x) | ||
| global x # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| print(x) | ||
|
|
||
| def f(): | ||
| global x | ||
| print(x) # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x | ||
| print(x) | ||
| global x # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| print(x) | ||
|
|
||
| def f(): | ||
| print(x) # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x, y | ||
| print(x) | ||
| global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| print(x) | ||
|
|
||
| def f(): | ||
| global x, y | ||
| print(x) # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x, y | ||
| print(x) | ||
| global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| print(x) | ||
|
|
||
| def f(): | ||
| x = 1 # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x | ||
| x = 1 | ||
| global x # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| x = 1 | ||
|
|
||
| def f(): | ||
| global x | ||
| x = 1 # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x | ||
| x = 1 | ||
| global x # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| x = 1 | ||
|
|
||
| def f(): | ||
| del x # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x, y | ||
| del x | ||
| global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| del x | ||
|
|
||
| def f(): | ||
| global x, y | ||
| del x # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x, y | ||
| del x | ||
| global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| del x | ||
|
|
||
| def f(): | ||
| del x # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x | ||
| del x | ||
| global x # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| del x | ||
|
|
||
| def f(): | ||
| global x | ||
| del x # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x | ||
| del x | ||
| global x # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| del x | ||
|
|
||
| def f(): | ||
| del x # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x, y | ||
| del x | ||
| global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| del x | ||
|
|
||
| def f(): | ||
| global x, y | ||
| del x # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x, y | ||
| del x | ||
| global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| del x | ||
|
|
||
| def f(): | ||
| print(f"{x=}") # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x | ||
| print(f"{x=}") | ||
| global x # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
|
|
||
| # still an error in module scope | ||
| x = None # TODO: error: [invalid-syntax] name `x` is used prior to global declaration | ||
| global x | ||
| x = None | ||
| global x # error: [invalid-syntax] "name `x` is used prior to global declaration" | ||
| ``` | ||
|
|
||
| ## Local bindings override preceding `global` bindings | ||
|
|
||
| ```py | ||
| x = 42 | ||
|
|
||
| def f(): | ||
| global x | ||
| reveal_type(x) # revealed: Unknown | Literal[42] | ||
| x = "56" | ||
| reveal_type(x) # revealed: Literal["56"] | ||
| ``` | ||
|
|
||
| ## Local assignment prevents falling back to the outer scope | ||
|
|
||
| ```py | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably best to give this a separate heading. Two unnamed code blocks under a single heading are merged into a single file for checking. Seems to be ok here at the moment, but this can lead to future confusion when we're dealing with global scope. E.g. at the moment the first test is only showing
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I see, thanks! This was a good excuse to come up with better headings anyway. |
||
| x = 42 | ||
|
|
||
| def f(): | ||
| # error: [unresolved-reference] "Name `x` used when not defined" | ||
| reveal_type(x) # revealed: Unknown | ||
| x = "56" | ||
| reveal_type(x) # revealed: Literal["56"] | ||
| ``` | ||
|
|
||
| ## Annotating a `global` binding is a syntax error | ||
|
|
||
| ```py | ||
| x: int = 1 | ||
|
|
||
| def f(): | ||
| global x | ||
| x: str = "foo" # TODO: error: [invalid-syntax] "annotated name 'x' can't be global" | ||
| ``` | ||
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.
I think there's a set of cases that we aren't testing, which is when we mark a name
globalin a nested scope, and then declare that name with a new type in that scope, e.g.We should definitely error on that assignment, since it's an assignment to the global with the wrong type. I think we should probably error any time a
globalname is declared with a type in the local scope, even if the type is the same -- that kind of local re-declaration of a global gives the wrong impression and doesn't make sense.It's OK if this is a TODO in this PR, but we should at least add a TODO for it.
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.
that's actually a (currently unimplemented?) syntax error to provide a local-scope annotation for a name declared global. So I don't think we should emit an error about it in type inference — that would lead to unnecessary double diagnostics.
We should implement the syntax error (either here or in a followup PR) though! And we should add tests now.
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.
Oh cool, didn't realize that was a syntax error -- even if the declaration comes after the
globalstatement. Agreed.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.
I added this to the
globalmdtest as a TODO and added the syntax error to #17412!