Skip to content

Commit c8d05be

Browse files
authored
Propagate expected dict and async value type (#8359)
* propagate expected dict value type into dict literal typing * cleaner impl * change approach back to structural, and fix async as well * changelog
1 parent d28c61c commit c8d05be

5 files changed

Lines changed: 72 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
- Rewatch: preserve warnings after atomic-save full rebuilds. https://github.com/rescript-lang/rescript/pull/8358
3030

3131
- Preserve JSX prop locations across the AST0 translation layer, fixing `0:0` editor diagnostics in PPX-related flows. https://github.com/rescript-lang/rescript/pull/8350
32+
- Fix type lowering for `dict{}` and `async`, so you don't need to annotate one extra time when the type is known. https://github.com/rescript-lang/rescript/pull/8359
3233

3334
#### :memo: Documentation
3435

compiler/ml/typecore.ml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,6 +2259,19 @@ let extract_function_name funct =
22592259
| Texp_ident (path, _, _) -> Some (Longident.parse (Path.name path))
22602260
| _ -> None
22612261
2262+
let should_unify_expected_result_before_typing_lowered_apply funct sargs =
2263+
match (extract_function_name funct, sargs) with
2264+
| ( Some (Longident.Ldot (Longident.Lident "Primitive_dict", "make")),
2265+
[(Asttypes.Nolabel, {Parsetree.pexp_desc = Parsetree.Pexp_array _})] ) ->
2266+
(* Dict literals *)
2267+
true
2268+
| ( Some
2269+
(Longident.Ldot (Longident.Lident "Primitive_promise", "unsafe_async")),
2270+
[(Asttypes.Nolabel, _)] ) ->
2271+
(* Async wrapper *)
2272+
true
2273+
| _ -> false
2274+
22622275
type lazy_args =
22632276
(Asttypes.arg_label * (unit -> Typedtree.expression) option) list
22642277
@@ -2460,6 +2473,17 @@ and type_expect_ ?deprecated_context ~context ?in_function ?(recarg = Rejected)
24602473
let funct =
24612474
type_exp ~deprecated_context:FunctionCall ~context:None env sfunct
24622475
in
2476+
(if should_unify_expected_result_before_typing_lowered_apply funct sargs
2477+
then
2478+
(* Lowered syntax like dict literals and async wrappers becomes a regular
2479+
application, so thread the expected result type into the application
2480+
before typing its arguments. *)
2481+
let _, ty_res =
2482+
filter_arrow ~env
2483+
~arity:(Some (List.length sargs))
2484+
funct.exp_type Nolabel
2485+
in
2486+
unify_exp_types ~context:None loc env ty_res (instance env ty_expected));
24632487
let ty = instance env funct.exp_type in
24642488
end_def ();
24652489
wrap_trace_gadt_instances env (lower_args env []) ty;
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11

22
We've found a bug for you!
3-
/.../fixtures/dict_show_no_coercion.res:2:23-35
3+
/.../fixtures/dict_show_no_coercion.res:2:33-34
44

55
1 │ // This should not show coercion suggestion since just the inner types a
66
│ re coercable, not the full type + expression (dict<float> -> dict<JSON.t
77
│ >)
8-
2 │ let x: dict<JSON.t> = dict{"1": 1.}
8+
2 │ let x: dict<JSON.t> = dict{"1": 1.}
99
3 │
1010

11-
This has type: dict<float>
12-
But it's expected to have type: dict<JSON.t>
13-
14-
The incompatible parts:
15-
float vs JSON.t (defined as JSON.t)
11+
This has type: float
12+
But it's expected to have type: JSON.t (defined as JSON.t)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Generated by ReScript, PLEASE EDIT WITH CARE
2+
3+
4+
let Hidden = {};
5+
6+
let dictValueInference = {
7+
health: {
8+
get: 200
9+
}
10+
};
11+
12+
async function asyncValueInference() {
13+
return {
14+
get: 200
15+
};
16+
}
17+
18+
let primitiveMakeValueInference = {
19+
health: {
20+
get: 200
21+
}
22+
};
23+
24+
export {
25+
Hidden,
26+
dictValueInference,
27+
asyncValueInference,
28+
primitiveMakeValueInference,
29+
}
30+
/* No side effect */
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module Hidden = {
2+
type routeHandlerObject = {get: int}
3+
}
4+
5+
let dictValueInference: Dict.t<Hidden.routeHandlerObject> = dict{
6+
"health": {get: 200},
7+
}
8+
9+
let asyncValueInference: unit => promise<Hidden.routeHandlerObject> = async () => {
10+
get: 200,
11+
}
12+
13+
let primitiveMakeValueInference: Dict.t<Hidden.routeHandlerObject> = dict{"health": {get: 200}}

0 commit comments

Comments
 (0)