Skip to content

Commit eca888c

Browse files
authored
Merge branch 'master' into todoRename
2 parents dacb594 + 36bd74a commit eca888c

File tree

11 files changed

+149
-124
lines changed

11 files changed

+149
-124
lines changed

NEWS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
8888
8. A data.table with a column of class `vctrs_list_of` (from package {vctrs}) prints as expected, [#5948](https://github.com/Rdatatable/data.table/issues/5948). Before, they could be printed messily, e.g. printing every entry in a nested data.frame. Thanks @jesse-smith for the report, @DavisVaughan and @r2evans for contributing, and @MichaelChirico for the PR.
8989
90-
9. Fixed incorrect sorting of merges where the first column of a key is a factor with non-`sort()`-ed levels (e.g. `factor(1:2, 2:1)` and it is joined to a character column, [#5361](https://github.com/Rdatatable/data.table/issues/5361). Thanks to @gbrunick for the report and Benjamin Schwendinger for the fix.
90+
9. Fixed incorrect sorting of merges where the first column of a key is a factor with non-`sort()`-ed levels (e.g. `factor(1:2, 2:1)` and it is joined to a character column, [#5361](https://github.com/Rdatatable/data.table/issues/5361). Thanks to @gbrunick for the report, Benjamin Schwendinger for the fix, and @MichaelChirico for a follow-up fix caught by revdep testing.
9191
9292
10. Spurious warnings from internal code in `cube()`, `rollup()`, and `groupingsets()` are no longer surfaced to the caller, [#6964](https://github.com/Rdatatable/data.table/issues/6964). Thanks @ferenci-tamas for the report and @venom1204 for the fix.
9393
@@ -122,6 +122,8 @@
122122
123123
5. A GitHub Actions workflow is now in place to warn the entire maintainer team, as well as any contributor following the GitHub repository, when the package is at risk of archival on CRAN [#7008](https://github.com/Rdatatable/data.table/issues/7008). Thanks @tdhock for the original report and @Bisaloo and @TysonStanley for the fix.
124124
125+
6. Using a double vector in `set()`'s `i=` and/or `j=` no longer throws a warning about preferring integer, [#6594](https://github.com/Rdatatable/data.table/issues/6594). While it may improve efficiency to use integer, there's no guarantee it's an improvement and the difference is likely to be minimal. The coercion will still be reported under `datatable.verbose=TRUE`. For package/production use cases, static analyzers such as `lintr::implicit_integer_linter()` can also report when numeric literals should be rewritten as integer literals.
126+
125127
## data.table [v1.17.8](https://github.com/Rdatatable/data.table/milestone/41) (6 July 2025)
126128

127129
1. Internal functions used to signal errors are now marked as non-returning, silencing a compiler warning about potentially unchecked allocation failure. Thanks to Prof. Brian D. Ripley for the report and @aitap for the fix, [#7070](https://github.com/Rdatatable/data.table/pull/7070).

R/data.table.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,8 +2047,9 @@ replace_dot_alias = function(e) {
20472047
if (!.Call(CisOrderedSubset, irows, nrow(x)))
20482048
return(NULL)
20492049

2050-
# see #1010. don't set key when i has no key, but irows is ordered and !roll
2051-
if (roll && length(irows) != 1L)
2050+
# see #1010. don't set key when i has no key, but irows is ordered and isFALSE(roll)
2051+
# NB: roll could still be a string like 'nearest', #7146
2052+
if (!is.character(roll) && roll && length(irows) != 1L)
20522053
return(NULL)
20532054

20542055
new_key <- head(x_key, key_length)

inst/tests/tests.Rraw

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,9 +1621,9 @@ test(514, x %chin% y, x %in% y)
16211621

16221622
# Test new function set() in v1.8.0
16231623
DT = data.table(a=1:3,b=4:6)
1624-
test(515, set(DT,2,1,3), data.table(a=c(1L,3L,3L),b=4:6), warning=c("Coerced i from numeric to integer","Coerced j from numeric to integer"))
1624+
test(515, set(DT,2,1,3), data.table(a=c(1L,3L,3L),b=4:6))
16251625
test(516, set(DT,"2",1,3), error="i is type 'character'")
1626-
test(517, set(DT,2L,1,3), DT, warning="Coerced j")
1626+
test(517, options=c(datatable.verbose=TRUE), set(DT,2,1,3), DT, output="Coerced i.*Coerced j")
16271627
# FR #2551 implemented - removed warning from 518
16281628
# test(518, set(DT,2L,1L,3), DT, warning="Coerced 'double' RHS to 'integer'")
16291629
test(518, set(DT,2L,1L,3), DT)
@@ -7131,6 +7131,11 @@ dt3 <- merge(dt1, dt2, by="x")
71317131
test(1483.81, key(dt3), "x")
71327132
test(1483.82, nrow(dt3[x %in% "c", ]), 2L)
71337133

7134+
# avoid regression of roll='nearest' join of int->double, #7146
7135+
DT1 <- data.table(a=1L, key='a')
7136+
DT2 <- data.table(a=2.0, key='a')
7137+
test(1483.83, DT1[DT2, roll='nearest'], data.table(a=2L, key='a'))
7138+
71347139
# NULL items should be removed when making data.table from list, #842
71357140
# Original fix for #842 added a branch in as.data.table.list() using point()
71367141
# Then PR#3471 moved logic from data.table() into as.data.table.list() and now removes NULL items up front, so longer need for the branch

src/assign.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ SEXP setdt_nrows(SEXP x)
219219
if (Rf_inherits(xi, "POSIXlt")) {
220220
error(_("Column %d has class 'POSIXlt'. Please convert it to POSIXct (using as.POSIXct) and run setDT() again. We do not recommend the use of POSIXlt at all because it uses 40 bytes to store one date."), i+1);
221221
}
222-
SEXP dim_xi = getAttrib(xi, R_DimSymbol);
222+
SEXP dim_xi = PROTECT(getAttrib(xi, R_DimSymbol));
223223
R_len_t len_xi, n_dim = length(dim_xi);
224224
if (n_dim) {
225225
if (test_matrix_cols && n_dim > 1) {
@@ -230,6 +230,7 @@ SEXP setdt_nrows(SEXP x)
230230
} else {
231231
len_xi = length(xi);
232232
}
233+
UNPROTECT(1);
233234
if (!base_length) {
234235
base_length = len_xi;
235236
} else if (len_xi != base_length) {
@@ -364,7 +365,8 @@ SEXP assign(SEXP dt, SEXP rows, SEXP cols, SEXP newcolnames, SEXP values)
364365
} else {
365366
if (isReal(rows)) {
366367
rows = PROTECT(coerceVector(rows, INTSXP)); protecti++;
367-
warning(_("Coerced i from numeric to integer. Please pass integer for efficiency; e.g., 2L rather than 2"));
368+
if (verbose)
369+
Rprintf(_("Coerced %s from numeric to integer. Passing integer directly may be more efficient, e.g., 2L rather than 2"), "i");
368370
}
369371
if (!isInteger(rows))
370372
error(_("i is type '%s'. Must be integer, or numeric is coerced with warning. If i is a logical subset, simply wrap with which(), and take the which() outside the loop if possible for efficiency."), type2char(TYPEOF(rows)));
@@ -417,7 +419,8 @@ SEXP assign(SEXP dt, SEXP rows, SEXP cols, SEXP newcolnames, SEXP values)
417419
} else {
418420
if (isReal(cols)) {
419421
cols = PROTECT(coerceVector(cols, INTSXP)); protecti++;
420-
warning(_("Coerced j from numeric to integer. Please pass integer for efficiency; e.g., 2L rather than 2"));
422+
if (verbose)
423+
Rprintf(_("Coerced %s from numeric to integer. Passing integer directly may be more efficient, e.g., 2L rather than 2"), "j");
421424
}
422425
if (!isInteger(cols))
423426
error(_("j is type '%s'. Must be integer, character, or numeric is coerced with warning."), type2char(TYPEOF(cols)));

src/inrange.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
#include "data.table.h"
22
#include <Rdefines.h>
33

4-
5-
SEXP inrange(SEXP ansArg, SEXP xoArg, SEXP startsArg, SEXP lenArg) {
6-
4+
SEXP inrange(SEXP ansArg, SEXP xoArg, SEXP startsArg, SEXP lenArg)
5+
{
76
int *ans = INTEGER(ansArg);
87
const int *xo = INTEGER(xoArg);
98
const int *starts = INTEGER(startsArg), *len = INTEGER(lenArg);
109
const int n = length(startsArg), nxo = length(xoArg);
11-
for (int i=0; i<n; ++i) {
12-
for (int j=starts[i]-1; j<starts[i]-1+len[i]; ++j) {
13-
ans[nxo ? xo[j]-1 : j] = 1;
10+
for (int i = 0; i < n; i++) {
11+
for (int j = starts[i] - 1; j < starts[i] - 1 + len[i]; j++) {
12+
ans[nxo ? xo[j] - 1 : j] = 1;
1413
}
1514
}
1615
// old complicated logic which is only really useful when matches
@@ -32,5 +31,5 @@ SEXP inrange(SEXP ansArg, SEXP xoArg, SEXP startsArg, SEXP lenArg) {
3231
// // Rprintf(_("Moved to %d, start=%d, end=%d\n"), i, ss, ee);
3332
// for (int j=ss; j<=ee; j++) ans[nxo ? xo[j]-1 : j] = 1;
3433
// }
35-
return (R_NilValue);
34+
return R_NilValue;
3635
}

src/negate.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
#include "data.table.h"
22

3-
void negateByRef(SEXP x) {
3+
void negateByRef(SEXP x)
4+
{
45
if(TYPEOF(x) != LGLSXP) {
56
error(_("not logical or integer vector")); // # nocov
67
}
78
const int n = length(x);
89
int *ansd = LOGICAL(x);
9-
for(int i=0; i<n; ++i) {
10+
for(int i = 0; i < n; i++) {
1011
ansd[i] ^= (ansd[i] != NA_LOGICAL); // invert true/false but leave NA alone
1112
}
1213
}
1314

14-
15-
SEXP notchin(SEXP x, SEXP table) {
15+
SEXP notchin(SEXP x, SEXP table)
16+
{
1617
// see discussion in PR#4931
1718
SEXP result = PROTECT(chin(x, table));
1819
negateByRef(result); // save memory

src/nqrecreateindices.c

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
// todo: Add oxygen style comments and cleanup var names.
44
// See other TODOs inside the function.
5-
SEXP nqRecreateIndices(SEXP xo, SEXP len, SEXP indices, SEXP nArg, SEXP nomatch) {
6-
7-
R_len_t n=INTEGER(nArg)[0], xn=length(xo);
5+
SEXP nqRecreateIndices(SEXP xo, SEXP len, SEXP indices, SEXP nArg, SEXP nomatch)
6+
{
7+
R_len_t n = INTEGER(nArg)[0], xn = length(xo);
88
SEXP ans, newstarts, newlen;
99
ans = PROTECT(allocVector(VECSXP, 2));
1010
SET_VECTOR_ELT(ans, 0, (newstarts = allocVector(INTSXP, n)));
@@ -17,26 +17,28 @@ SEXP nqRecreateIndices(SEXP xo, SEXP len, SEXP indices, SEXP nArg, SEXP nomatch)
1717
const int inomatch = isNull(nomatch) ? 0 : INTEGER(nomatch)[0];
1818
int *inewstarts = INTEGER(newstarts);
1919

20-
for (int i=0; i<n; ++i) inewlen[i] = 0;
20+
for (int i = 0; i < n; i++) inewlen[i] = 0;
21+
2122
// simplifying logic ... also fixes #2275
22-
for (int i=0; i<length(indices); ++i) {
23-
inewlen[iindices[i]-1] += ilen[i];
23+
for (int i = 0; i < length(indices); i++) {
24+
inewlen[iindices[i] - 1] += ilen[i];
2425
}
2526
// fix for #2360, rewriting the for-loop from before
27+
2628
// todo: revisit to see if this be simplified further when I've some time.
27-
R_len_t j=0, tmp=0;
28-
for (int i=0; i<n; ++i) {
29-
if (j>=xn || ixo[j]<=0) {
29+
R_len_t j = 0, tmp = 0;
30+
for (int i = 0; i < n; i++) {
31+
if (j >= xn || ixo[j] <= 0) {
3032
// NA_integer_ = INT_MIN is checked in init.c
3133
// j >= xn needed for special nomatch=NULL case, see issue#4388 (due to xo[irows] from R removing '0' value in xo)
3234
inewstarts[i] = inomatch;
3335
j++; // newlen will be 1 for xo=NA and 0 for xo=0 .. but we need to increment by 1 for both
3436
} else {
35-
inewstarts[i] = tmp+1;
37+
inewstarts[i] = tmp + 1;
3638
tmp += inewlen[i];
3739
j += inewlen[i];
3840
}
3941
}
4042
UNPROTECT(1);
41-
return (ans);
43+
return ans;
4244
}

src/openmp-utils.c

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ static bool RestoreAfterFork = true; // see #2885 in v1.12.0
1212
static int getIntEnv(const char *name, int def)
1313
{
1414
const char *val = getenv(name);
15-
if (val==NULL) return def;
15+
if (val == NULL) return def;
1616
size_t nchar = strlen(val);
17-
if (nchar==0) return def;
17+
if (nchar == 0) return def;
1818
char *end;
1919
errno = 0;
2020
long int ans = strtol(val, &end, 10); // ignores leading whitespace. If it fully consumed the string, *end=='\0' and isspace('\0')==false
2121
while (isspace(*end)) end++; // ignore trailing whitespace
22-
if (errno || (size_t)(end-val)!=nchar || ans<1 || ans>INT_MAX) {
22+
if (errno || (size_t)(end - val) != nchar || ans < 1 || ans > INT_MAX) {
2323
warning(_("Ignoring invalid %s==\"%s\". Not an integer >= 1. Please remove any characters that are not a digit [0-9]. See ?data.table::setDTthreads."), name, val);
2424
return def;
2525
}
@@ -29,23 +29,24 @@ static int getIntEnv(const char *name, int def)
2929
static inline int imin(int a, int b) { return a < b ? a : b; }
3030
static inline int imax(int a, int b) { return a > b ? a : b; }
3131

32-
void initDTthreads(void) {
32+
void initDTthreads(void)
33+
{
3334
// called at package startup from init.c
3435
// also called by setDTthreads(threads=NULL) (default) to reread environment variables; see setDTthreads below
3536
// No verbosity here in this setter. Verbosity is in getDTthreads(verbose=TRUE)
3637
int ans = getIntEnv("R_DATATABLE_NUM_THREADS", INT_MIN);
37-
if (ans>=1) {
38+
if (ans >= 1) {
3839
ans = imin(ans, omp_get_num_procs()); // num_procs is a hard limit; user cannot achieve more. ifndef _OPENMP then myomp.h defines this to be 1
3940
} else {
4041
// Only when R_DATATABLE_NUM_THREADS is unset (or <=0) do we use PROCS_PERCENT; #4514
4142
int perc = getIntEnv("R_DATATABLE_NUM_PROCS_PERCENT", 50); // use "NUM_PROCS" to use the same name as the OpenMP function this uses
4243
// 50% of logical CPUs by default; half of 8 is 4 on laptop with 4 cores. Leaves plenty of room for other processes: #3395 & #3298
43-
if (perc<=1 || perc>100) {
44+
if (perc <= 1 || perc > 100) {
4445
warning(_("Ignoring invalid R_DATATABLE_NUM_PROCS_PERCENT==%d. If used it must be an integer between 2 and 100. Default is 50. See ?setDTtheads."), perc);
4546
// not allowing 1 is to catch attempts to use 1 or 1.0 to represent 100%.
4647
perc = 50;
4748
}
48-
ans = imax(omp_get_num_procs()*perc/100, 1); // imax for when formula would result in 0.
49+
ans = imax(omp_get_num_procs() * perc / 100, 1); // imax for when formula would result in 0.
4950
}
5051
ans = imin(ans, omp_get_thread_limit()); // honors OMP_THREAD_LIMIT when OpenMP started; e.g. CRAN sets this to 2. Often INT_MAX meaning unlimited/unset
5152
ans = imin(ans, omp_get_max_threads()); // honors OMP_NUM_THREADS when OpenMP started, plus reflects any omp_set_* calls made since
@@ -57,24 +58,27 @@ void initDTthreads(void) {
5758
DTthrottle = imax(1, getIntEnv("R_DATATABLE_THROTTLE", 1024)); // 2nd thread is used only when n>1024, 3rd thread when n>2048, etc
5859
}
5960

60-
int getDTthreads(const int64_t n, const bool throttle) {
61+
int getDTthreads(const int64_t n, const bool throttle)
62+
{
6163
// this is the main getter used by all parallel regions; they specify num_threads(n, true|false).
6264
// Keep this light, simple and robust. initDTthreads() ensures 1 <= DTthreads <= omp_get_num_proc()
6365
// throttle introduced in 1.12.10 (see NEWS item); #4484
6466
// throttle==true : a number of iterations per thread (DTthrottle) is applied before a second thread is utilized
6567
// throttle==false : parallel region is already pre-chunked such as in fread; e.g. two batches intended for two threads
66-
if (n<1) return 1; // 0 or negative could be deliberate in calling code for edge cases where loop is not intended to run at all
67-
int64_t ans = throttle ? 1+(n-1)/DTthrottle : // 1 thread for n<=1024, 2 thread for n<=2048, etc
68+
if (n < 1) return 1; // 0 or negative could be deliberate in calling code for edge cases where loop is not intended to run at all
69+
int64_t ans = throttle ? 1 + (n - 1) / DTthrottle : // 1 thread for n<=1024, 2 thread for n<=2048, etc
6870
n; // don't use 20 threads for just one or two batches
69-
return ans>=DTthreads ? DTthreads : (int)ans; // apply limit in static local DTthreads saved there by initDTthreads() and setDTthreads()
71+
return ans >= DTthreads ? DTthreads : (int)ans; // apply limit in static local DTthreads saved there by initDTthreads() and setDTthreads()
7072
}
7173

72-
static const char *mygetenv(const char *name, const char *unset) {
74+
static const char *mygetenv(const char *name, const char *unset)
75+
{
7376
const char *ans = getenv(name);
74-
return (ans==NULL || ans[0]=='\0') ? unset : ans;
77+
return (ans == NULL || ans[0] == '\0') ? unset : ans;
7578
}
7679

77-
SEXP getDTthreads_R(SEXP verbose) {
80+
SEXP getDTthreads_R(SEXP verbose)
81+
{
7882
if(!IS_TRUE_OR_FALSE(verbose))
7983
error(_("%s must be TRUE or FALSE"), "verbose");
8084
if (LOGICAL(verbose)[0]) {
@@ -102,15 +106,16 @@ SEXP getDTthreads_R(SEXP verbose) {
102106
return ScalarInteger(getDTthreads(INT_MAX, false));
103107
}
104108

105-
SEXP setDTthreads(SEXP threads, SEXP restore_after_fork, SEXP percent, SEXP throttle) {
109+
SEXP setDTthreads(SEXP threads, SEXP restore_after_fork, SEXP percent, SEXP throttle)
110+
{
106111
if (!isNull(restore_after_fork)) {
107-
if (!isLogical(restore_after_fork) || LOGICAL(restore_after_fork)[0]==NA_LOGICAL) {
112+
if (!isLogical(restore_after_fork) || LOGICAL(restore_after_fork)[0] == NA_LOGICAL) {
108113
error(_("restore_after_fork= must be TRUE, FALSE, or NULL (default). getDTthreads(verbose=TRUE) reports the current setting.\n"));
109114
}
110115
RestoreAfterFork = LOGICAL(restore_after_fork)[0]; // # nocov
111116
}
112117
if (length(throttle)) {
113-
if (!isInteger(throttle) || LENGTH(throttle)!=1 || INTEGER(throttle)[0]<1)
118+
if (!isInteger(throttle) || LENGTH(throttle) != 1 || INTEGER(throttle)[0] < 1)
114119
error(_("'throttle' must be a single number, non-NA, and >=1"));
115120
DTthrottle = INTEGER(throttle)[0];
116121
}
@@ -123,19 +128,19 @@ SEXP setDTthreads(SEXP threads, SEXP restore_after_fork, SEXP percent, SEXP thro
123128
// If a CPU has been unplugged (high end servers allow live hardware replacement) then omp_get_num_procs() will
124129
// reflect that and a call to setDTthreads(threads=NULL) will update DTthreads.
125130
} else if (length(threads)) {
126-
int n=0;
127-
if (length(threads)!=1 || !isInteger(threads) || (n=INTEGER(threads)[0]) < 0) { // <0 catches NA too since NA is negative (INT_MIN)
131+
int n = 0;
132+
if (length(threads) != 1 || !isInteger(threads) || (n = INTEGER(threads)[0]) < 0) { // <0 catches NA too since NA is negative (INT_MIN)
128133
error(_("threads= must be either NULL or a single number >= 0. See ?setDTthreads."));
129134
}
130135
int num_procs = imax(omp_get_num_procs(), 1); // max just in case omp_get_num_procs() returns <= 0 (perhaps error, or unsupported)
131-
if (!isLogical(percent) || length(percent)!=1 || LOGICAL(percent)[0]==NA_LOGICAL) {
136+
if (!isLogical(percent) || length(percent) != 1 || LOGICAL(percent)[0] == NA_LOGICAL) {
132137
internal_error(__func__, "percent= must be TRUE or FALSE at C level"); // # nocov
133138
}
134139
if (LOGICAL(percent)[0]) {
135-
if (n<2 || n>100) internal_error(__func__, "threads==%d should be between 2 and 100 (percent=TRUE at C level)", n); // # nocov
136-
n = num_procs*n/100; // if 0 it will be reset to 1 in the imax() below
140+
if (n < 2 || n > 100) internal_error(__func__, "threads==%d should be between 2 and 100 (percent=TRUE at C level)", n); // # nocov
141+
n = num_procs * n / 100; // if 0 it will be reset to 1 in the imax() below
137142
} else {
138-
if (n==0 || n>num_procs) n = num_procs; // setDTthreads(0) == setDTthread(percent=100); i.e. use all logical CPUs (the default in 1.12.0 and before, from 1.12.2 it's 50%)
143+
if (n == 0 || n > num_procs) n = num_procs; // setDTthreads(0) == setDTthread(percent=100); i.e. use all logical CPUs (the default in 1.12.0 and before, from 1.12.2 it's 50%)
139144
}
140145
n = imin(n, omp_get_thread_limit()); // can't think why this might be different from its value on startup, but call it just in case
141146
n = imin(n, getIntEnv("OMP_THREAD_LIMIT", INT_MAX)); // user might have called Sys.setenv(OMP_THREAD_LIMIT=) since startup and expect setDTthreads to respect it
@@ -171,16 +176,19 @@ SEXP setDTthreads(SEXP threads, SEXP restore_after_fork, SEXP percent, SEXP thro
171176

172177
static int pre_fork_DTthreads = 0;
173178

174-
void when_fork(void) {
179+
void when_fork(void)
180+
{
175181
pre_fork_DTthreads = DTthreads;
176182
DTthreads = 1;
177183
}
178184

179-
void after_fork(void) {
185+
void after_fork(void)
186+
{
180187
if (RestoreAfterFork) DTthreads = pre_fork_DTthreads;
181188
}
182189

183-
void avoid_openmp_hang_within_fork(void) {
190+
void avoid_openmp_hang_within_fork(void)
191+
{
184192
// Called once on loading data.table from init.c
185193
#ifdef _OPENMP
186194
pthread_atfork(&when_fork, &after_fork, NULL);

src/programming.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "data.table.h"
22

3-
static void substitute_call_arg_names(SEXP expr, SEXP env) {
3+
static void substitute_call_arg_names(SEXP expr, SEXP env)
4+
{
45
if (!length(expr) || !isLanguage(expr))
56
return; // isLanguage is R's is.call
67
SEXP arg_names = getAttrib(expr, R_NamesSymbol);
@@ -9,17 +10,17 @@ static void substitute_call_arg_names(SEXP expr, SEXP env) {
910
int *imatches = INTEGER(PROTECT(chmatch(arg_names, env_names, 0)));
1011
const SEXP *env_sub = SEXPPTR_RO(env);
1112
SEXP tmp = expr;
12-
for (int i=0; i<length(arg_names); i++, tmp=CDR(tmp)) { // substitute call arg names
13+
for (int i = 0; i < length(arg_names); i++, tmp = CDR(tmp)) { // substitute call arg names
1314
if (!imatches[i])
1415
continue;
15-
SEXP sym = env_sub[imatches[i]-1];
16+
SEXP sym = env_sub[imatches[i] - 1];
1617
if (!isSymbol(sym))
1718
error(_("Attempting to substitute '%s' element with object of type '%s' but it has to be 'symbol' type when substituting name of the call argument, functions 'as.name' and 'I' can be used to work out proper substitution, see ?substitute2 examples."), CHAR(STRING_ELT(arg_names, i)), type2char(TYPEOF(sym)));
1819
SET_TAG(tmp, sym);
1920
}
2021
UNPROTECT(1); // chmatch
2122
}
22-
for (SEXP tmp=expr; tmp!=R_NilValue; tmp=CDR(tmp)) { // recursive call to substitute in nested expressions
23+
for (SEXP tmp = expr; tmp != R_NilValue; tmp = CDR(tmp)) { // recursive call to substitute in nested expressions
2324
substitute_call_arg_names(CADR(tmp), env);
2425
}
2526
}

0 commit comments

Comments
 (0)