diff --git a/NEWS.md b/NEWS.md index 4e64f3faa..ebbd328a0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -48,6 +48,8 @@ 8. `frollapply()` no longer produces output longer than the input when the window length is also longer than the input [#7646](https://github.com/Rdatatable/data.table/issues/7646). Thanks to @hadley-johnson for reporting and @jangorecki for the fix. +9. `fcoalesce()` and `setcoalesce()` could fail for inputs during implicit type coercions when items had different but still compatible underlying storage types (e.g., `Date` and `IDate`), #7545 (https://github.com/Rdatatable/data.table/issues/7545). This was particularly unexpected because `Date` objects may be stored as either integer or double. Thanks to @ethanbsmith for the report and @ben-schwen for the fix. + ### Notes 1. {data.table} now depends on R 3.5.0 (2018). diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index b73b2767a..0b3c1958b 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -15144,6 +15144,8 @@ date = as.Date(int, origin="1970-01-01") date_val = as.Date(int_val, origin="1970-01-01") idate = as.IDate(int, origin="1970-01-01") idate_val = as.IDate(int_val, origin="1970-01-01") +date_int = .Date(3L) +date_int_val = .Date(int_val) itime = as.ITime(int) itime_val = as.ITime(int_val) posix = as.POSIXct(int, origin="1970-01-01") @@ -15167,6 +15169,12 @@ test(2060.015, fcoalesce(itime, as.ITime(3L)), itime_val) test(2060.016, fcoalesce(itime, as.ITime(NA), as.ITime(3L)), itime_val) test(2060.017, fcoalesce(posix, as.POSIXct(3L, origin="1970-01-01")), posix_val) test(2060.018, fcoalesce(posix, as.POSIXct(NA_integer_, origin="1970-01-01"), as.POSIXct(3L, origin="1970-01-01")), posix_val) +# allow mixed underlying types of date class, as long as they can be coerced to the same class #7545 +test(2060.019, fcoalesce(date, date_int), date_val) +test(2060.020, fcoalesce(date, as.IDate("1970-01-04")), date_val) +test(2060.021, fcoalesce(idate, date_val), idate_val) +test(2060.022, typeof(fcoalesce(date, date_int_val)), "double") +test(2060.023, typeof(fcoalesce(idate, date_val)), "integer") # vector replacements test(2060.051, fcoalesce(bool, rep(TRUE, 3L)), bool_val) test(2060.052, fcoalesce(bool, rep(NA, 3L), rep(TRUE, 3L)), bool_val) diff --git a/src/coalesce.c b/src/coalesce.c index 10b7b7757..a3d434d2f 100644 --- a/src/coalesce.c +++ b/src/coalesce.c @@ -6,6 +6,14 @@ - The replacement of NAs with non-NA values from subsequent vectors - The conditional checks within parallelized loops */ +static bool compatibleDateTypes(SEXP x, SEXP y) { + if (!INHERITS(x, char_Date) || !INHERITS(y, char_Date)) return false; + const SEXPTYPE xType = TYPEOF(x), yType = TYPEOF(y); + if ((xType != INTSXP && xType != REALSXP) || (yType != INTSXP && yType != REALSXP)) return false; + if (xType == INTSXP && yType == REALSXP && !fitsInInt32(y)) return false; + return true; +} + SEXP coalesce(SEXP x, SEXP inplaceArg, SEXP nan_is_na_arg) { if (TYPEOF(x)!=VECSXP) internal_error(__func__, "input is list(...) at R level"); // # nocov if (!IS_TRUE_OR_FALSE(inplaceArg)) internal_error(__func__, "argument 'inplaceArg' must be TRUE or FALSE"); // # nocov @@ -31,6 +39,7 @@ SEXP coalesce(SEXP x, SEXP inplaceArg, SEXP nan_is_na_arg) { if (nval==0) return first; const bool factor = isFactor(first); const int nrow = length(first); + SEXP items = PROTECT(allocVector(VECSXP, nval)); nprotect++; for (int i=0; i