diff --git a/NEWS.md b/NEWS.md index 993c28fccc..f0747cc220 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,9 @@ 1. Using `print.data.table()` with character truncation using `datatable.prettyprint.char` no longer errors with `NA` entries, [#6441](https://github.com/Rdatatable/data.table/issues/6441). Thanks to @r2evans for the bug report, and @joshhwuu for the fix. + +2. 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. Fixed a typo in the NEWS for the last release -- that's version 1.16.0, not 1.6.0; apologies. Thanks @r2evans for flagging, [#6443](https://github.com/Rdatatable/data.table/issues/6443). diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index ff213bfdfb..98fe107452 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -19042,3 +19042,14 @@ test(2278.3, set(copy(dt), 0L, "b", NA), copy(dt)[0L, b := NA]) test(2278.4, set(copy(dt), NA_integer_, "b", logical(0)), copy(dt)[NA_integer_, b := logical(0)]) test(2278.5, set(copy(dt), integer(0), "b", numeric(0)), copy(dt)[integer(0), b := numeric(0)]) test(2278.6, { set(dt, 0L, "b", logical(0)); set(dt, 1L, "a", 2L); dt }, data.table(a=2L, b=NA)) + +# ensure proper PROTECT() within fcase, #6448 +x <- 1:3 +test(2279, + 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 bd28e88ad0..f117214d1d 100644 --- a/src/fifelse.c +++ b/src/fifelse.c @@ -209,14 +209,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