Skip to content

Commit 2324e1b

Browse files
committed
Safe promises
Introduce safe promises, based on rescript-lang#5707 - Add t-first Js.Promise2 with safe bindings - Remove type check for nested promises - Add example illustrating a typical example of nested promises, and how it goes away with Js.Promise2
1 parent 0bc4815 commit 2324e1b

19 files changed

+1744
-1661
lines changed

jscomp/ext/warnings.ml

-4
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ type t =
8484
| Bs_integer_literal_overflow (* 107 *)
8585
| Bs_uninterpreted_delimiters of string (* 108 *)
8686
| Bs_toplevel_expression_unit (* 109 *)
87-
| Bs_nested_promise of string (* 110 *)
8887

8988
(* If you remove a warning, leave a hole in the numbering. NEVER change
9089
the numbers of existing warnings.
@@ -150,7 +149,6 @@ let number = function
150149
| Bs_integer_literal_overflow -> 107
151150
| Bs_uninterpreted_delimiters _ -> 108
152151
| Bs_toplevel_expression_unit -> 109
153-
| Bs_nested_promise _ -> 110
154152

155153
let last_warning_number = 110
156154

@@ -494,8 +492,6 @@ let message = function
494492
| Bs_uninterpreted_delimiters s -> "Uninterpreted delimiters " ^ s
495493
| Bs_toplevel_expression_unit ->
496494
"Toplevel expression is expected to have unit type."
497-
| Bs_nested_promise s ->
498-
"Expression uses nested promise type " ^ s ^ " which is unsafe."
499495

500496
let sub_locs = function
501497
| Deprecated (_, def, use) ->

jscomp/ext/warnings.mli

-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ type t =
7777
| Bs_integer_literal_overflow (* 107 *)
7878
| Bs_uninterpreted_delimiters of string (* 108 *)
7979
| Bs_toplevel_expression_unit (* 109 *)
80-
| Bs_nested_promise of string (* 110 *)
8180

8281
val parse_options : bool -> string -> unit
8382

jscomp/main/builtin_cmj_datasets.ml

+30-28
Large diffs are not rendered by default.

jscomp/ml/typecore.ml

-35
Original file line numberDiff line numberDiff line change
@@ -1865,45 +1865,10 @@ and type_expect ?in_function ?recarg env sexp ty_expected =
18651865
type_expect_ ?in_function ?recarg env sexp ty_expected
18661866
)
18671867
in
1868-
checkTypeInvariant exp;
18691868
Cmt_format.set_saved_types
18701869
(Cmt_format.Partial_expression exp :: previous_saved_types);
18711870
exp
18721871

1873-
(* NOTE: the type invariant check should have no side effects and be efficient *)
1874-
and checkTypeInvariant exp : unit =
1875-
let rec extractPromise t =
1876-
match t.desc with
1877-
| Tconstr
1878-
(Pdot (Pdot (Pident { name = "Js" }, "Promise", _), "t", _), [ t1 ], _)
1879-
| Tconstr (Pident { name = "promise" }, [ t1 ], _) ->
1880-
(* Improvement: check for type aliases, if it can be done efficiently *)
1881-
Some t1
1882-
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> extractPromise t1
1883-
| _ -> None
1884-
in
1885-
(* Only traverse arguments of a type constructors and function types.
1886-
This should guarantee that the traversal finished quickly. *)
1887-
let rec findNestedPromise t =
1888-
match t.desc with
1889-
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> findNestedPromise t1
1890-
| Tconstr (_, ts, _) -> (
1891-
match extractPromise t with
1892-
| Some t1 -> (
1893-
match extractPromise t1 with
1894-
| Some _t2 ->
1895-
let nestedType = Format.asprintf "%a" Printtyp.type_expr t in
1896-
Location.prerr_warning exp.exp_loc
1897-
(Bs_nested_promise nestedType)
1898-
| None -> ts |> List.iter findNestedPromise)
1899-
| None -> ts |> List.iter findNestedPromise)
1900-
| Tarrow (_, t1, t2, _) ->
1901-
findNestedPromise t1;
1902-
findNestedPromise t2
1903-
| _ -> ()
1904-
in
1905-
findNestedPromise exp.exp_type
1906-
19071872
and type_expect_ ?in_function ?(recarg=Rejected) env sexp ty_expected =
19081873
let loc = sexp.pexp_loc in
19091874
(* Record the expression type before unifying it with the expected type *)

jscomp/others/js.ml

+3
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ module Re = Js_re
249249
module Promise = Js_promise
250250
(** Provide bindings to JS Promise *)
251251

252+
module Promise2 = Js_promise2
253+
(** Provide bindings to JS Promise *)
254+
252255
module Date = Js_date
253256
(** Provide bindings for JS Date *)
254257

jscomp/others/js_promise2.ml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
include Js_promise
2+
3+
let then_ : 'a promise -> ('a -> 'b promise) -> 'b promise =
4+
[%raw {|
5+
function(p, cont) {
6+
Promise.resolve(p).then(cont)
7+
}
8+
|}]
9+
10+
let catch_ : 'a promise -> (error -> 'a promise) -> 'a promise =
11+
[%raw
12+
{|
13+
function(p, cont) {
14+
Promise.resolve(p).catch(cont)
15+
}
16+
|}]

jscomp/others/release.ninja

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ o others/js_obj.cmi others/js_obj.cmj : cc others/js_obj.ml | others/belt_intern
4747
o others/js_option.cmj : cc_cmi others/js_option.ml | others/belt_internals.cmi others/js.cmi others/js_exn.cmj others/js_option.cmi $bsc
4848
o others/js_option.cmi : cc others/js_option.mli | others/belt_internals.cmi others/js.cmi $bsc
4949
o others/js_promise.cmi others/js_promise.cmj : cc others/js_promise.ml | others/belt_internals.cmi others/js.cmi $bsc
50+
o others/js_promise2.cmi others/js_promise2.cmj : cc others/js_promise2.ml | others/belt_internals.cmi others/js.cmi others/js_promise.cmj $bsc
5051
o others/js_re.cmi others/js_re.cmj : cc others/js_re.ml | others/belt_internals.cmi others/js.cmi others/js.cmj $bsc
5152
o others/js_result.cmj : cc_cmi others/js_result.ml | others/belt_internals.cmi others/js.cmi others/js_result.cmi $bsc
5253
o others/js_result.cmi : cc others/js_result.mli | others/belt_internals.cmi others/js.cmi $bsc
@@ -67,7 +68,7 @@ o others/jsx.cmi others/jsx.cmj : cc others/jsx.ml | others/belt_internals.cmi o
6768
o others/jsxDOM.cmi others/jsxDOM.cmj : cc others/jsxDOM.ml | others/belt_internals.cmi others/js.cmi others/jsx.cmj others/jsxDOMStyle.cmj others/jsxEvent.cmj $bsc
6869
o others/jsxDOMStyle.cmi others/jsxDOMStyle.cmj : cc others/jsxDOMStyle.ml | others/belt_internals.cmi others/js.cmi $bsc
6970
o others/jsxEvent.cmi others/jsxEvent.cmj : cc others/jsxEvent.ml | others/belt_internals.cmi others/js.cmi $bsc
70-
o js_pkg : phony others/js_OO.cmi others/js_OO.cmj others/js_array.cmi others/js_array.cmj others/js_array2.cmi others/js_array2.cmj others/js_bigint.cmi others/js_bigint.cmj others/js_cast.cmi others/js_cast.cmj others/js_console.cmi others/js_console.cmj others/js_date.cmi others/js_date.cmj others/js_dict.cmi others/js_dict.cmj others/js_exn.cmi others/js_exn.cmj others/js_float.cmi others/js_float.cmj others/js_global.cmi others/js_global.cmj others/js_int.cmi others/js_int.cmj others/js_json.cmi others/js_json.cmj others/js_list.cmi others/js_list.cmj others/js_map.cmi others/js_map.cmj others/js_mapperRt.cmi others/js_mapperRt.cmj others/js_math.cmi others/js_math.cmj others/js_null.cmi others/js_null.cmj others/js_null_undefined.cmi others/js_null_undefined.cmj others/js_obj.cmi others/js_obj.cmj others/js_option.cmi others/js_option.cmj others/js_promise.cmi others/js_promise.cmj others/js_re.cmi others/js_re.cmj others/js_result.cmi others/js_result.cmj others/js_set.cmi others/js_set.cmj others/js_string.cmi others/js_string.cmj others/js_string2.cmi others/js_string2.cmj others/js_typed_array.cmi others/js_typed_array.cmj others/js_typed_array2.cmi others/js_typed_array2.cmj others/js_types.cmi others/js_types.cmj others/js_undefined.cmi others/js_undefined.cmj others/js_vector.cmi others/js_vector.cmj others/js_weakmap.cmi others/js_weakmap.cmj others/js_weakset.cmi others/js_weakset.cmj others/jsx.cmi others/jsx.cmj others/jsxDOM.cmi others/jsxDOM.cmj others/jsxDOMStyle.cmi others/jsxDOMStyle.cmj others/jsxEvent.cmi others/jsxEvent.cmj
71+
o js_pkg : phony others/js_OO.cmi others/js_OO.cmj others/js_array.cmi others/js_array.cmj others/js_array2.cmi others/js_array2.cmj others/js_bigint.cmi others/js_bigint.cmj others/js_cast.cmi others/js_cast.cmj others/js_console.cmi others/js_console.cmj others/js_date.cmi others/js_date.cmj others/js_dict.cmi others/js_dict.cmj others/js_exn.cmi others/js_exn.cmj others/js_float.cmi others/js_float.cmj others/js_global.cmi others/js_global.cmj others/js_int.cmi others/js_int.cmj others/js_json.cmi others/js_json.cmj others/js_list.cmi others/js_list.cmj others/js_map.cmi others/js_map.cmj others/js_mapperRt.cmi others/js_mapperRt.cmj others/js_math.cmi others/js_math.cmj others/js_null.cmi others/js_null.cmj others/js_null_undefined.cmi others/js_null_undefined.cmj others/js_obj.cmi others/js_obj.cmj others/js_option.cmi others/js_option.cmj others/js_promise.cmi others/js_promise.cmj others/js_promise2.cmi others/js_promise2.cmj others/js_re.cmi others/js_re.cmj others/js_result.cmi others/js_result.cmj others/js_set.cmi others/js_set.cmj others/js_string.cmi others/js_string.cmj others/js_string2.cmi others/js_string2.cmj others/js_typed_array.cmi others/js_typed_array.cmj others/js_typed_array2.cmi others/js_typed_array2.cmj others/js_types.cmi others/js_types.cmj others/js_undefined.cmi others/js_undefined.cmj others/js_vector.cmi others/js_vector.cmj others/js_weakmap.cmi others/js_weakmap.cmj others/js_weakset.cmi others/js_weakset.cmj others/jsx.cmi others/jsx.cmj others/jsxDOM.cmi others/jsxDOM.cmj others/jsxDOMStyle.cmi others/jsxDOMStyle.cmj others/jsxEvent.cmi others/jsxEvent.cmj
7172
o others/belt_Array.cmj : cc_cmi others/belt_Array.ml | others/belt.cmi others/belt_Array.cmi others/belt_internals.cmi others/js.cmi others/js.cmj others/js_math.cmj $bsc js_pkg
7273
o others/belt_Array.cmi : cc others/belt_Array.mli | others/belt.cmi others/belt_internals.cmi others/js.cmi others/js.cmj $bsc js_pkg
7374
o others/belt_Float.cmj : cc_cmi others/belt_Float.ml | others/belt.cmi others/belt_Float.cmi others/belt_internals.cmi others/js.cmi $bsc js_pkg

jscomp/runtime/js.ml

+3
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ module Re = Js_re
258258
module Promise = Js_promise
259259
(** Provide bindings to JS promise *)
260260

261+
module Promise2 = Js_promise2
262+
(** Provide bindings to JS promise *)
263+
261264
module Date = Js_date
262265
(** Provide bindings for JS Date *)
263266

jscomp/test/SafePromises.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
3+
var Js_promise2 = require("../../lib/js/js_promise2.js");
4+
5+
async function nestedPromise(xxx) {
6+
var xx = await xxx;
7+
Js_promise2.then_(xx, (function (x) {
8+
return Promise.resolve((console.log("Promise2.then_", x), undefined));
9+
}));
10+
Js_promise2.catch_(xx, (function (x) {
11+
console.log("Promise2.catch_", x);
12+
return Promise.resolve(0);
13+
}));
14+
var arg1 = function (x) {
15+
return Promise.resolve((console.log("Promise.then_", x), undefined));
16+
};
17+
xx.then(arg1);
18+
}
19+
20+
async function create(x) {
21+
console.log("create", x);
22+
return x;
23+
}
24+
25+
var xx = create(10);
26+
27+
var xxx = create(xx);
28+
29+
nestedPromise(xxx);
30+
31+
exports.nestedPromise = nestedPromise;
32+
exports.create = create;
33+
exports.xx = xx;
34+
exports.xxx = xxx;
35+
/* xx Not a pure module */

jscomp/test/SafePromises.res

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*** Problematic example of nested promises is safe with Js.Promise2 */
2+
3+
let nestedPromise = async (xxx: promise<promise<int>>) => {
4+
let xx = await xxx
5+
6+
let _ = xx->Js.Promise2.then_(x => Js.log2("Promise2.then_", x) |> Js.Promise.resolve)
7+
let _ = xx->Js.Promise2.catch_(x => {
8+
Js.log2("Promise2.catch_", x)
9+
0 |> Js.Promise.resolve
10+
})
11+
12+
// This crashes
13+
let _ = Js.Promise.then_(x => Js.log2("Promise.then_", x) |> Js.Promise.resolve, xx)
14+
}
15+
16+
let create = async (x) => {
17+
Js.log2("create", x)
18+
x
19+
}
20+
21+
let xx = create(10)
22+
let xxx = create(xx)
23+
let _ = nestedPromise(xxx)

jscomp/test/build.ninja

+2-1
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)