diff --git a/NEWS.md b/NEWS.md index c011a3a6e0..06f1b87711 100644 --- a/NEWS.md +++ b/NEWS.md @@ -55,6 +55,8 @@ rowwiseDT( 5. Queries like `DT[, min(x):max(x)]` now work as expected, i.e. the same as `DT[, seq(min(x), max(x))]` or `with(DT, min(x):max(x))`, [#2069](https://github.com/Rdatatable/data.table/issues/2069). Shorthand like `DT[, a:b]` meaning "select from columns `a` through `b`" still works. Thanks to @franknarf1 for reporting and @jangorecki for the fix. +6. Fixed a segfault in `fcase()`, [#6448](https://github.com/Rdatatable/data.table/issues/6448). Thanks @ethanbsmith for reporting with reprex, @aitap for finding the root cause, and @MichaelChirico for the PR. + ## NOTES 1. Tests run again when some Suggests packages are missing, [#6411](https://github.com/Rdatatable/data.table/issues/6411). Thanks @aadler for the note and @MichaelChirico for the fix. diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index a0afa398da..c6ef5509c1 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -19182,3 +19182,14 @@ test(2285.09, merge(merge(y, x), z), data.table(a=3L, key="a")) test(2285.10, merge(merge(y, z), x), data.table(a=3L, key="a")) test(2285.11, merge(merge(z, x), y), data.table(a=3L, key="a")) test(2285.12, merge(merge(z, y), x), data.table(a=3L, key="a")) + +# ensure proper PROTECT() within fcase, #6448 +x <- 1:3 +test(2286, + fcase( + x<2, structure(list(1), class = "foo"), + x<3, structure(list(2), class = "foo"), + # Force gc() and some allocations which have a good chance at landing in the region that was earlier left unprotected + { gc(full = TRUE); replicate(10, FALSE); x<4 }, + `attr<-`(list(3), "class", "foo")), + structure(list(1, 2, 3), class = "foo")) diff --git a/src/fifelse.c b/src/fifelse.c index 5af0051b5e..737ed3dd0d 100644 --- a/src/fifelse.c +++ b/src/fifelse.c @@ -215,14 +215,16 @@ SEXP fcaseR(SEXP rho, SEXP args) { "Note that the default argument must be named explicitly, e.g., default=0"), narg - 2); } int nprotect=0, l; - int64_t len0=0, len1=0, len2=0; - SEXP ans=R_NilValue, value0=R_NilValue, tracker=R_NilValue, whens=R_NilValue, thens=R_NilValue; + int64_t n_ans=0, n_this_arg=0, n_undecided=0; + SEXP ans=R_NilValue, tracker=R_NilValue, whens=R_NilValue, thens=R_NilValue; + SEXP ans_class, ans_levels; PROTECT_INDEX Iwhens, Ithens; PROTECT_WITH_INDEX(whens, &Iwhens); nprotect++; PROTECT_WITH_INDEX(thens, &Ithens); nprotect++; - SEXPTYPE type0=NILSXP; + SEXPTYPE ans_type=NILSXP; // naout means if the output is scalar logic na bool imask = true, naout = false, idefault = false; + bool ans_is_factor; int *restrict p = NULL; const int n = narg/2; for (int i=0; i1 ? INT64_MAX : 0; + int64_t thenMask = n_this_arg>1 ? INT64_MAX : 0; switch(TYPEOF(ans)) { case LGLSXP: { const int *restrict pthens; if (!naout) pthens = LOGICAL(thens); // the content is not useful if out is NA_LOGICAL scalar int *restrict pans = LOGICAL(ans); const int pna = NA_LOGICAL; - for (int64_t j=0; j