Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
2026-09-30 Kevin Ushey <[email protected]>

* inst/include/Rcpp/traits/sugar_operand.h: New trait for how sugar
expressions hold their operands: vectors by reference, and nested
expressions and views by value, so stored expressions don't dangle
* inst/include/Rcpp/traits/is_elementwise.h: New trait for whether a
sugar expression can be written in place into storage it may read from
* inst/include/Rcpp/sugar/tools/check_sizes.h: New helpers checking that
sugar operands, and vectors assigned into views, have matching lengths
* inst/include/RcppCommon.h: Include the above
* inst/include/Rcpp/sugar/sugar_forward.h: Idem
* inst/include/Rcpp/sugar/: Hold operands via sugar_operand, declare
elementwise operands, and check operand lengths
* inst/include/Rcpp/stats/dpq/dpq.h: Idem
* inst/include/Rcpp/sugar/block/SugarBlock_3.h: Also fix the type of the
third operand
* inst/include/Rcpp/sugar/logical/and.h: Fix operator&& on two single
logical results, hold their operands by value, and make NA && FALSE
return FALSE
* inst/include/Rcpp/sugar/logical/or.h: Idem for operator||, making
NA || TRUE return TRUE, and fix the base class of Or_SingleLogicalResult_bool
* inst/include/Rcpp/sugar/logical/not.h: Hold the operand of operator!
by value
* inst/include/Rcpp/sugar/logical/SingleLogicalResult.h: Add get_ref()
* inst/include/Rcpp/vector/Vector.h: Evaluate sugar expressions that
aren't elementwise before assigning them in place
* inst/include/Rcpp/vector/RangeIndexer.h: Idem, and check lengths
* inst/include/Rcpp/vector/MatrixColumn.h: Idem
* inst/include/Rcpp/vector/MatrixRow.h: Idem
* inst/include/Rcpp/vector/MatrixBase.h: Add a const get_ref()
* inst/tinytest/cpp/sugar_expressions.cpp: New tests
* inst/tinytest/test_sugar.R: Idem
* vignettes/rmd/Rcpp-sugar.Rmd: Document operand lengths, storing
expressions, and assignment

2026-09-22 Iñaki Ucar <[email protected]>

* inst/include/Rcpp/sugar/matrix/col.h: Fix Col constructor using ncol()
Expand Down
12 changes: 12 additions & 0 deletions inst/NEWS.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@
warnings (Iñaki in \ghpr{1508} closing \ghit{1497})
\item The \code{col} constructor now uses \code{nrow} in an
initialization (Iñaki in \ghpr{1509})
\item Sugar expressions hold nested expressions by value so they can be
stored, evaluate expressions that may read the target before assigning
them in place, and signal an error for operands of different lengths
(Kevin in \ghpr{1513} closing \ghit{1512}). Same-length assignment
of an expression that is not elementwise, such as \code{x = rev(x)},
now goes through a temporary vector.
\item Assignment between overlapping subsets of the same vector, as in
\code{x[i] = x[j]}, reads the source elements before writing (Kevin in
\ghpr{1513})
\item The sugar operators \code{!}, \code{&&} and \code{||} on single
logical results such as \code{all()} now compile, and follow R's
handling of \code{NA} (Kevin in \ghpr{1513})
}
\item Changes in Rcpp Documentation:
\itemize{
Expand Down
60 changes: 36 additions & 24 deletions inst/include/Rcpp/stats/dpq/dpq.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ class D0 : public Rcpp::VectorBase< REALSXP, NA, D0<RTYPE,NA,T> > {
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

D0( FunPtr ptr_, const VEC_TYPE& vec_, bool log_ ) :
ptr(ptr_), vec(vec_), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], log );
Expand All @@ -47,7 +48,7 @@ class D0 : public Rcpp::VectorBase< REALSXP, NA, D0<RTYPE,NA,T> > {

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
int log;
};

Expand All @@ -56,9 +57,10 @@ class D1 : public Rcpp::VectorBase< REALSXP, NA, D1<RTYPE,NA,T> > {
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,double,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

D1( FunPtr ptr_, const VEC_TYPE& vec_, double p0_ , bool log_) :
ptr(ptr_), vec(vec_), p0(p0_), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), p0(p0_), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], p0, log );
Expand All @@ -68,7 +70,7 @@ class D1 : public Rcpp::VectorBase< REALSXP, NA, D1<RTYPE,NA,T> > {

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
double p0 ;
int log;
} ;
Expand All @@ -78,9 +80,10 @@ class D2 : public Rcpp::VectorBase< REALSXP, NA, D2<RTYPE,NA,T> > {
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,double,double,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

D2( FunPtr ptr_, const VEC_TYPE& vec_, double p0_, double p1_ , bool log_) :
ptr(ptr_), vec(vec_), p0(p0_), p1(p1_), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), p0(p0_), p1(p1_), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], p0, p1, log );
Expand All @@ -90,7 +93,7 @@ class D2 : public Rcpp::VectorBase< REALSXP, NA, D2<RTYPE,NA,T> > {

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
double p0, p1 ;
int log;
} ;
Expand All @@ -100,9 +103,10 @@ class D3 : public Rcpp::VectorBase< REALSXP, NA, D3<RTYPE,NA,T> > {
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,double,double,double,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

D3( FunPtr ptr_, const VEC_TYPE& vec_, double p0_, double p1_, double p2_ , bool log_ ) :
ptr(ptr_), vec(vec_), p0(p0_), p1(p1_), p2(p2_), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), p0(p0_), p1(p1_), p2(p2_), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], p0, p1, p2, log );
Expand All @@ -112,7 +116,7 @@ class D3 : public Rcpp::VectorBase< REALSXP, NA, D3<RTYPE,NA,T> > {

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
double p0, p1, p2 ;
int log;
} ;
Expand All @@ -125,10 +129,11 @@ class P0 : public Rcpp::VectorBase< REALSXP, NA, P0<RTYPE,NA,T> >{
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,int,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

P0( FunPtr ptr_, const VEC_TYPE& vec_,
bool lower_tail = true, bool log_ = false ) :
ptr(ptr_), vec(vec_), lower(lower_tail), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), lower(lower_tail), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], lower, log );
Expand All @@ -138,7 +143,7 @@ class P0 : public Rcpp::VectorBase< REALSXP, NA, P0<RTYPE,NA,T> >{

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
int lower, log;

};
Expand All @@ -149,10 +154,11 @@ class P1 : public Rcpp::VectorBase< REALSXP, NA, P1<RTYPE,NA,T> >{
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,double,int,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

P1( FunPtr ptr_, const VEC_TYPE& vec_, double p0_,
bool lower_tail = true, bool log_ = false ) :
ptr(ptr_), vec(vec_), p0(p0_), lower(lower_tail), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), p0(p0_), lower(lower_tail), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], p0, lower, log );
Expand All @@ -162,7 +168,7 @@ class P1 : public Rcpp::VectorBase< REALSXP, NA, P1<RTYPE,NA,T> >{

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
double p0 ;
int lower, log;

Expand All @@ -174,10 +180,11 @@ class P2 : public Rcpp::VectorBase< REALSXP, NA, P2<RTYPE,NA,T> >{
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,double,double,int,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

P2( FunPtr ptr_, const VEC_TYPE& vec_, double p0_, double p1_,
bool lower_tail = true, bool log_ = false ) :
ptr(ptr_), vec(vec_), p0(p0_), p1(p1_), lower(lower_tail), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), p0(p0_), p1(p1_), lower(lower_tail), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], p0, p1, lower, log );
Expand All @@ -187,7 +194,7 @@ class P2 : public Rcpp::VectorBase< REALSXP, NA, P2<RTYPE,NA,T> >{

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
double p0, p1 ;
int lower, log;
};
Expand All @@ -197,10 +204,11 @@ class P3 : public Rcpp::VectorBase< REALSXP, NA, P3<RTYPE,NA,T> >{
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,double,double,double,int,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

P3( FunPtr ptr_, const VEC_TYPE& vec_, double p0_, double p1_, double p2_,
bool lower_tail = true, bool log_ = false ) :
ptr(ptr_), vec(vec_), p0(p0_), p1(p1_), p2(p2_), lower(lower_tail), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), p0(p0_), p1(p1_), p2(p2_), lower(lower_tail), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], p0, p1, p2, lower, log );
Expand All @@ -210,7 +218,7 @@ class P3 : public Rcpp::VectorBase< REALSXP, NA, P3<RTYPE,NA,T> >{

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
double p0, p1,p2 ;
int lower, log;

Expand All @@ -223,10 +231,11 @@ class Q0 : public Rcpp::VectorBase< REALSXP, NA, Q0<RTYPE,NA,T> >{
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,int,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

Q0( FunPtr ptr_, const VEC_TYPE& vec_,
bool lower_tail = true, bool log_ = false ) :
ptr(ptr_), vec(vec_), lower(lower_tail), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), lower(lower_tail), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], lower, log );
Expand All @@ -236,7 +245,7 @@ class Q0 : public Rcpp::VectorBase< REALSXP, NA, Q0<RTYPE,NA,T> >{

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
int lower, log;

};
Expand All @@ -246,10 +255,11 @@ class Q1 : public Rcpp::VectorBase< REALSXP, NA, Q1<RTYPE,NA,T> >{
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,double,int,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

Q1( FunPtr ptr_, const VEC_TYPE& vec_, double p0_,
bool lower_tail = true, bool log_ = false ) :
ptr(ptr_), vec(vec_), p0(p0_), lower(lower_tail), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), p0(p0_), lower(lower_tail), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], p0, lower, log );
Expand All @@ -259,7 +269,7 @@ class Q1 : public Rcpp::VectorBase< REALSXP, NA, Q1<RTYPE,NA,T> >{

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
double p0 ;
int lower, log;

Expand All @@ -270,10 +280,11 @@ class Q2 : public Rcpp::VectorBase< REALSXP, NA, Q2<RTYPE,NA,T> >{
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,double,double,int,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

Q2( FunPtr ptr_, const VEC_TYPE& vec_, double p0_, double p1_,
bool lower_tail = true, bool log_ = false ) :
ptr(ptr_), vec(vec_), p0(p0_), p1(p1_), lower(lower_tail), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), p0(p0_), p1(p1_), lower(lower_tail), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], p0, p1, lower, log );
Expand All @@ -283,7 +294,7 @@ class Q2 : public Rcpp::VectorBase< REALSXP, NA, Q2<RTYPE,NA,T> >{

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
double p0, p1 ;
int lower, log;

Expand All @@ -294,10 +305,11 @@ class Q3 : public Rcpp::VectorBase< REALSXP, NA, Q3<RTYPE,NA,T> >{
public:
typedef typename Rcpp::VectorBase<RTYPE,NA,T> VEC_TYPE ;
typedef double (*FunPtr)(double,double,double,double,int,int) ;
typedef Rcpp::traits::elementwise_operands< VEC_TYPE > rcpp_elementwise ;

Q3( FunPtr ptr_, const VEC_TYPE& vec_, double p0_, double p1_, double p2_,
bool lower_tail = true, bool log_ = false ) :
ptr(ptr_), vec(vec_), p0(p0_), p1(p1_), p2(p2_), lower(lower_tail), log(log_) {}
ptr(ptr_), vec(vec_.get_ref()), p0(p0_), p1(p1_), p2(p2_), lower(lower_tail), log(log_) {}

inline double operator[]( R_xlen_t i) const {
return ptr( vec[i], p0, p1, p2, lower, log );
Expand All @@ -307,7 +319,7 @@ class Q3 : public Rcpp::VectorBase< REALSXP, NA, Q3<RTYPE,NA,T> >{

private:
FunPtr ptr ;
const VEC_TYPE& vec;
typename Rcpp::traits::sugar_operand< VEC_TYPE >::type vec;
double p0, p1, p2 ;
int lower, log;
};
Expand Down
2 changes: 2 additions & 0 deletions inst/include/Rcpp/sugar/Range.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ namespace Rcpp{

class Range : public VectorBase<INTSXP,false, Range >{
public:
typedef traits::elementwise_operands<> rcpp_elementwise ;

Range( R_xlen_t start_, R_xlen_t end__ ) : start(start_), end_(end__){
if( start_ > end__ ){
throw std::range_error( "upper value must be greater than lower value" ) ;
Expand Down
5 changes: 3 additions & 2 deletions inst/include/Rcpp/sugar/block/SugarBlock_1.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ template <bool NA, typename RESULT_TYPE, typename U1, typename T1>
class SugarBlock_1 : public Rcpp::VectorBase< Rcpp::traits::r_sexptype_traits<RESULT_TYPE>::rtype , NA, SugarBlock_1<NA,RESULT_TYPE,U1,T1> > {
public:
typedef RESULT_TYPE (*FunPtr)(U1) ;
SugarBlock_1( FunPtr ptr_, const T1 & vec_) : ptr(ptr_), vec(vec_){}
typedef Rcpp::traits::elementwise_operands< T1 > rcpp_elementwise ;
SugarBlock_1( FunPtr ptr_, const T1 & vec_) : ptr(ptr_), vec(vec_.get_ref()){}

inline RESULT_TYPE operator[]( R_xlen_t i) const {
return ptr( vec[i] ) ;
Expand All @@ -38,7 +39,7 @@ class SugarBlock_1 : public Rcpp::VectorBase< Rcpp::traits::r_sexptype_traits<RE

private:
FunPtr ptr ;
const T1& vec ;
typename Rcpp::traits::sugar_operand< T1 >::type vec ;
};

} // sugar
Expand Down
Loading
Loading