-
-
Notifications
You must be signed in to change notification settings - Fork 266
Catalina problems
Catalina does not seem to handle exceptions properly. This does not seem to be a problem with Stan code, but affects Stan adversely because
- Stan has many different types of exceptions
- Stan behaves differently depending on which type of exception is thrown
- The occurrence of an exception during MCMC, ADVI, or optimization is probabilistic but likely, even in well-written Stan programs
It is not clear whether this is a bug in the Catalina operating system, the XCode that comes with Catalina, the binary build of R from CRAN, or Rcpp. It is possibly related to this open Rcpp issue
https://github.com/RcppCore/Rcpp/issues/972
This wiki page attempts to document what works as well as what does not work. In short, the section on Avoiding the C++ system libraries. Otherwise, on Catalina, neither runtime Stan programs nor R packages that come with compiled Stan programs work, unless all loaded packages with C++ (even those unrelated to Stan) are compiled from source using clang++
from XCode. The same would apply to any package using Rcpp::sourceCpp
or inline::cxxfunction
at runtime.
Rcpp::sourceCpp(code =
'
#include <Rcpp.h>
// [[Rcpp::export]]
int throw_exception() {
std::stringstream errmsg; errmsg << "this is the expected behavior";
throw std::domain_error(errmsg.str());
return 0;
}
'
)
throw_exception()
The throw_exception()
function returns "this is the expected behavior" on
- Linux, regardless of whether
clang++
org++
is used to compile - Windows, using RTools35
- Mac (Mojave operating system) with
clang++
from CRAN via coatless' installer - Mac (Catalina operating system) with
clang++
from XCode, provided that Rcpp is compiled from source withclang++
from XCode and no binary packages with C++ code from CRAN (readr, ggplot2, etc.) have been loaded - Mac (Catalina operating system), with R installed via homebrew and
clang++
from XCode - Mac (Catalina operating system) with R from CRAN but
g++
installed from homebrew, but this may require that you install Rcpp from source withg++
.
The throw_exception()
function returns "c++ exception (unknown reason)" on
- Mac (Catalina operating system) with
clang++
from CRAN via coatless' installer - Mac (Catalina operating system) with
clang++
from XCode if any binary package with C++ code from CRAN (readr, ggplot2, etc.) has been loaded
Kevin Ushey wrote the following to show which C++ library was used on a Mac
dlls <- getLoadedDLLs()
paths <- vapply(dlls, `[[`, "path", FUN.VALUE = character(1))
invisible(lapply(paths, function(path) {
if (!file.exists(path))
return(FALSE)
output <- system(paste("otool -L", shQuote(path), "| grep libc++ || true"),
intern = TRUE)
if (length(output) == 0)
return(FALSE)
writeLines(paste0(path, ":"))
writeLines(output)
}))
For each dynamically loaded library, tt will say something like
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 800.6.0)
or
/Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1.0.0)
The above test program seems not to work if there are multiple versions of libc++.1.dylib dynamically loaded into R.
According to https://libcxx.llvm.org/docs/UsingLibcxx.html , the system version of libc++ can be avoided with something like
clang++ -std=c++11 -stdlib=libc++ -nostdinc++ \
-I<libcxx-install-prefix>/include/c++/v1 \
-L<libcxx-install-prefix>/lib \
-Wl,-rpath,<libcxx-install-prefix>/lib \
test.cpp
where <libcxx-install-prefix>
is the root of the directory where clang
is installed. In our case, it appears as if putting
CXX14 = /usr/local/clang7/bin/clang++ -stdlib=libc++ -nostdinc++ -I/usr/local/clang7/include/c++/v1
SHLIB_CXX14LD = /usr/local/clang7/bin/clang++ -L/usr/local/clang7/lib
into ~/.R/Makevars
allows Stan to work.
When we first encountered this problem back in 2017
https://github.com/jeroen/V8/issues/37#issuecomment-305266135
we were able to fix it with a suggested hack by Kevin Ushey, but the function below seems not to fix the problem on Catalina:
fix_Mac <- function(sm) {
stopifnot(is(sm, "stanmodel"))
dso_last_path <- sm@dso@.CXXDSOMISC$dso_last_path
CLANG_DIR <- tail(n = 1, grep("clang[456789]",
x = list.dirs("/usr/local", recursive = FALSE),
value = TRUE))
if (length(CLANG_DIR) == 0L) stop("no clang from CRAN found")
LIBCPP <- file.path(CLANG_DIR, "lib", "libc++.1.dylib")
if (!file.exists(LIBCPP)) stop("no unique libc++.1?.dylib found")
cmd <- paste(
"install_name_tool",
"-change",
"/usr/lib/libc++.1.dylib",
LIBCPP,
dso_last_path
)
system(cmd)
dyn.unload(dso_last_path)
dyn.load(dso_last_path)
return(invisible(NULL))
}
Here is a link to a complete session that attempts to use install_name_tool
with the above test program.
No one has tried this yet, and it is discouraged by Apple in some cases, but it may be possible to statically link the correct version of a C++ library.
It is possible we could modify
https://github.com/stan-dev/stan/blob/develop/src/stan/lang/rethrow_located.hpp#L105
to continue, irrespective of the exception type.