Skip to content
Open
Show file tree
Hide file tree
Changes from 62 commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
289ab59
Reject R-style recycling in elementwise ops, guard unknown lengths
mns-nordicals Jul 4, 2026
40a9f78
Spread c() fills as implied-dos; materialize matrix(scalar, m, n)
mns-nordicals Jul 4, 2026
649529b
Follow R's split on 1x1-matrix recycling; hoist casts before scalarizing
mns-nordicals Jul 5, 2026
28c6ebe
Add NEWS entries for recycling rejection and 1x1-matrix split
mns-nordicals Jul 6, 2026
0fc7d25
Materialize fill constructors outside broadcast and spread contexts
mns-nordicals Jul 6, 2026
732a857
Extract parent_call_name() and materialize_via_hoist() helpers
mns-nordicals Jul 7, 2026
6906f16
Guard symbolic-length vectors against 1x1 matrices instead of scalari…
mns-nordicals Jul 7, 2026
06f7057
Format with air
mns-nordicals Jul 11, 2026
7358165
Cover the length-0 verdict and closure-return materialization
mns-nordicals Aug 2, 2026
6150f57
Assert the hoist invariant instead of handling a NULL hoist
mns-nordicals Aug 2, 2026
bf7c91e
Guard BLAS conformability at runtime; drop warning and crossprod stop
mns-nordicals Jul 4, 2026
b34e832
Guard vector %*% vector by whole size, not rank-2 axes
mns-nordicals Jul 5, 2026
94d81c3
Add combinatorial conformability grid tests
mns-nordicals Jul 5, 2026
bd879cf
Format with air
mns-nordicals Jul 11, 2026
0793414
Grid: symbolic-vector-vs-1x1 cells are runtime guards for every op class
mns-nordicals Jul 7, 2026
6a763ef
Require a square matrix in solve(), matching R
mns-nordicals Jul 6, 2026
eb9c2a4
Extract the shared conformability helpers
mns-nordicals Aug 2, 2026
f77a07a
Unify lapack_solve's branch structure and output selection
mns-nordicals Jul 7, 2026
5446a3f
Resolve named handlers by name at dispatch, like dest_infer
mns-nordicals Aug 2, 2026
4b26653
Share the BLAS/LAPACK output-resolution and info-guard boilerplate
mns-nordicals Jul 7, 2026
e66439f
Merge the cbind/rbind twins and share diag()'s argument matching
mns-nordicals Jul 7, 2026
1be5756
Unify the read/write subscript lowering and superassignment validation
mns-nordicals Jul 7, 2026
19167af
Consolidate the hoist-materialization idiom and core dispatch accessors
mns-nordicals Jul 7, 2026
e939141
Drop an inherited hoist_mask in any()/all() like the numeric reductions
mns-nordicals Jul 7, 2026
02f63d8
Fix check_type_call()'s inverted mode check
mns-nordicals Jul 7, 2026
3ce83e0
Delete the dead set_once/allow_na property plumbing in classes.R
mns-nordicals Jul 7, 2026
8e5d635
Fold literal dims in the qr work-size and tidy the length-1 spellings
mns-nordicals Jul 7, 2026
d2ea324
Share the %*% shape computation between the handler and dest inference
mns-nordicals Jul 7, 2026
7c9a03a
Normalize matrix()'s argument policy in one place
mns-nordicals Jul 7, 2026
0235ae7
Check reassignment shape compatibility axis by axis
mns-nordicals Jul 7, 2026
b1b81f4
Require proven dims before reusing an assignment target for BLAS output
mns-nordicals Jul 7, 2026
d6e59f0
Support c(matrix) and as.vector(); share one flatten-to-vector helper
mns-nordicals Jul 7, 2026
4a92d01
Validate deferred-shape ranks and empty as.vector() calls
mns-nordicals Jul 11, 2026
0a3b0e3
Add semantics vignette: the type/shape contract and differences from R
mns-nordicals Jul 6, 2026
8f025cc
Share the comparison and logical operand lowering
mns-nordicals Jul 11, 2026
82707b0
Make && and || scalar short-circuit operators, matching R
mns-nordicals Jul 6, 2026
d70b883
Update the semantics vignette for the 08a-08d behavior changes
mns-nordicals Jul 6, 2026
bc857e2
Clarify lowering and inference helper names
mns-nordicals Jul 11, 2026
b92efe7
Give single-statement while/repeat bodies their own hoist target
mns-nordicals Jul 7, 2026
3e5855f
Align the vignette's complex, zero-length, and subscript claims with …
mns-nordicals Jul 6, 2026
92b7e42
Refuse unsupported modes and complex operations with clean errors
mns-nordicals Jul 6, 2026
d8a65c4
Clean up generated code: scalar-matrix broadcast, literal hoists, tra…
mns-nordicals Jul 6, 2026
b538036
Refuse reassignment between scalar and array shapes
mns-nordicals Jul 24, 2026
6bcadbb
Let hoists report emptiness instead of comparing rendered text
mns-nordicals Jul 7, 2026
7793ad5
Vignette: multi-arg reductions join logicals as integer, unlike c()
mns-nordicals Jul 7, 2026
5818aa0
Refuse complex operands in linear algebra
mns-nordicals Jul 6, 2026
8ae9808
Route the scalar-matrix broadcast guards through guard_conformable_di…
mns-nordicals Jul 7, 2026
39a5373
Take R's diag() identity form for any length-1 x
mns-nordicals Jul 24, 2026
17aad62
Format with air
mns-nordicals Jul 11, 2026
3b7c323
Vignette: state the 1x1-matrix recycling rule by observable shape
mns-nordicals Jul 7, 2026
ae4f3b8
Add NEWS entry for the complex linear-algebra refusal
mns-nordicals Jul 6, 2026
a13213a
Materialize fill constructors before matrix(): reshape() needs an arr…
mns-nordicals Jul 7, 2026
6a2c62b
Read fun_name through handler_field like every other handler property
mns-nordicals Aug 2, 2026
6a36efc
Test direct matrix fill constructors
t-kalinowski Aug 15, 2026
c4c13ea
Use wide kinds for size guards
t-kalinowski Aug 15, 2026
caaa55a
Update wide size guard snapshot
t-kalinowski Aug 15, 2026
72c1a84
Respect local closures in fill detection
t-kalinowski Aug 15, 2026
ff65a37
Clarify assumed-shape scalar handling
t-kalinowski Aug 15, 2026
9f85f73
test: execute short-circuit regressions
t-kalinowski Aug 15, 2026
4843453
Qualify undefined cases in semantics contract
t-kalinowski Aug 15, 2026
4de538c
fix: preserve scalar short-circuit semantics
t-kalinowski Aug 15, 2026
3c3b728
Merge updated recycling guards
t-kalinowski Aug 15, 2026
a198596
Merge semantics vignette updates
t-kalinowski Aug 15, 2026
d33e63e
Support zero-length BLAS contractions
t-kalinowski Aug 15, 2026
fa10219
Guard deferred-shape reassignments
t-kalinowski Aug 15, 2026
9abf66c
Remove redundant BLAS guard snapshot
t-kalinowski Aug 15, 2026
b1f42ac
Use ptrdiff shape guards
t-kalinowski Aug 15, 2026
7e0a3da
Size diag identities from scalar values
t-kalinowski Aug 15, 2026
90587f1
Resolve scalar size expressions
t-kalinowski Aug 15, 2026
2756b9c
Preserve long fill lengths
t-kalinowski Aug 15, 2026
d7fae38
Cast completed real size expressions
t-kalinowski Aug 15, 2026
6dacb9a
Respect local closures in fill detection
t-kalinowski Aug 15, 2026
e67b6ff
Document prod result type divergence
t-kalinowski Aug 15, 2026
5b59abc
Format assignment shape guard
t-kalinowski Aug 15, 2026
b5aa565
test: run full conformability grid by default
t-kalinowski Aug 15, 2026
0512dcf
Merge latest recycling fixes
t-kalinowski Aug 15, 2026
96b66e1
fix: keep scalar logical operators scalar
t-kalinowski Aug 15, 2026
d669b9d
Merge updated conformability guards
t-kalinowski Aug 15, 2026
b3caad2
test: execute bounded repeat BLAS lowering
t-kalinowski Aug 15, 2026
3cbf79a
Isolate flang auto-preference test state
t-kalinowski Aug 15, 2026
db1d0f2
Merge flang test isolation fix
t-kalinowski Aug 15, 2026
32e060b
Merge full conformability grid fixes
t-kalinowski Aug 15, 2026
0c63a7c
test: update wide ifelse guard snapshot
t-kalinowski Aug 15, 2026
bf0daf1
fix: handle empty symmetric BLAS products
t-kalinowski Aug 15, 2026
b6824c5
fix: guard dynamic empty BLAS outputs
t-kalinowski Aug 15, 2026
ea6ae9e
Merge commit 'b6824c54d78b86b2ebf5679a721f0985193cfe56' into fix/pr14…
t-kalinowski Aug 15, 2026
375e231
test: cover numeric size expression domains
t-kalinowski Aug 15, 2026
3b0c80d
Merge conformability fixes into covr registry
t-kalinowski Aug 15, 2026
4bf331f
Align numeric size expression domains
t-kalinowski Aug 15, 2026
e059731
test: cover shadowed diag size closures
t-kalinowski Aug 15, 2026
f6c9a2f
Reject local closures in result sizes
t-kalinowski Aug 15, 2026
64d29d4
Isolate auto-flang compiler tests
t-kalinowski Aug 15, 2026
48e7afd
Merge PR #145 test isolation into PR #146
t-kalinowski Aug 15, 2026
8aafe7c
test: cover signed size floor division
t-kalinowski Aug 15, 2026
f4d4749
Merge test isolation into covr registry
t-kalinowski Aug 15, 2026
702cbd6
Merge conformability fixes into short-circuit lowering
t-kalinowski Aug 15, 2026
6e92398
Use fmod for C size remainders
t-kalinowski Aug 15, 2026
cb71505
Merge conformability fixes into refusal diagnostics
t-kalinowski Aug 15, 2026
9aca06d
Keep size floor division in the real domain
t-kalinowski Aug 15, 2026
e1fa87d
Preserve integer exponents in size powers
t-kalinowski Aug 15, 2026
ba12e46
Update size floor division snapshots
t-kalinowski Aug 15, 2026
7b49af0
Merge conformability fixes into code generation cleanups
t-kalinowski Aug 15, 2026
2f81665
Merge conformability fixes into semantics vignette
t-kalinowski Aug 15, 2026
de01764
Promote size reducers to one numeric domain
t-kalinowski Aug 15, 2026
76852ef
Merge conformability fixes into maintainability
t-kalinowski Aug 15, 2026
a6d0cab
Update size reducer snapshots
t-kalinowski Aug 15, 2026
d375d6b
Reject empty outer product outputs
t-kalinowski Aug 15, 2026
b970725
Handle size reducer arity explicitly
t-kalinowski Aug 15, 2026
63cda9a
Support empty matrix RHS in QR solves
t-kalinowski Aug 15, 2026
4fbdfb2
Reject empty right-hand sides in square solves
t-kalinowski Aug 15, 2026
a012f98
Prevent triangular solves from overwriting coefficients
t-kalinowski Aug 15, 2026
e519be6
Update QR solve snapshots
t-kalinowski Aug 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ Imports:
Suggests:
bench,
cli,
knitr,
pkgload (>= 1.4.0),
rlang,
rmarkdown,
testthat (>= 3.0.0),
withr
VignetteBuilder: knitr
Config/testthat/edition: 3
Config/testthat/parallel: true
Config/testthat/start-first: unary-intrinsics, loops
Expand Down
79 changes: 79 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,85 @@

* Successful flang availability checks are now reused for the rest of the R
session. Restart R after changing the flang toolchain.
- `c()` now accepts matrix and array arguments, flattening them in
column-major order like R (previously `c(m)` for a matrix `m` errored
with "all args passed to c() must be scalars or 1-d arrays"). `as.vector()`
is now supported: it drops dimensions (mode `"any"`, the default,
preserves the type; `mode = "double"`/`"integer"`/`"numeric"` coerce).

- Reassigning a variable now requires the new value's shape to be
compatible with the declared shape, extending the existing rank check
to every dimension: quickr cannot re-declare a Fortran variable the
way R rebinds a symbol. A statically known mismatch (e.g.
`x <- numeric(2); x <- numeric(3)`) is a compile-time error;
dimensions that cannot be compared at compile time are checked at run
time. Previously such reassignments silently kept the old shape or
produced invalid Fortran. This also covers reassignment between a scalar
and an array in either direction: `x <- numeric(n); x <- 0` used to
broadcast the scalar across every element, and assigning an array to a
scalar variable used to keep only its first element, where R rebinds the
symbol in both cases. Locals declared with unknown (`NA`) dims still
reallocate on assignment, like R.

- `diag(x)` with a length-1 `x` and no `nrow`/`ncol` now builds the
identity matrix of size `x`, matching R. Previously only a *literal*
size took that path, so `diag(n)` with `n` declared `integer(1)`
returned a 1x1 matrix containing `n` instead of the n-by-n identity —
a silent divergence in both shape and values. As in R the size is
`as.integer(x)`, so a `double` or `logical` `x` works and truncates
toward zero (`diag(3.7)` is the 3x3 identity, as in R). Use
`diag(x, nrow)` for a 1x1 matrix holding `x`.

- `declare()` size expressions now accept `as.integer()`, which lowers to
Fortran's `INT()` and to an integer cast in the generated C bridge.

- Elementwise operations (arithmetic, comparisons, `&`, `|`) now require
operand lengths to match, unless one operand is a scalar or a vector is
combined column-wise with a matrix whose rows it spans. R-style partial
recycling was never implemented: expressions like `x + y` with
`length(x) == 4`, `length(y) == 2` previously compiled to code that read
out of bounds. Statically unequal lengths are now a compile-time error;
lengths that cannot be verified at compile time are checked at run time.

- `1x1`-matrix operands now follow R's rules: in arithmetic against a
vector of statically known length other than 1 they are treated as
scalars and the result is a plain vector (which R allows, with a
deprecation warning), while in comparisons and `&`/`|` they are treated
as one-row matrices, so mismatched shapes are rejected — matching R,
which raises an error. When the vector's length is only known at run
time, the result's shape would depend on that value (R keeps the `1x1`
dims for a length-1 vector and drops them otherwise), so arithmetic
also takes the one-row-matrix rule: a runtime check requires length 1
and the result is a `1x1` matrix; longer vectors raise an error where
R would recycle. Previously a `1x1` matrix was scalarized in arithmetic
and not shape-checked at all in comparisons and `&`/`|`, so e.g.
`x < m` with `m` a `1x1` matrix failed to build with a Fortran rank
mismatch instead of a quickr error, an operand needing a cast failed to
build even in arithmetic, and a symbolic-length `x + m` returned a plain
vector where R returns a `1x1` matrix.

- `&&` and `||` now behave like R's scalar control operators. They
require length-1 logical operands (longer operands are a compile-time
error, as they are a runtime error in R; use `&`/`|` for elementwise
logic), and they short-circuit: the right operand is evaluated only when
the left side does not decide the answer, so idioms like
`while (i <= n && x[i] > 0)` are safe. Previously they compiled exactly
like `&`/`|` — elementwise over vectors (returning answers where R
errors) and with both sides always evaluated.

- `solve(a, b)` now requires a square `a`, matching R. A rectangular `a`
previously fell through to a least-squares solve (dgels), returning an
answer where R raises `'a' (m x n) must be square`. Statically
rectangular systems are now a compile-time error; when squareness is not
known at compile time it is checked at run time. Use `qr.solve()` for
least-squares solutions of rectangular systems (unchanged).

- Complex operands in linear algebra (`%*%`, `crossprod()`, `solve()`,
`chol()`, ...) are now a compile-time error. quickr's lowerings use the
real (double-only) BLAS/LAPACK routines, which previously read complex
storage as reals and returned a plausible but wrong real result where R
returns a complex one. Elementwise complex arithmetic and the
mode-preserving `t()`/`diag()` are unaffected.

# quickr 0.3.0

Expand Down
10 changes: 0 additions & 10 deletions R/aaa-utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -274,16 +274,6 @@ str_flatten_commas <- function(...) {
paste0(unlist(c(character(), ...), use.names = FALSE), collapse = ", ")
}

str_flatten_args <- function(..., multiline = length(dots) >= 3) {
dots <- unlist(c(character(), ...), use.names = FALSE)
if (multiline) {
dots <- paste0("\n ", dots, collapse = ",")
paste(dots, "\n")
} else {
paste0(dots, collapse = ",")
}
}

interleave <- function(x, y) {
stopifnot(is.atomic(x), is.atomic(y), length(y) == 1L, typeof(x) == typeof(y))
drop_last(as.vector(rbind(x, y, deparse.level = 0L)))
Expand Down
97 changes: 84 additions & 13 deletions R/c-wrapper.R
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,16 @@ c_bridge_hoist_seq_checks <- function(hoist, from, to, by) {
}


as_c_name <- function(var, c_hoist = NULL) {
as_c_name <- function(var, c_hoist = NULL, preserve_numeric = FALSE) {
stopifnot(inherits(var, Variable))
if (isTRUE(preserve_numeric)) {
if (identical(var@mode, "double")) {
return(glue("Rf_asReal({var@name})"))
}
if (!var@mode %in% c("integer", "logical")) {
stop("unsupported numeric size expression mode: ", var@mode)
}
}
expr <- glue("Rf_asInteger({var@name})")
if (is.null(c_hoist)) {
return(expr)
Expand Down Expand Up @@ -531,13 +539,22 @@ dims2c_dim_index_expr <- function(cl, scope) {
get_size_name(var, axis)
}

dims2c_expr <- function(e, scope, c_hoist = NULL) {
dims2c_expr <- function(
e,
scope,
c_hoist = NULL,
preserve_numeric = FALSE
) {
if (is.null(e)) {
return(NULL)
}

if (inherits(e, Variable)) {
return(as_c_name(e, c_hoist = c_hoist))
return(as_c_name(
e,
c_hoist = c_hoist,
preserve_numeric = preserve_numeric
))
}

if (is_scalar_integer(e)) {
Expand All @@ -560,7 +577,11 @@ dims2c_expr <- function(e, scope, c_hoist = NULL) {
if (!inherits(var, Variable)) {
stop("could not resolve size: ", nm)
}
return(as_c_name(var, c_hoist = c_hoist))
return(as_c_name(
var,
c_hoist = c_hoist,
preserve_numeric = preserve_numeric
))
}

if (!is.call(e)) {
Expand All @@ -574,7 +595,12 @@ dims2c_expr <- function(e, scope, c_hoist = NULL) {
if (length(args) != 1L) {
stop("unsupported size expression: ", deparse1(e))
}
return(dims2c_expr(args[[1L]], scope, c_hoist = c_hoist))
return(dims2c_expr(
args[[1L]],
scope,
c_hoist = c_hoist,
preserve_numeric = preserve_numeric
))
}

if (identical(op, "length")) {
Expand Down Expand Up @@ -623,20 +649,55 @@ dims2c_expr <- function(e, scope, c_hoist = NULL) {
if (length(args) != 1L) {
stop("abs() expects one argument")
}
e1 <- dims2c_expr(args[[1L]], scope, c_hoist = c_hoist)
e1 <- dims2c_expr(
args[[1L]],
scope,
c_hoist = c_hoist,
preserve_numeric = preserve_numeric
)
return(glue("(({e1}) < 0 ? -({e1}) : ({e1}))"))
}

if (identical(op, "as.integer")) {
if (length(args) != 1L) {
stop("as.integer() expects one argument")
}
# a C cast to an integer type truncates toward zero, as R's
# as.integer() does
e1 <- dims2c_expr(
args[[1L]],
scope,
c_hoist = c_hoist,
preserve_numeric = TRUE
)
return(glue("((R_xlen_t)({e1}))"))
}

if (op %in% c("+", "-", "*", "/", "%/%", "%%", "^")) {
if (length(args) == 1L && op %in% c("+", "-")) {
e1 <- dims2c_expr(args[[1L]], scope, c_hoist = c_hoist)
e1 <- dims2c_expr(
args[[1L]],
scope,
c_hoist = c_hoist,
preserve_numeric = preserve_numeric
)
return(glue("({op}({e1}))"))
}
if (length(args) != 2L) {
stop("unsupported size expression: ", deparse1(e))
}
e1 <- dims2c_expr(args[[1L]], scope, c_hoist = c_hoist)
e2 <- dims2c_expr(args[[2L]], scope, c_hoist = c_hoist)
e1 <- dims2c_expr(
args[[1L]],
scope,
c_hoist = c_hoist,
preserve_numeric = preserve_numeric
)
e2 <- dims2c_expr(
args[[2L]],
scope,
c_hoist = c_hoist,
preserve_numeric = preserve_numeric
)
return(switch(
op,
`+` = glue("({e1} + {e2})"),
Expand All @@ -653,7 +714,13 @@ dims2c_expr <- function(e, scope, c_hoist = NULL) {
if (!length(args)) {
return("0")
}
rendered <- lapply(args, dims2c_expr, scope = scope, c_hoist = c_hoist)
rendered <- lapply(
args,
dims2c_expr,
scope = scope,
c_hoist = c_hoist,
preserve_numeric = preserve_numeric
)
cmp <- if (identical(op, "min")) "<" else ">"
reduce(rendered, \(a, b) glue("(({a}) {cmp} ({b}) ? ({a}) : ({b}))"))
} else {
Expand Down Expand Up @@ -837,9 +904,13 @@ fsub_extern_decl <- function(fsub) {
glue("{fsub_arg_var_c_type(var)} {var@name}__")
}
})
if (length(fsub_c_sig) >= 3L) {
fsub_c_sig <- paste0("\n ", fsub_c_sig)
args_sig <- if (length(fsub_c_sig) >= 3L) {
# one arg per line; join with a bare comma -- joining "\n "-prefixed
# elements with ", " leaves a trailing space on every line
paste0("\n ", fsub_c_sig, collapse = ",")
} else {
str_flatten_commas(fsub_c_sig)
}

glue("extern void {fsub@name}({str_flatten_commas(fsub_c_sig)});")
glue("extern void {fsub@name}({args_sig});")
}
Loading
Loading