Skip to content

Commit 624ea05

Browse files
pradeep90facebook-github-bot
authored andcommitted
Resolve concatenation of tuples via unpacking.
Summary: Problem: We used to resolve `(1, *(2, 3))` as `Tuple[int, unknown]`. This was because we correctly resolved `(2, 3)` as `Tuple[int, int]` and then dropped the ball on the `Starred` expression because there is no type to express a naked unpacked tuple. With variadic tuples, we can handle different kinds of unpacked tuples. Reviewed By: dkgi Differential Revision: D26821042 fbshipit-source-id: 7aad420ca4b2775e32617077a4f1d0e965efff30
1 parent 09e8a45 commit 624ea05

File tree

4 files changed

+149
-11
lines changed

4 files changed

+149
-11
lines changed

source/analysis/analysisError.ml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ and unsupported_operand_kind =
217217
and illegal_annotation_target_kind =
218218
| InvalidExpression
219219
| Reassignment
220+
221+
and tuple_concatenation_problem =
222+
| MultipleVariadics of { variadic_expressions: Expression.t list }
223+
| UnpackingNonTuple of { annotation: Type.t }
220224
[@@deriving compare, eq, sexp, show, hash]
221225

222226
type invalid_decoration = {
@@ -397,6 +401,7 @@ and kind =
397401
variable: Type.Variable.t;
398402
base: polymorphism_base_class;
399403
}
404+
| TupleConcatenationError of tuple_concatenation_problem
400405
(* Additional errors. *)
401406
(* TODO(T38384376): split this into a separate module. *)
402407
| DeadStore of Identifier.t
@@ -465,6 +470,7 @@ let code = function
465470
| IncompatibleAsyncGeneratorReturnType _ -> 57
466471
| UnsupportedOperand _ -> 58
467472
| DuplicateTypeVariables _ -> 59
473+
| TupleConcatenationError _ -> 60
468474
| ParserFailure _ -> 404
469475
(* Additional errors. *)
470476
| UnawaitedAwaitable _ -> 1001
@@ -534,6 +540,7 @@ let name = function
534540
| UnsupportedOperand _ -> "Unsupported operand"
535541
| UnusedIgnore _ -> "Unused ignore"
536542
| UnusedLocalMode _ -> "Unused local mode"
543+
| TupleConcatenationError _ -> "Unable to concatenate tuple"
537544

538545

539546
let weaken_literals kind =
@@ -1924,6 +1931,14 @@ let rec messages ~concise ~signature location kind =
19241931
(if provided > 1 then "were" else "was");
19251932
]
19261933
| Top -> ["Problem with analysis."]
1934+
| TupleConcatenationError (UnpackingNonTuple { annotation }) ->
1935+
[Format.asprintf "Expected to unpack a tuple, but got `%a`." pp_type annotation]
1936+
| TupleConcatenationError (MultipleVariadics { variadic_expressions }) ->
1937+
[
1938+
Format.asprintf
1939+
"Concatenation not yet support for multiple variadic tuples: `%s`."
1940+
(String.concat ~sep:", " (List.map ~f:show_sanitized_expression variadic_expressions));
1941+
]
19271942
| TypedDictionaryAccessWithNonLiteral acceptable_keys ->
19281943
let explanation =
19291944
let acceptable_keys =
@@ -2409,6 +2424,7 @@ let due_to_analysis_limitations { kind; _ } =
24092424
| PrivateProtocolProperty _
24102425
| ProhibitedAny _
24112426
| TooManyArguments _
2427+
| TupleConcatenationError _
24122428
| TypedDictionaryAccessWithNonLiteral _
24132429
| TypedDictionaryKeyNotFound _
24142430
| TypedDictionaryInitializationError _
@@ -2727,6 +2743,7 @@ let less_or_equal ~resolution left right =
27272743
| UnsafeCast _, _
27282744
| TooManyArguments _, _
27292745
| Top, _
2746+
| TupleConcatenationError _, _
27302747
| TypedDictionaryAccessWithNonLiteral _, _
27312748
| TypedDictionaryInvalidOperation _, _
27322749
| TypedDictionaryInitializationError _, _
@@ -3152,6 +3169,7 @@ let join ~resolution left right =
31523169
| RevealedType _, _
31533170
| UnsafeCast _, _
31543171
| TooManyArguments _, _
3172+
| TupleConcatenationError _, _
31553173
| TypedDictionaryAccessWithNonLiteral _, _
31563174
| TypedDictionaryKeyNotFound _, _
31573175
| TypedDictionaryInvalidOperation _, _
@@ -3713,6 +3731,7 @@ let dequalify
37133731
override = WeakenedPostcondition (dequalify_mismatch mismatch);
37143732
}
37153733
| InvalidDecoration expression -> InvalidDecoration expression
3734+
| TupleConcatenationError expressions -> TupleConcatenationError expressions
37163735
| TypedDictionaryAccessWithNonLiteral expression ->
37173736
TypedDictionaryAccessWithNonLiteral expression
37183737
| TypedDictionaryKeyNotFound { typed_dictionary_name; missing_key } ->

source/analysis/analysisError.mli

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ and unsupported_operand_kind =
208208
and illegal_annotation_target_kind =
209209
| InvalidExpression
210210
| Reassignment
211+
212+
and tuple_concatenation_problem =
213+
| MultipleVariadics of { variadic_expressions: Expression.t list }
214+
| UnpackingNonTuple of { annotation: Type.t }
211215
[@@deriving compare, eq, sexp, show, hash]
212216

213217
type invalid_decoration_reason =
@@ -388,6 +392,7 @@ and kind =
388392
variable: Type.Variable.t;
389393
base: polymorphism_base_class;
390394
}
395+
| TupleConcatenationError of tuple_concatenation_problem
391396
(* Additional errors. *)
392397
| DeadStore of Identifier.t
393398
| Deobfuscation of Source.t

source/analysis/test/integration/tupleTest.ml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,12 +592,84 @@ let test_length context =
592592
()
593593

594594

595+
let test_unpacking context =
596+
let assert_type_errors = assert_type_errors ~context in
597+
assert_type_errors
598+
{|
599+
from typing import Tuple
600+
601+
def foo(x: int, xs: Tuple[str, bool]) -> None:
602+
603+
y = (x, *xs)
604+
reveal_type(y)
605+
|}
606+
["Revealed type [-1]: Revealed type for `y` is `Tuple[int, str, bool]`."];
607+
assert_type_errors
608+
{|
609+
from typing import Tuple
610+
611+
def foo(x: int, xs: str) -> None:
612+
613+
y = (x, *xs)
614+
reveal_type(y)
615+
|}
616+
[
617+
"Unable to concatenate tuple [60]: Expected to unpack a tuple, but got `str`.";
618+
"Revealed type [-1]: Revealed type for `y` is `Tuple[int, typing.Any]`.";
619+
];
620+
assert_type_errors
621+
{|
622+
from typing import Tuple
623+
624+
def foo() -> None:
625+
626+
y = (1, *(2, 3), *(4, 5, 6), 7)
627+
reveal_type(y)
628+
|}
629+
[
630+
"Revealed type [-1]: Revealed type for `y` is `Tuple[typing_extensions.Literal[1], \
631+
typing_extensions.Literal[2], typing_extensions.Literal[3], typing_extensions.Literal[4], \
632+
typing_extensions.Literal[5], typing_extensions.Literal[6], typing_extensions.Literal[7]]`.";
633+
];
634+
assert_type_errors
635+
{|
636+
from typing import Tuple
637+
from pyre_extensions import TypeVarTuple
638+
639+
Ts = TypeVarTuple("Ts")
640+
641+
def foo(x: int, xs: Tuple[str, *Ts, bool]) -> Tuple[int, str, *Ts, bool]:
642+
y = (x, *xs)
643+
reveal_type(y)
644+
return y
645+
|}
646+
["Revealed type [-1]: Revealed type for `y` is `typing.Tuple[int, str, *test.Ts, bool]`."];
647+
assert_type_errors
648+
{|
649+
from typing import Tuple
650+
from pyre_extensions import TypeVarTuple
651+
652+
Ts = TypeVarTuple("Ts")
653+
654+
def foo(xs: Tuple[str, *Ts, bool]) -> None:
655+
y = ( *xs, *(2, 3), *xs)
656+
reveal_type(y)
657+
|}
658+
[
659+
"Unable to concatenate tuple [60]: Concatenation not yet support for multiple variadic \
660+
tuples: `*xs, *xs`.";
661+
"Revealed type [-1]: Revealed type for `y` is `typing.Tuple[typing.Any, ...]`.";
662+
];
663+
()
664+
665+
595666
let () =
596667
"tuple"
597668
>::: [
598669
"check_tuple" >:: test_check_tuple;
599670
"literal_access" >:: test_tuple_literal_access;
600671
"custom_tuple" >:: test_custom_tuple;
601672
"length" >:: test_length;
673+
"unpacking" >:: test_unpacking;
602674
]
603675
|> Test.run

source/analysis/typeCheck.ml

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3527,22 +3527,64 @@ module State (Context : Context) = struct
35273527
base = None;
35283528
}
35293529
| Tuple elements ->
3530-
let resolution, errors, resolved =
3530+
let resolution, errors, resolved_elements =
35313531
let forward_element (resolution, errors, resolved) expression =
3532-
let { Resolved.resolution; resolved = new_resolved; errors = new_errors; _ } =
3533-
forward_expression ~resolution ~expression
3532+
let resolution, new_errors, resolved_element =
3533+
match expression with
3534+
| { Node.value = Expression.Starred (Starred.Once expression); _ } ->
3535+
let { Resolved.resolution; resolved = resolved_element; errors = new_errors; _ } =
3536+
forward_expression ~resolution ~expression
3537+
in
3538+
let ordered_type, new_errors =
3539+
match resolved_element with
3540+
| Type.Tuple ordered_type -> ordered_type, new_errors
3541+
| _ ->
3542+
( Type.OrderedTypes.Concrete [Type.Any],
3543+
emit_error
3544+
~errors:new_errors
3545+
~location
3546+
~kind:
3547+
(Error.TupleConcatenationError
3548+
(UnpackingNonTuple { annotation = resolved_element })) )
3549+
in
3550+
resolution, new_errors, ordered_type
3551+
| _ ->
3552+
let { Resolved.resolution; resolved = resolved_element; errors = new_errors; _ } =
3553+
forward_expression ~resolution ~expression
3554+
in
3555+
resolution, new_errors, Type.OrderedTypes.Concrete [resolved_element]
35343556
in
3535-
resolution, List.append new_errors errors, new_resolved :: resolved
3557+
resolution, List.append new_errors errors, resolved_element :: resolved
35363558
in
35373559
List.fold elements ~f:forward_element ~init:(resolution, [], [])
35383560
in
3539-
{
3540-
resolution;
3541-
errors;
3542-
resolved = Type.tuple (List.rev resolved);
3543-
resolved_annotation = None;
3544-
base = None;
3545-
}
3561+
let resolved, errors =
3562+
let resolved_elements = List.rev resolved_elements in
3563+
let concatenated_elements =
3564+
let concatenate sofar next =
3565+
sofar >>= fun left -> Type.OrderedTypes.concatenate ~left ~right:next
3566+
in
3567+
List.fold resolved_elements ~f:concatenate ~init:(Some (Type.OrderedTypes.Concrete []))
3568+
in
3569+
match concatenated_elements with
3570+
| Some concatenated_elements -> Type.Tuple concatenated_elements, errors
3571+
| None ->
3572+
let variadic_expressions =
3573+
match List.zip elements resolved_elements with
3574+
| Ok pairs ->
3575+
List.filter_map pairs ~f:(function
3576+
| expression, Type.OrderedTypes.Concatenation _ -> Some expression
3577+
| _ -> None)
3578+
| Unequal_lengths -> elements
3579+
in
3580+
( Type.Tuple (Type.OrderedTypes.create_unbounded_concatenation Type.Any),
3581+
emit_error
3582+
~errors
3583+
~location
3584+
~kind:(Error.TupleConcatenationError (MultipleVariadics { variadic_expressions }))
3585+
)
3586+
in
3587+
{ resolution; errors; resolved; resolved_annotation = None; base = None }
35463588
| UnaryOperator ({ UnaryOperator.operand; _ } as operator) -> (
35473589
match UnaryOperator.override operator with
35483590
| Some expression -> forward_expression ~resolution ~expression

0 commit comments

Comments
 (0)