Skip to content

Commit b8952ba

Browse files
committed
Reject non-statement non-final expressions
1 parent 2c80b07 commit b8952ba

4 files changed

Lines changed: 84 additions & 7 deletions

File tree

R/aaa-utils.R

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,26 @@ drop_nulls <- function(x, i) {
116116

117117
last <- function(x) x[[length(x)]]
118118
drop_last <- function(x) x[-length(x)]
119+
120+
compile_nonreturn_statements <- function(stmts, scope) {
121+
if (!length(stmts)) {
122+
return("")
123+
}
124+
125+
compiled <- lapply(stmts, function(stmt) {
126+
stmt_f <- r2f(stmt, scope)
127+
if (!is.null(stmt_f@value)) {
128+
stop(
129+
"all expressions except the final return must compile to a statement (no value); found: ",
130+
deparse1(stmt),
131+
call. = FALSE
132+
)
133+
}
134+
stmt_f
135+
})
136+
137+
str_flatten_lines(compiled)
138+
}
119139
# fmt: skip
120140
{
121141
is_scalar_na <- function(x) is.atomic(x) && !is.object(x) && length(x) == 1L && is.na(x)

R/r2f-closures.R

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,16 +168,12 @@ compile_internal_subroutine <- function(
168168
if (is.null(last(stmts))) {
169169
stmts <- drop_last(stmts)
170170
}
171-
if (length(stmts)) {
172-
body_prefix <- lapply(stmts, function(stmt) r2f(stmt, proc_scope))
173-
body_prefix <- str_flatten_lines(body_prefix)
174-
}
171+
body_prefix <- compile_nonreturn_statements(stmts, proc_scope)
175172
} else {
176173
last_expr <- last(stmts)
177174
prefix <- drop_last(stmts)
178175

179-
body_prefix <- lapply(prefix, function(stmt) r2f(stmt, proc_scope))
180-
body_prefix <- str_flatten_lines(body_prefix)
176+
body_prefix <- compile_nonreturn_statements(prefix, proc_scope)
181177

182178
h <- new_hoist(proc_scope)
183179
expr_error <- NULL

R/subroutine.R

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ new_fortran_subroutine <- function(
3030
# declare(type(foo = integer(foo_dim_1_, foo_dim_2_)),
3131
# type(bar = integer(foo_dim_1_, 3L)))
3232
body <- substitute_declared_sizes(body)
33-
body <- r2f(drop_last(body), scope)
33+
stmts <- as.list(body)[-1L]
34+
body <- compile_nonreturn_statements(drop_last(stmts), scope)
3435

3536
# check all input vars were declared
3637
# TODO: this check might be too late, because r2f() might throw cryptic errors

tests/testthat/test-errors.R

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,63 @@ test_that("case-sensitive variable name clashes", {
77
})
88
})
99
})
10+
11+
test_that("non-final expressions must be assigned", {
12+
expect_error(regexp = "all expressions except the final return", {
13+
quick(function(x) {
14+
declare(type(x = double(1)))
15+
x + 1
16+
x
17+
})
18+
})
19+
20+
expect_error(regexp = "all expressions except the final return", {
21+
quick(function(x) {
22+
declare(type(x = double(1)))
23+
x
24+
x
25+
})
26+
})
27+
28+
expect_error(regexp = "all expressions except the final return", {
29+
quick(function(x) {
30+
declare(type(x = double(1)))
31+
x <- x + 1
32+
x + 1
33+
x
34+
})
35+
})
36+
})
37+
38+
test_that("value-returning local closures can be called as statements", {
39+
fn <- function(x) {
40+
declare(type(x = double(1)))
41+
42+
apply_boundary_conditions <- function() {
43+
x <<- x + 1
44+
}
45+
46+
apply_boundary_conditions()
47+
x
48+
}
49+
50+
# r2f(fn)
51+
52+
expect_quick_identical(fn, list(1))
53+
54+
fn <- function(x) {
55+
declare(type(x = double(1)))
56+
57+
apply_boundary_conditions <- function() {
58+
x <<- x + 1
59+
x
60+
}
61+
62+
apply_boundary_conditions()
63+
x
64+
}
65+
66+
# r2f(fn)
67+
68+
expect_quick_identical(fn, list(1))
69+
})

0 commit comments

Comments
 (0)