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

Fix infinite loop in shell caused by record with recursive typespec #1489

Closed
wants to merge 2 commits into from

Conversation

isvilen
Copy link
Contributor

@isvilen isvilen commented Jun 17, 2017

If record with recursive typespec such as
-record(r,{f :: #r{} | undefined}).
is used in interactive shell it stucks in inifinite loop when trying to find definitions for all records used in expression.

For example following module definition:

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

-record(r,{f :: #r{} | undefined}).

t() ->
   #r{f=#r{f=undefined}}.

leads to this result in interactive shell:

Erlang/OTP 20 [RELEASE CANDIDATE 2] [erts-9.0] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V9.0  (abort with ^G)
1> c("bug.erl").
{ok,bug}
2> R = bug:t().
{r,{r,undefined}}
3> rr(bug).
[r]
4> R#r.f.
eheap_alloc: Cannot allocate 20310580872 bytes of memory (of type "heap").

Crash dump is being written to: erl_crash.dump...done

@IngelaAndin IngelaAndin added the team:VM Assigned to OTP team VM label Jun 18, 2017
@uabboli uabboli added team:MW and removed team:VM Assigned to OTP team VM labels Jun 19, 2017
@uabboli uabboli self-assigned this Jun 19, 2017
@uabboli
Copy link
Contributor

uabboli commented Jun 19, 2017

Hi,

Thanks for pointing out the bug.

I don't think this is the best correction, though. I'd rather see:

diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 6eafc7b..9b72312 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -758,6 +758,8 @@ used_records({record,,R,Name,Ups}) ->
{name, Name, [R | Ups]};
used_records({record_field,
,R,F}) -> % illegal
{expr, [R | F]};
+used_records({typed_record_field,Field,_Type}) ->

  • used_records(Field);
    used_records({call,,{atom,,record},[A,{atom,,Name}]}) ->
    {name, Name, A};
    used_records({call,
    ,{atom,,is_record},[A,{atom,,Name}]}) ->

Another case we missed when changing the representation of typed
record fields some time ago.

Best regards

@uabboli
Copy link
Contributor

uabboli commented Jun 19, 2017

Hi,

[Sorry for the ugly indentation.]

Thanks for pointing out the bug.

I don't think this is the best correction, though. I'd rather see:

diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 6eafc7b..9b72312 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -758,6 +758,8 @@ used_records({record,_,R,Name,Ups}) ->
     {name, Name, [R | Ups]};
 used_records({record_field,_,R,F}) -> % illegal
     {expr, [R | F]};
+used_records({typed_record_field,Field,_Type}) ->
+    used_records(Field);
 used_records({call,_,{atom,_,record},[A,{atom,_,Name}]}) ->
     {name, Name, A};
 used_records({call,_,{atom,_,is_record},[A,{atom,_,Name}]}) ->

Another case we missed when changing the representation of typed
record fields some time ago.

Best regards

@isvilen
Copy link
Contributor Author

isvilen commented Jun 19, 2017

@uabboli This certainly looks nicer, but does not work when there are other record fields:

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

-record(r0,{f0}).
-record(r,{f :: #r{} | undefined, f0 :: #r0{}}).

t() ->
   #r{f=#r{f=undefined, f0 = #r0{f0 = v}}}.

results in:

Erlang/OTP 20 [RELEASE CANDIDATE 2] [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V9.0  (abort with ^G)
1> c("bug.erl").
{ok,bug}
2> R = bug:t().
{r,{r,undefined,{r0,v}},undefined}
3> rr(bug).
[r,r0]
4> R#r.f.
* 5: record r0 undefined

with proposed fix result is as expected:

4> R#r.f.
#r{f = undefined,f0 = #r0{f0 = v}}

@uabboli
Copy link
Contributor

uabboli commented Jun 19, 2017

I see. Forget my last suggestion; it's no good.

Your patch tests if record Name has already been seen. Note however
that you also need to take E1 into consideration: only if E1 has already
been processed, can Name be skipped.

Extending your example a bit:

-module(bug).
-export([t/0, t2/0]).

-record(r0,{f0}).
-record(r,{f :: #r{} | undefined, f0 :: #r0{}}).

t() ->
   #r{f=#r{f=undefined, f0 = #r0{f0 = v}}}.

-record(r2, {}).

t2() ->
    #r2{}.

Now try:

> erl
Eshell V9.0  (abort with ^G)
1> c(bug).
{ok,bug}
2> rr(bug).
[r,r0,r2]
3> (#r{f = #r2{}})#r{f = 18}.
* 1: record r2 undefined
4> 

@isvilen
Copy link
Contributor Author

isvilen commented Jun 19, 2017

@uabboli good point
I've updated the pull request to accordingly.
Now:

3> (#r{f = #r2{}})#r{f = 18}.
#r{f = 18,f0 = undefined}

@uabboli
Copy link
Contributor

uabboli commented Jun 19, 2017

Looks good.

A test in shell_SUITE.erl would be nice. It should be possible to
use, for example, otp_6166/1 as a starting point.

The patch cannot make it into Erlang/OTP 20.0 since the code is
frozen. As soon as the release is out (Wednesday is the plan), you
can rebase the patch on maint.

@isvilen
Copy link
Contributor Author

isvilen commented Jun 19, 2017

Added typed_records/1 test in shell_SUITE.erl .

It turns out that if already seen records are always skipped that messes up
the expected order and there are test failures, so I modified the patch to skip
a record only when its own definition is processed.

When Erlang/OTP 20.0 is out I will rebase the patch on maint.

@isvilen isvilen force-pushed the master branch 2 times, most recently from d922e54 to fcdb10c Compare June 20, 2017 12:05
If record with recursive typespec such as

  -record(r,{f :: #r{} | undefined}).

is used in interactive shell it stucks in inifinite loop when
trying to find definitions for all records used in expression.
@isvilen isvilen changed the base branch from master to maint June 21, 2017 13:07
@isvilen
Copy link
Contributor Author

isvilen commented Jun 21, 2017

@uabboli rebased onto erlang:maint

@uabboli uabboli added the testing currently being tested, tag is used by OTP internal CI label Jun 22, 2017
@uabboli
Copy link
Contributor

uabboli commented Jun 26, 2017

Thanks. I'll merge the branch soon.

@uabboli
Copy link
Contributor

uabboli commented Jun 26, 2017

Merged commit 693691a into erlang:maint.

@uabboli uabboli closed this Jun 26, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
testing currently being tested, tag is used by OTP internal CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants