`await` is rejected in a `@this async` function, which compiles to an `async function`

Aperta
#9,229 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
72/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
javascript, ocaml
Ambito
backend, compilers

Direzione di ricerca

Start in compiler/frontend/bs_builtin_ppx.ml, where expr_mapper handles the Pexp_fun case with @this; compare its async_context handling with the plain Pexp_fun branch. Check the compiler tests for @this async functions and add coverage for await in the body. Done means the example compiles while await remains rejected in non-async @this functions and plain nested functions.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

An await in the body of a function that has both @this and async is rejected with "Await on expression not in an async context", although the function is typed as returning a promise and is compiled to a JavaScript async function.

type obj = {name: string}

let m = @this async (self: obj, x: promise<int>) => {
  let v = await x
  self.name ++ Int.toString(v)
}
  We've found a bug for you!
  ThisAsyncAwait.res:4:11-17

  2 │ 
  3 │ let m = @this async (self: obj, x: promise<int>) => {
  4 │   let v = await x
  5 │   self.name ++ Int.toString(v)
  6 │ }

  Await on expression not in an async context

Without the await, a @this async function has type @this (obj, int) => promise<string> and compiles to an async function, where await is valid:

let m = @this async (self: obj, x: int) => self.name ++ Int.toString(x)
async function m(x) {
  let self = this ;
  return self.name + x.toString();
}

module M = await List and for await x of xs {...} in the body of a @this async function are rejected with the same error. To use await, the body has to go into a separate async function that the method calls:

let helper = async (self: obj, x: promise<int>) => {
  let v = await x
  self.name ++ Int.toString(v)
}

let m = @this (self: obj, x: promise<int>) => helper(self, x)
async function helper(self, x) {
  let v = await x;
  return self.name + v.toString();
}

function m(x) {
  let self = this ;
  return helper(self, x);
}

In compiler/frontend/bs_builtin_ppx.ml, expr_mapper handles a Pexp_fun with @this (the Meth_callback case of Ast_attributes.process_attributes_rev) by setting async_context := false and then calling Ast_uncurry_gen.to_method_callback ~async, which maps the body under that context and builds the function with ~async and Ast_async.make_function_async ~async. So the body is checked as non-async even when the function it is emitted into is async. The false dates from when a @this function could not be async; #7702 passed ~async to to_method_callback but left the context unchanged. Setting async_context := async in that branch, as the plain Pexp_fun branch above it does, fixes it: the example at the top then compiles to async function m(x) with let v = await x;, and m.call({name: "n"}, Promise.resolve(41)) resolves to "n41". await in a @this function without async, or in a plain function inside a @this async one, is still rejected.

Reproduced with 12.0.0-beta.3, 12.3.1, 13.0.0-alpha.6 and master (fc7444266). A @this async function compiles to an async function with a promise result since #7702 (12.0.0-beta.3), the fix for #7681. Before that, in 12.0.0-beta.2 and 11.1.4, it compiled to a plain function, and await in it was rejected as well.

#6100 reported this error together with the dropped async attribute; #6105 fixed the attribute. Its first example, where the async function is the one returned by the @this function (@this self => async arg => await ...), compiles since 13.0.0-alpha.6; the form above, with @this and async on the same function, does not.

Related: #6100, #7681, #7702.

Lingua principale
OCaml
Stelle
7.5k
Fork
485
Merge medio
17h 9m
PR unite (30g)
264

Preparare l'ambiente

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di rescript-lang/rescript

Tutte le issue di rescript-lang/rescript

Issue simili

Altre issue su Backend & API Design

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.