Skip to content

Commit

Permalink
Support forward references in type aliases
Browse files Browse the repository at this point in the history
Summary:
Based on the conformance tests, forward references should be supported in scoped and explicit type aliases, but not in implicit type aliases.

For explicit type aliases, we unfortunately have to match on the "TypeAlias" name.

Reviewed By: samwgoldman, ndmitchell

Differential Revision: D66318809

fbshipit-source-id: ef8419e63006803f02f708e60608e6d5132366e1
  • Loading branch information
rchen152 authored and facebook-github-bot committed Nov 22, 2024
1 parent 1e30ddc commit 6596a92
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 47 deletions.
70 changes: 30 additions & 40 deletions pyre2/conformance/third_party/conformance.exp
Original file line number Diff line number Diff line change
Expand Up @@ -966,36 +966,6 @@
"name": "PyreError",
"stop_column": 32,
"stop_line": 69
},
{
"code": -2,
"column": 35,
"concise_description": "untype, got Literal['RecursiveUnion']",
"description": "untype, got Literal['RecursiveUnion']",
"line": 72,
"name": "PyreError",
"stop_column": 51,
"stop_line": 72
},
{
"code": -2,
"column": 37,
"concise_description": "untype, got Literal['MutualReference2']",
"description": "untype, got Literal['MutualReference2']",
"line": 75,
"name": "PyreError",
"stop_column": 55,
"stop_line": 75
},
{
"code": -2,
"column": 99,
"concise_description": "untype, got Literal['MutualReference1']",
"description": "untype, got Literal['MutualReference1']",
"line": 75,
"name": "PyreError",
"stop_column": 117,
"stop_line": 75
}
],
"aliases_type_statement.py": [
Expand Down Expand Up @@ -1089,6 +1059,16 @@
"stop_column": 30,
"stop_line": 23
},
{
"code": -2,
"column": 27,
"concise_description": "Could not parse type string: , got Expected an expression at byte range 791..791",
"description": "Could not parse type string: , got Expected an expression at byte range 791..791",
"line": 37,
"name": "PyreError",
"stop_column": 29,
"stop_line": 37
},
{
"code": -2,
"column": 27,
Expand Down Expand Up @@ -1171,22 +1151,32 @@
},
{
"code": -2,
"column": 42,
"concise_description": "Expected 0 positional argument(s)",
"description": "Expected 0 positional argument(s)",
"line": 40,
"column": 1,
"concise_description": "Expected `BadTypeAlias5` to be a type alias, got dict[Error, Error]",
"description": "Expected `BadTypeAlias5` to be a type alias, got dict[Error, Error]",
"line": 41,
"name": "PyreError",
"stop_column": 43,
"stop_line": 40
"stop_column": 32,
"stop_line": 41
},
{
"code": -2,
"column": 1,
"concise_description": "Expected `BadTypeAlias5` to be a type alias, got dict[str, str]",
"description": "Expected `BadTypeAlias5` to be a type alias, got dict[str, str]",
"column": 23,
"concise_description": "Could not find name `a`",
"description": "Could not find name `a`",
"line": 41,
"name": "PyreError",
"stop_column": 32,
"stop_column": 24,
"stop_line": 41
},
{
"code": -2,
"column": 28,
"concise_description": "Could not find name `b`",
"description": "Could not find name `b`",
"line": 41,
"name": "PyreError",
"stop_column": 29,
"stop_line": 41
},
{
Expand Down
2 changes: 2 additions & 0 deletions pyre2/conformance/third_party/conformance.result
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
"Line 50: Expected 1 errors",
"Line 51: Expected 1 errors",
"Line 52: Expected 1 errors",
"Line 72: Expected 1 errors",
"Line 75: Expected 1 errors",
"Line 14: Unexpected errors [\"untype, got Literal['Json']\", \"untype, got Literal['Json']\"]",
"Line 24: Unexpected errors [\"untype, got Literal['Json2']\", \"untype, got Literal['Json2']\"]",
"Line 30: Unexpected errors [\"untype, got Literal['RecursiveTuple']\"]",
Expand Down
4 changes: 2 additions & 2 deletions pyre2/conformance/third_party/results.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"pass": 7,
"fail": 126,
"pass_rate": 0.05,
"differences": 1489,
"differences": 1491,
"passing": [
"directives_no_type_check.py",
"directives_type_ignore.py",
Expand All @@ -17,7 +17,7 @@
"aliases_explicit.py": 19,
"aliases_implicit.py": 25,
"aliases_newtype.py": 10,
"aliases_recursive.py": 17,
"aliases_recursive.py": 19,
"aliases_type_statement.py": 17,
"aliases_typealiastype.py": 24,
"aliases_variance.py": 3,
Expand Down
17 changes: 12 additions & 5 deletions pyre2/pyre2/bin/alt/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1154,15 +1154,22 @@ impl<'a> BindingsBuilder<'a> {
let ann_val = if let Some(special) = SpecialForm::new(&name.id, &x.annotation) {
BindingAnnotation::Type(special.to_type())
} else {
BindingAnnotation::AnnotateExpr(*x.annotation, None)
BindingAnnotation::AnnotateExpr(*x.annotation.clone(), None)
};
let ann_key = self.table.insert(ann_key, ann_val);

if let Some(value) = x.value
if let Some(mut value) = x.value
&& (!self.module_info.is_interface()
|| !matches!(&*value, Expr::EllipsisLiteral(_)))
{
self.ensure_expr(&value);
// Handle forward references in explicit type aliases.
if let Expr::Name(name) = *x.annotation
&& name.id == "TypeAlias"
{
self.ensure_type(&mut value, &mut BindingsBuilder::forward_lookup);
} else {
self.ensure_expr(&value);
}
let range = value.range();
self.bind_definition(
&name.clone(),
Expand Down Expand Up @@ -1220,14 +1227,14 @@ impl<'a> BindingsBuilder<'a> {
}
_ => self.todo("Bindings::stmt AnnAssign", &x),
},
Stmt::TypeAlias(x) => {
Stmt::TypeAlias(mut x) => {
if let Expr::Name(name) = *x.name {
let qs = if let Some(params) = x.type_params {
self.type_params(&params)
} else {
Vec::new()
};
self.ensure_expr(&x.value);
self.ensure_type(&mut x.value, &mut BindingsBuilder::forward_lookup);
let expr_binding = Binding::Expr(None, *x.value);
let binding = Binding::ScopedTypeAlias(
name.id.clone(),
Expand Down
21 changes: 21 additions & 0 deletions pyre2/pyre2/bin/test/type_alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,24 @@ X2.__add__ # ok
X3.__add__ # E: Cannot use type alias `X3`
"#,
);

simple_test!(
test_forward_ref,
r#"
from typing import TypeAlias, assert_type
X1 = "A" # Just a normal alias to a str
X2: TypeAlias = "A" # Forward ref
type X3 = "A" # Forward ref
class A:
pass
def f(x1: X1): # E: untype, got Literal['A']
pass
def g(x2: X2, x3: X3):
assert_type(x2, A)
assert_type(x3, A)
"#,
);

0 comments on commit 6596a92

Please sign in to comment.