Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Another case of inlining breaking async #5789

Closed
cknitt opened this issue Nov 9, 2022 · 8 comments
Closed

Another case of inlining breaking async #5789

cknitt opened this issue Nov 9, 2022 · 8 comments
Milestone

Comments

@cknitt
Copy link
Member

cknitt commented Nov 9, 2022

Broken.resi:

let broken: unit => promise<unit>

Broken.res:

@module("SomeModule")
external someExternal: unit => promise<unit> = "someExternal"

let doSomethingAsync = async () => {
  await someExternal()
}

let broken = () => doSomethingAsync()

JS output does not compile because function has no async:

function broken(param) {
  return await SomeModule.someExternal();
}

/cc @cristianoc

@cknitt cknitt added this to the v10.1 milestone Nov 9, 2022
@cknitt
Copy link
Member Author

cknitt commented Nov 9, 2022

External is not needed. Even easier repro:

Broken.resi:

let broken: (unit => promise<'a>) => promise<'a>

Broken.resi:

let doSomethingAsync = async someAsyncFunction => {
  await someAsyncFunction()
}

let broken = someAsyncFunction => doSomethingAsync(someAsyncFunction)

@cristianoc
Copy link
Collaborator

Not sure I understand either of the two examples. Why are there interface files. And if there are how many files are needed to repro.

@cknitt
Copy link
Member Author

cknitt commented Nov 9, 2022

There is an interface file because otherwise both functions will be exported and the inlining does not happen.
The whole thing is extracted from real-world code and very very much simplified.

Actually a single file is enough to reproduce if we use %%private:

%%private(
  let doSomethingAsync = async someAsyncFunction => {
    await someAsyncFunction()
  }
)

let broken = someAsyncFunction => doSomethingAsync(someAsyncFunction)

results in

function broken(someAsyncFunction) {
  return await Curry._1(someAsyncFunction, undefined);
}

@cristianoc
Copy link
Collaborator

OK without using private, one can just do the same with a module, or shadowing:

module M: {
  let broken: (unit => promise<'a>) => promise<'a>
} = {
  let doSomethingAsync = async (someAsyncFunction) => {
    await someAsyncFunction()
  }

  let broken = someAsyncFunction => doSomethingAsync(someAsyncFunction)
}

let broken = async (someAsyncFunction) => {
  await someAsyncFunction()
}

let broken = someAsyncFunction => broken(someAsyncFunction)

@cristianoc
Copy link
Collaborator

I'll go with the latter, and notice that this prevents inlining:

let broken = async (someAsyncFunction) => {
  await someAsyncFunction()
}

let _ = Js.log(broken)

let broken = someAsyncFunction => broken(someAsyncFunction)

cristianoc added a commit that referenced this issue Nov 9, 2022
@cristianoc
Copy link
Collaborator

@cknitt can you try if this #5790 resolved the issue?

@cknitt
Copy link
Member Author

cknitt commented Nov 9, 2022

@cristianoc Yes! It works! 🎉

Let's merge and do an rc.5.

@cristianoc
Copy link
Collaborator

I'll merge and apply to master too.

cristianoc added a commit that referenced this issue Nov 9, 2022
* Prevent inlining of async functions in last stage of the compiler

* Update CHANGELOG.md

Fixes #5789
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants