A dynamic import `module M = await List` fails with `Unbound module type __List__` after a statement, in a callback and in other positions
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 4/5
- Tempo estimado
- 3-5 dias
- Facilidade para iniciantes
- 55/100
- Tipo de issue
- Bug
- Clareza
- Claramente especificada
- Status de atividade
- Ativa
- Stack de tecnologia
- ocaml
- Domínio
- compilers
Direção de pesquisa
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.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
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.
- Linguagem predominante
- OCaml
- Estrelas
- 7.5k
- Forks
- 485
- Merge médio
- 17h 9min
- PRs com merge (30d)
- 264
Preparar o ambiente
Inicia o contêiner de desenvolvimento do projeto no navegador, com a sua própria conta do GitHub.
- Sem Dockerfile nem arquivo Docker Compose
- Tem um modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de rescript-lang/rescript
-
Exception analysis does not know the Temporal `fromStringOrThrow` functionsTalvez já em andamento @cknitt assumiu há 2 dias. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
rescript-lang/rescript#9182 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 5/5 Mais de uma semana Facilidade para iniciantes 25/100
rescript-lang/rescript#9233 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 3/5 1-2 dias Facilidade para iniciantes 72/100
rescript-lang/rescript#9229 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 4/5 3-5 dias Facilidade para iniciantes 44/100
rescript-lang/rescript#9228 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 4/5 3-5 dias Facilidade para iniciantes 45/100
rescript-lang/rescript#9227 ·
Mantenedores costumam responder em até 1 dia
Todas as issues de rescript-lang/rescript
Issues semelhantes
-
error not reported I/O low priority rt_lib
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 67/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 69/100
Mantenedores costumam responder em até 1 dia
-
A match arm above the largest i64 is compared as 0Talvez já em andamento Um pull request vinculado a esta issue está aberto ou já foi mesclado. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 86/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 65/100
paradigmxyz/solar#2436 ·
Mantenedores costumam responder em até 1 dia
-
bug bulk-filed needs triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
Mantenedores costumam responder em até 2 dias