Skip to content

Commit 2c80b07

Browse files
authored
Merge pull request #72 from t-kalinowski/fix/fallthrough-assignment
Fix fall-through assignment (a <- b <- expr)
2 parents 28d0d9d + e6f4dc4 commit 2c80b07

3 files changed

Lines changed: 73 additions & 5 deletions

File tree

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@
5858
- Fixed an issue where subsetting logical arrays could fail when compiling quick
5959
functions, e.g. `(x > 0)[2, 3]` (#68).
6060
61+
- Fixed a crash when compiling chained / fall-through assignments like
62+
`a <- b <- 1` (#60).
63+
6164
# quickr 0.2.1
6265
6366
- Added support for `!` and unary `-` and `+` (#49, @mns-nordicals)

R/r2f.R

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,15 +1147,15 @@ r2f_handlers[["cbind"]] <- function(e, scope) {
11471147
ncols <- eval(ncols, scope@sizes)
11481148
}
11491149

1150-
r2f_handlers[["<-"]] <- function(args, scope, ...) {
1150+
r2f_handlers[["<-"]] <- function(args, scope, ..., hoist = NULL) {
11511151
target <- args[[1]]
11521152
if (is.call(target)) {
11531153
# given a call like `foo(x) <- y`, dispatch to `foo<-`
11541154
target_callable <- target[[1]]
11551155
stopifnot(is.symbol(target_callable))
11561156
name <- as.symbol(paste0(as.character(target_callable), "<-"))
11571157
handler <- get_r2f_handler(name)
1158-
return(handler(args, scope, ...)) # new hoist target
1158+
return(handler(args, scope, ..., hoist = hoist)) # new hoist target
11591159
}
11601160

11611161
# It sure seems like it's be nice if the Fortran() constructor
@@ -1166,6 +1166,37 @@ r2f_handlers[["<-"]] <- function(args, scope, ...) {
11661166

11671167
rhs <- args[[2]]
11681168

1169+
# Fall-through assignment: `a <- b <- expr` (or `a <- (b <- expr)`).
1170+
# R evaluates this right-to-left and returns the assigned value, i.e.
1171+
# `a <- (b <- expr)` is equivalent to `b <- expr; a <- b`.
1172+
rhs_unwrapped <- rhs
1173+
while (is_call(rhs_unwrapped, "(") && length(rhs_unwrapped) == 2L) {
1174+
rhs_unwrapped <- rhs_unwrapped[[2L]]
1175+
}
1176+
if (
1177+
(is_call(rhs_unwrapped, "<-") || is_call(rhs_unwrapped, "=")) &&
1178+
length(rhs_unwrapped) == 3L &&
1179+
is.symbol(rhs_unwrapped[[2L]])
1180+
) {
1181+
inner_target <- rhs_unwrapped[[2L]]
1182+
inner_rhs <- rhs_unwrapped[[3L]]
1183+
1184+
inner_stmt <- r2f(
1185+
call("<-", inner_target, inner_rhs),
1186+
scope,
1187+
...,
1188+
hoist = hoist
1189+
)
1190+
outer_stmt <- r2f(
1191+
call("<-", target, inner_target),
1192+
scope,
1193+
...,
1194+
hoist = hoist
1195+
)
1196+
1197+
return(Fortran(str_flatten_lines(inner_stmt, outer_stmt)))
1198+
}
1199+
11691200
# Local closure definition: `f <- function(i) ...`
11701201
if (is_function_call(rhs)) {
11711202
scope[[name]] <- as_local_closure(
@@ -1182,15 +1213,21 @@ r2f_handlers[["<-"]] <- function(args, scope, ...) {
11821213
is.symbol(rhs[[1L]]) &&
11831214
inherits(scope[[as.character(rhs[[1L]])]], LocalClosure)
11841215
) {
1185-
return(compile_closure_call_assignment(name, rhs, scope, ...))
1216+
return(compile_closure_call_assignment(
1217+
name,
1218+
rhs,
1219+
scope,
1220+
...,
1221+
hoist = hoist
1222+
))
11861223
}
11871224

11881225
# Targeted higher-order lowering: `out <- sapply(seq_along(x), f)`
11891226
if (is_sapply_call(rhs)) {
1190-
return(compile_sapply_assignment(name, rhs, scope, ...))
1227+
return(compile_sapply_assignment(name, rhs, scope, ..., hoist = hoist))
11911228
}
11921229

1193-
value <- r2f(rhs, scope, ...)
1230+
value <- r2f(rhs, scope, ..., hoist = hoist)
11941231

11951232
# immutable / copy-on-modify usage of Variable()
11961233
var <- get0(name, scope, inherits = FALSE)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
test_that("fall-through assignment is supported", {
2+
expect_quick_identical(
3+
function(x) {
4+
declare(type(x = double(1)))
5+
a <- b <- 1
6+
x + a + b
7+
},
8+
8
9+
)
10+
11+
expect_quick_identical(
12+
function(x) {
13+
declare(type(x = double(1)))
14+
a <- (b <- 1)
15+
x + a + b
16+
},
17+
8
18+
)
19+
20+
expect_quick_identical(
21+
function(x) {
22+
declare(type(x = double(1)))
23+
a <- b <- c <- x + 1
24+
a + b + c
25+
},
26+
8
27+
)
28+
})

0 commit comments

Comments
 (0)