Bug 16644

Summary: unloadNamespace() does not address unevaluated promises in the S3 Methods Table
Product: R Reporter: Jim Hester <james.f.hester>
Component: Low-levelAssignee: R-core <R-core>
Status: UNCONFIRMED ---    
Severity: normal CC: gmbecker, michafla, murdoch
Priority: P5    
Version: R-devel (trunk)   
Hardware: All   
OS: All   

Description Jim Hester 2015-12-24 18:39:49 UTC
Given the extremely simple package at
https://github.com/jimhester/testUnload, which includes only one S3 method
'print.object' the following code produces a lazy load error from a new R
session (R-devel r69801)

install.packages("testUnload", repos = NULL)
library("testUnload")
unloadNamespace("testUnload")
install.packages("testUnload", repos = NULL)
library("testUnload")
#> Error in get(method, envir = home) :
#>   lazy-load database '{sic}/testUnload/R/testUnload.rdb' is corrupt
#> In addition: Warning message:
#> In get(method, envir = home) : internal error -3 in R_decompress1
#> Error: package or namespace load failed for ‘testUnload’

Upon investigation this is because the code in registerS3Methods creates a
promise using 'delayedAssign' for 'print.object' function in the
'.__S3MethodsTable__.' environment within the base environment (which is
where the 'print' generic is defined). (see lines 1387-1489 in
src/library/base/R/namespace.R).

When the second install.packages is called the files are changed before the
original promise is evaluated, which causes the error. An easy way to see
this is to explicitly evaluate the promise prior to the reinstall, which
removes the error.

library("testUnload")
get(".__S3MethodsTable__.", envir = baseenv())$print.object
#> function(x, ...) x
#> <environment: namespace:testUnload>
unloadNamespace("testUnload")
install.packages("testUnload", repos = NULL)
library("testUnload")


Explicitly deleting the promise after unloading the namespace also fixes
this issue.

library("testUnload")
unloadNamespace("testUnload")
rm(list="print.object", envir = get(".__S3MethodsTable__.", envir =
baseenv()))
install.packages("testUnload", repos = NULL)
library("testUnload")


In my opinion, once the namespace is unloaded the corresponding entries
should be removed in the S3 Methods Table by default.
Comment 1 Duncan Murdoch 2015-12-27 19:23:06 UTC
Could you post sessionInfo()?  I don't see this.  Here are my details on one system:

> sessionInfo()
R version 3.2.3 (2015-12-10)
Platform: x86_64-apple-darwin13.4.0 (64-bit)
Running under: OS X 10.11.2 (El Capitan)

locale:
[1] en_CA.UTF-8/en_CA.UTF-8/en_CA.UTF-8/C/en_CA.UTF-8/en_CA.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] testUnload_0.0.0.9000

And on another:

R Under development (unstable) (2015-12-26 r69814)
Platform: i386-w64-mingw32/i386 (32-bit)
Running under: Windows XP (build 2600) Service Pack 3

locale:
[1] LC_COLLATE=English_Canada.1252  LC_CTYPE=English_Canada.1252   
[3] LC_MONETARY=English_Canada.1252 LC_NUMERIC=C                   
[5] LC_TIME=English_Canada.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] testUnload_0.0.0.9000
Comment 2 Brian Ripley 2015-12-28 07:59:19 UTC
This is the documented behaviour!

?unloadNamespace directs you to ?detach, which says

     If a package has a namespace, detaching it does not by default
     unload the namespace (and may not even with ‘unload = TRUE’), and
     detaching will not in general unload any dynamically loaded
     compiled code (DLLs).  Further, registered S3 methods from the
     namespace will not be removed.
Comment 3 Michael Lawrence 2015-12-29 22:13:45 UTC
Why was the decision made to document that behavior, instead of supporting method unloading? Presumably there are some serious technical complications.