9

The following code reproducibly segfaults when executed in R (3.0.2 but I’m assuming it’s similar for other versions):

ns = new.env(parent = .BaseNamespaceEnv)
local(f <- function () 42, envir = ns)
x = list2env(as.list(ns), parent = as.environment(2))
parent.env(.GlobalEnv) = x
detach()

Yes, I know that the documentation on parent.env says

The replacement function parent.env<- is extremely dangerous as it can be used to destructively change environments in ways that violate assumptions made by the internal C code. It may be removed in the near future.

That’s what I seem to be running into here. However, I would like to understand why this behaviour is the way it is, and how to avoid it.

The following simplified code does not have this problem:

x = new.env(parent = as.environment(2))
local(f <- function () 42, envir = x)
parent.env(.GlobalEnv) = x
detach()

… so it seems that it makes a difference that x contains a function whose parent.env is a different (unattached) environment.

Likewise, using attach instead of parent.env<- does not lead to a crash. (So why not simply use attach? Because in my code, the .GlobalEnv part is a variable which may refer to different environments.)

The crash dump tells me that the segfault happens in do_detach (envir.c). The code contains the following lines:

isSpecial = IS_USER_DATABASE(s);
if(isSpecial) {
    R_ObjectTable *tb = (R_ObjectTable*) R_ExternalPtrAddr(HASHTAB(s));
    if(tb->onDetach) tb->onDetach(tb);
}

I have no idea what IS_USER_DATABASE does – maybe this is related? Merely adding an .onDetach method to my environment (.onDetach = function (x) x) did not help.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • I think you'd have to ask on r-devel. Messing around with the parent of globalenv seems like a _really_ bad idea. – hadley Apr 04 '14 at 02:02

2 Answers2

1

Note: this is more of a comment than a real answer, but can't fit it in the comment field. I have a loosely related question and have been trying to better understand the limitations of parent.env<-.

In your case, note that the problem is actually related to list2env, not the function. Consider:

f.1 <- function() NULL
ns.1 <- new.env(parent = .BaseNamespaceEnv)
x.1 <- new.env(parent = as.environment(2))
environment(f.1) <- ns.1
x.1$f.1 <- f.1
parent.env(.GlobalEnv) <- x.1
detach()

Works, but:

x.2 <- list2env(list(a=1), parent=as.environment(2))
parent.env(.GlobalEnv) <- x.2
detach()

crashes. Here, we are not even doing anything complex at all with the environment we're using as a parent, other than it was created with list2env. Looking at the source I don't see anything obvious as to why list2env is a problem but $<- is not since both internally seem to use defineVar, but clearly there is a lot going on there that I don't understand.

Community
  • 1
  • 1
BrodieG
  • 51,669
  • 9
  • 93
  • 146
0

For completeness’ sake, the obvious workaround is to test whether the environment is .GlobalEnv, and to special-case it:

new_env = something # e.g. .GlobalEnv

if (identical(new_env, .GlobalEnv))
    attach(x)
else {
    parent.env(x) = parent.env(new_env)
    parent.env(new_env) = x
}

… but this doesn’t explain the initial bug and, I fear, only hides the problem rather than removing it.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214