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

Confusing case_clause exception instead of function_clause #5513

Closed
bjorng opened this issue Dec 13, 2021 · 0 comments · Fixed by #5604
Closed

Confusing case_clause exception instead of function_clause #5513

bjorng opened this issue Dec 13, 2021 · 0 comments · Fixed by #5604
Assignees
Labels
bug Issue is reported as a bug team:VM Assigned to OTP team VM

Comments

@bjorng
Copy link
Contributor

bjorng commented Dec 13, 2021

Describe the bug

When a fun is used immediately, the compiler will inline it, and if no clause in the fun matches, there will be a case_clause error instead of a function_clause error.

To Reproduce

Consider this module:

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

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

Compile and run it:

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

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

Expected behavior

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

Affected versions

OTP 24, which introduced automatic inlining of funs that are immediately used and discarded.

Additional context

While not strictly a bug, this behavior is confusing.

@bjorng bjorng added the bug Issue is reported as a bug label Dec 13, 2021
@rickard-green rickard-green added the team:VM Assigned to OTP team VM label Dec 13, 2021
bjorng added a commit to bjorng/otp that referenced this issue 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 issue 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
bjorng added a commit that referenced this issue Jan 20, 2022
…-17860

Eliminate confusing `case_clause` exception
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 team:VM Assigned to OTP team VM
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants