Skip to content

Upgrade exponentiation to unified operator #7153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Mar 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- Make reanalyze exception tracking work with the new stdlib. https://github.com/rescript-lang/rescript/pull/7328
- Fix Pervasive.max using boolean comparison for floats. https://github.com/rescript-lang/rescript/pull/7333
- 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
- Add unified exponentiation (`**`) operator for numeric types using ES7 `**`. https://github.com/rescript-lang/rescript-compiler/pull/7153

#### :boom: Breaking Change

Expand Down
7 changes: 7 additions & 0 deletions compiler/core/js_exp_make.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,7 @@ let unchecked_int32_minus ?comment e1 e2 : J.expression =
float_minus ?comment e1 e2

let float_div ?comment e1 e2 = bin ?comment Div e1 e2
let float_pow ?comment e1 e2 = bin ?comment Pow e1 e2
let float_notequal ?comment e1 e2 = bin ?comment NotEqEq e1 e2

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

let int32_pow ?comment (e1 : t) (e2 : t) : J.expression =
match (e1.expression_desc, e2.expression_desc) with
| Number (Int {i = i1}), Number (Int {i = i2}) ->
int ?comment (Ext_int.int32_pow i1 i2)
| _ -> to_int32 (float_pow ?comment e1 e2)

let rec int32_bxor ?comment (e1 : t) (e2 : t) : J.expression =
match (e1.expression_desc, e2.expression_desc) with
| Number (Int {i = i1}), Number (Int {i = i2}) ->
Expand Down
4 changes: 4 additions & 0 deletions compiler/core/js_exp_make.mli
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ val int32_div : checked:bool -> ?comment:string -> t -> t -> t

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

val int32_pow : ?comment:string -> t -> t -> t

val int32_lsl : ?comment:string -> t -> t -> t

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

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

val float_pow : ?comment:string -> t -> t -> t

val int_comp : Lam_compat.comparison -> ?comment:string -> t -> t -> t

val bool_comp : Lam_compat.comparison -> ?comment:string -> t -> t -> t
Expand Down
3 changes: 2 additions & 1 deletion compiler/core/js_op.ml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ type int_op =
| Div
(* x / y | 0 *)
| Mod
(* x % y *)
(* x % y *)
| Pow (* x ** y | 0 *)

(* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise_operators
{[
Expand Down
5 changes: 4 additions & 1 deletion compiler/core/js_op_util.ml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ let op_prec (op : Js_op.binop) =
| Band -> (7, 7, 7)
| Lsl | Lsr | Asr -> (10, 10, 11)
| Plus | Minus -> (11, 11, 12)
| Mul | Div | Mod | Pow -> (12, 12, 13)
| Mul | Div | Mod -> (12, 12, 13)
| Pow -> (13, 14, 12)

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

let op_str (op : Js_op.binop) =
match op with
Expand Down Expand Up @@ -89,6 +91,7 @@ let op_int_str (op : Js_op.int_op) =
| Mul -> "*"
| Div -> "/"
| Mod -> "%"
| Pow -> "**"

let str_of_used_stats x =
match (x : Js_op.used_stats) with
Expand Down
9 changes: 5 additions & 4 deletions compiler/core/lam_analysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@ let rec no_side_effects (lam : Lam.t) : bool =
(* bool primitives *)
| Psequand | Psequor | Pnot | Pboolcomp _ | Pboolorder | Pboolmin | Pboolmax
(* int primitives *)
| Pnegint | Paddint | Psubint | Pmulint | Pandint | Porint | Pxorint
| Plslint | Plsrint | Pasrint | Pintcomp _ | Pintorder | Pintmin | Pintmax
| Pnegint | Paddint | Psubint | Pmulint | Ppowint | Pandint | Porint
| Pxorint | Plslint | Plsrint | Pasrint | Pintcomp _ | Pintorder | Pintmin
| Pintmax
(* float primitives *)
| Pintoffloat | Pfloatofint | Pnegfloat | Paddfloat | Psubfloat | Pmulfloat
| Pdivfloat | Pmodfloat | Pfloatcomp _ | Pjscomp _ | Pfloatorder | Pfloatmin
| Pfloatmax
| Ppowfloat | Pdivfloat | Pmodfloat | Pfloatcomp _ | Pjscomp _ | Pfloatorder
| Pfloatmin | Pfloatmax
(* bigint primitives *)
| Pnegbigint | Paddbigint | Psubbigint | Pmulbigint | Ppowbigint
| Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint
Expand Down
8 changes: 8 additions & 0 deletions compiler/core/lam_compile_primitive.ml
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,14 @@ let translate output_prefix loc (cxt : Lam_compile_context.t)
match args with
| [e1; e2] -> E.bigint_mod ~checked:!Js_config.check_div_by_zero e1 e2
| _ -> assert false)
| Ppowint -> (
match args with
| [e1; e2] -> E.int32_pow e1 e2
| _ -> assert false)
| Ppowfloat -> (
match args with
| [e1; e2] -> E.float_pow e1 e2
| _ -> assert false)
| Ppowbigint -> (
match args with
| [e1; e2] -> E.bigint_op Pow e1 e2
Expand Down
2 changes: 2 additions & 0 deletions compiler/core/lam_convert.ml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ let lam_prim ~primitive:(p : Lambda.primitive) ~args loc : Lam.t =
| Pmulint -> prim ~primitive:Pmulint ~args loc
| Pdivint -> prim ~primitive:Pdivint ~args loc
| Pmodint -> prim ~primitive:Pmodint ~args loc
| Ppowint -> prim ~primitive:Ppowint ~args loc
| Pandint -> prim ~primitive:Pandint ~args loc
| Porint -> prim ~primitive:Porint ~args loc
| Pxorint -> prim ~primitive:Pxorint ~args loc
Expand Down Expand Up @@ -281,6 +282,7 @@ let lam_prim ~primitive:(p : Lambda.primitive) ~args loc : Lam.t =
| Pmulfloat -> prim ~primitive:Pmulfloat ~args loc
| Pdivfloat -> prim ~primitive:Pdivfloat ~args loc
| Pmodfloat -> prim ~primitive:Pmodfloat ~args loc
| Ppowfloat -> prim ~primitive:Ppowfloat ~args loc
| Pfloatorder -> prim ~primitive:Pfloatorder ~args loc
| Pfloatmin -> prim ~primitive:Pfloatmin ~args loc
| Pfloatmax -> prim ~primitive:Pfloatmax ~args loc
Expand Down
10 changes: 6 additions & 4 deletions compiler/core/lam_primitive.ml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type t =
| Pmulint
| Pdivint
| Pmodint
| Ppowint
| Pandint
| Porint
| Pxorint
Expand All @@ -94,6 +95,7 @@ type t =
| Pmulfloat
| Pdivfloat
| Pmodfloat
| Ppowfloat
| Pfloatcomp of Lam_compat.comparison
| Pfloatorder
| Pfloatmin
Expand Down Expand Up @@ -197,12 +199,12 @@ let eq_primitive_approx (lhs : t) (rhs : t) =
(* bool primitives *)
| Psequand | Psequor | Pnot | Pboolcomp _ | Pboolorder | Pboolmin | Pboolmax
(* int primitives *)
| Pisint | Pnegint | Paddint | Psubint | Pmulint | Pdivint | Pmodint | Pandint
| Porint | Pxorint | Plslint | Plsrint | Pasrint | Pintorder | Pintmin
| Pintmax
| Pisint | Pnegint | Paddint | Psubint | Pmulint | Pdivint | Pmodint | Ppowint
| Pandint | Porint | Pxorint | Plslint | Plsrint | Pasrint | Pintorder
| Pintmin | Pintmax
(* float primitives *)
| Pintoffloat | Pfloatofint | Pnegfloat | Paddfloat | Psubfloat | Pmulfloat
| Pdivfloat | Pmodfloat | Pfloatorder | Pfloatmin | Pfloatmax
| Pdivfloat | Pmodfloat | Ppowfloat | Pfloatorder | Pfloatmin | Pfloatmax
(* bigint primitives *)
| Pnegbigint | Paddbigint | Psubbigint | Pmulbigint | Pdivbigint | Pmodbigint
| Ppowbigint | Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint
Expand Down
2 changes: 2 additions & 0 deletions compiler/core/lam_primitive.mli
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type t =
| Pmulint
| Pdivint
| Pmodint
| Ppowint
| Pandint
| Porint
| Pxorint
Expand All @@ -89,6 +90,7 @@ type t =
| Pmulfloat
| Pdivfloat
| Pmodfloat
| Ppowfloat
| Pfloatcomp of Lam_compat.comparison
| Pfloatorder
| Pfloatmin
Expand Down
2 changes: 2 additions & 0 deletions compiler/core/lam_print.ml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ let primitive ppf (prim : Lam_primitive.t) =
| Pmulint -> fprintf ppf "*"
| Pdivint -> fprintf ppf "/"
| Pmodint -> fprintf ppf "mod"
| Ppowint -> fprintf ppf "**"
| Pandint -> fprintf ppf "and"
| Porint -> fprintf ppf "or"
| Pxorint -> fprintf ppf "xor"
Expand All @@ -140,6 +141,7 @@ let primitive ppf (prim : Lam_primitive.t) =
| Pmulfloat -> fprintf ppf "*."
| Pdivfloat -> fprintf ppf "/."
| Pmodfloat -> fprintf ppf "mod"
| Ppowfloat -> fprintf ppf "**"
| Pfloatcomp Ceq -> fprintf ppf "==."
| Pfloatcomp Cneq -> fprintf ppf "!=."
| Pfloatcomp Clt -> fprintf ppf "<."
Expand Down
12 changes: 12 additions & 0 deletions compiler/ext/ext_int.ml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,15 @@ let move = 0x1_0000_0000
let int32_unsigned_to_int (n : int32) : int =
let i = Int32.to_int n in
if i < 0 then i + move else i

let int32_pow (x : int32) (y : int32) =
let x_float = Int32.to_float x in
let y_float = Int32.to_float y in
let result = x_float ** y_float in
let truncated =
if result > 2147483647.0 || result < -2147483648.0 then
let i = int_of_float result in
i land 0xFFFFFFFF
else int_of_float result
in
Int32.of_int truncated
2 changes: 2 additions & 0 deletions compiler/ext/ext_int.mli
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ val int32_unsigned_to_int : int32 -> int
works on 64 bit platform only
given input as an uint32 and convert it io int64
*)

val int32_pow : int32 -> int32 -> int32
2 changes: 2 additions & 0 deletions compiler/ml/lambda.ml
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ type primitive =
| Pmulint
| Pdivint
| Pmodint
| Ppowint
| Pandint
| Porint
| Pxorint
Expand All @@ -228,6 +229,7 @@ type primitive =
| Psubfloat
| Pmulfloat
| Pdivfloat
| Ppowfloat
| Pfloatcomp of comparison
| Pfloatorder
| Pfloatmin
Expand Down
2 changes: 2 additions & 0 deletions compiler/ml/lambda.mli
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ type primitive =
| Pmulint
| Pdivint
| Pmodint
| Ppowint
| Pandint
| Porint
| Pxorint
Expand All @@ -195,6 +196,7 @@ type primitive =
| Psubfloat
| Pmulfloat
| Pdivfloat
| Ppowfloat
| Pfloatcomp of comparison
| Pfloatorder
| Pfloatmin
Expand Down
2 changes: 2 additions & 0 deletions compiler/ml/printlambda.ml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ let primitive ppf = function
| Pmulint -> fprintf ppf "*"
| Pdivint -> fprintf ppf "/"
| Pmodint -> fprintf ppf "mod"
| Ppowint -> fprintf ppf "**"
| Pandint -> fprintf ppf "and"
| Porint -> fprintf ppf "or"
| Pxorint -> fprintf ppf "xor"
Expand All @@ -174,6 +175,7 @@ let primitive ppf = function
| Pmulfloat -> fprintf ppf "*."
| Pdivfloat -> fprintf ppf "/."
| Pmodfloat -> fprintf ppf "mod"
| Ppowfloat -> fprintf ppf "**"
| Pfloatcomp Ceq -> fprintf ppf "==."
| Pfloatcomp Cneq -> fprintf ppf "!=."
| Pfloatcomp Clt -> fprintf ppf "<."
Expand Down
13 changes: 13 additions & 0 deletions compiler/ml/unified_ops.ml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,19 @@ let entries =
string = None;
};
};
{
path = builtin "**";
name = "%pow";
form = Binary;
specialization =
{
int = Ppowint;
bool = None;
float = Some Ppowfloat;
bigint = Some Ppowbigint;
string = None;
};
};
|]

let index_by_path =
Expand Down
10 changes: 5 additions & 5 deletions compiler/syntax/src/res_parens.ml
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,12 @@ let binary_expr_operand ~is_lhs expr =
else Nothing)

let sub_binary_expr_operand parent_operator child_operator =
let prec_parent = ParsetreeViewer.operator_precedence parent_operator in
let prec_child = ParsetreeViewer.operator_precedence child_operator in
let open ParsetreeViewer in
let prec_parent = operator_precedence parent_operator in
let prec_child = operator_precedence child_operator in
prec_parent > prec_child
|| prec_parent == prec_child
&& not
(ParsetreeViewer.flattenable_operators parent_operator child_operator)
|| is_equality_operator parent_operator
&& is_equality_operator child_operator
||
(* a && b || c, add parens to (a && b) for readability, who knows the difference by heart… *)
(parent_operator = "||" && child_operator = "&&")
Expand Down
1 change: 1 addition & 0 deletions compiler/syntax/src/res_parsetree_viewer.mli
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ val is_unary_expression : Parsetree.expression -> bool
val is_binary_operator : string -> bool
val is_binary_expression : Parsetree.expression -> bool
val is_rhs_binary_operator : string -> bool
val is_equality_operator : string -> bool

val flattenable_operators : string -> string -> bool

Expand Down
4 changes: 1 addition & 3 deletions runtime/Pervasives.res
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ external \"*": ('a, 'a) => 'a = "%mul"
external \"/": ('a, 'a) => 'a = "%div"
external \"%": ('a, 'a) => 'a = "%mod"
external mod: ('a, 'a) => 'a = "%mod"
external \"**": ('a, 'a) => 'a = "%pow"

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

@deprecated("Use Core instead. This will be removed in v13") @val @scope("Math")
external \"**": (float, float) => float = "pow"

@deprecated("Use Core instead. This will be removed in v13") @val @scope("Math")
external exp: float => float = "exp"

Expand Down
1 change: 1 addition & 0 deletions runtime/Pervasives_mini.res
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ external \"*": (int, int) => int = "%mulint"
external \"/": (int, int) => int = "%divint"
external \"%": (int, int) => int = "%modint"
external mod: (int, int) => int = "%modint"
external \"**": (int, int) => int = "%powint"

/* Comparisons */
/* Note: Later comparisons will be converted to unified operations too */
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/src/b.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
function f(point) {
let y = point.y;
let x = point.x;
return Math.pow(x * x + y * y, 2);
return (x * x + y * y) ** 2;
}

export {
Expand Down
18 changes: 0 additions & 18 deletions tests/tests/src/exponentiation_precedence_test.mjs

This file was deleted.

4 changes: 0 additions & 4 deletions tests/tests/src/exponentiation_precedence_test.res

This file was deleted.

Loading