skip_on_cran() test_that("direct-call statement closure can host-mutate with x <<- expr", { fn <- function(x) { declare(type(x = double(NA))) f <- function() { x <<- x + 1 NULL } f() x } x1 <- c(1, 2, 3) x2 <- c(-1, 0, 4, 5) expect_identical(fn(x1), x1 + 1) expect_identical(fn(x2), x2 + 1) expect_quick_identical(fn, x1, x2) qfn <- quick(fn) for (x0 in list(x1, x2)) { x <- x0 out <- qfn(x) expect_identical(out, fn(x0)) expect_identical(x, x0) } }) test_that("direct-call statement closure supports rank>1 linear x[i] <<- ...", { fn <- function(x, k) { declare(type(x = double(a, b)), type(k = integer(1))) f <- function() { x[k] <<- x[k] + 1 NULL } f() x } set.seed(1) x <- matrix(runif(12), 3, 4) expect_quick_identical(fn, list(x, 1L), list(x, 5L), list(x, 12L)) }) test_that("direct-call statement closure supports missing indices in x[ , ] <<- ...", { fn <- function(nx, ny) { declare(type(nx = integer(1)), type(ny = integer(1))) temp <- matrix(0, nx, ny) j <- 2L f <- function() { temp[, 1] <<- 1 temp[1, ] <<- 2 temp[, j] <<- 3 temp[nx, ] <<- 4 NULL } f() temp } expect_quick_identical(fn, list(3L, 4L), list(2L, 3L)) }) test_that("direct-call statement closure can use hoist+block inside contains", { fn <- function(nx, ny) { declare(type(nx = integer(1)), type(ny = integer(1))) temp <- matrix(0, nx, ny) bc <- function() { temp[1, 1] <<- (temp + 1.0)[1, 1] temp[nx, ny] <<- (temp + 2.0)[nx, ny] NULL } bc() temp } expect_quick_identical(fn, list(3L, 4L), list(2L, 2L)) }) test_that("sapply closures can use hoist paths inside contains", { fn <- function(x) { declare(type(x = double(NA))) out <- double(length(x)) out <- sapply(seq_along(out), function(i) (x + 1.0)[i]) out } set.seed(1) x <- runif(10) expect_quick_identical(fn, list(x)) }) test_that("sapply closures can use reduction mask-hoist paths inside contains", { fn <- function(x) { declare(type(x = double(NA))) out <- double(length(x)) out <- sapply(seq_along(out), function(i) sum(x[x > 0.0])) out } set.seed(1) x <- runif(20) - 0.5 expect_quick_identical(fn, list(x)) }) test_that("<<- is rejected outside local closures", { fn <- function(x) { declare(type(x = double(NA))) x <<- x + 1 x } expect_error( r2f(fn), "<<- is only supported inside local closures", fixed = TRUE ) }) test_that("<<- errors when target does not exist in the host scope", { fn <- function(x) { declare(type(x = double(NA))) f <- function() { y <<- 1 NULL } f() x } expect_error(r2f(fn), "existing variable", fixed = TRUE) }) test_that("<<- errors when targeting a closure formal", { fn <- function(x) { declare(type(x = double(NA))) f <- function(x) { x <<- x + 1 NULL } f(1.0) x } expect_error(r2f(fn), "must not shadow closure formals", fixed = TRUE) }) test_that("statement local closure calls may ignore return values", { fn <- function(x) { declare(type(x = double(NA))) f <- function() 1 f() x } set.seed(1) x <- runif(10) expect_quick_identical(fn, list(x)) }) test_that("statement local closure calls support chaining void closures", { fn <- function(x) { declare(type(x = double(NA))) g <- function(i) { x[i] <- x[i] + 1.0 } f <- function(i) { g(i) } f(1L) x } set.seed(1) x <- runif(10) expect_quick_identical(fn, list(x)) }) test_that("local closure calls can be used in expression position", { fn <- function(x) { declare(type(x = double(NA))) f <- function(i) x[i] out <- double(1) out <- 1.0 + f(1L) out } set.seed(1) x <- runif(10) expect_quick_identical(fn, list(x)) }) test_that("local closure assignments shadow captured variables", { fn <- function(x) { declare(type(x = double(NA))) f <- function() { x <- x + 1.0 sum(x * x) } a <- f() b <- sum(x * x) diff <- a - b list(diff = diff, host_x = x) } x1 <- c(0, 0, 0) x2 <- c(1, 2, 3) expect_identical(fn(x1)$diff, 3) expect_identical(fn(x2)$diff, 15) expect_identical(fn(x1)$host_x, x1) expect_identical(fn(x2)$host_x, x2) expect_quick_identical(fn, x1, x2, as.double(seq(-10, 10))) }) test_that("<<- targets the host even when the name is shadowed locally", { fn <- function(x) { declare(type(x = double(NA))) f <- function() { x <- x + 1.0 x <<- x + 1.0 sum(x) } local_sum <- f() host_sum <- sum(x) diff <- host_sum - local_sum list( diff = diff, local_sum = local_sum, host_sum = host_sum, host_x = x ) } x1 <- c(0) x2 <- c(1, 2, 3) res1 <- fn(x1) res2 <- fn(x2) expect_identical(res1$diff, 1) expect_identical(res2$diff, 3) expect_identical(res1$local_sum, 1) expect_identical(res2$local_sum, 9) expect_identical(res1$host_sum, 2) expect_identical(res2$host_sum, 12) expect_identical(res1$host_x, x1 + 2.0) expect_identical(res2$host_x, x2 + 2.0) expect_quick_identical(fn, x1, x2, as.double(seq(-10, 10))) }) test_that("captured subset assignments shadow the host", { fn <- function(x) { declare(type(x = double(NA))) f <- function(i) { x[i] <- x[i] + 1.0 x[i] } out <- double(1) out <- f(1L) list(out = out, x = x) } x1 <- as.double(1:5) x2 <- c(-3.0, 0.0, 4.0) expect_identical(fn(x1), list(out = 2.0, x = x1)) expect_identical(fn(x2), list(out = -2.0, x = x2)) expect_quick_identical(fn, x1, x2, as.double(seq(-10, 10))) qfn <- quick(fn) for (x0 in list(x1, x2)) { x <- x0 res <- qfn(x) expect_identical(res, fn(x0)) expect_identical(x, x0) } }) test_that("shadowed logical captures do not mutate the host", { fn <- function(lgl) { declare(type(lgl = logical(NA))) f <- function() { lgl <- !lgl lgl } out <- logical(length(lgl)) out <- f() list(out = out, lgl = lgl) } lgl1 <- c(TRUE, FALSE, TRUE) lgl2 <- c(FALSE, FALSE, TRUE) expect_identical(fn(lgl1), list(out = c(FALSE, TRUE, FALSE), lgl = lgl1)) expect_identical(fn(lgl2), list(out = c(TRUE, TRUE, FALSE), lgl = lgl2)) expect_quick_identical(fn, lgl1, lgl2, rep(FALSE, 5)) qfn <- quick(fn) for (lgl0 in list(lgl1, lgl2)) { lgl <- lgl0 res <- qfn(lgl) expect_identical(res, fn(lgl0)) expect_identical(lgl, lgl0) } }) test_that("sapply() errors if the closure superassigns to the output variable", { fn <- function(x) { declare(type(x = double(NA))) out <- double(length(x)) f <- function(i) { out[i] <<- x[i] + 1.0 out[i] } out <- sapply(seq_along(out), f) out } expect_error( r2f(fn), "closure must not superassign to its output variable", fixed = TRUE ) }) test_that("<<- supports the same subset targets as [<- (e.g. x[1:2] <<- ...)", { fn <- function(x) { declare(type(x = double(NA))) f <- function() { x[1:2] <<- 0 NULL } f() x } expect_quick_identical(fn, list(as.double(1:5))) }) test_that("<<- errors on drop = FALSE", { fn <- function(x) { declare(type(x = double(m, n))) f <- function() { x[1, 1, drop = FALSE] <<- 0 NULL } f() x } expect_error( r2f(fn), "drop = FALSE not supported for superassignment", fixed = TRUE ) })