bugGNU Octave - Bugs: bug #45835, wrong scope may be used in...

 
 

bug #45835: wrong scope may be used in anonymous function evaluation

Submitter:  None
Submitted:  Thu 27 Aug 2015 02:23:43 PM UTC
   
 
Category:  Interpreter Severity:  3 - Normal
Priority:  5 - Normal Item Group:  Matlab Compatibility
Status:  Fixed Assigned to:  None
Originator Name:  Originator Email:  -email is unavailable-
Open/Closed:  * Closed Release:  * dev
Operating System:  * Any Fixed Release:  None
Planned Release:  None
* Mandatory Fields

Add a New Comment Rich Markup
   

Jump to the original submission

Sun 11 Oct 2015 11:22:13 PM UTC, comment #11: 

Since the patch appears to avoid the reported problem, I'd say we could go ahead and push it.  I updated it (needed because of the recent error_state changes) and pushed it here:

http://hg.savannah.gnu.org/hgweb/octave/rev/f9c991dc5c1a

John W. Eaton <jwe>
Group administrator
Sun 11 Oct 2015 12:03:13 AM UTC, comment #10: 

@jwe: Any reason why your patch can't be applied now?

Rik <rik5>
Group administrator
Sat 29 Aug 2015 05:25:15 PM UTC, comment #9: 

@jwe: Patch seems good.  It resolves this issue and 'make check' passes.  I appended to your changes a new file test/anonfunc.tst which has a few tests, including this one, for anonymous functions.  I ran some benchmarking with 'make check' and there is a very slight slowdown with the new code, but it is close to the measurement resolution so can be ignored.  Seems like this is fixed now.

(file #34768)

Rik <rik5>
Group administrator
Sat 29 Aug 2015 01:59:06 PM UTC, comment #8: 

Very deep in the weeds.  I had no idea functions like _current_scope_ existed.  I corrected the function name in the docstring and added a one-line definition here (http://hg.savannah.gnu.org/hgweb/octave/rev/33b03b06442b).

Attached is a little m-file mainfcn.m which shows how scope and context change with a function, subfunction, nested function and recursion--useful for learning.

Function is


function mainfcn ()
  persistent recurse_count = 0;

  [s1, c1] = __current_scope__ ();
  printf ("Scope, Context in main function: %d, %d\n", s1, c1);
  subfcn ();
  function nestfcn ()
    [s3, c3] = __current_scope__ ();
    printf ("Scope, Context in nested function: %d, %d\n", s3, c3);
  endfunction
  nestfcn ();

  printf ("------------------------------------------------------------\n");

  if (recurse_count++ < 5)
    mainfcn ();
  else
    return;
  endif

endfunction

function subfcn ()
  [s1, c1] = __current_scope__ ();
  printf ("Scope, Context in subfunction: %d, %d\n", s1, c1);
endfunction


Output is


octave:1> mainfcn
Scope, Context in main function: 58, 0
Scope, Context in subfunction: 60, 0
Scope, Context in nested function: 59, 0
------------------------------------------------------------
Scope, Context in main function: 58, 1
Scope, Context in subfunction: 60, 0
Scope, Context in nested function: 59, 0
------------------------------------------------------------
Scope, Context in main function: 58, 2
Scope, Context in subfunction: 60, 0
Scope, Context in nested function: 59, 0
------------------------------------------------------------
Scope, Context in main function: 58, 3
Scope, Context in subfunction: 60, 0
Scope, Context in nested function: 59, 0
------------------------------------------------------------
Scope, Context in main function: 58, 4
Scope, Context in subfunction: 60, 0
Scope, Context in nested function: 59, 0
------------------------------------------------------------
Scope, Context in main function: 58, 5
Scope, Context in subfunction: 60, 0
Scope, Context in nested function: 59, 0
------------------------------------------------------------




(file #34765)

Rik <rik5>
Group administrator
Sat 29 Aug 2015 01:57:36 PM UTC, comment #7: 

The attached patch avoids the problem for me.  I'm not yet sure if there is a better way than just avoiding the optimization for calls to eval/feval, or whether there are other cases that could cause trouble.

(file #34764)

John W. Eaton <jwe>
Group administrator
Fri 28 Aug 2015 08:03:07 PM UTC, comment #6: 

I meant


@(x) sin (x)


John W. Eaton <jwe>
Group administrator
Fri 28 Aug 2015 08:02:18 PM UTC, comment #5: 

I'm retitling this report.

Note the difference in the following:


f = @() __current_scope__
g = @() eval ('__current_scope__')
f()
g()


The trouble is caused by this function:

http://hg.savannah.gnu.org/hgweb/octave/annotate/2f94652de9ff/libinterp/octave-value/ov-fcn-handle.cc#l1945

I believe the purpose of the octave_fcn_binder object is to optimize things like


@f(x) sin (x)


so that they are almost as efficient as calling the internal function directly instead of going through all the effort of evaluating a user-defined function simply to call another function or simple expression.

You can show that the problem disappears if you make the body of the octave_fcn_binder::maybe_binder function be


  return new octave_fcn_handle (f, octave_fcn_handle::anonymous);


I'm looking to see whether there is a simple fix that will preserve this optimization and make the eval case work properly.

John W. Eaton <jwe>
Group administrator
Fri 28 Aug 2015 03:50:09 PM UTC, comment #4: 

I don't think Matlab is looking at strings for additional variable names.  For example, the following should produce 42 as a result, and at the time of parsing the anonymous function, there is no way to guess what string will be passed to the eval function:


f = @(s, t) eval (s)
f ('t', 42)


So I think the issue is that when evaluating anonymous functions, the parameters to the anonymous functions are not considered.  I haven't looked at this carefully, but my guess would be that the function parameters are in a separate scope but the body of the function is being evaluated in whatever scope it appears in, and that doesn't include the function parameters.  The fix that is needed is probably just a small change in the way variables are looked up in this case so that the function parameters are also searched and found first.

John W. Eaton <jwe>
Group administrator
Thu 27 Aug 2015 10:00:06 PM UTC, comment #3: 

Thanks Mike.  I re-titled the report to accurately reflect the issue.  Octave does not look for anonymous variables within strings; It treats strings as atomic objects, while Matlab does do another round of parsing looking for variables within the string.

Rik <rik5>
Group administrator
Thu 27 Aug 2015 09:46:13 PM UTC, comment #2: 

In Matlab:


>> clear all
>> f=@(t)eval('t');
>> f(2)
t =
     2
>> f('asdf')
t =
asdf
>> clear all
>> f=@(t) eval('sin(t)');
>> f(1)
ans =
    0.8415
>> t
Undefined function or variable 't'.



Mike Miller <mtmiller>
Group Member
Thu 27 Aug 2015 09:17:39 PM UTC, comment #1: 

Octave also waits to execute eval().


octave:1> f = @(t) eval ('t')
f =

@(t) eval ('t')

octave:2> eval ('t')
error: 't' undefined near line 1 column 1
octave:3> f (3)
error: 't' undefined near line 1 column 1
octave:4> t = 5
t =  5
octave:5> eval ('t')
t =  5
octave:6> f (3)
t =  5


In this case, 't' is a string and is not subject to variable replacement in the anonymous function.

In your second case, t is not used as a string but as a variable.


f = @(t) eval (num2str (t))

is truly equivalent to

tmp = num2str (t)
eval (tmp)


One can verify that the eval is taking place because the return value of num2str is a char variable, but the return value of the anonymous function f is a double value which only happens because a string like "42" has been eval'ed.


class (f (42))
ans = double


If you have access to Matlab, what does it return for the following?


clear all;
f = @(t) eval ('sin (t)');
f(1)



Rik <rik5>
Group administrator
Thu 27 Aug 2015 02:23:43 PM UTC, original submission:  

Calling


f=@(t)eval('t');f(3)


produces


error: 't' undefined


So you could think the string is evaluated on the assignment of f. This is confirmed by the following:


t=3;f=@(t)eval('t');f(2)


Which returns 3.
But if you type following, you see that the statement above is not true:


clear t;f=@(t)eval(num2str(t));f(42)


which returns 42.

In Matlab, the string within eval is only evaluated when f is called, consistently.


Anonymous

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

Attached Files
file #34768:  anonfcn.diffs added by rik5 (4KiB - application/octet-stream)
file #34765:  mainfcn.m added by rik5 (613B - text/x-objcsrc)
file #34764:  diffs.txt added by jwe (2KiB - text/plain)

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by jwe (Posted a comment)
  • -email is unavailable- added by mtmiller (Posted a comment)
  • -email is unavailable- added by rik5 (Posted a comment)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only group members can vote.

     

    Follow 12 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2015-11-21 rik5 StatusPatch Submitted Fixed
        Open/ClosedOpen Closed
    2015-08-30 rik5 StatusConfirmed Patch Submitted
    2015-08-29 rik5 Attached File- Added anonfcn.diffs, #34768
    2015-08-29 rik5 Attached File- Added mainfcn.m, #34765
        StatusNeed Info Confirmed
    2015-08-29 jwe Attached File- Added diffs.txt, #34764
    2015-08-28 jwe Summaryinterpreter does not search for anonymous function variables within a string wrong scope may be used in anonymous function evaluation
    2015-08-27 rik5 Summaryinconsistent behaviour of eval inside anonymous functions interpreter does not search for anonymous function variables within a string
    2015-08-27 mtmiller Release3.8.2 dev
        Operating SystemMicrosoft Windows Any
    2015-08-27 rik5 StatusNone Need Info

    Back to the top

    Powered by Savane 3.13-f8d8.
    Corresponding source code