Skip to content

Commit 7a64241

Browse files
authored
[JuliaLowering] ccall((lib,sym)...) and cfunction fixes (#60257)
- `ccall` with a `lib,sym` tuple argument was being desugared to a call to `Core.Tuple` when we actually want an `Expr(:tuple)` - `cfunction` only worked in simple cases, since the check for local variables in scope resolution will fail with any nontrivial function body. I know that using the new `static_eval` head could be considered a bugfix (see discussion at JuliaLang/JuliaLowering.jl#36), but this PR just uses `inert` to match Base. stdlib status: 38/51 (with #60255)
1 parent 68553c5 commit 7a64241

File tree

6 files changed

+59
-20
lines changed

6 files changed

+59
-20
lines changed

JuliaLowering/src/desugaring.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,13 +1724,12 @@ end
17241724

17251725
# Expand the (sym,lib) argument to ccall/cglobal
17261726
function expand_C_library_symbol(ctx, ex)
1727-
expanded = expand_forms_2(ctx, ex)
17281727
if kind(ex) == K"tuple"
1729-
expanded = @ast ctx ex [K"static_eval"(meta=name_hint("function name and library expression"))
1730-
expanded
1728+
return @ast ctx ex [K"static_eval"(meta=name_hint("function name and library expression"))
1729+
mapchildren(e->expand_forms_2(ctx,e), ctx, ex)
17311730
]
17321731
end
1733-
return expanded
1732+
return expand_forms_2(ctx, ex)
17341733
end
17351734

17361735
function expand_ccall(ctx, ex)

JuliaLowering/src/eval.jl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -412,13 +412,16 @@ function _to_lowered_expr(ex::SyntaxTree, stmt_offset::Int)
412412
Expr(:meta, args...)
413413
elseif k == K"static_eval"
414414
@assert numchildren(ex) == 1
415-
_to_lowered_expr(ex[1], stmt_offset)
416-
elseif k == K"cfunction"
417-
args = Any[_to_lowered_expr(e, stmt_offset) for e in children(ex)]
418-
if kind(ex[2]) == K"static_eval"
419-
args[2] = QuoteNode(args[2])
415+
if kind(ex[1]) === K"tuple"
416+
# Should just be ccall library spec
417+
@assert numchildren(ex[1]) === 2
418+
Expr(:tuple, _to_lowered_expr(ex[1][1], stmt_offset),
419+
_to_lowered_expr(ex[1][2], stmt_offset))
420+
elseif kind(ex[1]) === K"function"
421+
QuoteNode(Expr(ex))
422+
else
423+
_to_lowered_expr(ex[1], stmt_offset)
420424
end
421-
Expr(:cfunction, args...)
422425
else
423426
# Allowed forms according to https://docs.julialang.org/en/v1/devdocs/ast/
424427
#
@@ -438,6 +441,7 @@ function _to_lowered_expr(ex::SyntaxTree, stmt_offset::Int)
438441
k == K"gc_preserve_begin" ? :gc_preserve_begin :
439442
k == K"gc_preserve_end" ? :gc_preserve_end :
440443
k == K"foreigncall" ? :foreigncall :
444+
k == K"cfunction" ? :cfunction :
441445
k == K"new_opaque_closure" ? :new_opaque_closure :
442446
nothing
443447
if isnothing(head)

JuliaLowering/src/syntax_macros.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,10 @@ function Base.var"@cfunction"(__context__::MacroContext, callable, return_type,
9090
typ = Base.CFunction
9191
else
9292
# Kinda weird semantics here - without `$`, the callable is a top level
93-
# expression which will be evaluated by `jl_resolve_globals_in_ir`,
94-
# implicitly within the module where the `@cfunction` is expanded into.
95-
fptr = @ast __context__ callable [K"static_eval"(
96-
meta=name_hint("cfunction function name"))
93+
# expression evaluated within the module where the `@cfunction` is
94+
# expanded into.
95+
fptr = @ast __context__ callable [K"inert"(
96+
meta=CompileHints(:as_Expr, true))
9797
callable
9898
]
9999
typ = Ptr{Cvoid}

JuliaLowering/test/function_calls_ir.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ ccall((:strlen, libc), Csize_t, (Cstring,), "asdfg")
370370
1 TestMod.Cstring
371371
2 (call top.cconvert %"asdfg")
372372
3 (call top.unsafe_convert %%₂)
373-
4 (foreigncall (static_eval (call core.tuple :strlen TestMod.libc)) (static_eval TestMod.Csize_t) (static_eval (call core.svec TestMod.Cstring)) 0 :ccall %%₂)
373+
4 (foreigncall (static_eval (tuple-p :strlen TestMod.libc)) (static_eval TestMod.Csize_t) (static_eval (call core.svec TestMod.Cstring)) 0 :ccall %%₂)
374374
5 (return %₄)
375375

376376
########################################
@@ -521,7 +521,7 @@ ccall(:foo, Csize_t, (Cstring..., Cstring...), "asdfg", "blah")
521521
cglobal((:sym, lib), Int)
522522
#---------------------
523523
1 TestMod.Int
524-
2 (call core.cglobal (static_eval (call core.tuple :sym TestMod.lib)) %₁)
524+
2 (call core.cglobal (static_eval (tuple-p :sym TestMod.lib)) %₁)
525525
3 (return %₂)
526526

527527
########################################

JuliaLowering/test/misc.jl

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ function cvarargs_2(arg1::Float64, arg2::Float64)
6969
end
7070
""") isa Function
7171
@test test_mod.cvarargs_2(1.1, 2.2) == "1.1 2.2"
72+
# (function, library) syntax
73+
@test JuliaLowering.include_string(test_mod, """
74+
ccall((:ctest, :libccalltest), Complex{Int}, (Complex{Int},), 10 + 20im)
75+
""") === 11 + 18im
76+
# (function, library): library is a global
77+
@eval test_mod libccalltest_var = "libccalltest"
78+
@test JuliaLowering.include_string(test_mod, """
79+
ccall((:ctest, libccalltest_var), Complex{Int}, (Complex{Int},), 10 + 20im)
80+
""") === 11 + 18im
7281

7382
# cfunction
7483
JuliaLowering.include_string(test_mod, """
@@ -85,22 +94,49 @@ cf_float = JuliaLowering.include_string(test_mod, """
8594
""")
8695
@test @ccall($cf_float(2::Float64, 3::Float64)::Float64) == 32.0
8796

88-
# Test that hygiene works with @ccallable function names (this is broken in
89-
# Base)
97+
# Test that hygiene works with @ccallable function names
9098
JuliaLowering.include_string(test_mod, raw"""
9199
f_ccallable_hygiene() = 1
92100
93101
module Nested
94102
f_ccallable_hygiene() = 2
95103
macro cfunction_hygiene()
96-
:(@cfunction(f_ccallable_hygiene, Int, ()))
104+
:(@cfunction($f_ccallable_hygiene, Int, ()))
97105
end
98106
end
99107
""")
100108
cf_hygiene = JuliaLowering.include_string(test_mod, """
101109
Nested.@cfunction_hygiene
102110
""")
103111
@test @ccall($cf_hygiene()::Int) == 2
112+
# Same as above, but non-interpolated symbol. Arguably this could return 20,
113+
# but if it should, this is a bug in the macro implementation, not lowering.
114+
# Match Base for now.
115+
JuliaLowering.include_string(test_mod, raw"""
116+
f_ccallable_hygiene() = 10
117+
118+
module Nested
119+
f_ccallable_hygiene() = 20
120+
macro cfunction_hygiene()
121+
:(@cfunction(f_ccallable_hygiene, Int, ()))
122+
end
123+
end
124+
""")
125+
cf_hygiene = JuliaLowering.include_string(test_mod, """
126+
Nested.@cfunction_hygiene
127+
""")
128+
@test @ccall($cf_hygiene()::Int) == 10
129+
130+
# quoted function in cfunction
131+
quoted_cfn_anon = JuliaLowering.include_string(test_mod, raw"""
132+
@cfunction((function(x); x; end), Int, (Int,))
133+
""")
134+
@test ccall(quoted_cfn_anon, Int, (Int,), 1) == 1
135+
136+
quoted_cfn_named = JuliaLowering.include_string(test_mod, raw"""
137+
@cfunction((function fname_unused(x); x; end), Int, (Int,))
138+
""")
139+
@test ccall(quoted_cfn_named, Int, (Int,), 1) == 1
104140

105141
# Test that ccall can be passed static parameters in type signatures.
106142
#

JuliaLowering/test/misc_ir.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ JuxtuposeTest.@emit_juxtupose
355355
# @cfunction expansion with global generic function as function argument
356356
@cfunction(callable, Int, (Int, Float64))
357357
#---------------------
358-
1 (cfunction Ptr{Nothing} (static_eval TestMod.callable) (static_eval TestMod.Int) (static_eval (call core.svec TestMod.Int TestMod.Float64)) :ccall)
358+
1 (cfunction Ptr{Nothing} (inert callable) (static_eval TestMod.Int) (static_eval (call core.svec TestMod.Int TestMod.Float64)) :ccall)
359359
2 (return %₁)
360360

361361
########################################

0 commit comments

Comments
 (0)