A polymorphic variant argument of an external is evaluated twice, or inside a generated function
I maintainer di solito rispondono entro 1 giorno
Una pull request collegata è già stata integrata.
- #9217 di @mununki — integrata
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 20/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Ferma
- Stack tecnologico
- javascript, ocaml
- Ambito
- compilers
Direzione di ricerca
Start with compiler/core/js_of_lam_variant.ml and compiler/core/js_exp_make.ml, then inspect how Lam_compile_external_call.ocaml_to_js_eff translates external arguments. Review the existing regression in tests/tests/src/gpr_2633_test.mjs and related issues #2633 and #8932. Done means external arguments with side effects are evaluated once and async arguments compile successfully; an open linked pull request (#9217) is already addressing this issue.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
When an external takes a polymorphic variant with @int, or with @string whose constructors have a payload or an @as, an argument that is not a literal or a variable is evaluated twice, or inside a function the compiler generates. An event-style @string argument, whose constructors have a payload, is evaluated once for the tag and once more for the payload. An @int argument and a @string argument with @as are evaluated inside an arrow function that switches on the value, so an await in them is a syntax error. An event-style argument with @as gets both: the arrow function switches on its tag, and the payload evaluates it again. A @string argument with neither payloads nor @as is passed as it is and is not affected.
type t
@send external on: (t, @string [#click(int => unit) | #keydown(string => unit)]) => unit = "on"
@send external setLevel: (t, @int [#low | #high]) => unit = "setLevel"
@val external nextEvent: unit => [#click(int => unit) | #keydown(string => unit)] = "nextEvent"
let f = t => on(t, nextEvent())
let g = async (t, p) => on(t, await p)
let h = async (t, p) => setLevel(t, await p)
function f(t) {
t.on(nextEvent().NAME, nextEvent().VAL);
}
async function g(t, p) {
t.on((await p).NAME, (await p).VAL);
}
async function h(t, p) {
t.setLevel((() => {
switch (await p) {
case "low" :
return 0;
case "high" :
return 1;
}
})());
}
f calls nextEvent() twice, so it consumes two events and passes the name of the first with the handler of the second. g awaits p twice. The module does not load because of h: Node.js reports SyntaxError: Unexpected reserved word for the ES module and SyntaxError: Unexpected identifier 'p' for CommonJS. The same holds for the field of an @obj external, as in makeOptions(~level=await p, ()) with ~level: @int [#low | #high]. A @get external passed as an event-style argument runs the getter twice. A literal whose payload has side effects is evaluated twice as well:
@val external wrap: (int => unit) => int => unit = "wrap"
let l = (t, handler) => on(t, #click(wrap(handler)))
function l(t, handler) {
t.on(({
NAME: "click",
VAL: wrap(handler)
}).NAME, ({
NAME: "click",
VAL: wrap(handler)
}).VAL);
}
Lam_compile_external_call.ocaml_to_js_eff translates the compiled argument with the functions in compiler/core/js_of_lam_variant.ml, which return an expression and cannot bind the argument to a variable first. eval_as_event passes any argument other than a literal with a side-effect-free payload as arg.NAME and arg.VAL, copying the whole expression. eval, eval_as_int and the @as case of eval_as_event switch on the argument in a function built by E.of_block in compiler/core/js_exp_make.ml. The FIXME above eval_as_event, added with the fix for #2633 in 2018, notes the duplicated evaluation, and tests/tests/src/gpr_2633_test.mjs pins it as foo.on(h(event).NAME, h(event).VAL).
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). 11.1.4 and 12.0.0-alpha.1 print (function () { ... })() instead of the arrow function, with the same result.
Related: #2633, #8932.
- Lingua principale
- OCaml
- Stelle
- 7.5k
- Fork
- 485
- Merge medio
- 17h 9m
- PR unite (30g)
- 264
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di rescript-lang/rescript
-
Exception analysis does not know the Temporal `fromStringOrThrow` functionsForse già presa @cknitt l’ha presa 2 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
rescript-lang/rescript#9182 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
rescript-lang/rescript#9233 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 55/100
rescript-lang/rescript#9230 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 72/100
rescript-lang/rescript#9229 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 44/100
rescript-lang/rescript#9228 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di rescript-lang/rescript
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 69/100
I maintainer di solito rispondono entro 1 giorno
-
A match arm above the largest i64 is compared as 0Forse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 86/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
paradigmxyz/solar#2436 ·
I maintainer di solito rispondono entro 1 giorno
-
bug bulk-filed needs triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 2 giorni
-
area:codegen backend:slang bug direction:output
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
I maintainer di solito rispondono entro 1 giorno