What happens
R's &&/|| are scalar control operators: operands must be length 1,
and the right operand is evaluated only when the left side does not
decide. quickr compiles them exactly like &/|:
fn <- function(x, y) {
declare(type(x = logical(3)), type(y = logical(3)))
x && y
}
c(TRUE, TRUE, FALSE) && c(TRUE, FALSE, TRUE)
#> Error in ... && ... : 'length = 3' in coercion to 'logical(1)'
quick(fn)(c(TRUE, TRUE, FALSE), c(TRUE, FALSE, TRUE))
#> [1] TRUE FALSE FALSE # an answer where R errors
And with both sides always evaluated, the canonical guarded-access
idiom is unsafe — .and. gives Fortran license to evaluate x(i) even
when i > n, an out-of-bounds read R's semantics rule out:
while (i <= n && x[i] > 0) i <- i + 1L
Why it happens
R/r2f-logical.R registers && on the same handler as &, which emits
.and. (a marked TODO). Fortran's .and./.or. are elementwise and
leave operand evaluation order unspecified — there is no short-circuit
guarantee to inherit.
Expected behavior
Match R: non-scalar operands are an error (compile-time, since the
answer never changes at run time), and the right operand's evaluation —
including any side effects or potential errors — happens only inside a
conditional on the left operand's value. while conditions need the
same care: anything the condition's translation hoists must re-run
every iteration, not once before the loop.
What happens
R's
&&/||are scalar control operators: operands must be length 1,and the right operand is evaluated only when the left side does not
decide. quickr compiles them exactly like
&/|:And with both sides always evaluated, the canonical guarded-access
idiom is unsafe —
.and.gives Fortran license to evaluatex(i)evenwhen
i > n, an out-of-bounds read R's semantics rule out:Why it happens
R/r2f-logical.R registers
&&on the same handler as&, which emits.and.(a marked TODO). Fortran's.and./.or.are elementwise andleave operand evaluation order unspecified — there is no short-circuit
guarantee to inherit.
Expected behavior
Match R: non-scalar operands are an error (compile-time, since the
answer never changes at run time), and the right operand's evaluation —
including any side effects or potential errors — happens only inside a
conditional on the left operand's value.
whileconditions need thesame care: anything the condition's translation hoists must re-run
every iteration, not once before the loop.