Skip to main content

RAISE_CONDITION_HELPER_FN

Constant RAISE_CONDITION_HELPER_FN 

Source
pub(crate) const RAISE_CONDITION_HELPER_FN: &str = "function(.val, .call_default) {\n  # The condition object for one tagged value `.v`.\n  .cond <- function(.v) {\n    .msg <- .v$error\n    # `.v$call` is the call the Rust side captured, NULL to use the wrapper\'s\n    # call, or FALSE for a condition raised without one (`call = none`, R\'s\n    # `call. = FALSE`), which is signalled with `call = NULL`.\n    .call <- if (isFALSE(.v$call)) NULL else if (is.null(.v$call)) .call_default else .v$call\n    # A generated S3 method passes its frame instead of a call, so that the\n    # generic\'s call (`summary(x)` for `summary.cls(x)`, the assignment for a\n    # replacement method) is built only now, when a condition is raised. The\n    # resolver recovers the generic\'s call from that method frame.\n    if (is.environment(.call)) .call <- (function(env) {\n  frames <- sys.frames()\n  i <- length(frames)\n  while (i > 0L && !identical(frames[[i]], env)) i <- i - 1L\n  if (i == 0L) return(NULL)\n  call <- sys.call(i)\n  generic <- env[[\".Generic\"]]\n  if (!is.character(generic) || length(generic) != 1L) return(call)\n  if (length(call) > 1L && identical(call[[2L]], quote(`*tmp*`))) {\n    call[[2L]] <- as.name(names(formals(sys.function(i)))[[1L]])\n  }\n  if (!endsWith(generic, \"<-\")) {\n    call[[1L]] <- as.name(generic)\n    return(call)\n  }\n  call$value <- NULL\n  call[[1L]] <- as.name(substr(generic, 1L, nchar(generic) - 2L))\n  call(\"<-\", call, quote(value))\n})(.call)\n    .class <- .v$class\n    # `arg_error!` writes the class \".miniextendr_conversion_error_class\"\n    # where the crate\'s `conversion_error_class` belongs: only the generated R\n    # knows those classes, bound under that name at the end of the wrappers\n    # preamble, so they replace it here. The copy of this helper that signals\n    # deferred conditions lives in the base namespace, where the name is\n    # unbound and the marker is dropped.\n    .at <- match(\".miniextendr_conversion_error_class\", .class)\n    if (!is.na(.at)) {\n      .class <- append(.class[-.at], get0(\".miniextendr_conversion_error_class\", ifnotfound = NULL), after = .at - 1L)\n    }\n    # `.v$data` is an optional named list of structured fields (from the\n    # macros\' `data = ...` form). When present, splice its named elements into\n    # the condition object alongside message/call/kind so handlers can read\n    # `e$<name>`. `utils::modifyList` keeps the base fields and appends the\n    # data fields; a malformed (non-list / unnamed) payload is ignored.\n    # `keep.null = TRUE`: a field the Rust side set to `RValue::Null` is a\n    # present field whose value is NULL, and must stay in `names(e)`; the\n    # default would silently drop it (found downstream via a\n    # `RConditionData::data()` returning an explicit NULL entry).\n    .data <- .v$data\n    # A cause the Rust side attached (`RCondition::with_parent`) travels as\n    # the tagged value `.mx_parent` and becomes the condition object\n    # `e$parent`, as `rlang::abort(parent = )` chains conditions. It is not\n    # signalled, and its classes stay out of this condition\'s.\n    if (is.list(.data) && !is.null(.data[[\".mx_parent\", exact = TRUE]])) {\n      .parent <- .cond(.data[[\".mx_parent\", exact = TRUE]])\n      .data[[\".mx_parent\"]] <- NULL\n      .data[[\"parent\"]] <- .parent\n    }\n    .cond_fields <- function(base) {\n      if (is.null(.data) || !is.list(.data) || is.null(names(.data))) {\n        base\n      } else {\n        utils::modifyList(base, .data, keep.null = TRUE)\n      }\n    }\n    switch(.v$kind,\n      warning = structure(.cond_fields(list(message = .msg, call = .call, kind = \"warning\")),\n        class = c(.class, \"rust_warning\", \"simpleWarning\", \"warning\", \"condition\")),\n      message = structure(.cond_fields(list(message = paste0(.msg, \"\\n\"), call = NULL, kind = \"message\")),\n        class = c(.class, \"rust_message\", \"simpleMessage\", \"message\", \"condition\")),\n      condition = structure(.cond_fields(list(message = .msg, call = .call, kind = \"condition\")),\n        class = c(.class, \"rust_condition\", \"simpleCondition\", \"condition\")),\n      panic = structure(list(message = .msg, call = .call, kind = \"panic\"),\n        class = c(\"rust_error\", \"simpleError\", \"error\", \"condition\")),\n      # error / result_err / none_err / conversion / other: an error whose class\n      # vector and data (if any) come from the Rust side, e.g. a\n      # `Result<T, E: RConditionData>`.\n      structure(.cond_fields(list(message = .msg, call = .call, kind = .v$kind)),\n        class = c(.class, \"rust_error\", \"simpleError\", \"error\", \"condition\")))\n  }\n  .c <- .cond(.val)\n  switch(.val$kind,\n    warning = warning(.c),\n    message = message(.c),\n    condition = signalCondition(.c),\n    stop(.c)\n  )\n  invisible(NULL)\n}";
Expand description

R source of the .miniextendr_raise_condition helper as a bare function(.val, .call_default) expression.

crate::deferred_condition evaluates this self-contained expression once (in the base namespace) to signal queued conditions from Rust without depending on any package namespace. write_r_wrappers_to_file renders the same template with the preamble’s .miniextendr_frame_call binding as the resolver instead of a second inline copy. Keeping one source guarantees identical class layering and data splicing.

The helper dispatches on .val$kind (see crate::error_value::kind) and re-raises the tagged Rust condition value as the matching R condition: stop() for the error kinds, warning() / message() / signalCondition() for the non-fatal ones (returning invisible(NULL)). The marker class it replaces is the one arg_error! writes (#1740). A frame in the call slot or as the default (a generated S3 method passes environment(), #1851) is resolved to the generic’s call with an inline copy of frame_call_helper_fn!, since the base-namespace copy of this helper cannot see the preamble’s .miniextendr_frame_call.