Bug 16629 - Infinite recursion when S4 object has S3 superclass and a slot of type call
Summary: Infinite recursion when S4 object has S3 superclass and a slot of type call
Status: NEW
Alias: None
Product: R
Classification: Unclassified
Component: S4methods (show other bugs)
Version: R 3.2.2
Hardware: All All
: P5 critical
Assignee: R-core
URL:
Depends on:
Blocks:
 
Reported: 2015-12-14 23:16 UTC by Alexis
Modified: 2015-12-15 13:30 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alexis 2015-12-14 23:16:04 UTC
Small reproducible example:

==============================================================================

setClass("proc_time4", contains = "numeric", slots = c(names = "character"))
setOldClass("proc_time", S4Class = "proc_time4")
removeClass("proc_time4")

setClass("test", contains = "proc_time", slots = c(call = "call"))

foo <- function() {
   MYCALL <- match.call() 
   t <- proc.time() 
   new("test", t, call = MYCALL)
}

foo()

==============================================================================

Results in:

Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
Error during wrapup: evaluation nested too deeply: infinite recursion / options(expressions=)?

==============================================================================

When creating the "test" object, MYCALL actually gets evaluated again.

Happened in both Linux and Windows with R 3.2.3
Comment 1 John Chambers 2015-12-15 01:05:31 UTC
Good catch.  The problem is the use of do.call in the "S4" initialize method to construct a callNextmethod().  The substituted element in the call is the (recursive) call to foo().

Not obviously easy to fix.

A workaround, I think, is to use class "expression" instead of "call" and embed your call in the expression object.  That at least seems to create the new() object.
Comment 2 Alexis 2015-12-15 13:30:35 UTC
The following works for my specific example, thus it expects the slot of type 'call' to be called 'call'.

setClass("proc_time4", contains = "numeric", slots = c(names = "character"))
setOldClass("proc_time", S4Class = "proc_time4")
removeClass("proc_time4")

setClass("test", contains = "proc_time", slots = c(call = "call"))

# Isolate 'call' from the rest of the parameters
setMethod("initialize", "test",
          function(.Object, ..., call) {
              .Object <- callNextMethod(.Object = .Object, ...)
              
              if(!missing(call))
                  .Object@call <- call
              
              .Object
          })

foo <- function() {
   MYCALL <- match.call() 
   t <- proc.time() 
   new("test", t, call = MYCALL)
}

foo()