Skip to content

Commit 4b2ccf9

Browse files
rchen152meta-codesync[bot]
authored andcommitted
Use equivalence in assert_type (facebook#2702)
Summary: Pull Request resolved: facebook#2702 Changes `assert_type` to check for equivalence between types instead of normalizing types and and checking for equality. This is both more spec-compliant and simpler. Most of the test changes are because we no longer normalize `Unknown` to `Any`. Looks like we also recognize some more types as equivalent that we previously thought were different, in numpy. Differential Revision: D95641740
1 parent eadd15f commit 4b2ccf9

File tree

8 files changed

+28
-48
lines changed

8 files changed

+28
-48
lines changed

conformance/third_party/conformance.exp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,8 +1614,8 @@
16141614
{
16151615
"code": -2,
16161616
"column": 12,
1617-
"concise_description": "assert_type(Any, int) failed",
1618-
"description": "assert_type(Any, int) failed",
1617+
"concise_description": "assert_type(Unknown, int) failed",
1618+
"description": "assert_type(Unknown, int) failed",
16191619
"line": 96,
16201620
"name": "assert-type",
16211621
"severity": "error",
@@ -3688,8 +3688,8 @@
36883688
{
36893689
"code": -2,
36903690
"column": 12,
3691-
"concise_description": "assert_type(Class8[Any], Class8[str]) failed",
3692-
"description": "assert_type(Class8[Any], Class8[str]) failed",
3691+
"concise_description": "assert_type(Class8[Unknown], Class8[str]) failed",
3692+
"description": "assert_type(Class8[Unknown], Class8[str]) failed",
36933693
"line": 185,
36943694
"name": "assert-type",
36953695
"severity": "error",
@@ -5189,8 +5189,8 @@
51895189
{
51905190
"code": -2,
51915191
"column": 12,
5192-
"concise_description": "assert_type(int, Any) failed",
5193-
"description": "assert_type(int, Any) failed",
5192+
"concise_description": "assert_type(int, Unknown) failed",
5193+
"description": "assert_type(int, Unknown) failed",
51945194
"line": 116,
51955195
"name": "assert-type",
51965196
"severity": "error",
@@ -5222,8 +5222,8 @@
52225222
{
52235223
"code": -2,
52245224
"column": 20,
5225-
"concise_description": "assert_type(int, Any) failed",
5226-
"description": "assert_type(int, Any) failed",
5225+
"concise_description": "assert_type(int, Unknown) failed",
5226+
"description": "assert_type(int, Unknown) failed",
52275227
"line": 129,
52285228
"name": "assert-type",
52295229
"severity": "error",

conformance/third_party/conformance.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"annotations_coroutines.py": [],
1717
"annotations_forward_refs.py": [
1818
"Line 87: Unexpected errors ['Expected a type form, got instance of `(self: Self@ClassD) -> None`']",
19-
"Line 96: Unexpected errors ['assert_type(Any, int) failed']"
19+
"Line 96: Unexpected errors ['assert_type(Unknown, int) failed']"
2020
],
2121
"annotations_generators.py": [
2222
"Line 140: Unexpected errors ['This `yield` expression is unreachable']",
@@ -42,7 +42,7 @@
4242
"Line 186: Expected 1 errors",
4343
"Line 197: Expected 1 errors",
4444
"Line 167: Unexpected errors ['assert_type(Class7[int], Class7[str]) failed', \"Argument `Literal['']` is not assignable to parameter `x` with type `int`\"]",
45-
"Line 185: Unexpected errors ['assert_type(Class8[Any], Class8[str]) failed']"
45+
"Line 185: Unexpected errors ['assert_type(Class8[Unknown], Class8[str]) failed']"
4646
],
4747
"constructors_consistency.py": [],
4848
"dataclasses_descriptors.py": [

pyrefly/lib/alt/special_calls.rs

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ use crate::error::context::TypeCheckKind;
3737
use crate::types::callable::FunctionKind;
3838
use crate::types::callable::unexpected_keyword;
3939
use crate::types::class::Class;
40-
use crate::types::special_form::SpecialForm;
4140
use crate::types::tuple::Tuple;
4241
use crate::types::types::Type;
4342

@@ -53,34 +52,15 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
5352
let ret = if args.len() == 2 {
5453
let expr_a = &args[0];
5554
let expr_b = &args[1];
56-
let a = self.expr_infer_with_hint(expr_a, hint, errors);
57-
let b = self.expr_untype(expr_b, TypeFormContext::FunctionArgument, errors);
58-
let self_form = self.heap.mk_special_form(SpecialForm::SelfType);
59-
let normalize_type = |ty: Type, expr: &Expr| {
60-
let mut ty = self
61-
.canonicalize_all_class_types(
62-
self.solver().deep_force(ty),
63-
expr.range(),
64-
errors,
65-
)
66-
.promote_typevar_values(self.stdlib)
67-
.explicit_any()
68-
.explicit_literals()
69-
.noreturn_to_never()
70-
.nonetype_to_none()
71-
.anon_callables()
72-
.anon_typed_dicts(self.stdlib)
73-
.distribute_type_over_union(self.heap)
74-
.simplify_intersections();
75-
// Make assert_type(Self@SomeClass, typing.Self) work.
76-
ty.subst_self_type_mut(&self_form);
77-
// Re-sort unions & drop any display names.
78-
// Make sure to keep this as the final step before comparison.
79-
ty.sort_unions_and_drop_names()
80-
};
81-
let a = normalize_type(a, expr_a);
82-
let b = normalize_type(b, expr_b);
83-
if a != b {
55+
let a = self
56+
.solver()
57+
.deep_force(self.expr_infer_with_hint(expr_a, hint, errors));
58+
let b = self.solver().deep_force(self.expr_untype(
59+
expr_b,
60+
TypeFormContext::FunctionArgument,
61+
errors,
62+
));
63+
if !self.is_equivalent(&a, &b) {
8464
self.error(
8565
errors,
8666
range,

pyrefly/lib/test/annotation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class D:
1919
def int(self) -> None:
2020
...
2121
x: "int" = 0 # E: Expected a type form
22-
assert_type(D.x, int) # E: assert_type(Any, int) failed
22+
assert_type(D.x, int) # E: assert_type(Unknown, int) failed
2323
"#,
2424
);
2525

pyrefly/lib/test/attributes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ class A[T]:
333333
return cls(x)
334334
335335
assert_type(A[int].m(0), A[int])
336-
assert_type(A.m(0), A[int]) # TODO # E: assert_type(A[Any], A[int]) failed
336+
assert_type(A.m(0), A[int]) # TODO # E: assert_type(A[Unknown], A[int]) failed
337337
338338
def test_typevar_bounds[T: A[int]](x: type[T]):
339339
assert_type(x.m(0), A[int])

pyrefly/lib/test/callable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,7 @@ class Class8(Generic[T]):
14181418
14191419
r8 = accepts_callable(Class8)
14201420
# pyrefly incorrectly errors on this - should be OK
1421-
assert_type(r8([""], [""]), Class8[str]) # E: assert_type(Class8[Any], Class8[str]) failed
1421+
assert_type(r8([""], [""]), Class8[str]) # E: assert_type(Class8[Unknown], Class8[str]) failed
14221422
"#,
14231423
);
14241424

pyrefly/lib/test/contextual.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,8 @@ from typing import Callable, assert_type
377377
def f[**P, R](f: Callable[P, R], g: Callable[P, R]) -> Callable[P, R]: ...
378378
def g1(x: int, *args: int): ...
379379
def g2(x: int, **kwargs: str): ...
380-
x1 = f(g1, lambda x, *args: assert_type(args, tuple[int, ...])) # E: assert_type(Any, tuple[int, ...]) failed
381-
x2 = f(g2, lambda x, **kwargs: assert_type(kwargs, dict[str, str])) # E: assert_type(Any, dict[str, str]) failed
380+
x1 = f(g1, lambda x, *args: assert_type(args, tuple[int, ...])) # E: assert_type(Unknown, tuple[int, ...]) failed
381+
x2 = f(g2, lambda x, **kwargs: assert_type(kwargs, dict[str, str])) # E: assert_type(Unknown, dict[str, str]) failed
382382
"#,
383383
);
384384

@@ -622,8 +622,8 @@ testcase!(
622622
from typing import Protocol, assert_type, Any
623623
class Identity(Protocol):
624624
def __call__(self, *args: int, **kwargs: int) -> Any: ...
625-
x: Identity = lambda *args, **kwargs: assert_type(args, tuple[int, ...]) # E: assert_type(Any, tuple[int, ...]) failed
626-
y: Identity = lambda *args, **kwargs: assert_type(kwargs, dict[str, int]) # E: assert_type(Any, dict[str, int]) failed
625+
x: Identity = lambda *args, **kwargs: assert_type(args, tuple[int, ...]) # E: assert_type(Unknown, tuple[int, ...]) failed
626+
y: Identity = lambda *args, **kwargs: assert_type(kwargs, dict[str, int]) # E: assert_type(Unknown, dict[str, int]) failed
627627
"#,
628628
);
629629

pyrefly/lib/test/generic_restrictions.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ class C[T = int]:
267267
return self
268268
attr: T
269269
reveal_type(C.meth) # E: [T = int](self: C[T], /) -> C[T]
270-
assert_type(C.attr, int) # E: assert_type(Any, int) failed # E: Generic attribute `attr` of class `C` is not visible on the class
270+
assert_type(C.attr, int) # E: assert_type(Unknown, int) failed # E: Generic attribute `attr` of class `C` is not visible on the class
271271
"#,
272272
);
273273

@@ -1052,7 +1052,7 @@ testcase!(
10521052
r#"
10531053
from typing import assert_type
10541054
def f[T1, T2 = T1](x: T1, y: T2 | None = None) -> tuple[T1, T2]: ...
1055-
assert_type(f(1), tuple[int, int]) # E: assert_type(tuple[int, Any], tuple[int, int])
1055+
assert_type(f(1), tuple[int, int]) # E: assert_type(tuple[int, Unknown], tuple[int, int])
10561056
"#,
10571057
);
10581058

0 commit comments

Comments
 (0)