Skip to content

Commit 8088d21

Browse files
authored
Merge branch 'master' into bugfix/sugar-soundness
2 parents 9682f05 + 96a8b95 commit 8088d21

5 files changed

Lines changed: 59 additions & 14 deletions

File tree

‎ChangeLog‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
* vignettes/rmd/Rcpp-sugar.Rmd: Document when the lengths are checked
1212
* inst/NEWS.Rd: Idem
1313

14-
2026-10-09 Kevin Ushey <kevinushey@gmail.com>
15-
1614
* inst/include/Rcpp/sugar/functions/lapply.h: Index the operand through
1715
VectorBase so vectors yield their stored type rather than a proxy
1816
* inst/tinytest/cpp/sugar.cpp: Add a test
@@ -53,6 +51,23 @@
5351
* vignettes/rmd/Rcpp-sugar.Rmd: Document operand lengths, storing
5452
expressions, and assignment
5553

54+
2026-10-12 Kevin Ushey <kevinushey@gmail.com>
55+
56+
* inst/include/Rcpp/vector/traits.h: Update copyright
57+
* inst/include/Rcpp/vector/Vector.h: Idem
58+
* inst/include/Rcpp/vector/Matrix.h: Idem
59+
60+
2026-09-30 Kevin Ushey <kevinushey@gmail.com>
61+
62+
* inst/include/Rcpp/vector/traits.h: Move the out-of-bounds warning into
63+
a cold, out-of-line helper; add get_size() to the vector caches
64+
* inst/include/Rcpp/vector/Vector.h: Return the length cached by
65+
r_vector_cache from size() and length() rather than calling
66+
Rf_xlength() each time
67+
* inst/include/Rcpp/vector/Matrix.h: Derive ncol() from the cached
68+
length and nrow() instead of reading the dim attribute; document that
69+
no data members may be added, as the layout is shared across packages
70+
5671
2026-09-22 Iñaki Ucar <iucar@fedoraproject.org>
5772

5873
* inst/include/Rcpp/sugar/matrix/col.h: Fix Col constructor using ncol()

‎inst/NEWS.Rd‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@
4343
\item The sugar operators \code{!}, \code{&&} and \code{||} on single
4444
logical results such as \code{all()} now compile, and follow R's
4545
handling of \code{NA} (Kevin in \ghpr{1513})
46+
\item Bounds-checked element access is faster, \code{size()} of
47+
atomic vectors uses the cached length, and \code{ncol()} no longer
48+
reads the \code{dim} attribute; object layouts are unchanged, as
49+
packages such as \pkg{rust} and \pkg{revdbayes} exchange Rcpp
50+
objects across shared libraries (Kevin in \ghpr{1511} closing
51+
\ghit{1510})
4652
}
4753
\item Changes in Rcpp Documentation:
4854
\itemize{

‎inst/include/Rcpp/vector/Matrix.h‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Matrix.h: Rcpp R/C++ interface class library -- matrices
44
//
55
// Copyright (C) 2010 - 2025 Dirk Eddelbuettel and Romain Francois
6-
// Copyright (C) 2026 Dirk Eddelbuettel, Romain Francois and Iñaki Ucar
6+
// Copyright (C) 2026 Dirk Eddelbuettel, Romain Francois, Kevin Ushey and Iñaki Ucar
77
//
88
// This file is part of Rcpp.
99
//
@@ -27,6 +27,10 @@ namespace Rcpp{
2727

2828
template <int RTYPE, template <class> class StoragePolicy = PreserveStorage >
2929
class Matrix : public Vector<RTYPE, StoragePolicy>, public MatrixBase<RTYPE, true, Matrix<RTYPE,StoragePolicy> > {
30+
// The only data member. Packages pass Rcpp objects by reference across
31+
// shared-library boundaries (e.g. rust calls into revdbayes through XPtr
32+
// function pointers), and the two sides may have been compiled against
33+
// different Rcpp versions. Adding a member changes the layout they share.
3034
int nrows ;
3135

3236
public:
@@ -92,14 +96,17 @@ class Matrix : public Vector<RTYPE, StoragePolicy>, public MatrixBase<RTYPE, tru
9296

9397
explicit Matrix( const no_init_matrix& obj) : VECTOR(Rf_allocMatrix(RTYPE, obj.nrow(), obj.ncol())), nrows(obj.nrow()) {}
9498

99+
// A matrix has exactly nrow * ncol elements, so the column count follows
100+
// from the (cached) length without reading the dim attribute. Only an
101+
// empty matrix, which may still have columns, needs the attribute.
95102
inline int ncol() const {
96-
return VECTOR::dims()[1];
103+
return nrows == 0 ? VECTOR::dims()[1] : static_cast<int>( VECTOR::size() / nrows ) ;
97104
}
98105
inline int nrow() const {
99106
return nrows ;
100107
}
101108
inline int cols() const {
102-
return VECTOR::dims()[1];
109+
return ncol() ;
103110
}
104111
inline int rows() const {
105112
return nrows ;

‎inst/include/Rcpp/vector/Vector.h‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Vector.h: Rcpp R/C++ interface class library -- vectors
22
//
33
// Copyright (C) 2010 - 2025 Dirk Eddelbuettel and Romain Francois
4-
// Copyright (C) 2026 Dirk Eddelbuettel, Romain Francois and Iñaki Ucar
4+
// Copyright (C) 2026 Dirk Eddelbuettel, Romain Francois, Kevin Ushey and Iñaki Ucar
55
//
66
// This file is part of Rcpp.
77
//
@@ -263,17 +263,17 @@ class Vector :
263263
#endif
264264

265265
/**
266-
* the length of the vector, uses Rf_xlength
266+
* the length of the vector, as cached by update()
267267
*/
268268
inline R_xlen_t length() const {
269-
return ::Rf_xlength( Storage::get__() ) ;
269+
return cache.get_size() ;
270270
}
271271

272272
/**
273273
* alias of length
274274
*/
275275
inline R_xlen_t size() const {
276-
return ::Rf_xlength( Storage::get__() ) ;
276+
return cache.get_size() ;
277277
}
278278

279279
/**

‎inst/include/Rcpp/vector/traits.h‎

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11

22
// traits.h: Rcpp R/C++ interface class library -- support traits for vector
33
//
4-
// Copyright (C) 2010 - 2026 Dirk Eddelbuettel and Romain Francois
4+
// Copyright (C) 2010 - 2025 Dirk Eddelbuettel and Romain Francois
5+
// Copyright (C) 2026 Dirk Eddelbuettel, Romain Francois and Kevin Ushey
56
//
67
// This file is part of Rcpp.
78
//
@@ -24,6 +25,16 @@
2425
namespace Rcpp{
2526
namespace traits{
2627

28+
// Kept out of line and marked cold so that the (almost never taken)
29+
// warning path doesn't bloat element access and block optimization of
30+
// hot loops, e.g. in sugar expressions.
31+
#if defined(__GNUC__)
32+
__attribute__((noinline, cold))
33+
#endif
34+
inline void warn_index_out_of_bounds(R_xlen_t i, R_xlen_t size) {
35+
warning("subscript out of bounds (index %s >= vector size %s)", i, size); // #nocov
36+
}
37+
2738
template <int RTYPE, template <class> class StoragePolicy = PreserveStorage >
2839
class r_vector_cache{
2940
public:
@@ -38,11 +49,12 @@ namespace traits{
3849

3950
inline void update( const VECTOR& v ) {
4051
start = ::Rcpp::internal::r_vector_start<RTYPE>(v) ;
41-
size = v.size();
52+
size = ::Rf_xlength(v.get__());
4253
}
4354

4455
inline iterator get() const { return start; }
4556
inline const_iterator get_const() const { return start; }
57+
inline R_xlen_t get_size() const { return size; }
4658

4759
inline proxy ref() { check_index(0); return start[0] ;}
4860
inline proxy ref(R_xlen_t i) { check_index(i); return start[i] ; }
@@ -55,7 +67,7 @@ namespace traits{
5567
void check_index(R_xlen_t i) const {
5668
#ifndef RCPP_NO_BOUNDS_CHECK
5769
if (i >= size) {
58-
warning("subscript out of bounds (index %s >= vector size %s)", i, size); // #nocov
70+
warn_index_out_of_bounds(i, size); // #nocov
5971
}
6072
#endif
6173
}
@@ -80,6 +92,10 @@ namespace traits{
8092
}
8193
inline iterator get() const { return iterator( proxy(*p, 0 ) ) ;}
8294
inline const_iterator get_const() const { return const_iterator( const_proxy(*p, 0) ) ; }
95+
// Not cached: a size member here would change the layout of List and
96+
// CharacterVector, which packages pass by reference across
97+
// shared-library boundaries (see the note in Matrix.h).
98+
inline R_xlen_t get_size() const { return ::Rf_xlength(p->get__()); }
8399

84100
inline proxy ref() { check_index(0); return proxy(*p,0) ; }
85101
inline proxy ref(R_xlen_t i) { check_index(i); return proxy(*p,i);}
@@ -92,8 +108,9 @@ namespace traits{
92108

93109
void check_index(R_xlen_t i) const {
94110
#ifndef RCPP_NO_BOUNDS_CHECK
95-
if (i >= p->size()) {
96-
warning("subscript out of bounds (index %s >= vector size %s)", i, p->size()); // #nocov
111+
R_xlen_t size = get_size() ;
112+
if (i >= size) {
113+
warn_index_out_of_bounds(i, size); // #nocov
97114
}
98115
#endif
99116
}

0 commit comments

Comments
 (0)