Skip to content

Add support for the bitwise NOT(~) unary operator #7418

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 5 commits into from
May 5, 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 @@

- Add shift (`<<`, `>>`, `>>>`) operators for `int` and `bigint`. https://github.com/rescript-lang/rescript/pull/7183
- Add bitwise AND (`&`) operator for `int` and `bigint`. https://github.com/rescript-lang/rescript/pull/7415
- Add bitwise NOT (`~`) operator for `int` and `bigint`. https://github.com/rescript-lang/rescript/pull/7418
- Significantly reduced the download size by splitting binaries into optional platform-specific dependencies (e.g, `@rescript/linux-x64`). https://github.com/rescript-lang/rescript/pull/7395
- JSX: do not error on ref as prop anymore (which is allowed in React 19). https://github.com/rescript-lang/rescript/pull/7420

Expand Down
1 change: 1 addition & 0 deletions compiler/core/j.ml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ and expression_desc =
| Typeof of expression
| In of expression * expression (* prop in obj *)
| Js_not of expression (* !v *)
| Js_bnot of expression (* ~v *)
(* TODO: Add some primitives so that [js inliner] can do a better job *)
| Seq of expression * expression
| Cond of expression * expression * expression
Expand Down
6 changes: 3 additions & 3 deletions compiler/core/js_analyzer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ let rec no_side_effect_expression_desc (x : J.expression_desc) =
no_side_effect call_expr
&& Ext_list.for_all strings no_side_effect
&& Ext_list.for_all values no_side_effect
| Js_not e -> no_side_effect e
| Js_not e | Js_bnot e -> no_side_effect e
| In (prop, obj) -> no_side_effect prop && no_side_effect obj
| Cond (a, b, c) -> no_side_effect a && no_side_effect b && no_side_effect c
| Call ({expression_desc = Str {txt = "Array.isArray"}}, [e], _) ->
Expand Down Expand Up @@ -228,8 +228,8 @@ let rec eq_expression ({expression_desc = x0} : J.expression)
eq_expression_list ls0 ls1 && flag0 = flag1 && eq_expression tag0 tag1
| _ -> false)
| Length _ | Is_null_or_undefined _ | String_append _ | Typeof _ | Js_not _
| In _ | Cond _ | FlatCall _ | New _ | Fun _ | Raw_js_code _ | Array _
| Caml_block_tag _ | Object _ | Tagged_template _ | Await _ ->
| Js_bnot _ | In _ | Cond _ | FlatCall _ | New _ | Fun _ | Raw_js_code _
| Array _ | Caml_block_tag _ | Object _ | Tagged_template _ | Await _ ->
false
| Spread _ -> false

Expand Down
8 changes: 6 additions & 2 deletions compiler/core/js_dump.ml
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ let rec exp_need_paren ?(arrow = false) (e : J.expression) =
| Length _ | Call _ | Caml_block_tag _ | Seq _ | Static_index _ | Cond _
| Bin _ | Is_null_or_undefined _ | String_index _ | Array_index _
| String_append _ | Var _ | Undefined _ | Null | Str _ | Array _
| Caml_block _ | FlatCall _ | Typeof _ | Number _ | Js_not _ | In _ | Bool _
| New _ ->
| Caml_block _ | FlatCall _ | Typeof _ | Number _ | Js_not _ | Js_bnot _
| In _ | Bool _ | New _ ->
false
| Await _ -> false
| Spread _ -> false
Expand Down Expand Up @@ -677,6 +677,10 @@ and expression_desc cxt ~(level : int) f x : cxt =
P.cond_paren_group f (level > 13) (fun _ ->
P.string f "!";
expression ~level:13 cxt f e)
| Js_bnot e ->
P.cond_paren_group f (level > 13) (fun _ ->
P.string f "~";
expression ~level:13 cxt f e)
| In (prop, obj) ->
P.cond_paren_group f (level > 12) (fun _ ->
let cxt = expression ~level:0 cxt f prop in
Expand Down
5 changes: 5 additions & 0 deletions compiler/core/js_exp_make.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1616,6 +1616,11 @@ 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 int_bnot ?comment (e : t) : J.expression =
match e.expression_desc with
| Number (Int {i}) -> int ?comment (Int32.lognot i)
| _ -> {comment; expression_desc = Js_bnot e}

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}) ->
Expand Down
2 changes: 2 additions & 0 deletions compiler/core/js_exp_make.mli
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ val float_equal : ?comment:string -> t -> t -> t

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

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

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

val eq_null_undefined_boolean : ?comment:string -> t -> t -> t
Expand Down
3 changes: 3 additions & 0 deletions compiler/core/js_fold.ml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ class fold =
| Js_not _x0 ->
let _self = _self#expression _x0 in
_self
| Js_bnot _x0 ->
let _self = _self#expression _x0 in
_self
| In (_x0, _x1) ->
let _self = _self#expression _x0 in
let _self = _self#expression _x1 in
Expand Down
1 change: 1 addition & 0 deletions compiler/core/js_op.ml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type binop =
| Le
| Gt
| Ge
| Bnot
| Bor
| Bxor
| Band
Expand Down
3 changes: 2 additions & 1 deletion compiler/core/js_op_util.ml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ let op_prec (op : Js_op.binop) =
| Bxor -> (6, 6, 6)
| Band -> (7, 7, 7)
| Lsl | Lsr | Asr -> (10, 10, 11)
| Plus | Minus -> (11, 11, 12)
| Bnot | Plus | Minus -> (11, 11, 12)
| Mul | Div | Mod -> (12, 12, 13)
| Pow -> (13, 14, 12)

Expand All @@ -55,6 +55,7 @@ let op_int_prec (op : Js_op.int_op) =

let op_str (op : Js_op.binop) =
match op with
| Bnot -> "~"
| Bor -> "|"
| Bxor -> "^"
| Band -> "&"
Expand Down
3 changes: 3 additions & 0 deletions compiler/core/js_record_fold.ml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ let expression_desc : 'a. ('a, expression_desc) fn =
| Js_not _x0 ->
let st = _self.expression _self st _x0 in
st
| Js_bnot _x0 ->
let st = _self.expression _self st _x0 in
st
| In (_x0, _x1) ->
let st = _self.expression _self st _x0 in
let st = _self.expression _self st _x1 in
Expand Down
1 change: 1 addition & 0 deletions compiler/core/js_record_iter.ml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ let expression_desc : expression_desc fn =
| Bool _ -> ()
| Typeof _x0 -> _self.expression _self _x0
| Js_not _x0 -> _self.expression _self _x0
| Js_bnot _x0 -> _self.expression _self _x0
| In (_x0, _x1) ->
_self.expression _self _x0;
_self.expression _self _x1
Expand Down
3 changes: 3 additions & 0 deletions compiler/core/js_record_map.ml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ let expression_desc : expression_desc fn =
| Js_not _x0 ->
let _x0 = _self.expression _self _x0 in
Js_not _x0
| Js_bnot _x0 ->
let _x0 = _self.expression _self _x0 in
Js_bnot _x0
| In (_x0, _x1) ->
let _x0 = _self.expression _self _x0 in
let _x1 = _self.expression _self _x1 in
Expand Down
8 changes: 4 additions & 4 deletions compiler/core/lam_analysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,16 @@ let rec no_side_effects (lam : Lam.t) : bool =
(* bool primitives *)
| Psequand | Psequor | Pnot | Pboolcomp _ | Pboolorder | Pboolmin | Pboolmax
(* int primitives *)
| Pnegint | Paddint | Psubint | Pmulint | Ppowint | Pandint | Porint
| Pxorint | Plslint | Plsrint | Pasrint | Pintcomp _ | Pintorder | Pintmin
| Pintmax
| Pnegint | Paddint | Psubint | Pmulint | Ppowint | Pnotint | Pandint
| Porint | Pxorint | Plslint | Plsrint | Pasrint | Pintcomp _ | Pintorder
| Pintmin | Pintmax
(* float primitives *)
| Pintoffloat | Pfloatofint | Pnegfloat | Paddfloat | Psubfloat | Pmulfloat
| Ppowfloat | Pdivfloat | Pmodfloat | Pfloatcomp _ | Pjscomp _ | Pfloatorder
| Pfloatmin | Pfloatmax
(* bigint primitives *)
| Pnegbigint | Paddbigint | Psubbigint | Pmulbigint | Ppowbigint
| Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint
| Pnotbigint | Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint
| Pbigintcomp _ | Pbigintorder | Pbigintmin | Pbigintmax
(* string primitives *)
| Pstringlength | Pstringrefu | Pstringrefs | Pstringcomp _ | Pstringorder
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 @@ -283,6 +283,14 @@ let translate output_prefix loc (cxt : Lam_compile_context.t)
match args with
| [e1; e2] -> E.bigint_op Asr e1 e2
| _ -> assert false)
| Pnotint -> (
match args with
| [e] -> E.int_bnot e
| _ -> assert false)
| Pnotbigint -> (
match args with
| [e] -> E.int_bnot e
| _ -> assert false)
| Pandint -> (
match args with
| [e1; e2] -> E.int32_band 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 @@ -253,6 +253,7 @@ let lam_prim ~primitive:(p : Lambda.primitive) ~args loc : Lam.t =
| Pandint -> prim ~primitive:Pandint ~args loc
| Porint -> prim ~primitive:Porint ~args loc
| Pxorint -> prim ~primitive:Pxorint ~args loc
| Pnotint -> prim ~primitive:Pnotint ~args loc
| Plslint -> prim ~primitive:Plslint ~args loc
| Plsrint -> prim ~primitive:Plsrint ~args loc
| Pasrint -> prim ~primitive:Pasrint ~args loc
Expand Down Expand Up @@ -296,6 +297,7 @@ let lam_prim ~primitive:(p : Lambda.primitive) ~args loc : Lam.t =
| Pandbigint -> prim ~primitive:Pandbigint ~args loc
| Porbigint -> prim ~primitive:Porbigint ~args loc
| Pxorbigint -> prim ~primitive:Pxorbigint ~args loc
| Pnotbigint -> prim ~primitive:Pnotbigint ~args loc
| Plslbigint -> prim ~primitive:Plslbigint ~args loc
| Pasrbigint -> prim ~primitive:Pasrbigint ~args loc
| Pbigintcomp x -> prim ~primitive:(Pbigintcomp x) ~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 @@ -77,6 +77,7 @@ type t =
| Pandint
| Porint
| Pxorint
| Pnotint
| Plslint
| Plsrint
| Pasrint
Expand Down Expand Up @@ -111,6 +112,7 @@ type t =
| Pandbigint
| Porbigint
| Pxorbigint
| Pnotbigint
| Plslbigint
| Pasrbigint
| Pbigintcomp of Lam_compat.comparison
Expand Down Expand Up @@ -201,15 +203,15 @@ let eq_primitive_approx (lhs : t) (rhs : t) =
| Psequand | Psequor | Pnot | Pboolcomp _ | Pboolorder | Pboolmin | Pboolmax
(* int primitives *)
| Pisint | Pnegint | Paddint | Psubint | Pmulint | Pdivint | Pmodint | Ppowint
| Pandint | Porint | Pxorint | Plslint | Plsrint | Pasrint | Pintorder
| Pintmin | Pintmax
| Pnotint | Pandint | Porint | Pxorint | Plslint | Plsrint | Pasrint
| Pintorder | Pintmin | Pintmax
(* float primitives *)
| Pintoffloat | Pfloatofint | Pnegfloat | Paddfloat | Psubfloat | Pmulfloat
| Pdivfloat | Pmodfloat | Ppowfloat | Pfloatorder | Pfloatmin | Pfloatmax
(* bigint primitives *)
| Pnegbigint | Paddbigint | Psubbigint | Pmulbigint | Pdivbigint | Pmodbigint
| Ppowbigint | Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint
| Pbigintorder | Pbigintmin | Pbigintmax
| Ppowbigint | Pnotbigint | Pandbigint | Porbigint | Pxorbigint | Plslbigint
| Pasrbigint | Pbigintorder | Pbigintmin | Pbigintmax
(* string primitives *)
| Pstringlength | Pstringrefu | Pstringrefs | Pstringadd | Pstringcomp _
| Pstringorder | Pstringmin | Pstringmax
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 @@ -72,6 +72,7 @@ type t =
| Pandint
| Porint
| Pxorint
| Pnotint
| Plslint
| Plsrint
| Pasrint
Expand Down Expand Up @@ -106,6 +107,7 @@ type t =
| Pandbigint
| Porbigint
| Pxorbigint
| Pnotbigint
| Plslbigint
| Pasrbigint
| Pbigintcomp of Lam_compat.comparison
Expand Down
6 changes: 4 additions & 2 deletions compiler/core/lam_print.ml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ let primitive ppf (prim : Lam_primitive.t) =
| Pboolorder -> fprintf ppf "compare"
| Pboolmin -> fprintf ppf "min"
| Pboolmax -> fprintf ppf "max"
| Pnegint -> fprintf ppf "~"
| Pnegint -> fprintf ppf "~-"
| Paddint -> fprintf ppf "+"
| Pstringadd -> fprintf ppf "+*"
| Psubint -> fprintf ppf "-"
Expand All @@ -119,6 +119,7 @@ let primitive ppf (prim : Lam_primitive.t) =
| Pandint -> fprintf ppf "and"
| Porint -> fprintf ppf "or"
| Pxorint -> fprintf ppf "xor"
| Pnotint -> fprintf ppf "~~"
| Plslint -> fprintf ppf "lsl"
| Plsrint -> fprintf ppf "lsr"
| Pasrint -> fprintf ppf "asr"
Expand Down Expand Up @@ -151,7 +152,7 @@ let primitive ppf (prim : Lam_primitive.t) =
| Pfloatorder -> fprintf ppf "compare"
| Pfloatmin -> fprintf ppf "min"
| Pfloatmax -> fprintf ppf "max"
| Pnegbigint -> fprintf ppf "~"
| Pnegbigint -> fprintf ppf "~-"
| Paddbigint -> fprintf ppf "+"
| Psubbigint -> fprintf ppf "-"
| Pmulbigint -> fprintf ppf "*"
Expand All @@ -161,6 +162,7 @@ let primitive ppf (prim : Lam_primitive.t) =
| Pandbigint -> fprintf ppf "and"
| Porbigint -> fprintf ppf "or"
| Pxorbigint -> fprintf ppf "xor"
| Pnotbigint -> fprintf ppf "~~"
| Plslbigint -> fprintf ppf "lsl"
| Pasrbigint -> fprintf ppf "asr"
| Pbigintcomp Ceq -> fprintf ppf "=="
Expand Down
2 changes: 2 additions & 0 deletions compiler/ml/lambda.ml
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ type primitive =
| Pandint
| Porint
| Pxorint
| Pnotint
| Plslint
| Plsrint
| Pasrint
Expand Down Expand Up @@ -245,6 +246,7 @@ type primitive =
| Pandbigint
| Porbigint
| Pxorbigint
| Pnotbigint
| Plslbigint
| Pasrbigint
| Pbigintcomp of comparison
Expand Down
2 changes: 2 additions & 0 deletions compiler/ml/lambda.mli
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ type primitive =
| Pandint
| Porint
| Pxorint
| Pnotint
| Plslint
| Plsrint
| Pasrint
Expand Down Expand Up @@ -212,6 +213,7 @@ type primitive =
| Pandbigint
| Porbigint
| Pxorbigint
| Pnotbigint
| Plslbigint
| Pasrbigint
| Pbigintcomp of comparison
Expand Down
2 changes: 1 addition & 1 deletion compiler/ml/pprintast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ and expression ctxt f x =
| `Prefix s -> (
let s =
if
List.mem s ["~+"; "~-"; "~+."; "~-."]
List.mem s ["~+"; "~-"; "~+."; "~-."; "~~"]
&&
match l with
(* See #7200: avoid turning (~- 1) into (- 1) which is
Expand Down
8 changes: 5 additions & 3 deletions compiler/ml/printlambda.ml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ let primitive ppf = function
| Pboolorder -> fprintf ppf "compare"
| Pboolmin -> fprintf ppf "min"
| Pboolmax -> fprintf ppf "max"
| Pnegint -> fprintf ppf "~"
| Pnegint -> fprintf ppf "~-"
| Paddint -> fprintf ppf "+"
| Psubint -> fprintf ppf "-"
| Pmulint -> fprintf ppf "*"
Expand All @@ -152,6 +152,7 @@ let primitive ppf = function
| Pandint -> fprintf ppf "and"
| Porint -> fprintf ppf "or"
| Pxorint -> fprintf ppf "xor"
| Pnotint -> fprintf ppf "~~"
| Plslint -> fprintf ppf "lsl"
| Plsrint -> fprintf ppf "lsr"
| Pasrint -> fprintf ppf "asr"
Expand All @@ -168,7 +169,7 @@ let primitive ppf = function
| Poffsetref n -> fprintf ppf "+:=%i" n
| Pintoffloat -> fprintf ppf "int_of_float"
| Pfloatofint -> fprintf ppf "float_of_int"
| Pnegfloat -> fprintf ppf "~."
| Pnegfloat -> fprintf ppf "~-."
| Pabsfloat -> fprintf ppf "abs."
| Paddfloat -> fprintf ppf "+."
| Psubfloat -> fprintf ppf "-."
Expand All @@ -185,14 +186,15 @@ let primitive ppf = function
| Pfloatorder -> fprintf ppf "compare"
| Pfloatmin -> fprintf ppf "min"
| Pfloatmax -> fprintf ppf "max"
| Pnegbigint -> fprintf ppf "~"
| Pnegbigint -> fprintf ppf "~-"
| Paddbigint -> fprintf ppf "+"
| Psubbigint -> fprintf ppf "-"
| Pmulbigint -> fprintf ppf "*"
| Ppowbigint -> fprintf ppf "**"
| Pandbigint -> fprintf ppf "and"
| Porbigint -> fprintf ppf "or"
| Pxorbigint -> fprintf ppf "xor"
| Pnotbigint -> fprintf ppf "~~"
| Plslbigint -> fprintf ppf "lsl"
| Pasrbigint -> fprintf ppf "asr"
| Pdivbigint -> fprintf ppf "/"
Expand Down
2 changes: 2 additions & 0 deletions compiler/ml/translcore.ml
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ let primitives_table =
("%mulint", Pmulint);
("%divint", Pdivint);
("%modint", Pmodint);
("%bitnot_int", Pnotint);
("%andint", Pandint);
("%orint", Porint);
("%xorint", Pxorint);
Expand Down Expand Up @@ -322,6 +323,7 @@ let primitives_table =
("%lebigint", Pbigintcomp Cle);
("%gtbigint", Pbigintcomp Cgt);
("%gebigint", Pbigintcomp Cge);
("%bitnot_bigint", Pnotbigint);
("%andbigint", Pandbigint);
("%orbigint", Porbigint);
("%xorbigint", Pxorbigint);
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 @@ -207,6 +207,19 @@ let entries =
string = None;
};
};
{
path = builtin "~~";
name = "%bitnot";
form = Unary;
specialization =
{
int = Pnotint;
bool = None;
float = None;
bigint = Some Pnotbigint;
string = None;
};
};
{
path = builtin "&";
name = "%bitand";
Expand Down
Loading