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

Inline funs that are immediately used #4545

Merged
merged 1 commit into from
Feb 26, 2021

Conversation

bjorng
Copy link
Contributor

@bjorng bjorng commented Feb 24, 2021

Funs can be used to hide local variables. Example:

a() ->
    A = fun() -> A = 21, 2 * A end(),
    foo:bar(A).

To avoid the slight runtime cost for such use of funs, teach the
compiler to inline funs that are used only once immediately after
creation.

Resolves #4019

@bjorng bjorng added team:VM Assigned to OTP team VM fix bug Issue is reported as a bug labels Feb 24, 2021
@bjorng bjorng requested a review from jhogberg February 24, 2021 08:07
@bjorng bjorng self-assigned this Feb 24, 2021
@bjorng bjorng force-pushed the bjorn/compiler/optimize-fun-calls branch from 3b6198f to 4d280f8 Compare February 24, 2021 08:13
@bjorng bjorng added the testing currently being tested, tag is used by OTP internal CI label Feb 24, 2021
@josevalim
Copy link
Contributor

Neat! Quick question: does that work if the function is invoked once but across multiple clauses? For example:

Fun = fun(B) -> A = 21, 2 * B end(),

case Condition of
  true -> Fun(1);
  false -> Fun(-1)
end.

@bjorng
Copy link
Contributor Author

bjorng commented Feb 24, 2021

Quick question: does that work if the function is invoked once but across multiple clauses?

No.

@bjorng bjorng removed the testing currently being tested, tag is used by OTP internal CI label Feb 25, 2021
Funs can be used to hide local variables. Example:

    a() ->
        A = fun() -> A = 21, 2 * A end(),
        foo:bar(A).

To avoid the slight runtime cost for such use of funs, teach the
compiler to inline funs that are used only once immediately after
creation.

Resolves erlang#4019.
@bjorng bjorng force-pushed the bjorn/compiler/optimize-fun-calls branch from d1c5299 to 2c9fd89 Compare February 25, 2021 07:53
@bjorng bjorng added the testing currently being tested, tag is used by OTP internal CI label Feb 25, 2021
@bjorng bjorng force-pushed the bjorn/compiler/optimize-fun-calls branch from 5bdbb78 to 72675ba Compare February 26, 2021 13:32
@bjorng bjorng merged commit 9e821fa into erlang:master Feb 26, 2021
@bjorng bjorng deleted the bjorn/compiler/optimize-fun-calls branch February 26, 2021 13:37
@bjorng bjorng added this to the OTP-24.0 milestone Feb 26, 2021
bjorng added a commit to bjorng/otp that referenced this pull request Jan 13, 2022
Consider this module:

    -module(bug).
    -export([foo/0]).

    foo() ->
        fun(a) -> ok end(b).

The call to the fun will always fail, which will be noted by
the compiler:

    1> c(bug).
    bug.erl:5:5: Warning: no clause will ever match
    %    5|     fun(a) -> ok end(b).
    %     |     ^

    {ok,bug}

What is unexpected is that the exception that is raised is not
a `function_clause` exception:

    2> bug:foo().
    ** exception error: no case clause matching {b}
         in function  bug:foo/0 (bug.erl, line 5)

This confusing `case_clause` exception started to appear in OTP 24
because of 72675ba (erlang#4545) that inlines funs that are
immediately used.

Before OTP 24, when the fun was not inlined, the exception would be:

    2> bug:foo().
    ** exception error: no function clause matching bug:'-foo/0-fun-0-'(b) (bug.erl, line 5)

The reason that `function_clause` exceptions are rewritten to
`case_clause` exceptions in inlined code is to avoid the even more
confusing and misleading exception:

    2> bug:foo().
    ** exception error: no function clause matching bug:foo(b) (bug.erl, line 5)

This is confusing because it seems that `foo/0` was called with one argument.

To reduce the confusion, this commmit ensures that `function_clause` exceptions in
inlined code remains `function_clause` exceptions, but with a generated name that
makes it clear that the `function_clause` exception occurred in a fun:

    2> bug:foo().
    ** exception error: no function clause matching bug:'-foo/0-inlined-0-'(b) (bug.erl, line 5)

Fixes erlang#5513
bjorng added a commit to bjorng/otp that referenced this pull request Jan 14, 2022
Consider this module:

    -module(bug).
    -export([foo/0]).

    foo() ->
        fun(a) -> ok end(b).

The call to the fun will always fail, which will be noted by
the compiler:

    1> c(bug).
    bug.erl:5:5: Warning: no clause will ever match
    %    5|     fun(a) -> ok end(b).
    %     |     ^

    {ok,bug}

What is unexpected is that the exception that is raised is not
a `function_clause` exception:

    2> bug:foo().
    ** exception error: no case clause matching {b}
         in function  bug:foo/0 (bug.erl, line 5)

This confusing `case_clause` exception started to appear in OTP 24
because of 72675ba (erlang#4545) that inlines funs that are
immediately used.

Before OTP 24, when the fun was not inlined, the exception would be:

    2> bug:foo().
    ** exception error: no function clause matching bug:'-foo/0-fun-0-'(b) (bug.erl, line 5)

The reason that `function_clause` exceptions are rewritten to
`case_clause` exceptions in inlined code is to avoid the even more
confusing and misleading exception:

    2> bug:foo().
    ** exception error: no function clause matching bug:foo(b) (bug.erl, line 5)

This is confusing because it seems that `foo/0` was called with one argument.

To reduce the confusion, this commmit ensures that `function_clause` exceptions in
inlined code remains `function_clause` exceptions, but with a generated name that
makes it clear that the `function_clause` exception occurred in a fun:

    2> bug:foo().
    ** exception error: no function clause matching bug:'-foo/0-inlined-0-'(b) (bug.erl, line 5)

Fixes erlang#5513
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue is reported as a bug fix team:VM Assigned to OTP team VM testing currently being tested, tag is used by OTP internal CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ERL-889: Weird codegen when using scoping tricks
3 participants