Skip to content

Commit 644eca7

Browse files
yangdanny97facebook-github-bot
authored andcommitted
promote anonymous typed dicts when unioning (#2351)
Summary: Pull Request resolved: #2351 Differential Revision: D92586637
1 parent 50481d3 commit 644eca7

File tree

3 files changed

+31
-2
lines changed

3 files changed

+31
-2
lines changed

crates/pyrefly_types/src/simplify.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::class::ClassType;
1313
use crate::literal::Lit;
1414
use crate::stdlib::Stdlib;
1515
use crate::tuple::Tuple;
16+
use crate::typed_dict::TypedDict;
1617
use crate::types::Type;
1718
use crate::types::Union;
1819

@@ -74,6 +75,7 @@ fn unions_internal(
7475
let mut res = flatten_and_dedup(xs);
7576
if let Some(stdlib) = stdlib {
7677
collapse_literals(&mut res, stdlib, enum_members.unwrap_or(&|_| None));
78+
promote_anonymous_typed_dicts(&mut res, stdlib);
7779
}
7880
collapse_tuple_unions_with_empty(&mut res);
7981
// `res` is collapsible again if `flatten_and_dedup` drops `xs` to 0 or 1 elements
@@ -254,6 +256,17 @@ fn collapse_literals(
254256
}
255257
}
256258

259+
/// Promote anonymous typed dicts to `dict[str, value_type]`
260+
fn promote_anonymous_typed_dicts(types: &mut [Type], stdlib: &Stdlib) {
261+
for ty in types.iter_mut() {
262+
if let Type::TypedDict(TypedDict::Anonymous(inner)) = ty {
263+
*ty = stdlib
264+
.dict(stdlib.str().clone().to_type(), inner.value_type.clone())
265+
.to_type();
266+
}
267+
}
268+
}
269+
257270
fn collapse_tuple_unions_with_empty(types: &mut Vec<Type>) {
258271
let Some(empty_idx) = types.iter().position(|t| match t {
259272
Type::Tuple(Tuple::Concrete(elts)) => elts.is_empty(),

pyrefly/lib/test/attributes.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2284,7 +2284,8 @@ class A:
22842284
self.y = {"x": 0} if check else 42
22852285
def f(a: A):
22862286
x: TD = a.x
2287-
y: TD | int = a.y
2287+
# anoynmous typed dicts are promoted away when unioned
2288+
y: dict[str, int] | int = a.y
22882289
"#,
22892290
);
22902291

pyrefly/lib/test/dict.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@ dict(x = 1, y = "test")
1414
"#,
1515
);
1616

17+
testcase!(
18+
test_anonymous_typed_dict_union_promotion,
19+
r#"
20+
from typing import assert_type
21+
22+
def test(cond: bool):
23+
x = {"a": 1, "b": "2"}
24+
y = {"a": 1, "b": "2", "c": 3}
25+
# we promote anonymous typed dicts when unioning
26+
z = x if cond else y
27+
assert_type(z["a"], int | str)
28+
assert_type(z, dict[str, int | str])
29+
"#,
30+
);
31+
1732
testcase!(
1833
test_unpack_empty,
1934
r#"
@@ -48,6 +63,6 @@ def bar(yes: bool) -> None:
4863
else:
4964
kwargs = {"goodbye": 1}
5065
51-
foo(**kwargs)
66+
foo(**kwargs)
5267
"#,
5368
);

0 commit comments

Comments
 (0)