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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
use TokenStream;
use *;
use *;
use ;
/// 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 `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 a doc comment; if multiple functions are exported
/// // with the same name it shoud only be documented once. All exported items can be
/// // documented.
/// //
/// // If the function doesn't need to call into Julia, you can annotate it with `#[gc_safe]`
/// // to allow the GC to run without having to wait until the function has returned.
///
/// /// bar(arr::Array)
/// ///
/// /// Documentation for this function"]
/// #[gc_safe]
/// 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.
/// in MyType fn new(arg0: TypedValue<u32>) -> TypedValueRet<MyType> as MyForeignType;
///
/// // Exports `MyType::add` as the function `increment!`.
/// //
/// // If a method takes `self` in some way, it is tracked by default. You can opt out of this
/// // behavior with the `#[untracked_self]` attribute.
/// #[untracked_self]
/// in MyType fn add(&mut self, incr: u32) -> JlrsResult<u32> as increment!;
///
/// // Exports the alias `MyTypeAlias` for `MyType`.
/// //
/// // This is exposes as `const MyTypeAlias = MyType`.
/// type MyTypeAlias = MyType;
///
/// // 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;
///
/// // You can loop over types to export types and functions multiple times with
/// // different type parameters.
/// for T in [f32, f64] {
/// fn has_generic(t: T) -> T;
///
/// // POpaque<T> must implement `ParametricBase` and `ParametricVariant`.
/// struct POpaque<T>;
///
/// in POpaque<T> fn new(value: T) -> TypedValueRet<POpaque<T>> as POpaque;
/// }
///
/// // You can use an environment of type parameters to define generic functions.
/// // type GenericEnv = tvars!(tvar!('T'; AbstractFloat), tvar!('N'), tvar!('A'; AbstractArray<tvar!('T'), tvar!('N')>));
/// fn takes_generics_from_env(array: TypedValue<tvar!('A')>, data: TypedValue<tvar!('T')>) use GenericEnv;
/// }
/// ```
///
/// 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
/// Encode the literal string passed to this macro as [`ConstantBytes`].
///
/// [`ConstantBytes`]: jlrs::data::types::construct_type::ConstantBytes
/// 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 `IsBits`.
///
/// Should only be used in combination with layouts generated by JlrsReflect.jl
/// Derive `HasLayout`.
///
/// 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