Bug 15531 - sys.parent() provides incorrect result when multiple call frames use the same environment
Summary: sys.parent() provides incorrect result when multiple call frames use the same...
Status: NEW
Alias: None
Product: R
Classification: Unclassified
Component: Low-level (show other bugs)
Version: R 3.0.2
Hardware: All All
: P5 major
Assignee: R-core
URL:
Depends on:
Blocks:
 
Reported: 2013-11-06 22:38 UTC by Tony Lung
Modified: 2013-11-07 18:51 UTC (History)
1 user (show)

See Also:


Attachments
test case. submit via source() in interactive mode to replicate symptom (592 bytes, text/plain)
2013-11-07 14:54 UTC, Tony Lung
Details
version of test script with clarified labels in the output. (628 bytes, text/plain)
2013-11-07 18:51 UTC, Tony Lung
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Tony Lung 2013-11-06 22:38:24 UTC
I discovered this bug when I was attempting to walk up the call stack. I noticed that I could use parent.frame() to walk farther up the stack than I could using sys.parent(). 

This seemed to happen whenever two calls used the same environment. I think this happens mostly when eval() is called.

Execute the following code to reproducing the bug:


bug_fun<-function()
{
	print(parent.frame(n=2))
	
	print(sys.frame(sys.parent(n=2)))
}

bug_fun()


The two lines inside of bug_fun() do not produce the same output as they should. Example output from my system:

<environment: 0x000000000f86f708>
<environment: R_GlobalEnv>


I have noticed that the impact of this seems to be carried into functions like sys.status() as well.


When I marched up the stack using repeated calls to parent.frame() I noticed that the times when this behavior happens seemed to be when the same environment occurred more than once in the stack. I noticed this specifically with the R_GlobalEnv is association with eval() calls.

After taking a look at context.c I believe the problem exists in the R_sysparent() function. Based on the comments the function needs to return a particular environment and I believe the logic forces it to stop at the first context which references that environment rather than moving on to the appropriate context.

I have not tested this solution, but I think the following code might make it function correctly.


int attribute_hidden R_sysparent(int n, RCNTXT *cptr)
{
    int j;
    SEXP s;
    if(n <= 0)
	errorcall(R_ToplevelContext->call,
		  _("only positive values of 'n' are allowed"));

    while (cptr->nextcontext != NULL && n > 1) {
	if (cptr->callflag & CTXT_FUNCTION )
	    n--;
	cptr = cptr->nextcontext;
    }
    /* make sure we're looking at a return context */
    while (cptr->nextcontext != NULL && !(cptr->callflag & CTXT_FUNCTION) )
	cptr = cptr->nextcontext;
    
    return(framedepth(cptr));
}
Comment 1 Peter Dalgaard 2013-11-07 12:22:35 UTC
I get

> bug_fun<-function() 
+ {
+    print(parent.frame(n=2))
+ 
+    print(sys.frame(sys.parent(n=2)))
+ }
> 
> bug_fun()
<environment: R_GlobalEnv>
<environment: R_GlobalEnv>

So it seems something is not being told.

We're not going to even  consider untested fixes to unproven bugs....
Comment 2 Tony Lung 2013-11-07 14:52:40 UTC
Ok. After your feedback I took a closer look at how to replicate the symptoms. 

I have seen these results using R Term and using RJ in Eclipse in interactive mode. 

The symptoms do not show up when I highlight the code and "Run Selection in R".

The symptoms DO show up when I step through the code using "Run Entire Command in R and Go to Next Command".

The symptoms also show up when I submit the code as a file using a source() call, either by typing the source() call into the console or by using the "Run File in R via Command" option.


I will attach a file called sys_parent_defect.R. If you start an RJ console and submit this file via a source() command, you should be able to see the symptoms. (I think it's most easily replicated this way because the source() call appears to establish at least two contexts in the stack prior to R_GlobalEnv.)


Here's the code that will be in the file:

bug_fun<-function()
{
	print("sys.status()")
	print(sys.status())
	print("parent.frame(n=1)")
	print(parent.frame(n=1))
	print("parent.frame(n=1)")
	print(sys.frame(sys.parent(n=1)))
	
	print("parent.frame(n=2)")
	print(parent.frame(n=2))
	print("parent.frame(n=2)")
	print(sys.frame(sys.parent(n=2)))
	
	print("parent.frame(n=3)")
	print(parent.frame(n=3))
	print("parent.frame(n=3)")
	print(sys.frame(sys.parent(n=3)))
	
	print("parent.frame(n=4)")
	print(parent.frame(n=4))
	print("parent.frame(n=4)")
	print(sys.frame(sys.parent(n=4)))
	
}


bug_fun()


Here's the output I see when I submit the code via source(). You notice that parent.frame() and sys.frame(sys.parent()) disagree at both n=2 and n=3:


[1] "sys.status()"
$sys.calls
$sys.calls[[1]]
source("C:/Users/lunga/Desktop/Eclipse Workspace/SAMPLE/sys_parent_defect.R", 
    echo = FALSE, encoding = "Cp1252")

$sys.calls[[2]]
withVisible(eval(ei, envir))

$sys.calls[[3]]
eval(ei, envir)

$sys.calls[[4]]
eval(expr, envir, enclos)

$sys.calls[[5]]
bug_fun()

$sys.calls[[6]]
print(sys.status())

$sys.calls[[7]]
sys.status()


$sys.parents
[1] 0 1 1 3 0 5 5

$sys.frames
$sys.frames[[1]]
<environment: 0x000000000fc70590>

$sys.frames[[2]]
<environment: 0x000000000fb87730>

$sys.frames[[3]]
<environment: 0x000000000fb87928>

$sys.frames[[4]]
<environment: R_GlobalEnv>

$sys.frames[[5]]
<environment: 0x000000000fb87b20>

$sys.frames[[6]]
<environment: 0x000000000fb84dd0>

$sys.frames[[7]]
<environment: 0x000000000fb85188>


[1] "parent.frame(n=1)"
<environment: R_GlobalEnv>
[1] "parent.frame(n=1)"
<environment: R_GlobalEnv>
[1] "parent.frame(n=2)"
<environment: 0x000000000fb87928>
[1] "parent.frame(n=2)"
<environment: R_GlobalEnv>
[1] "parent.frame(n=3)"
<environment: 0x000000000fc70590>
[1] "parent.frame(n=3)"
<environment: R_GlobalEnv>
[1] "parent.frame(n=4)"
<environment: R_GlobalEnv>
[1] "parent.frame(n=4)"
<environment: R_GlobalEnv>
Comment 3 Tony Lung 2013-11-07 14:54:26 UTC
Created attachment 1506 [details]
test case. submit via source() in interactive mode to replicate symptom
Comment 4 Tony Lung 2013-11-07 18:51:21 UTC
Created attachment 1507 [details]
version of test script with clarified labels in the output.

This is an updated version of the test script. It works the same, I just adjusted the text in some of the print() statements to appropriately identify the outputs.

Properly labeled outputs look like this:

[1] "sys.status()"
$sys.calls
$sys.calls[[1]]
source("C:/Users/lunga/Desktop/Eclipse Workspace/SAMPLE/sys_parent_defect.R", 
    echo = FALSE, encoding = "Cp1252")

$sys.calls[[2]]
withVisible(eval(ei, envir))

$sys.calls[[3]]
eval(ei, envir)

$sys.calls[[4]]
eval(expr, envir, enclos)

$sys.calls[[5]]
bug_fun()

$sys.calls[[6]]
print(sys.status())

$sys.calls[[7]]
sys.status()


$sys.parents
[1] 0 1 1 3 0 5 5

$sys.frames
$sys.frames[[1]]
<environment: 0x000000000fcea1d0>

$sys.frames[[2]]
<environment: 0x000000000fd67780>

$sys.frames[[3]]
<environment: 0x000000000fd66ba0>

$sys.frames[[4]]
<environment: R_GlobalEnv>

$sys.frames[[5]]
<environment: 0x000000000fd66d60>

$sys.frames[[6]]
<environment: 0x000000000fd65e60>

$sys.frames[[7]]
<environment: 0x000000000fd662c0>


[1] "parent.frame(n=1)"
<environment: R_GlobalEnv>
[1] "sys.frame(sys.parent(n=1))"
<environment: R_GlobalEnv>
[1] "parent.frame(n=2)"
<environment: 0x000000000fd66ba0>
[1] "sys.frame(sys.parent(n=2))"
<environment: R_GlobalEnv>
[1] "parent.frame(n=3)"
<environment: 0x000000000fcea1d0>
[1] "sys.frame(sys.parent(n=3))"
<environment: R_GlobalEnv>
[1] "parent.frame(n=4)"
<environment: R_GlobalEnv>
[1] "sys.frame(sys.parent(n=4))"
<environment: R_GlobalEnv>