await in a functor body compiles to invalid JavaScript

已關閉
#9,219 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

維護者通常 1 天內回覆

@mununki 已經在處理了。

開始於 2026年11月4日。

  • #9220 來自 @mununki —— 未關閉

評估

難度
4/5
預估耗時
3-5 天
新手友好度
25/100
Issue 類型
缺陷
描述清晰度
描述清楚
活躍度
停滯
技術堆疊
javascript
領域
compilers

研究方向

The issue points to compiler/frontend/bs_builtin_ppx.ml, especially expr_mapper, module_expr_mapper, and structure_mapper. Read the await context handling around Pexp_fun, Pmod_functor, and structure-level module bindings, then check the existing cases described for functors and module M = await List. Done means those forms are rejected outside async contexts and accepted where allowed, without generating invalid JavaScript.

由索引模型根據 Issue 內容生成。

描述

await in the body of a functor passes the compiler's check, and the generated JavaScript has await in the function that the functor compiles to, which is not async:

module type Source = {
  let value: promise<int>
}

module Make = (Source: Source) => {
  let value = await Source.value
}
function Make(Source) {
  let value = await Source.value;
  return {
    value: value
  };
}

Loading the module fails with SyntaxError: Unexpected reserved word (with CommonJS output, SyntaxError: await is only valid in async functions and the top level bodies of modules); node --check reports the same.

A functor in a plain function is rejected with Await on expression not in an async context, but in an async function it is accepted. A first-class functor returned by an async function compiles to a plain arrow:

module type Make = (Source: Source) => {let value: int}

let make = async () => module((Source: Source) => {
  let value = await Source.value
}: Make)
async function make() {
  return Source => {
    let value = await Source.value;
    return {
      value: value
    };
  };
}

The same happens with for await and module M = await List in a functor body, with a generative functor, a functor with several parameters, and a functor defined in the body of another functor.

The JavaScript is valid only where the optimizer inlines every application of the functor and then drops the functor: a functor defined and applied in an async function, a top-level functor that an interface (.resi) or a module signature hides, and a functor applied where it is written, as in module M = ((X: S) => {...})({...}). This depends on the optimization, not on the code: after module Alias = Make, Alias({...}) is not inlined and Make stays a function with await in it, and an application in a plain function inside the async function moves the await into that plain function.

A dynamic import at the top level of a module that is nested in a plain function is accepted as well, while module L = await List directly in the function body is rejected:

let length = values => {
  module M = {
    module L = await List
    let length = L.length(values)
  }
  M.length
}
function length(values) {
  let L = await import("@rescript/runtime/lib/es6/Stdlib_List.mjs");
  let length$1 = L.length(values);
  return length$1;
}

The await check is in compiler/frontend/bs_builtin_ppx.ml. expr_mapper sets the async context at every Pexp_fun to whether that function is async, which #9169, the fix for #9168, made exact for functions. A functor is a Pmod_functor module expression, and module_expr_mapper leaves the context unchanged, so the functor body keeps the context around the functor: true at the top level of a file, where top-level await is allowed (#5925), and in an async function. In addition, structure_mapper rewrites module M = await List in a structure without checking the context, which expr_mapper checks only for the Pexp_letmodule form in an expression.

Reproduced with 11.1.4, 12.0.0-alpha.1, 12.0.0-alpha.6, 12.3.1, 13.0.0-alpha.6 and master (c10223e80).

Related: #9168, #9169, #5925, #8815.

主要語言
OCaml
星號
7.5k
分支
485
平均合併
17 小時 9 分鐘
30 天內合併 PR
264

環境準備

在 Codespaces 中開啟

在瀏覽器裡用你自己的 GitHub 帳號啟動這個專案的開發容器。

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

rescript-lang/rescript 的其他 Issue

查看 rescript-lang/rescript 的全部 Issue

相似的 Issue

更多 Compilers Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。