1//! Precondition markers: [`Checked<T>`] and [`Unchecked<T>`].
2//!
3//! A generated R wrapper checks each argument's R type before the `.Call()`
4//! (`is.integer(n)`, `length(n) == 1L`, ...), one guard per check, so a
5//! wrong argument fails in R with a message naming it. These are the
6//! *type-derived* checks: they come from the parameter's Rust type. Without
7//! them the Rust conversion still refuses bad input, with the same
8//! argument-error condition (`e$param`, `e$rust_type`, `kind =
9//! "conversion"`); only the message is the conversion's. Dropping them saves
10//! one `isTRUE()` guard per check on a hot path.
11//!
12//! Whether a parameter keeps them is decided per parameter, the most specific
13//! spelling first:
14//!
15//! | rank | spelling | where |
16//! |------|----------|-------|
17//! | 1 | `x: Checked<T>` / `x: Unchecked<T>` | a parameter of a function or inherent-impl method |
18//! | 1 | `#[miniextendr(preconditions)]` / `#[miniextendr(no_preconditions)]` on the parameter | a function parameter |
19//! | 1 | `preconditions(x, y)` / `no_preconditions(x)` | a method attribute (inherent and trait impls) |
20//! | 2 | `preconditions` / `no_preconditions` | the function or method attribute |
21//! | 3 | `preconditions` / `no_preconditions` | the impl-block attribute |
22//! | 4 | `preconditions = true \| false` | `[package.metadata.miniextendr]` in `Cargo.toml` |
23//! | 5 | the `no-preconditions-default` cargo feature | the whole build |
24//! | 6 | on | the framework default |
25//!
26//! ```ignore
27//! use miniextendr_api::{Checked, List, Unchecked, miniextendr};
28//!
29//! /// `n_iter` keeps its guards; the function's `no_preconditions` drops
30//! /// those of `data` and `tol`.
31//! #[miniextendr(no_preconditions)]
32//! pub fn fit(data: List, n_iter: Checked<i32>, tol: f64) -> f64 {
33//! let n = *n_iter; // or n_iter.into_inner()
34//! // ...
35//! # let _ = (data, n, tol); 0.0
36//! }
37//!
38//! /// `xs` skips `is.double(xs)`; `factor` keeps its guards.
39//! #[miniextendr]
40//! pub fn scale_by(factor: f64, xs: Unchecked<Vec<f64>>) -> Vec<f64> {
41//! xs.into_inner().into_iter().map(|x| x * factor).collect()
42//! }
43//! ```
44//!
45//! The switch covers the type-derived checks only. The checks an author names
46//! (`inherits`, `no_na`), the `match_arg` / `choices` validation and the Rust
47//! conversion stay whatever the spelling. A marker and a keyword that
48//! disagree on one parameter are a compile error; two keywords follow the
49//! pair rule, the last one written wins.
50//!
51//! The marker goes outermost (`Checked<Option<i32>>`, not
52//! `Option<Checked<i32>>`), at most one per parameter, and only on a
53//! parameter that has a type-derived check to keep or drop (not `SEXP`,
54//! `Missing<T>`, `ExternalPtr<T>`, `&Dots`, a choice parameter, ...). It
55//! cannot appear in a `#[miniextendr]` trait's method signature (spell it on
56//! the impl, `#[miniextendr(preconditions(x))]`) nor on an
57//! `extern "C-unwind"` function, which has no generated conversion to unwrap
58//! it. A marker is a parameter type only: as a return type it is a compile
59//! error.
60//!
61//! # Not a runtime type
62//!
63//! The guards are R text the macro writes before any type is resolved, so
64//! only the syntax `Checked<..>` / `Unchecked<..>` on the parameter selects
65//! them. The markers therefore implement neither `TryFromSexp` nor `IntoR`:
66//! the macro converts the inner `T` and wraps it (`from_inner`) before the
67//! call. A type alias (`type N = Checked<i32>`) or a rename is not seen as a
68//! marker, and fails to compile (E0277, no `TryFromSexp`) instead of silently
69//! following the crate default. The return-visibility markers
70//! (`Invisible<T>`, `Visible<T>`) differ: they forward `IntoR`, because a
71//! missed visibility marker still has a correct value to convert.
72//!
73//! # Name
74//!
75//! `Checked` means "the R-side guards are kept". It is not an
76//! overflow-checked conversion, and not the thread-checked FFI variants
77//! (`#[r_ffi_checked]`).
7879/// Parameter marker: keep this parameter's type-derived R-side checks,
80/// whatever the function, impl or crate default says. Transparent over `T`;
81/// see the [module docs](self).
82#[repr(transparent)]
83#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for Checked<T> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Checked",
&&self.0)
}
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for Checked<T> {
#[inline]
fn clone(&self) -> Self { Checked(::core::clone::Clone::clone(&self.0)) }
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy> ::core::marker::Copy for Checked<T> { }Copy, #[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::marker::StructuralPartialEq for
Checked<T> {
}
#[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::cmp::PartialEq for Checked<T> {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl<T: ::core::cmp::Eq> ::core::cmp::Eq for Checked<T> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq<T>; }
}Eq, #[automatically_derived]
impl<T: ::core::cmp::PartialOrd> ::core::cmp::PartialOrd for Checked<T> {
#[inline]
fn partial_cmp(&self, other: &Self)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0)
}
}PartialOrd, #[automatically_derived]
impl<T: ::core::cmp::Ord> ::core::cmp::Ord for Checked<T> {
#[inline]
fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.0, &other.0)
}
}Ord, #[automatically_derived]
impl<T: ::core::hash::Hash> ::core::hash::Hash for Checked<T> {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}Hash, #[automatically_derived]
impl<T: ::core::default::Default> ::core::default::Default for Checked<T> {
#[inline]
fn default() -> Self { Checked(::core::default::Default::default()) }
}Default)]
84pub struct Checked<T>(pub T);
8586/// Parameter marker: drop this parameter's type-derived R-side checks, and
87/// let the Rust conversion word a bad argument. Transparent over `T`; see the
88/// [module docs](self).
89#[repr(transparent)]
90#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for Unchecked<T> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Unchecked",
&&self.0)
}
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for Unchecked<T> {
#[inline]
fn clone(&self) -> Self {
Unchecked(::core::clone::Clone::clone(&self.0))
}
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy> ::core::marker::Copy for Unchecked<T> { }Copy, #[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::marker::StructuralPartialEq for
Unchecked<T> {
}
#[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::cmp::PartialEq for Unchecked<T> {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl<T: ::core::cmp::Eq> ::core::cmp::Eq for Unchecked<T> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq<T>; }
}Eq, #[automatically_derived]
impl<T: ::core::cmp::PartialOrd> ::core::cmp::PartialOrd for Unchecked<T> {
#[inline]
fn partial_cmp(&self, other: &Self)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0)
}
}PartialOrd, #[automatically_derived]
impl<T: ::core::cmp::Ord> ::core::cmp::Ord for Unchecked<T> {
#[inline]
fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.0, &other.0)
}
}Ord, #[automatically_derived]
impl<T: ::core::hash::Hash> ::core::hash::Hash for Unchecked<T> {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}Hash, #[automatically_derived]
impl<T: ::core::default::Default> ::core::default::Default for Unchecked<T> {
#[inline]
fn default() -> Self { Unchecked(::core::default::Default::default()) }
}Default)]
91pub struct Unchecked<T>(pub T);
9293macro_rules!precondition_marker_impls {
94 ($name:ident) => {
95impl<T> $name<T> {
96/// Wrap a converted argument. Generated code calls this after the
97 /// conversion of the inner `T`; user code can build the marker
98 /// directly (`Checked(x)`).
99#[doc(hidden)]
100 #[inline]
101pub const fn from_inner(value: T) -> Self {
102$name(value)
103 }
104105/// The wrapped value.
106#[inline]
107pub fn into_inner(self) -> T {
108self.0
109}
110 }
111112impl<T> ::std::ops::Deref for $name<T> {
113type Target = T;
114115#[inline]
116fn deref(&self) -> &T {
117&self.0
118}
119 }
120121impl<T> ::std::ops::DerefMut for $name<T> {
122#[inline]
123fn deref_mut(&mut self) -> &mut T {
124&mut self.0
125}
126 }
127 };
128}
129130impl<T> Checked<T> {
/// Wrap a converted argument. Generated code calls this after the
/// conversion of the inner `T`; user code can build the marker
/// directly (`Checked(x)`).
#[doc(hidden)]
#[inline]
pub const fn from_inner(value: T) -> Self { Checked(value) }
/// The wrapped value.
#[inline]
pub fn into_inner(self) -> T { self.0 }
}
impl<T> ::std::ops::Deref for Checked<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T { &self.0 }
}
impl<T> ::std::ops::DerefMut for Checked<T> {
#[inline]
fn deref_mut(&mut self) -> &mut T { &mut self.0 }
}precondition_marker_impls!(Checked);
131impl<T> Unchecked<T> {
/// Wrap a converted argument. Generated code calls this after the
/// conversion of the inner `T`; user code can build the marker
/// directly (`Checked(x)`).
#[doc(hidden)]
#[inline]
pub const fn from_inner(value: T) -> Self { Unchecked(value) }
/// The wrapped value.
#[inline]
pub fn into_inner(self) -> T { self.0 }
}
impl<T> ::std::ops::Deref for Unchecked<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T { &self.0 }
}
impl<T> ::std::ops::DerefMut for Unchecked<T> {
#[inline]
fn deref_mut(&mut self) -> &mut T { &mut self.0 }
}precondition_marker_impls!(Unchecked);
132133#[cfg(test)]
134mod tests {
135use super::*;
136137#[test]
138fn markers_are_transparent_over_the_value() {
139assert_eq!(std::mem::size_of::<Checked<f64>>(), 8);
140assert_eq!(std::mem::size_of::<Unchecked<u16>>(), 2);
141let mut n = Checked::from_inner(3_i32);
142*n += 1;
143assert_eq!(*n, 4);
144assert_eq!(n.into_inner(), 4);
145let s = Unchecked::from_inner("a");
146assert_eq!(s.len(), 1);
147assert_eq!(s.into_inner(), "a");
148 }
149}