1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
use TokenStream;
use *;
use *;
use emit_if_compatible;
/// Export functions, types and constants defined in Rust as a Julia module.
///
/// This macro generates an initialization function. This function is used in combination with the
/// macros provided by the `JlrsCore.Wrap` module to automatically generate the content of that
/// module.
///
/// The syntax is as follows:
///
/// ```ignore
/// julia_macro! {
/// // init_function_name is the name of the generated initialization function.
/// //
/// // The name of the generated function must be unique, it's recommended you prefix it with
/// // the crate name. If your crate is named foo-jl, you should use a name like
/// // `foo_jl_init`.
/// become init_function_name;
///
/// // Exports the function `foo` as `bar` with documentation.
/// //
/// // The `unsafe extern "C" part of the signature must be elided, the signature is verified
/// // in the generated code to ensure it's correct and that the function uses the C ABI.
/// //
/// // The `as <exposed_name>` part is optional, by default the function is exported with the
/// // name it has in Rust, the exposed name can end in an exclamation mark.
/// //
/// // A docstring can be provided with the doc attribute; if multiple functions are exported
/// // with the same name it shoud only be documented once. All exported items can be
/// // documented, a multi-line docstring can be created by providing multiple doc attributes
/// // for the same item.
/// #[doc = " bar(arr::Array)"]
/// #[doc = ""]
/// #[doc = "Documentation for this function"]
/// fn foo(arr: Array) -> usize as bar;
///
/// // Exports the function `foo` as `bar!` in the `Base` module.
/// //
/// // This syntax can be used to extend existing functions.
/// fn foo(arr: Array) -> usize as Base.bar!;
///
/// // Exports the struct `MyType` as `MyForeignType`. `MyType` must implement `OpaqueType`
/// // or `ForeignType`.
/// struct MyType as MyForeignType;
///
/// // Exports `MyType::new` as `MyForeignType`, turning it into a constructor for that type.
/// //
/// // A Rust function is generated to call this method, so unlike free-standing functions
/// // exported methods don't have to use the C ABI.
/// in MyType fn new(arg0: TypedValue<u32>) -> TypedValueRet<MyType> as MyForeignType;
///
/// // Exports `MyType::add` as the function `increment!`.
/// //
/// // Methods that take `self` in some way must return a `RustResultRet` because the
/// // generated function tracks the borrow of `self` before calling the exported method. If
/// // `self` is taken by value, it's cloned after being tracked.
/// in MyType fn add(&mut self, incr: u32) -> RustResultRet<u32> as increment!;
///
/// // Exports the function `long_running_func`, the returned closure is executed on another
/// // thread.
/// //
/// // After dispatching the closure to another thread, the generated Julia function waits for
/// // the closure to return using an `AsyncCondition`. Because the closure is executed on
/// // another thread you can't call Julia functions or allocate Julia data from it, but it is
/// // possible to (mutably) access Julia data by tracking it.
/// //
/// // In order to be able to use tracked data from the closure, `Unbound` managed types must
/// // be used. Only `(Typed)ValueUnbound` and `(Typed)ArrayUnbound` exist, they're aliases
/// // for `(Typed)Value` and `(Typed)Array` with static lifetimes. The generated Julia
/// // function guarantees all data passed as an argument lives at least until the closure has
/// // finished, the tracked data must only be shared with that closure.
/// async fn long_running_func(
/// array: ArrayUnbound
/// ) -> JlrsResult<impl AsyncCallback<i32>>;
///
/// // Exports `MY_CONST` as the constant `MY_CONST`, its type must implement `IntoJulia`.
/// // `MY_CONST` can be defined in Rust as either static or constant data, i.e. both
/// // `static MY_CONST: u8 = 1` and `const MY_CONST: u8 = 1` can be exposed this way.
/// const MY_CONST: u8;
///
/// // Exports `MY_CONST` as the global `MY_GLOBAL`, its type must implement `IntoJulia`.
/// // `MY_CONST` can be defined in Rust as either static or constant data, i.e. both
/// // `static MY_CONST: u8 = 1` and `const MY_CONST: u8 = 1` can be exposed this way.
/// static MY_CONST: u8 as MY_GLOBAL;
/// }
/// ```
///
/// And this is all you need to do in Julia:
///
/// ```julia
/// module MyRustModule
/// using JlrsCore.Wrap
///
/// @wrapmodule("path/to/lib", :init_function_name)
///
/// function __init__()
/// @initjlrs
/// end
/// end
/// ```
///
/// It can be rather tricky to figure out how data is passed from Julia to Rust when `ccall`ing
/// a function written in Rust. Primitive and `isbits` types are passed by value, managed types
/// provided directly by jlrs are guaranteed to be boxed, all other types might be passed by
/// value or be boxed.
///
/// In order to avoid figuring out how such data is passed you can work with `TypedValue`, which
/// ensures the data is boxed by using `Any` in the signature of the generated `ccall` invocation,
/// but restricts the type of the data in the generated function to the type constructed from the
/// `TypedValue`'s type parameter.
///
/// [`AsyncCondition`]: https://docs.julialang.org/en/v1/base/base/#Base.AsyncCondition
/// Conditional compilation depending on the used version of Julia.
///
/// This macro can be used instead of a custom `cfg` to conditionally compile code for
/// certain versions of Julia. For example, to enable a function when Julia 1.6, 1.7 or 1.10 is
/// used on Linux, or when Julia 1.7 or 1.10 is used on Windows:
///
/// `#[julia_version(since = "1.6", until = "1.10", except = ["1.8", "1.9"], windows_lts = false)]`
///
/// By default, `since = "1.6"`, `until = "1.10"`, `except = []`, and `windows_lts = None`, so the
/// above can be written more compactly as:
///
/// `#[julia_version(except = ["1.8", "1.9"], windows_lts = false)]`.
/// Derive `IntoJulia`.
///
/// Should only be used in combination with layouts generated by JlrsReflect.jl
/// Derive `Unbox`.
///
/// Should only be used in combination with layouts generated by JlrsReflect.jl
/// Derive `Typecheck`.
///
/// Should only be used in combination with layouts generated by JlrsReflect.jl
/// Derive `ValidLayout`.
///
/// Should only be used in combination with layouts generated by JlrsReflect.jl
/// Derive `ValidField`.
///
/// Should only be used in combination with layouts generated by JlrsReflect.jl
/// Derive `ConstructType`.
///
/// Should only be used in combination with layouts generated by JlrsReflect.jl
/// Derive `CCallArg`.
///
/// Should only be used in combination with layouts generated by JlrsReflect.jl
/// Derive `CCallReturn`.
///
/// Should only be used in combination with layouts generated by JlrsReflect.jl