Skip to content
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

Upgrade exponentiation to unified operator #7153

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
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 @@ -16,6 +16,7 @@

- Introduce "Unified operators" for arithmetic operators (`+`, `-`, `*`, `/`, `mod`). https://github.com/rescript-lang/rescript-compiler/pull/7057
- Add remainder (`%`, aka modulus) operator. https://github.com/rescript-lang/rescript-compiler/pull/7152
- Allow exponentiation (`**`) operator for `int` and `float`, using ES7 `**`. https://github.com/rescript-lang/rescript-compiler/pull/7153

# 12.0.0-alpha.4

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 @@ -1533,6 +1533,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 @@ -1604,6 +1605,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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you checked that this is semantically equivalent to js power, or whatever Pow does below?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different from js pow, especially when overflow. If I leave js pow out, wouldn't it cause undefined behavior in ReScript, like values ​​greater than max_int?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, seems it is already possible by n ** n

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intended question is: does the case with constants behave exactly like when 2 variables with the same constant values are passed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added more tests to verify it

| _ -> 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 @@ -251,6 +251,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 @@ -275,6 +277,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 @@ -251,6 +251,7 @@ let lam_prim ~primitive:(p : Lambda.primitive) ~args loc : Lam.t =
| Pmulint -> prim ~primitive:Pmulint ~args loc
| Pdivint _is_safe (*FIXME*) -> prim ~primitive:Pdivint ~args loc
| Pmodint _is_safe (*FIXME*) -> 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 @@ -283,6 +284,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 @@ -75,6 +75,7 @@ type t =
| Pmulint
| Pdivint
| Pmodint
| Ppowint
| Pandint
| Porint
| Pxorint
Expand All @@ -96,6 +97,7 @@ type t =
| Pmulfloat
| Pdivfloat
| Pmodfloat
| Ppowfloat
| Pfloatcomp of Lam_compat.comparison
| Pfloatorder
| Pfloatmin
Expand Down Expand Up @@ -199,12 +201,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 @@ -69,6 +69,7 @@ type t =
| Pmulint
| Pdivint
| Pmodint
| Ppowint
| Pandint
| Porint
| Pxorint
Expand All @@ -90,6 +91,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 @@ -116,6 +116,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 @@ -141,6 +142,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
8 changes: 8 additions & 0 deletions compiler/ext/ext_int.ml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ 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 rec int32_pow (a : int32) = function
| 0l -> 1l
| 1l -> a
| n ->
let b = int32_pow a (Int32.div n 2l) in
let b = Int32.mul b b in
Int32.mul b (if Int32.rem n 2l = 0l then 1l else a)
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 @@ -220,6 +220,7 @@ type primitive =
| Pmulint
| Pdivint of is_safe
| Pmodint of is_safe
| Ppowint
| Pandint
| Porint
| Pxorint
Expand All @@ -242,6 +243,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 @@ -183,6 +183,7 @@ type primitive =
| Pmulint
| Pdivint of is_safe
| Pmodint of is_safe
| Ppowint
| Pandint
| Porint
| Pxorint
Expand All @@ -205,6 +206,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 @@ -156,6 +156,7 @@ let primitive ppf = function
| Pdivint Unsafe -> fprintf ppf "/u"
| Pmodint Safe -> fprintf ppf "mod"
| Pmodint Unsafe -> fprintf ppf "mod_unsafe"
| Ppowint -> fprintf ppf "**"
| Pandint -> fprintf ppf "and"
| Porint -> fprintf ppf "or"
| Pxorint -> fprintf ppf "xor"
Expand All @@ -182,6 +183,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 @@ -148,12 +148,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
cristianoc marked this conversation as resolved.
Show resolved Hide resolved
&& 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 @@ -81,6 +81,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