Skip to content

Commit ab11aa0

Browse files
committed
Test the ifelse() rank-mismatch error, drop an unreachable guard
Coverage flagged both stop() calls in check_ifelse_branch_shape() as untested. The rank check is reachable: a matrix branch under a vector `test`, or the reverse, hits it, and the existing mismatch test only exercises the per-axis verdict. Add a case for each direction. The `is.null(hoist)` arm is not reachable. r2f() replaces a NULL hoist with a fresh one before dispatching to any handler, and the only other dispatch route resolves `f<-`-style names, so the ifelse() handler always has a hoist to emit the size guard into. Delete it and record the invariant in the function's comment rather than testing dead code.
1 parent e1b3e77 commit ab11aa0

2 files changed

Lines changed: 20 additions & 8 deletions

File tree

R/r2f-conditionals.R

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ ifelse_axis_verdict <- function(test_dim, branch_dim) {
3333
# natively; a non-scalar branch must match `test`'s shape, because
3434
# merge() requires conformable arguments and a runtime mismatch would
3535
# read past the shorter branch. Statically unequal dims are a compile
36-
# error; symbolic dims get a statement-level runtime size guard.
36+
# error; symbolic dims get a statement-level runtime size guard, emitted into
37+
# `hoist` -- always a live hoist context, since r2f() substitutes a fresh one
38+
# before dispatching to any handler.
3739
check_ifelse_branch_shape <- function(branch, mask, hoist, scope) {
3840
if (passes_as_scalar(branch@value)) {
3941
return(invisible())
@@ -57,13 +59,6 @@ check_ifelse_branch_shape <- function(branch, mask, hoist, scope) {
5759
if (!length(unknown_axes)) {
5860
return(invisible())
5961
}
60-
if (is.null(hoist)) {
61-
stop(
62-
"cannot emit a runtime length guard here; ",
63-
"ifelse() branch lengths must match `test` statically",
64-
call. = FALSE
65-
)
66-
}
6762
# size() is an inquiry, so applying it to operand expression text does
6863
# not evaluate the operands.
6964
condition <- str_flatten(

tests/testthat/test-ifelse.R

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,23 @@ test_that("ifelse with statically mismatched branch lengths is a compile error",
5959
expect_error(quick(fn), "R-style recycling is not supported")
6060
})
6161

62+
test_that("ifelse with a branch of different rank than test is a compile error", {
63+
# merge() requires conformable arguments: a matrix branch under a vector
64+
# `test` is R recycling, not broadcasting.
65+
fn <- function(c, m) {
66+
declare(type(c = logical(3)), type(m = double(3, 3)))
67+
ifelse(c, m, 0)
68+
}
69+
expect_error(quick(fn), "R-style recycling is not supported")
70+
71+
# the mirror image, and in `no` position: a vector branch under a matrix test
72+
fn2 <- function(c, a) {
73+
declare(type(c = logical(2, 2)), type(a = double(4)))
74+
ifelse(c, 0, a)
75+
}
76+
expect_error(quick(fn2), "R-style recycling is not supported")
77+
})
78+
6279
test_that("ifelse guards unknown branch lengths at runtime", {
6380
fn <- function(c, a, b) {
6481
declare(type(c = logical(NA)), type(a = double(NA)), type(b = double(NA)))

0 commit comments

Comments
 (0)