• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #38182

15 Aug 2025 03:55AM UTC coverage: 77.87% (-0.4%) from 78.28%
#38182

push

local

web-flow
🤖 [master] Bump the SparseArrays stdlib from 30201ab to bb5ecc0 (#59263)

Stdlib: SparseArrays
URL: https://github.com/JuliaSparse/SparseArrays.jl.git
Stdlib branch: main
Julia branch: master
Old commit: 30201ab
New commit: bb5ecc0
Julia version: 1.13.0-DEV
SparseArrays version: 1.13.0
Bump invoked by: @ViralBShah
Powered by:
[BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl)

Diff:
https://github.com/JuliaSparse/SparseArrays.jl/compare/30201abcb...bb5ecc091

```
$ git log --oneline 30201ab..bb5ecc0
bb5ecc0 fast quadratic form for dense matrix, sparse vectors (#640)
34ece87 Extend 3-arg `dot` to generic `HermOrSym` sparse matrices (#643)
095b685 Exclude unintended complex symmetric sparse matrices from 3-arg `dot` (#642)
8049287 Fix signature for 2-arg matrix-matrix `dot` (#641)
cff971d Make cond(::SparseMatrix, 1 / Inf) discoverable from 2-norm error (#629)
```

Co-authored-by: ViralBShah <744411+ViralBShah@users.noreply.github.com>

48274 of 61993 relevant lines covered (77.87%)

9571166.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.25
/base/c.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
# definitions related to C interface
4

5
import .Intrinsics: cglobal
6

7
"""
8
    cglobal((symbol, library) [, type=Cvoid])
9

10
Obtain a pointer to a global variable in a C-exported shared library, specified exactly as
11
in [`ccall`](@ref).
12
Returns a `Ptr{Type}`, defaulting to `Ptr{Cvoid}` if no `Type` argument is
13
supplied.
14
The values can be read or written by [`unsafe_load`](@ref) or [`unsafe_store!`](@ref),
15
respectively.
16
"""
17
cglobal
18

19
"""
20
    CFunction struct
21

22
Garbage-collection handle for the return value from `@cfunction`
23
when the first argument is annotated with '\\\$'.
24
Like all `cfunction` handles, it should be passed to `ccall` as a `Ptr{Cvoid}`,
25
and will be converted automatically at the call site to the appropriate type.
26

27
See [`@cfunction`](@ref).
28
"""
29
mutable struct CFunction <: Ref{Cvoid}
30
    ptr::Ptr{Cvoid}
31
    f::Any
32
    _1::Ptr{Cvoid}
33
    _2::Ptr{Cvoid}
34
    let constructor = false end
35
end
36
unsafe_convert(::Type{Ptr{Cvoid}}, cf::CFunction) = cf.ptr
×
37

38
"""
39
    @cfunction(callable, ReturnType, (ArgumentTypes...,)) -> Ptr{Cvoid}
40
    @cfunction(\$callable, ReturnType, (ArgumentTypes...,)) -> CFunction
41

42
Generate a C-callable function pointer from the Julia function `callable`
43
for the given type signature.
44
To pass the return value to a `ccall`, use the argument type `Ptr{Cvoid}` in the signature.
45

46
Note that the argument type tuple must be a literal tuple, and not a tuple-valued variable or expression
47
(although it can include a splat expression). And that these arguments will be evaluated in global scope
48
during compile-time (not deferred until runtime).
49
Adding a '\\\$' in front of the function argument changes this to instead create a runtime closure
50
over the local variable `callable` (this is not supported on all architectures).
51

52
See [manual section on ccall and cfunction usage](@ref Calling-C-and-Fortran-Code).
53

54
# Examples
55
```julia-repl
56
julia> function foo(x::Int, y::Int)
57
           return x + y
58
       end
59

60
julia> @cfunction(foo, Int, (Int, Int))
61
Ptr{Cvoid} @0x000000001b82fcd0
62
```
63
"""
64
macro cfunction(f, rt, at)
79✔
65
    if !(isa(at, Expr) && at.head === :tuple)
80✔
66
        throw(ArgumentError("@cfunction argument types must be a literal tuple"))
1✔
67
    end
68
    at.head = :call
78✔
69
    pushfirst!(at.args, GlobalRef(Core, :svec))
78✔
70
    if isa(f, Expr) && f.head === :$
78✔
71
        fptr = f.args[1]
11✔
72
        typ = CFunction
11✔
73
    else
74
        fptr = QuoteNode(f)
67✔
75
        typ = Ptr{Cvoid}
67✔
76
    end
77
    cfun = Expr(:cfunction, typ, fptr, rt, at, QuoteNode(:ccall))
78✔
78
    return esc(cfun)
78✔
79
end
80

81
if ccall(:jl_is_char_signed, Ref{Bool}, ())
82
    const Cchar = Int8
83
else
84
    const Cchar = UInt8
85
end
86
"""
87
    Cchar
88

89
Equivalent to the native `char` c-type.
90
"""
91
Cchar
92

93
# The ccall here is equivalent to Sys.iswindows(), but that's not defined yet
94
if ccall(:jl_get_UNAME, Any, ()) === :NT
95
    const Clong = Int32
96
    const Culong = UInt32
97
    const Cwchar_t = UInt16
98
else
99
    const Clong = Int
100
    const Culong = UInt
101
    const Cwchar_t = Int32
102
end
103

104
"""
105
    Clong
106

107
Equivalent to the native `signed long` c-type.
108
"""
109
Clong
110

111
"""
112
    Culong
113

114
Equivalent to the native `unsigned long` c-type.
115
"""
116
Culong
117

118
"""
119
    Cwchar_t
120

121
Equivalent to the native `wchar_t` c-type ([`Int32`](@ref)).
122
"""
123
Cwchar_t
124

125
if ccall(:jl_get_UNAME, Any, ()) !== :NT
126
    const sizeof_mode_t = ccall(:jl_sizeof_mode_t, Cint, ())
127
    if sizeof_mode_t == 2
128
        const Cmode_t = Int16
129
    elseif sizeof_mode_t == 4
130
        const Cmode_t = Int32
131
    elseif sizeof_mode_t == 8
132
        const Cmode_t = Int64
133
    else
134
        error("invalid sizeof mode_t")
135
    end
136
end
137

138
# deferring (or un-deferring) ctrl-c handler for external C code that
139
# is not interrupt safe (see also issue #2622).  The sigatomic_begin/end
140
# functions should always be called in matched pairs, ideally via:
141
#            disable_sigint() do .. end
142
# reennable_sigint is provided so that immediate ctrl-c handling is
143
# re-enabled within a sigatomic region, e.g. inside a Julia callback function
144
# within a long-running C routine.
145
sigatomic_begin() = ccall(:jl_sigatomic_begin, Cvoid, ())
556,042✔
146
sigatomic_end() = ccall(:jl_sigatomic_end, Cvoid, ())
11,105,310✔
147

148
"""
149
    disable_sigint(f::Function)
150

151
Disable Ctrl-C handler during execution of a function on the current task,
152
for calling external code that may call julia code that is not interrupt safe.
153
Intended to be called using `do` block syntax as follows:
154

155
    disable_sigint() do
156
        # interrupt-unsafe code
157
        ...
158
    end
159

160
This is not needed on worker threads (`Threads.threadid() != 1`) since the
161
`InterruptException` will only be delivered to the master thread.
162
External functions that do not call julia code or julia runtime
163
automatically disable sigint during their execution.
164
"""
165
function disable_sigint(f::Function)
×
166
    sigatomic_begin()
×
167
    res = f()
×
168
    # Exception unwind sigatomic automatically
169
    sigatomic_end()
×
170
    res
×
171
end
172

173
"""
174
    reenable_sigint(f::Function)
175

176
Re-enable Ctrl-C handler during execution of a function.
177
Temporarily reverses the effect of [`disable_sigint`](@ref).
178
"""
179
function reenable_sigint(f::Function)
×
180
    sigatomic_end()
×
181
    res = f()
×
182
    # Exception unwind sigatomic automatically
183
    sigatomic_begin()
×
184
    res
×
185
end
186

187
"""
188
    exit_on_sigint(on::Bool)
189

190
Set `exit_on_sigint` flag of the julia runtime.  If `false`, Ctrl-C
191
(SIGINT) is capturable as [`InterruptException`](@ref) in `try` block.
192
This is the default behavior in REPL, any code run via `-e` and `-E`
193
and in Julia script run with `-i` option.
194

195
If `true`, `InterruptException` is not thrown by Ctrl-C.  Running code
196
upon such event requires [`atexit`](@ref).  This is the default
197
behavior in Julia script run without `-i` option.
198

199
!!! compat "Julia 1.5"
200
    Function `exit_on_sigint` requires at least Julia 1.5.
201
"""
202
function exit_on_sigint(on::Bool)
203
    ccall(:jl_exit_on_sigint, Cvoid, (Cint,), on)
2✔
204
end
205

206
function _ccallable(name::Union{Nothing, String}, rt::Type, sigt::Type)
×
207
    ccall(:jl_extern_c, Cvoid, (Any, Any, Any), name, rt, sigt)
×
208
end
209

210
function expand_ccallable(name, rt, def)
2✔
211
    if isa(def,Expr) && (def.head === :(=) || def.head === :function)
2✔
212
        sig = def.args[1]
2✔
213
        if sig.head === :(::)
2✔
214
            if rt === nothing
2✔
215
                rt = sig.args[2]
2✔
216
            end
217
            sig = sig.args[1]
2✔
218
        end
219
        if rt === nothing
2✔
220
            error("@ccallable requires a return type")
×
221
        end
222
        if sig.head === :call
2✔
223
            f = sig.args[1]
2✔
224
            if isa(f,Expr) && f.head === :(::)
2✔
225
                f = f.args[end]
×
226
            else
227
                f = :(typeof($f))
2✔
228
            end
229
            at = Any[let a = sig.args[i]
2✔
230
                    if isa(a,Expr) && a.head === :(::)
×
231
                        a.args[end]
×
232
                    else
233
                        :Any
×
234
                    end
235
                end for i in 2:length(sig.args)]
236
            return quote
2✔
237
                @__doc__ $(esc(def))
238
                _ccallable($name, $(esc(rt)), $(Expr(:curly, :Tuple, esc(f), map!(esc, at, at)...)))
239
            end
240
        end
241
    end
242
    error("expected method definition in @ccallable")
×
243
end
244

245
"""
246
    @ccallable ["name"] function f(...)::RetType ... end
247

248
Make the annotated function be callable from C using its name. This can, for example,
249
be used to expose functionality as a C API when creating a custom Julia sysimage.
250

251
If the first argument is a string, it is used as the external name of the function.
252
"""
253
macro ccallable(def)
2✔
254
    expand_ccallable(nothing, nothing, def)
2✔
255
end
256
macro ccallable(rt, def)
257
    if rt isa String
258
        expand_ccallable(rt, nothing, def)
259
    else
260
        expand_ccallable(nothing, rt, def)
261
    end
262
end
263

264
# @ccall implementation
265
"""
266
    ccall_macro_parse(expression)
267

268
`ccall_macro_parse` is an implementation detail of `@ccall`.
269

270
It takes an expression like `:(printf("%d"::Cstring, value::Cuint)::Cvoid)`
271
returns: a tuple of `(function_name, return_type, arg_types, args)`
272

273
The above input outputs this:
274

275
    (:printf, :Cvoid, [:Cstring, :Cuint], ["%d", :value])
276
"""
277
function ccall_macro_parse(exprs)
33✔
278
    gc_safe = false
33✔
279
    expr = nothing
33✔
280
    if exprs isa Expr
33✔
281
        expr = exprs
8✔
282
    elseif length(exprs) == 1
25✔
283
        expr = exprs[1]
24✔
284
    elseif length(exprs) == 2
1✔
285
        gc_expr = exprs[1]
1✔
286
        expr = exprs[2]
1✔
287
        if gc_expr.head == :(=) && gc_expr.args[1] == :gc_safe
2✔
288
            if gc_expr.args[2] == true
1✔
289
                gc_safe = true
1✔
290
            elseif gc_expr.args[2] == false
×
291
                gc_safe = false
×
292
            else
293
                throw(ArgumentError("gc_safe must be true or false"))
×
294
            end
295
        else
296
            throw(ArgumentError("@ccall option must be `gc_safe=true` or `gc_safe=false`"))
×
297
        end
298
    else
299
        throw(ArgumentError("@ccall needs a function signature with a return type"))
×
300
    end
301

302
    # setup and check for errors
303
    if !isexpr(expr, :(::))
33✔
304
        throw(ArgumentError("@ccall needs a function signature with a return type"))
1✔
305
    end
306
    rettype = expr.args[2]
32✔
307

308
    call = expr.args[1]
32✔
309
    if !isexpr(call, :call)
32✔
310
        throw(ArgumentError("@ccall has to take a function call"))
1✔
311
    end
312

313
    # get the function symbols
314
    func = let f = call.args[1]
31✔
315
        if isexpr(f, :.)
31✔
316
            :(($(f.args[2]), $(f.args[1])))
3✔
317
        elseif isexpr(f, :$)
28✔
318
            f
4✔
319
        elseif f isa Symbol
24✔
320
            QuoteNode(f)
24✔
321
        else
322
            throw(ArgumentError("@ccall function name must be a symbol, a `.` node (e.g. `libc.printf`) or an interpolated function pointer (with `\$`)"))
59✔
323
        end
324
    end
325

326
    # detect varargs
327
    varargs = nothing
31✔
328
    argstart = 2
31✔
329
    callargs = call.args
31✔
330
    if length(callargs) >= 2 && isexpr(callargs[2], :parameters)
31✔
331
        argstart = 3
5✔
332
        varargs = callargs[2].args
5✔
333
    end
334

335
    # collect args and types
336
    args = []
31✔
337
    types = []
31✔
338

339
    function pusharg!(arg)
89✔
340
        if !isexpr(arg, :(::))
58✔
341
            throw(ArgumentError("args in @ccall need type annotations. '$arg' doesn't have one."))
2✔
342
        end
343
        push!(args, arg.args[1])
56✔
344
        push!(types, arg.args[2])
56✔
345
    end
346

347
    for i in argstart:length(callargs)
50✔
348
        pusharg!(callargs[i])
29✔
349
    end
38✔
350
    # add any varargs if necessary
351
    nreq = 0
30✔
352
    if varargs !== nothing
30✔
353
        if length(args) == 0
5✔
354
            throw(ArgumentError("C ABI prohibits vararg without one required argument"))
1✔
355
        end
356
        nreq = length(args)
4✔
357
        for a in varargs
4✔
358
            pusharg!(a)
29✔
359
        end
28✔
360
    end
361
    return func, rettype, types, args, gc_safe, nreq
28✔
362
end
363

364

365
function ccall_macro_lower(convention, func, rettype, types, args, gc_safe, nreq)
31✔
366
    statements = []
31✔
367

368
    # if interpolation was used, ensure the value is a function pointer at runtime.
369
    if isexpr(func, :$)
31✔
370
        push!(statements, Expr(:(=), :func, esc(func.args[1])))
5✔
371
        name = QuoteNode(func.args[1])
5✔
372
        func = :func
5✔
373
        check = quote
5✔
374
            if !isa(func, Ptr{Cvoid})
5✔
375
                name = $name
2✔
376
                throw(ArgumentError(LazyString("interpolated function `", name, "` was not a Ptr{Cvoid}, but ", typeof(func))))
2✔
377
            end
378
        end
379
        push!(statements, check)
5✔
380
    else
381
        func = esc(func)
26✔
382
    end
383
    cconv = nothing
31✔
384
    if convention isa Tuple
31✔
385
        cconv = Expr(:cconv, (convention..., gc_safe), nreq)
1✔
386
    else
387
        cconv = Expr(:cconv, (convention, UInt16(0), gc_safe), nreq)
30✔
388
    end
389

390
    return Expr(:block, statements...,
31✔
391
                Expr(:call, :ccall, func, cconv, esc(rettype),
392
                     Expr(:tuple, map!(esc, types, types)...), map!(esc, args, args)...))
393
end
394

395
"""
396
    @ccall library.function_name(argvalue1::argtype1, ...)::returntype
397
    @ccall function_name(argvalue1::argtype1, ...)::returntype
398
    @ccall \$function_pointer(argvalue1::argtype1, ...)::returntype
399

400
Call a function in a C-exported shared library, specified by
401
`library.function_name`, where `library` is a string constant or
402
literal. The library may be omitted, in which case the `function_name`
403
is resolved in the current process. Alternatively, `@ccall` may
404
also be used to call a function pointer `\$function_pointer`, such as
405
one returned by `dlsym`.
406

407
Each `argvalue` to `@ccall` is converted to the corresponding
408
`argtype`, by automatic insertion of calls to `unsafe_convert(argtype,
409
cconvert(argtype, argvalue))`. (See also the documentation for
410
[`unsafe_convert`](@ref Base.unsafe_convert) and [`cconvert`](@ref
411
Base.cconvert) for further details.) In most cases, this simply
412
results in a call to `convert(argtype, argvalue)`.
413

414

415
# Examples
416

417
    @ccall strlen(s::Cstring)::Csize_t
418

419
This calls the C standard library function:
420

421
    size_t strlen(char *)
422

423
with a Julia variable named `s`. See also `ccall`.
424

425
Varargs are supported with the following convention:
426

427
    @ccall printf("%s = %d"::Cstring ; "foo"::Cstring, foo::Cint)::Cint
428

429
The semicolon is used to separate required arguments (of which there
430
must be at least one) from variadic arguments.
431

432
Example using an external library:
433

434
    # C signature of g_uri_escape_string:
435
    # char *g_uri_escape_string(const char *unescaped, const char *reserved_chars_allowed, gboolean allow_utf8);
436

437
    const glib = "libglib-2.0"
438
    @ccall glib.g_uri_escape_string(my_uri::Cstring, ":/"::Cstring, true::Cint)::Cstring
439

440
The string literal could also be used directly before the function
441
name, if desired `"libglib-2.0".g_uri_escape_string(...`
442

443
It's possible to declare the ccall as `gc_safe` by using the `gc_safe = true` option:
444
    @ccall gc_safe=true strlen(s::Cstring)::Csize_t
445
This allows the garbage collector to run concurrently with the ccall, which can be useful whenever
446
the `ccall` may block outside of julia.
447
WARNING: This option should be used with caution, as it can lead to undefined behavior if the ccall
448
calls back into the julia runtime. (`@cfunction`/`@ccallables` are safe however)
449
"""
450
macro ccall(exprs...)
35✔
451
    return ccall_macro_lower((:ccall), ccall_macro_parse(exprs)...)
35✔
452
end
453

454
macro ccall_effects(effects::UInt16, expr)
3✔
455
    return ccall_macro_lower((:ccall, effects), ccall_macro_parse(expr)...)
3✔
456
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc