Skip to content

Commit ea38aa7

Browse files
rchen152stroxler
authored andcommitted
Finish up iterate() implementation
Summary: Adds support for unions to `iterate()`. With this, the conformance tests contain no more TODOs related to iteration, so we can replace the TODO error message with a non-TODO one. Reviewed By: yangdanny97 Differential Revision: D67680285 fbshipit-source-id: 4cafd7518d9b079697aecd38a685ba36399376f3
1 parent ab7b8ef commit ea38aa7

File tree

5 files changed

+143
-129
lines changed

5 files changed

+143
-129
lines changed

pyre2/conformance/third_party/conformance.exp

Lines changed: 18 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -20302,21 +20302,21 @@
2030220302
{
2030320303
"code": -2,
2030420304
"column": 14,
20305-
"concise_description": "TODO: Answers::solve_binding - Binding::IterableValue",
20306-
"description": "TODO: Answers::solve_binding - Binding::IterableValue",
20305+
"concise_description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 2) into 1 value",
20306+
"description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 2) into 1 value",
2030720307
"line": 93,
2030820308
"name": "PyreError",
2030920309
"stop_column": 18,
2031020310
"stop_line": 93
2031120311
},
2031220312
{
2031320313
"code": -2,
20314-
"column": 15,
20315-
"concise_description": "TODO: Answers::solve_binding - Binding::IterableValue",
20316-
"description": "TODO: Answers::solve_binding - Binding::IterableValue",
20314+
"column": 14,
20315+
"concise_description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 3) into 1 value",
20316+
"description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 3) into 1 value",
2031720317
"line": 93,
2031820318
"name": "PyreError",
20319-
"stop_column": 16,
20319+
"stop_column": 18,
2032020320
"stop_line": 93
2032120321
},
2032220322
{
@@ -20332,31 +20332,21 @@
2033220332
{
2033320333
"code": -2,
2033420334
"column": 14,
20335-
"concise_description": "TODO: Answers::solve_binding - Binding::IterableValue",
20336-
"description": "TODO: Answers::solve_binding - Binding::IterableValue",
20335+
"concise_description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 1) into 2 values",
20336+
"description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 1) into 2 values",
2033720337
"line": 97,
2033820338
"name": "PyreError",
2033920339
"stop_column": 20,
2034020340
"stop_line": 97
2034120341
},
2034220342
{
2034320343
"code": -2,
20344-
"column": 15,
20345-
"concise_description": "TODO: Answers::solve_binding - Binding::IterableValue",
20346-
"description": "TODO: Answers::solve_binding - Binding::IterableValue",
20347-
"line": 97,
20348-
"name": "PyreError",
20349-
"stop_column": 16,
20350-
"stop_line": 97
20351-
},
20352-
{
20353-
"code": -2,
20354-
"column": 18,
20355-
"concise_description": "TODO: Answers::solve_binding - Binding::IterableValue",
20356-
"description": "TODO: Answers::solve_binding - Binding::IterableValue",
20344+
"column": 14,
20345+
"concise_description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 3) into 2 values",
20346+
"description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 3) into 2 values",
2035720347
"line": 97,
2035820348
"name": "PyreError",
20359-
"stop_column": 19,
20349+
"stop_column": 20,
2036020350
"stop_line": 97
2036120351
},
2036220352
{
@@ -20372,41 +20362,21 @@
2037220362
{
2037320363
"code": -2,
2037420364
"column": 14,
20375-
"concise_description": "TODO: Answers::solve_binding - Binding::IterableValue",
20376-
"description": "TODO: Answers::solve_binding - Binding::IterableValue",
20365+
"concise_description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 1) into 3 values",
20366+
"description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 1) into 3 values",
2037720367
"line": 101,
2037820368
"name": "PyreError",
2037920369
"stop_column": 23,
2038020370
"stop_line": 101
2038120371
},
2038220372
{
2038320373
"code": -2,
20384-
"column": 15,
20385-
"concise_description": "TODO: Answers::solve_binding - Binding::IterableValue",
20386-
"description": "TODO: Answers::solve_binding - Binding::IterableValue",
20387-
"line": 101,
20388-
"name": "PyreError",
20389-
"stop_column": 16,
20390-
"stop_line": 101
20391-
},
20392-
{
20393-
"code": -2,
20394-
"column": 18,
20395-
"concise_description": "TODO: Answers::solve_binding - Binding::IterableValue",
20396-
"description": "TODO: Answers::solve_binding - Binding::IterableValue",
20397-
"line": 101,
20398-
"name": "PyreError",
20399-
"stop_column": 19,
20400-
"stop_line": 101
20401-
},
20402-
{
20403-
"code": -2,
20404-
"column": 21,
20405-
"concise_description": "TODO: Answers::solve_binding - Binding::IterableValue",
20406-
"description": "TODO: Answers::solve_binding - Binding::IterableValue",
20374+
"column": 14,
20375+
"concise_description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 2) into 3 values",
20376+
"description": "Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 2) into 3 values",
2040720377
"line": 101,
2040820378
"name": "PyreError",
20409-
"stop_column": 22,
20379+
"stop_column": 23,
2041020380
"stop_line": 101
2041120381
},
2041220382
{

pyre2/conformance/third_party/conformance.result

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,11 +1479,11 @@
14791479
"Line 78: Unexpected errors ['assert_type(tuple[int] | tuple[int, Any, int] | tuple[str, str], tuple[int, int] | tuple[str, str]) failed']",
14801480
"Line 82: Unexpected errors ['assert_type(tuple[int] | tuple[int, Any, int] | tuple[str, str], tuple[int, str, int]) failed']",
14811481
"Line 91: Unexpected errors ['TODO: Starred(ExprStarred - Answers::expr_infer']",
1482-
"Line 93: Unexpected errors ['TODO: Answers::solve_binding - Binding::IterableValue', 'TODO: Answers::solve_binding - Binding::IterableValue']",
1482+
"Line 93: Unexpected errors ['Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 2) into 1 value', 'Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 3) into 1 value']",
14831483
"Line 95: Unexpected errors ['assert_type(tuple[int] | tuple[int, Any, int] | tuple[str, str], tuple[int]) failed']",
1484-
"Line 97: Unexpected errors ['TODO: Answers::solve_binding - Binding::IterableValue', 'TODO: Answers::solve_binding - Binding::IterableValue', 'TODO: Answers::solve_binding - Binding::IterableValue']",
1484+
"Line 97: Unexpected errors ['Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 1) into 2 values', 'Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 3) into 2 values']",
14851485
"Line 99: Unexpected errors ['assert_type(tuple[int] | tuple[int, Any, int] | tuple[str, str], tuple[int, int] | tuple[str, str]) failed']",
1486-
"Line 101: Unexpected errors ['TODO: Answers::solve_binding - Binding::IterableValue', 'TODO: Answers::solve_binding - Binding::IterableValue', 'TODO: Answers::solve_binding - Binding::IterableValue', 'TODO: Answers::solve_binding - Binding::IterableValue']",
1486+
"Line 101: Unexpected errors ['Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 1) into 3 values', 'Cannot unpack tuple[int] | tuple[int, Error, int] | tuple[str, str] (of size 2) into 3 values']",
14871487
"Line 103: Unexpected errors ['assert_type(tuple[int] | tuple[int, Any, int] | tuple[str, str], tuple[int, str, int]) failed']",
14881488
"Line 115: Unexpected errors ['assert_type(tuple[int | str, int | str], tuple[int | str, str]) failed']",
14891489
"Line 117: Unexpected errors ['assert_type(tuple[int | str, int | str], tuple[int | str, int]) failed']",

pyre2/pyre2/bin/alt/answers.rs

Lines changed: 91 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
685685
Iterable::OfType(ty)
686686
}
687687

688-
fn iterate(&self, iterable: &Type, range: TextRange) -> Iterable {
688+
fn iterate(&self, iterable: &Type, range: TextRange) -> Vec<Iterable> {
689689
match iterable {
690690
Type::ClassType(cls) => {
691691
let call_method = |method_name: &Name,
@@ -699,23 +699,26 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
699699
.unwrap_or_else(|| {
700700
self.error(range, format!("Class `{}` is not iterable", cls.name()))
701701
});
702-
Iterable::OfType(ty)
702+
vec![Iterable::OfType(ty)]
703703
}
704-
Type::Tuple(Tuple::Concrete(elts)) => Iterable::FixedLen(elts.clone()),
705-
Type::Tuple(Tuple::Unbounded(box elt)) => Iterable::OfType(elt.clone()),
706-
Type::LiteralString => Iterable::OfType(self.stdlib.str().to_type()),
707-
Type::Literal(lit) if lit.is_string() => Iterable::OfType(self.stdlib.str().to_type()),
708-
Type::Any(_) => Iterable::OfType(Type::any_implicit()),
704+
Type::Tuple(Tuple::Concrete(elts)) => vec![Iterable::FixedLen(elts.clone())],
705+
Type::Tuple(Tuple::Unbounded(box elt)) => vec![Iterable::OfType(elt.clone())],
706+
Type::LiteralString => vec![Iterable::OfType(self.stdlib.str().to_type())],
707+
Type::Literal(lit) if lit.is_string() => {
708+
vec![Iterable::OfType(self.stdlib.str().to_type())]
709+
}
710+
Type::Any(_) => vec![Iterable::OfType(Type::any_implicit())],
709711
Type::Var(v) if let Some(_guard) = self.recurser.recurse(*v) => {
710712
self.iterate(&self.solver().force_var(*v), range)
711713
}
712714
Type::ClassDef(cls) => {
713-
self.iterate_by_metaclass(&self.promote_to_class_type(cls, range), range)
715+
vec![self.iterate_by_metaclass(&self.promote_to_class_type(cls, range), range)]
714716
}
715-
Type::Type(box Type::ClassType(cls)) => self.iterate_by_metaclass(cls, range),
716-
_ => Iterable::OfType(
717-
self.error_todo("Answers::solve_binding - Binding::IterableValue", range),
718-
),
717+
Type::Type(box Type::ClassType(cls)) => vec![self.iterate_by_metaclass(cls, range)],
718+
Type::Union(ts) => ts.iter().flat_map(|t| self.iterate(t, range)).collect(),
719+
_ => vec![Iterable::OfType(
720+
self.error(range, format!("`{iterable}` is not iterable")),
721+
)],
719722
}
720723
}
721724

@@ -1050,11 +1053,15 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
10501053
let ty = ann.map(|k| self.get_idx(k));
10511054
let hint =
10521055
ty.and_then(|x| x.ty.clone().map(|ty| self.stdlib.iterable(ty).to_type()));
1053-
let iterable = self.iterate(&self.expr(e, hint.as_ref()), e.range());
1054-
match iterable {
1055-
Iterable::OfType(ty) => ty,
1056-
Iterable::FixedLen(ts) => self.unions(&ts),
1056+
let iterables = self.iterate(&self.expr(e, hint.as_ref()), e.range());
1057+
let mut values = Vec::new();
1058+
for iterable in iterables {
1059+
match iterable {
1060+
Iterable::OfType(ty) => values.push(ty),
1061+
Iterable::FixedLen(ts) => values.extend(ts),
1062+
}
10571063
}
1064+
self.unions(&values)
10581065
}
10591066
Binding::ContextValue(ann, e, kind) => {
10601067
let context_manager = self.expr(e, None);
@@ -1082,80 +1089,86 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
10821089
)
10831090
}
10841091
Binding::UnpackedValue(b, range, pos) => {
1085-
let iterable = self.iterate(&self.solve_binding_inner(b), *range);
1086-
match iterable {
1087-
Iterable::OfType(ty) => match pos {
1088-
UnpackedPosition::Index(_) | UnpackedPosition::ReverseIndex(_) => ty,
1089-
UnpackedPosition::Slice(_, _) => self.stdlib.list(ty).to_type(),
1090-
},
1091-
Iterable::FixedLen(ts) => {
1092-
match pos {
1093-
UnpackedPosition::Index(i) | UnpackedPosition::ReverseIndex(i) => {
1094-
let idx = if matches!(pos, UnpackedPosition::Index(_)) {
1095-
*i
1096-
} else {
1097-
ts.len() - *i
1098-
};
1099-
if let Some(element) = ts.get(idx) {
1100-
element.clone()
1101-
} else {
1102-
// We'll report this error when solving for Binding::UnpackedLength.
1103-
Type::any_error()
1092+
let iterables = self.iterate(&self.solve_binding_inner(b), *range);
1093+
let mut values = Vec::new();
1094+
for iterable in iterables {
1095+
values.push(match iterable {
1096+
Iterable::OfType(ty) => match pos {
1097+
UnpackedPosition::Index(_) | UnpackedPosition::ReverseIndex(_) => ty,
1098+
UnpackedPosition::Slice(_, _) => self.stdlib.list(ty).to_type(),
1099+
},
1100+
Iterable::FixedLen(ts) => {
1101+
match pos {
1102+
UnpackedPosition::Index(i) | UnpackedPosition::ReverseIndex(i) => {
1103+
let idx = if matches!(pos, UnpackedPosition::Index(_)) {
1104+
*i
1105+
} else {
1106+
ts.len() - *i
1107+
};
1108+
if let Some(element) = ts.get(idx) {
1109+
element.clone()
1110+
} else {
1111+
// We'll report this error when solving for Binding::UnpackedLength.
1112+
Type::any_error()
1113+
}
11041114
}
1105-
}
1106-
UnpackedPosition::Slice(i, j) => {
1107-
let start = *i;
1108-
let end = ts.len() - *j;
1109-
if start <= ts.len() && end >= start {
1110-
let elem_ty = self.unions(&ts[start..end]);
1111-
self.stdlib.list(elem_ty).to_type()
1112-
} else {
1113-
// We'll report this error when solving for Binding::UnpackedLength.
1114-
Type::any_error()
1115+
UnpackedPosition::Slice(i, j) => {
1116+
let start = *i;
1117+
let end = ts.len() - *j;
1118+
if start <= ts.len() && end >= start {
1119+
let elem_ty = self.unions(&ts[start..end]);
1120+
self.stdlib.list(elem_ty).to_type()
1121+
} else {
1122+
// We'll report this error when solving for Binding::UnpackedLength.
1123+
Type::any_error()
1124+
}
11151125
}
11161126
}
11171127
}
1118-
}
1128+
})
11191129
}
1130+
self.unions(&values)
11201131
}
11211132
Binding::UnpackedLength(b, range, expect) => {
11221133
let iterable_ty = self.solve_binding_inner(b);
1123-
let iterable = self.iterate(&iterable_ty, *range);
1124-
match iterable {
1125-
Iterable::OfType(_) => {}
1126-
Iterable::FixedLen(ts) => {
1127-
let error = match expect {
1128-
SizeExpectation::Eq(n) => {
1129-
if ts.len() == *n {
1130-
None
1131-
} else {
1132-
match n {
1133-
1 => Some(format!("{n} value")),
1134-
_ => Some(format!("{n} values")),
1134+
let iterables = self.iterate(&iterable_ty, *range);
1135+
for iterable in iterables {
1136+
match iterable {
1137+
Iterable::OfType(_) => {}
1138+
Iterable::FixedLen(ts) => {
1139+
let error = match expect {
1140+
SizeExpectation::Eq(n) => {
1141+
if ts.len() == *n {
1142+
None
1143+
} else {
1144+
match n {
1145+
1 => Some(format!("{n} value")),
1146+
_ => Some(format!("{n} values")),
1147+
}
11351148
}
11361149
}
1137-
}
1138-
SizeExpectation::Ge(n) => {
1139-
if ts.len() >= *n {
1140-
None
1141-
} else {
1142-
Some(format!("{n}+ values"))
1150+
SizeExpectation::Ge(n) => {
1151+
if ts.len() >= *n {
1152+
None
1153+
} else {
1154+
Some(format!("{n}+ values"))
1155+
}
11431156
}
1157+
};
1158+
match error {
1159+
Some(expectation) => {
1160+
self.error(
1161+
*range,
1162+
format!(
1163+
"Cannot unpack {} (of size {}) into {}",
1164+
iterable_ty,
1165+
ts.len(),
1166+
expectation,
1167+
),
1168+
);
1169+
}
1170+
None => {}
11441171
}
1145-
};
1146-
match error {
1147-
Some(expectation) => {
1148-
self.error(
1149-
*range,
1150-
format!(
1151-
"Cannot unpack {} (of size {}) into {}",
1152-
iterable_ty,
1153-
ts.len(),
1154-
expectation,
1155-
),
1156-
);
1157-
}
1158-
None => {}
11591172
}
11601173
}
11611174
}

pyre2/pyre2/bin/test/simple.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,14 @@ def f(x: A):
829829
"#,
830830
);
831831

832+
testcase!(
833+
test_not_iterable,
834+
r#"
835+
for _ in None: # E: `None` is not iterable
836+
pass
837+
"#,
838+
);
839+
832840
testcase!(
833841
test_assert,
834842
r#"

pyre2/pyre2/bin/test/tuple.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,26 @@ from typing import Iterable
131131
x: Iterable[str] = ()
132132
"#,
133133
);
134+
135+
testcase!(
136+
test_unpack_union,
137+
r#"
138+
from typing import assert_type
139+
def f() -> tuple[int, str] | tuple[bool, ...]: ...
140+
(x, y) = f()
141+
assert_type(x, int | bool)
142+
assert_type(y, str | bool)
143+
144+
(x, y, z) = f() # E: Cannot unpack
145+
"#,
146+
);
147+
148+
testcase!(
149+
test_iterate_union,
150+
r#"
151+
from typing import assert_type
152+
def f() -> tuple[int, str] | tuple[bool, ...]: ...
153+
for x in f():
154+
assert_type(x, int | bool | str)
155+
"#,
156+
);

0 commit comments

Comments
 (0)