diff --git a/CHANGELOG.md b/CHANGELOG.md index 59b711b45e..bf81c46d85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - Rewatch: preserve warnings after atomic-save full rebuilds. https://github.com/rescript-lang/rescript/pull/8358 - 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 +- 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 #### :memo: Documentation diff --git a/compiler/ml/typecore.ml b/compiler/ml/typecore.ml index d7bc0aadaa..f069a9412e 100644 --- a/compiler/ml/typecore.ml +++ b/compiler/ml/typecore.ml @@ -2259,6 +2259,19 @@ let extract_function_name funct = | Texp_ident (path, _, _) -> Some (Longident.parse (Path.name path)) | _ -> None +let should_unify_expected_result_before_typing_lowered_apply funct sargs = + match (extract_function_name funct, sargs) with + | ( Some (Longident.Ldot (Longident.Lident "Primitive_dict", "make")), + [(Asttypes.Nolabel, {Parsetree.pexp_desc = Parsetree.Pexp_array _})] ) -> + (* Dict literals *) + true + | ( Some + (Longident.Ldot (Longident.Lident "Primitive_promise", "unsafe_async")), + [(Asttypes.Nolabel, _)] ) -> + (* Async wrapper *) + true + | _ -> false + type lazy_args = (Asttypes.arg_label * (unit -> Typedtree.expression) option) list @@ -2460,6 +2473,17 @@ and type_expect_ ?deprecated_context ~context ?in_function ?(recarg = Rejected) let funct = type_exp ~deprecated_context:FunctionCall ~context:None env sfunct in + (if should_unify_expected_result_before_typing_lowered_apply funct sargs + then + (* Lowered syntax like dict literals and async wrappers becomes a regular + application, so thread the expected result type into the application + before typing its arguments. *) + let _, ty_res = + filter_arrow ~env + ~arity:(Some (List.length sargs)) + funct.exp_type Nolabel + in + unify_exp_types ~context:None loc env ty_res (instance env ty_expected)); let ty = instance env funct.exp_type in end_def (); wrap_trace_gadt_instances env (lower_args env []) ty; diff --git a/tests/build_tests/super_errors/expected/dict_show_no_coercion.res.expected b/tests/build_tests/super_errors/expected/dict_show_no_coercion.res.expected index 65445bd9d1..ee47b4af0f 100644 --- a/tests/build_tests/super_errors/expected/dict_show_no_coercion.res.expected +++ b/tests/build_tests/super_errors/expected/dict_show_no_coercion.res.expected @@ -1,15 +1,12 @@ We've found a bug for you! - /.../fixtures/dict_show_no_coercion.res:2:23-35 + /.../fixtures/dict_show_no_coercion.res:2:33-34 1 │ // This should not show coercion suggestion since just the inner types a │ re coercable, not the full type + expression (dict -> dict) - 2 │ let x: dict = dict{"1": 1.} + 2 │ let x: dict = dict{"1": 1.} 3 │ - This has type: dict - But it's expected to have type: dict - - The incompatible parts: - float vs JSON.t (defined as JSON.t) \ No newline at end of file + This has type: float + But it's expected to have type: JSON.t (defined as JSON.t) diff --git a/tests/tests/src/DictScopedRecordLiteral.mjs b/tests/tests/src/DictScopedRecordLiteral.mjs new file mode 100644 index 0000000000..2cb678f01c --- /dev/null +++ b/tests/tests/src/DictScopedRecordLiteral.mjs @@ -0,0 +1,30 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + + +let Hidden = {}; + +let dictValueInference = { + health: { + get: 200 + } +}; + +async function asyncValueInference() { + return { + get: 200 + }; +} + +let primitiveMakeValueInference = { + health: { + get: 200 + } +}; + +export { + Hidden, + dictValueInference, + asyncValueInference, + primitiveMakeValueInference, +} +/* No side effect */ diff --git a/tests/tests/src/DictScopedRecordLiteral.res b/tests/tests/src/DictScopedRecordLiteral.res new file mode 100644 index 0000000000..e773d17728 --- /dev/null +++ b/tests/tests/src/DictScopedRecordLiteral.res @@ -0,0 +1,13 @@ +module Hidden = { + type routeHandlerObject = {get: int} +} + +let dictValueInference: Dict.t = dict{ + "health": {get: 200}, +} + +let asyncValueInference: unit => promise = async () => { + get: 200, +} + +let primitiveMakeValueInference: Dict.t = dict{"health": {get: 200}}