[R-pkg-devel] tryCatch() doesn't capture errors due to && or || with R CMD check --as-cran

Martin Maechler m@ech|er @end|ng |rom @t@t@m@th@ethz@ch
Thu Sep 1 17:19:47 CEST 2022


>>>>> Etienne Bacher 
>>>>>     on Thu, 01 Sep 2022 13:55:15 +0000 writes:

> Dear all, 

> Calling && or || with LHS or (if evaluated) RHS of length greater than one gives an error in the devel version of R and a warning since R 4.2.0. With R 4.2.1, I can capture this warning with tryCatch(). However, R CMD check with R-devel fails because of this new behavior, meaning that tryCatch() doesn't work. 

Very much on purpose, even though we (the R Core team) may not
have said so loudly.

The problem is that too many "smart" people, including the
authors of certain widely used testing packages do call
tryCatch(), or suppressWarnings() and similar to eliminate
warnings and errors in a situation we really *do* want these to
be shown.

For instance, we, the Matrix package authors, have planned to
deprecate certain usages of  as(*, "<someMatrixClass>")  and
give deprecation warnings -- see  help(Deprecated) -- so the
users and package authors can see the undesired usages and
eventually update their code.

No, because of too much use of suppressWarnings() they *NEVER
EVER* see *any* deprecation warnings for all their own code
which silences these warnings and for all (often 100%) of their
tests where they use the famous test...t  package ...

An additional reason for such a non-suppressible error is that
when you find such warnings/errors in package testing of
packages with many dependencies, if the warning/error is raised
and you *do* see it, it may *still* not at all be easy to see
from which of the (sometimes 20--100) packages that are in use
the problem is caused.

I hope you understand that such "obnoxious" behavior is
rational and desirable in such rare cases.

Best regards,
Martin


--
Martin Maechler
ETH Zurich  and  R Core team

> For example, take this function:
> 
> foo <- function() {
>   tryCatch(
>     if (c(TRUE, TRUE) || c(TRUE, TRUE)) 1,
>     warning = function(w) {
>       print(w)
>       NULL
>     },
>     error = function(e) {
>       print(e)
>       NULL
>     }
>   )
> }
> 
> With R 4.2.1, this correctly gives a warning and returns NULL when I run this locally:
> > foo()
> <simpleWarning in c(TRUE, TRUE) || c(TRUE, TRUE): 'length(x) = 2 > 1' in coercion to 'logical(1)'>
> NULL
> 
> And if I change the environment variables to return an error instead of the warning, it correctly gives an error and returns NULL:
> > Sys.setenv(
>     `_R_CHECK_LENGTH_1_CONDITION_` = "true",
>     `_R_CHECK_LENGTH_1_LOGIC2_` = "true"
> )
> >foo()
> <simpleError in c(TRUE, TRUE) || c(TRUE, TRUE): 'length(x) = 2 > 1' in coercion to 'logical(1)'>
> NULL
> 
> This behavior is expected. But if I put this function in a package, add a test for it, and run R CMD check "." --as-cran (or rcmdcheck::rcmdcheck(args = "--as-cran")), then I get an error that I paste at the end of this email because it's quite long. I thought that tryCatch() would capture this error, even when using R CMD check with R-devel, but this is not the case. Is this an expected behavior or a bug?
> 
> If you want to reproduce this error, I made a tiny package here: https://github.com/etiennebacher/testpackage. You can simply clone the repo and run R CMD check on it. 
> 
> If it matters, I'm using R 4.2.1 on Windows 10.
> 
> Thanks in advance,
> 
> Etienne
> 
> 
>  ----------- FAILURE REPORT -------------- 
>  --- failure: length > 1 in coercion to logical ---
>  --- srcref --- 
> : 
>  --- package (from environment) --- 
> compiler
>  --- call from context --- 
> do.call(ffun, args)
>  --- call from argument --- 
> .Primitive("||")(c(TRUE, TRUE), c(TRUE, TRUE))
>  --- R stacktrace ---
> where 1: do.call(ffun, args)
> where 2: mode(e)
> where 3: mode(e) %in% constModes
> where 4: checkConst(do.call(ffun, args))
> where 5: doTryCatch(return(expr), name, parentenv, handler)
> where 6: tryCatchOne(expr, names, parentenv, handlers[[1L]])
> where 7: tryCatchList(expr, names[-nh], parentenv, handlers[-nh])
> where 8: doTryCatch(return(expr), name, parentenv, handler)
> where 9: tryCatchOne(tryCatchList(expr, names[-nh], parentenv, handlers[-nh]), 
>     names[nh], parentenv, handlers[[nh]])
> where 10: tryCatchList(expr, classes, parentenv, handlers)
> where 11: tryCatch(checkConst(do.call(ffun, args)), error = function(e) NULL, 
>     warning = function(w) NULL)
> where 12: constantFoldCall(e, cntxt)
> where 13: constantFold(test, cntxt, loc = cb$savecurloc())
> where 14: h(e, cb, cntxt)
> where 15: tryInline(call, cb, cntxt)
> where 16: cmpCall(e, cb, cntxt)
> where 17: cmp(e, cb, cntxt, setloc = FALSE)
> where 18: genCode(a, pcntxt, loc = cb$savecurloc())
> where 19: cb$putconst(genCode(a, pcntxt, loc = cb$savecurloc()))
> where 20: cmpCallArgs(args, cb, cntxt, nse)
> where 21: cmpCallSymFun(fun, args, call, cb, cntxt)
> where 22: cmpCall(e, cb, cntxt)
> where 23: cmp(subexp, cb, cntxt, setloc = FALSE)
> where 24: h(e, cb, cntxt)
> where 25: tryInline(call, cb, cntxt)
> where 26: cmpCall(e, cb, cntxt)
> where 27: cmp(e, cb, cntxt, setloc = FALSE)
> where 28: genCode(body(f), ncntxt, loc = loc)
> where 29: cmpfun(f)
> where 30: doTryCatch(return(expr), name, parentenv, handler)
> where 31: tryCatchOne(expr, names, parentenv, handlers[[1L]])
> where 32: tryCatchList(expr, classes, parentenv, handlers)
> where 33: tryCatch(cmpfun(f), error = function(e) {
>     notifyCompilerError(paste(e$message, "at", deparse(e$call)))
>     f
> })
> where 34: compiler:::tryCmpfun(function () 
> {
>     tryCatch(if (c(TRUE, TRUE) || c(TRUE, TRUE)) 
>         1, warning = function(w) {
>         print(w)
>         NULL
>     }, error = function(e) {
>         print(e)
>         NULL
>     })
> })
> where 35: lazyLoadDBinsertVariable(vars[i], from, datafile, ascii, compress, 
>     envhook)
> where 36: makeLazyLoadDB(ns, dbbase, compress = compress, set.install.dir = set.install.dir)
> where 37: code2LazyLoadDB(package, lib.loc = lib.loc, keep.source = keep.source, 
>     keep.parse.data = keep.parse.data, compress = compress, set.install.dir = set.install.dir)
> where 38: tools:::makeLazyLoading("testpackage", "C:/Users/etienne/AppData/Local/Temp/Rtmp8QXtOL/file41b43ee61605/testpackage.Rcheck/00LOCK-TESTPA~1/00new", 
>     keep.source = FALSE, keep.parse.data = FALSE, set.install.dir = "C:/Users/etienne/AppData/Local/Temp/Rtmp8QXtOL/file41b43ee61605/testpackage.Rcheck/testpackage")
> 
>  --- value of length: 2 type: logical ---
> [1] TRUE TRUE
>  --- function from context --- 
> function (what, args, quote = FALSE, envir = parent.frame()) 
> {
>     if (!is.list(args)) 
>         stop("second argument must be a list")
>     if (quote) 
>         args <- lapply(args, enquote)
>     .Internal(do.call(what, args, envir))
> }
> <bytecode: 0x0000021b0a155e20>
> <environment: namespace:base>
>  --- function search by body ---
> Function do.call in namespace base has this body.
>  ----------- END OF FAILURE REPORT -------------- 
> Fatal error: length > 1 in coercion to logical
>



More information about the R-package-devel mailing list