Why is ccall syntax?

ccall isn’t a function or macro but syntax. Why does it need to be syntax?

3 Likes

well @ccall is a macro. I guess the answer is simply that because nothing is lower-level than ccall? it doesn’t make sense to make a function ccall just so it eventually perform what we now call ccall.

Good question. There’s a few possible answers…

The basic answer

It doesn’t need to be syntax. @ccall is a more recent and much more attractive syntax for the same thing which doesn’t need special support from the parser or lowering.

The historical answer

ccall dates from very early in Julia’s development. Perhaps around this commit where it was first added to the runtime or this commit where the syntax became more similar to its current form.

This was back when Julia source code was still stored in .j files, and the first ccall commit was before macros were even implemented as part of this commit.

The pedantic answer

ccall is parsed in the same way as a normal function call and it’s represented the same way in the AST. So it’d be somewhat reasonable to say it’s not special syntax at all. (This is almost true, though I admit there’s some special processing for the name ccall as part of syntax lowering. I’m not 100% sure whether this is essential or just a convenient place to put this.)

7 Likes

Here are some of the examples I am looking at

julia> n = 2; t = (Cstring, repeat([Cint], n)...); ccall(:printf, Cvoid, t, "%d\n", 1, 1)
ERROR: syntax: ccall argument types must be a tuple; try "(T,)" around REPL[10]:1
Stacktrace:
 [1] top-level scope
   @ REPL[10]:1

julia> n = 2; t = (Cstring, repeat([Cint], n)...); @code_lowered ccall(:printf, Cvoid, t, "%d\n", 1, 1)
ERROR: UndefVarError: ccall not defined
1 Like

Yes, I think these oddities are due to the way that lowering processes the ccall arguments — apparently it doesn’t produce very good error messages when combined with @code_lowered.

For variadic arguments, it’s rather neater to use @ccall:

julia> @ccall printf("%d\n"::Cstring; 12345::Cint)::Cint;
12345
1 Like

Is it possible to splat into @ccall?

@ccall printf("%d\n"::Cstring, $((i::Cint for c in 1:5)...))::Cint
ERROR: LoadError: ArgumentError: args in @ccall need type annotations. '$((i::Cint for c = 1:5)...)' doesn't have one.
Stacktrace:

You can splat into the AST which @ccall is a part of, provided you do it before macro expansion. For example:

@eval @ccall printf("%d %d %d %d %d\n"::Cstring, $((:($i::Cint) for i in 1:5)...))::Cint

But it’s unlikely that it’s a good idea to use @eval like this! What’s your surrounding code look like for this use case?

1 Like

This is just for quick interactive hacking.

But I am not familiar with the problems of using @eval in this way – what should I know about it?

For quick hacking in the REPL it’s fine! Also it’s good for top-level evaluation of expressions when doing code generation. For example, if you’re generating a bunch of function definitions to go into your module at precompile time.

The main surprise people have with @eval (and eval) is that it evaluates the expression at global scope, not in the local scope where the eval is called. This is great for making it possible to generate fast code, but it means it’s usually a mistake to use eval inside a function.

2 Likes