From 24ebe4ca9b58e0ded3e290650e87269710a255b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Tue, 28 Oct 2025 15:08:41 +0100 Subject: [PATCH 1/7] Mask definition of Rf_error to avoid longjmp issues --- ChangeLog | 12 +++++++++- inst/include/Rcpp/macros/mask.h | 24 +++++++++++++++++++ inst/include/RcppCommon.h | 4 +++- inst/tinytest/cpp/stack.cpp | 4 +++- .../src/RcppExports.cpp | 2 ++ src/attributes.cpp | 2 ++ 6 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 inst/include/Rcpp/macros/mask.h diff --git a/ChangeLog b/ChangeLog index 9911a8173..f15a083da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,10 +7,20 @@ * inst/include/Rcpp/hash/IndexHash.h: Normalize values for all comparisons * inst/include/Rcpp/hash/SelfHash.h: Idem * inst/tinytest/test_sugar.R: Add test for signed zeroes +2025-10-28 Iñaki Ucar + + * inst/include/Rcpp/macros/mask.h: Mask Rf_error with Rcpp::stop with + a warning at compilation time, unless RCPP_NO_MASK_RF_ERROR is defined + * inst/include/RcppCommon.h: Include the previous file in the last place + * inst/tinytest/cpp/stack.cpp: Define RCPP_NO_MASK_RF_ERROR to enable + Rf_error for this test + * src/attributes.cpp: Undef then re-include masking to allow generated + interface call to Rf_error + * inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp: Idem 2025-10-21 Iñaki Ucar - * inst/include/Rcpp/exceptions_impl.h: use __has_include to simplify checks + * inst/include/Rcpp/exceptions_impl.h: Use __has_include to simplify checks to enable demangling, making them robust for more platforms 2025-10-13 Dirk Eddelbuettel diff --git a/inst/include/Rcpp/macros/mask.h b/inst/include/Rcpp/macros/mask.h new file mode 100644 index 000000000..28cb8d9ad --- /dev/null +++ b/inst/include/Rcpp/macros/mask.h @@ -0,0 +1,24 @@ +// mask.h: Rcpp R/C++ interface class library -- masking macros +// +// Copyright (C) 2025 Iñaki Ucar +// +// This file is part of Rcpp. +// +// Rcpp is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// Rcpp is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Rcpp. If not, see . + +#ifndef RCPP_NO_MASK_RF_ERROR + #define Rf_error \ + _Pragma("GCC warning \"Invalid use of Rf_error, use Rcpp::stop instead\"") \ + Rcpp::stop +#endif diff --git a/inst/include/RcppCommon.h b/inst/include/RcppCommon.h index 8f28fd035..1c58e2531 100644 --- a/inst/include/RcppCommon.h +++ b/inst/include/RcppCommon.h @@ -4,7 +4,7 @@ // // Copyright (C) 2008 - 2009 Dirk Eddelbuettel // Copyright (C) 2009 - 2020 Dirk Eddelbuettel and Romain Francois -// Copyright (C) 2021 Dirk Eddelbuettel, Romain Francois and Iñaki Ucar +// Copyright (C) 2021 - 2025 Dirk Eddelbuettel, Romain Francois and Iñaki Ucar // // This file is part of Rcpp. // @@ -191,4 +191,6 @@ namespace Rcpp { #include +#include + #endif diff --git a/inst/tinytest/cpp/stack.cpp b/inst/tinytest/cpp/stack.cpp index c3fa41789..363e4a74a 100644 --- a/inst/tinytest/cpp/stack.cpp +++ b/inst/tinytest/cpp/stack.cpp @@ -2,7 +2,8 @@ // // misc.cpp: Rcpp R/C++ interface class library -- misc unit tests // -// Copyright (C) 2013 - 2022 Dirk Eddelbuettel and Romain Francois +// Copyright (C) 2013 - 2024 Dirk Eddelbuettel and Romain Francois +// Copyright (C) 2025 Dirk Eddelbuettel, Romain Francois and Iñaki Ucar // // This file is part of Rcpp. // @@ -21,6 +22,7 @@ // [[Rcpp::plugins(cpp11)]] +#define RCPP_NO_MASK_RF_ERROR #include using namespace Rcpp; diff --git a/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp b/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp index a6beb9b53..2e204a7cd 100644 --- a/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp +++ b/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp @@ -43,7 +43,9 @@ RcppExport SEXP _testRcppInterfaceExporter_test_cpp_interface(SEXP xSEXP, SEXP f if (rcpp_isError_gen) { SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen); UNPROTECT(1); + #undef Rf_error Rf_error("%s", CHAR(rcpp_msgSEXP_gen)); + #include } UNPROTECT(1); return rcpp_result_gen; diff --git a/src/attributes.cpp b/src/attributes.cpp index 81c2f5bef..108aca380 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -2953,7 +2953,9 @@ namespace attributes { << " if (rcpp_isError_gen) {" << std::endl << " SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen);" << std::endl << " UNPROTECT(1);" << std::endl + << " #undef Rf_error" << std::endl << " Rf_error(\"%s\", CHAR(rcpp_msgSEXP_gen));" << std::endl + << " #include " << std::endl << " }" << std::endl << " UNPROTECT(1);" << std::endl << " return rcpp_result_gen;" << std::endl From 953c456932958b8f2fa0844ef821604895cc48e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Tue, 28 Oct 2025 15:20:18 +0100 Subject: [PATCH 2/7] flush to the left --- inst/include/Rcpp/macros/mask.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inst/include/Rcpp/macros/mask.h b/inst/include/Rcpp/macros/mask.h index 28cb8d9ad..03b7b9f8a 100644 --- a/inst/include/Rcpp/macros/mask.h +++ b/inst/include/Rcpp/macros/mask.h @@ -18,7 +18,7 @@ // along with Rcpp. If not, see . #ifndef RCPP_NO_MASK_RF_ERROR - #define Rf_error \ - _Pragma("GCC warning \"Invalid use of Rf_error, use Rcpp::stop instead\"") \ - Rcpp::stop +#define Rf_error \ + _Pragma("GCC warning \"Invalid use of Rf_error, use Rcpp::stop instead\"") \ + Rcpp::stop #endif From ab77057cdaff9bfb090ce9947dd6e04bf41dc1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Tue, 28 Oct 2025 15:59:20 +0100 Subject: [PATCH 3/7] add RCPP_NO_MASK macro to protect individual calls --- ChangeLog | 6 ++---- inst/include/Rcpp/macros/mask.h | 12 ++++++++++-- inst/tinytest/cpp/stack.cpp | 3 +-- .../testRcppInterfaceExporter/src/RcppExports.cpp | 4 +--- src/attributes.cpp | 4 +--- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index f15a083da..4054a3583 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,10 +12,8 @@ * inst/include/Rcpp/macros/mask.h: Mask Rf_error with Rcpp::stop with a warning at compilation time, unless RCPP_NO_MASK_RF_ERROR is defined * inst/include/RcppCommon.h: Include the previous file in the last place - * inst/tinytest/cpp/stack.cpp: Define RCPP_NO_MASK_RF_ERROR to enable - Rf_error for this test - * src/attributes.cpp: Undef then re-include masking to allow generated - interface call to Rf_error + * src/attributes.cpp: Use RCPP_NO_MASK to protect call to Rf_error + * inst/tinytest/cpp/stack.cpp: Idem * inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp: Idem 2025-10-21 Iñaki Ucar diff --git a/inst/include/Rcpp/macros/mask.h b/inst/include/Rcpp/macros/mask.h index 03b7b9f8a..c588898ec 100644 --- a/inst/include/Rcpp/macros/mask.h +++ b/inst/include/Rcpp/macros/mask.h @@ -17,8 +17,16 @@ // You should have received a copy of the GNU General Public License // along with Rcpp. If not, see . +#ifndef Rcpp_macros_mask_h +#define Rcpp_macros_mask_h + +#define RCPP_NO_MASK_EMPTY() +#define RCPP_NO_MASK(id) id RCPP_NO_MASK_EMPTY() + #ifndef RCPP_NO_MASK_RF_ERROR -#define Rf_error \ +#define Rf_error(...) \ _Pragma("GCC warning \"Invalid use of Rf_error, use Rcpp::stop instead\"") \ - Rcpp::stop + Rcpp::stop(__VA_ARGS__) +#endif + #endif diff --git a/inst/tinytest/cpp/stack.cpp b/inst/tinytest/cpp/stack.cpp index 363e4a74a..f792e179a 100644 --- a/inst/tinytest/cpp/stack.cpp +++ b/inst/tinytest/cpp/stack.cpp @@ -22,7 +22,6 @@ // [[Rcpp::plugins(cpp11)]] -#define RCPP_NO_MASK_RF_ERROR #include using namespace Rcpp; @@ -57,7 +56,7 @@ SEXP testSendInterrupt() { SEXP maybeThrow(void* data) { bool* fail = (bool*) data; if (*fail) - Rf_error("throw!"); + RCPP_NO_MASK(Rf_error)("throw!"); else return NumericVector::create(42); } diff --git a/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp b/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp index 2e204a7cd..f1f3a13d5 100644 --- a/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp +++ b/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp @@ -43,9 +43,7 @@ RcppExport SEXP _testRcppInterfaceExporter_test_cpp_interface(SEXP xSEXP, SEXP f if (rcpp_isError_gen) { SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen); UNPROTECT(1); - #undef Rf_error - Rf_error("%s", CHAR(rcpp_msgSEXP_gen)); - #include + RCPP_NO_MASK(Rf_error)("%s", CHAR(rcpp_msgSEXP_gen)); } UNPROTECT(1); return rcpp_result_gen; diff --git a/src/attributes.cpp b/src/attributes.cpp index 108aca380..17e4e8a18 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -2953,9 +2953,7 @@ namespace attributes { << " if (rcpp_isError_gen) {" << std::endl << " SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen);" << std::endl << " UNPROTECT(1);" << std::endl - << " #undef Rf_error" << std::endl - << " Rf_error(\"%s\", CHAR(rcpp_msgSEXP_gen));" << std::endl - << " #include " << std::endl + << " RCPP_NO_MASK(Rf_error)(\"%s\", CHAR(rcpp_msgSEXP_gen));" << std::endl << " }" << std::endl << " UNPROTECT(1);" << std::endl << " return rcpp_result_gen;" << std::endl From 627586d94c5377b881d8127a07ce0b517c3b4372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Tue, 28 Oct 2025 16:12:46 +0100 Subject: [PATCH 4/7] protect use of RCPP_NO_MASK in attributes --- inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp | 4 ++++ src/attributes.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp b/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp index f1f3a13d5..946db90f2 100644 --- a/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp +++ b/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp @@ -43,7 +43,11 @@ RcppExport SEXP _testRcppInterfaceExporter_test_cpp_interface(SEXP xSEXP, SEXP f if (rcpp_isError_gen) { SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen); UNPROTECT(1); + #ifdef RCPP_NO_MASK RCPP_NO_MASK(Rf_error)("%s", CHAR(rcpp_msgSEXP_gen)); + #else + Rf_error("%s", CHAR(rcpp_msgSEXP_gen)); + #endif } UNPROTECT(1); return rcpp_result_gen; diff --git a/src/attributes.cpp b/src/attributes.cpp index 17e4e8a18..af865c4c6 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -2953,7 +2953,11 @@ namespace attributes { << " if (rcpp_isError_gen) {" << std::endl << " SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen);" << std::endl << " UNPROTECT(1);" << std::endl + << " #ifdef RCPP_NO_MASK" << std::endl << " RCPP_NO_MASK(Rf_error)(\"%s\", CHAR(rcpp_msgSEXP_gen));" << std::endl + << " #else" << std::endl + << " Rf_error(\"%s\", CHAR(rcpp_msgSEXP_gen));" << std::endl + << " #endif" << std::endl << " }" << std::endl << " UNPROTECT(1);" << std::endl << " return rcpp_result_gen;" << std::endl From 6472d3a51f8e6e2b8dae783a271c44087658a991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Tue, 28 Oct 2025 17:42:48 +0100 Subject: [PATCH 5/7] more informative warning --- inst/include/Rcpp/macros/mask.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/inst/include/Rcpp/macros/mask.h b/inst/include/Rcpp/macros/mask.h index c588898ec..d50fd5f0a 100644 --- a/inst/include/Rcpp/macros/mask.h +++ b/inst/include/Rcpp/macros/mask.h @@ -25,7 +25,9 @@ #ifndef RCPP_NO_MASK_RF_ERROR #define Rf_error(...) \ - _Pragma("GCC warning \"Invalid use of Rf_error, use Rcpp::stop instead\"") \ + _Pragma("GCC warning \"Use of Rf_error() replaced with Rcpp::stop(). Calls \ +to Rf_error() in C++ contexts are unsafe: consider using Rcpp::stop() instead, \ +or define RCPP_NO_MASK_RF_ERROR if this is a false positive.\"") \ Rcpp::stop(__VA_ARGS__) #endif From 40f2fcab643a1ac618b9dd3b0eecc6700772197c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Wed, 29 Oct 2025 09:41:37 +0100 Subject: [PATCH 6/7] use parentheses instead to prevent masking of individual calls --- ChangeLog | 2 +- inst/include/Rcpp/macros/mask.h | 3 --- inst/tinytest/cpp/stack.cpp | 2 +- .../tinytest/testRcppInterfaceExporter/src/RcppExports.cpp | 6 +----- src/attributes.cpp | 7 ++----- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4054a3583..75232bd40 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,7 +12,7 @@ * inst/include/Rcpp/macros/mask.h: Mask Rf_error with Rcpp::stop with a warning at compilation time, unless RCPP_NO_MASK_RF_ERROR is defined * inst/include/RcppCommon.h: Include the previous file in the last place - * src/attributes.cpp: Use RCPP_NO_MASK to protect call to Rf_error + * src/attributes.cpp: Use parentheses to protect call to Rf_error * inst/tinytest/cpp/stack.cpp: Idem * inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp: Idem diff --git a/inst/include/Rcpp/macros/mask.h b/inst/include/Rcpp/macros/mask.h index d50fd5f0a..a1ff50df2 100644 --- a/inst/include/Rcpp/macros/mask.h +++ b/inst/include/Rcpp/macros/mask.h @@ -20,9 +20,6 @@ #ifndef Rcpp_macros_mask_h #define Rcpp_macros_mask_h -#define RCPP_NO_MASK_EMPTY() -#define RCPP_NO_MASK(id) id RCPP_NO_MASK_EMPTY() - #ifndef RCPP_NO_MASK_RF_ERROR #define Rf_error(...) \ _Pragma("GCC warning \"Use of Rf_error() replaced with Rcpp::stop(). Calls \ diff --git a/inst/tinytest/cpp/stack.cpp b/inst/tinytest/cpp/stack.cpp index f792e179a..8bb7d52df 100644 --- a/inst/tinytest/cpp/stack.cpp +++ b/inst/tinytest/cpp/stack.cpp @@ -56,7 +56,7 @@ SEXP testSendInterrupt() { SEXP maybeThrow(void* data) { bool* fail = (bool*) data; if (*fail) - RCPP_NO_MASK(Rf_error)("throw!"); + (Rf_error)("throw!"); // prevent masking else return NumericVector::create(42); } diff --git a/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp b/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp index 946db90f2..4b018f29f 100644 --- a/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp +++ b/inst/tinytest/testRcppInterfaceExporter/src/RcppExports.cpp @@ -43,11 +43,7 @@ RcppExport SEXP _testRcppInterfaceExporter_test_cpp_interface(SEXP xSEXP, SEXP f if (rcpp_isError_gen) { SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen); UNPROTECT(1); - #ifdef RCPP_NO_MASK - RCPP_NO_MASK(Rf_error)("%s", CHAR(rcpp_msgSEXP_gen)); - #else - Rf_error("%s", CHAR(rcpp_msgSEXP_gen)); - #endif + (Rf_error)("%s", CHAR(rcpp_msgSEXP_gen)); } UNPROTECT(1); return rcpp_result_gen; diff --git a/src/attributes.cpp b/src/attributes.cpp index af865c4c6..5240e3627 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -2953,11 +2953,8 @@ namespace attributes { << " if (rcpp_isError_gen) {" << std::endl << " SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen);" << std::endl << " UNPROTECT(1);" << std::endl - << " #ifdef RCPP_NO_MASK" << std::endl - << " RCPP_NO_MASK(Rf_error)(\"%s\", CHAR(rcpp_msgSEXP_gen));" << std::endl - << " #else" << std::endl - << " Rf_error(\"%s\", CHAR(rcpp_msgSEXP_gen));" << std::endl - << " #endif" << std::endl + // Parentheses to prevent masking + << " (Rf_error)(\"%s\", CHAR(rcpp_msgSEXP_gen));" << std::endl << " }" << std::endl << " UNPROTECT(1);" << std::endl << " return rcpp_result_gen;" << std::endl From 9f00e56cd71a288265705ef28a790c013b2e59a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Wed, 5 Nov 2025 10:15:16 +0100 Subject: [PATCH 7/7] add newline --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 75232bd40..fa258238a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ * inst/include/Rcpp/hash/IndexHash.h: Normalize values for all comparisons * inst/include/Rcpp/hash/SelfHash.h: Idem * inst/tinytest/test_sugar.R: Add test for signed zeroes + 2025-10-28 Iñaki Ucar * inst/include/Rcpp/macros/mask.h: Mask Rf_error with Rcpp::stop with