Skip to content

Commit 6d3704e

Browse files
Brandon Wufacebook-github-bot
authored andcommitted
Changed IntExpression to take in abstract type
Summary: + We would like to move towards an abstract type for `IntExpression`s to make sure that all polynomials are only expressed in fully normalized form. + Currently, an `IntExpression` is of `'a Polynomial.t`, which is transparent. This allows for all kinds of redundant states, like the monomial list (informally) `[1, [x, 1]]`, which is better represented as a `Variable x`, and `[5, []]`, which is better represented as `Literal (Integer 5)`. This is also a gigantic pain because these errors tend to be "hidden", and the error messages you get for mistaking alternate representations are terrible. + We have added an abstract type `'a RecordIntExpression.t`, which is the new input to an `IntExpression`. Everywhere the `IntExpression` constructor was used, we now use `IntExpression.create`, which implicitly constructs proper values of the abstract type for use. It is a common occurrence to pattern match on an `IntExpression _` to extract out the polynomial, and perform some polynomial-only operation on it, so in addition we have added support for the private type `'a RecordIntExpression.t`, which is now supported at all call-sites. This means that we may deconstruct an `IntExpression` to obtain a `Polynomial.t`, but we cannot construct an `IntExpression` using the constructor itself (preserving normalization). Currently, no normalization is happening, but this change is to stage for later normalization features. In addition, we have a sum type `'a variant`, which is necessary because `IntExpression` regards the type of `RecordIntExpression.t` as abstract, so we must have `RecordIntExpression.t` implement its own normalizing constructor, with a sum to tag whether it is actually a polynomial, or some other type. This will be expanded on in future diffs. There's some redundant stuff that will need to be taken away in a future diff. For instance, we should get rid of some useless branches and re-evaluate everything to do with `type_to_int_expression`. - **Why `module IntExpression`?** Firstly, we need some function of signature `'a Polynomial.t -> type_t` - this function will take a polynomial and return its normalized form (which may be `IntExpression`, `Literal`, or `Variable`). Necessarily, since `RecordIntExpression` comes *before* `type_t` and we are not using recursive modules, this must be in a different module. `IntExpression` is this module. - **Why `'a variant`?** `'a variant` is here because, as stated before, `IntExpression` contains the function `create : 'a Polynomial.t -> type_t`. However, the type of `RecordIntExpression` is *abstract* to the module `IntExpression`, because we want to enforce that the entirety of `type.ml` regards `RecordIntExpression.t` as abstract, so we don't have any representation invariant violations. Thus, `RecordIntExpression` must itself implement its own normalization function, with a way of signalling to `IntExpression` whether the output it produced is normalized as a polynomial, or needs to be changed to a `Literal` or something. - **Why not just have `RecordIntExpression` have a function that turns a polynomial into a pseudo-normalized polynomial, and then have `IntExpression` pattern match on it to produce the proper value?** In other words, why not have `RecordIntExpression.normalize : 'a Polynomial.t -> 'a Polynomial.t`, and have `create` do the work of recognizing if its a literal or not. The problem is that `RecordIntExpression` must produce the abstract type that `IntExpression` (the constructor) takes in, since it comes before the type definition of `type_t`. Then, `IntExpression.create` has the burden of creating the value of type `RecordIntExpression.t` - which it can't, since it's an abstract type. So `RecordIntExpression.t` *has* to give a way to produce values of type `RecordIntExpression.t`, and because those values are abstract, it must tag them with whether or not they should be normalized. - **Why the `private` constructor for `RecordIntExpression.t`?** It is entirely possible to implement `RecordIntExpression.polynomial_of : 'a t -> 'a Polynomial.t`. However, this induces extreme headache in the existing codebase when trying to pattern match on `IntExpression` variants of the `type_t` type. Without a private constructor, this means that to pattern match on an empty polynomial, you must do `| IntExpression int_expression -> match int_expression of | [] -> ...`, or something similar, as opposed to `| IntExpression (Data []) -> ...`. It's just a lot of syntactic burden. This can be alleviated later by reducing the amount of points where we have to pattern match on `IntExpressions`, by changing `solve_less_or_equal` to do all of its simplification first, rather than recursively inside of the function. Right now, however, this is less bloat. Reviewed By: grievejia Differential Revision: D28890690 fbshipit-source-id: ef1d81aec74922d9c74f4e34b998d5e07f0fd63d
1 parent 842f400 commit 6d3704e

File tree

4 files changed

+97
-51
lines changed

4 files changed

+97
-51
lines changed

source/analysis/test/typeTest.ml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,7 +1996,7 @@ let test_replace_all _ =
19961996
polynomial_create_from_variables_list variable_list |> Type.polynomial_to_type
19971997
in
19981998
let divide_to_type numerator denominator =
1999-
Type.IntExpression (Type.Polynomial.divide ~compare_t:Type.compare numerator denominator)
1999+
Type.IntExpression.create (Type.Polynomial.divide ~compare_t:Type.compare numerator denominator)
20002000
in
20012001
(* TODO: Task T90507423. This needs to be normalized before output. *)
20022002
assert_equal
@@ -2014,14 +2014,14 @@ let test_replace_all _ =
20142014
(Type.Variable.GlobalTransforms.Unary.replace_all
20152015
(fun _ -> Some (Type.literal_integer 7))
20162016
(divide_to_type (Type.Polynomial.create_from_int 7) (Type.Polynomial.create_from_variable x)))
2017-
(Type.IntExpression (polynomial_create_from_variables_list [1, []]));
2017+
(Type.IntExpression.create (polynomial_create_from_variables_list [1, []]));
20182018
assert_equal
20192019
(Type.Variable.GlobalTransforms.Unary.replace_all
20202020
(fun _ -> Some (Type.literal_integer 6))
20212021
(divide_to_type
20222022
(Type.Polynomial.create_from_int 10)
20232023
(Type.Polynomial.create_from_variable x)))
2024-
(Type.IntExpression (polynomial_create_from_variables_list [1, []]));
2024+
(Type.IntExpression.create (polynomial_create_from_variables_list [1, []]));
20252025
assert_equal
20262026
(Type.Variable.GlobalTransforms.Unary.replace_all
20272027
(function
@@ -2032,7 +2032,7 @@ let test_replace_all _ =
20322032
(polynomial_create_from_variables_list [1, [y, 1]]))
20332033
| _ -> None)
20342034
(variable_list_to_type [1, [x, 1; y, 1]]))
2035-
(Type.IntExpression
2035+
(Type.IntExpression.create
20362036
(Type.Polynomial.multiply
20372037
~compare_t:Type.compare
20382038
(Type.Polynomial.create_from_variable y)
@@ -2045,7 +2045,7 @@ let test_replace_all _ =
20452045
(function
20462046
| variable when not ([%equal: Type.Variable.Unary.t] variable z) -> Some (Type.Variable z)
20472047
| _ -> None)
2048-
(Type.IntExpression
2048+
(Type.IntExpression.create
20492049
(Type.Polynomial.add
20502050
~compare_t:Type.compare
20512051
(Type.Polynomial.divide
@@ -2056,7 +2056,7 @@ let test_replace_all _ =
20562056
~compare_t:Type.compare
20572057
(Type.Polynomial.create_from_int 7)
20582058
(polynomial_create_from_variables_list [1, [y, 1; z, 1]])))))
2059-
(Type.IntExpression
2059+
(Type.IntExpression.create
20602060
(Type.Polynomial.multiply
20612061
~compare_t:Type.compare
20622062
(Type.Polynomial.create_from_int 2)
@@ -2069,15 +2069,15 @@ let test_replace_all _ =
20692069
(function
20702070
| variable when [%equal: Type.Variable.Unary.t] variable x -> Some (Type.Variable y)
20712071
| _ -> None)
2072-
(Type.IntExpression
2072+
(Type.IntExpression.create
20732073
(Type.Polynomial.add
20742074
~compare_t:Type.compare
20752075
(Type.Polynomial.create_from_int 1)
20762076
(Type.Polynomial.divide
20772077
~compare_t:Type.compare
20782078
(Type.Polynomial.create_from_variable x)
20792079
(Type.Polynomial.create_from_variable y)))))
2080-
(Type.IntExpression (polynomial_create_from_variables_list [2, []]));
2080+
(Type.IntExpression.create (polynomial_create_from_variables_list [2, []]));
20812081
assert_equal
20822082
(Type.Variable.GlobalTransforms.Unary.replace_all
20832083
(fun _ -> Some (Type.literal_integer 0))
@@ -2088,7 +2088,7 @@ let test_replace_all _ =
20882088
(Type.Variable.GlobalTransforms.Unary.replace_all
20892089
(fun _ -> Some (Type.literal_integer 4))
20902090
(variable_list_to_type [2, [x, 3]; 3, [y, 1; z, 1]]))
2091-
(Type.IntExpression (polynomial_create_from_variables_list [176, []]));
2091+
(Type.IntExpression.create (polynomial_create_from_variables_list [176, []]));
20922092
assert_equal
20932093
(Type.Variable.GlobalTransforms.Unary.replace_all
20942094
(function
@@ -2256,7 +2256,7 @@ let test_less_or_equal _ =
22562256
polynomial_create_from_variables_list variable_list |> Type.polynomial_to_type
22572257
in
22582258
let divide_to_type numerator denominator =
2259-
Type.IntExpression (Type.Polynomial.divide ~compare_t:Type.compare numerator denominator)
2259+
Type.IntExpression.create (Type.Polynomial.divide ~compare_t:Type.compare numerator denominator)
22602260
in
22612261
let very_complicated_type =
22622262
Type.tuple

source/analysis/type.ml

Lines changed: 74 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,24 @@ end = struct
979979
]
980980
end
981981

982+
module RecordIntExpression : sig
983+
type 'a t = private Data of 'a Polynomial.t [@@deriving compare, eq, sexp, show, hash]
984+
985+
type 'a variant =
986+
| NonPolynomial of 'a Polynomial.t
987+
| Polynomial of 'a t
988+
989+
val normalize_variant : compare_t:('a -> 'a -> int) -> 'a Polynomial.t -> 'a variant
990+
end = struct
991+
type 'a t = Data of 'a Polynomial.t [@@deriving compare, eq, sexp, show, hash]
992+
993+
type 'a variant =
994+
| NonPolynomial of 'a Polynomial.t
995+
| Polynomial of 'a t
996+
997+
let normalize_variant ~compare_t:_ polynomial = Polynomial (Data polynomial)
998+
end
999+
9821000
open Record.Callable
9831001
module CallableParameter = Record.Callable.RecordParameter
9841002

@@ -1031,12 +1049,23 @@ module T = struct
10311049
| Tuple of t Record.OrderedTypes.record
10321050
| Union of t list
10331051
| Variable of t Record.Variable.RecordUnary.record
1034-
| IntExpression of t Polynomial.t
1052+
| IntExpression of t RecordIntExpression.t
10351053
[@@deriving compare, eq, sexp, show, hash]
10361054
end
10371055

10381056
include T
10391057

1058+
type type_t = t [@@deriving compare, eq, sexp, show, hash]
1059+
1060+
module IntExpression : sig
1061+
val create : type_t Polynomial.t -> type_t
1062+
end = struct
1063+
let create polynomial =
1064+
match RecordIntExpression.normalize_variant ~compare_t:[%compare: type_t] polynomial with
1065+
| RecordIntExpression.NonPolynomial _ -> failwith "impossible"
1066+
| RecordIntExpression.Polynomial polynomial -> IntExpression polynomial
1067+
end
1068+
10401069
let _ = show (* shadowed below *)
10411070

10421071
type class_data = {
@@ -1046,60 +1075,64 @@ type class_data = {
10461075
}
10471076
[@@deriving sexp]
10481077

1049-
type type_t = t [@@deriving compare, eq, sexp, show, hash]
1050-
10511078
let polynomial_to_type polynomial =
10521079
match polynomial with
10531080
| [] -> Literal (Integer 0)
10541081
| [{ Monomial.variables = []; constant_factor }] -> Literal (Integer constant_factor)
10551082
| [{ Monomial.variables = [{ degree = 1; variable = Variable variable }]; constant_factor = 1 }]
10561083
->
10571084
Variable variable
1058-
| _ -> IntExpression polynomial
1085+
| _ -> IntExpression.create polynomial
10591086

10601087

10611088
let solve_less_or_equal_polynomial ~left ~right ~solve ~impossible =
10621089
match left, right with
1063-
| IntExpression polynomial, _ when Polynomial.is_base_case polynomial ->
1090+
| IntExpression (Data polynomial), _ when Polynomial.is_base_case polynomial ->
10641091
solve ~left:(polynomial_to_type polynomial) ~right
1065-
| _, IntExpression polynomial when Polynomial.is_base_case polynomial ->
1092+
| _, IntExpression (Data polynomial) when Polynomial.is_base_case polynomial ->
10661093
solve ~left ~right:(polynomial_to_type polynomial)
1067-
| ( IntExpression ({ Monomial.constant_factor; variables = [] } :: polynomial),
1094+
| ( IntExpression (Data ({ Monomial.constant_factor; variables = [] } :: polynomial)),
10681095
Literal (Integer literal) ) ->
1069-
solve ~left:(IntExpression polynomial) ~right:(Literal (Integer (literal - constant_factor)))
1096+
solve
1097+
~left:(IntExpression.create polynomial)
1098+
~right:(Literal (Integer (literal - constant_factor)))
10701099
| ( Literal (Integer literal),
1071-
IntExpression ({ Monomial.constant_factor; variables = [] } :: polynomial) ) ->
1072-
solve ~left:(Literal (Integer (literal - constant_factor))) ~right:(IntExpression polynomial)
1073-
| IntExpression [{ Monomial.constant_factor; variables }], Literal (Integer literal)
1100+
IntExpression (Data ({ Monomial.constant_factor; variables = [] } :: polynomial)) ) ->
1101+
solve
1102+
~left:(Literal (Integer (literal - constant_factor)))
1103+
~right:(IntExpression.create polynomial)
1104+
| IntExpression (Data [{ Monomial.constant_factor; variables }]), Literal (Integer literal)
10741105
when constant_factor <> 1 ->
10751106
if literal mod constant_factor <> 0 then
10761107
impossible
10771108
else
10781109
solve
1079-
~left:(IntExpression [{ constant_factor = 1; variables }])
1110+
~left:(IntExpression.create [{ constant_factor = 1; variables }])
10801111
~right:(Literal (Integer (literal / constant_factor)))
1081-
| Literal (Integer literal), IntExpression [{ Monomial.constant_factor; variables }]
1112+
| Literal (Integer literal), IntExpression (Data [{ Monomial.constant_factor; variables }])
10821113
when constant_factor <> 1 ->
10831114
if literal mod constant_factor <> 0 then
10841115
impossible
10851116
else
10861117
solve
10871118
~left:(Literal (Integer (literal / constant_factor)))
1088-
~right:(IntExpression [{ Monomial.constant_factor = 1; variables }])
1089-
| ( IntExpression (({ variables = []; _ } as left_monomial) :: left_polynomial_tail),
1090-
IntExpression ({ variables = []; _ } :: _ as right_polynomial) ) ->
1119+
~right:(IntExpression.create [{ Monomial.constant_factor = 1; variables }])
1120+
| ( IntExpression (Data (({ variables = []; _ } as left_monomial) :: left_polynomial_tail)),
1121+
IntExpression (Data ({ variables = []; _ } :: _ as right_polynomial)) ) ->
10911122
let right_polynomial =
10921123
Polynomial.subtract right_polynomial [left_monomial] ~compare_t:T.compare
10931124
in
1094-
solve ~left:(IntExpression left_polynomial_tail) ~right:(IntExpression right_polynomial)
1125+
solve
1126+
~left:(IntExpression.create left_polynomial_tail)
1127+
~right:(IntExpression.create right_polynomial)
10951128
| IntExpression _, Primitive _ -> solve ~left:(Primitive "int") ~right
10961129
| _ -> impossible
10971130

10981131

10991132
let type_to_int_expression = function
1100-
| Literal (Integer literal) -> Some (IntExpression (Polynomial.create_from_int literal))
1101-
| Variable variable -> Some (IntExpression (Polynomial.create_from_variable variable))
1102-
| IntExpression polynomial -> Some (IntExpression polynomial)
1133+
| Literal (Integer literal) -> Some (IntExpression.create (Polynomial.create_from_int literal))
1134+
| Variable variable -> Some (IntExpression.create (Polynomial.create_from_variable variable))
1135+
| IntExpression (Data polynomial) -> Some (IntExpression.create polynomial)
11031136
| Primitive "int" -> Some (Primitive "int")
11041137
| Any -> Some Any
11051138
| _ -> None
@@ -1109,7 +1142,7 @@ let checked_division left right =
11091142
if List.length right = 0 then
11101143
Bottom
11111144
else
1112-
IntExpression (Polynomial.divide left right ~compare_t:T.compare)
1145+
IntExpression.create (Polynomial.divide left right ~compare_t:T.compare)
11131146

11141147

11151148
let merge_int_expressions ?(divide = false) left right ~operation =
@@ -1123,11 +1156,11 @@ let merge_int_expressions ?(divide = false) left right ~operation =
11231156
| Primitive "int", _
11241157
| _, Primitive "int" ->
11251158
Primitive "int"
1126-
| IntExpression left, IntExpression right ->
1159+
| IntExpression (Data left), IntExpression (Data right) ->
11271160
if divide then
11281161
checked_division left right
11291162
else
1130-
IntExpression (operation left right)
1163+
IntExpression.create (operation left right)
11311164
| _ -> Bottom
11321165

11331166

@@ -1143,9 +1176,9 @@ let local_replace_polynomial polynomial ~replace_variable ~replace_recursive =
11431176
replace_variable variable >>| fun result -> monomial_variable, result
11441177
| Monomial.Divide (dividend, quotient) ->
11451178
let replace_polynomial polynomial =
1146-
match replace_recursive (IntExpression polynomial) with
1179+
match replace_recursive (IntExpression.create polynomial) with
11471180
| Some replaced -> replaced
1148-
| None -> IntExpression polynomial
1181+
| None -> IntExpression.create polynomial
11491182
in
11501183
let replaced_division =
11511184
merge_int_expressions
@@ -1165,7 +1198,7 @@ let local_replace_polynomial polynomial ~replace_variable ~replace_recursive =
11651198
Polynomial.replace left ~by:right ~variable ~compare_t:T.compare)
11661199
in
11671200
let replaced_expression =
1168-
List.fold replacements ~init:(IntExpression polynomial) ~f:merge_replace
1201+
List.fold replacements ~init:(IntExpression.create polynomial) ~f:merge_replace
11691202
in
11701203
Some replaced_expression
11711204

@@ -1455,9 +1488,9 @@ let rec pp format annotation =
14551488
"typing.Union[%s]"
14561489
(List.map parameters ~f:show |> String.concat ~sep:", ")
14571490
| Variable unary -> Record.Variable.RecordUnary.pp_concise format unary ~pp_type:pp
1458-
| IntExpression polynomial when Polynomial.is_base_case polynomial ->
1491+
| IntExpression (Data polynomial) when Polynomial.is_base_case polynomial ->
14591492
pp format (polynomial_to_type polynomial)
1460-
| IntExpression polynomial ->
1493+
| IntExpression (Data polynomial) ->
14611494
Format.fprintf
14621495
format
14631496
"pyre_extensions.IntExpression[%s]"
@@ -1542,9 +1575,9 @@ and pp_concise format annotation =
15421575
Format.fprintf format "Optional[%a]" pp_concise parameter
15431576
| Union parameters -> Format.fprintf format "Union[%a]" pp_comma_separated parameters
15441577
| Variable { variable; _ } -> Format.fprintf format "%s" (strip_qualification variable)
1545-
| IntExpression polynomial when Polynomial.is_base_case polynomial ->
1578+
| IntExpression (Data polynomial) when Polynomial.is_base_case polynomial ->
15461579
pp_concise format (polynomial_to_type polynomial)
1547-
| IntExpression polynomial ->
1580+
| IntExpression (Data polynomial) ->
15481581
Format.fprintf
15491582
format
15501583
"pyre_extensions.IntExpression[%s]"
@@ -1906,9 +1939,9 @@ let rec expression annotation =
19061939
get_item_call "typing.Optional" [expression parameter]
19071940
| Union parameters -> get_item_call "typing.Union" (List.map ~f:expression parameters)
19081941
| Variable { variable; _ } -> create_name variable
1909-
| IntExpression polynomial when Polynomial.is_base_case polynomial ->
1942+
| IntExpression (Data polynomial) when Polynomial.is_base_case polynomial ->
19101943
convert_annotation (polynomial_to_type polynomial)
1911-
| IntExpression polynomial ->
1944+
| IntExpression (Data polynomial) ->
19121945
let convert_int_expression arguments ~operator =
19131946
Expression.Call
19141947
{
@@ -1946,7 +1979,10 @@ let rec expression annotation =
19461979
{
19471980
name = "pyre_extensions.Divide";
19481981
parameters =
1949-
[Single (IntExpression dividend); Single (IntExpression quotient)];
1982+
[
1983+
Single (IntExpression.create dividend);
1984+
Single (IntExpression.create quotient);
1985+
];
19501986
})
19511987
in
19521988
if degree = 1 then
@@ -1957,7 +1993,7 @@ let rec expression annotation =
19571993
in
19581994
convert_int_expression ~operator:"Multiply" arguments)
19591995
in
1960-
if List.length variables = 0 then
1996+
if List.length variables = 1 then
19611997
constant_factor
19621998
else
19631999
convert_int_expression ~operator:"Multiply" (constant_factor :: variables)
@@ -3911,21 +3947,21 @@ end = struct
39113947

39123948
let rec local_collect = function
39133949
| Variable variable -> [variable]
3914-
| IntExpression polynomial ->
3950+
| IntExpression (Data polynomial) ->
39153951
List.concat_map polynomial ~f:(fun { variables; _ } ->
39163952
List.concat_map variables ~f:(fun { variable; _ } ->
39173953
match variable with
39183954
| Variable x -> [x]
39193955
| Divide (dividend, quotient) ->
3920-
local_collect (IntExpression dividend)
3921-
@ local_collect (IntExpression quotient)))
3956+
local_collect (IntExpression.create dividend)
3957+
@ local_collect (IntExpression.create quotient)))
39223958
|> List.dedup_and_sort ~compare
39233959
| _ -> []
39243960

39253961

39263962
let rec local_replace replacement = function
39273963
| Variable variable -> replacement variable
3928-
| IntExpression polynomial ->
3964+
| IntExpression (Data polynomial) ->
39293965
let replace_variable variable =
39303966
match replacement variable with
39313967
| Some replaced_variable ->

source/analysis/type.mli

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ module Polynomial : sig
262262
'a t
263263
end
264264

265+
module RecordIntExpression : sig
266+
type 'a t = private Data of 'a Polynomial.t [@@deriving compare, eq, sexp, show, hash]
267+
end
268+
265269
module Primitive : sig
266270
type t = Identifier.t [@@deriving compare, eq, sexp, show, hash]
267271

@@ -304,9 +308,13 @@ and t =
304308
| Tuple of t Record.OrderedTypes.record
305309
| Union of t list
306310
| Variable of t Record.Variable.RecordUnary.record
307-
| IntExpression of t Polynomial.t
311+
| IntExpression of t RecordIntExpression.t
308312
[@@deriving compare, eq, sexp, show, hash]
309313

314+
module IntExpression : sig
315+
val create : t Polynomial.t -> t
316+
end
317+
310318
type class_data = {
311319
instantiated: t;
312320
accessed_through_class: bool;

source/analysis/typeOrder.ml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,11 @@ module OrderImplementation = struct
179179
union
180180
else
181181
List.map elements ~f:(join order other) |> List.fold ~f:(join order) ~init:Type.Bottom
182-
| Type.IntExpression polynomial, other when Type.Polynomial.is_base_case polynomial ->
182+
| Type.IntExpression (Data polynomial), other when Type.Polynomial.is_base_case polynomial
183+
->
183184
join order other (Type.polynomial_to_type polynomial)
184-
| other, Type.IntExpression polynomial when Type.Polynomial.is_base_case polynomial ->
185+
| other, Type.IntExpression (Data polynomial) when Type.Polynomial.is_base_case polynomial
186+
->
185187
join order other (Type.polynomial_to_type polynomial)
186188
| Type.IntExpression _, other
187189
| other, Type.IntExpression _ ->

0 commit comments

Comments
 (0)