Skip to content

Commit ea6ae9e

Browse files
committed
Merge commit 'b6824c54d78b86b2ebf5679a721f0985193cfe56' into fix/pr146-merge-145-20260815
2 parents f77a07a + b6824c5 commit ea6ae9e

16 files changed

Lines changed: 487 additions & 366 deletions

R/classes.R

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,14 @@ Variable := new_class(
339339
# storage (0/1) rather than Fortran LOGICAL.
340340
logical_as_int = prop_bool(default = FALSE),
341341

342+
# Fortran kind for integer variables. User-facing R integers remain c_int;
343+
# pointer-sized compiler locals opt into c_ptrdiff_t explicitly.
344+
integer_kind = prop_enum(
345+
c("c_int", "c_ptrdiff_t"),
346+
default = "c_int",
347+
exact = TRUE
348+
),
349+
342350
# TRUE when the variable is available via host association and should not
343351
# be redeclared in the local scope.
344352
host_associated = prop_bool(default = FALSE),
@@ -350,7 +358,13 @@ Variable := new_class(
350358

351359
validator = function(self) {
352360
if (isTRUE(self@logical_as_int) && !identical(self@mode, "logical")) {
353-
"`logical_as_int` can only be TRUE when `mode` is 'logical'"
361+
return("`logical_as_int` can only be TRUE when `mode` is 'logical'")
362+
}
363+
if (
364+
!identical(self@integer_kind, "c_int") &&
365+
!identical(self@mode, "integer")
366+
) {
367+
"`integer_kind` can only differ from 'c_int' when `mode` is 'integer'"
354368
}
355369
}
356370
)

R/manifest.R

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,11 @@ var_storage_bytes <- function(var) {
7373
switch(
7474
var@mode,
7575
double = 8,
76-
integer = 4,
76+
integer = if (identical(var@integer_kind, "c_ptrdiff_t")) {
77+
.Machine$sizeof.pointer
78+
} else {
79+
4
80+
},
7781
complex = 16,
7882
logical = 4,
7983
raw = 1,
@@ -196,7 +200,7 @@ iso_c_binding_symbols <- function(
196200
switch(
197201
var@mode,
198202
double = "c_double",
199-
integer = "c_int",
203+
integer = var@integer_kind,
200204
complex = "c_double_complex",
201205
logical = if (isTRUE(logical_is_c_int(var))) "c_int",
202206
raw = "c_int8_t",
@@ -262,7 +266,7 @@ emit_decl_line <- function(
262266
type <- switch(
263267
var@mode,
264268
double = "real(c_double)",
265-
integer = "integer(c_int)",
269+
integer = glue("integer({var@integer_kind})"),
266270
complex = "complex(c_double_complex)",
267271
logical = if (logical_as_int(var)) "integer(c_int)" else "logical",
268272
raw = "integer(c_int8_t)",
@@ -371,7 +375,7 @@ r2f.scope <- function(scope, include_errors = FALSE) {
371375
type <- switch(
372376
var@mode,
373377
double = "real(c_double)",
374-
integer = "integer(c_int)",
378+
integer = glue("integer({var@integer_kind})"),
375379
complex = "complex(c_double_complex)",
376380
logical = if (logical_as_int(var)) "integer(c_int)" else "logical",
377381
raw = "integer(c_int8_t)",

R/r2f-constructors.R

Lines changed: 19 additions & 8 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) {
@@ -60,9 +64,16 @@ r2f_handlers[["c"]] <- function(args, scope = NULL, ...) {
6064
call. = FALSE
6165
)
6266
}
63-
spread_var <- spread_var %||% scope_unique_var(scope, "integer")
67+
spread_var <- spread_var %||%
68+
scope_unique_var(
69+
scope,
70+
"integer",
71+
integer_kind = "c_ptrdiff_t"
72+
)
6473
ff[[j]] <- Fortran(
65-
glue("({ff[[j]]}, {spread_var}=1, int({len_f}))"),
74+
glue(
75+
"({ff[[j]]}, {spread_var}=1_c_ptrdiff_t, int({len_f}, kind=c_ptrdiff_t))"
76+
),
6677
ff[[j]]@value
6778
)
6879
}
@@ -383,7 +394,7 @@ r2f_handlers[["array"]] <- function(args, scope = NULL, ..., hoist = NULL) {
383394
}
384395
shape <- glue("int([{dims_f}])")
385396

386-
is_fill_constructor <- is_fill_constructor_call(args$data)
397+
is_fill_constructor <- is_fill_constructor_call(args$data, scope)
387398

388399
axis_terms <- vapply(
389400
target_dims,

R/r2f-logical.R

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,13 @@ register_r2f_handler(
135135

136136
# ---- binary logical operators ----
137137

138-
# TODO: the scalar || probably need some more type checking.
139138
# TODO: gfortran supports implicit casting that of logical to integer when
140139
# assigning a logical to a variable declared integer, converting `.true.` to `1`,
141140
# but this is not a standard language feature, and Intel's `ifort` uses `-1` for `.true`.
142141
# We should explicitly use
143142
# `merge(1_c_int, 0_c_int, <lgl>)` to cast logical to int.
144143
register_r2f_handler(
145-
c("&", "&&", "|", "||"),
144+
c("&", "|"),
146145
function(args, scope, ..., hoist = NULL) {
147146
args <- lapply(args, r2f, scope, ..., hoist = hoist)
148147
args <- lapply(args, function(a) {
@@ -162,17 +161,65 @@ register_r2f_handler(
162161
scalarize_one_by_one = FALSE
163162
)
164163

165-
operator <- switch(
166-
last(list(...)$calls),
167-
`&` = ,
168-
`&&` = ".and.",
169-
`|` = ,
170-
`||` = ".or."
171-
)
164+
operator <- switch(last(list(...)$calls), `&` = ".and.", `|` = ".or.")
172165

173166
s <- glue("{left} {operator} {right}")
174167
val <- conform(left@value, right@value)
175168
val@mode <- "logical"
176169
Fortran(s, val)
177170
}
178171
)
172+
173+
andor_operand_is_length_one <- function(x) {
174+
passes_as_scalar(x@value) ||
175+
x@value@rank > 0L &&
176+
all(vapply(x@value@dims, dim_is_one, logical(1L)))
177+
}
178+
179+
scalarize_andor_operand <- function(x, op, hoist) {
180+
if (is.null(x@value) || !identical(x@value@mode, "logical")) {
181+
stop("`", op, "` requires logical operands", call. = FALSE)
182+
}
183+
if (!andor_operand_is_length_one(x)) {
184+
stop(
185+
"`",
186+
op,
187+
"` requires length-1 operands; use `",
188+
if (op == "&&") "&" else "|",
189+
"` for elementwise operations",
190+
call. = FALSE
191+
)
192+
}
193+
if (passes_as_scalar(x@value)) {
194+
return(booleanize_logical_as_int(x))
195+
}
196+
if (is.null(hoist)) {
197+
stop("internal error: `", op, "` requires hoist context", call. = FALSE)
198+
}
199+
200+
if (isTRUE(x@logical_booleanized)) {
201+
tmp <- hoist$declare_tmp(mode = "logical", dims = x@value@dims)
202+
hoist$emit(glue("{tmp@name} = {x}"))
203+
x <- Fortran(tmp@name, tmp)
204+
} else {
205+
x <- hoist_unless_name(x, hoist)
206+
}
207+
idxs <- rep("1", x@value@rank)
208+
Fortran(
209+
glue("{x}({str_flatten_commas(idxs)})"),
210+
Variable("logical")
211+
)
212+
}
213+
214+
register_r2f_handler(
215+
c("&&", "||"),
216+
function(args, scope, ..., hoist = NULL) {
217+
op <- last(list(...)$calls)
218+
stopifnot(length(args) == 2L, op %in% c("&&", "||"))
219+
.[left, right] <- lapply(args, r2f, scope, ..., hoist = hoist)
220+
left <- scalarize_andor_operand(left, op, hoist)
221+
right <- scalarize_andor_operand(right, op, hoist)
222+
operator <- if (op == "&&") ".and." else ".or."
223+
Fortran(glue("{left} {operator} {right}"), Variable("logical"))
224+
}
225+
)

0 commit comments

Comments
 (0)