-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Function keyword arguments argument evaluation order #9535
Comments
I'm surprised that having |
And forgot to mention, this is on latest master as of an hour ago.
|
it looks like there may be multiple bugs here:
allowing both |
AFAIK, although the parsing pass does move all parameters after julia> dump(:(f(a, b, c=0, d, args...; e=0, kw...)), 100, "")
Expr
head: Symbol call
args: Array(Any,(7,))
1: Symbol f
2: Expr
head: Symbol parameters
args: Array(Any,(2,))
1: Expr
head: Symbol kw
args: Array(Any,(2,))
1: Symbol e
2: Int64 0
typ: Any
2: Expr
head: Symbol ...
args: Array(Any,(1,))
1: Symbol kw
typ: Any
typ: Any
3: Symbol a
4: Symbol b
5: Expr
head: Symbol kw
args: Array(Any,(2,))
1: Symbol c
2: Int64 0
typ: Any
6: Symbol d
7: Expr
head: Symbol ...
args: Array(Any,(1,))
1: Symbol args
typ: Any
typ: Any It will be the right order as long as the I personally don't really mind to keep the current order either, but sticking to the syntatic order would be better. The Also, I just remembered another example of confusing printing of AST, related to #9503 and #9474. julia> Expr(:(=), :c, 0)
:(c = 0)
julia> Expr(:kw, :c, 0)
:(c=0) I guess this time they are technically printed differently and I'm also not sure how reasonably it is to parse |
Not sure if this is useful info but the bug is probably in type inference julia> function get_next()
global counter
counter = counter + 1
return counter
end
get_next (generic function with 1 method)
julia> function g()
global counter = 0
f(;[(:d, get_next()), (:f, get_next())]...,)
end
g (generic function with 1 method)
julia> function f(args...; kws...)
println((args, kws))
end
f (generic function with 1 method)
julia> g()
((),Any[(:d,2),(:f,2)])
julia> code_lowered(g, ())
1-element Array{Any,1}:
:($(Expr(:lambda, Any[], Any[Any[symbol("#s452"),symbol("#s449"),symbol("#s448"),symbol("#s447"),symbol("#s446"),symbol("#s444"),symbol("#s451"),symbol("#s443"),symbol("#s450"),symbol("#s445")],Any[Any[symbol("#s452"),:Any,18],Any[symbol("#s449"),:Any,18],Any[symbol("#s448"),:Any,2],Any[symbol("#s447"),:Any,18],Any[symbol("#s446"),:Any,18],Any[symbol("#s444"),:Any,18],Any[symbol("#s451"),:Any,18],Any[symbol("#s443"),:Any,18],Any[symbol("#s450"),:Any,18],Any[symbol("#s445"),:Any,2]],Any[]], :(begin # none, line 2:
counter = 0 # line 3:
#s452 = (top(Array))(top(Any),0)::Any
#s449 = vcat((top(tuple))(:d,get_next()::Any)::Any,(top(tuple))(:f,get_next()::Any)::Any)::Any
#s448 = (top(start))(#s449)::Any
unless (top(!))((top(done))(#s449,#s448)::Any)::Any goto 1
2:
#s447 = (top(next))(#s449,#s448)::Any
#s446 = (top(tupleref))(#s447,1)::Any
#s445 = (top(start))(#s446)::Any
#s444 = ((top(getfield))(top(Base),:indexed_next)::Any)(#s446,1,#s445)::Any
#s451 = (top(tupleref))(#s444,1)::Any
#s445 = (top(tupleref))(#s444,2)::Any
#s443 = ((top(getfield))(top(Base),:indexed_next)::Any)(#s446,2,#s445)::Any
#s450 = (top(tupleref))(#s443,1)::Any
#s445 = (top(tupleref))(#s443,2)::Any
#s448 = (top(tupleref))(#s447,2)::Any
(top(ccall))(:jl_cell_1d_push2,Void,(top(tuple))(Any,Any,Any)::Any,#s452,0,(top(typeassert))(#s451,top(Symbol))::Any,0,#s450,0)::Any
3:
unless (top(!))((top(!))((top(done))(#s449,#s448)::Any)::Any)::Any goto 2
1:
0:
unless (top(isempty))(#s452)::Any goto 4
return f()::Any
4:
return (top(kwcall))(call,0,f,#s452)::Any
end::Any))))
julia> code_typed(g, ())
1-element Array{Any,1}:
:($(Expr(:lambda, Any[], Any[Any[symbol("#s452"),symbol("#s449"),symbol("#s448"),symbol("#s447"),symbol("#s446"),symbol("#s444"),symbol("#s451"),symbol("#s443"),symbol("#s450"),symbol("#s445"),:_var1,:_var0,:_var2,:_var3],Any[Any[symbol("#s452"),Array{Any,1},18],Any[symbol("#s449"),Any,18],Any[symbol("#s448"),Any,2],Any[symbol("#s447"),Any,18],Any[symbol("#s446"),Any,18],Any[symbol("#s444"),Any,18],Any[symbol("#s451"),Any,18],Any[symbol("#s443"),Any,18],Any[symbol("#s450"),Any,18],Any[symbol("#s445"),Any,2],Any[:_var1,Int64,18],Any[:_var0,Array{Any,1},18],Any[:_var2,Array{Any,1},18],Any[:_var3,(((),Array{Any,1}),),0]],Any[]], :(begin # none, line 2:
counter = 0 # line 3:
#s452 = (top(ccall))(:jl_alloc_array_1d,$(Expr(:call1, :(top(apply_type)), :Array, Any, 1)),$(Expr(:call1, :(top(tuple)), :Any, :Int)),Array{Any,1},0,0,0)::Array{Any,1}
counter = counter + 1::Any
counter = counter + 1::Any
#s449 = vcat((top(tuple))(:d,counter)::(Symbol,Any),(top(tuple))(:f,counter)::(Symbol,Any))::Any
#s448 = (top(start))(#s449)::Any
unless (top(!))((top(done))(#s449,#s448)::Any)::Any goto 1
2:
#s447 = (top(next))(#s449,#s448)::Any
#s446 = (top(tupleref))(#s447,1)::Any
#s445 = (top(start))(#s446)::Any
#s444 = ((top(getfield))(top(Base),:indexed_next)::Any)(#s446,1,#s445)::Any
#s451 = (top(tupleref))(#s444,1)::Any
#s445 = (top(tupleref))(#s444,2)::Any
#s443 = ((top(getfield))(top(Base),:indexed_next)::Any)(#s446,2,#s445)::Any
#s450 = (top(tupleref))(#s443,1)::Any
#s445 = (top(tupleref))(#s443,2)::Any
#s448 = (top(tupleref))(#s447,2)::Any
(top(ccall))(:jl_cell_1d_push2,Void,$(Expr(:call1, :(top(tuple)), :Any, :Any, :Any)),#s452::Array{Any,1},0,(top(typeassert))(#s451,top(Symbol))::Symbol,0,#s450,0)::Void
3:
unless (top(!))((top(!))((top(done))(#s449,#s448)::Any)::Any)::Any goto 2
1:
0:
_var1 = (top(arraylen))(#s452::Array{Any,1})::Int64
unless _var1::Int64 === 0::Bool goto 4
_var0 = (top(ccall))(:jl_alloc_array_1d,$(Expr(:call1, :(top(apply_type)), :Array, Any, 1)),$(Expr(:call1, :(top(tuple)), :Any, :Int)),Array{Any,1},0,0,0)::Array{Any,1}
_var2 = _var0::Array{Any,1}
return println(GetfieldNode(Base,:STDOUT,Any),(top(tuple))($(Expr(:call1, :(top(tuple)))),_var2::Array{Any,1})::((),Array{Any,1}))::Any
4:
return (top(kwcall))(call,0,f,#s452::Array{Any,1})::Any
end::Any)))) The lowered code looks fine but the typed code is clearly wrong.... |
This is well-known: http://docs.julialang.org/en/latest/manual/performance-tips/#avoid-global-variables. See the item about annotating them at point-of-use. |
@timholy Which part are you refering to? I think this issue is more about correctness than performance. |
Sorry, I thought you were worried about all the |
The issue is with this part counter = counter + 1::Any
counter = counter + 1::Any
#s449 = vcat((top(tuple))(:d,counter)::(Symbol,Any),(top(tuple))(:f,counter)::(Symbol,Any))::Any The compiler decided to increment the global variable twice before the value is used. Type inference not being able to decide the type of counter is not the issue here |
should d071904 be scheduled for backporting? |
Yes |
It looks like maybe only the top half of the patch to inference.jl applies to 0.3? |
i thought both parts would still be applicable. the second part ensures that we compute affect_free even if occ > 6 (previously, it was assuming that inline_worthy would return false for occ > 6 and so it skipped finishing the computation of affect_free. but that's not a good assumption anymore) |
Ah, you're right, I thought 0.3 had less of the newer inlining logic. |
d071904 seems like another safer-for-0.3.6 change |
(cherry picked from commit d071904) Conflicts: base/inference.jl test/core.jl
backported in 41bfd37 |
I stumbled across this, which might be related. Certainly fits with the title:
I would expect both to fail as keywords are supposed to be evaluated left to right and |
Positional argument defaults are evaluated in left-to-right order but keyword argument defaults are evaluated all-at-once, which leaves things kind of inconsistent, imo. I'd argue that keyword argument defaults should also be evaluated in left-to-right order. |
The manual says "the [keyword] default expressions are evaluated left-to-right" but all in the same scope. |
Right, the scoping is what I meant. |
A possibly related issue came up on julia-users:
|
No it's #17240 |
has not seen any work for 0.6 timeframe |
The issue with splatted keywords is fixed:
The remaining issue is that keyword arguments are evaluated before positional arguments. That comes out weird in cases like
I suppose we should make everything strictly left-to-right. |
Yeah, the fix was when this issue was renamed with the bug and priority label removed. (edit: and apparently with the bug label added back again?....) |
fix #9535, evaluate positional and keyword args strictly left-to-right
I was trying to figure out what is the order julia evaluate it's arguments (especially when there's keyword arguments passed in different forms). IIRC, python grantee that the arguments are evaluated in the order they appear in the code. Is there similar garentee in julia? I'm sorry if it is already in the document but at least I didn't find anything when I look through the page about functions.
I was trying to figure out the current order myself with some code and I see the following very unexpected result.
As shown above in the REPL output, the arguments passed to the function are fine as long as I'm not passing (/forwarding) keyword arguments with the
(;kw...)
syntax (is there a name for it?) and the arguments are evaluated in the order they appear in the code with the keyword arguments first and then the positional ones. However, whenever I'm trying to forward some keyword arguments, the whole thing gets messed up and theget_next
function "appears to" return same value for multiple calls.This issue disappears when I tweak the
get_next
functionIt seems like sth related to the compiler/optimizer to me. It also shows that the forwarded kw args are evaluated before everything else (which is fine as long as it's consistent but a little bit weird....)
The text was updated successfully, but these errors were encountered: