[Rd] Bug in stats:::`[.formula`: (~ NULL)[2] gives Error ... missing value where TRUE/FALSE needed

Henrik Bengtsson henr|k@bengt@@on @end|ng |rom gm@||@com
Fri Aug 14 23:05:04 CEST 2020


Hi, it looks like:

> stats:::`[.formula`
function (x, i)
{
    ans <- NextMethod("[")
    if (length(ans) == 0L || as.character(ans[[1L]])[1L] == "~") {
        class(ans) <- "formula"
        environment(ans) <- environment(x)
    }
    ans
}
<bytecode: 0x556688d87cc0>
<environment: namespace:stats>

doesn't like to extract NULL components on either the LHS or RHS.  For
example, with

> (~ NULL)[2]
Error in if (length(ans) == 0L || as.character(ans[[1L]])[1L] == "~") { :
  missing value where TRUE/FALSE needed

this despite:

> str(as.list(~ NULL))
List of 2
 $ : symbol ~
 $ : NULL
 - attr(*, "class")= chr "formula"
 - attr(*, ".Environment")=<environment: R_GlobalEnv>

> length(~ NULL)
[1] 2

> (~ NULL)[[2]]
NULL

Other examples are:

> (NULL ~ .)[2]
Error in if (length(ans) == 0L || as.character(ans[[1L]])[1L] == "~") { :
  missing value where TRUE/FALSE needed

> (NULL ~ NULL)[3]
Error in if (length(ans) == 0L || as.character(ans[[1L]])[1L] == "~") { :
  missing value where TRUE/FALSE needed


TROUBLESHOOTING:

The reason is that ans[[1L]] becomes NULL;

> trace(stats:::`[.formula`, tracer = quote(utils::str(as.list(ans))), at = 3L)
Tracing function "[.formula" in package "stats (not-exported)"
[1] "[.formula"

> (~ NULL)[2]
Tracing `[.formula`((~NULL), 2) step 3
List of 1
 $ : NULL
Error in if (length(ans) == 0L || as.character(ans[[1L]])[1L] == "~") { :
  missing value where TRUE/FALSE needed

which causes 'as.character(ans[[1L]])[1L] == "~"' to resolve to NA.


PATCH:

A minimal backward compatible fix would be:

`[.formula` <- function(x,i) {
    ans <- NextMethod("[")
    ## as.character gives a vector.
    if(length(ans) == 0L || (is.null(ans[[1L]]) && i <= length(x)) ||
       as.character(ans[[1L]])[1L] == "~") {
        class(ans) <- "formula"
        environment(ans) <- environment(x)
    }
    ans
}

A better fix would probably be to also detect out of range as in:

`[.formula` <- function(x,i) {
    if (i > length(x))
        stop(gettext("index out of range"))
    ans <- NextMethod("[")
    ## as.character gives a vector.
    if(length(ans) == 0L || is.null(ans[[1L]]) ||
       as.character(ans[[1L]])[1L] == "~") {
        class(ans) <- "formula"
        environment(ans) <- environment(x)
    }
    ans
}

/Henrik



More information about the R-devel mailing list