Repository navigation
turn exceptions during module initialization into r errors - #1526
Conversation
|
The LLM I had look at this at comments about possible nesting of scopes. What do you think? PLAN.md below. Plan: Implement PR #1526 — turn exceptions during module initialization into R errorsSummary of findings
Local verification of the codebase
AssessmentCorrect and well-scoped. The fix is the boot-function analog of the earlier Concerns
Implementation steps1.
|
A module loaded from within another module body (say, via a namespace load whose .onLoad calls loadModule()) would reset the outer scope to null; ModuleScope now saves and restores the previous scope.
|
Good catch, thanks. Nesting can only happen if a module body itself triggers another module's boot (say, user code in the body loads a namespace whose That said, save/restore is one pointer, header-only (no |
|
I think this is ready to (squash) merge. Do you want to do the honours, @kevinushey ? |
Fixes #1525.
The boot function generated by
RCPP_MODULEis anextern "C"entry point called from R via.Call(), but had noBEGIN_RCPP/END_RCPP. Any C++ exception thrown by the module body, such as Rcpp's ownstd::range_error("no such class")from.derives<T>()with a misspelled parent, escaped into R andstd::terminate()aborted the session. It also skipped::setCurrentScope( 0 ), leaving the global module scope pointing at the half-initialized module. See #1525 for the reprex. This is the boot-function counterpart of #1495 / #1496.Changes (
inst/include/Rcpp/Module.h):RCPP_MODULE: the boot function body is wrapped inBEGIN_RCPP/END_RCPP, so exceptions become R errors like in every other.Callentry point.Rcpp::internal::ModuleScope, a small RAII guard that sets the current module scope and clears it in its destructor, so the scope is reset on the error path as well as on success.The change is confined to the macro, so it applies to a package once it is rebuilt; nothing in
Rcpp.sochanges. One caller-visible difference: code that calls the boot function directly from C++ instead of through.Call(), such as RInside'sLOAD_RCPP_MODULE, now gets an R error rather than a C++ exception when the module body throws. Previously a throwing module body was fatal whenever it was loaded from R, so this only matters for that direct-call path.Tests:
inst/tinytest/cpp/ModuleInitError.cpp: a module whose body throws (derives()with a parent that was never exposed).inst/tinytest/test_module.R:sourceCpp()of that file raises theno such classR error, and afterwards the current module scope is cleared (checked through a small exported helper added tocpp/Module.cpp).Verification:
SIGABRT, exit code 134) against current master and printsresult: no such classwith exit code 0 against this branch.test_module.R(47/47),test_modref.R,test_expose_class.Randtest_module_client_package.Rpass withRunAllRcppTests=yes RunVerboseRcppTests=yes(R 4.6.1, macOS).-Wall -Wextra -Wpedantic -Wconversion -Wshadow -Wunreachable-codereports no warnings from the new macro or guard.Checklist
R CMD checkstill passes all tests (module test files run locally as above)