A dynamic import `module M = await List` fails with `Unbound module type __List__` after a statement, in a callback and in other positions
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 55/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- ocaml
- 領域
- compilers
調査の方向性
Start by reading compiler/frontend/bs_builtin_ppx.ml, especially the Pexp_letmodule case in expr_mapper and aux in spelunk_vbs; then trace Ast_await.create_await_module_expression and structure_mapper's Pstr_value case. Reproduce the listed failing and working positions, including a Pstr_eval expression, and inspect existing coverage around #6431 and #7783. Done means every import rewritten by expr_mapper has its module type declared, with regression tests covering the reported positions.
索引モデルが issue の本文から書いたものです。
説明
A dynamic import of a module, module M = await List, is rejected with Unbound module type __List__ depending on where it appears in the async function. A statement before it is enough:
let f = async () => {
Console.log("start")
module M = await List
M.length(list{1, 2})
}
f()->Promise.thenResolve(Console.log)->ignore
We've found a bug for you!
Repro.res:3:14-23
1 │ let f = async () => {
2 │ Console.log("start")
3 │ module M = await List
4 │ M.length(list{1, 2})
5 │ }
Unbound module type __List__
The source is valid and should log start and then 2. __List__ is a name generated by the compiler, not one the source uses. Without the Console.log("start") line, the same function compiles to a dynamic import and logs 2:
async function f() {
let M = await import("@rescript/runtime/lib/js/Stdlib_List.cjs");
return M.length({hd: 1, tl: {hd: 2, tl: /* [] */0}});
}
The same error (__List__, or __Array__ for await Array) comes from an import
- after any statement (
e; module M = await List), also in aswitcharm, - in an async function passed as an argument, e.g.
[1]->Array.map(async _ => {...})orp->Promise.then(async _ => {...}), or written in a record field, an array or a tuple, - in the body of a
try, aforor awhile, or in anifwithoutelse, - after
open U, a localexception E, a plain local modulemodule L = List, or a constrained importmodule L = await (List: ListT), - in an expression at the top level of a file, e.g.
run(async () => { module M = await List; ... })->ignore, or of a nested module.
It compiles directly in the body of a function bound by let, after a let, in a nested block, in both branches of if/else and of a ternary, as the first expression of a switch arm, under a return type annotation, in an async function bound by let inside another function, in Some(async () => ...), and after another unconstrained import.
Whether an import compiles also depends on the rest of the file. The function from the first example compiles when an earlier binding imports List in one of the positions that work:
let g = async () => {
module M = await List
M.length(list{1})
}
let f = async () => {
Console.log("start")
module M = await List
M.length(list{1, 2})
}
f()->Promise.thenResolve(Console.log)->ignore
async function f() {
console.log("start");
let M = await import("@rescript/runtime/lib/js/Stdlib_List.cjs");
return M.length({hd: 1, tl: {hd: 2, tl: /* [] */0}});
}
This logs start and 2. With g placed after f, f fails again with Unbound module type __List__.
In compiler/frontend/bs_builtin_ppx.ml, the Pexp_letmodule case of expr_mapper rewrites every module M = await List into an unpack of import at the package type module(__List__) (Ast_await.create_await_module_expression), wherever the expression is. The declaration module type __List__ = module type of List is added separately, before the binding, by the Pstr_value case of structure_mapper. To find the imports, aux in spelunk_vbs follows only Pexp_letmodule, Pexp_let, Pexp_braces, Pexp_ifthenelse with an else, Pexp_ternary, Pexp_construct with one argument, Pexp_fun, Pexp_constraint and Pexp_match. Every other expression ends the search (| _ -> acc), among them Pexp_sequence, Pexp_apply, Pexp_try, Pexp_for, Pexp_while, Pexp_open, Pexp_letexception, Pexp_record, Pexp_array and Pexp_tuple. A Pexp_letmodule that is not an unconstrained import ends it too, without its body being searched. A Pstr_eval item, an expression at the top level of a file or of a module, is not searched at all. local_module_type_decl declares each name once per structure, so a declaration found for an earlier binding also serves every later import of the same module, which is why f compiles when g comes before it. Searching every subexpression, and Pstr_eval items too, would declare the module type for each import that expr_mapper rewrites.
Writing the module type out avoids the hoisting: with module type ListT = module type of List at the top level, module M = await (List: ListT) compiles to await import(...) in all of the positions above.
Reproduced with 11.1.4 (with Js.log for Console.log), 12.0.0-alpha.6, 12.3.1, 13.0.0-alpha.6 and master (fc7444266). The search has followed a fixed list of expression shapes since #6431 (11.0.0-rc.5). #7783, the fix for #7173, added Pexp_constraint and Pexp_match to it in 12.0.0-beta.7, so 12.0.0-alpha.6 also fails in a switch arm and under a return type annotation.
Related: #7173, #7783.
- 主要言語
- OCaml
- スター
- 7.5k
- フォーク
- 485
- 平均マージ
- 17時間 9分
- マージ済み PR(30日)
- 264
環境構築
このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
rescript-lang/rescript のほかの issue
-
Exception analysis does not know the Temporal `fromStringOrThrow` functions対応中かも @cknitt が 1 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
rescript-lang/rescript#9182 ·
メンテナーはふだん 1 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
rescript-lang/rescript#9233 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
rescript-lang/rescript#9229 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 44/100
rescript-lang/rescript#9228 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
rescript-lang/rescript#9227 ·
メンテナーはふだん 1 日以内に返信
rescript-lang/rescript の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 69/100
メンテナーはふだん 1 日以内に返信
-
A match arm above the largest i64 is compared as 0対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
paradigmxyz/solar#2436 ·
メンテナーはふだん 1 日以内に返信
-
bug bulk-filed needs triage
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 2 日以内に返信
-
area:codegen backend:slang bug direction:output
難易度 2/5 1〜3時間 初心者へのやさしさ 73/100
メンテナーはふだん 1 日以内に返信