Non-API R Functions Tracking
This document tracks usage of non-API R functions in miniextendr. Non-API functions are detected by R CMD check via tools:::.checksosymbols.
This document tracks usage of non-API R functions in miniextendr.
Non-API functions are detected by R CMD check via tools:::.check_so_symbols.
Reference: https://svn.r-project.org/R/trunk/src/library/tools/R/sotools.R
πCurrently Used Non-API Functions
| Function | Location | Feature Gate | Notes |
|---|---|---|---|
| (none currently) |
πFeature-Gated Non-API Functions
Locations intentionally omit line numbers (they drift); use search on the symbol name.
| Function | Location | Feature Gate | Notes |
|---|---|---|---|
DATAPTR | miniextendr-api/src/sys.rs | nonapi | Mutable data pointer - prefer DATAPTR_RO or DATAPTR_OR_NULL |
ATTRIB | miniextendr-api/src/sys.rs | nonapi | Attribute pairlist. Test for attributes with SexpExt::has_attributes(), read one with SexpExt::get_attr(), compare objects with SexpExt::is_identical(). Lint MXL304 flags uses and hand-written declarations |
SET_ATTRIB | miniextendr-api/src/sys.rs | nonapi | Replace the attribute pairlist. Use SexpExt::set_attr() for one attribute, DUPLICATE_ATTRIB / SHALLOW_DUPLICATE_ATTRIB to copy all of them. MXL304 |
OBJECT, SET_OBJECT | miniextendr-api/src/sys.rs | nonapi | Object bit. Use SexpExt::is_object() (Rf_isObject); setting a class attribute sets the bit |
LEVELS, SETLEVELS | miniextendr-api/src/sys.rs | nonapi | The gp header field, not factor levels (those are the levels attribute, SexpExt::get_levels()) |
R_curErrorBuf | miniextendr-api/src/sys.rs | nonapi | Current error message buffer - used for better error messages from worker thread |
R_CStackStart | miniextendr-api/src/sys.rs (nonapi_stack) | nonapi | Process-global stack top; package code must not rewrite it for secondary-thread R calls |
R_CStackLimit | miniextendr-api/src/sys.rs (nonapi_stack) | nonapi | Process-global stack limit; disabling it does not make Rβs API thread-safe |
R_CStackDir | miniextendr-api/src/sys.rs (nonapi_stack) | nonapi | Process-global stack growth direction (-1 = down, 1 = up) |
ptr_R_WriteConsoleEx | miniextendr-api/src/progress.rs | indicatif (implies nonapi) | R console hook used by indicatif TermLike backend |
Note: miniextendr-engine is entirely non-API (uses Rembedded.h/Rinterface.h for embedding R) and is not tracked here.
πTest Utilities (Not Part of R Package)
The file miniextendr-api/tests/r_test_utils.rs contains a disable_r_stack_checking() function
that directly accesses R_CStackLimit. This code is never part of the R package because:
- Itβs in the
tests/directory ofminiextendr-api, which is only compiled forcargo test - It uses
miniextendr-engineto embed R for Rust unit tests - The example R package (
rpkg/) only vendorsminiextendr-api/src/andminiextendr-macros/src/ - Test directories are excluded from vendoring
This test utility exists because cargo test embeds R in a non-standard way
(not via R CMD). It is test-harness infrastructure, not permission for an R
package to call R APIs from secondary threads. The package-facing helpers and
fixtures that currently suggest otherwise are tracked for removal in #1352.
πAPI Functions (Safe to Use)
These functions are NOT in the non-API list and are safe:
| Function | Status |
|---|---|
DATAPTR_RO | API (safe) |
DATAPTR_OR_NULL | API (safe) |
R_ExternalPtrAddr | API (safe) |
R_ExternalPtrTag | API (safe) |
R_ExternalPtrProtected | API (safe) |
R_MakeExternalPtr | API (safe) |
Rf_allocVector | API (safe) |
Rf_protect / Rf_unprotect | API (safe) |
Rf_install | API (safe) |
Rf_xlength | API (safe) |
STRING_ELT | API (safe) |
R_CHAR | API (safe) |
TYPEOF | API (safe) |
R_getVarEx | API (safe, R >= 4.5.0) β single-frame/inherited variable lookup with an ifnotfound default; replaces the now-removed Rf_findVarInFrame |
ANY_ATTRIB | API (safe, R >= 4.5.0) β whether ATTRIB(x) is non-empty. R 4.4βs libR does not export it, so SexpExt::has_attributes() evaluates attributes(quote(x)) instead, which works on every supported R |
R_compute_identical | API (safe) β Rβs identical(); SexpExt::is_identical() passes identical()βs default flags (16) |
DUPLICATE_ATTRIB / SHALLOW_DUPLICATE_ATTRIB | API (safe) β copy every attribute (and the object bit) from one object to another |
TRUELENGTH is not declared at all: R 4.5βs R CMD check reports it as
non-API, and R 4.6βs libR no longer exports it, so a package referencing it
cannot load there.
πChoosing between ANY_ATTRIB and attributes()
Writing R Extensions recommends ANY_ATTRIB (added in R 4.5.0) in place of
ATTRIB for testing whether an object has attributes. miniextendr supports
R 4.4, whose libR does not export ANY_ATTRIB, and R loads a packageβs shared
object with every symbol resolved up front. A safe wrapper that called
ANY_ATTRIB would make every package built with it fail to load on R 4.4,
whether or not the wrapper ever ran. So SexpExt::has_attributes() evaluates
attributes(quote(x)) on every R version. That allocates a call and, when
attributes exist, the list attributes() returns.
The two answers differ in two cases. attributes() reports the tag names of
a pairlist as a names attribute, while ANY_ATTRIB ignores them;
has_attributes() follows attributes(), so pairlist(a = 1) has
attributes. A CHARSXP keeps Rβs string-cache chain in its attribute slot, so
ANY_ATTRIB can be non-zero for one; has_attributes() returns false, as
identical() skips that slot too.
To reproduce identical(x, FALSE), call
x.is_identical(SEXP::scalar_logical(false)) rather than combining a value
check with an attribute check.
πFull Non-API List (R 4.5.x trunk)
Click to expand complete list
πObsolete
chol_,chol2inv_,cg_,ch_,rg_fft_factor,fft_work,Brent_fmin,optif0
πNon-API in headers or no header
OutDec,PRIMOFFSET,RC_fopen,R_CollectFromIndexR_CompiledFileName,R_FileExistsR_FreeStringBuffer,R_FunTab,R_GE_setVFontRoutinesR_GetVarLocMISSING,R_MethodsNamespace,R_NewHashedEnvR_OpenCompiledFile,R_PV,R_ParseContextR_ParseContextLast,R_ParseContextLineR_ParseError,R_ParseErrorMsg,R_SrcfileSymbolR_SrcrefSymbol,R_Visible,R_addTaskCallbackR_cairoCdynload,R_data_classR_deferred_default_method,R_execMethodR_findVarLocInFrame,R_fopen,R_gc_tortureR_getTaskCallbackNames,R_get_arith_functionR_gzclose,R_gzgets,R_gzopen,R_ignore_SIGPIPER_isForkedChild,R_isMethodsDispatchOnR_moduleCdynload,R_primitive_genericR_primitive_methods,R_print,R_removeTaskCallbackR_running_as_main_program,R_setInternetRoutinesR_setLapackRoutines,R_setX11RoutinesR_set_prim_method,R_set_quick_method_checkR_set_standardGeneric_ptr,R_strtod4R_subassign3_dflt,R_taskCallbackRoutineRconn_fgetc,Rconn_printf,RdownloadRf_EncodeComplex,Rf_EncodeElementRf_EncodeEnvironment,Rf_EncodeIntegerRf_EncodeLogical,Rf_EncodeReal,Rf_GPrettyRf_NewEnvironment,Rf_PrintDefaultsRf_ReplIteration,Rf_Seql,Rf_addTaskCallbackRf_begincontext,Rf_callToplevelHandlersRf_checkArityCall,Rf_con_pushbackRf_copyMostAttribNoTs,Rf_deparse1,Rf_deparse1lineRf_dpptr,Rf_endcontext,Rf_envlengthRf_formatComplex,Rf_formatIntegerRf_formatLogical,Rf_formatReal,Rf_init_conRf_isProtected,Rf_mbrtowc,Rf_mkFalseRf_printNamedVector,Rf_printRealVectorRf_printVector,Rf_removeTaskCallbackByIndexRf_removeTaskCallbackByName,Rf_set_iconvRf_sortVector,Rf_strIsASCII,Rf_strchrRf_strrchr,Rf_ucstomb,Rf_utf8towcsRf_wcstoutf8,Rg_PolledEvents,Rg_set_col_ptrsRf_wait_usec,Ri18n_*,Rsock*,RunzipUNIMPLEMENTED_TYPE,baseRegisterIndexRf_csduplicated,Rf_currentTimedcar,dcdr,do_*,dqrrsd_,dqrxb_,dtypedummy_*,epslon_,extR_*,fdhessgetConnection,getPRIMNAME,known_to_be_latin1locale2charset,match5,matherrmax_contour_segments,Rf_mbcsToUcs2,Rf_memtrace_reportparseError,pythag_,rs_,rwarnc_tql2_,tqlrat_,tred1_,tred2_,utf8locale,yyllocR_opendir,R_readdir,R_closedir
πRinternals.h non-API
ENSURE_NAMEDMAX,IS_ASCII,IS_UTF8,SET_PRSEEN,ddfindSET_TYPEOF,SET_OBJECT,SET_S4_OBJECT,UNSET_S4_OBJECTSETLENGTH,SET_TRUELENGTH,SETLEVELSSET_ENVFLAGS,SET_FRAME,SET_ENCLOS,SET_HASHTABSET_PRENV,SET_PRVALUE,SET_PRCODE,STDVEC_DATAPTRIS_GROWABLE,SET_GROWABLE_BIT,SET_NAMEDR_PromiseExpr,R_tryWrapDDVAL,NAMED,INTERNAL,SYMVALUE,PRSEENINTEGER0,LOGICAL0,RAW0,REAL0,COMPLEX0LEVELS,FRAME,HASHTAB,ENVFLAGS,RDEBUG,SET_RDEBUGSTRING_PTR,VECTOR_PTR,DATAPTR,STDVEC_DATAPTRSET_FORMALS,SET_BODY,SET_CLOENVPRCODE,PRENV,PRVALUE,R_ncharRf_NonNullStringMatch,TRUELENGTH,XLENGTH_EXXTRUELENGTH,Rf_gsetVarRf_isValidString,Rf_isValidStringFR_shallow_duplicate_attrEXTPTR_PROT,EXTPTR_TAG,EXTPTR_PTROBJECT,IS_S4_OBJECTRf_allocSExp,Rf_isFrameBODY,FORMALS,CLOENV,ENCLOSR_Pretty
πRinterface.h / Rembedded.h non-API
AllDevicesKilled,R_CStackLimit,R_CStackStartR_ClearerrConsole,R_CleanTempDir,R_ConsolefileR_DefCallbacks,R_DefParams,R_DefParamsExR_DirtyImage,R_GUIType,R_GlobalContextR_HistoryFile,R_HistorySize,R_Home,R_HomeDirR_Interactive,R_OutputfileR_PolledEvents,R_ReplDLLdo1,R_ReplDLLinitR_RestoreGlobalEnv,R_RestoreGlobalEnvFromFileR_RestoreHistory,R_RunExitFinalizers,R_SaveGlobalEnvR_SaveGlobalEnvToFile,R_SelectEx,R_SetParamsR_SetWin32,R_SignalHandlers,R_SizeFromEnv,R_NoEchoR_Suicide,R_TempDir,R_checkActivityR_checkActivityEx,R_runHandlersR_setStartTime,R_set_command_line_argumentsR_setupHistory,R_timeout_handler,R_timeout_valR_wait_usec,RestoreAction,Rf_CleanEdRf_KillAllDevices,Rf_endEmbeddedR,Rf_initEmbeddedRRf_initialize_R,Rf_jump_to_toplevel,Rf_mainloopSaveAction,editorcleanall,fpu_setupfreeRUser,free_R_HOMEgetDLLVersion,getRUser,get_R_HOMEgetSelectedHandler,initStdinHandlerprocess_site_Renviron,process_system_Renvironprocess_user_Renviron,ptr_R_*readconsolecfg,run_Rmainloop,setup_Rmainloop
πR_ext/Connections.h non-API (before R 4.6)
R_new_custom_connection,R_ReadConnectionR_WriteConnection,R_GetConnection
R 4.6 lists these as experimental API (tools:::funAPI() gives eapi). R
4.4 and 4.5 export the same symbols and ship the same header, but
R CMD check notes them there as non-API calls. Built against R < 4.6, the
connections feature reaches a connectionβs state and bytes through R
functions instead (CONNECTIONS.md, βR versions before 4.6β), and only
R_new_custom_connection, which has no R-level alternative, remains.
πDeprecated/removed
call_R(removed in R 4.5.0)Rf_setSVector
πFeature Gate Strategy
When using non-API functions, wrap them in a nonapi feature:
#[cfg(feature = "nonapi")]
pub fn DATAPTR(x: SEXP) -> *mut c_void;
This allows users to opt-in to non-API usage while keeping the default build CRAN-compatible.
πR-version cfgs
A package is compiled against the R it installs into, so the build knows the
R version. miniextendr-apiβs build.rs reads R_MAJOR / R_MINOR from
$R_HOME/include/Rversion.h and sets one cfg per listed version at or below
it, cumulatively: r_4_5 on R 4.5 and later, r_4_6 on R 4.6 and later
(#1905). A build that finds no header (a wasm32 target, CIβs lint job with an
empty R_HOME) sets them all.
Use them for an entry point that a newer R adds, or that a newer R makes API:
- R adds it. Gate the binding in
sys.rson the cfg (ANY_ATTRIBandR_getVarExare#[cfg(r_4_5)]), and keep a fallback beside every use for the older R. Referencing an entry point the R lacks fails at load, because packages load withdyn.load(now = TRUE). - R makes it API. The entry point exists before that version, but
R CMD checknotes it as non-API there. Gate the call on the cfg when the older R offers another route to the same result, and keep the entry point on the newer side:connection/access.rsreads a connection throughR_GetConnectionunderr_4_6and throughsummary.connection()/readBin()/writeBin()before.
Gate a public item on a cfg only in the first case. There a missing
entry point would fail at load, and the gate turns that into a compile error.
In the second, the entry point works on the older R, so keep the public
signature the same on every R and switch the implementation inside it:
packages compile on each userβs machine against that userβs R, so an item
that exists only on newer R breaks the install for a user on an older R while
its author, on a newer one, never sees it. Gate a public item there only when
the older R has no route to what it returns at all (get_connection, the raw
handle, is R >= 4.6 only).
To add a version, append it to R_VERSION_CFGS in miniextendr-api/build.rs.
Test both sides: CI runs R CMD check on R 4.4 and release on every PR, and
on oldrel too on main or when the PR carries the oldrel label (add it to a
PR that touches these cfgs). The lint job compiles the newest side.