Skip to content

Catalina problems

bgoodri edited this page Nov 8, 2019 · 10 revisions

Introduction

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.

Test Program

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()

Results of Test Program

Successful

The throw_exception() function returns "this is the expected behavior" on

  • Linux, regardless of whether clang++ or g++ 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 with clang++ 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 with g++.

Unsuccessful

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

Code to verify what C++ library was used

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.

Potential solutions

Avoiding the system C++ libraries

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.

install_name_tool

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.

Statically linking

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.

Patching rethrow_located.hpp

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.