Skip to content

Commit 9c6856d

Browse files
authored
Upgrade exponentiation to unified operator (#7153)
* Upgrade exponentiation to unified operator Also make its output to use ES7 exponentiation (`**`) operator. `**` is more concise, faster than `Math.pow()`, works well with all numeric types include bigint. We were already using it for bigint, now for int and float too. * adjust parens * result into int32 * more exponentiation tests * make sure it is int32 * remove unnecessary guard for constants * add changelog * add a test * update changelog * add a failing case * fix `Ext_int.int32_pow` to behave like JavaScript
1 parent 55d0837 commit 9c6856d

30 files changed

+187
-44
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- Make reanalyze exception tracking work with the new stdlib. https://github.com/rescript-lang/rescript/pull/7328
2222
- Fix Pervasive.max using boolean comparison for floats. https://github.com/rescript-lang/rescript/pull/7333
2323
- Experimental: Support nested/inline record types - records defined inside of other records, without needing explicit separate type definitions. https://github.com/rescript-lang/rescript/pull/7241
24+
- Add unified exponentiation (`**`) operator for numeric types using ES7 `**`. https://github.com/rescript-lang/rescript-compiler/pull/7153
2425

2526
#### :boom: Breaking Change
2627

Diff for: compiler/core/js_exp_make.ml

+7
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,7 @@ let unchecked_int32_minus ?comment e1 e2 : J.expression =
15491549
float_minus ?comment e1 e2
15501550

15511551
let float_div ?comment e1 e2 = bin ?comment Div e1 e2
1552+
let float_pow ?comment e1 e2 = bin ?comment Pow e1 e2
15521553
let float_notequal ?comment e1 e2 = bin ?comment NotEqEq e1 e2
15531554

15541555
let int32_asr ?comment e1 e2 : J.expression =
@@ -1618,6 +1619,12 @@ let int32_mul ?comment (e1 : J.expression) (e2 : J.expression) : J.expression =
16181619
let unchecked_int32_mul ?comment e1 e2 : J.expression =
16191620
{comment; expression_desc = Bin (Mul, e1, e2)}
16201621

1622+
let int32_pow ?comment (e1 : t) (e2 : t) : J.expression =
1623+
match (e1.expression_desc, e2.expression_desc) with
1624+
| Number (Int {i = i1}), Number (Int {i = i2}) ->
1625+
int ?comment (Ext_int.int32_pow i1 i2)
1626+
| _ -> to_int32 (float_pow ?comment e1 e2)
1627+
16211628
let rec int32_bxor ?comment (e1 : t) (e2 : t) : J.expression =
16221629
match (e1.expression_desc, e2.expression_desc) with
16231630
| Number (Int {i = i1}), Number (Int {i = i2}) ->

Diff for: compiler/core/js_exp_make.mli

+4
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ val int32_div : checked:bool -> ?comment:string -> t -> t -> t
254254

255255
val int32_mod : checked:bool -> ?comment:string -> t -> t -> t
256256

257+
val int32_pow : ?comment:string -> t -> t -> t
258+
257259
val int32_lsl : ?comment:string -> t -> t -> t
258260

259261
val int32_lsr : ?comment:string -> t -> t -> t
@@ -278,6 +280,8 @@ val float_notequal : ?comment:string -> t -> t -> t
278280

279281
val float_mod : ?comment:string -> t -> t -> t
280282

283+
val float_pow : ?comment:string -> t -> t -> t
284+
281285
val int_comp : Lam_compat.comparison -> ?comment:string -> t -> t -> t
282286

283287
val bool_comp : Lam_compat.comparison -> ?comment:string -> t -> t -> t

Diff for: compiler/core/js_op.ml

+2-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ type int_op =
105105
| Div
106106
(* x / y | 0 *)
107107
| Mod
108-
(* x % y *)
108+
(* x % y *)
109+
| Pow (* x ** y | 0 *)
109110

110111
(* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise_operators
111112
{[

Diff for: compiler/core/js_op_util.ml

+4-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ let op_prec (op : Js_op.binop) =
4040
| Band -> (7, 7, 7)
4141
| Lsl | Lsr | Asr -> (10, 10, 11)
4242
| Plus | Minus -> (11, 11, 12)
43-
| Mul | Div | Mod | Pow -> (12, 12, 13)
43+
| Mul | Div | Mod -> (12, 12, 13)
44+
| Pow -> (13, 14, 12)
4445

4546
let op_int_prec (op : Js_op.int_op) =
4647
match op with
@@ -50,6 +51,7 @@ let op_int_prec (op : Js_op.int_op) =
5051
| Lsl | Lsr | Asr -> (10, 10, 11)
5152
| Plus | Minus -> (11, 11, 12)
5253
| Mul | Div | Mod -> (12, 12, 13)
54+
| Pow -> (13, 14, 12)
5355

5456
let op_str (op : Js_op.binop) =
5557
match op with
@@ -89,6 +91,7 @@ let op_int_str (op : Js_op.int_op) =
8991
| Mul -> "*"
9092
| Div -> "/"
9193
| Mod -> "%"
94+
| Pow -> "**"
9295

9396
let str_of_used_stats x =
9497
match (x : Js_op.used_stats) with

Diff for: compiler/core/lam_analysis.ml

+5-4
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ let rec no_side_effects (lam : Lam.t) : bool =
5959
(* bool primitives *)
6060
| Psequand | Psequor | Pnot | Pboolcomp _ | Pboolorder | Pboolmin | Pboolmax
6161
(* int primitives *)
62-
| Pnegint | Paddint | Psubint | Pmulint | Pandint | Porint | Pxorint
63-
| Plslint | Plsrint | Pasrint | Pintcomp _ | Pintorder | Pintmin | Pintmax
62+
| Pnegint | Paddint | Psubint | Pmulint | Ppowint | Pandint | Porint
63+
| Pxorint | Plslint | Plsrint | Pasrint | Pintcomp _ | Pintorder | Pintmin
64+
| Pintmax
6465
(* float primitives *)
6566
| Pintoffloat | Pfloatofint | Pnegfloat | Paddfloat | Psubfloat | Pmulfloat
66-
| Pdivfloat | Pmodfloat | Pfloatcomp _ | Pjscomp _ | Pfloatorder | Pfloatmin
67-
| Pfloatmax
67+
| Ppowfloat | Pdivfloat | Pmodfloat | Pfloatcomp _ | Pjscomp _ | Pfloatorder
68+
| Pfloatmin | Pfloatmax
6869
(* bigint primitives *)
6970
| Pnegbigint | Paddbigint | Psubbigint | Pmulbigint | Ppowbigint
7071
| Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint

Diff for: compiler/core/lam_compile_primitive.ml

+8
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,14 @@ let translate output_prefix loc (cxt : Lam_compile_context.t)
250250
match args with
251251
| [e1; e2] -> E.bigint_mod ~checked:!Js_config.check_div_by_zero e1 e2
252252
| _ -> assert false)
253+
| Ppowint -> (
254+
match args with
255+
| [e1; e2] -> E.int32_pow e1 e2
256+
| _ -> assert false)
257+
| Ppowfloat -> (
258+
match args with
259+
| [e1; e2] -> E.float_pow e1 e2
260+
| _ -> assert false)
253261
| Ppowbigint -> (
254262
match args with
255263
| [e1; e2] -> E.bigint_op Pow e1 e2

Diff for: compiler/core/lam_convert.ml

+2
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ let lam_prim ~primitive:(p : Lambda.primitive) ~args loc : Lam.t =
249249
| Pmulint -> prim ~primitive:Pmulint ~args loc
250250
| Pdivint -> prim ~primitive:Pdivint ~args loc
251251
| Pmodint -> prim ~primitive:Pmodint ~args loc
252+
| Ppowint -> prim ~primitive:Ppowint ~args loc
252253
| Pandint -> prim ~primitive:Pandint ~args loc
253254
| Porint -> prim ~primitive:Porint ~args loc
254255
| Pxorint -> prim ~primitive:Pxorint ~args loc
@@ -281,6 +282,7 @@ let lam_prim ~primitive:(p : Lambda.primitive) ~args loc : Lam.t =
281282
| Pmulfloat -> prim ~primitive:Pmulfloat ~args loc
282283
| Pdivfloat -> prim ~primitive:Pdivfloat ~args loc
283284
| Pmodfloat -> prim ~primitive:Pmodfloat ~args loc
285+
| Ppowfloat -> prim ~primitive:Ppowfloat ~args loc
284286
| Pfloatorder -> prim ~primitive:Pfloatorder ~args loc
285287
| Pfloatmin -> prim ~primitive:Pfloatmin ~args loc
286288
| Pfloatmax -> prim ~primitive:Pfloatmax ~args loc

Diff for: compiler/core/lam_primitive.ml

+6-4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ type t =
7373
| Pmulint
7474
| Pdivint
7575
| Pmodint
76+
| Ppowint
7677
| Pandint
7778
| Porint
7879
| Pxorint
@@ -94,6 +95,7 @@ type t =
9495
| Pmulfloat
9596
| Pdivfloat
9697
| Pmodfloat
98+
| Ppowfloat
9799
| Pfloatcomp of Lam_compat.comparison
98100
| Pfloatorder
99101
| Pfloatmin
@@ -197,12 +199,12 @@ let eq_primitive_approx (lhs : t) (rhs : t) =
197199
(* bool primitives *)
198200
| Psequand | Psequor | Pnot | Pboolcomp _ | Pboolorder | Pboolmin | Pboolmax
199201
(* int primitives *)
200-
| Pisint | Pnegint | Paddint | Psubint | Pmulint | Pdivint | Pmodint | Pandint
201-
| Porint | Pxorint | Plslint | Plsrint | Pasrint | Pintorder | Pintmin
202-
| Pintmax
202+
| Pisint | Pnegint | Paddint | Psubint | Pmulint | Pdivint | Pmodint | Ppowint
203+
| Pandint | Porint | Pxorint | Plslint | Plsrint | Pasrint | Pintorder
204+
| Pintmin | Pintmax
203205
(* float primitives *)
204206
| Pintoffloat | Pfloatofint | Pnegfloat | Paddfloat | Psubfloat | Pmulfloat
205-
| Pdivfloat | Pmodfloat | Pfloatorder | Pfloatmin | Pfloatmax
207+
| Pdivfloat | Pmodfloat | Ppowfloat | Pfloatorder | Pfloatmin | Pfloatmax
206208
(* bigint primitives *)
207209
| Pnegbigint | Paddbigint | Psubbigint | Pmulbigint | Pdivbigint | Pmodbigint
208210
| Ppowbigint | Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint

Diff for: compiler/core/lam_primitive.mli

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ type t =
6868
| Pmulint
6969
| Pdivint
7070
| Pmodint
71+
| Ppowint
7172
| Pandint
7273
| Porint
7374
| Pxorint
@@ -89,6 +90,7 @@ type t =
8990
| Pmulfloat
9091
| Pdivfloat
9192
| Pmodfloat
93+
| Ppowfloat
9294
| Pfloatcomp of Lam_compat.comparison
9395
| Pfloatorder
9496
| Pfloatmin

Diff for: compiler/core/lam_print.ml

+2
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ let primitive ppf (prim : Lam_primitive.t) =
115115
| Pmulint -> fprintf ppf "*"
116116
| Pdivint -> fprintf ppf "/"
117117
| Pmodint -> fprintf ppf "mod"
118+
| Ppowint -> fprintf ppf "**"
118119
| Pandint -> fprintf ppf "and"
119120
| Porint -> fprintf ppf "or"
120121
| Pxorint -> fprintf ppf "xor"
@@ -140,6 +141,7 @@ let primitive ppf (prim : Lam_primitive.t) =
140141
| Pmulfloat -> fprintf ppf "*."
141142
| Pdivfloat -> fprintf ppf "/."
142143
| Pmodfloat -> fprintf ppf "mod"
144+
| Ppowfloat -> fprintf ppf "**"
143145
| Pfloatcomp Ceq -> fprintf ppf "==."
144146
| Pfloatcomp Cneq -> fprintf ppf "!=."
145147
| Pfloatcomp Clt -> fprintf ppf "<."

Diff for: compiler/ext/ext_int.ml

+12
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,15 @@ let move = 0x1_0000_0000
3434
let int32_unsigned_to_int (n : int32) : int =
3535
let i = Int32.to_int n in
3636
if i < 0 then i + move else i
37+
38+
let int32_pow (x : int32) (y : int32) =
39+
let x_float = Int32.to_float x in
40+
let y_float = Int32.to_float y in
41+
let result = x_float ** y_float in
42+
let truncated =
43+
if result > 2147483647.0 || result < -2147483648.0 then
44+
let i = int_of_float result in
45+
i land 0xFFFFFFFF
46+
else int_of_float result
47+
in
48+
Int32.of_int truncated

Diff for: compiler/ext/ext_int.mli

+2
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ val int32_unsigned_to_int : int32 -> int
3333
works on 64 bit platform only
3434
given input as an uint32 and convert it io int64
3535
*)
36+
37+
val int32_pow : int32 -> int32 -> int32

Diff for: compiler/ml/lambda.ml

+2
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ type primitive =
206206
| Pmulint
207207
| Pdivint
208208
| Pmodint
209+
| Ppowint
209210
| Pandint
210211
| Porint
211212
| Pxorint
@@ -228,6 +229,7 @@ type primitive =
228229
| Psubfloat
229230
| Pmulfloat
230231
| Pdivfloat
232+
| Ppowfloat
231233
| Pfloatcomp of comparison
232234
| Pfloatorder
233235
| Pfloatmin

Diff for: compiler/ml/lambda.mli

+2
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ type primitive =
173173
| Pmulint
174174
| Pdivint
175175
| Pmodint
176+
| Ppowint
176177
| Pandint
177178
| Porint
178179
| Pxorint
@@ -195,6 +196,7 @@ type primitive =
195196
| Psubfloat
196197
| Pmulfloat
197198
| Pdivfloat
199+
| Ppowfloat
198200
| Pfloatcomp of comparison
199201
| Pfloatorder
200202
| Pfloatmin

Diff for: compiler/ml/printlambda.ml

+2
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ let primitive ppf = function
148148
| Pmulint -> fprintf ppf "*"
149149
| Pdivint -> fprintf ppf "/"
150150
| Pmodint -> fprintf ppf "mod"
151+
| Ppowint -> fprintf ppf "**"
151152
| Pandint -> fprintf ppf "and"
152153
| Porint -> fprintf ppf "or"
153154
| Pxorint -> fprintf ppf "xor"
@@ -174,6 +175,7 @@ let primitive ppf = function
174175
| Pmulfloat -> fprintf ppf "*."
175176
| Pdivfloat -> fprintf ppf "/."
176177
| Pmodfloat -> fprintf ppf "mod"
178+
| Ppowfloat -> fprintf ppf "**"
177179
| Pfloatcomp Ceq -> fprintf ppf "==."
178180
| Pfloatcomp Cneq -> fprintf ppf "!=."
179181
| Pfloatcomp Clt -> fprintf ppf "<."

Diff for: compiler/ml/unified_ops.ml

+13
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,19 @@ let entries =
161161
string = None;
162162
};
163163
};
164+
{
165+
path = builtin "**";
166+
name = "%pow";
167+
form = Binary;
168+
specialization =
169+
{
170+
int = Ppowint;
171+
bool = None;
172+
float = Some Ppowfloat;
173+
bigint = Some Ppowbigint;
174+
string = None;
175+
};
176+
};
164177
|]
165178

166179
let index_by_path =

Diff for: compiler/syntax/src/res_parens.ml

+5-5
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,12 @@ let binary_expr_operand ~is_lhs expr =
140140
else Nothing)
141141

142142
let sub_binary_expr_operand parent_operator child_operator =
143-
let prec_parent = ParsetreeViewer.operator_precedence parent_operator in
144-
let prec_child = ParsetreeViewer.operator_precedence child_operator in
143+
let open ParsetreeViewer in
144+
let prec_parent = operator_precedence parent_operator in
145+
let prec_child = operator_precedence child_operator in
145146
prec_parent > prec_child
146-
|| prec_parent == prec_child
147-
&& not
148-
(ParsetreeViewer.flattenable_operators parent_operator child_operator)
147+
|| is_equality_operator parent_operator
148+
&& is_equality_operator child_operator
149149
||
150150
(* a && b || c, add parens to (a && b) for readability, who knows the difference by heart… *)
151151
(parent_operator = "||" && child_operator = "&&")

Diff for: compiler/syntax/src/res_parsetree_viewer.mli

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ val is_unary_expression : Parsetree.expression -> bool
6969
val is_binary_operator : string -> bool
7070
val is_binary_expression : Parsetree.expression -> bool
7171
val is_rhs_binary_operator : string -> bool
72+
val is_equality_operator : string -> bool
7273

7374
val flattenable_operators : string -> string -> bool
7475

Diff for: runtime/Pervasives.res

+1-3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ external \"*": ('a, 'a) => 'a = "%mul"
5151
external \"/": ('a, 'a) => 'a = "%div"
5252
external \"%": ('a, 'a) => 'a = "%mod"
5353
external mod: ('a, 'a) => 'a = "%mod"
54+
external \"**": ('a, 'a) => 'a = "%pow"
5455

5556
/* Comparisons */
5657
/* Note: Later comparisons will be converted to unified operations too */
@@ -114,9 +115,6 @@ external \"-.": (float, float) => float = "%subfloat"
114115
external \"*.": (float, float) => float = "%mulfloat"
115116
external \"/.": (float, float) => float = "%divfloat"
116117

117-
@deprecated("Use Core instead. This will be removed in v13") @val @scope("Math")
118-
external \"**": (float, float) => float = "pow"
119-
120118
@deprecated("Use Core instead. This will be removed in v13") @val @scope("Math")
121119
external exp: float => float = "exp"
122120

Diff for: runtime/Pervasives_mini.res

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ external \"*": (int, int) => int = "%mulint"
3030
external \"/": (int, int) => int = "%divint"
3131
external \"%": (int, int) => int = "%modint"
3232
external mod: (int, int) => int = "%modint"
33+
external \"**": (int, int) => int = "%powint"
3334

3435
/* Comparisons */
3536
/* Note: Later comparisons will be converted to unified operations too */

Diff for: tests/tests/src/b.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
function f(point) {
55
let y = point.y;
66
let x = point.x;
7-
return Math.pow(x * x + y * y, 2);
7+
return (x * x + y * y) ** 2;
88
}
99

1010
export {

Diff for: tests/tests/src/exponentiation_precedence_test.mjs

-18
This file was deleted.

Diff for: tests/tests/src/exponentiation_precedence_test.res

-4
This file was deleted.

0 commit comments

Comments
 (0)