From 7de7e78c113ba2d733bf521fd36e39865f32d80f Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 19 Aug 2024 16:03:34 -0700 Subject: [PATCH 01/13] Add a new devcontainer for ancient R --- .devcontainer/r-ancient-gcc/Dockerfile | 18 ++++++++++++++++++ .devcontainer/r-ancient-gcc/devcontainer.json | 10 ++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .devcontainer/r-ancient-gcc/Dockerfile create mode 100644 .devcontainer/r-ancient-gcc/devcontainer.json diff --git a/.devcontainer/r-ancient-gcc/Dockerfile b/.devcontainer/r-ancient-gcc/Dockerfile new file mode 100644 index 000000000..74e38ec53 --- /dev/null +++ b/.devcontainer/r-ancient-gcc/Dockerfile @@ -0,0 +1,18 @@ +FROM registry.gitlab.com/jangorecki/dockerfiles/r-3.3.0 + +RUN apt-get -qq update \ + && apt-get install -y --no-install-recommends git + +COPY DESCRIPTION . + +RUN Rscript -e ' \ +read.dcf("DESCRIPTION", c("Imports", "Suggests")) |> \ + tools:::.split_dependencies() |> \ + names() |> \ + setdiff(tools:::.get_standard_package_names()$base) |> \ + install.packages() \ +' + +# setup cc() +WORKDIR /root +COPY .devcontainer/.Rprofile . diff --git a/.devcontainer/r-ancient-gcc/devcontainer.json b/.devcontainer/r-ancient-gcc/devcontainer.json new file mode 100644 index 000000000..da60569c9 --- /dev/null +++ b/.devcontainer/r-ancient-gcc/devcontainer.json @@ -0,0 +1,10 @@ +{ + "build": { "dockerfile": "Dockerfile", "context": "../.." }, + "customizations": { "vscode": { + "extensions": [ + "REditorSupport.r", + "ms-vscode.cpptools-extension-pack" + ] + }} + } + \ No newline at end of file From c5f7769f445d5128211274242a2361feb8532f6d Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 19 Aug 2024 16:04:48 -0700 Subject: [PATCH 02/13] ws --- .devcontainer/r-ancient-gcc/devcontainer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.devcontainer/r-ancient-gcc/devcontainer.json b/.devcontainer/r-ancient-gcc/devcontainer.json index da60569c9..74c039fb7 100644 --- a/.devcontainer/r-ancient-gcc/devcontainer.json +++ b/.devcontainer/r-ancient-gcc/devcontainer.json @@ -7,4 +7,3 @@ ] }} } - \ No newline at end of file From 5fbdb04021e10eeae914dedf11167858eba6412d Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 19 Aug 2024 16:05:24 -0700 Subject: [PATCH 03/13] ws --- .devcontainer/r-ancient-gcc/devcontainer.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.devcontainer/r-ancient-gcc/devcontainer.json b/.devcontainer/r-ancient-gcc/devcontainer.json index 74c039fb7..de21d3dfe 100644 --- a/.devcontainer/r-ancient-gcc/devcontainer.json +++ b/.devcontainer/r-ancient-gcc/devcontainer.json @@ -1,9 +1,9 @@ { - "build": { "dockerfile": "Dockerfile", "context": "../.." }, - "customizations": { "vscode": { - "extensions": [ - "REditorSupport.r", - "ms-vscode.cpptools-extension-pack" - ] - }} - } + "build": { "dockerfile": "Dockerfile", "context": "../.." }, + "customizations": { "vscode": { + "extensions": [ + "REditorSupport.r", + "ms-vscode.cpptools-extension-pack" + ] + }} +} From 076a5e3104ad8d9e5e4a7d5b6a0d45ea170d922c Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 19 Aug 2024 16:47:52 -0700 Subject: [PATCH 04/13] Can't use |> --- .devcontainer/r-ancient-gcc/Dockerfile | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.devcontainer/r-ancient-gcc/Dockerfile b/.devcontainer/r-ancient-gcc/Dockerfile index 74e38ec53..e361b3c2a 100644 --- a/.devcontainer/r-ancient-gcc/Dockerfile +++ b/.devcontainer/r-ancient-gcc/Dockerfile @@ -5,12 +5,11 @@ RUN apt-get -qq update \ COPY DESCRIPTION . -RUN Rscript -e ' \ -read.dcf("DESCRIPTION", c("Imports", "Suggests")) |> \ - tools:::.split_dependencies() |> \ - names() |> \ - setdiff(tools:::.get_standard_package_names()$base) |> \ - install.packages() \ +RUN Rscript -e ' \ +dcf = read.dcf("DESCRIPTION", c("Imports", "Suggests")) \ +deps = names(tools:::.split_dependencies(dcf)) \ +standard_pkgs = tools:::.get_standard_package_names() \ +install.packages(setdiff(deps, standard_pkgs$base)) \ ' # setup cc() From fe17c66cdf1499e66fa51921a886bd0a0cadc73c Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 19 Aug 2024 16:54:30 -0700 Subject: [PATCH 05/13] Use ';' (not real newlines in docker string) --- .devcontainer/r-ancient-gcc/Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.devcontainer/r-ancient-gcc/Dockerfile b/.devcontainer/r-ancient-gcc/Dockerfile index e361b3c2a..3be4bc85f 100644 --- a/.devcontainer/r-ancient-gcc/Dockerfile +++ b/.devcontainer/r-ancient-gcc/Dockerfile @@ -5,11 +5,11 @@ RUN apt-get -qq update \ COPY DESCRIPTION . -RUN Rscript -e ' \ -dcf = read.dcf("DESCRIPTION", c("Imports", "Suggests")) \ -deps = names(tools:::.split_dependencies(dcf)) \ -standard_pkgs = tools:::.get_standard_package_names() \ -install.packages(setdiff(deps, standard_pkgs$base)) \ +RUN Rscript -e ' \ +dcf = read.dcf("DESCRIPTION", c("Imports", "Suggests")); \ +deps = names(tools:::.split_dependencies(dcf)); \ +standard_pkgs = tools:::.get_standard_package_names(); \ +install.packages(setdiff(deps, standard_pkgs$base)); \ ' # setup cc() From 6ba971e7bfcb62f965b958228f3c4a7d268aaab0 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 19 Aug 2024 20:55:13 -0700 Subject: [PATCH 06/13] Add repos=; skip unavailable packages --- .devcontainer/r-ancient-gcc/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.devcontainer/r-ancient-gcc/Dockerfile b/.devcontainer/r-ancient-gcc/Dockerfile index 3be4bc85f..b8b57fe5d 100644 --- a/.devcontainer/r-ancient-gcc/Dockerfile +++ b/.devcontainer/r-ancient-gcc/Dockerfile @@ -6,9 +6,11 @@ RUN apt-get -qq update \ COPY DESCRIPTION . RUN Rscript -e ' \ +options(repos = "https://cloud.r-project.org"); \ dcf = read.dcf("DESCRIPTION", c("Imports", "Suggests")); \ deps = names(tools:::.split_dependencies(dcf)); \ standard_pkgs = tools:::.get_standard_package_names(); \ +deps = intersect(deps, rownames(available.packages()); \ install.packages(setdiff(deps, standard_pkgs$base)); \ ' From d7f2715041060290eaaad5000db4e46f719158b5 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Tue, 20 Aug 2024 04:09:06 +0000 Subject: [PATCH 07/13] Missing ')' --- .devcontainer/r-ancient-gcc/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/r-ancient-gcc/Dockerfile b/.devcontainer/r-ancient-gcc/Dockerfile index b8b57fe5d..179184c69 100644 --- a/.devcontainer/r-ancient-gcc/Dockerfile +++ b/.devcontainer/r-ancient-gcc/Dockerfile @@ -10,7 +10,7 @@ options(repos = "https://cloud.r-project.org"); \ dcf = read.dcf("DESCRIPTION", c("Imports", "Suggests")); \ deps = names(tools:::.split_dependencies(dcf)); \ standard_pkgs = tools:::.get_standard_package_names(); \ -deps = intersect(deps, rownames(available.packages()); \ +deps = intersect(deps, rownames(available.packages())); \ install.packages(setdiff(deps, standard_pkgs$base)); \ ' From 4b968a44f671696735b31e82f2a21ea9a45ff031 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Tue, 20 Aug 2024 05:50:49 +0000 Subject: [PATCH 08/13] Various fixes for R 3.3.0 --- R/as.data.table.R | 2 +- R/print.data.table.R | 1 + R/utils.R | 7 ++++ inst/tests/tests.Rraw | 81 +++++++++++++++++++++++-------------------- src/assign.c | 5 ++- 5 files changed, 56 insertions(+), 40 deletions(-) diff --git a/R/as.data.table.R b/R/as.data.table.R index 1addc58aa..3137f7532 100644 --- a/R/as.data.table.R +++ b/R/as.data.table.R @@ -169,7 +169,7 @@ as.data.table.list = function(x, # not worse than before, and gets us in a better centralized place to port as.data.table.list to C and use MAYBE_REFERENCED # again in future, for #617. } - if (identical(x, list())) vector("list", nrow) else rep_len(x, nrow) # new objects don't need copy + if (identical(x, list())) vector("list", nrow) else safe_rep_len(x, nrow) # new objects don't need copy } vnames = character(ncol) k = 1L diff --git a/R/print.data.table.R b/R/print.data.table.R index 2e3dfc69f..528138279 100644 --- a/R/print.data.table.R +++ b/R/print.data.table.R @@ -248,6 +248,7 @@ char.trunc = function(x, trunc.char = getOption("datatable.prettyprint.char")) { nchar_chars = nchar(x, 'char') is_full_width = nchar_width > nchar_chars idx = pmin(nchar_width, nchar_chars) > trunc.char + if (!any(idx)) return(x) # strtrim() errors for width=integer() on R 3.3.0 x[idx] = paste0(strtrim(x[idx], trunc.char * fifelse(is_full_width[idx], 2L, 1L)), "...") x } diff --git a/R/utils.R b/R/utils.R index cf23609ee..4c9b02c78 100644 --- a/R/utils.R +++ b/R/utils.R @@ -21,6 +21,13 @@ nan_is_na = function(x) { stopf("Argument 'nan' must be NA or NaN") } +# In R 3.3.0, rep_len() strips attributes --> breaks data.table()'s internal recycle() helper. +if (inherits(rep_len(Sys.Date(), 1L), "Date")) { + safe_rep_len = rep_len +} else { + safe_rep_len = function(x, n) rep(x, length.out = n) +} + # endsWith no longer used from #5097 so no need to backport; prevent usage to avoid dev delay until GLCI's R 3.1.0 test endsWith = function(...) stop("Internal error: use endsWithAny instead of base::endsWith", call.=FALSE) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index fc36c67a9..5ed96387f 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -14787,49 +14787,54 @@ test(2029.2, fread(txt, quote=""), data.table(A=1:2, B=4:5, C=7:8), warning="Dis test(2029.3, fread(txt, quote="", fill=TRUE), data.table(A=1:3, B=4:6, C=c(7:8,NA))) # .Last.updated #1885 +# NB: assign .Last.updated outside test(), which itself might run :=/set() --> update the value again. +# Seen on R 3.3.0, unsure why only there. d = data.table(a=1:4, b=2:5) -d[, z:=5L] -test(2030.01, .Last.updated, 4L) # new column -d[, z:=6L] -test(2030.02, .Last.updated, 4L) # update existing column -d[2:3, z:=7L] -test(2030.03, .Last.updated, 2L) # sub assign -d[integer(), z:=8L] -test(2030.04, .Last.updated, 0L) # empty sub-assign -d[-1L, z:=9L] -test(2030.05, .Last.updated, 3L) # inverse sub-assign -d[-(1:4), z:=10L] -test(2030.06, .Last.updated, 0L) # inverse empty sub-assign +# On R 3.3.0, .Last.updated gets touched by test() itself and this cascades even to re-assignments +# of .Last.updated like n_updated=.Last.updated, so really force a deep copy by round-tripping from string. +force_copy = function(n) as.integer(sprintf("%d", n)) +d[, z:=5L]; n_updated = force_copy(.Last.updated) +test(2030.01, n_updated, 4L) # new column +d[, z:=6L]; n_updated = force_copy(.Last.updated) +test(2030.02, n_updated, 4L) # update existing column +d[2:3, z:=7L]; n_updated = force_copy(.Last.updated) +test(2030.03, n_updated, 2L) # sub assign +d[integer(), z:=8L]; n_updated = force_copy(.Last.updated) +test(2030.04, n_updated, 0L) # empty sub-assign +d[-1L, z:=9L]; n_updated = force_copy(.Last.updated) +test(2030.05, n_updated, 3L) # inverse sub-assign +d[-(1:4), z:=10L]; n_updated = force_copy(.Last.updated) +test(2030.06, n_updated, 0L) # inverse empty sub-assign +d[, z:=NULL]; n_updated = force_copy(.Last.updated) +test(2030.07, n_updated, 4L) # delete column +d[2:3, z:=11L]; n_updated = force_copy(.Last.updated) +test(2030.08, n_updated, 2L) # new column during sub-assign d[, z:=NULL] -test(2030.07, .Last.updated, 4L) # delete column -d[2:3, z:=11L] -test(2030.08, .Last.updated, 2L) # new column during sub-assign +d[integer(), z:=12L]; n_updated = force_copy(.Last.updated) +test(2030.09, n_updated, 0L) # new columns from empty sub-assign d[, z:=NULL] -d[integer(), z:=12L] -test(2030.09, .Last.updated, 0L) # new columns from empty sub-assign -d[, z:=NULL] -d[-(1:4), z:=13L] -test(2030.10, .Last.updated, 0L) # new columns from empty inverse sub-assign -d[, z:=NULL][, z:=14L] -test(2030.11, .Last.updated, 4L) # new column from chaining -d[, z:=NULL][2:3, z:=14L] -test(2030.12, .Last.updated, 2L) # sub-assign from chaining -d[2:3, z:=14L][, z:=NULL] -test(2030.13, .Last.updated, 4L) # delete column from chaining -set(d, 1:2, "z", 15L) -test(2030.14, .Last.updated, 2L) # set() updates .Last.updated too +d[-(1:4), z:=13L]; n_updated = force_copy(.Last.updated) +test(2030.10, n_updated, 0L) # new columns from empty inverse sub-assign +d[, z:=NULL][, z:=14L]; n_updated = force_copy(.Last.updated) +test(2030.11, n_updated, 4L) # new column from chaining +d[, z:=NULL][2:3, z:=14L]; n_updated = force_copy(.Last.updated) +test(2030.12, n_updated, 2L) # sub-assign from chaining +d[2:3, z:=14L][, z:=NULL]; n_updated = force_copy(.Last.updated) +test(2030.13, n_updated, 4L) # delete column from chaining +set(d, 1:2, "z", 15L); n_updated = force_copy(.Last.updated) +test(2030.14, n_updated, 2L) # set() updates .Last.updated too g = data.table(a=1:4, z=15L) # join -d[g, on="a", z:=i.z] -test(2030.15, .Last.updated, 4L) # all match of all rows +d[g, on="a", z:=i.z]; n_updated = force_copy(.Last.updated) +test(2030.15, n_updated, 4L) # all match of all rows g = data.table(a=2:4, z=16L) # join -d[, z:=NULL][g, on="a", z:=i.z] -test(2030.16, .Last.updated, 3L) # all match +d[, z:=NULL][g, on="a", z:=i.z]; n_updated = force_copy(.Last.updated) +test(2030.16, n_updated, 3L) # all match g = data.table(a=c(2L,4L,6L), z=17L) -d[, z:=NULL][g, on="a", z:=i.z] -test(2030.17, .Last.updated, 2L) # partial match +d[, z:=NULL][g, on="a", z:=i.z]; n_updated = force_copy(.Last.updated) +test(2030.17, n_updated, 2L) # partial match g = data.table(a=5:6, z=18L) -d[, z:=NULL][g, on="a", z:=i.z] -test(2030.18, .Last.updated, 0L) # zero match +d[, z:=NULL][g, on="a", z:=i.z]; n_updated = force_copy(.Last.updated) +test(2030.18, n_updated, 0L) # zero match # rbind vec with list regression dev 1.12.3; #3528 test(2031.01, rbind(data.table(A=1:3, B=7:9), data.table(A=4:6, B=as.list(10:12))), ans<-data.table(A=1:6, B=as.list(7:12))) @@ -18453,7 +18458,7 @@ rm(.datatable.aware) local({ lc_ctype = Sys.getlocale('LC_CTYPE') Sys.setlocale('LC_CTYPE', "en_US.UTF-8") # Japanese multibyte characters require utf8 - on.exit({Sys.setlocale('LC_CTYPE', lc_ctype)}) + on.exit(Sys.setlocale('LC_CTYPE', lc_ctype)) accented_a = "\u0061\u0301" ja_ichi = "\u4E00" ja_ni = "\u4E8C" @@ -18718,7 +18723,7 @@ test(2264.8, print(DT, show.indices=TRUE), output=ans) # integer64 columns print even when bit64 isn't loaded if (test_bit64) local({ DT = data.table(a = 'abc', b = as.integer64(1)) - invisible(unloadNamespace("bit64")) + suppressPackageStartupMessages(unloadNamespace("bit64")) on.exit(suppressPackageStartupMessages(library(bit64))) test(2265, DT, output="abc\\s*1$") }) diff --git a/src/assign.c b/src/assign.c index a78b9452c..4041bf2a9 100644 --- a/src/assign.c +++ b/src/assign.c @@ -220,7 +220,10 @@ SEXP setdt_nrows(SEXP x) } SEXP dim_xi = getAttrib(xi, R_DimSymbol); R_len_t len_xi; - R_len_t n_dim = LENGTH(dim_xi); + // NB: LENGTH() produces an undefined large number here on R 3.3.0. + // There's also a note in NEWS for R 3.1.0 saying length() should always be used by packages, + // but with some overhead for being a function/not macro... + R_len_t n_dim = length(dim_xi); if (n_dim) { if (test_matrix_cols && n_dim > 1) { warn_matrix_column(i+1); From 94e0226d1de7710381dfa04ab447c0277e9818ac Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 19 Aug 2024 23:30:31 -0700 Subject: [PATCH 09/13] Workaround R CMD check apparent false positive --- R/utils.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/utils.R b/R/utils.R index 4c9b02c78..baa62b679 100644 --- a/R/utils.R +++ b/R/utils.R @@ -23,7 +23,9 @@ nan_is_na = function(x) { # In R 3.3.0, rep_len() strips attributes --> breaks data.table()'s internal recycle() helper. if (inherits(rep_len(Sys.Date(), 1L), "Date")) { - safe_rep_len = rep_len + # NB: safe_rep_len=rep_len throws an R CMD check error because it _appears_ to the AST + # walker that we've used .Internal ourselves (which is not true, but codetools can't tell). + safe_rep_len = function(x, n) rep_len(x, n) } else { safe_rep_len = function(x, n) rep(x, length.out = n) } From f4edf82c6d61981dbca09922e93bfee0042275f7 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 19 Aug 2024 23:37:44 -0700 Subject: [PATCH 10/13] nolint for workaround usage --- R/utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/utils.R b/R/utils.R index baa62b679..8ac1ba991 100644 --- a/R/utils.R +++ b/R/utils.R @@ -27,7 +27,7 @@ if (inherits(rep_len(Sys.Date(), 1L), "Date")) { # walker that we've used .Internal ourselves (which is not true, but codetools can't tell). safe_rep_len = function(x, n) rep_len(x, n) } else { - safe_rep_len = function(x, n) rep(x, length.out = n) + safe_rep_len = function(x, n) rep(x, length.out = n) # nolint: rep_len_linter. } # endsWith no longer used from #5097 so no need to backport; prevent usage to avoid dev delay until GLCI's R 3.1.0 test From 4a08ee9e8469472a4f287d54c473ad1ddd879c1b Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Tue, 20 Aug 2024 09:06:27 -0700 Subject: [PATCH 11/13] Accurate R version for removing safe_rep_len() --- R/utils.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/utils.R b/R/utils.R index 8ac1ba991..58d968f9a 100644 --- a/R/utils.R +++ b/R/utils.R @@ -21,7 +21,8 @@ nan_is_na = function(x) { stopf("Argument 'nan' must be NA or NaN") } -# In R 3.3.0, rep_len() strips attributes --> breaks data.table()'s internal recycle() helper. +# TODO(R>=4.0.0): Remove this workaround. From R 4.0.0, rep_len() dispatches rep.Date(), which we need. +# Before that, rep_len() strips attributes --> breaks data.table()'s internal recycle() helper. if (inherits(rep_len(Sys.Date(), 1L), "Date")) { # NB: safe_rep_len=rep_len throws an R CMD check error because it _appears_ to the AST # walker that we've used .Internal ourselves (which is not true, but codetools can't tell). From ec035cca2ef99a33e8cd507fecd30679aeddf961 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Tue, 20 Aug 2024 09:07:58 -0700 Subject: [PATCH 12/13] even more precise comment about .Internal issue --- R/utils.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/utils.R b/R/utils.R index 58d968f9a..960b17ad1 100644 --- a/R/utils.R +++ b/R/utils.R @@ -25,7 +25,8 @@ nan_is_na = function(x) { # Before that, rep_len() strips attributes --> breaks data.table()'s internal recycle() helper. if (inherits(rep_len(Sys.Date(), 1L), "Date")) { # NB: safe_rep_len=rep_len throws an R CMD check error because it _appears_ to the AST - # walker that we've used .Internal ourselves (which is not true, but codetools can't tell). + # walker that we've used .Internal ourselves (which is not true, but codetools can't tell: + # safe_rep_len = rep_len; body(safe_rep_len)[[1]] # .Internal) safe_rep_len = function(x, n) rep_len(x, n) } else { safe_rep_len = function(x, n) rep(x, length.out = n) # nolint: rep_len_linter. From 84c7e293c165eb9f9b8623531b54179050c36fd5 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Tue, 20 Aug 2024 09:09:17 -0700 Subject: [PATCH 13/13] merge comments --- inst/tests/tests.Rraw | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index ec57f5c68..e948166f1 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -14787,11 +14787,10 @@ test(2029.2, fread(txt, quote=""), data.table(A=1:2, B=4:5, C=7:8), warning="Dis test(2029.3, fread(txt, quote="", fill=TRUE), data.table(A=1:3, B=4:6, C=c(7:8,NA))) # .Last.updated #1885 -# NB: assign .Last.updated outside test(), which itself might run :=/set() --> update the value again. -# Seen on R 3.3.0, unsure why only there. d = data.table(a=1:4, b=2:5) -# On R 3.3.0, .Last.updated gets touched by test() itself and this cascades even to re-assignments +# NB: On R 3.3.0, .Last.updated gets touched by test() itself and this cascades even to re-assignments # of .Last.updated like n_updated=.Last.updated, so really force a deep copy by round-tripping from string. +# Seen on R 3.3.0, unsure when this behavior changes. force_copy = function(n) as.integer(sprintf("%d", n)) d[, z:=5L]; n_updated = force_copy(.Last.updated) test(2030.01, n_updated, 4L) # new column