Hi Luke,

Thanks for the sleuthing and the suggested workaround. Unfortunately the
actual package code is a bit more complex, and surprisingly that breaks the
workaround via compiler::compile() (it fails just like the interpreted
version). In particular, my code is trying to guard against the (very
unlikely) scenario in which the calling code has redefined (or undefined)
`<-`, by explicitly qualifying it as base::`<-`():

f <- function (value) {
     if (missing(value)) {
         evalq(x[1L, ], .GlobalEnv)
     } else {
         assign_expr <- substitute(base::`<-`(x[1L, ], value), list(value =
value))
         cmp_assign_expr <- compiler::compile(assign_expr)
         eval(cmp_assign_expr, .GlobalEnv)
     }
}

(Incidentally I agree that this could be written much simpler, without
eval(); however, the purpose of the package is to provide a generic
mechanism to allow defining aliases to complex expressions via NSE; e.g.
you might write `ax := x[1L, ]`, and `ax` would henceforth operate as an
alias for that expression.)

Cheers,
Konrad


On Mon, 15 Dec 2025 at 04:26, <[email protected]> wrote:

> On Sun, 14 Dec 2025, Simon Urbanek wrote:
>
> > Konrad,
> >
> > I can reproduce this is current R-devel on any platform, so this should
> be very easy to reproduce, it is not OS-specific at all - all R-devel
> checks will flag it eventually. I would guess this is from:
> >
> > r89121 | luke | 2025-12-09 04:28:24 +1300 (Tue, 09 Dec 2025) | 3 lines
> >
> > Mark values returned by active binding functions as not mutable to
> > prevent unintended mutation in complex assignments.
> >
> > So it looks like it is intentional. May need some discussion on whether
> this requires some re-design of your package to make it safer or if it is a
> valid use-cases that may need further consideration.
>
> Looks like it is a side effect of that bug fix that it is waking up a
> misfeature of the interpreted complex assignment code. The compiled
> version of the complex assignment code is cleaner and does not have
> this issue, so you could use
>
> f <- function (value) {
>      if (missing(value)) {
>          evalq(x[1L, ], .GlobalEnv)
>      } else {
>          assign_expr <- substitute(x[1L, ] <- value, list(value = value))
>          cmp_assign_expr <- compiler::compile(assign_expr)
>          eval(cmp_assign_expr, .GlobalEnv)
>      }
> }
>
> Depending on how close this is to what you are really doing Konrad,
> you can also use .GlobalEnv$x and avoid eval():
>
> f <- function (value) {
>      if (missing(value))
>          .GlobalEnv$x[1L, ]
>      else
>          .GlobalEnv$x[1L, ] <- value
> }
>
> I'll see if I can figure out what is going on in the interpreted
> assignment code.
>
> Best,
>
> luke
>
> >
> > Cheers,
> > Simon
> >
> >
> >> On 15 Dec 2025, at 10:50, Konrad Rudolph <[email protected]>
> wrote:
> >>
> >> Hi all,
> >>
> >> One of my packages is failing on CRAN in R-devel [1], and I was
> requested
> >> to fix it. However, it is *only* failing on one specific configuration,
> >> 'r-devel-linux-x86_64-debian-gcc'. All other combinations — clang on
> >> Debian, both clang and GCC on Fedora, and Windows — keep running just
> fine.
> >> As my package is not using compiled code or anything OS-specific, I am
> at a
> >> loss to explain this highly specific failure. Before attempting to
> build a
> >> container image with this specific configuration locally (… are these
> >> configurations available as ready-made images?), I wanted to check if
> there
> >> was an obvious change in R-devel which might explain the issue.
> >>
> >> There are two failures, both with the same error message: “cannot change
> >> value of locked binding for '*tmp*'”. My package’s code isn’t
> attempting to
> >> directly change `*tmp*`, but it is using eval() to perform subset
> >> assignment to a complex expression inside an active binding. Here’s a
> >> minimal code snippet that *should* be equivalent to one of the two
> >> failures, and which should therefore also fail (in the last line):
> >>
> >>   x = data.frame(a = 1 : 2, b = c('a', 'b'))
> >>
> >>   f = function (value) {
> >>     if (missing(value)) {
> >>       evalq(x[1L, ], .GlobalEnv)
> >>     } else {
> >>       assign_expr = substitute(x[1L, ] <- value, list(value = value))
> >>       eval(assign_expr, .GlobalEnv)
> >>     }
> >>   }
> >>
> >>   makeActiveBinding('ax', f, .GlobalEnv)
> >>
> >>   ax[1L] = 3L
> >>
> >> I had a look at the changes in in R-devel, but I couldn’t find anything
> >> obviously relevant. In particular, the code of R_MakeActiveBinding()
> hasn’t
> >> been touched in literally decades, and similar for the code that (as
> far as
> >> I understand) performs subset assignment, applydefine().
> >>
> >> Does anybody have an idea what might be going on here, or how to debug
> this
> >> issue?
> >>
> >> Cheers,
> >> Konrad
> >>
> >> [1] https://cran.r-project.org/web/checks/check_results_aka.html
> >>
> >> --
> >> Konrad Rudolph
> >>
> >> [[alternative HTML version deleted]]
> >>
> >> ______________________________________________
> >> [email protected] mailing list
> >> https://stat.ethz.ch/mailman/listinfo/r-package-devel
> >>
> >
> > ______________________________________________
> > [email protected] mailing list
> > https://stat.ethz.ch/mailman/listinfo/r-package-devel
> >
>
> --
> Luke Tierney
> Ralph E. Wareham Professor of Mathematical Sciences
> University of Iowa                  Phone:             319-335-3386
> Department of Statistics and        Fax:               319-335-3017
>     Actuarial Science
> 241 Schaeffer Hall                  email:   [email protected]
> Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu/



-- 
Konrad Rudolph

        [[alternative HTML version deleted]]

______________________________________________
[email protected] mailing list
https://stat.ethz.ch/mailman/listinfo/r-package-devel

Reply via email to