Skip to content

Commit 72c1a84

Browse files
committed
Respect local closures in fill detection
1 parent 5446a3f commit 72c1a84

2 files changed

Lines changed: 44 additions & 6 deletions

File tree

R/r2f-constructors.R

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@
88
# double(k), numeric(k). These lower to a single scalar literal carrying
99
# array dims, so splicing contexts must spread them explicitly.
1010
# Used by: c(), array()
11-
is_fill_constructor_call <- function(e) {
12-
is.call(e) &&
13-
is.symbol(e[[1L]]) &&
14-
as.character(e[[1L]]) %in% c("logical", "integer", "double", "numeric")
11+
is_fill_constructor_call <- function(e, scope) {
12+
if (!is.call(e) || !is.symbol(e[[1L]])) {
13+
return(FALSE)
14+
}
15+
name <- as.character(e[[1L]])
16+
name %in%
17+
c("logical", "integer", "double", "numeric") &&
18+
(is.null(scope) || !inherits(scope[[name]], LocalClosure))
1519
}
1620

1721
# Name of the call one frame above the current handler ("" at top level).
@@ -44,7 +48,7 @@ r2f_handlers[["c"]] <- function(args, scope = NULL, ...) {
4448
mode <- promoted$mode
4549
# Fill constructors are one scalar literal claiming length k; spread them
4650
# as implied-dos so the emitted element count matches the claimed length.
47-
fill_idx <- which(map_lgl(args, is_fill_constructor_call))
51+
fill_idx <- which(map_lgl(args, is_fill_constructor_call, scope = scope))
4852
if (length(fill_idx)) {
4953
spread_var <- NULL
5054
for (j in fill_idx) {
@@ -383,7 +387,7 @@ r2f_handlers[["array"]] <- function(args, scope = NULL, ..., hoist = NULL) {
383387
}
384388
shape <- glue("int([{dims_f}])")
385389

386-
is_fill_constructor <- is_fill_constructor_call(args$data)
390+
is_fill_constructor <- is_fill_constructor_call(args$data, scope)
387391

388392
axis_terms <- vapply(
389393
target_dims,

tests/testthat/test-recycling.R

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,40 @@ test_that("fill constructors spread inside c()", {
242242
expect_quick_identical(logical_fill, list(c(TRUE, FALSE)))
243243
})
244244

245+
test_that("local closures can shadow fill constructors in c() and array()", {
246+
numeric_shadow <- function() {
247+
numeric <- function() c(1, 2)
248+
combined <- c(numeric(), 3)
249+
reshaped <- array(numeric(), dim = c(1L, 2L))
250+
list(combined = combined, reshaped = reshaped)
251+
}
252+
expect_quick_identical(numeric_shadow, list())
253+
254+
integer_shadow <- function() {
255+
integer <- function() c(1L, 2L)
256+
combined <- c(integer(), 3L)
257+
reshaped <- array(integer(), dim = c(1L, 2L))
258+
list(combined = combined, reshaped = reshaped)
259+
}
260+
expect_quick_identical(integer_shadow, list())
261+
262+
double_shadow <- function() {
263+
double <- function() c(1, 2)
264+
combined <- c(double(), 3)
265+
reshaped <- array(double(), dim = c(1L, 2L))
266+
list(combined = combined, reshaped = reshaped)
267+
}
268+
expect_quick_identical(double_shadow, list())
269+
270+
logical_shadow <- function() {
271+
logical <- function() c(1L, 2L)
272+
combined <- c(logical(), 3L)
273+
reshaped <- array(logical(), dim = c(1L, 2L))
274+
list(combined = combined, reshaped = reshaped)
275+
}
276+
expect_quick_identical(logical_shadow, list())
277+
})
278+
245279
test_that("fill constructors materialize where an array is required", {
246280
# A fill reaching c() through an expression is a real array, not a
247281
# scalar literal with claimed dims (which emitted one element where the

0 commit comments

Comments
 (0)